@transferwise/components 46.40.0 → 46.41.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.
@@ -1,8 +1,8 @@
1
- import { AlertCircle as AlertCircleIcon } from '@transferwise/icons';
2
1
  import classNames from 'classnames';
3
- import { ReactNode } from 'react';
2
+ import React, { ReactNode } from 'react';
4
3
 
5
- import { Sentiment } from '../common';
4
+ import { Sentiment, Size } from '../common';
5
+ import StatusIcon from '../statusIcon';
6
6
 
7
7
  export interface InlineAlertProps {
8
8
  id?: string;
@@ -17,15 +17,22 @@ export default function InlineAlert({
17
17
  className,
18
18
  children,
19
19
  }: InlineAlertProps) {
20
- const danger = type === 'negative' || type === 'error';
21
20
  return (
22
21
  <div
23
22
  role="alert"
24
23
  id={id}
25
- className={classNames('alert alert-detach', `alert-${danger ? 'danger' : type}`, className)}
24
+ className={classNames(
25
+ 'alert alert-detach',
26
+ `alert-${type === Sentiment.NEGATIVE || type === Sentiment.ERROR ? 'danger' : type}`,
27
+ className,
28
+ )}
26
29
  >
27
- {danger && <AlertCircleIcon />}
28
- <div>{children}</div>
30
+ <div className="d-inline-flex">
31
+ {type !== Sentiment.NEUTRAL && type !== Sentiment.PENDING && (
32
+ <StatusIcon sentiment={type} size={Size.SMALL} />
33
+ )}
34
+ <div className="np-text-body-default">{children}</div>
35
+ </div>
29
36
  </div>
30
37
  );
31
38
  }
package/src/main.css CHANGED
@@ -4208,14 +4208,16 @@ html:not([dir="rtl"]) .np-navigation-option {
4208
4208
  height: var(--size-32);
4209
4209
  }
4210
4210
  }
