@transferwise/components 46.92.0 → 46.93.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/build/actionOption/ActionOption.js +2 -0
  2. package/build/actionOption/ActionOption.js.map +1 -1
  3. package/build/actionOption/ActionOption.mjs +2 -0
  4. package/build/actionOption/ActionOption.mjs.map +1 -1
  5. package/build/common/Option/Option.js +9 -4
  6. package/build/common/Option/Option.js.map +1 -1
  7. package/build/common/Option/Option.mjs +10 -5
  8. package/build/common/Option/Option.mjs.map +1 -1
  9. package/build/dateLookup/dateTrigger/DateTrigger.js +11 -6
  10. package/build/dateLookup/dateTrigger/DateTrigger.js.map +1 -1
  11. package/build/dateLookup/dateTrigger/DateTrigger.mjs +11 -6
  12. package/build/dateLookup/dateTrigger/DateTrigger.mjs.map +1 -1
  13. package/build/definitionList/DefinitionList.js +5 -3
  14. package/build/definitionList/DefinitionList.js.map +1 -1
  15. package/build/definitionList/DefinitionList.mjs +5 -3
  16. package/build/definitionList/DefinitionList.mjs.map +1 -1
  17. package/build/main.css +23 -32
  18. package/build/snackbar/Snackbar.js +4 -2
  19. package/build/snackbar/Snackbar.js.map +1 -1
  20. package/build/snackbar/Snackbar.mjs +4 -2
  21. package/build/snackbar/Snackbar.mjs.map +1 -1
  22. package/build/styles/common/Option/Option.css +22 -0
  23. package/build/styles/dateLookup/dateTrigger/DateTrigger.css +1 -32
  24. package/build/styles/main.css +23 -32
  25. package/build/typeahead/typeaheadInput/TypeaheadInput.js +4 -1
  26. package/build/typeahead/typeaheadInput/TypeaheadInput.js.map +1 -1
  27. package/build/typeahead/typeaheadInput/TypeaheadInput.mjs +4 -1
  28. package/build/typeahead/typeaheadInput/TypeaheadInput.mjs.map +1 -1
  29. package/build/types/actionOption/ActionOption.d.ts +2 -1
  30. package/build/types/actionOption/ActionOption.d.ts.map +1 -1
  31. package/build/types/common/Option/Option.d.ts +1 -0
  32. package/build/types/common/Option/Option.d.ts.map +1 -1
  33. package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts.map +1 -1
  34. package/build/types/definitionList/DefinitionList.d.ts.map +1 -1
  35. package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts.map +1 -1
  36. package/package.json +1 -1
  37. package/src/actionOption/ActionOption.story.tsx +2 -0
  38. package/src/actionOption/ActionOption.tsx +3 -0
  39. package/src/common/Option/Option.css +22 -0
  40. package/src/common/Option/Option.less +21 -0
  41. package/src/common/Option/Option.spec.tsx +21 -0
  42. package/src/common/Option/Option.tsx +7 -1
  43. package/src/dateLookup/DateLookup.spec.tsx +445 -0
  44. package/src/dateLookup/dateTrigger/DateTrigger.css +1 -32
  45. package/src/dateLookup/dateTrigger/DateTrigger.less +4 -28
  46. package/src/dateLookup/dateTrigger/DateTrigger.tsx +9 -5
  47. package/src/definitionList/DefinitionList.tsx +3 -3
  48. package/src/inputs/InputGroup.story.tsx +5 -3
  49. package/src/listItem/ListItem.spec.tsx +10 -2
  50. package/src/listItem/ListItem.story.tsx +11 -3
  51. package/src/main.css +23 -32
  52. package/src/snackbar/Snackbar.tsx +3 -3
  53. package/src/typeahead/Typeahead.story.tsx +46 -0
  54. package/src/typeahead/typeaheadInput/TypeaheadInput.tsx +4 -1
  55. package/src/withNextPortal/withNextPortal.spec.tsx +24 -0
  56. package/src/common/Option/Option.spec.js +0 -129
  57. package/src/dateLookup/DateLookup.proptypes.spec.js +0 -28
  58. package/src/dateLookup/DateLookup.rtl.spec.tsx +0 -199
  59. package/src/dateLookup/DateLookup.state.spec.js +0 -76
  60. package/src/dateLookup/DateLookup.testingLibrary.spec.js +0 -256
  61. package/src/dateLookup/DateLookup.view.spec.js +0 -151
  62. package/src/dateLookup/dateHeader/DateHeader.spec.js +0 -95
  63. package/src/dateLookup/dateTrigger/DateTrigger.spec.js +0 -123
  64. package/src/dateLookup/dayCalendar/DayCalendar.spec.js +0 -122
  65. package/src/dateLookup/dayCalendar/table/DayCalendarTable.spec.js +0 -147
  66. package/src/dateLookup/monthCalendar/MonthCalendar.spec.js +0 -105
  67. package/src/dateLookup/monthCalendar/table/MonthCalendarTable.spec.js +0 -120
  68. package/src/dateLookup/tableLink/TableLink.spec.js +0 -109
  69. package/src/dateLookup/yearCalendar/YearCalendar.spec.js +0 -88
  70. package/src/dateLookup/yearCalendar/table/YearCalendarTable.spec.js +0 -121
  71. package/src/modal/Modal.spec.js +0 -197
  72. package/src/withNextPortal/withNextPortal.spec.js +0 -22
  73. /package/src/modal/{Modal.rtl.spec.tsx → Modal.spec.tsx} +0 -0
