@furystack/shades-common-components 13.5.0 → 15.0.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/CHANGELOG.md +109 -0
- package/README.md +3 -3
- package/esm/components/accordion/accordion-item.d.ts.map +1 -1
- package/esm/components/accordion/accordion-item.js +7 -10
- package/esm/components/accordion/accordion-item.js.map +1 -1
- package/esm/components/accordion/accordion.d.ts +7 -0
- package/esm/components/accordion/accordion.d.ts.map +1 -1
- package/esm/components/accordion/accordion.js +5 -2
- package/esm/components/accordion/accordion.js.map +1 -1
- package/esm/components/accordion/accordion.spec.js +91 -50
- package/esm/components/accordion/accordion.spec.js.map +1 -1
- package/esm/components/alert.js +1 -1
- package/esm/components/alert.js.map +1 -1
- package/esm/components/app-bar-link.js +1 -1
- package/esm/components/app-bar-link.js.map +1 -1
- package/esm/components/app-bar.js +1 -1
- package/esm/components/app-bar.js.map +1 -1
- package/esm/components/avatar.js +1 -1
- package/esm/components/avatar.js.map +1 -1
- package/esm/components/badge.js +1 -1
- package/esm/components/badge.js.map +1 -1
- package/esm/components/breadcrumb.js +1 -1
- package/esm/components/breadcrumb.js.map +1 -1
- package/esm/components/breadcrumb.spec.js +3 -3
- package/esm/components/breadcrumb.spec.js.map +1 -1
- package/esm/components/button-group.js +4 -4
- package/esm/components/button-group.js.map +1 -1
- package/esm/components/button.js +1 -1
- package/esm/components/button.js.map +1 -1
- package/esm/components/button.spec.js +1 -1
- package/esm/components/button.spec.js.map +1 -1
- package/esm/components/cache-view.js +1 -1
- package/esm/components/cache-view.js.map +1 -1
- package/esm/components/cache-view.spec.js +2 -2
- package/esm/components/cache-view.spec.js.map +1 -1
- package/esm/components/card.js +5 -5
- package/esm/components/card.js.map +1 -1
- package/esm/components/carousel.js +2 -2
- package/esm/components/carousel.js.map +1 -1
- package/esm/components/chip.d.ts.map +1 -1
- package/esm/components/chip.js +5 -3
- package/esm/components/chip.js.map +1 -1
- package/esm/components/chip.spec.js +42 -0
- package/esm/components/chip.spec.js.map +1 -1
- package/esm/components/circular-progress.d.ts +2 -4
- package/esm/components/circular-progress.d.ts.map +1 -1
- package/esm/components/circular-progress.js +3 -6
- package/esm/components/circular-progress.js.map +1 -1
- package/esm/components/circular-progress.spec.js +19 -14
- package/esm/components/circular-progress.spec.js.map +1 -1
- package/esm/components/command-palette/command-palette-input.js +1 -1
- package/esm/components/command-palette/command-palette-input.js.map +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.js +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.js.map +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.spec.js +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.spec.js.map +1 -1
- package/esm/components/command-palette/index.d.ts.map +1 -1
- package/esm/components/command-palette/index.js +15 -2
- package/esm/components/command-palette/index.js.map +1 -1
- package/esm/components/command-palette/index.spec.js +78 -33
- package/esm/components/command-palette/index.spec.js.map +1 -1
- package/esm/components/context-menu/context-menu-item.js +1 -1
- package/esm/components/context-menu/context-menu-item.js.map +1 -1
- package/esm/components/context-menu/context-menu.js +1 -1
- package/esm/components/context-menu/context-menu.js.map +1 -1
- package/esm/components/data-grid/body.js +1 -1
- package/esm/components/data-grid/body.js.map +1 -1
- package/esm/components/data-grid/data-grid-row.d.ts.map +1 -1
- package/esm/components/data-grid/data-grid-row.js +19 -3
- package/esm/components/data-grid/data-grid-row.js.map +1 -1
- package/esm/components/data-grid/data-grid.d.ts +12 -2
- package/esm/components/data-grid/data-grid.d.ts.map +1 -1
- package/esm/components/data-grid/data-grid.js +33 -13
- package/esm/components/data-grid/data-grid.js.map +1 -1
- package/esm/components/data-grid/data-grid.spec.js +170 -90
- package/esm/components/data-grid/data-grid.spec.js.map +1 -1
- package/esm/components/data-grid/filters/boolean-filter.d.ts +2 -2
- package/esm/components/data-grid/filters/boolean-filter.d.ts.map +1 -1
- package/esm/components/data-grid/filters/boolean-filter.js +4 -4
- package/esm/components/data-grid/filters/boolean-filter.js.map +1 -1
- package/esm/components/data-grid/filters/boolean-filter.spec.js +18 -17
- package/esm/components/data-grid/filters/boolean-filter.spec.js.map +1 -1
- package/esm/components/data-grid/filters/date-filter.d.ts +2 -2
- package/esm/components/data-grid/filters/date-filter.d.ts.map +1 -1
- package/esm/components/data-grid/filters/date-filter.js +6 -6
- package/esm/components/data-grid/filters/date-filter.js.map +1 -1
- package/esm/components/data-grid/filters/date-filter.spec.js +26 -21
- package/esm/components/data-grid/filters/date-filter.spec.js.map +1 -1
- package/esm/components/data-grid/filters/enum-filter.d.ts +2 -2
- package/esm/components/data-grid/filters/enum-filter.d.ts.map +1 -1
- package/esm/components/data-grid/filters/enum-filter.js +5 -5
- package/esm/components/data-grid/filters/enum-filter.js.map +1 -1
- package/esm/components/data-grid/filters/enum-filter.spec.js +21 -19
- package/esm/components/data-grid/filters/enum-filter.spec.js.map +1 -1
- package/esm/components/data-grid/filters/filter-dropdown.js +1 -1
- package/esm/components/data-grid/filters/filter-dropdown.js.map +1 -1
- package/esm/components/data-grid/filters/number-filter.d.ts +2 -2
- package/esm/components/data-grid/filters/number-filter.d.ts.map +1 -1
- package/esm/components/data-grid/filters/number-filter.js +5 -5
- package/esm/components/data-grid/filters/number-filter.js.map +1 -1
- package/esm/components/data-grid/filters/number-filter.spec.js +23 -21
- package/esm/components/data-grid/filters/number-filter.spec.js.map +1 -1
- package/esm/components/data-grid/filters/string-filter.d.ts +2 -2
- package/esm/components/data-grid/filters/string-filter.d.ts.map +1 -1
- package/esm/components/data-grid/filters/string-filter.js +5 -5
- package/esm/components/data-grid/filters/string-filter.js.map +1 -1
- package/esm/components/data-grid/filters/string-filter.spec.js +21 -19
- package/esm/components/data-grid/filters/string-filter.spec.js.map +1 -1
- package/esm/components/data-grid/footer.d.ts +2 -2
- package/esm/components/data-grid/footer.d.ts.map +1 -1
- package/esm/components/data-grid/footer.js +8 -13
- package/esm/components/data-grid/footer.js.map +1 -1
- package/esm/components/data-grid/footer.spec.js +38 -27
- package/esm/components/data-grid/footer.spec.js.map +1 -1
- package/esm/components/data-grid/header.d.ts +6 -6
- package/esm/components/data-grid/header.d.ts.map +1 -1
- package/esm/components/data-grid/header.js +16 -17
- package/esm/components/data-grid/header.js.map +1 -1
- package/esm/components/data-grid/header.spec.js +66 -60
- package/esm/components/data-grid/header.spec.js.map +1 -1
- package/esm/components/data-grid/selection-cell.d.ts.map +1 -1
- package/esm/components/data-grid/selection-cell.js +2 -2
- package/esm/components/data-grid/selection-cell.js.map +1 -1
- package/esm/components/dialog.d.ts +11 -0
- package/esm/components/dialog.d.ts.map +1 -1
- package/esm/components/dialog.js +3 -3
- package/esm/components/dialog.js.map +1 -1
- package/esm/components/dialog.spec.js +54 -2
- package/esm/components/dialog.spec.js.map +1 -1
- package/esm/components/divider.js +1 -1
- package/esm/components/divider.js.map +1 -1
- package/esm/components/drawer/drawer-toggle-button.js +1 -1
- package/esm/components/drawer/drawer-toggle-button.js.map +1 -1
- package/esm/components/drawer/index.js +1 -1
- package/esm/components/drawer/index.js.map +1 -1
- package/esm/components/dropdown.d.ts.map +1 -1
- package/esm/components/dropdown.js +2 -2
- package/esm/components/dropdown.js.map +1 -1
- package/esm/components/dropdown.spec.js +8 -0
- package/esm/components/dropdown.spec.js.map +1 -1
- package/esm/components/fab.js +1 -1
- package/esm/components/fab.js.map +1 -1
- package/esm/components/form.js +1 -1
- package/esm/components/form.js.map +1 -1
- package/esm/components/grid.js +1 -1
- package/esm/components/grid.js.map +1 -1
- package/esm/components/icons/icon.js +1 -1
- package/esm/components/icons/icon.js.map +1 -1
- package/esm/components/image.d.ts.map +1 -1
- package/esm/components/image.js +17 -8
- package/esm/components/image.js.map +1 -1
- package/esm/components/image.spec.js +60 -0
- package/esm/components/image.spec.js.map +1 -1
- package/esm/components/inputs/autocomplete.js +1 -1
- package/esm/components/inputs/autocomplete.js.map +1 -1
- package/esm/components/inputs/checkbox.d.ts.map +1 -1
- package/esm/components/inputs/checkbox.js +2 -1
- package/esm/components/inputs/checkbox.js.map +1 -1
- package/esm/components/inputs/checkbox.spec.js +1 -1
- package/esm/components/inputs/checkbox.spec.js.map +1 -1
- package/esm/components/inputs/input-number.js +1 -1
- package/esm/components/inputs/input-number.js.map +1 -1
- package/esm/components/inputs/input-number.spec.js +1 -1
- package/esm/components/inputs/input-number.spec.js.map +1 -1
- package/esm/components/inputs/input.js +1 -1
- package/esm/components/inputs/input.js.map +1 -1
- package/esm/components/inputs/input.spec.js +1 -1
- package/esm/components/inputs/input.spec.js.map +1 -1
- package/esm/components/inputs/radio-group.js +1 -1
- package/esm/components/inputs/radio-group.js.map +1 -1
- package/esm/components/inputs/radio-group.spec.js +1 -1
- package/esm/components/inputs/radio-group.spec.js.map +1 -1
- package/esm/components/inputs/radio.d.ts.map +1 -1
- package/esm/components/inputs/radio.js +2 -1
- package/esm/components/inputs/radio.js.map +1 -1
- package/esm/components/inputs/radio.spec.js +1 -1
- package/esm/components/inputs/radio.spec.js.map +1 -1
- package/esm/components/inputs/select.js +1 -1
- package/esm/components/inputs/select.js.map +1 -1
- package/esm/components/inputs/slider.d.ts.map +1 -1
- package/esm/components/inputs/slider.js +2 -1
- package/esm/components/inputs/slider.js.map +1 -1
- package/esm/components/inputs/switch.d.ts.map +1 -1
- package/esm/components/inputs/switch.js +2 -1
- package/esm/components/inputs/switch.js.map +1 -1
- package/esm/components/inputs/switch.spec.js +1 -1
- package/esm/components/inputs/switch.spec.js.map +1 -1
- package/esm/components/inputs/text-area.js +1 -1
- package/esm/components/inputs/text-area.js.map +1 -1
- package/esm/components/inputs/text-area.spec.js +1 -1
- package/esm/components/inputs/text-area.spec.js.map +1 -1
- package/esm/components/linear-progress.d.ts +2 -4
- package/esm/components/linear-progress.d.ts.map +1 -1
- package/esm/components/linear-progress.js +3 -6
- package/esm/components/linear-progress.js.map +1 -1
- package/esm/components/linear-progress.spec.js +21 -18
- package/esm/components/linear-progress.spec.js.map +1 -1
- package/esm/components/list/list-item.d.ts.map +1 -1
- package/esm/components/list/list-item.js +22 -6
- package/esm/components/list/list-item.js.map +1 -1
- package/esm/components/list/list.d.ts +7 -0
- package/esm/components/list/list.d.ts.map +1 -1
- package/esm/components/list/list.js +29 -9
- package/esm/components/list/list.js.map +1 -1
- package/esm/components/list/list.spec.js +117 -23
- package/esm/components/list/list.spec.js.map +1 -1
- package/esm/components/loader.js +1 -1
- package/esm/components/loader.js.map +1 -1
- package/esm/components/loader.spec.js +1 -1
- package/esm/components/loader.spec.js.map +1 -1
- package/esm/components/markdown/markdown-display.d.ts.map +1 -1
- package/esm/components/markdown/markdown-display.js +12 -2
- package/esm/components/markdown/markdown-display.js.map +1 -1
- package/esm/components/markdown/markdown-display.spec.js +98 -1
- package/esm/components/markdown/markdown-display.spec.js.map +1 -1
- package/esm/components/markdown/markdown-editor.js +1 -1
- package/esm/components/markdown/markdown-editor.js.map +1 -1
- package/esm/components/markdown/markdown-editor.spec.js +88 -1
- package/esm/components/markdown/markdown-editor.spec.js.map +1 -1
- package/esm/components/markdown/markdown-input.js +1 -1
- package/esm/components/markdown/markdown-input.js.map +1 -1
- package/esm/components/markdown/markdown-input.spec.js +1 -1
- package/esm/components/markdown/markdown-input.spec.js.map +1 -1
- package/esm/components/menu/menu.js +2 -2
- package/esm/components/menu/menu.js.map +1 -1
- package/esm/components/modal.d.ts +10 -0
- package/esm/components/modal.d.ts.map +1 -1
- package/esm/components/modal.js +25 -5
- package/esm/components/modal.js.map +1 -1
- package/esm/components/modal.spec.js +89 -4
- package/esm/components/modal.spec.js.map +1 -1
- package/esm/components/noty-list.js +2 -2
- package/esm/components/noty-list.js.map +1 -1
- package/esm/components/page-container/index.js +1 -1
- package/esm/components/page-container/index.js.map +1 -1
- package/esm/components/page-container/page-header.js +1 -1
- package/esm/components/page-container/page-header.js.map +1 -1
- package/esm/components/page-layout/index.js +2 -2
- package/esm/components/page-layout/index.js.map +1 -1
- package/esm/components/page-layout/index.spec.js +14 -0
- package/esm/components/page-layout/index.spec.js.map +1 -1
- package/esm/components/pagination.js +1 -1
- package/esm/components/pagination.js.map +1 -1
- package/esm/components/paper.js +1 -1
- package/esm/components/paper.js.map +1 -1
- package/esm/components/rating.d.ts.map +1 -1
- package/esm/components/rating.js +29 -22
- package/esm/components/rating.js.map +1 -1
- package/esm/components/rating.spec.js +152 -5
- package/esm/components/rating.spec.js.map +1 -1
- package/esm/components/result.js +1 -1
- package/esm/components/result.js.map +1 -1
- package/esm/components/skeleton.js +1 -1
- package/esm/components/skeleton.js.map +1 -1
- package/esm/components/suggest/index.d.ts.map +1 -1
- package/esm/components/suggest/index.js +15 -2
- package/esm/components/suggest/index.js.map +1 -1
- package/esm/components/suggest/index.spec.js +99 -44
- package/esm/components/suggest/index.spec.js.map +1 -1
- package/esm/components/suggest/suggest-input.js +1 -1
- package/esm/components/suggest/suggest-input.js.map +1 -1
- package/esm/components/suggest/suggest-input.spec.js +1 -1
- package/esm/components/suggest/suggest-input.spec.js.map +1 -1
- package/esm/components/suggest/suggestion-list.js +1 -1
- package/esm/components/suggest/suggestion-list.js.map +1 -1
- package/esm/components/suggest/suggestion-list.spec.js +1 -1
- package/esm/components/suggest/suggestion-list.spec.js.map +1 -1
- package/esm/components/tabs.d.ts.map +1 -1
- package/esm/components/tabs.js +6 -2
- package/esm/components/tabs.js.map +1 -1
- package/esm/components/timeline.js +2 -2
- package/esm/components/timeline.js.map +1 -1
- package/esm/components/tooltip.js +1 -1
- package/esm/components/tooltip.js.map +1 -1
- package/esm/components/tree/tree-item.d.ts.map +1 -1
- package/esm/components/tree/tree-item.js +19 -6
- package/esm/components/tree/tree-item.js.map +1 -1
- package/esm/components/tree/tree.d.ts +7 -0
- package/esm/components/tree/tree.d.ts.map +1 -1
- package/esm/components/tree/tree.js +13 -4
- package/esm/components/tree/tree.js.map +1 -1
- package/esm/components/tree/tree.spec.js +64 -2
- package/esm/components/tree/tree.spec.js.map +1 -1
- package/esm/components/typography.js +1 -1
- package/esm/components/typography.js.map +1 -1
- package/esm/components/wizard/index.js +1 -1
- package/esm/components/wizard/index.js.map +1 -1
- package/esm/components/wizard/index.spec.js +3 -3
- package/esm/components/wizard/index.spec.js.map +1 -1
- package/esm/services/collection-service.d.ts +9 -0
- package/esm/services/collection-service.d.ts.map +1 -1
- package/esm/services/collection-service.js +33 -11
- package/esm/services/collection-service.js.map +1 -1
- package/esm/services/collection-service.spec.js +33 -24
- package/esm/services/collection-service.spec.js.map +1 -1
- package/esm/services/css-variable-theme.d.ts +7 -0
- package/esm/services/css-variable-theme.d.ts.map +1 -1
- package/esm/services/css-variable-theme.js +23 -0
- package/esm/services/css-variable-theme.js.map +1 -1
- package/esm/services/css-variable-theme.spec.js +1 -0
- package/esm/services/css-variable-theme.spec.js.map +1 -1
- package/esm/services/list-service.d.ts +9 -0
- package/esm/services/list-service.d.ts.map +1 -1
- package/esm/services/list-service.js +13 -13
- package/esm/services/list-service.js.map +1 -1
- package/esm/services/list-service.spec.js +13 -33
- package/esm/services/list-service.spec.js.map +1 -1
- package/esm/services/theme-provider-service.d.ts +3 -0
- package/esm/services/theme-provider-service.d.ts.map +1 -1
- package/esm/services/theme-provider-service.js.map +1 -1
- package/esm/services/tree-service.d.ts.map +1 -1
- package/esm/services/tree-service.js +5 -9
- package/esm/services/tree-service.js.map +1 -1
- package/esm/services/tree-service.spec.js +12 -9
- package/esm/services/tree-service.spec.js.map +1 -1
- package/esm/themes/architect-theme.d.ts +1 -0
- package/esm/themes/architect-theme.d.ts.map +1 -1
- package/esm/themes/architect-theme.js +1 -0
- package/esm/themes/architect-theme.js.map +1 -1
- package/esm/themes/auditore-theme.d.ts +1 -0
- package/esm/themes/auditore-theme.d.ts.map +1 -1
- package/esm/themes/auditore-theme.js +1 -0
- package/esm/themes/auditore-theme.js.map +1 -1
- package/esm/themes/black-mesa-theme.d.ts +1 -0
- package/esm/themes/black-mesa-theme.d.ts.map +1 -1
- package/esm/themes/black-mesa-theme.js +1 -0
- package/esm/themes/black-mesa-theme.js.map +1 -1
- package/esm/themes/chieftain-theme.d.ts +1 -0
- package/esm/themes/chieftain-theme.d.ts.map +1 -1
- package/esm/themes/chieftain-theme.js +1 -0
- package/esm/themes/chieftain-theme.js.map +1 -1
- package/esm/themes/default-dark-theme.d.ts +1 -0
- package/esm/themes/default-dark-theme.d.ts.map +1 -1
- package/esm/themes/default-dark-theme.js +1 -0
- package/esm/themes/default-dark-theme.js.map +1 -1
- package/esm/themes/default-light-theme.d.ts +1 -0
- package/esm/themes/default-light-theme.d.ts.map +1 -1
- package/esm/themes/default-light-theme.js +1 -0
- package/esm/themes/default-light-theme.js.map +1 -1
- package/esm/themes/dragonborn-theme.d.ts +1 -0
- package/esm/themes/dragonborn-theme.d.ts.map +1 -1
- package/esm/themes/dragonborn-theme.js +1 -0
- package/esm/themes/dragonborn-theme.js.map +1 -1
- package/esm/themes/hawkins-theme.d.ts +1 -0
- package/esm/themes/hawkins-theme.d.ts.map +1 -1
- package/esm/themes/hawkins-theme.js +1 -0
- package/esm/themes/hawkins-theme.js.map +1 -1
- package/esm/themes/jedi-theme.d.ts +1 -0
- package/esm/themes/jedi-theme.d.ts.map +1 -1
- package/esm/themes/jedi-theme.js +1 -0
- package/esm/themes/jedi-theme.js.map +1 -1
- package/esm/themes/neon-runner-theme.d.ts +1 -0
- package/esm/themes/neon-runner-theme.d.ts.map +1 -1
- package/esm/themes/neon-runner-theme.js +1 -0
- package/esm/themes/neon-runner-theme.js.map +1 -1
- package/esm/themes/paladin-theme.d.ts +1 -0
- package/esm/themes/paladin-theme.d.ts.map +1 -1
- package/esm/themes/paladin-theme.js +1 -0
- package/esm/themes/paladin-theme.js.map +1 -1
- package/esm/themes/plumber-theme.d.ts +1 -0
- package/esm/themes/plumber-theme.d.ts.map +1 -1
- package/esm/themes/plumber-theme.js +1 -0
- package/esm/themes/plumber-theme.js.map +1 -1
- package/esm/themes/replicant-theme.d.ts +1 -0
- package/esm/themes/replicant-theme.d.ts.map +1 -1
- package/esm/themes/replicant-theme.js +1 -0
- package/esm/themes/replicant-theme.js.map +1 -1
- package/esm/themes/sandworm-theme.d.ts +1 -0
- package/esm/themes/sandworm-theme.d.ts.map +1 -1
- package/esm/themes/sandworm-theme.js +1 -0
- package/esm/themes/sandworm-theme.js.map +1 -1
- package/esm/themes/shadow-broker-theme.d.ts +1 -0
- package/esm/themes/shadow-broker-theme.d.ts.map +1 -1
- package/esm/themes/shadow-broker-theme.js +1 -0
- package/esm/themes/shadow-broker-theme.js.map +1 -1
- package/esm/themes/sith-theme.d.ts +1 -0
- package/esm/themes/sith-theme.d.ts.map +1 -1
- package/esm/themes/sith-theme.js +1 -0
- package/esm/themes/sith-theme.js.map +1 -1
- package/esm/themes/vault-dweller-theme.d.ts +1 -0
- package/esm/themes/vault-dweller-theme.d.ts.map +1 -1
- package/esm/themes/vault-dweller-theme.js +1 -0
- package/esm/themes/vault-dweller-theme.js.map +1 -1
- package/esm/themes/wild-hunt-theme.d.ts +1 -0
- package/esm/themes/wild-hunt-theme.d.ts.map +1 -1
- package/esm/themes/wild-hunt-theme.js +1 -0
- package/esm/themes/wild-hunt-theme.js.map +1 -1
- package/esm/themes/xenomorph-theme.d.ts +1 -0
- package/esm/themes/xenomorph-theme.d.ts.map +1 -1
- package/esm/themes/xenomorph-theme.js +1 -0
- package/esm/themes/xenomorph-theme.js.map +1 -1
- package/package.json +3 -3
- package/src/components/accordion/accordion-item.tsx +10 -15
- package/src/components/accordion/accordion.spec.tsx +134 -79
- package/src/components/accordion/accordion.tsx +14 -2
- package/src/components/alert.tsx +1 -1
- package/src/components/app-bar-link.tsx +1 -1
- package/src/components/app-bar.tsx +1 -1
- package/src/components/avatar.tsx +1 -1
- package/src/components/badge.tsx +1 -1
- package/src/components/breadcrumb.spec.tsx +3 -3
- package/src/components/breadcrumb.tsx +1 -1
- package/src/components/button-group.tsx +4 -4
- package/src/components/button.spec.tsx +1 -1
- package/src/components/button.tsx +1 -1
- package/src/components/cache-view.spec.tsx +2 -2
- package/src/components/cache-view.tsx +3 -3
- package/src/components/card.tsx +5 -5
- package/src/components/carousel.tsx +2 -2
- package/src/components/chip.spec.tsx +64 -0
- package/src/components/chip.tsx +5 -2
- package/src/components/circular-progress.spec.tsx +20 -14
- package/src/components/circular-progress.tsx +5 -11
- package/src/components/command-palette/command-palette-input.tsx +1 -1
- package/src/components/command-palette/command-palette-suggestion-list.spec.tsx +1 -1
- package/src/components/command-palette/command-palette-suggestion-list.tsx +1 -1
- package/src/components/command-palette/index.spec.tsx +95 -33
- package/src/components/command-palette/index.tsx +16 -4
- package/src/components/context-menu/context-menu-item.tsx +1 -1
- package/src/components/context-menu/context-menu.tsx +1 -1
- package/src/components/data-grid/body.tsx +1 -1
- package/src/components/data-grid/data-grid-row.tsx +21 -3
- package/src/components/data-grid/data-grid.spec.tsx +246 -92
- package/src/components/data-grid/data-grid.tsx +52 -21
- package/src/components/data-grid/filters/boolean-filter.spec.tsx +29 -18
- package/src/components/data-grid/filters/boolean-filter.tsx +6 -6
- package/src/components/data-grid/filters/date-filter.spec.tsx +35 -22
- package/src/components/data-grid/filters/date-filter.tsx +8 -8
- package/src/components/data-grid/filters/enum-filter.spec.tsx +35 -20
- package/src/components/data-grid/filters/enum-filter.tsx +7 -7
- package/src/components/data-grid/filters/filter-dropdown.tsx +1 -1
- package/src/components/data-grid/filters/number-filter.spec.tsx +32 -22
- package/src/components/data-grid/filters/number-filter.tsx +7 -7
- package/src/components/data-grid/filters/string-filter.spec.tsx +32 -20
- package/src/components/data-grid/filters/string-filter.tsx +7 -7
- package/src/components/data-grid/footer.spec.tsx +79 -31
- package/src/components/data-grid/footer.tsx +10 -15
- package/src/components/data-grid/header.spec.tsx +152 -68
- package/src/components/data-grid/header.tsx +64 -27
- package/src/components/data-grid/selection-cell.tsx +2 -1
- package/src/components/dialog.spec.tsx +77 -2
- package/src/components/dialog.tsx +15 -2
- package/src/components/divider.tsx +1 -1
- package/src/components/drawer/drawer-toggle-button.tsx +1 -1
- package/src/components/drawer/index.tsx +1 -1
- package/src/components/dropdown.spec.tsx +9 -0
- package/src/components/dropdown.tsx +2 -1
- package/src/components/fab.tsx +1 -1
- package/src/components/form.tsx +1 -1
- package/src/components/grid.tsx +1 -1
- package/src/components/icons/icon.tsx +1 -1
- package/src/components/image.spec.tsx +82 -0
- package/src/components/image.tsx +18 -9
- package/src/components/inputs/autocomplete.tsx +1 -1
- package/src/components/inputs/checkbox.spec.tsx +1 -1
- package/src/components/inputs/checkbox.tsx +2 -1
- package/src/components/inputs/input-number.spec.tsx +1 -1
- package/src/components/inputs/input-number.tsx +1 -1
- package/src/components/inputs/input.spec.tsx +1 -1
- package/src/components/inputs/input.tsx +1 -1
- package/src/components/inputs/radio-group.spec.tsx +1 -1
- package/src/components/inputs/radio-group.tsx +1 -1
- package/src/components/inputs/radio.spec.tsx +1 -1
- package/src/components/inputs/radio.tsx +2 -1
- package/src/components/inputs/select.tsx +1 -1
- package/src/components/inputs/slider.tsx +2 -1
- package/src/components/inputs/switch.spec.tsx +1 -1
- package/src/components/inputs/switch.tsx +2 -1
- package/src/components/inputs/text-area.spec.tsx +1 -1
- package/src/components/inputs/text-area.tsx +1 -1
- package/src/components/linear-progress.spec.tsx +22 -18
- package/src/components/linear-progress.tsx +5 -11
- package/src/components/list/list-item.tsx +23 -5
- package/src/components/list/list.spec.tsx +165 -32
- package/src/components/list/list.tsx +38 -11
- package/src/components/loader.spec.tsx +1 -1
- package/src/components/loader.tsx +1 -1
- package/src/components/markdown/markdown-display.spec.tsx +133 -1
- package/src/components/markdown/markdown-display.tsx +13 -2
- package/src/components/markdown/markdown-editor.spec.tsx +124 -1
- package/src/components/markdown/markdown-editor.tsx +1 -1
- package/src/components/markdown/markdown-input.spec.tsx +1 -1
- package/src/components/markdown/markdown-input.tsx +1 -1
- package/src/components/menu/menu.tsx +2 -2
- package/src/components/modal.spec.tsx +127 -4
- package/src/components/modal.tsx +42 -4
- package/src/components/noty-list.tsx +2 -2
- package/src/components/page-container/index.tsx +1 -1
- package/src/components/page-container/page-header.tsx +1 -1
- package/src/components/page-layout/index.spec.tsx +20 -0
- package/src/components/page-layout/index.tsx +2 -2
- package/src/components/pagination.tsx +1 -1
- package/src/components/paper.tsx +1 -1
- package/src/components/rating.spec.tsx +200 -5
- package/src/components/rating.tsx +29 -23
- package/src/components/result.tsx +1 -1
- package/src/components/skeleton.tsx +1 -1
- package/src/components/suggest/index.spec.tsx +148 -44
- package/src/components/suggest/index.tsx +16 -3
- package/src/components/suggest/suggest-input.spec.tsx +1 -1
- package/src/components/suggest/suggest-input.tsx +1 -1
- package/src/components/suggest/suggestion-list.spec.tsx +1 -1
- package/src/components/suggest/suggestion-list.tsx +1 -1
- package/src/components/tabs.tsx +6 -2
- package/src/components/timeline.tsx +2 -2
- package/src/components/tooltip.tsx +1 -1
- package/src/components/tree/tree-item.tsx +20 -5
- package/src/components/tree/tree.spec.tsx +101 -2
- package/src/components/tree/tree.tsx +22 -4
- package/src/components/typography.tsx +1 -1
- package/src/components/wizard/index.spec.tsx +3 -3
- package/src/components/wizard/index.tsx +1 -1
- package/src/services/collection-service.spec.ts +33 -24
- package/src/services/collection-service.ts +35 -13
- package/src/services/css-variable-theme.spec.ts +1 -0
- package/src/services/css-variable-theme.ts +25 -0
- package/src/services/list-service.spec.ts +13 -42
- package/src/services/list-service.ts +15 -13
- package/src/services/theme-provider-service.ts +2 -0
- package/src/services/tree-service.spec.ts +12 -9
- package/src/services/tree-service.ts +5 -8
- package/src/themes/architect-theme.ts +1 -0
- package/src/themes/auditore-theme.ts +1 -0
- package/src/themes/black-mesa-theme.ts +1 -0
- package/src/themes/chieftain-theme.ts +1 -0
- package/src/themes/default-dark-theme.ts +1 -0
- package/src/themes/default-light-theme.ts +1 -0
- package/src/themes/dragonborn-theme.ts +1 -0
- package/src/themes/hawkins-theme.ts +1 -0
- package/src/themes/jedi-theme.ts +1 -0
- package/src/themes/neon-runner-theme.ts +1 -0
- package/src/themes/paladin-theme.ts +1 -0
- package/src/themes/plumber-theme.ts +1 -0
- package/src/themes/replicant-theme.ts +1 -0
- package/src/themes/sandworm-theme.ts +1 -0
- package/src/themes/shadow-broker-theme.ts +1 -0
- package/src/themes/sith-theme.ts +1 -0
- package/src/themes/vault-dweller-theme.ts +1 -0
- package/src/themes/wild-hunt-theme.ts +1 -0
- package/src/themes/xenomorph-theme.ts +1 -0
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { Injector } from '@furystack/inject'
|
|
2
|
-
import { createComponent, initializeShadeRoot } from '@furystack/shades'
|
|
2
|
+
import { createComponent, initializeShadeRoot, Shade } from '@furystack/shades'
|
|
3
3
|
import { ObservableValue, sleepAsync, usingAsync } from '@furystack/utils'
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
5
5
|
import { ThemeProviderService } from '../services/theme-provider-service.js'
|
|
6
6
|
import { LinearProgress } from './linear-progress.js'
|
|
7
7
|
|
|
8
|
+
const ProgressWrapper = Shade<{ obs: ObservableValue<number> }>({
|
|
9
|
+
customElementName: 'test-linear-progress-wrapper',
|
|
10
|
+
render: ({ props, useObservable }) => {
|
|
11
|
+
const [value] = useObservable('value', props.obs)
|
|
12
|
+
return <LinearProgress variant="determinate" value={value} />
|
|
13
|
+
},
|
|
14
|
+
})
|
|
15
|
+
|
|
8
16
|
describe('LinearProgress', () => {
|
|
9
17
|
let originalAnimate: typeof Element.prototype.animate
|
|
10
18
|
|
|
@@ -35,7 +43,7 @@ describe('LinearProgress', () => {
|
|
|
35
43
|
vi.restoreAllMocks()
|
|
36
44
|
})
|
|
37
45
|
|
|
38
|
-
it('should render
|
|
46
|
+
it('should render as custom element', async () => {
|
|
39
47
|
await usingAsync(new Injector(), async (injector) => {
|
|
40
48
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
41
49
|
|
|
@@ -90,12 +98,11 @@ describe('LinearProgress', () => {
|
|
|
90
98
|
it('should set aria-valuenow for determinate variant', async () => {
|
|
91
99
|
await usingAsync(new Injector(), async (injector) => {
|
|
92
100
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
93
|
-
const value = new ObservableValue(50)
|
|
94
101
|
|
|
95
102
|
initializeShadeRoot({
|
|
96
103
|
injector,
|
|
97
104
|
rootElement,
|
|
98
|
-
jsxElement: <LinearProgress variant="determinate" value={
|
|
105
|
+
jsxElement: <LinearProgress variant="determinate" value={50} />,
|
|
99
106
|
})
|
|
100
107
|
|
|
101
108
|
await sleepAsync(50)
|
|
@@ -110,12 +117,11 @@ describe('LinearProgress', () => {
|
|
|
110
117
|
it('should set bar width based on value', async () => {
|
|
111
118
|
await usingAsync(new Injector(), async (injector) => {
|
|
112
119
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
113
|
-
const value = new ObservableValue(75)
|
|
114
120
|
|
|
115
121
|
initializeShadeRoot({
|
|
116
122
|
injector,
|
|
117
123
|
rootElement,
|
|
118
|
-
jsxElement: <LinearProgress variant="determinate" value={
|
|
124
|
+
jsxElement: <LinearProgress variant="determinate" value={75} />,
|
|
119
125
|
})
|
|
120
126
|
|
|
121
127
|
await sleepAsync(50)
|
|
@@ -128,12 +134,11 @@ describe('LinearProgress', () => {
|
|
|
128
134
|
it('should clamp value to 0-100 range', async () => {
|
|
129
135
|
await usingAsync(new Injector(), async (injector) => {
|
|
130
136
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
131
|
-
const value = new ObservableValue(150)
|
|
132
137
|
|
|
133
138
|
initializeShadeRoot({
|
|
134
139
|
injector,
|
|
135
140
|
rootElement,
|
|
136
|
-
jsxElement: <LinearProgress variant="determinate" value={
|
|
141
|
+
jsxElement: <LinearProgress variant="determinate" value={150} />,
|
|
137
142
|
})
|
|
138
143
|
|
|
139
144
|
await sleepAsync(50)
|
|
@@ -146,12 +151,11 @@ describe('LinearProgress', () => {
|
|
|
146
151
|
it('should clamp negative values to 0', async () => {
|
|
147
152
|
await usingAsync(new Injector(), async (injector) => {
|
|
148
153
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
149
|
-
const value = new ObservableValue(-20)
|
|
150
154
|
|
|
151
155
|
initializeShadeRoot({
|
|
152
156
|
injector,
|
|
153
157
|
rootElement,
|
|
154
|
-
jsxElement: <LinearProgress variant="determinate" value={
|
|
158
|
+
jsxElement: <LinearProgress variant="determinate" value={-20} />,
|
|
155
159
|
})
|
|
156
160
|
|
|
157
161
|
await sleepAsync(50)
|
|
@@ -161,15 +165,15 @@ describe('LinearProgress', () => {
|
|
|
161
165
|
})
|
|
162
166
|
})
|
|
163
167
|
|
|
164
|
-
it('should update bar width when
|
|
168
|
+
it('should update bar width when value prop changes', async () => {
|
|
165
169
|
await usingAsync(new Injector(), async (injector) => {
|
|
166
170
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
167
|
-
const
|
|
171
|
+
const obs = new ObservableValue(20)
|
|
168
172
|
|
|
169
173
|
initializeShadeRoot({
|
|
170
174
|
injector,
|
|
171
175
|
rootElement,
|
|
172
|
-
jsxElement: <
|
|
176
|
+
jsxElement: <ProgressWrapper obs={obs} />,
|
|
173
177
|
})
|
|
174
178
|
|
|
175
179
|
await sleepAsync(50)
|
|
@@ -177,22 +181,22 @@ describe('LinearProgress', () => {
|
|
|
177
181
|
const bar = document.querySelector('shade-linear-progress .progress-bar') as HTMLElement
|
|
178
182
|
expect(bar.style.width).toBe('20%')
|
|
179
183
|
|
|
180
|
-
|
|
184
|
+
obs.setValue(80)
|
|
181
185
|
await sleepAsync(50)
|
|
182
186
|
|
|
183
187
|
expect(bar.style.width).toBe('80%')
|
|
184
188
|
})
|
|
185
189
|
})
|
|
186
190
|
|
|
187
|
-
it('should update aria-valuenow when
|
|
191
|
+
it('should update aria-valuenow when value prop changes', async () => {
|
|
188
192
|
await usingAsync(new Injector(), async (injector) => {
|
|
189
193
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
190
|
-
const
|
|
194
|
+
const obs = new ObservableValue(30)
|
|
191
195
|
|
|
192
196
|
initializeShadeRoot({
|
|
193
197
|
injector,
|
|
194
198
|
rootElement,
|
|
195
|
-
jsxElement: <
|
|
199
|
+
jsxElement: <ProgressWrapper obs={obs} />,
|
|
196
200
|
})
|
|
197
201
|
|
|
198
202
|
await sleepAsync(50)
|
|
@@ -200,7 +204,7 @@ describe('LinearProgress', () => {
|
|
|
200
204
|
const el = document.querySelector('shade-linear-progress') as HTMLElement
|
|
201
205
|
expect(el.getAttribute('aria-valuenow')).toBe('30')
|
|
202
206
|
|
|
203
|
-
|
|
207
|
+
obs.setValue(90)
|
|
204
208
|
await sleepAsync(50)
|
|
205
209
|
|
|
206
210
|
expect(el.getAttribute('aria-valuenow')).toBe('90')
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Shade, createComponent } from '@furystack/shades'
|
|
2
|
-
import type { ObservableValue } from '@furystack/utils'
|
|
3
2
|
import { buildTransition, cssVariableTheme } from '../services/css-variable-theme.js'
|
|
4
3
|
import type { Palette } from '../services/theme-provider-service.js'
|
|
5
4
|
import { ThemeProviderService } from '../services/theme-provider-service.js'
|
|
@@ -7,10 +6,9 @@ import { promisifyAnimation } from '../utils/promisify-animation.js'
|
|
|
7
6
|
|
|
8
7
|
export type LinearProgressProps = {
|
|
9
8
|
/**
|
|
10
|
-
*
|
|
11
|
-
* The component subscribes internally and updates the bar without re-rendering.
|
|
9
|
+
* A progress value (0–100). Used when variant is 'determinate'.
|
|
12
10
|
*/
|
|
13
|
-
value?:
|
|
11
|
+
value?: number
|
|
14
12
|
/**
|
|
15
13
|
* The variant of the progress indicator.
|
|
16
14
|
* - 'determinate': shows a fixed progress bar based on `value`
|
|
@@ -33,7 +31,7 @@ export type LinearProgressProps = {
|
|
|
33
31
|
const clampValue = (v: number) => Math.max(0, Math.min(100, v))
|
|
34
32
|
|
|
35
33
|
export const LinearProgress = Shade<LinearProgressProps>({
|
|
36
|
-
|
|
34
|
+
customElementName: 'shade-linear-progress',
|
|
37
35
|
css: {
|
|
38
36
|
display: 'block',
|
|
39
37
|
fontFamily: cssVariableTheme.typography.fontFamily,
|
|
@@ -68,15 +66,11 @@ export const LinearProgress = Shade<LinearProgressProps>({
|
|
|
68
66
|
transition: 'none',
|
|
69
67
|
},
|
|
70
68
|
},
|
|
71
|
-
render: ({ props, injector,
|
|
69
|
+
render: ({ props, injector, useHostProps, useRef }) => {
|
|
72
70
|
const themeProvider = injector.getInstance(ThemeProviderService)
|
|
73
71
|
const barRef = useRef<HTMLElement>('progressBar')
|
|
74
72
|
const variant = props.variant || 'indeterminate'
|
|
75
|
-
|
|
76
|
-
if (variant === 'determinate' && props.value) {
|
|
77
|
-
useObservable('progressValue', props.value)
|
|
78
|
-
}
|
|
79
|
-
const value = clampValue(props.value?.getValue() ?? 0)
|
|
73
|
+
const value = clampValue(props.value ?? 0)
|
|
80
74
|
|
|
81
75
|
const color = themeProvider.theme.palette[props.color || 'primary'].main
|
|
82
76
|
useHostProps({
|
|
@@ -14,7 +14,7 @@ export type ListItemProps<T> = {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export const ListItem: <T>(props: ListItemProps<T>, children: ChildrenList) => JSX.Element<any> = Shade({
|
|
17
|
-
|
|
17
|
+
customElementName: 'shade-list-item',
|
|
18
18
|
css: {
|
|
19
19
|
display: 'flex',
|
|
20
20
|
fontFamily: cssVariableTheme.typography.fontFamily,
|
|
@@ -46,8 +46,21 @@ export const ListItem: <T>(props: ListItemProps<T>, children: ChildrenList) => J
|
|
|
46
46
|
const isSelected = selection.includes(item)
|
|
47
47
|
|
|
48
48
|
useHostProps({
|
|
49
|
+
tabIndex: isFocused ? 0 : -1,
|
|
50
|
+
'data-spatial-nav-target': '',
|
|
49
51
|
role: 'option',
|
|
50
52
|
'aria-selected': isSelected.toString(),
|
|
53
|
+
onpointerdown: () => {
|
|
54
|
+
listService.setFocusAnchor()
|
|
55
|
+
},
|
|
56
|
+
onfocus: () => {
|
|
57
|
+
if (listService.focusedItem.getValue() !== item) {
|
|
58
|
+
listService.focusedItem.setValue(item)
|
|
59
|
+
}
|
|
60
|
+
if (!listService.hasFocus.getValue()) {
|
|
61
|
+
listService.hasFocus.setValue(true)
|
|
62
|
+
}
|
|
63
|
+
},
|
|
51
64
|
onclick: (ev: MouseEvent) => {
|
|
52
65
|
listService.handleItemClick(item, ev)
|
|
53
66
|
},
|
|
@@ -65,11 +78,16 @@ export const ListItem: <T>(props: ListItemProps<T>, children: ChildrenList) => J
|
|
|
65
78
|
queueMicrotask(() => {
|
|
66
79
|
const el = wrapperRef.current
|
|
67
80
|
if (!el) return
|
|
81
|
+
const hostEl = el.closest('shade-list-item') as HTMLElement
|
|
82
|
+
if (!hostEl) return
|
|
83
|
+
|
|
84
|
+
if (document.activeElement !== hostEl) {
|
|
85
|
+
hostEl.focus({ preventScroll: true })
|
|
86
|
+
}
|
|
87
|
+
|
|
68
88
|
const scrollContainer = el.closest('shade-list') as HTMLElement
|
|
69
89
|
if (scrollContainer) {
|
|
70
90
|
const containerRect = scrollContainer.getBoundingClientRect()
|
|
71
|
-
const hostEl = el.closest('shade-list-item') as HTMLElement
|
|
72
|
-
if (!hostEl) return
|
|
73
91
|
const itemRect = hostEl.getBoundingClientRect()
|
|
74
92
|
const itemTopInContainer = itemRect.top - containerRect.top
|
|
75
93
|
const itemBottomInContainer = itemRect.bottom - containerRect.top
|
|
@@ -77,12 +95,12 @@ export const ListItem: <T>(props: ListItemProps<T>, children: ChildrenList) => J
|
|
|
77
95
|
if (itemTopInContainer < 0) {
|
|
78
96
|
scrollContainer.scrollTo({
|
|
79
97
|
top: scrollContainer.scrollTop + itemTopInContainer,
|
|
80
|
-
behavior: '
|
|
98
|
+
behavior: 'instant',
|
|
81
99
|
})
|
|
82
100
|
} else if (itemBottomInContainer > scrollContainer.clientHeight) {
|
|
83
101
|
scrollContainer.scrollTo({
|
|
84
102
|
top: scrollContainer.scrollTop + (itemBottomInContainer - scrollContainer.clientHeight),
|
|
85
|
-
behavior: '
|
|
103
|
+
behavior: 'instant',
|
|
86
104
|
})
|
|
87
105
|
}
|
|
88
106
|
}
|
|
@@ -252,7 +252,7 @@ describe('List', () => {
|
|
|
252
252
|
})
|
|
253
253
|
})
|
|
254
254
|
|
|
255
|
-
it('should lose focus on
|
|
255
|
+
it('should lose focus on focusout to an outside element', async () => {
|
|
256
256
|
await usingAsync(new Injector(), async (injector) => {
|
|
257
257
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
258
258
|
const service = createTestService()
|
|
@@ -262,7 +262,7 @@ describe('List', () => {
|
|
|
262
262
|
rootElement,
|
|
263
263
|
jsxElement: (
|
|
264
264
|
<>
|
|
265
|
-
<
|
|
265
|
+
<button data-testid="outside">Outside</button>
|
|
266
266
|
<List<TestItem> items={testItems} listService={service} renderItem={(item) => <span>{item.name}</span>} />
|
|
267
267
|
</>
|
|
268
268
|
),
|
|
@@ -277,7 +277,7 @@ describe('List', () => {
|
|
|
277
277
|
expect(service.hasFocus.getValue()).toBe(true)
|
|
278
278
|
|
|
279
279
|
const outside = document.querySelector('[data-testid="outside"]') as HTMLElement
|
|
280
|
-
|
|
280
|
+
wrapper?.dispatchEvent(new FocusEvent('focusout', { bubbles: true, relatedTarget: outside }))
|
|
281
281
|
|
|
282
282
|
expect(service.hasFocus.getValue()).toBe(false)
|
|
283
283
|
|
|
@@ -336,6 +336,87 @@ describe('List', () => {
|
|
|
336
336
|
service[Symbol.dispose]()
|
|
337
337
|
})
|
|
338
338
|
})
|
|
339
|
+
|
|
340
|
+
it('should not initialize focusedItem on wrapper focusin (items handle focus individually)', async () => {
|
|
341
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
342
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
343
|
+
const service = createTestService()
|
|
344
|
+
|
|
345
|
+
initializeShadeRoot({
|
|
346
|
+
injector,
|
|
347
|
+
rootElement,
|
|
348
|
+
jsxElement: (
|
|
349
|
+
<List<TestItem> items={testItems} listService={service} renderItem={(item) => <span>{item.name}</span>} />
|
|
350
|
+
),
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
await flushUpdates()
|
|
354
|
+
await new Promise((r) => setTimeout(r, 0))
|
|
355
|
+
|
|
356
|
+
expect(service.hasFocus.getValue()).toBe(false)
|
|
357
|
+
expect(service.focusedItem.getValue()).toBeUndefined()
|
|
358
|
+
|
|
359
|
+
service[Symbol.dispose]()
|
|
360
|
+
})
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
it('should clear hasFocus on focusout when focus moves outside', async () => {
|
|
364
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
365
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
366
|
+
const service = createTestService()
|
|
367
|
+
const outsideEl = document.createElement('button')
|
|
368
|
+
outsideEl.textContent = 'Outside'
|
|
369
|
+
document.body.appendChild(outsideEl)
|
|
370
|
+
|
|
371
|
+
initializeShadeRoot({
|
|
372
|
+
injector,
|
|
373
|
+
rootElement,
|
|
374
|
+
jsxElement: (
|
|
375
|
+
<List<TestItem> items={testItems} listService={service} renderItem={(item) => <span>{item.name}</span>} />
|
|
376
|
+
),
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
await flushUpdates()
|
|
380
|
+
await new Promise((r) => setTimeout(r, 0))
|
|
381
|
+
|
|
382
|
+
service.hasFocus.setValue(true)
|
|
383
|
+
|
|
384
|
+
const wrapper = document.querySelector('.shade-list-wrapper') as HTMLElement
|
|
385
|
+
wrapper?.dispatchEvent(new FocusEvent('focusout', { bubbles: true, relatedTarget: outsideEl }))
|
|
386
|
+
|
|
387
|
+
expect(service.hasFocus.getValue()).toBe(false)
|
|
388
|
+
|
|
389
|
+
outsideEl.remove()
|
|
390
|
+
service[Symbol.dispose]()
|
|
391
|
+
})
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
it('should clear hasFocus on focusout when relatedTarget is null', async () => {
|
|
395
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
396
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
397
|
+
const service = createTestService()
|
|
398
|
+
|
|
399
|
+
initializeShadeRoot({
|
|
400
|
+
injector,
|
|
401
|
+
rootElement,
|
|
402
|
+
jsxElement: (
|
|
403
|
+
<List<TestItem> items={testItems} listService={service} renderItem={(item) => <span>{item.name}</span>} />
|
|
404
|
+
),
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
await flushUpdates()
|
|
408
|
+
await new Promise((r) => setTimeout(r, 0))
|
|
409
|
+
|
|
410
|
+
service.hasFocus.setValue(true)
|
|
411
|
+
|
|
412
|
+
const wrapper = document.querySelector('.shade-list-wrapper') as HTMLElement
|
|
413
|
+
wrapper?.dispatchEvent(new FocusEvent('focusout', { bubbles: true, relatedTarget: null }))
|
|
414
|
+
|
|
415
|
+
expect(service.hasFocus.getValue()).toBe(false)
|
|
416
|
+
|
|
417
|
+
service[Symbol.dispose]()
|
|
418
|
+
})
|
|
419
|
+
})
|
|
339
420
|
})
|
|
340
421
|
|
|
341
422
|
describe('selection', () => {
|
|
@@ -426,7 +507,7 @@ describe('List', () => {
|
|
|
426
507
|
})
|
|
427
508
|
|
|
428
509
|
describe('keyboard navigation', () => {
|
|
429
|
-
it('should handle ArrowDown
|
|
510
|
+
it('should not handle ArrowDown (delegated to spatial navigation)', async () => {
|
|
430
511
|
await usingAsync(new Injector(), async (injector) => {
|
|
431
512
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
432
513
|
const service = createTestService()
|
|
@@ -446,13 +527,13 @@ describe('List', () => {
|
|
|
446
527
|
|
|
447
528
|
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }))
|
|
448
529
|
|
|
449
|
-
expect(service.focusedItem.getValue()).toEqual(testItems[
|
|
530
|
+
expect(service.focusedItem.getValue()).toEqual(testItems[0])
|
|
450
531
|
|
|
451
532
|
service[Symbol.dispose]()
|
|
452
533
|
})
|
|
453
534
|
})
|
|
454
535
|
|
|
455
|
-
it('should handle ArrowUp
|
|
536
|
+
it('should not handle ArrowUp (delegated to spatial navigation)', async () => {
|
|
456
537
|
await usingAsync(new Injector(), async (injector) => {
|
|
457
538
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
458
539
|
const service = createTestService()
|
|
@@ -472,7 +553,7 @@ describe('List', () => {
|
|
|
472
553
|
|
|
473
554
|
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true }))
|
|
474
555
|
|
|
475
|
-
expect(service.focusedItem.getValue()).toEqual(testItems[
|
|
556
|
+
expect(service.focusedItem.getValue()).toEqual(testItems[1])
|
|
476
557
|
|
|
477
558
|
service[Symbol.dispose]()
|
|
478
559
|
})
|
|
@@ -666,31 +747,6 @@ describe('List', () => {
|
|
|
666
747
|
})
|
|
667
748
|
})
|
|
668
749
|
|
|
669
|
-
it('should handle Tab to toggle focus', async () => {
|
|
670
|
-
await usingAsync(new Injector(), async (injector) => {
|
|
671
|
-
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
672
|
-
const service = createTestService()
|
|
673
|
-
|
|
674
|
-
service.hasFocus.setValue(true)
|
|
675
|
-
|
|
676
|
-
initializeShadeRoot({
|
|
677
|
-
injector,
|
|
678
|
-
rootElement,
|
|
679
|
-
jsxElement: (
|
|
680
|
-
<List<TestItem> items={testItems} listService={service} renderItem={(item) => <span>{item.name}</span>} />
|
|
681
|
-
),
|
|
682
|
-
})
|
|
683
|
-
|
|
684
|
-
await flushUpdates()
|
|
685
|
-
|
|
686
|
-
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', bubbles: true }))
|
|
687
|
-
|
|
688
|
-
expect(service.hasFocus.getValue()).toBe(false)
|
|
689
|
-
|
|
690
|
-
service[Symbol.dispose]()
|
|
691
|
-
})
|
|
692
|
-
})
|
|
693
|
-
|
|
694
750
|
it('should not handle keyboard when not focused', async () => {
|
|
695
751
|
await usingAsync(new Injector(), async (injector) => {
|
|
696
752
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
@@ -956,6 +1012,83 @@ describe('List', () => {
|
|
|
956
1012
|
})
|
|
957
1013
|
})
|
|
958
1014
|
|
|
1015
|
+
describe('item spatial navigation attributes', () => {
|
|
1016
|
+
it('should set data-spatial-nav-target on list items', async () => {
|
|
1017
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
1018
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
1019
|
+
const service = createTestService()
|
|
1020
|
+
|
|
1021
|
+
initializeShadeRoot({
|
|
1022
|
+
injector,
|
|
1023
|
+
rootElement,
|
|
1024
|
+
jsxElement: (
|
|
1025
|
+
<List<TestItem> items={testItems} listService={service} renderItem={(item) => <span>{item.name}</span>} />
|
|
1026
|
+
),
|
|
1027
|
+
})
|
|
1028
|
+
|
|
1029
|
+
await flushUpdates()
|
|
1030
|
+
|
|
1031
|
+
const items = document.querySelectorAll('shade-list-item')
|
|
1032
|
+
for (const item of items) {
|
|
1033
|
+
expect(item.hasAttribute('data-spatial-nav-target')).toBe(true)
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
service[Symbol.dispose]()
|
|
1037
|
+
})
|
|
1038
|
+
})
|
|
1039
|
+
|
|
1040
|
+
it('should set tabIndex 0 on focused item and -1 on others', async () => {
|
|
1041
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
1042
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
1043
|
+
const service = createTestService()
|
|
1044
|
+
|
|
1045
|
+
service.focusedItem.setValue(testItems[1])
|
|
1046
|
+
|
|
1047
|
+
initializeShadeRoot({
|
|
1048
|
+
injector,
|
|
1049
|
+
rootElement,
|
|
1050
|
+
jsxElement: (
|
|
1051
|
+
<List<TestItem> items={testItems} listService={service} renderItem={(item) => <span>{item.name}</span>} />
|
|
1052
|
+
),
|
|
1053
|
+
})
|
|
1054
|
+
|
|
1055
|
+
await flushUpdates()
|
|
1056
|
+
|
|
1057
|
+
const items = document.querySelectorAll<HTMLDivElement>('shade-list-item')
|
|
1058
|
+
expect(items[0]?.tabIndex).toBe(-1)
|
|
1059
|
+
expect(items[1]?.tabIndex).toBe(0)
|
|
1060
|
+
expect(items[2]?.tabIndex).toBe(-1)
|
|
1061
|
+
|
|
1062
|
+
service[Symbol.dispose]()
|
|
1063
|
+
})
|
|
1064
|
+
})
|
|
1065
|
+
|
|
1066
|
+
it('should sync focusedItem on item onfocus', async () => {
|
|
1067
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
1068
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
1069
|
+
const service = createTestService()
|
|
1070
|
+
|
|
1071
|
+
initializeShadeRoot({
|
|
1072
|
+
injector,
|
|
1073
|
+
rootElement,
|
|
1074
|
+
jsxElement: (
|
|
1075
|
+
<List<TestItem> items={testItems} listService={service} renderItem={(item) => <span>{item.name}</span>} />
|
|
1076
|
+
),
|
|
1077
|
+
})
|
|
1078
|
+
|
|
1079
|
+
await flushUpdates()
|
|
1080
|
+
|
|
1081
|
+
const items = document.querySelectorAll('shade-list-item')
|
|
1082
|
+
items[2]?.dispatchEvent(new FocusEvent('focus'))
|
|
1083
|
+
|
|
1084
|
+
expect(service.focusedItem.getValue()).toEqual(testItems[2])
|
|
1085
|
+
expect(service.hasFocus.getValue()).toBe(true)
|
|
1086
|
+
|
|
1087
|
+
service[Symbol.dispose]()
|
|
1088
|
+
})
|
|
1089
|
+
})
|
|
1090
|
+
})
|
|
1091
|
+
|
|
959
1092
|
describe('keyboard listener cleanup', () => {
|
|
960
1093
|
it('should remove keyboard listener when component is disconnected', async () => {
|
|
961
1094
|
await usingAsync(new Injector(), async (injector) => {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { ChildrenList, PartialElement } from '@furystack/shades'
|
|
2
2
|
import { createComponent, Shade } from '@furystack/shades'
|
|
3
|
-
import { ClickAwayService } from '../../services/click-away-service.js'
|
|
4
3
|
import { cssVariableTheme } from '../../services/css-variable-theme.js'
|
|
5
4
|
import type { ListService } from '../../services/list-service.js'
|
|
6
5
|
import { Pagination } from '../pagination.js'
|
|
7
6
|
import { ListItem } from './list-item.js'
|
|
8
7
|
|
|
8
|
+
let nextListId = 0
|
|
9
|
+
|
|
9
10
|
export type ListItemState = {
|
|
10
11
|
isFocused: boolean
|
|
11
12
|
isSelected: boolean
|
|
@@ -31,10 +32,17 @@ export type ListProps<T> = {
|
|
|
31
32
|
onSelectionChange?: (selected: T[]) => void
|
|
32
33
|
/** Optional pagination configuration. When provided, items are sliced and a Pagination control is rendered. */
|
|
33
34
|
pagination?: ListPaginationProps
|
|
35
|
+
/**
|
|
36
|
+
* Section name for spatial navigation scoping.
|
|
37
|
+
* Sets `data-nav-section` on the list wrapper so that SpatialNavigationService
|
|
38
|
+
* constrains arrow-key navigation within the list.
|
|
39
|
+
* Auto-generated per instance when not provided.
|
|
40
|
+
*/
|
|
41
|
+
navSection?: string
|
|
34
42
|
} & PartialElement<HTMLDivElement>
|
|
35
43
|
|
|
36
44
|
export const List: <T>(props: ListProps<T>, children: ChildrenList) => JSX.Element<any> = Shade({
|
|
37
|
-
|
|
45
|
+
customElementName: 'shade-list',
|
|
38
46
|
css: {
|
|
39
47
|
display: 'block',
|
|
40
48
|
fontFamily: cssVariableTheme.typography.fontFamily,
|
|
@@ -46,8 +54,9 @@ export const List: <T>(props: ListProps<T>, children: ChildrenList) => JSX.Eleme
|
|
|
46
54
|
padding: '8px 0',
|
|
47
55
|
},
|
|
48
56
|
},
|
|
49
|
-
render: ({ props, useDisposable, useHostProps, useRef }) => {
|
|
57
|
+
render: ({ props, useDisposable, useHostProps, useRef, useState }) => {
|
|
50
58
|
const wrapperRef = useRef<HTMLDivElement>('listWrapper')
|
|
59
|
+
const [navSectionId] = useState('navSectionId', String(nextListId++))
|
|
51
60
|
|
|
52
61
|
useDisposable('keydown-handler', () => {
|
|
53
62
|
const listener = (ev: KeyboardEvent) => {
|
|
@@ -60,8 +69,8 @@ export const List: <T>(props: ListProps<T>, children: ChildrenList) => JSX.Eleme
|
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
71
|
}
|
|
63
|
-
window.addEventListener('keydown', listener)
|
|
64
|
-
return { [Symbol.dispose]: () => window.removeEventListener('keydown', listener) }
|
|
72
|
+
window.addEventListener('keydown', listener, true)
|
|
73
|
+
return { [Symbol.dispose]: () => window.removeEventListener('keydown', listener, true) }
|
|
65
74
|
})
|
|
66
75
|
|
|
67
76
|
const { pagination } = props
|
|
@@ -79,13 +88,30 @@ export const List: <T>(props: ListProps<T>, children: ChildrenList) => JSX.Eleme
|
|
|
79
88
|
|
|
80
89
|
props.listService.items.setValue(visibleItems)
|
|
81
90
|
|
|
82
|
-
useDisposable(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
useDisposable('focus-coordination', () => {
|
|
92
|
+
const handleFocusOut = (ev: FocusEvent) => {
|
|
93
|
+
const wrapper = wrapperRef.current
|
|
94
|
+
if (wrapper && (!ev.relatedTarget || !wrapper.contains(ev.relatedTarget as Node))) {
|
|
86
95
|
props.listService.hasFocus.setValue(false)
|
|
87
|
-
}
|
|
88
|
-
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
queueMicrotask(() => {
|
|
100
|
+
const wrapper = wrapperRef.current
|
|
101
|
+
if (wrapper) {
|
|
102
|
+
wrapper.addEventListener('focusout', handleFocusOut)
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
[Symbol.dispose]: () => {
|
|
108
|
+
const wrapper = wrapperRef.current
|
|
109
|
+
if (wrapper) {
|
|
110
|
+
wrapper.removeEventListener('focusout', handleFocusOut)
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
})
|
|
89
115
|
|
|
90
116
|
if (props.onSelectionChange) {
|
|
91
117
|
const { onSelectionChange } = props
|
|
@@ -107,6 +133,7 @@ export const List: <T>(props: ListProps<T>, children: ChildrenList) => JSX.Eleme
|
|
|
107
133
|
role="listbox"
|
|
108
134
|
ariaMultiSelectable="true"
|
|
109
135
|
className="shade-list-wrapper"
|
|
136
|
+
data-nav-section={props.navSection ?? `list-${navSectionId}`}
|
|
110
137
|
onclick={() => props.listService.hasFocus.setValue(true)}
|
|
111
138
|
>
|
|
112
139
|
{visibleItems.map((item) => (
|
|
@@ -40,7 +40,7 @@ describe('Loader', () => {
|
|
|
40
40
|
vi.restoreAllMocks()
|
|
41
41
|
})
|
|
42
42
|
|
|
43
|
-
it('should render
|
|
43
|
+
it('should render as custom element', async () => {
|
|
44
44
|
await usingAsync(new Injector(), async (injector) => {
|
|
45
45
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
46
46
|
|