Skip to content
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
45 changes: 45 additions & 0 deletions storybook/decorators/utils/use-shared-style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useLayoutEffect } from '@wordpress/element';

interface StyleEntry {
element: HTMLStyleElement;
refCount: number;
}

const styleRefs = new Map< string, StyleEntry >();

/**
* Injects a `<style>` element into the document head, ref-counted so that
* multiple consumers sharing the same CSS text use a single `<style>` element
* (e.g. on the Docs tab where several stories render simultaneously). The
* element is removed from the DOM when the last consumer unmounts.
*
* @param cssText The CSS text to inject. Pass an empty string to skip
* injection.
*/
export function useSharedStyle( cssText: string ): void {
useLayoutEffect( () => {
if ( ! cssText ) {
return;
}

let entry = styleRefs.get( cssText );

if ( entry ) {
entry.refCount++;
} else {
const style = document.createElement( 'style' );
style.textContent = cssText;
document.head.appendChild( style );
entry = { element: style, refCount: 1 };
styleRefs.set( cssText, entry );
}

return () => {
entry.refCount--;
if ( entry.refCount === 0 ) {
entry.element.remove();
styleRefs.delete( cssText );
}
};
}, [ cssText ] );
}
9 changes: 2 additions & 7 deletions storybook/decorators/with-global-css.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import clsx from 'clsx';
import { useEffect } from '@wordpress/element';
import basicStyles from '../global-basic.scss?inline';
import wordPressStyles from '../global-wordpress.scss?inline';
import { useSharedStyle } from './utils/use-shared-style';

/**
* A Storybook decorator to inject global CSS.
Expand Down Expand Up @@ -42,12 +42,7 @@ export const WithGlobalCSS = ( Story, context ) => {
const { lazyStyles, externalStyles, classes } =
config[ context.globals.css ];

useEffect( () => {
const style = document.createElement( 'style' );
style.textContent = lazyStyles.join( '\n' );
document.head.appendChild( style );
return () => document.head.removeChild( style );
}, [ context.globals.css, lazyStyles ] );
useSharedStyle( lazyStyles.join( '\n' ) );

return (
<div className={ clsx( classes ) }>
Expand Down
30 changes: 9 additions & 21 deletions storybook/decorators/with-rtl.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@
* WordPress dependencies
*/
import { addFilter, removeFilter } from '@wordpress/hooks';
import {
useEffect,
useLayoutEffect,
useRef,
useState,
} from '@wordpress/element';
import { useEffect, useRef, useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import CONFIG from '../package-styles/config';
import { useSharedStyle } from './utils/use-shared-style';

export const WithRTL = ( Story, context ) => {
const [ rerenderKey, setRerenderKey ] = useState( 0 );
Expand Down Expand Up @@ -41,23 +37,15 @@ export const WithRTL = ( Story, context ) => {
return () => removeFilter( 'i18n.gettext_with_context', 'storybook' );
}, [ context.globals.direction ] );

useLayoutEffect( () => {
const stylesToUse = [];
const stylesToUse = [];

CONFIG.forEach( ( item ) => {
if ( item.componentIdMatcher.test( context.componentId ) ) {
stylesToUse.push( ...item[ context.globals.direction ] );
}
} );

const style = document.createElement( 'style' );
style.textContent = stylesToUse.join( '\n' );
document.head.appendChild( style );
CONFIG.forEach( ( item ) => {
if ( item.componentIdMatcher.test( context.componentId ) ) {
stylesToUse.push( ...item[ context.globals.direction ] );
}
} );

return () => {
document.head.removeChild( style );
};
}, [ context.componentId, context.globals.direction ] );
useSharedStyle( stylesToUse.join( '\n' ) );

return (
<div ref={ ref } key={ rerenderKey }>
Expand Down
Loading