@wallarm-org/design-system 3.4.1-rc-fix-data-display-category.3 → 4.0.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.
Files changed (40) hide show
  1. package/dist/components/Chat/Chat.js +20 -18
  2. package/dist/components/Chat/ChatTrigger.js +37 -33
  3. package/dist/components/DateInput/internal/DateInputInternal.js +8 -4
  4. package/dist/components/DateRangeInput/DateRangeInputInternal.js +8 -4
  5. package/dist/components/DateRangeInput/DateRangeSeparator.js +15 -6
  6. package/dist/components/FileUpload/FileUploadItemReplaceTrigger.js +14 -11
  7. package/dist/components/FilterCascade/FilterCascadeLevel.js +18 -10
  8. package/dist/components/FilterInput/FilterInput.d.ts +2 -1
  9. package/dist/components/FilterInput/FilterInput.js +160 -142
  10. package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.d.ts +2 -1
  11. package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.js +157 -138
  12. package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputRemoveButton.js +8 -4
  13. package/dist/components/FilterInput/FilterInputField/FilterInputChip/PairSeparator.js +20 -11
  14. package/dist/components/FilterInput/FilterInputField/FilterInputChip/Segment.js +4 -0
  15. package/dist/components/FilterInput/FilterInputField/FilterInputConnectorChip/FilterInputConnectorChip.js +98 -86
  16. package/dist/components/FilterInput/FilterInputField/FilterInputField.js +82 -70
  17. package/dist/components/FilterInput/FilterInputMenu/FilterInputFieldMenu/FilterInputFieldMenu.d.ts +2 -1
  18. package/dist/components/FilterInput/FilterInputMenu/FilterInputFieldMenu/FilterInputFieldMenu.js +5 -1
  19. package/dist/components/FilterInput/FilterInputMenu/FilterInputOperatorMenu.d.ts +2 -1
  20. package/dist/components/FilterInput/FilterInputMenu/FilterInputOperatorMenu.js +20 -14
  21. package/dist/components/NavRail/NavRailItem.js +57 -55
  22. package/dist/components/Select/SelectInput/SelectInputItemRenderer.js +20 -13
  23. package/dist/components/Selection/SelectionItem.js +8 -4
  24. package/dist/components/SimpleCharts/AreaChart/AreaChartArea.js +8 -4
  25. package/dist/components/SimpleCharts/BarList/BarListPercent.js +16 -13
  26. package/dist/components/SimpleCharts/LineChart/LineChartHoverPopover.js +8 -4
  27. package/dist/components/SimpleCharts/LineChart/LineChartLine.js +8 -4
  28. package/dist/components/SimpleCharts/LineChart/LineChartZoomBrush.js +3 -0
  29. package/dist/components/SimpleCharts/PieChart/PieChartDonut.js +33 -30
  30. package/dist/components/Tree/TreeItemContent.js +18 -12
  31. package/dist/components/Tree/TreeItemHeader.js +49 -38
  32. package/dist/components/TreeView/TreeViewItem.js +46 -40
  33. package/dist/metadata/components.json +68 -512
  34. package/package.json +1 -1
  35. package/dist/components/TreeBase/TreeBase.d.ts +0 -31
  36. package/dist/components/TreeBase/TreeBase.js +0 -245
  37. package/dist/components/TreeBase/classes.d.ts +0 -7
  38. package/dist/components/TreeBase/classes.js +0 -10
  39. package/dist/components/TreeBase/index.d.ts +0 -1
  40. package/dist/components/TreeBase/index.js +0 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "3.4.0",
3
- "generatedAt": "2026-10-22T11:24:18.052Z",
3
+ "generatedAt": "2026-10-22T11:42:57.295Z",
4
4
  "components": [
5
5
  {
6
6
  "name": "Accordion",
@@ -14367,12 +14367,12 @@
14367
14367
  },
14368
14368
  {
14369
14369
  "name": "ColorVariants",
14370
- "code": "() => (\n <table className='w-full'>\n <thead>\n <tr>\n {Object.keys(BadgeTypeEnum).map(type => (\n <th key={type} className='sb-annotation p-8 text-left'>\n {type}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {Object.entries(BadgeColorEnum).map(([label, color]) => (\n <tr key={color}>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge type={type} color={color}>\n {label}\n </Badge>\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n)",
14370
+ "code": "() => (\n <table className='w-full'>\n <thead>\n <tr>\n {Object.keys(BadgeTypeEnum).map(type => (\n <th key={type} className='sb-annotation p-8 text-left'>\n {type}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {Object.entries(BadgeColorEnum).map(([label, color]) => (\n <tr key={color}>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge data-testid='badge' type={type} color={color}>\n {label}\n </Badge>\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n)",
14371
14371
  "description": "The full palette. Every colour already means something in the system, so borrowing one for\ndecoration teaches the reader the wrong thing."
14372
14372
  },
14373
14373
  {
14374
14374
  "name": "ContentVariants",
14375
- "code": "() => (\n <table className='w-full'>\n <thead>\n <tr>\n {Object.keys(BadgeTypeEnum).map(type => (\n <th key={type} className='sb-annotation p-8 text-left'>\n {type}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {Object.entries(BadgeColorEnum).map(([label, color]) => (\n <Fragment key={color}>\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge type={type} color={color}>\n {label}\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge type={type} color={color}>\n {label}\n\n <X />\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge type={type} color={color}>\n <PenTool />\n {label}\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge type={type} color={color}>\n <PenTool />\n {label}\n <X />\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge type={type} color={color} variant='dotted'>\n {label}\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge type={type} color={color} variant='dotted'>\n {label}\n <X />\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge type={type} color={color}>\n {label}\n <Info />\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge type={type} color={color}>\n <PenTool />\n </Badge>\n </td>\n ))}\n </tr>\n\n {badgeColorsMuted.includes(color) && (\n <tr key={color}>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n {isMutedSupported(type, color) ? (\n <Badge type={type} color={color} muted>\n {capitalize(color)}\n </Badge>\n ) : null}\n </td>\n ))}\n </tr>\n )}\n </Fragment>\n ))}\n </tbody>\n </table>\n)",
14375
+ "code": "() => (\n <table className='w-full'>\n <thead>\n <tr>\n {Object.keys(BadgeTypeEnum).map(type => (\n <th key={type} className='sb-annotation p-8 text-left'>\n {type}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {Object.entries(BadgeColorEnum).map(([label, color]) => (\n <Fragment key={color}>\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge data-testid='badge' type={type} color={color}>\n {label}\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge data-testid='badge' type={type} color={color}>\n {label}\n\n <X />\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge data-testid='badge' type={type} color={color}>\n <PenTool />\n {label}\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge data-testid='badge' type={type} color={color}>\n <PenTool />\n {label}\n <X />\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge data-testid='badge' type={type} color={color} variant='dotted'>\n {label}\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge data-testid='badge' type={type} color={color} variant='dotted'>\n {label}\n <X />\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge data-testid='badge' type={type} color={color}>\n {label}\n <Info />\n </Badge>\n </td>\n ))}\n </tr>\n\n <tr>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n <Badge data-testid='badge' type={type} color={color}>\n <PenTool />\n </Badge>\n </td>\n ))}\n </tr>\n\n {badgeColorsMuted.includes(color) && (\n <tr key={color}>\n {Object.values(BadgeTypeEnum).map((type: BadgeType) => (\n <td key={type} className='p-8'>\n {isMutedSupported(type, color) ? (\n <Badge data-testid='badge' type={type} color={color} muted>\n {capitalize(color)}\n </Badge>\n ) : null}\n </td>\n ))}\n </tr>\n )}\n </Fragment>\n ))}\n </tbody>\n </table>\n)",
14376
14376
  "description": "What happens as the content grows. A badge does not truncate, so long text widens the row\nrather than being cut."
14377
14377
  }
14378
14378
  ]
