@transferwise/components 46.8.0 → 46.10.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.
Files changed (75) hide show
  1. package/build/index.esm.js +76 -127
  2. package/build/index.esm.js.map +1 -1
  3. package/build/index.js +76 -127
  4. package/build/index.js.map +1 -1
  5. package/build/types/checkboxOption/CheckboxOption.d.ts +2 -2
  6. package/build/types/checkboxOption/CheckboxOption.d.ts.map +1 -1
  7. package/build/types/common/index.d.ts +0 -1
  8. package/build/types/dateLookup/DateLookup.d.ts.map +1 -1
  9. package/build/types/index.d.ts +2 -0
  10. package/build/types/index.d.ts.map +1 -1
  11. package/build/types/inputs/SelectInput.d.ts +2 -1
  12. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  13. package/build/types/moneyInput/MoneyInput.d.ts.map +1 -1
  14. package/build/types/select/Select.d.ts.map +1 -1
  15. package/build/types/snackbar/Snackbar.d.ts +30 -22
  16. package/build/types/snackbar/Snackbar.d.ts.map +1 -1
  17. package/build/types/snackbar/SnackbarContext.d.ts +7 -2
  18. package/build/types/snackbar/SnackbarContext.d.ts.map +1 -1
  19. package/build/types/snackbar/SnackbarProvider.d.ts +7 -12
  20. package/build/types/snackbar/SnackbarProvider.d.ts.map +1 -1
  21. package/build/types/snackbar/index.d.ts +2 -0
  22. package/build/types/snackbar/index.d.ts.map +1 -0
  23. package/build/types/snackbar/useSnackbar.d.ts +1 -1
  24. package/build/types/snackbar/useSnackbar.d.ts.map +1 -1
  25. package/build/types/switch/Switch.d.ts.map +1 -1
  26. package/build/types/tabs/Tabs.d.ts.map +1 -1
  27. package/build/types/typeahead/Typeahead.d.ts.map +1 -1
  28. package/build/types/withNextPortal/withNextPortal.d.ts +1 -1
  29. package/build/types/withNextPortal/withNextPortal.d.ts.map +1 -1
  30. package/package.json +16 -19
  31. package/src/button/Button.story.tsx +3 -3
  32. package/src/checkboxOption/CheckboxOption.tsx +2 -2
  33. package/src/common/fakeEvents.js +2 -2
  34. package/src/common/index.js +0 -1
  35. package/src/dateInput/DateInput.story.tsx +4 -3
  36. package/src/dateLookup/DateLookup.js +6 -7
  37. package/src/dateLookup/DateLookup.keyboardEvents.spec.js +24 -25
  38. package/src/dateLookup/DateLookup.story.js +4 -3
  39. package/src/dateLookup/dateTrigger/DateTrigger.spec.js +3 -3
  40. package/src/index.ts +2 -0
  41. package/src/info/Info.story.tsx +3 -3
  42. package/src/inputWithDisplayFormat/InputWithDisplayFormat.story.js +2 -7
  43. package/src/inputs/SelectInput.spec.tsx +7 -0
  44. package/src/inputs/SelectInput.story.tsx +7 -7
  45. package/src/inputs/SelectInput.tsx +4 -0
  46. package/src/logo/Logo.js +4 -4
  47. package/src/moneyInput/MoneyInput.story.tsx +3 -3
  48. package/src/moneyInput/MoneyInput.tsx +14 -24
  49. package/src/select/Select.js +8 -9
  50. package/src/snackbar/{Snackbar.story.js → Snackbar.story.tsx} +4 -3
  51. package/src/snackbar/{Snackbar.js → Snackbar.tsx} +31 -32
  52. package/src/snackbar/SnackbarContext.ts +11 -0
  53. package/src/snackbar/SnackbarProvider.tsx +39 -0
  54. package/src/switch/Switch.spec.js +2 -3
  55. package/src/switch/Switch.tsx +1 -2
  56. package/src/switchOption/SwitchOption.spec.js +1 -4
  57. package/src/tabs/Tabs.js +1 -2
  58. package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.story.tsx +5 -4
  59. package/src/tile/Tile.js +2 -2
  60. package/src/tile/Tile.spec.js +5 -5
  61. package/src/tooltip/Tooltip.story.tsx +3 -3
  62. package/src/typeahead/Typeahead.js +5 -6
  63. package/src/typeahead/Typeahead.spec.js +3 -8
  64. package/src/typeahead/Typeahead.story.js +3 -4
  65. package/src/withNextPortal/withNextPortal.tsx +1 -1
  66. package/build/types/common/key.d.ts +0 -9
  67. package/build/types/common/key.d.ts.map +0 -1
  68. package/build/types/common/keyCodes.d.ts +0 -16
  69. package/build/types/common/keyCodes.d.ts.map +0 -1
  70. package/src/common/key.js +0 -8
  71. package/src/common/keyCodes.js +0 -19
  72. package/src/snackbar/SnackbarContext.js +0 -4
  73. package/src/snackbar/SnackbarProvider.js +0 -51
  74. /package/src/snackbar/{index.js → index.ts} +0 -0
  75. /package/src/snackbar/{useSnackbar.js → useSnackbar.ts} +0 -0
