Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[next] markdown code block use cases (script, render, story, preview, ...) #315

Open
daKmoR opened this issue Mar 6, 2022 · 8 comments
Open
Labels
enhancement New feature or request

Comments

@daKmoR
Copy link
Member

daKmoR commented Mar 6, 2022

in markdown we currently have this

```js server
const foo = 'bar';
```

```js client
const foo = 'bar';
```

but we also have more

Markdown

## Overview

You will be able to...

Which will result in:

```js xxx
import { html } from 'lit';
const users = [
  { firstName: 'John', lastName: 'Doe' },
  { firstName: 'Jane', lastName: 'Doe' },
];
```

```js xxx
export const listOfUsers = () => html`
  <ul>
    ${users.map(user => html`<li>${user.firstName} ${user.lastName}</li>`)}
  </ul>
`;
```

```js xxx
export const listOfUsersWithFrame = () => html`
  <ul>
    ${users.map(user => html`<li>${user.firstName} ${user.lastName}</li>`)}
  </ul>
`;
```

which shows/executes the code at the place of the code block on the client
e.g. the above example would become

HTML

<h2>Overview</h2>
<p>You will be able to...</p>
<p>Which will result in:</p>

<mdjs-story mdjs-story-name="listOfUsers">
  <ul>
    <li>John Doe</li>
    <li>Jane Doe</li>
  </ul>
</mdjs-story>

<mdjs-preview mdjs-story-name="listOfUsersWithFrame">
  <pre><code>[...]</code></pre>
  <div slot="story">
    <ul>
      <li>John Doe</li><li>Jane Doe</li>
    </ul>
  </div>
</mdjs-preview>

Rendered

Screenshot 2022-03-07 at 09 24 18

Question

now the question is... would that make sense server-side as well? I guess... and what should the key be 🤔

should it retain the story? or a complete "new" set?

suggestion from the top of my head...

  • js server
  • js server inject
  • js server inject-frame
  • js client
  • js client inject (former js story )
  • js client inject-frame (former js story-preview )

hmmm inject, inject-frame seems not very intuitive... anyone got better ideas? 🤔

@daKmoR
Copy link
Member Author

daKmoR commented Mar 7, 2022

  • js render
  • js render-frame

@daKmoR
Copy link
Member Author

daKmoR commented Mar 11, 2022

this get's quite complicated... maybe use a "full" configuration? 🤔

```js client
console.log('just execute');
```

```js server inject
export const foo = () => html`<p>render the p</p>`;
```

```js client inject({ withCode: true })
export const bar = () => html`<p>render the p AND the highlighted code block</p>`;
```