@@ -22240,17 +22240,17 @@
22240
22240
  "examples": [
22241
22241
  {
22242
22242
  "name": "Basic",
22243
- "code": "({ ...args }) => {\n // `data-last-click` records which target handled the click. It renders nothing,\n // and it is what the click-isolation e2e asserts on — the story used to prove\n // the same thing by writing to the console.\n const [lastClick, setLastClick] = useState<'card' | 'button' | 'none'>('none');\n\n return (\n <Card\n onClick={e => {\n onCardClick(e);\n setLastClick('card');\n }}\n data-last-click={lastClick}\n {...args}\n >\n <CardHeader>\n <CardTitle>Card title</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n\n <CardContent>Card description</CardContent>\n\n <CardFooter>\n <Button\n variant='secondary'\n color='neutral'\n onClick={(e: MouseEvent<HTMLButtonElement>) => {\n onButtonClick(e);\n setLastClick('button');\n }}\n >\n Button\n </Button>\n </CardFooter>\n </Card>\n );\n}",
22243
+ "code": "({ ...args }) => {\n // `data-last-click` records which target handled the click. It renders nothing,\n // and it is what the click-isolation e2e asserts on — the story used to prove\n // the same thing by writing to the console.\n const [lastClick, setLastClick] = useState<'card' | 'button' | 'none'>('none');\n\n return (\n <Card\n data-testid='card'\n onClick={e => {\n onCardClick(e);\n setLastClick('card');\n }}\n data-last-click={lastClick}\n {...args}\n >\n <CardHeader>\n <CardTitle>Card title</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n\n <CardContent>Card description</CardContent>\n\n <CardFooter>\n <Button\n variant='secondary'\n color='neutral'\n onClick={(e: MouseEvent<HTMLButtonElement>) => {\n onButtonClick(e);\n setLastClick('button');\n }}\n >\n Button\n </Button>\n </CardFooter>\n </Card>\n );\n}",
22244
22244
  "description": "The full stack — `CardHeader` with a `CardTitle`, `CardContent`, and a `CardFooter`\nfor actions — carrying a handler, so the footer button and the card are two\nseparate targets."
22245
22245
  },
22246
22246
  {
22247
22247
  "name": "Variants",
22248
- "code": "() => (\n <VStack gap={24} align='center'>\n <HStack gap={16} align='center'>\n <Card color='primary'>\n <CardHeader>\n <CardTitle>Primary card</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>This is a primary card variant</CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n\n <Card color='secondary'>\n <CardHeader>\n <CardTitle>Secondary card</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>This is a secondary card variant</CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n </HStack>\n\n <HStack gap={16} align='center'>\n <Card color='primary' onClick={onCardClick}>\n <CardHeader>\n <CardTitle>Primary card</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>This is a primary card variant with onClick event</CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n\n <Card color='secondary' onClick={onCardClick}>\n <CardHeader>\n <CardTitle>Secondary card</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>This is a secondary card variant with onClick event</CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n </HStack>\n\n <HStack gap={16} align='center'>\n <Card color='primary' disabled onClick={onCardClick}>\n <CardHeader>\n <CardTitle>Primary card</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>This is a disabled primary card variant</CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n\n <Card color='secondary' disabled onClick={onCardClick}>\n <CardHeader>\n <CardTitle>Secondary card</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>This is a disabled secondary card variant</CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n </HStack>\n </VStack>\n)",
22248
+ "code": "() => (\n <VStack gap={24} align='center'>\n <HStack gap={16} align='center'>\n <Card color='primary'>\n <CardHeader>\n <CardTitle>Primary card</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>This is a primary card variant</CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n\n <Card color='secondary'>\n <CardHeader>\n <CardTitle>Secondary card</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>This is a secondary card variant</CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n </HStack>\n\n <HStack gap={16} align='center'>\n <Card color='primary' onClick={onCardClick}>\n <CardHeader>\n <CardTitle>Primary card</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>This is a primary card variant with onClick event</CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n\n <Card color='secondary' onClick={onCardClick}>\n <CardHeader>\n <CardTitle>Secondary card</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>This is a secondary card variant with onClick event</CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n </HStack>\n\n <HStack gap={16} align='center'>\n <Card data-testid='disabled-card' color='primary' disabled onClick={onCardClick}>\n <CardHeader>\n <CardTitle>Primary card</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>This is a disabled primary card variant</CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n\n <Card data-testid='disabled-card' color='secondary' disabled onClick={onCardClick}>\n <CardHeader>\n <CardTitle>Secondary card</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>This is a disabled secondary card variant</CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n </HStack>\n </VStack>\n)",
22249
22249
  "description": "`primary` sits on the page surface and `secondary` on a tinted fill, each shown\nplain, with a handler, and disabled — where the card fades and stops answering\nthe pointer."
22250
22250
  },
22251
22251
  {
22252
22252
  "name": "VariousContent",
22253
- "code": "() => (\n <HStack gap={16}>\n <VStack gap={16} align='stretch'>\n <Card>\n <CardHeader>\n <CardTitle>Card title</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>\n Card description Very long! Even if it goes to the second line, it won't break anything.\n It works perfectly! Can you see? Amazing yeah?\n </CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Card title</CardTitle>\n </CardHeader>\n <CardContent>\n <CodeSnippetRoot code='npx wasd-new@latest add code-snippet' language='bash'>\n <CodeSnippetActions>\n <CodeSnippetCopyButton />\n </CodeSnippetActions>\n <CodeSnippetContent>\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n </VStack>\n\n <Card>\n <CardHeader>\n <CardTitle>Card title</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>\n <Field>\n <FieldLabel>Label</FieldLabel>\n <Input placeholder='Placeholder' />\n <FieldDescription>Input description</FieldDescription>\n </Field>\n </CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n\n <VStack gap={16}>\n <Card>\n <CardHeader>\n <CardTitle icon={<CircleDashed />}>Card title</CardTitle>\n </CardHeader>\n <CardContent>Card description.</CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle icon={<CircleDashed />}>Card title</CardTitle>\n </CardHeader>\n </Card>\n </VStack>\n </HStack>\n)",
22253
+ "code": "() => (\n <HStack gap={16}>\n <VStack gap={16} align='stretch'>\n <Card data-testid='card'>\n <CardHeader>\n <CardTitle>Card title</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>\n Card description Very long! Even if it goes to the second line, it won't break anything.\n It works perfectly! Can you see? Amazing yeah?\n </CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Card title</CardTitle>\n </CardHeader>\n <CardContent>\n <CodeSnippetRoot code='npx wasd-new@latest add code-snippet' language='bash'>\n <CodeSnippetActions>\n <CodeSnippetCopyButton />\n </CodeSnippetActions>\n <CodeSnippetContent>\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n </VStack>\n\n <Card>\n <CardHeader>\n <CardTitle>Card title</CardTitle>\n <Badge size='medium' type='solid' color='slate'>\n Badge\n </Badge>\n </CardHeader>\n <CardContent>\n <Field>\n <FieldLabel>Label</FieldLabel>\n <Input placeholder='Placeholder' />\n <FieldDescription>Input description</FieldDescription>\n </Field>\n </CardContent>\n <CardFooter>\n <Button variant='secondary' size='small' color='neutral'>\n Button\n </Button>\n </CardFooter>\n </Card>\n\n <VStack gap={16}>\n <Card>\n <CardHeader>\n <CardTitle icon={<CircleDashed />}>Card title</CardTitle>\n </CardHeader>\n <CardContent>Card description.</CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle icon={<CircleDashed />}>Card title</CardTitle>\n </CardHeader>\n </Card>\n </VStack>\n </HStack>\n)",
22254
22254
  "description": "What the block tolerates: body copy that wraps, a `CodeSnippet`, a whole `Field`,\na `CardTitle` with an `icon`, and a header standing on its own with nothing under it."
22255
22255
  }
22256
22256
  ]
@@ -35356,7 +35356,7 @@
35356
35356
  },
35357
35357
  {
35358
35358
  "name": "ShowMore",
35359
- "code": "() => (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '24px', width: '500px' }}>\n <div>\n <p className='sb-annotation mb-8'>12 lines, clipped at 7 — 5 hidden</p>\n <CodeSnippetRoot code={showMoreCode} language='text' maxLines={7}>\n <CodeSnippetContent>\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n <div>\n <p className='sb-annotation mb-8'>9 lines, clipped at 7 — 2 hidden, no button</p>\n <CodeSnippetRoot code={showMoreThresholdCode} language='text' maxLines={7}>\n <CodeSnippetContent>\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n <div>\n <p className='sb-annotation mb-8'>the button rendered by hand</p>\n <CodeSnippetRoot code={showMoreCode} language='text' maxLines={7}>\n <CodeSnippetContent>\n <CodeSnippetCode />\n </CodeSnippetContent>\n <CodeSnippetShowMoreButton data-analytics-id='CODE_SHOW_MORE' />\n </CodeSnippetRoot>\n </div>\n </div>\n)",
35359
+ "code": "() => (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '24px', width: '500px' }}>\n <div>\n <p className='sb-annotation mb-8'>12 lines, clipped at 7 — 5 hidden</p>\n <CodeSnippetRoot\n code={showMoreCode}\n language='text'\n maxLines={7}\n data-testid='show-more-clipped'\n >\n <CodeSnippetContent>\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n <div>\n <p className='sb-annotation mb-8'>9 lines, clipped at 7 — 2 hidden, no button</p>\n <CodeSnippetRoot\n code={showMoreThresholdCode}\n language='text'\n maxLines={7}\n data-testid='show-more-threshold'\n >\n <CodeSnippetContent>\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n <div>\n <p className='sb-annotation mb-8'>the button rendered by hand</p>\n <CodeSnippetRoot code={showMoreCode} language='text' maxLines={7}>\n <CodeSnippetContent>\n <CodeSnippetCode />\n </CodeSnippetContent>\n <CodeSnippetShowMoreButton data-analytics-id='CODE_SHOW_MORE' />\n </CodeSnippetRoot>\n </div>\n </div>\n)",
35360
35360
  "description": "`maxLines` clips the block and adds the control itself — but only when it would hide at\nleast three lines, so the middle snippet stays open; the third passes props to the real\nbutton by rendering `CodeSnippetShowMoreButton` in place of the automatic one."
35361
35361
  },
35362
35362
  {
@@ -39361,7 +39361,7 @@
39361
39361
  },
39362
39362
  {
39363
39363
  "name": "ReadOnly",
39364
- "code": "() => (\n <HStack gap={24}>\n <VStack gap={12}>\n <span className='sb-annotation'>Date</span>\n <DateInput readOnly defaultValue={sampleDate} />\n </VStack>\n <VStack gap={12}>\n <span className='sb-annotation'>Date + time</span>\n <DateInput readOnly granularity='minute' defaultValue={sampleDateTime} />\n </VStack>\n </HStack>\n)",
39364
+ "code": "() => (\n <HStack gap={24}>\n <VStack gap={12}>\n <span className='sb-annotation'>Date</span>\n <DateInput readOnly defaultValue={sampleDate} data-testid='date-input-read-only' />\n </VStack>\n <VStack gap={12}>\n <span className='sb-annotation'>Date + time</span>\n <DateInput\n readOnly\n granularity='minute'\n defaultValue={sampleDateTime}\n data-testid='date-input-read-only-datetime'\n />\n </VStack>\n </HStack>\n)",
39365
39365
  "description": "`readOnly` keeps the value legible and removes every edit affordance. Different from\ndisabled, which says the control exists but is unavailable."
39366
39366
  }
39367
39367
  ]
@@ -40287,7 +40287,7 @@
40287
40287
  },
40288
40288
  {
40289
40289
  "name": "Filled",
40290
- "code": "() => (\n <VStack gap={16}>\n <VStack gap={4}>\n <span className='sb-annotation'>Date range</span>\n <DateRangeInput defaultValue={sampleRange} />\n </VStack>\n <DateFormatProvider order='day-first' hourCycle={24}>\n <VStack gap={4}>\n <span className='sb-annotation'>Date + time range (24h)</span>\n <DateRangeInput granularity='minute' defaultValue={sampleRangeDateTime} />\n </VStack>\n </DateFormatProvider>\n <VStack gap={4}>\n <span className='sb-annotation'>Without icon</span>\n <DateRangeInput showIcon={false} defaultValue={sampleRange} />\n </VStack>\n </VStack>\n)",
40290
+ "code": "() => (\n <VStack gap={16}>\n <VStack gap={4}>\n <span className='sb-annotation'>Date range</span>\n <DateRangeInput defaultValue={sampleRange} data-testid='date-range-input-filled' />\n </VStack>\n <DateFormatProvider order='day-first' hourCycle={24}>\n <VStack gap={4}>\n <span className='sb-annotation'>Date + time range (24h)</span>\n <DateRangeInput granularity='minute' defaultValue={sampleRangeDateTime} />\n </VStack>\n </DateFormatProvider>\n <VStack gap={4}>\n <span className='sb-annotation'>Without icon</span>\n <DateRangeInput showIcon={false} defaultValue={sampleRange} />\n </VStack>\n </VStack>\n)",
40291
40291
  "description": "A complete range beside an empty one, which is the clearest way to see how much width the\nfilled state needs."
40292
40292
  },
40293
40293
  {
@@ -40307,7 +40307,7 @@
40307
40307
  },
40308
40308
  {
40309
40309
  "name": "ReadOnly",
40310
- "code": "() => (\n <HStack gap={24}>\n <VStack gap={12}>\n <span className='sb-annotation'>Date range</span>\n <DateRangeInput readOnly defaultValue={sampleRange} />\n </VStack>\n <VStack gap={12}>\n <span className='sb-annotation'>Date + time range</span>\n <DateRangeInput readOnly granularity='minute' defaultValue={sampleRangeDateTime} />\n </VStack>\n </HStack>\n)",
40310
+ "code": "() => (\n <HStack gap={24}>\n <VStack gap={12}>\n <span className='sb-annotation'>Date range</span>\n <DateRangeInput\n readOnly\n defaultValue={sampleRange}\n data-testid='date-range-input-read-only'\n />\n </VStack>\n <VStack gap={12}>\n <span className='sb-annotation'>Date + time range</span>\n <DateRangeInput\n readOnly\n granularity='minute'\n defaultValue={sampleRangeDateTime}\n data-testid='date-range-input-read-only-datetime'\n />\n </VStack>\n </HStack>\n)",
40311
40311
  "description": "Both ends legible, no edit affordances."
40312
40312
  }