@@ -2,7 +2,6 @@
2
2
  import { mount } from 'enzyme';
3
3
 
4
4
  import { fakeKeyDownEventForKey } from '../common/fakeEvents';
5
- import KEY_CODES from '../common/keyCodes';
6
5
  import { mockMatchMedia } from '../test-utils';
7
6
 
8
7
  import DateLookup from '.';
@@ -31,28 +30,28 @@ describe('DateLookup (keyboard events)', () => {
31
30
  });
32
31
 
33
32
  it('can be opened by LEFT arrow', () => {
34
- pressKey(KEY_CODES.LEFT);
33
+ pressKey('ArrowLeft');
35
34
  expect(component.instance().state.open).toBe(true);
36
35
  });
37
36
 
38
37
  it('can be opened by UP arrow', () => {
39
- pressKey(KEY_CODES.UP);
38
+ pressKey('ArrowUp');
40
39
  expect(component.instance().state.open).toBe(true);
41
40
  });
42
41
 
43
42
  it('can be opened by RIGHT arrow', () => {
44
- pressKey(KEY_CODES.RIGHT);
43
+ pressKey('ArrowRight');
45
44
  expect(component.instance().state.open).toBe(true);
46
45
  });
47
46
 
48
47
  it('can be opened by DOWN arrow', () => {
49
- pressKey(KEY_CODES.DOWN);
48
+ pressKey('ArrowDown');
50
49
  expect(component.instance().state.open).toBe(true);
51
50
  });
52
51
 
53
52
  it('can be closed by pressing ESCAPE', () => {
54
53
  component.setState({ open: true });
55
- pressKey(KEY_CODES.ESCAPE);
54
+ pressKey('Escape');
56
55
  expect(component.instance().state.open).toBe(false);
57
56
  });
58
57
 
@@ -62,33 +61,33 @@ describe('DateLookup (keyboard events)', () => {
62
61
  });
63
62
 
64
63
  it('moves 1 day back by pressing LEFT', () => {
65
- pressKey(KEY_CODES.LEFT);
64
+ pressKey('ArrowLeft');
66
65
  onChangeCalledWith(new Date(2018, 11, 26));
67
66
  });
68
67
 
69
68
  it('moves 7 days back by pressing UP', () => {
70
- pressKey(KEY_CODES.UP);
69
+ pressKey('ArrowUp');
71
70
  onChangeCalledWith(new Date(2018, 11, 20));
72
71
  });
73
72
 
74
73
  it('moves 1 day forward by pressing RIGHT', () => {
75
- pressKey(KEY_CODES.RIGHT);
74
+ pressKey('ArrowRight');
76
75
  onChangeCalledWith(new Date(2018, 11, 28));
77
76
  });
78
77
 
79
78
  it('moves 7 day forward by pressing DOWN', () => {
80
- pressKey(KEY_CODES.DOWN);
79
+ pressKey('ArrowDown');
81
80
  onChangeCalledWith(new Date(2019, 0, 3));
82
81
  });
83
82
 
84
83
  it('resets to original date when escape key is pressed', () => {
85
- pressKey(KEY_CODES.LEFT);
86
- pressKey(KEY_CODES.ESCAPE);
84
+ pressKey('ArrowLeft');
85
+ pressKey('Escape');
87
86
  onChangeCalledWith(date);
88
87
  });
