@navikt/ds-react 0.16.20 → 0.17.2-beta.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/accordion/Accordion.js +5 -1
- package/cjs/accordion/AccordionContent.js +5 -1
- package/cjs/accordion/AccordionHeader.js +5 -1
- package/cjs/accordion/AccordionItem.js +5 -1
- package/cjs/accordion/index.js +5 -1
- package/cjs/alert/Alert.js +5 -1
- package/cjs/alert/index.js +5 -1
- package/cjs/button/Button.js +5 -1
- package/cjs/button/index.js +5 -1
- package/cjs/card/MicroCard.js +5 -1
- package/cjs/card/index.js +5 -1
- package/cjs/form/ConfirmationPanel.js +5 -1
- package/cjs/form/Fieldset/Fieldset.js +5 -1
- package/cjs/form/Select.js +5 -1
- package/cjs/form/Switch.js +5 -1
- package/cjs/form/TextField.js +5 -1
- package/cjs/form/Textarea.js +5 -1
- package/cjs/form/checkbox/Checkbox.js +5 -1
- package/cjs/form/checkbox/CheckboxGroup.js +5 -1
- package/cjs/form/error-summary/ErrorSummary.js +5 -1
- package/cjs/form/error-summary/ErrorSummaryItem.js +5 -1
- package/cjs/form/error-summary/index.js +5 -1
- package/cjs/form/index.js +3 -3
- package/cjs/form/radio/Radio.js +5 -1
- package/cjs/form/radio/RadioGroup.js +5 -1
- package/cjs/form/search/Search.js +104 -0
- package/cjs/form/{search-field/SearchFieldButton.js → search/SearchButton.js} +19 -11
- package/cjs/form/search/index.js +8 -0
- package/cjs/form/search/package.json +6 -0
- package/cjs/form/search/useSearch.js +31 -0
- package/cjs/grid/Cell.js +5 -1
- package/cjs/grid/ContentContainer.js +5 -1
- package/cjs/grid/Grid.js +5 -1
- package/cjs/grid/index.js +5 -1
- package/cjs/guide-panel/Guide.js +5 -1
- package/cjs/guide-panel/GuidePanel.js +5 -1
- package/cjs/guide-panel/index.js +5 -1
- package/cjs/help-text/HelpText.js +5 -1
- package/cjs/help-text/index.js +5 -1
- package/cjs/index.js +5 -1
- package/cjs/link/Link.js +5 -1
- package/cjs/link/index.js +5 -1
- package/cjs/link-panel/LinkPanel.js +5 -1
- package/cjs/link-panel/LinkPanelDescription.js +5 -1
- package/cjs/link-panel/LinkPanelTitle.js +5 -1
- package/cjs/link-panel/index.js +5 -1
- package/cjs/loader/Loader.js +5 -1
- package/cjs/loader/index.js +5 -1
- package/cjs/menu/Menu.js +5 -1
- package/cjs/menu/MenuCollapse.js +5 -1
- package/cjs/menu/MenuItem.js +5 -1
- package/cjs/modal/Modal.js +5 -1
- package/cjs/modal/ModalContent.js +5 -1
- package/cjs/modal/index.js +5 -1
- package/cjs/page-header/PageHeader.js +5 -1
- package/cjs/page-header/index.js +5 -1
- package/cjs/pagination/index.js +5 -1
- package/cjs/panel/Panel.js +5 -1
- package/cjs/panel/index.js +5 -1
- package/cjs/popover/Popover.js +5 -1
- package/cjs/popover/PopoverContent.js +5 -1
- package/cjs/popover/index.js +5 -1
- package/cjs/speech-bubble/Bubble.js +5 -1
- package/cjs/speech-bubble/SpeechBubble.js +5 -1
- package/cjs/speech-bubble/index.js +5 -1
- package/cjs/step-indicator/Step.js +5 -1
- package/cjs/step-indicator/StepIndicator.js +5 -1
- package/cjs/step-indicator/index.js +5 -1
- package/cjs/table/Body.js +5 -1
- package/cjs/table/ColumnHeader.js +5 -1
- package/cjs/table/DataCell.js +5 -1
- package/cjs/table/Header.js +5 -1
- package/cjs/table/HeaderCell.js +5 -1
- package/cjs/table/Row.js +5 -1
- package/cjs/table/Table.js +5 -1
- package/cjs/table/index.js +5 -1
- package/cjs/tag/Tag.js +5 -1
- package/cjs/tag/index.js +5 -1
- package/cjs/toggle-group/ToggleGroup.js +5 -1
- package/cjs/toggle-group/ToggleItem.js +5 -1
- package/cjs/typography/BodyLong.js +5 -1
- package/cjs/typography/BodyShort.js +5 -1
- package/cjs/typography/Detail.js +5 -1
- package/cjs/typography/Heading.js +5 -1
- package/cjs/typography/Ingress.js +5 -1
- package/cjs/typography/Label.js +5 -1
- package/cjs/util/index.js +20 -2
- package/esm/form/index.d.ts +1 -1
- package/esm/form/index.js +1 -1
- package/esm/form/index.js.map +1 -1
- package/esm/form/search/Search.d.ts +61 -0
- package/esm/form/search/Search.js +76 -0
- package/esm/form/search/Search.js.map +1 -0
- package/esm/form/search/SearchButton.d.ts +11 -0
- package/esm/form/search/SearchButton.js +31 -0
- package/esm/form/search/SearchButton.js.map +1 -0
- package/esm/form/search/index.d.ts +1 -0
- package/esm/form/search/index.js +2 -0
- package/esm/form/search/index.js.map +1 -0
- package/esm/form/search/useSearch.d.ts +10 -0
- package/esm/form/search/useSearch.js +25 -0
- package/esm/form/search/useSearch.js.map +1 -0
- package/esm/help-text/HelpText.js.map +1 -1
- package/esm/modal/Modal.d.ts +4 -0
- package/esm/modal/Modal.js.map +1 -1
- package/esm/table/ColumnHeader.js.map +1 -1
- package/esm/util/index.d.ts +5 -0
- package/esm/util/index.js +13 -0
- package/esm/util/index.js.map +1 -1
- package/package.json +3 -4
- package/src/form/index.ts +1 -1
- package/src/form/search/Search.tsx +236 -0
- package/src/form/search/SearchButton.tsx +48 -0
- package/src/form/search/index.ts +1 -0
- package/src/form/search/search.stories.tsx +91 -0
- package/src/form/search/useSearch.ts +31 -0
- package/src/modal/Modal.tsx +4 -0
- package/src/util/index.ts +33 -0
- package/LICENCE +0 -31
- package/cjs/form/search-field/SearchField.js +0 -69
- package/cjs/form/search-field/SearchFieldClearButton.js +0 -50
- package/cjs/form/search-field/SearchFieldInput.js +0 -49
- package/cjs/form/search-field/index.js +0 -19
- package/cjs/form/search-field/package.json +0 -6
- package/esm/form/search-field/SearchField.d.ts +0 -36
- package/esm/form/search-field/SearchField.js +0 -45
- package/esm/form/search-field/SearchField.js.map +0 -1
- package/esm/form/search-field/SearchFieldButton.d.ts +0 -17
- package/esm/form/search-field/SearchFieldButton.js +0 -27
- package/esm/form/search-field/SearchFieldButton.js.map +0 -1
- package/esm/form/search-field/SearchFieldClearButton.d.ts +0 -17
- package/esm/form/search-field/SearchFieldClearButton.js +0 -27
- package/esm/form/search-field/SearchFieldClearButton.js.map +0 -1
- package/esm/form/search-field/SearchFieldInput.d.ts +0 -6
- package/esm/form/search-field/SearchFieldInput.js +0 -26
- package/esm/form/search-field/SearchFieldInput.js.map +0 -1
- package/esm/form/search-field/index.d.ts +0 -2
- package/esm/form/search-field/index.js +0 -3
- package/esm/form/search-field/index.js.map +0 -1
- package/src/form/search-field/SearchField.tsx +0 -132
- package/src/form/search-field/SearchFieldButton.tsx +0 -47
- package/src/form/search-field/SearchFieldClearButton.tsx +0 -49
- package/src/form/search-field/SearchFieldInput.tsx +0 -42
- package/src/form/search-field/index.ts +0 -2
- package/src/form/search-field/stories/search-field-example.tsx +0 -25
- package/src/form/search-field/stories/search-field.stories.mdx +0 -156
- package/src/form/search-field/stories/search-field.stories.tsx +0 -199
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { Close } from "@navikt/ds-icons";
|
|
2
|
+
import cl from "classnames";
|
|
3
|
+
import React, {
|
|
4
|
+
forwardRef,
|
|
5
|
+
InputHTMLAttributes,
|
|
6
|
+
useCallback,
|
|
7
|
+
useEffect,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
} from "react";
|
|
11
|
+
import mergeRefs from "react-merge-refs";
|
|
12
|
+
import { BodyShort, Label, omit, useEventListener } from "../..";
|
|
13
|
+
import { FormFieldProps } from "../useFormField";
|
|
14
|
+
import SearchButton, { SearchButtonType } from "./SearchButton";
|
|
15
|
+
import { useSearch } from "./useSearch";
|
|
16
|
+
|
|
17
|
+
export type SearchClearEvent =
|
|
18
|
+
| {
|
|
19
|
+
trigger: "Click";
|
|
20
|
+
event: React.MouseEvent<HTMLButtonElement, MouseEvent>;
|
|
21
|
+
}
|
|
22
|
+
| { trigger: "Escape"; event: React.KeyboardEvent<HTMLDivElement> };
|
|
23
|
+
|
|
24
|
+
export interface SearchProps
|
|
25
|
+
extends Omit<FormFieldProps, "error" | "errorId">,
|
|
26
|
+
Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "onChange"> {
|
|
27
|
+
children?: React.ReactNode;
|
|
28
|
+
/**
|
|
29
|
+
* Search label
|
|
30
|
+
* @info Will be hidden by default, is required for accessibility reasons.
|
|
31
|
+
*/
|
|
32
|
+
label: React.ReactNode;
|
|
33
|
+
/**
|
|
34
|
+
* Shows label and description for screenreaders-only
|
|
35
|
+
* @default true
|
|
36
|
+
*/
|
|
37
|
+
hideLabel?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Callback for value-change in input
|
|
40
|
+
*/
|
|
41
|
+
onChange?: (value: string) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Callback for <Search.Button/> click or onSubmit in form
|
|
44
|
+
*/
|
|
45
|
+
onSearch?: (value: string | number | readonly string[]) => void;
|
|
46
|
+
/**
|
|
47
|
+
* Callback for click on clear-button or Escape keydown
|
|
48
|
+
*/
|
|
49
|
+
onClear?: (e: SearchClearEvent) => void;
|
|
50
|
+
/**
|
|
51
|
+
* aria-label on clear button
|
|
52
|
+
* @default "Tøm"
|
|
53
|
+
*/
|
|
54
|
+
clearButtonLabel?: string;
|
|
55
|
+
/**
|
|
56
|
+
* If false, removes clear-button option from input.
|
|
57
|
+
* @default true
|
|
58
|
+
*/
|
|
59
|
+
clearButton?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Changes button-variant
|
|
62
|
+
* @default "tertiary"
|
|
63
|
+
*/
|
|
64
|
+
variant?: "tertiary" | "primary";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface SearchComponent
|
|
68
|
+
extends React.ForwardRefExoticComponent<
|
|
69
|
+
SearchProps & React.RefAttributes<HTMLDivElement>
|
|
70
|
+
> {
|
|
71
|
+
Button: SearchButtonType;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface SearchContextProps {
|
|
75
|
+
disabled?: boolean;
|
|
76
|
+
size: "medium" | "small";
|
|
77
|
+
variant?: "tertiary" | "primary";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const SearchContext = React.createContext<SearchContextProps | null>(
|
|
81
|
+
null
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
|
|
85
|
+
const { inputProps, size = "medium", inputDescriptionId } = useSearch(
|
|
86
|
+
props,
|
|
87
|
+
"searchfield"
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const {
|
|
91
|
+
className,
|
|
92
|
+
hideLabel = true,
|
|
93
|
+
label,
|
|
94
|
+
description,
|
|
95
|
+
value,
|
|
96
|
+
clearButtonLabel,
|
|
97
|
+
onClear,
|
|
98
|
+
clearButton = true,
|
|
99
|
+
children,
|
|
100
|
+
onSearch,
|
|
101
|
+
variant = "tertiary",
|
|
102
|
+
...rest
|
|
103
|
+
} = props;
|
|
104
|
+
|
|
105
|
+
const searchRef = useRef<HTMLInputElement | null>(null);
|
|
106
|
+
const mergedRef = mergeRefs([searchRef, ref]);
|
|
107
|
+
const [wrapperRef, setWrapperRef] = useState<HTMLFormElement | null>(null);
|
|
108
|
+
|
|
109
|
+
const [controlledValue, setControlledValue] = useState(value ?? "");
|
|
110
|
+
|
|
111
|
+
const handleChange = useCallback(
|
|
112
|
+
(v: string) => {
|
|
113
|
+
searchRef.current && value === undefined && setControlledValue(v);
|
|
114
|
+
props?.onChange?.(v);
|
|
115
|
+
},
|
|
116
|
+
[props, value]
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const handleClear = useCallback(
|
|
120
|
+
(event: SearchClearEvent) => {
|
|
121
|
+
onClear?.(event);
|
|
122
|
+
handleChange("");
|
|
123
|
+
if (searchRef.current && value === undefined) {
|
|
124
|
+
searchRef.current.value = "";
|
|
125
|
+
}
|
|
126
|
+
searchRef.current && searchRef.current?.focus?.();
|
|
127
|
+
},
|
|
128
|
+
[handleChange, onClear, value]
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
useEventListener(
|
|
132
|
+
"keydown",
|
|
133
|
+
useCallback(
|
|
134
|
+
(e) => {
|
|
135
|
+
if (e.key === "Escape") {
|
|
136
|
+
e.preventDefault();
|
|
137
|
+
handleClear({ trigger: "Escape", event: e });
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
[handleClear]
|
|
141
|
+
),
|
|
142
|
+
wrapperRef
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
value !== undefined && setControlledValue(value);
|
|
147
|
+
}, [value]);
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<form
|
|
151
|
+
role="search"
|
|
152
|
+
ref={setWrapperRef}
|
|
153
|
+
className={cl(
|
|
154
|
+
className,
|
|
155
|
+
"navds-form-field",
|
|
156
|
+
`navds-form-field--${size}`,
|
|
157
|
+
"navds-search",
|
|
158
|
+
{
|
|
159
|
+
"navds-search--disabled": !!inputProps.disabled,
|
|
160
|
+
}
|
|
161
|
+
)}
|
|
162
|
+
onSubmit={(e) => {
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
onSearch?.(controlledValue);
|
|
165
|
+
}}
|
|
166
|
+
>
|
|
167
|
+
<Label
|
|
168
|
+
htmlFor={inputProps.id}
|
|
169
|
+
size={size}
|
|
170
|
+
as="label"
|
|
171
|
+
className={cl("navds-text-field__label", {
|
|
172
|
+
"navds-sr-only": hideLabel,
|
|
173
|
+
})}
|
|
174
|
+
>
|
|
175
|
+
{label}
|
|
176
|
+
</Label>
|
|
177
|
+
{!!description && (
|
|
178
|
+
<BodyShort
|
|
179
|
+
as="div"
|
|
180
|
+
className={cl("navds-text-field__description", {
|
|
181
|
+
"navds-sr-only": hideLabel,
|
|
182
|
+
})}
|
|
183
|
+
id={inputDescriptionId}
|
|
184
|
+
size={size}
|
|
185
|
+
>
|
|
186
|
+
{description}
|
|
187
|
+
</BodyShort>
|
|
188
|
+
)}
|
|
189
|
+
<div className="navds-search__wrapper">
|
|
190
|
+
<div className="navds-search__wrapper-inner">
|
|
191
|
+
<input
|
|
192
|
+
ref={mergedRef}
|
|
193
|
+
{...omit(rest, ["size"])}
|
|
194
|
+
{...inputProps}
|
|
195
|
+
{...(props.value !== undefined && { value: props.value })}
|
|
196
|
+
onChange={(e) => handleChange(e.target.value)}
|
|
197
|
+
type="search"
|
|
198
|
+
role="searchbox"
|
|
199
|
+
className={cl(
|
|
200
|
+
className,
|
|
201
|
+
"navds-search__input",
|
|
202
|
+
"navds-text-field__input",
|
|
203
|
+
"navds-body-short",
|
|
204
|
+
`navds-body-${size ?? "medium"}`
|
|
205
|
+
)}
|
|
206
|
+
/>
|
|
207
|
+
{controlledValue && clearButton && (
|
|
208
|
+
<button
|
|
209
|
+
type="button"
|
|
210
|
+
onClick={(e) => handleClear({ trigger: "Click", event: e })}
|
|
211
|
+
className="navds-search__button-clear"
|
|
212
|
+
>
|
|
213
|
+
<span className="navds-sr-only">
|
|
214
|
+
{clearButtonLabel ? clearButtonLabel : "Tøm"}
|
|
215
|
+
</span>
|
|
216
|
+
<Close aria-hidden />
|
|
217
|
+
</button>
|
|
218
|
+
)}
|
|
219
|
+
</div>
|
|
220
|
+
<SearchContext.Provider
|
|
221
|
+
value={{
|
|
222
|
+
size,
|
|
223
|
+
disabled: inputProps.disabled,
|
|
224
|
+
variant,
|
|
225
|
+
}}
|
|
226
|
+
>
|
|
227
|
+
{children ? children : <SearchButton />}
|
|
228
|
+
</SearchContext.Provider>
|
|
229
|
+
</div>
|
|
230
|
+
</form>
|
|
231
|
+
);
|
|
232
|
+
}) as SearchComponent;
|
|
233
|
+
|
|
234
|
+
Search.Button = SearchButton;
|
|
235
|
+
|
|
236
|
+
export default Search;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Search } from "@navikt/ds-icons";
|
|
2
|
+
import cl from "classnames";
|
|
3
|
+
import React, { forwardRef, useContext } from "react";
|
|
4
|
+
import { Button, ButtonProps } from "../..";
|
|
5
|
+
import { SearchContext } from "./Search";
|
|
6
|
+
|
|
7
|
+
export interface SearchButtonProps
|
|
8
|
+
extends Omit<ButtonProps, "size" | "children" | "variant"> {
|
|
9
|
+
/**
|
|
10
|
+
* Text set before <Search/> icon
|
|
11
|
+
*/
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type SearchButtonType = React.ForwardRefExoticComponent<
|
|
16
|
+
SearchButtonProps & React.RefAttributes<HTMLButtonElement>
|
|
17
|
+
>;
|
|
18
|
+
|
|
19
|
+
const SearchButton: SearchButtonType = forwardRef(
|
|
20
|
+
({ className, children, disabled, onClick, ...rest }, ref) => {
|
|
21
|
+
const context = useContext(SearchContext);
|
|
22
|
+
|
|
23
|
+
if (context === null) {
|
|
24
|
+
console.warn("<Search.Button> has to be wrapped in <Search />");
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { size, variant } = context;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Button
|
|
32
|
+
type="submit"
|
|
33
|
+
{...rest}
|
|
34
|
+
ref={ref}
|
|
35
|
+
size={size}
|
|
36
|
+
variant={variant}
|
|
37
|
+
className={cl("navds-search__button-search", className)}
|
|
38
|
+
disabled={context?.disabled ?? disabled}
|
|
39
|
+
onClick={(e) => onClick?.(e)}
|
|
40
|
+
>
|
|
41
|
+
<Search aria-hidden />
|
|
42
|
+
{children ? children : <span className="navds-sr-only">Søk</span>}
|
|
43
|
+
</Button>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
export default SearchButton;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Search, SearchProps, SearchClearEvent } from "./Search";
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Meta } from "@storybook/react/types-6-0";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
|
+
|
|
4
|
+
import { Search } from "../index";
|
|
5
|
+
export default {
|
|
6
|
+
title: "ds-react/form/search",
|
|
7
|
+
component: Search,
|
|
8
|
+
} as Meta;
|
|
9
|
+
|
|
10
|
+
export const All = () => {
|
|
11
|
+
const [value, setValue] = useState("");
|
|
12
|
+
return (
|
|
13
|
+
<div style={{ maxWidth: 300 }}>
|
|
14
|
+
<h1>Search</h1>
|
|
15
|
+
<div>
|
|
16
|
+
<Search
|
|
17
|
+
label="Søk alle sider om X og Y"
|
|
18
|
+
onSearch={console.log}
|
|
19
|
+
></Search>
|
|
20
|
+
<br />
|
|
21
|
+
<Search
|
|
22
|
+
label="Søk alle sider om X og Y"
|
|
23
|
+
onSearch={console.log}
|
|
24
|
+
variant="primary"
|
|
25
|
+
></Search>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<h2>Search small</h2>
|
|
29
|
+
<Search
|
|
30
|
+
label="Søk alle sider om X og Y"
|
|
31
|
+
description="Beskrivelse av søket"
|
|
32
|
+
size="small"
|
|
33
|
+
>
|
|
34
|
+
<Search.Button />
|
|
35
|
+
</Search>
|
|
36
|
+
<br />
|
|
37
|
+
<Search
|
|
38
|
+
label="Søk alle sider om X og Y"
|
|
39
|
+
description="Beskrivelse av søket"
|
|
40
|
+
size="small"
|
|
41
|
+
hideLabel
|
|
42
|
+
variant="primary"
|
|
43
|
+
>
|
|
44
|
+
<Search.Button />
|
|
45
|
+
</Search>
|
|
46
|
+
|
|
47
|
+
<h2>Med knappe-tekst</h2>
|
|
48
|
+
<Search label="Søk alle sider om X og Y" hideLabel={false}>
|
|
49
|
+
<Search.Button>Søk</Search.Button>
|
|
50
|
+
</Search>
|
|
51
|
+
<Search
|
|
52
|
+
label="Søk alle sider om X og Y"
|
|
53
|
+
hideLabel={false}
|
|
54
|
+
variant="primary"
|
|
55
|
+
>
|
|
56
|
+
<Search.Button>Søk</Search.Button>
|
|
57
|
+
</Search>
|
|
58
|
+
<h2>Hidelabel false</h2>
|
|
59
|
+
<Search label="Søk alle sider om X og Y" hideLabel={false}>
|
|
60
|
+
<Search.Button />
|
|
61
|
+
</Search>
|
|
62
|
+
<h2>Controlled state </h2>
|
|
63
|
+
<Search
|
|
64
|
+
value={value}
|
|
65
|
+
label="Søk alle sider om X og Y"
|
|
66
|
+
description="Beskrivelse av søket"
|
|
67
|
+
onChange={(e) => setValue(e)}
|
|
68
|
+
onClear={() => setValue("")}
|
|
69
|
+
>
|
|
70
|
+
<Search.Button />
|
|
71
|
+
</Search>
|
|
72
|
+
<h2>No clear button</h2>
|
|
73
|
+
<Search
|
|
74
|
+
hideLabel
|
|
75
|
+
label="Søk alle sider om X og Y"
|
|
76
|
+
description="Beskrivelse av søket"
|
|
77
|
+
clearButton={false}
|
|
78
|
+
>
|
|
79
|
+
<Search.Button />
|
|
80
|
+
</Search>
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const UUDemo = () => {
|
|
86
|
+
return (
|
|
87
|
+
<Search label="Søk på nav.no" onSearch={console.log} placeholder="Søk">
|
|
88
|
+
<Search.Button />
|
|
89
|
+
</Search>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import cl from "classnames";
|
|
2
|
+
import { useContext } from "react";
|
|
3
|
+
import { useId } from "../../index";
|
|
4
|
+
import { FieldsetContext } from "../index";
|
|
5
|
+
import { FormFieldProps } from "../useFormField";
|
|
6
|
+
|
|
7
|
+
export const useSearch = (props: FormFieldProps, prefix: string) => {
|
|
8
|
+
const { size } = props;
|
|
9
|
+
|
|
10
|
+
const fieldset = useContext(FieldsetContext);
|
|
11
|
+
|
|
12
|
+
const genId = useId();
|
|
13
|
+
|
|
14
|
+
const id = props.id ?? `${prefix}-${genId}`;
|
|
15
|
+
const inputDescriptionId = `${prefix}-description-${genId}`;
|
|
16
|
+
|
|
17
|
+
const disabled = fieldset?.disabled || props.disabled;
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
inputDescriptionId,
|
|
21
|
+
size: size ?? fieldset?.size ?? "medium",
|
|
22
|
+
inputProps: {
|
|
23
|
+
id,
|
|
24
|
+
"aria-describedby":
|
|
25
|
+
cl(props["aria-describedby"], {
|
|
26
|
+
[inputDescriptionId]: !!props?.description,
|
|
27
|
+
}) || undefined,
|
|
28
|
+
disabled,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
package/src/modal/Modal.tsx
CHANGED
|
@@ -33,6 +33,10 @@ export interface ModalProps {
|
|
|
33
33
|
* @default true
|
|
34
34
|
*/
|
|
35
35
|
closeButton?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Callback for getting parent element modal will attach to
|
|
38
|
+
*/
|
|
39
|
+
parentSelector?(): HTMLElement;
|
|
36
40
|
"aria-labelledby"?: string;
|
|
37
41
|
"aria-describedby"?: string;
|
|
38
42
|
"aria-modal"?: boolean;
|
package/src/util/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
|
|
1
3
|
export * from "./OverridableComponent";
|
|
2
4
|
export * from "./useId";
|
|
3
5
|
|
|
@@ -11,3 +13,34 @@ export const omit = (obj: object, props: string[]) =>
|
|
|
11
13
|
}),
|
|
12
14
|
{}
|
|
13
15
|
);
|
|
16
|
+
|
|
17
|
+
export interface ListenerT {
|
|
18
|
+
addEventListener(
|
|
19
|
+
name: string,
|
|
20
|
+
handler: (event?: any) => void,
|
|
21
|
+
...args: any[]
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
removeEventListener(
|
|
25
|
+
name: string,
|
|
26
|
+
handler: (event?: any) => void,
|
|
27
|
+
...args: any[]
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* https://github.com/streamich/react-use/blob/master/src/useEvent.ts */
|
|
32
|
+
export const useEventListener = <T extends ListenerT>(
|
|
33
|
+
name: Parameters<ListenerT["addEventListener"]>[0],
|
|
34
|
+
handler: Parameters<ListenerT["addEventListener"]>[1],
|
|
35
|
+
target: null | T | Window = window
|
|
36
|
+
): void => {
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (!target) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
target?.addEventListener(name, handler);
|
|
42
|
+
return () => {
|
|
43
|
+
target?.addEventListener(name, handler);
|
|
44
|
+
};
|
|
45
|
+
}, [name, handler, target]);
|
|
46
|
+
};
|
package/LICENCE
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
Font License
|
|
2
|
-
|
|
3
|
-
The Source Sans Pro font files in packages/node_modules/nav-frontend-typografi-style/assets are a subset of Source Sans Pro, licensed under the SIL Open Font License, and copyright Adobe Systems Incorporated, with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
|
|
4
|
-
|
|
5
|
-
Icon License
|
|
6
|
-
|
|
7
|
-
This project uses Streamline Icons. If you use nav-frontend-moduler in your project please adhere to the Streamline Icons license agreement found here: https://streamlineicons.com/ux/extended-license.html
|
|
8
|
-
|
|
9
|
-
The rest of the codebase (excluding 3rd party dependencies):
|
|
10
|
-
|
|
11
|
-
# The MIT License
|
|
12
|
-
|
|
13
|
-
Copyright 2019 NAV (Arbeids- og velferdsdirektoratet) - The Norwegian Labour and Welfare Administration
|
|
14
|
-
|
|
15
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
|
16
|
-
a copy of this software and associated documentation files (the "Software"),
|
|
17
|
-
to deal in the Software without restriction, including without limitation
|
|
18
|
-
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
19
|
-
and/or sell copies of the Software, and to permit persons to whom the
|
|
20
|
-
Software is furnished to do so, subject to the following conditions:
|
|
21
|
-
|
|
22
|
-
The above copyright notice and this permission notice shall be included
|
|
23
|
-
in all copies or substantial portions of the Software.
|
|
24
|
-
|
|
25
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
26
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
27
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
28
|
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
29
|
-
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
30
|
-
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
31
|
-
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
-
}) : (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
o[k2] = m[k];
|
|
8
|
-
}));
|
|
9
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
-
}) : function(o, v) {
|
|
12
|
-
o["default"] = v;
|
|
13
|
-
});
|
|
14
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
-
if (mod && mod.__esModule) return mod;
|
|
16
|
-
var result = {};
|
|
17
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
-
__setModuleDefault(result, mod);
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
21
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
22
|
-
var t = {};
|
|
23
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
24
|
-
t[p] = s[p];
|
|
25
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
26
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
27
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
28
|
-
t[p[i]] = s[p[i]];
|
|
29
|
-
}
|
|
30
|
-
return t;
|
|
31
|
-
};
|
|
32
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
33
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
34
|
-
};
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.SearchFieldContext = void 0;
|
|
37
|
-
const classnames_1 = __importDefault(require("classnames"));
|
|
38
|
-
const react_1 = __importStar(require("react"));
|
|
39
|
-
const __1 = require("../..");
|
|
40
|
-
const ErrorMessage_1 = __importDefault(require("../ErrorMessage"));
|
|
41
|
-
const useFormField_1 = require("../useFormField");
|
|
42
|
-
const SearchFieldButton_1 = __importDefault(require("./SearchFieldButton"));
|
|
43
|
-
const SearchFieldClearButton_1 = __importDefault(require("./SearchFieldClearButton"));
|
|
44
|
-
const SearchFieldInput_1 = __importDefault(require("./SearchFieldInput"));
|
|
45
|
-
exports.SearchFieldContext = react_1.default.createContext(null);
|
|
46
|
-
const SearchField = (0, react_1.forwardRef)((props, ref) => {
|
|
47
|
-
const { inputProps, errorId, showErrorMsg, hasError, size, inputDescriptionId, } = (0, useFormField_1.useFormField)(props, "searchfield");
|
|
48
|
-
const { className, hideLabel, children, label, description, error } = props, rest = __rest(props, ["className", "hideLabel", "children", "label", "description", "error"]);
|
|
49
|
-
return (react_1.default.createElement("div", Object.assign({ ref: ref }, (0, __1.omit)(rest, ["id", "error", "errorId", "size", "disabled"]), { className: (0, classnames_1.default)(className, "navds-form-field", `navds-form-field--${size !== null && size !== void 0 ? size : "medium"}`, "navds-search-field", {
|
|
50
|
-
"navds-search-field--error": hasError,
|
|
51
|
-
"navds-search-field--disabled": !!inputProps.disabled,
|
|
52
|
-
}) }),
|
|
53
|
-
react_1.default.createElement(__1.Label, { htmlFor: inputProps.id, size: size, as: "label", className: (0, classnames_1.default)("navds-text-field__label", {
|
|
54
|
-
"navds-sr-only": hideLabel,
|
|
55
|
-
}) }, label),
|
|
56
|
-
!!description && (react_1.default.createElement(__1.BodyShort, { as: "div", className: (0, classnames_1.default)("navds-text-field__description", {
|
|
57
|
-
"navds-sr-only": hideLabel,
|
|
58
|
-
}), id: inputDescriptionId, size: size }, description)),
|
|
59
|
-
react_1.default.createElement("div", { className: "navds-search-field__input-wrapper" },
|
|
60
|
-
react_1.default.createElement(exports.SearchFieldContext.Provider, { value: {
|
|
61
|
-
inputProps,
|
|
62
|
-
size,
|
|
63
|
-
} }, children)),
|
|
64
|
-
react_1.default.createElement("div", { id: errorId, "aria-relevant": "additions removals", "aria-live": "polite" }, showErrorMsg && react_1.default.createElement(ErrorMessage_1.default, { size: size }, error))));
|
|
65
|
-
});
|
|
66
|
-
SearchField.Button = SearchFieldButton_1.default;
|
|
67
|
-
SearchField.Clear = SearchFieldClearButton_1.default;
|
|
68
|
-
SearchField.Input = SearchFieldInput_1.default;
|
|
69
|
-
exports.default = SearchField;
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
-
}) : (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
o[k2] = m[k];
|
|
8
|
-
}));
|
|
9
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
-
}) : function(o, v) {
|
|
12
|
-
o["default"] = v;
|
|
13
|
-
});
|
|
14
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
-
if (mod && mod.__esModule) return mod;
|
|
16
|
-
var result = {};
|
|
17
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
-
__setModuleDefault(result, mod);
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
21
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
22
|
-
var t = {};
|
|
23
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
24
|
-
t[p] = s[p];
|
|
25
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
26
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
27
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
28
|
-
t[p[i]] = s[p[i]];
|
|
29
|
-
}
|
|
30
|
-
return t;
|
|
31
|
-
};
|
|
32
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
33
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
34
|
-
};
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
const react_1 = __importStar(require("react"));
|
|
37
|
-
const classnames_1 = __importDefault(require("classnames"));
|
|
38
|
-
const SearchField_1 = require("./SearchField");
|
|
39
|
-
const __1 = require("../..");
|
|
40
|
-
const SearchFieldClearButton = (0, react_1.forwardRef)((_a, ref) => {
|
|
41
|
-
var { className, variant = "secondary", disabled } = _a, rest = __rest(_a, ["className", "variant", "disabled"]);
|
|
42
|
-
const searchField = (0, react_1.useContext)(SearchField_1.SearchFieldContext);
|
|
43
|
-
if (searchField === null) {
|
|
44
|
-
console.warn("SearchFieldClearButton has to be wrapped in <SearchField />");
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
const { size, inputProps } = searchField;
|
|
48
|
-
return (react_1.default.createElement(__1.Button, Object.assign({}, rest, { ref: ref, className: (0, classnames_1.default)(className, "navds-search-field__clear-button"), size: size, variant: variant, disabled: disabled !== null && disabled !== void 0 ? disabled : inputProps === null || inputProps === void 0 ? void 0 : inputProps.disabled })));
|
|
49
|
-
});
|
|
50
|
-
exports.default = SearchFieldClearButton;
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
-
}) : (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
o[k2] = m[k];
|
|
8
|
-
}));
|
|
9
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
-
}) : function(o, v) {
|
|
12
|
-
o["default"] = v;
|
|
13
|
-
});
|
|
14
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
-
if (mod && mod.__esModule) return mod;
|
|
16
|
-
var result = {};
|
|
17
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
-
__setModuleDefault(result, mod);
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
21
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
22
|
-
var t = {};
|
|
23
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
24
|
-
t[p] = s[p];
|
|
25
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
26
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
27
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
28
|
-
t[p[i]] = s[p[i]];
|
|
29
|
-
}
|
|
30
|
-
return t;
|
|
31
|
-
};
|
|
32
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
33
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
34
|
-
};
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
const react_1 = __importStar(require("react"));
|
|
37
|
-
const classnames_1 = __importDefault(require("classnames"));
|
|
38
|
-
const SearchField_1 = require("./SearchField");
|
|
39
|
-
const SearchFieldInput = (0, react_1.forwardRef)((_a, ref) => {
|
|
40
|
-
var { className } = _a, rest = __rest(_a, ["className"]);
|
|
41
|
-
const searchField = (0, react_1.useContext)(SearchField_1.SearchFieldContext);
|
|
42
|
-
if (searchField === null) {
|
|
43
|
-
console.warn("SearchFieldInput has to be wrapped in <SearchField />");
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
const { size, inputProps } = searchField;
|
|
47
|
-
return (react_1.default.createElement("input", Object.assign({}, rest, inputProps, { type: "search", role: "searchbox", ref: ref, className: (0, classnames_1.default)(className, "navds-search-field__input", "navds-text-field__input", "navds-body-short", `navds-body-${size !== null && size !== void 0 ? size : "medium"}`) })));
|
|
48
|
-
});
|
|
49
|
-
exports.default = SearchFieldInput;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
-
}) : (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
o[k2] = m[k];
|
|
8
|
-
}));
|
|
9
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
-
};
|
|
12
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
-
};
|
|
15
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.SearchField = void 0;
|
|
17
|
-
var SearchField_1 = require("./SearchField");
|
|
18
|
-
Object.defineProperty(exports, "SearchField", { enumerable: true, get: function () { return __importDefault(SearchField_1).default; } });
|
|
19
|
-
__exportStar(require("./SearchField"), exports);
|