@transferwise/components 46.84.1 → 46.85.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 (63) hide show
  1. package/build/dimmer/Dimmer.js.map +1 -1
  2. package/build/dimmer/Dimmer.mjs.map +1 -1
  3. package/build/i18n/en.json +1 -0
  4. package/build/i18n/en.json.js +1 -0
  5. package/build/i18n/en.json.js.map +1 -1
  6. package/build/i18n/en.json.mjs +1 -0
  7. package/build/i18n/en.json.mjs.map +1 -1
  8. package/build/i18n/th.json +5 -0
  9. package/build/i18n/th.json.js +5 -0
  10. package/build/i18n/th.json.js.map +1 -1
  11. package/build/i18n/th.json.mjs +5 -0
  12. package/build/i18n/th.json.mjs.map +1 -1
  13. package/build/i18n/zh-CN.json +10 -0
  14. package/build/i18n/zh-CN.json.js +10 -0
  15. package/build/i18n/zh-CN.json.js.map +1 -1
  16. package/build/i18n/zh-CN.json.mjs +10 -0
  17. package/build/i18n/zh-CN.json.mjs.map +1 -1
  18. package/build/i18n/zh-HK.json +5 -0
  19. package/build/i18n/zh-HK.json.js +5 -0
  20. package/build/i18n/zh-HK.json.js.map +1 -1
  21. package/build/i18n/zh-HK.json.mjs +5 -0
  22. package/build/i18n/zh-HK.json.mjs.map +1 -1
  23. package/build/stepper/Stepper.js +6 -3
  24. package/build/stepper/Stepper.js.map +1 -1
  25. package/build/stepper/Stepper.mjs +6 -3
  26. package/build/stepper/Stepper.mjs.map +1 -1
  27. package/build/types/dimmer/Dimmer.d.ts +1 -1
  28. package/build/types/dimmer/Dimmer.d.ts.map +1 -1
  29. package/build/types/stepper/Stepper.d.ts +2 -1
  30. package/build/types/stepper/Stepper.d.ts.map +1 -1
  31. package/build/types/test-utils/index.d.ts +2 -0
  32. package/build/types/test-utils/index.d.ts.map +1 -1
  33. package/build/types/uploadInput/uploadButton/UploadButton.messages.d.ts +5 -0
  34. package/build/types/uploadInput/uploadButton/UploadButton.messages.d.ts.map +1 -1
  35. package/build/uploadInput/uploadButton/UploadButton.js +3 -1
  36. package/build/uploadInput/uploadButton/UploadButton.js.map +1 -1
  37. package/build/uploadInput/uploadButton/UploadButton.messages.js +3 -0
  38. package/build/uploadInput/uploadButton/UploadButton.messages.js.map +1 -1
  39. package/build/uploadInput/uploadButton/UploadButton.messages.mjs +3 -0
  40. package/build/uploadInput/uploadButton/UploadButton.messages.mjs.map +1 -1
  41. package/build/uploadInput/uploadButton/UploadButton.mjs +3 -1
  42. package/build/uploadInput/uploadButton/UploadButton.mjs.map +1 -1
  43. package/package.json +3 -3
  44. package/src/dimmer/{Dimmer.rtl.spec.tsx → Dimmer.spec.tsx} +33 -29
  45. package/src/dimmer/Dimmer.tsx +4 -4
  46. package/src/flowNavigation/__snapshots__/FlowNavigation.spec.js.snap +3 -1
  47. package/src/i18n/en.json +1 -0
  48. package/src/i18n/th.json +5 -0
  49. package/src/i18n/zh-CN.json +10 -0
  50. package/src/i18n/zh-HK.json +5 -0
  51. package/src/navigationOption/NavigationOption.spec.tsx +113 -0
  52. package/src/radioOption/RadioOption.spec.tsx +73 -0
  53. package/src/stepper/Stepper.spec.tsx +236 -0
  54. package/src/stepper/Stepper.tests.story.tsx +89 -0
  55. package/src/stepper/Stepper.tsx +9 -4
  56. package/src/stepper/{deviceDetection.spec.js → deviceDetection.spec.ts} +6 -3
  57. package/src/uploadInput/uploadButton/UploadButton.messages.ts +7 -0
  58. package/src/uploadInput/uploadButton/UploadButton.tsx +1 -1
  59. package/src/dimmer/Dimmer.spec.js +0 -87
  60. package/src/navigationOption/NavigationOption.spec.js +0 -93
  61. package/src/radioOption/RadioOption.spec.js +0 -67
  62. package/src/stepper/Stepper.spec.js +0 -233
  63. /package/src/alert/{Alert.spec.story.tsx → Alert.tests.story.tsx} +0 -0
