@navikt/ds-react 0.14.10-next.0 → 0.14.13
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/button/Button.js +22 -3
- package/cjs/form/Fieldset/Fieldset.js +3 -3
- package/cjs/form/Select.js +2 -2
- package/cjs/form/Switch.js +1 -1
- package/cjs/form/TextField.js +5 -3
- package/cjs/form/Textarea.js +3 -3
- package/cjs/form/checkbox/Checkbox.js +1 -1
- package/cjs/form/search-field/SearchField.js +21 -42
- package/cjs/form/search-field/SearchFieldButton.js +50 -0
- package/cjs/form/search-field/SearchFieldClearButton.js +50 -0
- package/cjs/form/search-field/SearchFieldInput.js +49 -0
- package/cjs/modal/Modal.js +4 -4
- package/cjs/util/index.js +1 -15
- package/esm/button/Button.d.ts +5 -0
- package/esm/button/Button.js +24 -5
- package/esm/button/Button.js.map +1 -1
- package/esm/form/Fieldset/Fieldset.js +3 -3
- package/esm/form/Fieldset/Fieldset.js.map +1 -1
- package/esm/form/Select.js +2 -2
- package/esm/form/Select.js.map +1 -1
- package/esm/form/Switch.js +1 -1
- package/esm/form/Switch.js.map +1 -1
- package/esm/form/TextField.js +5 -3
- package/esm/form/TextField.js.map +1 -1
- package/esm/form/Textarea.js +3 -3
- package/esm/form/Textarea.js.map +1 -1
- package/esm/form/checkbox/Checkbox.js +1 -1
- package/esm/form/checkbox/Checkbox.js.map +1 -1
- package/esm/form/search-field/SearchField.d.ts +25 -25
- package/esm/form/search-field/SearchField.js +22 -44
- package/esm/form/search-field/SearchField.js.map +1 -1
- package/esm/form/search-field/SearchFieldButton.d.ts +17 -0
- package/esm/form/search-field/SearchFieldButton.js +27 -0
- package/esm/form/search-field/SearchFieldButton.js.map +1 -0
- package/esm/form/search-field/SearchFieldClearButton.d.ts +17 -0
- package/esm/form/search-field/SearchFieldClearButton.js +27 -0
- package/esm/form/search-field/SearchFieldClearButton.js.map +1 -0
- package/esm/form/search-field/SearchFieldInput.d.ts +6 -0
- package/esm/form/search-field/SearchFieldInput.js +26 -0
- package/esm/form/search-field/SearchFieldInput.js.map +1 -0
- package/esm/modal/Modal.d.ts +5 -0
- package/esm/modal/Modal.js +4 -4
- package/esm/modal/Modal.js.map +1 -1
- package/esm/util/index.d.ts +0 -5
- package/esm/util/index.js +0 -13
- package/esm/util/index.js.map +1 -1
- package/package.json +3 -3
- package/src/button/Button.tsx +55 -18
- package/src/button/stories/button.stories.mdx +17 -11
- package/src/button/stories/button.stories.tsx +41 -3
- package/src/form/Fieldset/Fieldset.tsx +3 -3
- package/src/form/Select.tsx +2 -2
- package/src/form/Switch.tsx +1 -1
- package/src/form/TextField.tsx +5 -3
- package/src/form/Textarea.tsx +3 -3
- package/src/form/checkbox/Checkbox.tsx +1 -1
- package/src/form/search-field/SearchField.tsx +69 -124
- package/src/form/search-field/SearchFieldButton.tsx +47 -0
- package/src/form/search-field/SearchFieldClearButton.tsx +49 -0
- package/src/form/search-field/SearchFieldInput.tsx +42 -0
- package/src/form/search-field/stories/search-field-example.tsx +25 -0
- package/src/form/search-field/stories/search-field.stories.mdx +89 -158
- package/src/form/search-field/stories/search-field.stories.tsx +154 -62
- package/src/loader/stories/loader.stories.mdx +0 -22
- package/src/modal/Modal.tsx +19 -12
- package/src/modal/stories/modal.stories.tsx +16 -0
- package/src/util/index.ts +0 -33
- package/cjs/form/search-field/useSearchField.js +0 -31
- package/esm/form/search-field/useSearchField.d.ts +0 -10
- package/esm/form/search-field/useSearchField.js +0 -25
- package/esm/form/search-field/useSearchField.js.map +0 -1
- package/src/form/search-field/useSearchField.ts +0 -31
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useState } from "react";
|
|
2
2
|
import { Button } from "../index";
|
|
3
3
|
import { Success } from "@navikt/ds-icons";
|
|
4
4
|
|
|
@@ -37,6 +37,13 @@ const varSwitch = {
|
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
export const All = () => {
|
|
40
|
+
const [loadingState, setLoadingState] = useState(true);
|
|
41
|
+
const [content, setContent] = useState<string>("");
|
|
42
|
+
|
|
43
|
+
const toggleLoading = () => {
|
|
44
|
+
setLoadingState(!loadingState);
|
|
45
|
+
};
|
|
46
|
+
|
|
40
47
|
return (
|
|
41
48
|
<div style={{ paddingLeft: "1rem" }}>
|
|
42
49
|
<h1>Button</h1>
|
|
@@ -82,7 +89,7 @@ export const All = () => {
|
|
|
82
89
|
<Section>
|
|
83
90
|
{variants.map((variant) => (
|
|
84
91
|
<Button key={variant} variant={variant}>
|
|
85
|
-
<span className="sr-only">Success ikon</span>
|
|
92
|
+
<span className="navds-sr-only">Success ikon</span>
|
|
86
93
|
<Success />
|
|
87
94
|
</Button>
|
|
88
95
|
))}
|
|
@@ -98,11 +105,42 @@ export const All = () => {
|
|
|
98
105
|
<Section>
|
|
99
106
|
{variants.map((variant) => (
|
|
100
107
|
<Button key={variant} variant={variant} size="small">
|
|
101
|
-
<span className="sr-only">Success ikon</span>
|
|
108
|
+
<span className="navds-sr-only">Success ikon</span>
|
|
102
109
|
<Success />
|
|
103
110
|
</Button>
|
|
104
111
|
))}
|
|
105
112
|
</Section>
|
|
113
|
+
<h2>Button w/loader</h2>
|
|
114
|
+
<Button onClick={toggleLoading}>Toggle loaders</Button>
|
|
115
|
+
<Button onClick={() => setContent((content) => `${content} wat`)}>
|
|
116
|
+
Change content
|
|
117
|
+
</Button>
|
|
118
|
+
<Section>
|
|
119
|
+
{variants.map((variant) => (
|
|
120
|
+
<Button
|
|
121
|
+
key={variant}
|
|
122
|
+
variant={variant}
|
|
123
|
+
loading={loadingState}
|
|
124
|
+
onClick={toggleLoading}
|
|
125
|
+
>
|
|
126
|
+
{content || varSwitch[variant]}
|
|
127
|
+
</Button>
|
|
128
|
+
))}
|
|
129
|
+
</Section>
|
|
130
|
+
<h2>Small w/loader</h2>
|
|
131
|
+
<Section>
|
|
132
|
+
{variants.map((variant) => (
|
|
133
|
+
<Button
|
|
134
|
+
key={variant}
|
|
135
|
+
variant={variant}
|
|
136
|
+
size="small"
|
|
137
|
+
loading={loadingState}
|
|
138
|
+
onClick={toggleLoading}
|
|
139
|
+
>
|
|
140
|
+
{varSwitch[variant]}
|
|
141
|
+
</Button>
|
|
142
|
+
))}
|
|
143
|
+
</Section>
|
|
106
144
|
</div>
|
|
107
145
|
);
|
|
108
146
|
};
|
|
@@ -96,7 +96,7 @@ const Fieldset = forwardRef<HTMLFieldSetElement, FieldsetProps>(
|
|
|
96
96
|
size={size}
|
|
97
97
|
as="legend"
|
|
98
98
|
className={cl("navds-fieldset__legend", {
|
|
99
|
-
"sr-only": !!hideLegend,
|
|
99
|
+
"navds-sr-only": !!hideLegend,
|
|
100
100
|
})}
|
|
101
101
|
>
|
|
102
102
|
{legend}
|
|
@@ -105,7 +105,7 @@ const Fieldset = forwardRef<HTMLFieldSetElement, FieldsetProps>(
|
|
|
105
105
|
(size === "medium" ? (
|
|
106
106
|
<BodyShort
|
|
107
107
|
className={cl("navds-fieldset__description", {
|
|
108
|
-
"sr-only": !!hideLegend,
|
|
108
|
+
"navds-sr-only": !!hideLegend,
|
|
109
109
|
})}
|
|
110
110
|
id={inputDescriptionId}
|
|
111
111
|
size="small"
|
|
@@ -116,7 +116,7 @@ const Fieldset = forwardRef<HTMLFieldSetElement, FieldsetProps>(
|
|
|
116
116
|
) : (
|
|
117
117
|
<Detail
|
|
118
118
|
className={cl("navds-fieldset__description", {
|
|
119
|
-
"sr-only": !!hideLegend,
|
|
119
|
+
"navds-sr-only": !!hideLegend,
|
|
120
120
|
})}
|
|
121
121
|
id={inputDescriptionId}
|
|
122
122
|
size="small"
|
package/src/form/Select.tsx
CHANGED
|
@@ -63,7 +63,7 @@ const Select = forwardRef<HTMLSelectElement, SelectProps>((props, ref) => {
|
|
|
63
63
|
size={size}
|
|
64
64
|
as="label"
|
|
65
65
|
className={cl("navds-select__label", {
|
|
66
|
-
"sr-only": hideLabel,
|
|
66
|
+
"navds-sr-only": hideLabel,
|
|
67
67
|
})}
|
|
68
68
|
>
|
|
69
69
|
{label}
|
|
@@ -72,7 +72,7 @@ const Select = forwardRef<HTMLSelectElement, SelectProps>((props, ref) => {
|
|
|
72
72
|
<BodyShort
|
|
73
73
|
as="div"
|
|
74
74
|
className={cl("navds-select__description", {
|
|
75
|
-
"sr-only": hideLabel,
|
|
75
|
+
"navds-sr-only": hideLabel,
|
|
76
76
|
})}
|
|
77
77
|
id={inputDescriptionId}
|
|
78
78
|
size={size}
|
package/src/form/Switch.tsx
CHANGED
|
@@ -115,7 +115,7 @@ const Switch = forwardRef<HTMLInputElement, SwitchProps>((props, ref) => {
|
|
|
115
115
|
<label htmlFor={inputProps.id} className="navds-switch__label-wrapper">
|
|
116
116
|
<div
|
|
117
117
|
className={cl("navds-switch__content", {
|
|
118
|
-
"sr-only": hideLabel,
|
|
118
|
+
"navds-sr-only": hideLabel,
|
|
119
119
|
"navds-switch--with-description": !!description && !hideLabel,
|
|
120
120
|
})}
|
|
121
121
|
>
|
package/src/form/TextField.tsx
CHANGED
|
@@ -62,7 +62,9 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>((props, ref) => {
|
|
|
62
62
|
htmlFor={inputProps.id}
|
|
63
63
|
size={size}
|
|
64
64
|
as="label"
|
|
65
|
-
className={cl("navds-text-field__label", {
|
|
65
|
+
className={cl("navds-text-field__label", {
|
|
66
|
+
"navds-sr-only": hideLabel,
|
|
67
|
+
})}
|
|
66
68
|
>
|
|
67
69
|
{label}
|
|
68
70
|
</Label>
|
|
@@ -72,7 +74,7 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>((props, ref) => {
|
|
|
72
74
|
{size === "medium" ? (
|
|
73
75
|
<BodyShort
|
|
74
76
|
className={cl("navds-text-field__description", {
|
|
75
|
-
"sr-only": hideLabel,
|
|
77
|
+
"navds-sr-only": hideLabel,
|
|
76
78
|
})}
|
|
77
79
|
id={inputDescriptionId}
|
|
78
80
|
size="small"
|
|
@@ -83,7 +85,7 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>((props, ref) => {
|
|
|
83
85
|
) : (
|
|
84
86
|
<Detail
|
|
85
87
|
className={cl("navds-text-field__description", {
|
|
86
|
-
"sr-only": hideLabel,
|
|
88
|
+
"navds-sr-only": hideLabel,
|
|
87
89
|
})}
|
|
88
90
|
id={inputDescriptionId}
|
|
89
91
|
size="small"
|
package/src/form/Textarea.tsx
CHANGED
|
@@ -77,7 +77,7 @@ const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
77
77
|
size={size}
|
|
78
78
|
as="label"
|
|
79
79
|
className={cl("navds-textarea__label", {
|
|
80
|
-
"sr-only": hideLabel,
|
|
80
|
+
"navds-sr-only": hideLabel,
|
|
81
81
|
})}
|
|
82
82
|
>
|
|
83
83
|
{label}
|
|
@@ -86,7 +86,7 @@ const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
86
86
|
<BodyShort
|
|
87
87
|
as="div"
|
|
88
88
|
className={cl("navds-textarea__description", {
|
|
89
|
-
"sr-only": hideLabel,
|
|
89
|
+
"navds-sr-only": hideLabel,
|
|
90
90
|
})}
|
|
91
91
|
id={inputDescriptionId}
|
|
92
92
|
size={size}
|
|
@@ -113,7 +113,7 @@ const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
113
113
|
/>
|
|
114
114
|
{hasMaxLength && (
|
|
115
115
|
<>
|
|
116
|
-
<span id={maxLengthId} className="sr-only">
|
|
116
|
+
<span id={maxLengthId} className="navds-sr-only">
|
|
117
117
|
Tekstområde med plass til {maxLength} tegn., Textarea can have{" "}
|
|
118
118
|
{maxLength} signs.
|
|
119
119
|
</span>
|
|
@@ -58,7 +58,7 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
|
|
|
58
58
|
<label htmlFor={inputProps.id} className="navds-checkbox__label">
|
|
59
59
|
<div
|
|
60
60
|
className={cl("navds-checkbox__content", {
|
|
61
|
-
"sr-only": props.hideLabel,
|
|
61
|
+
"navds-sr-only": props.hideLabel,
|
|
62
62
|
})}
|
|
63
63
|
>
|
|
64
64
|
<BodyShort as="div" size={size}>
|
|
@@ -1,21 +1,35 @@
|
|
|
1
|
-
import { Close, Search } from "@navikt/ds-icons";
|
|
2
1
|
import cl from "classnames";
|
|
3
|
-
import React, {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from "
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
import React, { forwardRef } from "react";
|
|
3
|
+
import { BodyShort, Label, omit } from "../..";
|
|
4
|
+
import ErrorMessage from "../ErrorMessage";
|
|
5
|
+
import { FormFieldProps, useFormField } from "../useFormField";
|
|
6
|
+
import SearchFieldButton, { SearchFieldButtonType } from "./SearchFieldButton";
|
|
7
|
+
import SearchFieldClearButton, {
|
|
8
|
+
SearchFieldClearButtonType,
|
|
9
|
+
} from "./SearchFieldClearButton";
|
|
10
|
+
import SearchFieldInput, { SearchFieldInputType } from "./SearchFieldInput";
|
|
11
|
+
|
|
12
|
+
export interface SearchFieldContextProps {
|
|
13
|
+
inputProps: {
|
|
14
|
+
id: string;
|
|
15
|
+
"aria-invalid": boolean;
|
|
16
|
+
"aria-describedby"?: string;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
};
|
|
19
|
+
size?: "medium" | "small";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const SearchFieldContext = React.createContext<SearchFieldContextProps | null>(
|
|
23
|
+
null
|
|
24
|
+
);
|
|
15
25
|
|
|
16
26
|
export interface SearchFieldProps
|
|
17
|
-
extends
|
|
18
|
-
|
|
27
|
+
extends FormFieldProps,
|
|
28
|
+
React.HTMLAttributes<HTMLDivElement> {
|
|
29
|
+
/**
|
|
30
|
+
* SearchFieldInput & SearchFieldButton
|
|
31
|
+
*/
|
|
32
|
+
children: React.ReactNode;
|
|
19
33
|
/**
|
|
20
34
|
* If enabled shows the label and description for screenreaders only
|
|
21
35
|
*/
|
|
@@ -24,101 +38,50 @@ export interface SearchFieldProps
|
|
|
24
38
|
* SearchField label
|
|
25
39
|
*/
|
|
26
40
|
label: React.ReactNode;
|
|
27
|
-
/**
|
|
28
|
-
* Inverts color theme
|
|
29
|
-
* @default false
|
|
30
|
-
*/
|
|
31
|
-
inverted?: boolean;
|
|
32
|
-
/**
|
|
33
|
-
* Customize aria-label on clear button
|
|
34
|
-
* @default "Slett tekst i felt"
|
|
35
|
-
*/
|
|
36
|
-
clearButtonLabel?: string;
|
|
37
|
-
/**
|
|
38
|
-
* Callback for when user manually clears input with button or Escape
|
|
39
|
-
*/
|
|
40
|
-
onClear?: () => void;
|
|
41
|
-
/**
|
|
42
|
-
* Callback for value in input after change
|
|
43
|
-
*/
|
|
44
|
-
onChange?: (value: string) => void;
|
|
45
|
-
/**
|
|
46
|
-
* Toggles display of "clear"-button when there is text in field
|
|
47
|
-
*/
|
|
48
|
-
clearButton?: boolean;
|
|
49
41
|
}
|
|
50
42
|
|
|
51
|
-
|
|
43
|
+
interface SearchFieldComponent
|
|
44
|
+
extends React.ForwardRefExoticComponent<
|
|
45
|
+
SearchFieldProps & React.RefAttributes<HTMLDivElement>
|
|
46
|
+
> {
|
|
47
|
+
Button: SearchFieldButtonType;
|
|
48
|
+
Clear: SearchFieldClearButtonType;
|
|
49
|
+
Input: SearchFieldInputType;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const SearchField = forwardRef<HTMLDivElement, SearchFieldProps>(
|
|
52
53
|
(props, ref) => {
|
|
53
|
-
const {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
const {
|
|
55
|
+
inputProps,
|
|
56
|
+
errorId,
|
|
57
|
+
showErrorMsg,
|
|
58
|
+
hasError,
|
|
59
|
+
size,
|
|
60
|
+
inputDescriptionId,
|
|
61
|
+
} = useFormField(props, "searchfield");
|
|
57
62
|
|
|
58
63
|
const {
|
|
59
64
|
className,
|
|
60
65
|
hideLabel,
|
|
66
|
+
children,
|
|
61
67
|
label,
|
|
62
68
|
description,
|
|
63
|
-
|
|
64
|
-
clearButtonLabel,
|
|
65
|
-
onClear,
|
|
66
|
-
inverted = false,
|
|
67
|
-
clearButton = true,
|
|
69
|
+
error,
|
|
68
70
|
...rest
|
|
69
71
|
} = props;
|
|
70
72
|
|
|
71
|
-
const [controlledValue, setControlledValue] = useState(value ?? "");
|
|
72
|
-
const searchRef = useRef<HTMLInputElement | null>(null);
|
|
73
|
-
const mergedRef = mergeRefs([searchRef, ref]);
|
|
74
|
-
const [wrapperRef, setWrapperRef] = useState<HTMLDivElement | null>(null);
|
|
75
|
-
|
|
76
|
-
const handleChange = useCallback(
|
|
77
|
-
(v: string) => {
|
|
78
|
-
if (searchRef.current && value === undefined) {
|
|
79
|
-
searchRef.current.value = v;
|
|
80
|
-
setControlledValue(v);
|
|
81
|
-
}
|
|
82
|
-
props?.onChange?.(v);
|
|
83
|
-
},
|
|
84
|
-
[props, value]
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
const handleClear = useCallback(() => {
|
|
88
|
-
onClear?.();
|
|
89
|
-
handleChange("");
|
|
90
|
-
searchRef.current && searchRef.current?.focus?.();
|
|
91
|
-
}, [handleChange, onClear]);
|
|
92
|
-
|
|
93
|
-
useEventListener(
|
|
94
|
-
"keydown",
|
|
95
|
-
useCallback(
|
|
96
|
-
(e) => {
|
|
97
|
-
if (e.key === "Escape") {
|
|
98
|
-
e.preventDefault();
|
|
99
|
-
handleClear();
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
[handleClear]
|
|
103
|
-
),
|
|
104
|
-
wrapperRef
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
useEffect(() => {
|
|
108
|
-
value !== undefined && setControlledValue(value);
|
|
109
|
-
}, [value]);
|
|
110
|
-
|
|
111
73
|
return (
|
|
112
74
|
<div
|
|
113
|
-
ref={
|
|
75
|
+
ref={ref}
|
|
76
|
+
{...omit(rest, ["id", "error", "errorId", "size", "disabled"])}
|
|
114
77
|
className={cl(
|
|
115
78
|
className,
|
|
116
79
|
"navds-form-field",
|
|
117
80
|
`navds-form-field--${size ?? "medium"}`,
|
|
118
81
|
"navds-search-field",
|
|
119
82
|
{
|
|
83
|
+
"navds-search-field--error": hasError,
|
|
120
84
|
"navds-search-field--disabled": !!inputProps.disabled,
|
|
121
|
-
"navds-search-field--inverted": inverted,
|
|
122
85
|
}
|
|
123
86
|
)}
|
|
124
87
|
>
|
|
@@ -144,44 +107,26 @@ const SearchField = forwardRef<HTMLInputElement, SearchFieldProps>(
|
|
|
144
107
|
{description}
|
|
145
108
|
</BodyShort>
|
|
146
109
|
)}
|
|
147
|
-
<div
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
onChange={(e) => handleChange(e.target.value)}
|
|
160
|
-
type="search"
|
|
161
|
-
role="searchbox"
|
|
162
|
-
className={cl(
|
|
163
|
-
className,
|
|
164
|
-
"navds-search-field__input",
|
|
165
|
-
"navds-text-field__input",
|
|
166
|
-
"navds-body-short",
|
|
167
|
-
`navds-body-${size ?? "medium"}`
|
|
168
|
-
)}
|
|
169
|
-
/>
|
|
170
|
-
{controlledValue && clearButton && (
|
|
171
|
-
<button
|
|
172
|
-
onClick={() => handleClear()}
|
|
173
|
-
className="navds-search-field__clear-button"
|
|
174
|
-
>
|
|
175
|
-
<span className="navds-sr-only">
|
|
176
|
-
{clearButtonLabel ? clearButtonLabel : "Slett tekst i felt"}
|
|
177
|
-
</span>
|
|
178
|
-
<Close aria-hidden />
|
|
179
|
-
</button>
|
|
180
|
-
)}
|
|
110
|
+
<div className="navds-search-field__input-wrapper">
|
|
111
|
+
<SearchFieldContext.Provider
|
|
112
|
+
value={{
|
|
113
|
+
inputProps,
|
|
114
|
+
size,
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
{children}
|
|
118
|
+
</SearchFieldContext.Provider>
|
|
119
|
+
</div>
|
|
120
|
+
<div id={errorId} aria-relevant="additions removals" aria-live="polite">
|
|
121
|
+
{showErrorMsg && <ErrorMessage size={size}>{error}</ErrorMessage>}
|
|
181
122
|
</div>
|
|
182
123
|
</div>
|
|
183
124
|
);
|
|
184
125
|
}
|
|
185
|
-
);
|
|
126
|
+
) as SearchFieldComponent;
|
|
127
|
+
|
|
128
|
+
SearchField.Button = SearchFieldButton;
|
|
129
|
+
SearchField.Clear = SearchFieldClearButton;
|
|
130
|
+
SearchField.Input = SearchFieldInput;
|
|
186
131
|
|
|
187
132
|
export default SearchField;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React, { forwardRef, useContext } from "react";
|
|
2
|
+
import cl from "classnames";
|
|
3
|
+
import { SearchFieldContext } from "./SearchField";
|
|
4
|
+
import { Button, ButtonProps } from "../..";
|
|
5
|
+
|
|
6
|
+
export interface SearchFieldButtonProps extends Omit<ButtonProps, "size"> {
|
|
7
|
+
/**
|
|
8
|
+
* Button text
|
|
9
|
+
* @example <Search /> <span>Søk</span>
|
|
10
|
+
*/
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* Changes design and interactions
|
|
14
|
+
* @default "primary"
|
|
15
|
+
*/
|
|
16
|
+
variant?: "primary" | "secondary";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type SearchFieldButtonType = React.ForwardRefExoticComponent<
|
|
20
|
+
SearchFieldButtonProps & React.RefAttributes<HTMLButtonElement>
|
|
21
|
+
>;
|
|
22
|
+
|
|
23
|
+
const SearchFieldButton: SearchFieldButtonType = forwardRef(
|
|
24
|
+
({ className, variant = "primary", disabled, ...rest }, ref) => {
|
|
25
|
+
const searchField = useContext(SearchFieldContext);
|
|
26
|
+
|
|
27
|
+
if (searchField === null) {
|
|
28
|
+
console.warn("SearchFieldButton has to be wrapped in <SearchField />");
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { size, inputProps } = searchField;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Button
|
|
36
|
+
ref={ref}
|
|
37
|
+
className={cl(className, "navds-search-field__button")}
|
|
38
|
+
{...rest}
|
|
39
|
+
size={size}
|
|
40
|
+
variant={variant}
|
|
41
|
+
disabled={disabled ?? inputProps?.disabled}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
export default SearchFieldButton;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { forwardRef, useContext } from "react";
|
|
2
|
+
import cl from "classnames";
|
|
3
|
+
import { SearchFieldContext } from "./SearchField";
|
|
4
|
+
import { Button, ButtonProps } from "../..";
|
|
5
|
+
|
|
6
|
+
export interface SearchFieldClearButtonProps extends Omit<ButtonProps, "size"> {
|
|
7
|
+
/**
|
|
8
|
+
* Button text
|
|
9
|
+
* @example <Close /> <span>Søk</span>
|
|
10
|
+
*/
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* Changes design and interactions
|
|
14
|
+
* @default "secondary"
|
|
15
|
+
*/
|
|
16
|
+
variant?: "primary" | "secondary";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type SearchFieldClearButtonType = React.ForwardRefExoticComponent<
|
|
20
|
+
SearchFieldClearButtonProps & React.RefAttributes<HTMLButtonElement>
|
|
21
|
+
>;
|
|
22
|
+
|
|
23
|
+
const SearchFieldClearButton: SearchFieldClearButtonType = forwardRef(
|
|
24
|
+
({ className, variant = "secondary", disabled, ...rest }, ref) => {
|
|
25
|
+
const searchField = useContext(SearchFieldContext);
|
|
26
|
+
|
|
27
|
+
if (searchField === null) {
|
|
28
|
+
console.warn(
|
|
29
|
+
"SearchFieldClearButton has to be wrapped in <SearchField />"
|
|
30
|
+
);
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { size, inputProps } = searchField;
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<Button
|
|
38
|
+
{...rest}
|
|
39
|
+
ref={ref}
|
|
40
|
+
className={cl(className, "navds-search-field__clear-button")}
|
|
41
|
+
size={size}
|
|
42
|
+
variant={variant}
|
|
43
|
+
disabled={disabled ?? inputProps?.disabled}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
export default SearchFieldClearButton;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React, { forwardRef, InputHTMLAttributes, useContext } from "react";
|
|
2
|
+
import cl from "classnames";
|
|
3
|
+
import { SearchFieldContext } from "./SearchField";
|
|
4
|
+
|
|
5
|
+
export interface SearchFieldInputProps
|
|
6
|
+
extends InputHTMLAttributes<HTMLInputElement> {}
|
|
7
|
+
|
|
8
|
+
export type SearchFieldInputType = React.ForwardRefExoticComponent<
|
|
9
|
+
SearchFieldInputProps & React.RefAttributes<HTMLInputElement>
|
|
10
|
+
>;
|
|
11
|
+
|
|
12
|
+
const SearchFieldInput: SearchFieldInputType = forwardRef(
|
|
13
|
+
({ className, ...rest }, ref) => {
|
|
14
|
+
const searchField = useContext(SearchFieldContext);
|
|
15
|
+
|
|
16
|
+
if (searchField === null) {
|
|
17
|
+
console.warn("SearchFieldInput has to be wrapped in <SearchField />");
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { size, inputProps } = searchField;
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<input
|
|
25
|
+
{...rest}
|
|
26
|
+
{...inputProps}
|
|
27
|
+
type="search"
|
|
28
|
+
role="searchbox"
|
|
29
|
+
ref={ref}
|
|
30
|
+
className={cl(
|
|
31
|
+
className,
|
|
32
|
+
"navds-search-field__input",
|
|
33
|
+
"navds-text-field__input",
|
|
34
|
+
"navds-body-short",
|
|
35
|
+
`navds-body-${size ?? "medium"}`
|
|
36
|
+
)}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
export default SearchFieldInput;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Close, Search } from "@navikt/ds-icons";
|
|
3
|
+
import { SearchField } from "../index";
|
|
4
|
+
|
|
5
|
+
export const Example = ({ size = "medium" }: { size: "medium" | "small" }) => {
|
|
6
|
+
const [value, setValue] = useState("");
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<SearchField size={size} label="Skriv i søkefeltet for å vise clearbutton">
|
|
10
|
+
<SearchField.Input
|
|
11
|
+
value={value}
|
|
12
|
+
onChange={(e) => setValue(e.target.value)}
|
|
13
|
+
/>
|
|
14
|
+
{!!value && (
|
|
15
|
+
<SearchField.Clear onClick={() => setValue("")}>
|
|
16
|
+
<Close />
|
|
17
|
+
Tøm
|
|
18
|
+
</SearchField.Clear>
|
|
19
|
+
)}
|
|
20
|
+
<SearchField.Button>
|
|
21
|
+
<Search /> Søk
|
|
22
|
+
</SearchField.Button>
|
|
23
|
+
</SearchField>
|
|
24
|
+
);
|
|
25
|
+
};
|