@transferwise/components 0.0.0-experimental-99925cd → 0.0.0-experimental-97a0d5e
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/alert/Alert.js +1 -1
- package/build/alert/Alert.mjs +1 -1
- package/build/button/legacyUtils/legacyUtils.js +1 -1
- package/build/button/legacyUtils/legacyUtils.mjs +1 -1
- package/build/drawer/Drawer.js +1 -1
- package/build/drawer/Drawer.mjs +1 -1
- package/build/inputs/SelectInput.js +4 -33
- package/build/inputs/SelectInput.js.map +1 -1
- package/build/inputs/SelectInput.mjs +4 -33
- package/build/inputs/SelectInput.mjs.map +1 -1
- package/build/markdown/Markdown.js +1 -1
- package/build/markdown/Markdown.mjs +1 -1
- package/build/popover/Popover.js +1 -1
- package/build/popover/Popover.mjs +1 -1
- package/build/types/inputs/SelectInput.d.ts.map +1 -1
- package/build/types/test-utils/style-mock.d.ts +1 -0
- package/build/types/test-utils/style-mock.d.ts.map +1 -0
- package/build/types/utilities/deprecatedProperty/deprecatedProperty.d.ts +8 -6
- package/build/types/utilities/deprecatedProperty/deprecatedProperty.d.ts.map +1 -1
- package/build/types/utilities/deprecatedProperty/index.d.ts +1 -1
- package/build/types/utilities/deprecatedProperty/index.d.ts.map +1 -1
- package/build/types/utilities/index.d.ts +2 -2
- package/build/types/utilities/index.d.ts.map +1 -1
- package/build/types/utilities/logActionRequired.d.ts +2 -2
- package/build/types/utilities/logActionRequired.d.ts.map +1 -1
- package/build/utilities/logActionRequired.js.map +1 -1
- package/build/utilities/logActionRequired.mjs.map +1 -1
- package/package.json +3 -3
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.story.tsx +78 -0
- package/src/inputs/SelectInput.spec.tsx +1 -80
- package/src/inputs/SelectInput.story.tsx +2 -2
- package/src/inputs/SelectInput.tsx +2 -39
- package/src/moneyInput/MoneyInput.rtl.spec.tsx +0 -9
- package/src/utilities/deprecatedProperty/{deprecatedProperty.spec.js → deprecatedProperty.spec.ts} +7 -13
- package/src/utilities/deprecatedProperty/{deprecatedProperty.js → deprecatedProperty.ts} +23 -4
- package/src/utilities/{logActionRequired.js → logActionRequired.ts} +2 -2
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.story.js +0 -85
- package/src/inputs/SelectInput.docs.mdx +0 -21
- /package/src/test-utils/{style-mock.js → style-mock.ts} +0 -0
- /package/src/utilities/deprecatedProperty/{index.js → index.ts} +0 -0
- /package/src/utilities/{index.js → index.ts} +0 -0
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from './SelectInput';
|
|
21
21
|
|
|
22
22
|
const meta = {
|
|
23
|
-
title: '
|
|
23
|
+
title: 'components/SelectInput',
|
|
24
24
|
component: SelectInput,
|
|
25
25
|
tags: ['autodocs'],
|
|
26
26
|
parameters: { actions: { argTypesRegex: '' } },
|
|
@@ -347,7 +347,7 @@ export const WithinField = {
|
|
|
347
347
|
args: Months.args,
|
|
348
348
|
decorators: [
|
|
349
349
|
(Story) => (
|
|
350
|
-
<Field message="Something went wrong" sentiment="negative"
|
|
350
|
+
<Field message="Something went wrong" sentiment="negative">
|
|
351
351
|
<Story />
|
|
352
352
|
</Field>
|
|
353
353
|
),
|
|
@@ -258,9 +258,8 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
258
258
|
onClose,
|
|
259
259
|
onClear,
|
|
260
260
|
}: SelectInputProps<T, M>) {
|
|
261
|
-
const inputAttributes = useInputAttributes(
|
|
261
|
+
const inputAttributes = useInputAttributes();
|
|
262
262
|
const id = idProp ?? inputAttributes.id;
|
|
263
|
-
|
|
264
263
|
const [open, setOpen] = useState(false);
|
|
265
264
|
|
|
266
265
|
const initialized = useRef(false);
|
|
@@ -296,35 +295,6 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
296
295
|
const listboxRef = useRef<HTMLDivElement>(null);
|
|
297
296
|
const controllerRef = filterable ? searchInputRef : listboxRef;
|
|
298
297
|
|
|
299
|
-
/**
|
|
300
|
-
* Attempts to resolve the `listbox` label
|
|
301
|
-
* @see https://storybook.wise.design/?path=/docs/forms-selectinput-accessibility--docs#labelling
|
|
302
|
-
*/
|
|
303
|
-
const getListBoxLabelProps = (): {
|
|
304
|
-
listBoxLabel?: string;
|
|
305
|
-
listBoxLabelledBy?: string;
|
|
306
|
-
} => {
|
|
307
|
-
if (UNSAFE_triggerButtonProps?.['aria-label']) {
|
|
308
|
-
return {
|
|
309
|
-
listBoxLabel: UNSAFE_triggerButtonProps['aria-label'],
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (UNSAFE_triggerButtonProps?.['aria-labelledby']) {
|
|
314
|
-
return {
|
|
315
|
-
listBoxLabelledBy: UNSAFE_triggerButtonProps['aria-labelledby'],
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
if (inputAttributes['aria-labelledby']) {
|
|
320
|
-
return {
|
|
321
|
-
listBoxLabelledBy: inputAttributes['aria-labelledby'],
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
return {};
|
|
326
|
-
};
|
|
327
|
-
|
|
328
298
|
return (
|
|
329
299
|
<ListboxBase
|
|
330
300
|
name={name}
|
|
@@ -418,7 +388,7 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
418
388
|
}}
|
|
419
389
|
>
|
|
420
390
|
<SelectInputOptions
|
|
421
|
-
id={
|
|
391
|
+
id={`${id}Search`}
|
|
422
392
|
items={items}
|
|
423
393
|
renderValue={renderValue}
|
|
424
394
|
renderFooter={renderFooter}
|
|
@@ -428,7 +398,6 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
428
398
|
listboxRef={listboxRef}
|
|
429
399
|
filterQuery={deferredFilterQuery}
|
|
430
400
|
onFilterChange={setFilterQuery}
|
|
431
|
-
{...getListBoxLabelProps()}
|
|
432
401
|
/>
|
|
433
402
|
</OptionsOverlay>
|
|
434
403
|
);
|
|
@@ -527,8 +496,6 @@ interface SelectInputOptionsProps<T = string>
|
|
|
527
496
|
listboxRef: React.MutableRefObject<HTMLDivElement | null>;
|
|
528
497
|
filterQuery: string;
|
|
529
498
|
onFilterChange: (query: string) => void;
|
|
530
|
-
listBoxLabel?: string;
|
|
531
|
-
listBoxLabelledBy?: string;
|
|
532
499
|
}
|
|
533
500
|
|
|
534
501
|
function SelectInputOptions<T = string>({
|
|
@@ -542,8 +509,6 @@ function SelectInputOptions<T = string>({
|
|
|
542
509
|
listboxRef,
|
|
543
510
|
filterQuery,
|
|
544
511
|
onFilterChange,
|
|
545
|
-
listBoxLabel,
|
|
546
|
-
listBoxLabelledBy,
|
|
547
512
|
}: SelectInputOptionsProps<T>) {
|
|
548
513
|
const intl = useIntl();
|
|
549
514
|
|
|
@@ -697,8 +662,6 @@ function SelectInputOptions<T = string>({
|
|
|
697
662
|
id={listboxId}
|
|
698
663
|
role="listbox"
|
|
699
664
|
aria-orientation="vertical"
|
|
700
|
-
aria-label={listBoxLabel}
|
|
701
|
-
aria-labelledby={listBoxLabelledBy}
|
|
702
665
|
tabIndex={0}
|
|
703
666
|
className="np-select-input-listbox"
|
|
704
667
|
>
|
|
@@ -137,13 +137,4 @@ describe('MoneyInput', () => {
|
|
|
137
137
|
messages.selectCurrencyLabel.defaultMessage,
|
|
138
138
|
);
|
|
139
139
|
});
|
|
140
|
-
|
|
141
|
-
it('should have a listbox label', async () => {
|
|
142
|
-
render(<MoneyInput {...props} />);
|
|
143
|
-
const trigger = screen.getByRole('combobox');
|
|
144
|
-
await userEvent.click(trigger);
|
|
145
|
-
const triggerLabel = trigger.getAttribute('aria-label');
|
|
146
|
-
expect(triggerLabel).toBeTruthy();
|
|
147
|
-
expect(screen.getByRole('listbox', { name: triggerLabel ?? '' })).toBeInTheDocument();
|
|
148
|
-
});
|
|
149
140
|
});
|
package/src/utilities/deprecatedProperty/{deprecatedProperty.spec.js → deprecatedProperty.spec.ts}
RENAMED
|
@@ -5,6 +5,7 @@ const props = { prop1: 'prop1', prop2: 'prop2' };
|
|
|
5
5
|
const validator = jest.fn();
|
|
6
6
|
|
|
7
7
|
describe('deprecated', () => {
|
|
8
|
+
const component = 'Test';
|
|
8
9
|
const origWarn = console.warn;
|
|
9
10
|
let mockedWarn;
|
|
10
11
|
|
|
@@ -22,11 +23,10 @@ describe('deprecated', () => {
|
|
|
22
23
|
});
|
|
23
24
|
|
|
24
25
|
describe('if prop has been listed as deprecated', () => {
|
|
26
|
+
const propertyName = 'prop1';
|
|
27
|
+
|
|
25
28
|
it('calls console.warn with the correct message', () => {
|
|
26
|
-
const component = 'Test';
|
|
27
29
|
const newProperty = 'newProp';
|
|
28
|
-
const propertyName = 'prop1';
|
|
29
|
-
|
|
30
30
|
const expiryDate = new Date('01-01-2000');
|
|
31
31
|
const expiryDateString = expiryDate.toLocaleString('en-GB', {
|
|
32
32
|
year: 'numeric',
|
|
@@ -42,10 +42,6 @@ describe('deprecated', () => {
|
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
it('calls console.warn with correct message if newProp is not provided', () => {
|
|
45
|
-
const component = 'Test';
|
|
46
|
-
|
|
47
|
-
const propertyName = 'prop1';
|
|
48
|
-
|
|
49
45
|
const expiryDate = new Date('01-01-2000');
|
|
50
46
|
const expiryDateString = expiryDate.toLocaleString('en-GB', {
|
|
51
47
|
year: 'numeric',
|
|
@@ -61,8 +57,6 @@ describe('deprecated', () => {
|
|
|
61
57
|
});
|
|
62
58
|
|
|
63
59
|
it('calls console.warn with correct message if message is provided', () => {
|
|
64
|
-
const component = 'Test';
|
|
65
|
-
const propertyName = 'prop1';
|
|
66
60
|
const message = 'Some custom message.';
|
|
67
61
|
|
|
68
62
|
deprecated(validator, { component, message })(props, propertyName);
|
|
@@ -74,15 +68,15 @@ describe('deprecated', () => {
|
|
|
74
68
|
});
|
|
75
69
|
|
|
76
70
|
describe('if prop has not been listed as deprecated', () => {
|
|
71
|
+
const propertyName = 'prop3';
|
|
72
|
+
|
|
77
73
|
it('calls validator with props', () => {
|
|
78
|
-
|
|
79
|
-
deprecated(validator, {})(props, propertyName);
|
|
74
|
+
deprecated(validator, { component })(props, propertyName);
|
|
80
75
|
expect(console.warn).not.toHaveBeenCalled();
|
|
81
76
|
});
|
|
82
77
|
|
|
83
78
|
it('calls validator with expected args', () => {
|
|
84
|
-
|
|
85
|
-
deprecated(validator, {})(props, propertyName);
|
|
79
|
+
deprecated(validator, { component })(props, propertyName);
|
|
86
80
|
expect(validator).toHaveBeenCalledWith({ prop1: 'prop1', prop2: 'prop2' }, 'prop3');
|
|
87
81
|
});
|
|
88
82
|
});
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import { logActionRequired } from '../logActionRequired';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
type DeprecatedMessageType = {
|
|
4
|
+
component: string;
|
|
5
|
+
propName: string;
|
|
6
|
+
message: string;
|
|
7
|
+
expiryDate?: Date;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type ValidatorType = (props: Record<string, any>, propName: string, ...rest: any[]) => Error | null;
|
|
11
|
+
|
|
12
|
+
type DeprecatedOptionsType = {
|
|
13
|
+
component: string;
|
|
14
|
+
message?: string;
|
|
15
|
+
newProp?: string;
|
|
16
|
+
expiryDate?: Date;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const deprecatedMessage = ({ component, propName, message, expiryDate }: DeprecatedMessageType) => {
|
|
4
20
|
const messages = [`${component} has deprecated the use of ${propName}.`];
|
|
5
21
|
|
|
6
22
|
if (message) {
|
|
@@ -21,11 +37,14 @@ const deprecatedMessage = ({ component, propName, message, expiryDate }) => {
|
|
|
21
37
|
};
|
|
22
38
|
|
|
23
39
|
const deprecated =
|
|
24
|
-
(
|
|
25
|
-
|
|
40
|
+
(
|
|
41
|
+
validator: ValidatorType,
|
|
42
|
+
{ component, message = '', newProp: newProperty, expiryDate }: DeprecatedOptionsType,
|
|
43
|
+
) =>
|
|
44
|
+
(props: Record<string, any>, propertyName: string, ...rest: any[]) => {
|
|
26
45
|
const newPropertyMessage = newProperty ? `Please use ${newProperty} instead.` : message;
|
|
27
46
|
|
|
28
|
-
if (props[propertyName]
|
|
47
|
+
if (props[propertyName] !== null && props[propertyName] !== undefined) {
|
|
29
48
|
logActionRequired(
|
|
30
49
|
deprecatedMessage({
|
|
31
50
|
component,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export function logActionRequired(message) {
|
|
1
|
+
export function logActionRequired(message: string) {
|
|
2
2
|
if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
|
|
3
3
|
// eslint-disable-next-line no-console
|
|
4
4
|
console.warn(message);
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export function logActionRequiredIf(message, conditional) {
|
|
8
|
+
export function logActionRequiredIf(message: string, conditional: boolean) {
|
|
9
9
|
if (conditional) {
|
|
10
10
|
logActionRequired(message);
|
|
11
11
|
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { text } from '@storybook/addon-knobs';
|
|
2
|
-
import { userEvent, within } from '@storybook/test';
|
|
3
|
-
|
|
4
|
-
import InputWithDisplayFormat from '.';
|
|
5
|
-
import { Field } from '../field/Field';
|
|
6
|
-
|
|
7
|
-
export default {
|
|
8
|
-
component: InputWithDisplayFormat,
|
|
9
|
-
title: 'Forms/InputWithDisplayFormat',
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const Basic = () => {
|
|
13
|
-
const placeholder = text('Placeholder', '**-**-**');
|
|
14
|
-
const displayPattern = text('DisplayPattern', '**-**-**');
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<Field label="Display pattern is controlled via knobs" id="Basic">
|
|
18
|
-
<InputWithDisplayFormat
|
|
19
|
-
id="Basic"
|
|
20
|
-
placeholder={placeholder}
|
|
21
|
-
displayPattern={displayPattern}
|
|
22
|
-
className="form-control"
|
|
23
|
-
onChange={(v) => console.log(v)}
|
|
24
|
-
onBlur={(v) => console.log(v)}
|
|
25
|
-
onFocus={(v) => console.log(v)}
|
|
26
|
-
/>
|
|
27
|
-
</Field>
|
|
28
|
-
);
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const Template = (args) => {
|
|
32
|
-
const placeholder = args.pattern;
|
|
33
|
-
const displayPattern = args.pattern;
|
|
34
|
-
const label = args.label;
|
|
35
|
-
const id = label.replaceAll(' ', '-').toLowerCase();
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<Field label={label} id={id}>
|
|
39
|
-
<InputWithDisplayFormat
|
|
40
|
-
id={id}
|
|
41
|
-
placeholder={placeholder}
|
|
42
|
-
displayPattern={displayPattern}
|
|
43
|
-
className="form-control"
|
|
44
|
-
onChange={(v) => console.log(v)}
|
|
45
|
-
onBlur={(v) => console.log(v)}
|
|
46
|
-
onFocus={(v) => console.log(v)}
|
|
47
|
-
/>
|
|
48
|
-
</Field>
|
|
49
|
-
);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export const CardNumber = Template.bind({});
|
|
53
|
-
CardNumber.args = {
|
|
54
|
-
label: 'Card Number',
|
|
55
|
-
pattern: '**** **** **** **** ***',
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export const CVC = Template.bind({});
|
|
59
|
-
CVC.args = {
|
|
60
|
-
label: 'CVC',
|
|
61
|
-
pattern: '***',
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export const ExpiryDate = Template.bind({});
|
|
65
|
-
ExpiryDate.args = {
|
|
66
|
-
label: 'Expiry Date',
|
|
67
|
-
pattern: '** / **',
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export const SecurityPin = Template.bind({});
|
|
71
|
-
SecurityPin.args = {
|
|
72
|
-
label: 'Security Pin',
|
|
73
|
-
pattern: '*-*-*-*-*-*',
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
export const SecurityPinPlay = Template.bind({});
|
|
77
|
-
SecurityPinPlay.args = {
|
|
78
|
-
label: 'Security Pin',
|
|
79
|
-
pattern: '*-*-*-*-*-*',
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
SecurityPinPlay.play = async ({ canvasElement }) => {
|
|
83
|
-
const canvas = within(canvasElement);
|
|
84
|
-
await userEvent.type(canvas.getByPlaceholderText('*-*-*-*-*-*'), '123456');
|
|
85
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Meta } from '@storybook/blocks';
|
|
2
|
-
import { MoneyInput } from '..';
|
|
3
|
-
|
|
4
|
-
<Meta title="Forms/SelectInput/Accessibility" />
|
|
5
|
-
|
|
6
|
-
# Accessibility
|
|
7
|
-
|
|
8
|
-
## Labelling
|
|
9
|
-
|
|
10
|
-
In order for the `<SelectInput />` to be considered accessible, it must be provided with a matching label, preferably via the <a href="/?path=/docs/field--docs">Field</a> component.
|
|
11
|
-
|
|
12
|
-
Additionally, the `listbox` container that holds all the options is also expected to have its own label, which the component will attempt to resolve by looking in the following places:
|
|
13
|
-
|
|
14
|
-
1. `UNSAFE_triggerButtonProps['aria-label']` prop, which reuses the custom label provided for the trigger button.
|
|
15
|
-
<br /> A good example of this strategy is the `<MoneyInput />` component, internally setting
|
|
16
|
-
`"Select currency"` as a trigger button label, which then gets automatically applied to the
|
|
17
|
-
`listbox`.
|
|
18
|
-
2. `UNSAFE_triggerButtonProps['aria-labelledby']` prop, which holds the id of the element labelling the trigger button.
|
|
19
|
-
3. Correctly paired input `<label />` text, ideally via the `Field` component.
|
|
20
|
-
|
|
21
|
-
> Using option group heading is possible but complicated as we can have multiple groups, and those are not necessarily rendered consistently if search or virtualisation are enabled.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|