@@ -0,0 +1,236 @@
1
+ import { render, screen, userEvent } from '../test-utils';
2
+ import * as mockedDeviceDetection from './deviceDetection';
3
+
4
+ import Stepper from './Stepper';
5
+
6
+ jest.mock('./deviceDetection', () => ({
7
+ isTouchDevice: jest.fn(() => false),
8
+ }));
9
+
10
+ describe('Stepper', () => {
11
+ beforeEach(() => {
12
+ jest.clearAllMocks();
13
+ });
14
+
15
+ const generateSteps = (stepsCount: number) =>
16
+ Array.from({ length: stepsCount }, () => ({
17
+ label: Math.random().toString(),
18
+ onClick: jest.fn(),
19
+ }));
20
+
21
+ const getSteps = () => screen.getAllByRole('listitem');
22
+
23
+ const initialProps = {
24
+ activeStep: 0,
25
+ steps: generateSteps(3),
26
+ };
27
+
28
+ const customRender = (overrides = {}) => {
29
+ return render(<Stepper {...initialProps} {...overrides} />);
30
+ };
31
+
32
+ describe('progress bar', () => {
33
+ it('renders nothing when no steps are passed in', () => {
34
+ customRender({ steps: [] });
35
+ expect(screen.queryByTestId('progress-bar')).not.toBeInTheDocument();
36
+ });
37
+ });
38
+
39
+ describe('steps', () => {
40
+ it('have rendered labels', () => {
41
+ const steps = generateSteps(5);
42
+ customRender({ steps });
43
+
44
+ getSteps().forEach((step, index) => {
45
+ expect(step).toHaveTextContent(steps[index].label);
46
+ });
47
+ });
48
+
49
+ describe('step interactive style', () => {
50
+ const expectStepIsVisuallyInteractive = (stepIndex: number) => {
51
+ // eslint-disable-next-line jest/valid-expect
52
+ return expect(getSteps()[stepIndex].classList.contains('tw-stepper__step--clickable'));
53
+ };
54
+
55
+ it('is not styled as interactive if it is the active step', async () => {
56
+ const steps = [
57
+ { label: '0' },
58
+ { label: '1', onClick: jest.fn() },
59
+ { label: '2', onClick: jest.fn() },
60
+ ];
61
+ customRender({ steps, activeStep: 0 });
62
+ expectStepIsVisuallyInteractive(0).toBe(false);
63
+ });
64
+
65
+ it('is not styled as interactive if not the active step and has no click handler', () => {
66
+ const steps = [
67
+ { label: '0' },
68
+ { label: '1', onClick: jest.fn() },
69
+ { label: '2', onClick: jest.fn() },
70
+ ];
71
+ customRender({ steps, activeStep: 1 });
72
+ expectStepIsVisuallyInteractive(0).toBe(false);
73
+ expectStepIsVisuallyInteractive(1).toBe(false);
74
+ });
75
+
76
+ it('is styled as interactive if not the active step and has click handler', () => {
77
+ const steps = [
78
+ { label: '0', onClick: jest.fn() },
79
+ { label: '1', onClick: jest.fn() },
80
+ { label: '2', onClick: jest.fn() },
81
+ ];
82
+ customRender({ steps, activeStep: 2 });
83
+ expectStepIsVisuallyInteractive(0).toBe(true);
84
+ expectStepIsVisuallyInteractive(1).toBe(true);
85
+ expectStepIsVisuallyInteractive(2).toBe(false);
86
+ });
87
+ });
88
+
89
+ describe('step interactivity', () => {
90
+ const getStepChild = (stepIndex: number) => getSteps()[stepIndex].children[0];
91
+
92
+ it('is not interactive if it is the active step', async () => {
93
+ const steps = [
94
+ { label: '0', onClick: jest.fn() },
95
+ { label: '1', onClick: jest.fn() },
96
+ { label: '2', onClick: jest.fn() },
97
+ ];
98
+ customRender({ steps, activeStep: 0 });
99
+ await userEvent.click(getStepChild(0));
100
+ expect(steps[0].onClick).not.toHaveBeenCalled();
101
+ });
102
+
103
+ it('is not interactive if not the active step but has no click handler', async () => {
104
+ const steps = [
105
+ { label: '0' },
106
+ { label: '1', onClick: jest.fn() },
107
+ { label: '2', onClick: jest.fn() },
108
+ ];
109
+ customRender({ steps, activeStep: 1 });
110
+ await userEvent.click(getStepChild(0));
111
+ expect(steps[1].onClick).not.toHaveBeenCalled();
112
+ await userEvent.click(getStepChild(1));
113
+ expect(steps[1].onClick).not.toHaveBeenCalled();
114
+ });
115
+
116
+ it('is interactive if not the active step and has click handler', async () => {
117
+ const steps = [
118
+ { label: '0', onClick: jest.fn() },
119
+ { label: '1', onClick: jest.fn() },
120
+ { label: '2', onClick: jest.fn() },
121
+ ];
122
+ customRender({ steps, activeStep: 2 });
123
+ await userEvent.click(getStepChild(1));
124
+ expect(steps[1].onClick).toHaveBeenCalledTimes(1);
125
+ });
126
+ });
127
+
128
+ it('are not clickable when active', async () => {
129
+ const clickedOnFirstStep = jest.fn();
130
+ const clickedOnSecondStep = jest.fn();
131
+ const initialProps = {
132
+ steps: [
133
+ { label: 'one', onClick: clickedOnFirstStep },
134
+ { label: 'two', onClick: clickedOnSecondStep },
135
+ ],
136
+ activeStep: 0,
137
+ };
138
+ const { rerender } = customRender(initialProps);
139
+
140
+ const clickOnStep = async (stepIndex: number) => {
141
+ const step = screen.getByText(initialProps.steps[stepIndex].label).parentElement;
142
+ if (step) {
143
+ return userEvent.click(step);
144
+ }
145
+ };
146
+
147
+ await clickOnStep(0);
148
+ expect(clickedOnFirstStep).not.toHaveBeenCalled();
149
+
150
+ rerender(<Stepper {...initialProps} activeStep={1} />);
151
+ await clickOnStep(0);
152
+ expect(clickedOnFirstStep).toHaveBeenCalledTimes(1);
153
+
154
+ await clickOnStep(1);
155
+ expect(clickedOnSecondStep).not.toHaveBeenCalled();
156
+ });
157
+
158
+ it('are active when they are the currently active step', () => {
159
+ const { rerender } = customRender({
160
+ steps: Array(4)
161
+ .fill('')
162
+ .map((_, i) => ({ label: i.toString() })),
163
+ activeStep: 1,
164
+ });
165
+
166
+ const stepActive = (index: number) =>
167
+ getSteps()[index].classList.contains('tw-stepper__step--active');
168
+
169
+ expect(stepActive(0)).toBe(false);
170
+ expect(stepActive(1)).toBe(true);
171
+ expect(stepActive(2)).toBe(false);
172
+ expect(stepActive(3)).toBe(false);
173
+
174
+ rerender(<Stepper {...initialProps} activeStep={2} />);
175
+
176
+ expect(stepActive(1)).toBe(false);
177
+ expect(stepActive(2)).toBe(true);
178
+ });
179
+
180
+ it('are aria-current=step when active', () => {
181
+ const { rerender } = customRender({ steps: Array(4).fill({ label: '' }), activeStep: 1 });
182
+
183
+ const stepCurrent = (index: number) =>
184
+ getSteps()[index].getAttribute('aria-current') === 'step';
185
+
186
+ expect(stepCurrent(0)).toBe(false);
187
+ expect(stepCurrent(1)).toBe(true);
188
+ expect(stepCurrent(2)).toBe(false);
189
+ expect(stepCurrent(3)).toBe(false);
190
+
191
+ rerender(<Stepper {...initialProps} activeStep={2} />);
192
+
193
+ expect(stepCurrent(1)).toBe(false);
194
+ expect(stepCurrent(2)).toBe(true);
195
+ });
196
+ });
197
+
198
+ describe('hover labels', () => {
199
+ it('will be rendered when provided', async () => {
200
+ const hoverLabel = 'hover label';
201
+ customRender({
202
+ steps: [{ hoverLabel, label: 'label' }, { label: 'label 2' }],
203
+ });
204
+ expect(await screen.findByText(hoverLabel)).toBeInTheDocument();
205
+ });
206
+
207
+ it('renders jsx', async () => {
208
+ customRender({
209
+ steps: [
210
+ {
211
+ hoverLabel: (
212
+ <>
213
+ <span>hover label 1</span>
214
+ <p>hover label 2</p>
215
+ </>
216
+ ),
217
+ label: 'one',
218
+ },
219
+ ],
220
+ });
221
+
222
+ expect(await screen.findByText('hover label 1')).toBeInTheDocument();
223
+ expect(await screen.findByText('hover label 2')).toBeInTheDocument();
224
+ });
225
+
226
+ it('will not be rendered if the user is on a touch device', () => {
227
+ jest.spyOn(mockedDeviceDetection, 'isTouchDevice').mockImplementation(() => true);
228
+
229
+ customRender({
230
+ steps: [{ hoverLabel: 'hover label', label: 'label' }, { label: 'label 2' }],
231
+ });
232
+
233
+ expect(screen.queryByText('hover label')).not.toBeInTheDocument();
234
+ });
235
+ });
236
+ });
@@ -0,0 +1,89 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { fn, within, expect, screen } from '@storybook/test';
3
+ import Stepper from './Stepper';
4
+
5
+ const STEPS = [
6
+ {
7
+ label: 'One',
8
+ onClick: fn(),
9
+ },
10
+ {
11
+ label: 'Two',
12
+ hoverLabel: (
13
+ <>
14
+ <div>
15
+ <strong>Diana Jaramillo</strong>
16
+ </div>
17
+ dianajarm123@gmail.com
18
+ </>
19
+ ),
20
+ onClick: fn(),
21
+ },
22
+ { label: 'Three', onClick: fn() },
23
+ { label: 'Four', onClick: fn() },
24
+ { label: 'Five', onClick: fn() },
25
+ ];
26
+
27
+ const meta = {
28
+ component: Stepper,
29
+ title: 'Navigation/Stepper/tests',
30
+ argTypes: {
31
+ activeStep: {
32
+ control: 'radio',
33
+ options: [...Array(STEPS.length).keys()],
34
+ },
35
+ },
36
+ } satisfies Meta<typeof Stepper>;
37
+ export default meta;
38
+
39
+ type Story = StoryObj<typeof meta>;
40
+
41
+ export const ProgressbarWidth: Story = {
42
+ play: async ({ step }) => {
43
+ const getBarBaseWidth = (stepIndex: number) => {
44
+ const instance = screen.getByTestId(`stepper${stepIndex}`);
45
+ return within(instance).getByTestId('progress-bar')?.style.width;
46
+ };
47
+
48
+ await step('activeStep: -10 (negative) ▸ width should be 0', async () => {
49
+ await expect(getBarBaseWidth(-10)).toBe(`0%`);
50
+ });
51
+
52
+ await step('activeStep: 0 ▸ width should be 0', async () => {
53
+ await expect(getBarBaseWidth(0)).toBe(`0%`);
54
+ });
55
+
56
+ const WIDTHS = [0, 25, 50, 75, 100];
57
+ // eslint-disable-next-line no-plusplus
58
+ for (let stepIndex = 1; stepIndex < STEPS.length; stepIndex++) {
59
+ await step(
60
+ `activeStep: ${stepIndex} ▸ base width should be ${WIDTHS[stepIndex]}%`,
61
+ async () => {
62
+ await expect(getBarBaseWidth(stepIndex)).toMatch(
63
+ new RegExp(`^calc\\(${WIDTHS[stepIndex]}%.*?`),
64
+ );
65
+ },
66
+ );
67
+ }
68
+
69
+ await step('activeStep: 1000 ▸ base width should be 100%', async () => {
70
+ await expect(getBarBaseWidth(1000)).toMatch(/^calc\(100%.*?/);
71
+ });
72
+ },
73
+ render: function Render() {
74
+ return (
75
+ <>
76
+ <Stepper steps={STEPS} activeStep={0} testId="stepper-10" />
77
+ <Stepper steps={STEPS} activeStep={0} testId="stepper0" />
78
+ <Stepper steps={STEPS} activeStep={1} testId="stepper1" />
79
+ <Stepper steps={STEPS} activeStep={2} testId="stepper2" />
80
+ <Stepper steps={STEPS} activeStep={3} testId="stepper3" />
81
+ <Stepper steps={STEPS} activeStep={4} testId="stepper4" />
82
+ <Stepper steps={STEPS} activeStep={4} testId="stepper1000" />
83
+ </>
84
+ );
85
+ },
86
+ args: {
87
+ steps: STEPS,
88
+ },
89
+ };
@@ -21,13 +21,14 @@ export interface StepperProps {
21
21
  steps: readonly Step[];
22
22
  activeStep?: number;
23
23
  className?: string;
24
+ testId?: string;
24
25
  }
25
26
 
26
27
  /**
27
28
  * This component is considered user-unfriendly and inaccessible on its own and will likely be made internal in the future. Please use `FlowNavigation` instead.
28
29
  * @see https://storybook.wise.design/?path=/story/navigation-flownavigation--variants
29
30
  */
30
- const Stepper = ({ steps, activeStep = 0, className }: StepperProps) => {
31
+ const Stepper = ({ steps, activeStep = 0, className, testId }: StepperProps) => {
31
32
  const { isRTL } = useDirection();
32
33
 
33
34
  if (steps.length === 0) {
@@ -40,7 +41,7 @@ const Stepper = ({ steps, activeStep = 0, className }: StepperProps) => {
40
41
 
41
42
  const getProgressWidth = (): string => {
42
43
  if (percentageCompleted === 0) {
43
- return '0px';
44
+ return '0%';
44
45
  }
45
46
  /**
46
47
  * Progress bar starts with left/right (depends on rtl) shift `--progress-bar-start-shift` for hiding Progress bar's left and right borders
@@ -95,9 +96,13 @@ const Stepper = ({ steps, activeStep = 0, className }: StepperProps) => {
95
96
  };
96
97
 
97
98
  return (
98
- <div className={clsx('tw-stepper', className)}>
99
+ <div className={clsx('tw-stepper', className)} data-testid={testId}>
99
100
  <div className="progress">
100
- <div className="progress-bar" style={{ width: getProgressWidth() }} />
101
+ <div
102
+ className="progress-bar"
103
+ style={{ width: getProgressWidth() }}
104
+ data-testid="progress-bar"
105
+ />
101
106
  </div>
102
107
  <ol className="tw-stepper-steps p-t-1 m-b-0">{steps.map(renderStep)}</ol>
103
108
  </div>
@@ -1,14 +1,14 @@
1
1
  import { isTouchDevice } from './deviceDetection';
2
2
 
3
3
  describe('Device detection', () => {
4
- function fakeUserAgent(userAgent) {
4
+ function fakeUserAgent(userAgent: string) {
5
5
  Object.defineProperty(navigator, 'userAgent', {
6
6
  value: userAgent,
7
7
  configurable: true,
8
8
  });
9
9
  }
10
10
 
11
- function fakeMaxTouchPoints(maxTouchPoints) {
11
+ function fakeMaxTouchPoints(maxTouchPoints: number | undefined) {
12
12
  Object.defineProperty(navigator, 'maxTouchPoints', {
13
13
  value: maxTouchPoints,
14
14
  configurable: true,
@@ -24,7 +24,10 @@ describe('Device detection', () => {
24
24
 
25
25
  it('recognizes touch devices via window events', () => {
26
26
  expect(isTouchDevice()).toBe(false);
27
- window.ontouchstart = {};
27
+ Object.defineProperty(window, 'ontouchstart', {
28
+ value: () => {},
29
+ writable: true,
30
+ });
28
31
  expect(isTouchDevice()).toBe(true);
29
32
  });
30
33
 
@@ -19,6 +19,13 @@ export default defineMessages({
19
19
  description: 'Description about filetypes and size limit for uploading files',
20
20
  },
21
21
 
22
+ maximumFiles: {
23
+ id: 'neptune.UploadButton.maximumFiles',
24
+ defaultMessage: 'Maximum {maxFiles} files.',
25
+ description:
26
+ 'When number of files to be uploaded is restricted, this information is displayed within the upload button.',
27
+ },
28
+
22
29
  allFileTypes: {
23
30
  id: 'neptune.UploadButton.allFileTypes',
24
31
  defaultMessage: 'All file types',
@@ -200,7 +200,7 @@ const UploadButton = forwardRef<HTMLInputElement | null, UploadButtonProps>(
200
200
  {maxFiles && (
201
201
  <>
202
202
  <br />
203
- {`Maximum ${maxFiles} files.`}
203
+ {formatMessage(MESSAGES.maximumFiles, { maxFiles })}
204
204
  </>
205
205
  )}
206
206
  </Body>
@@ -1,87 +0,0 @@
1
- import { shallow, mount } from 'enzyme';
2
- import ReactDOM from 'react-dom';
3
-
4
- import DimmerAppendingToBody, { Dimmer } from './Dimmer';
5
-
6
- jest.mock('react-dom');
7
-
8
- describe('Dimmer', () => {
9
- let component;
10
- const props = {
11
- open: true,
12
- fadeContentOnExit: false,
13
- fadeContentOnEnter: false,
14
- onClick: jest.fn(),
15
- children: <div />,
16
- className: undefined,
17
- onClose: undefined,
18
- onExited: jest.fn(),
19
- };
20
-
21
- beforeEach(() => {
22
- jest.spyOn(ReactDOM, 'createPortal').mockImplementation();
23
- ReactDOM.createPortal.mockReturnValue(<Dimmer {...props} />);
24
- component = shallow(<Dimmer {...props} />);
25
- });
26
-
27
- afterEach(() => {
28
- ReactDOM.createPortal.mockClear();
29
- jest.clearAllMocks();
30
- });
31
-
32
- it('is appended to body', () => {
33
- expect(ReactDOM.createPortal).not.toHaveBeenCalled();
34
- mount(<DimmerAppendingToBody {...props} />);
35
-
36
- expect(ReactDOM.createPortal).toHaveBeenCalledTimes(1);
37
- });
38
-
39
- it('renders with right props', () => {
40
- component = mount(
41
- <Dimmer {...props} disableClickToClose={false} transparent={false} scrollable={false} />,
42
- );
43
- expect(component.find(Dimmer)).toHaveLength(1);
44
- expect(component.find(Dimmer).props()).toStrictEqual({
45
- ...props,
46
- disableClickToClose: false,
47
- transparent: false,
48
- scrollable: false,
49
- });
50
- });
51
-
52
- it('do not make Dimmer auto scrollable if scrollable=false', () => {
53
- expect(component.find('.dimmer').hasClass('dimmer--scrollable')).toBe(false);
54
- });
55
-
56
- it('makes Dimmer auto scrollable if scrollable=true', () => {
57
- component.setProps({ scrollable: true });
58
- expect(component.find('.dimmer').hasClass('dimmer--scrollable')).toBe(true);
59
- });
60
-
61
- it('fade content on enter if fadeContentOnEnter is true', () => {
62
- component.setProps({ fadeContentOnEnter: true });
63
- const cssTransition = component.find('CSSTransition');
64
- expect(cssTransition.prop('classNames')).toStrictEqual({
65
- enter: 'dimmer--enter-fade',
66
- enterDone: 'dimmer--enter-done dimmer--enter-fade',
67
- exit: 'dimmer--exit',
68
- });
69
- });
70
-
71
- it('fade content on exit if fadeContentOnExit is true', () => {
72
- component.setProps({ fadeContentOnExit: true });
73
- const cssTransition = component.find('CSSTransition');
74
- expect(cssTransition.prop('classNames')).toStrictEqual({
75
- enter: '',
76
- enterDone: 'dimmer--enter-done',
77
- exit: 'dimmer--exit dimmer--exit-fade',
78
- });
79
- });
80
-
81
- it('calls onExited when the exit animation is finished', () => {
82
- component.setProps({ fadeContentOnExit: true });
83
- const transition = component.find('CSSTransition');
84
- transition.prop('onExited')();
85
- expect(props.onExited).toHaveBeenCalled();
86
- });
87
- });
@@ -1,93 +0,0 @@
1
- import { shallow } from 'enzyme';
2
- import { createRef } from 'react';
3
-
4
- import Option from '../common/Option';
5
- import { render } from '../test-utils';
6
-
7
- import NavigationOption from '.';
8
-
9
- describe('Navigation option', () => {
10
- let component;
11
- beforeEach(() => {
12
- component = shallow(<NavigationOption title="" onClick={jest.fn()} />);
13
- });
14
-
15
- it('passes shared props to option', () => {
16
- const Icon = () => <svg />;
17
- const sharedProps = {
18
- media: <Icon />,
19
- title: 'A title',
20
- content: 'A content',
21
- complex: true,
22
- disabled: true,
23
- href: 'https://example.com',
24
- };
25
- component.setProps(sharedProps);
26
-
27
- expect(option().props()).toMatchObject(sharedProps);
28
- });
29
-
30
- it('calls click handler on option click when not disabled', () => {
31
- const onClick = jest.fn();
32
- component.setProps({ onClick });
33
-
34
- expect(onClick).not.toHaveBeenCalled();
35
- option().simulate('click', { event: true });
36
- expect(onClick).toHaveBeenCalledWith({ event: true });
37
- });
38
-
39
- it('tells option to render as an anchor when a href is passed', () => {
40
- expect(option().prop('as')).not.toBe('a');
41
- component.setProps({ href: 'https://example.com' });
42
- expect(option().prop('as')).toBe('a');
43
- });
44
-
45
- it('tells option to render as the `as` prop when it is passed', () => {
46
- expect(option().prop('as')).not.toBe('label');
47
- component.setProps({ as: 'label' });
48
- expect(option().prop('as')).toBe('label');
49
- });
50
-
51
- it('does not call click handler when disabled', () => {
52
- const onClick = jest.fn();
53
- component.setProps({ onClick, disabled: true });
54
-
55
- expect(onClick).not.toHaveBeenCalled();
56
- option().simulate('click', {
57
- preventDefault: () => {},
58
- });
59
- expect(onClick).not.toHaveBeenCalled();
60
- });
61
-
62
- it('renders the circle when enabled', () => {
63
- const { container, rerender } = render(<NavigationOption title="" />);
64
-
65
- expect(container.querySelector('.circle')).toBeDefined();
66
-
67
- rerender(<NavigationOption title="" showMediaCircle={false} />);
68
-
69
- expect(container.querySelector('.circle')).toBeNull();
70
- });
71
-
72
- it('should pass correct class names to Option', () => {
73
- expect(option().props().className).toBe('np-navigation-option');
74
- component.setProps({ className: 'test-class-name' });
75
-
76
- expect(option().props().className).toBe('np-navigation-option test-class-name');
77
- });
78
-
79
- it('`ref` attribute is passed to NavigationOption and reference is created', () => {
80
- const reference = createRef();
81
-
82
- expect(reference.current).toBeFalsy();
83
- render(<NavigationOption ref={reference} title="" onClick={jest.fn()} />);
84
- expect(reference.current).toBeTruthy();
85
- });
86
-
87
- it('renders aligned with container content', () => {
88
- render(<NavigationOption isContainerAligned />);
89
- expect(document.querySelector('.np-option__container-aligned')).toBeInTheDocument();
90
- });
91
-
92
- const option = () => component.find(Option);
93
- });
@@ -1,67 +0,0 @@
1
- import { shallow } from 'enzyme';
2
-
3
- import Option from '../common/Option';
4
-
5
- import RadioOption from '.';
6
-
7
- describe('Radio option', () => {
8
- let component;
9
- beforeEach(() => {
10
- component = shallow(
11
- <RadioOption media={<span />} id="" name="" title="" content="" onChange={jest.fn()} />,
12
- );
13
- });
14
-
15
- it('passes shared props to option', () => {
16
- const Icon = () => <svg />;
17
- const sharedProps = {
18
- media: <Icon />,
19
- title: 'A title',
20
- content: 'A content',
21
- name: 'a-name',
22
- complex: true,
23
- disabled: true,
24
- };
25
- component.setProps(sharedProps);
26
-
27
- expect(option().props()).toStrictEqual(expect.objectContaining(sharedProps));
28
- });
29
-
30
- it('passes id to radio button passed as button', () => {
31
- component.setProps({ id: 'some-id' });
32
- expect(buttonProperty('id')).toBe('some-id');
33
- });
34
-
35
- it('passes name to radio button passed as button', () => {
36
- component.setProps({ name: 'a-name' });
37
- expect(buttonProperty('name')).toBe('a-name');
38
- });
39
-
40
- it('passes checked to radio button passed as button', () => {
41
- expect(buttonProperty('checked')).toBeFalsy();
42
- component.setProps({ checked: true });
43
- expect(buttonProperty('checked')).toBeTruthy();
44
- });
45
-
46
- it('passes change handler to radio button passed as button', () => {
47
- const onChange = jest.fn();
48
- component.setProps({ onChange });
49
- expect(buttonProperty('onChange')).toBe(onChange);
50
- });
51
-
52
- it('passes disabled to radio button passed as button', () => {
53
- expect(buttonProperty('disabled')).toBeFalsy();
54
- component.setProps({ disabled: true });
55
- expect(buttonProperty('disabled')).toBeTruthy();
56
- });
57
-
58
- it('renders aligned with container content', () => {
59
- component.setProps({ isContainerAligned: true });
60
- expect(component.find(Option).shallow().prop('className')).toContain(
61
- 'np-option__container-aligned',
62
- );
63
- });
64
-
65
- const option = () => component.find(Option);
66
- const buttonProperty = (name) => option().prop('button').props[name];
67
- });