@transferwise/components 46.85.0 → 46.86.1
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/avatarLayout/AvatarLayout.js +4 -3
- package/build/avatarLayout/AvatarLayout.js.map +1 -1
- package/build/avatarLayout/AvatarLayout.mjs +4 -3
- package/build/avatarLayout/AvatarLayout.mjs.map +1 -1
- package/build/avatarView/AvatarView.js +12 -1
- package/build/avatarView/AvatarView.js.map +1 -1
- package/build/avatarView/AvatarView.mjs +12 -1
- package/build/avatarView/AvatarView.mjs.map +1 -1
- package/build/circularButton/CircularButton.js +18 -21
- package/build/circularButton/CircularButton.js.map +1 -1
- package/build/circularButton/CircularButton.mjs +19 -22
- package/build/circularButton/CircularButton.mjs.map +1 -1
- package/build/definitionList/DefinitionList.js.map +1 -1
- package/build/definitionList/DefinitionList.mjs.map +1 -1
- package/build/i18n/de.json +1 -0
- package/build/i18n/de.json.js +1 -0
- package/build/i18n/de.json.js.map +1 -1
- package/build/i18n/de.json.mjs +1 -0
- package/build/i18n/de.json.mjs.map +1 -1
- package/build/i18n/es.json +1 -0
- package/build/i18n/es.json.js +1 -0
- package/build/i18n/es.json.js.map +1 -1
- package/build/i18n/es.json.mjs +1 -0
- package/build/i18n/es.json.mjs.map +1 -1
- package/build/i18n/fr.json +6 -5
- package/build/i18n/fr.json.js +6 -5
- package/build/i18n/fr.json.js.map +1 -1
- package/build/i18n/fr.json.mjs +6 -5
- package/build/i18n/fr.json.mjs.map +1 -1
- package/build/i18n/hu.json +1 -0
- package/build/i18n/hu.json.js +1 -0
- package/build/i18n/hu.json.js.map +1 -1
- package/build/i18n/hu.json.mjs +1 -0
- package/build/i18n/hu.json.mjs.map +1 -1
- package/build/i18n/id.json +1 -0
- package/build/i18n/id.json.js +1 -0
- package/build/i18n/id.json.js.map +1 -1
- package/build/i18n/id.json.mjs +1 -0
- package/build/i18n/id.json.mjs.map +1 -1
- package/build/i18n/it.json +1 -0
- package/build/i18n/it.json.js +1 -0
- package/build/i18n/it.json.js.map +1 -1
- package/build/i18n/it.json.mjs +1 -0
- package/build/i18n/it.json.mjs.map +1 -1
- package/build/i18n/pl.json +1 -0
- package/build/i18n/pl.json.js +1 -0
- package/build/i18n/pl.json.js.map +1 -1
- package/build/i18n/pl.json.mjs +1 -0
- package/build/i18n/pl.json.mjs.map +1 -1
- package/build/i18n/ro.json +1 -0
- package/build/i18n/ro.json.js +1 -0
- package/build/i18n/ro.json.js.map +1 -1
- package/build/i18n/ro.json.mjs +1 -0
- package/build/i18n/ro.json.mjs.map +1 -1
- package/build/i18n/th.json +1 -0
- package/build/i18n/th.json.js +1 -0
- package/build/i18n/th.json.js.map +1 -1
- package/build/i18n/th.json.mjs +1 -0
- package/build/i18n/th.json.mjs.map +1 -1
- package/build/i18n/tr.json +1 -0
- package/build/i18n/tr.json.js +1 -0
- package/build/i18n/tr.json.js.map +1 -1
- package/build/i18n/tr.json.mjs +1 -0
- package/build/i18n/tr.json.mjs.map +1 -1
- package/build/i18n/zh-CN.json +1 -0
- package/build/i18n/zh-CN.json.js +1 -0
- package/build/i18n/zh-CN.json.js.map +1 -1
- package/build/i18n/zh-CN.json.mjs +1 -0
- package/build/i18n/zh-CN.json.mjs.map +1 -1
- package/build/main.css +18 -159
- package/build/moneyInput/MoneyInput.js.map +1 -1
- package/build/moneyInput/MoneyInput.mjs.map +1 -1
- package/build/styles/avatarLayout/AvatarLayout.css +1 -1
- package/build/styles/circularButton/CircularButton.css +17 -158
- package/build/styles/main.css +18 -159
- package/build/types/avatarLayout/AvatarLayout.d.ts.map +1 -1
- package/build/types/avatarView/AvatarView.d.ts.map +1 -1
- package/build/types/circularButton/CircularButton.d.ts +11 -4
- package/build/types/circularButton/CircularButton.d.ts.map +1 -1
- package/build/types/definitionList/DefinitionList.d.ts +1 -2
- package/build/types/definitionList/DefinitionList.d.ts.map +1 -1
- package/build/types/moneyInput/MoneyInput.d.ts +1 -1
- package/build/types/moneyInput/MoneyInput.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/avatar/Avatar.story.tsx +4 -1
- package/src/avatarLayout/AvatarLayout.css +1 -1
- package/src/avatarLayout/AvatarLayout.less +1 -1
- package/src/avatarLayout/AvatarLayout.story.tsx +2 -0
- package/src/avatarLayout/AvatarLayout.tsx +6 -4
- package/src/avatarView/AvatarView.tsx +15 -1
- package/src/avatarWrapper/AvatarWrapper.story.tsx +4 -0
- package/src/badge/Badge.story.tsx +4 -0
- package/src/circularButton/CircularButton.css +17 -158
- package/src/circularButton/CircularButton.less +22 -91
- package/src/circularButton/CircularButton.story.tsx +45 -24
- package/src/circularButton/CircularButton.tsx +38 -25
- package/src/dateInput/DateInput.spec.tsx +45 -26
- package/src/definitionList/DefinitionList.story.tsx +57 -57
- package/src/definitionList/DefinitionList.tsx +1 -1
- package/src/i18n/de.json +1 -0
- package/src/i18n/es.json +1 -0
- package/src/i18n/fr.json +6 -5
- package/src/i18n/hu.json +1 -0
- package/src/i18n/id.json +1 -0
- package/src/i18n/it.json +1 -0
- package/src/i18n/pl.json +1 -0
- package/src/i18n/ro.json +1 -0
- package/src/i18n/th.json +1 -0
- package/src/i18n/tr.json +1 -0
- package/src/i18n/zh-CN.json +1 -0
- package/src/iconButton/IconButton.story.tsx +6 -6
- package/src/main.css +18 -159
- package/src/moneyInput/MoneyInput.spec.tsx +468 -0
- package/src/moneyInput/MoneyInput.tsx +2 -1
- package/src/phoneNumberInput/PhoneNumberInput.spec.tsx +283 -0
- package/src/slidingPanel/SlidingPanel.spec.tsx +69 -0
- package/src/circularButton/_button-label-states.less +0 -34
- package/src/definitionList/DefinitionList.spec.js +0 -91
- package/src/moneyInput/MoneyInput.rtl.spec.tsx +0 -149
- package/src/moneyInput/MoneyInput.spec.js +0 -820
- package/src/phoneNumberInput/PhoneNumberInput.rtl.spec.tsx +0 -32
- package/src/phoneNumberInput/PhoneNumberInput.spec.js +0 -356
- package/src/slidingPanel/SlidingPanel.spec.js +0 -56
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { Field } from '../field/Field';
|
|
2
|
+
import {
|
|
3
|
+
mockMatchMedia,
|
|
4
|
+
mockResizeObserver,
|
|
5
|
+
render,
|
|
6
|
+
screen,
|
|
7
|
+
within,
|
|
8
|
+
userEvent,
|
|
9
|
+
} from '../test-utils';
|
|
10
|
+
|
|
11
|
+
import PhoneNumberInput, { PhoneNumberInputProps } from './PhoneNumberInput';
|
|
12
|
+
|
|
13
|
+
mockMatchMedia();
|
|
14
|
+
mockResizeObserver();
|
|
15
|
+
|
|
16
|
+
describe('PhoneNumberInput', () => {
|
|
17
|
+
afterEach(jest.clearAllMocks);
|
|
18
|
+
|
|
19
|
+
const props = { onChange: jest.fn() };
|
|
20
|
+
|
|
21
|
+
const customRender = (overrides: Partial<PhoneNumberInputProps> = {}, locale?: string) =>
|
|
22
|
+
render(<PhoneNumberInput {...props} {...overrides} />, { locale });
|
|
23
|
+
|
|
24
|
+
const getPrefixEl = () => screen.getByRole('combobox');
|
|
25
|
+
const getInputEl = () => screen.getByRole('textbox');
|
|
26
|
+
|
|
27
|
+
describe('defaults', () => {
|
|
28
|
+
it('should set prefix control to default UK value', () => {
|
|
29
|
+
customRender();
|
|
30
|
+
expect(getPrefixEl()).toHaveTextContent('+44');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should set number control to empty', () => {
|
|
34
|
+
customRender();
|
|
35
|
+
expect(getInputEl()).toHaveValue('');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should not disable the select', () => {
|
|
39
|
+
customRender();
|
|
40
|
+
expect(getPrefixEl()).toBeEnabled();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should not disable the input', () => {
|
|
44
|
+
customRender();
|
|
45
|
+
expect(getInputEl()).toBeEnabled();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should propagate `initialValue`', () => {
|
|
50
|
+
const prefix = '+39';
|
|
51
|
+
const number = '123456789';
|
|
52
|
+
customRender({ initialValue: `${prefix}${number}` });
|
|
53
|
+
expect(getPrefixEl()).toHaveTextContent(prefix);
|
|
54
|
+
expect(getInputEl()).toHaveValue(number);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('id prop', () => {
|
|
58
|
+
it('should not render id by default', () => {
|
|
59
|
+
customRender();
|
|
60
|
+
expect(getPrefixEl()).not.toHaveAttribute('id');
|
|
61
|
+
expect(getInputEl()).not.toHaveAttribute('id');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should respect `id` for the input and ignore the select', () => {
|
|
65
|
+
const id = 'component-id';
|
|
66
|
+
customRender({ id });
|
|
67
|
+
expect(getPrefixEl()).not.toHaveAttribute('id');
|
|
68
|
+
expect(getInputEl()).toHaveAttribute('id', id);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('pasting', () => {
|
|
73
|
+
const initialPrefix = '+48';
|
|
74
|
+
const initialNumber = '987654321';
|
|
75
|
+
|
|
76
|
+
const renderAndPaste = async (value: string) => {
|
|
77
|
+
customRender({ initialValue: `${initialPrefix}${initialNumber}` });
|
|
78
|
+
await userEvent.tab();
|
|
79
|
+
await userEvent.tab();
|
|
80
|
+
await userEvent.paste(value);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
[
|
|
84
|
+
{ number: '+36303932551', countryCode: '+36', localNumber: '303932551' },
|
|
85
|
+
{ number: '+39123456781', countryCode: '+39', localNumber: '123456781' },
|
|
86
|
+
{ number: '+44 7700 900415', countryCode: '+44', localNumber: '7700900415' },
|
|
87
|
+
{ number: '+2975557308515', countryCode: '+297', localNumber: '5557308515' },
|
|
88
|
+
{ number: '+297-555-7217-360', countryCode: '+297', localNumber: '5557217360' },
|
|
89
|
+
{ number: '+213-555-5160-67', countryCode: '+213', localNumber: '555516067' },
|
|
90
|
+
{ number: '+246-387-5553', countryCode: '+246', localNumber: '3875553' },
|
|
91
|
+
{ number: '+852-940-5558--6', countryCode: '+852', localNumber: '94055586' },
|
|
92
|
+
{ number: '+228 253 5558 4', countryCode: '+228', localNumber: '25355584' },
|
|
93
|
+
].forEach(({ number, countryCode, localNumber }) => {
|
|
94
|
+
it(`'${number}' number should update the value properly`, async () => {
|
|
95
|
+
await renderAndPaste(number);
|
|
96
|
+
|
|
97
|
+
expect(getPrefixEl()).toHaveTextContent(countryCode);
|
|
98
|
+
expect(getInputEl()).toHaveValue(localNumber);
|
|
99
|
+
expect(props.onChange).toHaveBeenCalledWith(number.replace(/[\s-]+/g, ''), countryCode);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should not paste invalid characters', async () => {
|
|
104
|
+
await renderAndPaste('+36asdasdasd');
|
|
105
|
+
expect(getPrefixEl()).toHaveTextContent(initialPrefix);
|
|
106
|
+
expect(getInputEl()).toHaveValue(initialNumber);
|
|
107
|
+
expect(props.onChange).not.toHaveBeenCalled();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should not paste countries which are not in the select', async () => {
|
|
111
|
+
await renderAndPaste('+9992342343423');
|
|
112
|
+
expect(getPrefixEl()).toHaveTextContent(initialPrefix);
|
|
113
|
+
expect(getInputEl()).toHaveValue(initialNumber);
|
|
114
|
+
expect(props.onChange).not.toHaveBeenCalled();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("should not paste numbers which doesn't start with the country code", async () => {
|
|
118
|
+
await renderAndPaste('0+36303932551');
|
|
119
|
+
expect(getPrefixEl()).toHaveTextContent(initialPrefix);
|
|
120
|
+
expect(getInputEl()).toHaveValue(initialNumber);
|
|
121
|
+
expect(props.onChange).not.toHaveBeenCalled();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("should allow pasting numbers which don't contain a country code", async () => {
|
|
125
|
+
const newNumber = '06303932551';
|
|
126
|
+
await renderAndPaste(newNumber);
|
|
127
|
+
expect(getPrefixEl()).toHaveTextContent(initialPrefix);
|
|
128
|
+
expect(getInputEl()).toHaveValue(newNumber);
|
|
129
|
+
expect(props.onChange).toHaveBeenCalledWith(`${initialPrefix}${newNumber}`, initialPrefix);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('initialValue', () => {
|
|
134
|
+
describe('when a model is supplied that could match more than one prefix', () => {
|
|
135
|
+
const initialProps = { initialValue: '+1868123456789' };
|
|
136
|
+
|
|
137
|
+
it('should set the select to the longest matching prefix', () => {
|
|
138
|
+
customRender(initialProps);
|
|
139
|
+
expect(getPrefixEl()).toHaveTextContent('+1868');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should set the number input to the rest of the number', () => {
|
|
143
|
+
customRender(initialProps);
|
|
144
|
+
expect(getInputEl()).toHaveValue('123456789');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe('when a model is supplied with no matching prefix', () => {
|
|
149
|
+
const initialProps = { initialValue: '+999123456789' };
|
|
150
|
+
|
|
151
|
+
it('should empty the select', () => {
|
|
152
|
+
customRender(initialProps);
|
|
153
|
+
expect(getPrefixEl()).toHaveTextContent('Select an option...');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should put the whole value in the input without the plus', () => {
|
|
157
|
+
customRender(initialProps);
|
|
158
|
+
expect(getInputEl()).toHaveValue('999123456789');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('when an invalid model is supplied', () => {
|
|
163
|
+
it('should not re-explode model', () => {
|
|
164
|
+
customRender({ initialValue: '+123' });
|
|
165
|
+
expect(getPrefixEl()).toHaveTextContent('+44');
|
|
166
|
+
expect(getInputEl()).toHaveValue('');
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('when disabled is true', () => {
|
|
172
|
+
it('should disable both controls', () => {
|
|
173
|
+
customRender({ disabled: true });
|
|
174
|
+
expect(getPrefixEl()).toBeDisabled();
|
|
175
|
+
expect(getInputEl()).toBeDisabled();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('placeholders', () => {
|
|
180
|
+
it('should use the provided placeholder', () => {
|
|
181
|
+
const placeholder = 'custom placeholder';
|
|
182
|
+
customRender({ placeholder });
|
|
183
|
+
expect(getInputEl()).toHaveAttribute('placeholder', placeholder);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should use the provided searchPlaceholder', async () => {
|
|
187
|
+
const searchPlaceholder = 'search placeholder';
|
|
188
|
+
customRender({ searchPlaceholder });
|
|
189
|
+
await userEvent.click(getPrefixEl());
|
|
190
|
+
expect(screen.getByRole('combobox', { name: searchPlaceholder })).toBeInTheDocument();
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('when supplied with a locale', () => {
|
|
195
|
+
describe('and a value', () => {
|
|
196
|
+
it('should use the prefix of the supplied value', () => {
|
|
197
|
+
customRender({ initialValue: '+12345678' }, 'es');
|
|
198
|
+
expect(getPrefixEl()).toHaveTextContent('+1');
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('and no value', () => {
|
|
203
|
+
describe('and no country code', () => {
|
|
204
|
+
it('should default the prefix to the local country', () => {
|
|
205
|
+
customRender(undefined, 'es');
|
|
206
|
+
expect(getPrefixEl()).toHaveTextContent('+34');
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe('and country code', () => {
|
|
211
|
+
it('should override locale prefix with country specific prefix', () => {
|
|
212
|
+
customRender({ countryCode: 'US' }, 'es');
|
|
213
|
+
expect(getPrefixEl()).toHaveTextContent('+1');
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('user input', () => {
|
|
220
|
+
describe('valid number', () => {
|
|
221
|
+
it('should trigger onChange handler', async () => {
|
|
222
|
+
customRender();
|
|
223
|
+
await userEvent.type(getInputEl(), '123');
|
|
224
|
+
expect(props.onChange).toHaveBeenCalledWith('+44123', '+44');
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe('invalid number', () => {
|
|
229
|
+
it('should trigger onChange with null value', async () => {
|
|
230
|
+
customRender({ initialValue: '+1234' });
|
|
231
|
+
await userEvent.type(getInputEl(), '{Backspace}{Backspace}{Backspace}1');
|
|
232
|
+
expect(props.onChange).toHaveBeenCalledWith(null, '+1');
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe('when user insert invalid character', () => {
|
|
237
|
+
it('should strip them', async () => {
|
|
238
|
+
customRender({ initialValue: '+12345678' });
|
|
239
|
+
await userEvent.type(getInputEl(), '123--');
|
|
240
|
+
expect(props.onChange).toHaveBeenCalledWith('+12345678123', '+1');
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
describe('overlapping prefix and suffix numbers', () => {
|
|
245
|
+
it("shouldn't change the prefix number on matching suffix input", async () => {
|
|
246
|
+
customRender({ countryCode: 'eg' });
|
|
247
|
+
await userEvent.type(getInputEl(), '1111111');
|
|
248
|
+
expect(getInputEl()).toHaveValue('1111111');
|
|
249
|
+
expect(props.onChange).toHaveBeenCalledWith('+201111111', '+20');
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('when selectProps is supplied', () => {
|
|
255
|
+
it('renders Select component with expected props', () => {
|
|
256
|
+
customRender({ selectProps: { className: 'custom-class' } });
|
|
257
|
+
expect(getPrefixEl().parentElement).toHaveClass('custom-class');
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('supports custom `aria-labelledby` attribute', () => {
|
|
262
|
+
render(
|
|
263
|
+
<>
|
|
264
|
+
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
|
|
265
|
+
<label id="prioritized-label">Prioritized label</label>
|
|
266
|
+
<PhoneNumberInput aria-labelledby="prioritized-label" onChange={() => {}} />
|
|
267
|
+
</>,
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
expect(
|
|
271
|
+
within(screen.getByLabelText('Prioritized label')).getByRole('textbox'),
|
|
272
|
+
).toBeInTheDocument();
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('supports `Field` for labeling', () => {
|
|
276
|
+
render(
|
|
277
|
+
<Field label="Phone number">
|
|
278
|
+
<PhoneNumberInput initialValue="+12345678" onChange={() => {}} />
|
|
279
|
+
</Field>,
|
|
280
|
+
);
|
|
281
|
+
expect(screen.getAllByRole('group')[0]).toHaveAccessibleName(/^Phone number/);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { render, screen } from '../test-utils';
|
|
2
|
+
import SlidingPanel, { SlidingPanelProps } from './SlidingPanel';
|
|
3
|
+
|
|
4
|
+
describe('SlidingPanel', () => {
|
|
5
|
+
const initialProps: SlidingPanelProps = {
|
|
6
|
+
open: true,
|
|
7
|
+
children: <div data-testid="content">Content</div>,
|
|
8
|
+
testId: 'wrapper',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const customRender = (overrides: Partial<SlidingPanelProps> = {}) =>
|
|
12
|
+
render(<SlidingPanel {...initialProps} {...overrides} />);
|
|
13
|
+
|
|
14
|
+
describe('open', () => {
|
|
15
|
+
it('should respect open=false', () => {
|
|
16
|
+
customRender({ open: false });
|
|
17
|
+
expect(screen.queryByTestId('wrapper')).not.toBeInTheDocument();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should respect open=true', () => {
|
|
21
|
+
customRender();
|
|
22
|
+
expect(screen.getByTestId('wrapper')).toBeInTheDocument();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('position', () => {
|
|
27
|
+
it(`should default to 'left'`, () => {
|
|
28
|
+
customRender();
|
|
29
|
+
expect(screen.queryByTestId('wrapper')).toHaveClass(`sliding-panel--open-left`);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
(['left', 'right', 'top', 'bottom'] as const).forEach((position) => {
|
|
33
|
+
describe(position, () => {
|
|
34
|
+
it(`should add the classname`, () => {
|
|
35
|
+
customRender({ position });
|
|
36
|
+
expect(screen.queryByTestId('wrapper')).toHaveClass(`sliding-panel--open-${position}`);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it(`should add the classname for 'showSlidingPanelBorder'`, () => {
|
|
40
|
+
customRender({ position, showSlidingPanelBorder: true });
|
|
41
|
+
expect(screen.queryByTestId('wrapper')).toHaveClass(`sliding-panel--border-${position}`);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should render children', () => {
|
|
48
|
+
customRender();
|
|
49
|
+
expect(screen.getByTestId('content')).toBeInTheDocument();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should respect className', () => {
|
|
53
|
+
const className = 'customClass';
|
|
54
|
+
customRender({ className });
|
|
55
|
+
expect(screen.getByTestId('wrapper')).toHaveClass(className);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('slidingPanelPositionFixed', () => {
|
|
59
|
+
it('should not be fixed by default', () => {
|
|
60
|
+
customRender();
|
|
61
|
+
expect(screen.getByTestId('wrapper')).not.toHaveClass('sliding-panel--fixed');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should respect slidingPanelPositionFixed=true', () => {
|
|
65
|
+
customRender({ slidingPanelPositionFixed: true });
|
|
66
|
+
expect(screen.getByTestId('wrapper')).toHaveClass('sliding-panel--fixed');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
.button-label-states(@prefix, @type, @normal, @hover, @active, @isModernTheme: false) {
|
|
2
|
-
.@{prefix}.@{type} {
|
|
3
|
-
.@{prefix}__label {
|
|
4
|
-
color: @normal;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
&:not(.disabled, :disabled):hover .@{prefix}__label {
|
|
8
|
-
color: @hover;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
&:active .@{prefix}__label,
|
|
12
|
-
input[type="button"]:active ~ .@{prefix}__label {
|
|
13
|
-
color: @active;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
&.secondary {
|
|
17
|
-
.tw-icon {
|
|
18
|
-
color: @normal;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
&:not(.disabled, :disabled):hover .tw-icon,
|
|
22
|
-
input[type="button"]:active + .tw-icon {
|
|
23
|
-
color: white;
|
|
24
|
-
& when (@isModernTheme = true) {
|
|
25
|
-
color: var(--color-interactive-control);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
&:active input[type="button"] + .tw-icon when (@isModernTheme = true) {
|
|
30
|
-
color: var(--color-interactive-control);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { shallow, mount } from 'enzyme';
|
|
2
|
-
|
|
3
|
-
import { DefinitionList } from '..';
|
|
4
|
-
import { Layout } from '../common';
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
VERTICAL_TWO_COLUMN,
|
|
8
|
-
VERTICAL_ONE_COLUMN,
|
|
9
|
-
HORIZONTAL_LEFT_ALIGNED,
|
|
10
|
-
HORIZONTAL_RIGHT_ALIGNED,
|
|
11
|
-
HORIZONTAL_JUSTIFIED,
|
|
12
|
-
} = Layout;
|
|
13
|
-
|
|
14
|
-
describe('DefinitionList', () => {
|
|
15
|
-
const someDefinitions = () => [{ title: 'First', value: 'first value', key: 'first' }, null];
|
|
16
|
-
|
|
17
|
-
const withLayout = (layout) =>
|
|
18
|
-
shallow(<DefinitionList layout={layout} definitions={someDefinitions()} />);
|
|
19
|
-
|
|
20
|
-
const listHasClass = (layout, className) =>
|
|
21
|
-
withLayout(layout).find('dl.tw-definition-list').hasClass(className);
|
|
22
|
-
|
|
23
|
-
const valueHasClass = (layout, className) =>
|
|
24
|
-
withLayout(layout).find('dl.tw-definition-list dd').hasClass(className);
|
|
25
|
-
|
|
26
|
-
it('applies correct flex class', () => {
|
|
27
|
-
const colClass = 'flex-column';
|
|
28
|
-
|
|
29
|
-
expect(listHasClass(VERTICAL_ONE_COLUMN, colClass)).toBe(true);
|
|
30
|
-
expect(listHasClass(VERTICAL_TWO_COLUMN, colClass)).toBe(true);
|
|
31
|
-
expect(listHasClass(HORIZONTAL_LEFT_ALIGNED, colClass)).toBe(true);
|
|
32
|
-
expect(listHasClass(HORIZONTAL_RIGHT_ALIGNED, colClass)).toBe(true);
|
|
33
|
-
expect(listHasClass(HORIZONTAL_JUSTIFIED, colClass)).toBe(true);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('applies correct class for vertical two-column layout', () => {
|
|
37
|
-
const colClass = 'tw-definition-list--columns flex-column flex-row--sm';
|
|
38
|
-
|
|
39
|
-
expect(listHasClass(VERTICAL_TWO_COLUMN, colClass)).toBe(true);
|
|
40
|
-
expect(listHasClass(VERTICAL_ONE_COLUMN, colClass)).toBe(false);
|
|
41
|
-
expect(listHasClass(HORIZONTAL_LEFT_ALIGNED, colClass)).toBe(false);
|
|
42
|
-
expect(listHasClass(HORIZONTAL_RIGHT_ALIGNED, colClass)).toBe(false);
|
|
43
|
-
expect(listHasClass(HORIZONTAL_JUSTIFIED, colClass)).toBe(false);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('applies correct class for horiztonal layouts', () => {
|
|
47
|
-
const rowClass = 'tw-definition-list--horizontal flex-column';
|
|
48
|
-
|
|
49
|
-
expect(listHasClass(VERTICAL_TWO_COLUMN, rowClass)).toBe(false);
|
|
50
|
-
expect(listHasClass(VERTICAL_ONE_COLUMN, rowClass)).toBe(false);
|
|
51
|
-
expect(listHasClass(HORIZONTAL_LEFT_ALIGNED, rowClass)).toBe(true);
|
|
52
|
-
expect(listHasClass(HORIZONTAL_RIGHT_ALIGNED, rowClass)).toBe(true);
|
|
53
|
-
expect(listHasClass(HORIZONTAL_JUSTIFIED, rowClass)).toBe(true);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('applies correct class for alignment', () => {
|
|
57
|
-
expect(valueHasClass(VERTICAL_TWO_COLUMN, 'd-flex justify-content-between')).toBe(true);
|
|
58
|
-
expect(valueHasClass(VERTICAL_ONE_COLUMN, 'd-flex justify-content-between')).toBe(true);
|
|
59
|
-
expect(valueHasClass(HORIZONTAL_LEFT_ALIGNED, 'd-flex justify-content-between')).toBe(true);
|
|
60
|
-
expect(valueHasClass(HORIZONTAL_RIGHT_ALIGNED, 'text-sm-right')).toBe(true);
|
|
61
|
-
expect(valueHasClass(HORIZONTAL_JUSTIFIED, 'text-sm-justify')).toBe(true);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('has muted text class on title and value when muted flag is passed', () => {
|
|
65
|
-
const muted = shallow(<DefinitionList muted definitions={someDefinitions()} />);
|
|
66
|
-
const notMuted = shallow(<DefinitionList definitions={someDefinitions()} />);
|
|
67
|
-
|
|
68
|
-
expect(muted.find('dl.tw-definition-list').hasClass('text-muted')).toBe(true);
|
|
69
|
-
expect(notMuted.find('dl.tw-definition-list').hasClass('text-muted')).toBe(false);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('shows action button when one is passed in', () => {
|
|
73
|
-
const buttonSpy = jest.fn();
|
|
74
|
-
|
|
75
|
-
const definitionList = mount(
|
|
76
|
-
<DefinitionList
|
|
77
|
-
definitions={[
|
|
78
|
-
{
|
|
79
|
-
title: 'Name',
|
|
80
|
-
value: 'Jenkins',
|
|
81
|
-
key: '1',
|
|
82
|
-
action: { label: 'A button', onClick: buttonSpy },
|
|
83
|
-
},
|
|
84
|
-
]}
|
|
85
|
-
/>,
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
definitionList.find('button').simulate('click');
|
|
89
|
-
expect(buttonSpy).toHaveBeenCalledTimes(1);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { Field } from '../field/Field';
|
|
2
|
-
import { mockMatchMedia, mockResizeObserver, render, screen, userEvent } from '../test-utils';
|
|
3
|
-
|
|
4
|
-
import MoneyInput from './MoneyInput';
|
|
5
|
-
import messages from './MoneyInput.messages';
|
|
6
|
-
|
|
7
|
-
mockMatchMedia();
|
|
8
|
-
mockResizeObserver();
|
|
9
|
-
|
|
10
|
-
describe('MoneyInput', () => {
|
|
11
|
-
const currencies = [
|
|
12
|
-
{
|
|
13
|
-
header: 'Popular currencies',
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
value: 'EUR',
|
|
17
|
-
label: 'EUR',
|
|
18
|
-
note: 'Euro',
|
|
19
|
-
currency: 'eur',
|
|
20
|
-
searchable: 'Spain, Germany, France, Austria, Estonia',
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
value: 'USD',
|
|
24
|
-
label: 'USD',
|
|
25
|
-
note: 'United States dollar',
|
|
26
|
-
currency: 'usd',
|
|
27
|
-
searchable: 'Hong Kong, Saudi Arabia',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
value: 'GBP',
|
|
31
|
-
label: 'GBP',
|
|
32
|
-
note: 'British pound',
|
|
33
|
-
currency: 'gbp',
|
|
34
|
-
searchable: 'England, Scotland, Wales',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
header: 'Some other currencies',
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
value: 'CAD',
|
|
41
|
-
label: 'CAD',
|
|
42
|
-
note: 'Canadian dollar',
|
|
43
|
-
currency: 'cad',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
value: 'AUD',
|
|
47
|
-
label: 'AUD',
|
|
48
|
-
note: 'Australian dollar',
|
|
49
|
-
currency: 'aud',
|
|
50
|
-
},
|
|
51
|
-
] as const;
|
|
52
|
-
const props = {
|
|
53
|
-
currencies,
|
|
54
|
-
selectedCurrency: currencies[1],
|
|
55
|
-
searchPlaceholder: 'Type a currency / country',
|
|
56
|
-
amount: 1000,
|
|
57
|
-
onAmountChange: jest.fn(),
|
|
58
|
-
onCurrencyChange: jest.fn(),
|
|
59
|
-
};
|
|
60
|
-
it.each([
|
|
61
|
-
['asd', ''],
|
|
62
|
-
['1a2s3d', '123'],
|
|
63
|
-
['±!@#$^*_+?><', ''],
|
|
64
|
-
['1±!@#$^*,_+?><2', '1,2'],
|
|
65
|
-
['12,3', '12,3'],
|
|
66
|
-
['12.3', '12.3'],
|
|
67
|
-
])("ignores the letters when typed '%s' and shows '%s'", async (testValue, expectedValue) => {
|
|
68
|
-
render(<MoneyInput {...props} amount={null} />);
|
|
69
|
-
|
|
70
|
-
const input = screen.getByRole('textbox');
|
|
71
|
-
await userEvent.type(input, testValue);
|
|
72
|
-
expect(input).toHaveValue(expectedValue);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('supports custom `aria-labelledby` attribute', () => {
|
|
76
|
-
render(
|
|
77
|
-
<>
|
|
78
|
-
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
|
|
79
|
-
<label id="prioritized-label">Prioritized label</label>
|
|
80
|
-
<MoneyInput {...props} aria-labelledby="prioritized-label" />
|
|
81
|
-
</>,
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
expect(screen.getAllByLabelText('Prioritized label')[0]).toHaveClass('input-group');
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('supports `Field` for labeling', () => {
|
|
88
|
-
render(
|
|
89
|
-
<Field label="Recipient gets">
|
|
90
|
-
<MoneyInput {...props} />
|
|
91
|
-
</Field>,
|
|
92
|
-
);
|
|
93
|
-
expect(screen.getAllByRole('group')[0]).toHaveAccessibleName(/^Recipient gets/);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
describe('ids', () => {
|
|
97
|
-
it('should guarantee id and connect the input with the selected currency via withId HoC', () => {
|
|
98
|
-
render(<MoneyInput {...props} />);
|
|
99
|
-
const input = screen.getByRole('textbox');
|
|
100
|
-
const button = screen.getByRole('combobox');
|
|
101
|
-
expect(input.getAttribute('id')).toBeTruthy();
|
|
102
|
-
expect(input).toHaveAttribute('aria-describedby', button.getAttribute('id'));
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should have unique id for the select filter with predefined id', async () => {
|
|
106
|
-
const fieldId = 'myFieldId';
|
|
107
|
-
render(
|
|
108
|
-
<Field label="Multiple currencies" id={fieldId}>
|
|
109
|
-
<MoneyInput {...props} />
|
|
110
|
-
</Field>,
|
|
111
|
-
);
|
|
112
|
-
await userEvent.click(screen.getByRole('combobox'));
|
|
113
|
-
expect(screen.getByLabelText(props.searchPlaceholder)).toHaveAttribute(
|
|
114
|
-
'id',
|
|
115
|
-
`${fieldId}SelectedCurrencySearch`,
|
|
116
|
-
);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should have unique id for the select filter without predefined id', async () => {
|
|
120
|
-
render(
|
|
121
|
-
<Field label="Multiple currencies">
|
|
122
|
-
<MoneyInput {...props} />
|
|
123
|
-
</Field>,
|
|
124
|
-
);
|
|
125
|
-
await userEvent.click(screen.getByRole('combobox'));
|
|
126
|
-
expect(screen.getByLabelText(props.searchPlaceholder)).toHaveAttribute(
|
|
127
|
-
'id',
|
|
128
|
-
expect.stringMatching(/^:.*?:SelectedCurrencySearch$/),
|
|
129
|
-
);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('should have AT label for the currency dropdown', () => {
|
|
134
|
-
render(<MoneyInput {...props} />);
|
|
135
|
-
expect(screen.getByRole('combobox')).toHaveAttribute(
|
|
136
|
-
'aria-label',
|
|
137
|
-
messages.selectCurrencyLabel.defaultMessage,
|
|
138
|
-
);
|
|
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
|
-
});
|