89
88
 
90
89
  it('resets to original date when clicking outside modal', () => {
91
- pressKey(KEY_CODES.LEFT);
90
+ pressKey('ArrowLeft');
92
91
  component.find('.dimmer').simulate('click');
93
92
  onChangeCalledWith(date);
94
93
  });
@@ -100,22 +99,22 @@ describe('DateLookup (keyboard events)', () => {
100
99
  });
101
100
 
102
101
  it('moves 1 month back by pressing LEFT', () => {
103
- pressKey(KEY_CODES.LEFT);
102
+ pressKey('ArrowLeft');
104
103
  onChangeCalledWith(new Date(2018, 10, 27));
105
104
  });
106
105
 
107
106
  it('moves 4 months back by pressing UP', () => {
108
- pressKey(KEY_CODES.UP);
107
+ pressKey('ArrowUp');
109
108
  onChangeCalledWith(new Date(2018, 7, 27));
110
109
  });
111
110
 
112
111
  it('moves 1 month forward by pressing RIGHT', () => {
113
- pressKey(KEY_CODES.RIGHT);
112
+ pressKey('ArrowRight');
114
113
  onChangeCalledWith(new Date(2019, 0, 27));
115
114
  });
116
115
 
117
116
  it('moves 4 months forward by pressing DOWN', () => {
118
- pressKey(KEY_CODES.DOWN);
117
+ pressKey('ArrowDown');
119
118
  onChangeCalledWith(new Date(2019, 3, 27));
120
119
  });
121
120
  });
@@ -126,22 +125,22 @@ describe('DateLookup (keyboard events)', () => {
126
125
  });
127
126
 
128
127
  it('moves 1 year back by pressing LEFT', () => {
129
- pressKey(KEY_CODES.LEFT);
128
+ pressKey('ArrowLeft');
130
129
  onChangeCalledWith(new Date(2017, 11, 27));
131
130
  });
132
131
 
133
132
  it('moves 4 years back by pressing UP', () => {
134
- pressKey(KEY_CODES.UP);
133
+ pressKey('ArrowUp');
135
134
  onChangeCalledWith(new Date(2014, 11, 27));
136
135
  });
137
136
 
138
137
  it('moves 1 year forward by pressing RIGHT', () => {
139
- pressKey(KEY_CODES.RIGHT);
138
+ pressKey('ArrowRight');
140
139
  onChangeCalledWith(new Date(2019, 11, 27));
141
140
  });
142
141
 
143
142
  it('moves 4 year forward by pressing DOWN', () => {
144
- pressKey(KEY_CODES.DOWN);
143
+ pressKey('ArrowDown');
145
144
  onChangeCalledWith(new Date(2022, 11, 27));
146
145
  });
147
146
  });
@@ -157,16 +156,16 @@ describe('DateLookup (keyboard events)', () => {
157
156
  });
158
157
 
159
158
  it('makes sure that date is >= min', () => {
160
- pressKey(KEY_CODES.LEFT);
159
+ pressKey('ArrowLeft');
161
160
  onChangeCalledWith(new Date(2018, 11, 26));
162
- pressKey(KEY_CODES.LEFT);
161
+ pressKey('ArrowLeft');
163
162
  onChangeCalledWith(new Date(2018, 11, 26));
164
163
  });
165
164
 
166
165
  it('makes sure that date is <= max', () => {
167
- pressKey(KEY_CODES.RIGHT);
166
+ pressKey('ArrowRight');
168
167
  onChangeCalledWith(new Date(2018, 11, 28));
169
- pressKey(KEY_CODES.RIGHT);
168
+ pressKey('ArrowRight');
170
169
  onChangeCalledWith(new Date(2018, 11, 28));
171
170
  });
172
171
  });
@@ -1,8 +1,9 @@
1
1
  import { boolean, select, date, text } from '@storybook/addon-knobs';
2
+ import { userEvent, within } from '@storybook/test';
2
3
  import { useState } from 'react';
3
4
 
4
5
  import { Size } from '../common';
5
- import { within, userEvent, storyConfig } from '../test-utils';
6
+ import { storyConfig } from '../test-utils';
6
7
 
7
8
  import DateLookup from './DateLookup';
8
9
 
