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

✨ add declarative shadow dom and docs #18

Merged
merged 2 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"cem": "custom-elements-manifest analyze --config 'lib/custom-elements-manifest.config.js'",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"preview-storybook": "storybook preview",
"preview-storybook": "storybook dev",
"test-storybook": "test-storybook --coverage",
"predist": "npm run cem && npm run build-storybook",
"dist": "node lib/esbuild.config.js",
Expand Down
104 changes: 47 additions & 57 deletions src/devto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,46 +23,6 @@ see README at root of repo for usage details

---

## Members

<dl>
<dt><a href="#DEV-Post-Declarative-Shadow-DOM">DEV-Post-Declarative-Shadow-DOM</a> ⇒ <code>string</code></dt>
<dd></dd>
<dt><a href="#DEV-Declarative-Shadow-DOM">DEV-Declarative-Shadow-DOM</a> ⇒ <code>string</code></dt>
<dd></dd>
</dl>

## Objects

<dl>
<dt><a href="#DEVUtils">DEVUtils</a> : <code>object</code></dt>
<dd><p>Utility functions for fetching and parsing dev.to api data, getting
styles and generating HTML for dev.to profile UIs</p>
</dd>
</dl>

<a name="DEV-Post-Declarative-Shadow-DOM"></a>

## DEV-Post-Declarative-Shadow-DOM ⇒ <code>string</code>
**Kind**: global variable
**Returns**: <code>string</code> - DEV post HTML wrapped in a `template`

| Param | Type | Description |
| --- | --- | --- |
| content | <code>ForemPostHTML</code> | Content about one post by dev.to (or Forem) user |
| fetch | <code>boolean</code> | |

<a name="DEV-Declarative-Shadow-DOM"></a>

## DEV-Declarative-Shadow-DOM ⇒ <code>string</code>
**Kind**: global variable
**Returns**: <code>string</code> - DEV HTML wrapped in a `template`

| Param | Type | Description |
| --- | --- | --- |
| content | <code>ForemUserHTML</code> | a content object representing a DEV user |
| fetch | <code>boolean</code> | |

<a name="DEVUtils"></a>

## DEVUtils : <code>object</code>
Expand All @@ -74,6 +34,7 @@ Utility functions for fetching and parsing dev.to api data, getting

