@navikt/ds-react 6.5.0 → 6.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/cjs/collapsible/Collapsible.context.d.ts +48 -0
  2. package/cjs/collapsible/Collapsible.context.js +10 -0
  3. package/cjs/collapsible/Collapsible.context.js.map +1 -0
  4. package/cjs/collapsible/Collapsible.d.ts +48 -0
  5. package/cjs/collapsible/Collapsible.js +91 -0
  6. package/cjs/collapsible/Collapsible.js.map +1 -0
  7. package/cjs/collapsible/Collapsible.types.d.ts +19 -0
  8. package/cjs/collapsible/Collapsible.types.js +3 -0
  9. package/cjs/collapsible/Collapsible.types.js.map +1 -0
  10. package/cjs/collapsible/index.d.ts +3 -0
  11. package/cjs/collapsible/index.js +14 -0
  12. package/cjs/collapsible/index.js.map +1 -0
  13. package/cjs/collapsible/parts/Collapsible.Content.d.ts +10 -0
  14. package/cjs/collapsible/parts/Collapsible.Content.js +48 -0
  15. package/cjs/collapsible/parts/Collapsible.Content.js.map +1 -0
  16. package/cjs/collapsible/parts/Collapsible.Trigger.d.ts +10 -0
  17. package/cjs/collapsible/parts/Collapsible.Trigger.js +49 -0
  18. package/cjs/collapsible/parts/Collapsible.Trigger.js.map +1 -0
  19. package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js +2 -4
  20. package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
  21. package/cjs/form/combobox/Input/Input.js +6 -1
  22. package/cjs/form/combobox/Input/Input.js.map +1 -1
  23. package/cjs/form/form-summary/FormSummary.d.ts +82 -0
  24. package/cjs/form/form-summary/FormSummary.js +81 -0
  25. package/cjs/form/form-summary/FormSummary.js.map +1 -0
  26. package/cjs/form/form-summary/FormSummaryAnswer.d.ts +11 -0
  27. package/cjs/form/form-summary/FormSummaryAnswer.js +25 -0
  28. package/cjs/form/form-summary/FormSummaryAnswer.js.map +1 -0
  29. package/cjs/form/form-summary/FormSummaryAnswers.d.ts +9 -0
  30. package/cjs/form/form-summary/FormSummaryAnswers.js +48 -0
  31. package/cjs/form/form-summary/FormSummaryAnswers.js.map +1 -0
  32. package/cjs/form/form-summary/FormSummaryEditLink.d.ts +17 -0
  33. package/cjs/form/form-summary/FormSummaryEditLink.js +49 -0
  34. package/cjs/form/form-summary/FormSummaryEditLink.js.map +1 -0
  35. package/cjs/form/form-summary/FormSummaryHeader.d.ts +9 -0
  36. package/cjs/form/form-summary/FormSummaryHeader.js +48 -0
  37. package/cjs/form/form-summary/FormSummaryHeader.js.map +1 -0
  38. package/cjs/form/form-summary/FormSummaryHeading.d.ts +14 -0
  39. package/cjs/form/form-summary/FormSummaryHeading.js +31 -0
  40. package/cjs/form/form-summary/FormSummaryHeading.js.map +1 -0
  41. package/cjs/form/form-summary/FormSummaryLabel.d.ts +6 -0
  42. package/cjs/form/form-summary/FormSummaryLabel.js +45 -0
  43. package/cjs/form/form-summary/FormSummaryLabel.js.map +1 -0
  44. package/cjs/form/form-summary/FormSummaryValue.d.ts +6 -0
  45. package/cjs/form/form-summary/FormSummaryValue.js +49 -0
  46. package/cjs/form/form-summary/FormSummaryValue.js.map +1 -0
  47. package/cjs/form/form-summary/index.d.ts +8 -0
  48. package/cjs/form/form-summary/index.js +24 -0
  49. package/cjs/form/form-summary/index.js.map +1 -0
  50. package/cjs/index.d.ts +3 -2
  51. package/cjs/index.js +5 -3
  52. package/cjs/index.js.map +1 -1
  53. package/cjs/layout/stack/Spacer.js +1 -1
  54. package/cjs/layout/stack/Spacer.js.map +1 -1
  55. package/cjs/util/hooks/descendants/descendant.js +10 -1
  56. package/cjs/util/hooks/descendants/descendant.js.map +1 -1
  57. package/cjs/util/hooks/descendants/useDescendant.js +0 -5
  58. package/cjs/util/hooks/descendants/useDescendant.js.map +1 -1
  59. package/esm/collapsible/Collapsible.context.d.ts +48 -0
  60. package/esm/collapsible/Collapsible.context.js +6 -0
  61. package/esm/collapsible/Collapsible.context.js.map +1 -0
  62. package/esm/collapsible/Collapsible.d.ts +48 -0
  63. package/esm/collapsible/Collapsible.js +62 -0
  64. package/esm/collapsible/Collapsible.js.map +1 -0
  65. package/esm/collapsible/Collapsible.types.d.ts +19 -0
  66. package/esm/collapsible/Collapsible.types.js +2 -0
  67. package/esm/collapsible/Collapsible.types.js.map +1 -0
  68. package/esm/collapsible/index.d.ts +3 -0
  69. package/esm/collapsible/index.js +5 -0
  70. package/esm/collapsible/index.js.map +1 -0
  71. package/esm/collapsible/parts/Collapsible.Content.d.ts +10 -0
  72. package/esm/collapsible/parts/Collapsible.Content.js +22 -0
  73. package/esm/collapsible/parts/Collapsible.Content.js.map +1 -0
  74. package/esm/collapsible/parts/Collapsible.Trigger.d.ts +10 -0
  75. package/esm/collapsible/parts/Collapsible.Trigger.js +23 -0
  76. package/esm/collapsible/parts/Collapsible.Trigger.js.map +1 -0
  77. package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js +2 -4
  78. package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
  79. package/esm/form/combobox/Input/Input.js +6 -1
  80. package/esm/form/combobox/Input/Input.js.map +1 -1
  81. package/esm/form/form-summary/FormSummary.d.ts +82 -0
  82. package/esm/form/form-summary/FormSummary.js +52 -0
  83. package/esm/form/form-summary/FormSummary.js.map +1 -0
  84. package/esm/form/form-summary/FormSummaryAnswer.d.ts +11 -0
  85. package/esm/form/form-summary/FormSummaryAnswer.js +19 -0
  86. package/esm/form/form-summary/FormSummaryAnswer.js.map +1 -0
  87. package/esm/form/form-summary/FormSummaryAnswers.d.ts +9 -0
  88. package/esm/form/form-summary/FormSummaryAnswers.js +19 -0
  89. package/esm/form/form-summary/FormSummaryAnswers.js.map +1 -0
  90. package/esm/form/form-summary/FormSummaryEditLink.d.ts +17 -0
  91. package/esm/form/form-summary/FormSummaryEditLink.js +20 -0
  92. package/esm/form/form-summary/FormSummaryEditLink.js.map +1 -0
  93. package/esm/form/form-summary/FormSummaryHeader.d.ts +9 -0
  94. package/esm/form/form-summary/FormSummaryHeader.js +19 -0
  95. package/esm/form/form-summary/FormSummaryHeader.js.map +1 -0
  96. package/esm/form/form-summary/FormSummaryHeading.d.ts +14 -0
  97. package/esm/form/form-summary/FormSummaryHeading.js +5 -0
  98. package/esm/form/form-summary/FormSummaryHeading.js.map +1 -0
  99. package/esm/form/form-summary/FormSummaryLabel.d.ts +6 -0
  100. package/esm/form/form-summary/FormSummaryLabel.js +19 -0
  101. package/esm/form/form-summary/FormSummaryLabel.js.map +1 -0
  102. package/esm/form/form-summary/FormSummaryValue.d.ts +6 -0
  103. package/esm/form/form-summary/FormSummaryValue.js +20 -0
  104. package/esm/form/form-summary/FormSummaryValue.js.map +1 -0
  105. package/esm/form/form-summary/index.d.ts +8 -0
  106. package/esm/form/form-summary/index.js +10 -0
  107. package/esm/form/form-summary/index.js.map +1 -0
  108. package/esm/index.d.ts +3 -2
  109. package/esm/index.js +2 -1
  110. package/esm/index.js.map +1 -1
  111. package/esm/layout/stack/Spacer.js +1 -1
  112. package/esm/layout/stack/Spacer.js.map +1 -1
  113. package/esm/util/hooks/descendants/descendant.js +10 -1
  114. package/esm/util/hooks/descendants/descendant.js.map +1 -1
  115. package/esm/util/hooks/descendants/useDescendant.js +0 -5
  116. package/esm/util/hooks/descendants/useDescendant.js.map +1 -1
  117. package/package.json +15 -4
  118. package/src/collapsible/Collapsible.context.tsx +32 -0
  119. package/src/collapsible/Collapsible.tsx +100 -0
  120. package/src/collapsible/Collapsible.types.ts +19 -0
  121. package/src/collapsible/index.ts +10 -0
  122. package/src/collapsible/parts/Collapsible.Content.tsx +39 -0
  123. package/src/collapsible/parts/Collapsible.Trigger.tsx +42 -0
  124. package/src/form/combobox/FilteredOptions/filteredOptionsContext.tsx +9 -1
  125. package/src/form/combobox/Input/Input.tsx +5 -0
  126. package/src/form/form-summary/FormSummary.tsx +106 -0
  127. package/src/form/form-summary/FormSummaryAnswer.tsx +27 -0
  128. package/src/form/form-summary/FormSummaryAnswers.tsx +25 -0
  129. package/src/form/form-summary/FormSummaryEditLink.tsx +35 -0
  130. package/src/form/form-summary/FormSummaryHeader.tsx +25 -0
  131. package/src/form/form-summary/FormSummaryHeading.tsx +23 -0
  132. package/src/form/form-summary/FormSummaryLabel.tsx +17 -0
  133. package/src/form/form-summary/FormSummaryValue.tsx +24 -0
  134. package/src/form/form-summary/index.ts +30 -0
  135. package/src/index.ts +16 -15
  136. package/src/layout/stack/Spacer.tsx +1 -1
  137. package/src/util/hooks/descendants/descendant.ts +15 -1
  138. package/src/util/hooks/descendants/useDescendant.tsx +0 -5
  139. package/src/accordion/accordion.stories.tsx +0 -286
  140. package/src/alert/alert.stories.tsx +0 -306
  141. package/src/button/button.stories.tsx +0 -185
  142. package/src/chat/chat.stories.tsx +0 -341
  143. package/src/chips/chips.stories.tsx +0 -260
  144. package/src/copybutton/copy-button.stories.tsx +0 -261
  145. package/src/date/datepicker/datepicker.stories.tsx +0 -614
  146. package/src/date/monthpicker/monthpicker.stories.tsx +0 -221
  147. package/src/dropdown/dropdown.stories.tsx +0 -124
  148. package/src/expansion-card/expansion-card.stories.tsx +0 -282
  149. package/src/form/checkbox/checkbox.stories.tsx +0 -281
  150. package/src/form/combobox/combobox.stories.tsx +0 -626
  151. package/src/form/confirmation-panel/confirmation-panel.stories.tsx +0 -128
  152. package/src/form/error-summary/error-summary.stories.tsx +0 -81
  153. package/src/form/fieldset/fieldset.stories.tsx +0 -157
  154. package/src/form/file-upload/file-upload-dropzone.stories.tsx +0 -123
  155. package/src/form/file-upload/file-upload-item.stories.tsx +0 -148
  156. package/src/form/file-upload/file-upload.stories.tsx +0 -248
  157. package/src/form/radio/radio.stories.tsx +0 -230
  158. package/src/form/search/search.stories.tsx +0 -238
  159. package/src/form/select/select.stories.tsx +0 -172
  160. package/src/form/switch/switch.stories.tsx +0 -171
  161. package/src/form/textarea/textarea.stories.tsx +0 -254
  162. package/src/form/textfield/text-field.stories.tsx +0 -143
  163. package/src/guide-panel/guidepanel.stories.tsx +0 -90
  164. package/src/help-text/help-text.stories.tsx +0 -91
  165. package/src/internal-header/header.stories.tsx +0 -229
  166. package/src/layout/bleed/Bleed.stories.tsx +0 -395
  167. package/src/layout/box/Box.stories.tsx +0 -380
  168. package/src/layout/grid/h-grid.stories.tsx +0 -122
  169. package/src/layout/page/Page.stories.tsx +0 -271
  170. package/src/layout/responsive/hide.stories.tsx +0 -80
  171. package/src/layout/responsive/show.stories.tsx +0 -80
  172. package/src/layout/sidemal-test/navno-sidemal.stories.tsx +0 -69
  173. package/src/layout/stack/stack.stories.tsx +0 -183
  174. package/src/link/stories/link.stories.tsx +0 -304
  175. package/src/link-panel/link-panel.stories.tsx +0 -59
  176. package/src/list/list.stories.tsx +0 -280
  177. package/src/loader/loader.stories.tsx +0 -82
  178. package/src/modal/modal.stories.tsx +0 -391
  179. package/src/pagination/pagination.stories.tsx +0 -110
  180. package/src/popover/popover.stories.tsx +0 -113
  181. package/src/portal/Portal.stories.tsx +0 -102
  182. package/src/read-more/readmore.stories.tsx +0 -91
  183. package/src/skeleton/skeleton.stories.tsx +0 -130
  184. package/src/stepper/stepper.stories.tsx +0 -200
  185. package/src/table/stories/table-1.stories.tsx +0 -292
  186. package/src/table/stories/table-2-expandable.stories.tsx +0 -298
  187. package/src/table/stories/table-3-async.stories.tsx +0 -179
  188. package/src/table/stories/tests/table.stories.tsx +0 -102
  189. package/src/tabs/Tabs.stories.tsx +0 -311
  190. package/src/tag/tag.stories.tsx +0 -126
  191. package/src/timeline/timeline.stories.tsx +0 -445
  192. package/src/toggle-group/ToggleGroup.stories.tsx +0 -198
  193. package/src/tooltip/tooltip.stories.tsx +0 -101
  194. package/src/typography/stories/bodylong.stories.tsx +0 -209
  195. package/src/typography/stories/bodyshort.stories.tsx +0 -208
  196. package/src/typography/stories/detail.stories.tsx +0 -115
  197. package/src/typography/stories/error-message.stories.tsx +0 -122
  198. package/src/typography/stories/heading.stories.tsx +0 -169
  199. package/src/typography/stories/label.stories.tsx +0 -131
  200. package/src/util/hooks/descendants/descendant.stories.tsx +0 -147
