@leafygreen-ui/combobox 0.9.0 → 1.0.2
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/CHANGELOG.md +37 -0
- package/README.md +48 -41
- package/dist/Combobox.d.ts +1 -1
- package/dist/Combobox.d.ts.map +1 -1
- package/dist/Combobox.styles.d.ts.map +1 -1
- package/dist/Combobox.types.d.ts +23 -0
- package/dist/Combobox.types.d.ts.map +1 -1
- package/dist/ComboboxOption.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +33 -30
- package/src/Chip.tsx +1 -1
- package/src/Combobox.spec.tsx +286 -124
- package/src/Combobox.story.tsx +10 -4
- package/src/Combobox.styles.ts +14 -24
- package/src/Combobox.tsx +37 -13
- package/src/Combobox.types.ts +28 -0
- package/src/ComboboxOption.tsx +7 -2
- package/tsconfig.tsbuildinfo +135 -47
package/src/Combobox.spec.tsx
CHANGED
|
@@ -154,6 +154,22 @@ describe('packages/combobox', () => {
|
|
|
154
154
|
const [optionEl] = Array.from(optionElements!);
|
|
155
155
|
expect(optionEl).toHaveTextContent('abc-def');
|
|
156
156
|
});
|
|
157
|
+
|
|
158
|
+
test('Options with long names are rendered with the full text', () => {
|
|
159
|
+
const displayName = `Donec id elit non mi porta gravida at eget metus. Aenean lacinia bibendum nulla sed consectetur.`;
|
|
160
|
+
const options: Array<OptionObject> = [
|
|
161
|
+
{
|
|
162
|
+
value: 'paragraph',
|
|
163
|
+
displayName,
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
const { openMenu } = renderCombobox(select, { options });
|
|
168
|
+
const { optionElements } = openMenu();
|
|
169
|
+
const [optionEl] = Array.from(optionElements!);
|
|
170
|
+
expect(optionEl).toHaveTextContent(displayName);
|
|
171
|
+
});
|
|
172
|
+
|
|
157
173
|
// Grouped Options
|
|
158
174
|
describe('Grouped Options', () => {
|
|
159
175
|
test('Grouped items should render', () => {
|
|
@@ -213,6 +229,26 @@ describe('packages/combobox', () => {
|
|
|
213
229
|
expect(inputEl).toHaveValue('Apple');
|
|
214
230
|
});
|
|
215
231
|
|
|
232
|
+
testSingleSelect(
|
|
233
|
+
'Initial value prop renders truncated long text input value',
|
|
234
|
+
() => {
|
|
235
|
+
const displayName = `Donec id elit non mi porta gravida at eget metus. Aenean lacinia bibendum nulla sed consectetur.`;
|
|
236
|
+
const options: Array<OptionObject> = [
|
|
237
|
+
{
|
|
238
|
+
value: 'paragraph',
|
|
239
|
+
displayName,
|
|
240
|
+
},
|
|
241
|
+
...defaultOptions,
|
|
242
|
+
];
|
|
243
|
+
const initialValue = 'paragraph';
|
|
244
|
+
const { inputEl } = renderCombobox(select, { initialValue, options });
|
|
245
|
+
expect(inputEl).toHaveValue(displayName);
|
|
246
|
+
expect(inputEl.scrollWidth).toBeGreaterThanOrEqual(
|
|
247
|
+
inputEl.clientWidth,
|
|
248
|
+
);
|
|
249
|
+
},
|
|
250
|
+
);
|
|
251
|
+
|
|
216
252
|
testMultiSelect('Initial value prop renders chips', () => {
|
|
217
253
|
const initialValue = ['apple', 'banana'];
|
|
218
254
|
const { queryChipsByName, queryAllChips } = renderCombobox(select, {
|
|
@@ -248,11 +284,40 @@ describe('packages/combobox', () => {
|
|
|
248
284
|
);
|
|
249
285
|
});
|
|
250
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Input element
|
|
289
|
+
*/
|
|
290
|
+
describe('Input interaction', () => {
|
|
291
|
+
test('Typing any character updates the input', () => {
|
|
292
|
+
const { inputEl } = renderCombobox(select);
|
|
293
|
+
userEvent.type(inputEl, 'zy');
|
|
294
|
+
expect(inputEl).toHaveValue('zy');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test('Initial value prop renders truncated long text input value', () => {
|
|
298
|
+
const displayName = `Donec id elit non mi porta gravida at eget metus. Aenean lacinia bibendum nulla sed consectetur.`;
|
|
299
|
+
const { inputEl } = renderCombobox(select);
|
|
300
|
+
userEvent.type(inputEl, displayName);
|
|
301
|
+
expect(inputEl).toHaveValue(displayName);
|
|
302
|
+
expect(inputEl.scrollWidth).toBeGreaterThanOrEqual(inputEl.clientWidth);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
251
306
|
/**
|
|
252
307
|
* Controlled
|
|
253
308
|
* (i.e. `value` prop)
|
|
254
309
|
*/
|
|
255
310
|
describe('When value is controlled', () => {
|
|
311
|
+
test('Typing any character updates the input', () => {
|
|
312
|
+
const value = select === 'multiple' ? [] : '';
|
|
313
|
+
const { inputEl } = renderCombobox(select, {
|
|
314
|
+
value,
|
|
315
|
+
});
|
|
316
|
+
expect(inputEl).toHaveValue('');
|
|
317
|
+
userEvent.type(inputEl, 'z');
|
|
318
|
+
expect(inputEl).toHaveValue('z');
|
|
319
|
+
});
|
|
320
|
+
|
|
256
321
|
testSingleSelect('Text input renders with value update', () => {
|
|
257
322
|
let value = 'apple';
|
|
258
323
|
const { inputEl, rerenderCombobox } = renderCombobox(select, {
|
|
@@ -347,6 +412,18 @@ describe('packages/combobox', () => {
|
|
|
347
412
|
expect(containerEl).not.toContainFocus();
|
|
348
413
|
});
|
|
349
414
|
|
|
415
|
+
test('Clicking an option sets selection', () => {
|
|
416
|
+
const { openMenu, queryChipsByName, inputEl } = renderCombobox(select);
|
|
417
|
+
const { optionElements } = openMenu();
|
|
418
|
+
expect(optionElements).not.toBeUndefined();
|
|
419
|
+
userEvent.click((optionElements as HTMLCollectionOf<HTMLLIElement>)[2]);
|
|
420
|
+
if (select === 'multiple') {
|
|
421
|
+
expect(queryChipsByName('Carrot')).toBeInTheDocument();
|
|
422
|
+
} else {
|
|
423
|
+
expect(inputEl).toHaveValue('Carrot');
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
350
427
|
testSingleSelect('Clicking selected option closes menu', async () => {
|
|
351
428
|
const { openMenu } = renderCombobox(select, {
|
|
352
429
|
initialValue: 'apple',
|
|
@@ -358,29 +435,58 @@ describe('packages/combobox', () => {
|
|
|
358
435
|
expect(menuContainerEl).not.toBeInTheDocument();
|
|
359
436
|
});
|
|
360
437
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
438
|
+
testMultiSelect(
|
|
439
|
+
'Clicking selected option toggles selection & does NOT close menu',
|
|
440
|
+
async () => {
|
|
441
|
+
const { openMenu, queryChipsByName } = renderCombobox(select, {
|
|
442
|
+
initialValue: ['apple'],
|
|
443
|
+
});
|
|
444
|
+
const selectedChip = queryChipsByName('Apple');
|
|
445
|
+
expect(selectedChip).toBeInTheDocument();
|
|
446
|
+
const { optionElements, menuContainerEl } = openMenu();
|
|
447
|
+
expect(optionElements).not.toBeUndefined();
|
|
448
|
+
|
|
449
|
+
userEvent.click(
|
|
450
|
+
(optionElements as HTMLCollectionOf<HTMLLIElement>)[0],
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
await waitFor(() => {
|
|
454
|
+
expect(selectedChip).not.toBeInTheDocument();
|
|
455
|
+
expect(menuContainerEl).toBeInTheDocument();
|
|
456
|
+
});
|
|
457
|
+
},
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
testSingleSelect('Clicking any option closes menu', async () => {
|
|
461
|
+
const { openMenu } = renderCombobox(select);
|
|
462
|
+
const { optionElements, menuContainerEl } = openMenu();
|
|
364
463
|
expect(optionElements).not.toBeUndefined();
|
|
365
464
|
userEvent.click((optionElements as HTMLCollectionOf<HTMLLIElement>)[1]);
|
|
366
|
-
|
|
367
|
-
expect(menuContainerEl).toBeInTheDocument();
|
|
465
|
+
await waitForElementToBeRemoved(menuContainerEl);
|
|
466
|
+
expect(menuContainerEl).not.toBeInTheDocument();
|
|
368
467
|
});
|
|
369
468
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
469
|
+
testMultiSelect(
|
|
470
|
+
'Clicking any option toggles selection & does NOT close menu',
|
|
471
|
+
async () => {
|
|
472
|
+
const { openMenu, queryChipsByName } = renderCombobox(select);
|
|
473
|
+
const { optionElements, menuContainerEl } = openMenu();
|
|
474
|
+
expect(optionElements).not.toBeUndefined();
|
|
475
|
+
|
|
476
|
+
userEvent.click(
|
|
477
|
+
(optionElements as HTMLCollectionOf<HTMLLIElement>)[0],
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
await waitFor(() => {
|
|
481
|
+
const selectedChip = queryChipsByName('Apple');
|
|
482
|
+
expect(selectedChip).toBeInTheDocument();
|
|
483
|
+
expect(menuContainerEl).toBeInTheDocument();
|
|
484
|
+
});
|
|
485
|
+
},
|
|
486
|
+
);
|
|
381
487
|
|
|
382
488
|
testSingleSelect(
|
|
383
|
-
'Input
|
|
489
|
+
'Input returned to previous selection when menu closes',
|
|
384
490
|
() => {
|
|
385
491
|
const initialValue = 'apple';
|
|
386
492
|
const { inputEl } = renderCombobox(select, {
|
|
@@ -391,70 +497,71 @@ describe('packages/combobox', () => {
|
|
|
391
497
|
},
|
|
392
498
|
);
|
|
393
499
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
allChips?.forEach(chip => expect(chip).toBeInTheDocument());
|
|
407
|
-
expect(queryAllChips()).toHaveLength(2);
|
|
408
|
-
});
|
|
409
|
-
});
|
|
500
|
+
testSingleSelect(
|
|
501
|
+
'Unfocusing the menu should keep text if input is a valid value',
|
|
502
|
+
async () => {
|
|
503
|
+
const { inputEl, containerEl, openMenu } = renderCombobox(select);
|
|
504
|
+
const { menuContainerEl } = openMenu();
|
|
505
|
+
userEvent.type(inputEl, 'Apple');
|
|
506
|
+
userEvent.click(document.body);
|
|
507
|
+
await waitForElementToBeRemoved(menuContainerEl);
|
|
508
|
+
expect(containerEl).not.toContainFocus();
|
|
509
|
+
expect(inputEl).toHaveValue('Apple');
|
|
510
|
+
},
|
|
511
|
+
);
|
|
410
512
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
513
|
+
testSingleSelect(
|
|
514
|
+
'Unfocusing the menu should NOT keep text if input is not a valid value',
|
|
515
|
+
async () => {
|
|
516
|
+
const { inputEl, containerEl, openMenu } = renderCombobox(select);
|
|
517
|
+
const { menuContainerEl } = openMenu();
|
|
518
|
+
userEvent.type(inputEl, 'abc');
|
|
519
|
+
userEvent.click(document.body);
|
|
520
|
+
await waitForElementToBeRemoved(menuContainerEl);
|
|
521
|
+
expect(containerEl).not.toContainFocus();
|
|
522
|
+
expect(inputEl).toHaveValue('');
|
|
523
|
+
},
|
|
524
|
+
);
|
|
421
525
|
|
|
422
526
|
testMultiSelect(
|
|
423
|
-
'
|
|
527
|
+
'Unfocusing the menu should keep text as typed',
|
|
424
528
|
async () => {
|
|
425
|
-
const
|
|
426
|
-
const {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
userEvent.click(carrotChipButton!);
|
|
433
|
-
await waitFor(() => {
|
|
434
|
-
expect(queryAllChips()).toHaveLength(3);
|
|
435
|
-
});
|
|
529
|
+
const { inputEl, containerEl, openMenu } = renderCombobox(select);
|
|
530
|
+
const { menuContainerEl } = openMenu();
|
|
531
|
+
userEvent.type(inputEl, 'abc');
|
|
532
|
+
userEvent.click(document.body);
|
|
533
|
+
await waitForElementToBeRemoved(menuContainerEl);
|
|
534
|
+
expect(containerEl).not.toContainFocus();
|
|
535
|
+
expect(inputEl).toHaveValue('abc');
|
|
436
536
|
},
|
|
437
537
|
);
|
|
438
538
|
|
|
439
|
-
|
|
440
|
-
'
|
|
539
|
+
testSingleSelect(
|
|
540
|
+
'Clicking the combobox after making a selection should re-open the menu',
|
|
441
541
|
async () => {
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
const
|
|
449
|
-
const
|
|
450
|
-
userEvent.click(
|
|
542
|
+
const {
|
|
543
|
+
comboboxEl,
|
|
544
|
+
inputEl,
|
|
545
|
+
openMenu,
|
|
546
|
+
getMenuElements,
|
|
547
|
+
} = renderCombobox(select);
|
|
548
|
+
const { optionElements, menuContainerEl } = openMenu();
|
|
549
|
+
const firstOption = optionElements![0];
|
|
550
|
+
userEvent.click(firstOption);
|
|
551
|
+
await waitForElementToBeRemoved(menuContainerEl);
|
|
552
|
+
userEvent.click(comboboxEl);
|
|
451
553
|
await waitFor(() => {
|
|
452
|
-
|
|
453
|
-
expect(
|
|
554
|
+
const { menuContainerEl: newMenuContainerEl } = getMenuElements();
|
|
555
|
+
expect(newMenuContainerEl).not.toBeNull();
|
|
556
|
+
expect(newMenuContainerEl).toBeInTheDocument();
|
|
557
|
+
expect(inputEl).toHaveFocus();
|
|
454
558
|
});
|
|
455
559
|
},
|
|
456
560
|
);
|
|
457
561
|
|
|
562
|
+
/**
|
|
563
|
+
* Clicking buttons
|
|
564
|
+
*/
|
|
458
565
|
test('Clicking clear all button clears selection', () => {
|
|
459
566
|
const initialValue =
|
|
460
567
|
select === 'single' ? 'apple' : ['apple', 'banana', 'carrot'];
|
|
@@ -492,44 +599,71 @@ describe('packages/combobox', () => {
|
|
|
492
599
|
}
|
|
493
600
|
});
|
|
494
601
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
const {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
602
|
+
describe('Clicking chips', () => {
|
|
603
|
+
testMultiSelect('Clicking chip X button removes option', async () => {
|
|
604
|
+
const initialValue = ['apple', 'banana', 'carrot'];
|
|
605
|
+
const { queryChipsByName, queryAllChips } = renderCombobox(select, {
|
|
606
|
+
initialValue,
|
|
607
|
+
});
|
|
608
|
+
const appleChip = queryChipsByName('Apple');
|
|
609
|
+
expect(appleChip).not.toBeNull();
|
|
610
|
+
const appleChipButton = appleChip!.querySelector('button')!;
|
|
611
|
+
userEvent.click(appleChipButton);
|
|
612
|
+
await waitFor(() => {
|
|
613
|
+
expect(appleChip).not.toBeInTheDocument();
|
|
614
|
+
const allChips = queryChipsByName(['Banana', 'Carrot']);
|
|
615
|
+
allChips?.forEach(chip => expect(chip).toBeInTheDocument());
|
|
616
|
+
expect(queryAllChips()).toHaveLength(2);
|
|
617
|
+
});
|
|
618
|
+
});
|
|
507
619
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
userEvent.click(
|
|
515
|
-
|
|
516
|
-
expect(
|
|
517
|
-
|
|
518
|
-
},
|
|
519
|
-
);
|
|
620
|
+
testMultiSelect('Clicking chip text focuses the chip', () => {
|
|
621
|
+
const initialValue = ['apple', 'banana', 'carrot'];
|
|
622
|
+
const { queryChipsByName, queryAllChips } = renderCombobox(select, {
|
|
623
|
+
initialValue,
|
|
624
|
+
});
|
|
625
|
+
const appleChip = queryChipsByName('Apple');
|
|
626
|
+
userEvent.click(appleChip!);
|
|
627
|
+
expect(appleChip!).toContainFocus();
|
|
628
|
+
expect(queryAllChips()).toHaveLength(3);
|
|
629
|
+
});
|
|
520
630
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
631
|
+
testMultiSelect(
|
|
632
|
+
'Clicking chip X button does nothing when disabled',
|
|
633
|
+
async () => {
|
|
634
|
+
const initialValue = ['apple', 'banana', 'carrot'];
|
|
635
|
+
const { queryChipsByName, queryAllChips } = renderCombobox(select, {
|
|
636
|
+
initialValue,
|
|
637
|
+
disabled: true,
|
|
638
|
+
});
|
|
639
|
+
const carrotChip = queryChipsByName('Carrot');
|
|
640
|
+
const carrotChipButton = carrotChip!.querySelector('button');
|
|
641
|
+
userEvent.click(carrotChipButton!);
|
|
642
|
+
await waitFor(() => {
|
|
643
|
+
expect(queryAllChips()).toHaveLength(3);
|
|
644
|
+
});
|
|
645
|
+
},
|
|
646
|
+
);
|
|
647
|
+
|
|
648
|
+
testMultiSelect(
|
|
649
|
+
'Removing a chip sets focus to the next chip',
|
|
650
|
+
async () => {
|
|
651
|
+
const initialValue = ['apple', 'banana', 'carrot'];
|
|
652
|
+
const { queryChipsByName } = renderCombobox(select, {
|
|
653
|
+
initialValue,
|
|
654
|
+
});
|
|
655
|
+
const appleChip = queryChipsByName('Apple');
|
|
656
|
+
const bananaChip = queryChipsByName('Banana');
|
|
657
|
+
const appleChipButton = appleChip!.querySelector('button');
|
|
658
|
+
const bananaChipButton = bananaChip!.querySelector('button');
|
|
659
|
+
userEvent.click(appleChipButton!);
|
|
660
|
+
await waitFor(() => {
|
|
661
|
+
expect(appleChip).not.toBeInTheDocument();
|
|
662
|
+
expect(bananaChipButton!).toHaveFocus();
|
|
663
|
+
});
|
|
664
|
+
},
|
|
665
|
+
);
|
|
666
|
+
});
|
|
533
667
|
|
|
534
668
|
test.todo(
|
|
535
669
|
'Clicking in the middle of the input text should set the cursor there',
|
|
@@ -549,26 +683,47 @@ describe('packages/combobox', () => {
|
|
|
549
683
|
).toHaveAttribute('aria-selected', 'true');
|
|
550
684
|
});
|
|
551
685
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
686
|
+
describe('Enter key', () => {
|
|
687
|
+
test('selects highlighted option', () => {
|
|
688
|
+
const { inputEl, openMenu, queryChipsByName } = renderCombobox(
|
|
689
|
+
select,
|
|
690
|
+
);
|
|
691
|
+
openMenu();
|
|
692
|
+
userEvent.type(inputEl!, '{arrowdown}{enter}');
|
|
693
|
+
if (select === 'multiple') {
|
|
694
|
+
expect(queryChipsByName('Banana')).toBeInTheDocument();
|
|
695
|
+
} else {
|
|
696
|
+
expect(inputEl).toHaveValue('Banana');
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
testSingleSelect('Re-opens menu after making a selection', async () => {
|
|
701
|
+
const { inputEl, openMenu, getMenuElements } = renderCombobox(
|
|
702
|
+
'single',
|
|
703
|
+
);
|
|
704
|
+
const { optionElements, menuContainerEl } = openMenu();
|
|
705
|
+
const firstOption = optionElements![0];
|
|
706
|
+
userEvent.click(firstOption);
|
|
707
|
+
await waitForElementToBeRemoved(menuContainerEl);
|
|
708
|
+
userEvent.type(inputEl, '{emter}');
|
|
709
|
+
await waitFor(() => {
|
|
710
|
+
const { menuContainerEl: newMenuContainerEl } = getMenuElements();
|
|
711
|
+
expect(newMenuContainerEl).not.toBeNull();
|
|
712
|
+
expect(newMenuContainerEl).toBeInTheDocument();
|
|
713
|
+
});
|
|
714
|
+
});
|
|
561
715
|
});
|
|
562
716
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
expect(
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
717
|
+
describe('Space key', () => {
|
|
718
|
+
test('Types a space character', () => {
|
|
719
|
+
const { inputEl, openMenu, queryAllChips } = renderCombobox(select);
|
|
720
|
+
openMenu();
|
|
721
|
+
userEvent.type(inputEl, 'a{space}fruit');
|
|
722
|
+
expect(inputEl).toHaveValue('a fruit');
|
|
723
|
+
if (select === 'multiple') {
|
|
724
|
+
expect(queryAllChips()).toHaveLength(0);
|
|
725
|
+
}
|
|
726
|
+
});
|
|
572
727
|
});
|
|
573
728
|
|
|
574
729
|
test('Escape key closes menu', async () => {
|
|
@@ -872,8 +1027,15 @@ describe('packages/combobox', () => {
|
|
|
872
1027
|
describe('Any other key', () => {
|
|
873
1028
|
test('Updates the value of the input', () => {
|
|
874
1029
|
const { inputEl } = renderCombobox(select);
|
|
875
|
-
userEvent.type(inputEl, '
|
|
876
|
-
expect(inputEl).toHaveValue('
|
|
1030
|
+
userEvent.type(inputEl, 'z');
|
|
1031
|
+
expect(inputEl).toHaveValue('z');
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
test('Updates the input when options are highlighted', () => {
|
|
1035
|
+
const { inputEl, openMenu } = renderCombobox(select);
|
|
1036
|
+
openMenu();
|
|
1037
|
+
userEvent.type(inputEl, '{arrowdown}z');
|
|
1038
|
+
expect(inputEl).toHaveValue('z');
|
|
877
1039
|
});
|
|
878
1040
|
|
|
879
1041
|
test("Opens the menu if it's closed", async () => {
|
package/src/Combobox.story.tsx
CHANGED
|
@@ -50,6 +50,14 @@ storiesOf('Combobox', module)
|
|
|
50
50
|
<ComboboxOption value="apple" displayName="Apple" />
|
|
51
51
|
<ComboboxOption value="banana" displayName="Banana" />
|
|
52
52
|
<ComboboxOption value="carrot" displayName="Carrot" />
|
|
53
|
+
<ComboboxOption
|
|
54
|
+
value="paragraph"
|
|
55
|
+
displayName="Nullam quis risus eget urna mollis ornare vel eu leo. Vestibulum id ligula porta felis euismod semper."
|
|
56
|
+
/>
|
|
57
|
+
<ComboboxOption
|
|
58
|
+
value="hash"
|
|
59
|
+
displayName="5f4dcc3b5aa765d61d8327deb882cf99"
|
|
60
|
+
/>
|
|
53
61
|
<ComboboxOption value="dragonfruit" displayName="Dragonfruit" />
|
|
54
62
|
<ComboboxOption value="eggplant" displayName="Eggplant" />
|
|
55
63
|
<ComboboxOption value="fig" displayName="Fig" />
|
|
@@ -189,10 +197,9 @@ storiesOf('Combobox', module)
|
|
|
189
197
|
);
|
|
190
198
|
})
|
|
191
199
|
.add('Controlled single select', () => {
|
|
192
|
-
const [selection, setSelection] = useState<string | null>(
|
|
200
|
+
const [selection, setSelection] = useState<string | null>(null);
|
|
193
201
|
|
|
194
202
|
const handleChange = (value: string | null) => {
|
|
195
|
-
console.log({ value });
|
|
196
203
|
setSelection(value);
|
|
197
204
|
};
|
|
198
205
|
|
|
@@ -215,10 +222,9 @@ storiesOf('Combobox', module)
|
|
|
215
222
|
);
|
|
216
223
|
})
|
|
217
224
|
.add('Controlled multiselect', () => {
|
|
218
|
-
const [selection, setSelection] = useState([
|
|
225
|
+
const [selection, setSelection] = useState([] as Array<string>);
|
|
219
226
|
|
|
220
227
|
const handleChange = (value: Array<string>) => {
|
|
221
|
-
console.log({ value });
|
|
222
228
|
setSelection(value);
|
|
223
229
|
};
|
|
224
230
|
|
package/src/Combobox.styles.ts
CHANGED
|
@@ -50,7 +50,6 @@ export const comboboxParentStyle = ({
|
|
|
50
50
|
--lg-combobox-font-size: 14px;
|
|
51
51
|
--lg-combobox-line-height: 20px;
|
|
52
52
|
--lg-combobox-border-radius: 3px;
|
|
53
|
-
--lg-combobox-input-default-width: 12ch;
|
|
54
53
|
--lg-combobox-icon-height: 16px;
|
|
55
54
|
`;
|
|
56
55
|
}
|
|
@@ -139,19 +138,11 @@ export const inputWrapperStyle = ({
|
|
|
139
138
|
const isMultiselect = isArray(selection) && selection.length > 0;
|
|
140
139
|
const inputLength = value?.length ?? 0;
|
|
141
140
|
|
|
142
|
-
// The input should be hidden when there are elements selected in a multiselect
|
|
143
|
-
// We don't set \`display: none\` since we need to be able to set .focus() on the element
|
|
144
|
-
const inputWidth = isMultiselect
|
|
145
|
-
? isOpen || inputLength > 0
|
|
146
|
-
? `${inputLength + 1}ch`
|
|
147
|
-
: '0'
|
|
148
|
-
: 'var(--lg-combobox-input-default-width)';
|
|
149
|
-
|
|
150
141
|
const baseWrapperStyle = css`
|
|
151
142
|
flex-grow: 1;
|
|
152
143
|
width: var(--lg-combobox-width);
|
|
153
144
|
|
|
154
|
-
--lg-combobox-input-width: ${
|
|
145
|
+
--lg-combobox-input-width: ${isMultiselect ? `${inputLength}ch` : '100%'};
|
|
155
146
|
`;
|
|
156
147
|
|
|
157
148
|
switch (overflow) {
|
|
@@ -178,12 +169,12 @@ export const inputWrapperStyle = ({
|
|
|
178
169
|
& > * {
|
|
179
170
|
margin-inline: 2px;
|
|
180
171
|
|
|
181
|
-
&:first-child {
|
|
182
|
-
margin-inline-start:
|
|
172
|
+
&:first-child:not(input) {
|
|
173
|
+
margin-inline-start: 0;
|
|
183
174
|
}
|
|
184
175
|
|
|
185
|
-
&:last-child {
|
|
186
|
-
margin-inline-end:
|
|
176
|
+
&:last-child:not(input) {
|
|
177
|
+
margin-inline-end: 0;
|
|
187
178
|
}
|
|
188
179
|
}
|
|
189
180
|
`;
|
|
@@ -196,7 +187,8 @@ export const inputWrapperStyle = ({
|
|
|
196
187
|
gap: 4px;
|
|
197
188
|
flex-wrap: nowrap;
|
|
198
189
|
white-space: nowrap;
|
|
199
|
-
--lg-combobox-
|
|
190
|
+
height: var(--lg-combobox-height);
|
|
191
|
+
--lg-combobox-input-transition: none;
|
|
200
192
|
`;
|
|
201
193
|
}
|
|
202
194
|
|
|
@@ -208,6 +200,7 @@ export const inputWrapperStyle = ({
|
|
|
208
200
|
flex-wrap: wrap;
|
|
209
201
|
gap: 4px;
|
|
210
202
|
overflow-x: visible;
|
|
203
|
+
min-height: var(--lg-combobox-height);
|
|
211
204
|
`;
|
|
212
205
|
}
|
|
213
206
|
}
|
|
@@ -218,15 +211,12 @@ export const inputElementStyle = css`
|
|
|
218
211
|
cursor: inherit;
|
|
219
212
|
background-color: inherit;
|
|
220
213
|
box-sizing: content-box;
|
|
221
|
-
padding
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
height: var(--lg-combobox-
|
|
226
|
-
width: var(
|
|
227
|
-
--lg-combobox-input-width,
|
|
228
|
-
var(--lg-combobox-input-default-width, auto)
|
|
229
|
-
);
|
|
214
|
+
padding: 0;
|
|
215
|
+
margin: 0;
|
|
216
|
+
text-overflow: ellipsis;
|
|
217
|
+
line-height: var(--lg-combobox-line-height);
|
|
218
|
+
height: var(--lg-combobox-height);
|
|
219
|
+
width: var(--lg-combobox-input-width, 0);
|
|
230
220
|
transition: var(--lg-combobox-input-transition);
|
|
231
221
|
|
|
232
222
|
&:focus {
|