40313
40313
  ]
@@ -41258,7 +41258,7 @@
41258
41258
  },
41259
41259
  {
41260
41260
  "name": "ResizableWithOverflowList",
41261
- "code": "() => {\n const [open, setOpen] = useState(false);\n\n return (\n <Drawer width={480} open={open} onOpenChange={setOpen}>\n <DrawerTrigger asChild>\n <Button variant='outline' color='neutral'>\n Open Resizable Drawer with OverflowList\n </Button>\n </DrawerTrigger>\n <DrawerContent>\n <DrawerResizeHandle />\n <DrawerHeader>\n <DrawerTitle>Resizable Drawer with OverflowList</DrawerTitle>\n </DrawerHeader>\n <DrawerBody>\n <p className='mb-16'>Drag the left edge — the tag list reflows live.</p>\n <Attribute>\n <AttributeLabel>Attack types</AttributeLabel>\n <AttributeValue>\n <OverflowList\n className='gap-4'\n items={DRAWER_TAGS}\n itemRenderer={item => <Tag key={item}>{item}</Tag>}\n />\n </AttributeValue>\n </Attribute>\n </DrawerBody>\n </DrawerContent>\n </Drawer>\n );\n}",
41261
+ "code": "() => {\n const [open, setOpen] = useState(false);\n\n return (\n <Drawer data-testid='drawer' width={480} open={open} onOpenChange={setOpen}>\n <DrawerTrigger asChild>\n <Button variant='outline' color='neutral'>\n Open Resizable Drawer with OverflowList\n </Button>\n </DrawerTrigger>\n <DrawerContent>\n <DrawerResizeHandle />\n <DrawerHeader>\n <DrawerTitle>Resizable Drawer with OverflowList</DrawerTitle>\n </DrawerHeader>\n <DrawerBody>\n <p className='mb-16'>Drag the left edge — the tag list reflows live.</p>\n <Attribute>\n <AttributeLabel>Attack types</AttributeLabel>\n <AttributeValue>\n <OverflowList\n data-testid='tags-overflow-list'\n className='gap-4'\n items={DRAWER_TAGS}\n itemRenderer={item => (\n <Tag key={item} data-testid='tag'>\n {item}\n </Tag>\n )}\n />\n </AttributeValue>\n </Attribute>\n </DrawerBody>\n </DrawerContent>\n </Drawer>\n );\n}",
41262
41262
  "description": "Resizing with content that reflows, which is the case worth testing: the panel is only useful\nat its new width if what is inside adapts."
41263
41263
  },
41264
41264
  {
@@ -57183,6 +57183,11 @@
57183
57183
  "type": "string | undefined",
57184
57184
  "required": false,
57185
57185
  "description": "@see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/part}"
57186
+ },
57187
+ {
57188
+ "name": "data-testid",
57189
+ "type": "string | undefined",
57190
+ "required": false
57186
57191
  }
57187
57192
  ],
57188
57193
  "variants": [],
@@ -57555,6 +57560,11 @@
57555
57560
  "type": "string | undefined",
57556
57561
  "required": false,
57557
57562
  "description": "@see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/part}"
57563
+ },
57564
+ {
57565
+ "name": "data-testid",
57566
+ "type": "string | undefined",
57567
+ "required": false
57558
57568
  }
57559
57569
  ]
57560
57570
  },
@@ -57638,6 +57648,11 @@
57638
57648
  "type": "RefObject<HTMLDivElement | null> | undefined",
57639
57649
  "required": false,
57640
57650
  "description": "Ref to the menu content (shared across menus for focus management)."
57651
+ },
57652
+ {
57653
+ "name": "data-testid",
57654
+ "type": "string | undefined",
57655
+ "required": false
57641
57656
  }
57642
57657
  ]
57643
57658
  },
@@ -57705,6 +57720,11 @@
57705
57720
  "type": "RefObject<HTMLDivElement | null> | undefined",
57706
57721
  "required": false,
57707
57722
  "description": "Ref to the menu content element — shared across menus for focus management"
57723
+ },
57724
+ {
57725
+ "name": "data-testid",
57726
+ "type": "string | undefined",
57727
+ "required": false
57708
57728
  }
57709
57729
  ]
57710
57730
  },
@@ -63009,7 +63029,7 @@
63009
63029
  "examples": [
63010
63030
  {
63011
63031
  "name": "Basic",
63012
- "code": "() => (\n <VStack gap={12}>\n <InputGroup data-testid='input-group'>\n <InputGroupAddon>\n <Search />\n </InputGroupAddon>\n <Input placeholder='Enter' />\n <InputGroupAddon align='inline-end'>\n <Info />\n </InputGroupAddon>\n </InputGroup>\n\n <InputGroup>\n <InputGroupAddon variant='outline'>\n <InputGroupText>https://</InputGroupText>\n </InputGroupAddon>\n <Input placeholder='Enter' value='wallarm/user-settings' />\n <InputGroupAddon variant='outline' align='inline-end'>\n <InputGroupText>.com</InputGroupText>\n </InputGroupAddon>\n </InputGroup>\n </VStack>\n)",
63032
+ "code": "() => (\n <VStack gap={12}>\n <InputGroup data-testid='input-group'>\n <InputGroupAddon>\n <Search />\n </InputGroupAddon>\n <Input placeholder='Enter' data-testid='input-group-input' />\n <InputGroupAddon align='inline-end'>\n <Info />\n </InputGroupAddon>\n </InputGroup>\n\n <InputGroup>\n <InputGroupAddon variant='outline'>\n <InputGroupText>https://</InputGroupText>\n </InputGroupAddon>\n <Input placeholder='Enter' value='wallarm/user-settings' />\n <InputGroupAddon variant='outline' align='inline-end'>\n <InputGroupText>.com</InputGroupText>\n </InputGroupAddon>\n </InputGroup>\n </VStack>\n)",
63013
63033
  "description": "`InputGroupAddon` on either side of the control. The group draws the border, so the input\ninside it draws none — which is why an `Input` used here looks different from one used alone."
63014
63034
  },
63015
63035
  {
@@ -64412,7 +64432,7 @@
64412
64432
  },
64413
64433
  {
64414
64434
  "name": "VerticalMultiple",
64415
- "code": "() => (\n <VStack gap={12}>\n <IpList>\n <Ip>\n <IpCountry code='US' />\n <IpAddress>8.8.8.8</IpAddress>\n </Ip>\n <Ip>\n <IpCountry code='AU' />\n <IpAddress>1.1.1.1</IpAddress>\n </Ip>\n <Ip>\n <IpCountry code='DE' />\n <IpAddress>77.88.8.8</IpAddress>\n </Ip>\n </IpList>\n <IpList>\n <Ip>\n <IpCountry code='NL' />\n <IpAddress>52.94.76.0</IpAddress>\n </Ip>\n <Ip>\n <IpCountry code='NL' />\n <IpAddress>20.33.0.0</IpAddress>\n </Ip>\n <Ip>\n <IpCountry code='AZ' />\n <IpAddress>35.190.0.0</IpAddress>\n <IpProvider>GCP</IpProvider>\n </Ip>\n <Ip>\n <IpCountry code='US' />\n <IpAddress>169.55.0.0</IpAddress>\n <IpProvider>IBM Cloud</IpProvider>\n </Ip>\n <Ip>\n <IpAddress>88.198.0.0</IpAddress>\n <IpPort>:8080</IpPort>\n </Ip>\n </IpList>\n <IpList>\n <Ip>\n <IpAddress>10.0.0.1</IpAddress>\n <IpPort>:8080</IpPort>\n <IpProvider>Tor</IpProvider>\n </Ip>\n <Ip>\n <IpAddress>10.0.0.2</IpAddress>\n <IpPort>:443</IpPort>\n <IpProvider>VPN</IpProvider>\n </Ip>\n </IpList>\n </VStack>\n)",
64435
+ "code": "() => (\n <VStack gap={12}>\n <IpList>\n <Ip>\n <IpCountry code='US' />\n <IpAddress>8.8.8.8</IpAddress>\n </Ip>\n <Ip>\n <IpCountry code='AU' />\n <IpAddress>1.1.1.1</IpAddress>\n </Ip>\n <Ip>\n <IpCountry code='DE' />\n <IpAddress>77.88.8.8</IpAddress>\n </Ip>\n </IpList>\n <IpList>\n <Ip data-testid='ip'>\n <IpCountry code='NL' />\n <IpAddress>52.94.76.0</IpAddress>\n </Ip>\n <Ip data-testid='ip'>\n <IpCountry code='NL' />\n <IpAddress>20.33.0.0</IpAddress>\n </Ip>\n <Ip data-testid='ip'>\n <IpCountry code='AZ' />\n <IpAddress>35.190.0.0</IpAddress>\n <IpProvider>GCP</IpProvider>\n </Ip>\n <Ip data-testid='ip'>\n <IpCountry code='US' />\n <IpAddress>169.55.0.0</IpAddress>\n <IpProvider>IBM Cloud</IpProvider>\n </Ip>\n <Ip data-testid='ip'>\n <IpAddress>88.198.0.0</IpAddress>\n <IpPort>:8080</IpPort>\n </Ip>\n </IpList>\n <IpList>\n <Ip>\n <IpAddress>10.0.0.1</IpAddress>\n <IpPort>:8080</IpPort>\n <IpProvider>Tor</IpProvider>\n </Ip>\n <Ip>\n <IpAddress>10.0.0.2</IpAddress>\n <IpPort>:443</IpPort>\n <IpProvider>VPN</IpProvider>\n </Ip>\n </IpList>\n </VStack>\n)",
64416
64436
  "description": "Three vertical lists, each showing its first address only and holding the rest behind a\n`+N addresses` link — the link indents to the address when the row opens with a flag."
64417
64437
  }
64418
64438
  ]
@@ -74085,7 +74105,7 @@
74085
74105
  },
74086
74106
  {
74087
74107
  "name": "Content",
74088
- "code": "() => (\n <VStack gap={16} align='start'>\n {(['default', 'small'] as const).map(size => (\n <HStack key={size} gap={12} align='center'>\n <span className='sb-annotation w-96 text-right'>{size}</span>\n <NumericBadge size={size}>1</NumericBadge>\n <NumericBadge size={size}>99</NumericBadge>\n <NumericBadge size={size} color='danger' aria-label='Needs attention'>\n !\n </NumericBadge>\n <NumericBadge size={size} color='info' aria-label='Unknown'>\n ?\n </NumericBadge>\n <NumericBadge size={size} color='success' aria-label='Completed'>\n <Check />\n </NumericBadge>\n </HStack>\n ))}\n </VStack>\n)",
74108
+ "code": "() => (\n <VStack gap={16} align='start'>\n {(['default', 'small'] as const).map(size => (\n <HStack key={size} gap={12} align='center'>\n <span className='sb-annotation w-96 text-right'>{size}</span>\n <NumericBadge data-testid='numeric-badge' size={size}>\n 1\n </NumericBadge>\n <NumericBadge data-testid='numeric-badge' size={size}>\n 99\n </NumericBadge>\n <NumericBadge\n data-testid='numeric-badge'\n size={size}\n color='danger'\n aria-label='Needs attention'\n >\n !\n </NumericBadge>\n <NumericBadge data-testid='numeric-badge' size={size} color='info' aria-label='Unknown'>\n ?\n </NumericBadge>\n <NumericBadge\n data-testid='numeric-badge'\n size={size}\n color='success'\n aria-label='Completed'\n >\n <Check />\n </NumericBadge>\n </HStack>\n ))}\n </VStack>\n)",
74089
74109
  "description": "Numbers, short glyphs, and icons share the same badge heights."
74090
74110
  },
