@lumx/react 3.3.0 → 3.3.1-alpha.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/_internal/types.d.ts +2 -0
- package/index.d.ts +0 -2
- package/index.js +24 -57
- package/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/index.ts +2 -0
- package/src/components/popover/Popover.tsx +9 -7
- package/src/components/select/SelectMultiple.stories.tsx +2 -105
- package/src/components/select/WithSelectContext.tsx +4 -10
- package/src/components/text-field/TextField.stories.tsx +0 -1
- package/src/components/text-field/TextField.test.tsx +4 -52
- package/src/components/text-field/TextField.tsx +4 -32
- package/src/hooks/useFocusTrap.ts +2 -2
package/package.json
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@juggle/resize-observer": "^3.2.0",
|
|
10
|
-
"@lumx/core": "^3.3.0",
|
|
11
|
-
"@lumx/icons": "^3.3.0",
|
|
10
|
+
"@lumx/core": "^3.3.1-alpha.0",
|
|
11
|
+
"@lumx/icons": "^3.3.1-alpha.0",
|
|
12
12
|
"@popperjs/core": "^2.5.4",
|
|
13
13
|
"body-scroll-lock": "^3.1.5",
|
|
14
14
|
"classnames": "^2.2.6",
|
|
@@ -113,5 +113,5 @@
|
|
|
113
113
|
"build:storybook": "cd storybook && ./build"
|
|
114
114
|
},
|
|
115
115
|
"sideEffects": false,
|
|
116
|
-
"version": "3.3.0"
|
|
116
|
+
"version": "3.3.1-alpha.0"
|
|
117
117
|
}
|
package/src/components/index.ts
CHANGED
|
@@ -146,6 +146,8 @@ export type Typography = TypographyInterface | TypographyCustom;
|
|
|
146
146
|
export const AspectRatio = {
|
|
147
147
|
/** Intrinsic content ratio. */
|
|
148
148
|
original: 'original',
|
|
149
|
+
/** Ratio 3:1 */
|
|
150
|
+
panoramic: 'panoramic',
|
|
149
151
|
/** Ratio 16:9 */
|
|
150
152
|
wide: 'wide',
|
|
151
153
|
/** Ratio 3:2 */
|
|
@@ -173,16 +173,18 @@ const _InnerPopover: Comp<PopoverProps, HTMLDivElement> = forwardRef((props, ref
|
|
|
173
173
|
* unless specifically requested not to.
|
|
174
174
|
*/
|
|
175
175
|
if (isFocusedWithin.current && focusAnchorOnClose) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
// Focus the first focusable element in anchor.
|
|
179
|
-
elementToFocus = getFirstAndLastFocusable(anchorRef.current).first;
|
|
176
|
+
if (parentElement?.current) {
|
|
177
|
+
parentElement?.current.focus();
|
|
180
178
|
}
|
|
181
|
-
|
|
179
|
+
|
|
180
|
+
const firstFocusable = anchorRef?.current && getFirstAndLastFocusable(anchorRef?.current).first;
|
|
181
|
+
if (firstFocusable) {
|
|
182
|
+
// Focus the first focusable element in anchor.
|
|
183
|
+
firstFocusable.focus();
|
|
184
|
+
} else {
|
|
182
185
|
// Fallback on the anchor element.
|
|
183
|
-
|
|
186
|
+
anchorRef?.current?.focus();
|
|
184
187
|
}
|
|
185
|
-
elementToFocus?.focus({ preventScroll: true });
|
|
186
188
|
}
|
|
187
189
|
|
|
188
190
|
onClose();
|
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
/* istanbul ignore file */
|
|
2
2
|
import { mdiTram } from '@lumx/icons/';
|
|
3
|
-
import {
|
|
4
|
-
Chip,
|
|
5
|
-
Dialog,
|
|
6
|
-
List,
|
|
7
|
-
ListDivider,
|
|
8
|
-
ListItem,
|
|
9
|
-
ListSubheader,
|
|
10
|
-
SelectMultiple,
|
|
11
|
-
Size,
|
|
12
|
-
TextField,
|
|
13
|
-
Toolbar,
|
|
14
|
-
} from '@lumx/react';
|
|
3
|
+
import { Chip, List, ListItem, SelectMultiple, Size } from '@lumx/react';
|
|
15
4
|
import { useBooleanState } from '@lumx/react/hooks/useBooleanState';
|
|
16
5
|
import noop from 'lodash/noop';
|
|
17
|
-
import React, { MouseEventHandler, SyntheticEvent,
|
|
6
|
+
import React, { MouseEventHandler, SyntheticEvent, useState } from 'react';
|
|
18
7
|
import { SelectVariant } from './constants';
|
|
19
8
|
|
|
20
9
|
export default { title: 'LumX components/select/Select Multiple' };
|
|
@@ -236,95 +225,3 @@ export const ChipsCustomSelectMultiple = ({ theme }: any) => {
|
|
|
236
225
|
</SelectMultiple>
|
|
237
226
|
);
|
|
238
227
|
};
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Test select focus trap (focus is contained inside the dialog then inside the select dropdown)
|
|
242
|
-
*/
|
|
243
|
-
export const SelectWithinADialog = ({ theme }: any) => {
|
|
244
|
-
const searchFieldRef = useRef(null);
|
|
245
|
-
|
|
246
|
-
const [searchText, setSearchText] = useState<string>();
|
|
247
|
-
const [values, setValues] = useState<string[]>([]);
|
|
248
|
-
const [isOpen, closeSelect, , toggleSelect] = useBooleanState(false);
|
|
249
|
-
|
|
250
|
-
const clearSelected = (event: SyntheticEvent, value: string) => {
|
|
251
|
-
event.stopPropagation();
|
|
252
|
-
setValues(value ? values.filter((val) => val !== value) : []);
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
const selectItem = (item: string) => () => {
|
|
256
|
-
if (values.includes(item)) {
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
closeSelect();
|
|
261
|
-
setValues([...values, item]);
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const filteredChoices =
|
|
265
|
-
searchText && searchText.length > 0 ? CHOICES.filter((choice) => choice.includes(searchText)) : CHOICES;
|
|
266
|
-
|
|
267
|
-
return (
|
|
268
|
-
<>
|
|
269
|
-
<Dialog isOpen>
|
|
270
|
-
<header>
|
|
271
|
-
<Toolbar label={<span className="lumx-typography-title">Dialog header</span>} />
|
|
272
|
-
</header>
|
|
273
|
-
<div className="lumx-spacing-padding-horizontal-huge lumx-spacing-padding-bottom-huge">
|
|
274
|
-
{/* Testing hidden input do not count in th focus trap*/}
|
|
275
|
-
<input hidden type="file" />
|
|
276
|
-
<input type="hidden" />
|
|
277
|
-
|
|
278
|
-
<div className="lumx-spacing-margin-bottom-huge">The select should capture the focus on open.</div>
|
|
279
|
-
|
|
280
|
-
<SelectMultiple
|
|
281
|
-
isOpen={isOpen}
|
|
282
|
-
value={values}
|
|
283
|
-
onClear={clearSelected}
|
|
284
|
-
clearButtonProps={{ label: 'Clear' }}
|
|
285
|
-
label={LABEL}
|
|
286
|
-
placeholder={PLACEHOLDER}
|
|
287
|
-
theme={theme}
|
|
288
|
-
onInputClick={toggleSelect}
|
|
289
|
-
onDropdownClose={closeSelect}
|
|
290
|
-
icon={mdiTram}
|
|
291
|
-
focusElement={searchFieldRef}
|
|
292
|
-
>
|
|
293
|
-
<List isClickable>
|
|
294
|
-
<>
|
|
295
|
-
<ListSubheader>
|
|
296
|
-
<TextField
|
|
297
|
-
clearButtonProps={{ label: 'Clear' }}
|
|
298
|
-
placeholder="Search"
|
|
299
|
-
role="searchbox"
|
|
300
|
-
inputRef={searchFieldRef}
|
|
301
|
-
onChange={setSearchText}
|
|
302
|
-
value={searchText}
|
|
303
|
-
/>
|
|
304
|
-
</ListSubheader>
|
|
305
|
-
<ListDivider role="presentation" />
|
|
306
|
-
</>
|
|
307
|
-
|
|
308
|
-
{filteredChoices.length > 0
|
|
309
|
-
? filteredChoices.map((choice) => (
|
|
310
|
-
<ListItem
|
|
311
|
-
isSelected={values.includes(choice)}
|
|
312
|
-
key={choice}
|
|
313
|
-
onItemSelected={selectItem(choice)}
|
|
314
|
-
size={Size.tiny}
|
|
315
|
-
>
|
|
316
|
-
{choice}
|
|
317
|
-
</ListItem>
|
|
318
|
-
))
|
|
319
|
-
: [
|
|
320
|
-
<ListItem key={0} size={Size.tiny}>
|
|
321
|
-
No data
|
|
322
|
-
</ListItem>,
|
|
323
|
-
]}
|
|
324
|
-
</List>
|
|
325
|
-
</SelectMultiple>
|
|
326
|
-
</div>
|
|
327
|
-
</Dialog>
|
|
328
|
-
</>
|
|
329
|
-
);
|
|
330
|
-
};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import classNames from 'classnames';
|
|
2
1
|
import React, { Ref, useCallback, useMemo, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import classNames from 'classnames';
|
|
3
4
|
import { uid } from 'uid';
|
|
4
5
|
|
|
5
|
-
import { Placement } from '@lumx/react';
|
|
6
6
|
import { Kind, Theme } from '@lumx/react/components';
|
|
7
7
|
import { Dropdown } from '@lumx/react/components/dropdown/Dropdown';
|
|
8
8
|
import { InputHelper } from '@lumx/react/components/input-helper/InputHelper';
|
|
9
|
-
import { useFocusTrap } from '@lumx/react/hooks/useFocusTrap';
|
|
10
|
-
import { useListenFocus } from '@lumx/react/hooks/useListenFocus';
|
|
11
9
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
12
10
|
import { mergeRefs } from '@lumx/react/utils/mergeRefs';
|
|
11
|
+
import { useListenFocus } from '@lumx/react/hooks/useListenFocus';
|
|
12
|
+
import { Placement } from '@lumx/react';
|
|
13
13
|
|
|
14
14
|
import { CoreSelectProps, SelectVariant } from './constants';
|
|
15
15
|
|
|
@@ -30,7 +30,6 @@ export const WithSelectContext = (
|
|
|
30
30
|
{
|
|
31
31
|
children,
|
|
32
32
|
className,
|
|
33
|
-
focusElement,
|
|
34
33
|
isMultiple,
|
|
35
34
|
closeOnClick = !isMultiple,
|
|
36
35
|
disabled,
|
|
@@ -59,7 +58,6 @@ export const WithSelectContext = (
|
|
|
59
58
|
const selectId = useMemo(() => id || `select-${uid()}`, [id]);
|
|
60
59
|
const anchorRef = useRef<HTMLElement>(null);
|
|
61
60
|
const selectRef = useRef<HTMLDivElement>(null);
|
|
62
|
-
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
63
61
|
const isFocus = useListenFocus(anchorRef);
|
|
64
62
|
|
|
65
63
|
const handleKeyboardNav = useCallback(
|
|
@@ -79,9 +77,6 @@ export const WithSelectContext = (
|
|
|
79
77
|
anchorRef?.current?.blur();
|
|
80
78
|
};
|
|
81
79
|
|
|
82
|
-
// Handle focus trap.
|
|
83
|
-
useFocusTrap(isOpen && dropdownRef.current, focusElement?.current);
|
|
84
|
-
|
|
85
80
|
return (
|
|
86
81
|
<div
|
|
87
82
|
ref={mergeRefs(ref, selectRef)}
|
|
@@ -130,7 +125,6 @@ export const WithSelectContext = (
|
|
|
130
125
|
placement={Placement.BOTTOM_START}
|
|
131
126
|
onClose={onClose}
|
|
132
127
|
onInfiniteScroll={onInfiniteScroll}
|
|
133
|
-
ref={dropdownRef}
|
|
134
128
|
>
|
|
135
129
|
{children}
|
|
136
130
|
</Dropdown>
|
|
@@ -5,14 +5,8 @@ import camelCase from 'lodash/camelCase';
|
|
|
5
5
|
import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
|
|
6
6
|
import { getBasicClass } from '@lumx/react/utils/className';
|
|
7
7
|
|
|
8
|
-
import { render
|
|
9
|
-
import {
|
|
10
|
-
getByClassName,
|
|
11
|
-
getByTagName,
|
|
12
|
-
queryAllByClassName,
|
|
13
|
-
queryByClassName,
|
|
14
|
-
queryByTagName,
|
|
15
|
-
} from '@lumx/react/testing/utils/queries';
|
|
8
|
+
import { render } from '@testing-library/react';
|
|
9
|
+
import { getByClassName, getByTagName, queryAllByClassName, queryByTagName } from '@lumx/react/testing/utils/queries';
|
|
16
10
|
import partition from 'lodash/partition';
|
|
17
11
|
import userEvent from '@testing-library/user-event';
|
|
18
12
|
|
|
@@ -33,11 +27,9 @@ const setup = (propsOverride: Partial<TextFieldProps> = {}) => {
|
|
|
33
27
|
| HTMLInputElement;
|
|
34
28
|
const helpers = queryAllByClassName(container, 'lumx-text-field__helper');
|
|
35
29
|
const [[helper], [error]] = partition(helpers, (h) => !h.className.includes('lumx-input-helper--color-red'));
|
|
36
|
-
const clearButton = queryByClassName(container, 'lumx-text-field__input-clear');
|
|
37
30
|
|
|
38
31
|
return {
|
|
39
32
|
props,
|
|
40
|
-
clearButton,
|
|
41
33
|
container,
|
|
42
34
|
element,
|
|
43
35
|
inputNative,
|
|
@@ -114,27 +106,22 @@ describe(`<${TextField.displayName}>`, () => {
|
|
|
114
106
|
});
|
|
115
107
|
|
|
116
108
|
it('should have helper text', () => {
|
|
117
|
-
const { helper
|
|
109
|
+
const { helper } = setup({
|
|
118
110
|
helper: 'helper',
|
|
119
111
|
label: 'test',
|
|
120
112
|
placeholder: 'test',
|
|
121
113
|
});
|
|
122
|
-
|
|
123
114
|
expect(helper).toHaveTextContent('helper');
|
|
124
|
-
expect(inputNative).toHaveAttribute('aria-describedby');
|
|
125
115
|
});
|
|
126
116
|
|
|
127
117
|
it('should have error text', () => {
|
|
128
|
-
const { error
|
|
118
|
+
const { error } = setup({
|
|
129
119
|
error: 'error',
|
|
130
120
|
hasError: true,
|
|
131
121
|
label: 'test',
|
|
132
122
|
placeholder: 'test',
|
|
133
123
|
});
|
|
134
|
-
|
|
135
124
|
expect(error).toHaveTextContent('error');
|
|
136
|
-
expect(inputNative).toHaveAttribute('aria-invalid', 'true');
|
|
137
|
-
expect(inputNative).toHaveAttribute('aria-describedby');
|
|
138
125
|
});
|
|
139
126
|
|
|
140
127
|
it('should not have error text', () => {
|
|
@@ -173,41 +160,6 @@ describe(`<${TextField.displayName}>`, () => {
|
|
|
173
160
|
|
|
174
161
|
expect(onChange).toHaveBeenCalledWith('a', 'name', expect.objectContaining({}));
|
|
175
162
|
});
|
|
176
|
-
|
|
177
|
-
it('should trigger `onChange` with empty value when text field is cleared', async () => {
|
|
178
|
-
const onChange = jest.fn();
|
|
179
|
-
const { clearButton } = setup({
|
|
180
|
-
value: 'initial value',
|
|
181
|
-
name: 'name',
|
|
182
|
-
clearButtonProps: { label: 'Clear' },
|
|
183
|
-
onChange,
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
expect(clearButton).toBeInTheDocument();
|
|
187
|
-
|
|
188
|
-
await userEvent.click(clearButton as HTMLElement);
|
|
189
|
-
|
|
190
|
-
expect(onChange).toHaveBeenCalledWith('');
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('should trigger `onChange` with empty value and `onClear` when text field is cleared', async () => {
|
|
194
|
-
const onChange = jest.fn();
|
|
195
|
-
const onClear = jest.fn();
|
|
196
|
-
const { clearButton } = setup({
|
|
197
|
-
value: 'initial value',
|
|
198
|
-
name: 'name',
|
|
199
|
-
clearButtonProps: { label: 'Clear' },
|
|
200
|
-
onChange,
|
|
201
|
-
onClear,
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
expect(clearButton).toBeInTheDocument();
|
|
205
|
-
|
|
206
|
-
await userEvent.click(clearButton as HTMLElement);
|
|
207
|
-
|
|
208
|
-
expect(onChange).toHaveBeenCalledWith('');
|
|
209
|
-
expect(onClear).toHaveBeenCalled();
|
|
210
|
-
});
|
|
211
163
|
});
|
|
212
164
|
|
|
213
165
|
// Common tests suite.
|
|
@@ -61,8 +61,6 @@ export interface TextFieldProps extends GenericProps, HasTheme {
|
|
|
61
61
|
onBlur?(event: React.FocusEvent): void;
|
|
62
62
|
/** On change callback. */
|
|
63
63
|
onChange(value: string, name?: string, event?: SyntheticEvent): void;
|
|
64
|
-
/** On clear callback. */
|
|
65
|
-
onClear?(event?: SyntheticEvent): void;
|
|
66
64
|
/** On focus callback. */
|
|
67
65
|
onFocus?(event: React.FocusEvent): void;
|
|
68
66
|
}
|
|
@@ -155,8 +153,6 @@ interface InputNativeProps {
|
|
|
155
153
|
onChange(value: string, name?: string, event?: SyntheticEvent): void;
|
|
156
154
|
onFocus?(value: React.FocusEvent): void;
|
|
157
155
|
onBlur?(value: React.FocusEvent): void;
|
|
158
|
-
hasError?: boolean;
|
|
159
|
-
describedById?: string;
|
|
160
156
|
}
|
|
161
157
|
|
|
162
158
|
const renderInputNative: React.FC<InputNativeProps> = (props) => {
|
|
@@ -176,8 +172,6 @@ const renderInputNative: React.FC<InputNativeProps> = (props) => {
|
|
|
176
172
|
recomputeNumberOfRows,
|
|
177
173
|
type,
|
|
178
174
|
name,
|
|
179
|
-
hasError,
|
|
180
|
-
describedById,
|
|
181
175
|
...forwardedProps
|
|
182
176
|
} = props;
|
|
183
177
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
@@ -220,8 +214,6 @@ const renderInputNative: React.FC<InputNativeProps> = (props) => {
|
|
|
220
214
|
onFocus: onTextFieldFocus,
|
|
221
215
|
onBlur: onTextFieldBlur,
|
|
222
216
|
onChange: handleChange,
|
|
223
|
-
'aria-invalid': hasError ? 'true' : undefined,
|
|
224
|
-
'aria-describedby': describedById,
|
|
225
217
|
ref: mergeRefs(inputRef as any, ref) as any,
|
|
226
218
|
};
|
|
227
219
|
if (multiline) {
|
|
@@ -262,7 +254,6 @@ export const TextField: Comp<TextFieldProps, HTMLDivElement> = forwardRef((props
|
|
|
262
254
|
name,
|
|
263
255
|
onBlur,
|
|
264
256
|
onChange,
|
|
265
|
-
onClear,
|
|
266
257
|
onFocus,
|
|
267
258
|
placeholder,
|
|
268
259
|
textFieldRef,
|
|
@@ -273,17 +264,6 @@ export const TextField: Comp<TextFieldProps, HTMLDivElement> = forwardRef((props
|
|
|
273
264
|
...forwardedProps
|
|
274
265
|
} = props;
|
|
275
266
|
const textFieldId = useMemo(() => id || `text-field-${uid()}`, [id]);
|
|
276
|
-
/**
|
|
277
|
-
* Generate unique ids for both the helper and error texts, in order to
|
|
278
|
-
* later on add them to the input native as aria-describedby. If both the error and the helper are present,
|
|
279
|
-
* we want to first use the most important one, which is the errorId. That way, screen readers will read first
|
|
280
|
-
* the error and then the helper
|
|
281
|
-
*/
|
|
282
|
-
const helperId = helper ? `text-field-helper-${uid()}` : undefined;
|
|
283
|
-
const errorId = error ? `text-field-error-${uid()}` : undefined;
|
|
284
|
-
const describedByIds = [errorId, helperId].filter(Boolean);
|
|
285
|
-
const describedById = describedByIds.length === 0 ? undefined : describedByIds.join(' ');
|
|
286
|
-
|
|
287
267
|
const [isFocus, setFocus] = useState(false);
|
|
288
268
|
const { rows, recomputeNumberOfRows } = useComputeNumberOfRows(multiline ? minimumRows || DEFAULT_MIN_ROWS : 0);
|
|
289
269
|
const valueLength = (value || '').length;
|
|
@@ -295,16 +275,12 @@ export const TextField: Comp<TextFieldProps, HTMLDivElement> = forwardRef((props
|
|
|
295
275
|
* and remove focus from the clear button.
|
|
296
276
|
* @param evt On clear event.
|
|
297
277
|
*/
|
|
298
|
-
const
|
|
278
|
+
const onClear = (evt: React.ChangeEvent) => {
|
|
299
279
|
evt.nativeEvent.preventDefault();
|
|
300
280
|
evt.nativeEvent.stopPropagation();
|
|
301
281
|
(evt.currentTarget as HTMLElement).blur();
|
|
302
282
|
|
|
303
283
|
onChange('');
|
|
304
|
-
|
|
305
|
-
if (onClear) {
|
|
306
|
-
onClear(evt);
|
|
307
|
-
}
|
|
308
284
|
};
|
|
309
285
|
|
|
310
286
|
return (
|
|
@@ -383,8 +359,6 @@ export const TextField: Comp<TextFieldProps, HTMLDivElement> = forwardRef((props
|
|
|
383
359
|
type,
|
|
384
360
|
value,
|
|
385
361
|
name,
|
|
386
|
-
hasError,
|
|
387
|
-
describedById,
|
|
388
362
|
...forwardedProps,
|
|
389
363
|
})}
|
|
390
364
|
</div>
|
|
@@ -409,8 +383,6 @@ export const TextField: Comp<TextFieldProps, HTMLDivElement> = forwardRef((props
|
|
|
409
383
|
type,
|
|
410
384
|
value,
|
|
411
385
|
name,
|
|
412
|
-
hasError,
|
|
413
|
-
describedById,
|
|
414
386
|
...forwardedProps,
|
|
415
387
|
})}
|
|
416
388
|
</div>
|
|
@@ -433,7 +405,7 @@ export const TextField: Comp<TextFieldProps, HTMLDivElement> = forwardRef((props
|
|
|
433
405
|
emphasis={Emphasis.low}
|
|
434
406
|
size={Size.s}
|
|
435
407
|
theme={theme}
|
|
436
|
-
onClick={
|
|
408
|
+
onClick={onClear}
|
|
437
409
|
type="button"
|
|
438
410
|
/>
|
|
439
411
|
)}
|
|
@@ -442,13 +414,13 @@ export const TextField: Comp<TextFieldProps, HTMLDivElement> = forwardRef((props
|
|
|
442
414
|
</div>
|
|
443
415
|
|
|
444
416
|
{hasError && error && (
|
|
445
|
-
<InputHelper className={`${CLASSNAME}__helper`} kind={Kind.error} theme={theme}
|
|
417
|
+
<InputHelper className={`${CLASSNAME}__helper`} kind={Kind.error} theme={theme}>
|
|
446
418
|
{error}
|
|
447
419
|
</InputHelper>
|
|
448
420
|
)}
|
|
449
421
|
|
|
450
422
|
{helper && (
|
|
451
|
-
<InputHelper className={`${CLASSNAME}__helper`} theme={theme}
|
|
423
|
+
<InputHelper className={`${CLASSNAME}__helper`} theme={theme}>
|
|
452
424
|
{helper}
|
|
453
425
|
</InputHelper>
|
|
454
426
|
)}
|
|
@@ -72,10 +72,10 @@ export function useFocusTrap(focusZoneElement: HTMLElement | Falsy, focusElement
|
|
|
72
72
|
// SETUP:
|
|
73
73
|
if (focusElement && focusZoneElement.contains(focusElement)) {
|
|
74
74
|
// Focus the given element.
|
|
75
|
-
focusElement.focus(
|
|
75
|
+
focusElement.focus();
|
|
76
76
|
} else {
|
|
77
77
|
// Focus the first focusable element in the zone.
|
|
78
|
-
getFirstAndLastFocusable(focusZoneElement).first?.focus(
|
|
78
|
+
getFirstAndLastFocusable(focusZoneElement).first?.focus();
|
|
79
79
|
}
|
|
80
80
|
FOCUS_TRAPS.register(focusTrap);
|
|
81
81
|
|