* [DEVUtils](#DEVUtils) : <code>object</code>
* [.post](#DEVUtils.post) : <code>object</code>
* [.dsd](#DEVUtils.post.dsd) ⇒ <code>string</code>
* [.html(content)](#DEVUtils.post.html) ⇒ <code>string</code>
* [.generateContent(content, [fetch])](#DEVUtils.post.generateContent) ⇒ <code>ForemPost</code> \| <code>ForemError</code>
* [.ForemPost](#DEVUtils.post.ForemPost) : <code>Object</code>
Expand All @@ -82,6 +43,7 @@ Utility functions for fetching and parsing dev.to api data, getting
* [.styles](#DEVUtils.user.styles)
* [.html(content)](#DEVUtils.user.html) ⇒ <code>string</code>
* [.generateContent(content, [fetch])](#DEVUtils.user.generateContent) ⇒ <code>ForemUserHTML</code>
* [.dsd(content, fetch)](#DEVUtils.user.dsd) ⇒ <code>string</code>
* [.ForemUser](#DEVUtils.user.ForemUser) : <code>Object</code>
* [.ForemUserHTML](#DEVUtils.user.ForemUserHTML) : <code>ForemUser</code>

Expand All @@ -91,6 +53,27 @@ Utility functions for fetching and parsing dev.to api data, getting
Utility functions for a post

**Kind**: static namespace of [<code>DEVUtils</code>](#DEVUtils)

* [.post](#DEVUtils.post) : <code>object</code>
* [.dsd](#DEVUtils.post.dsd) ⇒ <code>string</code>
* [.html(content)](#DEVUtils.post.html) ⇒ <code>string</code>
* [.generateContent(content, [fetch])](#DEVUtils.post.generateContent) ⇒ <code>ForemPost</code> \| <code>ForemError</code>
* [.ForemPost](#DEVUtils.post.ForemPost) : <code>Object</code>
* [.ForemPostHTML](#DEVUtils.post.ForemPostHTML) : <code>ForemPost</code>

<a name="DEVUtils.post.dsd"></a>

#### post.dsd ⇒ <code>string</code>
Generate a `template` element with a shadowdom with a Post in it

**Kind**: static namespace of [<code>post</code>](#DEVUtils.post)
**Returns**: <code>string</code> - DEV post HTML wrapped in a `template`

| Param | Type | Description |
| --- | --- | --- |
| content | <code>ForemPostHTML</code> | Content about one post by dev.to (or Forem) user |
| fetch | <code>boolean</code> | |

**Example** *(Server side rendering a post with Declarative Shadow Dom)*
```js
<devto-post></devto-post>
Expand All @@ -101,13 +84,6 @@ const dsdHTML = post.dsd({id: '12345'}, true);
document.querySelector('devto-post').innerHTML = dsdHTML;
</script>
```

* [.post](#DEVUtils.post) : <code>object</code>
* [.html(content)](#DEVUtils.post.html) ⇒ <code>string</code>
* [.generateContent(content, [fetch])](#DEVUtils.post.generateContent) ⇒ <code>ForemPost</code> \| <code>ForemError</code>
* [.ForemPost](#DEVUtils.post.ForemPost) : <code>Object</code>
* [.ForemPostHTML](#DEVUtils.post.ForemPostHTML) : <code>ForemPost</code>

<a name="DEVUtils.post.html"></a>

#### post.html(content) ⇒ <code>string</code>
Expand Down Expand Up @@ -168,21 +144,12 @@ Forem post content, ready for HTML
Utility functions for a user

**Kind**: static namespace of [<code>DEVUtils</code>](#DEVUtils)
**Example** *(Server side rendering with Declarative Shadow Dom)*
```js
<devto-user></devto-user>

<script type="module">
import {dsd} from 'profile-components/devto-utils';
const dsdHTML = dsd({username: 'scottnath'}, true);
document.querySelector('devto-user').innerHTML = dsdHTML;
</script>
```

* [.user](#DEVUtils.user) : <code>object</code>
* [.styles](#DEVUtils.user.styles)
* [.html(content)](#DEVUtils.user.html) ⇒ <code>string</code>
* [.generateContent(content, [fetch])](#DEVUtils.user.generateContent) ⇒ <code>ForemUserHTML</code>
* [.dsd(content, fetch)](#DEVUtils.user.dsd) ⇒ <code>string</code>
* [.ForemUser](#DEVUtils.user.ForemUser) : <code>Object</code>
* [.ForemUserHTML](#DEVUtils.user.ForemUserHTML) : <code>ForemUser</code>

Expand Down Expand Up @@ -217,6 +184,29 @@ Generates an object of content for the user HTML
| content | <code>ForemUserHTML</code> |
| [fetch] | <code>boolean</code> |

<a name="DEVUtils.user.dsd"></a>

#### user.dsd(content, fetch) ⇒ <code>string</code>
Generate a `template` element with shadowrootmode with a User in it

**Kind**: static method of [<code>user</code>](#DEVUtils.user)
**Returns**: <code>string</code> - DEV HTML wrapped in a `template`

| Param | Type | Description |
| --- | --- | --- |
| content | <code>ForemUserHTML</code> | a content object representing a DEV user |
| fetch | <code>boolean</code> | |

**Example** *(Server side rendering with Declarative Shadow Dom)*
```js
<devto-user></devto-user>

<script type="module">
import {dsd} from 'profile-components/devto-utils';
const dsdHTML = dsd({username: 'scottnath'}, true);
document.querySelector('devto-user').innerHTML = dsdHTML;
</script>
```
<a name="DEVUtils.user.ForemUser"></a>

#### user.ForemUser : <code>Object</code>
Expand Down
114 changes: 114 additions & 0 deletions src/devto/dsd.docs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Meta, Title, Source } from '@storybook/blocks';

<Meta isTemplate />

<Title />

Both DEV components can be implemented via Declarative Shadow DOM using methods exported from the `devto-utils.js` file.


## Server Side Rendering HTML in Node.js

<Source code={`
// import from npm module
import { dsd } from 'profile-components/devto-utils';

const generatedTemplate = await dsd({
username: 'scottnath',
},true);

/**
generatedTemplate contains:
<template shadowrootmode="open">
<styles>(...css styles for DEV component)</styles>
<section (...rest of generated HTML)</section>
</template>
*/

const componentHTML = \`<devto-user>\${generatedTemplate}</devto-user>\`;
`} language='js' />

## Server side render in an Astro component

<Source code={`
---
import {dsd} from 'profile-components/devto-utils';

const declaredDOM = await dsd({
username: 'scottnath',
},true)
---

<devto-user
data-theme="light_high_contrast"
set:html={declaredDOM}>
</devto-user>
`} language='jsx' />

## Client side rendering via unpkg

<Source code={`

<!-- add empty elements to HTML -->
<devto-post></devto-post>
<hr />
<devto-user></devto-user>

<script type="module">
// import from unpkg
import {
user,
post,
} from 'https://unpkg.com/profile-components/dist/devto-utils.js';

// post has it's own DSD method:
const dsdPost = post.dsd;

/**
* Polyfill for Declarative Shadow DOM which, when triggered, converts
* the template element into actual shadow DOM.
* This is only needed when injecting _after_ page is loaded
* @see https://developer.chrome.com/docs/css-ui/declarative-shadow-dom#polyfill
*/
const triggerAttachShadowRoots = () => {
(function attachShadowRoots(root) {
root
.querySelectorAll('template[shadowrootmode]')
.forEach((template) => {
const mode = template.getAttribute('shadowrootmode');
const shadowRoot = template.parentNode.attachShadow({ mode });
shadowRoot.appendChild(template.content);
template.remove();
attachShadowRoots(shadowRoot);
});
})(document);
};

/**
* Uses the "dsd" method to generate DSD, add the string of DSD content
* to the element, then trigger the polyfill to convert the template
*/
const injectDSD = async () => {
const dsdHTML = await dsd({ username: 'scottnath' }, true);
document.querySelector('devto-user').innerHTML = dsdHTML;
// now that the HTML is async-created, the polyfill can convert it
triggerAttachShadowRoots();
};
injectDSD();

/**
* Uses the "dsdPost" method to generate DSD, add the string of DSD content
* to the element, then trigger the polyfill to convert the template
*/
const injectPostDSD = async () => {
const dsdHTML = await dsdPost(
{ full_name: 'scottnath/profile-components' },
true
);
document.querySelector('devto-post').innerHTML = dsdHTML;
// now that the HTML is async-created, the polyfill can convert it
triggerAttachShadowRoots();
};
injectPostDSD();
</script>
`} language='html' />
70 changes: 70 additions & 0 deletions src/devto/dsd.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@

import { parseFetchedPost } from './post/content.js';
import { parseFetchedUser } from './user/content.js';
import { default as userScottnath } from './fixtures/generated/user--scottnath.json';
import { default as postProfileComponents } from './fixtures/generated/post--profile-components.json';
import { default as postDependabot } from './fixtures/generated/post--dependabot.json';
import { default as postBugfix } from './fixtures/generated/post--bugfix-multi-vite.json';
import { post, dsd } from './index.js';
import docs from './dsd.docs.mdx';


export default {
title: 'DevTo/Declarative Shadow DOM',
parameters: {
docs: {
page: docs
},
},
tags: ['autodocs'],
decorators: [(story) => `${story()}
<script>

(function attachShadowRoots(root) {
root.querySelectorAll("template[shadowrootmode]").forEach(template => {
const mode = template.getAttribute("shadowrootmode");
const shadowRoot = template.parentNode.attachShadow({ mode });
shadowRoot.appendChild(template.content);
template.remove();
attachShadowRoots(shadowRoot);
});
})(document);
</script>
`],
};

export const Post = {
loaders: [
async ({args}) => ({
dsdOutput: await (await post.dsd(args)),
}),
],
render: (args, { loaded: { dsdOutput } }) => {
return `
<devto-post-dsd>${dsdOutput}</devto-post-dsd>

`;
},
args: {
...parseFetchedPost(postProfileComponents)
},
}

export const User = {
loaders: [
async ({args}) => ({
dsdOutput: await (await dsd(args)),
}),
],
render: (args, { loaded: { dsdOutput } }) => {
return `
<devto-user-dsd data-theme="dark">${dsdOutput}</devto-user-dsd>

`;
},
args: {
...parseFetchedUser(userScottnath),
latest_post: stringify(parseFetchedPost(postDependabot)),
popular_post: stringify(parseFetchedPost(postBugfix)),
},
}
4 changes: 2 additions & 2 deletions src/devto/fixtures/generated/post--bugfix-multi-vite.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"id": 1466138,
"title": "Bugfix: Multiple Vite Storybooks from Same node_modules",
"description": "tl;dr Encountering Failed to fetch dynamically imported module or ENOTEMPTY: directory not...",
"readable_publish_date": "May 12 2023",
"readable_publish_date": "May 12 '23",
"slug": "bugfix-multiple-vite-storybooks-from-same-nodemodules-4mg1",
"path": "/scottnath/bugfix-multiple-vite-storybooks-from-same-nodemodules-4mg1",
"url": "https://dev.to/scottnath/bugfix-multiple-vite-storybooks-from-same-nodemodules-4mg1",
Expand Down Expand Up @@ -40,4 +40,4 @@
"profile_image": "https://media.dev.to/cdn-cgi/image/width=640,height=640,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1055555%2F4d0bf90a-bec7-4228-b1ca-d663fa40adeb.jpeg",
"profile_image_90": "https://media.dev.to/cdn-cgi/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1055555%2F4d0bf90a-bec7-4228-b1ca-d663fa40adeb.jpeg"
}
}
}
4 changes: 2 additions & 2 deletions src/devto/fixtures/generated/post--dependabot.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"id": 1568661,
"title": "A crazy-simple way to bulk-update NPM dependencies with GitHub's Dependabot",
"description": "This is the simplest way I've found to keep your NPM dependencies up-to-date. This will update all...",
"readable_publish_date": "Aug 15 2023",
"readable_publish_date": "Aug 15 '23",
"slug": "a-crazy-simple-way-to-bulk-update-npm-dependencies-with-githubs-dependabot-3e2o",
"path": "/scottnath/a-crazy-simple-way-to-bulk-update-npm-dependencies-with-githubs-dependabot-3e2o",
"url": "https://dev.to/scottnath/a-crazy-simple-way-to-bulk-update-npm-dependencies-with-githubs-dependabot-3e2o",
Expand Down Expand Up @@ -40,4 +40,4 @@
"profile_image": "https://media.dev.to/cdn-cgi/image/width=640,height=640,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1055555%2F4d0bf90a-bec7-4228-b1ca-d663fa40adeb.jpeg",
"profile_image_90": "https://media.dev.to/cdn-cgi/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1055555%2F4d0bf90a-bec7-4228-b1ca-d663fa40adeb.jpeg"
}
}
}
Loading
Loading