@transferwise/components 46.67.1 → 46.68.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.
Files changed (75) hide show
  1. package/build/button/Button.js +22 -6
  2. package/build/button/Button.js.map +1 -1
  3. package/build/button/Button.mjs +22 -6
  4. package/build/button/Button.mjs.map +1 -1
  5. package/build/main.css +212 -210
  6. package/build/styles/button/Button.css +3 -0
  7. package/build/styles/main.css +212 -210
  8. package/build/styles/uploadInput/UploadInput.css +13 -81
  9. package/build/styles/uploadInput/uploadButton/UploadButton.css +77 -31
  10. package/build/styles/uploadInput/uploadItem/UploadItem.css +130 -109
  11. package/build/typeahead/Typeahead.js +23 -10
  12. package/build/typeahead/Typeahead.js.map +1 -1
  13. package/build/typeahead/Typeahead.mjs +23 -11
  14. package/build/typeahead/Typeahead.mjs.map +1 -1
  15. package/build/typeahead/typeaheadInput/TypeaheadInput.js +6 -3
  16. package/build/typeahead/typeaheadInput/TypeaheadInput.js.map +1 -1
  17. package/build/typeahead/typeaheadInput/TypeaheadInput.mjs +6 -3
  18. package/build/typeahead/typeaheadInput/TypeaheadInput.mjs.map +1 -1
  19. package/build/typeahead/typeaheadOption/TypeaheadOption.js +9 -6
  20. package/build/typeahead/typeaheadOption/TypeaheadOption.js.map +1 -1
  21. package/build/typeahead/typeaheadOption/TypeaheadOption.mjs +9 -6
  22. package/build/typeahead/typeaheadOption/TypeaheadOption.mjs.map +1 -1
  23. package/build/types/button/Button.d.ts.map +1 -1
  24. package/build/types/typeahead/Typeahead.d.ts +1 -1
  25. package/build/types/typeahead/Typeahead.d.ts.map +1 -1
  26. package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts +2 -0
  27. package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts.map +1 -1
  28. package/build/types/typeahead/typeaheadOption/TypeaheadOption.d.ts +2 -1
  29. package/build/types/typeahead/typeaheadOption/TypeaheadOption.d.ts.map +1 -1
  30. package/build/types/uploadInput/UploadInput.d.ts.map +1 -1
  31. package/build/types/uploadInput/uploadButton/UploadButton.d.ts +5 -0
  32. package/build/types/uploadInput/uploadButton/UploadButton.d.ts.map +1 -1
  33. package/build/types/uploadInput/uploadItem/UploadItem.d.ts.map +1 -1
  34. package/build/uploadInput/UploadInput.js +30 -20
  35. package/build/uploadInput/UploadInput.js.map +1 -1
  36. package/build/uploadInput/UploadInput.mjs +30 -20
  37. package/build/uploadInput/UploadInput.mjs.map +1 -1
  38. package/build/uploadInput/uploadButton/UploadButton.js +25 -32
  39. package/build/uploadInput/uploadButton/UploadButton.js.map +1 -1
  40. package/build/uploadInput/uploadButton/UploadButton.mjs +25 -32
  41. package/build/uploadInput/uploadButton/UploadButton.mjs.map +1 -1
  42. package/build/uploadInput/uploadItem/UploadItem.js +32 -38
  43. package/build/uploadInput/uploadItem/UploadItem.js.map +1 -1
  44. package/build/uploadInput/uploadItem/UploadItem.mjs +33 -39
  45. package/build/uploadInput/uploadItem/UploadItem.mjs.map +1 -1
  46. package/build/uploadInput/uploadItem/UploadItemLink.js +2 -1
  47. package/build/uploadInput/uploadItem/UploadItemLink.js.map +1 -1
  48. package/build/uploadInput/uploadItem/UploadItemLink.mjs +2 -1
  49. package/build/uploadInput/uploadItem/UploadItemLink.mjs.map +1 -1
  50. package/package.json +3 -3
  51. package/src/button/Button.css +3 -0
  52. package/src/button/Button.less +6 -0
  53. package/src/button/Button.spec.js +81 -45
  54. package/src/button/Button.story.tsx +54 -20
  55. package/src/button/Button.tsx +32 -7
  56. package/src/button/__snapshots__/Button.spec.js.snap +56 -12
  57. package/src/main.css +212 -210
  58. package/src/typeahead/Typeahead.spec.js +14 -0
  59. package/src/typeahead/Typeahead.story.tsx +12 -9
  60. package/src/typeahead/Typeahead.tsx +34 -16
  61. package/src/typeahead/typeaheadInput/TypeaheadInput.spec.js +6 -0
  62. package/src/typeahead/typeaheadInput/TypeaheadInput.tsx +10 -1
  63. package/src/typeahead/typeaheadOption/TypeaheadOption.spec.js +8 -0
  64. package/src/typeahead/typeaheadOption/TypeaheadOption.tsx +9 -6
  65. package/src/uploadInput/UploadInput.css +13 -81
  66. package/src/uploadInput/UploadInput.less +18 -80
  67. package/src/uploadInput/UploadInput.tests.story.tsx +5 -5
  68. package/src/uploadInput/UploadInput.tsx +43 -32
  69. package/src/uploadInput/uploadButton/UploadButton.css +77 -31
  70. package/src/uploadInput/uploadButton/UploadButton.less +77 -35
  71. package/src/uploadInput/uploadButton/UploadButton.tsx +37 -26
  72. package/src/uploadInput/uploadItem/UploadItem.css +130 -109
  73. package/src/uploadInput/uploadItem/UploadItem.less +130 -118
  74. package/src/uploadInput/uploadItem/UploadItem.tsx +26 -28
  75. package/src/uploadInput/uploadItem/UploadItemLink.tsx +3 -3