4211
- .status-circle.negative {
4211
+ .status-circle.negative,
4212
+ .status-circle.error {
4212
4213
  background-color: var(--color-sentiment-negative);
4213
4214
  }
4214
4215
  .status-circle.neutral {
4215
4216
  background-color: #5d7079;
4216
4217
  background-color: var(--color-content-secondary);
4217
4218
  }
4218
- .status-circle.positive {
4219
+ .status-circle.positive,
4220
+ .status-circle.success {
4219
4221
  background-color: var(--color-sentiment-positive);
4220
4222
  }
4221
4223
  .tw-stepper {
@@ -66,13 +66,15 @@
66
66
  height: var(--size-32);
67
67
  }
68
68
  }
69
- .status-circle.negative {
69
+ .status-circle.negative,
70
+ .status-circle.error {
70
71
  background-color: var(--color-sentiment-negative);
71
72
  }
72
73
  .status-circle.neutral {
73
74
  background-color: #5d7079;
74
75
  background-color: var(--color-content-secondary);
75
76
  }
76
- .status-circle.positive {
77
+ .status-circle.positive,
78
+ .status-circle.success {
77
79
  background-color: var(--color-sentiment-positive);
78
80
  }
@@ -60,7 +60,8 @@
60
60
  }
61
61
  }
62
62
 
63
- .status-circle.negative {
63
+ .status-circle.negative,
64
+ .status-circle.error {
64
65
  background-color: var(--color-sentiment-negative);
65
66
  }
66
67
 
@@ -68,6 +69,7 @@
68
69
  background-color: var(--color-content-secondary);
69
70
  }
70
71
 
71
- .status-circle.positive {
72
+ .status-circle.positive,
73
+ .status-circle.success {
72
74
  background-color: var(--color-sentiment-positive);
73
75
  }
@@ -26,7 +26,7 @@ const StatusIcon = ({ sentiment = 'neutral', size = 'md' }: StatusIconProps) =>
26
26
  return (
27
27
  <span
28
28
  data-testid="status-icon"
29
- className={classNames('status-circle', 'status-circle-' + size, sentiment)}
29
+ className={classNames('status-circle', `status-circle-${size}`, sentiment)}
30
30
  >
31
31
  <Icon className={classNames('status-icon', iconColor)} />
32
32
  </span>
@@ -1,17 +0,0 @@
1
- import { Field } from '../field/Field';
2
- import { mockMatchMedia, mockResizeObserver, render, screen } from '../test-utils';
3
- import DateInput from './DateInput';
4
-
5
- mockMatchMedia();
6
- mockResizeObserver();
7
-
8
- describe('DateInput', () => {
9
- it('supports `Field` for labeling', () => {
10
- render(
11
- <Field label="Date of birth">
12
- <DateInput onChange={() => {}} />
13
- </Field>,
14
- );
15
- expect(screen.getAllByRole('group')[0]).toHaveAccessibleName(/^Date of birth/);
16
- });
17
- });
@@ -1,477 +0,0 @@
1
- import { shallow, mount } from 'enzyme';
2
- import { useIntl } from 'react-intl';
3
-
4
- import { DateInput } from '..';
5
- import { mockMatchMedia, mockResizeObserver } from '../test-utils';
6
-
7
- const LOCALES = {
8
- jp: 'ja-JP',
9
- us: 'en-US',
10
- };
11
- const DEFAULT_LOCALE = 'en-GB';
12
- const FEBRUARY_INDEX = 1;
13
- const AUGUST_INDEX = 7;
14
-
15
- const DAY_SELECTOR = 'ForwardRef(Input)[name="day"]';
16
- const MONTH_SELECTOR = 'SelectInput';
17
- const YEAR_SELECTOR = 'ForwardRef(Input)[name="year"]';
18
-
19
- jest.mock('./DateInput.messages', () => ({
20
- monthLabel: {
21
- id: 'neptune.DateInput.month.label',
22
- defaultMessage: 'Month',
23
- },
24
- dayLabel: {
25
- id: 'neptune.DateInput.day.label',
26
- defaultMessage: 'Day',
27
- },
28
- yearLabel: {
29
- id: 'neptune.DateInput.year.label',
30
- defaultMessage: 'Year',
31
- },
32
- dayPlaceholder: {
33
- id: 'neptune.DateInput.day.placeholder',
34
- defaultMessage: 'DD',
35
- description: 'Placeholder for 2-digit day values within an input.',
36
- },
37
- yearPlaceholder: {
38
- id: 'neptune.DateInput.year.placeholder',
39
- defaultMessage: 'YYYY',
40
- description: 'Placeholder for 4-digit year values within an input.',
41
- },
42
- }));
43
-
44
- jest.mock('react-intl');
45
-
46
- describe('Date Input Component', () => {
47
- let selectMonth;
48
- let inputDay;
49
- let inputYear;
50
- let component;
51
- const props = { onChange: jest.fn() };
52
-
53
- beforeEach(() => {
54
- mockMatchMedia();
55
- mockResizeObserver();
56
- useIntl.mockReturnValue({
57
- locale: DEFAULT_LOCALE,
58
- formatMessage: (message) => message.defaultMessage,
59
- });
60
- component = shallow(<DateInput {...props} />);
61
-
62
- selectMonth = component.find(MONTH_SELECTOR);
63
- inputDay = component.find(DAY_SELECTOR);
64
- inputYear = component.find(YEAR_SELECTOR);
65
- });
66
-
67
- afterEach(() => {
68
- jest.resetAllMocks();
69
- });
70
-
71
- describe('when initialised without a model', () => {
72
- it('sets day field to empty', () => {
73
- expect(inputDay.prop('value')).toBe('');
74
- });
75
-
76
- it('sets month field to 0', () => {
77
- expect(selectMonth.props().value).toBeNull();
78
- });
79
-
80
- it('sets year field to empty', () => {
81
- expect(inputYear.prop('value')).toBe('');
82
- });
83
-
84
- it('allows 0 prefixed day values', () => {
85
- inputDay.simulate('change', { target: { value: '0' } });
86
- setTimeout(() => {
87
- expect(inputDay.prop('value')).toBe('0');
88
- });
89
- });
90
-
91
- it('does not allow 00 as a day value', () => {
92
- inputDay.simulate('change', { target: { value: '00' } });
93
- setTimeout(() => {
94
- expect(inputDay.prop('value')).toBe('1');
95
- });
96
- });
97
- });
98
-
99
- describe('when initialised with a model', () => {
100
- describe('as a valid Date instance', () => {
101
- it(`sets values correctly`, () => {
102
- component = shallow(<DateInput {...props} value="1971-02-01" />);
103
-
104
- selectMonth = component.find(MONTH_SELECTOR);
105
- inputDay = component.find(DAY_SELECTOR);
106
- inputYear = component.find(YEAR_SELECTOR);
107
-
108
- expect(inputDay.prop('value')).toBe(1);
109
- expect(selectMonth.prop('value')).toBe(FEBRUARY_INDEX);
110
- expect(inputYear.prop('value')).toBe(1971);
111
- });
112
- });
113
-
114
- describe('as a valid short ISO8601 string', () => {
115
- it('sets values correctly', () => {
116
- component = shallow(<DateInput {...props} value="1990-08-22" />);
117
-
118
- selectMonth = component.find(MONTH_SELECTOR);
119
- inputDay = component.find(DAY_SELECTOR);
120
- inputYear = component.find(YEAR_SELECTOR);
121
-
122
- expect(inputDay.prop('value')).toBe(22);
123
- expect(selectMonth.prop('value')).toBe(AUGUST_INDEX);
124
- expect(inputYear.prop('value')).toBe(1990);
125
- });
126
- });
127
-
128
- describe('as a valid short ISO8601 string with year and month only', () => {
129
- it('sets values correctly', () => {
130
- component = shallow(<DateInput {...props} value="1990-08" />);
131
-
132
- selectMonth = component.find(MONTH_SELECTOR);
133
- inputDay = component.find(DAY_SELECTOR);
134
- inputYear = component.find(YEAR_SELECTOR);
135
-
136
- expect(inputDay.prop('value')).toBe('');
137
- expect(selectMonth.prop('value')).toBe(AUGUST_INDEX);
138
- expect(inputYear.prop('value')).toBe(1990);
139
- });
140
- });
141
-
142
- describe('as a valid long ISO8601 string', () => {
143
- it('sets values correctly', () => {
144
- component = shallow(<DateInput {...props} value="1990-02-28T00:00:00.000Z" />);
145
-
146
- selectMonth = component.find(MONTH_SELECTOR);
147
- inputDay = component.find(DAY_SELECTOR);
148
- inputYear = component.find(YEAR_SELECTOR);
149
-
150
- expect(inputDay.prop('value')).toBe(28);
151
- expect(selectMonth.prop('value')).toBe(FEBRUARY_INDEX);
152
- expect(inputYear.prop('value')).toBe(1990);
153
- });
154
- });
155
-
156
- describe('when disabled is set to true', () => {
157
- it('sets values to disabled', () => {
158
- component = shallow(<DateInput {...props} disabled />);
159
-
160
- selectMonth = component.find(MONTH_SELECTOR);
161
- inputDay = component.find(DAY_SELECTOR);
162
- inputYear = component.find(YEAR_SELECTOR);
163
-
164
- expect(inputDay.prop('disabled')).toBe(true);
165
- expect(selectMonth.prop('disabled')).toBe(true);
166
- expect(inputYear.prop('disabled')).toBe(true);
167
- });
168
- });
169
-
170
- describe('when disabled is set to false', () => {
171
- it("doesn't sets values to disabled", () => {
172
- component = shallow(<DateInput {...props} />);
173
-
174
- selectMonth = component.find(MONTH_SELECTOR);
175
- inputDay = component.find(DAY_SELECTOR);
176
- inputYear = component.find(YEAR_SELECTOR);
177
-
178
- expect(inputDay.prop('disabled')).toBe(false);
179
- expect(selectMonth.prop('disabled')).toBe(false);
180
- expect(inputYear.prop('disabled')).toBe(false);
181
- });
182
- });
183
- });
184
-
185
- describe('when locale is provided', () => {
186
- it('shows day before month if locale is GB', () => {
187
- useIntl.mockReturnValue({
188
- locale: DEFAULT_LOCALE,
189
- formatMessage: (message) => message.defaultMessage,
190
- });
191
-
192
- component = shallow(<DateInput {...props} />);
193
-
194
- expect(component.find('span').at(0).text()).toBe('Day');
195
- expect(component.find('span').at(1).text()).toBe('Month');
196
- expect(component.find('span').at(2).text()).toBe('Year');
197
- });
198
-
199
- it('shows month before day if locale is US', () => {
200
- useIntl.mockReturnValue({
201
- locale: LOCALES.us,
202
- formatMessage: (message) => message.defaultMessage,
203
- });
204
-
205
- component = shallow(<DateInput {...props} />);
206
-
207
- expect(component.find('span').at(0).text()).toBe('Month');
208
- expect(component.find('span').at(1).text()).toBe('Day');
209
- expect(component.find('span').at(2).text()).toBe('Year');
210
- });
211
-
212
- it('shows year, month, day if locale is JP', () => {
213
- useIntl.mockReturnValue({
214
- locale: LOCALES.jp,
215
- formatMessage: (message) => message.defaultMessage,
216
- });
217
-
218
- component = shallow(<DateInput {...props} />);
219
-
220
- expect(component.find('span').at(0).text()).toBe('Year');
221
- expect(component.find('span').at(1).text()).toBe('Month');
222
- expect(component.find('span').at(2).text()).toBe('Day');
223
- });
224
- });
225
-
226
- describe('when initialised', () => {
227
- describe('without an initial value', () => {
228
- it(`doesn't call the onChange callback`, () => {
229
- component = mount(<DateInput {...props} />);
230
-
231
- expect(props.onChange).not.toHaveBeenCalled();
232
- });
233
- });
234
-
235
- describe('with an initial value', () => {
236
- it(`doesn't call the onChange callback`, () => {
237
- component = mount(<DateInput {...props} value="1990-08" />);
238
-
239
- expect(props.onChange).not.toHaveBeenCalled();
240
- });
241
- });
242
-
243
- describe('with placeholders set', () => {
244
- it(`doesn't override placeholders`, () => {
245
- const placeholders = {
246
- day: 'DayPlaceholder',
247
- month: 'MonthPlaceholder',
248
- year: 'YearPlaceholder',
249
- };
250
- component = mount(<DateInput {...props} placeholders={placeholders} />);
251
-
252
- expect(component.find(DAY_SELECTOR).props().placeholder).toStrictEqual('DayPlaceholder');
253
- expect(component.find(MONTH_SELECTOR).props().placeholder).toStrictEqual(
254
- 'MonthPlaceholder',
255
- );
256
- expect(component.find(YEAR_SELECTOR).props().placeholder).toStrictEqual('YearPlaceholder');
257
- });
258
- });
259
-
260
- describe('with placeholders not set', () => {
261
- it('uses localized defaults', () => {
262
- component = mount(<DateInput {...props} />);
263
-
264
- expect(component.find(DAY_SELECTOR).props().placeholder).toStrictEqual('DD');
265
- expect(component.find(MONTH_SELECTOR).props().placeholder).toStrictEqual('Month');
266
- expect(component.find(YEAR_SELECTOR).props().placeholder).toStrictEqual('YYYY');
267
- });
268
- });
269
-
270
- describe('with labels set', () => {
271
- it(`doesn't override placeholders`, () => {
272
- component = mount(
273
- <DateInput
274
- {...props}
275
- dayLabel="dayLabel"
276
- monthLabel="monthLabel"
277
- yearLabel="yearLabel"
278
- />,
279
- );
280
-
281
- expect(component.find({ children: 'dayLabel', type: 'span' })).toBeTruthy();
282
- expect(component.find({ children: 'monthLabel', type: 'span' })).toBeTruthy();
283
- expect(component.find({ children: 'yearLabel', type: 'span' })).toBeTruthy();
284
- });
285
- });
286
-
287
- describe('with labels not set', () => {
288
- it('uses localized defaults', () => {
289
- component = mount(<DateInput {...props} />);
290
-
291
- expect(component.find({ children: 'Day', type: 'span' })).toBeTruthy();
292
- expect(component.find({ children: 'Month', type: 'span' })).toBeTruthy();
293
- expect(component.find({ children: 'Year', type: 'span' })).toBeTruthy();
294
- });
295
- });
296
- });
297
-
298
- describe('when user interacts', () => {
299
- describe('with an empty date input', () => {
300
- it('calls onChange with null if month is not selected', () => {
301
- component = mount(<DateInput {...props} />);
302
-
303
- inputDay = component.find(DAY_SELECTOR);
304
-
305
- inputDay.simulate('change', { target: { value: '12' } });
306
-
307
- inputYear = component.find(YEAR_SELECTOR);
308
-
309
- inputYear.simulate('change', { target: { value: '1990' } });
310
-
311
- expect(props.onChange).toHaveBeenLastCalledWith(null);
312
- });
313
- });
314
-
315
- describe('with day input', () => {
316
- it('returns correct value for correct input', () => {
317
- component = mount(<DateInput {...props} value="2001-02-11" />);
318
-
319
- inputDay = component.find(DAY_SELECTOR);
320
-
321
- inputDay.simulate('change', { target: { value: '12' } });
322
-
323
- expect(props.onChange).toHaveBeenCalledWith('2001-02-12');
324
- });
325
-
326
- it('returns null for invalid input', () => {
327
- component = mount(<DateInput {...props} value="2001-01-01" />);
328
-
329
- inputDay = component.find(DAY_SELECTOR);
330
-
331
- inputDay.simulate('change', { target: { value: 'aa' } });
332
-
333
- expect(props.onChange).toHaveBeenCalledWith(null);
334
- });
335
-
336
- it('returns null when day input is cleared', () => {
337
- component = mount(<DateInput {...props} value="2001-01-01" />);
338
-
339
- inputDay = component.find(DAY_SELECTOR);
340
-
341
- inputDay.simulate('change', { target: { value: '' } });
342
-
343
- expect(props.onChange).toHaveBeenLastCalledWith(null);
344
- });
345
- });
346
-
347
- describe('with year input', () => {
348
- it('returns correct value for correct input', () => {
349
- component = mount(<DateInput {...props} value="2001-01-01" />);
350
- inputYear = component.find(YEAR_SELECTOR);
351
-
352
- inputYear.simulate('change', { target: { value: '1990' } });
353
-
354
- expect(props.onChange).toHaveBeenCalledWith('1990-01-01');
355
- });
356
- });
357
- });
358
-
359
- describe('with day input and year input', () => {
360
- describe('when switching from day input to year input', () => {
361
- it('does not call onBlur nor onFocus', () => {
362
- const onFocus = jest.fn();
363
- const onBlur = jest.fn();
364
-
365
- component = mount(<DateInput {...props} onFocus={onFocus} onBlur={onBlur} />);
366
-
367
- inputDay = component.find(DAY_SELECTOR);
368
- inputYear = component.find(YEAR_SELECTOR);
369
-
370
- inputDay.simulate('focus');
371
-
372
- inputDay.simulate('blur', { relatedTarget: inputYear.getDOMNode() });
373
- inputYear.simulate('focus', { relatedTarget: inputDay.getDOMNode() });
374
- inputYear.simulate('blur');
375
-
376
- expect(onFocus).toHaveBeenCalledTimes(1);
377
- expect(onBlur).toHaveBeenCalledTimes(1);
378
-
379
- jest.useRealTimers();
380
- });
381
-
382
- it('does not call onBlur on IE11 either', () => {
383
- const onBlur = jest.fn();
384
-
385
- component = mount(<DateInput {...props} onBlur={onBlur} />);
386
-
387
- inputDay = component.find(DAY_SELECTOR);
388
- inputYear = component.find(YEAR_SELECTOR);
389
-
390
- inputDay.simulate('focus');
391
-
392
- Object.defineProperty(document, 'activeElement', {
393
- value: inputYear.getDOMNode(),
394
- });
395
-
396
- inputDay.simulate('blur', { relatedTarget: null });
397
- inputYear.simulate('focus', { relatedTarget: inputDay.getDOMNode() });
398
-
399
- expect(onBlur).not.toHaveBeenCalled();
400
-
401
- jest.useRealTimers();
402
- });
403
- });
404
- });
405
-
406
- describe('when user selects invalid dates', () => {
407
- it('corrects days in lap years February', () => {
408
- component = mount(<DateInput {...props} value="2000-02-29" />);
409
-
410
- expect(component.find(DAY_SELECTOR).prop('value')).toBe(29);
411
-
412
- inputYear = component.find(YEAR_SELECTOR);
413
- inputYear.simulate('change', { target: { value: 1999 } });
414
-
415
- expect(component.find(DAY_SELECTOR).prop('value')).toBe(28);
416
- });
417
-
418
- it('lowers days if value entered too high', () => {
419
- const comp = shallow(<DateInput {...props} />);
420
-
421
- inputDay = comp.find(DAY_SELECTOR);
422
-
423
- inputDay.simulate('change', { target: { value: 32 } });
424
-
425
- expect(comp.find(DAY_SELECTOR).prop('value')).toBe(31);
426
- });
427
-
428
- it('highers days if value entered too low', () => {
429
- const comp = shallow(<DateInput {...props} />);
430
-
431
- inputDay = comp.find(DAY_SELECTOR);
432
-
433
- inputDay.simulate('change', { target: { value: -1 } });
434
-
435
- expect(comp.find(DAY_SELECTOR).prop('value')).toBe(1);
436
- });
437
- });
438
-
439
- describe('when in mode month and year only', () => {
440
- beforeEach(() => {
441
- const extraProps = {
442
- mode: 'month-year',
443
- value: '2001-01-01',
444
- };
445
- component = mount(<DateInput {...{ ...props, ...extraProps }} />);
446
- });
447
-
448
- it('should only display month and year inputs', () => {
449
- expect(component.exists(YEAR_SELECTOR)).toBe(true);
450
- expect(component.exists(MONTH_SELECTOR)).toBe(true);
451
- expect(component.exists(DAY_SELECTOR)).toBe(false);
452
- });
453
- });
454
-
455
- describe('when selectProps is provided', () => {
456
- beforeEach(() => {
457
- component = shallow(
458
- <DateInput
459
- selectProps={{
460
- buttonProps: {
461
- 'aria-label': 'mock-button-label',
462
- },
463
- }}
464
- {...props}
465
- />,
466
- );
467
- });
468
-
469
- it('renders Select component with expected props', () => {
470
- const select = component.find(MONTH_SELECTOR);
471
-
472
- expect(select.prop('buttonProps')).toStrictEqual({
473
- 'aria-label': 'mock-button-label',
474
- });
475
- });
476
- });
477
- });