@reshape-biotech/design-system 1.2.6 → 2.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 (187) hide show
  1. package/README.md +5 -1
  2. package/dist/app.css +97 -97
  3. package/dist/components/activity/Activity.stories.svelte +104 -104
  4. package/dist/components/activity/Activity.svelte +112 -112
  5. package/dist/components/avatar/Avatar.stories.svelte +23 -23
  6. package/dist/components/avatar/Avatar.svelte +54 -54
  7. package/dist/components/banner/Banner.stories.svelte +105 -105
  8. package/dist/components/banner/Banner.svelte +42 -42
  9. package/dist/components/button/Button.stories.svelte +61 -61
  10. package/dist/components/button/Button.svelte +95 -95
  11. package/dist/components/card/Card.stories.svelte +112 -112
  12. package/dist/components/card/Card.svelte +18 -18
  13. package/dist/components/checkbox/Checkbox.stories.svelte +8 -8
  14. package/dist/components/checkbox/Checkbox.svelte +17 -17
  15. package/dist/components/collapsible/Collapsible.stories.svelte +26 -26
  16. package/dist/components/collapsible/components/collapsible-content.svelte +20 -20
  17. package/dist/components/collapsible/components/collapsible-trigger.svelte +12 -12
  18. package/dist/components/collapsible/index.d.ts +1 -1
  19. package/dist/components/combobox/Combobox.stories.svelte +412 -412
  20. package/dist/components/combobox/components/combobox-add.svelte +8 -8
  21. package/dist/components/combobox/components/combobox-content.svelte +39 -39
  22. package/dist/components/combobox/components/combobox-indicator.svelte +1 -1
  23. package/dist/components/combobox/index.d.ts +1 -1
  24. package/dist/components/datepicker/DatePicker.stories.svelte +196 -196
  25. package/dist/components/datepicker/DatePicker.svelte +173 -173
  26. package/dist/components/divider/Divider.stories.svelte +7 -7
  27. package/dist/components/divider/Divider.svelte +9 -9
  28. package/dist/components/drawer/Drawer.stories.svelte +51 -51
  29. package/dist/components/drawer/Drawer.svelte +33 -33
  30. package/dist/components/drawer/DrawerLabel.svelte +10 -10
  31. package/dist/components/dropdown/Dropdown.stories.svelte +210 -210
  32. package/dist/components/dropdown/Dropdown.svelte +57 -57
  33. package/dist/components/dropdown/components/DropdownContent.svelte +16 -16
  34. package/dist/components/dropdown/components/DropdownMenu.svelte +10 -10
  35. package/dist/components/dropdown/components/DropdownTrigger.svelte +37 -37
  36. package/dist/components/dropdown/components/OutlinedButton.svelte +9 -9
  37. package/dist/components/empty-content/EmptyContent.stories.svelte +38 -38
  38. package/dist/components/empty-content/EmptyContent.svelte +12 -12
  39. package/dist/components/graphs/bar-chart/BarChart.stories.svelte +91 -91
  40. package/dist/components/graphs/bar-chart/BarChart.svelte +147 -147
  41. package/dist/components/graphs/bar-chart/StackedBarChart.stories.svelte +57 -57
  42. package/dist/components/graphs/bar-chart/StackedBarChart.svelte +198 -199
  43. package/dist/components/graphs/chart/Chart.stories.svelte +96 -96
  44. package/dist/components/graphs/chart/Chart.svelte +207 -207
  45. package/dist/components/graphs/line/LineChart.stories.svelte +138 -138
  46. package/dist/components/graphs/line/LineChart.svelte +140 -142
  47. package/dist/components/graphs/matrix/Matrix.stories.svelte +117 -117
  48. package/dist/components/graphs/matrix/Matrix.svelte +141 -141
  49. package/dist/components/graphs/multiline/MultiLineChart.stories.svelte +168 -168
  50. package/dist/components/graphs/multiline/MultiLineChart.svelte +236 -236
  51. package/dist/components/graphs/scatterplot/Scatterplot.stories.svelte +84 -84
  52. package/dist/components/graphs/scatterplot/Scatterplot.svelte +302 -302
  53. package/dist/components/graphs/utils/duration.d.ts +1 -0
  54. package/dist/components/graphs/utils/duration.js +33 -0
  55. package/dist/components/graphs/utils/tooltipFormatter.js +1 -1
  56. package/dist/components/icon-button/IconButton.stories.svelte +64 -64
  57. package/dist/components/icon-button/IconButton.svelte +88 -88
  58. package/dist/components/icons/AnalysisIcon.stories.svelte +18 -18
  59. package/dist/components/icons/AnalysisIcon.svelte +96 -96
  60. package/dist/components/icons/Icon.stories.svelte +111 -111
  61. package/dist/components/icons/Icon.svelte +17 -17
  62. package/dist/components/icons/PrincipalIcon.svelte +59 -59
  63. package/dist/components/icons/custom/Halo.svelte +31 -31
  64. package/dist/components/icons/custom/Well.svelte +27 -27
  65. package/dist/components/icons/index.js +1 -1
  66. package/dist/components/image/Image.svelte +42 -42
  67. package/dist/components/input/Input.stories.svelte +55 -55
  68. package/dist/components/input/Input.svelte +121 -121
  69. package/dist/components/label/Label.stories.svelte +18 -18
  70. package/dist/components/label/Label.svelte +11 -11
  71. package/dist/components/list/List.stories.svelte +84 -84
  72. package/dist/components/list/List.svelte +20 -20
  73. package/dist/components/logo/Logo.stories.svelte +15 -15
  74. package/dist/components/logo/Logo.svelte +30 -30
  75. package/dist/components/manual-cfu-counter/ManualCFUCounter.stories.svelte +102 -102
  76. package/dist/components/manual-cfu-counter/ManualCFUCounter.svelte +557 -557
  77. package/dist/components/manual-cfu-counter/test/ManualCFUCounterTestWrapper.svelte +11 -11
  78. package/dist/components/markdown/Markdown.stories.svelte +10 -10
  79. package/dist/components/markdown/Markdown.svelte +6 -6
  80. package/dist/components/modal/Modal.stories.svelte +29 -29
  81. package/dist/components/modal/Modal.svelte +71 -71
  82. package/dist/components/multi-cfu-counter/MultiCFUCounter.stories.svelte +201 -201
  83. package/dist/components/multi-cfu-counter/MultiCFUCounter.svelte +606 -606
  84. package/dist/components/multi-cfu-counter/test/MultiCFUCounterTestWrapper.svelte +17 -17
  85. package/dist/components/notification-popup/NotificationPopup.stories.svelte +18 -18
  86. package/dist/components/notification-popup/NotificationPopup.svelte +26 -26
  87. package/dist/components/notifications/Notifications.stories.svelte +101 -101
  88. package/dist/components/notifications/Notifications.svelte +9 -9
  89. package/dist/components/pill/Pill.stories.svelte +8 -8
  90. package/dist/components/pill/Pill.svelte +27 -27
  91. package/dist/components/progress-circle/ProgressCircle.stories.svelte +8 -8
  92. package/dist/components/progress-circle/ProgressCircle.svelte +54 -54
  93. package/dist/components/required-status-indicator/RequiredStatusIndicator.stories.svelte +18 -18
  94. package/dist/components/required-status-indicator/RequiredStatusIndicator.svelte +14 -14
  95. package/dist/components/segmented-control-buttons/ControlButton.svelte +36 -36
  96. package/dist/components/segmented-control-buttons/SegmentedControlButtons.stories.svelte +35 -35
  97. package/dist/components/segmented-control-buttons/SegmentedControlButtons.svelte +13 -13
  98. package/dist/components/select/Select.stories.svelte +200 -94
  99. package/dist/components/select/Select.stories.svelte.d.ts +1 -1
  100. package/dist/components/select/components/Group.svelte +24 -0
  101. package/dist/components/select/components/MultiSelectTrigger.svelte +66 -0
  102. package/dist/components/select/components/SelectContent.svelte +33 -0
  103. package/dist/components/select/components/SelectGroupHeading.svelte +19 -0
  104. package/dist/components/select/components/SelectItem.svelte +39 -0
  105. package/dist/components/select/components/SelectTrigger.svelte +48 -0
  106. package/dist/components/select/index.d.ts +10 -7
  107. package/dist/components/select/index.js +12 -1
  108. package/dist/components/sjsf-wrappers/SjsfNumberInputWrapper.svelte +102 -87
  109. package/dist/components/sjsf-wrappers/SjsfNumberInputWrapper.svelte.d.ts +1 -1
  110. package/dist/components/sjsf-wrappers/SjsfTextInputWrapper.svelte +53 -53
  111. package/dist/components/sjsf-wrappers/SjsfTextInputWrapper.svelte.d.ts +1 -1
  112. package/dist/components/sjsf-wrappers/sjsfCustomTheme.js +1 -1
  113. package/dist/components/skeleton-loader/SkeletonLoader.stories.svelte +32 -32
  114. package/dist/components/skeleton-loader/SkeletonLoader.svelte +10 -10
  115. package/dist/components/skeleton-loader/StatcardSkeleton.svelte +9 -9
  116. package/dist/components/skeleton-loader/components/Skeleton.svelte +7 -7
  117. package/dist/components/skeleton-loader/components/SkeletonImage.svelte +12 -12
  118. package/dist/components/slider/Slider.stories.svelte +23 -23
  119. package/dist/components/slider/Slider.svelte +107 -107
  120. package/dist/components/spinner/Spinner.stories.svelte +8 -8
  121. package/dist/components/spinner/Spinner.svelte +18 -18
  122. package/dist/components/stat-card/StatCard.stories.svelte +26 -26
  123. package/dist/components/stat-card/StatCard.svelte +128 -128
  124. package/dist/components/status-badge/StatusBadge.stories.svelte +365 -365
  125. package/dist/components/status-badge/StatusBadge.svelte +54 -54
  126. package/dist/components/stepper/Stepper.stories.svelte +219 -219
  127. package/dist/components/stepper/components/stepper-root.svelte +12 -12
  128. package/dist/components/stepper/components/stepper-step.svelte +83 -83
  129. package/dist/components/table/Table.stories.svelte +87 -87
  130. package/dist/components/table/Table.svelte +32 -32
  131. package/dist/components/table/components/TBody.svelte +7 -7
  132. package/dist/components/table/components/THead.svelte +7 -7
  133. package/dist/components/table/components/Td.svelte +8 -8
  134. package/dist/components/table/components/Th.svelte +8 -8
  135. package/dist/components/table/components/Tr.svelte +11 -11
  136. package/dist/components/tabs/Tabs.stories.svelte +20 -20
  137. package/dist/components/tabs/Tabs.svelte +8 -8
  138. package/dist/components/tabs/components/Content.svelte +8 -8
  139. package/dist/components/tabs/components/Tab.svelte +14 -14
  140. package/dist/components/tabs/components/Tabs.svelte +7 -7
  141. package/dist/components/tag/Tag.stories.svelte +57 -57
  142. package/dist/components/tag/Tag.svelte +95 -95
  143. package/dist/components/textarea/Textarea.stories.svelte +70 -70
  144. package/dist/components/textarea/Textarea.svelte +76 -76
  145. package/dist/components/toast/Toast.stories.svelte +204 -204
  146. package/dist/components/toast/Toast.svelte +53 -53
  147. package/dist/components/toggle/Toggle.stories.svelte +9 -9
  148. package/dist/components/toggle/Toggle.svelte +53 -53
  149. package/dist/components/toggle-icon-button/ToggleIconButton.stories.svelte +152 -152
  150. package/dist/components/toggle-icon-button/ToggleIconButton.svelte +99 -99
  151. package/dist/components/tooltip/Tooltip.stories.svelte +85 -111
  152. package/dist/components/tooltip/Tooltip.svelte +57 -46
  153. package/dist/components/tooltip/Tooltip.svelte.d.ts +1 -1
  154. package/dist/components/tooltip/TooltipTest.svelte +31 -0
  155. package/dist/components/tooltip/TooltipTest.svelte.d.ts +11 -0
  156. package/dist/fonts/index.js +1 -1
  157. package/dist/index.d.ts +0 -1
  158. package/dist/index.js +0 -1
  159. package/dist/notifications.d.ts +1 -4
  160. package/dist/notifications.js +1 -1
  161. package/dist/tailwind-safelist.js +406 -406
  162. package/dist/tailwind.preset.js +10 -10
  163. package/dist/tokens/colors.js +18 -18
  164. package/dist/tokens/typography.js +6 -6
  165. package/dist/tokens.js +19 -19
  166. package/dist/types/fonts.d.ts +2 -2
  167. package/package.json +199 -204
  168. package/dist/components/select/Select.svelte +0 -139
  169. package/dist/components/select/Select.svelte.d.ts +0 -60
  170. package/dist/components/select-new/Select.stories.svelte +0 -219
  171. package/dist/components/select-new/Select.stories.svelte.d.ts +0 -19
  172. package/dist/components/select-new/components/Group.svelte +0 -24
  173. package/dist/components/select-new/components/MultiSelectTrigger.svelte +0 -66
  174. package/dist/components/select-new/components/SelectContent.svelte +0 -33
  175. package/dist/components/select-new/components/SelectGroupHeading.svelte +0 -19
  176. package/dist/components/select-new/components/SelectItem.svelte +0 -39
  177. package/dist/components/select-new/components/SelectTrigger.svelte +0 -48
  178. package/dist/components/select-new/index.d.ts +0 -10
  179. package/dist/components/select-new/index.js +0 -12
  180. /package/dist/components/{select-new → select}/components/Group.svelte.d.ts +0 -0
  181. /package/dist/components/{select-new → select}/components/MultiSelectTrigger.svelte.d.ts +0 -0
  182. /package/dist/components/{select-new → select}/components/SelectContent.svelte.d.ts +0 -0
  183. /package/dist/components/{select-new → select}/components/SelectGroupHeading.svelte.d.ts +0 -0
  184. /package/dist/components/{select-new → select}/components/SelectItem.svelte.d.ts +0 -0
  185. /package/dist/components/{select-new → select}/components/SelectTrigger.svelte.d.ts +0 -0
  186. /package/dist/components/{select-new → select}/types.d.ts +0 -0
  187. /package/dist/components/{select-new → select}/types.js +0 -0
