@transferwise/components 0.0.0-experimental-db4013c → 0.0.0-experimental-bd6f8cc

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 (118) hide show
  1. package/build/avatarLayout/AvatarLayout.js +2 -9
  2. package/build/avatarLayout/AvatarLayout.js.map +1 -1
  3. package/build/avatarLayout/AvatarLayout.mjs +2 -9
  4. package/build/avatarLayout/AvatarLayout.mjs.map +1 -1
  5. package/build/button/Button.js +79 -84
  6. package/build/button/Button.js.map +1 -1
  7. package/build/button/Button.mjs +80 -85
  8. package/build/button/Button.mjs.map +1 -1
  9. package/build/criticalBanner/CriticalCommsBanner.js +2 -2
  10. package/build/criticalBanner/CriticalCommsBanner.js.map +1 -1
  11. package/build/criticalBanner/CriticalCommsBanner.mjs +1 -1
  12. package/build/header/Header.js +2 -2
  13. package/build/header/Header.js.map +1 -1
  14. package/build/header/Header.mjs +1 -1
  15. package/build/index.js +2 -2
  16. package/build/index.mjs +1 -1
  17. package/build/link/Link.js +3 -8
  18. package/build/link/Link.js.map +1 -1
  19. package/build/link/Link.mjs +3 -8
  20. package/build/link/Link.mjs.map +1 -1
  21. package/build/main.css +11 -226
  22. package/build/nudge/Nudge.js.map +1 -1
  23. package/build/nudge/Nudge.mjs.map +1 -1
  24. package/build/primitives/PrimitiveAnchor/src/PrimitiveAnchor.js +4 -2
  25. package/build/primitives/PrimitiveAnchor/src/PrimitiveAnchor.js.map +1 -1
  26. package/build/primitives/PrimitiveAnchor/src/PrimitiveAnchor.mjs +4 -2
  27. package/build/primitives/PrimitiveAnchor/src/PrimitiveAnchor.mjs.map +1 -1
  28. package/build/primitives/PrimitiveButton/src/PrimitiveButton.js +5 -3
  29. package/build/primitives/PrimitiveButton/src/PrimitiveButton.js.map +1 -1
  30. package/build/primitives/PrimitiveButton/src/PrimitiveButton.mjs +5 -3
  31. package/build/primitives/PrimitiveButton/src/PrimitiveButton.mjs.map +1 -1
  32. package/build/select/Select.js +2 -2
  33. package/build/select/Select.js.map +1 -1
  34. package/build/select/Select.mjs +1 -1
  35. package/build/styles/avatarLayout/AvatarLayout.css +0 -11
  36. package/build/styles/button/Button.css +15 -207
  37. package/build/styles/main.css +11 -226
  38. package/build/styles/nudge/Nudge.css +11 -0
  39. package/build/types/avatarLayout/AvatarLayout.d.ts.map +1 -1
  40. package/build/types/button/Button.d.ts +23 -2
  41. package/build/types/button/Button.d.ts.map +1 -1
  42. package/build/types/button/index.d.ts +2 -2
  43. package/build/types/button/index.d.ts.map +1 -1
  44. package/build/types/link/Link.d.ts +2 -2
  45. package/build/types/link/Link.d.ts.map +1 -1
  46. package/build/types/nudge/Nudge.d.ts +1 -1
  47. package/build/types/nudge/Nudge.d.ts.map +1 -1
  48. package/build/types/primitives/PrimitiveAnchor/src/PrimitiveAnchor.d.ts.map +1 -1
  49. package/build/types/primitives/PrimitiveAnchor/src/PrimitiveAnchor.types.d.ts +2 -6
  50. package/build/types/primitives/PrimitiveAnchor/src/PrimitiveAnchor.types.d.ts.map +1 -1
  51. package/build/types/primitives/PrimitiveButton/src/PrimitiveButton.d.ts.map +1 -1
  52. package/build/types/test-utils/story-config.d.ts +1 -1
  53. package/build/types/test-utils/story-config.d.ts.map +1 -1
  54. package/build/upload/steps/completeStep/completeStep.js +2 -2
  55. package/build/upload/steps/completeStep/completeStep.js.map +1 -1
  56. package/build/upload/steps/completeStep/completeStep.mjs +1 -1
  57. package/build/upload/steps/processingStep/processingStep.js +2 -2
  58. package/build/upload/steps/processingStep/processingStep.js.map +1 -1
  59. package/build/upload/steps/processingStep/processingStep.mjs +1 -1
  60. package/build/uploadInput/UploadInput.js +3 -3
  61. package/build/uploadInput/UploadInput.js.map +1 -1
  62. package/build/uploadInput/UploadInput.mjs +1 -1
  63. package/package.json +3 -3
  64. package/src/alert/Alert.tests.story.tsx +1 -1
  65. package/src/avatarLayout/AvatarLayout.css +0 -11
  66. package/src/avatarLayout/AvatarLayout.less +1 -18
  67. package/src/avatarLayout/AvatarLayout.tsx +2 -10
  68. package/src/button/Button.css +15 -207
  69. package/src/button/Button.less +14 -214
  70. package/src/button/Button.spec.tsx +224 -188
  71. package/src/button/Button.story.tsx +135 -701
  72. package/src/button/Button.tsx +131 -97
  73. package/src/button/__snapshots__/Button.spec.tsx.snap +309 -0
  74. package/src/button/index.ts +3 -2
  75. package/src/field/Field.story.tsx +1 -1
  76. package/src/flowNavigation/__snapshots__/FlowNavigation.spec.js.snap +2 -1
  77. package/src/inputs/SelectInput.story.tsx +1 -1
  78. package/src/label/Label.story.tsx +1 -1
  79. package/src/link/Link.tsx +6 -15
  80. package/src/main.css +11 -226
  81. package/src/main.less +0 -1
  82. package/src/nudge/Nudge.css +11 -0
  83. package/src/nudge/Nudge.less +3 -0
  84. package/src/nudge/Nudge.story.tsx +10 -0
  85. package/src/nudge/Nudge.tsx +2 -1
  86. package/src/primitives/PrimitiveAnchor/src/PrimitiveAnchor.tsx +8 -2
  87. package/src/primitives/PrimitiveAnchor/src/PrimitiveAnchor.types.ts +2 -7
  88. package/src/primitives/PrimitiveAnchor/test/PrimitiveAnchor.spec.tsx +3 -1
  89. package/src/primitives/PrimitiveButton/src/PrimitiveButton.tsx +12 -4
  90. package/src/primitives/PrimitiveButton/test/PrimitiveButton.spec.tsx +13 -16
  91. package/src/test-utils/Parameters.d.ts +1 -9
  92. package/src/test-utils/story-config.ts +1 -10
  93. package/build/button/Button.resolver.js +0 -74
  94. package/build/button/Button.resolver.js.map +0 -1
  95. package/build/button/Button.resolver.mjs +0 -72
  96. package/build/button/Button.resolver.mjs.map +0 -1
  97. package/build/button/LegacyButton.js +0 -114
  98. package/build/button/LegacyButton.js.map +0 -1
  99. package/build/button/LegacyButton.mjs +0 -112
  100. package/build/button/LegacyButton.mjs.map +0 -1
  101. package/build/styles/button/Button.vars.css +0 -46
  102. package/build/styles/button/LegacyButton.css +0 -23
  103. package/build/types/button/Button.resolver.d.ts +0 -33
  104. package/build/types/button/Button.resolver.d.ts.map +0 -1
  105. package/build/types/button/Button.types.d.ts +0 -67
  106. package/build/types/button/Button.types.d.ts.map +0 -1
  107. package/build/types/button/LegacyButton.d.ts +0 -30
  108. package/build/types/button/LegacyButton.d.ts.map +0 -1
  109. package/src/button/Button.resolver.tsx +0 -73
  110. package/src/button/Button.tests.story.tsx +0 -27
  111. package/src/button/Button.types.ts +0 -94
  112. package/src/button/Button.vars.css +0 -46
  113. package/src/button/Button.vars.less +0 -60
  114. package/src/button/LegacyButton.css +0 -23
  115. package/src/button/LegacyButton.less +0 -24
  116. package/src/button/LegacyButton.spec.tsx +0 -147
  117. package/src/button/LegacyButton.story.tsx +0 -220
  118. package/src/button/LegacyButton.tsx +0 -160
