@primer/components 31.0.2-rc.1e80de40 → 31.0.2-rc.95622264
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/.changeset/tiny-ghosts-repeat.md +5 -0
- package/CHANGELOG.md +3 -1
- package/dist/browser.esm.js +11 -10
- package/dist/browser.esm.js.map +1 -1
- package/dist/browser.umd.js +31 -30
- package/dist/browser.umd.js.map +1 -1
- package/docs/content/TextInputWithTokens.mdx +114 -0
- package/lib/TextInputWithTokens.d.ts +4 -0
- package/lib/TextInputWithTokens.js +86 -20
- package/lib/_TextInputWrapper.js +1 -1
- package/lib/__tests__/TextInputWithTokens.test.js +197 -5
- package/lib/stories/TextInputWithTokens.stories.js +19 -2
- package/lib-esm/TextInputWithTokens.d.ts +4 -0
- package/lib-esm/TextInputWithTokens.js +85 -21
- package/lib-esm/_TextInputWrapper.js +1 -1
- package/lib-esm/__tests__/TextInputWithTokens.test.js +184 -5
- package/lib-esm/stories/TextInputWithTokens.stories.js +15 -1
- package/package.json +1 -1
- package/src/TextInputWithTokens.tsx +83 -12
- package/src/_TextInputWrapper.tsx +1 -0
- package/src/__tests__/TextInputWithTokens.test.tsx +171 -1
- package/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap +7 -0
- package/src/__tests__/__snapshots__/TextInput.test.tsx.snap +6 -0
- package/src/__tests__/__snapshots__/TextInputWithTokens.test.tsx.snap +463 -0
- package/src/stories/TextInputWithTokens.stories.tsx +10 -1
- package/stats.html +1 -1
@@ -1,6 +1,6 @@
|
|
1
1
|
import React from 'react'
|
2
2
|
import {render} from '../utils/testing'
|
3
|
-
import {render as HTMLRender,
|
3
|
+
import {render as HTMLRender, fireEvent, act, cleanup} from '@testing-library/react'
|
4
4
|
import {axe, toHaveNoViolations} from 'jest-axe'
|
5
5
|
import 'babel-polyfill'
|
6
6
|
import {TokenSizeKeys, tokenSizes} from '../Token/TokenBase'
|
@@ -29,6 +29,18 @@ const LabelledTextInputWithTokens: React.FC<TextInputWithTokensProps> = ({onToke
|
|
29
29
|
</>
|
30
30
|
)
|
31
31
|
|
32
|
+
// describe('axe test', () => {
|
33
|
+
// it('should have no axe violations', async () => {
|
34
|
+
// const onRemoveMock = jest.fn()
|
35
|
+
// const {container} = HTMLRender(<LabelledTextInputWithTokens tokens={mockTokens} onTokenRemove={onRemoveMock} />)
|
36
|
+
// const results = await axe(container)
|
37
|
+
// expect(results).toHaveNoViolations()
|
38
|
+
// cleanup()
|
39
|
+
// })
|
40
|
+
// })
|
41
|
+
|
42
|
+
jest.useFakeTimers()
|
43
|
+
|
32
44
|
describe('TextInputWithTokens', () => {
|
33
45
|
it('renders without tokens', () => {
|
34
46
|
const onRemoveMock = jest.fn()
|
@@ -85,6 +97,13 @@ describe('TextInputWithTokens', () => {
|
|
85
97
|
).toMatchSnapshot()
|
86
98
|
})
|
87
99
|
|
100
|
+
it('renders a truncated set of tokens', () => {
|
101
|
+
const onRemoveMock = jest.fn()
|
102
|
+
expect(
|
103
|
+
render(<TextInputWithTokens tokens={mockTokens} onTokenRemove={onRemoveMock} visibleTokenCount={2} />)
|
104
|
+
).toMatchSnapshot()
|
105
|
+
})
|
106
|
+
|
88
107
|
it('focuses the previous token when keying ArrowLeft', () => {
|
89
108
|
const onRemoveMock = jest.fn()
|
90
109
|
const {getByLabelText, getByText} = HTMLRender(
|
@@ -165,6 +184,119 @@ describe('TextInputWithTokens', () => {
|
|
165
184
|
expect(document.activeElement?.id).toEqual(inputNode.id)
|
166
185
|
})
|
167
186
|
|
187
|
+
it('does not focus the input when clicking a token', () => {
|
188
|
+
const onRemoveMock = jest.fn()
|
189
|
+
const {getByLabelText, getByText} = HTMLRender(
|
190
|
+
<LabelledTextInputWithTokens tokens={mockTokens} onTokenRemove={onRemoveMock} visibleTokenCount={2} />
|
191
|
+
)
|
192
|
+
const inputNode = getByLabelText('Tokens')
|
193
|
+
const tokenNode = getByText(mockTokens[0].text)
|
194
|
+
|
195
|
+
expect(document.activeElement).not.toEqual(inputNode.id)
|
196
|
+
fireEvent.click(tokenNode)
|
197
|
+
expect(document.activeElement?.id).not.toEqual(inputNode.id)
|
198
|
+
})
|
199
|
+
|
200
|
+
it('focuses the input when clicking somewhere in the component besides the tokens', () => {
|
201
|
+
const onRemoveMock = jest.fn()
|
202
|
+
const {getByLabelText, getByText} = HTMLRender(
|
203
|
+
<LabelledTextInputWithTokens tokens={mockTokens} onTokenRemove={onRemoveMock} visibleTokenCount={2} />
|
204
|
+
)
|
205
|
+
const inputNode = getByLabelText('Tokens')
|
206
|
+
const truncatedTokenCount = getByText('+6')
|
207
|
+
|
208
|
+
expect(document.activeElement).not.toEqual(inputNode.id)
|
209
|
+
fireEvent.click(truncatedTokenCount)
|
210
|
+
expect(document.activeElement?.id).toEqual(inputNode.id)
|
211
|
+
})
|
212
|
+
|
213
|
+
it('shows all tokens when the input is focused and hides them when it is blurred (when visibleTokenCount is set)', () => {
|
214
|
+
const onRemoveMock = jest.fn()
|
215
|
+
const visibleTokenCount = 2
|
216
|
+
const {getByLabelText, getByText} = HTMLRender(
|
217
|
+
<>
|
218
|
+
<LabelledTextInputWithTokens
|
219
|
+
tokens={mockTokens}
|
220
|
+
onTokenRemove={onRemoveMock}
|
221
|
+
visibleTokenCount={visibleTokenCount}
|
222
|
+
/>
|
223
|
+
<button id="focusableOutsideComponent">Focus me</button>
|
224
|
+
</>
|
225
|
+
)
|
226
|
+
const inputNode = getByLabelText('Tokens')
|
227
|
+
const focusableOutsideComponentNode = getByText('Focus me')
|
228
|
+
const allTokenLabels = mockTokens.map(token => token.text)
|
229
|
+
const truncatedTokenCountNode = getByText('+6')
|
230
|
+
|
231
|
+
act(() => {
|
232
|
+
jest.runAllTimers()
|
233
|
+
fireEvent.focus(inputNode)
|
234
|
+
})
|
235
|
+
|
236
|
+
setTimeout(() => {
|
237
|
+
for (const tokenLabel of allTokenLabels) {
|
238
|
+
const tokenNode = getByText(tokenLabel)
|
239
|
+
expect(tokenNode).toBeDefined()
|
240
|
+
}
|
241
|
+
}, 0)
|
242
|
+
|
243
|
+
act(() => {
|
244
|
+
jest.runAllTimers()
|
245
|
+
// onBlur isn't called on input unless we specifically fire the "blur" event
|
246
|
+
// eslint-disable-next-line github/no-blur
|
247
|
+
fireEvent.blur(inputNode)
|
248
|
+
fireEvent.focus(focusableOutsideComponentNode)
|
249
|
+
})
|
250
|
+
|
251
|
+
setTimeout(() => {
|
252
|
+
expect(truncatedTokenCountNode).toBeDefined()
|
253
|
+
|
254
|
+
for (const tokenLabel of allTokenLabels) {
|
255
|
+
const tokenNode = getByText(tokenLabel)
|
256
|
+
if (allTokenLabels.indexOf(tokenLabel) > visibleTokenCount) {
|
257
|
+
// eslint-disable-next-line jest/no-conditional-expect
|
258
|
+
expect(tokenNode).toBeDefined()
|
259
|
+
} else {
|
260
|
+
// eslint-disable-next-line jest/no-conditional-expect
|
261
|
+
expect(tokenNode).not.toBeDefined()
|
262
|
+
}
|
263
|
+
}
|
264
|
+
}, 0)
|
265
|
+
|
266
|
+
jest.useRealTimers()
|
267
|
+
})
|
268
|
+
|
269
|
+
it('does not hide tokens when blurring the input to focus within the component (when visibleTokenCount is set)', () => {
|
270
|
+
const onRemoveMock = jest.fn()
|
271
|
+
const visibleTokenCount = 2
|
272
|
+
const {getByLabelText, getByText} = HTMLRender(
|
273
|
+
<>
|
274
|
+
<LabelledTextInputWithTokens
|
275
|
+
tokens={mockTokens}
|
276
|
+
onTokenRemove={onRemoveMock}
|
277
|
+
visibleTokenCount={visibleTokenCount}
|
278
|
+
/>
|
279
|
+
<button id="focusableOutsideComponent">Focus me</button>
|
280
|
+
</>
|
281
|
+
)
|
282
|
+
const inputNode = getByLabelText('Tokens')
|
283
|
+
const firstTokenNode = getByText(mockTokens[visibleTokenCount - 1].text)
|
284
|
+
const allTokenLabels = mockTokens.map(token => token.text)
|
285
|
+
const truncatedTokenCountNode = getByText('+6')
|
286
|
+
|
287
|
+
act(() => {
|
288
|
+
fireEvent.focus(inputNode)
|
289
|
+
fireEvent.focus(firstTokenNode)
|
290
|
+
})
|
291
|
+
|
292
|
+
expect(truncatedTokenCountNode).toBeDefined()
|
293
|
+
|
294
|
+
for (const tokenLabel of allTokenLabels) {
|
295
|
+
const tokenNode = getByText(tokenLabel)
|
296
|
+
expect(tokenNode).toBeDefined()
|
297
|
+
}
|
298
|
+
})
|
299
|
+
|
168
300
|
it('focuses the first token when keying ArrowRight in the input', () => {
|
169
301
|
const onRemoveMock = jest.fn()
|
170
302
|
const {getByLabelText, getByText} = HTMLRender(
|
@@ -238,6 +370,44 @@ describe('TextInputWithTokens', () => {
|
|
238
370
|
expect(onRemoveMock).toHaveBeenCalledWith(mockTokens[4].id)
|
239
371
|
})
|
240
372
|
|
373
|
+
it('moves focus to the next token when removing the first token', () => {
|
374
|
+
jest.useFakeTimers()
|
375
|
+
const onRemoveMock = jest.fn()
|
376
|
+
const {getByText} = HTMLRender(
|
377
|
+
<TextInputWithTokens tokens={[...mockTokens].slice(0, 2)} onTokenRemove={onRemoveMock} />
|
378
|
+
)
|
379
|
+
const tokenNode = getByText(mockTokens[0].text)
|
380
|
+
|
381
|
+
fireEvent.focus(tokenNode)
|
382
|
+
fireEvent.keyDown(tokenNode, {key: 'Backspace'})
|
383
|
+
|
384
|
+
jest.runAllTimers()
|
385
|
+
setTimeout(() => {
|
386
|
+
expect(document.activeElement?.textContent).toBe(mockTokens[1].text)
|
387
|
+
}, 0)
|
388
|
+
|
389
|
+
jest.useRealTimers()
|
390
|
+
})
|
391
|
+
|
392
|
+
it('moves focus to the input when the last token is removed', () => {
|
393
|
+
jest.useFakeTimers()
|
394
|
+
const onRemoveMock = jest.fn()
|
395
|
+
const {getByText, getByLabelText} = HTMLRender(
|
396
|
+
<LabelledTextInputWithTokens tokens={[mockTokens[0]]} onTokenRemove={onRemoveMock} />
|
397
|
+
)
|
398
|
+
const tokenNode = getByText(mockTokens[0].text)
|
399
|
+
const inputNode = getByLabelText('Tokens')
|
400
|
+
|
401
|
+
fireEvent.focus(tokenNode)
|
402
|
+
fireEvent.keyDown(tokenNode, {key: 'Backspace'})
|
403
|
+
|
404
|
+
jest.runAllTimers()
|
405
|
+
setTimeout(() => {
|
406
|
+
expect(document.activeElement?.id).toBe(inputNode.id)
|
407
|
+
}, 0)
|
408
|
+
jest.useRealTimers()
|
409
|
+
})
|
410
|
+
|
241
411
|
it('calls onKeyDown', () => {
|
242
412
|
const onRemoveMock = jest.fn()
|
243
413
|
const onKeyDownMock = jest.fn()
|
@@ -36,6 +36,7 @@ Array [
|
|
36
36
|
border-radius: 6px;
|
37
37
|
outline: none;
|
38
38
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
39
|
+
cursor: text;
|
39
40
|
padding: 6px 12px;
|
40
41
|
}
|
41
42
|
|
@@ -148,6 +149,7 @@ Array [
|
|
148
149
|
border-radius: 6px;
|
149
150
|
outline: none;
|
150
151
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
152
|
+
cursor: text;
|
151
153
|
padding: 6px 12px;
|
152
154
|
}
|
153
155
|
|
@@ -295,6 +297,7 @@ Array [
|
|
295
297
|
border-radius: 6px;
|
296
298
|
outline: none;
|
297
299
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
300
|
+
cursor: text;
|
298
301
|
padding: 6px 12px;
|
299
302
|
}
|
300
303
|
|
@@ -1040,6 +1043,7 @@ Array [
|
|
1040
1043
|
border-radius: 6px;
|
1041
1044
|
outline: none;
|
1042
1045
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
1046
|
+
cursor: text;
|
1043
1047
|
padding: 6px 12px;
|
1044
1048
|
}
|
1045
1049
|
|
@@ -1708,6 +1712,7 @@ Array [
|
|
1708
1712
|
border-radius: 6px;
|
1709
1713
|
outline: none;
|
1710
1714
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
1715
|
+
cursor: text;
|
1711
1716
|
padding: 6px 12px;
|
1712
1717
|
}
|
1713
1718
|
|
@@ -2376,6 +2381,7 @@ Array [
|
|
2376
2381
|
border-radius: 6px;
|
2377
2382
|
outline: none;
|
2378
2383
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
2384
|
+
cursor: text;
|
2379
2385
|
padding: 6px 12px;
|
2380
2386
|
}
|
2381
2387
|
|
@@ -3337,6 +3343,7 @@ Array [
|
|
3337
3343
|
border-radius: 6px;
|
3338
3344
|
outline: none;
|
3339
3345
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
3346
|
+
cursor: text;
|
3340
3347
|
padding: 6px 12px;
|
3341
3348
|
}
|
3342
3349
|
|
@@ -35,6 +35,7 @@ exports[`TextInput renders 1`] = `
|
|
35
35
|
border-radius: 6px;
|
36
36
|
outline: none;
|
37
37
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
38
|
+
cursor: text;
|
38
39
|
padding: 6px 12px;
|
39
40
|
}
|
40
41
|
|
@@ -106,6 +107,7 @@ exports[`TextInput renders block 1`] = `
|
|
106
107
|
border-radius: 6px;
|
107
108
|
outline: none;
|
108
109
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
110
|
+
cursor: text;
|
109
111
|
padding: 6px 12px;
|
110
112
|
display: block;
|
111
113
|
width: 100%;
|
@@ -179,6 +181,7 @@ exports[`TextInput renders consistently 1`] = `
|
|
179
181
|
border-radius: 6px;
|
180
182
|
outline: none;
|
181
183
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
184
|
+
cursor: text;
|
182
185
|
padding: 6px 12px;
|
183
186
|
}
|
184
187
|
|
@@ -249,6 +252,7 @@ exports[`TextInput renders large 1`] = `
|
|
249
252
|
border-radius: 6px;
|
250
253
|
outline: none;
|
251
254
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
255
|
+
cursor: text;
|
252
256
|
padding: 6px 12px;
|
253
257
|
padding-left: 8px;
|
254
258
|
padding-right: 8px;
|
@@ -325,6 +329,7 @@ exports[`TextInput renders small 1`] = `
|
|
325
329
|
border-radius: 6px;
|
326
330
|
outline: none;
|
327
331
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
332
|
+
cursor: text;
|
328
333
|
padding: 6px 12px;
|
329
334
|
min-height: 28px;
|
330
335
|
padding-left: 8px;
|
@@ -403,6 +408,7 @@ exports[`TextInput should render a password input 1`] = `
|
|
403
408
|
border-radius: 6px;
|
404
409
|
outline: none;
|
405
410
|
box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
|
411
|
+
cursor: text;
|
406
412
|
padding: 6px 12px;
|
407
413
|
}
|
408
414
|
|