@@ -1,626 +0,0 @@
1
- import { Meta, StoryFn, StoryObj } from "@storybook/react";
2
- import { expect, fn, userEvent, within } from "@storybook/test";
3
- import React, { useMemo, useRef, useState } from "react";
4
- import { Chips } from "../../chips";
5
- import { TextField } from "../textfield";
6
- import { ComboboxProps, UNSAFE_Combobox } from "./index";
7
-
8
- export default {
9
- title: "ds-react/Combobox",
10
- component: UNSAFE_Combobox,
11
- decorators: [(story) => <div style={{ width: "300px" }}>{story()}</div>],
12
- } satisfies Meta<typeof UNSAFE_Combobox>;
13
-
14
- type StoryObject = StoryObj<typeof UNSAFE_Combobox>;
15
- type StoryFunction = StoryFn<typeof UNSAFE_Combobox>;
16
-
17
- const options = [
18
- "banana",
19
- "apple",
20
- "apple pie",
21
- "tangerine",
22
- "pear",
23
- "grape",
24
- "kiwi",
25
- "mango",
26
- "passion fruit",
27
- "pineapple",
28
- "strawberry",
29
- "watermelon",
30
- "grape fruit",
31
- ];
32
-
33
- export const Default: StoryFunction = (props) => (
34
- <UNSAFE_Combobox {...props} id="combobox" />
35
- );
36
-
37
- Default.args = {
38
- options,
39
- label: "Hva er dine favorittfrukter?",
40
- shouldAutocomplete: true,
41
- isLoading: false,
42
- isMultiSelect: false,
43
- allowNewValues: false,
44
- };
45
- Default.argTypes = {
46
- isListOpen: {
47
- control: { type: "boolean" },
48
- },
49
- maxSelected: {
50
- control: { type: "number" },
51
- },
52
- size: {
53
- options: ["medium", "small"],
54
- defaultValue: "medium",
55
- control: { type: "radio" },
56
- },
57
- };
58
-
59
- export const MultiSelect: StoryFunction = (props) => {
60
- return (
61
- <UNSAFE_Combobox
62
- id="combobox-with-multiselect"
63
- label="Komboboks - velg flere"
64
- options={props.options}
65
- isMultiSelect={props.isMultiSelect}
66
- size={props.size}
67
- />
68
- );
69
- };
70
-
71
- MultiSelect.args = {
72
- options,
73
- isMultiSelect: true,
74
- size: "medium",
75
- };
76
-
77
- export const MultiSelectWithComplexOptions: StoryFunction = (props) => {
78
- const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
79
- return (
80
- <>
81
- <UNSAFE_Combobox
82
- {...props}
83
- options={props.options.map((option) => ({
84
- ...option,
85
- label: `${option.label} [${option.value}]`,
86
- }))}
87
- id="combobox-with-complex-options"
88
- label="Velg temakoder"
89
- allowNewValues
90
- onToggleSelected={(value, isSelected) =>
91
- isSelected
92
- ? setSelectedOptions([...selectedOptions, value])
93
- : setSelectedOptions(selectedOptions.filter((o) => o !== value))
94
- }
95
- selectedOptions={selectedOptions}
96
- />
97
- </>
98
- );
99
- };
100
-
101
- MultiSelectWithComplexOptions.args = {
102
- options: [
103
- { label: "Hjelpemidler", value: "HJE" },
104
- { label: "Oppfølging", value: "OPP" },
105
- { label: "Sykepenger", value: "SYK" },
106
- { label: "Sykemelding", value: "SYM" },
107
- { label: "Foreldre- og svangerskapspenger", value: "FOR" },
108
- { label: "Arbeidsavklaringspenger", value: "AAP" },
109
- { label: "Uføretrygd", value: "UFO" },
110
- { label: "Pensjon", value: "PEN" },
111
- { label: "Barnetrygd", value: "BAR" },
112
- { label: "Kontantstøtte", value: "KON" },
113
- { label: "Bostøtte", value: "BOS" },
114
- { label: "Barnebidrag", value: "BBI" },
115
- { label: "Bidragsforskudd", value: "BIF" },
116
- { label: "Grunn- og hjelpestønad", value: "GRU" },
117
- ],
118
- isMultiSelect: true,
119
- size: "medium",
120
- };
121
-
122
- export const WithAddNewOptions: StoryFunction = (props) => {
123
- return (
124
- <UNSAFE_Combobox
125
- id="combobox-with-add-new-options"
126
- label="Komboboks med mulighet for å legge til nye verdier"
127
- options={props.options}
128
- allowNewValues={props.allowNewValues}
129
- shouldAutocomplete={props.shouldAutocomplete}
130
- />
131
- );
132
- };
133
-
134
- WithAddNewOptions.args = {
135
- options,
136
- allowNewValues: true,
137
- shouldAutocomplete: true,
138
- };
139
-
140
- export const MultiSelectWithAddNewOptions: StoryFunction = (props) => {
141
- return (
142
- <UNSAFE_Combobox
143
- id="combobox-with-multiselect-and-add-new-options"
144
- isMultiSelect={props.isMultiSelect}
145
- label="Multiselect komboboks med mulighet for å legge til nye verdier"
146
- options={props.options}
147
- allowNewValues={props.allowNewValues}
148
- />
149
- );
150
- };
151
-
152
- MultiSelectWithAddNewOptions.args = {
153
- allowNewValues: true,
154
- isMultiSelect: true,
155
- options,
156
- shouldAutocomplete: false,
157
- };
158
-
159
- export const MultiSelectWithExternalChips: StoryFn<{
160
- controlled: boolean;
161
- options: ComboboxProps["options"];
162
- }> = (props) => {
163
- const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
164
- const [value, setValue] = useState("");
165
-
166
- const toggleSelected = (option: string) =>
167
- selectedOptions.includes(option)
168
- ? setSelectedOptions(selectedOptions.filter((opt) => opt !== option))
169
- : setSelectedOptions([...selectedOptions, option]);
170
- return (
171
- <>
172
- {selectedOptions && (
173
- <Chips>
174
- {selectedOptions.map((option) => (
175
- <Chips.Removable
176
- key={option}
177
- onPointerUp={() => toggleSelected(option)}
178
- onKeyUp={(e) => e.key === "Enter" && toggleSelected(option)}
179
- >
180
- {option}
181
- </Chips.Removable>
182
- ))}
183
- </Chips>
184
- )}
185
- <UNSAFE_Combobox
186
- id="combobox-with-external-chips"
187
- options={props.options}
188
- selectedOptions={selectedOptions}
189
- onToggleSelected={(option) => toggleSelected(option)}
190
- isMultiSelect
191
- value={props.controlled ? value : undefined}
192
- onChange={(event) =>
193
- props.controlled
194
- ? setValue(event?.currentTarget.value || "")
195
- : undefined
196
- }
197
- label="Komboboks"
198
- size="medium"
199
- shouldShowSelectedOptions={false}
200
- />
201
- </>
202
- );
203
- };
204
-
205
- MultiSelectWithExternalChips.args = {
206
- controlled: false,
207
- options,
208
- };
209
-
210
- export const Loading: StoryFunction = (props) => (
211
- <UNSAFE_Combobox
212
- id="combobox-with-loading-indicator"
213
- label="Komboboks (laster)"
214
- options={[]}
215
- selectedOptions={[]}
216
- isListOpen={props.isListOpen}
217
- isLoading={props.isLoading}
218
- />
219
- );
220
-
221
- Loading.args = {
222
- isLoading: true,
223
- isListOpen: true,
224
- };
225
-
226
- export const ComboboxWithNoHits: StoryFunction = (props) => {
227
- const [value, setValue] = useState(props.value);
228
- return (
229
- <UNSAFE_Combobox
230
- id="combobox-with-no-hits"
231
- label="Komboboks (uten søketreff)"
232
- options={props.options}
233
- value={value}
234
- onChange={(event) => setValue(event?.currentTarget.value)}
235
- isListOpen={true}
236
- />
237
- );
238
- };
239
-
240
- ComboboxWithNoHits.args = {
241
- options,
242
- value: "Orange",
243
- };
244
-
245
- export const Controlled: StoryFn<{
246
- value: string;
247
- options: string[];
248
- initialSelectedOptions: string[];
249
- }> = (props) => {
250
- const [value, setValue] = useState(props.value);
251
- const [selectedOptions, setSelectedOptions] = useState(
252
- props.initialSelectedOptions,
253
- );
254
- const filteredOptions = useMemo(
255
- () => props.options.filter((option) => option.includes(value)),
256
- [props.options, value],
257
- );
258
-
259
- const onToggleSelected = (option: string, isSelected: boolean) => {
260
- if (isSelected) {
261
- setSelectedOptions([...selectedOptions, option]);
262
- } else {
263
- setSelectedOptions(selectedOptions.filter((o) => o !== option));
264
- }
265
- };
266
-
267
- return (
268
- <>
269
- <TextField
270
- label="Overstyr value"
271
- onChange={(event) => setValue(event.target.value)}
272
- value={value}
273
- />
274
- <br />
275
- <UNSAFE_Combobox
276
- label="Hva er dine favorittfrukter?"
277
- id="combobox-controlled"
278
- filteredOptions={filteredOptions}
279
- isMultiSelect
280
- options={props.options}
281
- onChange={(event) => setValue(event?.target.value || "")}
282
- onToggleSelected={onToggleSelected}
283
- selectedOptions={selectedOptions}
284
- value={value}
285
- />
286
- </>
287
- );
288
- };
289
-
290
- Controlled.args = {
291
- value: "apple",
292
- options,
293
- initialSelectedOptions: ["passion fruit", "grape fruit"],
294
- };
295
-
296
- export const ComboboxSizes = () => (
297
- <>
298
- <UNSAFE_Combobox
299
- label="Hva er dine favorittfrukter?"
300
- description="Medium single-select"
301
- options={options}
302
- />
303
- <br />
304
- <UNSAFE_Combobox
305
- label="Hva er dine favorittfrukter?"
306
- description="Small single-select"
307
- options={options}
308
- size="small"
309
- />
310
- <br />
311
- <UNSAFE_Combobox
312
- label="Hva er dine favorittfrukter?"
313
- description="Medium multiselect"
314
- options={options}
315
- isMultiSelect
316
- allowNewValues
317
- />
318
- <br />
319
- <UNSAFE_Combobox
320
- label="Hva er dine favorittfrukter?"
321
- description="Small multiselect"
322
- options={options}
323
- isMultiSelect
324
- size="small"
325
- allowNewValues
326
- />
327
- </>
328
- );
329
-
330
- export const MaxSelectedOptions: StoryFunction = () => {
331
- const [value, setValue] = useState<string | undefined>("");
332
- const [selectedOptions, setSelectedOptions] = useState([
333
- options[0],
334
- options[1],
335
- ]);
336
- const comboboxRef = useRef<HTMLInputElement>(null);
337
- return (
338
- <UNSAFE_Combobox
339
- id="combobox-with-max-selected-options"
340
- label="Komboboks med begrenset antall valg"
341
- options={options}
342
- maxSelected={{ limit: 2 }}
343
- selectedOptions={selectedOptions}
344
- onToggleSelected={(option, isSelected) =>
345
- isSelected
346
- ? setSelectedOptions([...selectedOptions, option])
347
- : setSelectedOptions(selectedOptions.filter((o) => o !== option))
348
- }
349
- isMultiSelect
350
- allowNewValues
351
- isListOpen={comboboxRef.current ? undefined : true}
352
- value={value}
353
- onChange={(event) => setValue(event?.target.value)}
354
- ref={comboboxRef}
355
- />
356
- );
357
- };
358
-
359
- export const WithError: StoryFunction = (props) => {
360
- const [hasSelectedValue, setHasSelectedValue] = useState(false);
361
- const [isLoading, setIsLoading] = useState(false);
362
- return (
363
- <UNSAFE_Combobox
364
- filteredOptions={isLoading ? [] : undefined}
365
- options={options}
366
- label="Hva er dine favorittfrukter?"
367
- error={!hasSelectedValue && props.error}
368
- isLoading={isLoading}
369
- onChange={() => {
370
- setIsLoading(true);
371
- setTimeout(() => setIsLoading(false), 2000);
372
- }}
373
- onToggleSelected={(_, isSelected) => setHasSelectedValue(isSelected)}
374
- />
375
- );
376
- };
377
- WithError.args = {
378
- error: "Du må velge en favorittfrukt.",
379
- };
380
-
381
- function sleep(ms: number) {
382
- return new Promise((resolve) => setTimeout(resolve, ms));
383
- }
384
-
385
- export const CancelInputTest: StoryObject = {
386
- render: () => {
387
- return (
388
- <UNSAFE_Combobox options={options} label="Hva er dine favorittfrukter?" />
389
- );
390
- },
391
- play: async ({ canvasElement }) => {
392
- const canvas = within(canvasElement);
393
-
394
- const input = canvas.getByLabelText("Hva er dine favorittfrukter?");
395
-
396
- userEvent.click(input);
397
- await userEvent.type(input, "apple", { delay: 200 });
398
- await sleep(1000);
399
-
400
- userEvent.keyboard("{ArrowDown}");
401
- await sleep(1000);
402
- userEvent.keyboard("{Escape}");
403
- await sleep(1000);
404
- userEvent.keyboard("{ArrowDown}");
405
- await sleep(500);
406
- const banana = canvas.getByText("banana");
407
- userEvent.click(banana);
408
- },
409
- };
410
-
411
- export const RemoveSelectedMultiSelectTest: StoryObject = {
412
- render: () => {
413
- return (
414
- <UNSAFE_Combobox
415
- options={options}
416
- label="Hva er dine favorittfrukter?"
417
- isMultiSelect
418
- />
419
- );
420
- },
421
- play: async ({ canvasElement }) => {
422
- const canvas = within(canvasElement);
423
-
424
- const input = canvas.getByLabelText("Hva er dine favorittfrukter?");
425
-
426
- userEvent.click(input);
427
- await userEvent.type(input, "apple", { delay: 200 });
428
- await sleep(250);
429
-
430
- userEvent.keyboard("{ArrowDown}");
431
- await sleep(250);
432
- userEvent.keyboard("{Enter}");
433
- await sleep(250);
434
- userEvent.keyboard("{Escape}");
435
- await sleep(250);
436
-
437
- userEvent.click(input);
438
- await userEvent.type(input, "banana", { delay: 200 });
439
- await sleep(250);
440
-
441
- userEvent.keyboard("{ArrowDown}");
442
- await sleep(250);
443
- userEvent.keyboard("{Enter}");
444
- await sleep(250);
445
-
446
- const appleSlett = canvas.getByLabelText("apple slett");
447
- appleSlett.focus();
448
- await sleep(250);
449
- userEvent.click(appleSlett);
450
- await sleep(250);
451
- const appleOption = canvas.getByRole("option", {
452
- name: "apple",
453
- selected: false,
454
- });
455
- expect(appleOption).toBeVisible();
456
- userEvent.keyboard("{Escape}");
457
- await sleep(250);
458
- expect(appleOption).not.toBeVisible();
459
-
460
- const bananaSlett = canvas.getByLabelText("banana slett");
461
- expect(bananaSlett).toBeInTheDocument();
462
- const appleSlettAgain = canvas.queryByLabelText("apple slett");
463
- expect(appleSlettAgain).not.toBeInTheDocument();
464
- },
465
- };
466
-
467
- export const AddWhenAddNewDisabledTest: StoryObject = {
468
- render: () => {
469
- return (
470
- <UNSAFE_Combobox
471
- options={options}
472
- label="Hva er dine favorittfrukter?"
473
- isMultiSelect
474
- />
475
- );
476
- },
477
- play: async ({ canvasElement }) => {
478
- const canvas = within(canvasElement);
479
-
480
- const input = canvas.getByLabelText("Hva er dine favorittfrukter?");
481
-
482
- userEvent.click(input);
483
- await userEvent.type(input, "aaa", { delay: 200 });
484
- await sleep(250);
485
-
486
- userEvent.keyboard("{ArrowDown}");
487
- await sleep(250);
488
- userEvent.keyboard("{ArrowDown}");
489
- await sleep(250);
490
- userEvent.keyboard("{Enter}");
491
- await sleep(250);
492
- userEvent.keyboard("{Escape}");
493
- await sleep(250);
494
-
495
- const invalidSelect = canvas.queryByLabelText("aaa slett");
496
- expect(invalidSelect).not.toBeInTheDocument();
497
- },
498
- };
499
-
500
- export const TestThatCallbacksOnlyFireWhenExpected: StoryObj<{
501
- onChange: ReturnType<typeof fn>;
502
- onClear: ReturnType<typeof fn>;
503
- onToggleSelected: ReturnType<typeof fn>;
504
- }> = {
505
- args: {
506
- onChange: fn(),
507
- onClear: fn(),
508
- onToggleSelected: fn(),
509
- },
510
- render: (props) => {
511
- return (
512
- <UNSAFE_Combobox
513
- options={options}
514
- label="Hva er dine favorittfrukter?"
515
- {...props}
516
- />
517
- );
518
- },
519
- play: async ({ canvasElement, args }) => {
520
- args.onToggleSelected.mockClear();
521
- args.onClear.mockClear();
522
- args.onChange.mockClear();
523
- const canvas = within(canvasElement);
524
-
525
- const input = canvas.getByLabelText("Hva er dine favorittfrukter?");
526
- const searchWord = "tangerine";
527
-
528
- userEvent.click(input);
529
- await userEvent.type(input, searchWord, { delay: 200 });
530
- await sleep(250);
531
- userEvent.keyboard("{ArrowDown}");
532
- await sleep(250);
533
- userEvent.keyboard("{Enter}");
534
- await sleep(250);
535
- expect(args.onClear.mock.calls).toHaveLength(1);
536
- expect(args.onToggleSelected.mock.calls).toHaveLength(1);
537
- expect(args.onChange.mock.calls).toHaveLength(searchWord.length + 1);
538
- },
539
- };
540
-
541
- export const TestCasingWhenAutoCompleting = {
542
- args: {
543
- onChange: fn(),
544
- onClear: fn(),
545
- onToggleSelected: fn(),
546
- },
547
- render: (props) => {
548
- return (
549
- <UNSAFE_Combobox
550
- options={["Camel Case", "lowercase", "UPPERCASE"]}
551
- label="Liker du best store eller små bokstaver?"
552
- shouldAutocomplete
553
- allowNewValues
554
- {...props}
555
- />
556
- );
557
- },
558
- play: async ({ canvasElement }) => {
559
- const canvas = within(canvasElement);
560
- const input = canvas.getByRole<HTMLInputElement>("combobox");
561
-
562
- // With exisiting option
563
- userEvent.click(input);
564
- await userEvent.type(input, "cAmEl CaSe", { delay: 250 });
565
- await sleep(250);
566
- expect(input.value).toBe("cAmEl CaSe");
567
- await userEvent.type(input, "{Enter}");
568
- await sleep(250);
569
- const chips = canvas.getAllByRole("list")[0];
570
- const selectedUpperCaseChip = within(chips).getAllByRole("listitem")[0];
571
- expect(selectedUpperCaseChip).toHaveTextContent("Camel Case"); // A weird issue is preventing the accessible name from being used in the test, even if it works in VoiceOver
572
-
573
- // With custom option
574
- userEvent.click(input);
575
- await userEvent.type(input, "cAmEl{Backspace}", { delay: 250 });
576
- await sleep(250);
577
- expect(input.value).toBe("cAmEl");
578
- await userEvent.type(input, "{Enter}");
579
- await sleep(250);
580
- const selectedNewValueChip = within(chips).getAllByRole("listitem")[0];
581
- expect(selectedNewValueChip).toHaveTextContent("cAmEl"); // A weird issue is preventing the accessible name from being used in the test, even if it works in VoiceOver
582
- },
583
- };
584
-
585
- export const TestHoverAndFocusSwitching: StoryObject = {
586
- render: () => {
587
- return (
588
- <UNSAFE_Combobox options={options} label="Hva er dine favorittfrukter?" />
589
- );
590
- },
591
- play: async ({ canvasElement }) => {
592
- const canvas = within(canvasElement);
593
-
594
- await sleep(500);
595
-
596
- const getInput = () =>
597
- canvas.getByRole("combobox", {
598
- name: "Hva er dine favorittfrukter?",
599
- });
600
-
601
- userEvent.click(getInput());
602
- expect(getInput().getAttribute("aria-expanded")).toEqual("false");
603
- expect(getInput().getAttribute("aria-activedescendant")).toBeNull();
604
-
605
- await sleep(250);
606
- userEvent.keyboard("{ArrowDown}");
607
- await sleep(250);
608
- const bananaOption = canvas.getByRole("option", { name: "banana" });
609
- expect(getInput().getAttribute("aria-activedescendant")).toBe(
610
- bananaOption.getAttribute("id"),
611
- );
612
-
613
- userEvent.keyboard("{ArrowDown}");
614
- await sleep(250);
615
- const appleOption = canvas.getByRole("option", { name: "apple" });
616
- expect(getInput().getAttribute("aria-activedescendant")).toBe(
617
- appleOption.getAttribute("id"),
618
- );
619
-
620
- userEvent.hover(bananaOption);
621
- await sleep(250);
622
- expect(getInput().getAttribute("aria-activedescendant")).toBe(
623
- bananaOption.getAttribute("id"),
624
- );
625
- },
626
- };