@charcuterie/ui 4.22.1 → 4.24.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/README.md +13 -0
- package/dist/GroupedList/GroupedList.d.ts +130 -0
- package/dist/GroupedList/GroupedList.d.ts.map +1 -0
- package/dist/GroupedList/GroupedList.js +71 -0
- package/dist/GroupedList/GroupedList.js.map +1 -0
- package/dist/GroupedList/GroupedListRow.d.ts +29 -0
- package/dist/GroupedList/GroupedListRow.d.ts.map +1 -0
- package/dist/GroupedList/GroupedListRow.js +38 -0
- package/dist/GroupedList/GroupedListRow.js.map +1 -0
- package/dist/GroupedList/groupedListStickyHeading.d.ts +20 -0
- package/dist/GroupedList/groupedListStickyHeading.d.ts.map +1 -0
- package/dist/GroupedList/groupedListStickyHeading.js +20 -0
- package/dist/GroupedList/groupedListStickyHeading.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/GroupedList/GroupedList.mdx +95 -0
- package/src/GroupedList/GroupedList.stories.tsx +562 -0
- package/src/GroupedList/GroupedList.test.tsx +390 -0
- package/src/GroupedList/GroupedList.tsx +235 -0
- package/src/GroupedList/GroupedListRow.tsx +117 -0
- package/src/GroupedList/groupedListStickyHeading.ts +24 -0
- package/src/index.ts +12 -0
- package/src/sourceRules.test.ts +4 -1
- package/src/Button/Button.test.tsx +0 -114
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import type { CSSProperties } from "react"
|
|
4
|
+
|
|
5
|
+
import { Badge } from "../Badge/Badge.tsx"
|
|
6
|
+
import { Button } from "../Button/Button.tsx"
|
|
7
|
+
import {
|
|
8
|
+
ContainerBoard,
|
|
9
|
+
StoryCell,
|
|
10
|
+
StoryGrid,
|
|
11
|
+
} from "../board.storyHelpers.tsx"
|
|
12
|
+
import { EmptyState } from "../EmptyState/EmptyState.tsx"
|
|
13
|
+
import type { GroupedListGroup } from "./GroupedList.tsx"
|
|
14
|
+
import { GroupedList } from "./GroupedList.tsx"
|
|
15
|
+
import { GROUPED_LIST_HEADING_SURFACE_PROPERTY } from "./groupedListStickyHeading.ts"
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Fixture data only. A signage app's pages, grouped by the screen
|
|
19
|
+
* that shows them — the shape castkit's page picker draws.
|
|
20
|
+
*/
|
|
21
|
+
const openButton = (name: string, prefix = "") => (
|
|
22
|
+
<Button
|
|
23
|
+
appearance="outline"
|
|
24
|
+
aria-label={`Open ${prefix}${name}`}
|
|
25
|
+
size="sm"
|
|
26
|
+
>
|
|
27
|
+
Open
|
|
28
|
+
</Button>
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const SIGNAGE_GROUPS: GroupedListGroup[] = [
|
|
32
|
+
{
|
|
33
|
+
heading: "Screens",
|
|
34
|
+
items: [
|
|
35
|
+
{
|
|
36
|
+
actions: openButton("Lobby"),
|
|
37
|
+
badges: <Badge size="sm">PIN</Badge>,
|
|
38
|
+
description:
|
|
39
|
+
"3 views: Welcome, Weather, and Schedule",
|
|
40
|
+
key: "lobby",
|
|
41
|
+
label: "Lobby",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
actions: openButton("Break Room"),
|
|
45
|
+
description: "1 view: Menu",
|
|
46
|
+
key: "break-room",
|
|
47
|
+
label: "Break Room",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
key: "screens",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
heading: "On the Lobby screen",
|
|
54
|
+
items: [
|
|
55
|
+
{
|
|
56
|
+
actions: openButton("Welcome"),
|
|
57
|
+
description: "/views/welcome",
|
|
58
|
+
key: "welcome",
|
|
59
|
+
label: "Welcome",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
actions: openButton("Weather"),
|
|
63
|
+
description: "/views/weather",
|
|
64
|
+
key: "weather",
|
|
65
|
+
label: "Weather",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
actions: openButton("Schedule"),
|
|
69
|
+
description: "/views/schedule",
|
|
70
|
+
key: "schedule",
|
|
71
|
+
label: "Schedule",
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
key: "screen:lobby",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
heading: "Not on a screen",
|
|
78
|
+
items: [
|
|
79
|
+
{
|
|
80
|
+
actions: openButton("Test Pattern"),
|
|
81
|
+
description: "/views/test-pattern",
|
|
82
|
+
key: "test-pattern",
|
|
83
|
+
label: "Test Pattern",
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
key: "views",
|
|
87
|
+
},
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Fixture data only. Chores grouped by the team that earns them,
|
|
92
|
+
* each with a summary, badges and an Edit button — the shape
|
|
93
|
+
* tally-marks' card list draws.
|
|
94
|
+
*/
|
|
95
|
+
const CHORE_GROUPS: GroupedListGroup[] = [
|
|
96
|
+
{
|
|
97
|
+
heading: "Team Orion",
|
|
98
|
+
items: [
|
|
99
|
+
{
|
|
100
|
+
actions: (
|
|
101
|
+
<Button
|
|
102
|
+
appearance="outline"
|
|
103
|
+
aria-label="Edit Water the ferns"
|
|
104
|
+
size="sm"
|
|
105
|
+
>
|
|
106
|
+
Edit
|
|
107
|
+
</Button>
|
|
108
|
+
),
|
|
109
|
+
badges: (
|
|
110
|
+
<>
|
|
111
|
+
<Badge
|
|
112
|
+
appearance="soft"
|
|
113
|
+
intent="info"
|
|
114
|
+
size="sm"
|
|
115
|
+
>
|
|
116
|
+
Once a day
|
|
117
|
+
</Badge>
|
|
118
|
+
|
|
119
|
+
<Badge
|
|
120
|
+
appearance="soft"
|
|
121
|
+
intent="warning"
|
|
122
|
+
size="sm"
|
|
123
|
+
>
|
|
124
|
+
after 3:00 PM
|
|
125
|
+
</Badge>
|
|
126
|
+
</>
|
|
127
|
+
),
|
|
128
|
+
content: "Up to 5 pts a day",
|
|
129
|
+
description: "water-ferns",
|
|
130
|
+
key: "water-ferns",
|
|
131
|
+
label: "Water the ferns",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
actions: (
|
|
135
|
+
<Button
|
|
136
|
+
appearance="outline"
|
|
137
|
+
aria-label="Edit Sort the recycling"
|
|
138
|
+
size="sm"
|
|
139
|
+
>
|
|
140
|
+
Edit
|
|
141
|
+
</Button>
|
|
142
|
+
),
|
|
143
|
+
badges: (
|
|
144
|
+
<Badge appearance="soft" size="sm">
|
|
145
|
+
No tag
|
|
146
|
+
</Badge>
|
|
147
|
+
),
|
|
148
|
+
content: "Up to 3 scans a day · 6 pts max",
|
|
149
|
+
description: "sort-recycling",
|
|
150
|
+
key: "sort-recycling",
|
|
151
|
+
label: "Sort the recycling",
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
key: "orion",
|
|
155
|
+
summary: "2 cards · up to 11 pts a day",
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
heading: "Team Vega",
|
|
159
|
+
items: [
|
|
160
|
+
{
|
|
161
|
+
actions: (
|
|
162
|
+
<Button
|
|
163
|
+
appearance="outline"
|
|
164
|
+
aria-label="Edit Feed the goldfish"
|
|
165
|
+
size="sm"
|
|
166
|
+
>
|
|
167
|
+
Edit
|
|
168
|
+
</Button>
|
|
169
|
+
),
|
|
170
|
+
badges: (
|
|
171
|
+
<Badge appearance="soft" intent="info" size="sm">
|
|
172
|
+
Once a day
|
|
173
|
+
</Badge>
|
|
174
|
+
),
|
|
175
|
+
content: "Up to 2 pts a day",
|
|
176
|
+
description: "feed-goldfish",
|
|
177
|
+
key: "feed-goldfish",
|
|
178
|
+
label: "Feed the goldfish",
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
key: "vega",
|
|
182
|
+
summary: "1 card · up to 2 pts a day",
|
|
183
|
+
},
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
const LONG_GROUPS: GroupedListGroup[] = [
|
|
187
|
+
{
|
|
188
|
+
heading:
|
|
189
|
+
"On the Conference Center North Atrium Wayfinding screen",
|
|
190
|
+
items: [
|
|
191
|
+
{
|
|
192
|
+
actions: openButton(
|
|
193
|
+
"Quarterly All-Hands Countdown and Seating Chart",
|
|
194
|
+
),
|
|
195
|
+
badges: (
|
|
196
|
+
<>
|
|
197
|
+
<Badge size="sm">PIN</Badge>
|
|
198
|
+
|
|
199
|
+
<Badge
|
|
200
|
+
appearance="soft"
|
|
201
|
+
intent="info"
|
|
202
|
+
size="sm"
|
|
203
|
+
>
|
|
204
|
+
Rotates every 30 s
|
|
205
|
+
</Badge>
|
|
206
|
+
</>
|
|
207
|
+
),
|
|
208
|
+
description:
|
|
209
|
+
"/views/quarterly-all-hands-countdown-and-seating-chart-for-the-north-atrium",
|
|
210
|
+
key: "all-hands",
|
|
211
|
+
label:
|
|
212
|
+
"Quarterly All-Hands Countdown and Seating Chart",
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
actions: openButton("Map"),
|
|
216
|
+
description: "/views/map",
|
|
217
|
+
key: "map",
|
|
218
|
+
label: "Map",
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
key: "atrium",
|
|
222
|
+
},
|
|
223
|
+
]
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Fixture data only. Enough screens that the pane has to scroll, so
|
|
227
|
+
* a stuck heading has rows to sit over.
|
|
228
|
+
*/
|
|
229
|
+
const MANY_SCREEN_GROUPS: GroupedListGroup[] = [
|
|
230
|
+
"Lobby",
|
|
231
|
+
"Break Room",
|
|
232
|
+
"Loading Dock",
|
|
233
|
+
].map((screen) => ({
|
|
234
|
+
heading: `On the ${screen} screen`,
|
|
235
|
+
items: ["Welcome", "Weather", "Schedule", "Menu"].map(
|
|
236
|
+
(view) => ({
|
|
237
|
+
actions: openButton(`${view} on ${screen}`),
|
|
238
|
+
description: `/views/${view.toLowerCase()}`,
|
|
239
|
+
key: view,
|
|
240
|
+
label: view,
|
|
241
|
+
}),
|
|
242
|
+
),
|
|
243
|
+
key: screen,
|
|
244
|
+
}))
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* A fixed block size is what makes the pane scroll; the heading
|
|
248
|
+
* sticks to the pane, the nearest scrolling ancestor.
|
|
249
|
+
*/
|
|
250
|
+
const SCROLL_PANE_STYLE: CSSProperties = {
|
|
251
|
+
blockSize: "16rem",
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* The host says which surface the list sits on. The pane is raised,
|
|
256
|
+
* so the stuck heading is raised too.
|
|
257
|
+
*/
|
|
258
|
+
const RAISED_PANE_STYLE = {
|
|
259
|
+
...SCROLL_PANE_STYLE,
|
|
260
|
+
[GROUPED_LIST_HEADING_SURFACE_PROPERTY]:
|
|
261
|
+
"var(--color-surface-raised)",
|
|
262
|
+
} as CSSProperties
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* A cap rather than a width, so the long label clips in every window
|
|
266
|
+
* — an ultrawide grid cell would otherwise hold it whole and the
|
|
267
|
+
* truncation half of the board would show nothing.
|
|
268
|
+
*/
|
|
269
|
+
const TRUNCATION_CELL_STYLE = { maxInlineSize: "20rem" }
|
|
270
|
+
|
|
271
|
+
const meta = {
|
|
272
|
+
title: "Components/Data/GroupedList",
|
|
273
|
+
component: GroupedList,
|
|
274
|
+
parameters: { layout: "padded" },
|
|
275
|
+
args: {
|
|
276
|
+
groups: SIGNAGE_GROUPS,
|
|
277
|
+
headingLevel: 3,
|
|
278
|
+
isTextTruncated: true,
|
|
279
|
+
size: "md",
|
|
280
|
+
},
|
|
281
|
+
} satisfies Meta<typeof GroupedList>
|
|
282
|
+
|
|
283
|
+
export default meta
|
|
284
|
+
|
|
285
|
+
type Story = StoryObj<typeof meta>
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* A signage app's pages under the screen that shows them. Each row is
|
|
289
|
+
* a name, a muted line, an optional badge, and one action named
|
|
290
|
+
* after its row.
|
|
291
|
+
*/
|
|
292
|
+
export const Playground: Story = {
|
|
293
|
+
parameters: playgroundParameters,
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* `size` and `isTextTruncated` are the two axes. `sm` is a settings
|
|
298
|
+
* pane or a sidebar; `md` is a page. Wrapping is for a list whose
|
|
299
|
+
* rows are read rather than scanned.
|
|
300
|
+
*/
|
|
301
|
+
export const AllVariants: Story = {
|
|
302
|
+
render: () => (
|
|
303
|
+
<StoryGrid columns={2}>
|
|
304
|
+
<StoryCell align="stretch" label="md (default)">
|
|
305
|
+
<GroupedList groups={CHORE_GROUPS} />
|
|
306
|
+
</StoryCell>
|
|
307
|
+
|
|
308
|
+
<StoryCell align="stretch" label="sm">
|
|
309
|
+
<GroupedList groups={CHORE_GROUPS} size="sm" />
|
|
310
|
+
</StoryCell>
|
|
311
|
+
|
|
312
|
+
<StoryCell
|
|
313
|
+
align="stretch"
|
|
314
|
+
label="isTextTruncated (default)"
|
|
315
|
+
>
|
|
316
|
+
<div style={TRUNCATION_CELL_STYLE}>
|
|
317
|
+
<GroupedList groups={LONG_GROUPS} size="sm" />
|
|
318
|
+
</div>
|
|
319
|
+
</StoryCell>
|
|
320
|
+
|
|
321
|
+
<StoryCell
|
|
322
|
+
align="stretch"
|
|
323
|
+
label="isTextTruncated={false}"
|
|
324
|
+
>
|
|
325
|
+
<div style={TRUNCATION_CELL_STYLE}>
|
|
326
|
+
<GroupedList
|
|
327
|
+
groups={LONG_GROUPS}
|
|
328
|
+
isTextTruncated={false}
|
|
329
|
+
size="sm"
|
|
330
|
+
/>
|
|
331
|
+
</div>
|
|
332
|
+
</StoryCell>
|
|
333
|
+
</StoryGrid>
|
|
334
|
+
),
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Every optional part of a row, added one at a time — and the two
|
|
339
|
+
* group states: an empty group is not drawn, and a list with no
|
|
340
|
+
* items at all draws its `emptyState`.
|
|
341
|
+
*/
|
|
342
|
+
export const AllStates: Story = {
|
|
343
|
+
render: () => (
|
|
344
|
+
<StoryGrid columns={2}>
|
|
345
|
+
<StoryCell align="stretch" label="label only">
|
|
346
|
+
<GroupedList
|
|
347
|
+
groups={[
|
|
348
|
+
{
|
|
349
|
+
heading: "Label only",
|
|
350
|
+
items: [
|
|
351
|
+
{ key: "alpha", label: "Alpha" },
|
|
352
|
+
{ key: "beta", label: "Beta" },
|
|
353
|
+
],
|
|
354
|
+
key: "plain",
|
|
355
|
+
},
|
|
356
|
+
]}
|
|
357
|
+
size="sm"
|
|
358
|
+
/>
|
|
359
|
+
</StoryCell>
|
|
360
|
+
|
|
361
|
+
<StoryCell
|
|
362
|
+
align="stretch"
|
|
363
|
+
label="description, badges, content, actions"
|
|
364
|
+
>
|
|
365
|
+
<GroupedList
|
|
366
|
+
groups={[CHORE_GROUPS[1] as GroupedListGroup]}
|
|
367
|
+
size="sm"
|
|
368
|
+
/>
|
|
369
|
+
</StoryCell>
|
|
370
|
+
|
|
371
|
+
<StoryCell
|
|
372
|
+
align="stretch"
|
|
373
|
+
label="an empty group is not drawn"
|
|
374
|
+
>
|
|
375
|
+
<GroupedList
|
|
376
|
+
groups={[
|
|
377
|
+
{
|
|
378
|
+
heading: "Never drawn",
|
|
379
|
+
items: [],
|
|
380
|
+
key: "empty",
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
heading: "Drawn",
|
|
384
|
+
items: [{ key: "gamma", label: "Gamma" }],
|
|
385
|
+
key: "drawn",
|
|
386
|
+
},
|
|
387
|
+
]}
|
|
388
|
+
size="sm"
|
|
389
|
+
/>
|
|
390
|
+
</StoryCell>
|
|
391
|
+
|
|
392
|
+
<StoryCell
|
|
393
|
+
align="stretch"
|
|
394
|
+
label="every group empty: emptyState"
|
|
395
|
+
>
|
|
396
|
+
<GroupedList
|
|
397
|
+
emptyState={
|
|
398
|
+
<EmptyState
|
|
399
|
+
description="Try fewer words."
|
|
400
|
+
heading="No page matches"
|
|
401
|
+
headingLevel={3}
|
|
402
|
+
size="sm"
|
|
403
|
+
/>
|
|
404
|
+
}
|
|
405
|
+
groups={[
|
|
406
|
+
{
|
|
407
|
+
heading: "Screens",
|
|
408
|
+
items: [],
|
|
409
|
+
key: "screens",
|
|
410
|
+
},
|
|
411
|
+
]}
|
|
412
|
+
size="sm"
|
|
413
|
+
/>
|
|
414
|
+
</StoryCell>
|
|
415
|
+
</StoryGrid>
|
|
416
|
+
),
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* The text column asks for 12rem and grows. Below that, the actions
|
|
421
|
+
* drop under the text instead of crushing the label — decided by the
|
|
422
|
+
* content, with no container query and no media query.
|
|
423
|
+
*/
|
|
424
|
+
export const Responsive: Story = {
|
|
425
|
+
render: () => (
|
|
426
|
+
<ContainerBoard>
|
|
427
|
+
<GroupedList groups={CHORE_GROUPS} size="sm" />
|
|
428
|
+
</ContainerBoard>
|
|
429
|
+
),
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Tab walks the actions in reading order, group by group. Nothing
|
|
434
|
+
* else in the list takes focus: a row is not a control, and its
|
|
435
|
+
* action carries its name ("Open Lobby").
|
|
436
|
+
*/
|
|
437
|
+
export const Interactive: Story = {}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* A long label and a long path clip to one line, and the full text
|
|
441
|
+
* is the tooltip while — and only while — it is clipped. The short
|
|
442
|
+
* row underneath has no tooltip at all.
|
|
443
|
+
*/
|
|
444
|
+
export const LongText: Story = {
|
|
445
|
+
render: () => (
|
|
446
|
+
<div style={{ inlineSize: "28rem" }}>
|
|
447
|
+
<GroupedList groups={LONG_GROUPS} />
|
|
448
|
+
</div>
|
|
449
|
+
),
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Nothing to list. The app chooses the wording — "nothing yet" and
|
|
454
|
+
* "nothing matches" are different sentences — and passes it as
|
|
455
|
+
* `emptyState`.
|
|
456
|
+
*/
|
|
457
|
+
export const Empty: Story = {
|
|
458
|
+
args: {
|
|
459
|
+
emptyState: (
|
|
460
|
+
<EmptyState
|
|
461
|
+
description="Add screens and views in the management page."
|
|
462
|
+
heading="This server has no pages"
|
|
463
|
+
headingLevel={3}
|
|
464
|
+
size="sm"
|
|
465
|
+
/>
|
|
466
|
+
),
|
|
467
|
+
groups: [],
|
|
468
|
+
},
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* The Narrow View: a phone-width window. Badges wrap under the
|
|
473
|
+
* label, and the actions wrap under the text.
|
|
474
|
+
*/
|
|
475
|
+
export const NarrowView: Story = {
|
|
476
|
+
args: { groups: CHORE_GROUPS, size: "sm" },
|
|
477
|
+
globals: { viewport: { value: "mobile1" } },
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* `isHeadingSticky` pins each heading to the top of the scrolling
|
|
482
|
+
* pane while its rows scroll under it. On the left the pane is the
|
|
483
|
+
* page surface, which is the default heading background. On the
|
|
484
|
+
* right the pane is raised, and it sets
|
|
485
|
+
* `--charcuterie-grouped-list-heading-surface` to match.
|
|
486
|
+
*/
|
|
487
|
+
export const StickyHeadings: Story = {
|
|
488
|
+
render: () => (
|
|
489
|
+
<StoryGrid columns={2}>
|
|
490
|
+
<StoryCell
|
|
491
|
+
align="stretch"
|
|
492
|
+
label="default: surface-base"
|
|
493
|
+
>
|
|
494
|
+
<section
|
|
495
|
+
aria-label="Pages on a base surface"
|
|
496
|
+
className="overflow-y-auto rounded-lg border border-border-subtle bg-surface-base px-3"
|
|
497
|
+
style={SCROLL_PANE_STYLE}
|
|
498
|
+
>
|
|
499
|
+
<GroupedList
|
|
500
|
+
groups={MANY_SCREEN_GROUPS}
|
|
501
|
+
isHeadingSticky
|
|
502
|
+
size="sm"
|
|
503
|
+
/>
|
|
504
|
+
</section>
|
|
505
|
+
</StoryCell>
|
|
506
|
+
|
|
507
|
+
<StoryCell
|
|
508
|
+
align="stretch"
|
|
509
|
+
label="host sets the surface: surface-raised"
|
|
510
|
+
>
|
|
511
|
+
<section
|
|
512
|
+
aria-label="Pages on a raised surface"
|
|
513
|
+
className="overflow-y-auto rounded-lg border border-border-subtle bg-surface-raised px-3"
|
|
514
|
+
style={RAISED_PANE_STYLE}
|
|
515
|
+
>
|
|
516
|
+
<GroupedList
|
|
517
|
+
groups={MANY_SCREEN_GROUPS}
|
|
518
|
+
isHeadingSticky
|
|
519
|
+
size="sm"
|
|
520
|
+
/>
|
|
521
|
+
</section>
|
|
522
|
+
</StoryCell>
|
|
523
|
+
</StoryGrid>
|
|
524
|
+
),
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Light and dark side by side, each panel scoped with its own
|
|
529
|
+
* `data-scheme`, so both read at once whatever the toolbar says.
|
|
530
|
+
*/
|
|
531
|
+
export const ColorSchemes: Story = {
|
|
532
|
+
render: () => (
|
|
533
|
+
<StoryGrid columns={2}>
|
|
534
|
+
{(["light", "dark"] as const).map((scheme) => (
|
|
535
|
+
<StoryCell
|
|
536
|
+
align="stretch"
|
|
537
|
+
key={scheme}
|
|
538
|
+
label={scheme}
|
|
539
|
+
>
|
|
540
|
+
<div
|
|
541
|
+
className="rounded-lg bg-surface-base p-4 text-content-primary"
|
|
542
|
+
data-scheme={scheme}
|
|
543
|
+
>
|
|
544
|
+
<GroupedList
|
|
545
|
+
groups={SIGNAGE_GROUPS.map((group) => ({
|
|
546
|
+
...group,
|
|
547
|
+
items: group.items.map((item) => ({
|
|
548
|
+
...item,
|
|
549
|
+
actions: openButton(
|
|
550
|
+
item.label,
|
|
551
|
+
`${scheme} `,
|
|
552
|
+
),
|
|
553
|
+
})),
|
|
554
|
+
}))}
|
|
555
|
+
size="sm"
|
|
556
|
+
/>
|
|
557
|
+
</div>
|
|
558
|
+
</StoryCell>
|
|
559
|
+
))}
|
|
560
|
+
</StoryGrid>
|
|
561
|
+
),
|
|
562
|
+
}
|