@@ -48,8 +49,8 @@ export const Basic = () => {
48
49
  Basic.play = async ({ canvasElement }) => {
49
50
  // testing focus state on keyboard nav
50
51
  const canvas = within(canvasElement);
51
- userEvent.tab(canvas.getByRole('button'));
52
- userEvent.keyboard('{space}');
52
+ await userEvent.tab(canvas.getByRole('button'));
53
+ await userEvent.keyboard(' ');
53
54
  };
54
55
 
55
56
  export const Basic400Zoom = storyConfig(
@@ -111,21 +111,21 @@ describe('DateTrigger', () => {
111
111
  it('calls onClear when user press Space', () => {
112
112
  const onClear = jest.fn();
113
113
  component.setProps({ onClear });
114
- clearButton().simulate('keypress', fakeKeyDownEventForKey(32));
114
+ clearButton().simulate('keypress', fakeKeyDownEventForKey(' '));
115
115
  expect(onClear).toHaveBeenCalledTimes(1);
116
116
  });
117
117
 
118
118
  it('calls onClear when user press Enter', () => {
119
119
  const onClear = jest.fn();
120
120
  component.setProps({ onClear });
121
- clearButton().simulate('keypress', fakeKeyDownEventForKey(13));
121
+ clearButton().simulate('keypress', fakeKeyDownEventForKey('Enter'));
122
122
  expect(onClear).toHaveBeenCalledTimes(1);
123
123
  });
124
124
 
125
125
  it(`doesn't call onClear when user press a random key`, () => {
126
126
  const onClear = jest.fn();
127
127
  component.setProps({ onClear });
128
- clearButton().simulate('keypress', fakeKeyDownEventForKey(6));
128
+ clearButton().simulate('keypress', fakeKeyDownEventForKey('r'));
129
129
  expect(onClear).not.toHaveBeenCalled();
130
130
  });
131
131
 
package/src/index.ts CHANGED
@@ -16,6 +16,8 @@ export type {
16
16
  } from './inputs/SelectInput';
17
17
  export type { TextAreaProps } from './inputs/TextArea';
18
18
  export type { PhoneNumberInputProps } from './phoneNumberInput/PhoneNumberInput';
19
+ export type { SnackbarProps } from './snackbar/Snackbar';
20
+ export type { SnackbarContextType } from './snackbar/SnackbarContext';
19
21
  export type { TextareaWithDisplayFormatProps } from './textareaWithDisplayFormat';
20
22
  export type { UploadedFile, UploadError, UploadResponse } from './uploadInput/types';
21
23
  export type { ModalProps } from './modal';
@@ -1,5 +1,5 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
- import { userEvent, within } from '@storybook/testing-library';
2
+ import { userEvent, within } from '@storybook/test';
3
3
 
4
4
  import { lorem10, storyConfig } from '../test-utils';
5
5
 
@@ -35,9 +35,9 @@ export const OpenedPopover: Story = {
35
35
  delay: 1000,
36
36
  },
37
37
  },
38
- play: ({ canvasElement }) => {
38
+ play: async ({ canvasElement }) => {
39
39
  const canvas = within(canvasElement);
40
- userEvent.click(canvas.getByRole('button'));
40
+ await userEvent.click(canvas.getByRole('button'));
41
41
  },
42
42
  };
43
43
 
@@ -1,6 +1,5 @@
1
1
  import { text } from '@storybook/addon-knobs';
2
-
3
- import { within, userEvent } from '../test-utils';
2
+ import { userEvent, within } from '@storybook/test';
4
3
 
5
4
  import InputWithDisplayFormat from '.';
6
5
 
@@ -86,9 +85,5 @@ SecurityPinPlay.args = {
86
85
 
87
86
  SecurityPinPlay.play = async ({ canvasElement }) => {
88
87
  const canvas = within(canvasElement);
89
- let i = 0;
90
- while (i < 6) {
91
- await userEvent.type(canvas.getByPlaceholderText('*-*-*-*-*-*'), `${i + 1}`, { delay: 5000 });
92
- i++;
93
- }
88
+ await userEvent.type(canvas.getByPlaceholderText('*-*-*-*-*-*'), '123456');
94
89
  };
@@ -208,4 +208,11 @@ describe('SelectInput', () => {
208
208
 
209
209
  expect(trigger).toHaveTextContent('USD, EUR');
210
210
  });
211
+
212
+ it('supports custom `id` attribute', () => {
213
+ render(<SelectInput id="custom" items={[]} />);
214
+
215
+ const trigger = screen.getAllByRole('button')[0];
216
+ expect(trigger).toHaveAttribute('id', 'custom');
217
+ });
211
218
  });
@@ -1,4 +1,5 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
+ import { userEvent, within } from '@storybook/test';
2
3
  import { Calendar, ChevronDown } from '@transferwise/icons';
3
4
  import { Flag } from '@wise/art';
4
5
  import classNames from 'classnames';
@@ -7,7 +8,6 @@ import { useState } from 'react';
7
8
  import { getMonthNames } from '../common/dateUtils';
8
9
  import Drawer from '../drawer';
9
10
  import Modal from '../modal';
10
- import { within, userEvent } from '../test-utils';
11
11
 
12
12
  import {
13
13
  SelectInput,
@@ -131,9 +131,9 @@ export const Basic: StoryObj<{
131
131
  action: 'cleared',
132
132
  },
133
133
  },
134
- play: ({ canvasElement }) => {
134
+ play: async ({ canvasElement }) => {
135
135
  const triggerButton = within(canvasElement).getByRole('button');
136
- userEvent.click(triggerButton);
136
+ await userEvent.click(triggerButton);
137
137
  },
138
138
  };
139
139
 
@@ -184,9 +184,9 @@ export const Months: StoryObj<{
184
184
  action: 'cleared',
185
185
  },
186
186
  },
187
- play: ({ canvasElement }) => {
187
+ play: async ({ canvasElement }) => {
188
188
  const triggerButton = within(canvasElement).getByText('January');
189
- userEvent.click(triggerButton);
189
+ await userEvent.click(triggerButton);
190
190
  },
191
191
  };
192
192
 
@@ -402,9 +402,9 @@ export const CustomTrigger: StoryObj = {
402
402
  </div>
403
403
  );
404
404
  },
