@coreui/react-pro 5.24.1 → 5.26.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/README.md +1 -1
- package/dist/cjs/components/card/CCardImage.js.map +1 -1
- package/dist/cjs/components/carousel/CCarousel.js +44 -33
- package/dist/cjs/components/carousel/CCarousel.js.map +1 -1
- package/dist/cjs/components/carousel/CCarouselItem.js +19 -28
- package/dist/cjs/components/carousel/CCarouselItem.js.map +1 -1
- package/dist/cjs/components/chip/CChip.d.ts +12 -0
- package/dist/cjs/components/chip/CChip.js +16 -63
- package/dist/cjs/components/chip/CChip.js.map +1 -1
- package/dist/cjs/components/chip/const.d.ts +1 -0
- package/dist/cjs/components/chip/const.js +8 -0
- package/dist/cjs/components/chip/const.js.map +1 -0
- package/dist/cjs/components/chip-set/CChipSet.d.ts +73 -0
- package/dist/cjs/components/chip-set/CChipSet.js +66 -0
- package/dist/cjs/components/chip-set/CChipSet.js.map +1 -0
- package/dist/cjs/components/chip-set/index.d.ts +2 -0
- package/dist/cjs/components/chip-set/useChipSet.d.ts +48 -0
- package/dist/cjs/components/chip-set/useChipSet.js +163 -0
- package/dist/cjs/components/chip-set/useChipSet.js.map +1 -0
- package/dist/cjs/components/chip-set/utils.d.ts +7 -0
- package/dist/cjs/components/chip-set/utils.js +18 -0
- package/dist/cjs/components/chip-set/utils.js.map +1 -0
- package/dist/cjs/components/collapse/CCollapse.js +14 -6
- package/dist/cjs/components/collapse/CCollapse.js.map +1 -1
- package/dist/cjs/components/conditional-portal/CConditionalPortal.js +3 -1
- package/dist/cjs/components/conditional-portal/CConditionalPortal.js.map +1 -1
- package/dist/cjs/components/date-picker/CDatePicker.d.ts +1 -1
- package/dist/cjs/components/date-picker/CDatePicker.js.map +1 -1
- package/dist/cjs/components/date-range-picker/CDateRangePicker.js +2 -0
- package/dist/cjs/components/date-range-picker/CDateRangePicker.js.map +1 -1
- package/dist/cjs/components/dropdown/CDropdown.js +23 -30
- package/dist/cjs/components/dropdown/CDropdown.js.map +1 -1
- package/dist/cjs/components/dropdown/CDropdownToggle.js.map +1 -1
- package/dist/cjs/components/form/CChipInput.d.ts +14 -2
- package/dist/cjs/components/form/CChipInput.js +77 -49
- package/dist/cjs/components/form/CChipInput.js.map +1 -1
- package/dist/cjs/components/form/CFormCheck.js.map +1 -1
- package/dist/cjs/components/form/CFormInput.js.map +1 -1
- package/dist/cjs/components/form/CFormSelect.js.map +1 -1
- package/dist/cjs/components/form/CFormTextarea.js.map +1 -1
- package/dist/cjs/components/form/CInputGroupText.js.map +1 -1
- package/dist/cjs/components/grid/CContainer.js +3 -1
- package/dist/cjs/components/grid/CContainer.js.map +1 -1
- package/dist/cjs/components/header/CHeaderBrand.js.map +1 -1
- package/dist/cjs/components/index.d.ts +2 -0
- package/dist/cjs/components/link/CLink.js +4 -2
- package/dist/cjs/components/link/CLink.js.map +1 -1
- package/dist/cjs/components/list-group/CListGroupItem.js.map +1 -1
- package/dist/cjs/components/modal/CModal.js +14 -2
- package/dist/cjs/components/modal/CModal.js.map +1 -1
- package/dist/cjs/components/multi-select/CMultiSelect.d.ts +73 -1
- package/dist/cjs/components/multi-select/CMultiSelect.js +157 -18
- package/dist/cjs/components/multi-select/CMultiSelect.js.map +1 -1
- package/dist/cjs/components/multi-select/CMultiSelectNativeSelect.js +1 -1
- package/dist/cjs/components/multi-select/CMultiSelectNativeSelect.js.map +1 -1
- package/dist/cjs/components/multi-select/CMultiSelectOptions.d.ts +3 -0
- package/dist/cjs/components/multi-select/CMultiSelectOptions.js +45 -2
- package/dist/cjs/components/multi-select/CMultiSelectOptions.js.map +1 -1
- package/dist/cjs/components/multi-select/CMultiSelectSelection.d.ts +1 -0
- package/dist/cjs/components/multi-select/CMultiSelectSelection.js +4 -3
- package/dist/cjs/components/multi-select/CMultiSelectSelection.js.map +1 -1
- package/dist/cjs/components/multi-select/types.d.ts +12 -0
- package/dist/cjs/components/multi-select/utils.js +1 -4
- package/dist/cjs/components/multi-select/utils.js.map +1 -1
- package/dist/cjs/components/nav/CNav.js.map +1 -1
- package/dist/cjs/components/nav/CNavGroup.d.ts +13 -6
- package/dist/cjs/components/nav/CNavGroup.js +86 -23
- package/dist/cjs/components/nav/CNavGroup.js.map +1 -1
- package/dist/cjs/components/nav/CNavGroupContext.d.ts +25 -0
- package/dist/cjs/components/nav/CNavGroupContext.js +13 -0
- package/dist/cjs/components/nav/CNavGroupContext.js.map +1 -0
- package/dist/cjs/components/nav/CNavLink.d.ts +1 -5
- package/dist/cjs/components/nav/CNavLink.js +21 -8
- package/dist/cjs/components/nav/CNavLink.js.map +1 -1
- package/dist/cjs/components/one-time-password-input/COneTimePassword.js +3 -3
- package/dist/cjs/components/one-time-password-input/COneTimePassword.js.map +1 -1
- package/dist/cjs/components/popover/CPopover.js +69 -24
- package/dist/cjs/components/popover/CPopover.js.map +1 -1
- package/dist/cjs/components/progress/CProgress.js.map +1 -1
- package/dist/cjs/components/rating/CRating.d.ts +1 -1
- package/dist/cjs/components/rating/CRating.js.map +1 -1
- package/dist/cjs/components/search-button/CSearchButton.d.ts +32 -0
- package/dist/cjs/components/search-button/CSearchButton.js +77 -0
- package/dist/cjs/components/search-button/CSearchButton.js.map +1 -0
- package/dist/cjs/components/search-button/index.d.ts +2 -0
- package/dist/cjs/components/search-button/types.d.ts +10 -0
- package/dist/cjs/components/search-button/utils.d.ts +11 -0
- package/dist/cjs/components/search-button/utils.js +115 -0
- package/dist/cjs/components/search-button/utils.js.map +1 -0
- package/dist/cjs/components/sidebar/CSidebar.js +40 -23
- package/dist/cjs/components/sidebar/CSidebar.js.map +1 -1
- package/dist/cjs/components/sidebar/CSidebarNav.d.ts +12 -0
- package/dist/cjs/components/sidebar/CSidebarNav.js +12 -36
- package/dist/cjs/components/sidebar/CSidebarNav.js.map +1 -1
- package/dist/cjs/components/smart-table/CSmartTable.js +2 -2
- package/dist/cjs/components/smart-table/CSmartTable.js.map +1 -1
- package/dist/cjs/components/smart-table/CSmartTableInterface.d.ts +2 -2
- package/dist/cjs/components/table/CTableDataCell.js.map +1 -1
- package/dist/cjs/components/table/types.d.ts +2 -1
- package/dist/cjs/components/table/utils.js +4 -4
- package/dist/cjs/components/table/utils.js.map +1 -1
- package/dist/cjs/components/tabs/CTabPanel.js +3 -1
- package/dist/cjs/components/tabs/CTabPanel.js.map +1 -1
- package/dist/cjs/components/time-picker/CTimePicker.d.ts +1 -1
- package/dist/cjs/components/time-picker/CTimePicker.js +1 -1
- package/dist/cjs/components/time-picker/CTimePicker.js.map +1 -1
- package/dist/cjs/components/tooltip/CTooltip.js +69 -24
- package/dist/cjs/components/tooltip/CTooltip.js.map +1 -1
- package/dist/cjs/components/virtual-scroller/CVirtualScroller.js +11 -8
- package/dist/cjs/components/virtual-scroller/CVirtualScroller.js.map +1 -1
- package/dist/cjs/helpers/polymorphicComponent.d.ts +1 -2
- package/dist/cjs/hooks/useDebouncedCallback.d.ts +1 -1
- package/dist/cjs/hooks/useDebouncedCallback.js +1 -1
- package/dist/cjs/hooks/useDebouncedCallback.js.map +1 -1
- package/dist/cjs/hooks/useForkedRef.d.ts +1 -1
- package/dist/cjs/hooks/useForkedRef.js +6 -3
- package/dist/cjs/hooks/useForkedRef.js.map +1 -1
- package/dist/cjs/hooks/usePopper.js +8 -6
- package/dist/cjs/hooks/usePopper.js.map +1 -1
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js +1 -1
- package/dist/cjs/node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js.map +1 -1
- package/dist/cjs/node_modules/@coreui/icons/dist/esm/free/cil-arrow-bottom.js +1 -1
- package/dist/cjs/node_modules/@coreui/icons/dist/esm/free/cil-arrow-bottom.js.map +1 -1
- package/dist/cjs/node_modules/@coreui/icons/dist/esm/free/cil-arrow-top.js +1 -1
- package/dist/cjs/node_modules/@coreui/icons/dist/esm/free/cil-arrow-top.js.map +1 -1
- package/dist/cjs/node_modules/@coreui/icons/dist/esm/free/cil-filter-x.js +1 -1
- package/dist/cjs/node_modules/@coreui/icons/dist/esm/free/cil-filter-x.js.map +1 -1
- package/dist/cjs/node_modules/@coreui/icons/dist/esm/free/cil-swap-vertical.js +1 -1
- package/dist/cjs/node_modules/@coreui/icons/dist/esm/free/cil-swap-vertical.js.map +1 -1
- package/dist/cjs/node_modules/ua-parser-js/dist/ua-parser.min.js +4 -4
- package/dist/cjs/node_modules/ua-parser-js/dist/ua-parser.min.js.map +1 -1
- package/dist/cjs/utils/executeAfterTransition.js.map +1 -1
- package/dist/cjs/utils/index.d.ts +2 -1
- package/dist/cjs/utils/swipe.d.ts +26 -0
- package/dist/cjs/utils/swipe.js +94 -0
- package/dist/cjs/utils/swipe.js.map +1 -0
- package/dist/esm/components/card/CCardImage.js.map +1 -1
- package/dist/esm/components/carousel/CCarousel.js +44 -33
- package/dist/esm/components/carousel/CCarousel.js.map +1 -1
- package/dist/esm/components/carousel/CCarouselItem.js +19 -28
- package/dist/esm/components/carousel/CCarouselItem.js.map +1 -1
- package/dist/esm/components/chip/CChip.d.ts +12 -0
- package/dist/esm/components/chip/CChip.js +16 -63
- package/dist/esm/components/chip/CChip.js.map +1 -1
- package/dist/esm/components/chip/const.d.ts +1 -0
- package/dist/esm/components/chip/const.js +6 -0
- package/dist/esm/components/chip/const.js.map +1 -0
- package/dist/esm/components/chip-set/CChipSet.d.ts +73 -0
- package/dist/esm/components/chip-set/CChipSet.js +64 -0
- package/dist/esm/components/chip-set/CChipSet.js.map +1 -0
- package/dist/esm/components/chip-set/index.d.ts +2 -0
- package/dist/esm/components/chip-set/useChipSet.d.ts +48 -0
- package/dist/esm/components/chip-set/useChipSet.js +161 -0
- package/dist/esm/components/chip-set/useChipSet.js.map +1 -0
- package/dist/esm/components/chip-set/utils.d.ts +7 -0
- package/dist/esm/components/chip-set/utils.js +16 -0
- package/dist/esm/components/chip-set/utils.js.map +1 -0
- package/dist/esm/components/collapse/CCollapse.js +14 -6
- package/dist/esm/components/collapse/CCollapse.js.map +1 -1
- package/dist/esm/components/conditional-portal/CConditionalPortal.js +3 -1
- package/dist/esm/components/conditional-portal/CConditionalPortal.js.map +1 -1
- package/dist/esm/components/date-picker/CDatePicker.d.ts +1 -1
- package/dist/esm/components/date-picker/CDatePicker.js.map +1 -1
- package/dist/esm/components/date-range-picker/CDateRangePicker.js +2 -0
- package/dist/esm/components/date-range-picker/CDateRangePicker.js.map +1 -1
- package/dist/esm/components/dropdown/CDropdown.js +23 -30
- package/dist/esm/components/dropdown/CDropdown.js.map +1 -1
- package/dist/esm/components/dropdown/CDropdownToggle.js.map +1 -1
- package/dist/esm/components/form/CChipInput.d.ts +14 -2
- package/dist/esm/components/form/CChipInput.js +78 -50
- package/dist/esm/components/form/CChipInput.js.map +1 -1
- package/dist/esm/components/form/CFormCheck.js.map +1 -1
- package/dist/esm/components/form/CFormInput.js.map +1 -1
- package/dist/esm/components/form/CFormSelect.js.map +1 -1
- package/dist/esm/components/form/CFormTextarea.js.map +1 -1
- package/dist/esm/components/form/CInputGroupText.js.map +1 -1
- package/dist/esm/components/grid/CContainer.js +3 -1
- package/dist/esm/components/grid/CContainer.js.map +1 -1
- package/dist/esm/components/header/CHeaderBrand.js.map +1 -1
- package/dist/esm/components/index.d.ts +2 -0
- package/dist/esm/components/link/CLink.js +4 -2
- package/dist/esm/components/link/CLink.js.map +1 -1
- package/dist/esm/components/list-group/CListGroupItem.js.map +1 -1
- package/dist/esm/components/modal/CModal.js +14 -2
- package/dist/esm/components/modal/CModal.js.map +1 -1
- package/dist/esm/components/multi-select/CMultiSelect.d.ts +73 -1
- package/dist/esm/components/multi-select/CMultiSelect.js +158 -19
- package/dist/esm/components/multi-select/CMultiSelect.js.map +1 -1
- package/dist/esm/components/multi-select/CMultiSelectNativeSelect.js +1 -1
- package/dist/esm/components/multi-select/CMultiSelectNativeSelect.js.map +1 -1
- package/dist/esm/components/multi-select/CMultiSelectOptions.d.ts +3 -0
- package/dist/esm/components/multi-select/CMultiSelectOptions.js +45 -2
- package/dist/esm/components/multi-select/CMultiSelectOptions.js.map +1 -1
- package/dist/esm/components/multi-select/CMultiSelectSelection.d.ts +1 -0
- package/dist/esm/components/multi-select/CMultiSelectSelection.js +4 -3
- package/dist/esm/components/multi-select/CMultiSelectSelection.js.map +1 -1
- package/dist/esm/components/multi-select/types.d.ts +12 -0
- package/dist/esm/components/multi-select/utils.js +1 -4
- package/dist/esm/components/multi-select/utils.js.map +1 -1
- package/dist/esm/components/nav/CNav.js.map +1 -1
- package/dist/esm/components/nav/CNavGroup.d.ts +13 -6
- package/dist/esm/components/nav/CNavGroup.js +87 -24
- package/dist/esm/components/nav/CNavGroup.js.map +1 -1
- package/dist/esm/components/nav/CNavGroupContext.d.ts +25 -0
- package/dist/esm/components/nav/CNavGroupContext.js +11 -0
- package/dist/esm/components/nav/CNavGroupContext.js.map +1 -0
- package/dist/esm/components/nav/CNavLink.d.ts +1 -5
- package/dist/esm/components/nav/CNavLink.js +21 -8
- package/dist/esm/components/nav/CNavLink.js.map +1 -1
- package/dist/esm/components/one-time-password-input/COneTimePassword.js +3 -3
- package/dist/esm/components/one-time-password-input/COneTimePassword.js.map +1 -1
- package/dist/esm/components/popover/CPopover.js +69 -24
- package/dist/esm/components/popover/CPopover.js.map +1 -1
- package/dist/esm/components/progress/CProgress.js.map +1 -1
- package/dist/esm/components/rating/CRating.d.ts +1 -1
- package/dist/esm/components/rating/CRating.js.map +1 -1
- package/dist/esm/components/search-button/CSearchButton.d.ts +32 -0
- package/dist/esm/components/search-button/CSearchButton.js +75 -0
- package/dist/esm/components/search-button/CSearchButton.js.map +1 -0
- package/dist/esm/components/search-button/index.d.ts +2 -0
- package/dist/esm/components/search-button/types.d.ts +10 -0
- package/dist/esm/components/search-button/utils.d.ts +11 -0
- package/dist/esm/components/search-button/utils.js +104 -0
- package/dist/esm/components/search-button/utils.js.map +1 -0
- package/dist/esm/components/sidebar/CSidebar.js +40 -23
- package/dist/esm/components/sidebar/CSidebar.js.map +1 -1
- package/dist/esm/components/sidebar/CSidebarNav.d.ts +12 -0
- package/dist/esm/components/sidebar/CSidebarNav.js +13 -37
- package/dist/esm/components/sidebar/CSidebarNav.js.map +1 -1
- package/dist/esm/components/smart-table/CSmartTable.js +2 -2
- package/dist/esm/components/smart-table/CSmartTable.js.map +1 -1
- package/dist/esm/components/smart-table/CSmartTableInterface.d.ts +2 -2
- package/dist/esm/components/table/CTableDataCell.js.map +1 -1
- package/dist/esm/components/table/types.d.ts +2 -1
- package/dist/esm/components/table/utils.js +4 -4
- package/dist/esm/components/table/utils.js.map +1 -1
- package/dist/esm/components/tabs/CTabPanel.js +3 -1
- package/dist/esm/components/tabs/CTabPanel.js.map +1 -1
- package/dist/esm/components/time-picker/CTimePicker.d.ts +1 -1
- package/dist/esm/components/time-picker/CTimePicker.js +1 -1
- package/dist/esm/components/time-picker/CTimePicker.js.map +1 -1
- package/dist/esm/components/tooltip/CTooltip.js +69 -24
- package/dist/esm/components/tooltip/CTooltip.js.map +1 -1
- package/dist/esm/components/virtual-scroller/CVirtualScroller.js +11 -8
- package/dist/esm/components/virtual-scroller/CVirtualScroller.js.map +1 -1
- package/dist/esm/helpers/polymorphicComponent.d.ts +1 -2
- package/dist/esm/hooks/useDebouncedCallback.d.ts +1 -1
- package/dist/esm/hooks/useDebouncedCallback.js +1 -1
- package/dist/esm/hooks/useDebouncedCallback.js.map +1 -1
- package/dist/esm/hooks/useForkedRef.d.ts +1 -1
- package/dist/esm/hooks/useForkedRef.js +6 -3
- package/dist/esm/hooks/useForkedRef.js.map +1 -1
- package/dist/esm/hooks/usePopper.js +9 -7
- package/dist/esm/hooks/usePopper.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js +1 -1
- package/dist/esm/node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js.map +1 -1
- package/dist/esm/node_modules/@coreui/icons/dist/esm/free/cil-arrow-bottom.js +1 -1
- package/dist/esm/node_modules/@coreui/icons/dist/esm/free/cil-arrow-bottom.js.map +1 -1
- package/dist/esm/node_modules/@coreui/icons/dist/esm/free/cil-arrow-top.js +1 -1
- package/dist/esm/node_modules/@coreui/icons/dist/esm/free/cil-arrow-top.js.map +1 -1
- package/dist/esm/node_modules/@coreui/icons/dist/esm/free/cil-filter-x.js +1 -1
- package/dist/esm/node_modules/@coreui/icons/dist/esm/free/cil-filter-x.js.map +1 -1
- package/dist/esm/node_modules/@coreui/icons/dist/esm/free/cil-swap-vertical.js +1 -1
- package/dist/esm/node_modules/@coreui/icons/dist/esm/free/cil-swap-vertical.js.map +1 -1
- package/dist/esm/node_modules/ua-parser-js/dist/ua-parser.min.js +4 -4
- package/dist/esm/node_modules/ua-parser-js/dist/ua-parser.min.js.map +1 -1
- package/dist/esm/utils/executeAfterTransition.js.map +1 -1
- package/dist/esm/utils/index.d.ts +2 -1
- package/dist/esm/utils/swipe.d.ts +26 -0
- package/dist/esm/utils/swipe.js +90 -0
- package/dist/esm/utils/swipe.js.map +1 -0
- package/package.json +8 -6
- package/src/components/autocomplete/CAutocompleteOptions.tsx +1 -1
- package/src/components/autocomplete/__tests__/CAutocomplete.spec.tsx +18 -16
- package/src/components/autocomplete/__tests__/CAutocompleteOptions.spec.tsx +37 -28
- package/src/components/autocomplete/__tests__/__snapshots__/CAutocomplete.spec.tsx.snap +129 -0
- package/src/components/autocomplete/__tests__/__snapshots__/CAutocompleteOptions.spec.tsx.snap +36 -0
- package/src/components/calendar/__tests__/CCalendar.spec.tsx +40 -45
- package/src/components/calendar/__tests__/__snapshots__/CCalendar.spec.tsx.snap +99 -75
- package/src/components/calendar/__tests__/utils.spec.tsx +8 -18
- package/src/components/card/CCardImage.tsx +3 -2
- package/src/components/carousel/CCarousel.tsx +48 -51
- package/src/components/carousel/CCarouselContext.ts +1 -1
- package/src/components/carousel/CCarouselItem.tsx +25 -26
- package/src/components/carousel/__tests__/CCarousel.spec.tsx +47 -4
- package/src/components/carousel/__tests__/CCarouselItem.spec.tsx +37 -0
- package/src/components/chip/CChip.tsx +45 -78
- package/src/components/chip/__tests__/CChip.spec.tsx +26 -19
- package/src/components/chip/const.ts +3 -0
- package/src/components/chip-set/CChipSet.tsx +183 -0
- package/src/components/chip-set/__tests__/CChipSet.spec.tsx +215 -0
- package/src/components/chip-set/__tests__/__snapshots__/CChipSet.spec.tsx.snap +22 -0
- package/src/components/chip-set/index.ts +3 -0
- package/src/components/chip-set/useChipSet.ts +243 -0
- package/src/components/chip-set/utils.ts +19 -0
- package/src/components/collapse/CCollapse.tsx +18 -6
- package/src/components/conditional-portal/CConditionalPortal.tsx +4 -2
- package/src/components/date-picker/CDatePicker.tsx +12 -13
- package/src/components/date-picker/__tests__/CDatePicker.spec.tsx +1 -1
- package/src/components/date-picker/__tests__/__snapshots__/CDatePicker.spec.tsx.snap +12 -640
- package/src/components/date-range-picker/CDateRangePicker.tsx +2 -0
- package/src/components/date-range-picker/__tests__/CDateRangePicker.spec.tsx +1 -1
- package/src/components/date-range-picker/__tests__/__snapshots__/CDateRangePicker.spec.tsx.snap +21 -1259
- package/src/components/dropdown/CDropdown.tsx +29 -34
- package/src/components/dropdown/CDropdownToggle.tsx +1 -1
- package/src/components/dropdown/__tests__/CDropdown.spec.tsx +56 -1
- package/src/components/dropdown/__tests__/__snapshots__/CDropdown.spec.tsx.snap +2 -2
- package/src/components/dropdown/__tests__/__snapshots__/CDropdownMenu.spec.tsx.snap +1 -1
- package/src/components/focus-trap/__tests__/CFocusTrap.spec.tsx +1 -1
- package/src/components/form/CChipInput.tsx +105 -67
- package/src/components/form/CFormCheck.tsx +1 -2
- package/src/components/form/CFormInput.tsx +1 -2
- package/src/components/form/CFormSelect.tsx +1 -2
- package/src/components/form/CFormTextarea.tsx +1 -2
- package/src/components/form/CInputGroupText.tsx +3 -2
- package/src/components/form/__tests__/CChipInput.spec.tsx +67 -1
- package/src/components/form/__tests__/CFormInput.spec.tsx +17 -20
- package/src/components/form/__tests__/__snapshots__/CChipInput.spec.tsx.snap +7 -0
- package/src/components/form/__tests__/__snapshots__/CFormInput.spec.tsx.snap +12 -3
- package/src/components/grid/CContainer.tsx +3 -1
- package/src/components/header/CHeaderBrand.tsx +3 -2
- package/src/components/index.ts +2 -0
- package/src/components/link/CLink.tsx +7 -4
- package/src/components/list-group/CListGroupItem.tsx +3 -2
- package/src/components/loading-button/__tests__/CLoadingButton.spec.tsx +1 -1
- package/src/components/loading-button/__tests__/__snapshots__/CLoadingButton.spec.tsx.snap +4 -4
- package/src/components/modal/CModal.tsx +19 -2
- package/src/components/modal/__tests__/CModal.spec.tsx +43 -0
- package/src/components/multi-select/CMultiSelect.tsx +341 -46
- package/src/components/multi-select/CMultiSelectNativeSelect.tsx +1 -1
- package/src/components/multi-select/CMultiSelectOptions.tsx +74 -3
- package/src/components/multi-select/CMultiSelectSelection.tsx +5 -1
- package/src/components/multi-select/__tests__/CMultiSelect.spec.tsx +27 -69
- package/src/components/multi-select/__tests__/CMultiSelectNativeSelect.spec.tsx +4 -5
- package/src/components/multi-select/__tests__/CMultiSelectOptions.spec.tsx +4 -4
- package/src/components/multi-select/__tests__/CMultiSelectSelection.spec.tsx +7 -24
- package/src/components/multi-select/__tests__/__snapshots__/CMultiSelect.spec.tsx.snap +108 -61
- package/src/components/multi-select/__tests__/__snapshots__/CMultiSelectNativeSelect.spec.tsx.snap +2 -4
- package/src/components/multi-select/__tests__/__snapshots__/CMultiSelectOptions.spec.tsx.snap +7 -0
- package/src/components/multi-select/__tests__/__snapshots__/CMultiSelectSelection.spec.tsx.snap +16 -9
- package/src/components/multi-select/types.ts +14 -0
- package/src/components/nav/CNav.tsx +3 -2
- package/src/components/nav/CNavGroup.tsx +196 -105
- package/src/components/nav/CNavGroupContext.ts +27 -0
- package/src/components/nav/CNavLink.tsx +26 -12
- package/src/components/nav/__tests__/CNavGroup.spec.tsx +207 -4
- package/src/components/nav/__tests__/__snapshots__/CNavGroup.spec.tsx.snap +3 -1
- package/src/components/offcanvas/__tests__/COffcanvas.spec.tsx +1 -0
- package/src/components/one-time-password-input/COneTimePassword.tsx +10 -11
- package/src/components/picker/__tests__/CPicker.spec.tsx +1 -1
- package/src/components/picker/__tests__/__snapshots__/CPicker.spec.tsx.snap +2 -16
- package/src/components/popover/CPopover.tsx +71 -26
- package/src/components/popover/__tests__/CPopover.spec.tsx +85 -4
- package/src/components/progress/CProgress.tsx +1 -2
- package/src/components/rating/CRating.tsx +1 -0
- package/src/components/search-button/CSearchButton.tsx +195 -0
- package/src/components/search-button/__tests__/CSearchButton.spec.tsx +95 -0
- package/src/components/search-button/__tests__/__snapshots__/CSearchButton.spec.tsx.snap +87 -0
- package/src/components/search-button/index.ts +3 -0
- package/src/components/search-button/types.ts +10 -0
- package/src/components/search-button/utils.ts +140 -0
- package/src/components/sidebar/CSidebar.tsx +51 -26
- package/src/components/sidebar/CSidebarNav.tsx +39 -53
- package/src/components/sidebar/__tests__/CSidebar.spec.tsx +26 -0
- package/src/components/smart-table/CSmartTable.tsx +5 -4
- package/src/components/smart-table/CSmartTableInterface.tsx +2 -2
- package/src/components/smart-table/__tests__/CSmartTable.spec.tsx +3 -2
- package/src/components/smart-table/__tests__/__snapshots__/CSmartTable.spec.tsx.snap +7 -15
- package/src/components/stepper/__tests__/CStepper.spec.tsx +24 -21
- package/src/components/stepper/__tests__/__snapshots__/CStepper.spec.tsx.snap +295 -0
- package/src/components/table/CTableDataCell.tsx +2 -1
- package/src/components/table/types.ts +3 -1
- package/src/components/table/utils.ts +4 -4
- package/src/components/tabs/CTabPanel.tsx +3 -1
- package/src/components/tabs/CTabsContext.ts +1 -1
- package/src/components/tabs/__tests__/CTabContent.spec.tsx +1 -0
- package/src/components/tabs/__tests__/CTabPane.spec.tsx +1 -0
- package/src/components/time-picker/CTimePicker.tsx +6 -7
- package/src/components/time-picker/__tests__/CTimePicker.spec.tsx +10 -1
- package/src/components/time-picker/__tests__/__snapshots__/CTimePicker.spec.tsx.snap +599 -315
- package/src/components/tooltip/CTooltip.tsx +72 -27
- package/src/components/tooltip/__tests__/CTooltip.spec.tsx +102 -1
- package/src/components/virtual-scroller/CVirtualScroller.tsx +13 -14
- package/src/helpers/polymorphicComponent.ts +1 -1
- package/src/hooks/useColorModes.ts +2 -2
- package/src/hooks/useDebouncedCallback.ts +7 -4
- package/src/hooks/useForkedRef.ts +20 -15
- package/src/hooks/usePopper.ts +13 -9
- package/src/utils/__tests__/swipe.spec.ts +82 -0
- package/src/utils/executeAfterTransition.ts +1 -1
- package/src/utils/index.ts +2 -0
- package/src/utils/mergeClassNames.ts +1 -1
- package/src/utils/swipe.ts +114 -0
- package/dist/cjs/components/accordion/CAccordionCollapse.d.ts +0 -3
- package/dist/cjs/components/sidebar/CSidebarNavContext.d.ts +0 -5
- package/dist/cjs/components/sidebar/CSidebarNavContext.js +0 -8
- package/dist/cjs/components/sidebar/CSidebarNavContext.js.map +0 -1
- package/dist/esm/components/accordion/CAccordionCollapse.d.ts +0 -3
- package/dist/esm/components/sidebar/CSidebarNavContext.d.ts +0 -5
- package/dist/esm/components/sidebar/CSidebarNavContext.js +0 -6
- package/dist/esm/components/sidebar/CSidebarNavContext.js.map +0 -1
- package/src/components/accordion/CAccordionCollapse.tsx +0 -20
- package/src/components/accordion/__tests__/CAccordionCollapse.spec.tsx +0 -10
- package/src/components/accordion/__tests__/__snapshots__/CAccordionCollapse.spec.tsx.snap +0 -11
- package/src/components/button/__tests__/__snapshots__/CButtonClose.spec.tsx.snap +0 -26
- package/src/components/calendar/__tests__/CCalendar.accessibility.spec.tsx +0 -595
- package/src/components/calendar/__tests__/CCalendar.comprehensive.spec.tsx +0 -402
- package/src/components/calendar/__tests__/CCalendar.edge-cases.spec.tsx +0 -416
- package/src/components/calendar/__tests__/CCalendar.interactions.spec.tsx +0 -593
- package/src/components/header/CHeaderBrand.tsx~HEAD +0 -36
- package/src/components/sidebar/CSidebarNavContext.ts +0 -8
- /package/src/components/calendar/__tests__/{test-setup.ts → setup.ts} +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
ElementType,
|
|
3
|
+
HTMLAttributes,
|
|
4
|
+
KeyboardEvent,
|
|
5
|
+
ReactNode,
|
|
6
|
+
forwardRef,
|
|
7
|
+
useState,
|
|
8
|
+
} from 'react'
|
|
9
|
+
import PropTypes from 'prop-types'
|
|
10
|
+
import classNames from 'classnames'
|
|
11
|
+
|
|
12
|
+
import { useChipSet, type CChipSetItem } from './useChipSet'
|
|
13
|
+
import { PolymorphicRefForwardingComponent } from '../../helpers'
|
|
14
|
+
import { useForkedRef } from '../../hooks'
|
|
15
|
+
|
|
16
|
+
export type { CChipSetItem }
|
|
17
|
+
|
|
18
|
+
const itemValue = (item: string | CChipSetItem): string =>
|
|
19
|
+
typeof item === 'string' ? item : item.value
|
|
20
|
+
|
|
21
|
+
export interface CChipSetProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onSelect'> {
|
|
22
|
+
/**
|
|
23
|
+
* Provides an accessible label for the remove button of every chip rendered by the React Chip Set component.
|
|
24
|
+
*/
|
|
25
|
+
ariaRemoveLabel?: string
|
|
26
|
+
/**
|
|
27
|
+
* Specifies the root element or custom component used by the React Chip Set component.
|
|
28
|
+
*/
|
|
29
|
+
as?: ElementType
|
|
30
|
+
/**
|
|
31
|
+
* Renders chips from data instead of children. Each item is a string or an object with a `value`, an optional `label`, and any `CChip` props. Children are used when this is omitted.
|
|
32
|
+
*/
|
|
33
|
+
chips?: (string | CChipSetItem)[]
|
|
34
|
+
/**
|
|
35
|
+
* Adds custom classes to the React Chip Set root element.
|
|
36
|
+
*/
|
|
37
|
+
className?: string
|
|
38
|
+
/**
|
|
39
|
+
* Sets the initial uncontrolled chips. In this mode the React Chip Set component owns the list and removes chips itself; use `chips` (with `onRemove`) for a controlled list.
|
|
40
|
+
*/
|
|
41
|
+
defaultChips?: (string | CChipSetItem)[]
|
|
42
|
+
/**
|
|
43
|
+
* Sets the initial uncontrolled selection of the React Chip Set component.
|
|
44
|
+
*/
|
|
45
|
+
defaultSelected?: string[]
|
|
46
|
+
/**
|
|
47
|
+
* Disables every chip rendered by the React Chip Set component.
|
|
48
|
+
*/
|
|
49
|
+
disabled?: boolean
|
|
50
|
+
/**
|
|
51
|
+
* Turns the chips into filter chips, each showing a leading check icon while selected.
|
|
52
|
+
*/
|
|
53
|
+
filter?: boolean
|
|
54
|
+
/**
|
|
55
|
+
* Callback fired when a chip requests removal. The chips are controlled by their rendered children, so remove the chip from your data in response.
|
|
56
|
+
*/
|
|
57
|
+
onRemove?: (value: string) => void
|
|
58
|
+
/**
|
|
59
|
+
* Callback fired when the selected chip values of the React Chip Set component change.
|
|
60
|
+
*/
|
|
61
|
+
onSelect?: (selected: string[]) => void
|
|
62
|
+
/**
|
|
63
|
+
* Displays a remove button on every chip rendered by the React Chip Set component.
|
|
64
|
+
*/
|
|
65
|
+
removable?: boolean
|
|
66
|
+
/**
|
|
67
|
+
* Replaces the default remove icon on every chip with a custom icon node.
|
|
68
|
+
*/
|
|
69
|
+
removeIcon?: ReactNode
|
|
70
|
+
/**
|
|
71
|
+
* Enables selection behavior for the chips rendered by the React Chip Set component.
|
|
72
|
+
*/
|
|
73
|
+
selectable?: boolean
|
|
74
|
+
/**
|
|
75
|
+
* Controls the selected chip values of the React Chip Set component.
|
|
76
|
+
*
|
|
77
|
+
* @controllable onSelect
|
|
78
|
+
*/
|
|
79
|
+
selected?: string[]
|
|
80
|
+
/**
|
|
81
|
+
* Replaces the default selected icon shown by filter chips with a custom icon node.
|
|
82
|
+
*/
|
|
83
|
+
selectedIcon?: ReactNode
|
|
84
|
+
/**
|
|
85
|
+
* Sets how many chips can be selected at once in the React Chip Set component.
|
|
86
|
+
*/
|
|
87
|
+
selectionMode?: 'single' | 'multiple'
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const CChipSet: PolymorphicRefForwardingComponent<'div', CChipSetProps> = forwardRef<
|
|
91
|
+
HTMLDivElement,
|
|
92
|
+
CChipSetProps
|
|
93
|
+
>(
|
|
94
|
+
(
|
|
95
|
+
{
|
|
96
|
+
ariaRemoveLabel,
|
|
97
|
+
as: Component = 'div',
|
|
98
|
+
children,
|
|
99
|
+
chips,
|
|
100
|
+
className,
|
|
101
|
+
defaultChips,
|
|
102
|
+
defaultSelected,
|
|
103
|
+
disabled,
|
|
104
|
+
filter,
|
|
105
|
+
onKeyDown,
|
|
106
|
+
onRemove,
|
|
107
|
+
onSelect,
|
|
108
|
+
removable,
|
|
109
|
+
removeIcon,
|
|
110
|
+
selectable,
|
|
111
|
+
selected,
|
|
112
|
+
selectedIcon,
|
|
113
|
+
selectionMode,
|
|
114
|
+
...rest
|
|
115
|
+
},
|
|
116
|
+
ref
|
|
117
|
+
) => {
|
|
118
|
+
// The chip list is controlled by `chips` (or children) or, with `defaultChips`,
|
|
119
|
+
// uncontrolled — then the set owns the list and removes chips itself.
|
|
120
|
+
const isUncontrolledList = chips === undefined && defaultChips !== undefined
|
|
121
|
+
const [_chips, setChips] = useState<(string | CChipSetItem)[]>(defaultChips ?? [])
|
|
122
|
+
|
|
123
|
+
const { rootRef, handleKeyDown, renderChips, renderChipsFromData } = useChipSet({
|
|
124
|
+
ariaRemoveLabel,
|
|
125
|
+
defaultSelected,
|
|
126
|
+
disabled,
|
|
127
|
+
filter,
|
|
128
|
+
removable,
|
|
129
|
+
removeIcon,
|
|
130
|
+
selectable,
|
|
131
|
+
selected,
|
|
132
|
+
selectedIcon,
|
|
133
|
+
selectionMode,
|
|
134
|
+
onRemoveChip: (value) => {
|
|
135
|
+
if (isUncontrolledList) {
|
|
136
|
+
setChips((prev) => prev.filter((item) => itemValue(item) !== value))
|
|
137
|
+
}
|
|
138
|
+
onRemove?.(value)
|
|
139
|
+
},
|
|
140
|
+
onSelectionChange: onSelect,
|
|
141
|
+
})
|
|
142
|
+
const forkedRef = useForkedRef(ref, rootRef)
|
|
143
|
+
|
|
144
|
+
const items = chips ?? (isUncontrolledList ? _chips : undefined)
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<Component
|
|
148
|
+
className={classNames('chip-set', { disabled }, className)}
|
|
149
|
+
{...(disabled && { 'aria-disabled': true })}
|
|
150
|
+
onKeyDown={(event: KeyboardEvent<HTMLDivElement>) => {
|
|
151
|
+
handleKeyDown(event)
|
|
152
|
+
onKeyDown?.(event)
|
|
153
|
+
}}
|
|
154
|
+
{...rest}
|
|
155
|
+
ref={forkedRef}
|
|
156
|
+
>
|
|
157
|
+
{items ? renderChipsFromData(items) : renderChips(children)}
|
|
158
|
+
</Component>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
CChipSet.propTypes = {
|
|
164
|
+
ariaRemoveLabel: PropTypes.string,
|
|
165
|
+
as: PropTypes.elementType,
|
|
166
|
+
children: PropTypes.node,
|
|
167
|
+
chips: PropTypes.array,
|
|
168
|
+
className: PropTypes.string,
|
|
169
|
+
defaultChips: PropTypes.array,
|
|
170
|
+
defaultSelected: PropTypes.array,
|
|
171
|
+
disabled: PropTypes.bool,
|
|
172
|
+
filter: PropTypes.bool,
|
|
173
|
+
onRemove: PropTypes.func,
|
|
174
|
+
onSelect: PropTypes.func,
|
|
175
|
+
removable: PropTypes.bool,
|
|
176
|
+
removeIcon: PropTypes.node,
|
|
177
|
+
selectable: PropTypes.bool,
|
|
178
|
+
selected: PropTypes.array,
|
|
179
|
+
selectedIcon: PropTypes.node,
|
|
180
|
+
selectionMode: PropTypes.oneOf(['single', 'multiple']),
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
CChipSet.displayName = 'CChipSet'
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { fireEvent, render } from '@testing-library/react'
|
|
3
|
+
import '@testing-library/jest-dom'
|
|
4
|
+
import { CChip } from '../../chip'
|
|
5
|
+
import { CChipSet } from '../index'
|
|
6
|
+
|
|
7
|
+
test('loads and displays CChipSet component', async () => {
|
|
8
|
+
const { container } = render(
|
|
9
|
+
<CChipSet>
|
|
10
|
+
<CChip value="a">A</CChip>
|
|
11
|
+
<CChip value="b">B</CChip>
|
|
12
|
+
</CChipSet>
|
|
13
|
+
)
|
|
14
|
+
expect(container).toMatchSnapshot()
|
|
15
|
+
expect(container.firstChild).toHaveClass('chip-set')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('CChipSet passes selectable down to its chips', async () => {
|
|
19
|
+
const { getByText } = render(
|
|
20
|
+
<CChipSet selectable={true}>
|
|
21
|
+
<CChip value="a">A</CChip>
|
|
22
|
+
<CChip value="b">B</CChip>
|
|
23
|
+
</CChipSet>
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
expect(getByText('A')).toHaveAttribute('aria-selected', 'false')
|
|
27
|
+
fireEvent.click(getByText('A'))
|
|
28
|
+
expect(getByText('A')).toHaveClass('active')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('CChipSet allows multiple selected chips by default', async () => {
|
|
32
|
+
const onSelect = jest.fn()
|
|
33
|
+
const { getByText } = render(
|
|
34
|
+
<CChipSet selectable={true} onSelect={onSelect}>
|
|
35
|
+
<CChip value="a">A</CChip>
|
|
36
|
+
<CChip value="b">B</CChip>
|
|
37
|
+
</CChipSet>
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
fireEvent.click(getByText('A'))
|
|
41
|
+
fireEvent.click(getByText('B'))
|
|
42
|
+
|
|
43
|
+
expect(getByText('A')).toHaveClass('active')
|
|
44
|
+
expect(getByText('B')).toHaveClass('active')
|
|
45
|
+
expect(onSelect).toHaveBeenLastCalledWith(['a', 'b'])
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('CChipSet deselects siblings in single selection mode', async () => {
|
|
49
|
+
const onSelect = jest.fn()
|
|
50
|
+
const { getByText } = render(
|
|
51
|
+
<CChipSet selectable={true} selectionMode="single" onSelect={onSelect}>
|
|
52
|
+
<CChip value="a">A</CChip>
|
|
53
|
+
<CChip value="b">B</CChip>
|
|
54
|
+
</CChipSet>
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
fireEvent.click(getByText('A'))
|
|
58
|
+
fireEvent.click(getByText('B'))
|
|
59
|
+
|
|
60
|
+
expect(getByText('A')).not.toHaveClass('active')
|
|
61
|
+
expect(getByText('B')).toHaveClass('active')
|
|
62
|
+
expect(onSelect).toHaveBeenLastCalledWith(['b'])
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('CChipSet honors a controlled selected', async () => {
|
|
66
|
+
const { getByText } = render(
|
|
67
|
+
<CChipSet selectable={true} selected={['b']}>
|
|
68
|
+
<CChip value="a">A</CChip>
|
|
69
|
+
<CChip value="b">B</CChip>
|
|
70
|
+
</CChipSet>
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
expect(getByText('A')).not.toHaveClass('active')
|
|
74
|
+
expect(getByText('B')).toHaveClass('active')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('CChipSet forwards filter so a selected chip shows a check icon', async () => {
|
|
78
|
+
const { getByText, container } = render(
|
|
79
|
+
<CChipSet filter={true} selected={['a']}>
|
|
80
|
+
<CChip value="a">A</CChip>
|
|
81
|
+
</CChipSet>
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
expect(getByText('A')).toHaveClass('active')
|
|
85
|
+
expect(container.querySelector('.chip-check')).toBeInTheDocument()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('CChipSet moves focus between chips with the keyboard', async () => {
|
|
89
|
+
const { getByText } = render(
|
|
90
|
+
<CChipSet selectable>
|
|
91
|
+
<CChip value="first">First</CChip>
|
|
92
|
+
<CChip value="second">Second</CChip>
|
|
93
|
+
<CChip value="third">Third</CChip>
|
|
94
|
+
</CChipSet>
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const first = getByText('First')
|
|
98
|
+
const second = getByText('Second')
|
|
99
|
+
const third = getByText('Third')
|
|
100
|
+
|
|
101
|
+
first.focus()
|
|
102
|
+
fireEvent.keyDown(first, { key: 'ArrowRight' })
|
|
103
|
+
expect(second).toHaveFocus()
|
|
104
|
+
|
|
105
|
+
fireEvent.keyDown(second, { key: 'End' })
|
|
106
|
+
expect(third).toHaveFocus()
|
|
107
|
+
|
|
108
|
+
fireEvent.keyDown(third, { key: 'Home' })
|
|
109
|
+
expect(first).toHaveFocus()
|
|
110
|
+
|
|
111
|
+
// No cycling past the first chip.
|
|
112
|
+
fireEvent.keyDown(first, { key: 'ArrowLeft' })
|
|
113
|
+
expect(first).toHaveFocus()
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('CChipSet mirrors arrow keys in RTL', async () => {
|
|
117
|
+
document.documentElement.dir = 'rtl'
|
|
118
|
+
|
|
119
|
+
const { getByText } = render(
|
|
120
|
+
<CChipSet selectable>
|
|
121
|
+
<CChip value="first">First</CChip>
|
|
122
|
+
<CChip value="second">Second</CChip>
|
|
123
|
+
</CChipSet>
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
const first = getByText('First')
|
|
127
|
+
const second = getByText('Second')
|
|
128
|
+
|
|
129
|
+
first.focus()
|
|
130
|
+
// In RTL, ArrowLeft moves to the next chip.
|
|
131
|
+
fireEvent.keyDown(first, { key: 'ArrowLeft' })
|
|
132
|
+
expect(second).toHaveFocus()
|
|
133
|
+
|
|
134
|
+
fireEvent.keyDown(second, { key: 'ArrowRight' })
|
|
135
|
+
expect(first).toHaveFocus()
|
|
136
|
+
|
|
137
|
+
document.documentElement.dir = ''
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
test('CChipSet fires onRemove so the parent can drop the chip', async () => {
|
|
141
|
+
const onRemove = jest.fn()
|
|
142
|
+
|
|
143
|
+
const Example = () => {
|
|
144
|
+
const [values, setValues] = React.useState(['a', 'b'])
|
|
145
|
+
return (
|
|
146
|
+
<CChipSet
|
|
147
|
+
removable
|
|
148
|
+
onRemove={(value) => {
|
|
149
|
+
onRemove(value)
|
|
150
|
+
setValues((prev) => prev.filter((item) => item !== value))
|
|
151
|
+
}}
|
|
152
|
+
>
|
|
153
|
+
{values.map((value) => (
|
|
154
|
+
<CChip key={value} value={value}>
|
|
155
|
+
{value.toUpperCase()}
|
|
156
|
+
</CChip>
|
|
157
|
+
))}
|
|
158
|
+
</CChipSet>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const { getByText, queryByText } = render(<Example />)
|
|
163
|
+
|
|
164
|
+
const removeButton = getByText('A').querySelector('.chip-remove')
|
|
165
|
+
fireEvent.click(removeButton as Element)
|
|
166
|
+
|
|
167
|
+
expect(onRemove).toHaveBeenCalledWith('a')
|
|
168
|
+
expect(queryByText('A')).not.toBeInTheDocument()
|
|
169
|
+
expect(getByText('B')).toBeInTheDocument()
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
test('CChipSet with defaultChips removes the chip itself (uncontrolled list)', async () => {
|
|
173
|
+
const onRemove = jest.fn()
|
|
174
|
+
const { getByText, queryByText } = render(
|
|
175
|
+
<CChipSet removable defaultChips={['a', { value: 'b', label: 'B' }]} onRemove={onRemove} />
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
fireEvent.click(getByText('a').querySelector('.chip-remove') as Element)
|
|
179
|
+
|
|
180
|
+
expect(onRemove).toHaveBeenCalledWith('a')
|
|
181
|
+
expect(queryByText('a')).not.toBeInTheDocument()
|
|
182
|
+
expect(getByText('B')).toBeInTheDocument()
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
test('CChipSet renders chips from the chips prop (strings and objects)', async () => {
|
|
186
|
+
const { getByText, container } = render(
|
|
187
|
+
<CChipSet
|
|
188
|
+
selectable
|
|
189
|
+
chips={[
|
|
190
|
+
'react',
|
|
191
|
+
{ value: 'vue', label: 'Vue' },
|
|
192
|
+
{ value: 'ng', label: 'Angular', selectable: false },
|
|
193
|
+
]}
|
|
194
|
+
/>
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
expect(container.querySelectorAll('.chip')).toHaveLength(3)
|
|
198
|
+
expect(getByText('react')).toBeInTheDocument()
|
|
199
|
+
expect(getByText('Vue')).toBeInTheDocument()
|
|
200
|
+
|
|
201
|
+
// Per-item override still works with the data-driven API.
|
|
202
|
+
fireEvent.click(getByText('Angular'))
|
|
203
|
+
expect(getByText('Angular')).not.toHaveClass('active')
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
test('CChipSet disables every chip', async () => {
|
|
207
|
+
const { getByText } = render(
|
|
208
|
+
<CChipSet disabled={true} removable={true}>
|
|
209
|
+
<CChip value="a">A</CChip>
|
|
210
|
+
</CChipSet>
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
expect(getByText('A')).toHaveClass('disabled')
|
|
214
|
+
expect(getByText('A').querySelector('.chip-remove')).toBeNull()
|
|
215
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`loads and displays CChipSet component 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="chip-set"
|
|
7
|
+
>
|
|
8
|
+
<span
|
|
9
|
+
class="chip"
|
|
10
|
+
data-coreui-chip-value="a"
|
|
11
|
+
>
|
|
12
|
+
A
|
|
13
|
+
</span>
|
|
14
|
+
<span
|
|
15
|
+
class="chip"
|
|
16
|
+
data-coreui-chip-value="b"
|
|
17
|
+
>
|
|
18
|
+
B
|
|
19
|
+
</span>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
`;
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
KeyboardEvent,
|
|
3
|
+
MouseEvent,
|
|
4
|
+
ReactNode,
|
|
5
|
+
isValidElement,
|
|
6
|
+
useEffect,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
} from 'react'
|
|
10
|
+
|
|
11
|
+
import { CChip, type CChipProps } from '../chip/CChip'
|
|
12
|
+
import { SELECTOR_CHIP_FOCUSABLE } from '../chip/const'
|
|
13
|
+
import { resolveChipValue } from './utils'
|
|
14
|
+
import { isRTL } from '../../utils'
|
|
15
|
+
|
|
16
|
+
export interface CChipSetItem extends Omit<CChipProps, 'value' | 'children'> {
|
|
17
|
+
/**
|
|
18
|
+
* The value that identifies the chip and tracks its selection.
|
|
19
|
+
*/
|
|
20
|
+
value: string
|
|
21
|
+
/**
|
|
22
|
+
* The chip content. Falls back to `value` when omitted.
|
|
23
|
+
*/
|
|
24
|
+
label?: ReactNode
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Build a CChip element from a data item (string or descriptor).
|
|
28
|
+
const buildChip = (item: string | CChipSetItem) => {
|
|
29
|
+
if (typeof item === 'string') {
|
|
30
|
+
return React.createElement(CChip, { key: item, value: item }, item)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const { label, value, ...chipProps } = item
|
|
34
|
+
return React.createElement(CChip, { key: value, value, ...chipProps }, label ?? value)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface UseChipSetOptions {
|
|
38
|
+
ariaRemoveLabel?: string
|
|
39
|
+
defaultSelected?: string[]
|
|
40
|
+
disabled?: boolean
|
|
41
|
+
filter?: boolean
|
|
42
|
+
removable?: boolean
|
|
43
|
+
removeIcon?: ReactNode
|
|
44
|
+
/**
|
|
45
|
+
* Move focus to a neighboring chip after one is removed. Containers that own a
|
|
46
|
+
* different focus target after removal (e.g. CChipInput refocuses its input)
|
|
47
|
+
* set this to `false`.
|
|
48
|
+
*/
|
|
49
|
+
restoreFocusOnRemove?: boolean
|
|
50
|
+
selectable?: boolean
|
|
51
|
+
selected?: string[]
|
|
52
|
+
selectedIcon?: ReactNode
|
|
53
|
+
selectionMode?: 'single' | 'multiple'
|
|
54
|
+
onRemoveChip?: (value: string) => void
|
|
55
|
+
onSelectionChange?: (selected: string[]) => void
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The shared chip-set engine. Both `CChipSet` and `CChipInput` build on it — the
|
|
60
|
+
* React equivalent of the vanilla `ChipInput extends ChipSet`. It owns selection
|
|
61
|
+
* coordination (single/multiple, controlled-or-uncontrolled), roving focus, the
|
|
62
|
+
* focus-a-neighbor-after-removal behavior, and the per-chip prop injection.
|
|
63
|
+
* Existence of the chips stays with the caller (its children / value list).
|
|
64
|
+
*/
|
|
65
|
+
export const useChipSet = (options: UseChipSetOptions) => {
|
|
66
|
+
const {
|
|
67
|
+
ariaRemoveLabel,
|
|
68
|
+
defaultSelected = [],
|
|
69
|
+
disabled,
|
|
70
|
+
filter,
|
|
71
|
+
removable,
|
|
72
|
+
removeIcon,
|
|
73
|
+
restoreFocusOnRemove = true,
|
|
74
|
+
selectable,
|
|
75
|
+
selected,
|
|
76
|
+
selectedIcon,
|
|
77
|
+
selectionMode = 'multiple',
|
|
78
|
+
onRemoveChip,
|
|
79
|
+
onSelectionChange,
|
|
80
|
+
} = options
|
|
81
|
+
|
|
82
|
+
const rootRef = useRef<HTMLDivElement>(null)
|
|
83
|
+
const pendingFocusValue = useRef<string | null>(null)
|
|
84
|
+
const isControlled = selected !== undefined
|
|
85
|
+
const [_selected, setSelected] = useState<string[]>(defaultSelected)
|
|
86
|
+
// In controlled mode the selected prop drives selection directly; _selected is
|
|
87
|
+
// only read when uncontrolled, so no syncing effect is needed.
|
|
88
|
+
const selectedValues = isControlled ? (selected as string[]) : _selected
|
|
89
|
+
|
|
90
|
+
// Focus the saved neighbor once the removed chip has left the DOM.
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (pendingFocusValue.current === null) {
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const selector = `[data-coreui-chip-value="${pendingFocusValue.current}"]`
|
|
97
|
+
rootRef.current?.querySelector<HTMLElement>(selector)?.focus()
|
|
98
|
+
pendingFocusValue.current = null
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const getFocusableChips = (): HTMLElement[] => [
|
|
102
|
+
...(rootRef.current?.querySelectorAll<HTMLElement>(SELECTOR_CHIP_FOCUSABLE) ?? []),
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
const emitSelection = (next: string[]) => {
|
|
106
|
+
if (!isControlled) {
|
|
107
|
+
setSelected(next)
|
|
108
|
+
}
|
|
109
|
+
onSelectionChange?.(next)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const updateSelection = (chipValue: string, nextSelected: boolean) => {
|
|
113
|
+
const isSelected = selectedValues.includes(chipValue)
|
|
114
|
+
if (nextSelected === isSelected) {
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (nextSelected) {
|
|
119
|
+
// Single selection mode keeps only the freshly selected chip.
|
|
120
|
+
emitSelection(selectionMode === 'single' ? [chipValue] : [...selectedValues, chipValue])
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
emitSelection(selectedValues.filter((item) => item !== chipValue))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const clearSelection = () => {
|
|
128
|
+
if (selectedValues.length > 0) {
|
|
129
|
+
emitSelection([])
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const removeChip = (chipValue: string) => {
|
|
134
|
+
if (restoreFocusOnRemove) {
|
|
135
|
+
const chips = getFocusableChips()
|
|
136
|
+
const index = chips.findIndex((chip) => chip.dataset.coreuiChipValue === chipValue)
|
|
137
|
+
// Prefer the next chip, fall back to the previous one at the end.
|
|
138
|
+
const neighbor = chips[index + 1] ?? chips[index - 1] ?? null
|
|
139
|
+
pendingFocusValue.current = neighbor?.dataset.coreuiChipValue ?? null
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
emitSelection(selectedValues.filter((item) => item !== chipValue))
|
|
143
|
+
onRemoveChip?.(chipValue)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Roving focus: arrow keys move between chips and Home/End jump to the edges,
|
|
147
|
+
// with no cycling. Returns true when the key was handled.
|
|
148
|
+
const handleKeyDown = (event: KeyboardEvent<HTMLElement>): boolean => {
|
|
149
|
+
const chip = (event.target as HTMLElement).closest<HTMLElement>(SELECTOR_CHIP_FOCUSABLE)
|
|
150
|
+
if (!chip) {
|
|
151
|
+
return false
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const chips = getFocusableChips()
|
|
155
|
+
const index = chips.indexOf(chip)
|
|
156
|
+
// In RTL the visual direction is mirrored, so left/right swap.
|
|
157
|
+
const rtl = isRTL(rootRef.current)
|
|
158
|
+
|
|
159
|
+
switch (event.key) {
|
|
160
|
+
case 'ArrowLeft': {
|
|
161
|
+
event.preventDefault()
|
|
162
|
+
chips[rtl ? index + 1 : index - 1]?.focus()
|
|
163
|
+
return true
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
case 'ArrowRight': {
|
|
167
|
+
event.preventDefault()
|
|
168
|
+
chips[rtl ? index - 1 : index + 1]?.focus()
|
|
169
|
+
return true
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
case 'Home': {
|
|
173
|
+
event.preventDefault()
|
|
174
|
+
chips[0]?.focus()
|
|
175
|
+
return true
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
case 'End': {
|
|
179
|
+
event.preventDefault()
|
|
180
|
+
chips[chips.length - 1]?.focus()
|
|
181
|
+
return true
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
default: {
|
|
185
|
+
return false
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const renderChips = (children: ReactNode): ReactNode =>
|
|
191
|
+
React.Children.map(children, (child, index) => {
|
|
192
|
+
if (!isValidElement<CChipProps>(child)) {
|
|
193
|
+
return child
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const chipValue = resolveChipValue(child, index)
|
|
197
|
+
const childSelectable = child.props.selectable ?? selectable
|
|
198
|
+
const childFilter = child.props.filter ?? filter
|
|
199
|
+
const childRemovable = child.props.removable ?? removable
|
|
200
|
+
const isCoordinated = Boolean(childSelectable || childFilter)
|
|
201
|
+
|
|
202
|
+
return React.cloneElement(child, {
|
|
203
|
+
ariaRemoveLabel: child.props.ariaRemoveLabel ?? ariaRemoveLabel,
|
|
204
|
+
disabled: child.props.disabled ?? disabled,
|
|
205
|
+
filter: childFilter,
|
|
206
|
+
removable: childRemovable,
|
|
207
|
+
removeIcon: child.props.removeIcon ?? removeIcon,
|
|
208
|
+
selectable: childSelectable,
|
|
209
|
+
selectedIcon: child.props.selectedIcon ?? selectedIcon,
|
|
210
|
+
...(isCoordinated && {
|
|
211
|
+
selected: selectedValues.includes(chipValue),
|
|
212
|
+
onSelectedChange: (
|
|
213
|
+
nextSelected: boolean,
|
|
214
|
+
event: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>
|
|
215
|
+
) => {
|
|
216
|
+
updateSelection(chipValue, nextSelected)
|
|
217
|
+
child.props.onSelectedChange?.(nextSelected, event)
|
|
218
|
+
},
|
|
219
|
+
}),
|
|
220
|
+
...(childRemovable && {
|
|
221
|
+
onRemove: (event: MouseEvent<HTMLButtonElement> | KeyboardEvent<HTMLElement>) => {
|
|
222
|
+
removeChip(chipValue)
|
|
223
|
+
child.props.onRemove?.(event)
|
|
224
|
+
},
|
|
225
|
+
}),
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
// Build CChip elements from data, then run them through the same transform —
|
|
230
|
+
// used by CChipSet's `chips` prop and by CChipInput's value list.
|
|
231
|
+
const renderChipsFromData = (items: (string | CChipSetItem)[]): ReactNode =>
|
|
232
|
+
renderChips(items.map((item) => buildChip(item)))
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
rootRef,
|
|
236
|
+
selectedValues,
|
|
237
|
+
clearSelection,
|
|
238
|
+
getFocusableChips,
|
|
239
|
+
handleKeyDown,
|
|
240
|
+
renderChips,
|
|
241
|
+
renderChipsFromData,
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ReactElement } from 'react'
|
|
2
|
+
|
|
3
|
+
import type { CChipProps } from '../chip/CChip'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolves the value used to identify a chip in a `CChipSet`: its explicit
|
|
7
|
+
* `value` prop, its text children, or the render index as a last resort.
|
|
8
|
+
*/
|
|
9
|
+
export const resolveChipValue = (chip: ReactElement<CChipProps>, index: number): string => {
|
|
10
|
+
if (chip.props.value !== undefined) {
|
|
11
|
+
return chip.props.value
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (typeof chip.props.children === 'string') {
|
|
15
|
+
return chip.props.children
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return String(index)
|
|
19
|
+
}
|