@primer/components 31.0.2-rc.c7dafefb → 31.1.0-rc.71d00800

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.
@@ -48,6 +48,7 @@ render(BasicExample)
48
48
  | preventTokenWrapping | `boolean` | `false` | Optional. Whether tokens should render inline horizontally. By default, tokens wrap to new lines. |
49
49
  | size | `TokenSizeKeys` | `extralarge` | Optional. The size of the tokens |
50
50
  | hideTokenRemoveButtons | `boolean` | `false` | Optional. Whether the remove buttons should be rendered in the tokens |
51
+ | visibleTokenCount | `number` | `undefined` | Optional. The number of tokens to display before truncating |
51
52
 
52
53
  ## Adding and removing tokens
53
54
 
@@ -95,3 +96,116 @@ const UsingIssueLabelTokens = () => {
95
96
 
96
97
  render(<UsingIssueLabelTokens />)
97
98
  ```
99
+
100
+ ## Dealing with long lists of tokens
101
+
102
+ By default, all tokens will be visible when the component is rendered.
103
+
104
+ If the component is being used in an area where it's height needs to be constrained, there are options to limit the height of the input.
105
+
106
+ ### Hide and show tokens
107
+
108
+ ```javascript live noinline
109
+ const VisibleTokenCountExample = () => {
110
+ const [tokens, setTokens] = React.useState([
111
+ {text: 'zero', id: 0},
112
+ {text: 'one', id: 1},
113
+ {text: 'two', id: 2},
114
+ {text: 'three', id: 3}
115
+ ])
116
+ const onTokenRemove = tokenId => {
117
+ setTokens(tokens.filter(token => token.id !== tokenId))
118
+ }
119
+
120
+ return (
121
+ <Box maxWidth="500px">
122
+ <Box as="label" display="block" htmlFor="inputWithTokens-basic">
123
+ Tokens truncated after 2
124
+ </Box>
125
+ <TextInputWithTokens
126
+ visibleTokenCount={2}
127
+ block
128
+ tokens={tokens}
129
+ onTokenRemove={onTokenRemove}
130
+ id="inputWithTokens-basic"
131
+ />
132
+ </Box>
133
+ )
134
+ }
135
+
136
+ render(VisibleTokenCountExample)
137
+ ```
138
+
139
+ ### Render tokens on a single line
140
+
141
+ ```javascript live noinline
142
+ const PreventTokenWrappingExample = () => {
143
+ const [tokens, setTokens] = React.useState([
144
+ {text: 'zero', id: 0},
145
+ {text: 'one', id: 1},
146
+ {text: 'two', id: 2},
147
+ {text: 'three', id: 3},
148
+ {text: 'four', id: 4},
149
+ {text: 'five', id: 5},
150
+ {text: 'six', id: 6},
151
+ {text: 'seven', id: 7}
152
+ ])
153
+ const onTokenRemove = tokenId => {
154
+ setTokens(tokens.filter(token => token.id !== tokenId))
155
+ }
156
+
157
+ return (
158
+ <Box maxWidth="500px">
159
+ <Box as="label" display="block" htmlFor="inputWithTokens-basic">
160
+ Tokens on one line
161
+ </Box>
162
+ <TextInputWithTokens
163
+ preventTokenWrapping
164
+ block
165
+ tokens={tokens}
166
+ onTokenRemove={onTokenRemove}
167
+ id="inputWithTokens-basic"
168
+ />
169
+ </Box>
170
+ )
171
+ }
172
+
173
+ render(PreventTokenWrappingExample)
174
+ ```
175
+
176
+ ### Set a maximum height for the input
177
+
178
+ ```javascript live noinline
179
+ const MaxHeightExample = () => {
180
+ const [tokens, setTokens] = React.useState([
181
+ {text: 'zero', id: 0},
182
+ {text: 'one', id: 1},
183
+ {text: 'two', id: 2},
184
+ {text: 'three', id: 3},
185
+ {text: 'four', id: 4},
186
+ {text: 'five', id: 5},
187
+ {text: 'six', id: 6},
188
+ {text: 'seven', id: 7}
189
+ ])
190
+ const onTokenRemove = tokenId => {
191
+ setTokens(tokens.filter(token => token.id !== tokenId))
192
+ }
193
+
194
+ return (
195
+ <Box maxWidth="500px">
196
+ <Box as="label" display="block" htmlFor="inputWithTokens-basic">
197
+ Tokens restricted to a max height
198
+ </Box>
199
+ <TextInputWithTokens
200
+ maxHeight="50px"
201
+ block
202
+ tokens={tokens}
203
+ onTokenRemove={onTokenRemove}
204
+ id="inputWithTokens-basic"
205
+ />
206
+ </Box>
207
+ )
208
+ }
209
+
210
+ render(MaxHeightExample)
211
+ ```
@@ -32,6 +32,10 @@ declare const TextInputWithTokens: React.ForwardRefExoticComponent<Pick<{
32
32
  * Whether the remove buttons should be rendered in the tokens
33
33
  */
34
34
  hideTokenRemoveButtons?: boolean | undefined;
35
+ /**
36
+ * The number of tokens to display before truncating
37
+ */
38
+ visibleTokenCount?: number | undefined;
35
39
  } & Pick<Omit<Pick<{
36
40
  [x: string]: any;
37
41
  [x: number]: any;
@@ -25,6 +25,8 @@ var _TextInputWrapper = _interopRequireDefault(require("./_TextInputWrapper"));
25
25
 
26
26
  var _Box = _interopRequireDefault(require("./Box"));
27
27
 
28
+ var _Text = _interopRequireDefault(require("./Text"));
29
+
28
30
  var _iterateFocusableElements = require("./utils/iterateFocusableElements");
29
31
 
30
32
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -35,7 +37,13 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
35
37
 
36
38
  function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
37
39
 
38
- // using forwardRef is important so that other components (ex. Autocomplete) can use the ref
40
+ const overflowCountFontSizeMap = {
41
+ small: 0,
42
+ medium: 1,
43
+ large: 1,
44
+ extralarge: 2
45
+ }; // using forwardRef is important so that other components (ex. Autocomplete) can use the ref
46
+
39
47
  function TextInputWithTokensInnerComponent({
40
48
  icon: IconComponent,
41
49
  contrast,
@@ -55,9 +63,11 @@ function TextInputWithTokensInnerComponent({
55
63
  minWidth: minWidthProp,
56
64
  maxWidth: maxWidthProp,
57
65
  variant: variantProp,
66
+ visibleTokenCount,
58
67
  ...rest
59
68
  }, externalRef) {
60
69
  const {
70
+ onBlur,
61
71
  onFocus,
62
72
  onKeyDown,
63
73
  ...inputPropsRest
@@ -66,6 +76,7 @@ function TextInputWithTokensInnerComponent({
66
76
  const localInputRef = (0, _react.useRef)(null);
67
77
  const combinedInputRef = (0, _useCombinedRefs.useCombinedRefs)(localInputRef, ref);
68
78
  const [selectedTokenIndex, setSelectedTokenIndex] = (0, _react.useState)();
79
+ const [tokensAreTruncated, setTokensAreTruncated] = (0, _react.useState)(Boolean(visibleTokenCount));
69
80
  const {
70
81
  containerRef
71
82
  } = (0, _useFocusZone.useFocusZone)({
@@ -126,20 +137,45 @@ function TextInputWithTokensInnerComponent({
126
137
  };
127
138
 
128
139
  const handleTokenBlur = () => {
129
- setSelectedTokenIndex(undefined);
140
+ setSelectedTokenIndex(undefined); // HACK: wait a tick and check the focused element before hiding truncated tokens
141
+ // this prevents the tokens from hiding when the user is moving focus between tokens,
142
+ // but still hides the tokens when the user blurs the token by tabbing out or clicking somewhere else on the page
143
+
144
+ setTimeout(() => {
145
+ var _containerRef$current4;
146
+
147
+ if (!((_containerRef$current4 = containerRef.current) !== null && _containerRef$current4 !== void 0 && _containerRef$current4.contains(document.activeElement)) && visibleTokenCount) {
148
+ setTokensAreTruncated(true);
149
+ }
150
+ }, 0);
130
151
  };
131
152
 
132
- const handleTokenKeyUp = e => {
133
- if (e.key === 'Escape') {
153
+ const handleTokenKeyUp = event => {
154
+ if (event.key === 'Escape') {
134
155
  var _ref$current2;
135
156
 
136
157
  (_ref$current2 = ref.current) === null || _ref$current2 === void 0 ? void 0 : _ref$current2.focus();
137
158
  }
138
159
  };
139
160
 
140
- const handleInputFocus = e => {
141
- onFocus && onFocus(e);
161
+ const handleInputFocus = event => {
162
+ onFocus && onFocus(event);
142
163
  setSelectedTokenIndex(undefined);
164
+ visibleTokenCount && setTokensAreTruncated(false);
165
+ };
166
+
167
+ const handleInputBlur = event => {
168
+ onBlur && onBlur(event); // HACK: wait a tick and check the focused element before hiding truncated tokens
169
+ // this prevents the tokens from hiding when the user is moving focus from the input to a token,
170
+ // but still hides the tokens when the user blurs the input by tabbing out or clicking somewhere else on the page
171
+
172
+ setTimeout(() => {
173
+ var _containerRef$current5;
174
+
175
+ if (!((_containerRef$current5 = containerRef.current) !== null && _containerRef$current5 !== void 0 && _containerRef$current5.contains(document.activeElement)) && visibleTokenCount) {
176
+ setTokensAreTruncated(true);
177
+ }
178
+ }, 0);
143
179
  };
144
180
 
145
181
  const handleInputKeyDown = e => {
@@ -177,6 +213,17 @@ function TextInputWithTokensInnerComponent({
177
213
  }
178
214
  };
179
215
 
216
+ const focusInput = () => {
217
+ var _combinedInputRef$cur;
218
+
219
+ (_combinedInputRef$cur = combinedInputRef.current) === null || _combinedInputRef$cur === void 0 ? void 0 : _combinedInputRef$cur.focus();
220
+ };
221
+
222
+ const preventTokenClickPropagation = event => {
223
+ event.stopPropagation();
224
+ };
225
+
226
+ const visibleTokens = tokensAreTruncated ? tokens.slice(0, visibleTokenCount) : tokens;
180
227
  return /*#__PURE__*/_react.default.createElement(_TextInputWrapper.default, {
181
228
  block: block,
182
229
  className: className,
@@ -188,6 +235,7 @@ function TextInputWithTokensInnerComponent({
188
235
  minWidth: minWidthProp,
189
236
  maxWidth: maxWidthProp,
190
237
  variant: variantProp,
238
+ onClick: focusInput,
191
239
  sx: { ...(block ? {
192
240
  display: 'flex',
193
241
  width: '100%'
@@ -227,12 +275,13 @@ function TextInputWithTokensInnerComponent({
227
275
  ref: combinedInputRef,
228
276
  disabled: disabled,
229
277
  onFocus: handleInputFocus,
278
+ onBlur: handleInputBlur,
230
279
  onKeyDown: handleInputKeyDown,
231
280
  type: "text",
232
281
  sx: {
233
282
  height: '100%'
234
283
  }
235
- }, inputPropsRest))), tokens.length && TokenComponent ? tokens.map(({
284
+ }, inputPropsRest))), TokenComponent ? visibleTokens.map(({
236
285
  id,
237
286
  ...tokenRest
238
287
  }, i) => /*#__PURE__*/_react.default.createElement(TokenComponent, _extends({
@@ -240,6 +289,7 @@ function TextInputWithTokensInnerComponent({
240
289
  onFocus: handleTokenFocus(i),
241
290
  onBlur: handleTokenBlur,
242
291
  onKeyUp: handleTokenKeyUp,
292
+ onClick: preventTokenClickPropagation,
243
293
  isSelected: selectedTokenIndex === i,
244
294
  onRemove: () => {
245
295
  handleTokenRemove(id);
@@ -247,7 +297,10 @@ function TextInputWithTokensInnerComponent({
247
297
  hideRemoveButton: hideTokenRemoveButtons,
248
298
  size: size,
249
299
  tabIndex: 0
250
- }, tokenRest))) : null));
300
+ }, tokenRest))) : null, tokensAreTruncated ? /*#__PURE__*/_react.default.createElement(_Text.default, {
301
+ color: "fg.muted",
302
+ fontSize: size && overflowCountFontSizeMap[size]
303
+ }, "+", tokens.length - visibleTokens.length) : null));
251
304
  }
252
305
 
253
306
  TextInputWithTokensInnerComponent.displayName = "TextInputWithTokensInnerComponent";
@@ -39,7 +39,7 @@ const sizeVariants = (0, _styledSystem.variant)({
39
39
  const TextInputWrapper = _styledComponents.default.span.withConfig({
40
40
  displayName: "_TextInputWrapper__TextInputWrapper",
41
41
  componentId: "sc-5vfcis-0"
42
- })(["display:inline-flex;align-items:stretch;min-height:34px;font-size:", ";line-height:20px;color:", ";vertical-align:middle;background-repeat:no-repeat;background-position:right 8px center;border:1px solid ", ";border-radius:", ";outline:none;box-shadow:", ";", " .TextInput-icon{align-self:center;color:", ";margin:0 ", ";flex-shrink:0;}&:focus-within{border-color:", ";box-shadow:", ";}", " ", " ", " @media (min-width:", "){font-size:", ";}", " ", " ", " ", " ", ";"], (0, _constants.get)('fontSizes.1'), (0, _constants.get)('colors.fg.default'), (0, _constants.get)('colors.border.default'), (0, _constants.get)('radii.2'), (0, _constants.get)('shadows.primer.shadow.inset'), props => {
42
+ })(["display:inline-flex;align-items:stretch;min-height:34px;font-size:", ";line-height:20px;color:", ";vertical-align:middle;background-repeat:no-repeat;background-position:right 8px center;border:1px solid ", ";border-radius:", ";outline:none;box-shadow:", ";cursor:text;", " .TextInput-icon{align-self:center;color:", ";margin:0 ", ";flex-shrink:0;}&:focus-within{border-color:", ";box-shadow:", ";}", " ", " ", " @media (min-width:", "){font-size:", ";}", " ", " ", " ", " ", ";"], (0, _constants.get)('fontSizes.1'), (0, _constants.get)('colors.fg.default'), (0, _constants.get)('colors.border.default'), (0, _constants.get)('radii.2'), (0, _constants.get)('shadows.primer.shadow.inset'), props => {
43
43
  if (props.hasIcon) {
44
44
  return (0, _styledComponents.css)(["padding:0;"]);
45
45
  } else {
@@ -58,8 +58,18 @@ const LabelledTextInputWithTokens = ({
58
58
  tokens: tokens,
59
59
  onTokenRemove: onTokenRemove,
60
60
  id: "tokenInput"
61
- }, rest)));
62
-
61
+ }, rest))); // describe('axe test', () => {
62
+ // it('should have no axe violations', async () => {
63
+ // const onRemoveMock = jest.fn()
64
+ // const {container} = HTMLRender(<LabelledTextInputWithTokens tokens={mockTokens} onTokenRemove={onRemoveMock} />)
65
+ // const results = await axe(container)
66
+ // expect(results).toHaveNoViolations()
67
+ // cleanup()
68
+ // })
69
+ // })
70
+
71
+
72
+ jest.useFakeTimers();
63
73
  describe('TextInputWithTokens', () => {
64
74
  it('renders without tokens', () => {
65
75
  const onRemoveMock = jest.fn();
@@ -127,6 +137,14 @@ describe('TextInputWithTokens', () => {
127
137
  onTokenRemove: onRemoveMock
128
138
  }))).toMatchSnapshot();
129
139
  });
140
+ it('renders a truncated set of tokens', () => {
141
+ const onRemoveMock = jest.fn();
142
+ expect((0, _testing.render)( /*#__PURE__*/_react.default.createElement(_TextInputWithTokens.default, {
143
+ tokens: mockTokens,
144
+ onTokenRemove: onRemoveMock,
145
+ visibleTokenCount: 2
146
+ }))).toMatchSnapshot();
147
+ });
130
148
  it('focuses the previous token when keying ArrowLeft', () => {
131
149
  var _document$activeEleme, _document$activeEleme2;
132
150
 
@@ -250,8 +268,130 @@ describe('TextInputWithTokens', () => {
250
268
  expect((_document$activeEleme8 = document.activeElement) === null || _document$activeEleme8 === void 0 ? void 0 : _document$activeEleme8.id).not.toEqual(lastTokenNode.id);
251
269
  expect((_document$activeEleme9 = document.activeElement) === null || _document$activeEleme9 === void 0 ? void 0 : _document$activeEleme9.id).toEqual(inputNode.id);
252
270
  });
271
+ it('does not focus the input when clicking a token', () => {
272
+ var _document$activeEleme10;
273
+
274
+ const onRemoveMock = jest.fn();
275
+ const {
276
+ getByLabelText,
277
+ getByText
278
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledTextInputWithTokens, {
279
+ tokens: mockTokens,
280
+ onTokenRemove: onRemoveMock,
281
+ visibleTokenCount: 2
282
+ }));
283
+ const inputNode = getByLabelText('Tokens');
284
+ const tokenNode = getByText(mockTokens[0].text);
285
+ expect(document.activeElement).not.toEqual(inputNode.id);
286
+
287
+ _react2.fireEvent.click(tokenNode);
288
+
289
+ expect((_document$activeEleme10 = document.activeElement) === null || _document$activeEleme10 === void 0 ? void 0 : _document$activeEleme10.id).not.toEqual(inputNode.id);
290
+ });
291
+ it('focuses the input when clicking somewhere in the component besides the tokens', () => {
292
+ var _document$activeEleme11;
293
+
294
+ const onRemoveMock = jest.fn();
295
+ const {
296
+ getByLabelText,
297
+ getByText
298
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledTextInputWithTokens, {
299
+ tokens: mockTokens,
300
+ onTokenRemove: onRemoveMock,
301
+ visibleTokenCount: 2
302
+ }));
303
+ const inputNode = getByLabelText('Tokens');
304
+ const truncatedTokenCount = getByText('+6');
305
+ expect(document.activeElement).not.toEqual(inputNode.id);
306
+
307
+ _react2.fireEvent.click(truncatedTokenCount);
308
+
309
+ expect((_document$activeEleme11 = document.activeElement) === null || _document$activeEleme11 === void 0 ? void 0 : _document$activeEleme11.id).toEqual(inputNode.id);
310
+ });
311
+ it('shows all tokens when the input is focused and hides them when it is blurred (when visibleTokenCount is set)', () => {
312
+ const onRemoveMock = jest.fn();
313
+ const visibleTokenCount = 2;
314
+ const {
315
+ getByLabelText,
316
+ getByText
317
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(LabelledTextInputWithTokens, {
318
+ tokens: mockTokens,
319
+ onTokenRemove: onRemoveMock,
320
+ visibleTokenCount: visibleTokenCount
321
+ }), /*#__PURE__*/_react.default.createElement("button", {
322
+ id: "focusableOutsideComponent"
323
+ }, "Focus me")));
324
+ const inputNode = getByLabelText('Tokens');
325
+ const focusableOutsideComponentNode = getByText('Focus me');
326
+ const allTokenLabels = mockTokens.map(token => token.text);
327
+ const truncatedTokenCountNode = getByText('+6');
328
+ (0, _react2.act)(() => {
329
+ jest.runAllTimers();
330
+
331
+ _react2.fireEvent.focus(inputNode);
332
+ });
333
+ setTimeout(() => {
334
+ for (const tokenLabel of allTokenLabels) {
335
+ const tokenNode = getByText(tokenLabel);
336
+ expect(tokenNode).toBeDefined();
337
+ }
338
+ }, 0);
339
+ (0, _react2.act)(() => {
340
+ jest.runAllTimers(); // onBlur isn't called on input unless we specifically fire the "blur" event
341
+ // eslint-disable-next-line github/no-blur
342
+
343
+ _react2.fireEvent.blur(inputNode);
344
+
345
+ _react2.fireEvent.focus(focusableOutsideComponentNode);
346
+ });
347
+ setTimeout(() => {
348
+ expect(truncatedTokenCountNode).toBeDefined();
349
+
350
+ for (const tokenLabel of allTokenLabels) {
351
+ const tokenNode = getByText(tokenLabel);
352
+
353
+ if (allTokenLabels.indexOf(tokenLabel) > visibleTokenCount) {
354
+ // eslint-disable-next-line jest/no-conditional-expect
355
+ expect(tokenNode).toBeDefined();
356
+ } else {
357
+ // eslint-disable-next-line jest/no-conditional-expect
358
+ expect(tokenNode).not.toBeDefined();
359
+ }
360
+ }
361
+ }, 0);
362
+ jest.useRealTimers();
363
+ });
364
+ it('does not hide tokens when blurring the input to focus within the component (when visibleTokenCount is set)', () => {
365
+ const onRemoveMock = jest.fn();
366
+ const visibleTokenCount = 2;
367
+ const {
368
+ getByLabelText,
369
+ getByText
370
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(LabelledTextInputWithTokens, {
371
+ tokens: mockTokens,
372
+ onTokenRemove: onRemoveMock,
373
+ visibleTokenCount: visibleTokenCount
374
+ }), /*#__PURE__*/_react.default.createElement("button", {
375
+ id: "focusableOutsideComponent"
376
+ }, "Focus me")));
377
+ const inputNode = getByLabelText('Tokens');
378
+ const firstTokenNode = getByText(mockTokens[visibleTokenCount - 1].text);
379
+ const allTokenLabels = mockTokens.map(token => token.text);
380
+ const truncatedTokenCountNode = getByText('+6');
381
+ (0, _react2.act)(() => {
382
+ _react2.fireEvent.focus(inputNode);
383
+
384
+ _react2.fireEvent.focus(firstTokenNode);
385
+ });
386
+ expect(truncatedTokenCountNode).toBeDefined();
387
+
388
+ for (const tokenLabel of allTokenLabels) {
389
+ const tokenNode = getByText(tokenLabel);
390
+ expect(tokenNode).toBeDefined();
391
+ }
392
+ });
253
393
  it('focuses the first token when keying ArrowRight in the input', () => {
254
- var _document$activeEleme10, _document$activeEleme11;
394
+ var _document$activeEleme12, _document$activeEleme13;
255
395
 
256
396
  const onRemoveMock = jest.fn();
257
397
  const {
@@ -271,8 +411,8 @@ describe('TextInputWithTokens', () => {
271
411
  key: 'ArrowRight'
272
412
  });
273
413
 
274
- expect((_document$activeEleme10 = document.activeElement) === null || _document$activeEleme10 === void 0 ? void 0 : _document$activeEleme10.id).not.toEqual(inputNode.id);
275
- expect((_document$activeEleme11 = document.activeElement) === null || _document$activeEleme11 === void 0 ? void 0 : _document$activeEleme11.id).toEqual(firstTokenNode.id);
414
+ expect((_document$activeEleme12 = document.activeElement) === null || _document$activeEleme12 === void 0 ? void 0 : _document$activeEleme12.id).not.toEqual(inputNode.id);
415
+ expect((_document$activeEleme13 = document.activeElement) === null || _document$activeEleme13 === void 0 ? void 0 : _document$activeEleme13.id).toEqual(firstTokenNode.id);
276
416
  });
277
417
  it('calls onTokenRemove on the last token when keying Backspace in an empty input', () => {
278
418
  const onRemoveMock = jest.fn();
@@ -372,9 +512,9 @@ describe('TextInputWithTokens', () => {
372
512
 
373
513
  jest.runAllTimers();
374
514
  setTimeout(() => {
375
- var _document$activeEleme12;
515
+ var _document$activeEleme14;
376
516
 
377
- expect((_document$activeEleme12 = document.activeElement) === null || _document$activeEleme12 === void 0 ? void 0 : _document$activeEleme12.textContent).toBe(mockTokens[1].text);
517
+ expect((_document$activeEleme14 = document.activeElement) === null || _document$activeEleme14 === void 0 ? void 0 : _document$activeEleme14.textContent).toBe(mockTokens[1].text);
378
518
  }, 0);
379
519
  jest.useRealTimers();
380
520
  });
@@ -399,9 +539,9 @@ describe('TextInputWithTokens', () => {
399
539
 
400
540
  jest.runAllTimers();
401
541
  setTimeout(() => {
402
- var _document$activeEleme13;
542
+ var _document$activeEleme15;
403
543
 
404
- expect((_document$activeEleme13 = document.activeElement) === null || _document$activeEleme13 === void 0 ? void 0 : _document$activeEleme13.id).toBe(inputNode.id);
544
+ expect((_document$activeEleme15 = document.activeElement) === null || _document$activeEleme15 === void 0 ? void 0 : _document$activeEleme15.id).toBe(inputNode.id);
405
545
  }, 0);
406
546
  jest.useRealTimers();
407
547
  });
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.Unstyled = exports.TokenRemoveButtonsHidden = exports.TokenWrappingPrevented = exports.MaxHeight = exports.TokenSizeVariants = exports.UsingIssueLabelTokens = exports.Default = exports.default = void 0;
6
+ exports.Unstyled = exports.WithVisibleTokenCount = exports.TokenRemoveButtonsHidden = exports.TokenWrappingPrevented = exports.MaxHeight = exports.TokenSizeVariants = exports.UsingIssueLabelTokens = exports.Default = exports.default = void 0;
7
7
 
8
8
  var _react = _interopRequireWildcard(require("react"));
9
9
 
@@ -208,6 +208,23 @@ const TokenRemoveButtonsHidden = () => {
208
208
  exports.TokenRemoveButtonsHidden = TokenRemoveButtonsHidden;
209
209
  TokenRemoveButtonsHidden.displayName = "TokenRemoveButtonsHidden";
210
210
 
211
+ const WithVisibleTokenCount = () => {
212
+ const [tokens, setTokens] = (0, _react.useState)([...mockTokens].slice(0, 5));
213
+
214
+ const onTokenRemove = tokenId => {
215
+ setTokens(tokens.filter(token => token.id !== tokenId));
216
+ };
217
+
218
+ return /*#__PURE__*/_react.default.createElement(_TextInputWithTokens.default, {
219
+ tokens: tokens,
220
+ onTokenRemove: onTokenRemove,
221
+ visibleTokenCount: 2
222
+ });
223
+ };
224
+
225
+ exports.WithVisibleTokenCount = WithVisibleTokenCount;
226
+ WithVisibleTokenCount.displayName = "WithVisibleTokenCount";
227
+
211
228
  const Unstyled = () => {
212
229
  const [tokens, setTokens] = (0, _react.useState)([...mockTokens].slice(0, 2));
213
230
 
@@ -32,6 +32,10 @@ declare const TextInputWithTokens: React.ForwardRefExoticComponent<Pick<{
32
32
  * Whether the remove buttons should be rendered in the tokens
33
33
  */
34
34
  hideTokenRemoveButtons?: boolean | undefined;
35
+ /**
36
+ * The number of tokens to display before truncating
37
+ */
38
+ visibleTokenCount?: number | undefined;
35
39
  } & Pick<Omit<Pick<{
36
40
  [x: string]: any;
37
41
  [x: number]: any;