package/src/main.css CHANGED
@@ -543,6 +543,9 @@ div.critical-comms .critical-comms-body {
543
543
  .np-btn.np-btn-xs.btn-block > span.btn-loader {
544
544
  top: 0;
545
545
  }
546
+ .np-btn.disabled[class] {
547
+ pointer-events: auto;
548
+ }
546
549
  .np-card {
547
550
  overflow: hidden;
548
551
  transition-property: transform, box-shadow;
@@ -5458,280 +5461,279 @@ html:not([dir="rtl"]) .np-navigation-option {
5458
5461
  .upload-error-message .alert {
5459
5462
  min-width: 100px;
5460
5463
  }
5461
- .np-upload-button-container {
5462
- border-style: solid;
5463
- }
5464
- .np-upload-button-container .droppable-card-content {
5464
+ .np-upload-input__upload-button {
5465
+ position: relative;
5466
+ padding: 16px;
5467
+ padding: var(--size-16);
5465
5468
  display: flex;
5469
+ align-items: center;
5470
+ margin: 0;
5471
+ border-bottom-left-radius: 10px;
5472
+ border-bottom-left-radius: var(--radius-small);
5473
+ border-bottom-right-radius: 10px;
5474
+ border-bottom-right-radius: var(--radius-small);
5475
+ border: var(--outerBorder);
5476
+ cursor: pointer;
5477
+ }
5478
+ .np-upload-input__upload-button .np-upload-input__title {
5479
+ color: var(--color-content-link);
5480
+ -webkit-text-decoration: underline;
5481
+ text-decoration: underline;
5482
+ text-underline-offset: 0.3em;
5466
5483
  }
5467
- .np-upload-button-container.droppable-dropping {
5468
- border-color: #c9cbce !important;
5469
- border-color: var(--color-interactive-secondary) !important;
5484
+ .np-upload-input__upload-button .np-upload-input__title + .np-upload-input__text {
5485
+ margin-top: 4px;
5486
+ margin-top: var(--size-4);
5470
5487
  }
5471
- .np-upload-button-container.droppable-dropping:before {
5472
- z-index: 2;
5488
+ .np-upload-input__upload-button .np-upload-input__icon {
5489
+ padding-right: 16px;
5490
+ padding-right: var(--size-16);
5491
+ color: var(--color-interactive-primary);
5473
5492
  }
5474
- .np-upload-button-container input[type="file"] {
5493
+ .np-upload-input__upload-button.is-dropping .np-upload-input__icon,
5494
+ .np-upload-input__upload-button.is-dropping .np-upload-input__item-content {
5495
+ display: none;
5496
+ }
5497
+ .np-upload-input__upload-button:focus-within,
5498
+ .np-upload-input__upload-button:focus-visible {
5499
+ outline: var(--ring-outline-color) solid 3px;
5500
+ outline-offset: -3px;
5501
+ }
5502
+ .np-upload-input__upload-button-input {
5503
+ position: absolute;
5475
5504
  opacity: 0;
5476
5505
  z-index: -1;
5506
+ }
5507
+ .np-upload-input__upload-button-input:focus {
5508
+ outline: none;
5509
+ }
5510
+ .np-upload-input__upload-button .np-upload-input__drop-file-overlay {
5511
+ display: flex;
5512
+ flex: 1;
5513
+ padding: 13px 0 !important;
5514
+ background-color: transparent;
5515
+ transition: transform 0.3s ease;
5516
+ position: relative;
5517
+ }
5518
+ .np-upload-input__upload-button--with-entries {
5519
+ border-top-width: 0;
5520
+ }
5521
+ .np-upload-input__upload-button--with-entries:before {
5522
+ display: block;
5477
5523
  position: absolute;
5524
+ height: 1px;
5525
+ background-color: rgba(0,0,0,0.10196);
5526
+ background-color: var(--color-border-neutral);
5527
+ content: " ";
5528
+ left: 16px;
5529
+ left: var(--size-16);
5530
+ width: calc(100% - 2 * 16px);
5531
+ width: calc(100% - 2 * var(--size-16));
5532
+ top: 0;
5478
5533
  }
5479
- .np-upload-button-container .np-upload-button {
5480
- border: none;
5534
+ .np-upload-input__upload-button--without-entries {
5535
+ border-top: var(--outerBorder);
5536
+ border-radius: 10px;
5537
+ border-radius: var(--radius-small);
5538
+ }
5539
+ .np-upload-input__upload-button--enabled.is-dropping,
5540
+ .np-upload-input__upload-button--enabled:hover,
5541
+ .np-upload-input__upload-button--enabled:active {
5542
+ background: rgba(134,167,189,0.10196);
5543
+ background: var(--color-background-neutral);
5481
5544
  }
5482
- .np-upload-button {
5545
+ .np-upload-input__upload-button--enabled.is-dropping:before,
5546
+ .np-upload-input__upload-button--enabled:hover:before,
5547
+ .np-upload-input__upload-button--enabled:active:before {
5483
5548
  width: 100%;
5484
- border-top: 1px solid transparent;
5485
- padding: 16px;
5486
- padding: var(--padding-small);
5487
- border-radius: 0;
5549
+ left: 0;
5488
5550
  }
5489
- label.np-upload-button:not(.disabled):hover,
5490
- label.np-upload-button:not(.disabled):active {
5491
- background-color: var(--color-background-screen-hover);
5551
+ .np-upload-input__upload-button--disabled {
5552
+ cursor: inherit;
5492
5553
  }
5493
- .disabled label.np-upload-button:not(.disabled):hover,
5494
- .disabled label.np-upload-button:not(.disabled):active {
5495
- background-color: transparent;
5554
+ .np-upload-input {
5555
+ --outerBorder: 1px solid var(--color-interactive-secondary);
5556
+ border-radius: 10px;
5557
+ border-radius: var(--radius-small);
5496
5558
  }
5497
- .np-upload-button .media {
5498
- align-items: flex-start;
5559
+ .np-upload-input .np-upload-input__section {
5560
+ margin: 0;
5499
5561
  }
5500
- @media (max-width: 320px) {
5501
- .np-upload-icon {
5502
- padding-left: 0;
5503
- }
5562
+ .np-upload-input .np-upload-input__section:only-child .np-upload-input__item {
5563
+ border-radius: 10px;
5564
+ border-radius: var(--radius-small);
5565
+ border-bottom: var(--outerBorder);
5504
5566
  }
5505
- .np-upload-input.form-control {
5506
- height: auto;
5507
- padding: 0;
5508
- padding: initial;
5567
+ .np-upload-input .np-upload-input__section:has(.np-upload-input__item:last-child.is-interactive:hover):not(:has(.np-upload-input__item-button:hover)) + .np-upload-input__section--uploader .np-upload-input__upload-button::before {
5568
+ left: 0;
5569
+ width: 100%;
5570
+ top: 0;
5571
+ }
5572
+ .np-upload-input__item {
5573
+ position: relative;
5574
+ padding: 16px;
5575
+ padding: var(--size-16);
5576
+ display: flex;
5577
+ align-items: flex-start;
5578
+ border-left: var(--outerBorder);
5579
+ border-right: var(--outerBorder);
5509
5580
  }
5510
- .np-upload-input > div:first-child,
5511
- .np-upload-input > div:first-child .np-upload-item--single-file,
5512
- .np-upload-input > div:first-child .np-upload-item--link {
5581
+ .np-upload-input__item:first-child {
5582
+ border-top: var(--outerBorder);
5513
5583
  border-top-left-radius: 10px;
5514
5584
  border-top-left-radius: var(--radius-small);
5515
5585
  border-top-right-radius: 10px;
5516
5586
  border-top-right-radius: var(--radius-small);
5517
5587
  }
5518
- .np-upload-input > div:last-child {
5519
- border-bottom-left-radius: 10px;
5520
- border-bottom-left-radius: var(--radius-small);
5521
- border-bottom-right-radius: 10px;
5522
- border-bottom-right-radius: var(--radius-small);
5523
- }
5524
- .np-theme-personal .np-upload-input.disabled .btn {
5525
- cursor: inherit;
5526
- }
5527
- .np-theme-personal .np-upload-input .np-upload-icon {
5528
- color: var(--color-interactive-primary);
5588
+ .np-upload-input__item + .np-upload-input__item:before {
5589
+ content: " ";
5590
+ display: block;
5591
+ position: absolute;
5592
+ height: 1px;
5593
+ left: 16px;
5594
+ left: var(--size-16);
5595
+ width: calc(100% - 2 * 16px);
5596
+ width: calc(100% - 2 * var(--size-16));
5597
+ top: 0;
5598
+ background: rgba(0,0,0,0.10196);
5599
+ background: var(--color-border-neutral);
5529
5600
  }
5530
- .np-theme-personal .np-upload-input .media-body {
5601
+ .np-upload-input__item .np-upload-input__item-content {
5531
5602
  padding-right: 32px;
5532
5603
  padding-right: var(--size-32);
5533
- color: var(--color-content-link);
5534
- white-space: break-spaces;
5604
+ flex: 1;
5535
5605
  }
5536
5606
  @media (max-width: 320px) {
5537
- .np-theme-personal .np-upload-input .media-body {
5607
+ .np-upload-input__item .np-upload-input__item-content {
5538
5608
  padding-right: 64px;
5539
5609
  padding-right: var(--size-64);
5540
5610
  }
5541
5611
  }
5542
- .np-theme-personal .np-upload-input .media-body .np-text-body-large-bold {
5543
- -webkit-text-decoration: underline;
5544
- text-decoration: underline;
5545
- text-underline-offset: 0.3em;
5546
- }
5547
- .np-theme-personal .np-upload-input .media-body .np-text-body-default,
5548
- .np-theme-personal .np-upload-input .media-body .np-upload-description,
5549
- .np-theme-personal .np-upload-input .media-body .text-positive {
5550
- color: #5d7079 !important;
5551
- color: var(--color-content-secondary) !important;
5552
- }
5553
- .np-theme-personal .np-upload-input .media-body .text-negative {
5554
- color: var(--color-sentiment-negative) !important;
5612
+ .np-upload-input__item .np-upload-input__title,
5613
+ .np-upload-input__item .np-upload-input__text {
5614
+ margin: 0;
5615
+ -moz-text-align-last: left;
5616
+ text-align-last: left;
5617
+ color: #5d7079;
5618
+ color: var(--color-content-secondary);
5555
5619
  }
5556
- .np-theme-personal .np-upload-input-errors {
5557
- list-style: none;
5558
- padding-left: 0;
5620
+ .np-upload-input__item .np-upload-input__title + .np-upload-input__text {
5621
+ margin-top: 4px;
5622
+ margin-top: var(--size-4);
5559
5623
  }
5560
- .np-theme-personal .np-upload-input-errors li {
5561
- position: relative;
5562
- padding-left: 16px;
5563
- padding-left: var(--size-16);
5624
+ .np-upload-input__item .np-upload-input__icon {
5625
+ padding-right: 16px;
5626
+ padding-right: var(--size-16);
5564
5627
  }
5565
- @media (max-width: 320px) {
5566
- .np-theme-personal .np-upload-input-errors li {
5567
- padding-left: 32px;
5568
- padding-left: var(--size-32);
5569
- }
5628
+ .np-upload-input__item .np-upload-input__item-link,
5629
+ .np-upload-input__item .np-upload-input__item-container {
5630
+ align-items: flex-start;
5631
+ display: flex;
5632
+ width: 100%;
5570
5633
  }
5571
- .np-theme-personal .np-upload-input-errors li:before {
5572
- content: '•';
5634
+ .np-upload-input__item .np-upload-input__item-action {
5635
+ --iconSize: var(--size-24);
5636
+ --clickAreaSize: 44px;
5637
+ --buttonTopRightOffset: var(--size-16);
5638
+ --clickAreaTopRightOffset: calc((var(--clickAreaSize) - var(--iconSize)) * -0.5);
5573
5639
  position: absolute;
5574
- display: block;
5575
- left: 0;
5576
- }
5577
- .np-theme-personal .np-upload-input .status-circle {
5578
- width: 24px;
5579
- width: var(--size-x-small);
5580
- height: 24px;
5581
- height: var(--size-x-small);
5640
+ right: 16px;
5641
+ right: var(--buttonTopRightOffset);
5642
+ top: 16px;
5643
+ top: var(--buttonTopRightOffset);
5582
5644
  }
5583
5645
  @media (max-width: 320px) {
5584
- .np-theme-personal .np-upload-input .status-circle {
5585
- width: 48px;
5586
- width: var(--size-large);
5587
- height: 48px;
5588
- height: var(--size-large);
5646
+ .np-upload-input__item .np-upload-input__item-action {
5647
+ --iconSize: var(--size-48);
5648
+ --clickAreaTopRightOffset: calc(-1 * var(--buttonTopRightOffset));
5589
5649
  }
5590
5650
  }
5591
- .np-upload-item {
5592
- border: 1px solid #c9cbce;
5593
- border: 1px solid var(--color-interactive-secondary);
5594
- position: relative;
5651
+ .np-upload-input__item .np-upload-input__item-action .np-upload-input__item-button {
5652
+ -webkit-appearance: none;
5653
+ -moz-appearance: none;
5654
+ appearance: none;
5655
+ height: var(--iconSize);
5656
+ width: var(--iconSize);
5657
+ padding: 0 4px;
5658
+ padding: 0 var(--size-4);
5659
+ border-radius: 50%;
5660
+ border: 0;
5661
+ background-color: rgba(134,167,189,0.10196);
5662
+ background-color: var(--color-background-neutral);
5663
+ color: var(--color-interactive-primary);
5664
+ transition: color, background-color 0.15s ease-in-out;
5665
+ outline-offset: 0;
5666
+ display: flex;
5667
+ align-items: center;
5668
+ justify-content: center;
5595
5669
  }
5596
- .np-upload-item:first-child ~ div:not(.np-upload-item--link):before,
5597
- .np-upload-item:not(:first-child).np-upload-item--link .np-upload-item__link:before,
5598
- .np-upload-item.np-upload-item--link:hover .np-upload-item__link:after {
5670
+ .np-upload-input__item .np-upload-input__item-action .np-upload-input__item-button:before {
5671
+ content: '';
5599
5672
  display: block;
5673
+ width: var(--clickAreaSize);
5674
+ height: var(--clickAreaSize);
5675
+ border-radius: 50%;
5600
5676
  position: absolute;
5601
- height: 1px;
5602
- background-color: rgba(0,0,0,0.10196);
5603
- background-color: var(--color-border-neutral);
5604
- content: " ";
5605
- left: 16px;
5606
- left: var(--size-16);
5607
- width: calc(100% - 2 * 16px);
5608
- width: calc(100% - 2 * var(--size-16));
5609
- }
5610
- .np-upload-item:first-child ~ div:not(.np-upload-item--link):before,
5611
- .np-upload-item:not(:first-child).np-upload-item--link .np-upload-item__link:before {
5612
- top: 0;
5613
- }
5614
- .np-upload-item.np-upload-item--link:hover .np-upload-item__link:after {
5615
- bottom: -1px;
5616
- }
5617
- .np-upload-item:first-child ~ div {
5618
- border-top: 1px;
5619
- }
5620
- .np-upload-item:not(:first-child) .np-upload-item__link:hover {
5621
- border-top-color: rgba(0,0,0,0.10196);
5622
- border-top-color: var(--color-border-neutral);
5677
+ top: var(--clickAreaTopRightOffset);
5678
+ right: var(--clickAreaTopRightOffset);
5623
5679
  }
5624
- .np-upload-item:not(:last-child) {
5625
- border-bottom: 0;
5626
- }
5627
- .np-upload-item.np-upload-item--link:hover + .np-upload-item:before,
5628
- .np-upload-item.np-upload-item--link:hover + .np-upload-button-container:before,
5629
- .np-upload-item.np-upload-item--link:hover + .np-upload-item .np-upload-item__link:before,
5630
- .np-upload-item.np-upload-item--link:hover + .np-upload-button-container .np-upload-item__link:before {
5631
- display: none;
5680
+ .np-upload-input__item .np-upload-input__item-action .np-upload-input__item-button:hover {
5681
+ background-color: var(--color-sentiment-negative);
5682
+ color: var(--color-contrast-overlay) !important;
5632
5683
  }
5633
- .np-upload-button-container:hover:before,
5634
- .np-upload-button-container.droppable-dropping:before {
5635
- left: 0 !important;
5636
- width: 100% !important;
5684
+ .np-upload-input__item .np-upload-input__item-action .np-upload-input__item-button:active {
5685
+ background-color: var(--color-background-neutral-active);
5637
5686
  }
5638
- .np-upload-button-container:has(:focus-visible) {
5639
- outline: var(--ring-outline-color) solid var(--ring-outline-width);
5640
- outline-offset: var(--ring-outline-offset);
5641
- border-color: transparent;
5642
- outline-offset: -3px;
5687
+ .np-upload-input__item.is-interactive {
5688
+ padding: 0;
5643
5689
  }
5644
- .np-upload-item--single-file:focus-visible,
5645
- .np-upload-item__link:focus-visible,
5646
- .np-upload-button-container:has(:focus-visible) {
5647
- outline-width: 3px;
5690
+ .np-upload-input__item.is-interactive:hover:not(:has(.np-upload-input__item-button:hover)):before,
5691
+ .np-upload-input__item.is-interactive:hover:not(:has(.np-upload-input__item-button:hover)) + .np-upload-input__item:before {
5692
+ width: 100%;
5693
+ left: 0;
5648
5694
  }
5649
- .np-upload-item--link a {
5650
- flex: 1;
5695
+ .np-upload-input__item.is-interactive .np-upload-input__item-link {
5696
+ padding: 16px;
5697
+ padding: var(--size-16);
5651
5698
  -webkit-text-decoration: none;
5652
5699
  text-decoration: none;
5653
- border-top: 1px solid transparent;
5654
5700
  border-radius: inherit;
5701
+ border-top: 1px solid transparent;
5702
+ background-clip: padding-box;
5655
5703
  }
5656
- .np-upload-item--link a:focus-visible {
5704
+ .np-upload-input__item.is-interactive .np-upload-input__item-link:focus-visible {
5657
5705
  outline-offset: -2px;
5706
+ outline-width: 3px;
5658
5707
  }
5659
- .np-upload-item--link a:hover:before {
5660
- display: none !important;
5661
- }
5662
- .np-upload-item--link a:hover:after {
5663
- left: 0 !important;
5664
- width: 100% !important;
5665
- }
5666
- .np-upload-item--link a:hover,
5667
- .np-upload-item--link a:active {
5668
- -webkit-text-decoration: none;
5669
- text-decoration: none;
5670
- }
5671
- .np-upload-item--link a:hover .np-upload-button,
5672
- .np-upload-item--link a:active .np-upload-button {
5708
+ .np-upload-input__item.is-interactive .np-upload-input__item-link:hover,
5709
+ .np-upload-input__item.is-interactive .np-upload-input__item-link:active {
5673
5710
  background-color: rgba(134,167,189,0.10196);
5674
5711
  background-color: var(--color-background-neutral);
5675
- border-radius: inherit;
5676
5712
  }
5677
- .np-upload-item--link:first-of-type a {
5678
- border-top: 0;
5713
+ .np-upload-input__item.is-interactive:first-child .np-upload-input__item-link {
5714
+ border-top-width: 0;
5679
5715
  }
5680
- .np-upload-item__body {
5681
- display: flex;
5682
- align-items: center;
5683
- justify-content: space-between;
5716
+ .np-upload-input__item .np-upload-input-errors {
5717
+ padding-left: 0;
5718
+ list-style-type: "";
5719
+ }
5720
+ .np-upload-input__item .np-upload-input-errors > li {
5684
5721
  position: relative;
5685
- border-radius: inherit;
5722
+ padding-left: 16px;
5723
+ padding-left: var(--size-16);
5686
5724
  }
5687
- .np-upload-item__remove-button {
5688
- display: flex;
5689
- align-items: center;
5690
- justify-content: center;
5691
- align-self: flex-start;
5725
+ .np-upload-input__item .np-upload-input-errors > li::before {
5726
+ content: '•';
5692
5727
  position: absolute;
5693
- height: 24px;
5694
- height: var(--size-24);
5695
- min-height: 0;
5696
- width: 24px;
5697
- width: var(--size-24);
5698
- padding: 0;
5699
- border-radius: 50% !important;
5700
- outline-offset: 0 !important;
5701
- background-color: rgba(134,167,189,0.10196);
5702
- background-color: var(--color-background-neutral);
5703
- border: none;
5704
- color: var(--color-interactive-primary);
5705
- right: 16px;
5706
- right: var(--size-16);
5707
- top: 16px;
5708
- top: var(--size-16);
5709
- transition: color, background-color 0.15s ease-in-out;
5728
+ display: block;
5729
+ left: 0;
5710
5730
  }
5711
5731
  @media (max-width: 320px) {
5712
- .np-upload-item__remove-button {
5713
- top: 16px;
5714
- top: var(--size-16);
5715
- right: 16px;
5716
- right: var(--size-16);
5717
- height: 48px;
5718
- height: var(--size-48);
5719
- width: 48px;
5720
- width: var(--size-48);
5732
+ .np-upload-input__item .np-upload-input-errors > li {
5733
+ padding-left: 32px;
5734
+ padding-left: var(--size-32);
5721
5735
  }
5722
5736
  }
5723
- .np-upload-item__remove-button:hover {
5724
- background-color: var(--color-sentiment-negative);
5725
- color: var(--color-contrast-overlay) !important;
5726
- }
5727
- .np-upload-item__remove-button:before {
5728
- display: block;
5729
- width: 44px;
5730
- height: 44px;
5731
- content: '';
5732
- border-radius: 50%;
5733
- position: absolute;
5734
- }
5735
5737
  .np-progress {
5736
5738
  border-radius: 10px;
5737
5739
  border-radius: var(--radius-small);
@@ -258,6 +258,14 @@ describe('Typeahead', () => {
258
258
  expect(event.preventDefault).toHaveBeenCalledTimes(1);
259
259
  });
260
260
 
261
+ it('sets aria-activedescendant correctly based on focus', () => {
262
+ const inputElement = input().getDOMNode();
263
+
264
+ doTimes(3, () => input().simulate('keyDown', fakeKeyDownEventForKey('ArrowDown')));
265
+
266
+ expect(inputElement).toHaveAttribute('aria-activedescendant', 'menu-test option-Test2');
267
+ });
268
+
261
269
  it('displays alert when alert is provided and chips are valid or alert type is error', () => {
262
270
  const event = {
263
271
  key: 'Enter',
@@ -347,6 +355,12 @@ describe('Typeahead', () => {
347
355
  expect(options.every((label, i) => label === props.options[i].label)).toBe(true);
348
356
  });
349
357
 
358
+ it('renders dropdown-menu if has options', () => {
359
+ expect(component.find('.dropdown-menu')).toHaveLength(1);
360
+ component.setProps({ options: [] });
361
+ expect(component.find('.dropdown-menu')).toHaveLength(0);
362
+ });
363
+
350
364
  it('does not render new option if showNewEntry is false', () => {
351
365
  component.setProps({
352
366
  allowNew: true,
@@ -6,6 +6,7 @@ import Typeahead, { type TypeaheadOption } from './Typeahead';
6
6
  import { Size } from '../common';
7
7
  import { useState } from 'react';
8
8
  import { Input } from '../inputs/Input';
9
+ import { Field } from '../field/Field';
9
10
 
10
11
  type Story = StoryObj<typeof Typeahead>;
11
12
 
@@ -103,15 +104,17 @@ export const Basic: Story = {
103
104
 
104
105
  export const Creatable: Story = {
105
106
  render: (args) => (
106
- <Typeahead
107
- {...args}
108
- allowNew
109
- multiple
110
- initialValue={[]}
111
- validateChip={validateOptionAsEmail}
112
- addon={<SearchIcon size={24} />}
113
- options={[]}
114
- />
107
+ <Field id={`input-${args.id}`} label="Typeahead with values">
108
+ <Typeahead
109
+ {...args}
110
+ allowNew
111
+ multiple
112
+ initialValue={[]}
113
+ validateChip={validateOptionAsEmail}
114
+ addon={<SearchIcon size={24} />}
115
+ options={[]}
116
+ />
117
+ </Field>
115
118
  ),
116
119
  play: async ({ canvasElement }) => {
117
120
  const canvas = within(canvasElement);
@@ -3,7 +3,7 @@ import { clsx } from 'clsx';
3
3
  import { DebouncedFunc } from 'lodash';
4
4
  import clamp from 'lodash.clamp';
5
5
  import debounce from 'lodash.debounce';
6
- import { Component, ReactNode } from 'react';
6
+ import React, { Component, ReactNode } from 'react';
7
7
  import { injectIntl, WrappedComponentProps } from 'react-intl';
8
8
 
9
9
  import Chip from '../chips/Chip';
@@ -101,6 +101,7 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
101
101
  size: Size.MEDIUM,
102
102
  validateChip: () => true,
103
103
  } satisfies Partial<TypeaheadProps<unknown>>;
104
+ optionRefs: (React.RefObject<HTMLLIElement> | null)[];
104
105
 
105
106
  constructor(props: TypeaheadPropsWithInputAttributes<T>) {
106
107
  super(props);
@@ -115,6 +116,7 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
115
116
  optionsShown: false,
116
117
  isFocused: false,
117
118
  };
119
+ this.optionRefs = [] as (React.RefObject<HTMLLIElement> | null)[];
118
120
  }
119
121
 
120
122
  handleSearchDebounced: DebouncedFunc<Typeahead<T>['handleSearch']>;
@@ -228,7 +230,6 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
228
230
  }
229
231
  }
230
232
  };
231
-
232
233
  moveFocusedOption(offset: number) {
233
234
  this.setState((previousState) => {
234
235
  const { keyboardFocusedOptionIndex } = previousState;
@@ -237,6 +238,10 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
237
238
  if (keyboardFocusedOptionIndex !== null) {
238
239
  index = clamp(keyboardFocusedOptionIndex + offset, 0, options.length - 1);
239
240
  }
241
+ const optionRef = this.optionRefs[index];
242
+ if (optionRef?.current) {
243
+ optionRef.current.focus(); // Set focus on the option element
244
+ }
240
245
  return {
241
246
  keyboardFocusedOptionIndex: index,
242
247
  };
@@ -401,20 +406,28 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
401
406
  className={clsx('dropdown btn-group btn-block', { open: dropdownOpen })}
402
407
  id={`menu-${id}`}
403
408
  >
404
- <ul className="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
405
- {optionsToRender.map((option, idx) => (
406
- <TypeaheadOption
407
- key={`${option.label}${idx.toString()}`}
408
- query={query}
409
- option={option}
410
- selected={keyboardFocusedOptionIndex === idx}
411
- onClick={(event) => {
412
- this.onOptionSelected(event, option);
413
- }}
414
- />
415
- ))}
416
- {footer}
417
- </ul>
409
+ {!!optionsToRender.length && (
410
+ <ul className="dropdown-menu" role="menu">
411
+ {optionsToRender.map((option, idx) => {
412
+ const ref = React.createRef<HTMLLIElement>();
413
+ this.optionRefs[idx] = ref;
414
+ return (
415
+ <TypeaheadOption
416
+ key={`${option.label}${idx.toString()}`}
417
+ ref={ref}
418
+ query={query}
419
+ option={option}
420
+ selected={keyboardFocusedOptionIndex === idx}
421
+ id={String(option.label.replace(/\s+/g, '-'))}
422
+ onClick={(event) => {
423
+ this.onOptionSelected(event, option);
424
+ }}
425
+ />
426
+ )
427
+ })}
428
+ {footer}
429
+ </ul>
430
+ )}
418
431
  </div>
419
432
  );
420
433
  };
@@ -507,6 +520,11 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
507
520
  typeaheadId={id}
508
521
  renderChip={this.renderChip}
509
522
  autoComplete={inputAutoComplete}
523
+ ariaActivedescendant={
524
+ keyboardFocusedOptionIndex !== null && options[keyboardFocusedOptionIndex]
525
+ ? `option-${options[keyboardFocusedOptionIndex].label.replace(/\s+/g, '-')}`
526
+ : undefined
527
+ }
510
528
  onChange={this.handleOnChange}
511
529
  onKeyDown={this.handleOnKeyDown}
512
530
  onFocus={this.handleOnFocus}
@@ -45,6 +45,12 @@ describe('Typeahead input', () => {
45
45
  expect(inputWrapper().find('span')).toHaveLength(selected.length);
46
46
  });
47
47
 
48
+ it('adds aria-controls prop if dropdown is open', () => {
49
+ expect(component.prop('aria-controls')).toBeUndefined();
50
+ component.setProps({ dropdownOpen: true });
51
+ expect(component.prop('aria-controls')).toBe('menu-test');
52
+ });
53
+
48
54
  it('passes all callbacks to input', () => {
49
55
  const event = {};
50
56
  const extraProps = {