@@ -1,442 +1,442 @@
1
1
  <script module lang="ts">
2
- import { defineMeta } from '@storybook/addon-svelte-csf';
3
- import { userEvent, within } from '@storybook/test';
4
- import { Root as ComboboxRootForMeta } from './index';
5
- import * as Combobox from './index';
6
- import { Icon } from '../icons';
7
- import IconButton from '../icon-button/IconButton.svelte';
8
- import Divider from '../divider/Divider.svelte';
9
- import Tag from '../tag/Tag.svelte';
10
- import Button from '../button/Button.svelte';
11
-
12
- const { Story } = defineMeta({
13
- component: ComboboxRootForMeta,
14
- title: 'Design System/Combobox',
15
- tags: ['autodocs'],
16
- parameters: {
17
- design: {
18
- type: 'figma',
19
- url: 'https://www.figma.com/design/VHTMNdy8PAXAMx43edNZGW/%F0%9F%92%A0--Reshape-Design-System%3A-V1?node-id=9-3294&t=sCuBI8dX6K6NjNR6-0'
20
- }
21
- }
22
- });
23
-
24
- const fruits = [
25
- { value: 'mango', label: 'Mango' },
26
- { value: 'watermelon', label: 'Watermelon' },
27
- { value: 'apple', label: 'Apple' },
28
- { value: 'pineapple', label: 'Pineapple' },
29
- { value: 'orange', label: 'Orange' },
30
- { value: 'grape', label: 'Grape' },
31
- { value: 'strawberry', label: 'Strawberry' },
32
- { value: 'banana', label: 'Banana' },
33
- { value: 'kiwi', label: 'Kiwi' },
34
- { value: 'peach', label: 'Peach' },
35
- { value: 'cherry', label: 'Cherry' },
36
- { value: 'blueberry', label: 'Blueberry' },
37
- { value: 'raspberry', label: 'Raspberry' },
38
- { value: 'blackberry', label: 'Blackberry' },
39
- { value: 'plum', label: 'Plum' },
40
- { value: 'apricot', label: 'Apricot' },
41
- { value: 'pear', label: 'Pear' },
42
- { value: 'grapefruit', label: 'Grapefruit' }
43
- ];
44
-
45
- const categories = [
46
- { value: 'citrus', label: 'Citrus', items: ['orange', 'grapefruit', 'lemon'] },
47
- {
48
- value: 'berries',
49
- label: 'Berries',
50
- items: ['strawberry', 'blueberry', 'raspberry', 'blackberry']
51
- },
52
- { value: 'tropical', label: 'Tropical', items: ['mango', 'pineapple', 'banana', 'kiwi'] },
53
- { value: 'stone', label: 'Stone Fruits', items: ['peach', 'cherry', 'plum', 'apricot'] },
54
- { value: 'other', label: 'Other', items: ['apple', 'pear', 'watermelon', 'grape'] }
55
- ];
56
-
57
- let searchValue = $state('');
58
- let searchValueSingle = $state('');
59
- let searchValueGrouped = $state('');
60
- let searchValueCustom = $state('');
61
-
62
- let selected = $state<string[]>([]);
63
- let selectedSingle = $state<string>('');
64
- let selectedGrouped = $state<string[]>([]);
65
- let selectedCustom = $state<string[]>([]);
66
-
67
- const filteredFruits = $derived(
68
- searchValue === ''
69
- ? fruits
70
- : fruits.filter((fruit) => fruit.label.toLowerCase().includes(searchValue.toLowerCase()))
71
- );
72
-
73
- const filteredFruitsSingle = $derived(
74
- searchValueSingle === ''
75
- ? fruits
76
- : fruits.filter((fruit) =>
77
- fruit.label.toLowerCase().includes(searchValueSingle.toLowerCase())
78
- )
79
- );
80
-
81
- const filteredFruitsGrouped = $derived(
82
- searchValueGrouped === ''
83
- ? fruits
84
- : fruits.filter((fruit) =>
85
- fruit.label.toLowerCase().includes(searchValueGrouped.toLowerCase())
86
- )
87
- );
88
-
89
- const filteredFruitsCustom = $derived(
90
- searchValueCustom === ''
91
- ? fruits
92
- : fruits.filter((fruit) =>
93
- fruit.label.toLowerCase().includes(searchValueCustom.toLowerCase())
94
- )
95
- );
96
-
97
- const exactMatch = $derived(
98
- filteredFruits.find((fruit) => fruit.value.toLowerCase() === searchValue.toLowerCase())
99
- );
100
-
101
- const exactMatchCustom = $derived(
102
- filteredFruitsCustom.find(
103
- (fruit) => fruit.value.toLowerCase() === searchValueCustom.toLowerCase()
104
- )
105
- );
106
-
107
- let customAnchor = $state<HTMLElement>(null!);
108
- let customAnchorButton = $state<HTMLElement>(null!);
109
- let customAnchorSingle = $state<HTMLElement>(null!);
110
- let customAnchorGrouped = $state<HTMLElement>(null!);
111
- let customAnchorCustom = $state<HTMLElement>(null!);
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import { userEvent, within } from '@storybook/test';
4
+ import { Root as ComboboxRootForMeta } from './index';
5
+ import * as Combobox from './index';
6
+ import { Icon } from '../icons';
7
+ import IconButton from '../icon-button/IconButton.svelte';
8
+ import Divider from '../divider/Divider.svelte';
9
+ import Tag from '../tag/Tag.svelte';
10
+ import Button from '../button/Button.svelte';
11
+
12
+ const { Story } = defineMeta({
13
+ component: ComboboxRootForMeta,
14
+ title: 'Design System/Combobox',
15
+ tags: ['autodocs'],
16
+ parameters: {
17
+ design: {
18
+ type: 'figma',
19
+ url: 'https://www.figma.com/design/VHTMNdy8PAXAMx43edNZGW/%F0%9F%92%A0--Reshape-Design-System%3A-V1?node-id=9-3294&t=sCuBI8dX6K6NjNR6-0',
20
+ },
21
+ },
22
+ });
23
+
24
+ const fruits = [
25
+ { value: 'mango', label: 'Mango' },
26
+ { value: 'watermelon', label: 'Watermelon' },
27
+ { value: 'apple', label: 'Apple' },
28
+ { value: 'pineapple', label: 'Pineapple' },
29
+ { value: 'orange', label: 'Orange' },
30
+ { value: 'grape', label: 'Grape' },
31
+ { value: 'strawberry', label: 'Strawberry' },
32
+ { value: 'banana', label: 'Banana' },
33
+ { value: 'kiwi', label: 'Kiwi' },
34
+ { value: 'peach', label: 'Peach' },
35
+ { value: 'cherry', label: 'Cherry' },
36
+ { value: 'blueberry', label: 'Blueberry' },
37
+ { value: 'raspberry', label: 'Raspberry' },
38
+ { value: 'blackberry', label: 'Blackberry' },
39
+ { value: 'plum', label: 'Plum' },
40
+ { value: 'apricot', label: 'Apricot' },
41
+ { value: 'pear', label: 'Pear' },
42
+ { value: 'grapefruit', label: 'Grapefruit' },
43
+ ];
44
+
45
+ const categories = [
46
+ { value: 'citrus', label: 'Citrus', items: ['orange', 'grapefruit', 'lemon'] },
47
+ {
48
+ value: 'berries',
49
+ label: 'Berries',
50
+ items: ['strawberry', 'blueberry', 'raspberry', 'blackberry'],
51
+ },
52
+ { value: 'tropical', label: 'Tropical', items: ['mango', 'pineapple', 'banana', 'kiwi'] },
53
+ { value: 'stone', label: 'Stone Fruits', items: ['peach', 'cherry', 'plum', 'apricot'] },
54
+ { value: 'other', label: 'Other', items: ['apple', 'pear', 'watermelon', 'grape'] },
55
+ ];
56
+
57
+ let searchValue = $state('');
58
+ let searchValueSingle = $state('');
59
+ let searchValueGrouped = $state('');
60
+ let searchValueCustom = $state('');
61
+
62
+ let selected = $state<string[]>([]);
63
+ let selectedSingle = $state<string>('');
64
+ let selectedGrouped = $state<string[]>([]);
65
+ let selectedCustom = $state<string[]>([]);
66
+
67
+ const filteredFruits = $derived(
68
+ searchValue === ''
69
+ ? fruits
70
+ : fruits.filter((fruit) => fruit.label.toLowerCase().includes(searchValue.toLowerCase()))
71
+ );
72
+
73
+ const filteredFruitsSingle = $derived(
74
+ searchValueSingle === ''
75
+ ? fruits
76
+ : fruits.filter((fruit) =>
77
+ fruit.label.toLowerCase().includes(searchValueSingle.toLowerCase())
78
+ )
79
+ );
80
+
81
+ const filteredFruitsGrouped = $derived(
82
+ searchValueGrouped === ''
83
+ ? fruits
84
+ : fruits.filter((fruit) =>
85
+ fruit.label.toLowerCase().includes(searchValueGrouped.toLowerCase())
86
+ )
87
+ );
88
+
89
+ const filteredFruitsCustom = $derived(
90
+ searchValueCustom === ''
91
+ ? fruits
92
+ : fruits.filter((fruit) =>
93
+ fruit.label.toLowerCase().includes(searchValueCustom.toLowerCase())
94
+ )
95
+ );
96
+
97
+ const exactMatch = $derived(
98
+ filteredFruits.find((fruit) => fruit.value.toLowerCase() === searchValue.toLowerCase())
99
+ );
100
+
101
+ const exactMatchCustom = $derived(
102
+ filteredFruitsCustom.find(
103
+ (fruit) => fruit.value.toLowerCase() === searchValueCustom.toLowerCase()
104
+ )
105
+ );
106
+
107
+ let customAnchor = $state<HTMLElement>(null!);
108
+ let customAnchorButton = $state<HTMLElement>(null!);
109
+ let customAnchorSingle = $state<HTMLElement>(null!);
110
+ let customAnchorGrouped = $state<HTMLElement>(null!);
111
+ let customAnchorCustom = $state<HTMLElement>(null!);
112
112
  </script>
