@kouji-ui/core 0.0.3 → 0.1.1
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/.turbo/turbo-build.log +6 -3
- package/CHANGELOG.md +177 -0
- package/ng-package.json +5 -1
- package/package.json +18 -6
- package/src/a11y/aria-describedby.ts +7 -3
- package/src/a11y/aria-labelledby.spec.ts +55 -0
- package/src/a11y/aria-labelledby.ts +38 -0
- package/src/a11y/focus-trap.ts +4 -2
- package/src/a11y/index.ts +1 -0
- package/src/a11y/live-region.ts +4 -2
- package/src/a11y/roving-tabindex.spec.ts +139 -0
- package/src/a11y/roving-tabindex.ts +56 -7
- package/src/a11y/visually-hidden.ts +4 -2
- package/src/accordion/accordion.context.ts +22 -0
- package/src/accordion/accordion.spec.ts +233 -23
- package/src/accordion/accordion.ts +311 -39
- package/src/accordion/index.ts +8 -2
- package/src/alert/alert.context.ts +28 -0
- package/src/alert/alert.spec.ts +217 -0
- package/src/alert/alert.ts +417 -0
- package/src/alert/config.ts +46 -0
- package/src/alert/index.ts +19 -0
- package/src/avatar/avatar-group.context.ts +45 -0
- package/src/avatar/avatar-group.spec.ts +358 -0
- package/src/avatar/avatar-group.ts +259 -0
- package/src/avatar/avatar.ts +17 -4
- package/src/avatar/index.ts +11 -1
- package/src/badge/badge.spec.ts +8 -0
- package/src/badge/badge.ts +24 -3
- package/src/breadcrumb/breadcrumb-current.ts +38 -0
- package/src/breadcrumb/breadcrumb-ellipsis.ts +71 -0
- package/src/breadcrumb/breadcrumb-item.ts +41 -0
- package/src/breadcrumb/breadcrumb-link.ts +44 -0
- package/src/breadcrumb/breadcrumb-list.ts +42 -0
- package/src/breadcrumb/breadcrumb-separator.ts +45 -0
- package/src/breadcrumb/breadcrumb.context.ts +45 -0
- package/src/breadcrumb/breadcrumb.spec.ts +354 -0
- package/src/breadcrumb/breadcrumb.ts +239 -0
- package/src/breadcrumb/config.ts +84 -0
- package/src/breadcrumb/index.ts +18 -0
- package/src/button/_examples/button.example.ts +71 -0
- package/src/button/_examples/button.finance.example.ts +83 -0
- package/src/button/_examples/button.retro.example.ts +91 -0
- package/src/button/_examples/button.sizes.example.ts +60 -0
- package/src/button/_examples/index.ts +6 -0
- package/src/button/button.spec.ts +139 -8
- package/src/button/button.ts +114 -16
- package/src/button/config.spec.ts +33 -0
- package/src/button/config.ts +45 -0
- package/src/button/index.ts +2 -1
- package/src/button-group/button-group.context.ts +28 -0
- package/src/button-group/button-group.spec.ts +99 -0
- package/src/button-group/button-group.ts +99 -0
- package/src/button-group/index.ts +2 -0
- package/src/calendar/calendar-day.ts +102 -0
- package/src/calendar/calendar-grid.ts +90 -0
- package/src/calendar/calendar-header.ts +65 -0
- package/src/calendar/calendar.context.ts +53 -0
- package/src/calendar/calendar.spec.ts +225 -0
- package/src/calendar/calendar.ts +233 -0
- package/src/calendar/date-utils.ts +225 -0
- package/src/calendar/index.ts +28 -0
- package/src/carousel/carousel.context.ts +81 -0
- package/src/carousel/carousel.spec.ts +337 -0
- package/src/carousel/carousel.ts +919 -0
- package/src/carousel/index.ts +22 -0
- package/src/cascade-select/cascade-select-option.ts +205 -0
- package/src/cascade-select/cascade-select-panel.ts +106 -0
- package/src/cascade-select/cascade-select-root.ts +265 -0
- package/src/cascade-select/cascade-select-sub-panel.ts +168 -0
- package/src/cascade-select/cascade-select-trigger.ts +44 -0
- package/src/cascade-select/cascade-select.context.ts +71 -0
- package/src/cascade-select/cascade-select.spec.ts +241 -0
- package/src/cascade-select/cascade-select.ts +5 -0
- package/src/cascade-select/index.ts +11 -0
- package/src/chart/chart.ts +6 -2
- package/src/chat/chat-avatar.ts +31 -0
- package/src/chat/chat-bubble.ts +49 -0
- package/src/chat/chat-footer.ts +67 -0
- package/src/chat/chat-header.ts +41 -0
- package/src/chat/chat-log.ts +43 -0
- package/src/chat/chat.context.ts +64 -0
- package/src/chat/chat.spec.ts +341 -0
- package/src/chat/chat.ts +191 -0
- package/src/chat/config.ts +53 -0
- package/src/chat/index.ts +21 -0
- package/src/checkbox/checkbox.ts +9 -2
- package/src/color-picker/color-picker.context.ts +74 -0
- package/src/color-picker/color-picker.spec.ts +241 -0
- package/src/color-picker/color-picker.ts +698 -0
- package/src/color-picker/color-picker.utils.ts +140 -0
- package/src/color-picker/index.ts +28 -0
- package/src/combobox/combobox-input.ts +143 -0
- package/src/combobox/combobox-listbox.ts +49 -0
- package/src/combobox/combobox-option.ts +43 -0
- package/src/combobox/combobox-root.ts +281 -0
- package/src/combobox/combobox.spec.ts +75 -0
- package/src/combobox/combobox.ts +4 -0
- package/src/combobox/index.ts +4 -0
- package/src/command-palette/command-empty.ts +36 -0
- package/src/command-palette/command-group.ts +21 -0
- package/src/command-palette/command-input.ts +70 -0
- package/src/command-palette/command-item.ts +60 -0
- package/src/command-palette/command-list.ts +25 -0
- package/src/command-palette/command-palette-dialog.ts +74 -0
- package/src/command-palette/command-palette-trigger.ts +64 -0
- package/src/command-palette/command-palette.filters.ts +53 -0
- package/src/command-palette/command-palette.spec.ts +349 -0
- package/src/command-palette/command-palette.ts +204 -0
- package/src/command-palette/command-separator.ts +18 -0
- package/src/command-palette/index.ts +15 -0
- package/src/confirm-popup/confirm-popup-action.ts +39 -0
- package/src/confirm-popup/confirm-popup-cancel.ts +38 -0
- package/src/confirm-popup/confirm-popup-content.ts +113 -0
- package/src/confirm-popup/confirm-popup-message.ts +36 -0
- package/src/confirm-popup/confirm-popup-trigger.ts +31 -0
- package/src/confirm-popup/confirm-popup.context.ts +41 -0
- package/src/confirm-popup/confirm-popup.spec.ts +337 -0
- package/src/confirm-popup/confirm-popup.ts +109 -0
- package/src/confirm-popup/index.ts +12 -0
- package/src/date-picker/date-picker-calendar.ts +65 -0
- package/src/date-picker/date-picker-trigger.ts +215 -0
- package/src/date-picker/date-picker.context.ts +50 -0
- package/src/date-picker/date-picker.spec.ts +117 -0
- package/src/date-picker/date-picker.ts +127 -0
- package/src/date-picker/index.ts +7 -0
- package/src/dialog/_examples/dialog.confirm.example.ts +145 -0
- package/src/dialog/_examples/dialog.example.ts +106 -0
- package/src/dialog/_examples/dialog.finance.example.ts +121 -0
- package/src/dialog/_examples/dialog.retro.example.ts +126 -0
- package/src/dialog/_examples/index.ts +6 -0
- package/src/dialog/dialog.ref.ts +42 -0
- package/src/dialog/dialog.service.ts +57 -167
- package/src/dialog/dialog.spec.ts +35 -120
- package/src/dialog/dialog.ts +17 -192
- package/src/dialog/index.ts +3 -9
- package/src/divider/divider.spec.ts +223 -0
- package/src/divider/divider.ts +137 -0
- package/src/divider/index.ts +5 -0
- package/src/drawer/drawer.ref.ts +63 -0
- package/src/drawer/drawer.service.ts +106 -0
- package/src/drawer/drawer.spec.ts +117 -0
- package/src/drawer/drawer.ts +122 -0
- package/src/drawer/index.ts +10 -0
- package/src/dropdown-menu/dropdown-menu-content.ts +258 -0
- package/src/dropdown-menu/dropdown-menu-group.ts +31 -0
- package/src/dropdown-menu/dropdown-menu-item.ts +51 -0
- package/src/dropdown-menu/dropdown-menu-label.ts +41 -0
- package/src/dropdown-menu/dropdown-menu-separator.ts +27 -0
- package/src/dropdown-menu/dropdown-menu-trigger.ts +140 -0
- package/src/dropdown-menu/dropdown-menu.spec.ts +265 -0
- package/src/dropdown-menu/dropdown-menu.ts +60 -0
- package/src/dropdown-menu/index.ts +15 -0
- package/src/example-components.ts +39 -30
- package/src/field/field-error.ts +49 -0
- package/src/field/field-group.ts +39 -0
- package/src/field/field-help.ts +47 -0
- package/src/field/field-label.ts +30 -0
- package/src/field/field.context.ts +28 -0
- package/src/field/field.spec.ts +179 -0
- package/src/field/field.ts +121 -0
- package/src/field/index.ts +6 -0
- package/src/file-upload/file-upload.context.ts +89 -0
- package/src/file-upload/file-upload.spec.ts +290 -0
- package/src/file-upload/file-upload.ts +639 -0
- package/src/file-upload/file-upload.types.ts +59 -0
- package/src/file-upload/index.ts +23 -0
- package/src/form/form-error-summary.ts +87 -0
- package/src/form/form-field.context.ts +3 -0
- package/src/form/form-field.spec.ts +25 -0
- package/src/form/form-field.ts +52 -0
- package/src/form/form.context.ts +52 -2
- package/src/form/form.spec.ts +155 -18
- package/src/form/form.ts +315 -35
- package/src/form/index.ts +11 -2
- package/src/icon/E2E.md +14 -0
- package/src/icon/icon.css +40 -0
- package/src/icon/icon.directive.spec.ts +198 -0
- package/src/icon/icon.directive.ts +113 -0
- package/src/icon/icon.mode.spec.ts +29 -0
- package/src/icon/icon.mode.ts +23 -0
- package/src/icon/icon.providers.spec.ts +63 -0
- package/src/icon/icon.providers.ts +47 -0
- package/src/icon/icon.resolver.spec.ts +96 -0
- package/src/icon/icon.resolver.ts +49 -0
- package/src/icon/icon.tokens.ts +88 -0
- package/src/icon/icon.types.ts +48 -0
- package/src/icon/index.ts +21 -0
- package/src/input/input.ts +12 -3
- package/src/input-group/index.ts +3 -0
- package/src/input-group/input-group-addon.ts +92 -0
- package/src/input-group/input-group.context.ts +27 -0
- package/src/input-group/input-group.spec.ts +186 -0
- package/src/input-group/input-group.ts +136 -0
- package/src/input-mask/index.ts +11 -0
- package/src/input-mask/input-mask.compile.ts +69 -0
- package/src/input-mask/input-mask.engine.ts +376 -0
- package/src/input-mask/input-mask.presets.ts +24 -0
- package/src/input-mask/input-mask.spec.ts +304 -0
- package/src/input-mask/input-mask.tokens.ts +49 -0
- package/src/input-mask/input-mask.ts +280 -0
- package/src/input-otp/index.ts +2 -0
- package/src/input-otp/input-otp.context.ts +41 -0
- package/src/input-otp/input-otp.spec.ts +308 -0
- package/src/input-otp/input-otp.ts +470 -0
- package/src/kbd/index.ts +1 -0
- package/src/kbd/kbd.spec.ts +127 -0
- package/src/kbd/kbd.ts +132 -0
- package/src/link/config.ts +44 -0
- package/src/link/index.ts +2 -0
- package/src/link/link.spec.ts +246 -0
- package/src/link/link.ts +226 -0
- package/src/list/index.ts +11 -0
- package/src/list/list.spec.ts +237 -0
- package/src/list/list.ts +277 -0
- package/src/menubar/index.ts +7 -0
- package/src/menubar/menubar-item.ts +204 -0
- package/src/menubar/menubar.context.ts +43 -0
- package/src/menubar/menubar.spec.ts +433 -0
- package/src/menubar/menubar.ts +309 -0
- package/src/number-input/index.ts +4 -0
- package/src/number-input/number-input-group.ts +64 -0
- package/src/number-input/number-input.context.ts +31 -0
- package/src/number-input/number-input.format.ts +148 -0
- package/src/number-input/number-input.spec.ts +170 -0
- package/src/number-input/number-input.ts +457 -0
- package/src/number-input/number-stepper.ts +193 -0
- package/src/overlay-badge/index.ts +7 -0
- package/src/overlay-badge/overlay-badge-content.ts +73 -0
- package/src/overlay-badge/overlay-badge.context.ts +37 -0
- package/src/overlay-badge/overlay-badge.spec.ts +220 -0
- package/src/overlay-badge/overlay-badge.ts +128 -0
- package/src/pagination/config.ts +82 -0
- package/src/pagination/index.ts +19 -0
- package/src/pagination/page-tokens.ts +78 -0
- package/src/pagination/pagination-ellipsis.ts +69 -0
- package/src/pagination/pagination-first.ts +84 -0
- package/src/pagination/pagination-info.ts +70 -0
- package/src/pagination/pagination-item.ts +81 -0
- package/src/pagination/pagination-last.ts +85 -0
- package/src/pagination/pagination-next.ts +86 -0
- package/src/pagination/pagination-previous.ts +90 -0
- package/src/pagination/pagination.context.ts +52 -0
- package/src/pagination/pagination.spec.ts +388 -0
- package/src/pagination/pagination.ts +288 -0
- package/src/password-input/index.ts +15 -0
- package/src/password-input/password-input.context.ts +65 -0
- package/src/password-input/password-input.scorer.ts +34 -0
- package/src/password-input/password-input.spec.ts +274 -0
- package/src/password-input/password-input.ts +488 -0
- package/src/popover/_examples/index.ts +5 -0
- package/src/popover/_examples/popover.example.ts +92 -0
- package/src/popover/_examples/popover.finance.example.ts +106 -0
- package/src/popover/_examples/popover.retro.example.ts +103 -0
- package/src/popover/index.ts +5 -12
- package/src/popover/popover-arrow.ts +30 -0
- package/src/popover/popover-close.ts +36 -0
- package/src/popover/popover-content.ts +41 -0
- package/src/popover/popover-title.ts +39 -0
- package/src/popover/popover-trigger.ts +40 -0
- package/src/popover/popover.spec.ts +51 -44
- package/src/popover/popover.ts +5 -124
- package/src/presets/bind-presets.spec.ts +50 -0
- package/src/presets/bind-presets.ts +43 -0
- package/src/presets/index.ts +3 -0
- package/src/presets/size.spec.ts +67 -0
- package/src/presets/size.ts +59 -0
- package/src/presets/variant.spec.ts +72 -0
- package/src/presets/variant.ts +62 -0
- package/src/primitives/directionality/directionality.spec.ts +112 -0
- package/src/primitives/directionality/directionality.ts +89 -0
- package/src/primitives/directionality/index.ts +1 -0
- package/src/primitives/forms/form-control.spec.ts +14 -6
- package/src/primitives/forms/form-control.ts +6 -2
- package/src/primitives/index.ts +3 -0
- package/src/primitives/interaction/disabled.ts +12 -5
- package/src/primitives/interaction/focus-ring.ts +4 -2
- package/src/primitives/list/filterable-list.spec.ts +114 -0
- package/src/primitives/list/filterable-list.ts +126 -0
- package/src/primitives/list/group.spec.ts +99 -0
- package/src/primitives/list/group.ts +139 -0
- package/src/primitives/list/index.ts +8 -0
- package/src/primitives/list/inject-helpers.ts +8 -0
- package/src/primitives/list/item.spec.ts +173 -0
- package/src/primitives/list/item.ts +222 -0
- package/src/primitives/list/navigator.spec.ts +286 -0
- package/src/primitives/list/navigator.ts +238 -0
- package/src/primitives/list/selection.spec.ts +214 -0
- package/src/primitives/list/selection.ts +339 -0
- package/src/primitives/list/tokens.ts +106 -0
- package/src/primitives/list/type-ahead.spec.ts +58 -0
- package/src/primitives/list/type-ahead.ts +46 -0
- package/src/primitives/overlay/backdrop.spec.ts +54 -0
- package/src/primitives/overlay/backdrop.ts +31 -0
- package/src/primitives/overlay/builder.spec.ts +38 -0
- package/src/primitives/overlay/builder.ts +151 -0
- package/src/primitives/overlay/container.ts +42 -0
- package/src/primitives/overlay/context.ts +14 -0
- package/src/primitives/overlay/controller.spec.ts +86 -0
- package/src/primitives/overlay/controller.ts +190 -0
- package/src/primitives/overlay/id.spec.ts +24 -0
- package/src/primitives/overlay/id.ts +17 -0
- package/src/primitives/overlay/index.ts +18 -1
- package/src/primitives/overlay/overlay.css +42 -0
- package/src/primitives/overlay/panel.spec.ts +52 -0
- package/src/primitives/overlay/panel.ts +102 -0
- package/src/primitives/overlay/stack.spec.ts +55 -0
- package/src/primitives/overlay/stack.ts +113 -0
- package/src/primitives/overlay/strategies/backdrop/blurred.spec.ts +11 -0
- package/src/primitives/overlay/strategies/backdrop/blurred.ts +5 -0
- package/src/primitives/overlay/strategies/backdrop/none.spec.ts +10 -0
- package/src/primitives/overlay/strategies/backdrop/none.ts +9 -0
- package/src/primitives/overlay/strategies/backdrop/solid.spec.ts +18 -0
- package/src/primitives/overlay/strategies/backdrop/solid.ts +23 -0
- package/src/primitives/overlay/strategies/focus-trap/inert-based.spec.ts +28 -0
- package/src/primitives/overlay/strategies/focus-trap/inert-based.ts +38 -0
- package/src/primitives/overlay/strategies/focus-trap/no-trap.spec.ts +9 -0
- package/src/primitives/overlay/strategies/focus-trap/no-trap.ts +8 -0
- package/src/primitives/overlay/strategies/focus-trap/tab-cycle.spec.ts +49 -0
- package/src/primitives/overlay/strategies/focus-trap/tab-cycle.ts +85 -0
- package/src/primitives/overlay/strategies/index.ts +34 -0
- package/src/primitives/overlay/strategies/live-announcer/_announce.ts +35 -0
- package/src/primitives/overlay/strategies/live-announcer/assertive.spec.ts +21 -0
- package/src/primitives/overlay/strategies/live-announcer/assertive.ts +11 -0
- package/src/primitives/overlay/strategies/live-announcer/polite.spec.ts +21 -0
- package/src/primitives/overlay/strategies/live-announcer/polite.ts +11 -0
- package/src/primitives/overlay/strategies/live-announcer/silent.spec.ts +12 -0
- package/src/primitives/overlay/strategies/live-announcer/silent.ts +5 -0
- package/src/primitives/overlay/strategies/mount/body-portal.spec.ts +40 -0
- package/src/primitives/overlay/strategies/mount/body-portal.ts +91 -0
- package/src/primitives/overlay/strategies/mount/in-container.spec.ts +43 -0
- package/src/primitives/overlay/strategies/mount/in-container.ts +39 -0
- package/src/primitives/overlay/strategies/mount/in-place.spec.ts +40 -0
- package/src/primitives/overlay/strategies/mount/in-place.ts +20 -0
- package/src/primitives/overlay/strategies/position/anchored-to.spec.ts +56 -0
- package/src/primitives/overlay/strategies/position/anchored-to.ts +248 -0
- package/src/primitives/overlay/strategies/position/corner.spec.ts +36 -0
- package/src/primitives/overlay/strategies/position/corner.ts +33 -0
- package/src/primitives/overlay/strategies/position/edge-sheet.spec.ts +32 -0
- package/src/primitives/overlay/strategies/position/edge-sheet.ts +36 -0
- package/src/primitives/overlay/strategies/position/in-place-sibling.spec.ts +25 -0
- package/src/primitives/overlay/strategies/position/in-place-sibling.ts +13 -0
- package/src/primitives/overlay/strategies/position/point-at.spec.ts +40 -0
- package/src/primitives/overlay/strategies/position/point-at.ts +34 -0
- package/src/primitives/overlay/strategies/position/viewport-centered.spec.ts +31 -0
- package/src/primitives/overlay/strategies/position/viewport-centered.ts +31 -0
- package/src/primitives/overlay/strategies/scroll-lock/css-clip.spec.ts +15 -0
- package/src/primitives/overlay/strategies/scroll-lock/css-clip.ts +39 -0
- package/src/primitives/overlay/strategies/scroll-lock/html-overflow.spec.ts +21 -0
- package/src/primitives/overlay/strategies/scroll-lock/html-overflow.ts +44 -0
- package/src/primitives/overlay/strategies/scroll-lock/none.spec.ts +9 -0
- package/src/primitives/overlay/strategies/scroll-lock/none.ts +5 -0
- package/src/primitives/overlay/strategies/trigger-event/on-click.spec.ts +32 -0
- package/src/primitives/overlay/strategies/trigger-event/on-click.ts +28 -0
- package/src/primitives/overlay/strategies/trigger-event/on-context-menu.spec.ts +45 -0
- package/src/primitives/overlay/strategies/trigger-event/on-context-menu.ts +70 -0
- package/src/primitives/overlay/strategies/trigger-event/on-focus-or-input.spec.ts +35 -0
- package/src/primitives/overlay/strategies/trigger-event/on-focus-or-input.ts +34 -0
- package/src/primitives/overlay/strategies/trigger-event/on-focus.spec.ts +64 -0
- package/src/primitives/overlay/strategies/trigger-event/on-focus.ts +37 -0
- package/src/primitives/overlay/strategies/trigger-event/on-hotkey.spec.ts +37 -0
- package/src/primitives/overlay/strategies/trigger-event/on-hotkey.ts +59 -0
- package/src/primitives/overlay/strategies/trigger-event/on-hover.spec.ts +34 -0
- package/src/primitives/overlay/strategies/trigger-event/on-hover.ts +86 -0
- package/src/primitives/overlay/strategies/trigger-event/programmatic.spec.ts +16 -0
- package/src/primitives/overlay/strategies/trigger-event/programmatic.ts +12 -0
- package/src/primitives/overlay/tokens.ts +54 -0
- package/src/primitives/overlay/trigger.spec.ts +103 -0
- package/src/primitives/overlay/trigger.ts +75 -0
- package/src/primitives/overlay/types.ts +24 -0
- package/src/primitives/overlay/wrapper.ts +50 -0
- package/src/primitives/signals/deep-signal.ts +109 -0
- package/src/primitives/signals/index.ts +1 -0
- package/src/progress-bar/config.ts +48 -0
- package/src/progress-bar/index.ts +12 -0
- package/src/progress-bar/progress-bar-fill.ts +37 -0
- package/src/progress-bar/progress-bar.context.ts +34 -0
- package/src/progress-bar/progress-bar.spec.ts +423 -0
- package/src/progress-bar/progress-bar.ts +217 -0
- package/src/public-api.ts +58 -9
- package/src/radio/radio.ts +14 -3
- package/src/select/index.ts +4 -2
- package/src/select/select-content.ts +69 -0
- package/src/select/select-option.ts +35 -0
- package/src/select/select-root.ts +129 -0
- package/src/select/select-trigger.ts +93 -0
- package/src/select/select.spec.ts +133 -140
- package/src/select/select.ts +4 -250
- package/src/skeleton/index.ts +5 -0
- package/src/skeleton/skeleton.spec.ts +115 -0
- package/src/skeleton/skeleton.ts +56 -0
- package/src/slider/index.ts +10 -0
- package/src/slider/slider-range.ts +49 -0
- package/src/slider/slider-thumb.ts +335 -0
- package/src/slider/slider-track.ts +70 -0
- package/src/slider/slider.context.ts +79 -0
- package/src/slider/slider.geometry.ts +73 -0
- package/src/slider/slider.spec.ts +216 -0
- package/src/slider/slider.ts +443 -0
- package/src/speed-dial/index.ts +10 -0
- package/src/speed-dial/speed-dial-action.ts +40 -0
- package/src/speed-dial/speed-dial-actions.ts +31 -0
- package/src/speed-dial/speed-dial-trigger.ts +55 -0
- package/src/speed-dial/speed-dial.context.ts +36 -0
- package/src/speed-dial/speed-dial.spec.ts +143 -0
- package/src/speed-dial/speed-dial.ts +127 -0
- package/src/spinner/config.ts +48 -0
- package/src/spinner/index.ts +8 -0
- package/src/spinner/spinner.spec.ts +262 -0
- package/src/spinner/spinner.ts +129 -0
- package/src/stepper/index.ts +15 -0
- package/src/stepper/stepper.context.ts +63 -0
- package/src/stepper/stepper.spec.ts +403 -0
- package/src/stepper/stepper.ts +544 -0
- package/src/styles.css +17 -0
- package/src/table/column-helpers.spec.ts +74 -0
- package/src/table/column-helpers.ts +68 -0
- package/src/table/filter-fns.ts +230 -0
- package/src/table/filter-models.spec.ts +240 -0
- package/src/table/filter-models.ts +124 -0
- package/src/table/filter-params.ts +90 -0
- package/src/table/grid-api-impl.ts +321 -0
- package/src/table/grid-api.spec.ts +178 -0
- package/src/table/grid-api.ts +285 -0
- package/src/table/index.ts +73 -1
- package/src/table/table-cell.spec.ts +43 -0
- package/src/table/table-cell.ts +38 -0
- package/src/table/table-filter-outlet.ts +120 -0
- package/src/table/table-header.spec.ts +54 -0
- package/src/table/table-header.ts +63 -0
- package/src/table/table-keyboard.spec.ts +68 -0
- package/src/table/table-keyboard.ts +107 -0
- package/src/table/table-resource.spec.ts +37 -0
- package/src/table/table-resource.ts +36 -0
- package/src/table/table-row.spec.ts +54 -0
- package/src/table/table-row.ts +31 -0
- package/src/table/table-storage.spec.ts +67 -0
- package/src/table/table-storage.ts +76 -0
- package/src/table/table.spec.ts +102 -43
- package/src/table/table.ts +228 -73
- package/src/table/table.types.ts +83 -0
- package/src/tabs/index.ts +7 -2
- package/src/tabs/tabs.context.ts +27 -4
- package/src/tabs/tabs.spec.ts +336 -24
- package/src/tabs/tabs.ts +313 -79
- package/src/tag/config.ts +53 -0
- package/src/tag/index.ts +7 -0
- package/src/tag/tag-list.ts +76 -0
- package/src/tag/tag-remove.ts +61 -0
- package/src/tag/tag.context.ts +36 -0
- package/src/tag/tag.spec.ts +240 -0
- package/src/tag/tag.ts +203 -0
- package/src/textarea/config.ts +42 -0
- package/src/textarea/index.ts +8 -0
- package/src/textarea/textarea.spec.ts +176 -0
- package/src/textarea/textarea.ts +235 -0
- package/src/time-picker/index.ts +15 -0
- package/src/time-picker/time-picker-meridiem.ts +123 -0
- package/src/time-picker/time-picker-segment.ts +318 -0
- package/src/time-picker/time-picker.context.ts +38 -0
- package/src/time-picker/time-picker.format.ts +181 -0
- package/src/time-picker/time-picker.spec.ts +189 -0
- package/src/time-picker/time-picker.ts +274 -0
- package/src/toast/_examples/index.ts +5 -0
- package/src/toast/_examples/toast.example.ts +282 -0
- package/src/toast/_examples/toast.finance.example.ts +180 -0
- package/src/toast/_examples/toast.retro.example.ts +161 -0
- package/src/toast/index.ts +8 -1
- package/src/toast/toast.ref.ts +26 -0
- package/src/toast/toast.service.ts +227 -41
- package/src/toast/toast.spec.ts +178 -25
- package/src/toast/toast.strategy.ts +10 -0
- package/src/toast/toast.ts +178 -22
- package/src/toggle/toggle.ts +10 -2
- package/src/tooltip/_examples/index.ts +6 -0
- package/src/tooltip/_examples/tooltip.example.ts +82 -0
- package/src/tooltip/_examples/tooltip.finance.example.ts +97 -0
- package/src/tooltip/_examples/tooltip.placements.example.ts +83 -0
- package/src/tooltip/_examples/tooltip.retro.example.ts +107 -0
- package/src/tooltip/index.ts +4 -5
- package/src/tooltip/tooltip-arrow.ts +31 -0
- package/src/tooltip/tooltip-content.ts +38 -0
- package/src/tooltip/tooltip-group.ts +32 -0
- package/src/tooltip/tooltip-trigger.ts +47 -0
- package/src/tooltip/tooltip.spec.ts +38 -102
- package/src/tooltip/tooltip.ts +5 -129
- package/src/tree-select/index.ts +12 -0
- package/src/tree-select/tree-select-content.ts +199 -0
- package/src/tree-select/tree-select-node.ts +132 -0
- package/src/tree-select/tree-select-root.ts +272 -0
- package/src/tree-select/tree-select-trigger.ts +90 -0
- package/src/tree-select/tree-select.context.ts +63 -0
- package/src/tree-select/tree-select.spec.ts +433 -0
- package/src/tree-select/tree-select.ts +2 -0
- package/src/typography/blockquote.ts +57 -0
- package/src/typography/code.ts +53 -0
- package/src/typography/index.ts +22 -0
- package/src/typography/lead.ts +49 -0
- package/src/typography/muted.ts +29 -0
- package/src/typography/prose.css +312 -0
- package/src/typography/truncate.ts +99 -0
- package/src/typography/typography.spec.ts +203 -0
- package/tsconfig.lib.json +9 -1
- package/tsconfig.lib.prod.json +7 -1
- package/tsconfig.spec.json +9 -1
- package/vite.config.ts +8 -0
- package/src/button/button.example.ts +0 -30
- package/src/button/button.finance.example.ts +0 -37
- package/src/button/button.retro.example.ts +0 -47
- package/src/button/button.sizes.example.ts +0 -25
- package/src/dialog/dialog.confirm.example.ts +0 -57
- package/src/dialog/dialog.context.ts +0 -11
- package/src/dialog/dialog.example.ts +0 -40
- package/src/dialog/dialog.finance.example.ts +0 -47
- package/src/dialog/dialog.retro.example.ts +0 -46
- package/src/menu/index.ts +0 -2
- package/src/menu/menu.context.ts +0 -10
- package/src/menu/menu.example.ts +0 -39
- package/src/menu/menu.spec.ts +0 -168
- package/src/menu/menu.ts +0 -176
- package/src/popover/popover.context.ts +0 -18
- package/src/popover/popover.example.ts +0 -50
- package/src/popover/popover.finance.example.ts +0 -52
- package/src/popover/popover.retro.example.ts +0 -53
- package/src/primitives/overlay/overlay.spec.ts +0 -90
- package/src/primitives/overlay/overlay.ts +0 -102
- package/src/select/select.context.ts +0 -3
- package/src/toast/toast.example.ts +0 -134
- package/src/toast/toast.finance.example.ts +0 -68
- package/src/toast/toast.retro.example.ts +0 -69
- package/src/tooltip/tooltip.example.ts +0 -46
- package/src/tooltip/tooltip.finance.example.ts +0 -54
- package/src/tooltip/tooltip.placements.example.ts +0 -56
- package/src/tooltip/tooltip.retro.example.ts +0 -65
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { DestroyRef, Directive, computed, inject, input } from '@angular/core';
|
|
2
|
+
import { KJ_CHAT, nextChatFooterId, type KjChatState } from './chat.context';
|
|
3
|
+
|
|
4
|
+
const STATE_LABEL: Readonly<Record<KjChatState, string>> = {
|
|
5
|
+
sending: 'Sending',
|
|
6
|
+
sent: 'Sent',
|
|
7
|
+
delivered: 'Delivered',
|
|
8
|
+
read: 'Read',
|
|
9
|
+
error: 'Failed to send',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Mints a stable id for the chat row's footer and registers it with the
|
|
14
|
+
* parent `KjChat` so the row's host `aria-describedby` references it.
|
|
15
|
+
*
|
|
16
|
+
* Reflects `kjState` to a `data-state` host attribute that components-package
|
|
17
|
+
* CSS reads to paint a status glyph (clock / single-check / double-check /
|
|
18
|
+
* filled-double-check / exclamation). Also injects an `aria-label` so the
|
|
19
|
+
* description is intelligible to AT even when the glyph is the only visible
|
|
20
|
+
* content.
|
|
21
|
+
*
|
|
22
|
+
* Free-form text ("Seen 12:46") is fine too — leave `kjState` undefined and
|
|
23
|
+
* the directive renders only the consumer's projected content.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```html
|
|
27
|
+
* <footer kjChatFooter kjState="read">Read 12:46</footer>
|
|
28
|
+
* ```
|
|
29
|
+
* @doc-category Core/Data display
|
|
30
|
+
* @doc
|
|
31
|
+
* @doc-name chat
|
|
32
|
+
*/
|
|
33
|
+
@Directive({
|
|
34
|
+
selector: '[kjChatFooter]',
|
|
35
|
+
standalone: true,
|
|
36
|
+
host: {
|
|
37
|
+
'[id]': 'id',
|
|
38
|
+
'[attr.data-state]': 'kjState() ?? null',
|
|
39
|
+
'[attr.aria-label]': 'ariaLabel()',
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
export class KjChatFooter {
|
|
43
|
+
private readonly chat = inject(KJ_CHAT, { optional: true });
|
|
44
|
+
private readonly destroyRef = inject(DestroyRef);
|
|
45
|
+
|
|
46
|
+
/** Auto-generated id used for the parent row's `aria-describedby`. */
|
|
47
|
+
readonly id = nextChatFooterId();
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Read / delivery state. Drives `data-state` for the glyph and an injected
|
|
51
|
+
* `aria-label` for AT. When undefined, only projected content renders.
|
|
52
|
+
*/
|
|
53
|
+
readonly kjState = input<KjChatState | undefined>(undefined);
|
|
54
|
+
|
|
55
|
+
/** Aria label derived from `kjState`. `null` when no state is set. */
|
|
56
|
+
readonly ariaLabel = computed(() => {
|
|
57
|
+
const s = this.kjState();
|
|
58
|
+
return s ? STATE_LABEL[s] : null;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
constructor() {
|
|
62
|
+
if (this.chat) {
|
|
63
|
+
this.chat.registerFooterId(this.id);
|
|
64
|
+
this.destroyRef.onDestroy(() => this.chat?.unregisterFooterId(this.id));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DestroyRef, Directive, inject } from '@angular/core';
|
|
2
|
+
import { KJ_CHAT, nextChatHeaderId } from './chat.context';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mints a stable id for the chat row's header and registers it with the
|
|
6
|
+
* parent `KjChat` so the row's host `aria-labelledby` attribute references
|
|
7
|
+
* the sender's name + time. Visual chrome (small font, muted colour, spacing
|
|
8
|
+
* above the bubble) belongs to CSS; the directive does not paint.
|
|
9
|
+
*
|
|
10
|
+
* Standalone (no `KjChat` parent) the directive still mints an id but no
|
|
11
|
+
* wiring fires — the consumer is responsible for the link.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```html
|
|
15
|
+
* <header kjChatHeader>Alice <time datetime="…">12:46</time></header>
|
|
16
|
+
* ```
|
|
17
|
+
* @doc-category Core/Data display
|
|
18
|
+
* @doc
|
|
19
|
+
* @doc-name chat
|
|
20
|
+
*/
|
|
21
|
+
@Directive({
|
|
22
|
+
selector: '[kjChatHeader]',
|
|
23
|
+
standalone: true,
|
|
24
|
+
host: {
|
|
25
|
+
'[id]': 'id',
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
export class KjChatHeader {
|
|
29
|
+
private readonly chat = inject(KJ_CHAT, { optional: true });
|
|
30
|
+
private readonly destroyRef = inject(DestroyRef);
|
|
31
|
+
|
|
32
|
+
/** Auto-generated id used for the parent row's `aria-labelledby`. */
|
|
33
|
+
readonly id = nextChatHeaderId();
|
|
34
|
+
|
|
35
|
+
constructor() {
|
|
36
|
+
if (this.chat) {
|
|
37
|
+
this.chat.registerHeaderId(this.id);
|
|
38
|
+
this.destroyRef.onDestroy(() => this.chat?.unregisterHeaderId(this.id));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Directive, input } from '@angular/core';
|
|
2
|
+
import { KJ_CHAT_LOG, type KjChatLogContext } from './chat.context';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Container for a stream of `[kjChat]` rows. Owns the live region: per APG's
|
|
6
|
+
* Log pattern, the **container** announces new arrivals — never the bubble
|
|
7
|
+
* itself. Default `aria-live="polite"` matches the chat-app convention; we
|
|
8
|
+
* deliberately do not expose `'assertive'` (it would interrupt the user
|
|
9
|
+
* mid-typing on every inbound message and is the canonical chat-app a11y bug).
|
|
10
|
+
*
|
|
11
|
+
* `aria-relevant="additions"` confines announcements to *new* messages so that
|
|
12
|
+
* edits to existing bubbles don't re-announce the full edited content.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```html
|
|
16
|
+
* <div kjChatLog kjChatLogLabel="Conversation with Alice">
|
|
17
|
+
* <div kjChat kjSide="start" kjChatAuthor="alice">…</div>
|
|
18
|
+
* <div kjChat kjSide="end" kjChatAuthor="me">…</div>
|
|
19
|
+
* </div>
|
|
20
|
+
* ```
|
|
21
|
+
* @doc-category Core/Data display
|
|
22
|
+
* @doc
|
|
23
|
+
* @doc-name chat
|
|
24
|
+
*/
|
|
25
|
+
@Directive({
|
|
26
|
+
selector: '[kjChatLog]',
|
|
27
|
+
standalone: true,
|
|
28
|
+
providers: [{ provide: KJ_CHAT_LOG, useExisting: KjChatLog }],
|
|
29
|
+
host: {
|
|
30
|
+
'role': 'log',
|
|
31
|
+
'[attr.aria-live]': 'kjChatLogLive()',
|
|
32
|
+
'[attr.aria-relevant]': '"additions"',
|
|
33
|
+
'[attr.aria-atomic]': '"false"',
|
|
34
|
+
'[attr.aria-label]': 'kjChatLogLabel() ?? null',
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
export class KjChatLog implements KjChatLogContext {
|
|
38
|
+
/** Live-region politeness. `'polite'` (default) or `'off'`. */
|
|
39
|
+
readonly kjChatLogLive = input<'polite' | 'off'>('polite');
|
|
40
|
+
|
|
41
|
+
/** Optional `aria-label` for the log landmark. */
|
|
42
|
+
readonly kjChatLogLabel = input<string | undefined>(undefined);
|
|
43
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { InjectionToken, type Signal } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
/** Logical side of a chat row. CSS handles the physical RTL flip via cascade. */
|
|
4
|
+
export type KjChatSide = 'start' | 'end';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Per-row role override. Default `'article'` gives screen readers a per-message
|
|
8
|
+
* landmark; `'listitem'` opts in to list semantics (parent log must then carry
|
|
9
|
+
* `role="list"`); `null` drops the role attribute entirely.
|
|
10
|
+
*/
|
|
11
|
+
export type KjChatRole = 'article' | 'listitem' | null;
|
|
12
|
+
|
|
13
|
+
/** Read / delivery state machine for the footer. */
|
|
14
|
+
export type KjChatState =
|
|
15
|
+
| 'sending'
|
|
16
|
+
| 'sent'
|
|
17
|
+
| 'delivered'
|
|
18
|
+
| 'read'
|
|
19
|
+
| 'error';
|
|
20
|
+
|
|
21
|
+
/** Context shape exposed by `KjChatLog` to descendant chat directives. */
|
|
22
|
+
export interface KjChatLogContext {
|
|
23
|
+
/** Reflected to `aria-live`. `'polite'` (default) or `'off'`. */
|
|
24
|
+
readonly kjChatLogLive: Signal<'polite' | 'off'>;
|
|
25
|
+
/** Optional landmark label for the log. */
|
|
26
|
+
readonly kjChatLogLabel: Signal<string | undefined>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Context shape exposed by `KjChat` to its child directives. Children read this
|
|
31
|
+
* to register their minted ids (header / footer) for `aria-labelledby` /
|
|
32
|
+
* `aria-describedby` wiring, and to ask whether the row is part of a grouped
|
|
33
|
+
* run for tail / avatar suppression.
|
|
34
|
+
*/
|
|
35
|
+
export interface KjChatContext {
|
|
36
|
+
readonly kjSide: Signal<KjChatSide>;
|
|
37
|
+
readonly kjChatAuthor: Signal<string | undefined>;
|
|
38
|
+
/** True when this row is grouped with the previous row (same author). */
|
|
39
|
+
readonly grouped: Signal<boolean>;
|
|
40
|
+
/** Register a header id; the row reflects it as `aria-labelledby`. */
|
|
41
|
+
registerHeaderId(id: string): void;
|
|
42
|
+
unregisterHeaderId(id: string): void;
|
|
43
|
+
/** Register a footer id; the row reflects it as `aria-describedby`. */
|
|
44
|
+
registerFooterId(id: string): void;
|
|
45
|
+
unregisterFooterId(id: string): void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Injection token for the `KjChatLog` container. */
|
|
49
|
+
export const KJ_CHAT_LOG = new InjectionToken<KjChatLogContext>('KjChatLog');
|
|
50
|
+
|
|
51
|
+
/** Injection token for the `KjChat` row. */
|
|
52
|
+
export const KJ_CHAT = new InjectionToken<KjChatContext>('KjChat');
|
|
53
|
+
|
|
54
|
+
let _chatHeaderIdCounter = 0;
|
|
55
|
+
/** Allocate a stable id used for `aria-labelledby` wiring. */
|
|
56
|
+
export function nextChatHeaderId(): string {
|
|
57
|
+
return `kj-chat-header-${++_chatHeaderIdCounter}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let _chatFooterIdCounter = 0;
|
|
61
|
+
/** Allocate a stable id used for `aria-describedby` wiring. */
|
|
62
|
+
export function nextChatFooterId(): string {
|
|
63
|
+
return `kj-chat-footer-${++_chatFooterIdCounter}`;
|
|
64
|
+
}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { render } from '@testing-library/angular';
|
|
2
|
+
import { axe, toHaveNoViolations } from 'jest-axe';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { KjChat } from './chat';
|
|
5
|
+
import { KjChatAvatar } from './chat-avatar';
|
|
6
|
+
import { KjChatBubble } from './chat-bubble';
|
|
7
|
+
import { KjChatFooter } from './chat-footer';
|
|
8
|
+
import { KjChatHeader } from './chat-header';
|
|
9
|
+
import { KjChatLog } from './chat-log';
|
|
10
|
+
import { provideKjChatBubble } from './config';
|
|
11
|
+
|
|
12
|
+
expect.extend(toHaveNoViolations);
|
|
13
|
+
|
|
14
|
+
const imports = [
|
|
15
|
+
KjChatLog,
|
|
16
|
+
KjChat,
|
|
17
|
+
KjChatAvatar,
|
|
18
|
+
KjChatHeader,
|
|
19
|
+
KjChatBubble,
|
|
20
|
+
KjChatFooter,
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
describe('KjChatLog', () => {
|
|
24
|
+
it('hosts role=log + aria-live=polite by default', async () => {
|
|
25
|
+
const { container } = await render(
|
|
26
|
+
`<div kjChatLog data-testid="log"></div>`,
|
|
27
|
+
{ imports },
|
|
28
|
+
);
|
|
29
|
+
const log = container.querySelector('[data-testid="log"]')!;
|
|
30
|
+
expect(log).toHaveAttribute('role', 'log');
|
|
31
|
+
expect(log).toHaveAttribute('aria-live', 'polite');
|
|
32
|
+
expect(log).toHaveAttribute('aria-relevant', 'additions');
|
|
33
|
+
expect(log).toHaveAttribute('aria-atomic', 'false');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('reflects kjChatLogLive=off', async () => {
|
|
37
|
+
const { container } = await render(
|
|
38
|
+
`<div kjChatLog kjChatLogLive="off" data-testid="log"></div>`,
|
|
39
|
+
{ imports },
|
|
40
|
+
);
|
|
41
|
+
expect(container.querySelector('[data-testid="log"]')).toHaveAttribute(
|
|
42
|
+
'aria-live',
|
|
43
|
+
'off',
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('reflects kjChatLogLabel as aria-label', async () => {
|
|
48
|
+
const { container } = await render(
|
|
49
|
+
`<div kjChatLog kjChatLogLabel="Conversation with Alice" data-testid="log"></div>`,
|
|
50
|
+
{ imports },
|
|
51
|
+
);
|
|
52
|
+
expect(container.querySelector('[data-testid="log"]')).toHaveAttribute(
|
|
53
|
+
'aria-label',
|
|
54
|
+
'Conversation with Alice',
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('KjChat', () => {
|
|
60
|
+
it('default role is article', async () => {
|
|
61
|
+
const { container } = await render(
|
|
62
|
+
`<div kjChat data-testid="row"></div>`,
|
|
63
|
+
{ imports },
|
|
64
|
+
);
|
|
65
|
+
expect(container.querySelector('[data-testid="row"]')).toHaveAttribute(
|
|
66
|
+
'role',
|
|
67
|
+
'article',
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('kjRole=listitem reflects on host', async () => {
|
|
72
|
+
const { container } = await render(
|
|
73
|
+
`<div kjChat kjRole="listitem" data-testid="row"></div>`,
|
|
74
|
+
{ imports },
|
|
75
|
+
);
|
|
76
|
+
expect(container.querySelector('[data-testid="row"]')).toHaveAttribute(
|
|
77
|
+
'role',
|
|
78
|
+
'listitem',
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('kjRole=null drops the role attribute', async () => {
|
|
83
|
+
const { container } = await render(
|
|
84
|
+
`<div kjChat [kjRole]="null" data-testid="row"></div>`,
|
|
85
|
+
{ imports },
|
|
86
|
+
);
|
|
87
|
+
expect(
|
|
88
|
+
container.querySelector('[data-testid="row"]'),
|
|
89
|
+
).not.toHaveAttribute('role');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('kjSide reflects to data-side', async () => {
|
|
93
|
+
const { container } = await render(
|
|
94
|
+
`<div kjChat kjSide="end" data-testid="row"></div>`,
|
|
95
|
+
{ imports },
|
|
96
|
+
);
|
|
97
|
+
expect(container.querySelector('[data-testid="row"]')).toHaveAttribute(
|
|
98
|
+
'data-side',
|
|
99
|
+
'end',
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('auto-grouping marks the second same-author row with data-grouped', async () => {
|
|
104
|
+
const { container, fixture } = await render(
|
|
105
|
+
`<div kjChatLog>
|
|
106
|
+
<div kjChat kjChatAuthor="alice" data-testid="r1"></div>
|
|
107
|
+
<div kjChat kjChatAuthor="alice" data-testid="r2"></div>
|
|
108
|
+
<div kjChat kjChatAuthor="bob" data-testid="r3"></div>
|
|
109
|
+
</div>`,
|
|
110
|
+
{ imports },
|
|
111
|
+
);
|
|
112
|
+
fixture.detectChanges();
|
|
113
|
+
await fixture.whenStable();
|
|
114
|
+
fixture.detectChanges();
|
|
115
|
+
expect(
|
|
116
|
+
container.querySelector('[data-testid="r1"]'),
|
|
117
|
+
).not.toHaveAttribute('data-grouped');
|
|
118
|
+
expect(container.querySelector('[data-testid="r2"]')).toHaveAttribute(
|
|
119
|
+
'data-grouped',
|
|
120
|
+
'',
|
|
121
|
+
);
|
|
122
|
+
expect(
|
|
123
|
+
container.querySelector('[data-testid="r3"]'),
|
|
124
|
+
).not.toHaveAttribute('data-grouped');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('explicit kjChatGrouped overrides auto-grouping', async () => {
|
|
128
|
+
const { container, fixture } = await render(
|
|
129
|
+
`<div kjChatLog>
|
|
130
|
+
<div kjChat kjChatAuthor="alice" data-testid="r1"></div>
|
|
131
|
+
<div kjChat kjChatAuthor="bob" [kjChatGrouped]="true" data-testid="r2"></div>
|
|
132
|
+
</div>`,
|
|
133
|
+
{ imports },
|
|
134
|
+
);
|
|
135
|
+
fixture.detectChanges();
|
|
136
|
+
await fixture.whenStable();
|
|
137
|
+
expect(container.querySelector('[data-testid="r2"]')).toHaveAttribute(
|
|
138
|
+
'data-grouped',
|
|
139
|
+
'',
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('header/footer ids wire aria-labelledby and aria-describedby on the row', async () => {
|
|
144
|
+
const { container } = await render(
|
|
145
|
+
`<div kjChat data-testid="row">
|
|
146
|
+
<header kjChatHeader data-testid="h">Alice 12:46</header>
|
|
147
|
+
<p kjChatBubble>Hi</p>
|
|
148
|
+
<footer kjChatFooter data-testid="f">Sent</footer>
|
|
149
|
+
</div>`,
|
|
150
|
+
{ imports },
|
|
151
|
+
);
|
|
152
|
+
const row = container.querySelector('[data-testid="row"]')!;
|
|
153
|
+
const header = container.querySelector('[data-testid="h"]')!;
|
|
154
|
+
const footer = container.querySelector('[data-testid="f"]')!;
|
|
155
|
+
expect(header.id).toMatch(/^kj-chat-header-\d+$/);
|
|
156
|
+
expect(footer.id).toMatch(/^kj-chat-footer-\d+$/);
|
|
157
|
+
expect(row.getAttribute('aria-labelledby')).toBe(header.id);
|
|
158
|
+
expect(row.getAttribute('aria-describedby')).toBe(footer.id);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('KjChatAvatar', () => {
|
|
163
|
+
it('forces aria-hidden=true', async () => {
|
|
164
|
+
const { container } = await render(
|
|
165
|
+
`<span kjChatAvatar data-testid="av"></span>`,
|
|
166
|
+
{ imports },
|
|
167
|
+
);
|
|
168
|
+
const av = container.querySelector('[data-testid="av"]')!;
|
|
169
|
+
expect(av).toHaveAttribute('aria-hidden', 'true');
|
|
170
|
+
expect(av).toHaveAttribute('role', 'presentation');
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('KjChatHeader / KjChatFooter', () => {
|
|
175
|
+
it('mint unique ids for two adjacent headers / footers', async () => {
|
|
176
|
+
const { container } = await render(
|
|
177
|
+
`<div kjChatLog>
|
|
178
|
+
<div kjChat>
|
|
179
|
+
<header kjChatHeader data-testid="h1">A</header>
|
|
180
|
+
<footer kjChatFooter data-testid="f1">x</footer>
|
|
181
|
+
</div>
|
|
182
|
+
<div kjChat>
|
|
183
|
+
<header kjChatHeader data-testid="h2">B</header>
|
|
184
|
+
<footer kjChatFooter data-testid="f2">y</footer>
|
|
185
|
+
</div>
|
|
186
|
+
</div>`,
|
|
187
|
+
{ imports },
|
|
188
|
+
);
|
|
189
|
+
const h1 = container.querySelector('[data-testid="h1"]')!.id;
|
|
190
|
+
const h2 = container.querySelector('[data-testid="h2"]')!.id;
|
|
191
|
+
const f1 = container.querySelector('[data-testid="f1"]')!.id;
|
|
192
|
+
const f2 = container.querySelector('[data-testid="f2"]')!.id;
|
|
193
|
+
expect(h1).not.toEqual(h2);
|
|
194
|
+
expect(f1).not.toEqual(f2);
|
|
195
|
+
expect(h1).toMatch(/^kj-chat-header-/);
|
|
196
|
+
expect(f1).toMatch(/^kj-chat-footer-/);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('footer reflects kjState as data-state and aria-label', async () => {
|
|
200
|
+
const { container } = await render(
|
|
201
|
+
`<footer kjChatFooter kjState="read" data-testid="f">Read 12:46</footer>`,
|
|
202
|
+
{ imports },
|
|
203
|
+
);
|
|
204
|
+
const f = container.querySelector('[data-testid="f"]')!;
|
|
205
|
+
expect(f).toHaveAttribute('data-state', 'read');
|
|
206
|
+
expect(f).toHaveAttribute('aria-label', 'Read');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('footer omits data-state and aria-label when kjState is unset', async () => {
|
|
210
|
+
const { container } = await render(
|
|
211
|
+
`<footer kjChatFooter data-testid="f">Sent 12:46</footer>`,
|
|
212
|
+
{ imports },
|
|
213
|
+
);
|
|
214
|
+
const f = container.querySelector('[data-testid="f"]')!;
|
|
215
|
+
expect(f).not.toHaveAttribute('data-state');
|
|
216
|
+
expect(f).not.toHaveAttribute('aria-label');
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe('KjChatBubble', () => {
|
|
221
|
+
it('composes KjVariant -> data-variant default', async () => {
|
|
222
|
+
const { container } = await render(
|
|
223
|
+
`<p kjChatBubble data-testid="b">Hi</p>`,
|
|
224
|
+
{ imports },
|
|
225
|
+
);
|
|
226
|
+
expect(container.querySelector('[data-testid="b"]')).toHaveAttribute(
|
|
227
|
+
'data-variant',
|
|
228
|
+
'default',
|
|
229
|
+
);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('composes KjSize -> data-size default', async () => {
|
|
233
|
+
const { container } = await render(
|
|
234
|
+
`<p kjChatBubble data-testid="b">Hi</p>`,
|
|
235
|
+
{ imports },
|
|
236
|
+
);
|
|
237
|
+
expect(container.querySelector('[data-testid="b"]')).toHaveAttribute(
|
|
238
|
+
'data-size',
|
|
239
|
+
'md',
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('forwards kjVariant + kjSize through hostDirectives', async () => {
|
|
244
|
+
const { container } = await render(
|
|
245
|
+
`<p kjChatBubble kjVariant="primary" kjSize="lg" data-testid="b">Hi</p>`,
|
|
246
|
+
{ imports },
|
|
247
|
+
);
|
|
248
|
+
const b = container.querySelector('[data-testid="b"]')!;
|
|
249
|
+
expect(b).toHaveAttribute('data-variant', 'primary');
|
|
250
|
+
expect(b).toHaveAttribute('data-size', 'lg');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('renders data-tail when not grouped and no-tail not set', async () => {
|
|
254
|
+
const { container } = await render(
|
|
255
|
+
`<div kjChat>
|
|
256
|
+
<p kjChatBubble data-testid="b">Hi</p>
|
|
257
|
+
</div>`,
|
|
258
|
+
{ imports },
|
|
259
|
+
);
|
|
260
|
+
expect(container.querySelector('[data-testid="b"]')).toHaveAttribute(
|
|
261
|
+
'data-tail',
|
|
262
|
+
'',
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('omits data-tail when kjChatBubbleNoTail=true', async () => {
|
|
267
|
+
const { container } = await render(
|
|
268
|
+
`<div kjChat>
|
|
269
|
+
<p kjChatBubble [kjChatBubbleNoTail]="true" data-testid="b">Hi</p>
|
|
270
|
+
</div>`,
|
|
271
|
+
{ imports },
|
|
272
|
+
);
|
|
273
|
+
expect(
|
|
274
|
+
container.querySelector('[data-testid="b"]'),
|
|
275
|
+
).not.toHaveAttribute('data-tail');
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('omits data-tail when the parent row is auto-grouped', async () => {
|
|
279
|
+
const { container, fixture } = await render(
|
|
280
|
+
`<div kjChatLog>
|
|
281
|
+
<div kjChat kjChatAuthor="alice">
|
|
282
|
+
<p kjChatBubble data-testid="b1">A</p>
|
|
283
|
+
</div>
|
|
284
|
+
<div kjChat kjChatAuthor="alice">
|
|
285
|
+
<p kjChatBubble data-testid="b2">B</p>
|
|
286
|
+
</div>
|
|
287
|
+
</div>`,
|
|
288
|
+
{ imports },
|
|
289
|
+
);
|
|
290
|
+
fixture.detectChanges();
|
|
291
|
+
await fixture.whenStable();
|
|
292
|
+
fixture.detectChanges();
|
|
293
|
+
expect(container.querySelector('[data-testid="b1"]')).toHaveAttribute(
|
|
294
|
+
'data-tail',
|
|
295
|
+
'',
|
|
296
|
+
);
|
|
297
|
+
expect(
|
|
298
|
+
container.querySelector('[data-testid="b2"]'),
|
|
299
|
+
).not.toHaveAttribute('data-tail');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('respects provideKjChatBubble preset overrides', async () => {
|
|
303
|
+
const { container } = await render(
|
|
304
|
+
`<p kjChatBubble data-testid="b">x</p>`,
|
|
305
|
+
{
|
|
306
|
+
imports,
|
|
307
|
+
providers: [
|
|
308
|
+
provideKjChatBubble({
|
|
309
|
+
variants: ['default', 'primary', 'brand'],
|
|
310
|
+
defaults: { variant: 'brand', size: 'md' },
|
|
311
|
+
}),
|
|
312
|
+
],
|
|
313
|
+
},
|
|
314
|
+
);
|
|
315
|
+
expect(container.querySelector('[data-testid="b"]')).toHaveAttribute(
|
|
316
|
+
'data-variant',
|
|
317
|
+
'brand',
|
|
318
|
+
);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
describe('a11y', () => {
|
|
323
|
+
it('passes axe audit on a populated chat log', async () => {
|
|
324
|
+
const { container } = await render(
|
|
325
|
+
`<div kjChatLog kjChatLogLabel="Test conversation">
|
|
326
|
+
<div kjChat kjSide="start" kjChatAuthor="alice">
|
|
327
|
+
<span kjChatAvatar></span>
|
|
328
|
+
<header kjChatHeader>Alice 12:46</header>
|
|
329
|
+
<p kjChatBubble>Hi there</p>
|
|
330
|
+
<footer kjChatFooter kjState="read">Read 12:46</footer>
|
|
331
|
+
</div>
|
|
332
|
+
<div kjChat kjSide="end" kjChatAuthor="me">
|
|
333
|
+
<p kjChatBubble kjVariant="primary">Hello back</p>
|
|
334
|
+
<footer kjChatFooter kjState="delivered">Delivered</footer>
|
|
335
|
+
</div>
|
|
336
|
+
</div>`,
|
|
337
|
+
{ imports },
|
|
338
|
+
);
|
|
339
|
+
expect(await axe(container)).toHaveNoViolations();
|
|
340
|
+
});
|
|
341
|
+
});
|