@transferwise/components 46.26.2 → 46.28.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 (158) hide show
  1. package/build/i18n/th.json +2 -2
  2. package/build/i18n/zh-CN.json +5 -5
  3. package/build/index.js +410 -645
  4. package/build/index.js.map +1 -1
  5. package/build/index.mjs +411 -646
  6. package/build/index.mjs.map +1 -1
  7. package/build/logo/svg/flag-platform-white.svg +1 -0
  8. package/build/logo/svg/flag-platform.svg +1 -0
  9. package/build/logo/svg/logo-platform-white.svg +1 -0
  10. package/build/logo/svg/logo-platform.svg +1 -0
  11. package/build/main.css +0 -16
  12. package/build/styles/logo/Logo.css +0 -16
  13. package/build/styles/main.css +0 -16
  14. package/build/types/alert/Alert.d.ts +47 -58
  15. package/build/types/alert/Alert.d.ts.map +1 -1
  16. package/build/types/alert/index.d.ts +2 -1
  17. package/build/types/alert/index.d.ts.map +1 -1
  18. package/build/types/button/Button.d.ts +7 -9
  19. package/build/types/button/Button.d.ts.map +1 -1
  20. package/build/types/common/dateUtils/isWithinRange/isWithinRange.d.ts +1 -1
  21. package/build/types/common/dateUtils/isWithinRange/isWithinRange.d.ts.map +1 -1
  22. package/build/types/common/dateUtils/moveToWithinRange/moveToWithinRange.d.ts +1 -1
  23. package/build/types/common/dateUtils/moveToWithinRange/moveToWithinRange.d.ts.map +1 -1
  24. package/build/types/common/propsValues/sentiment.d.ts +0 -1
  25. package/build/types/common/propsValues/sentiment.d.ts.map +1 -1
  26. package/build/types/dateLookup/DateLookup.d.ts +75 -28
  27. package/build/types/dateLookup/DateLookup.d.ts.map +1 -1
  28. package/build/types/dateLookup/DateLookup.messages.d.ts +42 -63
  29. package/build/types/dateLookup/DateLookup.messages.d.ts.map +1 -1
  30. package/build/types/dateLookup/dateHeader/DateHeader.d.ts +9 -22
  31. package/build/types/dateLookup/dateHeader/DateHeader.d.ts.map +1 -1
  32. package/build/types/dateLookup/dateHeader/index.d.ts +1 -1
  33. package/build/types/dateLookup/dateHeader/index.d.ts.map +1 -1
  34. package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts +13 -31
  35. package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts.map +1 -1
  36. package/build/types/dateLookup/dateTrigger/index.d.ts +1 -1
  37. package/build/types/dateLookup/dateTrigger/index.d.ts.map +1 -1
  38. package/build/types/dateLookup/dayCalendar/DayCalendar.d.ts +19 -2
  39. package/build/types/dateLookup/dayCalendar/DayCalendar.d.ts.map +1 -1
  40. package/build/types/dateLookup/dayCalendar/index.d.ts +1 -1
  41. package/build/types/dateLookup/dayCalendar/index.d.ts.map +1 -1
  42. package/build/types/dateLookup/dayCalendar/table/DayCalendarTable.d.ts +12 -2
  43. package/build/types/dateLookup/dayCalendar/table/DayCalendarTable.d.ts.map +1 -1
  44. package/build/types/dateLookup/dayCalendar/table/index.d.ts +1 -1
  45. package/build/types/dateLookup/dayCalendar/table/index.d.ts.map +1 -1
  46. package/build/types/dateLookup/getStartOfDay/getStartOfDay.d.ts +1 -1
  47. package/build/types/dateLookup/getStartOfDay/getStartOfDay.d.ts.map +1 -1
  48. package/build/types/dateLookup/getStartOfDay/index.d.ts +1 -1
  49. package/build/types/dateLookup/getStartOfDay/index.d.ts.map +1 -1
  50. package/build/types/dateLookup/index.d.ts +2 -1
  51. package/build/types/dateLookup/index.d.ts.map +1 -1
  52. package/build/types/dateLookup/monthCalendar/MonthCalendar.d.ts +17 -2
  53. package/build/types/dateLookup/monthCalendar/MonthCalendar.d.ts.map +1 -1
  54. package/build/types/dateLookup/monthCalendar/index.d.ts +1 -1
  55. package/build/types/dateLookup/monthCalendar/index.d.ts.map +1 -1
  56. package/build/types/dateLookup/monthCalendar/table/MonthCalendarTable.d.ts +10 -26
  57. package/build/types/dateLookup/monthCalendar/table/MonthCalendarTable.d.ts.map +1 -1
  58. package/build/types/dateLookup/monthCalendar/table/index.d.ts +1 -1
  59. package/build/types/dateLookup/monthCalendar/table/index.d.ts.map +1 -1
  60. package/build/types/dateLookup/yearCalendar/YearCalendar.d.ts +15 -2
  61. package/build/types/dateLookup/yearCalendar/YearCalendar.d.ts.map +1 -1
  62. package/build/types/dateLookup/yearCalendar/index.d.ts +1 -1
  63. package/build/types/dateLookup/yearCalendar/index.d.ts.map +1 -1
  64. package/build/types/dateLookup/yearCalendar/table/YearCalendarTable.d.ts +10 -26
  65. package/build/types/dateLookup/yearCalendar/table/YearCalendarTable.d.ts.map +1 -1
  66. package/build/types/dateLookup/yearCalendar/table/index.d.ts +1 -1
  67. package/build/types/dateLookup/yearCalendar/table/index.d.ts.map +1 -1
  68. package/build/types/index.d.ts +2 -1
  69. package/build/types/index.d.ts.map +1 -1
  70. package/build/types/inlineAlert/InlineAlert.d.ts +2 -4
  71. package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
  72. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  73. package/build/types/inputs/_BottomSheet.d.ts.map +1 -1
  74. package/build/types/inputs/_Popover.d.ts.map +1 -1
  75. package/build/types/instructionsList/InstructionsList.d.ts +1 -1
  76. package/build/types/instructionsList/InstructionsList.d.ts.map +1 -1
  77. package/build/types/logo/Logo.d.ts +1 -1
  78. package/build/types/logo/Logo.d.ts.map +1 -1
  79. package/build/types/logo/logoTypes.d.ts +2 -1
  80. package/build/types/logo/logoTypes.d.ts.map +1 -1
  81. package/build/types/markdown/Markdown.d.ts +1 -0
  82. package/build/types/markdown/Markdown.d.ts.map +1 -1
  83. package/build/types/statusIcon/StatusIcon.d.ts +1 -1
  84. package/build/types/statusIcon/StatusIcon.d.ts.map +1 -1
  85. package/package.json +1 -1
  86. package/src/alert/{Alert.spec.js → Alert.spec.tsx} +43 -40
  87. package/src/alert/Alert.story.tsx +1 -2
  88. package/src/alert/Alert.tsx +218 -0
  89. package/src/alert/index.ts +2 -0
  90. package/src/button/Button.tsx +6 -10
  91. package/src/common/dateUtils/isWithinRange/isWithinRange.spec.ts +21 -0
  92. package/src/common/dateUtils/isWithinRange/isWithinRange.ts +2 -2
  93. package/src/common/dateUtils/moveToWithinRange/moveToWithinRange.ts +8 -4
  94. package/src/common/propsValues/sentiment.ts +0 -10
  95. package/src/dateLookup/DateLookup.state.spec.js +7 -0
  96. package/src/dateLookup/{DateLookup.story.js → DateLookup.story.tsx} +13 -14
  97. package/src/dateLookup/DateLookup.tests.story.tsx +70 -0
  98. package/src/dateLookup/{DateLookup.js → DateLookup.tsx} +115 -81
  99. package/src/dateLookup/dateHeader/{DateHeader.js → DateHeader.tsx} +15 -15
  100. package/src/dateLookup/dateTrigger/DateTrigger.spec.js +0 -22
  101. package/src/dateLookup/dateTrigger/{DateTrigger.js → DateTrigger.tsx} +15 -32
  102. package/src/dateLookup/dayCalendar/{DayCalendar.js → DayCalendar.tsx} +14 -21
  103. package/src/dateLookup/dayCalendar/table/{DayCalendarTable.js → DayCalendarTable.tsx} +26 -37
  104. package/src/dateLookup/getStartOfDay/{getStartOfDay.js → getStartOfDay.tsx} +1 -1
  105. package/src/dateLookup/index.ts +2 -0
  106. package/src/dateLookup/monthCalendar/{MonthCalendar.js → MonthCalendar.tsx} +19 -22
  107. package/src/dateLookup/monthCalendar/table/{MonthCalendarTable.js → MonthCalendarTable.tsx} +31 -30
  108. package/src/dateLookup/yearCalendar/{YearCalendar.js → YearCalendar.tsx} +18 -21
  109. package/src/dateLookup/yearCalendar/table/{YearCalendarTable.js → YearCalendarTable.tsx} +26 -28
  110. package/src/i18n/th.json +2 -2
  111. package/src/i18n/zh-CN.json +5 -5
  112. package/src/index.ts +2 -1
  113. package/src/inlineAlert/InlineAlert.spec.tsx +0 -7
  114. package/src/inlineAlert/InlineAlert.tsx +19 -47
  115. package/src/inputs/InputGroup.tsx +3 -3
  116. package/src/inputs/SelectInput.tsx +1 -0
  117. package/src/inputs/_BottomSheet.tsx +44 -54
  118. package/src/inputs/_Popover.tsx +20 -23
  119. package/src/instructionsList/InstructionsList.spec.tsx +5 -0
  120. package/src/instructionsList/InstructionsList.story.tsx +1 -0
  121. package/src/instructionsList/InstructionsList.tsx +3 -2
  122. package/src/logo/Logo.css +0 -16
  123. package/src/logo/Logo.js +27 -5
  124. package/src/logo/Logo.less +0 -16
  125. package/src/logo/Logo.spec.js +15 -1
  126. package/src/logo/__snapshots__/Logo.spec.js.snap +45 -71
  127. package/src/logo/logoTypes.ts +1 -0
  128. package/src/logo/svg/flag-platform-white.svg +1 -0
  129. package/src/logo/svg/flag-platform.svg +1 -0
  130. package/src/logo/svg/logo-platform-white.svg +1 -0
  131. package/src/logo/svg/logo-platform.svg +1 -0
  132. package/src/main.css +0 -16
  133. package/src/markdown/Markdown.spec.tsx +16 -0
  134. package/src/markdown/Markdown.tsx +6 -1
  135. package/src/statusIcon/StatusIcon.tsx +14 -14
  136. package/build/types/alert/withArrow/alertArrowPositions.d.ts +0 -9
  137. package/build/types/alert/withArrow/alertArrowPositions.d.ts.map +0 -1
  138. package/build/types/alert/withArrow/index.d.ts +0 -3
  139. package/build/types/alert/withArrow/index.d.ts.map +0 -1
  140. package/build/types/alert/withArrow/withArrow.d.ts +0 -11
  141. package/build/types/alert/withArrow/withArrow.d.ts.map +0 -1
  142. package/src/alert/Alert.js +0 -196
  143. package/src/alert/index.js +0 -1
  144. package/src/alert/withArrow/alertArrowPositions.ts +0 -9
  145. package/src/alert/withArrow/index.js +0 -2
  146. package/src/alert/withArrow/withArrow.js +0 -50
  147. package/src/alert/withArrow/withArrow.spec.js +0 -51
  148. package/src/dateLookup/index.js +0 -1
  149. /package/src/dateLookup/{DateLookup.messages.js → DateLookup.messages.ts} +0 -0
  150. /package/src/dateLookup/dateHeader/{index.js → index.ts} +0 -0
  151. /package/src/dateLookup/dateTrigger/{index.js → index.ts} +0 -0
  152. /package/src/dateLookup/dayCalendar/{index.js → index.ts} +0 -0
  153. /package/src/dateLookup/dayCalendar/table/{index.js → index.ts} +0 -0
  154. /package/src/dateLookup/getStartOfDay/{index.js → index.ts} +0 -0
  155. /package/src/dateLookup/monthCalendar/{index.js → index.ts} +0 -0
  156. /package/src/dateLookup/monthCalendar/table/{index.js → index.ts} +0 -0
  157. /package/src/dateLookup/yearCalendar/{index.js → index.ts} +0 -0
  158. /package/src/dateLookup/yearCalendar/table/{index.js → index.ts} +0 -0