113
113
 
114
114
  <Story name="Multiple Selection" asChild>
115
- <Combobox.Root
116
- onOpenChange={(o) => {
117
- if (!o) searchValue = '';
118
- }}
119
- type="multiple"
120
- name="favoriteFruit"
121
- items={filteredFruits}
122
- bind:value={selected}
123
- >
124
- <div class="flex gap-2">
125
- {#each selected as fruit}
126
- <Tag>
127
- {fruit}
128
- </Tag>
129
- {/each}
130
- </div>
131
- <Combobox.Trigger>
132
- <div bind:this={customAnchor}>
133
- <IconButton rounded={false}>
134
- <Icon iconName="Plus" />
135
- </IconButton>
136
- </div>
137
- </Combobox.Trigger>
138
- <Combobox.Content {customAnchor} class="flex flex-col justify-between">
139
- <div>
140
- <Combobox.Input
141
- placeholder="Search a fruit"
142
- oninput={(e: Event) => (searchValue = (e.target as HTMLInputElement).value)}
143
- autofocus
144
- />
145
- <Divider />
146
- </div>
147
- <div class="flex flex-grow flex-col">
148
- {#if filteredFruits.length > 0}
149
- <Combobox.Group>
150
- <Combobox.GroupHeading>Fruits</Combobox.GroupHeading>
151
- {#each filteredFruits as fruit (fruit.value)}
152
- <Combobox.Item value={fruit.value} label={fruit.label}>
153
- {#snippet children({ selected })}
154
- {fruit.label}
155
- {#if selected}
156
- <Combobox.Indicator />
157
- {/if}
158
- {/snippet}
159
- </Combobox.Item>
160
- {:else}
161
- <span class="block px-5 py-2 text-sm text-muted-foreground"> No results found </span>
162
- {/each}
163
- </Combobox.Group>
164
- {/if}
165
- </div>
166
- {#if !exactMatch && searchValue !== ''}
167
- <Divider />
168
-
169
- <Combobox.Add
170
- onclick={() => {
171
- selected.push(searchValue);
172
- fruits.push({ value: searchValue, label: searchValue });
173
- searchValue = '';
174
- }}
175
- >
176
- <p>Add new fruit</p>
177
- </Combobox.Add>
178
- {/if}
179
- </Combobox.Content>
180
- </Combobox.Root>
115
+ <Combobox.Root
116
+ onOpenChange={(o) => {
117
+ if (!o) searchValue = '';
118
+ }}
119
+ type="multiple"
120
+ name="favoriteFruit"
121
+ items={filteredFruits}
122
+ bind:value={selected}
123
+ >
124
+ <div class="flex gap-2">
125
+ {#each selected as fruit}
126
+ <Tag>
127
+ {fruit}
128
+ </Tag>
129
+ {/each}
130
+ </div>
131
+ <Combobox.Trigger>
132
+ <div bind:this={customAnchor}>
133
+ <IconButton rounded={false}>
134
+ <Icon iconName="Plus" />
135
+ </IconButton>
136
+ </div>
137
+ </Combobox.Trigger>
138
+ <Combobox.Content {customAnchor} class="flex flex-col justify-between">
139
+ <div>
140
+ <Combobox.Input
141
+ placeholder="Search a fruit"
142
+ oninput={(e: Event) => (searchValue = (e.target as HTMLInputElement).value)}
143
+ autofocus
144
+ />
145
+ <Divider />
146
+ </div>
147
+ <div class="flex flex-grow flex-col">
148
+ {#if filteredFruits.length > 0}
149
+ <Combobox.Group>
150
+ <Combobox.GroupHeading>Fruits</Combobox.GroupHeading>
151
+ {#each filteredFruits as fruit (fruit.value)}
152
+ <Combobox.Item value={fruit.value} label={fruit.label}>
153
+ {#snippet children({ selected })}
154
+ {fruit.label}
155
+ {#if selected}
156
+ <Combobox.Indicator />
157
+ {/if}
158
+ {/snippet}
159
+ </Combobox.Item>
160
+ {:else}
161
+ <span class="block px-5 py-2 text-sm text-muted-foreground"> No results found </span>
162
+ {/each}
163
+ </Combobox.Group>
164
+ {/if}
165
+ </div>
166
+ {#if !exactMatch && searchValue !== ''}
167
+ <Divider />
168
+
169
+ <Combobox.Add
170
+ onclick={() => {
171
+ selected.push(searchValue);
172
+ fruits.push({ value: searchValue, label: searchValue });
173
+ searchValue = '';
174
+ }}
175
+ >
176
+ <p>Add new fruit</p>
177
+ </Combobox.Add>
178
+ {/if}
179
+ </Combobox.Content>
180
+ </Combobox.Root>
181
181
  </Story>
182
182
 
183
183
  <Story name="Single Selection" asChild>
184
- <Combobox.Root
185
- onOpenChange={(o) => {
186
- if (!o) searchValueSingle = '';
187
- }}
188
- type="single"
189
- name="singleFruit"
190
- items={filteredFruitsSingle}
191
- bind:value={selectedSingle}
192
- >
193
- <Combobox.Trigger>
194
- <div bind:this={customAnchorSingle}>
195
- <Button variant="secondary" size="sm">
196
- {selectedSingle ? selectedSingle : 'Select a fruit'}
197
- </Button>
198
- </div>
199
- </Combobox.Trigger>
200
- <Combobox.Content class="flex flex-col justify-between" customAnchor={customAnchorSingle}>
201
- <div class="flex flex-grow flex-col">
202
- {#if filteredFruitsSingle.length > 0}
203
- <Combobox.Group>
204
- <Combobox.GroupHeading>Fruits</Combobox.GroupHeading>
205
- {#each filteredFruitsSingle as fruit (fruit.value)}
206
- <Combobox.Item value={fruit.value} label={fruit.label}>
207
- {#snippet children({ selected })}
208
- {fruit.label}
209
- {#if selected}
210
- <Combobox.Indicator />
211
- {/if}
212
- {/snippet}
213
- </Combobox.Item>
214
- {:else}
215
- <span class="block px-5 py-2 text-sm text-muted-foreground"> No results found </span>
216
- {/each}
217
- </Combobox.Group>
218
- {/if}
219
- </div>
220
- </Combobox.Content>
221
- </Combobox.Root>
184
+ <Combobox.Root
185
+ onOpenChange={(o) => {
186
+ if (!o) searchValueSingle = '';
187
+ }}
188
+ type="single"
189
+ name="singleFruit"
190
+ items={filteredFruitsSingle}
191
+ bind:value={selectedSingle}
192
+ >
193
+ <Combobox.Trigger>
194
+ <div bind:this={customAnchorSingle}>
195
+ <Button variant="secondary" size="sm">
196
+ {selectedSingle ? selectedSingle : 'Select a fruit'}
197
+ </Button>
198
+ </div>
199
+ </Combobox.Trigger>
200
+ <Combobox.Content class="flex flex-col justify-between" customAnchor={customAnchorSingle}>
201
+ <div class="flex flex-grow flex-col">
202
+ {#if filteredFruitsSingle.length > 0}
203
+ <Combobox.Group>
204
+ <Combobox.GroupHeading>Fruits</Combobox.GroupHeading>
205
+ {#each filteredFruitsSingle as fruit (fruit.value)}
206
+ <Combobox.Item value={fruit.value} label={fruit.label}>
207
+ {#snippet children({ selected })}
208
+ {fruit.label}
209
+ {#if selected}
210
+ <Combobox.Indicator />
211
+ {/if}
212
+ {/snippet}
213
+ </Combobox.Item>
214
+ {:else}
215
+ <span class="block px-5 py-2 text-sm text-muted-foreground"> No results found </span>
216
+ {/each}
217
+ </Combobox.Group>
218
+ {/if}
219
+ </div>
220
+ </Combobox.Content>
221
+ </Combobox.Root>
222
222
  </Story>
223
223
 
224
224
  <Story name="Grouped Items" asChild>
225
- <Combobox.Root
226
- onOpenChange={(o) => {
227
- if (!o) searchValueGrouped = '';
228
- }}
229
- type="multiple"
230
- name="groupedFruits"
231
- items={filteredFruitsGrouped}
232
- bind:value={selectedGrouped}
233
- >
234
- <div class="flex flex-wrap gap-2">
235
- {#each selectedGrouped as fruit}
236
- <Tag>
237
- {fruit}
238
- </Tag>
239
- {/each}
240
- </div>
241
- <Combobox.Trigger>
242
- <div bind:this={customAnchorGrouped}>
243
- <Button variant="primary" size="sm">
244
- <Icon iconName="List" />
245
- Select fruits by category
246
- </Button>
247
- </div>
248
- </Combobox.Trigger>
249
- <Combobox.Content class="flex flex-col justify-between" customAnchor={customAnchorGrouped}>
250
- <div>
251
- <Combobox.Input
252
- placeholder="Search across categories"
253
- oninput={(e: Event) => (searchValueGrouped = (e.target as HTMLInputElement).value)}
254
- autofocus
255
- />
256
- <Divider />
257
- </div>
258
- <div class="flex max-h-80 flex-grow flex-col overflow-y-auto">
259
- {#if searchValueGrouped === ''}
260
- {#each categories as category}
261
- <Combobox.Group>
262
- <Combobox.GroupHeading>{category.label}</Combobox.GroupHeading>
263
- {#each category.items as item}
264
- {@const fruit = fruits.find((f) => f.value === item)}
265
- {#if fruit}
266
- <Combobox.Item value={fruit.value} label={fruit.label}>
267
- {#snippet children({ selected })}
268
- {fruit.label}
269
- {#if selected}
270
- <Combobox.Indicator />
271
- {/if}
272
- {/snippet}
273
- </Combobox.Item>
274
- {/if}
275
- {/each}
276
- </Combobox.Group>
277
- {/each}
278
- {:else if filteredFruitsGrouped.length > 0}
279
- <Combobox.Group>
280
- <Combobox.GroupHeading>Search Results</Combobox.GroupHeading>
281
- {#each filteredFruitsGrouped as fruit (fruit.value)}
282
- <Combobox.Item value={fruit.value} label={fruit.label}>
283
- {#snippet children({ selected })}
284
- {fruit.label}
285
- {#if selected}
286
- <Combobox.Indicator />
287
- {/if}
288
- {/snippet}
289
- </Combobox.Item>
290
- {/each}
291
- </Combobox.Group>
292
- {:else}
293
- <span class="text-muted-foreground block px-5 py-2 text-sm"> No results found </span>
294
- {/if}
295
- </div>
296
- </Combobox.Content>
297
- </Combobox.Root>
225
+ <Combobox.Root
226
+ onOpenChange={(o) => {
227
+ if (!o) searchValueGrouped = '';
228
+ }}
229
+ type="multiple"
230
+ name="groupedFruits"
231
+ items={filteredFruitsGrouped}
232
+ bind:value={selectedGrouped}
233
+ >
234
+ <div class="flex flex-wrap gap-2">
235
+ {#each selectedGrouped as fruit}
236
+ <Tag>
237
+ {fruit}
238
+ </Tag>
239
+ {/each}
240
+ </div>
241
+ <Combobox.Trigger>
242
+ <div bind:this={customAnchorGrouped}>
243
+ <Button variant="primary" size="sm">
244
+ <Icon iconName="List" />
245
+ Select fruits by category
246
+ </Button>
247
+ </div>
248
+ </Combobox.Trigger>
249
+ <Combobox.Content class="flex flex-col justify-between" customAnchor={customAnchorGrouped}>
250
+ <div>
251
+ <Combobox.Input
252
+ placeholder="Search across categories"
253
+ oninput={(e: Event) => (searchValueGrouped = (e.target as HTMLInputElement).value)}
254
+ autofocus
255
+ />
256
+ <Divider />
257
+ </div>
258
+ <div class="flex max-h-80 flex-grow flex-col overflow-y-auto">
259
+ {#if searchValueGrouped === ''}
260
+ {#each categories as category}
261
+ <Combobox.Group>
262
+ <Combobox.GroupHeading>{category.label}</Combobox.GroupHeading>
263
+ {#each category.items as item}
264
+ {@const fruit = fruits.find((f) => f.value === item)}
265
+ {#if fruit}
266
+ <Combobox.Item value={fruit.value} label={fruit.label}>
267
+ {#snippet children({ selected })}
268
+ {fruit.label}
269
+ {#if selected}
270
+ <Combobox.Indicator />
271
+ {/if}
272
+ {/snippet}
273
+ </Combobox.Item>
274
+ {/if}
275
+ {/each}
276
+ </Combobox.Group>
277
+ {/each}
278
+ {:else if filteredFruitsGrouped.length > 0}
279
+ <Combobox.Group>
280
+ <Combobox.GroupHeading>Search Results</Combobox.GroupHeading>
281
+ {#each filteredFruitsGrouped as fruit (fruit.value)}
282
+ <Combobox.Item value={fruit.value} label={fruit.label}>
283
+ {#snippet children({ selected })}
284
+ {fruit.label}
285
+ {#if selected}
286
+ <Combobox.Indicator />
287
+ {/if}
288
+ {/snippet}
289
+ </Combobox.Item>
290
+ {/each}
291
+ </Combobox.Group>
292
+ {:else}
293
+ <span class="text-muted-foreground block px-5 py-2 text-sm"> No results found </span>
294
+ {/if}
295
+ </div>
296
+ </Combobox.Content>
297
+ </Combobox.Root>
298
298
  </Story>
299
299
 
300
300
  <Story
301
- name="Interaction Test"
302
- asChild
303
- play={async ({ canvasElement }) => {
304
- // Get the canvas element
305
- const canvas = within(canvasElement);
301
+ name="Interaction Test"
302
+ asChild
303
+ play={async ({ canvasElement }) => {
304
+ // Get the canvas element
305
+ const canvas = within(canvasElement);
306
306
 
307
- // Find and click the trigger button to open the combobox
308
- const triggerButton = canvas.getByTestId('combobox-trigger');
309
- await userEvent.click(triggerButton);
307
+ // Find and click the trigger button to open the combobox
308
+ const triggerButton = canvas.getByTestId('combobox-trigger');
309
+ await userEvent.click(triggerButton);
310
310
 
311
- // Wait for the combobox content to appear in the document
312
- await new Promise((resolve) => setTimeout(resolve, 300));
311
+ // Wait for the combobox content to appear in the document
312
+ await new Promise((resolve) => setTimeout(resolve, 300));
313
313
 
314
- // Find the combobox content in the document
315
- const comboboxContent = document.querySelector(
316
- '[data-testid="combobox-content"]'
317
- ) as HTMLElement;
318
- if (!comboboxContent) {
319
- console.log('Combobox content not found');
320
- return;
321
- }
314
+ // Find the combobox content in the document
315
+ const comboboxContent = document.querySelector(
316
+ '[data-testid="combobox-content"]'
317
+ ) as HTMLElement;
318
+ if (!comboboxContent) {
319
+ console.log('Combobox content not found');
320
+ return;
321
+ }
322
322
 
323
- // Use within to scope queries to the combobox content
324
- const contentScope = within(comboboxContent);
325
- const searchInput = contentScope.getByTestId('combobox-input');
323
+ // Use within to scope queries to the combobox content
324
+ const contentScope = within(comboboxContent);
325
+ const searchInput = contentScope.getByTestId('combobox-input');
326
326
 
327
- // Type "berry" in the search input
328
- await userEvent.type(searchInput, 'berry');
327
+ // Type "berry" in the search input
328
+ await userEvent.type(searchInput, 'berry');
329
329
 
330
- // Wait for the filtering to complete
331
- await new Promise((resolve) => setTimeout(resolve, 300));
330
+ // Wait for the filtering to complete
331
+ await new Promise((resolve) => setTimeout(resolve, 300));
332
332
 
333
- // Find and click on the Strawberry option
334
- const strawberryOption = contentScope.getByText('Strawberry');
335
- await userEvent.click(strawberryOption);
333
+ // Find and click on the Strawberry option
334
+ const strawberryOption = contentScope.getByText('Strawberry');
335
+ await userEvent.click(strawberryOption);
336
336
 
337
- // Wait for selection to be processed
337
+ // Wait for selection to be processed
338
338
 
339
- // Open the combobox again
339
+ // Open the combobox again
340
340
 
341
- // Get the search input again
341
+ // Get the search input again
342
342
 
343
- // Clear the previous search and type "mango"
344
- await userEvent.clear(searchInput);
345
- await userEvent.type(searchInput, 'mango');
343
+ // Clear the previous search and type "mango"
344
+ await userEvent.clear(searchInput);
345
+ await userEvent.type(searchInput, 'mango');
346
346
 
347
- // Wait for filtering to complete
348
- await new Promise((resolve) => setTimeout(resolve, 300));
347
+ // Wait for filtering to complete
348
+ await new Promise((resolve) => setTimeout(resolve, 300));
349
349
 
350
- // Find and click on the Mango option
351
- const mangoOption = contentScope.getByText('Mango');
352
- await userEvent.click(mangoOption);
350
+ // Find and click on the Mango option
351
+ const mangoOption = contentScope.getByText('Mango');
352
+ await userEvent.click(mangoOption);
353
353
 
354
- await userEvent.keyboard('{Escape}');
354
+ await userEvent.keyboard('{Escape}');
355
355
 
356
- // Test complete
357
- console.log('Successfully selected Strawberry and Mango');
358
- }}
356
+ // Test complete
357
+ console.log('Successfully selected Strawberry and Mango');
358
+ }}
359
359
  >
360
- <div data-testid="combobox-container">
361
- <Combobox.Root
362
- onOpenChange={(o) => {
363
- if (!o) searchValue = '';
364
- }}
365
- type="multiple"
366
- name="favoriteFruit"
367
- items={filteredFruits}
368
- bind:value={selected}
369
- >
370
- <div class="flex gap-2" data-testid="selected-tags">
371
- {#each selected as fruit}
372
- <Tag>
373
- <div data-value={fruit}>
374
- {fruit}
375
- </div>
376
- </Tag>
377
- {/each}
378
- </div>
379
- <Combobox.Trigger data-testid="combobox-trigger">
380
- <div bind:this={customAnchor}>
381
- <IconButton rounded={false}>
382
- <Icon iconName="Plus" />
383
- </IconButton>
384
- </div>
385
- </Combobox.Trigger>
386
- <Combobox.Content
387
- {customAnchor}
388
- class="flex flex-col justify-between"
389
- data-testid="combobox-content"
390
- >
391
- <div>
392
- <Combobox.Input
393
- placeholder="Search a fruit"
394
- oninput={(e: Event) => (searchValue = (e.target as HTMLInputElement).value)}
395
- autofocus
396
- data-testid="combobox-input"
397
- />
398
- <Divider />
399
- </div>
400
- <div class="flex flex-grow flex-col">
401
- {#if filteredFruits.length > 0}
402
- <Combobox.Group>
403
- <Combobox.GroupHeading>Fruits</Combobox.GroupHeading>
404
- {#each filteredFruits as fruit (fruit.value)}
405
- <Combobox.Item
406
- value={fruit.value}
407
- label={fruit.label}
408
- data-testid={`fruit-option-${fruit.value}`}
409
- >
410
- {#snippet children({ selected })}
411
- {fruit.label}
412
- {#if selected}
413
- <Combobox.Indicator />
414
- {/if}
415
- {/snippet}
416
- </Combobox.Item>
417
- {:else}
418
- <span class="block px-5 py-2 text-sm text-muted-foreground">
419
- No results found
420
- </span>
421
- {/each}
422
- </Combobox.Group>
423
- {/if}
424
- </div>
425
- {#if !exactMatch && searchValue !== ''}
426
- <Divider />
427
-
428
- <Combobox.Add
429
- data-testid="add-new-fruit"
430
- onclick={() => {
431
- selected.push(searchValue);
432
- fruits.push({ value: searchValue, label: searchValue });
433
- searchValue = '';
434
- }}
435
- >
436
- <p>Add new fruit</p>
437
- </Combobox.Add>
438
- {/if}
439
- </Combobox.Content>
440
- </Combobox.Root>
441
- </div>
360
+ <div data-testid="combobox-container">
361
+ <Combobox.Root
362
+ onOpenChange={(o) => {
363
+ if (!o) searchValue = '';
364
+ }}
365
+ type="multiple"
366
+ name="favoriteFruit"
367
+ items={filteredFruits}
368
+ bind:value={selected}
369
+ >
370
+ <div class="flex gap-2" data-testid="selected-tags">
371
+ {#each selected as fruit}
372
+ <Tag>
373
+ <div data-value={fruit}>
374
+ {fruit}
375
+ </div>
376
+ </Tag>
377
+ {/each}
378
+ </div>
379
+ <Combobox.Trigger data-testid="combobox-trigger">
380
+ <div bind:this={customAnchor}>
381
+ <IconButton rounded={false}>
382
+ <Icon iconName="Plus" />
383
+ </IconButton>
384
+ </div>
385
+ </Combobox.Trigger>
386
+ <Combobox.Content
387
+ {customAnchor}
388
+ class="flex flex-col justify-between"
389
+ data-testid="combobox-content"
390
+ >
391
+ <div>
392
+ <Combobox.Input
393
+ placeholder="Search a fruit"
394
+ oninput={(e: Event) => (searchValue = (e.target as HTMLInputElement).value)}
395
+ autofocus
396
+ data-testid="combobox-input"
397
+ />
398
+ <Divider />
399
+ </div>
400
+ <div class="flex flex-grow flex-col">
401
+ {#if filteredFruits.length > 0}
402
+ <Combobox.Group>
403
+ <Combobox.GroupHeading>Fruits</Combobox.GroupHeading>
404
+ {#each filteredFruits as fruit (fruit.value)}
405
+ <Combobox.Item
406
+ value={fruit.value}
407
+ label={fruit.label}
408
+ data-testid={`fruit-option-${fruit.value}`}
409
+ >
410
+ {#snippet children({ selected })}
411
+ {fruit.label}
412
+ {#if selected}
413
+ <Combobox.Indicator />
414
+ {/if}
415
+ {/snippet}
416
+ </Combobox.Item>
417
+ {:else}
418
+ <span class="block px-5 py-2 text-sm text-muted-foreground">
419
+ No results found
420
+ </span>
421
+ {/each}
422
+ </Combobox.Group>
423
+ {/if}
424
+ </div>
425
+ {#if !exactMatch && searchValue !== ''}
426
+ <Divider />
427
+
428
+ <Combobox.Add
429
+ data-testid="add-new-fruit"
430
+ onclick={() => {
431
+ selected.push(searchValue);
432
+ fruits.push({ value: searchValue, label: searchValue });
433
+ searchValue = '';
434
+ }}
435
+ >
436
+ <p>Add new fruit</p>
437
+ </Combobox.Add>
438
+ {/if}
439
+ </Combobox.Content>
440
+ </Combobox.Root>
441
+ </div>
442
442
  </Story>