@navikt/ds-react 6.5.0 → 6.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js +2 -4
- package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
- package/cjs/form/combobox/Input/Input.js +6 -1
- package/cjs/form/combobox/Input/Input.js.map +1 -1
- package/cjs/form/form-summary/FormSummary.d.ts +82 -0
- package/cjs/form/form-summary/FormSummary.js +81 -0
- package/cjs/form/form-summary/FormSummary.js.map +1 -0
- package/cjs/form/form-summary/FormSummaryAnswer.d.ts +11 -0
- package/cjs/form/form-summary/FormSummaryAnswer.js +25 -0
- package/cjs/form/form-summary/FormSummaryAnswer.js.map +1 -0
- package/cjs/form/form-summary/FormSummaryAnswers.d.ts +9 -0
- package/cjs/form/form-summary/FormSummaryAnswers.js +48 -0
- package/cjs/form/form-summary/FormSummaryAnswers.js.map +1 -0
- package/cjs/form/form-summary/FormSummaryEditLink.d.ts +17 -0
- package/cjs/form/form-summary/FormSummaryEditLink.js +49 -0
- package/cjs/form/form-summary/FormSummaryEditLink.js.map +1 -0
- package/cjs/form/form-summary/FormSummaryHeader.d.ts +9 -0
- package/cjs/form/form-summary/FormSummaryHeader.js +48 -0
- package/cjs/form/form-summary/FormSummaryHeader.js.map +1 -0
- package/cjs/form/form-summary/FormSummaryHeading.d.ts +14 -0
- package/cjs/form/form-summary/FormSummaryHeading.js +31 -0
- package/cjs/form/form-summary/FormSummaryHeading.js.map +1 -0
- package/cjs/form/form-summary/FormSummaryLabel.d.ts +6 -0
- package/cjs/form/form-summary/FormSummaryLabel.js +45 -0
- package/cjs/form/form-summary/FormSummaryLabel.js.map +1 -0
- package/cjs/form/form-summary/FormSummaryValue.d.ts +6 -0
- package/cjs/form/form-summary/FormSummaryValue.js +49 -0
- package/cjs/form/form-summary/FormSummaryValue.js.map +1 -0
- package/cjs/form/form-summary/index.d.ts +8 -0
- package/cjs/form/form-summary/index.js +24 -0
- package/cjs/form/form-summary/index.js.map +1 -0
- package/cjs/index.d.ts +3 -2
- package/cjs/index.js +5 -3
- package/cjs/index.js.map +1 -1
- package/cjs/util/hooks/descendants/descendant.js +10 -1
- package/cjs/util/hooks/descendants/descendant.js.map +1 -1
- package/cjs/util/hooks/descendants/useDescendant.js +0 -5
- package/cjs/util/hooks/descendants/useDescendant.js.map +1 -1
- package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js +2 -4
- package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
- package/esm/form/combobox/Input/Input.js +6 -1
- package/esm/form/combobox/Input/Input.js.map +1 -1
- package/esm/form/form-summary/FormSummary.d.ts +82 -0
- package/esm/form/form-summary/FormSummary.js +52 -0
- package/esm/form/form-summary/FormSummary.js.map +1 -0
- package/esm/form/form-summary/FormSummaryAnswer.d.ts +11 -0
- package/esm/form/form-summary/FormSummaryAnswer.js +19 -0
- package/esm/form/form-summary/FormSummaryAnswer.js.map +1 -0
- package/esm/form/form-summary/FormSummaryAnswers.d.ts +9 -0
- package/esm/form/form-summary/FormSummaryAnswers.js +19 -0
- package/esm/form/form-summary/FormSummaryAnswers.js.map +1 -0
- package/esm/form/form-summary/FormSummaryEditLink.d.ts +17 -0
- package/esm/form/form-summary/FormSummaryEditLink.js +20 -0
- package/esm/form/form-summary/FormSummaryEditLink.js.map +1 -0
- package/esm/form/form-summary/FormSummaryHeader.d.ts +9 -0
- package/esm/form/form-summary/FormSummaryHeader.js +19 -0
- package/esm/form/form-summary/FormSummaryHeader.js.map +1 -0
- package/esm/form/form-summary/FormSummaryHeading.d.ts +14 -0
- package/esm/form/form-summary/FormSummaryHeading.js +5 -0
- package/esm/form/form-summary/FormSummaryHeading.js.map +1 -0
- package/esm/form/form-summary/FormSummaryLabel.d.ts +6 -0
- package/esm/form/form-summary/FormSummaryLabel.js +19 -0
- package/esm/form/form-summary/FormSummaryLabel.js.map +1 -0
- package/esm/form/form-summary/FormSummaryValue.d.ts +6 -0
- package/esm/form/form-summary/FormSummaryValue.js +20 -0
- package/esm/form/form-summary/FormSummaryValue.js.map +1 -0
- package/esm/form/form-summary/index.d.ts +8 -0
- package/esm/form/form-summary/index.js +10 -0
- package/esm/form/form-summary/index.js.map +1 -0
- package/esm/index.d.ts +3 -2
- package/esm/index.js +2 -1
- package/esm/index.js.map +1 -1
- package/esm/util/hooks/descendants/descendant.js +10 -1
- package/esm/util/hooks/descendants/descendant.js.map +1 -1
- package/esm/util/hooks/descendants/useDescendant.js +0 -5
- package/esm/util/hooks/descendants/useDescendant.js.map +1 -1
- package/package.json +15 -4
- package/src/form/combobox/FilteredOptions/filteredOptionsContext.tsx +9 -1
- package/src/form/combobox/Input/Input.tsx +5 -0
- package/src/form/form-summary/FormSummary.tsx +106 -0
- package/src/form/form-summary/FormSummaryAnswer.tsx +27 -0
- package/src/form/form-summary/FormSummaryAnswers.tsx +25 -0
- package/src/form/form-summary/FormSummaryEditLink.tsx +35 -0
- package/src/form/form-summary/FormSummaryHeader.tsx +25 -0
- package/src/form/form-summary/FormSummaryHeading.tsx +23 -0
- package/src/form/form-summary/FormSummaryLabel.tsx +17 -0
- package/src/form/form-summary/FormSummaryValue.tsx +24 -0
- package/src/form/form-summary/index.ts +30 -0
- package/src/index.ts +16 -15
- package/src/util/hooks/descendants/descendant.ts +15 -1
- package/src/util/hooks/descendants/useDescendant.tsx +0 -5
- package/src/accordion/accordion.stories.tsx +0 -286
- package/src/alert/alert.stories.tsx +0 -306
- package/src/button/button.stories.tsx +0 -185
- package/src/chat/chat.stories.tsx +0 -341
- package/src/chips/chips.stories.tsx +0 -260
- package/src/copybutton/copy-button.stories.tsx +0 -261
- package/src/date/datepicker/datepicker.stories.tsx +0 -614
- package/src/date/monthpicker/monthpicker.stories.tsx +0 -221
- package/src/dropdown/dropdown.stories.tsx +0 -124
- package/src/expansion-card/expansion-card.stories.tsx +0 -282
- package/src/form/checkbox/checkbox.stories.tsx +0 -281
- package/src/form/combobox/combobox.stories.tsx +0 -626
- package/src/form/confirmation-panel/confirmation-panel.stories.tsx +0 -128
- package/src/form/error-summary/error-summary.stories.tsx +0 -81
- package/src/form/fieldset/fieldset.stories.tsx +0 -157
- package/src/form/file-upload/file-upload-dropzone.stories.tsx +0 -123
- package/src/form/file-upload/file-upload-item.stories.tsx +0 -148
- package/src/form/file-upload/file-upload.stories.tsx +0 -248
- package/src/form/radio/radio.stories.tsx +0 -230
- package/src/form/search/search.stories.tsx +0 -238
- package/src/form/select/select.stories.tsx +0 -172
- package/src/form/switch/switch.stories.tsx +0 -171
- package/src/form/textarea/textarea.stories.tsx +0 -254
- package/src/form/textfield/text-field.stories.tsx +0 -143
- package/src/guide-panel/guidepanel.stories.tsx +0 -90
- package/src/help-text/help-text.stories.tsx +0 -91
- package/src/internal-header/header.stories.tsx +0 -229
- package/src/layout/bleed/Bleed.stories.tsx +0 -395
- package/src/layout/box/Box.stories.tsx +0 -380
- package/src/layout/grid/h-grid.stories.tsx +0 -122
- package/src/layout/page/Page.stories.tsx +0 -271
- package/src/layout/responsive/hide.stories.tsx +0 -80
- package/src/layout/responsive/show.stories.tsx +0 -80
- package/src/layout/sidemal-test/navno-sidemal.stories.tsx +0 -69
- package/src/layout/stack/stack.stories.tsx +0 -183
- package/src/link/stories/link.stories.tsx +0 -304
- package/src/link-panel/link-panel.stories.tsx +0 -59
- package/src/list/list.stories.tsx +0 -280
- package/src/loader/loader.stories.tsx +0 -82
- package/src/modal/modal.stories.tsx +0 -391
- package/src/pagination/pagination.stories.tsx +0 -110
- package/src/popover/popover.stories.tsx +0 -113
- package/src/portal/Portal.stories.tsx +0 -102
- package/src/read-more/readmore.stories.tsx +0 -91
- package/src/skeleton/skeleton.stories.tsx +0 -130
- package/src/stepper/stepper.stories.tsx +0 -200
- package/src/table/stories/table-1.stories.tsx +0 -292
- package/src/table/stories/table-2-expandable.stories.tsx +0 -298
- package/src/table/stories/table-3-async.stories.tsx +0 -179
- package/src/table/stories/tests/table.stories.tsx +0 -102
- package/src/tabs/Tabs.stories.tsx +0 -311
- package/src/tag/tag.stories.tsx +0 -126
- package/src/timeline/timeline.stories.tsx +0 -445
- package/src/toggle-group/ToggleGroup.stories.tsx +0 -198
- package/src/tooltip/tooltip.stories.tsx +0 -101
- package/src/typography/stories/bodylong.stories.tsx +0 -209
- package/src/typography/stories/bodyshort.stories.tsx +0 -208
- package/src/typography/stories/detail.stories.tsx +0 -115
- package/src/typography/stories/error-message.stories.tsx +0 -122
- package/src/typography/stories/heading.stories.tsx +0 -169
- package/src/typography/stories/label.stories.tsx +0 -131
- 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
|
-
};
|