package/src/main.css CHANGED
@@ -1770,6 +1770,28 @@ button.np-option {
1770
1770
  border-radius: var(--radius-small);
1771
1771
  }
1772
1772
  }
1773
+ .np-theme-personal .np-option-additional-content {
1774
+ margin-left: 80px;
1775
+ margin-left: var(--size-80);
1776
+ margin-right: 16px;
1777
+ margin-right: var(--size-16);
1778
+ margin-top: -10px;
1779
+ padding-bottom: 16px;
1780
+ padding-bottom: var(--size-16);
1781
+ max-width: -moz-fit-content;
1782
+ max-width: fit-content;
1783
+ }
1784
+ @media (max-width: 480px) {
1785
+ .np-theme-personal .np-option-additional-content {
1786
+ margin-left: 16px;
1787
+ margin-left: var(--size-16);
1788
+ }
1789
+ }
1790
+ @media (max-width: 320px) {
1791
+ .np-theme-personal .np-option-additional-content {
1792
+ margin-top: -3px;
1793
+ }
1794
+ }
1773
1795
  .np-select-option {
1774
1796
  border-radius: 10px;
1775
1797
  border-radius: var(--radius-small);
@@ -2003,55 +2025,24 @@ button.np-option {
2003
2025
  padding-left: var(--size-16);
2004
2026
  }
2005
2027
  .clear-btn {
2006
- transition: color 0.15s ease-in-out;
2007
- color: #c9cbce;
2008
- color: var(--color-interactive-secondary);
2009
2028
  position: absolute;
2010
- top: 16px;
2011
- top: var(--size-16);
2012
- right: 16px;
2013
- right: var(--size-16);
2014
- }
2015
- [dir="rtl"] .clear-btn {
2016
- left: 16px;
2017
- left: var(--size-16);
2018
- right: auto;
2019
- right: initial;
2020
- }
2021
- .np-theme-personal .clear-btn {
2022
2029
  top: 8px;
2023
2030
  top: var(--size-8);
2024
2031
  right: 8px;
2025
2032
  right: var(--size-8);
2026
2033
  }
2027
- [dir="rtl"] .np-theme-personal .clear-btn {
2034
+ [dir="rtl"] .clear-btn {
2028
2035
  left: 8px;
2029
2036
  left: var(--size-8);
2030
2037
  right: auto;
2031
2038
  right: initial;
2032
2039
  }
2033
2040
  .clear-btn--sm {
2034
- top: 8px;
2035
- top: var(--size-8);
2036
- }
2037
- .np-theme-personal .clear-btn--sm {
2038
2041
  top: 0;
2039
2042
  }
2040
2043
  .clear-btn--lg {
2041
- top: 28px;
2042
- }
2043
- .np-theme-personal .clear-btn--lg {
2044
2044
  top: 20px;
2045
2045
  }
2046
- .clear-btn:not(.disabled):not(:disabled):hover,
2047
- .clear-btn:not(.disabled):not(:disabled):focus {
2048
- color: #d03238;
2049
- color: var(--color-interactive-negative-hover);
2050
- }
2051
- .clear-btn:not(.disabled):not(:disabled):active {
2052
- color: #bf1e2c;
2053
- color: var(--color-interactive-negative-active);
2054
- }
2055
2046
  .np-decision .decision {
2056
2047
  display: flex;
2057
2048
  }
@@ -1,7 +1,7 @@
1
1
  import { Component, createRef } from 'react';
2
2
  import { CSSTransition } from 'react-transition-group';
3
3
 
4
- import ActionButton from '../actionButton';
4
+ import Button from '../button';
5
5
  import Body from '../body';
6
6
  import { Theme, type ThemeDark, type ThemeLight } from '../common';
7
7
  import { DirectionContext } from '../provider/direction';
@@ -110,9 +110,9 @@ export class Snackbar extends Component<SnackbarProps, SnackbarState> {
110
110
  <Body ref={this.bodyRef} as="span" className="snackbar__text">
111
111
  {text}
112
112
  {action ? (
113
- <ActionButton className="snackbar__text__action" onClick={action.onClick}>
113
+ <Button className="snackbar__text__action" v2 size="sm" onClick={action.onClick}>
114
114
  {action.label}
115
- </ActionButton>
115
+ </Button>
116
116
  ) : null}
117
117
  </Body>
118
118
  </CSSTransition>
@@ -7,6 +7,8 @@ import { Size } from '../common';
7
7
  import { useState } from 'react';
8
8
  import { Input } from '../inputs/Input';
9
9
  import { Field } from '../field/Field';
10
+ import Button from '../button';
11
+ import Modal from '../modal';
10
12
 
11
13
  type Story = StoryObj<typeof Typeahead>;
12
14
 
@@ -231,3 +233,47 @@ export const Search: Story = {
231
233
  );
232
234
  },
233
235
  };
236
+
237
+ export const AutoFocusInModal: Story = {
238
+ render: function Render() {
239
+ const [open, setOpen] = useState<boolean>(true);
240
+ return (
241
+ <>
242
+ <Button v2 onClick={() => setOpen(true)}>
243
+ Open Modal
244
+ </Button>
245
+ <Modal
246
+ title="Global Search"
247
+ body={
248
+ <Typeahead
249
+ id="typeahead"
250
+ name="search-input"
251
+ // eslint-disable-next-line jsx-a11y/no-autofocus
252
+ autoFocus
253
+ size="md"
254
+ showSuggestions
255
+ showNewEntry={false}
256
+ addon={<SearchIcon size={24} />}
257
+ options={[
258
+ { label: 'Option 1' },
259
+ { label: 'Option 2' },
260
+ { label: 'Option 3' },
261
+ { label: 'Option 4' },
262
+ ]}
263
+ inputAutoComplete="off"
264
+ onSearch={(search) => {
265
+ console.log(search);
266
+ }}
267
+ onChange={(option) => {}}
268
+ onBlur={() => {}}
269
+ />
270
+ }
271
+ open={open}
272
+ onClose={() => {
273
+ setOpen(false);
274
+ }}
275
+ />
276
+ </>
277
+ );
278
+ },
279
+ };
@@ -46,7 +46,10 @@ export default class TypeaheadInput<T> extends Component<
46
46
  componentDidMount() {
47
47
  const { autoFocus } = this.props;
48
48
  if (autoFocus) {
49
- this.inputRef.current?.focus();
49
+ // Autofocus after context code executions (e.g open Modal) is completed
50
+ setTimeout(() => {
51
+ this.inputRef.current?.focus();
52
+ });
50
53
  }
51
54
  }
52
55
 
@@ -0,0 +1,24 @@
1
+ import { HTMLAttributes } from 'react';
2
+ import { render } from '../test-utils';
3
+ import withNextPortalWrapper from './withNextPortal';
4
+
5
+ describe('withNextPortalWrapper', () => {
6
+ it('renders component in React portal', () => {
7
+ type Props = Pick<
8
+ HTMLAttributes<HTMLButtonElement>,
9
+ 'id' | 'children' | 'className' | 'aria-label'
10
+ >;
11
+ const RandomComponentForTest = withNextPortalWrapper((props: Props) => <span {...props} />);
12
+ const data: Props = {
13
+ 'aria-label': 'Test aria-label',
14
+ id: 'random-component',
15
+ children: 'Test Children',
16
+ };
17
+ render(<RandomComponentForTest {...data} />);
18
+
19
+ const componentDomRef = document.body.lastChild;
20
+ expect(componentDomRef).toHaveAttribute('aria-label', data['aria-label']);
21
+ expect(componentDomRef).toHaveTextContent(data.children as string);
22
+ expect(componentDomRef).toHaveAttribute('id', data.id);
23
+ });
24
+ });
@@ -1,129 +0,0 @@
1
- import { shallow } from 'enzyme';
2
- import { createRef } from 'react';
3
-
4
- import { render, screen } from '../../test-utils';
5
-
6
- import Option from '.';
7
-
8
- describe('Option', () => {
9
- let component;
10
- beforeEach(() => {
11
- component = shallow(<Option title="" content="" media={<span />} button={<span />} />);
12
- });
13
-
14
- it('does not have decision class when the flag is set to false', () => {
15
- expect(hasDecisonClass()).toBe(true);
16
- component.setProps({ decision: false });
17
- expect(hasDecisonClass()).toBe(false);
18
- });
19
-
20
- it('has complex class when the flag is passed', () => {
21
- expect(hasComplexClass()).toBe(false);
22
- component.setProps({ complex: true });
23
- expect(hasComplexClass()).toBe(true);
24
- });
25
-
26
- it('has disabled class when the flag is passed', () => {
27
- expect(hasDisabledClass()).toBe(false);
28
- component.setProps({ disabled: true });
29
- expect(hasDisabledClass()).toBe(true);
30
- });
31
-
32
- it('has disabled attibute when the flag is passed for a button', () => {
33
- expect(hasDisabledAttribute()).toBe(false);
34
- component.setProps({ disabled: true });
35
- expect(hasDisabledAttribute()).toBe(false);
36
- component.setProps({ disabled: false, as: 'button' });
37
- expect(hasDisabledAttribute()).toBe(false);
38
- component.setProps({ disabled: true, as: 'button' });
39
- expect(hasDisabledAttribute()).toBe(true);
40
- });
41
-
42
- it('passes the className it is given to the element it renders', () => {
43
- expect(component.hasClass('some-class')).toBe(false);
44
- component.setProps({ className: 'some-class' });
45
- expect(component.hasClass('some-class')).toBe(true);
46
- });
47
-
48
- it('calls click handler on click', () => {
49
- const onClick = jest.fn();
50
- const event = { iAmAnEvent: true };
51
- component.setProps({ onClick });
52
-
53
- expect(onClick).not.toHaveBeenCalled();
54
- component.simulate('click', event);
55
- expect(onClick).toHaveBeenCalledWith(event);
56
- });
57
-
58
- it('has for attribute to label when prop is passed', () => {
59
- expect(htmlFor()).toBeUndefined();
60
- component.setProps({ htmlFor: 'some-id' });
61
- expect(htmlFor()).toBe('some-id');
62
- });
63
-
64
- it('has passed media in the circle on the left', () => {
65
- const Icon = () => <svg />;
66
- component.setProps({ media: <Icon /> });
67
-
68
- expect(circleContentIsElement(<Icon />)).toBe(true);
69
- });
70
-
71
- it('does not render the circle content with circle-inverse class when inverseMediaCircle is set to false', () => {
72
- expect(circleContentHasInverseClass()).toBe(true);
73
- component.setProps({ inverseMediaCircle: false });
74
- expect(circleContentHasInverseClass()).toBe(false);
75
- });
76
-
77
- it('renders the title', () => {
78
- render(<Option title="Option title" content="" media={<span />} button={<span />} />);
79
-
80
- expect(() => screen.getByText('Option title')).not.toThrow();
81
- });
82
-
83
- it('has passed content', () => {
84
- component.setProps({ content: <p>A content</p> });
85
- expect(bodyHasElement(<p>A content</p>)).toBe(true);
86
- });
87
-
88
- it('renders as a label by default', () => {
89
- expect(mainComponentTag()).toBe('label');
90
- });
91
-
92
- it('renders as the tag that you pass it', () => {
93
- component.setProps({ as: 'a', href: 'https://example.com' });
94
- expect(mainComponentTag()).toBe('a');
95
- });
96
-
97
- it('does not render the circle if media not passed', () => {
98
- expect(circle().exists()).toBe(true);
99
- component.setProps({ media: null });
100
- expect(circle().exists()).toBe(false);
101
- });
102
-
103
- it('passes href to the underlying component if passed', () => {
104
- expect(component.prop('href')).toBeFalsy();
105
- component.setProps({ href: 'https://example.com' });
106
- expect(component.prop('href')).toBe('https://example.com');
107
- });
108
-
109
- it('`ref` attribute is passed to Option and reference is created', () => {
110
- const reference = createRef();
111
-
112
- expect(reference.current).toBeFalsy();
113
- render(<Option ref={reference} title="" content="" media={<span />} button={<span />} />);
114
- expect(reference.current).toBeTruthy();
115
- });
116
-
117
- const hasDecisonClass = () => component.hasClass('decision');
118
- const hasComplexClass = () => component.hasClass('decision-complex');
119
- const hasDisabledClass = () => component.hasClass('disabled');
120
- const hasDisabledAttribute = () => component.prop('disabled');
121
- const htmlFor = () => component.prop('htmlFor');
122
- const circle = () => component.find('.media-left');
123
- const circleContent = () => component.find('.media-left .circle');
124
- const circleContentHasInverseClass = () => circleContent().hasClass('circle-inverse');
125
- const circleContentIsElement = (element) => circleContent().childAt(0).matchesElement(element);
126
- const mainComponentTag = () => component.name();
127
- const bodyHasElement = (element) =>
128
- component.find('.media-body').containsMatchingElement(element);
129
- });
@@ -1,28 +0,0 @@
1
- import { render, mockMatchMedia } from '../test-utils';
2
-
3
- import DateLookup from '.';
4
-
5
- mockMatchMedia();
6
-
7
- // This test suite could become redundant
8
- // once we refactor the DateLookup component to TypeScript
9
-
10
- describe('DateLookup propTypes', () => {
11
- describe('when the value prop is set to null', () => {
12
- it('renders without prop type warnings in the console', () => {
13
- const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
14
-
15
- render(<DateLookup value={null} onChange={jest.fn()} />);
16
-
17
- // eslint-disable-next-line no-console
18
- expect(console.error).not.toHaveBeenCalledWith(
19
- expect.stringContaining('Warning: Failed %s type'),
20
- 'prop',
21
- 'The prop `value` is marked as required in `DateLookup`, but its value is `null`.',
22
- expect.anything(),
23
- );
24
-
25
- consoleSpy.mockRestore();
26
- });
27
- });
28
- });
@@ -1,199 +0,0 @@
1
- import { Field } from '../field/Field';
2
- import { mockMatchMedia, mockResizeObserver, render, screen, userEvent } from '../test-utils';
3
- import DateLookup, { DateLookupProps } from './DateLookup';
4
-
5
- import { act } from 'react';
6
-
7
- const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTimeAsync });
8
-
9
- mockMatchMedia();
10
- mockResizeObserver();
11
-
12
- const initialValue = new Date(2000, 0, 1);
13
-
14
- describe('DateLookup', () => {
15
- beforeEach(() => {
16
- jest.useFakeTimers();
17
- });
18
-
19
- afterEach(async () => {
20
- await jest.runOnlyPendingTimersAsync();
21
- jest.useRealTimers();
22
- jest.clearAllMocks();
23
- });
24
-
25
- it('supports `Field` for labeling', () => {
26
- render(
27
- <Field label="Date of birth">
28
- <DateLookup value={initialValue} onChange={() => {}} />
29
- </Field>,
30
- );
31
- const button = screen.getByRole('button', { name: /^Date of birth/ });
32
-
33
- expect(button).toBeInTheDocument();
34
- expect(button).toHaveAttribute('aria-haspopup');
35
- });
36
-
37
- it.each([' ', '{Enter}', '{ArrowDown}', '{ArrowUp}', '{ArrowRight}', '{ArrowLeft}'])(
38
- "opens with '%s' and closes with '{Escape}'",
39
- async (text) => {
40
- render(<DateLookup value={initialValue} onChange={() => {}} />);
41
-
42
- await user.tab();
43
- await user.keyboard(text);
44
-
45
- expect(screen.getByRole('button', { name: /next/iu })).toBeInTheDocument();
46
-
47
- await user.keyboard('{Escape}');
48
- await act(async () => {
49
- await jest.runOnlyPendingTimersAsync();
50
- });
51
-
52
- expect(screen.queryByRole('button', { name: /next/iu })).not.toBeInTheDocument();
53
- },
54
- );
55
-
56
- const setupAndOpenWithMouse = async (props: Partial<DateLookupProps> = {}) => {
57
- const view = render(<DateLookup value={initialValue} onChange={() => {}} {...props} />);
58
-
59
- await user.click(screen.getByRole('button'));
60
- await act(async () => {
61
- await jest.runOnlyPendingTimersAsync();
62
- });
63
-
64
- return view;
65
- };
66
-
67
- it('opens and closes with mouse', async () => {
68
- await setupAndOpenWithMouse();
69
-
70
- expect(screen.getByRole('button', { name: /next/iu })).toBeInTheDocument();
71
-
72
- const dimmerElement = screen.getByRole('dialog').parentElement?.parentElement;
73
- if (dimmerElement != null) {
74
- await user.click(dimmerElement);
75
- }
76
- await act(async () => {
77
- await jest.runOnlyPendingTimersAsync();
78
- });
79
-
80
- expect(screen.queryByRole('button', { name: /next/iu })).not.toBeInTheDocument();
81
- });
82
-
83
- describe('in day view', () => {
84
- it.each([
85
- ['{ArrowLeft}', -1],
86
- ['{ArrowRight}', +1],
87
- ['{ArrowUp}', -7],
88
- ['{ArrowDown}', +7],
89
- ])("handles '%s' to step %d day(s)", async (text, step) => {
90
- const handleChange = jest.fn();
91
- await setupAndOpenWithMouse({ onChange: handleChange });
92
-
93
- expect(handleChange).not.toHaveBeenCalled();
94
-
95
- await user.keyboard(text);
96
-
97
- const value = new Date(initialValue);
98
- value.setDate(initialValue.getDate() + step);
99
- expect(handleChange).toHaveBeenCalledWith(value);
100
-
101
- await user.keyboard('{Escape}');
102
- await act(async () => {
103
- await jest.runOnlyPendingTimersAsync();
104
- });
105
-
106
- expect(handleChange).toHaveBeenCalledWith(initialValue);
107
- });
108
- });
109
-
110
- describe('in year view', () => {
111
- it.each([
112
- ['{ArrowLeft}', -1],
113
- ['{ArrowRight}', +1],
114
- ['{ArrowUp}', -4],
115
- ['{ArrowDown}', +4],
116
- ])("handles '%s' to step %d year(s)", async (text, step) => {
117
- const handleChange = jest.fn();
118
- await setupAndOpenWithMouse({ onChange: handleChange });
119
-
120
- await user.click(screen.getByRole('button', { name: /year view/iu }));
121
- await act(async () => {
122
- await jest.runOnlyPendingTimersAsync();
123
- });
124
-
125
- expect(handleChange).not.toHaveBeenCalled();
126
-
127
- await user.keyboard(text);
128
-
129
- const value = new Date(initialValue);
130
- value.setFullYear(initialValue.getFullYear() + step);
131
- expect(handleChange).toHaveBeenCalledWith(value);
132
-
133
- await user.keyboard('{Escape}');
134
- await act(async () => {
135
- await jest.runOnlyPendingTimersAsync();
136
- });
137
-
138
- expect(handleChange).toHaveBeenCalledWith(initialValue);
139
- });
140
- });
141
-
142
- describe('in month view', () => {
143
- it.each([
144
- ['{ArrowLeft}', -1],
145
- ['{ArrowRight}', +1],
146
- ['{ArrowUp}', -4],
147
- ['{ArrowDown}', +4],
148
- ])("handles '%s' to step %d month(s)", async (text, step) => {
149
- const handleChange = jest.fn();
150
- await setupAndOpenWithMouse({ onChange: handleChange });
151
-
152
- await user.click(screen.getByRole('button', { name: /year view/iu }));
153
- await act(async () => {
154
- await jest.runOnlyPendingTimersAsync();
155
- });
156
- await user.keyboard(' ');
157
- await act(async () => {
158
- await jest.runOnlyPendingTimersAsync();
159
- });
160
-
161
- expect(handleChange).not.toHaveBeenCalled();
162
-
163
- await user.keyboard(text);
164
-
165
- const value = new Date(initialValue);
166
- value.setMonth(initialValue.getMonth() + step);
167
- expect(handleChange).toHaveBeenCalledWith(value);
168
-
169
- await user.keyboard('{Escape}');
170
- await act(async () => {
171
- await jest.runOnlyPendingTimersAsync();
172
- });
173
-
174
- expect(handleChange).toHaveBeenCalledWith(initialValue);
175
- });
176
- });
177
-
178
- it('limits min value', async () => {
179
- const min = new Date(initialValue);
180
- min.setDate(min.getDate() - 1);
181
- const handleChange = jest.fn();
182
- await setupAndOpenWithMouse({ min, onChange: handleChange });
183
-
184
- await user.keyboard('{ArrowLeft}{ArrowLeft}');
185
-
186
- expect(handleChange).toHaveBeenCalledWith(min);
187
- });
188
-
189
- it('limits max value', async () => {
190
- const max = new Date(initialValue);
191
- max.setDate(max.getDate() + 1);
192
- const handleChange = jest.fn();
193
- await setupAndOpenWithMouse({ max, onChange: handleChange });
194
-
195
- await user.keyboard('{ArrowRight}{ArrowRight}');
196
-
197
- expect(handleChange).toHaveBeenCalledWith(max);
198
- });
199
- });
@@ -1,76 +0,0 @@
1
- import { shallow } from 'enzyme';
2
- import { useIntl } from 'react-intl';
3
-
4
- import { DateLookupWithoutInputAttributes as DateLookup } from './DateLookup';
5
-
6
- jest.mock('react-intl');
7
-
8
- // Tests for getDerivedStateFromProps results
9
- describe('DateLookup state', () => {
10
- let component;
11
- let defaultProps;
12
- let initialValue;
13
- const defaultState = { selectedDate: null, min: null, max: null };
14
-
15
- beforeEach(() => {
16
- initialValue = new Date();
17
- defaultProps = { onChange: jest.fn(), value: initialValue };
18
- useIntl.mockReturnValue({ locale: 'en-GB' });
19
- component = shallow(<DateLookup {...defaultProps} />);
20
- });
21
-
22
- it('sets correct defaults', () => {
23
- const { selectedDate, min, max, viewMonth, viewYear, open, mode } = component.instance().state;
24
- expect(selectedDate).not.toBeNull();
25
- expect(min).toBeNull();
26
- expect(max).toBeNull();
27
- expect(viewMonth).not.toBeNull();
28
- expect(viewYear).not.toBeNull();
29
- expect(open).toBe(false);
30
- expect(mode).toBe('day');
31
- });
32
-
33
- it('does not update when date props did not change', () => {
34
- const props = { open: true };
35
- expect(DateLookup.getDerivedStateFromProps(props, defaultState)).toBeNull();
36
- });
37
-
38
- it('updates selectedDate on props value change', () => {
39
- const props = { value: new Date(2018, 11, 27) };
40
- const newState = DateLookup.getDerivedStateFromProps(props, defaultState);
41
- expect(Number(newState.selectedDate)).toBe(Number(new Date(2018, 11, 27)));
42
- });
43
-
44
- it('sets date values to midnight', () => {
45
- const props = {
46
- value: new Date(2018, 11, 27, 15, 23),
47
- min: new Date(2018, 10, 27, 22, 23),
48
- max: new Date(2018, 11, 28, 0, 30),
49
- };
50
- const newState = DateLookup.getDerivedStateFromProps(props, defaultState);
51
- expect(Number(newState.selectedDate)).toBe(Number(new Date(2018, 11, 27, 0, 0)));
52
- expect(Number(newState.min)).toBe(Number(new Date(2018, 10, 27, 0, 0)));
53
- expect(Number(newState.max)).toBe(Number(new Date(2018, 11, 28, 0, 0)));
54
- });
55
-
56
- it('calls onChange with min when it is < min', () => {
57
- const onChange = jest.fn();
58
- const props = { value: new Date(2018, 11, 27), min: new Date(2018, 11, 28), onChange };
59
- DateLookup.getDerivedStateFromProps(props, defaultState);
60
- expect(onChange).toHaveBeenCalledWith(new Date(2018, 11, 28));
61
- });
62
-
63
- it('calls onChange with max when it is > min', () => {
64
- const onChange = jest.fn();
65
- const props = { value: new Date(2018, 11, 27), max: new Date(2018, 10, 1), onChange };
66
- DateLookup.getDerivedStateFromProps(props, defaultState);
67
- expect(onChange).toHaveBeenCalledWith(new Date(2018, 10, 1));
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
- });
76
- });