@transferwise/components 46.32.0 → 46.34.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/build/index.js +71 -39
- package/build/index.js.map +1 -1
- package/build/index.mjs +71 -39
- package/build/index.mjs.map +1 -1
- package/build/types/alert/Alert.d.ts +3 -2
- package/build/types/alert/Alert.d.ts.map +1 -1
- package/build/types/common/domHelpers/documentIosClick.d.ts +0 -1
- package/build/types/common/domHelpers/documentIosClick.d.ts.map +1 -1
- package/build/types/common/domHelpers/index.d.ts +1 -1
- package/build/types/common/domHelpers/index.d.ts.map +1 -1
- package/build/types/dateLookup/DateLookup.d.ts.map +1 -1
- package/build/types/moneyInput/MoneyInput.d.ts +4 -2
- package/build/types/moneyInput/MoneyInput.d.ts.map +1 -1
- package/build/types/phoneNumberInput/PhoneNumberInput.d.ts +1 -1
- package/build/types/phoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
- package/build/types/select/Select.d.ts +7 -7
- package/build/types/select/Select.d.ts.map +1 -1
- package/build/types/typeahead/Typeahead.d.ts +4 -55
- package/build/types/typeahead/Typeahead.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/alert/Alert.spec.tsx +12 -0
- package/src/alert/Alert.story.tsx +11 -1
- package/src/alert/Alert.tsx +33 -14
- package/src/common/domHelpers/documentIosClick.ts +0 -5
- package/src/common/domHelpers/index.ts +0 -1
- package/src/dateLookup/DateLookup.rtl.spec.tsx +2 -3
- package/src/dateLookup/DateLookup.tsx +1 -3
- package/src/inputs/SelectInput.spec.tsx +1 -1
- package/src/moneyInput/MoneyInput.rtl.spec.tsx +10 -0
- package/src/moneyInput/MoneyInput.spec.js +10 -5
- package/src/moneyInput/MoneyInput.tsx +21 -14
- package/src/phoneNumberInput/PhoneNumberInput.rtl.spec.tsx +10 -0
- package/src/phoneNumberInput/PhoneNumberInput.tsx +11 -2
- package/src/select/Select.js +18 -15
- package/src/select/Select.rtl.spec.tsx +17 -0
- package/src/select/Select.spec.js +2 -7
- package/src/typeahead/Typeahead.rtl.spec.tsx +16 -0
- package/src/typeahead/Typeahead.tsx +21 -7
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Field } from '../field/Field';
|
|
1
2
|
import { mockMatchMedia, mockResizeObserver, render, screen, within } from '../test-utils';
|
|
2
3
|
|
|
3
4
|
import PhoneNumberInput from './PhoneNumberInput';
|
|
@@ -19,4 +20,13 @@ describe('PhoneNumberInput', () => {
|
|
|
19
20
|
within(screen.getByLabelText('Prioritized label')).getByRole('textbox'),
|
|
20
21
|
).toBeInTheDocument();
|
|
21
22
|
});
|
|
23
|
+
|
|
24
|
+
it('supports `Field` for labeling', () => {
|
|
25
|
+
render(
|
|
26
|
+
<Field label="Phone number">
|
|
27
|
+
<PhoneNumberInput initialValue="+12345678" onChange={() => {}} />
|
|
28
|
+
</Field>,
|
|
29
|
+
);
|
|
30
|
+
expect(screen.getAllByRole('group')[0]).toHaveAccessibleName(/^Phone number/);
|
|
31
|
+
});
|
|
22
32
|
});
|
|
@@ -2,6 +2,7 @@ import { useState, useEffect, useMemo } from 'react';
|
|
|
2
2
|
import { useIntl } from 'react-intl';
|
|
3
3
|
|
|
4
4
|
import { Size, SizeLarge, SizeMedium, SizeSmall } from '../common';
|
|
5
|
+
import { useInputAttributes } from '../inputs/contexts';
|
|
5
6
|
import { SelectInput, SelectInputOptionContent, SelectInputProps } from '../inputs/SelectInput';
|
|
6
7
|
|
|
7
8
|
import messages from './PhoneNumberInput.messages';
|
|
@@ -43,7 +44,7 @@ const defaultDisabledCountries = [] satisfies PhoneNumberInputProps['disabledCou
|
|
|
43
44
|
|
|
44
45
|
const PhoneNumberInput = ({
|
|
45
46
|
id,
|
|
46
|
-
'aria-labelledby':
|
|
47
|
+
'aria-labelledby': ariaLabelledByProp,
|
|
47
48
|
required,
|
|
48
49
|
disabled,
|
|
49
50
|
initialValue,
|
|
@@ -57,6 +58,9 @@ const PhoneNumberInput = ({
|
|
|
57
58
|
selectProps = defaultSelectProps,
|
|
58
59
|
disabledCountries = defaultDisabledCountries,
|
|
59
60
|
}: PhoneNumberInputProps) => {
|
|
61
|
+
const inputAttributes = useInputAttributes({ nonLabelable: true });
|
|
62
|
+
const ariaLabelledBy = ariaLabelledByProp ?? inputAttributes['aria-labelledby'];
|
|
63
|
+
|
|
60
64
|
const { locale, formatMessage } = useIntl();
|
|
61
65
|
|
|
62
66
|
const [internalValue, setInternalValue] = useState<PhoneNumber>(() => {
|
|
@@ -140,7 +144,12 @@ const PhoneNumberInput = ({
|
|
|
140
144
|
}, [onChange, broadcastedValue, internalValue]);
|
|
141
145
|
|
|
142
146
|
return (
|
|
143
|
-
<div
|
|
147
|
+
<div
|
|
148
|
+
role="group"
|
|
149
|
+
{...inputAttributes}
|
|
150
|
+
aria-labelledby={ariaLabelledBy}
|
|
151
|
+
className="tw-telephone"
|
|
152
|
+
>
|
|
144
153
|
<div className="tw-telephone__country-select">
|
|
145
154
|
<SelectInput
|
|
146
155
|
placeholder={formatMessage(messages.selectInputPlaceholder)}
|
package/src/select/Select.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useId } from '@radix-ui/react-id';
|
|
1
2
|
import { useTheme } from '@wise/components-theming';
|
|
2
3
|
import classNames from 'classnames';
|
|
3
4
|
import PropTypes from 'prop-types';
|
|
@@ -6,12 +7,13 @@ import { useIntl } from 'react-intl';
|
|
|
6
7
|
|
|
7
8
|
import Button from '../button';
|
|
8
9
|
import Chevron from '../chevron';
|
|
9
|
-
import { Position
|
|
10
|
+
import { Position } from '../common';
|
|
10
11
|
import BottomSheet from '../common/bottomSheet';
|
|
11
12
|
import { stopPropagation } from '../common/domHelpers';
|
|
12
13
|
import { useLayout } from '../common/hooks';
|
|
13
14
|
import Panel from '../common/panel';
|
|
14
15
|
import Drawer from '../drawer';
|
|
16
|
+
import { useInputAttributes } from '../inputs/contexts';
|
|
15
17
|
|
|
16
18
|
import messages from './Select.messages';
|
|
17
19
|
import Option from './option';
|
|
@@ -102,6 +104,8 @@ export default function Select({
|
|
|
102
104
|
dropdownProps,
|
|
103
105
|
buttonProps,
|
|
104
106
|
}) {
|
|
107
|
+
const inputAttributes = useInputAttributes();
|
|
108
|
+
|
|
105
109
|
const { formatMessage } = useIntl();
|
|
106
110
|
const { isModern } = useTheme();
|
|
107
111
|
const s = (className) => classNamesProp[className] || className;
|
|
@@ -118,8 +122,6 @@ export default function Select({
|
|
|
118
122
|
const isSearchEnabled = !!onSearchChange || !!search;
|
|
119
123
|
const isDropdownAutoWidth = dropdownWidth == null;
|
|
120
124
|
|
|
121
|
-
const fallbackButtonId = useMemo(() => getSimpleRandomId('np-select-'), []);
|
|
122
|
-
|
|
123
125
|
const options = useMemo(() => {
|
|
124
126
|
if (!search || !searchValue) {
|
|
125
127
|
return defaultOptions;
|
|
@@ -128,16 +130,16 @@ export default function Select({
|
|
|
128
130
|
return defaultOptions.filter(isSearchableOption).filter((option) => {
|
|
129
131
|
if (typeof search === 'function') {
|
|
130
132
|
return search(option, searchValue);
|
|
131
|
-
} else {
|
|
132
|
-
return defaultFilterFunction(option, searchValue);
|
|
133
133
|
}
|
|
134
|
+
return defaultFilterFunction(option, searchValue);
|
|
134
135
|
});
|
|
135
136
|
}, [defaultOptions, search, searchValue]);
|
|
136
137
|
|
|
137
138
|
const selectableOptions = useMemo(() => options.filter(isActionableOption), [options]);
|
|
138
139
|
const focusedOption = selectableOptions[keyboardFocusedOptionIndex];
|
|
139
140
|
|
|
140
|
-
const
|
|
141
|
+
const fallbackButtonId = useId();
|
|
142
|
+
const computedId = id || inputAttributes.id || fallbackButtonId;
|
|
141
143
|
const listboxId = `${computedId}-listbox`;
|
|
142
144
|
const searchBoxId = `${computedId}-searchbox`;
|
|
143
145
|
|
|
@@ -280,7 +282,7 @@ export default function Select({
|
|
|
280
282
|
useEffect(() => {
|
|
281
283
|
if (open) {
|
|
282
284
|
if (!isMobile || searchValue) {
|
|
283
|
-
if (isSearchEnabled &&
|
|
285
|
+
if (isSearchEnabled && searchBoxReference.current) {
|
|
284
286
|
searchBoxReference.current.focus();
|
|
285
287
|
}
|
|
286
288
|
if (
|
|
@@ -496,6 +498,7 @@ export default function Select({
|
|
|
496
498
|
>
|
|
497
499
|
<Button
|
|
498
500
|
ref={dropdownButtonReference}
|
|
501
|
+
{...inputAttributes}
|
|
499
502
|
id={computedId}
|
|
500
503
|
block={block}
|
|
501
504
|
size={size}
|
|
@@ -585,9 +588,6 @@ Select.propTypes = {
|
|
|
585
588
|
* if `function` you can define your own search function to implement custom search experience. This search function used while filtering the options array. The custom search function takes two parameters. First is the option the second is the keyword.
|
|
586
589
|
*/
|
|
587
590
|
search: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
|
|
588
|
-
onChange: PropTypes.func.isRequired,
|
|
589
|
-
onFocus: PropTypes.func,
|
|
590
|
-
onBlur: PropTypes.func,
|
|
591
591
|
options: PropTypes.arrayOf(
|
|
592
592
|
PropTypes.shape({
|
|
593
593
|
value: PropTypes.any,
|
|
@@ -602,17 +602,20 @@ Select.propTypes = {
|
|
|
602
602
|
searchStrings: PropTypes.arrayOf(PropTypes.string),
|
|
603
603
|
}),
|
|
604
604
|
).isRequired,
|
|
605
|
-
/**
|
|
606
|
-
* To have full control of your search value and response use `onSearchChange` function combined with `searchValue` and custom filtering on the options array.
|
|
607
|
-
* DO NOT USE TOGETHER WITH `search` PROPERTY
|
|
608
|
-
*/
|
|
609
|
-
onSearchChange: PropTypes.func,
|
|
610
605
|
searchValue: PropTypes.string,
|
|
611
606
|
searchPlaceholder: PropTypes.string,
|
|
612
607
|
classNames: PropTypes.objectOf(PropTypes.string),
|
|
613
608
|
dropdownUp: PropTypes.bool,
|
|
614
609
|
buttonProps: PropTypes.object,
|
|
615
610
|
dropdownProps: PropTypes.object,
|
|
611
|
+
onChange: PropTypes.func.isRequired,
|
|
612
|
+
onFocus: PropTypes.func,
|
|
613
|
+
onBlur: PropTypes.func,
|
|
614
|
+
/**
|
|
615
|
+
* To have full control of your search value and response use `onSearchChange` function combined with `searchValue` and custom filtering on the options array.
|
|
616
|
+
* DO NOT USE TOGETHER WITH `search` PROPERTY
|
|
617
|
+
*/
|
|
618
|
+
onSearchChange: PropTypes.func,
|
|
616
619
|
};
|
|
617
620
|
|
|
618
621
|
Select.defaultProps = {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Field } from '../field/Field';
|
|
2
|
+
import { mockMatchMedia, render, screen } from '../test-utils';
|
|
3
|
+
import Select from './Select';
|
|
4
|
+
|
|
5
|
+
mockMatchMedia();
|
|
6
|
+
|
|
7
|
+
describe('Select', () => {
|
|
8
|
+
it('supports `Field` for labeling', () => {
|
|
9
|
+
const options = [{ value: 'USD', label: 'USD' }];
|
|
10
|
+
render(
|
|
11
|
+
<Field label="Currency">
|
|
12
|
+
<Select options={options} selected={options[0]} onChange={() => {}} />
|
|
13
|
+
</Field>,
|
|
14
|
+
);
|
|
15
|
+
expect(screen.getByLabelText('Currency')).toHaveTextContent('USD');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -5,11 +5,6 @@ import Select from '.';
|
|
|
5
5
|
|
|
6
6
|
mockMatchMedia();
|
|
7
7
|
|
|
8
|
-
jest.mock('../common/domHelpers', () => ({
|
|
9
|
-
...jest.requireActual('../common/domHelpers'),
|
|
10
|
-
getSimpleRandomId: jest.fn((prefix) => `${prefix}mock-random-id`),
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
8
|
function enableDesktopScreen() {
|
|
14
9
|
window.innerWidth = Breakpoint.LARGE;
|
|
15
10
|
}
|
|
@@ -326,8 +321,8 @@ describe('Select', () => {
|
|
|
326
321
|
const button = screen.getByRole('button');
|
|
327
322
|
const options = screen.getByRole('listbox');
|
|
328
323
|
|
|
329
|
-
expect(button).toHaveAttribute('id'
|
|
330
|
-
expect(options).toHaveAttribute('id'
|
|
324
|
+
expect(button).toHaveAttribute('id');
|
|
325
|
+
expect(options).toHaveAttribute('id');
|
|
331
326
|
});
|
|
332
327
|
|
|
333
328
|
it('renders controls with passed id', async () => {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Field } from '../field/Field';
|
|
2
|
+
import { mockMatchMedia, render, screen } from '../test-utils';
|
|
3
|
+
import Typeahead from './Typeahead';
|
|
4
|
+
|
|
5
|
+
mockMatchMedia();
|
|
6
|
+
|
|
7
|
+
describe('Typeahead', () => {
|
|
8
|
+
it('supports `Field` for labeling', () => {
|
|
9
|
+
render(
|
|
10
|
+
<Field id="test" label="Tags">
|
|
11
|
+
<Typeahead id="test" name="test" options={[{ label: 'Test' }]} onChange={() => {}} />
|
|
12
|
+
</Field>,
|
|
13
|
+
);
|
|
14
|
+
expect(screen.getAllByRole('group')[0]).toHaveAccessibleName(/^Tags/);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from '../common/domHelpers';
|
|
19
19
|
import InlineAlert from '../inlineAlert';
|
|
20
20
|
import { InlineAlertProps } from '../inlineAlert/InlineAlert';
|
|
21
|
+
import { withInputAttributes, WithInputAttributesProps } from '../inputs/contexts';
|
|
21
22
|
|
|
22
23
|
import TypeaheadInput from './typeaheadInput/TypeaheadInput';
|
|
23
24
|
import TypeaheadOption from './typeaheadOption/TypeaheadOption';
|
|
@@ -68,6 +69,8 @@ export interface TypeaheadProps<T> {
|
|
|
68
69
|
validateChip?: (chip: TypeaheadOption<T>) => boolean;
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
type TypeaheadPropsWithInputAttributes<T> = TypeaheadProps<T> & Partial<WithInputAttributesProps>;
|
|
73
|
+
|
|
71
74
|
type TypeaheadState<T> = {
|
|
72
75
|
selected: readonly TypeaheadOption<T>[];
|
|
73
76
|
keyboardFocusedOptionIndex: number | null;
|
|
@@ -77,9 +80,9 @@ type TypeaheadState<T> = {
|
|
|
77
80
|
isFocused: boolean;
|
|
78
81
|
};
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
declare props:
|
|
82
|
-
Required<Pick<
|
|
83
|
+
class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, TypeaheadState<T>> {
|
|
84
|
+
declare props: TypeaheadPropsWithInputAttributes<T> &
|
|
85
|
+
Required<Pick<TypeaheadPropsWithInputAttributes<T>, keyof typeof Typeahead.defaultProps>>;
|
|
83
86
|
|
|
84
87
|
static defaultProps = {
|
|
85
88
|
allowNew: false,
|
|
@@ -98,7 +101,7 @@ export default class Typeahead<T> extends Component<TypeaheadProps<T>, Typeahead
|
|
|
98
101
|
validateChip: () => true,
|
|
99
102
|
} satisfies Partial<TypeaheadProps<unknown>>;
|
|
100
103
|
|
|
101
|
-
constructor(props:
|
|
104
|
+
constructor(props: TypeaheadPropsWithInputAttributes<T>) {
|
|
102
105
|
super(props);
|
|
103
106
|
const { searchDelay, initialValue, multiple } = this.props;
|
|
104
107
|
this.handleSearchDebounced = debounce(this.handleSearch, searchDelay);
|
|
@@ -115,7 +118,7 @@ export default class Typeahead<T> extends Component<TypeaheadProps<T>, Typeahead
|
|
|
115
118
|
|
|
116
119
|
handleSearchDebounced: DebouncedFunc<Typeahead<T>['handleSearch']>;
|
|
117
120
|
|
|
118
|
-
UNSAFE_componentWillReceiveProps(nextProps:
|
|
121
|
+
UNSAFE_componentWillReceiveProps(nextProps: TypeaheadPropsWithInputAttributes<T>) {
|
|
119
122
|
if (nextProps.multiple !== this.props.multiple) {
|
|
120
123
|
this.setState((previousState) => {
|
|
121
124
|
const { selected } = previousState;
|
|
@@ -374,7 +377,10 @@ export default class Typeahead<T> extends Component<TypeaheadProps<T>, Typeahead
|
|
|
374
377
|
allowNew,
|
|
375
378
|
showNewEntry,
|
|
376
379
|
dropdownOpen,
|
|
377
|
-
}: Pick<
|
|
380
|
+
}: Pick<
|
|
381
|
+
TypeaheadPropsWithInputAttributes<T>,
|
|
382
|
+
'footer' | 'options' | 'id' | 'allowNew' | 'showNewEntry'
|
|
383
|
+
> &
|
|
378
384
|
Pick<TypeaheadState<T>, 'keyboardFocusedOptionIndex' | 'query'> & {
|
|
379
385
|
dropdownOpen: boolean;
|
|
380
386
|
}) => {
|
|
@@ -414,7 +420,8 @@ export default class Typeahead<T> extends Component<TypeaheadProps<T>, Typeahead
|
|
|
414
420
|
|
|
415
421
|
render() {
|
|
416
422
|
const {
|
|
417
|
-
|
|
423
|
+
inputAttributes,
|
|
424
|
+
id: idProp,
|
|
418
425
|
placeholder,
|
|
419
426
|
multiple,
|
|
420
427
|
size,
|
|
@@ -432,6 +439,7 @@ export default class Typeahead<T> extends Component<TypeaheadProps<T>, Typeahead
|
|
|
432
439
|
alert,
|
|
433
440
|
inputAutoComplete,
|
|
434
441
|
} = this.props;
|
|
442
|
+
const id = idProp ?? inputAttributes?.id;
|
|
435
443
|
|
|
436
444
|
const { errorState, query, selected, optionsShown, keyboardFocusedOptionIndex } = this.state;
|
|
437
445
|
|
|
@@ -457,6 +465,8 @@ export default class Typeahead<T> extends Component<TypeaheadProps<T>, Typeahead
|
|
|
457
465
|
const hasInfo = displayAlert && alertType === Sentiment.NEUTRAL;
|
|
458
466
|
return (
|
|
459
467
|
<div
|
|
468
|
+
role="group"
|
|
469
|
+
{...inputAttributes}
|
|
460
470
|
id={id}
|
|
461
471
|
className={classNames('typeahead', `typeahead-${size}`, {
|
|
462
472
|
'typeahead--has-value': selected.length > 0,
|
|
@@ -515,3 +525,7 @@ export default class Typeahead<T> extends Component<TypeaheadProps<T>, Typeahead
|
|
|
515
525
|
);
|
|
516
526
|
}
|
|
517
527
|
}
|
|
528
|
+
|
|
529
|
+
export default withInputAttributes(Typeahead, { nonLabelable: true }) as <T>(
|
|
530
|
+
props: TypeaheadProps<T>,
|
|
531
|
+
) => React.ReactElement;
|