74091
74111
  {
@@ -80189,7 +80209,7 @@
80189
80209
  "examples": [
80190
80210
  {
80191
80211
  "name": "FullPath",
80192
- "code": "() => (\n <ParameterPath method='POST' segments={['JSON', 'nginx_config']} encoding='BASE64' attack />\n)",
80212
+ "code": "() => (\n <ParameterPath\n data-testid='parameter-path'\n method='POST'\n segments={['JSON', 'nginx_config']}\n encoding='BASE64'\n attack\n />\n)",
80193
80213
  "description": "Every part at once: a method, two segments, an encoding, and the `attack` bolt on the\nparameter that was hit."
80194
80214
  },
80195
80215
  {
@@ -80214,7 +80234,7 @@
80214
80234
  },
80215
80235
  {
80216
80236
  "name": "DeepNestedTruncated",
80217
- "code": "() => (\n <div style={{ width: 280 }}>\n <ParameterPath\n method='POST'\n segments={['multipart', 'json_abc', 'json_doc', 'qwerty_doc', 'hash', 'formData', 'get']}\n attack\n />\n </div>\n)",
80237
+ "code": "() => (\n <div style={{ width: 280 }}>\n <ParameterPath\n data-testid='parameter-path'\n method='POST'\n segments={['multipart', 'json_abc', 'json_doc', 'qwerty_doc', 'hash', 'formData', 'get']}\n attack\n />\n </div>\n)",
80218
80238
  "description": "Seven segments in 280px: the middle collapses to an ellipsis and the full path moves into\na tooltip. The truncation is measured, not a CSS clip, so it reacts to the real width."
80219
80239
  },
80220
80240
  {
@@ -80224,12 +80244,12 @@
80224
80244
  },
80225
80245
  {
80226
80246
  "name": "ExpandableTruncated",
80227
- "code": "() => (\n <div style={{ width: 600 }}>\n {/* The root is w-full (it must span the available space to measure it),\n so centering happens inside the component, not on the container. */}\n <ParameterPath\n className='justify-center'\n method='POST'\n segments={[\n 'multipart',\n 'json_abc',\n 'json_doc',\n 'qwerty_doc',\n 'hash',\n 'formData',\n 'session_token',\n 'nested_payload',\n 'get',\n ]}\n attack\n expandable\n />\n </div>\n)",
80247
+ "code": "() => (\n <div style={{ width: 600 }}>\n {/* The root is w-full (it must span the available space to measure it),\n so centering happens inside the component, not on the container. */}\n <ParameterPath\n data-testid='parameter-path'\n className='justify-center'\n method='POST'\n segments={[\n 'multipart',\n 'json_abc',\n 'json_doc',\n 'qwerty_doc',\n 'hash',\n 'formData',\n 'session_token',\n 'nested_payload',\n 'get',\n ]}\n attack\n expandable\n />\n </div>\n)",
80228
80248
  "description": "`expandable` makes a truncated path clickable and focusable: it opens inline to show every\nsegment, and the tooltip stands down while it is open."
80229
80249
  },
80230
80250
  {
80231
80251
  "name": "ExpandableFitsInline",
80232
- "code": "() => (\n <div style={{ width: 720, display: 'flex', justifyContent: 'center' }}>\n <ParameterPath\n method='POST'\n segments={['multipart', 'json_abc', 'json_doc', 'qwerty_doc', 'hash', 'formData', 'get']}\n attack\n expandable\n />\n </div>\n)",
80252
+ "code": "() => (\n <div style={{ width: 720, display: 'flex', justifyContent: 'center' }}>\n <ParameterPath\n data-testid='parameter-path'\n method='POST'\n segments={['multipart', 'json_abc', 'json_doc', 'qwerty_doc', 'hash', 'formData', 'get']}\n attack\n expandable\n />\n </div>\n)",
80233
80253
  "description": "The same path given room to fit. `expandable` leaves no affordance behind when there is\nnothing to expand — no ellipsis, no cursor, no focus stop."
80234
80254
  },
80235
80255
  {
@@ -93812,47 +93832,47 @@
93812
93832
  "examples": [
93813
93833
  {
93814
93834
  "name": "Default",
93815
- "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection items={clusters} getItemId={c => c.id} value={selected} onChange={setSelected}>\n <VStack gap={12}>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n\n <SelectionBulkBar>\n <Button variant='ghost' color='neutral-alt' onClick={() => onDuplicate(selected)}>\n <Copy /> Duplicate\n </Button>\n <Button color='brand' onClick={() => onDelete(selected)}>\n <Trash2 /> Delete\n </Button>\n </SelectionBulkBar>\n </Selection>\n );\n}",
93835
+ "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection\n data-testid='selection'\n items={clusters}\n getItemId={c => c.id}\n value={selected}\n onChange={setSelected}\n >\n <VStack gap={12}>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n\n <SelectionBulkBar>\n <Button variant='ghost' color='neutral-alt' onClick={() => onDuplicate(selected)}>\n <Copy /> Duplicate\n </Button>\n <Button color='brand' onClick={() => onDelete(selected)}>\n <Trash2 /> Delete\n </Button>\n </SelectionBulkBar>\n </Selection>\n );\n}",
93816
93836
  "description": "The shape at its smallest: `Selection` around the collection, a `SelectionItem` per card,\nand a `SelectionBulkBar` that stays out of the way until something is selected."
93817
93837
  },
93818
93838
  {
93819
93839
  "name": "WithSelectAll",
93820
- "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection items={clusters} getItemId={c => c.id} value={selected} onChange={setSelected}>\n <VStack gap={16}>\n <HStack gap={8} align='center'>\n <SelectionAll />\n <Text size='sm' color='secondary'>\n Select all\n </Text>\n </HStack>\n\n <VStack gap={12}>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n </VStack>\n\n <SelectionBulkBar>\n <Button color='brand' onClick={() => onDelete(selected)}>\n <Trash2 /> Delete\n </Button>\n </SelectionBulkBar>\n </Selection>\n );\n}",
93840
+ "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection\n data-testid='selection'\n items={clusters}\n getItemId={c => c.id}\n value={selected}\n onChange={setSelected}\n >\n <VStack gap={16}>\n <HStack gap={8} align='center'>\n <SelectionAll />\n <Text size='sm' color='secondary'>\n Select all\n </Text>\n </HStack>\n\n <VStack gap={12}>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n </VStack>\n\n <SelectionBulkBar>\n <Button color='brand' onClick={() => onDelete(selected)}>\n <Trash2 /> Delete\n </Button>\n </SelectionBulkBar>\n </Selection>\n );\n}",
93821
93841
  "description": "`SelectionAll` is the header checkbox — indeterminate while the selection is partial, and\na press while everything is selected clears it rather than reselecting."
93822
93842
  },
93823
93843
  {
93824
93844
  "name": "Grid",
93825
- "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection items={clusters} getItemId={c => c.id} value={selected} onChange={setSelected}>\n <div className='grid grid-cols-3 gap-12'>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </div>\n\n <SelectionBulkBar>\n <Button onClick={() => onInspect(selected)}>Inspect</Button>\n </SelectionBulkBar>\n </Selection>\n );\n}",
93845
+ "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection\n data-testid='selection'\n items={clusters}\n getItemId={c => c.id}\n value={selected}\n onChange={setSelected}\n >\n <div className='grid grid-cols-3 gap-12'>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </div>\n\n <SelectionBulkBar>\n <Button onClick={() => onInspect(selected)}>Inspect</Button>\n </SelectionBulkBar>\n </Selection>\n );\n}",
93826
93846
  "description": "The same machinery over a three-column grid: `Selection` lays nothing out, so the\narrangement stays entirely yours."
93827
93847
  },
93828
93848
  {
93829
93849
  "name": "WithDisabled",
93830
- "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection items={clusters} getItemId={c => c.id} value={selected} onChange={setSelected}>\n <VStack gap={8}>\n <HStack gap={8} align='center'>\n <SelectionAll />\n <Text size='sm' color='secondary'>\n Select all (locked items skipped)\n </Text>\n </HStack>\n\n <VStack gap={12}>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id} disabled={c.locked}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n </VStack>\n\n <SelectionBulkBar>\n <Button color='brand' onClick={() => onDelete(selected)}>\n <Trash2 /> Delete\n </Button>\n </SelectionBulkBar>\n </Selection>\n );\n}",
93850
+ "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection\n data-testid='selection'\n items={clusters}\n getItemId={c => c.id}\n value={selected}\n onChange={setSelected}\n >\n <VStack gap={8}>\n <HStack gap={8} align='center'>\n <SelectionAll />\n <Text size='sm' color='secondary'>\n Select all (locked items skipped)\n </Text>\n </HStack>\n\n <VStack gap={12}>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id} disabled={c.locked}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n </VStack>\n\n <SelectionBulkBar>\n <Button color='brand' onClick={() => onDelete(selected)}>\n <Trash2 /> Delete\n </Button>\n </SelectionBulkBar>\n </Selection>\n );\n}",
93831
93851
  "description": "A `disabled` item leaves both select-all and any shift range, so \"all\" means all the ones\nthis user is allowed to touch — and the header checkbox counts only those."
93832
93852
  },
93833
93853
  {
93834
93854
  "name": "RangeSelection",
93835
- "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection items={clusters} getItemId={c => c.id} value={selected} onChange={setSelected}>\n <VStack gap={12}>\n <span className='sb-annotation'>hold shift and click another checkbox</span>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n\n <SelectionBulkBar>\n <Button color='brand' onClick={() => onConfirm(selected)}>\n Confirm\n </Button>\n </SelectionBulkBar>\n </Selection>\n );\n}",
93855
+ "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection\n data-testid='selection'\n items={clusters}\n getItemId={c => c.id}\n value={selected}\n onChange={setSelected}\n >\n <VStack gap={12}>\n <span className='sb-annotation'>hold shift and click another checkbox</span>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n\n <SelectionBulkBar>\n <Button color='brand' onClick={() => onConfirm(selected)}>\n Confirm\n </Button>\n </SelectionBulkBar>\n </Selection>\n );\n}",
93836
93856
  "description": "Shift-click selects everything between the last checkbox you touched and this one,\nfollowing the order of `items` rather than the order you clicked in."
93837
93857
  },
93838
93858
  {
93839
93859
  "name": "BulkActions",
93840
- "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection items={clusters} getItemId={c => c.id} value={selected} onChange={setSelected}>\n <VStack gap={12}>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n\n <SelectionBulkBar>\n <Button variant='ghost' color='neutral-alt' onClick={() => onMove(selected)}>\n <Folder /> Move\n </Button>\n <Button variant='ghost' color='neutral-alt' onClick={() => onDuplicate(selected)}>\n <Copy /> Duplicate\n </Button>\n <Button color='brand' onClick={() => onDelete(selected)}>\n <Trash2 /> Delete\n </Button>\n </SelectionBulkBar>\n </Selection>\n );\n}",
93860
+ "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection\n data-testid='selection'\n items={clusters}\n getItemId={c => c.id}\n value={selected}\n onChange={setSelected}\n >\n <VStack gap={12}>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n\n <SelectionBulkBar>\n <Button variant='ghost' color='neutral-alt' onClick={() => onMove(selected)}>\n <Folder /> Move\n </Button>\n <Button variant='ghost' color='neutral-alt' onClick={() => onDuplicate(selected)}>\n <Copy /> Duplicate\n </Button>\n <Button color='brand' onClick={() => onDelete(selected)}>\n <Trash2 /> Delete\n </Button>\n </SelectionBulkBar>\n </Selection>\n );\n}",
93841
93861
  "description": "Three actions in one bar. Only the destructive one is solid, so a bar with several verbs\nstill has a single obvious primary."
93842
93862
  },
93843
93863
  {
93844
93864
  "name": "EmptyAndPartial",
93845
- "code": "() => {\n const [items, setItems] = useState<Cluster[]>(clusters);\n const [selected, setSelected] = useState<string[]>(['1', 'ghost']);\n\n // Demonstrate cleanup pattern: drop ids that no longer exist in items.\n useEffect(() => {\n setSelected(prev => prev.filter(id => items.some(c => c.id === id)));\n }, [items]);\n\n return (\n <VStack gap={16}>\n <HStack gap={8}>\n <Button onClick={() => setItems([])} variant='outline'>\n Clear items\n </Button>\n <Button onClick={() => setItems(clusters)} variant='outline'>\n Restore items\n </Button>\n </HStack>\n\n <Selection items={items} getItemId={c => c.id} value={selected} onChange={setSelected}>\n <VStack gap={12}>\n {items.length === 0 ? (\n <Text size='sm' color='secondary'>\n No items.\n </Text>\n ) : (\n items.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))\n )}\n </VStack>\n\n <SelectionBulkBar>\n <Button color='brand' onClick={() => onDelete(selected)}>\n <Trash2 /> Delete\n </Button>\n </SelectionBulkBar>\n </Selection>\n </VStack>\n );\n}",
93865
+ "code": "() => {\n const [items, setItems] = useState<Cluster[]>(clusters);\n const [selected, setSelected] = useState<string[]>(['1', 'ghost']);\n\n // Demonstrate cleanup pattern: drop ids that no longer exist in items.\n useEffect(() => {\n setSelected(prev => prev.filter(id => items.some(c => c.id === id)));\n }, [items]);\n\n return (\n <VStack gap={16}>\n <HStack gap={8}>\n <Button onClick={() => setItems([])} variant='outline'>\n Clear items\n </Button>\n <Button onClick={() => setItems(clusters)} variant='outline'>\n Restore items\n </Button>\n </HStack>\n\n <Selection\n data-testid='selection'\n items={items}\n getItemId={c => c.id}\n value={selected}\n onChange={setSelected}\n >\n <VStack gap={12}>\n {items.length === 0 ? (\n <Text size='sm' color='secondary'>\n No items.\n </Text>\n ) : (\n items.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))\n )}\n </VStack>\n\n <SelectionBulkBar>\n <Button color='brand' onClick={() => onDelete(selected)}>\n <Trash2 /> Delete\n </Button>\n </SelectionBulkBar>\n </Selection>\n </VStack>\n );\n}",
93846
93866
  "description": "Emptying the list strands the ids that were selected, because the component keeps them on\npurpose — the effect here is the cleanup pattern, dropping any id the list no longer has."
93847
93867
  },
93848
93868
  {
93849
93869
  "name": "WithoutBulkBar",
93850
- "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection items={clusters} getItemId={c => c.id} value={selected} onChange={setSelected}>\n <VStack gap={16}>\n <Text size='sm' color='secondary'>\n Selected: {selected.length}\n </Text>\n\n <VStack gap={12}>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n </VStack>\n </Selection>\n );\n}",
93870
+ "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Selection\n data-testid='selection'\n items={clusters}\n getItemId={c => c.id}\n value={selected}\n onChange={setSelected}\n >\n <VStack gap={16}>\n <Text size='sm' color='secondary'>\n Selected: {selected.length}\n </Text>\n\n <VStack gap={12}>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n </VStack>\n </Selection>\n );\n}",
93851
93871
  "description": "No bar at all: the selection still works and the count is rendered from state, for a page\nthat acts on the selection somewhere else entirely."
93852
93872
  },
93853
93873
  {
93854
93874
  "name": "CompoundBulkBar",
93855
- "code": "() => {\n const [items, setItems] = useState<Cluster[]>(clusters);\n const [selected, setSelected] = useState<string[]>([]);\n\n // Delete actually deletes here: a bulk action that only logged left nothing to\n // observe, and the row leaving is the honest demonstration of one.\n const deleteSelected = (ids: string[]) => {\n setItems(prev => prev.filter(c => !ids.includes(c.id)));\n setSelected([]);\n };\n\n return (\n <Selection items={items} getItemId={c => c.id} value={selected} onChange={setSelected}>\n <VStack gap={12}>\n {items.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n\n <CompoundToolbar onDeleteSelected={deleteSelected} />\n </Selection>\n );\n}",
93875
+ "code": "() => {\n const [items, setItems] = useState<Cluster[]>(clusters);\n const [selected, setSelected] = useState<string[]>([]);\n\n // Delete actually deletes here: a bulk action that only logged left nothing to\n // observe, and the row leaving is the honest demonstration of one.\n const deleteSelected = (ids: string[]) => {\n setItems(prev => prev.filter(c => !ids.includes(c.id)));\n setSelected([]);\n };\n\n return (\n <Selection\n data-testid='selection'\n items={items}\n getItemId={c => c.id}\n value={selected}\n onChange={setSelected}\n >\n <VStack gap={12}>\n {items.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n\n <CompoundToolbar onDeleteSelected={deleteSelected} />\n </Selection>\n );\n}",
93856
93876
  "description": "Composing the summary — count, select-all, clear — instead of taking the default. It has\nto be a direct child of the bar; a wrapper component hides it and the default appears too."
93857
93877
  },
93858
93878
  {
@@ -108653,12 +108673,12 @@
108653
108673
  },
108654
108674
  {
108655
108675
  "name": "WithInput",
108656
- "code": "args => (\n <Slider {...args}>\n <SliderControl>\n <SliderThumb aria-label='Value' />\n </SliderControl>\n <SliderInput />\n </Slider>\n)",
108676
+ "code": "args => (\n <Slider {...args} data-testid='slider-with-input'>\n <SliderControl>\n <SliderThumb aria-label='Value' />\n </SliderControl>\n <SliderInput />\n </Slider>\n)",
108657
108677
  "description": "`SliderInput` is the precision escape hatch: the track for approximate moves, the field for\nan exact figure. Typing commits on blur or Enter rather than live, so clamping never fights a\nhalf-typed number."
108658
108678
  },
108659
108679
  {
108660
108680
  "name": "RangeWithInput",
108661
- "code": "args => (\n <Slider {...args} defaultValue={[20, 80]}>\n <SliderInput index={0} />\n <SliderControl>\n <SliderThumb index={0} />\n <SliderThumb index={1} />\n </SliderControl>\n <SliderInput index={1} />\n </Slider>\n)",
108681
+ "code": "args => (\n <Slider {...args} defaultValue={[20, 80]} data-testid='slider-range-with-input'>\n <SliderInput index={0} />\n <SliderControl>\n <SliderThumb index={0} />\n <SliderThumb index={1} />\n </SliderControl>\n <SliderInput index={1} />\n </Slider>\n)",
108662
108682
  "description": "A field at each end, low on the left and high on the right. Neither crosses the other, so the\nfields cannot produce a range the track could not."
108663
108683
  },
108664
108684
  {
@@ -121538,7 +121558,7 @@
121538
121558
  },
121539
121559
  {
121540
121560
  "name": "ColumnResizingWithOverflowList",
121541
- "code": "() => {\n const [columnSizing, setColumnSizing] = useState<TableColumnSizingState>({});\n\n const columns = useMemo<TableColumnDef<SecurityEvent>[]>(\n () => [\n ...securityColumns.slice(0, 1),\n securityColumnHelper.display({\n id: 'tags',\n header: 'Tags',\n size: 240,\n cell: ({ row }) => (\n <OverflowList\n className='gap-4'\n items={row.original.tags}\n itemRenderer={(item: string) => <Tag key={item}>{item}</Tag>}\n />\n ),\n }),\n ...securityColumns.slice(1),\n ],\n [],\n );\n\n return (\n <Table\n data={securityEvents}\n columns={columns}\n getRowId={row => row.id}\n columnSizing={columnSizing}\n onColumnSizingChange={setColumnSizing}\n />\n );\n}",
121561
+ "code": "() => {\n const [columnSizing, setColumnSizing] = useState<TableColumnSizingState>({});\n\n const columns = useMemo<TableColumnDef<SecurityEvent>[]>(\n () => [\n ...securityColumns.slice(0, 1),\n securityColumnHelper.display({\n id: 'tags',\n header: 'Tags',\n size: 240,\n cell: ({ row }) => (\n <OverflowList\n data-testid='tags-overflow-list'\n className='gap-4'\n items={row.original.tags}\n itemRenderer={(item: string) => (\n <Tag key={item} data-testid='tag'>\n {item}\n </Tag>\n )}\n />\n ),\n }),\n ...securityColumns.slice(1),\n ],\n [],\n );\n\n return (\n <Table\n data={securityEvents}\n columns={columns}\n getRowId={row => row.id}\n columnSizing={columnSizing}\n onColumnSizingChange={setColumnSizing}\n data-testid='resizing-overflow-table'\n />\n );\n}",
121542
121562
  "description": "A resizable column whose cell holds an `OverflowList`: the tags re-measure as the column\nnarrows instead of being clipped by it."
121543
121563
  },
121544
121564
  {
@@ -121728,7 +121748,7 @@
121728
121748
  },
121729
121749
  {
121730
121750
  "name": "InlineCellEditing",
121731
- "code": "() => {\n const [data, setData] = useState<SecurityEvent[]>(() => securityEvents.slice(0, 6));\n // Category starts unselected for every row, so the select shows its placeholder.\n const [categories, setCategories] = useState<Record<string, string>>({});\n\n const updateCell = useCallback(\n <K extends keyof SecurityEvent>(rowId: string, key: K, value: SecurityEvent[K]) => {\n setData(prev => prev.map(row => (row.id === rowId ? { ...row, [key]: value } : row)));\n },\n [],\n );\n\n const columns = useMemo<TableColumnDef<SecurityEvent>[]>(\n () => [\n // The first column is the Table's pinned \"master\" column — keep it\n // read-only; editable cells live in the plain columns after it.\n securityColumnHelper.accessor('sourceIp', {\n header: 'Source',\n size: 200,\n meta: { resizeType: 'cut' as const },\n cell: ({ getValue }) => <Text size='sm'>{getValue()}</Text>,\n }),\n securityColumnHelper.accessor('objectName', {\n header: 'Object name',\n size: 400,\n meta: EDITABLE_CELL_COLUMN_META,\n cell: ({ row, getValue }) => (\n <EditableTextCell\n value={getValue()}\n aria-label='Edit object name'\n onCommit={value => updateCell(row.id, 'objectName', value)}\n />\n ),\n }),\n securityColumnHelper.accessor('status', {\n header: 'Status',\n size: 200,\n meta: EDITABLE_CELL_COLUMN_META,\n cell: ({ row, getValue }) => (\n <EditableSelectCell\n value={getValue()}\n items={statusItems}\n onCommit={value => updateCell(row.id, 'status', value as SecurityEvent['status'])}\n >\n <StatusBadge value={getValue()} />\n </EditableSelectCell>\n ),\n }),\n // Empty-by-default select: consumer supplies the `placeholder` text.\n securityColumnHelper.display({\n id: 'category',\n header: 'Category',\n size: 200,\n meta: EDITABLE_CELL_COLUMN_META,\n cell: ({ row }) => {\n const value = categories[row.id] ?? '';\n return (\n <EditableSelectCell\n value={value}\n items={categoryItems}\n placeholder='Select'\n aria-label='Edit category'\n onCommit={next => setCategories(prev => ({ ...prev, [row.id]: next }))}\n >\n {categoryItems.find(item => item.value === value)?.label}\n </EditableSelectCell>\n );\n },\n }),\n securityColumnHelper.accessor('parameter', {\n header: 'Parameters',\n size: 240,\n meta: { resizeType: 'cut' as const },\n cell: ({ getValue }) => <InlineCodeSnippet code={getValue()} size='sm' copyable={false} />,\n }),\n ],\n [updateCell, categories],\n );\n\n return (\n <VStack gap={12} align='stretch'>\n <span className='sb-annotation'>click a cell to edit it</span>\n <Table data={data} columns={columns} getRowId={row => row.id} />\n </VStack>\n );\n}"
121751
+ "code": "() => {\n const [data, setData] = useState<SecurityEvent[]>(() => securityEvents.slice(0, 6));\n // Category starts unselected for every row, so the select shows its placeholder.\n const [categories, setCategories] = useState<Record<string, string>>({});\n\n const updateCell = useCallback(\n <K extends keyof SecurityEvent>(rowId: string, key: K, value: SecurityEvent[K]) => {\n setData(prev => prev.map(row => (row.id === rowId ? { ...row, [key]: value } : row)));\n },\n [],\n );\n\n const columns = useMemo<TableColumnDef<SecurityEvent>[]>(\n () => [\n // The first column is the Table's pinned \"master\" column — keep it\n // read-only; editable cells live in the plain columns after it.\n securityColumnHelper.accessor('sourceIp', {\n header: 'Source',\n size: 200,\n meta: { resizeType: 'cut' as const },\n cell: ({ getValue }) => <Text size='sm'>{getValue()}</Text>,\n }),\n securityColumnHelper.accessor('objectName', {\n header: 'Object name',\n size: 400,\n meta: EDITABLE_CELL_COLUMN_META,\n cell: ({ row, getValue }) => (\n <EditableTextCell\n data-testid='editable-text-cell'\n value={getValue()}\n aria-label='Edit object name'\n onCommit={value => updateCell(row.id, 'objectName', value)}\n />\n ),\n }),\n securityColumnHelper.accessor('status', {\n header: 'Status',\n size: 200,\n meta: EDITABLE_CELL_COLUMN_META,\n cell: ({ row, getValue }) => (\n <EditableSelectCell\n data-testid='editable-select-cell'\n value={getValue()}\n items={statusItems}\n onCommit={value => updateCell(row.id, 'status', value as SecurityEvent['status'])}\n >\n <StatusBadge value={getValue()} />\n </EditableSelectCell>\n ),\n }),\n // Empty-by-default select: consumer supplies the `placeholder` text.\n securityColumnHelper.display({\n id: 'category',\n header: 'Category',\n size: 200,\n meta: EDITABLE_CELL_COLUMN_META,\n cell: ({ row }) => {\n const value = categories[row.id] ?? '';\n return (\n <EditableSelectCell\n value={value}\n items={categoryItems}\n placeholder='Select'\n aria-label='Edit category'\n onCommit={next => setCategories(prev => ({ ...prev, [row.id]: next }))}\n >\n {categoryItems.find(item => item.value === value)?.label}\n </EditableSelectCell>\n );\n },\n }),\n securityColumnHelper.accessor('parameter', {\n header: 'Parameters',\n size: 240,\n meta: { resizeType: 'cut' as const },\n cell: ({ getValue }) => <InlineCodeSnippet code={getValue()} size='sm' copyable={false} />,\n }),\n ],\n [updateCell, categories],\n );\n\n return (\n <VStack gap={12} align='stretch'>\n <span className='sb-annotation'>click a cell to edit it</span>\n <Table data={data} columns={columns} getRowId={row => row.id} />\n </VStack>\n );\n}"
121732
121752
  },
121733
121753
  {
121734
121754
  "name": "StickyGroupParentStory",
@@ -123803,7 +123823,7 @@
123803
123823
  },
123804
123824
  {
123805
123825
  "name": "ReadOnly",
123806
- "code": "() => (\n <HStack gap={24}>\n <VStack gap={12}>\n <span className='sb-annotation'>Locale</span>\n <TimeInput readOnly defaultValue={new Time(22, 0)} />\n </VStack>\n <DateFormatProvider order='day-first' hourCycle={12}>\n <VStack gap={12}>\n <span className='sb-annotation'>12-hour</span>\n <TimeInput readOnly defaultValue={new Time(22, 0)} />\n </VStack>\n </DateFormatProvider>\n <DateFormatProvider order='day-first' hourCycle={24}>\n <VStack gap={12}>\n <span className='sb-annotation'>24-hour</span>\n <TimeInput readOnly defaultValue={new Time(22, 0)} />\n </VStack>\n </DateFormatProvider>\n </HStack>\n)",
123826
+ "code": "() => (\n <HStack gap={24}>\n <VStack gap={12}>\n <span className='sb-annotation'>Locale</span>\n <TimeInput readOnly defaultValue={new Time(22, 0)} data-testid='time-input-read-only' />\n </VStack>\n <DateFormatProvider order='day-first' hourCycle={12}>\n <VStack gap={12}>\n <span className='sb-annotation'>12-hour</span>\n <TimeInput readOnly defaultValue={new Time(22, 0)} data-testid='time-input-read-only-12h' />\n </VStack>\n </DateFormatProvider>\n <DateFormatProvider order='day-first' hourCycle={24}>\n <VStack gap={12}>\n <span className='sb-annotation'>24-hour</span>\n <TimeInput readOnly defaultValue={new Time(22, 0)} data-testid='time-input-read-only-24h' />\n </VStack>\n </DateFormatProvider>\n </HStack>\n)",
123807
123827
  "description": "Value legible, every edit affordance gone, as on `DateInput`."
123808
123828
  }
123809
123829
  ]
@@ -126135,7 +126155,7 @@
126135
126155
  "examples": [
126136
126156
  {
126137
126157
  "name": "Basic",
126138
- "code": "() => (\n <Timeline>\n <TimelineItem>\n <TimelineConnector>\n <TimelineIndicator>1</TimelineIndicator>\n <TimelineSeparator />\n </TimelineConnector>\n </TimelineItem>\n <TimelineItem>\n <TimelineConnector>\n <TimelineIndicator>2</TimelineIndicator>\n <TimelineSeparator />\n </TimelineConnector>\n </TimelineItem>\n <TimelineItem>\n <TimelineConnector>\n <TimelineIndicator>3</TimelineIndicator>\n <TimelineSeparator />\n </TimelineConnector>\n </TimelineItem>\n <TimelineItem>\n <TimelineConnector>\n <TimelineIndicator>4</TimelineIndicator>\n <TimelineSeparator />\n </TimelineConnector>\n </TimelineItem>\n <TimelineItem>\n <TimelineConnector>\n <TimelineIndicator>5</TimelineIndicator>\n <TimelineSeparator />\n </TimelineConnector>\n </TimelineItem>\n </Timeline>\n)",
126158
+ "code": "() => (\n <Timeline data-testid='timeline'>\n <TimelineItem>\n <TimelineConnector>\n <TimelineIndicator>1</TimelineIndicator>\n <TimelineSeparator />\n </TimelineConnector>\n </TimelineItem>\n <TimelineItem>\n <TimelineConnector>\n <TimelineIndicator>2</TimelineIndicator>\n <TimelineSeparator />\n </TimelineConnector>\n </TimelineItem>\n <TimelineItem>\n <TimelineConnector>\n <TimelineIndicator>3</TimelineIndicator>\n <TimelineSeparator />\n </TimelineConnector>\n </TimelineItem>\n <TimelineItem>\n <TimelineConnector>\n <TimelineIndicator>4</TimelineIndicator>\n <TimelineSeparator />\n </TimelineConnector>\n </TimelineItem>\n <TimelineItem>\n <TimelineConnector>\n <TimelineIndicator>5</TimelineIndicator>\n <TimelineSeparator />\n </TimelineConnector>\n </TimelineItem>\n </Timeline>\n)",
126139
126159
  "description": "The rail on its own — numbered indicators with the line between them, and no line trailing\noff the bottom of the last one."
126140
126160
  },
126141
126161
  {
@@ -129869,470 +129889,6 @@
129869
129889
  }
129870
129890
  ]
129871
129891
  },
129872
- {
129873
- "name": "TreeBase",
129874
- "importPath": "@wallarm-org/design-system/TreeBase",
129875
- "props": [],
129876
- "variants": [],
129877
- "subComponents": [
129878
- {
129879
- "name": "TreeBaseRoot",
129880
- "props": [
129881
- {
129882
- "name": "slot",
129883
- "type": "string | undefined",
129884
- "required": false
129885
- },
129886
- {
129887
- "name": "title",
129888
- "type": "string | undefined",
129889
- "required": false
129890
- },
129891
- {
129892
- "name": "defaultChecked",
129893
- "type": "boolean | undefined",
129894
- "required": false
129895
- },
129896
- {
129897
- "name": "defaultValue",
129898
- "type": "string | number | readonly string[] | undefined",
129899
- "required": false
129900
- },
129901
- {
129902
- "name": "suppressContentEditableWarning",
129903
- "type": "boolean | undefined",
129904
- "required": false
129905
- },
129906
- {
129907
- "name": "suppressHydrationWarning",
129908
- "type": "boolean | undefined",
129909
- "required": false
129910
- },
129911
- {
129912
- "name": "accessKey",
129913
- "type": "string | undefined",
129914
- "required": false
129915
- },
129916
- {
129917
- "name": "autoCapitalize",
129918
- "type": "\"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\" | (string & {}) | undefined",
129919
- "required": false
129920
- },
129921
- {
129922
- "name": "autoFocus",
129923
- "type": "boolean | undefined",
129924
- "required": false
129925
- },
129926
- {
129927
- "name": "contentEditable",
129928
- "type": "Booleanish | \"inherit\" | \"plaintext-only\" | undefined",
129929
- "required": false
129930
- },
129931
- {
129932
- "name": "contextMenu",
129933
- "type": "string | undefined",
129934
- "required": false
129935
- },
129936
- {
129937
- "name": "dir",
129938
- "type": "string | undefined",
129939
- "required": false
129940
- },
129941
- {
129942
- "name": "draggable",
129943
- "type": "Booleanish | undefined",
129944
- "required": false
129945
- },
129946
- {
129947
- "name": "enterKeyHint",
129948
- "type": "\"search\" | \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"send\" | undefined",
129949
- "required": false
129950
- },
129951
- {
129952
- "name": "hidden",
129953
- "type": "boolean | undefined",
129954
- "required": false
129955
- },
129956
- {
129957
- "name": "id",
129958
- "type": "string | undefined",
129959
- "required": false
129960
- },
129961
- {
129962
- "name": "lang",
129963
- "type": "string | undefined",
129964
- "required": false
129965
- },
129966
- {
129967
- "name": "nonce",
129968
- "type": "string | undefined",
129969
- "required": false
129970
- },
129971
- {
129972
- "name": "spellCheck",
129973
- "type": "Booleanish | undefined",
129974
- "required": false
129975
- },
129976
- {
129977
- "name": "tabIndex",
129978
- "type": "number | undefined",
129979
- "required": false
129980
- },
129981
- {
129982
- "name": "translate",
129983
- "type": "\"yes\" | \"no\" | undefined",
129984
- "required": false
129985
- },
129986
- {
129987
- "name": "radioGroup",
129988
- "type": "string | undefined",
129989
- "required": false
129990
- },
129991
- {
129992
- "name": "role",
129993
- "type": "AriaRole | undefined",
129994
- "required": false
129995
- },
129996
- {
129997
- "name": "about",
129998
- "type": "string | undefined",
129999
- "required": false
130000
- },
130001
- {
130002
- "name": "content",
130003
- "type": "string | undefined",
130004
- "required": false
130005
- },
130006
- {
130007
- "name": "datatype",
130008
- "type": "string | undefined",
130009
- "required": false
130010
- },
130011
- {
130012
- "name": "inlist",
130013
- "type": "any",
130014
- "required": false
130015
- },
130016
- {
130017
- "name": "prefix",
130018
- "type": "string | undefined",
130019
- "required": false
130020
- },
130021
- {
130022
- "name": "property",
130023
- "type": "string | undefined",
130024
- "required": false
130025
- },
130026
- {
130027
- "name": "rel",
130028
- "type": "string | undefined",
130029
- "required": false
130030
- },
130031
- {
130032
- "name": "resource",
130033
- "type": "string | undefined",
130034
- "required": false
130035
- },
130036
- {
130037
- "name": "rev",
130038
- "type": "string | undefined",
130039
- "required": false
130040
- },
130041
- {
130042
- "name": "typeof",
130043
- "type": "string | undefined",
130044
- "required": false
130045
- },
130046
- {
130047
- "name": "vocab",
130048
- "type": "string | undefined",
130049
- "required": false
130050
- },
130051
- {
130052
- "name": "autoCorrect",
130053
- "type": "string | undefined",
130054
- "required": false
130055
- },
130056
- {
130057
- "name": "autoSave",
130058
- "type": "string | undefined",
130059
- "required": false
130060
- },
130061
- {
130062
- "name": "color",
130063
- "type": "string | undefined",
130064
- "required": false
130065
- },
130066
- {
130067
- "name": "itemProp",
130068
- "type": "string | undefined",
130069
- "required": false
130070
- },
130071
- {
130072
- "name": "itemScope",
130073
- "type": "boolean | undefined",
130074
- "required": false
130075
- },
130076
- {
130077
- "name": "itemType",
130078
- "type": "string | undefined",
130079
- "required": false
130080
- },
130081
- {
130082
- "name": "itemID",
130083
- "type": "string | undefined",
130084
- "required": false
130085
- },
130086
- {
130087
- "name": "itemRef",
130088
- "type": "string | undefined",
130089
- "required": false
130090
- },
130091
- {
130092
- "name": "results",
130093
- "type": "number | undefined",
130094
- "required": false
130095
- },
130096
- {
130097
- "name": "security",
130098
- "type": "string | undefined",
130099
- "required": false
130100
- },
130101
- {
130102
- "name": "unselectable",
130103
- "type": "\"off\" | \"on\" | undefined",
130104
- "required": false
130105
- },
130106
- {
130107
- "name": "popover",
130108
- "type": "\"\" | \"auto\" | \"manual\" | \"hint\" | undefined",
130109
- "required": false
130110
- },
130111
- {
130112
- "name": "popoverTargetAction",
130113
- "type": "\"toggle\" | \"show\" | \"hide\" | undefined",
130114
- "required": false
130115
- },
130116
- {
130117
- "name": "popoverTarget",
130118
- "type": "string | undefined",
130119
- "required": false
130120
- },
130121
- {
130122
- "name": "inert",
130123
- "type": "boolean | undefined",
130124
- "required": false,
130125
- "description": "@see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert}"
130126
- },
130127
- {
130128
- "name": "inputMode",
130129
- "type": "\"search\" | \"text\" | \"none\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined",
130130
- "required": false,
130131
- "description": "Hints at the type of data that might be entered by the user while editing the element or its contents"
130132
- },
130133
- {
130134
- "name": "is",
130135
- "type": "string | undefined",
130136
- "required": false,
130137
- "description": "Specify that a standard HTML element should behave like a defined custom built-in element"
130138
- },
130139
- {
130140
- "name": "exportparts",
130141
- "type": "string | undefined",
130142
- "required": false,
130143
- "description": "@see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/exportparts}"
130144
- },
130145
- {
130146
- "name": "part",
130147
- "type": "string | undefined",
130148
- "required": false,
130149
- "description": "@see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/part}"
130150
- },
130151
- {
130152
- "name": "collection",
130153
- "type": "TreeCollection<T>",
130154
- "required": true,
130155
- "description": "The collection of tree nodes"
130156
- },
130157
- {
130158
- "name": "translations",
130159
- "type": "IntlTranslations | undefined",
130160
- "required": false,
130161
- "description": "Specifies the localized strings that identifies the accessibility elements and their states"
130162
- },
130163
- {
130164
- "name": "ids",
130165
- "type": "ElementIds | undefined",
130166
- "required": false,
130167
- "description": "The ids of the tree elements. Useful for composition."
130168
- },
130169
- {
130170
- "name": "expandedValue",
130171
- "type": "string[] | undefined",
130172
- "required": false,
130173
- "description": "The controlled expanded node ids"
130174
- },
130175
- {
130176
- "name": "defaultExpandedValue",
130177
- "type": "string[] | undefined",
130178
- "required": false,
130179
- "description": "The initial expanded node ids when rendered.\nUse when you don't need to control the expanded node value."
130180
- },
130181
- {
130182
- "name": "selectedValue",
130183
- "type": "string[] | undefined",
130184
- "required": false,
130185
- "description": "The controlled selected node value"
130186
- },
130187
- {
130188
- "name": "defaultSelectedValue",
130189
- "type": "string[] | undefined",
130190
- "required": false,
130191
- "description": "The initial selected node value when rendered.\nUse when you don't need to control the selected node value."
130192
- },
130193
- {
130194
- "name": "defaultCheckedValue",
130195
- "type": "string[] | undefined",
130196
- "required": false,
130197
- "description": "The initial checked node value when rendered.\nUse when you don't need to control the checked node value."
130198
- },
130199
- {
130200
- "name": "checkedValue",
130201
- "type": "string[] | undefined",
130202
- "required": false,
130203
- "description": "The controlled checked node value"
130204
- },
130205
- {
130206
- "name": "defaultFocusedValue",
130207
- "type": "string | null | undefined",
130208
- "required": false,
130209
- "description": "The initial focused node value when rendered.\nUse when you don't need to control the focused node value."
130210
- },
130211
- {
130212
- "name": "focusedValue",
130213
- "type": "string | null | undefined",
130214
- "required": false,
130215
- "description": "The value of the focused node"
130216
- },
130217
- {
130218
- "name": "selectionMode",
130219
- "type": "\"single\" | \"multiple\" | undefined",
130220
- "required": false,
130221
- "description": "Whether the tree supports multiple selection\n- \"single\": only one node can be selected\n- \"multiple\": multiple nodes can be selected"
130222
- },
130223
- {
130224
- "name": "onExpandedChange",
130225
- "type": "complex",
130226
- "required": false,
130227
- "description": "Called when the tree is opened or closed"
130228
- },
130229
- {
130230
- "name": "onSelectionChange",
130231
- "type": "complex",
130232
- "required": false,
130233
- "description": "Called when the selection changes"
130234
- },
130235
- {
130236
- "name": "onFocusChange",
130237
- "type": "complex",
130238
- "required": false,
130239
- "description": "Called when the focused node changes"
130240
- },
130241
- {
130242
- "name": "onCheckedChange",
130243
- "type": "complex",
130244
- "required": false,
130245
- "description": "Called when the checked value changes"
130246
- },
130247
- {
130248
- "name": "canRename",
130249
- "type": "((node: T, indexPath: IndexPath) => boolean) | undefined",
130250
- "required": false,
130251
- "description": "Function to determine if a node can be renamed"
130252
- },
130253
- {
130254
- "name": "onRenameStart",
130255
- "type": "complex",
130256
- "required": false,
130257
- "description": "Called when a node starts being renamed"
130258
- },
130259
- {
130260
- "name": "onBeforeRename",
130261
- "type": "complex",
130262
- "required": false,
130263
- "description": "Called before a rename is completed. Return false to prevent the rename."
130264
- },
130265
- {
130266
- "name": "onRenameComplete",
130267
- "type": "complex",
130268
- "required": false,
130269
- "description": "Called when a node label rename is completed"
130270
- },
130271
- {
130272
- "name": "onLoadChildrenComplete",
130273
- "type": "complex",
130274
- "required": false,
130275
- "description": "Called when a node finishes loading children"
130276
- },
130277
- {
130278
- "name": "onLoadChildrenError",
130279
- "type": "complex",
130280
- "required": false,
130281
- "description": "Called when loading children fails for one or more nodes"
130282
- },
130283
- {
130284
- "name": "expandOnClick",
130285
- "type": "boolean | undefined",
130286
- "required": false,
130287
- "description": "Whether clicking on a branch should open it or not"
130288
- },
130289
- {
130290
- "name": "typeahead",
130291
- "type": "boolean | undefined",
130292
- "required": false,
130293
- "description": "Whether the tree supports typeahead search"
130294
- },
130295
- {
130296
- "name": "loadChildren",
130297
- "type": "complex",
130298
- "required": false,
130299
- "description": "Function to load children for a node asynchronously.\nWhen provided, branches will wait for this promise to resolve before expanding."
130300
- },
130301
- {
130302
- "name": "scrollToIndexFn",
130303
- "type": "complex",
130304
- "required": false,
130305
- "description": "Function to scroll to a specific index.\nUseful for virtualized tree views."
130306
- },
130307
- {
130308
- "name": "lazyMount",
130309
- "type": "boolean | undefined",
130310
- "required": false,
130311
- "description": "Whether to enable lazy mounting"
130312
- },
130313
- {
130314
- "name": "unmountOnExit",
130315
- "type": "boolean | undefined",
130316
- "required": false,
130317
- "description": "Whether to unmount on exit."
130318
- },
130319
- {
130320
- "name": "hideMode",
130321
- "type": "\"display-none\" | \"activity\"",
130322
- "required": false,
130323
- "description": "How to hide content when mounted but not present.\n- `'display-none'`: HTML `hidden` attribute. Effects stay alive.\n- `'activity'`: React 19 `<Activity mode=\"hidden\">`. Effects pause. Requires React 19+."
130324
- },
130325
- {
130326
- "name": "asChild",
130327
- "type": "boolean | undefined",
130328
- "required": false,
130329
- "description": "Use the provided child element as the default rendered element, combining their props and behavior."
130330
- }
130331
- ]
130332
- }
130333
- ],
130334
- "examples": []
130335
- },
130336
129892
  {
130337
129893
  "name": "TreeView",
130338
129894
  "description": "Horizontal indentation added per nesting level, in pixels.",
@@ -130427,7 +129983,7 @@
130427
129983
  },
130428
129984
  {
130429
129985
  "name": "enterKeyHint",
130430
- "type": "\"search\" | \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"send\" | undefined",
129986
+ "type": "\"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\" | undefined",
130431
129987
  "required": false
130432
129988
  },
130433
129989
  {
@@ -130618,7 +130174,7 @@
130618
130174
  },
130619
130175
  {
130620
130176
  "name": "inputMode",
130621
- "type": "\"search\" | \"text\" | \"none\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined",
130177
+ "type": "\"none\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined",
130622
130178
  "required": false,
130623
130179
  "description": "Hints at the type of data that might be entered by the user while editing the element or its contents"
130624
130180
  },
@@ -130719,16 +130275,6 @@
130719
130275
  "description": "Dim the row and disable all interaction (toggle, selection, checkbox).",
130720
130276
  "defaultValue": "false"
130721
130277
  },
130722
- {
130723
- "name": "slot",
130724
- "type": "string | undefined",
130725
- "required": false
130726
- },
130727
- {
130728
- "name": "title",
130729
- "type": "string | undefined",
130730
- "required": false
130731
- },
130732
130278
  {
130733
130279
  "name": "defaultChecked",
130734
130280
  "type": "boolean | undefined",
@@ -130786,7 +130332,7 @@
130786
130332
  },
130787
130333
  {
130788
130334
  "name": "enterKeyHint",
130789
- "type": "\"search\" | \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"send\" | undefined",
130335
+ "type": "\"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\" | undefined",
130790
130336
  "required": false
130791
130337
  },
130792
130338
  {
@@ -130804,6 +130350,11 @@
130804
130350
  "type": "string | undefined",
130805
130351
  "required": false
130806
130352
  },
130353
+ {
130354
+ "name": "slot",
130355
+ "type": "string | undefined",
130356
+ "required": false
130357
+ },
130807
130358
  {
130808
130359
  "name": "spellCheck",
130809
130360
  "type": "Booleanish | undefined",
@@ -130814,6 +130365,11 @@
130814
130365
  "type": "number | undefined",
130815
130366
  "required": false
130816
130367
  },
130368
+ {
130369
+ "name": "title",
130370
+ "type": "string | undefined",
130371
+ "required": false
130372
+ },
130817
130373
  {
130818
130374
  "name": "translate",
130819
130375
  "type": "\"yes\" | \"no\" | undefined",
@@ -130962,7 +130518,7 @@
130962
130518
  },
130963
130519
  {
130964
130520
  "name": "inputMode",
130965
- "type": "\"search\" | \"text\" | \"none\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined",
130521
+ "type": "\"none\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined",
130966
130522
  "required": false,
130967
130523
  "description": "Hints at the type of data that might be entered by the user while editing the element or its contents"
130968
130524
  },
@@ -131025,7 +130581,7 @@
131025
130581
  },
