@12-apps/ui 1.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/components-catalog.md +806 -0
- package/components-guidelines.md +347 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/eslint.config.js +77 -0
- package/package.json +247 -0
- package/src/__tests__/package-wiring.test.ts +37 -0
- package/src/__tests__/setup.ts +1 -0
- package/src/__tests__/social-login-button.test.tsx +186 -0
- package/src/__tests__/user-avatar.test.tsx +195 -0
- package/src/button.tsx +22 -0
- package/src/components/command/__tests__/command.test.tsx +112 -0
- package/src/components/data-display/Alert/Alert.md +209 -0
- package/src/components/data-display/Alert/Alert.stories.tsx +561 -0
- package/src/components/data-display/Alert/Alert.test.stories.tsx +709 -0
- package/src/components/data-display/Alert/Alert.tsx +449 -0
- package/src/components/data-display/Alert/Alert.types.ts +79 -0
- package/src/components/data-display/Alert/index.ts +2 -0
- package/src/components/data-display/AlertDialog/AlertDialog.md +408 -0
- package/src/components/data-display/AlertDialog/AlertDialog.stories.tsx +360 -0
- package/src/components/data-display/AlertDialog/AlertDialog.test.stories.tsx +753 -0
- package/src/components/data-display/AlertDialog/AlertDialog.tsx +273 -0
- package/src/components/data-display/AlertDialog/AlertDialog.types.ts +74 -0
- package/src/components/data-display/AlertDialog/index.ts +2 -0
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.stories.tsx +230 -0
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.test.stories.tsx +270 -0
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.tsx +116 -0
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.types.ts +70 -0
- package/src/components/data-display/AsyncStateContainer/index.ts +2 -0
- package/src/components/data-display/Avatar/Avatar.md +568 -0
- package/src/components/data-display/Avatar/Avatar.stories.tsx +718 -0
- package/src/components/data-display/Avatar/Avatar.test.stories.tsx +820 -0
- package/src/components/data-display/Avatar/Avatar.tsx +507 -0
- package/src/components/data-display/Avatar/Avatar.types.ts +88 -0
- package/src/components/data-display/Avatar/index.ts +2 -0
- package/src/components/data-display/Badge/Badge.md +469 -0
- package/src/components/data-display/Badge/Badge.stories.tsx +1136 -0
- package/src/components/data-display/Badge/Badge.test.stories.tsx +1193 -0
- package/src/components/data-display/Badge/Badge.tsx +606 -0
- package/src/components/data-display/Badge/Badge.types.ts +118 -0
- package/src/components/data-display/Badge/index.ts +2 -0
- package/src/components/data-display/Banner/Banner.stories.tsx +524 -0
- package/src/components/data-display/Banner/Banner.test.stories.tsx +503 -0
- package/src/components/data-display/Banner/Banner.tsx +350 -0
- package/src/components/data-display/Banner/Banner.types.ts +41 -0
- package/src/components/data-display/Banner/banner.md +141 -0
- package/src/components/data-display/Banner/index.ts +2 -0
- package/src/components/data-display/Carousel/Carousel.md +815 -0
- package/src/components/data-display/Carousel/Carousel.stories.tsx +607 -0
- package/src/components/data-display/Carousel/Carousel.test.stories.tsx +691 -0
- package/src/components/data-display/Carousel/Carousel.tsx +765 -0
- package/src/components/data-display/Carousel/Carousel.types.ts +79 -0
- package/src/components/data-display/Carousel/index.ts +8 -0
- package/src/components/data-display/Chart/Chart.md +352 -0
- package/src/components/data-display/Chart/Chart.stories.tsx +568 -0
- package/src/components/data-display/Chart/Chart.test.stories.tsx +174 -0
- package/src/components/data-display/Chart/Chart.tsx +520 -0
- package/src/components/data-display/Chart/Chart.types.ts +65 -0
- package/src/components/data-display/Chart/index.ts +2 -0
- package/src/components/data-display/Chip/Chip.stories.tsx +251 -0
- package/src/components/data-display/Chip/Chip.test.stories.tsx +263 -0
- package/src/components/data-display/Chip/Chip.tsx +134 -0
- package/src/components/data-display/Chip/Chip.types.ts +51 -0
- package/src/components/data-display/Chip/chip.md +395 -0
- package/src/components/data-display/Chip/index.ts +2 -0
- package/src/components/data-display/DataGrid/DataGrid.md +602 -0
- package/src/components/data-display/DataGrid/DataGrid.stories.tsx +515 -0
- package/src/components/data-display/DataGrid/DataGrid.test.stories.tsx +659 -0
- package/src/components/data-display/DataGrid/DataGrid.tsx +743 -0
- package/src/components/data-display/DataGrid/DataGrid.types.ts +171 -0
- package/src/components/data-display/DataGrid/index.ts +21 -0
- package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.tsx +197 -0
- package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.types.ts +46 -0
- package/src/components/data-display/DateTimeDisplay/index.ts +2 -0
- package/src/components/data-display/DescriptionItem/DescriptionItem.md +226 -0
- package/src/components/data-display/DescriptionItem/DescriptionItem.stories.tsx +254 -0
- package/src/components/data-display/DescriptionItem/DescriptionItem.tsx +100 -0
- package/src/components/data-display/DescriptionItem/DescriptionItem.types.ts +1 -0
- package/src/components/data-display/DescriptionItem/index.ts +2 -0
- package/src/components/data-display/EmptyState/EmptyState.md +265 -0
- package/src/components/data-display/EmptyState/EmptyState.stories.tsx +282 -0
- package/src/components/data-display/EmptyState/EmptyState.test.stories.tsx +441 -0
- package/src/components/data-display/EmptyState/EmptyState.tsx +180 -0
- package/src/components/data-display/EmptyState/EmptyState.types.ts +31 -0
- package/src/components/data-display/EmptyState/index.ts +2 -0
- package/src/components/data-display/ErrorState/ErrorState.stories.tsx +110 -0
- package/src/components/data-display/ErrorState/ErrorState.test.stories.tsx +133 -0
- package/src/components/data-display/ErrorState/ErrorState.tsx +118 -0
- package/src/components/data-display/ErrorState/ErrorState.types.ts +45 -0
- package/src/components/data-display/ErrorState/index.ts +2 -0
- package/src/components/data-display/HoverCard/HoverCard.md +389 -0
- package/src/components/data-display/HoverCard/HoverCard.stories.tsx +648 -0
- package/src/components/data-display/HoverCard/HoverCard.test.stories.tsx +929 -0
- package/src/components/data-display/HoverCard/HoverCard.tsx +657 -0
- package/src/components/data-display/HoverCard/HoverCard.types.ts +116 -0
- package/src/components/data-display/HoverCard/index.ts +2 -0
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.md +532 -0
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.stories.tsx +411 -0
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.tsx +245 -0
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.types.ts +64 -0
- package/src/components/data-display/InteractiveTooltip/index.ts +2 -0
- package/src/components/data-display/LazyImage/LazyImage.md +356 -0
- package/src/components/data-display/LazyImage/LazyImage.stories.tsx +387 -0
- package/src/components/data-display/LazyImage/LazyImage.tsx +323 -0
- package/src/components/data-display/LazyImage/LazyImage.types.ts +205 -0
- package/src/components/data-display/LazyImage/index.ts +2 -0
- package/src/components/data-display/Lightbox/Lightbox.stories.tsx +315 -0
- package/src/components/data-display/Lightbox/Lightbox.test.stories.tsx +605 -0
- package/src/components/data-display/Lightbox/Lightbox.tsx +666 -0
- package/src/components/data-display/Lightbox/Lightbox.types.ts +49 -0
- package/src/components/data-display/Lightbox/index.ts +2 -0
- package/src/components/data-display/Lightbox/lightbox.md +298 -0
- package/src/components/data-display/LoadingState/LoadingState.stories.tsx +124 -0
- package/src/components/data-display/LoadingState/LoadingState.test.stories.tsx +99 -0
- package/src/components/data-display/LoadingState/LoadingState.tsx +103 -0
- package/src/components/data-display/LoadingState/LoadingState.types.ts +37 -0
- package/src/components/data-display/LoadingState/index.ts +2 -0
- package/src/components/data-display/MapPreview/MapPreview.md +154 -0
- package/src/components/data-display/MapPreview/MapPreview.stories.tsx +458 -0
- package/src/components/data-display/MapPreview/MapPreview.test.stories.tsx +390 -0
- package/src/components/data-display/MapPreview/MapPreview.tsx +922 -0
- package/src/components/data-display/MapPreview/MapPreview.types.ts +56 -0
- package/src/components/data-display/MapPreview/index.ts +2 -0
- package/src/components/data-display/Popover/Popover.md +245 -0
- package/src/components/data-display/Popover/Popover.stories.tsx +295 -0
- package/src/components/data-display/Popover/Popover.test.stories.tsx +1168 -0
- package/src/components/data-display/Popover/Popover.tsx +132 -0
- package/src/components/data-display/Popover/Popover.types.ts +33 -0
- package/src/components/data-display/Popover/index.ts +2 -0
- package/src/components/data-display/Progress/Progress.md +450 -0
- package/src/components/data-display/Progress/Progress.stories.tsx +481 -0
- package/src/components/data-display/Progress/Progress.test.stories.tsx +2070 -0
- package/src/components/data-display/Progress/Progress.tsx +332 -0
- package/src/components/data-display/Progress/Progress.types.ts +61 -0
- package/src/components/data-display/Progress/index.ts +2 -0
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.md +108 -0
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.stories.tsx +160 -0
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.test.stories.tsx +190 -0
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.tsx +224 -0
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.types.ts +101 -0
- package/src/components/data-display/SectionOnboarding/index.ts +7 -0
- package/src/components/data-display/Sheet/Sheet.md +475 -0
- package/src/components/data-display/Sheet/Sheet.stories.tsx +883 -0
- package/src/components/data-display/Sheet/Sheet.test.stories.tsx +1101 -0
- package/src/components/data-display/Sheet/Sheet.tsx +978 -0
- package/src/components/data-display/Sheet/Sheet.types.ts +93 -0
- package/src/components/data-display/Sheet/index.ts +8 -0
- package/src/components/data-display/Stepper/Stepper.md +542 -0
- package/src/components/data-display/Stepper/Stepper.stories.tsx +540 -0
- package/src/components/data-display/Stepper/Stepper.test.stories.tsx +607 -0
- package/src/components/data-display/Stepper/Stepper.tsx +328 -0
- package/src/components/data-display/Stepper/Stepper.types.ts +97 -0
- package/src/components/data-display/Stepper/index.ts +2 -0
- package/src/components/data-display/Table/Table.md +243 -0
- package/src/components/data-display/Table/Table.stories.tsx +801 -0
- package/src/components/data-display/Table/Table.test.stories.tsx +369 -0
- package/src/components/data-display/Table/Table.tsx +831 -0
- package/src/components/data-display/Table/Table.types.ts +335 -0
- package/src/components/data-display/Table/index.ts +2 -0
- package/src/components/data-display/Timeline/Timeline.md +201 -0
- package/src/components/data-display/Timeline/Timeline.stories.tsx +444 -0
- package/src/components/data-display/Timeline/Timeline.test.stories.tsx +944 -0
- package/src/components/data-display/Timeline/Timeline.tsx +348 -0
- package/src/components/data-display/Timeline/Timeline.types.ts +26 -0
- package/src/components/data-display/Timeline/index.ts +2 -0
- package/src/components/data-display/TimingDiagram/TimingDiagram.md +162 -0
- package/src/components/data-display/TimingDiagram/TimingDiagram.stories.tsx +475 -0
- package/src/components/data-display/TimingDiagram/TimingDiagram.test.stories.tsx +734 -0
- package/src/components/data-display/TimingDiagram/TimingDiagram.tsx +399 -0
- package/src/components/data-display/TimingDiagram/TimingDiagram.types.ts +19 -0
- package/src/components/data-display/TimingDiagram/index.ts +2 -0
- package/src/components/data-display/Tooltip/Tooltip.md +397 -0
- package/src/components/data-display/Tooltip/Tooltip.stories.tsx +465 -0
- package/src/components/data-display/Tooltip/Tooltip.test.stories.tsx +1168 -0
- package/src/components/data-display/Tooltip/Tooltip.tsx +161 -0
- package/src/components/data-display/Tooltip/Tooltip.types.ts +33 -0
- package/src/components/data-display/Tooltip/index.ts +2 -0
- package/src/components/data-display/index.ts +29 -0
- package/src/components/feedback/Dialog/Dialog.md +527 -0
- package/src/components/feedback/Dialog/Dialog.stories.tsx +833 -0
- package/src/components/feedback/Dialog/Dialog.styles.ts +171 -0
- package/src/components/feedback/Dialog/Dialog.test.stories.tsx +955 -0
- package/src/components/feedback/Dialog/Dialog.tsx +256 -0
- package/src/components/feedback/Dialog/Dialog.types.ts +46 -0
- package/src/components/feedback/Dialog/index.ts +9 -0
- package/src/components/feedback/Modal/Modal.md +629 -0
- package/src/components/feedback/Modal/Modal.stories.tsx +765 -0
- package/src/components/feedback/Modal/Modal.test.stories.tsx +251 -0
- package/src/components/feedback/Modal/Modal.tsx +279 -0
- package/src/components/feedback/Modal/Modal.types.ts +26 -0
- package/src/components/feedback/Modal/index.ts +7 -0
- package/src/components/feedback/Sonner/Sonner.stories.tsx +698 -0
- package/src/components/feedback/Sonner/Sonner.test.stories.tsx +896 -0
- package/src/components/feedback/Sonner/Sonner.tsx +493 -0
- package/src/components/feedback/Sonner/Sonner.types.ts +65 -0
- package/src/components/feedback/Sonner/index.ts +8 -0
- package/src/components/feedback/StackedModal/StackedModal.md +273 -0
- package/src/components/feedback/StackedModal/StackedModal.stories.tsx +1785 -0
- package/src/components/feedback/StackedModal/StackedModal.test.stories.tsx +975 -0
- package/src/components/feedback/StackedModal/StackedModal.tsx +664 -0
- package/src/components/feedback/StackedModal/StackedModal.types.ts +70 -0
- package/src/components/feedback/StackedModal/index.ts +11 -0
- package/src/components/feedback/Toast/Toast.md +547 -0
- package/src/components/feedback/Toast/Toast.stories.tsx +548 -0
- package/src/components/feedback/Toast/Toast.test.stories.tsx +834 -0
- package/src/components/feedback/Toast/Toast.tsx +279 -0
- package/src/components/feedback/Toast/Toast.types.ts +59 -0
- package/src/components/feedback/Toast/index.ts +8 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.md +610 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.stories.tsx +691 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.test.stories.tsx +634 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.tsx +554 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.types.ts +30 -0
- package/src/components/feedback/TutorialOverlay/index.ts +2 -0
- package/src/components/feedback/index.ts +6 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.md +633 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.stories.tsx +663 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.test.stories.tsx +723 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.tsx +836 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.types.ts +37 -0
- package/src/components/form/AddressAutocomplete/index.ts +2 -0
- package/src/components/form/Autocomplete/Autocomplete.helpers.ts +115 -0
- package/src/components/form/Autocomplete/Autocomplete.keys.ts +134 -0
- package/src/components/form/Autocomplete/Autocomplete.parts.tsx +337 -0
- package/src/components/form/Autocomplete/Autocomplete.stories.tsx +715 -0
- package/src/components/form/Autocomplete/Autocomplete.test.stories.tsx +825 -0
- package/src/components/form/Autocomplete/Autocomplete.tsx +166 -0
- package/src/components/form/Autocomplete/Autocomplete.types.ts +116 -0
- package/src/components/form/Autocomplete/autocomplete.md +789 -0
- package/src/components/form/Autocomplete/index.ts +8 -0
- package/src/components/form/Autocomplete/useAutocomplete.ts +311 -0
- package/src/components/form/Button/Button.md +411 -0
- package/src/components/form/Button/Button.stories.tsx +362 -0
- package/src/components/form/Button/Button.test.stories.tsx +603 -0
- package/src/components/form/Button/Button.tsx +326 -0
- package/src/components/form/Button/Button.types.ts +74 -0
- package/src/components/form/Button/index.ts +2 -0
- package/src/components/form/Calendar/Calendar.md +807 -0
- package/src/components/form/Calendar/Calendar.stories.tsx +349 -0
- package/src/components/form/Calendar/Calendar.test.stories.tsx +465 -0
- package/src/components/form/Calendar/Calendar.tsx +571 -0
- package/src/components/form/Calendar/Calendar.types.ts +81 -0
- package/src/components/form/Calendar/index.ts +11 -0
- package/src/components/form/Checkbox/Checkbox.md +200 -0
- package/src/components/form/Checkbox/Checkbox.stories.tsx +265 -0
- package/src/components/form/Checkbox/Checkbox.test.stories.tsx +181 -0
- package/src/components/form/Checkbox/Checkbox.tsx +170 -0
- package/src/components/form/Checkbox/Checkbox.types.ts +20 -0
- package/src/components/form/Checkbox/index.ts +2 -0
- package/src/components/form/CodeEditor/CodeEditor.md +350 -0
- package/src/components/form/CodeEditor/CodeEditor.stories.tsx +871 -0
- package/src/components/form/CodeEditor/CodeEditor.test.stories.tsx +575 -0
- package/src/components/form/CodeEditor/CodeEditor.tsx +415 -0
- package/src/components/form/CodeEditor/CodeEditor.types.ts +38 -0
- package/src/components/form/CodeEditor/index.ts +2 -0
- package/src/components/form/Command/Command.md +468 -0
- package/src/components/form/Command/Command.stories.tsx +580 -0
- package/src/components/form/Command/Command.test.stories.tsx +215 -0
- package/src/components/form/Command/Command.tsx +545 -0
- package/src/components/form/Command/Command.types.ts +116 -0
- package/src/components/form/Command/index.ts +12 -0
- package/src/components/form/CreatableSelect/CreatableSelect.md +114 -0
- package/src/components/form/CreatableSelect/CreatableSelect.stories.tsx +112 -0
- package/src/components/form/CreatableSelect/CreatableSelect.test.stories.tsx +134 -0
- package/src/components/form/CreatableSelect/CreatableSelect.tsx +149 -0
- package/src/components/form/CreatableSelect/CreatableSelect.types.ts +37 -0
- package/src/components/form/CreatableSelect/index.ts +2 -0
- package/src/components/form/Form/Form.md +325 -0
- package/src/components/form/Form/Form.stories.tsx +687 -0
- package/src/components/form/Form/Form.test.stories.tsx +601 -0
- package/src/components/form/Form/Form.tsx +136 -0
- package/src/components/form/Form/Form.types.ts +42 -0
- package/src/components/form/Form/index.ts +8 -0
- package/src/components/form/Input/Input.md +252 -0
- package/src/components/form/Input/Input.stories.tsx +256 -0
- package/src/components/form/Input/Input.test.stories.tsx +1184 -0
- package/src/components/form/Input/Input.tsx +263 -0
- package/src/components/form/Input/Input.types.ts +24 -0
- package/src/components/form/Input/index.ts +2 -0
- package/src/components/form/InputOTP/InputOTP.md +419 -0
- package/src/components/form/InputOTP/InputOTP.stories.tsx +774 -0
- package/src/components/form/InputOTP/InputOTP.test.stories.tsx +596 -0
- package/src/components/form/InputOTP/InputOTP.tsx +231 -0
- package/src/components/form/InputOTP/InputOTP.types.ts +68 -0
- package/src/components/form/InputOTP/index.ts +2 -0
- package/src/components/form/Label/Label.md +426 -0
- package/src/components/form/Label/Label.stories.tsx +473 -0
- package/src/components/form/Label/Label.test.stories.tsx +909 -0
- package/src/components/form/Label/Label.tsx +367 -0
- package/src/components/form/Label/Label.types.ts +35 -0
- package/src/components/form/Label/index.ts +2 -0
- package/src/components/form/Menubar/Menubar.md +275 -0
- package/src/components/form/Menubar/Menubar.stories.tsx +472 -0
- package/src/components/form/Menubar/Menubar.test.stories.tsx +588 -0
- package/src/components/form/Menubar/Menubar.tsx +467 -0
- package/src/components/form/Menubar/Menubar.types.ts +71 -0
- package/src/components/form/Menubar/index.ts +8 -0
- package/src/components/form/PasswordStrength/PasswordStrength.md +139 -0
- package/src/components/form/PasswordStrength/PasswordStrength.stories.tsx +562 -0
- package/src/components/form/PasswordStrength/PasswordStrength.test.stories.tsx +327 -0
- package/src/components/form/PasswordStrength/PasswordStrength.tsx +419 -0
- package/src/components/form/PasswordStrength/PasswordStrength.types.ts +18 -0
- package/src/components/form/PasswordStrength/index.ts +2 -0
- package/src/components/form/PhoneInput/PhoneInput.md +578 -0
- package/src/components/form/PhoneInput/PhoneInput.stories.tsx +495 -0
- package/src/components/form/PhoneInput/PhoneInput.test.stories.tsx +666 -0
- package/src/components/form/PhoneInput/PhoneInput.tsx +376 -0
- package/src/components/form/PhoneInput/PhoneInput.types.ts +25 -0
- package/src/components/form/PhoneInput/index.ts +2 -0
- package/src/components/form/RadioGroup/RadioGroup.md +477 -0
- package/src/components/form/RadioGroup/RadioGroup.stories.tsx +518 -0
- package/src/components/form/RadioGroup/RadioGroup.test.stories.tsx +685 -0
- package/src/components/form/RadioGroup/RadioGroup.tsx +664 -0
- package/src/components/form/RadioGroup/RadioGroup.types.ts +82 -0
- package/src/components/form/RadioGroup/index.ts +2 -0
- package/src/components/form/RichTextEditor/RichTextEditor.md +710 -0
- package/src/components/form/RichTextEditor/RichTextEditor.stories.tsx +261 -0
- package/src/components/form/RichTextEditor/RichTextEditor.test.stories.tsx +498 -0
- package/src/components/form/RichTextEditor/RichTextEditor.tsx +468 -0
- package/src/components/form/RichTextEditor/RichTextEditor.types.ts +72 -0
- package/src/components/form/RichTextEditor/index.ts +2 -0
- package/src/components/form/SearchPalette/SearchPalette.md +69 -0
- package/src/components/form/SearchPalette/SearchPalette.parts.tsx +358 -0
- package/src/components/form/SearchPalette/SearchPalette.stories.tsx +135 -0
- package/src/components/form/SearchPalette/SearchPalette.tsx +141 -0
- package/src/components/form/SearchPalette/SearchPalette.types.ts +82 -0
- package/src/components/form/SearchPalette/index.ts +7 -0
- package/src/components/form/Select/Select.md +254 -0
- package/src/components/form/Select/Select.stories.tsx +364 -0
- package/src/components/form/Select/Select.test.stories.tsx +977 -0
- package/src/components/form/Select/Select.tsx +239 -0
- package/src/components/form/Select/Select.types.ts +57 -0
- package/src/components/form/Select/index.ts +2 -0
- package/src/components/form/Slider/Slider.md +326 -0
- package/src/components/form/Slider/Slider.stories.tsx +418 -0
- package/src/components/form/Slider/Slider.test.stories.tsx +1044 -0
- package/src/components/form/Slider/Slider.tsx +374 -0
- package/src/components/form/Slider/Slider.types.ts +113 -0
- package/src/components/form/Slider/index.ts +2 -0
- package/src/components/form/Switch/Switch.md +565 -0
- package/src/components/form/Switch/Switch.stories.tsx +697 -0
- package/src/components/form/Switch/Switch.test.stories.tsx +597 -0
- package/src/components/form/Switch/Switch.tsx +578 -0
- package/src/components/form/Switch/Switch.types.ts +130 -0
- package/src/components/form/Switch/index.ts +2 -0
- package/src/components/form/Textarea/Textarea.stories.tsx +473 -0
- package/src/components/form/Textarea/Textarea.test.stories.tsx +708 -0
- package/src/components/form/Textarea/Textarea.tsx +678 -0
- package/src/components/form/Textarea/Textarea.types.ts +84 -0
- package/src/components/form/Textarea/index.ts +2 -0
- package/src/components/form/Toggle/Toggle.md +234 -0
- package/src/components/form/Toggle/Toggle.stories.tsx +638 -0
- package/src/components/form/Toggle/Toggle.test.stories.tsx +1167 -0
- package/src/components/form/Toggle/Toggle.tsx +265 -0
- package/src/components/form/Toggle/Toggle.types.ts +79 -0
- package/src/components/form/Toggle/index.ts +2 -0
- package/src/components/form/ToggleGroup/ToggleGroup.md +329 -0
- package/src/components/form/ToggleGroup/ToggleGroup.stories.tsx +617 -0
- package/src/components/form/ToggleGroup/ToggleGroup.test.stories.tsx +918 -0
- package/src/components/form/ToggleGroup/ToggleGroup.tsx +129 -0
- package/src/components/form/ToggleGroup/ToggleGroup.types.ts +53 -0
- package/src/components/form/ToggleGroup/index.ts +2 -0
- package/src/components/form/UploadButton/UploadButton.md +44 -0
- package/src/components/form/UploadButton/UploadButton.stories.tsx +366 -0
- package/src/components/form/UploadButton/UploadButton.test.stories.tsx +454 -0
- package/src/components/form/UploadButton/UploadButton.tsx +312 -0
- package/src/components/form/UploadButton/UploadButton.types.ts +58 -0
- package/src/components/form/UploadButton/index.ts +2 -0
- package/src/components/form/index.ts +25 -0
- package/src/components/form/total-form/Fields/SelectField.tsx +57 -0
- package/src/components/form/total-form/Fields/TextField.tsx +58 -0
- package/src/components/form/total-form/Fields/index.ts +4 -0
- package/src/components/form/total-form/FormContainer.tsx +104 -0
- package/src/components/form/total-form/FormContext.tsx +38 -0
- package/src/components/form/total-form/FormErrorSnackbar.tsx +35 -0
- package/src/components/form/total-form/SubmitButton.tsx +27 -0
- package/src/components/form/total-form/index.ts +12 -0
- package/src/components/form/total-form/total-form.test.tsx +97 -0
- package/src/components/index.ts +7 -0
- package/src/components/layout/Accordion/Accordion.md +725 -0
- package/src/components/layout/Accordion/Accordion.stories.tsx +690 -0
- package/src/components/layout/Accordion/Accordion.test.stories.tsx +1362 -0
- package/src/components/layout/Accordion/Accordion.tsx +242 -0
- package/src/components/layout/Accordion/Accordion.types.ts +32 -0
- package/src/components/layout/Accordion/index.ts +8 -0
- package/src/components/layout/Card/Card.md +533 -0
- package/src/components/layout/Card/Card.stories.tsx +593 -0
- package/src/components/layout/Card/Card.test.stories.tsx +1134 -0
- package/src/components/layout/Card/Card.tsx +404 -0
- package/src/components/layout/Card/Card.types.ts +57 -0
- package/src/components/layout/Card/index.ts +8 -0
- package/src/components/layout/Collapsible/Collapsible.md +135 -0
- package/src/components/layout/Collapsible/Collapsible.stories.tsx +180 -0
- package/src/components/layout/Collapsible/Collapsible.test.stories.tsx +304 -0
- package/src/components/layout/Collapsible/Collapsible.tsx +198 -0
- package/src/components/layout/Collapsible/Collapsible.types.ts +35 -0
- package/src/components/layout/Collapsible/index.ts +6 -0
- package/src/components/layout/Container/Container.md +420 -0
- package/src/components/layout/Container/Container.stories.tsx +557 -0
- package/src/components/layout/Container/Container.test.stories.tsx +750 -0
- package/src/components/layout/Container/Container.tsx +72 -0
- package/src/components/layout/Container/Container.types.ts +11 -0
- package/src/components/layout/Container/index.ts +2 -0
- package/src/components/layout/ContentToolbar/ColumnsMenu.tsx +99 -0
- package/src/components/layout/ContentToolbar/ContentToolbar.md +75 -0
- package/src/components/layout/ContentToolbar/ContentToolbar.stories.tsx +129 -0
- package/src/components/layout/ContentToolbar/ContentToolbar.test.stories.tsx +251 -0
- package/src/components/layout/ContentToolbar/ContentToolbar.tsx +108 -0
- package/src/components/layout/ContentToolbar/ContentToolbar.types.ts +229 -0
- package/src/components/layout/ContentToolbar/FilterTrigger.tsx +71 -0
- package/src/components/layout/ContentToolbar/MultiSelectDropdown.tsx +297 -0
- package/src/components/layout/ContentToolbar/SavedViewsMenu.tsx +334 -0
- package/src/components/layout/ContentToolbar/SortByDropdown.tsx +213 -0
- package/src/components/layout/ContentToolbar/ViewSelector.tsx +98 -0
- package/src/components/layout/ContentToolbar/__tests__/ColumnsMenu.test.tsx +39 -0
- package/src/components/layout/ContentToolbar/__tests__/MultiSelectDropdown.test.tsx +110 -0
- package/src/components/layout/ContentToolbar/__tests__/SavedViewsMenu.test.tsx +99 -0
- package/src/components/layout/ContentToolbar/index.ts +24 -0
- package/src/components/layout/ContentToolbar/tests.md +42 -0
- package/src/components/layout/ContentToolbar/track.md +41 -0
- package/src/components/layout/Dashboard/Dashboard.md +71 -0
- package/src/components/layout/Dashboard/Dashboard.stories.tsx +104 -0
- package/src/components/layout/Dashboard/Dashboard.test.stories.tsx +164 -0
- package/src/components/layout/Dashboard/Dashboard.tsx +297 -0
- package/src/components/layout/Dashboard/Dashboard.types.ts +157 -0
- package/src/components/layout/Dashboard/DashboardContext.tsx +47 -0
- package/src/components/layout/Dashboard/DashboardParts.tsx +216 -0
- package/src/components/layout/Dashboard/index.ts +18 -0
- package/src/components/layout/Dashboard/tests.md +46 -0
- package/src/components/layout/Dashboard/track.md +50 -0
- package/src/components/layout/Drawer/Drawer.md +479 -0
- package/src/components/layout/Drawer/Drawer.stories.tsx +660 -0
- package/src/components/layout/Drawer/Drawer.test.stories.tsx +1339 -0
- package/src/components/layout/Drawer/Drawer.tsx +150 -0
- package/src/components/layout/Drawer/Drawer.types.ts +34 -0
- package/src/components/layout/Drawer/index.ts +2 -0
- package/src/components/layout/Resizable/Resizable.md +308 -0
- package/src/components/layout/Resizable/Resizable.stories.tsx +1039 -0
- package/src/components/layout/Resizable/Resizable.test.stories.tsx +750 -0
- package/src/components/layout/Resizable/Resizable.tsx +190 -0
- package/src/components/layout/Resizable/Resizable.types.ts +34 -0
- package/src/components/layout/Resizable/index.ts +2 -0
- package/src/components/layout/ScrollArea/ScrollArea.md +122 -0
- package/src/components/layout/ScrollArea/ScrollArea.stories.tsx +651 -0
- package/src/components/layout/ScrollArea/ScrollArea.test.stories.tsx +619 -0
- package/src/components/layout/ScrollArea/ScrollArea.tsx +449 -0
- package/src/components/layout/ScrollArea/ScrollArea.types.ts +80 -0
- package/src/components/layout/ScrollArea/index.ts +7 -0
- package/src/components/layout/Separator/Separator.stories.tsx +563 -0
- package/src/components/layout/Separator/Separator.test.stories.tsx +527 -0
- package/src/components/layout/Separator/Separator.tsx +187 -0
- package/src/components/layout/Separator/Separator.types.ts +17 -0
- package/src/components/layout/Separator/index.ts +2 -0
- package/src/components/layout/SettingsLayout/SettingsLayout.md +139 -0
- package/src/components/layout/SettingsLayout/SettingsLayout.parts.tsx +357 -0
- package/src/components/layout/SettingsLayout/SettingsLayout.stories.tsx +194 -0
- package/src/components/layout/SettingsLayout/SettingsLayout.test.stories.tsx +175 -0
- package/src/components/layout/SettingsLayout/SettingsLayout.tsx +69 -0
- package/src/components/layout/SettingsLayout/SettingsLayout.types.ts +61 -0
- package/src/components/layout/SettingsLayout/index.ts +6 -0
- package/src/components/layout/Sidebar/Sidebar.md +367 -0
- package/src/components/layout/Sidebar/Sidebar.stories.tsx +611 -0
- package/src/components/layout/Sidebar/Sidebar.test.stories.tsx +1326 -0
- package/src/components/layout/Sidebar/Sidebar.tsx +131 -0
- package/src/components/layout/Sidebar/Sidebar.types.ts +30 -0
- package/src/components/layout/Sidebar/index.ts +7 -0
- package/src/components/layout/Skeleton/Skeleton.md +240 -0
- package/src/components/layout/Skeleton/Skeleton.stories.tsx +689 -0
- package/src/components/layout/Skeleton/Skeleton.test.stories.tsx +521 -0
- package/src/components/layout/Skeleton/Skeleton.tsx +166 -0
- package/src/components/layout/Skeleton/Skeleton.types.ts +19 -0
- package/src/components/layout/Skeleton/index.ts +2 -0
- package/src/components/layout/Spacer/Spacer.md +84 -0
- package/src/components/layout/Spacer/Spacer.stories.tsx +502 -0
- package/src/components/layout/Spacer/Spacer.test.stories.tsx +472 -0
- package/src/components/layout/Spacer/Spacer.tsx +68 -0
- package/src/components/layout/Spacer/Spacer.types.ts +12 -0
- package/src/components/layout/Spacer/index.ts +2 -0
- package/src/components/layout/TableFilter/TableFilter.md +55 -0
- package/src/components/layout/TableFilter/TableFilter.stories.tsx +82 -0
- package/src/components/layout/TableFilter/TableFilter.test.stories.tsx +87 -0
- package/src/components/layout/TableFilter/TableFilter.tsx +383 -0
- package/src/components/layout/TableFilter/TableFilter.types.ts +87 -0
- package/src/components/layout/TableFilter/TableFilterContext.tsx +29 -0
- package/src/components/layout/TableFilter/index.ts +15 -0
- package/src/components/layout/TableFilter/tests.md +37 -0
- package/src/components/layout/TableFilter/track.md +36 -0
- package/src/components/layout/index.ts +14 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.md +558 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsx +640 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.test.stories.tsx +1007 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.tsx +516 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.types.ts +132 -0
- package/src/components/navigation/Breadcrumbs/index.ts +2 -0
- package/src/components/navigation/CommandPalette/CommandPalette.md +201 -0
- package/src/components/navigation/CommandPalette/CommandPalette.stories.tsx +384 -0
- package/src/components/navigation/CommandPalette/CommandPalette.test.stories.tsx +437 -0
- package/src/components/navigation/CommandPalette/CommandPalette.tsx +434 -0
- package/src/components/navigation/CommandPalette/CommandPalette.types.ts +25 -0
- package/src/components/navigation/CommandPalette/index.ts +2 -0
- package/src/components/navigation/ContextMenu/ContextMenu.md +535 -0
- package/src/components/navigation/ContextMenu/ContextMenu.stories.tsx +611 -0
- package/src/components/navigation/ContextMenu/ContextMenu.test.stories.tsx +850 -0
- package/src/components/navigation/ContextMenu/ContextMenu.tsx +254 -0
- package/src/components/navigation/ContextMenu/ContextMenu.types.ts +96 -0
- package/src/components/navigation/ContextMenu/index.ts +2 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.md +514 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.stories.tsx +580 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.test.stories.tsx +660 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.tsx +305 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.types.ts +116 -0
- package/src/components/navigation/DropdownMenu/index.ts +2 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.md +580 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.tsx +874 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.types.ts +135 -0
- package/src/components/navigation/NavigationMenu/index.ts +2 -0
- package/src/components/navigation/NavigationMenu/stories/NavigationMenu.stories.tsx +518 -0
- package/src/components/navigation/NavigationMenu/stories/NavigationMenu.test.stories.tsx +931 -0
- package/src/components/navigation/NavigationMenu/stories/NavigationMenuHorizontal.stories.tsx +503 -0
- package/src/components/navigation/NavigationMenu/stories/NavigationMenuVertical.stories.tsx +455 -0
- package/src/components/navigation/Pagination/Pagination.md +699 -0
- package/src/components/navigation/Pagination/Pagination.stories.tsx +604 -0
- package/src/components/navigation/Pagination/Pagination.test.stories.tsx +520 -0
- package/src/components/navigation/Pagination/Pagination.tsx +356 -0
- package/src/components/navigation/Pagination/Pagination.types.ts +129 -0
- package/src/components/navigation/Pagination/index.ts +2 -0
- package/src/components/navigation/Tabs/Tabs.md +774 -0
- package/src/components/navigation/Tabs/Tabs.stories.tsx +1006 -0
- package/src/components/navigation/Tabs/Tabs.test.stories.tsx +1225 -0
- package/src/components/navigation/Tabs/Tabs.tsx +433 -0
- package/src/components/navigation/Tabs/Tabs.types.ts +239 -0
- package/src/components/navigation/Tabs/index.ts +2 -0
- package/src/components/navigation/index.ts +7 -0
- package/src/components/typography/Blockquote/Blockquote.md +130 -0
- package/src/components/typography/Blockquote/Blockquote.stories.tsx +359 -0
- package/src/components/typography/Blockquote/Blockquote.test.stories.tsx +432 -0
- package/src/components/typography/Blockquote/Blockquote.tsx +166 -0
- package/src/components/typography/Blockquote/Blockquote.types.ts +11 -0
- package/src/components/typography/Blockquote/index.ts +2 -0
- package/src/components/typography/Code/Code.md +182 -0
- package/src/components/typography/Code/Code.stories.tsx +686 -0
- package/src/components/typography/Code/Code.test.stories.tsx +703 -0
- package/src/components/typography/Code/Code.tsx +228 -0
- package/src/components/typography/Code/Code.types.ts +12 -0
- package/src/components/typography/Code/index.ts +2 -0
- package/src/components/typography/Heading/Heading.md +143 -0
- package/src/components/typography/Heading/Heading.stories.tsx +451 -0
- package/src/components/typography/Heading/Heading.test.stories.tsx +830 -0
- package/src/components/typography/Heading/Heading.tsx +178 -0
- package/src/components/typography/Heading/Heading.types.ts +11 -0
- package/src/components/typography/Heading/index.ts +2 -0
- package/src/components/typography/Paragraph/Paragraph.md +80 -0
- package/src/components/typography/Paragraph/Paragraph.stories.tsx +280 -0
- package/src/components/typography/Paragraph/Paragraph.test.stories.tsx +238 -0
- package/src/components/typography/Paragraph/Paragraph.tsx +99 -0
- package/src/components/typography/Paragraph/Paragraph.types.ts +10 -0
- package/src/components/typography/Paragraph/index.ts +2 -0
- package/src/components/typography/Text/Text.md +129 -0
- package/src/components/typography/Text/Text.stories.tsx +359 -0
- package/src/components/typography/Text/Text.test.stories.tsx +762 -0
- package/src/components/typography/Text/Text.tsx +159 -0
- package/src/components/typography/Text/Text.types.ts +15 -0
- package/src/components/typography/Text/index.ts +2 -0
- package/src/components/typography/index.ts +5 -0
- package/src/components/utility/AnimatedIcon/AnimatedIcon.md +359 -0
- package/src/components/utility/AnimatedIcon/AnimatedIcon.stories.tsx +947 -0
- package/src/components/utility/AnimatedIcon/AnimatedIcon.test.stories.tsx +596 -0
- package/src/components/utility/AnimatedIcon/AnimatedIcon.tsx +595 -0
- package/src/components/utility/AnimatedIcon/AnimatedIcon.types.ts +98 -0
- package/src/components/utility/AnimatedIcon/index.ts +2 -0
- package/src/components/utility/AspectRatio/AspectRatio.md +200 -0
- package/src/components/utility/AspectRatio/AspectRatio.stories.tsx +414 -0
- package/src/components/utility/AspectRatio/AspectRatio.test.stories.tsx +859 -0
- package/src/components/utility/AspectRatio/AspectRatio.tsx +76 -0
- package/src/components/utility/AspectRatio/AspectRatio.types.ts +13 -0
- package/src/components/utility/AspectRatio/index.ts +2 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.md +531 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.stories.tsx +620 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.test.stories.tsx +358 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.tsx +204 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.types.ts +39 -0
- package/src/components/utility/InfiniteScroll/index.ts +6 -0
- package/src/components/utility/Portal/Portal.button-container.example.tsx +99 -0
- package/src/components/utility/Portal/Portal.md +593 -0
- package/src/components/utility/Portal/Portal.stories.tsx +340 -0
- package/src/components/utility/Portal/Portal.test.stories.tsx +2086 -0
- package/src/components/utility/Portal/Portal.tsx +39 -0
- package/src/components/utility/Portal/Portal.types.ts +7 -0
- package/src/components/utility/Portal/index.ts +2 -0
- package/src/components/utility/Transition/Transition.md +684 -0
- package/src/components/utility/Transition/Transition.stories.tsx +493 -0
- package/src/components/utility/Transition/Transition.test.stories.tsx +854 -0
- package/src/components/utility/Transition/Transition.tsx +140 -0
- package/src/components/utility/Transition/Transition.types.ts +14 -0
- package/src/components/utility/Transition/index.ts +6 -0
- package/src/components/utility/VirtualList/VirtualList.md +520 -0
- package/src/components/utility/VirtualList/VirtualList.stories.tsx +1073 -0
- package/src/components/utility/VirtualList/VirtualList.test.stories.tsx +1074 -0
- package/src/components/utility/VirtualList/VirtualList.tsx +369 -0
- package/src/components/utility/VirtualList/VirtualList.types.ts +56 -0
- package/src/components/utility/VirtualList/index.ts +7 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.md +187 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.stories.tsx +433 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.test.stories.tsx +364 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.tsx +506 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.types.ts +133 -0
- package/src/components/utility/WorkflowStep/index.ts +13 -0
- package/src/components/utility/index.ts +7 -0
- package/src/index.ts +17 -0
- package/src/mui/AppBar.ts +2 -0
- package/src/mui/Box.ts +2 -0
- package/src/mui/CssBaseline.ts +2 -0
- package/src/mui/FormControl.ts +2 -0
- package/src/mui/FormControlLabel.ts +2 -0
- package/src/mui/Grid2.ts +2 -0
- package/src/mui/InputLabel.ts +2 -0
- package/src/mui/List.ts +2 -0
- package/src/mui/ListItem.ts +2 -0
- package/src/mui/ListItemButton.ts +2 -0
- package/src/mui/ListItemText.ts +2 -0
- package/src/mui/Stack.ts +2 -0
- package/src/mui/SvgIcon.ts +2 -0
- package/src/mui/Toolbar.ts +2 -0
- package/src/mui/styles.ts +2 -0
- package/src/mui/useMediaQuery.ts +1 -0
- package/src/social-login-button.tsx +199 -0
- package/src/user-avatar.tsx +102 -0
- package/src/utils/cn.ts +9 -0
- package/src/utils/index.ts +1 -0
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
# RichTextEditor
|
|
2
|
+
|
|
3
|
+
A comprehensive rich text editor component with formatting capabilities, built on MUI and integrated with modern rich text editing libraries. Provides a complete set of text editing features including bold, italic, underline, lists, links, and more.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Rich Text Formatting**: Bold, italic, underline, strikethrough
|
|
8
|
+
- **Lists**: Ordered and unordered lists
|
|
9
|
+
- **Links and Images**: Insert links and images
|
|
10
|
+
- **Code Blocks**: Formatted code blocks with syntax highlighting
|
|
11
|
+
- **Quotes**: Blockquote formatting
|
|
12
|
+
- **Character Count**: Optional character limit with visual feedback
|
|
13
|
+
- **Accessibility**: Full ARIA support and keyboard navigation
|
|
14
|
+
- **Theming**: Full MUI theme integration
|
|
15
|
+
- **Customizable Toolbar**: Configure which tools are available
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Basic Usage
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { RichTextEditor } from '@procurement/ui';
|
|
23
|
+
|
|
24
|
+
function App() {
|
|
25
|
+
const [content, setContent] = useState('');
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<RichTextEditor
|
|
29
|
+
value={content}
|
|
30
|
+
onChange={setContent}
|
|
31
|
+
placeholder="Start writing your content..."
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### With Custom Toolbar
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<RichTextEditor
|
|
41
|
+
value={content}
|
|
42
|
+
onChange={setContent}
|
|
43
|
+
toolbar={{
|
|
44
|
+
bold: true,
|
|
45
|
+
italic: true,
|
|
46
|
+
underline: false,
|
|
47
|
+
orderedList: true,
|
|
48
|
+
unorderedList: true,
|
|
49
|
+
link: true,
|
|
50
|
+
image: false,
|
|
51
|
+
}}
|
|
52
|
+
/>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### With Character Limit
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<RichTextEditor
|
|
59
|
+
value={content}
|
|
60
|
+
onChange={setContent}
|
|
61
|
+
maxLength={500}
|
|
62
|
+
placeholder="Keep it under 500 characters..."
|
|
63
|
+
/>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Read-Only Mode
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
<RichTextEditor
|
|
70
|
+
value={content}
|
|
71
|
+
readOnly
|
|
72
|
+
toolbar={{}} // Hide toolbar in read-only mode
|
|
73
|
+
/>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Props
|
|
77
|
+
|
|
78
|
+
| Prop | Type | Default | Description |
|
|
79
|
+
|------|------|---------|-------------|
|
|
80
|
+
| `value` | `string` | `''` | The current value of the rich text editor content |
|
|
81
|
+
| `onChange` | `(value: string) => void` | - | Callback when the content changes |
|
|
82
|
+
| `placeholder` | `string` | `'Start typing...'` | Placeholder text when empty |
|
|
83
|
+
| `disabled` | `boolean` | `false` | Whether the editor is disabled |
|
|
84
|
+
| `readOnly` | `boolean` | `false` | Whether the editor is read-only |
|
|
85
|
+
| `toolbar` | `ToolbarConfig` | Default config | Configuration for toolbar buttons and features |
|
|
86
|
+
| `height` | `number \| string` | `300` | Height of the editor |
|
|
87
|
+
| `maxLength` | `number` | - | Maximum number of characters allowed |
|
|
88
|
+
| `onFocus` | `() => void` | - | Callback when editor receives focus |
|
|
89
|
+
| `onBlur` | `() => void` | - | Callback when editor loses focus |
|
|
90
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
91
|
+
| `data-testid` | `string` | - | Test ID for testing purposes |
|
|
92
|
+
| `aria-label` | `string` | `'Rich text editor'` | ARIA label for accessibility |
|
|
93
|
+
| `aria-describedby` | `string` | - | ARIA described by for accessibility |
|
|
94
|
+
|
|
95
|
+
## ToolbarConfig
|
|
96
|
+
|
|
97
|
+
| Prop | Type | Default | Description |
|
|
98
|
+
|------|------|---------|-------------|
|
|
99
|
+
| `bold` | `boolean` | `true` | Show bold button |
|
|
100
|
+
| `italic` | `boolean` | `true` | Show italic button |
|
|
101
|
+
| `underline` | `boolean` | `true` | Show underline button |
|
|
102
|
+
| `strikethrough` | `boolean` | `false` | Show strikethrough button |
|
|
103
|
+
| `orderedList` | `boolean` | `true` | Show ordered list button |
|
|
104
|
+
| `unorderedList` | `boolean` | `true` | Show unordered list button |
|
|
105
|
+
| `link` | `boolean` | `true` | Show link button |
|
|
106
|
+
| `image` | `boolean` | `false` | Show image button |
|
|
107
|
+
| `codeBlock` | `boolean` | `false` | Show code block button |
|
|
108
|
+
| `quote` | `boolean` | `false` | Show quote button |
|
|
109
|
+
| `customItems` | `ToolbarItem[]` | `[]` | Custom toolbar items |
|
|
110
|
+
|
|
111
|
+
## Keyboard Shortcuts
|
|
112
|
+
|
|
113
|
+
- **Ctrl/Cmd + B**: Bold
|
|
114
|
+
- **Ctrl/Cmd + I**: Italic
|
|
115
|
+
- **Ctrl/Cmd + U**: Underline
|
|
116
|
+
- **Ctrl/Cmd + K**: Insert Link
|
|
117
|
+
- **Tab**: Indent (in lists)
|
|
118
|
+
- **Shift + Tab**: Outdent (in lists)
|
|
119
|
+
|
|
120
|
+
## Accessibility
|
|
121
|
+
|
|
122
|
+
The RichTextEditor component follows WAI-ARIA guidelines:
|
|
123
|
+
|
|
124
|
+
- Proper ARIA roles and labels
|
|
125
|
+
- Keyboard navigation support
|
|
126
|
+
- Screen reader compatibility
|
|
127
|
+
- Focus management
|
|
128
|
+
- High contrast mode support
|
|
129
|
+
|
|
130
|
+
### Screen Reader Support
|
|
131
|
+
|
|
132
|
+
- Content changes are announced
|
|
133
|
+
- Toolbar buttons have descriptive labels
|
|
134
|
+
- Character count is announced when approaching limit
|
|
135
|
+
|
|
136
|
+
### Keyboard Navigation
|
|
137
|
+
|
|
138
|
+
- All toolbar buttons are keyboard accessible
|
|
139
|
+
- Standard text editing keyboard shortcuts work
|
|
140
|
+
- Tab navigation follows logical order
|
|
141
|
+
|
|
142
|
+
## Best Practices
|
|
143
|
+
|
|
144
|
+
1. **Content Validation**: Always validate and sanitize rich text content on the server side
|
|
145
|
+
2. **Character Limits**: Use `maxLength` for forms with content restrictions
|
|
146
|
+
3. **Accessibility**: Provide meaningful `aria-label` and `aria-describedby` attributes
|
|
147
|
+
4. **Performance**: Consider debouncing the `onChange` callback for frequent updates
|
|
148
|
+
5. **Theming**: Use MUI theme tokens for consistent styling
|
|
149
|
+
|
|
150
|
+
## Examples
|
|
151
|
+
|
|
152
|
+
See the Storybook stories for comprehensive usage examples and interactive demos.
|
|
153
|
+
|
|
154
|
+
## Testing
|
|
155
|
+
|
|
156
|
+
This section describes all `data-testid` attributes available in the RichTextEditor component for testing purposes.
|
|
157
|
+
|
|
158
|
+
### Test IDs
|
|
159
|
+
|
|
160
|
+
#### Container Elements
|
|
161
|
+
|
|
162
|
+
##### `rich-text-editor`
|
|
163
|
+
- **Element:** Main RichTextEditor container (Paper)
|
|
164
|
+
- **Location:** Root Paper element that wraps the entire editor
|
|
165
|
+
- **Usage:** Query the main container to verify RichTextEditor is rendered
|
|
166
|
+
```typescript
|
|
167
|
+
const editor = await canvas.findByTestId('rich-text-editor');
|
|
168
|
+
expect(editor).toBeInTheDocument();
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
##### `editor-toolbar`
|
|
172
|
+
- **Element:** Toolbar container
|
|
173
|
+
- **Location:** Toolbar component containing all formatting buttons
|
|
174
|
+
- **Usage:** Query the toolbar to verify it's rendered and accessible
|
|
175
|
+
```typescript
|
|
176
|
+
const toolbar = await canvas.findByTestId('editor-toolbar');
|
|
177
|
+
expect(toolbar).toBeInTheDocument();
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
##### `editor-content`
|
|
181
|
+
- **Element:** Editor content area
|
|
182
|
+
- **Location:** The contentEditable Box where users type text
|
|
183
|
+
- **Usage:** Query the content area for typing and content verification
|
|
184
|
+
```typescript
|
|
185
|
+
const content = await canvas.findByTestId('editor-content');
|
|
186
|
+
expect(content).toHaveAttribute('contenteditable', 'true');
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### Toolbar Buttons
|
|
190
|
+
|
|
191
|
+
##### `toolbar-bold`
|
|
192
|
+
- **Element:** Bold formatting button
|
|
193
|
+
- **Location:** Toolbar - Text formatting section
|
|
194
|
+
- **Usage:** Test bold text formatting functionality
|
|
195
|
+
```typescript
|
|
196
|
+
const boldButton = await canvas.findByTestId('toolbar-bold');
|
|
197
|
+
await userEvent.click(boldButton);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
##### `toolbar-italic`
|
|
201
|
+
- **Element:** Italic formatting button
|
|
202
|
+
- **Location:** Toolbar - Text formatting section
|
|
203
|
+
- **Usage:** Test italic text formatting functionality
|
|
204
|
+
```typescript
|
|
205
|
+
const italicButton = await canvas.findByTestId('toolbar-italic');
|
|
206
|
+
await userEvent.click(italicButton);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
##### `toolbar-underline`
|
|
210
|
+
- **Element:** Underline formatting button
|
|
211
|
+
- **Location:** Toolbar - Text formatting section
|
|
212
|
+
- **Usage:** Test underline text formatting functionality
|
|
213
|
+
```typescript
|
|
214
|
+
const underlineButton = await canvas.findByTestId('toolbar-underline');
|
|
215
|
+
await userEvent.click(underlineButton);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
##### `toolbar-strikethrough`
|
|
219
|
+
- **Element:** Strikethrough formatting button
|
|
220
|
+
- **Location:** Toolbar - Text formatting section
|
|
221
|
+
- **Usage:** Test strikethrough text formatting functionality
|
|
222
|
+
```typescript
|
|
223
|
+
const strikethroughButton = await canvas.findByTestId('toolbar-strikethrough');
|
|
224
|
+
await userEvent.click(strikethroughButton);
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
##### `toolbar-orderedList`
|
|
228
|
+
- **Element:** Numbered list button
|
|
229
|
+
- **Location:** Toolbar - List formatting section
|
|
230
|
+
- **Usage:** Test ordered (numbered) list creation
|
|
231
|
+
```typescript
|
|
232
|
+
const orderedListButton = await canvas.findByTestId('toolbar-orderedList');
|
|
233
|
+
await userEvent.click(orderedListButton);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
##### `toolbar-unorderedList`
|
|
237
|
+
- **Element:** Bulleted list button
|
|
238
|
+
- **Location:** Toolbar - List formatting section
|
|
239
|
+
- **Usage:** Test unordered (bulleted) list creation
|
|
240
|
+
```typescript
|
|
241
|
+
const unorderedListButton = await canvas.findByTestId('toolbar-unorderedList');
|
|
242
|
+
await userEvent.click(unorderedListButton);
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
##### `toolbar-link`
|
|
246
|
+
- **Element:** Insert link button
|
|
247
|
+
- **Location:** Toolbar - Insert section
|
|
248
|
+
- **Usage:** Test link insertion functionality (opens prompt)
|
|
249
|
+
```typescript
|
|
250
|
+
const linkButton = await canvas.findByTestId('toolbar-link');
|
|
251
|
+
await userEvent.click(linkButton);
|
|
252
|
+
// Note: Will trigger window.prompt() - mock if needed
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
##### `toolbar-image`
|
|
256
|
+
- **Element:** Insert image button
|
|
257
|
+
- **Location:** Toolbar - Insert section
|
|
258
|
+
- **Usage:** Test image insertion functionality (opens prompt)
|
|
259
|
+
```typescript
|
|
260
|
+
const imageButton = await canvas.findByTestId('toolbar-image');
|
|
261
|
+
await userEvent.click(imageButton);
|
|
262
|
+
// Note: Will trigger window.prompt() - mock if needed
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
##### `toolbar-codeBlock`
|
|
266
|
+
- **Element:** Code block button
|
|
267
|
+
- **Location:** Toolbar - Block formatting section
|
|
268
|
+
- **Usage:** Test code block formatting
|
|
269
|
+
```typescript
|
|
270
|
+
const codeButton = await canvas.findByTestId('toolbar-codeBlock');
|
|
271
|
+
await userEvent.click(codeButton);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
##### `toolbar-quote`
|
|
275
|
+
- **Element:** Blockquote button
|
|
276
|
+
- **Location:** Toolbar - Block formatting section
|
|
277
|
+
- **Usage:** Test blockquote formatting
|
|
278
|
+
```typescript
|
|
279
|
+
const quoteButton = await canvas.findByTestId('toolbar-quote');
|
|
280
|
+
await userEvent.click(quoteButton);
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
##### `toolbar-custom-{id}`
|
|
284
|
+
- **Element:** Custom toolbar items
|
|
285
|
+
- **Location:** Toolbar - Custom items section (if configured)
|
|
286
|
+
- **Pattern:** `toolbar-custom-` followed by the custom item's `id` prop
|
|
287
|
+
- **Usage:** Test custom toolbar functionality
|
|
288
|
+
```typescript
|
|
289
|
+
// If custom item has id="highlight"
|
|
290
|
+
const customButton = await canvas.findByTestId('toolbar-custom-highlight');
|
|
291
|
+
await userEvent.click(customButton);
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
#### Status/Counter Elements
|
|
295
|
+
|
|
296
|
+
##### `editor-counter`
|
|
297
|
+
- **Element:** Character counter display
|
|
298
|
+
- **Location:** Right side of toolbar (only visible when `maxLength` prop is set)
|
|
299
|
+
- **Usage:** Verify character count and limit enforcement
|
|
300
|
+
```typescript
|
|
301
|
+
const counter = await canvas.findByTestId('editor-counter');
|
|
302
|
+
expect(counter).toHaveTextContent('25/100');
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Test Patterns
|
|
306
|
+
|
|
307
|
+
#### Wait for Editor to Render
|
|
308
|
+
```typescript
|
|
309
|
+
import { waitFor, within } from 'storybook/test';
|
|
310
|
+
|
|
311
|
+
// Wait for container
|
|
312
|
+
const editor = await canvas.findByTestId('rich-text-editor');
|
|
313
|
+
|
|
314
|
+
// Wait for editor to be ready
|
|
315
|
+
const content = await canvas.findByTestId('editor-content');
|
|
316
|
+
expect(content).toBeInTheDocument();
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### Verify Toolbar Configuration
|
|
320
|
+
```typescript
|
|
321
|
+
// Check which toolbar buttons are visible
|
|
322
|
+
const toolbar = await canvas.findByTestId('editor-toolbar');
|
|
323
|
+
|
|
324
|
+
// Try to find specific buttons
|
|
325
|
+
const boldButton = within(toolbar).queryByTestId('toolbar-bold');
|
|
326
|
+
const italicButton = within(toolbar).queryByTestId('toolbar-italic');
|
|
327
|
+
|
|
328
|
+
expect(boldButton).toBeInTheDocument();
|
|
329
|
+
expect(italicButton).toBeInTheDocument();
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### Test Text Input and Formatting
|
|
333
|
+
```typescript
|
|
334
|
+
// Get editor content
|
|
335
|
+
const content = await canvas.findByTestId('editor-content');
|
|
336
|
+
|
|
337
|
+
// Type text
|
|
338
|
+
await userEvent.click(content);
|
|
339
|
+
await userEvent.type(content, 'Hello World');
|
|
340
|
+
|
|
341
|
+
// Apply formatting
|
|
342
|
+
const boldButton = await canvas.findByTestId('toolbar-bold');
|
|
343
|
+
await userEvent.click(boldButton);
|
|
344
|
+
|
|
345
|
+
// Verify formatted HTML
|
|
346
|
+
await waitFor(() => {
|
|
347
|
+
expect(content.innerHTML).toContain('<strong>');
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
#### Test Character Counter
|
|
352
|
+
```typescript
|
|
353
|
+
// Component with maxLength
|
|
354
|
+
const editor = await canvas.findByTestId('rich-text-editor');
|
|
355
|
+
const content = await canvas.findByTestId('editor-content');
|
|
356
|
+
const counter = await canvas.findByTestId('editor-counter');
|
|
357
|
+
|
|
358
|
+
// Type text
|
|
359
|
+
await userEvent.click(content);
|
|
360
|
+
await userEvent.type(content, 'Test text');
|
|
361
|
+
|
|
362
|
+
// Check counter updates
|
|
363
|
+
await waitFor(() => {
|
|
364
|
+
expect(counter).toHaveTextContent(/9\/50/);
|
|
365
|
+
});
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
#### Test Disabled State
|
|
369
|
+
```typescript
|
|
370
|
+
const content = await canvas.findByTestId('editor-content');
|
|
371
|
+
const boldButton = await canvas.findByTestId('toolbar-bold');
|
|
372
|
+
|
|
373
|
+
// Verify disabled state
|
|
374
|
+
expect(content).toHaveAttribute('contenteditable', 'false');
|
|
375
|
+
expect(boldButton).toBeDisabled();
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### Test Read-Only State
|
|
379
|
+
```typescript
|
|
380
|
+
const content = await canvas.findByTestId('editor-content');
|
|
381
|
+
const toolbar = await canvas.findByTestId('editor-toolbar');
|
|
382
|
+
|
|
383
|
+
// Verify read-only state
|
|
384
|
+
expect(content).toHaveAttribute('contenteditable', 'false');
|
|
385
|
+
|
|
386
|
+
// Toolbar buttons should be disabled
|
|
387
|
+
const buttons = within(toolbar).queryAllByRole('button');
|
|
388
|
+
buttons.forEach(button => {
|
|
389
|
+
expect(button).toBeDisabled();
|
|
390
|
+
});
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Props That Affect Test Behavior
|
|
394
|
+
|
|
395
|
+
#### `toolbar`
|
|
396
|
+
- **Type:** `ToolbarConfig` object
|
|
397
|
+
- **Impact:** Controls which toolbar buttons are visible
|
|
398
|
+
- **Default:** All basic formatting buttons enabled
|
|
399
|
+
- **Test Tip:** Query toolbar to verify only configured buttons exist
|
|
400
|
+
```typescript
|
|
401
|
+
// With toolbar={{ bold: true, italic: false }}
|
|
402
|
+
const boldButton = canvas.queryByTestId('toolbar-bold');
|
|
403
|
+
const italicButton = canvas.queryByTestId('toolbar-italic');
|
|
404
|
+
|
|
405
|
+
expect(boldButton).toBeInTheDocument();
|
|
406
|
+
expect(italicButton).not.toBeInTheDocument();
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
#### `disabled`
|
|
410
|
+
- **Values:** `boolean`
|
|
411
|
+
- **Impact:** Disables all interactions (toolbar and content)
|
|
412
|
+
- **Test Tip:** Check `contenteditable="false"` and button disabled states
|
|
413
|
+
|
|
414
|
+
#### `readOnly`
|
|
415
|
+
- **Values:** `boolean`
|
|
416
|
+
- **Impact:** Similar to disabled but may have different visual styling
|
|
417
|
+
- **Test Tip:** Check `contenteditable="false"` and button disabled states
|
|
418
|
+
|
|
419
|
+
#### `maxLength`
|
|
420
|
+
- **Values:** `number`
|
|
421
|
+
- **Impact:** Shows character counter and enforces text limit
|
|
422
|
+
- **Test Tip:** Counter only appears when `maxLength` is set
|
|
423
|
+
|
|
424
|
+
#### `height`
|
|
425
|
+
- **Values:** `number | string`
|
|
426
|
+
- **Impact:** Sets minimum height of editor content area
|
|
427
|
+
- **Test Tip:** Check computed styles if testing layout
|
|
428
|
+
|
|
429
|
+
### Common Test Scenarios
|
|
430
|
+
|
|
431
|
+
#### 1. Basic Rendering Test
|
|
432
|
+
```typescript
|
|
433
|
+
play: async ({ canvasElement }) => {
|
|
434
|
+
const canvas = within(canvasElement);
|
|
435
|
+
|
|
436
|
+
// Wait for editor to render
|
|
437
|
+
const editor = await canvas.findByTestId('rich-text-editor');
|
|
438
|
+
expect(editor).toBeInTheDocument();
|
|
439
|
+
|
|
440
|
+
// Verify essential elements
|
|
441
|
+
const toolbar = await canvas.findByTestId('editor-toolbar');
|
|
442
|
+
const content = await canvas.findByTestId('editor-content');
|
|
443
|
+
|
|
444
|
+
expect(toolbar).toBeInTheDocument();
|
|
445
|
+
expect(content).toBeInTheDocument();
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
#### 2. Toolbar Button Test
|
|
450
|
+
```typescript
|
|
451
|
+
play: async ({ canvasElement }) => {
|
|
452
|
+
const canvas = within(canvasElement);
|
|
453
|
+
|
|
454
|
+
// Get all toolbar buttons
|
|
455
|
+
const boldButton = await canvas.findByTestId('toolbar-bold');
|
|
456
|
+
const italicButton = await canvas.findByTestId('toolbar-italic');
|
|
457
|
+
const underlineButton = await canvas.findByTestId('toolbar-underline');
|
|
458
|
+
|
|
459
|
+
// Verify they're all present and enabled
|
|
460
|
+
expect(boldButton).toBeEnabled();
|
|
461
|
+
expect(italicButton).toBeEnabled();
|
|
462
|
+
expect(underlineButton).toBeEnabled();
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
#### 3. Text Formatting Test
|
|
467
|
+
```typescript
|
|
468
|
+
play: async ({ canvasElement }) => {
|
|
469
|
+
const canvas = within(canvasElement);
|
|
470
|
+
|
|
471
|
+
const content = await canvas.findByTestId('editor-content');
|
|
472
|
+
const boldButton = await canvas.findByTestId('toolbar-bold');
|
|
473
|
+
|
|
474
|
+
// Type text
|
|
475
|
+
await userEvent.click(content);
|
|
476
|
+
await userEvent.type(content, 'Test text');
|
|
477
|
+
|
|
478
|
+
// Select text and apply bold
|
|
479
|
+
await userEvent.keyboard('{Control>}a{/Control}');
|
|
480
|
+
await userEvent.click(boldButton);
|
|
481
|
+
|
|
482
|
+
// Verify HTML contains strong tag
|
|
483
|
+
await waitFor(() => {
|
|
484
|
+
expect(content.innerHTML).toContain('<strong>');
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
#### 4. Character Limit Test
|
|
490
|
+
```typescript
|
|
491
|
+
play: async ({ canvasElement }) => {
|
|
492
|
+
const canvas = within(canvasElement);
|
|
493
|
+
|
|
494
|
+
const content = await canvas.findByTestId('editor-content');
|
|
495
|
+
const counter = await canvas.findByTestId('editor-counter');
|
|
496
|
+
|
|
497
|
+
// Initial state
|
|
498
|
+
expect(counter).toHaveTextContent('0/100');
|
|
499
|
+
|
|
500
|
+
// Type text
|
|
501
|
+
await userEvent.click(content);
|
|
502
|
+
await userEvent.type(content, 'Hello');
|
|
503
|
+
|
|
504
|
+
// Counter should update
|
|
505
|
+
await waitFor(() => {
|
|
506
|
+
expect(counter).toHaveTextContent('5/100');
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// Try to exceed limit
|
|
510
|
+
const longText = 'x'.repeat(120);
|
|
511
|
+
await userEvent.type(content, longText);
|
|
512
|
+
|
|
513
|
+
// Should be limited to maxLength
|
|
514
|
+
await waitFor(() => {
|
|
515
|
+
const textLength = content.textContent?.length || 0;
|
|
516
|
+
expect(textLength).toBeLessThanOrEqual(100);
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
#### 5. Link Insertion Test
|
|
522
|
+
```typescript
|
|
523
|
+
play: async ({ canvasElement }) => {
|
|
524
|
+
const canvas = within(canvasElement);
|
|
525
|
+
|
|
526
|
+
// Mock window.prompt
|
|
527
|
+
const originalPrompt = window.prompt;
|
|
528
|
+
window.prompt = vi.fn().mockReturnValue('https://example.com');
|
|
529
|
+
|
|
530
|
+
const content = await canvas.findByTestId('editor-content');
|
|
531
|
+
const linkButton = await canvas.findByTestId('toolbar-link');
|
|
532
|
+
|
|
533
|
+
// Type text and select it
|
|
534
|
+
await userEvent.click(content);
|
|
535
|
+
await userEvent.type(content, 'Click here');
|
|
536
|
+
await userEvent.keyboard('{Control>}a{/Control}');
|
|
537
|
+
|
|
538
|
+
// Insert link
|
|
539
|
+
await userEvent.click(linkButton);
|
|
540
|
+
|
|
541
|
+
// Verify link was created
|
|
542
|
+
await waitFor(() => {
|
|
543
|
+
expect(content.innerHTML).toContain('<a');
|
|
544
|
+
expect(content.innerHTML).toContain('href="https://example.com"');
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
// Restore prompt
|
|
548
|
+
window.prompt = originalPrompt;
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
#### 6. List Formatting Test
|
|
553
|
+
```typescript
|
|
554
|
+
play: async ({ canvasElement }) => {
|
|
555
|
+
const canvas = within(canvasElement);
|
|
556
|
+
|
|
557
|
+
const content = await canvas.findByTestId('editor-content');
|
|
558
|
+
const orderedListButton = await canvas.findByTestId('toolbar-orderedList');
|
|
559
|
+
|
|
560
|
+
// Type text
|
|
561
|
+
await userEvent.click(content);
|
|
562
|
+
await userEvent.type(content, 'List item');
|
|
563
|
+
|
|
564
|
+
// Create ordered list
|
|
565
|
+
await userEvent.click(orderedListButton);
|
|
566
|
+
|
|
567
|
+
// Verify list HTML
|
|
568
|
+
await waitFor(() => {
|
|
569
|
+
expect(content.innerHTML).toContain('<ol>');
|
|
570
|
+
expect(content.innerHTML).toContain('<li>');
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
#### 7. Focus Management Test
|
|
576
|
+
```typescript
|
|
577
|
+
play: async ({ canvasElement }) => {
|
|
578
|
+
const canvas = within(canvasElement);
|
|
579
|
+
|
|
580
|
+
const content = await canvas.findByTestId('editor-content');
|
|
581
|
+
const boldButton = await canvas.findByTestId('toolbar-bold');
|
|
582
|
+
|
|
583
|
+
// Focus editor
|
|
584
|
+
await userEvent.click(content);
|
|
585
|
+
expect(content).toHaveFocus();
|
|
586
|
+
|
|
587
|
+
// Click toolbar button
|
|
588
|
+
await userEvent.click(boldButton);
|
|
589
|
+
|
|
590
|
+
// Focus should return to editor
|
|
591
|
+
await waitFor(() => {
|
|
592
|
+
expect(content).toHaveFocus();
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
#### 8. Custom Toolbar Items Test
|
|
598
|
+
```typescript
|
|
599
|
+
// Given: toolbar={{ customItems: [{ id: 'custom', label: 'Custom', icon: <Icon />, action: fn }] }}
|
|
600
|
+
play: async ({ canvasElement }) => {
|
|
601
|
+
const canvas = within(canvasElement);
|
|
602
|
+
|
|
603
|
+
const customButton = await canvas.findByTestId('toolbar-custom-custom');
|
|
604
|
+
expect(customButton).toBeInTheDocument();
|
|
605
|
+
|
|
606
|
+
await userEvent.click(customButton);
|
|
607
|
+
|
|
608
|
+
// Verify custom action was called (if using mock)
|
|
609
|
+
expect(args.toolbar.customItems[0].action).toHaveBeenCalled();
|
|
610
|
+
}
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
### Troubleshooting
|
|
614
|
+
|
|
615
|
+
#### Issue: "Unable to find element with testId X"
|
|
616
|
+
|
|
617
|
+
**Solution:** Always use `await` with `findByTestId`:
|
|
618
|
+
```typescript
|
|
619
|
+
// ❌ Wrong - synchronous
|
|
620
|
+
const element = canvas.getByTestId('rich-text-editor');
|
|
621
|
+
|
|
622
|
+
// ✅ Correct - asynchronous
|
|
623
|
+
const element = await canvas.findByTestId('rich-text-editor');
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
#### Issue: Toolbar button not found
|
|
627
|
+
|
|
628
|
+
**Solution:** Check if button is enabled in toolbar config:
|
|
629
|
+
```typescript
|
|
630
|
+
// Some buttons may be disabled via toolbar prop
|
|
631
|
+
const button = canvas.queryByTestId('toolbar-strikethrough');
|
|
632
|
+
if (button === null) {
|
|
633
|
+
// Button not configured in toolbar
|
|
634
|
+
}
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
#### Issue: Content changes not detected
|
|
638
|
+
|
|
639
|
+
**Solution:** Use `waitFor` for asynchronous content updates:
|
|
640
|
+
```typescript
|
|
641
|
+
await userEvent.type(content, 'text');
|
|
642
|
+
await waitFor(() => {
|
|
643
|
+
expect(content.innerHTML).toContain('text');
|
|
644
|
+
});
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
#### Issue: Character counter not visible
|
|
648
|
+
|
|
649
|
+
**Solution:** Counter only appears when `maxLength` prop is set:
|
|
650
|
+
```typescript
|
|
651
|
+
// This will fail if maxLength is not set
|
|
652
|
+
const counter = canvas.queryByTestId('editor-counter');
|
|
653
|
+
if (counter === null) {
|
|
654
|
+
// maxLength prop is not set on this instance
|
|
655
|
+
}
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
#### Issue: Prompt dialogs in tests
|
|
659
|
+
|
|
660
|
+
**Solution:** Mock `window.prompt` for link/image insertion:
|
|
661
|
+
```typescript
|
|
662
|
+
const originalPrompt = window.prompt;
|
|
663
|
+
window.prompt = vi.fn().mockReturnValue('https://example.com');
|
|
664
|
+
|
|
665
|
+
// ... perform test ...
|
|
666
|
+
|
|
667
|
+
window.prompt = originalPrompt;
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
#### Issue: HTML sanitization affects tests
|
|
671
|
+
|
|
672
|
+
**Solution:** Component uses DOMPurify - test with safe HTML:
|
|
673
|
+
```typescript
|
|
674
|
+
// Some tags/attributes may be stripped
|
|
675
|
+
// Check ALLOWED_TAGS and ALLOWED_ATTR in component source
|
|
676
|
+
await waitFor(() => {
|
|
677
|
+
// Test for sanitized HTML structure
|
|
678
|
+
expect(content.innerHTML).toContain('<p>');
|
|
679
|
+
expect(content.innerHTML).not.toContain('<script>');
|
|
680
|
+
});
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
### Testing Best Practices
|
|
684
|
+
|
|
685
|
+
1. **Always use async/await** with testId queries in Storybook tests
|
|
686
|
+
2. **Use `waitFor`** when testing content that updates after user interaction
|
|
687
|
+
3. **Scope queries** with `within()` when looking inside toolbar or content
|
|
688
|
+
4. **Mock prompts** when testing link/image insertion: `window.prompt = vi.fn()`
|
|
689
|
+
5. **Check toolbar config** before asserting button presence
|
|
690
|
+
6. **Test with sanitized HTML** - component uses DOMPurify security
|
|
691
|
+
7. **Verify focus behavior** - focus returns to editor after toolbar actions
|
|
692
|
+
8. **Test character limits** when `maxLength` is set
|
|
693
|
+
9. **Use regex patterns** for dynamic testIds: `/toolbar-custom-/`
|
|
694
|
+
10. **Check ARIA attributes** for accessibility testing
|
|
695
|
+
|
|
696
|
+
### Related Elements
|
|
697
|
+
|
|
698
|
+
- **Toolbar:** Contains all formatting buttons - query with `editor-toolbar`
|
|
699
|
+
- **Content Area:** Where user types - query with `editor-content` or role="textbox"
|
|
700
|
+
- **IconButtons:** Toolbar buttons - query individually by `toolbar-{name}`
|
|
701
|
+
- **Counter:** Character limit display - query with `editor-counter` (when visible)
|
|
702
|
+
- **Custom Items:** Dynamic toolbar buttons - query with `toolbar-custom-{id}`
|
|
703
|
+
|
|
704
|
+
### Security Notes
|
|
705
|
+
|
|
706
|
+
The component uses DOMPurify to sanitize HTML content. When testing:
|
|
707
|
+
- Scripts and dangerous tags are automatically removed
|
|
708
|
+
- Only allowed tags: p, br, strong, em, u, s, a, img, ul, ol, li, blockquote, pre, code, h1-h6, span, div
|
|
709
|
+
- Only allowed attributes: href, src, alt, target, rel, style, class
|
|
710
|
+
- Data attributes are not allowed (except data-testid on component root)
|