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

202: Evaluate/Document Outline templates storybook starter kit #1

Merged
2 changes: 0 additions & 2 deletions packages/storybook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@
"devDependencies": {
"@phase2/outline-adopted-stylesheets-controller": "^1.0.4",
"@phase2/outline-config": "^0.0.14",
"@phase2/outline-core": "^0.2.7",
"@phase2/outline-core-alert": "^0.0.4",
"@phase2/outline-core-link": "^0.0.16",
"@phase2/outline-docs": "^0.0.21",
"@storybook/addon-essentials": "^7.6.17",
"@storybook/addon-links": "^7.6.17",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Meta } from '@storybook/addon-docs';
import { OutlineExample } from '../outline-example';

<Meta
title="Outline Core/Example"
parameters={{
viewMode: 'docs',
previewTabs: {
canvas: {
hidden: true,
},
},
}}
/>

# `OutlineExample`: `outline-example`

> This is an `outline-example` component.
> This component is a wrapper around an anchor tag that provides **encapsulated** and **global** styles for links.
> This component extends `LitElement` and adds custom styling.

<outline-example><a href="https://github.com/phase2/outline" class="sb-unstyled">Outline</a></outline-example>

```html
<outline-example>
<a href="https://github.com/phase2/outline">Outline</a>
</outline-example>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { customElement } from 'lit/decorators.js';
import { LitElement, html } from 'lit';

import globalStyles from './styles/outline-example.global.css?inline';
import encapsulatedStyles from './styles/outline-example.encapsulated.css?inline';
import { AdoptedStyleSheets } from '../../../controllers/adopted-stylesheets';

/**
* Example component.
*
* @see {@link https://lit.dev/docs/components/defining/ | Defining components}
* @see {@link https://www.npmjs.com/package/@phase2/outline-example-alert | @phase2/outline-example-alert }
* @element outline-example
* @extends OutlineExample
*/
@customElement('outline-example')
export class OutlineExample extends LitElement {
adoptedStyleSheets = new AdoptedStyleSheets(this, {
globalCSS: globalStyles,
encapsulatedCSS: encapsulatedStyles,
});

/**
* Wrapping the rendered content in a div with the class "encapsulated-container".
* This demonstrates the difference between encapsulated and light DOM styling.
*
* @category rendering
* @returns {TemplateResult} The template to render.
*/
render() {
return html`
<div class="encapsulated-container">
<slot></slot>
</div>
`;
}
}

/**
* TypeScript declaration extends the HTMLElementTagNameMap interface, adding the web component.
* This enhances type checking and autocompletion in IDEs.
*
* @see {@link https://lit.dev/docs/components/defining/#typescript-typings | Providing good TypeScript typings}
*/
declare global {
interface HTMLElementTagNameMap {
'outline-example': OutlineExample;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
outline-link {
outline-example {
/* This is just here to demonstrate using a nested import in a CSS file. */
@nested-import './outline-link.imported.css';
@nested-import './outline-example.imported.css';
}

This file was deleted.

This file was deleted.

82 changes: 82 additions & 0 deletions packages/storybook/src/controllers/adopted-stylesheets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { ReactiveController, ReactiveControllerHost } from 'lit';

/**
* A controller for managing adopted stylesheets in a Lit element.
* This allows for styles to be dynamically applied to the component's
* light DOM or shadow DOM.
*/
export class AdoptedStyleSheets implements ReactiveController {
// The host element that the controller is associated with.
private host: ReactiveControllerHost & HTMLElement;
// An object containing the CSS to be applied globally or encapsulated within the shadow DOM.
private css: { globalCSS?: string; encapsulatedCSS?: string };

// A set to track the stylesheets applied to the light DOM.
private static appliedLightDomStylesheets: Set<string> = new Set();

/**
* Constructs an instance of the AdoptedStyleSheets controller.
* @param host The host element that the controller will be associated with.
* @param css An object containing optional global and encapsulated CSS strings.
*/
constructor(
host: ReactiveControllerHost & HTMLElement,
css: {
globalCSS?: string;
encapsulatedCSS?: string;
} = {}
) {
this.host = host;
this.css = css;
this.host.addController(this); // Register this instance as a controller for the host element.
}

/**
* Applies the given CSS text to the specified target (Document or ShadowRoot).
* @param cssText The CSS text to apply.
* @param target The target where the CSS should be applied.
*/
private applyCssToDom(cssText: string, target: Document | ShadowRoot) {
if (target instanceof Document) {
const store = AdoptedStyleSheets.appliedLightDomStylesheets;

if (store.has(cssText)) {
// If the stylesheet has already been applied, no further action is required.
return;
}
store.add(cssText); // Store the stylesheet with the provided key.
}

// Create a new stylesheet and replace its contents with the provided CSS text.
const sheet = new CSSStyleSheet();
sheet.replaceSync(cssText);

// Apply the stylesheet to the target's adoptedStyleSheets.
target.adoptedStyleSheets = [...target.adoptedStyleSheets, sheet];
}

/**
* Lifecycle callback called when the host element is connected to the document's DOM.
* Applies global and encapsulated CSS to the respective DOM targets.
*/
hostConnected() {
if (this.css.globalCSS) {
this.applyCssToDom(this.css.globalCSS, document); // Apply global CSS to the document if it exists.
}

// Apply encapsulated CSS to the host's shadow root if it exists.
if (this.css.encapsulatedCSS && this.host.shadowRoot) {
this.applyCssToDom(this.css.encapsulatedCSS, this.host.shadowRoot); // Apply encapsulated CSS to the host's shadow root if it exists.
}
}

/**
* Lifecycle callback called when the host element is disconnected from the document's DOM.
* Note: When a component with a Shadow DOM is disconnected from the document's DOM, the Shadow DOM is also removed along with the component.
* However, for Light DOM styles, they are not removed here because other instances of the component
* might still be present on the page and require these styles.
*/
hostDisconnected() {
// No action is taken when the host is disconnected.
}
}
Loading
Loading