@@ -1,22 +1,15 @@
1
1
  /* eslint-disable no-console */
2
- import {
3
- InfoCircle,
4
- CheckCircle,
5
- AlertCircle,
6
- Warning as AlertIcon,
7
- HappyEmoji,
8
- } from '@transferwise/icons';
2
+ import { HappyEmoji } from '@transferwise/icons';
9
3
  import { ThemeProvider } from '@wise/components-theming';
10
4
  import React from 'react';
11
5
 
12
6
  import { Sentiment, Size, Theme, Variant } from '../common';
13
7
  import { render, cleanup, screen, userEvent, fireEvent } from '../test-utils';
14
8
 
15
- import Alert from './Alert';
16
- import { AlertArrowPosition } from './withArrow';
9
+ import Alert, { AlertAction, AlertArrowPosition, AlertType } from './Alert';
17
10
 
18
11
  jest.mock('react', () => {
19
- const originReact = jest.requireActual('react');
12
+ const originReact = jest.requireActual<typeof import('react')>('react');
20
13
  const mUseReference = jest.fn();
21
14
  return {
22
15
  ...originReact,
@@ -25,20 +18,18 @@ jest.mock('react', () => {
25
18
  });
26
19
 
27
20
  describe('Alert', () => {
28
- let component;
29
- let container;
30
- let alert;
31
- let closeButton;
32
- let action;
21
+ let component: HTMLElement;
22
+ let container: HTMLElement;
23
+ let alert: HTMLElement;
24
+ let closeButton: HTMLElement;
25
+ let action: AlertAction;
33
26
 
34
- const renderIcon = (Icon) => render(<Icon size={24} />).container.innerHTML;
35
-
36
- const classForType = (type) => `alert-${type}`;
27
+ const classForType = (type: AlertType) => `alert-${type}`;
37
28
 
38
29
  const message = 'Your card is on its way.';
39
30
 
40
31
  const origWarn = console.warn;
41
- let mockedWarn;
32
+ let mockedWarn: jest.Mock;
42
33
 
43
34
  beforeAll(() => {
44
35
  mockedWarn = jest.fn();
@@ -56,11 +47,11 @@ describe('Alert', () => {
56
47
 
57
48
  describe('defaults', () => {
58
49
  beforeEach(() => {
59
- ({ container } = render(
50
+ container = render(
60
51
  <ThemeProvider theme={Theme.LIGHT}>
61
52
  <Alert message={message} />
62
53
  </ThemeProvider>,
63
- ));
54
+ ).container;
64
55
  component = screen.getByTestId('alert');
65
56
  });
66
57
 
@@ -70,7 +61,7 @@ describe('Alert', () => {
70
61
 
71
62
  it('will be of type neutral', () => {
72
63
  expect(component).toHaveClass(classForType(Sentiment.NEUTRAL));
73
- expect(screen.getByTestId('info-circle-icon')).toBeInTheDocument();
64
+ expect(screen.getByTestId('info-icon')).toBeInTheDocument();
74
65
  });
75
66
 
76
67
  it('is not dismissible', () => {
@@ -90,7 +81,9 @@ describe('Alert', () => {
90
81
  expect(component).toHaveClass('arrow');
91
82
  expect(component).toHaveClass('arrow-bottom');
92
83
  expect(mockedWarn).toHaveBeenCalledWith(
93
- expect.stringContaining('Alert has deprecated the use of arrow.'),
84
+ expect.stringContaining(
85
+ "Alert component doesn't support 'arrow' anymore, use 'InlineAlert' instead.",
86
+ ),
94
87
  );
95
88
  });
96
89
 
@@ -99,7 +92,9 @@ describe('Alert', () => {
99
92
 
100
93
  expect(screen.getByText(message)).toBeInTheDocument();
101
94
  expect(mockedWarn).toHaveBeenCalledWith(
102
- expect.stringContaining('Alert has deprecated the use of children.'),
95
+ expect.stringContaining(
96
+ "Alert component doesn't support 'children' anymore, use 'message' instead.",
97
+ ),
103
98
  );
104
99
  });
105
100
 
@@ -108,7 +103,9 @@ describe('Alert', () => {
108
103
 
109
104
  expect(container.querySelector('button')).not.toBeInTheDocument();
110
105
  expect(mockedWarn).toHaveBeenCalledWith(
111
- expect.stringContaining('Alert has deprecated the use of dismissible.'),
106
+ expect.stringContaining(
107
+ "Alert component doesn't support 'dismissible' anymore, use 'onDismiss' instead.",
108
+ ),
112
109
  );
113
110
  });
114
111
 
@@ -118,7 +115,9 @@ describe('Alert', () => {
118
115
 
119
116
  expect(small.innerHTML).toStrictEqual(large.innerHTML);
120
117
  expect(mockedWarn).toHaveBeenCalledWith(
121
- expect.stringContaining('Alert no longer supports any possible variations in size'),
118
+ expect.stringContaining(
119
+ "Alert component doesn't support 'size' anymore, please remove that prop.",
120
+ ),
122
121
  );
123
122
  });
124
123
 
@@ -128,9 +127,9 @@ describe('Alert', () => {
128
127
  const success = screen.getByTestId('alert');
129
128
 
130
129
  expect(success).toHaveClass(classForType(Sentiment.POSITIVE));
131
- expect(screen.getByTestId('check-circle-icon')).toBeInTheDocument();
130
+ expect(screen.getByTestId('check-icon')).toBeInTheDocument();
132
131
  expect(mockedWarn).toHaveBeenCalledWith(
133
- 'Alert has deprecated the success value for the `type` prop. Please update to Sentiment.POSITIVE.',
132
+ "Alert component has deprecated 'success' value for the 'type' prop. Please use 'positive' instead.",
134
133
  );
135
134
  });
136
135
 
@@ -140,9 +139,9 @@ describe('Alert', () => {
140
139
  const info = screen.getByTestId('alert');
141
140
 
142
141
  expect(info).toHaveClass(classForType(Sentiment.NEUTRAL));
143
- expect(screen.getByTestId('info-circle-icon')).toBeInTheDocument();
142
+ expect(screen.getByTestId('info-icon')).toBeInTheDocument();
144
143
  expect(mockedWarn).toHaveBeenCalledWith(
145
- 'Alert has deprecated the info value for the `type` prop. Please update to Sentiment.NEUTRAL.',
144
+ "Alert component has deprecated 'info' value for the 'type' prop. Please use 'neutral' instead.",
146
145
  );
147
146
  });
148
147
  });
@@ -153,9 +152,11 @@ describe('Alert', () => {
153
152
  const error = screen.getByTestId('alert');
154
153
 
155
154
  expect(error).toHaveClass(classForType(Sentiment.NEGATIVE));
156
- expect(screen.getByTestId('cross-circle-icon')).toBeInTheDocument();
155
+ expect(screen.getByTestId('cross-icon')).toBeInTheDocument();
157
156
  expect(mockedWarn).toHaveBeenCalledWith(
158
- expect.stringContaining('Alert has deprecated the error value for the `type` prop.'),
157
+ expect.stringContaining(
158
+ "Alert component has deprecated 'error' value for the 'type' prop. Please use 'negative' instead.",
159
+ ),
159
160
  );
160
161
  });
161
162
 
@@ -167,7 +168,7 @@ describe('Alert', () => {
167
168
  };
168
169
  render(<Alert action={action} message={message} />);
169
170
 
170
- const element = screen.getByText(action.text);
171
+ const element = screen.getByText(action.text?.toString() ?? '');
171
172
 
172
173
  expect(element).toHaveAttribute('href', action.href);
173
174
  expect(element).not.toHaveAttribute('aria-label');
@@ -183,7 +184,7 @@ describe('Alert', () => {
183
184
  };
184
185
  render(<Alert action={action} message={message} />);
185
186
 
186
- const element = screen.getByText(action.text);
187
+ const element = screen.getByText(action.text?.toString() ?? '');
187
188
 
188
189
  expect(element).toHaveAttribute('aria-label', action['aria-label']);
189
190
  expect(element).toHaveAttribute('target', action.target);
@@ -245,7 +246,7 @@ describe('Alert', () => {
245
246
  });
246
247
 
247
248
  describe('types', () => {
248
- const getComponentWithType = (type) => {
249
+ const getComponentWithType = (type: AlertType) => {
249
250
  render(<Alert type={type} message={message} />);
250
251
  return screen.getByTestId('alert');
251
252
  };
@@ -254,28 +255,28 @@ describe('Alert', () => {
254
255
  component = getComponentWithType(Sentiment.NEUTRAL);
255
256
 
256
257
  expect(component).toHaveClass(classForType(Sentiment.NEUTRAL));
257
- expect(screen.getByTestId('info-circle-icon')).toBeInTheDocument();
258
+ expect(screen.getByTestId('info-icon')).toBeInTheDocument();
258
259
  });
259
260
 
260
261
  it('renders positive', () => {
261
262
  component = getComponentWithType(Sentiment.POSITIVE);
262
263
 
263
264
  expect(component).toHaveClass(classForType(Sentiment.POSITIVE));
264
- expect(screen.getByTestId('check-circle-icon')).toBeInTheDocument();
265
+ expect(screen.getByTestId('check-icon')).toBeInTheDocument();
265
266
  });
266
267
 
267
268
  it('renders negative', () => {
268
269
  component = getComponentWithType(Sentiment.NEGATIVE);
269
270
 
270
271
  expect(component).toHaveClass(classForType(Sentiment.NEGATIVE));
271
- expect(screen.getByTestId('cross-circle-icon')).toBeInTheDocument();
272
+ expect(screen.getByTestId('cross-icon')).toBeInTheDocument();
272
273
  });
273
274
 
274
275
  it('renders warning', () => {
275
276
  component = getComponentWithType(Sentiment.WARNING);
276
277
 
277
278
  expect(component).toHaveClass(classForType(Sentiment.WARNING));
278
- expect(screen.getByTestId('warning-icon')).toBeInTheDocument();
279
+ expect(screen.getByTestId('alert-icon')).toBeInTheDocument();
279
280
  });
280
281
 
281
282
  it('renders error alerts with aria-role alert', () => {
@@ -292,9 +293,11 @@ describe('Alert', () => {
292
293
  const { location } = window;
293
294
 
294
295
  beforeAll(() => {
295
- delete window.location;
296
296
  jest.spyOn(window, 'open').mockImplementation();
297
+ // @ts-expect-error value gets set right after its deletion
298
+ delete window.location;
297
299
  window.location = {
300
+ ...window.location,
298
301
  assign: jest.fn(),
299
302
  };
300
303
  });
@@ -4,8 +4,7 @@ import { ClockBorderless } from '@transferwise/icons';
4
4
 
5
5
  import { Sentiment } from '../common';
6
6
 
7
- import Alert from './Alert';
8
- import { AlertArrowPosition } from './withArrow';
7
+ import Alert, { AlertArrowPosition } from './Alert';
9
8
 
10
9
  export default {
11
10
  component: Alert,
@@ -0,0 +1,218 @@
1
+ import classNames from 'classnames';
2
+ import { useState, useRef, useEffect } from 'react';
3
+
4
+ import Body from '../body/Body';
5
+ import { Sentiment, Size, Typography, Variant } from '../common';
6
+ import { CloseButton } from '../common/closeButton';
7
+ import Link from '../link';
8
+ import StatusIcon from '../statusIcon';
9
+ import Title from '../title/Title';
10
+ import { logActionRequired } from '../utilities';
11
+
12
+ import InlineMarkdown from './inlineMarkdown';
13
+
14
+ export type AlertAction = {
15
+ 'aria-label'?: string;
16
+ href: string;
17
+ target?: string;
18
+ text: React.ReactNode;
19
+ };
20
+
21
+ /** @deprecated Use `"top" | "bottom"` instead. */
22
+ type AlertTypeDeprecated = `${Sentiment.SUCCESS | Sentiment.INFO | Sentiment.ERROR}`;
23
+ type AlertTypeResolved = `${
24
+ | Sentiment.POSITIVE
25
+ | Sentiment.NEUTRAL
26
+ | Sentiment.WARNING
27
+ | Sentiment.NEGATIVE}`;
28
+ export type AlertType = AlertTypeResolved | AlertTypeDeprecated;
29
+
30
+ export enum AlertArrowPosition {
31
+ TOP_LEFT = 'up-left',
32
+ TOP = 'up-center',
33
+ TOP_RIGHT = 'up-right',
34
+ BOTTOM_LEFT = 'down-left',
35
+ BOTTOM = 'down-center',
36
+ BOTTOM_RIGHT = 'down-right',
37
+ }
38
+
39
+ export interface AlertProps {
40
+ /** An optional call to action to sit under the main body of the alert. If your label is short, use aria-label to provide more context */
41
+ action?: AlertAction;
42
+ className?: string;
43
+ /** An optional icon. If not provided, we will default the icon to something appropriate for the type */
44
+ icon?: React.ReactElement;
45
+ /** Title for the alert component */
46
+ title?: string;
47
+ /** The main body of the alert. Accepts plain text and bold words specified with **double stars*/
48
+ message?: string;
49
+ /** The presence of the onDismiss handler will trigger the visibility of the close button */
50
+ onDismiss?: React.MouseEventHandler<HTMLButtonElement>;
51
+ /** The type dictates which icon and colour will be used */
52
+ type?: AlertType;
53
+ variant?: `${Variant}`;
54
+ /** @deprecated Use `InlineAlert` instead. */
55
+ arrow?: `${AlertArrowPosition}`;
56
+ /** @deprecated Use `message` instead. Be aware `message` only accepts plain text or text with **bold** markdown. */
57
+ children?: React.ReactNode;
58
+ /** @deprecated Use `onDismiss` instead. */
59
+ dismissible?: boolean;
60
+ /** @deprecated Alert component doesn't support `size` anymore, please remove this prop. */
61
+ size?: `${Size}`;
62
+ }
63
+
64
+ function resolveType(type: AlertType): AlertTypeResolved {
65
+ switch (type) {
66
+ case 'success':
67
+ return 'positive';
68
+ case 'info':
69
+ return 'neutral';
70
+ case 'error':
71
+ return 'negative';
72
+ }
73
+ return type;
74
+ }
75
+
76
+ export default function Alert({
77
+ arrow,
78
+ action,
79
+ children,
80
+ className,
81
+ dismissible,
82
+ icon,
83
+ onDismiss,
84
+ message,
85
+ size,
86
+ title,
87
+ type = 'neutral',
88
+ variant = 'desktop',
89
+ }: AlertProps) {
90
+ useEffect(() => {
91
+ if (arrow !== undefined) {
92
+ logActionRequired(
93
+ "Alert component doesn't support 'arrow' anymore, use 'InlineAlert' instead.",
94
+ );
95
+ }
96
+ }, [arrow]);
97
+
98
+ useEffect(() => {
99
+ if (children !== undefined) {
100
+ logActionRequired(
101
+ "Alert component doesn't support 'children' anymore, use 'message' instead.",
102
+ );
103
+ }
104
+ }, [children]);
105
+
106
+ useEffect(() => {
107
+ if (dismissible !== undefined) {
108
+ logActionRequired(
109
+ "Alert component doesn't support 'dismissible' anymore, use 'onDismiss' instead.",
110
+ );
111
+ }
112
+ }, [dismissible]);
113
+
114
+ useEffect(() => {
115
+ if (size !== undefined) {
116
+ logActionRequired("Alert component doesn't support 'size' anymore, please remove that prop.");
117
+ }
118
+ }, [size]);
119
+
120
+ const resolvedType = resolveType(type);
121
+ useEffect(() => {
122
+ if (resolvedType !== type) {
123
+ logActionRequired(
124
+ `Alert component has deprecated '${type}' value for the 'type' prop. Please use '${resolvedType}' instead.`,
125
+ );
126
+ }
127
+ }, [resolvedType, type]);
128
+
129
+ const [shouldFire, setShouldFire] = useState(false);
130
+
131
+ const closeButtonReference = useRef<HTMLButtonElement>(null);
132
+
133
+ return (
134
+ <div
135
+ className={classNames(
136
+ 'alert d-flex',
137
+ `alert-${resolvedType}`,
138
+ arrow != null && alertArrowClassNames(arrow),
139
+ className,
140
+ )}
141
+ data-testid="alert"
142
+ onTouchStart={() => setShouldFire(true)}
143
+ onTouchEnd={(event) => {
144
+ if (
145
+ shouldFire &&
146
+ action &&
147
+ // Check if current event is triggered from closeButton
148
+ event.target instanceof Node &&
149
+ closeButtonReference.current &&
150
+ !closeButtonReference.current.contains(event.target)
151
+ ) {
152
+ if (action.target === '_blank') {
153
+ window.top?.open(action.href);
154
+ } else {
155
+ window.top?.location.assign(action.href);
156
+ }
157
+ }
158
+ setShouldFire(false);
159
+ }}
160
+ onTouchMove={() => setShouldFire(false)}
161
+ >
162
+ <div
163
+ className={classNames('alert__content', 'd-flex', 'flex-grow-1', variant)}
164
+ data-testid={variant}
165
+ >
166
+ {icon ? (
167
+ <div className="alert__icon">{icon}</div>
168
+ ) : (
169
+ <StatusIcon size={Size.LARGE} sentiment={resolvedType} />
170
+ )}
171
+ <div className="alert__message">
172
+ <div role={Sentiment.NEGATIVE === resolvedType ? 'alert' : 'status'}>
173
+ {title && (
174
+ <Title className="m-b-1" type={Typography.TITLE_BODY}>
175
+ {title}
176
+ </Title>
177
+ )}
178
+ <Body as="span" className="d-block" type={Typography.BODY_LARGE}>
179
+ {children || <InlineMarkdown>{message}</InlineMarkdown>}
180
+ </Body>
181
+ </div>
182
+ {action && (
183
+ <Link
184
+ href={action.href}
185
+ className="m-t-1"
186
+ aria-label={action['aria-label']}
187
+ target={action.target}
188
+ type={Typography.LINK_LARGE}
189
+ >
190
+ {action.text}
191
+ </Link>
192
+ )}
193
+ </div>
194
+ </div>
195
+ {onDismiss && (
196
+ <CloseButton ref={closeButtonReference} className="m-l-2" onClick={onDismiss} />
197
+ )}
198
+ </div>
199
+ );
200
+ }
201
+
202
+ function alertArrowClassNames(arrow: `${AlertArrowPosition}`) {
203
+ switch (arrow) {
204
+ case 'down-center':
205
+ return 'arrow arrow-bottom arrow-center';
206
+ case 'down-left':
207
+ return 'arrow arrow-bottom arrow-left';
208
+ case 'down-right':
209
+ return 'arrow arrow-bottom arrow-right';
210
+ case 'up-center':
211
+ return 'arrow arrow-center';
212
+ case 'up-right':
213
+ return 'arrow arrow-right';
214
+ case 'up-left':
215
+ default:
216
+ return 'arrow';
217
+ }
218
+ }
@@ -0,0 +1,2 @@
1
+ export { default } from './Alert';
2
+ export type { AlertProps, AlertAction, AlertArrowPosition, AlertType } from './Alert';
@@ -16,7 +16,6 @@ import {
16
16
  SizeSmall,
17
17
  SizeMedium,
18
18
  SizeLarge,
19
- LinkProps,
20
19
  } from '../common';
21
20
  import ProcessIndicator from '../processIndicator';
22
21
 
@@ -30,8 +29,7 @@ type DeprecatedTypes = 'primary' | 'pay' | 'secondary' | 'danger' | 'link';
30
29
  /** @deprecated */
31
30
  type DeprecatedSizes = SizeExtraSmall;
32
31
 
33
- export type CommonProps = {
34
- as?: ElementType;
32
+ type CommonProps = {
35
33
  block?: boolean;
36
34
  disabled?: boolean;
37
35
  loading?: boolean;
@@ -40,22 +38,20 @@ export type CommonProps = {
40
38
  size?: SizeSmall | SizeMedium | SizeLarge | DeprecatedSizes;
41
39
  };
42
40
 
43
- export type ButtonProps = CommonProps &
44
- Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type'> & {
41
+ type ButtonProps = CommonProps &
42
+ Omit<React.ComponentPropsWithRef<'button'>, 'type'> & {
45
43
  as?: 'button';
46
44
  htmlType?: 'submit' | 'reset' | 'button';
47
45
  };
48
46
 
49
- export type AnchorProps = CommonProps &
50
- AnchorHTMLAttributes<HTMLAnchorElement> &
51
- Omit<LinkProps, 'href'> & {
47
+ type AnchorProps = CommonProps &
48
+ React.ComponentPropsWithRef<'a'> & {
52
49
  as?: 'a';
53
- href?: string; // Optional due to usage of component with next/link's passHref behavior
54
50
  };
55
51
 
56
52
  export type Props = ButtonProps | AnchorProps;
57
53
 
58
- export type ButtonReferenceType = HTMLButtonElement | HTMLAnchorElement;
54
+ type ButtonReferenceType = HTMLButtonElement | HTMLAnchorElement;
59
55
 
60
56
  const Button = forwardRef<ButtonReferenceType, Props>(
61
57
  (
@@ -22,10 +22,31 @@ describe('isWithinRange', () => {
22
22
  expect(isWithinRange(date, min, max)).toBe(false);
23
23
  });
24
24
 
25
+ it('returns false when min > date and max is undefined', () => {
26
+ const date = new Date('1995-12-20');
27
+ const min = new Date('1995-12-21');
28
+ const max = null;
29
+ expect(isWithinRange(date, min, max)).toBe(false);
30
+ });
31
+
25
32
  it('returns false when date > max', () => {
26
33
  const date = new Date('1995-12-23');
27
34
  const min = new Date('1995-12-20');
28
35
  const max = new Date('1995-12-22');
29
36
  expect(isWithinRange(date, min, max)).toBe(false);
30
37
  });
38
+
39
+ it('returns false when date > max and min is undefined', () => {
40
+ const date = new Date('1995-12-23');
41
+ const min = null;
42
+ const max = new Date('1995-12-22');
43
+ expect(isWithinRange(date, min, max)).toBe(false);
44
+ });
45
+
46
+ it('returns true when min and max are undefined', () => {
47
+ const date = new Date('1995-12-23');
48
+ const min = null;
49
+ const max = null;
50
+ expect(isWithinRange(date, min, max)).toBe(true);
51
+ });
31
52
  });
@@ -1,3 +1,3 @@
1
- export function isWithinRange(date: Date, min: Date, max: Date) {
2
- return !date || ((!min || date >= min) && (!max || date <= max));
1
+ export function isWithinRange(date: Date, min: Date | null, max: Date | null) {
2
+ return (!min || date >= min) && (!max || date <= max);
3
3
  }
@@ -1,6 +1,10 @@
1
- import { isWithinRange } from '..';
2
-
3
1
  // Makes sure that date is between min and max dates, returns a cloned min or max
4
- export function moveToWithinRange(date: Date, min: Date, max: Date) {
5
- return isWithinRange(date, min, max) ? date : new Date(min && date < min ? +min : +max);
2
+ export function moveToWithinRange(date: Date, min: Date | null, max: Date | null) {
3
+ if (min && date < min) {
4
+ return new Date(min);
5
+ }
6
+ if (max && date > max) {
7
+ return new Date(max);
8
+ }
9
+ return date;
6
10
  }
@@ -18,13 +18,3 @@ export enum Sentiment {
18
18
  */
19
19
  SUCCESS = 'success',
20
20
  }
21
-
22
- export type SentimentString =
23
- | 'negative'
24
- | 'neutral'
25
- | 'positive'
26
- | 'warning'
27
- | 'pending'
28
- | 'info'
29
- | 'error'
30
- | 'success';
@@ -66,4 +66,11 @@ describe('DateLookup state', () => {
66
66
  DateLookup.getDerivedStateFromProps(props, defaultState);
67
67
  expect(onChange).toHaveBeenCalledWith(new Date(2018, 10, 1));
68
68
  });
69
+
70
+ it('updates viewMonth and viewYear to date within min/max on Clear', () => {
71
+ const props = { value: null, min: new Date(2018, 11, 26), max: new Date(2018, 11, 26) };
72
+ const newState = DateLookup.getDerivedStateFromProps(props, defaultState);
73
+ expect(newState.viewMonth).toBe(11);
74
+ expect(newState.viewYear).toBe(2018);
75
+ });
69
76
  });
@@ -1,5 +1,5 @@
1
- import { boolean, select, date, text } from '@storybook/addon-knobs';
2
- import { userEvent, within } from '@storybook/test';
1
+ import { boolean, select, text, date } from '@storybook/addon-knobs';
2
+ import { userEvent } from '@storybook/test';
3
3
  import { useState } from 'react';
4
4
 
5
5
  import { Size } from '../common';
@@ -13,21 +13,21 @@ export default {
13
13
  };
14
14
 
15
15
  const epoch = new Date('2011-01-01');
16
- let theFuture = new Date(epoch);
16
+ const theFuture = new Date(epoch);
17
17
  theFuture.setUTCDate(epoch.getUTCDate() + 10);
18
- let thePast = new Date(epoch);
18
+ const thePast = new Date(epoch);
19
19
  thePast.setUTCDate(epoch.getUTCDate() - 10);
20
+ const size = select('size', Object.values([Size.SMALL, Size.MEDIUM, Size.LARGE]), Size.MEDIUM);
20
21
 
21
22
  export const Basic = () => {
22
- const [value, setValue] = useState(epoch);
23
+ const [value, setValue] = useState<Date | null>(epoch);
23
24
  const disabled = boolean('disabled', false);
24
25
  const label = text('label', 'label');
25
- const monthFormat = select('monthFormat', ['long', 'short']);
26
+ const monthFormat = select('monthFormat', ['long', 'short'], 'long');
26
27
  const placeholder = text('placeholder', 'placeholder');
27
- const size = select('size', Object.values(Size), Size.MEDIUM);
28
28
  const id = text('id', 'date-lookup');
29
29
 
30
- const clearable = boolean('clearable', false);
30
+ const clearable = boolean('clearable', true);
31
31
 
32
32
  return (
33
33
  <DateLookup
@@ -35,6 +35,7 @@ export const Basic = () => {
35
35
  id={id}
36
36
  label={label}
37
37
  min={thePast}
38
+ max={theFuture}
38
39
  monthFormat={monthFormat}
39
40
  placeholder={placeholder}
40
41
  size={size}
@@ -48,10 +49,9 @@ export const Basic = () => {
48
49
  );
49
50
  };
50
51
 
51
- Basic.play = async ({ canvasElement }) => {
52
+ Basic.play = async ({ canvasElement }: { canvasElement: HTMLElement }) => {
52
53
  // testing focus state on keyboard nav
53
- const canvas = within(canvasElement);
54
- await userEvent.tab(canvas.getByRole('button'));
54
+ await userEvent.tab();
55
55
  await userEvent.keyboard(' ');
56
56
  };
57
57
 
@@ -61,12 +61,11 @@ export const Basic400Zoom = storyConfig(
61
61
  );
62
62
 
63
63
  export const RightAligned = () => {
64
- const [value, setValue] = useState(epoch);
64
+ const [value, setValue] = useState<Date | null>(epoch);
65
65
  const disabled = boolean('disabled', false);
66
66
  const label = text('label', 'label');
67
- const monthFormat = select('monthFormat', ['long', 'short']);
67
+ const monthFormat = select('monthFormat', ['long', 'short'], 'long');
68
68
  const placeholder = text('placeholder', 'placeholder');
69
- const size = select('size', Object.values(Size), Size.MEDIUM);
70
69
 
71
70
  const minvalue = date('minvalue', thePast);
72
71
  const maxvalue = date('maxvalue', theFuture);