@emulsify/core 4.0.4 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/css-components.js +3 -0
- package/.storybook/css-dist.js +1 -0
- package/.storybook/main-static-assets.js +217 -0
- package/.storybook/main-vite.js +224 -0
- package/.storybook/main.js +10 -370
- package/.storybook/manager-head.css +120 -0
- package/.storybook/preview.js +8 -18
- package/.storybook/utils.js +1 -1
- package/README.md +11 -3
- package/config/a11y.config.js +2 -1
- package/config/vite/environment.js +2 -0
- package/config/vite/plugins/virtual-twig-asset-sources.js +29 -3
- package/config/vite/project-config.js +65 -0
- package/config/vite/project-structure.js +35 -1
- package/package.json +5 -1
- package/scripts/a11y.js +150 -13
- package/scripts/audit.js +14 -2
- package/src/storybook/preview-decorator.js +45 -0
package/.storybook/main.js
CHANGED
|
@@ -14,32 +14,13 @@
|
|
|
14
14
|
import fs from 'fs';
|
|
15
15
|
import path, { resolve } from 'path';
|
|
16
16
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
17
|
-
import viteConfig from '../config/vite/vite.config.js';
|
|
18
17
|
import { resolveEnvironment } from '../config/vite/environment.js';
|
|
19
|
-
import {
|
|
20
|
-
mergeReactSingletonOptimizeDeps,
|
|
21
|
-
mergeReactSingletonResolve,
|
|
22
|
-
} from '../config/vite/utils/react-singleton.js';
|
|
23
|
-
import { twigExtensionModuleSpecifiers } from '../config/vite/twig-extensions.js';
|
|
24
18
|
import {
|
|
25
19
|
applyStorybookConfigOverrides,
|
|
26
20
|
normalizeStorybookConfigOverrideModule,
|
|
27
21
|
} from '../src/storybook/main-config.js';
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const twigVirtualModuleIds = [
|
|
32
|
-
'virtual:emulsify-twig-globs',
|
|
33
|
-
'virtual:emulsify-twig-asset-sources',
|
|
34
|
-
'virtual:emulsify-twig-extension-installers',
|
|
35
|
-
];
|
|
36
|
-
|
|
37
|
-
const twigRuntimeOptimizeDepsExclude = [
|
|
38
|
-
...twigVirtualModuleIds,
|
|
39
|
-
'@emulsify/core/storybook/twig/source-function',
|
|
40
|
-
'@emulsify/core/storybook/twig/source',
|
|
41
|
-
'@emulsify/core/storybook/twig/resolver',
|
|
42
|
-
];
|
|
22
|
+
import { buildAssetStaticDirs } from './main-static-assets.js';
|
|
23
|
+
import { createViteFinal } from './main-vite.js';
|
|
43
24
|
|
|
44
25
|
/**
|
|
45
26
|
* Minimal subset of the resolved Emulsify environment used by this file.
|
|
@@ -70,27 +51,6 @@ const _filename = fileURLToPath(import.meta.url);
|
|
|
70
51
|
*/
|
|
71
52
|
const _dirname = path.dirname(_filename);
|
|
72
53
|
|
|
73
|
-
/**
|
|
74
|
-
* The consuming project root for Storybook static mounts.
|
|
75
|
-
*
|
|
76
|
-
* Storybook loads this package config from different physical locations
|
|
77
|
-
* depending on whether Core is linked locally or installed in node_modules, so
|
|
78
|
-
* static paths must be rooted at the process cwd rather than this file.
|
|
79
|
-
*
|
|
80
|
-
* @type {string}
|
|
81
|
-
*/
|
|
82
|
-
const projectRoot = process.cwd();
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Vite-generated Storybook chunks should not share `/assets` with project
|
|
86
|
-
* static files. Storybook copies staticDirs while the preview build runs, so
|
|
87
|
-
* keeping generated chunks in a separate folder avoids concurrent writers in
|
|
88
|
-
* `.out/assets`.
|
|
89
|
-
*
|
|
90
|
-
* @type {string}
|
|
91
|
-
*/
|
|
92
|
-
const storybookViteAssetsDir = 'storybook-assets';
|
|
93
|
-
|
|
94
54
|
/**
|
|
95
55
|
* Reads an optional HTML fragment relative to this config file.
|
|
96
56
|
*
|
|
@@ -110,65 +70,6 @@ function readOptionalHtmlFragment(relativePath) {
|
|
|
110
70
|
return fs.readFileSync(fragmentPath, 'utf8');
|
|
111
71
|
}
|
|
112
72
|
|
|
113
|
-
/**
|
|
114
|
-
* Keeps Storybook static directory config aligned to the consuming project.
|
|
115
|
-
*
|
|
116
|
-
* Storybook errors when a declared static directory is absent, so only expose
|
|
117
|
-
* project asset directories that exist in the current workspace.
|
|
118
|
-
*
|
|
119
|
-
* @param {Array<string|{from: string, to: string}>} staticDirs - Static directory entries.
|
|
120
|
-
* @returns {Array<string|{from: string, to: string}>} Existing static directory entries.
|
|
121
|
-
*/
|
|
122
|
-
function existingStaticDirs(staticDirs) {
|
|
123
|
-
return staticDirs.filter((staticDir) => {
|
|
124
|
-
const directory =
|
|
125
|
-
typeof staticDir === 'string' ? staticDir : staticDir.from;
|
|
126
|
-
|
|
127
|
-
return directory && fs.existsSync(directory);
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Merge Storybook and project optimizeDeps excludes with Core Twig runtime IDs.
|
|
133
|
-
*
|
|
134
|
-
* Storybook's dependency optimizer runs before normal Vite virtual module
|
|
135
|
-
* resolution. Core Twig runtime modules import virtual IDs that must stay in
|
|
136
|
-
* the Vite module graph so Emulsify's virtual plugins can resolve them.
|
|
137
|
-
*
|
|
138
|
-
* @param {...string[]} excludeLists - Existing optimizeDeps exclude arrays.
|
|
139
|
-
* @returns {string[]} Merged exclude list.
|
|
140
|
-
*/
|
|
141
|
-
function mergeTwigRuntimeOptimizeDepsExcludes(...excludeLists) {
|
|
142
|
-
return Array.from(
|
|
143
|
-
new Set([
|
|
144
|
-
...excludeLists.flatMap((excludeList) =>
|
|
145
|
-
Array.isArray(excludeList) ? excludeList : [],
|
|
146
|
-
),
|
|
147
|
-
...twigRuntimeOptimizeDepsExclude,
|
|
148
|
-
]),
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Keep Emulsify Twig virtual imports out of Storybook dependency prebundles.
|
|
154
|
-
*
|
|
155
|
-
* @returns {import('esbuild').Plugin} Esbuild plugin for optimizeDeps.
|
|
156
|
-
*/
|
|
157
|
-
function makeTwigVirtualModuleOptimizerPlugin() {
|
|
158
|
-
return {
|
|
159
|
-
name: 'emulsify-twig-virtual-modules',
|
|
160
|
-
setup(build) {
|
|
161
|
-
build.onResolve(
|
|
162
|
-
{ filter: /^virtual:emulsify-twig-(?:globs|asset-sources)$/ },
|
|
163
|
-
(args) => ({
|
|
164
|
-
path: args.path,
|
|
165
|
-
external: true,
|
|
166
|
-
}),
|
|
167
|
-
);
|
|
168
|
-
},
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
73
|
/**
|
|
173
74
|
* Reads optional project-level Storybook overrides.
|
|
174
75
|
*
|
|
@@ -265,22 +166,7 @@ const baseConfig = {
|
|
|
265
166
|
*
|
|
266
167
|
* @type {Array<string|{from: string, to: string}>}
|
|
267
168
|
*/
|
|
268
|
-
staticDirs:
|
|
269
|
-
...existingStaticDirs([
|
|
270
|
-
{
|
|
271
|
-
from: path.resolve(projectRoot, 'assets'),
|
|
272
|
-
to: '/assets',
|
|
273
|
-
},
|
|
274
|
-
{
|
|
275
|
-
from: path.resolve(projectRoot, 'dist/assets'),
|
|
276
|
-
to: '/assets',
|
|
277
|
-
},
|
|
278
|
-
{
|
|
279
|
-
from: path.resolve(projectRoot, 'dist'),
|
|
280
|
-
to: '/dist',
|
|
281
|
-
},
|
|
282
|
-
]),
|
|
283
|
-
],
|
|
169
|
+
staticDirs: buildAssetStaticDirs(resolvedStorybookEnv),
|
|
284
170
|
|
|
285
171
|
/**
|
|
286
172
|
* Enable the default addon set used by Emulsify.
|
|
@@ -336,109 +222,12 @@ const baseConfig = {
|
|
|
336
222
|
* @returns {string} Manager head markup with Emulsify additions appended.
|
|
337
223
|
*/
|
|
338
224
|
managerHead: (head) => {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
--colors-emulsify-blue-200: #CCECFA;
|
|
346
|
-
--colors-emulsify-blue-300: #99D9F4;
|
|
347
|
-
--colors-emulsify-blue-400: #66c5ef;
|
|
348
|
-
--colors-emulsify-blue-500: #33b2e9;
|
|
349
|
-
--colors-emulsify-blue-600: #009fe4;
|
|
350
|
-
--colors-emulsify-blue-700: #007FB6;
|
|
351
|
-
--colors-emulsify-blue-800: #005f89;
|
|
352
|
-
--colors-emulsify-blue-900: #00405b;
|
|
353
|
-
--colors-emulsify-blue-1000: #00202e;
|
|
354
|
-
--colors-purple: #8B1E7E;
|
|
355
|
-
}
|
|
356
|
-
.sidebar-container {
|
|
357
|
-
background-color: var(--colors-emulsify-blue-900);
|
|
358
|
-
}
|
|
359
|
-
.sidebar-container .sidebar-subheading {
|
|
360
|
-
color: var(--colors-emulsify-blue-200);
|
|
361
|
-
font-size: 13px;
|
|
362
|
-
letter-spacing: 0.15em;
|
|
363
|
-
}
|
|
364
|
-
.sidebar-container .sidebar-subheading button:focus {
|
|
365
|
-
color: var(--colors-emulsify-blue-300);
|
|
366
|
-
}
|
|
367
|
-
/* Triangle icon. */
|
|
368
|
-
.sidebar-container .sidebar-subheading button span {
|
|
369
|
-
color: var(--colors-emulsify-blue-300);
|
|
370
|
-
}
|
|
371
|
-
.sidebar-container .search-field input {
|
|
372
|
-
border-color: var(--colors-emulsify-blue-700);
|
|
373
|
-
}
|
|
374
|
-
.sidebar-container .search-field input:active {
|
|
375
|
-
border-color: var(--colors-emulsify-blue-700);
|
|
376
|
-
}
|
|
377
|
-
.sidebar-container .search-result-recentlyOpened,
|
|
378
|
-
.sidebar-container .search-result-back,
|
|
379
|
-
.sidebar-container .search-result-clearHistory {
|
|
380
|
-
color: var(--colors-emulsify-blue-300) !important;
|
|
381
|
-
letter-spacing: 0.15em;
|
|
382
|
-
}
|
|
383
|
-
.sidebar-container .search-result-back span,
|
|
384
|
-
.sidebar-container .search-result-back svg,
|
|
385
|
-
.sidebar-container .search-result-clearHistory span,
|
|
386
|
-
.sidebar-container .search-result-clearHistory svg {
|
|
387
|
-
letter-spacing: normal;
|
|
388
|
-
color: white;
|
|
389
|
-
}
|
|
390
|
-
.sidebar-container .sidebar-item svg {
|
|
391
|
-
margin-top: 1px;
|
|
392
|
-
}
|
|
393
|
-
.sidebar-container .sidebar-item span {
|
|
394
|
-
margin-top: 4px;
|
|
395
|
-
}
|
|
396
|
-
.sidebar-container .sidebar-subheading-action svg {
|
|
397
|
-
color: var(--colors-emulsify-blue-400);
|
|
398
|
-
}
|
|
399
|
-
.sidebar-container .sidebar-subheading-action:hover svg {
|
|
400
|
-
color: var(--colors-emulsify-blue-300);
|
|
401
|
-
}
|
|
402
|
-
.sidebar-header button[title="Shortcuts"] {
|
|
403
|
-
box-shadow: none;
|
|
404
|
-
border: 1px solid var(--colors-emulsify-blue-700);
|
|
405
|
-
}
|
|
406
|
-
.sidebar-header button[title="Shortcuts"]:active {
|
|
407
|
-
border: 1px solid var(--colors-emulsify-blue-500);
|
|
408
|
-
}
|
|
409
|
-
.sidebar-header button[title="Shortcuts"]:focus {
|
|
410
|
-
background: transparent;
|
|
411
|
-
}
|
|
412
|
-
#shortcuts {
|
|
413
|
-
border-bottom-color: var(--colors-emulsify-blue-900) !important;
|
|
414
|
-
}
|
|
415
|
-
[role="main"]:not(:nth-child(3)) {
|
|
416
|
-
top: 1rem !important;
|
|
417
|
-
height: calc(100vh - 2rem) !important;
|
|
418
|
-
}
|
|
419
|
-
[role="main"] .os-host .os-content button:hover {
|
|
420
|
-
background: var(--colors-emulsify-blue-100);
|
|
421
|
-
}
|
|
422
|
-
[role="main"] .os-host .os-content button:hover svg {
|
|
423
|
-
color: var(--colors-emulsify-blue-900);
|
|
424
|
-
}
|
|
425
|
-
#panel-tab-content,
|
|
426
|
-
#panel-tab-content>* {
|
|
427
|
-
color: var(--colors-emulsify-blue-100) !important;
|
|
428
|
-
}
|
|
429
|
-
#panel-tab-content a,
|
|
430
|
-
#panel-tab-content a span,
|
|
431
|
-
#panel-tab-content a span svg {
|
|
432
|
-
color: var(--colors-emulsify-blue-800);
|
|
433
|
-
}
|
|
434
|
-
#panel-tab-content>div>div>div>div>div>div {
|
|
435
|
-
background: transparent;
|
|
436
|
-
}
|
|
437
|
-
#panel-tab-content>div>div>div>div>div>div>div {
|
|
438
|
-
color: var(--colors-emulsify-blue-1000) !important;
|
|
439
|
-
}
|
|
440
|
-
</style>
|
|
441
|
-
`;
|
|
225
|
+
const managerStyles = readOptionalHtmlFragment('./manager-head.css');
|
|
226
|
+
const inlineStyles = managerStyles
|
|
227
|
+
? `<style>
|
|
228
|
+
${managerStyles}
|
|
229
|
+
</style>`
|
|
230
|
+
: '';
|
|
442
231
|
const externalManagerHtml = readOptionalHtmlFragment(
|
|
443
232
|
'../../../../config/emulsify-core/storybook/manager-head.html',
|
|
444
233
|
);
|
|
@@ -466,156 +255,7 @@ const baseConfig = {
|
|
|
466
255
|
${externalHtml}`;
|
|
467
256
|
},
|
|
468
257
|
|
|
469
|
-
|
|
470
|
-
* Merges Storybook's generated Vite config with Emulsify's shared Vite config.
|
|
471
|
-
*
|
|
472
|
-
* Storybook supplies a baseline config, but Emulsify still needs to expose
|
|
473
|
-
* the resolved environment, expand filesystem access, and expose the Twig
|
|
474
|
-
* virtual glob module used by the runtime resolver.
|
|
475
|
-
*
|
|
476
|
-
* @param {import('vite').UserConfig} config - Storybook's generated Vite config.
|
|
477
|
-
* @returns {Promise<import('vite').UserConfig>} Final Vite config used by Storybook.
|
|
478
|
-
*/
|
|
479
|
-
async viteFinal(config) {
|
|
480
|
-
const { mergeConfig } = await import('vite');
|
|
481
|
-
/** @type {StorybookEnvironment} */
|
|
482
|
-
const env = resolvedStorybookEnv;
|
|
483
|
-
const storybookBuildConfig = config?.build || {};
|
|
484
|
-
|
|
485
|
-
// Keep using the `serve` branch of the shared Vite config here. Storybook
|
|
486
|
-
// has historically consumed that branch, while `mode` still reflects
|
|
487
|
-
// whether Storybook is running in development or production.
|
|
488
|
-
const mode = config?.mode || 'development';
|
|
489
|
-
const baseViteConfig =
|
|
490
|
-
typeof viteConfig === 'function'
|
|
491
|
-
? await viteConfig({ command: 'serve', mode })
|
|
492
|
-
: viteConfig;
|
|
493
|
-
const existingDefine = (config && config.define) || {};
|
|
494
|
-
const viteDefine = (baseViteConfig && baseViteConfig.define) || {};
|
|
495
|
-
|
|
496
|
-
// Allow Storybook's dev server to read component sources from the project
|
|
497
|
-
// root and any structure override paths used by Emulsify consumers.
|
|
498
|
-
const allowList = new Set([
|
|
499
|
-
...(config?.server?.fs?.allow || []),
|
|
500
|
-
env.projectDir,
|
|
501
|
-
path.resolve(env.projectDir, 'src'),
|
|
502
|
-
path.resolve(env.projectDir, 'components'),
|
|
503
|
-
path.resolve(env.projectDir, 'dist'),
|
|
504
|
-
...(Array.isArray(env.projectStructure?.sourceRoots)
|
|
505
|
-
? env.projectStructure.sourceRoots
|
|
506
|
-
: []),
|
|
507
|
-
...(Array.isArray(env.componentRoots) ? env.componentRoots : []),
|
|
508
|
-
...(Array.isArray(env.structureRoots) ? env.structureRoots : []),
|
|
509
|
-
...(env.namespaceRoots && typeof env.namespaceRoots === 'object'
|
|
510
|
-
? Object.values(env.namespaceRoots)
|
|
511
|
-
: []),
|
|
512
|
-
...(Array.isArray(env.projectStructure?.assetRoots)
|
|
513
|
-
? env.projectStructure.assetRoots
|
|
514
|
-
: []),
|
|
515
|
-
]);
|
|
516
|
-
|
|
517
|
-
// Twig files are loaded through custom resolvers/plugins, so they need to
|
|
518
|
-
// be treated as importable assets by Storybook's Vite pipeline.
|
|
519
|
-
const assetsInclude = Array.from(
|
|
520
|
-
new Set([
|
|
521
|
-
...(config.assetsInclude || []),
|
|
522
|
-
...(baseViteConfig.assetsInclude || []),
|
|
523
|
-
'**/*.twig',
|
|
524
|
-
]),
|
|
525
|
-
);
|
|
526
|
-
const optimizeDepsInclude = mergeReactSingletonOptimizeDeps(
|
|
527
|
-
baseViteConfig?.optimizeDeps?.include,
|
|
528
|
-
config?.optimizeDeps?.include,
|
|
529
|
-
[
|
|
530
|
-
'twig',
|
|
531
|
-
'@emulsify/core/extensions/twig',
|
|
532
|
-
...twigExtensionModuleSpecifiers(env),
|
|
533
|
-
],
|
|
534
|
-
);
|
|
535
|
-
|
|
536
|
-
const mergedConfig = mergeConfig(config, {
|
|
537
|
-
...baseViteConfig,
|
|
538
|
-
resolve: mergeReactSingletonResolve(baseViteConfig, config),
|
|
539
|
-
define: {
|
|
540
|
-
// Preserve shared and Storybook-provided constants, then publish the
|
|
541
|
-
// resolved Emulsify environment to client-side code.
|
|
542
|
-
...viteDefine,
|
|
543
|
-
...existingDefine,
|
|
544
|
-
__EMULSIFY_ENV__: JSON.stringify(env),
|
|
545
|
-
'globalThis.__EMULSIFY_ENV__': JSON.stringify(env),
|
|
546
|
-
},
|
|
547
|
-
server: {
|
|
548
|
-
...(baseViteConfig?.server || {}),
|
|
549
|
-
fs: {
|
|
550
|
-
allow: Array.from(allowList),
|
|
551
|
-
},
|
|
552
|
-
},
|
|
553
|
-
assetsInclude,
|
|
554
|
-
plugins: [...(baseViteConfig?.plugins || [])],
|
|
555
|
-
esbuild: {
|
|
556
|
-
// Some downstream code is authored as `.js` files containing JSX, so
|
|
557
|
-
// keep Storybook's esbuild settings aligned with the shared Vite config.
|
|
558
|
-
jsx: 'automatic',
|
|
559
|
-
loader: 'jsx',
|
|
560
|
-
include: /.*\.jsx?$/,
|
|
561
|
-
exclude: [],
|
|
562
|
-
},
|
|
563
|
-
optimizeDeps: {
|
|
564
|
-
...(baseViteConfig?.optimizeDeps || {}),
|
|
565
|
-
...(config?.optimizeDeps || {}),
|
|
566
|
-
include: optimizeDepsInclude,
|
|
567
|
-
exclude: mergeTwigRuntimeOptimizeDepsExcludes(
|
|
568
|
-
baseViteConfig?.optimizeDeps?.exclude,
|
|
569
|
-
config?.optimizeDeps?.exclude,
|
|
570
|
-
),
|
|
571
|
-
esbuildOptions: {
|
|
572
|
-
...(baseViteConfig?.optimizeDeps?.esbuildOptions || {}),
|
|
573
|
-
...(config?.optimizeDeps?.esbuildOptions || {}),
|
|
574
|
-
plugins: [
|
|
575
|
-
...(baseViteConfig?.optimizeDeps?.esbuildOptions?.plugins || []),
|
|
576
|
-
...(config?.optimizeDeps?.esbuildOptions?.plugins || []),
|
|
577
|
-
makeTwigVirtualModuleOptimizerPlugin(),
|
|
578
|
-
],
|
|
579
|
-
loader: {
|
|
580
|
-
...(baseViteConfig?.optimizeDeps?.esbuildOptions?.loader || {}),
|
|
581
|
-
...(config?.optimizeDeps?.esbuildOptions?.loader || {}),
|
|
582
|
-
// Pre-bundle `.js` dependencies with the JSX loader for packages
|
|
583
|
-
// that ship JSX without a `.jsx` extension.
|
|
584
|
-
'.js': 'jsx',
|
|
585
|
-
},
|
|
586
|
-
},
|
|
587
|
-
},
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
return {
|
|
591
|
-
...mergedConfig,
|
|
592
|
-
build: {
|
|
593
|
-
...(mergedConfig.build || {}),
|
|
594
|
-
...(storybookBuildConfig.outDir
|
|
595
|
-
? { outDir: storybookBuildConfig.outDir }
|
|
596
|
-
: {}),
|
|
597
|
-
assetsDir: storybookViteAssetsDir,
|
|
598
|
-
emptyOutDir: false,
|
|
599
|
-
},
|
|
600
|
-
resolve: mergeReactSingletonResolve(mergedConfig),
|
|
601
|
-
optimizeDeps: {
|
|
602
|
-
...(mergedConfig.optimizeDeps || {}),
|
|
603
|
-
include: mergeReactSingletonOptimizeDeps(
|
|
604
|
-
mergedConfig.optimizeDeps?.include,
|
|
605
|
-
),
|
|
606
|
-
exclude: mergeTwigRuntimeOptimizeDepsExcludes(
|
|
607
|
-
mergedConfig.optimizeDeps?.exclude,
|
|
608
|
-
),
|
|
609
|
-
esbuildOptions: {
|
|
610
|
-
...(mergedConfig.optimizeDeps?.esbuildOptions || {}),
|
|
611
|
-
loader: {
|
|
612
|
-
...(mergedConfig.optimizeDeps?.esbuildOptions?.loader || {}),
|
|
613
|
-
'.js': 'jsx',
|
|
614
|
-
},
|
|
615
|
-
},
|
|
616
|
-
},
|
|
617
|
-
};
|
|
618
|
-
},
|
|
258
|
+
viteFinal: createViteFinal(resolvedStorybookEnv),
|
|
619
259
|
};
|
|
620
260
|
|
|
621
261
|
/**
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--colors-emulsify-blue-100: #e6f5fc;
|
|
3
|
+
--colors-emulsify-blue-200: #ccecfa;
|
|
4
|
+
--colors-emulsify-blue-300: #99d9f4;
|
|
5
|
+
--colors-emulsify-blue-400: #66c5ef;
|
|
6
|
+
--colors-emulsify-blue-500: #33b2e9;
|
|
7
|
+
--colors-emulsify-blue-600: #009fe4;
|
|
8
|
+
--colors-emulsify-blue-700: #007fb6;
|
|
9
|
+
--colors-emulsify-blue-800: #005f89;
|
|
10
|
+
--colors-emulsify-blue-900: #00405b;
|
|
11
|
+
--colors-emulsify-blue-1000: #00202e;
|
|
12
|
+
--colors-purple: #8b1e7e;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.sidebar-container {
|
|
16
|
+
background-color: var(--colors-emulsify-blue-900);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.sidebar-container .sidebar-subheading {
|
|
20
|
+
color: var(--colors-emulsify-blue-200);
|
|
21
|
+
font-size: 13px;
|
|
22
|
+
letter-spacing: 0.15em;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.sidebar-container .sidebar-subheading button:focus {
|
|
26
|
+
color: var(--colors-emulsify-blue-300);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Triangle icon. */
|
|
30
|
+
.sidebar-container .sidebar-subheading button span {
|
|
31
|
+
color: var(--colors-emulsify-blue-300);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.sidebar-container .search-field input {
|
|
35
|
+
border-color: var(--colors-emulsify-blue-700);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.sidebar-container .search-field input:active {
|
|
39
|
+
border-color: var(--colors-emulsify-blue-700);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.sidebar-container .search-result-recentlyOpened,
|
|
43
|
+
.sidebar-container .search-result-back,
|
|
44
|
+
.sidebar-container .search-result-clearHistory {
|
|
45
|
+
color: var(--colors-emulsify-blue-300) !important;
|
|
46
|
+
letter-spacing: 0.15em;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.sidebar-container .search-result-back span,
|
|
50
|
+
.sidebar-container .search-result-back svg,
|
|
51
|
+
.sidebar-container .search-result-clearHistory span,
|
|
52
|
+
.sidebar-container .search-result-clearHistory svg {
|
|
53
|
+
letter-spacing: normal;
|
|
54
|
+
color: white;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.sidebar-container .sidebar-item svg {
|
|
58
|
+
margin-top: 1px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.sidebar-container .sidebar-item span {
|
|
62
|
+
margin-top: 4px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.sidebar-container .sidebar-subheading-action svg {
|
|
66
|
+
color: var(--colors-emulsify-blue-400);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.sidebar-container .sidebar-subheading-action:hover svg {
|
|
70
|
+
color: var(--colors-emulsify-blue-300);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.sidebar-header button[title='Shortcuts'] {
|
|
74
|
+
box-shadow: none;
|
|
75
|
+
border: 1px solid var(--colors-emulsify-blue-700);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.sidebar-header button[title='Shortcuts']:active {
|
|
79
|
+
border: 1px solid var(--colors-emulsify-blue-500);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.sidebar-header button[title='Shortcuts']:focus {
|
|
83
|
+
background: transparent;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#shortcuts {
|
|
87
|
+
border-bottom-color: var(--colors-emulsify-blue-900) !important;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
[role='main']:not(:nth-child(3)) {
|
|
91
|
+
top: 1rem !important;
|
|
92
|
+
height: calc(100vh - 2rem) !important;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
[role='main'] .os-host .os-content button:hover {
|
|
96
|
+
background: var(--colors-emulsify-blue-100);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
[role='main'] .os-host .os-content button:hover svg {
|
|
100
|
+
color: var(--colors-emulsify-blue-900);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#panel-tab-content,
|
|
104
|
+
#panel-tab-content > * {
|
|
105
|
+
color: var(--colors-emulsify-blue-100) !important;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
#panel-tab-content a,
|
|
109
|
+
#panel-tab-content a span,
|
|
110
|
+
#panel-tab-content a span svg {
|
|
111
|
+
color: var(--colors-emulsify-blue-800);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
#panel-tab-content > div > div > div > div > div > div {
|
|
115
|
+
background: transparent;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#panel-tab-content > div > div > div > div > div > div > div {
|
|
119
|
+
color: var(--colors-emulsify-blue-1000) !important;
|
|
120
|
+
}
|
package/.storybook/preview.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { getRules } from 'axe-core';
|
|
6
|
-
import React from 'react';
|
|
7
6
|
import { defaultDecorateStory, useEffect } from 'storybook/preview-api';
|
|
8
7
|
import Twig from 'twig';
|
|
9
8
|
import { twigExtensionInstallers } from 'virtual:emulsify-twig-extension-installers';
|
|
@@ -12,15 +11,12 @@ import {
|
|
|
12
11
|
normalizePreviewOverrideModule,
|
|
13
12
|
} from '../src/storybook/preview-parameters.js';
|
|
14
13
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} from '../src/storybook/
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
getStorybookPlatformAdapter,
|
|
22
|
-
setupTwig,
|
|
23
|
-
} from './utils.js';
|
|
14
|
+
applyStoryDecorators,
|
|
15
|
+
renderPreviewStory,
|
|
16
|
+
} from '../src/storybook/preview-decorator.js';
|
|
17
|
+
import { attachStorybookBehaviors } from '../src/storybook/platform-behaviors.js';
|
|
18
|
+
import { setupTwig } from '../src/storybook/twig/setup.js';
|
|
19
|
+
import { fetchCSSFiles, getStorybookPlatformAdapter } from './utils.js';
|
|
24
20
|
|
|
25
21
|
const previewOverrideModules = import.meta.glob(
|
|
26
22
|
[
|
|
@@ -93,13 +89,7 @@ const AxeRules = enableRulesByTag([
|
|
|
93
89
|
* @returns {Function} Decorated story function.
|
|
94
90
|
*/
|
|
95
91
|
export const applyDecorators = (storyFn, decorators) =>
|
|
96
|
-
defaultDecorateStory
|
|
97
|
-
(context) =>
|
|
98
|
-
withLegacyStoryToString(React.createElement(storyFn, context), () =>
|
|
99
|
-
storyFn(context),
|
|
100
|
-
),
|
|
101
|
-
decorators,
|
|
102
|
-
);
|
|
92
|
+
applyStoryDecorators(defaultDecorateStory, storyFn, decorators);
|
|
103
93
|
|
|
104
94
|
/**
|
|
105
95
|
* Storybook decorators to apply platform-specific behavior after each story render.
|
|
@@ -125,7 +115,7 @@ export const decorators = [
|
|
|
125
115
|
});
|
|
126
116
|
}, [args]);
|
|
127
117
|
|
|
128
|
-
return
|
|
118
|
+
return renderPreviewStory(Story, context, {
|
|
129
119
|
platformAdapter,
|
|
130
120
|
});
|
|
131
121
|
},
|
package/.storybook/utils.js
CHANGED
|
@@ -89,7 +89,7 @@ export function getProjectMachineName() {
|
|
|
89
89
|
: undefined;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
// Keep these
|
|
92
|
+
// Keep these compatibility exports for downstream preview overrides.
|
|
93
93
|
export {
|
|
94
94
|
attachStorybookBehaviors,
|
|
95
95
|
fetchCSSFiles,
|
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ See [Version Evolution](docs/version-evolution.md) for more release history.
|
|
|
34
34
|
|
|
35
35
|
Twig and React are equally valid ways to build component libraries with Emulsify Core. The right authoring model depends on the consuming project:
|
|
36
36
|
|
|
37
|
-
- Use Twig for CMS themes and server-rendered template systems. Drupal has a dedicated adapter today
|
|
37
|
+
- Use Twig for CMS themes and server-rendered template systems. Drupal has a dedicated adapter today. WordPress and Timber projects should currently use `platform: "none"` unless a project adds its own platform-specific behavior.
|
|
38
38
|
- Use React for standalone UI libraries, application components, or projects that already use React.
|
|
39
39
|
- Use mixed Twig and React when a design system needs to document both CMS-rendered and JavaScript-rendered components in the same Storybook instance.
|
|
40
40
|
|
|
@@ -56,10 +56,18 @@ Every project should provide a `project.emulsify.json` file at the project root:
|
|
|
56
56
|
"platform": "none",
|
|
57
57
|
"name": "example",
|
|
58
58
|
"machineName": "example"
|
|
59
|
+
},
|
|
60
|
+
"assets": {
|
|
61
|
+
"roots": ["./design/assets"]
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
64
|
```
|
|
62
65
|
|
|
66
|
+
Asset files are discovered from the default asset roots and any additional
|
|
67
|
+
roots configured in `project.emulsify.json`. Use asset roots when a project
|
|
68
|
+
stores fonts, images, icons, or other static files outside the default
|
|
69
|
+
locations.
|
|
70
|
+
|
|
63
71
|
Common project scripts call the shared Emulsify Core Vite and Storybook config:
|
|
64
72
|
|
|
65
73
|
- `storybook`: starts Storybook development.
|
|
@@ -86,7 +94,7 @@ The documentation is split by task:
|
|
|
86
94
|
|
|
87
95
|
## Known Limitations
|
|
88
96
|
|
|
89
|
-
- Implemented platform adapters are currently `none` and `drupal`. WordPress
|
|
97
|
+
- Implemented platform adapters are currently `none` and `drupal`. WordPress and Timber projects should currently use `platform: "none"`. This keeps Emulsify Core in platform-neutral mode while still supporting Twig-oriented component development. A dedicated WordPress adapter may be added later when WordPress-specific behavior is introduced. See [Platform Adapters](docs/platform-adapters.md).
|
|
90
98
|
- Storybook's Twig resolver eagerly imports Twig modules and raw Twig source. This is reliable for `include()` and `source()`, but large Twig libraries should keep Storybook source roots intentional. See [Performance](docs/performance.md).
|
|
91
99
|
- Production sourcemaps are enabled by default unless a project overrides Vite config through `config/emulsify-core/vite/plugins.*`. See [Performance](docs/performance.md).
|
|
92
100
|
- Project extensions use the public `config/emulsify-core` directory: `config/emulsify-core/vite/plugins.*` for Vite, `config/emulsify-core/storybook/...` for Storybook, and `config/emulsify-core/a11y.config.js` for a11y. See [Extension Points](docs/extension-points.md).
|
|
@@ -103,7 +111,7 @@ Release-readiness coverage validates:
|
|
|
103
111
|
- Projects using multiple `variant.structureImplementations`.
|
|
104
112
|
- Mixed Twig + React Storybook projects.
|
|
105
113
|
|
|
106
|
-
WordPress
|
|
114
|
+
WordPress and Timber projects should currently use `platform: "none"`. This keeps Emulsify Core in platform-neutral mode while still supporting Twig-oriented component development. A dedicated WordPress adapter may be added later when WordPress-specific behavior is introduced. The implemented adapters in this package are currently `none` and `drupal`.
|
|
107
115
|
|
|
108
116
|
## Public Imports
|
|
109
117
|
|
package/config/a11y.config.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
export default {
|
|
9
9
|
storybookBuildDir: '../../../../.out',
|
|
10
|
+
discoverStories: true,
|
|
10
11
|
pa11y: {
|
|
11
12
|
includeNotices: false,
|
|
12
13
|
includeWarnings: false,
|
|
@@ -17,7 +18,7 @@ export default {
|
|
|
17
18
|
codes: ['landmark-one-main', 'page-has-heading-one'],
|
|
18
19
|
descriptions: ['Ensures all page content is contained by landmarks'],
|
|
19
20
|
},
|
|
20
|
-
//
|
|
21
|
+
// Manual Storybook IDs merged with IDs discovered from built Storybook output.
|
|
21
22
|
components: [
|
|
22
23
|
'base-colors--palettes',
|
|
23
24
|
'base-motion--usage',
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - `SDC`: boolean from project.emulsify.json `project.singleDirectoryComponents`.
|
|
10
10
|
* - `structureOverrides`: true when safe `variant.structureImplementations` exist.
|
|
11
11
|
* - `structureRoots`: array of directories from `variant.structureImplementations`.
|
|
12
|
+
* - `assetRoots`: array of directories from safe `assets.roots` config.
|
|
12
13
|
* - `platformAdapter`: active adapter for platform-specific behavior.
|
|
13
14
|
*/
|
|
14
15
|
|
|
@@ -26,6 +27,7 @@ import { resolveProjectConfig } from './project-config.js';
|
|
|
26
27
|
* structureOverrides: boolean,
|
|
27
28
|
* structureRoots: string[],
|
|
28
29
|
* structureImplementations: Array<{name: string, directory: string}>,
|
|
30
|
+
* assetRoots: string[],
|
|
29
31
|
* componentRoots: string[],
|
|
30
32
|
* globalRoots: string[],
|
|
31
33
|
* namespaceRoots: Record<string, string>,
|