@transferwise/components 46.91.0 → 46.93.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/actionOption/ActionOption.js +11 -5
- package/build/actionOption/ActionOption.js.map +1 -1
- package/build/actionOption/ActionOption.mjs +11 -5
- package/build/actionOption/ActionOption.mjs.map +1 -1
- package/build/common/Option/Option.js +9 -4
- package/build/common/Option/Option.js.map +1 -1
- package/build/common/Option/Option.mjs +10 -5
- package/build/common/Option/Option.mjs.map +1 -1
- package/build/dateLookup/dateTrigger/DateTrigger.js +11 -6
- package/build/dateLookup/dateTrigger/DateTrigger.js.map +1 -1
- package/build/dateLookup/dateTrigger/DateTrigger.mjs +11 -6
- package/build/dateLookup/dateTrigger/DateTrigger.mjs.map +1 -1
- package/build/definitionList/DefinitionList.js +5 -3
- package/build/definitionList/DefinitionList.js.map +1 -1
- package/build/definitionList/DefinitionList.mjs +5 -3
- package/build/definitionList/DefinitionList.mjs.map +1 -1
- package/build/main.css +23 -32
- package/build/snackbar/Snackbar.js +4 -2
- package/build/snackbar/Snackbar.js.map +1 -1
- package/build/snackbar/Snackbar.mjs +4 -2
- package/build/snackbar/Snackbar.mjs.map +1 -1
- package/build/styles/common/Option/Option.css +22 -0
- package/build/styles/dateLookup/dateTrigger/DateTrigger.css +1 -32
- package/build/styles/main.css +23 -32
- package/build/types/actionOption/ActionOption.d.ts +4 -3
- package/build/types/actionOption/ActionOption.d.ts.map +1 -1
- package/build/types/common/Option/Option.d.ts +1 -0
- package/build/types/common/Option/Option.d.ts.map +1 -1
- package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts.map +1 -1
- package/build/types/definitionList/DefinitionList.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/actionOption/ActionOption.spec.tsx +20 -0
- package/src/actionOption/ActionOption.story.tsx +2 -0
- package/src/actionOption/ActionOption.tsx +15 -5
- package/src/button/Button.story.tsx +8 -0
- package/src/common/Option/Option.css +22 -0
- package/src/common/Option/Option.less +21 -0
- package/src/common/Option/Option.spec.tsx +21 -0
- package/src/common/Option/Option.tsx +7 -1
- package/src/dateLookup/DateLookup.spec.tsx +445 -0
- package/src/dateLookup/dateTrigger/DateTrigger.css +1 -32
- package/src/dateLookup/dateTrigger/DateTrigger.less +4 -28
- package/src/dateLookup/dateTrigger/DateTrigger.tsx +9 -5
- package/src/definitionList/DefinitionList.tsx +3 -3
- package/src/inputs/InputGroup.story.tsx +5 -3
- package/src/listItem/ListItem.spec.tsx +10 -2
- package/src/listItem/ListItem.story.tsx +11 -3
- package/src/main.css +23 -32
- package/src/snackbar/Snackbar.tsx +3 -3
- package/src/withNextPortal/withNextPortal.spec.tsx +24 -0
- package/src/common/Option/Option.spec.js +0 -129
- package/src/dateLookup/DateLookup.proptypes.spec.js +0 -28
- package/src/dateLookup/DateLookup.rtl.spec.tsx +0 -199
- package/src/dateLookup/DateLookup.state.spec.js +0 -76
- package/src/dateLookup/DateLookup.testingLibrary.spec.js +0 -256
- package/src/dateLookup/DateLookup.view.spec.js +0 -151
- package/src/dateLookup/dateHeader/DateHeader.spec.js +0 -95
- package/src/dateLookup/dateTrigger/DateTrigger.spec.js +0 -123
- package/src/dateLookup/dayCalendar/DayCalendar.spec.js +0 -122
- package/src/dateLookup/dayCalendar/table/DayCalendarTable.spec.js +0 -147
- package/src/dateLookup/monthCalendar/MonthCalendar.spec.js +0 -105
- package/src/dateLookup/monthCalendar/table/MonthCalendarTable.spec.js +0 -120
- package/src/dateLookup/tableLink/TableLink.spec.js +0 -109
- package/src/dateLookup/yearCalendar/YearCalendar.spec.js +0 -88
- package/src/dateLookup/yearCalendar/table/YearCalendarTable.spec.js +0 -121
- package/src/modal/Modal.spec.js +0 -197
- package/src/withNextPortal/withNextPortal.spec.js +0 -22
- /package/src/modal/{Modal.rtl.spec.tsx → Modal.spec.tsx} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable react/display-name */
|
|
1
2
|
import { clsx } from 'clsx';
|
|
2
3
|
import { MouseEvent, forwardRef, HTMLAttributes, ReactNode, ElementType } from 'react';
|
|
3
4
|
|
|
@@ -24,6 +25,7 @@ export interface BaseOptionProps extends Omit<HTMLAttributes<HTMLElement>, 'titl
|
|
|
24
25
|
showMediaAtAllSizes?: boolean;
|
|
25
26
|
showMediaCircle?: boolean;
|
|
26
27
|
isContainerAligned?: boolean;
|
|
28
|
+
additionalContent?: ReactNode;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
interface AnchorOptionProps extends BaseOptionProps, LinkProps {
|
|
@@ -50,6 +52,7 @@ const Option = forwardRef<ReferenceType, OptionProps>(
|
|
|
50
52
|
showMediaAtAllSizes,
|
|
51
53
|
showMediaCircle = true,
|
|
52
54
|
isContainerAligned = false,
|
|
55
|
+
additionalContent,
|
|
53
56
|
...rest
|
|
54
57
|
},
|
|
55
58
|
reference,
|
|
@@ -69,7 +72,7 @@ const Option = forwardRef<ReferenceType, OptionProps>(
|
|
|
69
72
|
})}
|
|
70
73
|
disabled={disabled && Element === 'button'}
|
|
71
74
|
>
|
|
72
|
-
<div className=
|
|
75
|
+
<div className={clsx('media')}>
|
|
73
76
|
{media && (
|
|
74
77
|
<div className="media-left">
|
|
75
78
|
{showMediaCircle ? (
|
|
@@ -101,6 +104,9 @@ const Option = forwardRef<ReferenceType, OptionProps>(
|
|
|
101
104
|
</div>
|
|
102
105
|
<div className="media-right">{button}</div>
|
|
103
106
|
</div>
|
|
107
|
+
{additionalContent ? (
|
|
108
|
+
<div className="np-option-additional-content"> {additionalContent} </div>
|
|
109
|
+
) : null}
|
|
104
110
|
</Element>
|
|
105
111
|
);
|
|
106
112
|
},
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import { Field } from '../field/Field';
|
|
2
|
+
import {
|
|
3
|
+
mockMatchMedia,
|
|
4
|
+
mockResizeObserver,
|
|
5
|
+
render,
|
|
6
|
+
RenderResult,
|
|
7
|
+
screen,
|
|
8
|
+
userEvent,
|
|
9
|
+
} from '../test-utils';
|
|
10
|
+
import DateLookup, { DateLookupProps } from './DateLookup';
|
|
11
|
+
|
|
12
|
+
import { act } from 'react';
|
|
13
|
+
|
|
14
|
+
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTimeAsync });
|
|
15
|
+
|
|
16
|
+
mockMatchMedia();
|
|
17
|
+
mockResizeObserver();
|
|
18
|
+
|
|
19
|
+
const initialValue = new Date(2000, 0, 1);
|
|
20
|
+
const labelProp = 'label';
|
|
21
|
+
|
|
22
|
+
describe('DateLookup', () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
jest.useFakeTimers();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(async () => {
|
|
28
|
+
await jest.runOnlyPendingTimersAsync();
|
|
29
|
+
jest.useRealTimers();
|
|
30
|
+
jest.clearAllMocks();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('supports `Field` for labeling', () => {
|
|
34
|
+
render(
|
|
35
|
+
<Field label="Date of birth">
|
|
36
|
+
<DateLookup value={initialValue} onChange={() => {}} />
|
|
37
|
+
</Field>,
|
|
38
|
+
);
|
|
39
|
+
const button = screen.getByRole('button', { name: /^Date of birth/ });
|
|
40
|
+
|
|
41
|
+
expect(button).toBeInTheDocument();
|
|
42
|
+
expect(button).toHaveAttribute('aria-haspopup');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it.each([' ', '{Enter}', '{ArrowDown}', '{ArrowUp}', '{ArrowRight}', '{ArrowLeft}'])(
|
|
46
|
+
"opens with '%s' and closes with '{Escape}'",
|
|
47
|
+
async (text) => {
|
|
48
|
+
render(<DateLookup value={initialValue} onChange={() => {}} />);
|
|
49
|
+
|
|
50
|
+
await user.tab();
|
|
51
|
+
await user.keyboard(text);
|
|
52
|
+
|
|
53
|
+
expect(screen.getByRole('button', { name: /next/iu })).toBeInTheDocument();
|
|
54
|
+
|
|
55
|
+
await user.keyboard('{Escape}');
|
|
56
|
+
await act(async () => {
|
|
57
|
+
await jest.runOnlyPendingTimersAsync();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(screen.queryByRole('button', { name: /next/iu })).not.toBeInTheDocument();
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const setupAndOpenWithMouse = async (props: Partial<DateLookupProps> = {}) => {
|
|
65
|
+
const view = render(<DateLookup value={initialValue} onChange={() => {}} {...props} />);
|
|
66
|
+
|
|
67
|
+
await user.click(screen.getByRole('button'));
|
|
68
|
+
await act(async () => {
|
|
69
|
+
await jest.runOnlyPendingTimersAsync();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return view;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
it('opens and closes with mouse', async () => {
|
|
76
|
+
await setupAndOpenWithMouse();
|
|
77
|
+
|
|
78
|
+
expect(screen.getByRole('button', { name: /next/iu })).toBeInTheDocument();
|
|
79
|
+
|
|
80
|
+
const dimmerElement = screen.getByRole('dialog').parentElement?.parentElement;
|
|
81
|
+
if (dimmerElement != null) {
|
|
82
|
+
await user.click(dimmerElement);
|
|
83
|
+
}
|
|
84
|
+
await act(async () => {
|
|
85
|
+
await jest.runOnlyPendingTimersAsync();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(screen.queryByRole('button', { name: /next/iu })).not.toBeInTheDocument();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('in day view', () => {
|
|
92
|
+
it.each([
|
|
93
|
+
['{ArrowLeft}', -1],
|
|
94
|
+
['{ArrowRight}', +1],
|
|
95
|
+
['{ArrowUp}', -7],
|
|
96
|
+
['{ArrowDown}', +7],
|
|
97
|
+
])("handles '%s' to step %d day(s)", async (text, step) => {
|
|
98
|
+
const handleChange = jest.fn();
|
|
99
|
+
await setupAndOpenWithMouse({ onChange: handleChange });
|
|
100
|
+
|
|
101
|
+
expect(handleChange).not.toHaveBeenCalled();
|
|
102
|
+
|
|
103
|
+
await user.keyboard(text);
|
|
104
|
+
|
|
105
|
+
const value = new Date(initialValue);
|
|
106
|
+
value.setDate(initialValue.getDate() + step);
|
|
107
|
+
expect(handleChange).toHaveBeenCalledWith(value);
|
|
108
|
+
|
|
109
|
+
await user.keyboard('{Escape}');
|
|
110
|
+
await act(async () => {
|
|
111
|
+
await jest.runOnlyPendingTimersAsync();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
expect(handleChange).toHaveBeenCalledWith(initialValue);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('in year view', () => {
|
|
119
|
+
it.each([
|
|
120
|
+
['{ArrowLeft}', -1],
|
|
121
|
+
['{ArrowRight}', +1],
|
|
122
|
+
['{ArrowUp}', -4],
|
|
123
|
+
['{ArrowDown}', +4],
|
|
124
|
+
])("handles '%s' to step %d year(s)", async (text, step) => {
|
|
125
|
+
const handleChange = jest.fn();
|
|
126
|
+
await setupAndOpenWithMouse({ onChange: handleChange });
|
|
127
|
+
|
|
128
|
+
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
129
|
+
await act(async () => {
|
|
130
|
+
await jest.runOnlyPendingTimersAsync();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
expect(handleChange).not.toHaveBeenCalled();
|
|
134
|
+
|
|
135
|
+
await user.keyboard(text);
|
|
136
|
+
|
|
137
|
+
const value = new Date(initialValue);
|
|
138
|
+
value.setFullYear(initialValue.getFullYear() + step);
|
|
139
|
+
expect(handleChange).toHaveBeenCalledWith(value);
|
|
140
|
+
|
|
141
|
+
await user.keyboard('{Escape}');
|
|
142
|
+
await act(async () => {
|
|
143
|
+
await jest.runOnlyPendingTimersAsync();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(handleChange).toHaveBeenCalledWith(initialValue);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('in month view', () => {
|
|
151
|
+
it.each([
|
|
152
|
+
['{ArrowLeft}', -1],
|
|
153
|
+
['{ArrowRight}', +1],
|
|
154
|
+
['{ArrowUp}', -4],
|
|
155
|
+
['{ArrowDown}', +4],
|
|
156
|
+
])("handles '%s' to step %d month(s)", async (text, step) => {
|
|
157
|
+
const handleChange = jest.fn();
|
|
158
|
+
await setupAndOpenWithMouse({ onChange: handleChange });
|
|
159
|
+
|
|
160
|
+
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
161
|
+
await act(async () => {
|
|
162
|
+
await jest.runOnlyPendingTimersAsync();
|
|
163
|
+
});
|
|
164
|
+
await user.keyboard(' ');
|
|
165
|
+
await act(async () => {
|
|
166
|
+
await jest.runOnlyPendingTimersAsync();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(handleChange).not.toHaveBeenCalled();
|
|
170
|
+
|
|
171
|
+
await user.keyboard(text);
|
|
172
|
+
|
|
173
|
+
const value = new Date(initialValue);
|
|
174
|
+
value.setMonth(initialValue.getMonth() + step);
|
|
175
|
+
expect(handleChange).toHaveBeenCalledWith(value);
|
|
176
|
+
|
|
177
|
+
await user.keyboard('{Escape}');
|
|
178
|
+
await act(async () => {
|
|
179
|
+
await jest.runOnlyPendingTimersAsync();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
expect(handleChange).toHaveBeenCalledWith(initialValue);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('limits min value', async () => {
|
|
187
|
+
const min = new Date(initialValue);
|
|
188
|
+
min.setDate(min.getDate() - 1);
|
|
189
|
+
const handleChange = jest.fn();
|
|
190
|
+
await setupAndOpenWithMouse({ min, onChange: handleChange });
|
|
191
|
+
|
|
192
|
+
await user.keyboard('{ArrowLeft}{ArrowLeft}');
|
|
193
|
+
|
|
194
|
+
expect(handleChange).toHaveBeenCalledWith(min);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('limits max value', async () => {
|
|
198
|
+
const max = new Date(initialValue);
|
|
199
|
+
max.setDate(max.getDate() + 1);
|
|
200
|
+
const handleChange = jest.fn();
|
|
201
|
+
await setupAndOpenWithMouse({ max, onChange: handleChange });
|
|
202
|
+
|
|
203
|
+
await user.keyboard('{ArrowRight}{ArrowRight}');
|
|
204
|
+
|
|
205
|
+
expect(handleChange).toHaveBeenCalledWith(max);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe('DateLookup propTypes', () => {
|
|
210
|
+
describe('when the value prop is set to null', () => {
|
|
211
|
+
it('renders without prop type warnings in the console', () => {
|
|
212
|
+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
213
|
+
|
|
214
|
+
render(<DateLookup value={null} onChange={jest.fn()} />);
|
|
215
|
+
|
|
216
|
+
// eslint-disable-next-line no-console
|
|
217
|
+
expect(console.error).not.toHaveBeenCalledWith(
|
|
218
|
+
expect.stringContaining('Warning: Failed %s type'),
|
|
219
|
+
'prop',
|
|
220
|
+
'The prop `value` is marked as required in `DateLookup`, but its value is `null`.',
|
|
221
|
+
expect.anything(),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
consoleSpy.mockRestore();
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe('DateLookup (events)', () => {
|
|
230
|
+
const date = new Date(2018, 11, 27);
|
|
231
|
+
const min = new Date(2018, 11, 26);
|
|
232
|
+
const max = new Date(2018, 11, 28);
|
|
233
|
+
|
|
234
|
+
describe('when not clearable', () => {
|
|
235
|
+
let handleChange: jest.Mock;
|
|
236
|
+
|
|
237
|
+
const setup = async (overrides = {}) => {
|
|
238
|
+
handleChange = jest.fn();
|
|
239
|
+
|
|
240
|
+
return render(
|
|
241
|
+
<>
|
|
242
|
+
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
|
|
243
|
+
<label id="prioritized-label">Prioritized label</label>
|
|
244
|
+
<DateLookup
|
|
245
|
+
value={date}
|
|
246
|
+
min={min}
|
|
247
|
+
max={max}
|
|
248
|
+
size="lg"
|
|
249
|
+
placeholder="Select date"
|
|
250
|
+
label={labelProp}
|
|
251
|
+
aria-labelledby="prioritized-label"
|
|
252
|
+
disabled={false}
|
|
253
|
+
clearable={false}
|
|
254
|
+
onChange={handleChange}
|
|
255
|
+
{...overrides}
|
|
256
|
+
/>
|
|
257
|
+
</>,
|
|
258
|
+
);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
it('switches to years', async () => {
|
|
262
|
+
const view = await setup();
|
|
263
|
+
await openDateLookup();
|
|
264
|
+
await clickDateButton();
|
|
265
|
+
|
|
266
|
+
expect(getActiveYearButton(view)).toHaveFocus();
|
|
267
|
+
|
|
268
|
+
await closeDateLookup(view);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('has aria-label for 20 years', async () => {
|
|
272
|
+
const view = await setup();
|
|
273
|
+
await openDateLookup();
|
|
274
|
+
await clickDateButton();
|
|
275
|
+
|
|
276
|
+
expect(getButtonByAriaLabel('next 20 years')).toBeInTheDocument();
|
|
277
|
+
expect(getButtonByAriaLabel('previous 20 years')).toBeInTheDocument();
|
|
278
|
+
|
|
279
|
+
await closeDateLookup(view);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('switches to months', async () => {
|
|
283
|
+
const view = await setup();
|
|
284
|
+
await openDateLookup();
|
|
285
|
+
await clickDateButton();
|
|
286
|
+
await userEvent.click(getActiveYearButton(view));
|
|
287
|
+
|
|
288
|
+
expect(getActiveMonthButton(view)).toHaveFocus();
|
|
289
|
+
|
|
290
|
+
await closeDateLookup(view);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('has aria label for year', async () => {
|
|
294
|
+
const view = await setup();
|
|
295
|
+
await openDateLookup();
|
|
296
|
+
await clickDateButton();
|
|
297
|
+
await userEvent.click(getActiveYearButton(view));
|
|
298
|
+
|
|
299
|
+
expect(getButtonByAriaLabel('next year')).toBeInTheDocument();
|
|
300
|
+
expect(getButtonByAriaLabel('previous year')).toBeInTheDocument();
|
|
301
|
+
|
|
302
|
+
await closeDateLookup(view);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('switches to days', async () => {
|
|
306
|
+
const view = await setup();
|
|
307
|
+
await openDateLookup();
|
|
308
|
+
await clickDateButton();
|
|
309
|
+
await userEvent.click(getActiveYearButton(view));
|
|
310
|
+
await userEvent.click(getActiveMonthButton(view));
|
|
311
|
+
|
|
312
|
+
expect(getActiveDayButton(view)).toHaveFocus();
|
|
313
|
+
|
|
314
|
+
await closeDateLookup(view);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('has aria label for month', async () => {
|
|
318
|
+
const view = await setup();
|
|
319
|
+
await openDateLookup();
|
|
320
|
+
await clickDateButton();
|
|
321
|
+
await userEvent.click(getActiveYearButton(view));
|
|
322
|
+
await userEvent.click(getActiveMonthButton(view));
|
|
323
|
+
|
|
324
|
+
expect(getButtonByAriaLabel('next month')).toBeInTheDocument();
|
|
325
|
+
expect(getButtonByAriaLabel('previous month')).toBeInTheDocument();
|
|
326
|
+
|
|
327
|
+
await closeDateLookup(view);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('has aria label on selected date', async () => {
|
|
331
|
+
const view = await setup();
|
|
332
|
+
await openDateLookup();
|
|
333
|
+
const d = new Date(2018, 11, 28);
|
|
334
|
+
const newDay = screen.getByText(d.getDate().toString());
|
|
335
|
+
await userEvent.click(newDay);
|
|
336
|
+
await openDateLookup();
|
|
337
|
+
expect(screen.getByRole('button', { name: /selected day/i })).toBeInTheDocument();
|
|
338
|
+
|
|
339
|
+
await closeDateLookup(view);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('supports custom `aria-labelledby` attribute', async () => {
|
|
343
|
+
const view = await setup();
|
|
344
|
+
expect(screen.getByRole('button', { name: /^Prioritized label/ })).toBeInTheDocument();
|
|
345
|
+
await closeDateLookup(view);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('reads our the HTML label as well as the input prefix and the value', async () => {
|
|
349
|
+
const view = await setup();
|
|
350
|
+
expect(
|
|
351
|
+
screen.getByRole('button', { name: 'Prioritized label label 27 December 2018' }),
|
|
352
|
+
).toBeInTheDocument();
|
|
353
|
+
await closeDateLookup(view);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('reads our the HTML label as well as the input prefix and the placeholder', async () => {
|
|
357
|
+
const view = await setup({ value: undefined });
|
|
358
|
+
expect(
|
|
359
|
+
screen.getByRole('button', { name: 'Prioritized label label Select date' }),
|
|
360
|
+
).toBeInTheDocument();
|
|
361
|
+
await closeDateLookup(view);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
describe('when is clearable', () => {
|
|
366
|
+
const props: DateLookupProps = {
|
|
367
|
+
value: date,
|
|
368
|
+
onChange: jest.fn(),
|
|
369
|
+
clearable: true,
|
|
370
|
+
label: labelProp,
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
it(`doesn't show clear button if disable is true`, async () => {
|
|
374
|
+
const view = render(<DateLookup {...props} />);
|
|
375
|
+
expect(getClearButton(view)).toBeInTheDocument();
|
|
376
|
+
|
|
377
|
+
view.rerender(<DateLookup {...props} disabled />);
|
|
378
|
+
|
|
379
|
+
expect(getClearButton(view)).not.toBeInTheDocument();
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('when user clicks on clear the focus returns to btn', async () => {
|
|
383
|
+
const view = render(<DateLookup {...props} />);
|
|
384
|
+
await clickClearButton(view);
|
|
385
|
+
expect(getOpenButton(labelProp)).toHaveFocus();
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('onChange gets called with null when reset button is clicked', async () => {
|
|
389
|
+
const view = render(<DateLookup {...props} />);
|
|
390
|
+
await clickClearButton(view);
|
|
391
|
+
expect(props.onChange).toHaveBeenCalledWith(null);
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
const getClearButton = (view: RenderResult) => {
|
|
396
|
+
return view.container.querySelector('.clear-btn');
|
|
397
|
+
};
|
|
398
|
+
const getOpenButton = (label: DateLookupProps['label']) => {
|
|
399
|
+
return screen.getByRole('button', {
|
|
400
|
+
name: new RegExp(String(label), 'i'),
|
|
401
|
+
expanded: false,
|
|
402
|
+
});
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const getDateButton = () => {
|
|
406
|
+
return screen.getByRole('button', {
|
|
407
|
+
name: /Go to 20 year view/i,
|
|
408
|
+
});
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const openDateLookup = async () => userEvent.click(getOpenButton(labelProp));
|
|
412
|
+
|
|
413
|
+
const clickDateButton = async () => userEvent.click(getDateButton());
|
|
414
|
+
|
|
415
|
+
// Close dateLookup and removes events attached to documents.
|
|
416
|
+
const closeDateLookup = async (view: RenderResult) => userEvent.click(view.container);
|
|
417
|
+
|
|
418
|
+
// @ts-expect-error getClearButton returns node
|
|
419
|
+
const clickClearButton = async (view: RenderResult) => userEvent.click(getClearButton(view));
|
|
420
|
+
|
|
421
|
+
const getActiveYearButton = (view: RenderResult) => {
|
|
422
|
+
return screen.getByRole('button', {
|
|
423
|
+
name: /selected year/i,
|
|
424
|
+
pressed: true,
|
|
425
|
+
});
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
const getActiveMonthButton = (view: RenderResult) => {
|
|
429
|
+
return screen.getByRole('button', {
|
|
430
|
+
name: /selected month/i,
|
|
431
|
+
pressed: true,
|
|
432
|
+
});
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const getActiveDayButton = (view: RenderResult) => {
|
|
436
|
+
return screen.getByRole('button', {
|
|
437
|
+
name: /selected day/i,
|
|
438
|
+
pressed: true,
|
|
439
|
+
});
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const getButtonByAriaLabel = (ariaLabel: string) => {
|
|
443
|
+
return screen.getByRole('button', { name: ariaLabel });
|
|
444
|
+
};
|
|
445
|
+
});
|
|
@@ -9,52 +9,21 @@
|
|
|
9
9
|
padding-left: var(--size-16);
|
|
10
10
|
}
|
|
11
11
|
.clear-btn {
|
|
12
|
-
transition: color 0.15s ease-in-out;
|
|
13
|
-
color: #c9cbce;
|
|
14
|
-
color: var(--color-interactive-secondary);
|
|
15
12
|
position: absolute;
|
|
16
|
-
top: 16px;
|
|
17
|
-
top: var(--size-16);
|
|
18
|
-
right: 16px;
|
|
19
|
-
right: var(--size-16);
|
|
20
|
-
}
|
|
21
|
-
[dir="rtl"] .clear-btn {
|
|
22
|
-
left: 16px;
|
|
23
|
-
left: var(--size-16);
|
|
24
|
-
right: auto;
|
|
25
|
-
right: initial;
|
|
26
|
-
}
|
|
27
|
-
.np-theme-personal .clear-btn {
|
|
28
13
|
top: 8px;
|
|
29
14
|
top: var(--size-8);
|
|
30
15
|
right: 8px;
|
|
31
16
|
right: var(--size-8);
|
|
32
17
|
}
|
|
33
|
-
[dir="rtl"] .
|
|
18
|
+
[dir="rtl"] .clear-btn {
|
|
34
19
|
left: 8px;
|
|
35
20
|
left: var(--size-8);
|
|
36
21
|
right: auto;
|
|
37
22
|
right: initial;
|
|
38
23
|
}
|
|
39
24
|
.clear-btn--sm {
|
|
40
|
-
top: 8px;
|
|
41
|
-
top: var(--size-8);
|
|
42
|
-
}
|
|
43
|
-
.np-theme-personal .clear-btn--sm {
|
|
44
25
|
top: 0;
|
|
45
26
|
}
|
|
46
27
|
.clear-btn--lg {
|
|
47
|
-
top: 28px;
|
|
48
|
-
}
|
|
49
|
-
.np-theme-personal .clear-btn--lg {
|
|
50
28
|
top: 20px;
|
|
51
29
|
}
|
|
52
|
-
.clear-btn:not(.disabled):not(:disabled):hover,
|
|
53
|
-
.clear-btn:not(.disabled):not(:disabled):focus {
|
|
54
|
-
color: #d03238;
|
|
55
|
-
color: var(--color-interactive-negative-hover);
|
|
56
|
-
}
|
|
57
|
-
.clear-btn:not(.disabled):not(:disabled):active {
|
|
58
|
-
color: #bf1e2c;
|
|
59
|
-
color: var(--color-interactive-negative-active);
|
|
60
|
-
}
|
|
@@ -13,39 +13,15 @@
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
.clear-btn {
|
|
16
|
-
transition: color 0.15s ease-in-out;
|
|
17
|
-
color: var(--color-interactive-secondary);
|
|
18
16
|
position: absolute;
|
|
19
|
-
top: var(--size-
|
|
20
|
-
.right(var(--size-
|
|
21
|
-
|
|
22
|
-
.np-theme-personal & {
|
|
23
|
-
top: var(--size-8);
|
|
24
|
-
.right(var(--size-8));
|
|
25
|
-
}
|
|
17
|
+
top: var(--size-8);
|
|
18
|
+
.right(var(--size-8));
|
|
26
19
|
|
|
27
20
|
&--sm {
|
|
28
|
-
top:
|
|
29
|
-
|
|
30
|
-
.np-theme-personal & {
|
|
31
|
-
top: 0;
|
|
32
|
-
}
|
|
21
|
+
top: 0;
|
|
33
22
|
}
|
|
34
23
|
|
|
35
24
|
&--lg {
|
|
36
|
-
top:
|
|
37
|
-
|
|
38
|
-
.np-theme-personal & {
|
|
39
|
-
top: 20px;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
&:not(.disabled, :disabled):hover,
|
|
44
|
-
&:not(.disabled, :disabled):focus {
|
|
45
|
-
color: var(--color-interactive-negative-hover);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
&:not(.disabled, :disabled):active {
|
|
49
|
-
color: var(--color-interactive-negative-active);
|
|
25
|
+
top: 20px;
|
|
50
26
|
}
|
|
51
27
|
}
|
|
@@ -3,12 +3,13 @@ import { useIntl } from 'react-intl';
|
|
|
3
3
|
|
|
4
4
|
import Chevron from '../../chevron';
|
|
5
5
|
import { Size, Position, SizeSmall, SizeMedium, SizeLarge, Typography } from '../../common';
|
|
6
|
-
import { CloseButton } from '../../common/closeButton/CloseButton';
|
|
7
6
|
|
|
8
7
|
import messages from './DateTrigger.messages';
|
|
9
8
|
import { useContext, useId } from 'react';
|
|
10
9
|
import { OverlayIdContext } from '../../provider/overlay/OverlayIdProvider';
|
|
11
10
|
import Body from '../../body';
|
|
11
|
+
import IconButton from '../../iconButton';
|
|
12
|
+
import { Cross } from '@transferwise/icons';
|
|
12
13
|
|
|
13
14
|
interface DateTriggerProps {
|
|
14
15
|
ariaLabelledBy?: string;
|
|
@@ -78,16 +79,19 @@ const DateTrigger: React.FC<DateTriggerProps> = ({
|
|
|
78
79
|
<>
|
|
79
80
|
<div className="clearfix" />
|
|
80
81
|
<span className="input-group-addon">
|
|
81
|
-
<
|
|
82
|
+
<IconButton
|
|
83
|
+
size={32}
|
|
84
|
+
priority="minimal"
|
|
82
85
|
className={`clear-btn clear-btn--${size}`}
|
|
83
86
|
aria-label={`${formatMessage(messages.ariaLabel)} ${label}`}
|
|
84
|
-
|
|
85
|
-
onClick={(event: React.MouseEvent<HTMLButtonElement>) => {
|
|
87
|
+
onClick={(event) => {
|
|
86
88
|
event.stopPropagation();
|
|
87
89
|
event.preventDefault();
|
|
88
90
|
onClear();
|
|
89
91
|
}}
|
|
90
|
-
|
|
92
|
+
>
|
|
93
|
+
<Cross />
|
|
94
|
+
</IconButton>
|
|
91
95
|
</span>
|
|
92
96
|
</>
|
|
93
97
|
) : null}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
2
|
|
|
3
|
-
import ActionButton from '../actionButton';
|
|
4
3
|
import { Layout } from '../common';
|
|
4
|
+
import Button from '../button';
|
|
5
5
|
|
|
6
6
|
export interface DefinitionListDefinition {
|
|
7
7
|
action?: {
|
|
@@ -85,9 +85,9 @@ export default function DefinitionList({
|
|
|
85
85
|
'tw-definition-list__action',
|
|
86
86
|
)}
|
|
87
87
|
>
|
|
88
|
-
<
|
|
88
|
+
<Button as="button" size="sm" v2 onClick={action.onClick}>
|
|
89
89
|
{action.label}
|
|
90
|
-
</
|
|
90
|
+
</Button>
|
|
91
91
|
</div>
|
|
92
92
|
) : null}
|
|
93
93
|
</dd>
|
|
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|
|
2
2
|
import { Search } from '@transferwise/icons';
|
|
3
3
|
import { useRef, useState } from 'react';
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { Button } from '..';
|
|
6
6
|
import { Field } from '../field/Field';
|
|
7
7
|
|
|
8
8
|
import { Input } from './Input';
|
|
@@ -59,7 +59,9 @@ function InputGroupWithSuffix(args: NonNullable<Story['args']>) {
|
|
|
59
59
|
{...args}
|
|
60
60
|
addonEnd={{
|
|
61
61
|
content: (
|
|
62
|
-
<
|
|
62
|
+
<Button
|
|
63
|
+
size="sm"
|
|
64
|
+
v2
|
|
63
65
|
onClick={async () => {
|
|
64
66
|
await navigator.clipboard.writeText(value);
|
|
65
67
|
if (ref.current != null) {
|
|
@@ -69,7 +71,7 @@ function InputGroupWithSuffix(args: NonNullable<Story['args']>) {
|
|
|
69
71
|
}}
|
|
70
72
|
>
|
|
71
73
|
Copy
|
|
72
|
-
</
|
|
74
|
+
</Button>
|
|
73
75
|
),
|
|
74
76
|
interactive: true,
|
|
75
77
|
padding: 'sm',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Button } from '..';
|
|
2
2
|
import { render, screen, within } from '../test-utils';
|
|
3
3
|
|
|
4
4
|
import ListItem from '.';
|
|
@@ -17,7 +17,15 @@ describe('ListItem', () => {
|
|
|
17
17
|
|
|
18
18
|
it('shows an action', async () => {
|
|
19
19
|
render(
|
|
20
|
-
<ListItem
|
|
20
|
+
<ListItem
|
|
21
|
+
title="Account number"
|
|
22
|
+
value="123"
|
|
23
|
+
action={
|
|
24
|
+
<Button v2 size="sm">
|
|
25
|
+
Copy
|
|
26
|
+
</Button>
|
|
27
|
+
}
|
|
28
|
+
/>,
|
|
21
29
|
);
|
|
22
30
|
|
|
23
31
|
const listItem = await findListItem();
|