@hyphen/hyphen-components 2.16.3 → 2.18.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.
- package/dist/components/Button/Button.d.ts +5 -1
- package/dist/components/Button/Button.stories.d.ts +1 -0
- package/dist/css/utilities.css +19 -1
- package/dist/css/variables.css +5 -2
- package/dist/hyphen-components.cjs.development.js +10 -20
- package/dist/hyphen-components.cjs.development.js.map +1 -1
- package/dist/hyphen-components.cjs.production.min.js +1 -1
- package/dist/hyphen-components.cjs.production.min.js.map +1 -1
- package/dist/hyphen-components.esm.js +10 -20
- package/dist/hyphen-components.esm.js.map +1 -1
- package/dist/lib/tokens.d.ts +1 -1
- package/package.json +2 -2
- package/src/components/Button/Button.mdx +7 -0
- package/src/components/Button/Button.stories.tsx +19 -3
- package/src/components/Button/Button.test.tsx +241 -293
- package/src/components/Button/Button.tsx +13 -2
|
@@ -4,457 +4,405 @@ import { fireEvent, render, screen } from '@testing-library/react';
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
+
const renderButton = (props = {}) => render(<Button {...props} />);
|
|
8
|
+
const getButton = (text: string): HTMLButtonElement =>
|
|
9
|
+
screen.getByText(text).closest('button') as HTMLButtonElement;
|
|
10
|
+
const getAnchor = (text: string): HTMLAnchorElement =>
|
|
11
|
+
screen.getByText(text).closest('a') as HTMLAnchorElement;
|
|
12
|
+
|
|
7
13
|
describe('Button', () => {
|
|
8
|
-
describe('
|
|
9
|
-
test('is set to button', () => {
|
|
10
|
-
|
|
14
|
+
describe('HTML Button Type', () => {
|
|
15
|
+
test('is set to button by default', () => {
|
|
16
|
+
renderButton({ children: 'Button' });
|
|
11
17
|
const testBtn = screen.getByRole('button');
|
|
12
|
-
expect(testBtn.
|
|
18
|
+
expect(testBtn).toHaveAttribute('type', 'button');
|
|
13
19
|
});
|
|
14
20
|
|
|
15
|
-
test('is set to
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
test('is set to submit if specified', () => {
|
|
22
|
+
renderButton({ type: 'submit', children: 'Submit Button' });
|
|
18
23
|
const testBtn = screen.getByRole('button');
|
|
19
|
-
expect(testBtn.
|
|
24
|
+
expect(testBtn).toHaveAttribute('type', 'submit');
|
|
20
25
|
});
|
|
21
26
|
|
|
22
|
-
test('is set to
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
test('is set to reset if specified', () => {
|
|
28
|
+
renderButton({ type: 'reset', children: 'Reset Button' });
|
|
25
29
|
const testBtn = screen.getByRole('button');
|
|
26
|
-
expect(testBtn.
|
|
30
|
+
expect(testBtn).toHaveAttribute('type', 'reset');
|
|
27
31
|
});
|
|
28
32
|
|
|
29
|
-
test('is not set if
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
);
|
|
33
|
+
test('is not set if as prop is an anchor tag', () => {
|
|
34
|
+
renderButton({
|
|
35
|
+
as: 'a',
|
|
36
|
+
href: 'https://www.hyphen.ai',
|
|
37
|
+
children: 'link button',
|
|
38
|
+
});
|
|
35
39
|
const testBtn = screen.getByText('link button').parentElement;
|
|
36
40
|
expect(testBtn).not.toHaveAttribute('type');
|
|
37
41
|
});
|
|
38
42
|
});
|
|
39
43
|
|
|
40
|
-
describe('
|
|
41
|
-
test('
|
|
42
|
-
|
|
44
|
+
describe('With Icon', () => {
|
|
45
|
+
test('renders an icon prefix if specified', () => {
|
|
46
|
+
renderButton({ iconPrefix: 'alarm', children: 'Alarm Button' });
|
|
43
47
|
expect(screen.getByTestId('prefixIcon')).toBeInTheDocument();
|
|
44
48
|
});
|
|
45
49
|
|
|
46
|
-
test('
|
|
47
|
-
|
|
50
|
+
test('renders an icon suffix if specified', () => {
|
|
51
|
+
renderButton({ iconSuffix: 'alarm', children: 'Alarm Button' });
|
|
48
52
|
expect(screen.getByTestId('suffixIcon')).toBeInTheDocument();
|
|
49
53
|
});
|
|
50
54
|
|
|
51
|
-
test('
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
);
|
|
55
|
+
test('renders icon prefix and suffix if specified', () => {
|
|
56
|
+
renderButton({
|
|
57
|
+
iconPrefix: 'alarm',
|
|
58
|
+
iconSuffix: 'check',
|
|
59
|
+
children: 'Suffix Prefix Icon Button',
|
|
60
|
+
});
|
|
57
61
|
expect(screen.getByTestId('prefixIcon')).toBeInTheDocument();
|
|
58
62
|
expect(screen.getByTestId('suffixIcon')).toBeInTheDocument();
|
|
59
63
|
});
|
|
60
64
|
});
|
|
61
65
|
|
|
62
66
|
describe('Sizes', () => {
|
|
63
|
-
BUTTON_SIZES.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
BUTTON_SIZES.forEach((size) => {
|
|
68
|
+
test(`it has a ${size} class applied to it`, () => {
|
|
69
|
+
renderButton({ size, children: `${size} Button` });
|
|
70
|
+
const btn = getButton(`${size} Button`);
|
|
71
|
+
expect(btn).toHaveClass(`size-${size}`);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
69
74
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
test('applies responsive classes', () => {
|
|
76
|
+
renderButton({
|
|
77
|
+
size: { base: 'lg', tablet: 'sm', desktop: 'md', hd: 'lg' },
|
|
78
|
+
children: 'button',
|
|
79
|
+
});
|
|
80
|
+
const btn = getButton('button');
|
|
81
|
+
expect(btn).toHaveClass(
|
|
82
|
+
'size-lg',
|
|
83
|
+
'size-sm-tablet',
|
|
84
|
+
'size-md-desktop',
|
|
85
|
+
'size-lg-hd'
|
|
80
86
|
);
|
|
81
|
-
|
|
82
|
-
const btn = screen.getByText('button').closest('button');
|
|
83
|
-
|
|
84
|
-
expect(btn?.getAttribute('class')).toContain('size-lg');
|
|
85
|
-
expect(btn?.getAttribute('class')).toContain('size-sm-tablet');
|
|
86
|
-
expect(btn?.getAttribute('class')).toContain('size-md-desktop');
|
|
87
|
-
expect(btn?.getAttribute('class')).toContain('size-lg-hd');
|
|
88
87
|
});
|
|
89
88
|
});
|
|
90
89
|
|
|
91
90
|
describe('Variants', () => {
|
|
92
|
-
BUTTON_VARIANTS.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
expect(btn?.getAttribute('class')).toContain(variant);
|
|
100
|
-
});
|
|
101
|
-
})
|
|
102
|
-
);
|
|
91
|
+
BUTTON_VARIANTS.forEach((variant) => {
|
|
92
|
+
test(`it has a ${variant} class applied to it`, () => {
|
|
93
|
+
renderButton({ variant, children: `${variant} Button` });
|
|
94
|
+
const btn = getButton(`${variant} Button`);
|
|
95
|
+
expect(btn).toHaveClass(variant);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
103
98
|
});
|
|
104
99
|
|
|
105
100
|
describe('Callback Handling', () => {
|
|
106
101
|
describe('onClick', () => {
|
|
107
|
-
test('
|
|
102
|
+
test('fires onClick callback', () => {
|
|
108
103
|
const mockedHandleClick = jest.fn();
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (buttonElement) {
|
|
114
|
-
fireEvent.click(buttonElement);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
expect(mockedHandleClick).toBeCalledTimes(1);
|
|
104
|
+
renderButton({ onClick: mockedHandleClick, children: 'Click' });
|
|
105
|
+
const buttonElement: HTMLButtonElement = getButton('Click');
|
|
106
|
+
fireEvent.click(buttonElement);
|
|
107
|
+
expect(mockedHandleClick).toHaveBeenCalledTimes(1);
|
|
118
108
|
});
|
|
119
109
|
|
|
120
|
-
test('
|
|
110
|
+
test('does not fire function if onClick callback not provided', () => {
|
|
121
111
|
const mockedHandleClick = jest.fn();
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (buttonElement) {
|
|
127
|
-
fireEvent.click(buttonElement);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
expect(mockedHandleClick).toBeCalledTimes(0);
|
|
112
|
+
renderButton({ children: 'Click' });
|
|
113
|
+
const buttonElement = getButton('Click');
|
|
114
|
+
fireEvent.click(buttonElement);
|
|
115
|
+
expect(mockedHandleClick).toHaveBeenCalledTimes(0);
|
|
131
116
|
});
|
|
132
117
|
|
|
133
|
-
test('
|
|
118
|
+
test('prevents default event behavior if specified by onClick', () => {
|
|
134
119
|
const mockedHandleClick = jest.fn((event) => event.preventDefault());
|
|
135
|
-
const mockedNavigate = jest.fn(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
fireEvent.click(buttonElement);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
expect(mockedHandleClick).toBeCalledTimes(1);
|
|
120
|
+
const mockedNavigate = jest.fn();
|
|
121
|
+
renderButton({
|
|
122
|
+
navigate: mockedNavigate,
|
|
123
|
+
onClick: mockedHandleClick,
|
|
124
|
+
children: 'Click',
|
|
125
|
+
});
|
|
126
|
+
const buttonElement = getButton('Click');
|
|
127
|
+
fireEvent.click(buttonElement);
|
|
128
|
+
expect(mockedHandleClick).toHaveBeenCalledTimes(1);
|
|
148
129
|
expect(mockedNavigate).not.toBeCalled();
|
|
149
130
|
});
|
|
150
131
|
});
|
|
151
132
|
|
|
152
133
|
describe('onFocus', () => {
|
|
153
|
-
test('
|
|
134
|
+
test('fires onFocus callback', () => {
|
|
154
135
|
const mockedHandleFocus = jest.fn();
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (buttonElement) {
|
|
160
|
-
fireEvent.focus(buttonElement);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
expect(mockedHandleFocus).toBeCalledTimes(1);
|
|
136
|
+
renderButton({ onFocus: mockedHandleFocus, children: 'Focus' });
|
|
137
|
+
const buttonElement = getButton('Focus');
|
|
138
|
+
fireEvent.focus(buttonElement);
|
|
139
|
+
expect(mockedHandleFocus).toHaveBeenCalledTimes(1);
|
|
164
140
|
});
|
|
165
141
|
|
|
166
|
-
test('
|
|
142
|
+
test('does not fire function if onFocus callback not provided', () => {
|
|
167
143
|
const mockedHandleFocus = jest.fn();
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (buttonElement) {
|
|
173
|
-
fireEvent.focus(buttonElement);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
expect(mockedHandleFocus).toBeCalledTimes(0);
|
|
144
|
+
renderButton({ children: 'Focus' });
|
|
145
|
+
const buttonElement = getButton('Focus');
|
|
146
|
+
fireEvent.focus(buttonElement);
|
|
147
|
+
expect(mockedHandleFocus).toHaveBeenCalledTimes(0);
|
|
177
148
|
});
|
|
178
149
|
});
|
|
179
150
|
|
|
180
151
|
describe('onBlur', () => {
|
|
181
|
-
test('
|
|
152
|
+
test('fires onBlur callback', () => {
|
|
182
153
|
const mockedHandleBlur = jest.fn();
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (buttonElement) {
|
|
188
|
-
fireEvent.blur(buttonElement);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
expect(mockedHandleBlur).toBeCalledTimes(1);
|
|
154
|
+
renderButton({ onBlur: mockedHandleBlur, children: 'Blur' });
|
|
155
|
+
const buttonElement = getButton('Blur');
|
|
156
|
+
fireEvent.blur(buttonElement);
|
|
157
|
+
expect(mockedHandleBlur).toHaveBeenCalledTimes(1);
|
|
192
158
|
});
|
|
193
159
|
|
|
194
|
-
test('
|
|
160
|
+
test('does not fire onBlur callback if not provided', () => {
|
|
195
161
|
const mockedHandleBlur = jest.fn();
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (buttonElement) {
|
|
201
|
-
fireEvent.blur(buttonElement);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
expect(mockedHandleBlur).toBeCalledTimes(0);
|
|
162
|
+
renderButton({ children: 'Blur' });
|
|
163
|
+
const buttonElement = getButton('Blur');
|
|
164
|
+
fireEvent.blur(buttonElement);
|
|
165
|
+
expect(mockedHandleBlur).toHaveBeenCalledTimes(0);
|
|
205
166
|
});
|
|
206
167
|
});
|
|
207
168
|
});
|
|
208
169
|
|
|
209
170
|
describe('States', () => {
|
|
210
171
|
describe('Default', () => {
|
|
211
|
-
test('
|
|
212
|
-
|
|
172
|
+
test('renders the button with simple text', () => {
|
|
173
|
+
renderButton({ children: 'Button!' });
|
|
213
174
|
const buttonElement = screen.getByText('Button!');
|
|
214
|
-
|
|
215
175
|
expect(buttonElement).toBeInTheDocument();
|
|
216
176
|
});
|
|
217
177
|
|
|
218
|
-
test('
|
|
219
|
-
|
|
220
|
-
|
|
178
|
+
test('renders the button with nested DOM nodes', () => {
|
|
179
|
+
renderButton({
|
|
180
|
+
children: (
|
|
221
181
|
<div className="buttonLoadingIndicator">
|
|
222
|
-
<div>
|
|
182
|
+
<div>I'm a nested DOM node!</div>
|
|
223
183
|
</div>
|
|
224
|
-
|
|
225
|
-
);
|
|
226
|
-
const buttonElement = screen.getByText('
|
|
227
|
-
|
|
184
|
+
),
|
|
185
|
+
});
|
|
186
|
+
const buttonElement = screen.getByText("I'm a nested DOM node!");
|
|
228
187
|
expect(buttonElement).toBeInTheDocument();
|
|
229
188
|
});
|
|
230
189
|
|
|
231
|
-
test('
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
expect(
|
|
235
|
-
screen.getByText('Not Disabled Button').closest('button')
|
|
236
|
-
).not.toBeDisabled();
|
|
190
|
+
test('does not have a disabled attribute', () => {
|
|
191
|
+
renderButton({ children: 'Not Disabled Button' });
|
|
192
|
+
expect(getButton('Not Disabled Button')).not.toBeDisabled();
|
|
237
193
|
});
|
|
238
194
|
|
|
239
|
-
test('
|
|
240
|
-
|
|
195
|
+
test('renders an empty button when no children are passed', () => {
|
|
196
|
+
renderButton();
|
|
241
197
|
const buttonElement = screen.getByRole('button');
|
|
242
|
-
|
|
243
198
|
expect(buttonElement).toBeInTheDocument();
|
|
244
|
-
expect(buttonElement
|
|
199
|
+
expect(buttonElement).toBeEmptyDOMElement();
|
|
245
200
|
});
|
|
246
201
|
});
|
|
247
202
|
|
|
248
203
|
describe('Full Width', () => {
|
|
249
|
-
test('
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
.getByText('Full Width Button')
|
|
254
|
-
.closest('button');
|
|
255
|
-
|
|
256
|
-
expect(fullWidthBtn?.getAttribute('class')).toContain('full-width');
|
|
204
|
+
test('has a fullWidth class applied to it', () => {
|
|
205
|
+
renderButton({ fullWidth: true, children: 'Full Width Button' });
|
|
206
|
+
const fullWidthBtn = getButton('Full Width Button');
|
|
207
|
+
expect(fullWidthBtn).toHaveClass('full-width');
|
|
257
208
|
});
|
|
258
209
|
});
|
|
259
210
|
|
|
260
211
|
describe('Custom ClassName', () => {
|
|
261
|
-
test('
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
expect(customClassNameBtn?.getAttribute('class')).toContain(
|
|
269
|
-
'custom-class'
|
|
270
|
-
);
|
|
212
|
+
test('adds custom className to the button', () => {
|
|
213
|
+
renderButton({
|
|
214
|
+
className: 'custom-class',
|
|
215
|
+
children: 'Custom ClassName',
|
|
216
|
+
});
|
|
217
|
+
const customClassNameBtn = getButton('Custom ClassName');
|
|
218
|
+
expect(customClassNameBtn).toHaveClass('custom-class');
|
|
271
219
|
});
|
|
272
220
|
});
|
|
273
221
|
|
|
274
222
|
describe('Disabled', () => {
|
|
275
|
-
test('
|
|
276
|
-
|
|
223
|
+
test('has a disabled attribute', () => {
|
|
224
|
+
renderButton({ isDisabled: true, children: 'Disabled Button' });
|
|
225
|
+
expect(getButton('Disabled Button')).toBeDisabled();
|
|
226
|
+
});
|
|
277
227
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
228
|
+
test('applies aria-disabled attribute when button is disabled', () => {
|
|
229
|
+
renderButton({ isDisabled: true, children: 'Aria Disabled Button' });
|
|
230
|
+
const buttonElement = getButton('Aria Disabled Button');
|
|
231
|
+
expect(buttonElement).toHaveAttribute('aria-disabled', 'true');
|
|
281
232
|
});
|
|
282
233
|
});
|
|
283
234
|
|
|
284
235
|
describe('Loading', () => {
|
|
285
|
-
test('
|
|
286
|
-
|
|
236
|
+
test('renders the spinning loading indicator', () => {
|
|
237
|
+
renderButton({ isLoading: true, children: 'Button is loading' });
|
|
287
238
|
const spinnerElement = document.getElementsByClassName('spinner')[0];
|
|
288
239
|
expect(spinnerElement).toBeInTheDocument();
|
|
289
240
|
});
|
|
290
241
|
|
|
291
|
-
test('
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
);
|
|
242
|
+
test('renders the grey spinning indicator if button variant is secondary', () => {
|
|
243
|
+
renderButton({
|
|
244
|
+
isLoading: true,
|
|
245
|
+
variant: 'secondary',
|
|
246
|
+
children: 'Button is loading',
|
|
247
|
+
});
|
|
297
248
|
const spinnerElement = document.getElementsByClassName('spinner')[0];
|
|
298
249
|
expect(spinnerElement).toBeInTheDocument();
|
|
299
250
|
});
|
|
300
251
|
|
|
301
|
-
test('
|
|
302
|
-
|
|
252
|
+
test('keeps the button text in the DOM so the button width does not change', () => {
|
|
253
|
+
renderButton({ isLoading: true, children: 'Button is loading' });
|
|
303
254
|
expect(screen.getByText('Button is loading')).toBeInTheDocument();
|
|
304
255
|
});
|
|
305
256
|
|
|
306
|
-
test('
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
);
|
|
257
|
+
test('renders white spinning indicator when button is primary', () => {
|
|
258
|
+
renderButton({
|
|
259
|
+
isLoading: true,
|
|
260
|
+
variant: 'primary',
|
|
261
|
+
children: 'Button is loading',
|
|
262
|
+
});
|
|
312
263
|
const spinnerElement = document.getElementsByClassName('spinner')[0];
|
|
313
264
|
expect(spinnerElement).toBeInTheDocument();
|
|
314
265
|
});
|
|
315
266
|
|
|
316
|
-
test('
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
);
|
|
267
|
+
test('renders white spinning indicator when button is danger', () => {
|
|
268
|
+
renderButton({
|
|
269
|
+
isLoading: true,
|
|
270
|
+
variant: 'danger',
|
|
271
|
+
children: 'Button is loading',
|
|
272
|
+
});
|
|
322
273
|
const spinnerElement = document.getElementsByClassName('spinner')[0];
|
|
323
274
|
expect(spinnerElement).toBeInTheDocument();
|
|
324
275
|
});
|
|
325
276
|
});
|
|
326
277
|
|
|
327
278
|
describe('Disabled and Loading', () => {
|
|
328
|
-
test('
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
);
|
|
334
|
-
|
|
335
|
-
expect(
|
|
336
|
-
screen.getByText('Disabled and Loading Button').closest('button')
|
|
337
|
-
).toBeDisabled();
|
|
279
|
+
test('has a disabled attribute', () => {
|
|
280
|
+
renderButton({
|
|
281
|
+
isDisabled: true,
|
|
282
|
+
isLoading: true,
|
|
283
|
+
children: 'Disabled and Loading Button',
|
|
284
|
+
});
|
|
285
|
+
expect(getButton('Disabled and Loading Button')).toBeDisabled();
|
|
338
286
|
});
|
|
339
287
|
});
|
|
340
288
|
|
|
341
289
|
describe('Color Variations', () => {
|
|
342
|
-
test('
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
expect(screen.getByText('primary').closest('button')).toHaveClass(
|
|
346
|
-
'primary'
|
|
347
|
-
);
|
|
290
|
+
test('renders button with default variant primary', () => {
|
|
291
|
+
renderButton({ children: 'primary' });
|
|
292
|
+
expect(getButton('primary')).toHaveClass('primary');
|
|
348
293
|
});
|
|
349
294
|
|
|
350
|
-
const variants = [
|
|
295
|
+
const variants: ButtonVariant[] = [
|
|
351
296
|
'primary',
|
|
352
297
|
'secondary',
|
|
353
298
|
'tertiary',
|
|
354
299
|
'danger',
|
|
355
|
-
]
|
|
300
|
+
];
|
|
356
301
|
variants.forEach((variant) => {
|
|
357
|
-
test(`
|
|
358
|
-
|
|
359
|
-
expect(
|
|
360
|
-
variant
|
|
361
|
-
);
|
|
302
|
+
test(`renders component with variant: ${variant} when passed`, () => {
|
|
303
|
+
renderButton({ variant, children: variant });
|
|
304
|
+
expect(getButton(variant)).toHaveClass(variant);
|
|
362
305
|
});
|
|
363
306
|
});
|
|
364
307
|
});
|
|
365
308
|
|
|
366
309
|
describe('Anchor', () => {
|
|
367
|
-
test('
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
);
|
|
310
|
+
test('renders an anchor tag if as prop `a` is passed', () => {
|
|
311
|
+
renderButton({
|
|
312
|
+
as: 'a',
|
|
313
|
+
href: 'http://hyphen.ai',
|
|
314
|
+
children: 'hey there',
|
|
315
|
+
});
|
|
373
316
|
const buttonElement = screen.getByRole('link');
|
|
374
|
-
|
|
375
317
|
expect(buttonElement).toBeInTheDocument();
|
|
376
318
|
});
|
|
377
319
|
|
|
378
|
-
test('
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
);
|
|
320
|
+
test('does not have a button type attribute if as prop `a` is passed', () => {
|
|
321
|
+
renderButton({
|
|
322
|
+
as: 'a',
|
|
323
|
+
href: 'http://hyphen.ai',
|
|
324
|
+
children: 'hey there',
|
|
325
|
+
});
|
|
384
326
|
const buttonElement = screen.getByRole('link');
|
|
385
|
-
|
|
386
|
-
expect(buttonElement.getAttribute('type')).toBe(null);
|
|
327
|
+
expect(buttonElement).not.toHaveAttribute('type');
|
|
387
328
|
});
|
|
388
329
|
|
|
389
|
-
test('
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
330
|
+
test('renders a target attribute if one is passed, the element is an anchor, and there is a href', () => {
|
|
331
|
+
renderButton({
|
|
332
|
+
as: 'a',
|
|
333
|
+
href: 'http://hyphen.ai',
|
|
334
|
+
target: '_blank',
|
|
335
|
+
children: 'hey there',
|
|
336
|
+
});
|
|
395
337
|
const buttonElement = screen.getByRole('link');
|
|
396
|
-
|
|
397
|
-
expect(buttonElement).toBeInTheDocument();
|
|
398
|
-
expect(buttonElement).toHaveAttribute('target');
|
|
338
|
+
expect(buttonElement).toHaveAttribute('target', '_blank');
|
|
399
339
|
});
|
|
400
340
|
|
|
401
|
-
test('
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
);
|
|
341
|
+
test('does not render a target attribute if the element is not an anchor', () => {
|
|
342
|
+
renderButton({
|
|
343
|
+
href: 'http://hyphen.ai',
|
|
344
|
+
target: '_blank',
|
|
345
|
+
children: 'hey there',
|
|
346
|
+
});
|
|
407
347
|
const buttonElement = screen.getByRole('button');
|
|
408
|
-
|
|
409
|
-
expect(buttonElement).toBeInTheDocument();
|
|
410
348
|
expect(buttonElement).not.toHaveAttribute('target');
|
|
411
349
|
});
|
|
412
350
|
|
|
413
|
-
test('
|
|
414
|
-
|
|
415
|
-
<Button as="a" target="_blank">
|
|
416
|
-
hey there
|
|
417
|
-
</Button>
|
|
418
|
-
);
|
|
351
|
+
test('does not render a target attribute if the element does not have an href', () => {
|
|
352
|
+
renderButton({ as: 'a', target: '_blank', children: 'hey there' });
|
|
419
353
|
const buttonElement = screen.getByText('hey there');
|
|
420
|
-
|
|
421
|
-
expect(buttonElement).toBeInTheDocument();
|
|
422
354
|
expect(buttonElement).not.toHaveAttribute('target');
|
|
423
355
|
});
|
|
356
|
+
|
|
357
|
+
describe('Rel Attribute', () => {
|
|
358
|
+
test('applies rel attribute when target is _blank', () => {
|
|
359
|
+
renderButton({
|
|
360
|
+
as: 'a',
|
|
361
|
+
href: 'http://hyphen.ai',
|
|
362
|
+
target: '_blank',
|
|
363
|
+
children: 'Link with rel',
|
|
364
|
+
});
|
|
365
|
+
const anchorElement = getAnchor('Link with rel');
|
|
366
|
+
expect(anchorElement).toHaveAttribute('rel', 'noopener noreferrer');
|
|
367
|
+
});
|
|
368
|
+
});
|
|
424
369
|
});
|
|
425
370
|
});
|
|
426
371
|
|
|
427
372
|
describe('React Router', () => {
|
|
428
|
-
|
|
429
|
-
const mockedNavigate = jest.fn(
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const anchorElement =
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
expect(mockedNavigate).toBeCalledTimes(1);
|
|
373
|
+
test('fires navigate callback when included', () => {
|
|
374
|
+
const mockedNavigate = jest.fn();
|
|
375
|
+
renderButton({
|
|
376
|
+
as: 'a',
|
|
377
|
+
navigate: mockedNavigate,
|
|
378
|
+
href: '/',
|
|
379
|
+
children: 'react router link',
|
|
380
|
+
});
|
|
381
|
+
const anchorElement = getAnchor('react router link');
|
|
382
|
+
fireEvent.click(anchorElement);
|
|
383
|
+
expect(mockedNavigate).toHaveBeenCalledTimes(1);
|
|
442
384
|
});
|
|
443
385
|
|
|
444
|
-
|
|
445
|
-
const mockedNavigate = jest.fn(
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
386
|
+
test('does not fire navigate callback if target is _blank', () => {
|
|
387
|
+
const mockedNavigate = jest.fn();
|
|
388
|
+
renderButton({
|
|
389
|
+
as: 'a',
|
|
390
|
+
navigate: mockedNavigate,
|
|
391
|
+
href: '/',
|
|
392
|
+
target: '_blank',
|
|
393
|
+
children: 'react router link',
|
|
394
|
+
});
|
|
395
|
+
const anchorElement = getAnchor('react router link');
|
|
396
|
+
fireEvent.click(anchorElement);
|
|
397
|
+
expect(mockedNavigate).toHaveBeenCalledTimes(0);
|
|
398
|
+
});
|
|
399
|
+
});
|
|
456
400
|
|
|
457
|
-
|
|
401
|
+
describe('Role Attribute', () => {
|
|
402
|
+
test('applies role attribute', () => {
|
|
403
|
+
renderButton({ role: 'button', children: 'Button with Role' });
|
|
404
|
+
const buttonElement = getButton('Button with Role');
|
|
405
|
+
expect(buttonElement).toHaveAttribute('role', 'button');
|
|
458
406
|
});
|
|
459
407
|
});
|
|
460
408
|
});
|