```js client inject-frame({ openCode: true })
export const baz = () => html`<p>render the p AND the highlighted code block OPENED within a FRAME</p>`;
```
```

@bashmish
Copy link
Member

bashmish commented Mar 11, 2022

follow-up to the discussion #288

my use cases extend the proposal above with the following:

  • render story and show highlighted code, but do not show the frame (mostly for <style> blocks)
  • execute script, show highlighted code, but no need to render anything (it's similar to current js script with the only difference: shown highlighted code)

in addition to that I also want to

  • control the visibility of the code in the frame, sometimes it's quite handy to have it open by default, because many code blocks are quite short and showing them by default makes sense
  • optionally render story in the ShadowDOM
  • optionally render story in an iframe (that's less of an MDJS feature, so in Backlight.dev we gonna have the extension)
  • define minimal height of the story preview block (can be archived by adding a div with style to the story itself, so not super urgent)

the syntax story({ option1: true, option2: 'value' }) has a principal difference: it allows using arbitrary configuration value types with ease, while unique keys leave you only with booleans

it might solve the following cases:

  • define minimal height
  • choose the rendering method: Light DOM, Shadow DOM, iframe

and last, but not least: object syntax will be easier to extend in the future, using real JS

pseudo code

---
defaultStoryConfig: { openCode: true, renderMethod: 'iframe' }
---

```js story({ ...defaultStoryConfig, minHeight: '200px' })
export const myStory = () => html`...my-story-code...`;
```

since we have story and script, I think it might be good to keep them both
the difference still makes sense: story is for rendering and script is for executing only
but the names can be improved, because not every rendering is a story, see the example below:

## Setup

Setup data for demos:

```js script({ showCode: true })
import { html } from `lit`;
const users = [
  { firstName: 'John', lastName: 'Doe' },
  { firstName: 'Jane', lastName: 'Doe' },
];
```

Setup styles for demos:

```html story({ showCode: true })
<style>
/* some basic styles, e.g. for ul and li */
</style>
```

## Demos

### Demo in Light DOM (+ code block open by default)

```js story({ showCode: true, withFrame: true, openCode: true })
export const listOfUsers = () => html`
  <ul>
    ${users.map(user => html`<li>${user.firstName} ${user.lastName}</li>`)}
  </ul>
`;
```

### Demo in Shadow DOM

```js story({ showCode: true, withFrame: true, renderMethod: 'ShadowDOM' })
export const listOfUsers = () => html`
  <style>
  /* some isolated styles */
  </style>
  <ul>
    ${users.map(user => html`<li>${user.firstName} ${user.lastName}</li>`)}
  </ul>
`;
```

### Demo in Iframe

```js story({ showCode: true, withFrame: true, renderMethod: 'iframe' })
// isolated JS env
import 'path/to/my-user.define.js'
export const listOfUsers = () => {
  const localUsers = [...];
  return html`
    <style>
    /* some isolated styles */
    </style>
    <ul>
      ${localUsers.map(user => html`<li><my-user user=${user}></my-user></li>`)}
    </ul>
  `;
}
```

I'd improve the names like this:

  • story => render
  • script => execute
  • frame => wrapper (to prevent confusion with an iframe)

I'm just scraping the surface here, but I'll be happy to work on a test suite with many input/output examples for my use cases. Do you already have or are you planning to set smth up? I can either extend it or start making it myself and let you extend with your cases later.

@daKmoR
Copy link
Member Author

daKmoR commented Mar 11, 2022

all your examples are meant to run client side right?

so it would be client render and client execute?

Comparing client VS server of render

## Client Rendering

```js client render
export const foo = () => html`<p>content</p>`;
```
⤵️
<mdjs-render story-name="foo"></mdjs-render>

⤵️ (after mdjs-render is registered and initialized)
<mdjs-render story-name="foo">
  # shadow-dom
    <slot>
  <p>content</p>  
</mdjs-render>

// VS

## Server Only Rendering

```js server render
export const foo = () => html`<p>content</p>`;
```
⤵️
<p>content</p> 

So js client render executes the render on the client and requires an initialized web component to render. js server render executes the render on the server and does not need any js.

hmmmm....

or do we even want hydration? 🤔

e.g. similar to hydration of web components...
(render-mode is the current attribute for it... again better name would be nice)

```js render-mode="hydrate:onVisible" render
export const foo = () => html`<p>content</p>`;
```
⤵️
<mdjs-render story-name="foo" render-mode="hydrate:onVisible">
  <template shadowroot="open">
    <slot>
  </template>  
  <p>content</p>  
</mdjs-render>

Benefit would be NO js needed to display it... and it could become interactive on click, on visible, on idle, ...

@daKmoR
Copy link
Member Author

daKmoR commented Mar 11, 2022

puuhhh that is getting more and more complex 😅

I think the workflow should be

  1. update to v10 of unified
  2. "finish" partial hydration for web components
  3. look into this API again

so this might be a little bit 😅

as I do not want to block you... here are two ideas
a) only do small adjustments to the API for example by "just" adding js story with-code and release it
b) do (1) and (a) and release as a breaking change => e.g. server/hydration/add js options would happen in a next breaking change

@daKmoR daKmoR changed the title [next] markdown server side js story? [next] markdown code block use cases (script, render, story, preview, ...) Mar 11, 2022
@bashmish
Copy link
Member

all your examples are meant to run client side right?

so it would be client render and client execute?

not really
partially I do not understand your original proposal, but I feel like it's just another dimension to what we are discussing here, so can be solved with the same API which translates to another configuration option

also, why do you want to be explicit about it? I mean your end goal is to render, so if you can prerender on the server, then do it as a perf optimisation, if you can't, leave it to the client, if smth doesn't go right automatically, then forcing client/server via an option can give a way to solve it

```js server render
export const foo = () => html`<p>content</p>`;
```
⤵️
<p>content</p> 

this example makes me think we are talking about 2 different "server" render:

  1. render only the Light DOM defined in the story template and leave the rest of work to the client
  2. full SSR: entire tree with Shadow DOM and all children/other Shadow DOMs produced as a result of it (I'm not an expert in Shadow DOM SSR, so not sure I fully understand how it works), and this is proposed here First Level hydration #308

the 1 is pretty simple to do on the server, even if <p> is a WC, just rendering LightDOM is easy
while 2 is a totally different story and relying on the automatic behavior is probably not a good idea, so it's indeed important to keep it in mind when designing the API for new MDJS

@bashmish
Copy link
Member

bashmish commented Mar 11, 2022

puuhhh that is getting more and more complex 😅

I think the workflow should be

  1. update to v10 of unified
  2. "finish" partial hydration for web components
  3. look into this API again

so this might be a little bit 😅

as I do not want to block you... here are two ideas a) only do small adjustments to the API for example by "just" adding js story with-code and release it b) do (1) and (a) and release as a breaking change => e.g. server/hydration/add js options would happen in a next breaking change

thanks for suggesting a way to unblock me :) I agree it's good to start small and solve the low hanging fruits first, but from my perspective the most valuable low hanging fruits are the ones which would solve my 2 issues here:

https://backlight.dev/edit/4BeMe20hqOWTkdUL2NuJ?branch=mdjs-issues%4044v7XI9EE5hzBwU7oxi8dbGjKz03&p=doc

image

given the current syntax, I'd introduce 2 new keys as previously discussed in #288, except that after all good input we had here I'd name them differently, currently I like script-with-code and story-with-code, but we can change it when PR is technically ready, wdyt?

@daKmoR
Copy link
Member Author

daKmoR commented Mar 11, 2022

currently I like script-with-code and story-with-code, but we can change it when PR is technically ready, wdyt?

sounds good to me 👍 looking forward to a PR 🤗

@daKmoR daKmoR added the enhancement New feature or request label Aug 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants