@conduction/docusaurus-preset 3.32.0 → 3.34.0
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/MISSING_COMPONENTS.md +7 -6
- package/package.json +1 -1
- package/src/components/AgentTrace/AgentTrace.jsx +26 -5
- package/src/components/AgentTrace/AgentTrace.module.css +41 -2
- package/src/components/AgentTrace/__tests__/AgentTrace.render.test.js +108 -0
- package/src/components/AppMock/AppMock.jsx +14 -5
- package/src/components/AppMock/AppMock.module.css +439 -0
- package/src/components/AppMock/__tests__/AppMock.render.test.js +181 -0
- package/src/components/AppMock/variants/LarpingAppMock.jsx +23 -4
- package/src/components/AppMock/variants/LaunchPadBiMock.jsx +12 -5
- package/src/components/AppMock/variants/LaunchPadTilesMock.jsx +6 -1
- package/src/components/AppMock/variants/LaunchPadWidgetsMock.jsx +17 -8
- package/src/components/AppMock/variants/NLDesignMock.jsx +15 -4
- package/src/components/AppMock/variants/PipelinQMock.jsx +12 -2
- package/src/components/AppMock/variants/PortaliqMock.jsx +21 -9
- package/src/components/AppMock/variants/ScholiqMock.jsx +12 -6
- package/src/components/FlowMock/FlowMock.jsx +5 -3
- package/src/components/FlowMock/FlowMock.module.css +36 -9
- package/src/components/FlowMock/__tests__/FlowMock.render.test.js +83 -0
- package/src/components/KanbanMock/KanbanMock.jsx +35 -5
- package/src/components/KanbanMock/KanbanMock.module.css +120 -28
- package/src/components/KanbanMock/__tests__/KanbanMock.render.test.js +114 -0
- package/src/components/LeafMock/LeafMock.jsx +23 -8
- package/src/components/LeafMock/LeafMock.module.css +123 -0
- package/src/components/LeafMock/__tests__/LeafMock.render.test.js +105 -0
- package/src/components/MockScene/MockScene.jsx +16 -2
- package/src/components/MockScene/MockScene.module.css +22 -2
- package/src/components/MockScene/__tests__/MockScene.render.test.js +101 -0
- package/src/components/WidgetShelf/WidgetShelf.jsx +321 -28
- package/src/components/WidgetShelf/WidgetShelf.module.css +193 -1
- package/src/components/WidgetShelf/__tests__/WidgetShelf.render.test.js +149 -0
|
@@ -7,6 +7,17 @@
|
|
|
7
7
|
* duplicated for a seamless loop, paused on hover/focus) and the
|
|
8
8
|
* static responsive grid (carousel={false}, and what
|
|
9
9
|
* prefers-reduced-motion collapses the carousel to).
|
|
10
|
+
*
|
|
11
|
+
* The CSS keyframe marquee is the SSR/no-JS fallback; once the
|
|
12
|
+
* component mounts, the JSX adds .jsDriven to the track and a rAF
|
|
13
|
+
* loop drives the same transform so the prev/next/pause controls can
|
|
14
|
+
* steer it. Controls are cobalt ghost buttons (never orange fill)
|
|
15
|
+
* and are hidden under reduced motion together with the marquee.
|
|
16
|
+
*
|
|
17
|
+
* Auto panels (.auto*): token-built mini mocks for widgets without an
|
|
18
|
+
* explicit panel. Four archetypes (KPI tile, bar chart, list rows,
|
|
19
|
+
* donut gauge); the accent comes from --wsa / --wsa-soft, set inline
|
|
20
|
+
* by the JSX from a hash of the widget title.
|
|
10
21
|
*/
|
|
11
22
|
|
|
12
23
|
.shelf {
|
|
@@ -89,6 +100,56 @@
|
|
|
89
100
|
to { transform: translateX(-50%); }
|
|
90
101
|
}
|
|
91
102
|
|
|
103
|
+
/* Once the rAF loop owns the transform, the keyframe animation must
|
|
104
|
+
let go — otherwise it would overwrite the inline transform. */
|
|
105
|
+
.track.jsDriven { animation: none; }
|
|
106
|
+
|
|
107
|
+
/* ---- Controls (pause/play + prev/next) ----
|
|
108
|
+
*
|
|
109
|
+
* Cobalt ghost buttons per the kit: transparent ground, cobalt ink,
|
|
110
|
+
* cobalt-200 hairline; hover tints, focus ring is a visible cobalt
|
|
111
|
+
* outline. Never orange. Hidden under reduced motion because the
|
|
112
|
+
* static grid has nothing to steer. */
|
|
113
|
+
|
|
114
|
+
.controls {
|
|
115
|
+
display: flex;
|
|
116
|
+
justify-content: flex-end;
|
|
117
|
+
gap: var(--space-2);
|
|
118
|
+
margin-top: var(--space-4);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.ctrl {
|
|
122
|
+
appearance: none;
|
|
123
|
+
display: inline-flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
justify-content: center;
|
|
126
|
+
width: 38px;
|
|
127
|
+
height: 38px;
|
|
128
|
+
padding: 0;
|
|
129
|
+
border: 1px solid var(--c-cobalt-200);
|
|
130
|
+
border-radius: var(--radius-pill);
|
|
131
|
+
background: transparent;
|
|
132
|
+
color: var(--c-blue-cobalt);
|
|
133
|
+
cursor: pointer;
|
|
134
|
+
}
|
|
135
|
+
.ctrl:hover {
|
|
136
|
+
background: var(--c-cobalt-50);
|
|
137
|
+
border-color: var(--c-blue-cobalt);
|
|
138
|
+
}
|
|
139
|
+
.ctrl:focus-visible {
|
|
140
|
+
outline: 2px solid var(--c-blue-cobalt);
|
|
141
|
+
outline-offset: 2px;
|
|
142
|
+
}
|
|
143
|
+
.ctrl svg { width: 16px; height: 16px; display: block; }
|
|
144
|
+
.chev {
|
|
145
|
+
fill: none;
|
|
146
|
+
stroke: currentColor;
|
|
147
|
+
stroke-width: 2.4;
|
|
148
|
+
stroke-linecap: round;
|
|
149
|
+
stroke-linejoin: round;
|
|
150
|
+
}
|
|
151
|
+
.glyph { fill: currentColor; }
|
|
152
|
+
|
|
92
153
|
/* Reduced motion: drop the duplicate copy and render the first group
|
|
93
154
|
as the static wrapping grid — identical to carousel={false}. The
|
|
94
155
|
.cols-2 / .cols-3 class on the group supplies the column template. */
|
|
@@ -98,7 +159,8 @@
|
|
|
98
159
|
-webkit-mask-image: none;
|
|
99
160
|
mask-image: none;
|
|
100
161
|
}
|
|
101
|
-
.
|
|
162
|
+
.controls { display: none; }
|
|
163
|
+
.track { animation: none; display: block; width: 100%; transform: none !important; }
|
|
102
164
|
.group {
|
|
103
165
|
display: grid;
|
|
104
166
|
gap: var(--space-5);
|
|
@@ -150,3 +212,133 @@
|
|
|
150
212
|
margin: 0;
|
|
151
213
|
padding: var(--space-2) var(--space-5) var(--space-5);
|
|
152
214
|
}
|
|
215
|
+
|
|
216
|
+
/* ---- Auto-generated mini panels ----
|
|
217
|
+
*
|
|
218
|
+
* White widget tile on the tinted panel ground, same chrome family as
|
|
219
|
+
* the hand-built .w mocks. All greeked (bars, dots, one number); the
|
|
220
|
+
* accent tokens --wsa / --wsa-soft arrive inline from the JSX. */
|
|
221
|
+
|
|
222
|
+
.auto {
|
|
223
|
+
box-sizing: border-box;
|
|
224
|
+
width: 100%;
|
|
225
|
+
max-width: 280px;
|
|
226
|
+
min-height: 124px;
|
|
227
|
+
background: white;
|
|
228
|
+
border: 1px solid var(--c-cobalt-100);
|
|
229
|
+
border-radius: var(--radius-md);
|
|
230
|
+
box-shadow: var(--shadow-1);
|
|
231
|
+
padding: 16px 18px;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* KPI number tile */
|
|
235
|
+
.autoKpi {
|
|
236
|
+
display: flex;
|
|
237
|
+
flex-direction: column;
|
|
238
|
+
justify-content: center;
|
|
239
|
+
gap: 10px;
|
|
240
|
+
}
|
|
241
|
+
.autoHex {
|
|
242
|
+
display: block;
|
|
243
|
+
width: 13px;
|
|
244
|
+
height: 15px;
|
|
245
|
+
clip-path: var(--hex-pointy-top);
|
|
246
|
+
background: var(--wsa);
|
|
247
|
+
}
|
|
248
|
+
.autoKpiValue {
|
|
249
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
250
|
+
font-size: 34px;
|
|
251
|
+
font-weight: 700;
|
|
252
|
+
line-height: 1;
|
|
253
|
+
letter-spacing: -0.01em;
|
|
254
|
+
color: var(--c-cobalt-900);
|
|
255
|
+
}
|
|
256
|
+
.autoKpiBar {
|
|
257
|
+
display: block;
|
|
258
|
+
height: 5px;
|
|
259
|
+
border-radius: 1.5px;
|
|
260
|
+
background: var(--wsa-soft);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* Mini bar chart */
|
|
264
|
+
.autoBars {
|
|
265
|
+
display: flex;
|
|
266
|
+
align-items: flex-end;
|
|
267
|
+
gap: 8px;
|
|
268
|
+
height: 124px;
|
|
269
|
+
}
|
|
270
|
+
.autoBar {
|
|
271
|
+
flex: 1;
|
|
272
|
+
border-radius: 2px 2px 0 0;
|
|
273
|
+
background: var(--c-cobalt-200);
|
|
274
|
+
}
|
|
275
|
+
.autoBarHi { background: var(--wsa); }
|
|
276
|
+
|
|
277
|
+
/* List rows */
|
|
278
|
+
.autoList {
|
|
279
|
+
display: flex;
|
|
280
|
+
flex-direction: column;
|
|
281
|
+
justify-content: center;
|
|
282
|
+
gap: 12px;
|
|
283
|
+
}
|
|
284
|
+
.autoRow {
|
|
285
|
+
display: flex;
|
|
286
|
+
align-items: center;
|
|
287
|
+
gap: 10px;
|
|
288
|
+
}
|
|
289
|
+
.autoDot {
|
|
290
|
+
flex-shrink: 0;
|
|
291
|
+
width: 12px;
|
|
292
|
+
height: 14px;
|
|
293
|
+
clip-path: var(--hex-pointy-top);
|
|
294
|
+
background: var(--c-cobalt-200);
|
|
295
|
+
}
|
|
296
|
+
.autoDotHi { background: var(--wsa); }
|
|
297
|
+
.autoRowBars {
|
|
298
|
+
flex: 1;
|
|
299
|
+
display: flex;
|
|
300
|
+
flex-direction: column;
|
|
301
|
+
gap: 5px;
|
|
302
|
+
}
|
|
303
|
+
.autoRowBar {
|
|
304
|
+
display: block;
|
|
305
|
+
height: 5px;
|
|
306
|
+
border-radius: 1.5px;
|
|
307
|
+
background: var(--c-cobalt-700);
|
|
308
|
+
}
|
|
309
|
+
.autoRowLine {
|
|
310
|
+
display: block;
|
|
311
|
+
width: 86%;
|
|
312
|
+
height: 4px;
|
|
313
|
+
border-radius: 1px;
|
|
314
|
+
background: var(--c-cobalt-100);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* Donut gauge */
|
|
318
|
+
.autoDonut {
|
|
319
|
+
display: flex;
|
|
320
|
+
align-items: center;
|
|
321
|
+
gap: 18px;
|
|
322
|
+
}
|
|
323
|
+
.autoRing {
|
|
324
|
+
flex-shrink: 0;
|
|
325
|
+
width: 84px;
|
|
326
|
+
height: 84px;
|
|
327
|
+
}
|
|
328
|
+
.autoRingTrack {
|
|
329
|
+
fill: none;
|
|
330
|
+
stroke: var(--c-cobalt-100);
|
|
331
|
+
stroke-width: 6;
|
|
332
|
+
}
|
|
333
|
+
.autoRingFill {
|
|
334
|
+
fill: none;
|
|
335
|
+
stroke: var(--wsa);
|
|
336
|
+
stroke-width: 6;
|
|
337
|
+
stroke-linecap: round;
|
|
338
|
+
}
|
|
339
|
+
.autoDonutBars {
|
|
340
|
+
flex: 1;
|
|
341
|
+
display: flex;
|
|
342
|
+
flex-direction: column;
|
|
343
|
+
gap: 6px;
|
|
344
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WidgetShelf.render.test.js — renders the real <WidgetShelf> JSX to
|
|
3
|
+
* static markup (the SSR path Docusaurus takes at build time) and
|
|
4
|
+
* asserts on the output: derived carousel speed, auto-generated
|
|
5
|
+
* panels for widgets without an explicit `panel`, and the carousel
|
|
6
|
+
* controls with their accessible names.
|
|
7
|
+
*
|
|
8
|
+
* Uses the same esbuild-bundle-then-renderToStaticMarkup technique as
|
|
9
|
+
* AiDisclosure.render.test.js / scripts/build-kit.mjs. CSS modules
|
|
10
|
+
* are stubbed with an identity proxy (styles.foo → 'foo'), so class
|
|
11
|
+
* assertions read as the source class names.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
const test = require('node:test');
|
|
17
|
+
const {before, after} = test;
|
|
18
|
+
const assert = require('node:assert/strict');
|
|
19
|
+
const path = require('node:path');
|
|
20
|
+
const fs = require('node:fs/promises');
|
|
21
|
+
const {build} = require('esbuild');
|
|
22
|
+
const React = require('react');
|
|
23
|
+
const {renderToStaticMarkup} = require('react-dom/server');
|
|
24
|
+
|
|
25
|
+
const COMPONENT = path.resolve(__dirname, '..', 'WidgetShelf.jsx');
|
|
26
|
+
const PRESET_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
|
|
27
|
+
const SCRATCH = path.join(PRESET_ROOT, '.tmp-widget-shelf-test');
|
|
28
|
+
|
|
29
|
+
const cssModuleStub = {
|
|
30
|
+
name: 'css-module-stub',
|
|
31
|
+
setup(b) {
|
|
32
|
+
b.onResolve({filter: /\.module\.css$/}, (args) => ({path: args.path, namespace: 'css-stub'}));
|
|
33
|
+
b.onLoad({filter: /.*/, namespace: 'css-stub'}, () => ({
|
|
34
|
+
contents: 'export default new Proxy({}, {get: (_, p) => p});',
|
|
35
|
+
loader: 'js',
|
|
36
|
+
}));
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
let WidgetShelf;
|
|
41
|
+
|
|
42
|
+
before(async () => {
|
|
43
|
+
await fs.mkdir(SCRATCH, {recursive: true});
|
|
44
|
+
const tmpDir = await fs.mkdtemp(path.join(SCRATCH, 'run-'));
|
|
45
|
+
const outFile = path.join(tmpDir, 'bundle.cjs');
|
|
46
|
+
await build({
|
|
47
|
+
entryPoints: [COMPONENT],
|
|
48
|
+
outfile: outFile,
|
|
49
|
+
bundle: true,
|
|
50
|
+
format: 'cjs',
|
|
51
|
+
jsx: 'automatic',
|
|
52
|
+
jsxImportSource: 'react',
|
|
53
|
+
tsconfigRaw: {compilerOptions: {jsx: 'react-jsx', jsxImportSource: 'react'}},
|
|
54
|
+
platform: 'node',
|
|
55
|
+
external: ['react'],
|
|
56
|
+
plugins: [cssModuleStub],
|
|
57
|
+
logLevel: 'warning',
|
|
58
|
+
});
|
|
59
|
+
delete require.cache[require.resolve(outFile)];
|
|
60
|
+
WidgetShelf = require(outFile).default;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
after(async () => {
|
|
64
|
+
await fs.rm(SCRATCH, {recursive: true, force: true});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
function render(props) {
|
|
68
|
+
return renderToStaticMarkup(React.createElement(WidgetShelf, props));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function makeWidgets(n) {
|
|
72
|
+
return Array.from({length: n}, (_, i) => ({
|
|
73
|
+
title: `Widget number ${i}`,
|
|
74
|
+
desc: `Description ${i}`,
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
test('SSR smoke: carousel renders every widget twice (live group + aria-hidden dup)', () => {
|
|
79
|
+
const html = render({title: 'Shelf', widgets: makeWidgets(5)});
|
|
80
|
+
assert.equal((html.match(/Widget number 3/g) || []).length, 2);
|
|
81
|
+
assert.match(html, /aria-hidden="true"/);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('default loop duration derives from widget count at 6s per card', () => {
|
|
85
|
+
const html = render({widgets: makeWidgets(15)});
|
|
86
|
+
assert.match(html, /--ws-speed:90s/);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('default loop duration never drops below the 30s floor', () => {
|
|
90
|
+
const html = render({widgets: makeWidgets(3)});
|
|
91
|
+
assert.match(html, /--ws-speed:30s/);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('an explicit speed prop overrides the derived duration', () => {
|
|
95
|
+
const html = render({widgets: makeWidgets(15), speed: 45});
|
|
96
|
+
assert.match(html, /--ws-speed:45s/);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('a widget without a panel gets an auto-generated mini panel', () => {
|
|
100
|
+
const html = render({widgets: [{title: 'Werkvoorraad', desc: 'x'}]});
|
|
101
|
+
assert.match(html, /class="auto auto(Kpi|Bars|List|Donut)"/);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('an explicit panel always wins over the auto panel', () => {
|
|
105
|
+
const html = render({
|
|
106
|
+
widgets: [{title: 'Custom', desc: 'x', panel: React.createElement('div', {className: 'w w-custom'})}],
|
|
107
|
+
});
|
|
108
|
+
assert.match(html, /w-custom/);
|
|
109
|
+
assert.doesNotMatch(html, /class="auto /);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('auto panels are deterministic: same input, same markup', () => {
|
|
113
|
+
const props = {widgets: makeWidgets(8)};
|
|
114
|
+
assert.equal(render(props), render(props));
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('auto panels vary: 8 distinct titles hit at least two archetypes and two accent families', () => {
|
|
118
|
+
const html = render({widgets: makeWidgets(8)});
|
|
119
|
+
const kinds = new Set(
|
|
120
|
+
(html.match(/class="auto auto(Kpi|Bars|List|Donut)"/g) || []).map((m) => m),
|
|
121
|
+
);
|
|
122
|
+
assert.ok(kinds.size >= 2, `expected >=2 archetypes, saw ${[...kinds].join(', ')}`);
|
|
123
|
+
const families = new Set(
|
|
124
|
+
(html.match(/--wsa:var\(--c-([a-z]+)-500\)/g) || []).map((m) => m),
|
|
125
|
+
);
|
|
126
|
+
assert.ok(families.size >= 2, `expected >=2 accent families, saw ${[...families].join(', ')}`);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('auto panels never use reserved accent families (coral/orange, gold)', () => {
|
|
130
|
+
// 64 distinct titles sweep the family picker; the hex-family policy
|
|
131
|
+
// reserves coral (KNVB orange) and gold, so neither may ever appear.
|
|
132
|
+
const html = render({widgets: makeWidgets(64)});
|
|
133
|
+
assert.doesNotMatch(html, /--c-coral|--c-gold|--c-orange/);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('carousel renders pause/play and prev/next controls with accessible names', () => {
|
|
137
|
+
const html = render({widgets: makeWidgets(4)});
|
|
138
|
+
assert.match(html, /aria-label="Previous widgets"/);
|
|
139
|
+
assert.match(html, /aria-label="Next widgets"/);
|
|
140
|
+
assert.match(html, /aria-label="Pause"/);
|
|
141
|
+
assert.match(html, /<button type="button"/);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('the static grid (carousel={false}) has no controls and no duplicate group', () => {
|
|
145
|
+
const html = render({widgets: makeWidgets(4), carousel: false});
|
|
146
|
+
assert.doesNotMatch(html, /aria-label="Previous widgets"/);
|
|
147
|
+
assert.doesNotMatch(html, /aria-label="Pause"/);
|
|
148
|
+
assert.equal((html.match(/Widget number 3/g) || []).length, 1);
|
|
149
|
+
});
|