@@ -1,224 +1,24 @@
1
- @import './Button.vars.less';
1
+ @import (reference) "../../node_modules/@transferwise/neptune-css/src/less/mixins/_logical-properties.less";
2
2
 
3
- /* Button Styles */
4
- .wds-Button {
5
- display: inline-flex;
6
- flex: none;
7
- width: auto;
8
- align-items: center;
9
- justify-content: center;
10
- vertical-align: middle;
11
- text-align: center;
12
- text-decoration: none;
13
- white-space: nowrap;
14
- word-wrap: break-word;
15
- appearance: none;
16
- background-color: var(--Button-background);
17
- border: none;
18
- border-radius: var(--Button-border-radius);
19
- color: var(--Button-color);
20
- cursor: pointer;
21
- transition: color, background-color var(--Button-transition-duration) var(--Button-transition-easing);
3
+ .np-btn {
4
+ position: relative;
22
5
 
23
- &:hover {
24
- background-color: var(--Button-background-hover);
25
- }
26
-
27
- &:active {
28
- background-color: var(--Button-background-active);
29
- }
30
-
31
- &.wds-Button--disabled,
32
- &:disabled {
33
- // The declarations below are necessary as design specs for
34
- // the button's disabled state have changed. This is to be
35
- // further investigated at a later stage.
36
- // @see https://transferwise.atlassian.net/browse/DS-7386
37
- filter: none;
38
- mix-blend-mode: luminosity;
39
- opacity: 0.45;
40
- cursor: not-allowed;
41
-
42
- &,
43
- &:hover,
44
- &:active {
45
- background-color: var(--Button-background);
46
- }
47
- }
48
-
49
- // Priority modifiers
50
- &--secondary {
51
- --Button-background: var(--Button-secondary-background);
52
- --Button-background-hover: var(--Button-secondary-background-hover);
53
- --Button-background-active: var(--Button-secondary-background-active);
54
- --Button-color: var(--Button-secondary-color);
55
- }
56
-
57
- &--tertiary {
58
- --Button-background: var(--Button-tertiary-background);
59
- --Button-background-hover: var(--Button-tertiary-background-hover);
60
- --Button-background-active: var(--Button-tertiary-background-active);
61
- --Button-color: var(--Button-tertiary-color);
62
-
63
- .wds-Button-icon--end {
64
- color: var(--color-interactive-primary);
65
- }
66
- }
67
-
68
- &--minimal {
69
- --Button-background: var(--Button-minimal-background);
70
- --Button-background-hover: var(--Button-minimal-background-hover);
71
- --Button-background-active: var(--Button-minimal-background-active);
72
- --Button-color: var(--Button-minimal-color);
73
-
74
- text-decoration: underline;
75
- text-underline-offset: 3px;
76
- text-decoration-thickness: 1px;
77
- }
78
-
79
- // Negative styles
80
- &--negative {
81
- &.wds-Button--primary {
82
- --Button-background: var(--Button-primary-negative-background);
83
- --Button-background-hover: var(--Button-primary-negative-background-hover);
84
- --Button-background-active: var(--Button-primary-negative-background-active);
85
- --Button-color: var(--Button-primary-negative-color);
86
- }
87
-
88
- &.wds-Button--secondary {
89
- --Button-background: var(--Button-secondary-negative-background);
90
- --Button-background-hover: var(--Button-secondary-negative-background-hover);
91
- --Button-background-active: var(--Button-secondary-negative-background-active);
92
- --Button-color: var(--Button-secondary-negative-color);
93
- }
94
- }
95
-
96
- // Size modifiers
97
- &--large {
98
- padding: var(--Button-large-padding);
99
- }
100
-
101
- &--medium {
102
- padding: var(--Button-medium-padding);
103
-
104
- &:has(.wds-Button-avatars){
105
- padding-inline-start: var(--size-8);
106
- }
107
-
108
- &:has(.wds-Button-icon--end){
109
- padding-inline-end: var(--size-8);
110
- }
111
-
112
- .wds-Button-icon--start{
113
- margin-inline-end: var(--Button-label-gap);
114
- }
115
- }
116
-
117
- &--small {
118
- padding: var(--Button-small-padding);
119
-
120
- &:has(.wds-Button-icon--start){
121
- padding-inline-start: var(--size-8);
122
- }
123
-
124
- &:has(.wds-Button-icon--end){
125
- padding-inline-end: var(--size-8);
126
- }
127
- }
128
-
129
-
130
-
131
- // Width modifiers
132
- &--block {
133
- width: 100%;
134
- }
135
-
136
- &-avatars {
137
- display: inline-flex;
138
-
139
- .np-avatar-view .np-avatar-view-content {
140
- color: var(--Button-color);
141
- }
142
- }
143
-
144
- // Icon styles
145
- &-icon {
146
- display: inline-block;
6
+ &.np-btn-block > span.btn-loader {
7
+ position: absolute;
8
+ .left(var(--size-24));
147
9
  }
148
10
 
149
- &-content {
150
- position: relative;
151
-
152
- &--loading {
153
- .wds-Button-label,
154
- .wds-Button-media,
155
- .wds-Button-icon {
156
- visibility: hidden;
157
- opacity: 0;
158
- }
159
- }
11
+ &.np-btn-xs > span.btn-loader {
12
+ background-size: 16px 16px;
160
13
  }
161
14
 
162
- &-label {
163
- display: flex;
164
- justify-content: center;
165
- align-items: center;
166
- gap: var(--Button-label-gap);
167
- position: relative;
15
+ &.np-btn-xs.btn-block > span.btn-loader {
16
+ top: 0;
168
17
  }
169
18
 
170
- &-loader {
171
- position: absolute;
172
- width: 100%;
173
- height: 100%;
174
-
175
- .process-circle {
176
- stroke: var(--Button-color);
177
- }
19
+ // we do not want pointer events disabled as that
20
+ // prevents the custom cursor to be shown
21
+ &.disabled[class] {
22
+ pointer-events: auto;
178
23
  }
179
24
  }
180
-
181
- /* Avatar border transparency */
182
- /* dark buttons get 20% transparency, light buttons get 12% */
183
- .wds-Button-avatars {
184
- .np-circle {
185
- --circle-border-color: color-mix(in srgb, var(--Button-color) 20%, transparent);
186
-
187
- .wds-Button--secondary.wds-Button--negative &,
188
- .wds-Button--secondary &,
189
- .wds-Button--tertiary &,
190
- .wds-Button--minimal & {
191
- .np-theme-personal:not(.np-theme-personal--dark, .np-theme-personal--forest-green, .np-theme-personal--bright-green) & {
192
- --circle-border-color: color-mix(in srgb, var(--Button-color) 12%, transparent);
193
- }
194
- }
195
- .wds-Button--primary &,
196
- .wds-Button--primary.wds-Button--negative & {
197
- .np-theme-personal--dark &,
198
- .np-theme-personal--forest-green & {
199
- --circle-border-color: color-mix(in srgb, var(--Button-color) 12%, transparent);
200
- }
201
- }
202
- .wds-Button--tertiary &,
203
- .wds-Button--minimal & {
204
- .np-theme-personal--bright-green & {
205
- --circle-border-color: color-mix(in srgb, var(--Button-color) 12%, transparent);
206
- }
207
- }
208
- }
209
- }
210
-
211
- :dir(rtl) {
212
- .wds-Button {
213
- .tw-icon-chevron-right,
214
- .tw-icon-arrow-right,
215
- .tw-icon-chevron-left,
216
- .tw-icon-arrow-left,
217
- .tw-icon-arrow-diagonal-down,
218
- .tw-icon-arrow-diagonal-up,
219
- .tw-icon-list,
220
- .tw-icon-activity {
221
- scale: -1 1;
222
- }
223
- }
224
- }
@@ -1,210 +1,246 @@
1
- import { render, screen } from '../test-utils';
2
- import Button, { ButtonProps } from './Button.resolver';
1
+ import { createRef } from 'react';
2
+
3
+ import { ControlType, Type, Priority, Size } from '../common';
4
+ import { render, screen, userEvent } from '../test-utils';
5
+
6
+ import Button from '.';
7
+ import messages from '../i18n/commonMessages/Button.messages';
8
+ import { ButtonReferenceType } from './Button';
9
+
10
+ const { ACCENT, POSITIVE, NEGATIVE } = ControlType;
11
+ const { PAY, LINK, DANGER } = Type;
12
+ const { PRIMARY, SECONDARY, TERTIARY } = Priority;
13
+ const { SMALL, MEDIUM, LARGE } = Size;
3
14
 
4
15
  describe('Button', () => {
5
- const legacyProps: ButtonProps = {
6
- as: 'button',
7
- priority: 'primary',
8
- size: 'md',
9
- type: 'accent',
16
+ // eslint-disable-next-line no-console
17
+ const originalWarn = console.warn;
18
+ let mockedWarn: typeof originalWarn;
19
+
20
+ const props = {
21
+ onClick: jest.fn(),
22
+ onFocus: jest.fn(),
23
+ onBlur: jest.fn(),
24
+ children: 'Send money',
10
25
  };
11
26
 
12
- const newProps: ButtonProps = {
13
- as: 'button',
14
- v2: true,
15
- priority: 'primary',
16
- size: 'md',
17
- };
27
+ beforeAll(() => {
28
+ mockedWarn = jest.fn().mockImplementation((args) => {
29
+ if (typeof args !== 'string' || !args.startsWith('Button has deprecated the')) {
30
+ originalWarn(args);
31
+ }
32
+ });
33
+ // eslint-disable-next-line no-console
34
+ console.warn = mockedWarn;
35
+ });
18
36
 
19
- const testId = 'new-button';
37
+ beforeEach(jest.clearAllMocks);
20
38
 
21
- it('renders LegacyButton when v2 is false', () => {
22
- render(<Button {...legacyProps}>Legacy Button</Button>);
23
- const button = screen.getByText('Legacy Button');
24
- expect(button).toBeInTheDocument();
25
- expect(button).toHaveClass('btn btn-md np-btn np-btn-md btn-accent btn-priority-1');
39
+ afterAll(() => {
40
+ // eslint-disable-next-line no-console
41
+ console.warn = originalWarn;
26
42
  });
27
43
 
28
- it('renders the new Button when v2 is true', () => {
29
- render(
30
- <Button data-testid={testId} {...newProps}>
31
- New Button
32
- </Button>,
33
- );
34
- const button = screen.getByTestId('new-button');
35
- expect(button).toBeInTheDocument();
36
- expect(button).toHaveClass('wds-Button wds-Button--medium wds-Button--primary');
44
+ describe('by default', () => {
45
+ it('renders the text', () => {
46
+ render(<Button {...props} />);
47
+ expect(screen.getByText(props.children)).toBeInTheDocument();
48
+ });
49
+
50
+ it('set `ref` to be true on Button', () => {
51
+ const reference = createRef<ButtonReferenceType>();
52
+
53
+ expect(reference.current).toBeFalsy();
54
+ render(<Button ref={reference}>Click me!</Button>);
55
+ expect(reference.current).toBeTruthy();
56
+ });
57
+
58
+ it('is not disabled', () => {
59
+ render(<Button {...props} />);
60
+ expect(screen.getByRole('button')).toBeEnabled();
61
+ });
62
+
63
+ it('renders a medium button of type accent and priority primary', () => {
64
+ expect(render(<Button {...props} />).container).toMatchSnapshot();
65
+ });
66
+
67
+ it('renders an anchor tag with button styles of type accent and priority primary', () => {
68
+ expect(render(<Button {...props} as="a" href="#" />).container).toMatchSnapshot();
69
+ });
37
70
  });
38
71
 
39
- describe('render as HTML anchor', () => {
40
- const href = 'https://example.com';
41
- const name = 'Button as link';
72
+ describe('button attributes', () => {
73
+ it('sets the htmlType if set', () => {
74
+ render(<Button {...props} htmlType="submit" />);
75
+ expect(screen.getByRole('button')).toHaveAttribute('type', 'submit');
76
+ });
42
77
 
43
- describe('v2', () => {
44
- it('should render as button by default', () => {
45
- render(<Button v2>{name}</Button>);
46
- expect(screen.getByRole('button', { name })).toBeInTheDocument();
47
- });
48
- it('should render as link if href is provided', () => {
49
- render(
50
- <Button v2 href={href}>
51
- {name}
52
- </Button>,
53
- );
54
- expect(screen.getByRole('link', { name })).toHaveAttribute('href', href);
55
- });
78
+ it('passes through custom classes if set', () => {
79
+ render(<Button {...props} className="donkeysarethebest" />);
80
+ expect(screen.getByRole('button')).toHaveClass('donkeysarethebest');
81
+ });
56
82
 
57
- it('should render as link if `as="a"` is provided', () => {
58
- render(
59
- <Button v2 as="a">
60
- {name}
61
- </Button>,
62
- );
63
- expect(screen.getByText(name).closest('a')).toBeInTheDocument();
64
- });
83
+ it('passes through aria-label if set', () => {
84
+ render(<Button {...props} aria-label="unique label" />);
85
+ const loadingButton = screen.getByLabelText('unique label');
86
+ expect(loadingButton).toBeInTheDocument();
87
+ });
88
+ });
65
89
 
66
- it('should render as link if href is set and `as="button"`', () => {
67
- render(
68
- <Button v2 href={href} as="button">
69
- {name}
70
- </Button>,
71
- );
72
- expect(screen.getByRole('link', { name })).toHaveAttribute('href', href);
73
- });
90
+ describe('onClick', () => {
91
+ it('calls onClick when clicked', async () => {
92
+ render(<Button {...props} />);
93
+ await userEvent.click(screen.getByRole('button'));
94
+ expect(props.onClick).toHaveBeenCalledTimes(1);
95
+ });
74
96
 
75
- describe('disabled mode', () => {
76
- describe('button', () => {
77
- it('should not be disabled by default', () => {
78
- render(<Button v2>{name}</Button>);
79
- const button = screen.getByRole('button');
80
- expect(button).toBeEnabled();
81
- expect(button).not.toHaveAttribute('aria-disabled');
82
- });
83
-
84
- it('should respect disabled mode and set it only via the `disabled` attribute', () => {
85
- render(
86
- <Button v2 disabled>
87
- {name}
88
- </Button>,
89
- );
90
- const button = screen.getByRole('button');
91
- expect(button).toBeDisabled();
92
- expect(button).not.toHaveAttribute('aria-disabled');
93
- });
94
- });
95
-
96
- describe('anchor', () => {
97
- it('should not be disabled by default', () => {
98
- render(
99
- <Button v2 href="wise.com">
100
- {name}
101
- </Button>,
102
- );
103
- const button = screen.getByRole('link');
104
- expect(button).toBeEnabled();
105
- expect(button).not.toHaveAttribute('aria-disabled');
106
- });
107
-
108
- it('should respect disabled mode', () => {
109
- render(
110
- <Button v2 href="wise.com" disabled>
111
- {name}
112
- </Button>,
113
- );
114
- const button = screen.getByRole('link');
115
- expect(button).toHaveAttribute('aria-disabled');
116
- expect(button).toBeEnabled();
117
- expect(button).not.toHaveAttribute('href');
118
- });
119
- });
120
- });
97
+ it('does not call onClick when clicked if disabled', async () => {
98
+ render(<Button {...props} disabled />);
99
+ await userEvent.click(screen.getByRole('button'));
100
+ expect(props.onClick).toHaveBeenCalledTimes(0);
101
+ });
121
102
 
122
- describe('loading mode', () => {
123
- describe('button', () => {
124
- it('should not be loading by default', () => {
125
- render(<Button v2>{name}</Button>);
126
- const button = screen.getByRole('button');
127
- expect(button).toBeEnabled();
128
- expect(button).not.toHaveAttribute('aria-busy');
129
- });
130
-
131
- it('should respect loading mode', () => {
132
- render(
133
- <Button v2 loading>
134
- {name}
135
- </Button>,
136
- );
137
- const button = screen.getByRole('button');
138
- expect(button).toHaveAttribute('aria-busy');
139
- // the `disabled` attribute is not set to keep the button
140
- // focusable but aria attribute is defined to make it
141
- // announced properly by the assistive tech
142
- expect(button).toBeEnabled();
143
- expect(button).toHaveAttribute('aria-disabled');
144
- });
145
- });
146
-
147
- describe('anchor', () => {
148
- it('should not be loading by default', () => {
149
- render(
150
- <Button v2 href="wise.com">
151
- {name}
152
- </Button>,
153
- );
154
- const button = screen.getByRole('link');
155
- expect(button).toBeEnabled();
156
- expect(button).not.toHaveAttribute('aria-busy');
157
- expect(button).not.toHaveAttribute('aria-disabled');
158
- });
159
-
160
- it('should respect loading mode', () => {
161
- render(
162
- <Button v2 loading href="wise.com">
163
- {name}
164
- </Button>,
165
- );
166
- const button = screen.getByRole('link');
167
- expect(button).toHaveAttribute('aria-busy');
168
- // the `disabled` attribute is not set to keep the button
169
- // focusable but aria attribute is defined to make it
170
- // announced properly by the assistive tech
171
- expect(button).toBeEnabled();
172
- expect(button).toHaveAttribute('aria-disabled');
173
- expect(button).not.toHaveAttribute('href');
174
- });
175
- });
176
- });
103
+ it('does not call onClick when clicked if loading', async () => {
104
+ render(<Button {...props} loading />);
105
+ await userEvent.click(screen.getByRole('button'));
106
+ expect(props.onClick).toHaveBeenCalledTimes(0);
177
107
  });
178
108
  });
179
109
 
180
- it('does not set type when type is in LegacyButtonType', () => {
181
- const legacyTypeProps: ButtonProps = {
182
- ...newProps,
183
- // @ts-expect-error here we intentionally set wrong value to test `type` attr in DOM
184
- type: 'accent',
185
- };
186
- render(
187
- <Button data-testid={testId} {...legacyTypeProps}>
188
- Button Type
189
- </Button>,
190
- );
191
- const button = screen.getByTestId('new-button');
192
- expect(button).toBeInTheDocument();
193
- expect(button).not.toHaveAttribute('type');
110
+ describe('onFocus and onBlur', () => {
111
+ it('calls both handlers by default', async () => {
112
+ render(<Button {...props} />);
113
+ await userEvent.tab();
114
+ expect(props.onFocus).toHaveBeenCalledTimes(1);
115
+ await userEvent.tab();
116
+ expect(props.onFocus).toHaveBeenCalledTimes(1);
117
+ });
118
+
119
+ it('does not call either handler if disabled', async () => {
120
+ render(<Button {...props} disabled />);
121
+ await userEvent.tab();
122
+ expect(props.onFocus).not.toHaveBeenCalled();
123
+ await userEvent.tab();
124
+ expect(props.onFocus).not.toHaveBeenCalled();
125
+ });
126
+
127
+ it('calls both handlers if loading', async () => {
128
+ render(<Button {...props} loading />);
129
+ await userEvent.tab();
130
+ expect(props.onFocus).toHaveBeenCalledTimes(1);
131
+ await userEvent.tab();
132
+ expect(props.onFocus).toHaveBeenCalledTimes(1);
133
+ });
194
134
  });
195
135
 
196
- it('sets type when type is not in LegacyButtonType', () => {
197
- const buttonTypeProps: ButtonProps = {
198
- ...newProps,
199
- type: 'submit',
200
- };
201
- render(
202
- <Button data-testid={testId} {...buttonTypeProps}>
203
- Submit Button
204
- </Button>,
205
- );
206
- const button = screen.getByTestId('new-button');
207
- expect(button).toBeInTheDocument();
208
- expect(button).toHaveAttribute('type', 'submit');
136
+ describe('sizes', () => {
137
+ it('renders small buttons', () => {
138
+ expect(render(<Button {...props} size={SMALL} />).container).toMatchSnapshot();
139
+ });
140
+
141
+ it('renders medium buttons', () => {
142
+ expect(render(<Button {...props} size={MEDIUM} />).container).toMatchSnapshot();
143
+ });
144
+
145
+ it('renders large buttons', () => {
146
+ expect(render(<Button {...props} size={LARGE} />).container).toMatchSnapshot();
147
+ });
148
+ });
149
+
150
+ describe('types', () => {
151
+ it('renders accent buttons', () => {
152
+ expect(render(<Button {...props} type={ACCENT} />).container).toMatchSnapshot();
153
+ });
154
+
155
+ it('renders positive buttons', () => {
156
+ expect(render(<Button {...props} type={POSITIVE} />).container).toMatchSnapshot();
157
+ });
158
+
159
+ it('renders negative buttons', () => {
160
+ expect(render(<Button {...props} type={NEGATIVE} />).container).toMatchSnapshot();
161
+ });
162
+ });
163
+
164
+ describe('priorities', () => {
165
+ it('renders primary buttons', () => {
166
+ [ACCENT, POSITIVE, NEGATIVE].forEach((type) =>
167
+ expect(
168
+ render(<Button {...props} priority={PRIMARY} type={type} />).container,
169
+ ).toMatchSnapshot(),
170
+ );
171
+ });
172
+
173
+ it('renders secondary buttons', () => {
174
+ [ACCENT, POSITIVE, NEGATIVE].forEach((type) =>
175
+ expect(
176
+ render(<Button {...props} priority={SECONDARY} type={type} />).container,
177
+ ).toMatchSnapshot(),
178
+ );
179
+ });
180
+
181
+ it('renders tertiary buttons', () => {
182
+ expect(
183
+ render(<Button {...props} priority={TERTIARY} type={ACCENT} />).container,
184
+ ).toMatchSnapshot();
185
+ });
186
+
187
+ it('defaults tertiary buttons to secondary for positive buttons', () => {
188
+ [POSITIVE, NEGATIVE].forEach((type) =>
189
+ expect(
190
+ render(<Button {...props} priority={TERTIARY} type={type} />).container,
191
+ ).toMatchSnapshot(),
192
+ );
193
+ });
194
+ });
195
+
196
+ describe('other states', () => {
197
+ it('renders as loading if `loading` is true', () => {
198
+ render(<Button {...props} loading />);
199
+ const button = screen.queryByRole('button', {
200
+ name: messages.loadingAriaLabel.defaultMessage,
201
+ });
202
+ expect(button).toBeInTheDocument();
203
+ expect(button).toBeEnabled();
204
+ expect(button).toHaveClass('btn-loading');
205
+ expect(button).toHaveAttribute('aria-disabled', 'true');
206
+ expect(button).toHaveAttribute('aria-busy', 'true');
207
+ expect(button).toHaveAttribute('aria-live', 'polite');
208
+ expect(screen.getByTestId('ButtonProgressIndicator')).toBeInTheDocument();
209
+ });
210
+
211
+ it('disables the button', () => {
212
+ render(<Button {...props} disabled />);
213
+ const button = screen.queryByRole('button');
214
+ expect(button).toBeDisabled();
215
+ expect(button).toHaveClass('disabled');
216
+ expect(button).toHaveAttribute('aria-disabled', 'false');
217
+ expect(button).toHaveAttribute('aria-busy', 'false');
218
+ expect(button).toHaveAttribute('aria-live', 'off');
219
+ });
220
+
221
+ it('renders as block if block is true', () => {
222
+ expect(render(<Button {...props} block />).container).toMatchSnapshot();
223
+ });
224
+ });
225
+
226
+ describe('deprecated types', () => {
227
+ it('renders primary as accent buttons and logs a warning', () => {
228
+ expect(render(<Button {...props} type={PRIMARY} />).container).toMatchSnapshot();
229
+ expect(mockedWarn).toHaveBeenCalledTimes(1);
230
+ });
231
+
232
+ it('renders pay as positive buttons and logs a warning', () => {
233
+ expect(render(<Button {...props} type={PAY} />).container).toMatchSnapshot();
234
+ expect(mockedWarn).toHaveBeenCalledTimes(1);
235
+ });
236
+
237
+ it('renders danger as negative buttons with priority secondary and logs a warning', () => {
238
+ expect(render(<Button {...props} type={DANGER} />).container).toMatchSnapshot();
239
+ expect(mockedWarn).toHaveBeenCalledTimes(1);
240
+ });
241
+
242
+ it('renders link as accent buttons with priority tertiary and logs a warning', () => {
243
+ expect(render(<Button {...props} type={LINK} />).container).toMatchSnapshot();
244
+ });
209
245
  });
210
246
  });