405
- play: ({ canvasElement }) => {
405
+ play: async ({ canvasElement }) => {
406
406
  const triggerButton = within(canvasElement).getByRole('button');
407
- userEvent.click(triggerButton);
407
+ await userEvent.click(triggerButton);
408
408
  },
409
409
  };
410
410
 
@@ -41,6 +41,7 @@ function inferSearchableStrings(value: unknown) {
41
41
 
42
42
  const SelectInputTriggerButtonPropsContext = createContext<{
43
43
  ref?: React.ForwardedRef<HTMLButtonElement>;
44
+ id?: string;
44
45
  onClick?: (event: React.MouseEvent) => void;
45
46
  onKeyDown?: (event: React.KeyboardEvent) => void;
46
47
  [key: string]: unknown;
@@ -125,6 +126,7 @@ function filterSelectInputItems<T>(items: readonly SelectInputItem<T>[], needle:
125
126
  }
126
127
 
127
128
  export interface SelectInputProps<T = string, M extends boolean = false> {
129
+ id?: string;
128
130
  name?: string;
129
131
  multiple?: M;
130
132
  placeholder?: string;
@@ -218,6 +220,7 @@ function SelectInputClearButton({ className, onClick }: SelectInputClearButtonPr
218
220
  const noop = () => {};
219
221
 
220
222
  export function SelectInput<T = string, M extends boolean = false>({
223
+ id,
221
224
  name,
222
225
  multiple,
223
226
  placeholder,
@@ -288,6 +291,7 @@ export function SelectInput<T = string, M extends boolean = false>({
288
291
  // eslint-disable-next-line react/jsx-no-constructed-context-values
289
292
  value={{
290
293
  ref: mergeRefs([ref, triggerRef]),
294
+ id,
291
295
  ...mergeProps(
292
296
  {
293
297
  onClick: () => {
package/src/logo/Logo.js CHANGED
@@ -2,10 +2,10 @@ import classNames from 'classnames';
2
2
  import PropTypes from 'prop-types';
3
3
 
4
4
  import { LogoType } from './logoTypes';
5
- import { LogoFlagInverse } from './svg/flag-inverse.svg';
6
- import { LogoFlag } from './svg/flag.svg';
7
- import { LogoWiseInverse } from './svg/logo-inverse.svg';
8
- import { LogoWise } from './svg/logo.svg';
5
+ import { ReactComponent as LogoFlagInverse } from './svg/flag-inverse.svg';
6
+ import { ReactComponent as LogoFlag } from './svg/flag.svg';
7
+ import { ReactComponent as LogoWiseInverse } from './svg/logo-inverse.svg';
8
+ import { ReactComponent as LogoWise } from './svg/logo.svg';
9
9
 
10
10
  const svgPaths = {
11
11
  WISE: LogoWise,
@@ -1,5 +1,5 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
- import { within, userEvent } from '@storybook/testing-library';
2
+ import { within, userEvent } from '@storybook/test';
3
3
  import { Lock } from '@transferwise/icons';
4
4
  import React, { useState } from 'react';
5
5
 
@@ -150,9 +150,9 @@ export const BalanceCurrencies: Story = {
150
150
 
151
151
  export const OpenedInput: Story = {
152
152
  ...MultipleCurrencies,
153
- play: ({ canvasElement }) => {
153
+ play: async ({ canvasElement }) => {
154
154
  const canvas = within(canvasElement);
155
- userEvent.click(canvas.getByRole('button'));
155
+ await userEvent.click(canvas.getByRole('button'));
156
156
  },
157
157
  };
158
158
 
@@ -5,8 +5,6 @@ import { Component } from 'react';
5
5
  import { injectIntl, WrappedComponentProps } from 'react-intl';
6
6
 
7
7
  import { Typography } from '../common';
8
- import { Key as keyValues } from '../common/key';
9
- import keyCodes from '../common/keyCodes';
10
8
  import { Size, SizeLarge, SizeMedium, SizeSmall } from '../common/propsValues/size';
11
9
  import { Input } from '../inputs/Input';
12
10
  import {
@@ -76,22 +74,20 @@ const parseNumber = ({
76
74
  return Number(amount);
77
75
  };
78
76
 
79
- const inputKeyCodeAllowlist = new Set([
80
- keyCodes.BACKSPACE,
81
- keyCodes.DELETE,
82
- keyCodes.COMMA,
83
- keyCodes.PERIOD,
84
- keyCodes.DOWN,
85
- keyCodes.UP,
86
- keyCodes.LEFT,
87
- keyCodes.RIGHT,
88
- keyCodes.ENTER,
89
- keyCodes.ESCAPE,
90
- keyCodes.TAB,
77
+ const allowedInputKeys = new Set([
78
+ 'Backspace',
79
+ 'Delete',
80
+ ',',
81
+ '.',
82
+ 'ArrowDown',
83
+ 'ArrowUp',
84
+ 'ArrowLeft',
85
+ 'ArrowRight',
86
+ 'Enter',
87
+ 'Escape',
88
+ 'Tab',
91
89
  ]);
92
90
 
93
- const inputKeyAllowlist = new Set([keyValues.PERIOD, keyValues.COMMA]);
94
-
95
91
  export interface MoneyInputProps extends WrappedComponentProps {
96
92
  id?: string;
97
93
  currencies: readonly CurrencyItem[];
@@ -162,16 +158,10 @@ class MoneyInput extends Component<MoneyInputProps, MoneyInputState> {
162
158
  }
163
159
 
164
160
  isInputAllowedForKeyEvent = (event: React.KeyboardEvent<HTMLInputElement>) => {
165
- const { keyCode, metaKey, key, ctrlKey } = event;
161
+ const { metaKey, key, ctrlKey } = event;
166
162
  const isNumberKey = isNumber(parseInt(key, 10));
167
163
 
168
- return (
169
- isNumberKey ||
170
- metaKey ||
171
- ctrlKey ||
172
- inputKeyCodeAllowlist.has(keyCode) ||
173
- inputKeyAllowlist.has(key)
174
- );
164
+ return isNumberKey || metaKey || ctrlKey || allowedInputKeys.has(key);
175
165
  };
176
166
 
177
167
  handleKeyDown: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
@@ -10,7 +10,6 @@ import { Position, getSimpleRandomId } from '../common';
10
10
  import BottomSheet from '../common/bottomSheet';
11
11
  import { stopPropagation } from '../common/domHelpers';
12
12
  import { useLayout } from '../common/hooks';
13
- import KeyCodes from '../common/keyCodes';
14
13
  import Panel from '../common/panel';
15
14
  import Drawer from '../drawer';
16
15
 
@@ -204,17 +203,17 @@ export default function Select({
204
203
  };
205
204
 
206
205
  const handleKeyDown = (event) => {
207
- switch (event.keyCode) {
208
- case KeyCodes.UP:
209
- case KeyCodes.DOWN:
206
+ switch (event.key) {
207
+ case 'ArrowUp':
208
+ case 'ArrowDown':
210
209
  if (open) {
211
- moveFocusWithDifference(event.keyCode === KeyCodes.UP ? -1 : 1);
210
+ moveFocusWithDifference(event.key === 'ArrowUp' ? -1 : 1);
212
211
  } else {
213
212
  setOpen(true);
214
213
  }
215
214
  stopPropagation(event);
216
215
  break;
217
- case KeyCodes.SPACE:
216
+ case ' ':
218
217
  if (event.target !== searchBoxReference.current) {
219
218
  if (open) {
220
219
  selectKeyboardFocusedOption();
@@ -224,7 +223,7 @@ export default function Select({
224
223
  stopPropagation(event);
225
224
  }
226
225
  break;
227
- case KeyCodes.ENTER:
226
+ case 'Enter':
228
227
  if (open) {
229
228
  selectKeyboardFocusedOption();
230
229
  } else {
@@ -232,11 +231,11 @@ export default function Select({
232
231
  }
233
232
  stopPropagation(event);
234
233
  break;
235
- case KeyCodes.ESCAPE:
234
+ case 'Escape':
236
235
  handleCloseOptions();
237
236
  stopPropagation(event);
238
237
  break;
239
- case KeyCodes.TAB:
238
+ case 'Tab':
240
239
  if (open) {
241
240
  selectKeyboardFocusedOption();
242
241
  }
@@ -1,10 +1,11 @@
1
1
  import { action } from '@storybook/addon-actions';
2
2
  import { number } from '@storybook/addon-knobs';
3
+ import { StoryContext } from '@storybook/react';
4
+ import { userEvent, within } from '@storybook/test';
3
5
  import { Mobile, Theme, Switch, Bulb, Info, Coins } from '@transferwise/icons';
4
6
 
5
7
  import Button from '../button';
6
8
  import CheckboxOption from '../checkboxOption/CheckboxOption';
7
- import { within, userEvent } from '../test-utils';
8
9
 
9
10
  import { Snackbar } from './Snackbar';
10
11
  import { SnackbarConsumer } from './SnackbarContext';
@@ -120,7 +121,7 @@ export const basic = () => {
120
121
  );
121
122
  };
122
123
 
123
- basic.play = async ({ canvasElement }) => {
124
+ basic.play = async ({ canvasElement }: StoryContext) => {
124
125
  const canvas = within(canvasElement);
125
- userEvent.click(canvas.getByRole('button'));
126
+ await userEvent.click(canvas.getByRole('button'));
126
127
  };
@@ -1,21 +1,36 @@
1
- import PropTypes from 'prop-types';
2
- import { Component, createRef, RefObject } from 'react';
1
+ import { Component, createRef } from 'react';
3
2
  import { CSSTransition } from 'react-transition-group';
4
3
 
5
4
  import ActionButton from '../actionButton';
6
5
  import Body from '../body';
7
- import { Theme } from '../common';
6
+ import { Theme, ThemeDark, ThemeLight } from '../common';
8
7
  import { DirectionContext } from '../provider/direction';
9
8
  import withNextPortal from '../withNextPortal/withNextPortal';
10
9
 
11
10
  export const CSS_TRANSITION_DURATION = 400;
12
11
 
13
- export class Snackbar extends Component {
14
- /** @type {RefObject<HTMLSpanElement>} */
15
- bodyRef = createRef();
12
+ export interface SnackbarProps {
13
+ action?: {
14
+ label: string;
15
+ onClick?: React.MouseEventHandler<HTMLButtonElement>;
16
+ };
17
+ text: React.ReactNode;
18
+ theme?: ThemeLight | ThemeDark;
19
+ timeout: number;
20
+ timestamp: number;
21
+ }
22
+
23
+ interface SnackbarState extends Pick<SnackbarProps, 'action' | 'text' | 'theme'> {
24
+ visible: boolean;
25
+ }
26
+
27
+ export class Snackbar extends Component<SnackbarProps, SnackbarState> {
28
+ bodyRef = createRef<HTMLSpanElement>();
29
+ timeout = 0;
30
+ transitionTimeout = 0;
16
31
 
17
- constructor() {
18
- super();
32
+ constructor(props: SnackbarProps) {
33
+ super(props);
19
34
  this.state = {
20
35
  visible: false,
21
36
  text: '',
@@ -23,11 +38,11 @@ export class Snackbar extends Component {
23
38
  }
24
39
 
25
40
  componentWillUnmount() {
26
- clearTimeout(this.timeout);
27
- clearTimeout(this.transitionTimeout);
41
+ window.clearTimeout(this.timeout);
42
+ window.clearTimeout(this.transitionTimeout);
28
43
  }
29
44
 
30
- shouldComponentUpdate(nextProps, nextState) {
45
+ shouldComponentUpdate(nextProps: SnackbarProps, nextState: SnackbarState) {
31
46
  if (!nextProps.text) {
32
47
  return false;
33
48
  }
@@ -45,12 +60,12 @@ export class Snackbar extends Component {
45
60
  setLeaveTimeout = () => {
46
61
  const { timeout } = this.props;
47
62
 
48
- this.timeout = setTimeout(() => {
63
+ this.timeout = window.setTimeout(() => {
49
64
  this.setState({ visible: false });
50
65
  }, timeout);
51
66
  };
52
67
 
53
- componentDidUpdate(previousProps) {
68
+ componentDidUpdate(previousProps: SnackbarProps) {
54
69
  const { action, text, theme, timestamp } = this.props;
55
70
 
56
71
  if (!previousProps.text) {
@@ -58,11 +73,11 @@ export class Snackbar extends Component {
58
73
  this.setLeaveTimeout();
59
74
  });
60
75
  } else if (previousProps.timestamp !== timestamp) {
61
- clearTimeout(this.timeout);
76
+ window.clearTimeout(this.timeout);
62
77
 
63
78
  if (this.state.visible) {
64
79
  this.setState({ visible: false }, () => {
65
- this.transitionTimeout = setTimeout(() => {
80
+ this.transitionTimeout = window.setTimeout(() => {
66
81
  this.setState({ visible: true, action, text, theme });
67
82
  this.setLeaveTimeout();
68
83
  }, CSS_TRANSITION_DURATION);
@@ -75,7 +90,7 @@ export class Snackbar extends Component {
75
90
  }
76
91
 
77
92
  render() {
78
- const { action, text, theme, visible } = this.state;
93
+ const { action, text, theme = Theme.LIGHT, visible } = this.state;
79
94
  const { timeout } = this.props;
80
95
 
81
96
  return (
@@ -112,20 +127,4 @@ export class Snackbar extends Component {
112
127
 
113
128
  Snackbar.contextType = DirectionContext;
114
129
 
115
- Snackbar.propTypes = {
116
- action: PropTypes.shape({
117
- label: PropTypes.string.isRequired,
118
- onClick: PropTypes.func,
119
- }),
120
- text: PropTypes.node.isRequired,
121
- theme: PropTypes.oneOf(['light', 'dark']),
122
- timeout: PropTypes.number.isRequired,
123
- timestamp: PropTypes.number.isRequired,
124
- };
125
-
126
- Snackbar.defaultProps = {
127
- action: null,
128
- theme: Theme.LIGHT,
129
- };
130
-
131
130
  export default withNextPortal(Snackbar);
@@ -0,0 +1,11 @@
1
+ import { createContext } from 'react';
2
+
3
+ import { SnackbarProps } from './Snackbar';
4
+
5
+ export type SnackbarContextType = {
6
+ createSnackbar: (props: Omit<SnackbarProps, 'timeout' | 'timestamp'>) => void;
7
+ };
8
+
9
+ export const SnackbarContext = createContext<SnackbarContextType>({ createSnackbar: () => {} });
10
+
11
+ export const SnackbarConsumer = SnackbarContext.Consumer;