npm package: @mdjs/mdjs-preview
Description: Rendering storybook story functions inside a preview window with show code capabilities
Author: Modern Web
Latest version: 0.6.0

```js server
/* START - Rocket auto generated - do not touch */
export const sourceRelativeFilePath = '30--tools/20--markdown-javascript/20--preview.rocket.md';
// prettier-ignore
import { html, layout, setupUnifiedPlugins, components, openGraphLayout } from '../../recursive.data.js';
export { html, layout, setupUnifiedPlugins, components, openGraphLayout };
export async function registerCustomElements() {
  // server-only components
  // prettier-ignore
  customElements.define('rocket-social-link', await import('@rocket/components/social-link.js').then(m => m.RocketSocialLink));
  // prettier-ignore
  customElements.define('rocket-header', await import('@rocket/components/header.js').then(m => m.RocketHeader));
  // prettier-ignore
  customElements.define('inline-notification', await import('@rocket/components/inline-notification.js').then(m => m.InlineNotification));
  // prettier-ignore
  customElements.define('rocket-main-docs', await import('@rocket/components/main-docs.js').then(m => m.RocketMainDocs));
  // prettier-ignore
  customElements.define('rocket-content-area', await import('@rocket/components/content-area.js').then(m => m.RocketContentArea));
  // hydrate-able components
  // prettier-ignore
  customElements.define('rocket-search', await import('@rocket/search/search.js').then(m => m.RocketSearch));
  // prettier-ignore
  customElements.define('rocket-drawer', await import('@rocket/components/drawer.js').then(m => m.RocketDrawer));
}
export const needsLoader = true;
/* END - Rocket auto generated - do not touch */
```

# Preview

You can showcase live running code by annotating a code block with `js preview-story`.

## Features

- Shows components inside the page as they are
- You can enable “Simulation Mode” to break them out
- Simulation mode renders in an iframe to supporting media queries and isolated Simulation settings
- Simulation Settings
  - Style (windows, mac, android, iOS)
  - Size (small, medium, large, Galaxy S5, iPhone X, iPad …)
  - Automatic Height
  - Theme (light, dark)
  - Language (en, nl, …)
- Settings are ”global” for all Simulators (e.g. changing one will change all)
- Settings can be remembered for other pages / return visits

```js client
import { html } from '@mdjs/mdjs-preview';
import './assets/demo-element.js';
```

## JavaScript Story

````md
```js client
import { html } from '@mdjs/mdjs-preview';
import './assets/demo-element.js';
```

```js preview-story
export const foo = () => html`<demo-element></demo-element>`;
```
````

will result in

```js preview-story
export const foo = () => html` <demo-element></demo-element> `;
```

```js story-code
// not defined for android
```

```js story-code
// not defined for ios
```

#### Story Code

If your preview is followed by a code blocks marked as `story-code` then those will be shown when switching between multiple platforms

````md
```js preview-story
// will be visible when platform web is selected
export const JsPreviewStory = () => html` <demo-element></demo-element> `;
```

```xml story-code
<!-- will be visible when platform android is selected -->
<Button
    android:id="@+id/demoElement"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Android Code"
    style="@style/Widget.Demo.Element"
/>
```

```swift story-code
// will be visible when platform ios is selected
import Demo.Element

let card = DemoElement()
```
````

See it in action by opening up the code block and switching platforms

```js preview-story
// will be visible when platform web is selected
export const JsPreviewStory = () => html` <demo-element></demo-element> `;
```

```xml story-code
<!-- will be visible when platform android is selected -->
<Button
    android:id="@+id/demoElement"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Android Code"
    style="@style/Widget.Demo.Element"
/>
```

```swift story-code
// will be visible when platform ios is selected
import Demo.Element

let card = DemoElement()
```

## HTML Story

````md
```html preview-story
<demo-element></demo-element>
```
````

will result in

```html preview-story
<demo-element></demo-element>
```

## Setup Simulation Mode

For simulation mode we need a dedicated html file that will be used as an iframe target while loading stories.

The fastest way to create such a file is to use the `layout-simulator` layout.

Create a file `docs/simulator.md` with the following content.

```md
---
layout: layout-simulator
eleventyExcludeFromCollections: true
excludeFromSearch: true
---
```

Once you have that you need to configure it for the story renderer by setting it in your `config/rocket.config.js`.

<!-- prettier-ignore-start -->
```js
/** @type {import('rocket/cli').RocketCliConfig} */
export default ({
  setupUnifiedPlugins: [
    adjustPluginOptions('mdjsSetupCode', {
      simulationSettings: { simulatorUrl: '/simulator/' },
    }),
  ],
});
```
<!-- prettier-ignore-end -->

<inline-notification type="tip">

You can freely choose the path for the "simulator" by creating the md file in a different folder and adjusting the path in the config.

</inline-notification>

## Simulator states

To simulate these stats that usually come from the device itself we put those infos on the document tag.

We can simulate the following settings

1. `platform`
   Adopting styles and behavior depending on which device platform you are.
   ```html
   <html platform="web"></html>
   <html platform="android"></html>
   <html platform="ios"></html>
   <!-- potentially later -->
   <html platform="web-windows"></html>
   <html platform="web-mac"></html>
   ```
2. `theme`
   Adjust your styles based on a theme - light/dark are the default but you can add as many as you want.
   ```html
   <html theme="light"></html>
   <html theme="dark"></html>
   ```
3. `language`
   Best to relay on `data-lang` as `lang` often gets changes by translations services which may interfere with your translation loading system.
   ```html
   <html lang="en-US" data-lang="en-US"></html>
   <html lang="de-DE" data-lang="de-DE"></html>
   ```

If you want to react to such document changes you can use an [MutationObserver](https://developer.mozilla.org/de/docs/Web/API/MutationObserver).

For a vanilla web component it could look something like this:

```js
class DemoElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });

    this.platform = 'the web';
    this.language = 'en-US';
    this.theme = 'light';

    this.observer = new MutationObserver(this.updateData);
  }

  updateData = () => {
    this.platform = document.documentElement.getAttribute('platform') || 'the web';
    this.language = document.documentElement.getAttribute('data-lang') || 'en-US';
    this.theme = document.documentElement.getAttribute('theme') || 'light';
    this.requestUpdate();
  };

  connectedCallback() {
    this.updateData();

    this.observer.observe(document.documentElement, { attributes: true });
  }

  requestUpdate() {
    this.shadowRoot.innerHTML = this.render();
  }

  render() {
    return `
      ...
    `;
  }
}

customElements.define('demo-element', DemoElement);
```

## Extending mdjs-preview

It is possible to define a custom version of mdjs-preview in order to add functionality, change
its appearance of make it run in 'hybrid mode' (accepting both lit1 and -2 TemplateResults).
The example below shows how the latter can be achieved by providing a custom render function.
Note that we define `mdjs-preview` as the custom element name. We need to make sure that this
file is executed before the original mdjs-preview definition file is executed.

```js
import { MdJsPreview } from '@mdjs/mdjs-preview';
import { render as render2 } from 'lit';
import { isTemplateResult as isTemplateResult2 } from 'lit/directive-helpers.js';
import { render as render1  } from 'lit-html';

export class HybridLitMdjsPreview extends MdJsPreview {
  renderStory(html, container, options) {
    if (isTemplateResult2(html)) {
      render2(html, container, options);
    } else {
      render1(html, container, options);
    }
  }
customElements.define('mdjs-preview', HybridLitMdjsPreview);
```