131026
130582
  {
131027
130583
  "name": "WithHeaderAndSearch",
131028
- "code": "() => {\n const [query, setQuery] = useState('');\n const q = query.trim().toLowerCase();\n const [openIds, setOpenIds] = useState<Set<string>>(() => new Set(['src', 'components']));\n\n const isOpen = (id: string) => (q ? true : openIds.has(id));\n const toggle = (id: string, v: boolean) =>\n setOpenIds(prev => {\n const next = new Set(prev);\n if (v) {\n next.add(id);\n } else {\n next.delete(id);\n }\n return next;\n });\n const setAll = (open: boolean) => setOpenIds(open ? new Set(BRANCH_IDS) : new Set());\n\n const renderNode = (node: SearchNode): ReactNode => {\n const selfMatch = !q || node.label.toLowerCase().includes(q);\n const icon = node.type === 'folder' ? <Folder /> : <FileText />;\n\n if (node.children) {\n const kids = node.children.map(renderNode).filter(Boolean);\n if (q && !selfMatch && kids.length === 0) {\n return null;\n }\n return (\n <TreeViewItem key={node.id} open={isOpen(node.id)} onOpenChange={v => toggle(node.id, v)}>\n {icon}\n {node.label}\n {kids}\n </TreeViewItem>\n );\n }\n\n if (!selfMatch) {\n return null;\n }\n return (\n <TreeViewItem key={node.id}>\n {icon}\n {node.label}\n </TreeViewItem>\n );\n };\n\n const nodes = TREE_DATA.map(renderNode).filter(Boolean);\n\n return (\n <div className='w-320'>\n <TreeView>\n {/* Header — expand/collapse buttons grouped tightly, close set apart */}\n <div className='flex items-center gap-8 px-8 pt-8 pb-4'>\n <Text size='xs' weight='medium' color='secondary' grow truncate>\n Header\n </Text>\n <div className='flex shrink-0 items-center gap-8'>\n <div className='flex items-center gap-2'>\n <Button\n variant='ghost'\n color='neutral'\n size='small'\n aria-label='Collapse all'\n onClick={() => setAll(false)}\n >\n <ChevronsDownUp size='sm' />\n </Button>\n <Button\n variant='ghost'\n color='neutral'\n size='small'\n aria-label='Expand all'\n onClick={() => setAll(true)}\n >\n <ChevronsUpDown size='sm' />\n </Button>\n </div>\n <Button variant='ghost' color='neutral' size='small' aria-label='Close panel'>\n <PanelRightClose size='sm' />\n </Button>\n </div>\n </div>\n\n {/* Search — full width, aligned with the items' selection highlight */}\n <div className='pb-4'>\n <div className='relative'>\n <Search\n size='md'\n className='pointer-events-none absolute top-1/2 left-12 -translate-y-1/2 text-text-secondary'\n />\n <Input\n placeholder='Search'\n value={query}\n onChange={e => setQuery(e.target.value)}\n className='pl-40'\n />\n </div>\n </div>\n\n {nodes}\n </TreeView>\n </div>\n );\n}",
130584
+ "code": "() => {\n const [query, setQuery] = useState('');\n const q = query.trim().toLowerCase();\n const [openIds, setOpenIds] = useState<Set<string>>(() => new Set(['src', 'components']));\n\n const isOpen = (id: string) => (q ? true : openIds.has(id));\n const toggle = (id: string, v: boolean) =>\n setOpenIds(prev => {\n const next = new Set(prev);\n if (v) {\n next.add(id);\n } else {\n next.delete(id);\n }\n return next;\n });\n const setAll = (open: boolean) => setOpenIds(open ? new Set(BRANCH_IDS) : new Set());\n\n const renderNode = (node: SearchNode): ReactNode => {\n const selfMatch = !q || node.label.toLowerCase().includes(q);\n const icon = node.type === 'folder' ? <Folder /> : <FileText />;\n\n if (node.children) {\n const kids = node.children.map(renderNode).filter(Boolean);\n if (q && !selfMatch && kids.length === 0) {\n return null;\n }\n return (\n <TreeViewItem key={node.id} open={isOpen(node.id)} onOpenChange={v => toggle(node.id, v)}>\n {icon}\n {node.label}\n {kids}\n </TreeViewItem>\n );\n }\n\n if (!selfMatch) {\n return null;\n }\n return (\n <TreeViewItem key={node.id}>\n {icon}\n {node.label}\n </TreeViewItem>\n );\n };\n\n const nodes = TREE_DATA.map(renderNode).filter(Boolean);\n\n return (\n <div className='w-320'>\n <TreeView data-testid='tree-view'>\n {/* Header — expand/collapse buttons grouped tightly, close set apart */}\n <div className='flex items-center gap-8 px-8 pt-8 pb-4'>\n <Text size='xs' weight='medium' color='secondary' grow truncate>\n Header\n </Text>\n <div className='flex shrink-0 items-center gap-8'>\n <div className='flex items-center gap-2'>\n <Button\n variant='ghost'\n color='neutral'\n size='small'\n aria-label='Collapse all'\n onClick={() => setAll(false)}\n >\n <ChevronsDownUp size='sm' />\n </Button>\n <Button\n variant='ghost'\n color='neutral'\n size='small'\n aria-label='Expand all'\n onClick={() => setAll(true)}\n >\n <ChevronsUpDown size='sm' />\n </Button>\n </div>\n <Button variant='ghost' color='neutral' size='small' aria-label='Close panel'>\n <PanelRightClose size='sm' />\n </Button>\n </div>\n </div>\n\n {/* Search — full width, aligned with the items' selection highlight */}\n <div className='pb-4'>\n <div className='relative'>\n <Search\n size='md'\n className='pointer-events-none absolute top-1/2 left-12 -translate-y-1/2 text-text-secondary'\n />\n <Input\n placeholder='Search'\n value={query}\n onChange={e => setQuery(e.target.value)}\n className='pl-40'\n />\n </div>\n </div>\n\n {nodes}\n </TreeView>\n </div>\n );\n}",
131029
130585
  "description": "The toolbar is assembled from `Text`, `Button` and `Input` — there is no TreeView toolbar. Expand-all drives the items’ controlled `open` state, and the search keeps ancestors of matches and opens them."
131030
130586
  }
131031
130587
  ]