@fpkit/acss 0.5.12 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -0
- package/libs/chunk-2NRIP6RB.cjs +17 -0
- package/libs/chunk-2NRIP6RB.cjs.map +1 -0
- package/libs/chunk-33PNJ4LO.cjs +15 -0
- package/libs/chunk-33PNJ4LO.cjs.map +1 -0
- package/libs/chunk-4BZKFPEC.cjs +17 -0
- package/libs/chunk-4BZKFPEC.cjs.map +1 -0
- package/libs/{chunk-O6QZBB6G.js → chunk-5QD3DWFI.js} +5 -5
- package/libs/chunk-5QD3DWFI.js.map +1 -0
- package/libs/chunk-6SAHIYCZ.js +7 -0
- package/libs/chunk-6SAHIYCZ.js.map +1 -0
- package/libs/{chunk-KKLTUJFB.cjs → chunk-6WTC4JXH.cjs} +5 -5
- package/libs/chunk-6WTC4JXH.cjs.map +1 -0
- package/libs/chunk-75QHTLFO.js +7 -0
- package/libs/chunk-75QHTLFO.js.map +1 -0
- package/libs/{chunk-YWOYVRFT.js → chunk-7XPFW7CB.js} +3 -3
- package/libs/chunk-BFK62VX5.js +5 -0
- package/libs/chunk-BFK62VX5.js.map +1 -0
- package/libs/{chunk-ICCKQ2GC.cjs → chunk-DKTHCQ5P.cjs} +4 -4
- package/libs/{chunk-6TE5QEVE.cjs → chunk-E2AJURUW.cjs} +3 -3
- package/libs/chunk-E2AJURUW.cjs.map +1 -0
- package/libs/chunk-ENTCUJ3A.cjs +13 -0
- package/libs/chunk-ENTCUJ3A.cjs.map +1 -0
- package/libs/chunk-F5EYMVQM.js +10 -0
- package/libs/chunk-F5EYMVQM.js.map +1 -0
- package/libs/chunk-FVROL3V5.js +9 -0
- package/libs/chunk-FVROL3V5.js.map +1 -0
- package/libs/chunk-GT77BX4L.cjs +17 -0
- package/libs/chunk-GT77BX4L.cjs.map +1 -0
- package/libs/chunk-GUJSMQ3V.cjs +16 -0
- package/libs/chunk-GUJSMQ3V.cjs.map +1 -0
- package/libs/chunk-HHLNOC5T.js +7 -0
- package/libs/chunk-HHLNOC5T.js.map +1 -0
- package/libs/chunk-HRRHPLER.js +8 -0
- package/libs/chunk-HRRHPLER.js.map +1 -0
- package/libs/chunk-IEB64SWY.js +8 -0
- package/libs/chunk-IEB64SWY.js.map +1 -0
- package/libs/{chunk-LIQJ7ZZR.js → chunk-IQ76HGVP.js} +2 -2
- package/libs/chunk-IRLFZ3OL.js +9 -0
- package/libs/chunk-IRLFZ3OL.js.map +1 -0
- package/libs/chunk-KK47SYZI.js +8 -0
- package/libs/chunk-KK47SYZI.js.map +1 -0
- package/libs/chunk-O3JIHC5M.cjs +15 -0
- package/libs/chunk-O3JIHC5M.cjs.map +1 -0
- package/libs/chunk-O5XAJ7BY.cjs +18 -0
- package/libs/chunk-O5XAJ7BY.cjs.map +1 -0
- package/libs/chunk-OVWLQYMK.js +10 -0
- package/libs/chunk-OVWLQYMK.js.map +1 -0
- package/libs/chunk-PNWIRCG3.cjs +7 -0
- package/libs/chunk-PNWIRCG3.cjs.map +1 -0
- package/libs/chunk-QVW6W76L.cjs +18 -0
- package/libs/chunk-QVW6W76L.cjs.map +1 -0
- package/libs/chunk-T4T6GWYQ.cjs +17 -0
- package/libs/chunk-T4T6GWYQ.cjs.map +1 -0
- package/libs/chunk-TON2YGMD.cjs +9 -0
- package/libs/chunk-TON2YGMD.cjs.map +1 -0
- package/libs/chunk-UEPAWMDF.js +8 -0
- package/libs/chunk-UEPAWMDF.js.map +1 -0
- package/libs/{chunk-LT5KZ2QW.cjs → chunk-US2I5GI7.cjs} +3 -3
- package/libs/{chunk-E3XP6BEX.cjs → chunk-W2UIN7EV.cjs} +3 -3
- package/libs/chunk-W5TKWBFC.cjs +18 -0
- package/libs/chunk-W5TKWBFC.cjs.map +1 -0
- package/libs/chunk-WXBFBWYF.cjs +16 -0
- package/libs/chunk-WXBFBWYF.cjs.map +1 -0
- package/libs/chunk-X3JCTEPD.js +11 -0
- package/libs/chunk-X3JCTEPD.js.map +1 -0
- package/libs/chunk-X5LGFCWG.js +9 -0
- package/libs/chunk-X5LGFCWG.js.map +1 -0
- package/libs/{chunk-5M57K4SW.js → chunk-Y2PFDELK.js} +2 -2
- package/libs/chunk-ZFJ4U45S.js +10 -0
- package/libs/chunk-ZFJ4U45S.js.map +1 -0
- package/libs/{component-props-a8a2f97e.d.ts → component-props-67d978a2.d.ts} +4 -4
- package/libs/components/alert/alert.css +1 -1
- package/libs/components/alert/alert.css.map +1 -1
- package/libs/components/alert/alert.min.css +2 -2
- package/libs/components/badge/badge.css +1 -1
- package/libs/components/badge/badge.css.map +1 -1
- package/libs/components/badge/badge.min.css +2 -2
- package/libs/components/breadcrumbs/breadcrumb.cjs +9 -5
- package/libs/components/breadcrumbs/breadcrumb.d.cts +275 -36
- package/libs/components/breadcrumbs/breadcrumb.d.ts +275 -36
- package/libs/components/breadcrumbs/breadcrumb.js +3 -3
- package/libs/components/button.cjs +6 -4
- package/libs/components/button.d.cts +97 -4
- package/libs/components/button.d.ts +97 -4
- package/libs/components/button.js +4 -2
- package/libs/components/buttons/button.css +1 -1
- package/libs/components/buttons/button.css.map +1 -1
- package/libs/components/buttons/button.min.css +2 -2
- package/libs/components/card.cjs +7 -7
- package/libs/components/card.d.cts +278 -34
- package/libs/components/card.d.ts +278 -34
- package/libs/components/card.js +2 -2
- package/libs/components/cards/card.css +1 -1
- package/libs/components/cards/card.css.map +1 -1
- package/libs/components/cards/card.min.css +2 -2
- package/libs/components/details/details.css +1 -1
- package/libs/components/details/details.css.map +1 -1
- package/libs/components/details/details.min.css +2 -2
- package/libs/components/dialog/dialog.cjs +9 -7
- package/libs/components/dialog/dialog.css +1 -1
- package/libs/components/dialog/dialog.css.map +1 -1
- package/libs/components/dialog/dialog.d.cts +88 -34
- package/libs/components/dialog/dialog.d.ts +88 -34
- package/libs/components/dialog/dialog.js +7 -5
- package/libs/components/dialog/dialog.min.css +2 -2
- package/libs/components/form/fields.cjs +4 -4
- package/libs/components/form/fields.d.cts +16 -7
- package/libs/components/form/fields.d.ts +16 -7
- package/libs/components/form/fields.js +2 -2
- package/libs/components/form/inputs.cjs +6 -4
- package/libs/components/form/inputs.d.cts +50 -2
- package/libs/components/form/inputs.d.ts +50 -2
- package/libs/components/form/inputs.js +4 -2
- package/libs/components/form/textarea.cjs +5 -4
- package/libs/components/form/textarea.d.cts +32 -23
- package/libs/components/form/textarea.d.ts +32 -23
- package/libs/components/form/textarea.js +3 -2
- package/libs/components/heading/heading.cjs +3 -3
- package/libs/components/heading/heading.d.cts +3 -14
- package/libs/components/heading/heading.d.ts +3 -14
- package/libs/components/heading/heading.js +2 -2
- package/libs/components/icons/icon.cjs +4 -4
- package/libs/components/icons/icon.d.cts +183 -39
- package/libs/components/icons/icon.d.ts +183 -39
- package/libs/components/icons/icon.js +2 -2
- package/libs/components/images/img.css +1 -1
- package/libs/components/images/img.css.map +1 -1
- package/libs/components/images/img.min.css +2 -2
- package/libs/components/link/link.cjs +4 -4
- package/libs/components/link/link.css +1 -1
- package/libs/components/link/link.css.map +1 -1
- package/libs/components/link/link.d.cts +3 -19
- package/libs/components/link/link.d.ts +3 -19
- package/libs/components/link/link.js +2 -2
- package/libs/components/link/link.min.css +2 -2
- package/libs/components/list/list.cjs +5 -5
- package/libs/components/list/list.css +1 -0
- package/libs/components/list/list.css.map +1 -0
- package/libs/components/list/list.d.cts +120 -33
- package/libs/components/list/list.d.ts +120 -33
- package/libs/components/list/list.js +2 -2
- package/libs/components/list/list.min.css +3 -0
- package/libs/components/modal.cjs +6 -4
- package/libs/components/modal.d.cts +8 -8
- package/libs/components/modal.d.ts +8 -8
- package/libs/components/modal.js +5 -3
- package/libs/components/nav/nav.cjs +7 -7
- package/libs/components/nav/nav.css +1 -1
- package/libs/components/nav/nav.css.map +1 -1
- package/libs/components/nav/nav.d.cts +550 -34
- package/libs/components/nav/nav.d.ts +550 -34
- package/libs/components/nav/nav.js +3 -3
- package/libs/components/nav/nav.min.css +2 -2
- package/libs/components/popover/popover.d.cts +5 -5
- package/libs/components/popover/popover.d.ts +5 -5
- package/libs/components/tables/table.cjs +5 -5
- package/libs/components/tables/table.d.cts +8 -8
- package/libs/components/tables/table.d.ts +8 -8
- package/libs/components/tables/table.js +2 -2
- package/libs/components/tag/tag.css +1 -1
- package/libs/components/tag/tag.css.map +1 -1
- package/libs/components/tag/tag.min.css +2 -2
- package/libs/components/text/text.cjs +5 -5
- package/libs/components/text/text.d.cts +5 -5
- package/libs/components/text/text.d.ts +5 -5
- package/libs/components/text/text.js +2 -2
- package/libs/form.types-d25ebfac.d.ts +233 -0
- package/libs/heading-7446cb46.d.ts +250 -0
- package/libs/hooks.cjs +12 -0
- package/libs/hooks.d.cts +140 -1
- package/libs/hooks.d.ts +140 -1
- package/libs/hooks.js +4 -0
- package/libs/icons.cjs +3 -3
- package/libs/icons.d.cts +2 -2
- package/libs/icons.d.ts +2 -2
- package/libs/icons.js +2 -2
- package/libs/index.cjs +117 -94
- package/libs/index.cjs.map +1 -1
- package/libs/index.css +1 -1
- package/libs/index.css.map +1 -1
- package/libs/index.d.cts +834 -61
- package/libs/index.d.ts +834 -61
- package/libs/index.js +36 -22
- package/libs/index.js.map +1 -1
- package/libs/link-5192f411.d.ts +323 -0
- package/libs/list.types-d26de310.d.ts +245 -0
- package/libs/ui-d01b50d4.d.ts +289 -0
- package/package.json +4 -87
- package/src/components/README-UI.mdx +416 -0
- package/src/components/alert/ACCESSIBILITY.md +319 -0
- package/src/components/alert/README.mdx +475 -19
- package/src/components/alert/alert.scss +110 -6
- package/src/components/alert/alert.stories.tsx +372 -0
- package/src/components/alert/alert.test.tsx +762 -0
- package/src/components/alert/alert.tsx +331 -66
- package/src/components/alert/views/alert-actions.tsx +13 -0
- package/src/components/alert/views/alert-content.tsx +17 -0
- package/src/components/alert/views/alert-icon.tsx +53 -0
- package/src/components/alert/views/alert-screen-reader-text.tsx +30 -0
- package/src/components/alert/views/alert-title.tsx +23 -0
- package/src/components/alert/views/alert-view.tsx +158 -0
- package/src/components/alert/views/index.ts +12 -0
- package/src/components/badge/badge.mdx +186 -49
- package/src/components/badge/badge.scss +20 -2
- package/src/components/badge/badge.stories.tsx +160 -14
- package/src/components/badge/badge.test.tsx +179 -0
- package/src/components/badge/badge.tsx +97 -4
- package/src/components/breadcrumbs/README.mdx +364 -45
- package/src/components/breadcrumbs/__snapshots__/breadcrumb.test.tsx.snap +152 -0
- package/src/components/breadcrumbs/breadcrumb.stories.tsx +7 -3
- package/src/components/breadcrumbs/breadcrumb.test.tsx +490 -0
- package/src/components/breadcrumbs/breadcrumb.tsx +430 -170
- package/src/components/buttons/README.mdx +102 -1
- package/src/components/buttons/button.scss +34 -31
- package/src/components/buttons/button.stories.tsx +141 -0
- package/src/components/buttons/button.tsx +82 -52
- package/src/components/cards/README.mdx +657 -0
- package/src/components/cards/card.scss +22 -0
- package/src/components/cards/card.stories.tsx +167 -5
- package/src/components/cards/card.test.tsx +360 -20
- package/src/components/cards/card.tsx +200 -79
- package/src/components/cards/card.types.ts +135 -0
- package/src/components/cards/card.utils.ts +79 -0
- package/src/components/details/ACCESSIBILITY-REVIEW-LIVE.md +1050 -0
- package/src/components/details/ACCESSIBILITY-REVIEW.md +502 -0
- package/src/components/details/README.mdx +437 -69
- package/src/components/details/details.scss +16 -7
- package/src/components/details/details.test.tsx +385 -0
- package/src/components/details/details.tsx +101 -69
- package/src/components/details/details.types.ts +76 -0
- package/src/components/dialog/README.mdx +513 -110
- package/src/components/dialog/dialog-a11y-review.md +653 -0
- package/src/components/dialog/dialog-modal.tsx +79 -56
- package/src/components/dialog/dialog.scss +53 -3
- package/src/components/dialog/dialog.stories.tsx +10 -7
- package/src/components/dialog/dialog.test.tsx +450 -0
- package/src/components/dialog/dialog.tsx +69 -59
- package/src/components/dialog/dialog.types.ts +133 -0
- package/src/components/dialog/views/dialog-footer.tsx +54 -11
- package/src/components/dialog/views/dialog-header.tsx +20 -15
- package/src/components/form/README.mdx +725 -43
- package/src/components/form/WCAG-REVIEW.md +654 -0
- package/src/components/form/fields.tsx +10 -1
- package/src/components/form/form.stories.tsx +604 -23
- package/src/components/form/form.tsx +204 -63
- package/src/components/form/form.types.ts +378 -0
- package/src/components/form/input.stories.tsx +71 -3
- package/src/components/form/inputs.tsx +159 -67
- package/src/components/form/select.tsx +122 -66
- package/src/components/form/textarea.tsx +120 -73
- package/src/components/fp.tsx +86 -11
- package/src/components/heading/heading.stories.tsx +44 -4
- package/src/components/heading/heading.tsx +89 -23
- package/src/components/icons/README.mdx +332 -0
- package/src/components/icons/icon.stories.tsx +74 -1
- package/src/components/icons/icon.tsx +89 -1
- package/src/components/icons/types.ts +47 -0
- package/src/components/images/README.mdx +340 -24
- package/src/components/images/img.scss +19 -3
- package/src/components/images/img.stories.tsx +424 -15
- package/src/components/images/img.test.tsx +354 -25
- package/src/components/images/img.tsx +186 -63
- package/src/components/images/img.types.ts +211 -0
- package/src/components/link/README.mdx +923 -0
- package/src/components/link/link.scss +79 -26
- package/src/components/link/link.stories.tsx +383 -30
- package/src/components/link/link.test.tsx +677 -0
- package/src/components/link/link.tsx +163 -57
- package/src/components/link/link.types.ts +261 -0
- package/src/components/list/README.mdx +764 -0
- package/src/components/list/list.scss +285 -0
- package/src/components/list/list.stories.tsx +514 -27
- package/src/components/list/list.test.tsx +554 -0
- package/src/components/list/list.tsx +153 -51
- package/src/components/list/list.types.ts +255 -0
- package/src/components/nav/ACCESSIBILITY.md +649 -0
- package/src/components/nav/README.mdx +782 -0
- package/src/components/nav/nav.scss +32 -1
- package/src/components/nav/nav.stories.tsx +44 -6
- package/src/components/nav/nav.tsx +302 -51
- package/src/components/nav/nav.types.ts +308 -0
- package/src/components/tag/README.mdx +426 -0
- package/src/components/tag/tag.scss +101 -27
- package/src/components/tag/tag.stories.tsx +384 -10
- package/src/components/tag/tag.test.tsx +210 -0
- package/src/components/tag/tag.tsx +106 -9
- package/src/components/tag/tag.types.ts +107 -0
- package/src/components/title/MIGRATION.md +199 -0
- package/src/components/title/README.md +326 -0
- package/src/components/title/README.mdx +452 -0
- package/src/components/title/title.stories.tsx +393 -0
- package/src/components/title/title.test.tsx +251 -0
- package/src/components/title/title.tsx +219 -0
- package/src/components/ui.stories.tsx +894 -0
- package/src/components/ui.test.tsx +559 -0
- package/src/components/ui.tsx +274 -18
- package/src/components/word-count/README.md +240 -0
- package/src/hooks/use-disabled-state.test.tsx +536 -0
- package/src/hooks/use-disabled-state.ts +246 -0
- package/src/hooks/useDisabledState.md +393 -0
- package/src/hooks.ts +7 -0
- package/src/index.scss +2 -0
- package/src/index.ts +12 -3
- package/src/sass/_globals.scss +2 -7
- package/src/sass/_properties.scss +1 -0
- package/src/styles/alert/alert.css +92 -4
- package/src/styles/alert/alert.css.map +1 -1
- package/src/styles/badge/badge.css +20 -2
- package/src/styles/badge/badge.css.map +1 -1
- package/src/styles/buttons/button.css +31 -31
- package/src/styles/buttons/button.css.map +1 -1
- package/src/styles/cards/card.css +16 -0
- package/src/styles/cards/card.css.map +1 -1
- package/src/styles/details/details.css +19 -8
- package/src/styles/details/details.css.map +1 -1
- package/src/styles/dialog/dialog.css +43 -2
- package/src/styles/dialog/dialog.css.map +1 -1
- package/src/styles/images/img.css +15 -3
- package/src/styles/images/img.css.map +1 -1
- package/src/styles/index.css +691 -128
- package/src/styles/index.css.map +1 -1
- package/src/styles/link/link.css +45 -28
- package/src/styles/link/link.css.map +1 -1
- package/src/styles/list/list.css +214 -0
- package/src/styles/list/list.css.map +1 -0
- package/src/styles/nav/nav.css +21 -1
- package/src/styles/nav/nav.css.map +1 -1
- package/src/styles/tag/tag.css +113 -35
- package/src/styles/tag/tag.css.map +1 -1
- package/src/styles/utilities/_disabled.scss +58 -0
- package/src/test/setup.d.ts +9 -0
- package/src/test/setup.ts +53 -1
- package/src/types/shared.ts +43 -6
- package/src/utils/accessibility.ts +109 -0
- package/libs/chunk-5ZM4XL44.js +0 -8
- package/libs/chunk-5ZM4XL44.js.map +0 -1
- package/libs/chunk-6BVXFW7U.cjs +0 -15
- package/libs/chunk-6BVXFW7U.cjs.map +0 -1
- package/libs/chunk-6TE5QEVE.cjs.map +0 -1
- package/libs/chunk-7K76RW2A.cjs +0 -18
- package/libs/chunk-7K76RW2A.cjs.map +0 -1
- package/libs/chunk-BHRQBJRY.js +0 -8
- package/libs/chunk-BHRQBJRY.js.map +0 -1
- package/libs/chunk-BIP2NY53.js +0 -8
- package/libs/chunk-BIP2NY53.js.map +0 -1
- package/libs/chunk-BSPKFLO4.js +0 -8
- package/libs/chunk-BSPKFLO4.js.map +0 -1
- package/libs/chunk-BV5CLH44.cjs +0 -18
- package/libs/chunk-BV5CLH44.cjs.map +0 -1
- package/libs/chunk-DKGJHKGW.js +0 -9
- package/libs/chunk-DKGJHKGW.js.map +0 -1
- package/libs/chunk-DV56L5YX.cjs +0 -18
- package/libs/chunk-DV56L5YX.cjs.map +0 -1
- package/libs/chunk-ECLD37WN.cjs +0 -16
- package/libs/chunk-ECLD37WN.cjs.map +0 -1
- package/libs/chunk-EQ67LF46.js +0 -9
- package/libs/chunk-EQ67LF46.js.map +0 -1
- package/libs/chunk-HYBZBN4G.js +0 -8
- package/libs/chunk-HYBZBN4G.js.map +0 -1
- package/libs/chunk-IYUN2EW3.cjs +0 -15
- package/libs/chunk-IYUN2EW3.cjs.map +0 -1
- package/libs/chunk-KKLTUJFB.cjs.map +0 -1
- package/libs/chunk-LHVJKDMA.cjs +0 -15
- package/libs/chunk-LHVJKDMA.cjs.map +0 -1
- package/libs/chunk-LL7HTLMS.cjs +0 -15
- package/libs/chunk-LL7HTLMS.cjs.map +0 -1
- package/libs/chunk-M5QL5TAE.cjs +0 -14
- package/libs/chunk-M5QL5TAE.cjs.map +0 -1
- package/libs/chunk-NE6YXTMC.js +0 -7
- package/libs/chunk-NE6YXTMC.js.map +0 -1
- package/libs/chunk-NHYXGV3L.js +0 -8
- package/libs/chunk-NHYXGV3L.js.map +0 -1
- package/libs/chunk-O6QZBB6G.js.map +0 -1
- package/libs/chunk-P7TTEYCD.js +0 -7
- package/libs/chunk-P7TTEYCD.js.map +0 -1
- package/libs/chunk-PPOOBUOS.js +0 -8
- package/libs/chunk-PPOOBUOS.js.map +0 -1
- package/libs/chunk-QCMV4VQZ.js +0 -8
- package/libs/chunk-QCMV4VQZ.js.map +0 -1
- package/libs/chunk-QVV34QEH.cjs +0 -32
- package/libs/chunk-QVV34QEH.cjs.map +0 -1
- package/libs/chunk-S7BABR7Z.cjs +0 -13
- package/libs/chunk-S7BABR7Z.cjs.map +0 -1
- package/libs/chunk-SXVZSWX6.js +0 -11
- package/libs/chunk-SXVZSWX6.js.map +0 -1
- package/libs/chunk-X3EVB7VS.cjs +0 -15
- package/libs/chunk-X3EVB7VS.cjs.map +0 -1
- package/libs/inputs-f3a216db.d.ts +0 -45
- package/libs/ui-9a6f9f8d.d.ts +0 -24
- package/src/components/cards/README.md +0 -80
- package/src/components/dialog/hooks/useClickOutside.ts +0 -33
- /package/libs/{chunk-YWOYVRFT.js.map → chunk-7XPFW7CB.js.map} +0 -0
- /package/libs/{chunk-ICCKQ2GC.cjs.map → chunk-DKTHCQ5P.cjs.map} +0 -0
- /package/libs/{chunk-LIQJ7ZZR.js.map → chunk-IQ76HGVP.js.map} +0 -0
- /package/libs/{chunk-LT5KZ2QW.cjs.map → chunk-US2I5GI7.cjs.map} +0 -0
- /package/libs/{chunk-E3XP6BEX.cjs.map → chunk-W2UIN7EV.cjs.map} +0 -0
- /package/libs/{chunk-5M57K4SW.js.map → chunk-Y2PFDELK.js.map} +0 -0
|
@@ -0,0 +1,762 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
3
|
+
import { render, screen, waitFor, act } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import Alert from './alert';
|
|
6
|
+
|
|
7
|
+
describe('Alert', () => {
|
|
8
|
+
describe('Rendering', () => {
|
|
9
|
+
it('should render alert with title and message', () => {
|
|
10
|
+
render(
|
|
11
|
+
<Alert open={true} title="Test Title" severity="info">
|
|
12
|
+
Test message
|
|
13
|
+
</Alert>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
expect(screen.getByRole('alert')).toBeInTheDocument();
|
|
17
|
+
expect(screen.getByText('Test Title')).toBeInTheDocument();
|
|
18
|
+
expect(screen.getByText('Test message')).toBeInTheDocument();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should not render when open is false', () => {
|
|
22
|
+
render(
|
|
23
|
+
<Alert open={false} severity="info">
|
|
24
|
+
Test message
|
|
25
|
+
</Alert>
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should render all severity levels correctly', () => {
|
|
32
|
+
const severities = ['default', 'info', 'success', 'warning', 'error'] as const;
|
|
33
|
+
|
|
34
|
+
severities.forEach((severity) => {
|
|
35
|
+
const { unmount } = render(
|
|
36
|
+
<Alert open={true} severity={severity}>
|
|
37
|
+
{severity} message
|
|
38
|
+
</Alert>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const alert = screen.getByRole('alert');
|
|
42
|
+
expect(alert).toHaveAttribute('data-alert', severity);
|
|
43
|
+
unmount();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should hide icon when hideIcon is true', () => {
|
|
48
|
+
const { container } = render(
|
|
49
|
+
<Alert open={true} severity="info" hideIcon={true}>
|
|
50
|
+
Test message
|
|
51
|
+
</Alert>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
expect(container.querySelector('.alert-icon')).not.toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should show icon by default', () => {
|
|
58
|
+
const { container } = render(
|
|
59
|
+
<Alert open={true} severity="info">
|
|
60
|
+
Test message
|
|
61
|
+
</Alert>
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
expect(container.querySelector('.alert-icon')).toBeInTheDocument();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should render with custom icon size', () => {
|
|
68
|
+
const { container } = render(
|
|
69
|
+
<Alert open={true} severity="info" iconSize={32}>
|
|
70
|
+
Test message
|
|
71
|
+
</Alert>
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const icon = container.querySelector('.alert-icon svg');
|
|
75
|
+
expect(icon).toBeInTheDocument();
|
|
76
|
+
// Note: Actual size verification would require checking the SVG element's attributes
|
|
77
|
+
// which depends on the Icon component implementation
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should use default icon size of 24px when iconSize not specified', () => {
|
|
81
|
+
const { container } = render(
|
|
82
|
+
<Alert open={true} severity="info">
|
|
83
|
+
Test message
|
|
84
|
+
</Alert>
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const iconContainer = container.querySelector('.alert-icon');
|
|
88
|
+
expect(iconContainer).toBeInTheDocument();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should render actions when provided', () => {
|
|
92
|
+
render(
|
|
93
|
+
<Alert
|
|
94
|
+
open={true}
|
|
95
|
+
severity="info"
|
|
96
|
+
actions={
|
|
97
|
+
<>
|
|
98
|
+
<button>Undo</button>
|
|
99
|
+
<button>Dismiss</button>
|
|
100
|
+
</>
|
|
101
|
+
}
|
|
102
|
+
>
|
|
103
|
+
Test message
|
|
104
|
+
</Alert>
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
expect(screen.getByText('Undo')).toBeInTheDocument();
|
|
108
|
+
expect(screen.getByText('Dismiss')).toBeInTheDocument();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should render dismiss button when dismissible is true', () => {
|
|
112
|
+
render(
|
|
113
|
+
<Alert open={true} severity="info" dismissible={true}>
|
|
114
|
+
Test message
|
|
115
|
+
</Alert>
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(screen.getByRole('button', { name: /close alert/i })).toBeInTheDocument();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should apply correct variant attribute', () => {
|
|
122
|
+
const variants = ['outlined', 'filled', 'soft'] as const;
|
|
123
|
+
|
|
124
|
+
variants.forEach((variant) => {
|
|
125
|
+
const { unmount } = render(
|
|
126
|
+
<Alert open={true} severity="info" variant={variant}>
|
|
127
|
+
Test message
|
|
128
|
+
</Alert>
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const alert = screen.getByRole('alert');
|
|
132
|
+
expect(alert).toHaveAttribute('data-variant', variant);
|
|
133
|
+
unmount();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('Interactions', () => {
|
|
139
|
+
it('should call onDismiss when dismiss button is clicked', async () => {
|
|
140
|
+
const user = userEvent.setup();
|
|
141
|
+
const onDismiss = vi.fn();
|
|
142
|
+
|
|
143
|
+
render(
|
|
144
|
+
<Alert open={true} severity="info" dismissible={true} onDismiss={onDismiss}>
|
|
145
|
+
Test message
|
|
146
|
+
</Alert>
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const dismissButton = screen.getByRole('button', { name: /close alert/i });
|
|
150
|
+
await user.click(dismissButton);
|
|
151
|
+
|
|
152
|
+
// Wait for animation timeout (300ms)
|
|
153
|
+
await waitFor(() => {
|
|
154
|
+
expect(onDismiss).toHaveBeenCalledTimes(1);
|
|
155
|
+
}, { timeout: 500 });
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should dismiss alert when ESC key is pressed', async () => {
|
|
159
|
+
const user = userEvent.setup();
|
|
160
|
+
const onDismiss = vi.fn();
|
|
161
|
+
|
|
162
|
+
render(
|
|
163
|
+
<Alert open={true} severity="info" dismissible={true} onDismiss={onDismiss}>
|
|
164
|
+
Test message
|
|
165
|
+
</Alert>
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
await user.keyboard('{Escape}');
|
|
169
|
+
|
|
170
|
+
// Wait for animation timeout
|
|
171
|
+
await waitFor(() => {
|
|
172
|
+
expect(onDismiss).toHaveBeenCalledTimes(1);
|
|
173
|
+
}, { timeout: 500 });
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should not dismiss with ESC key when not dismissible', async () => {
|
|
177
|
+
const user = userEvent.setup();
|
|
178
|
+
const onDismiss = vi.fn();
|
|
179
|
+
|
|
180
|
+
render(
|
|
181
|
+
<Alert open={true} severity="info" dismissible={false} onDismiss={onDismiss}>
|
|
182
|
+
Test message
|
|
183
|
+
</Alert>
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
await user.keyboard('{Escape}');
|
|
187
|
+
|
|
188
|
+
// Wait a bit to ensure it doesn't dismiss
|
|
189
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
190
|
+
expect(onDismiss).not.toHaveBeenCalled();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should set data-visible to false when dismissing', async () => {
|
|
194
|
+
const user = userEvent.setup();
|
|
195
|
+
|
|
196
|
+
render(
|
|
197
|
+
<Alert open={true} severity="info" dismissible={true}>
|
|
198
|
+
Test message
|
|
199
|
+
</Alert>
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const alert = screen.getByRole('alert');
|
|
203
|
+
expect(alert).toHaveAttribute('data-visible', 'true');
|
|
204
|
+
|
|
205
|
+
const dismissButton = screen.getByRole('button', { name: /close alert/i });
|
|
206
|
+
await user.click(dismissButton);
|
|
207
|
+
|
|
208
|
+
// Check that data-visible changes immediately
|
|
209
|
+
expect(alert).toHaveAttribute('data-visible', 'false');
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe('Auto-dismiss', () => {
|
|
214
|
+
beforeEach(() => {
|
|
215
|
+
vi.useFakeTimers();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
afterEach(() => {
|
|
219
|
+
vi.useRealTimers();
|
|
220
|
+
vi.restoreAllMocks();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('should auto-dismiss after specified duration', async () => {
|
|
224
|
+
const onDismiss = vi.fn();
|
|
225
|
+
|
|
226
|
+
render(
|
|
227
|
+
<Alert
|
|
228
|
+
open={true}
|
|
229
|
+
severity="success"
|
|
230
|
+
dismissible={true}
|
|
231
|
+
autoHideDuration={3000}
|
|
232
|
+
onDismiss={onDismiss}
|
|
233
|
+
>
|
|
234
|
+
Auto-dismiss message
|
|
235
|
+
</Alert>
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
// Fast-forward time by 3000ms (auto-hide duration)
|
|
239
|
+
act(() => {
|
|
240
|
+
vi.advanceTimersByTime(3000);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Fast-forward by 300ms for animation
|
|
244
|
+
await act(async () => {
|
|
245
|
+
vi.advanceTimersByTime(300);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
expect(onDismiss).toHaveBeenCalledTimes(1);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should not auto-dismiss when autoHideDuration is 0', async () => {
|
|
252
|
+
const onDismiss = vi.fn();
|
|
253
|
+
|
|
254
|
+
render(
|
|
255
|
+
<Alert
|
|
256
|
+
open={true}
|
|
257
|
+
severity="success"
|
|
258
|
+
autoHideDuration={0}
|
|
259
|
+
onDismiss={onDismiss}
|
|
260
|
+
>
|
|
261
|
+
No auto-dismiss
|
|
262
|
+
</Alert>
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
vi.advanceTimersByTime(5000);
|
|
266
|
+
|
|
267
|
+
expect(onDismiss).not.toHaveBeenCalled();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should not auto-dismiss when autoHideDuration is undefined', async () => {
|
|
271
|
+
const onDismiss = vi.fn();
|
|
272
|
+
|
|
273
|
+
render(
|
|
274
|
+
<Alert
|
|
275
|
+
open={true}
|
|
276
|
+
severity="success"
|
|
277
|
+
onDismiss={onDismiss}
|
|
278
|
+
>
|
|
279
|
+
No auto-dismiss
|
|
280
|
+
</Alert>
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
vi.advanceTimersByTime(5000);
|
|
284
|
+
|
|
285
|
+
expect(onDismiss).not.toHaveBeenCalled();
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
describe('Accessibility', () => {
|
|
290
|
+
it('should have correct aria-live for error severity', () => {
|
|
291
|
+
render(
|
|
292
|
+
<Alert open={true} severity="error">
|
|
293
|
+
Error message
|
|
294
|
+
</Alert>
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
const alert = screen.getByRole('alert');
|
|
298
|
+
expect(alert).toHaveAttribute('aria-live', 'assertive');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('should have correct aria-live for non-error severities', () => {
|
|
302
|
+
const severities = ['default', 'info', 'success', 'warning'] as const;
|
|
303
|
+
|
|
304
|
+
severities.forEach((severity) => {
|
|
305
|
+
const { unmount } = render(
|
|
306
|
+
<Alert open={true} severity={severity}>
|
|
307
|
+
Test message
|
|
308
|
+
</Alert>
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
const alert = screen.getByRole('alert');
|
|
312
|
+
expect(alert).toHaveAttribute('aria-live', 'polite');
|
|
313
|
+
unmount();
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should have aria-atomic attribute', () => {
|
|
318
|
+
render(
|
|
319
|
+
<Alert open={true} severity="info">
|
|
320
|
+
Test message
|
|
321
|
+
</Alert>
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const alert = screen.getByRole('alert');
|
|
325
|
+
expect(alert).toHaveAttribute('aria-atomic', 'true');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('should have role="alert"', () => {
|
|
329
|
+
render(
|
|
330
|
+
<Alert open={true} severity="info">
|
|
331
|
+
Test message
|
|
332
|
+
</Alert>
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
expect(screen.getByRole('alert')).toBeInTheDocument();
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('should focus alert when autoFocus is true', () => {
|
|
339
|
+
render(
|
|
340
|
+
<Alert open={true} severity="error" autoFocus={true}>
|
|
341
|
+
Critical alert
|
|
342
|
+
</Alert>
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
const alert = screen.getByRole('alert');
|
|
346
|
+
expect(alert).toHaveFocus();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('should not focus alert when autoFocus is false', () => {
|
|
350
|
+
render(
|
|
351
|
+
<Alert open={true} severity="info" autoFocus={false}>
|
|
352
|
+
Normal alert
|
|
353
|
+
</Alert>
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
const alert = screen.getByRole('alert');
|
|
357
|
+
expect(alert).not.toHaveFocus();
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('should have tabIndex=-1 when autoFocus is true', () => {
|
|
361
|
+
render(
|
|
362
|
+
<Alert open={true} severity="error" autoFocus={true}>
|
|
363
|
+
Critical alert
|
|
364
|
+
</Alert>
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
const alert = screen.getByRole('alert');
|
|
368
|
+
expect(alert).toHaveAttribute('tabIndex', '-1');
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
describe('Animation State', () => {
|
|
373
|
+
it('should start with data-visible=true when open=true', () => {
|
|
374
|
+
render(
|
|
375
|
+
<Alert open={true} severity="info">
|
|
376
|
+
Test message
|
|
377
|
+
</Alert>
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const alert = screen.getByRole('alert');
|
|
381
|
+
expect(alert).toHaveAttribute('data-visible', 'true');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should transition visibility states when dismissing', async () => {
|
|
385
|
+
const user = userEvent.setup();
|
|
386
|
+
|
|
387
|
+
render(
|
|
388
|
+
<Alert open={true} severity="info" dismissible={true}>
|
|
389
|
+
Test message
|
|
390
|
+
</Alert>
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
const alert = screen.getByRole('alert');
|
|
394
|
+
expect(alert).toHaveAttribute('data-visible', 'true');
|
|
395
|
+
|
|
396
|
+
const dismissButton = screen.getByRole('button', { name: /close alert/i });
|
|
397
|
+
await user.click(dismissButton);
|
|
398
|
+
|
|
399
|
+
// After click, visibility should be false but component still rendered
|
|
400
|
+
expect(alert).toHaveAttribute('data-visible', 'false');
|
|
401
|
+
expect(screen.getByRole('alert')).toBeInTheDocument();
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
describe('Phase 4: WCAG 2.1 Accessibility', () => {
|
|
406
|
+
describe('Severity Text for Screen Readers', () => {
|
|
407
|
+
it('should include visually hidden severity text for info', () => {
|
|
408
|
+
const { container } = render(
|
|
409
|
+
<Alert open={true} severity="info">
|
|
410
|
+
Test message
|
|
411
|
+
</Alert>
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
const srOnlyText = container.querySelector('.sr-only');
|
|
415
|
+
expect(srOnlyText).toBeInTheDocument();
|
|
416
|
+
expect(srOnlyText).toHaveTextContent('Information:');
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('should include visually hidden severity text for success', () => {
|
|
420
|
+
const { container } = render(
|
|
421
|
+
<Alert open={true} severity="success">
|
|
422
|
+
Test message
|
|
423
|
+
</Alert>
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
const srOnlyText = container.querySelector('.sr-only');
|
|
427
|
+
expect(srOnlyText).toHaveTextContent('Success:');
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('should include visually hidden severity text for warning', () => {
|
|
431
|
+
const { container } = render(
|
|
432
|
+
<Alert open={true} severity="warning">
|
|
433
|
+
Test message
|
|
434
|
+
</Alert>
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
const srOnlyText = container.querySelector('.sr-only');
|
|
438
|
+
expect(srOnlyText).toHaveTextContent('Warning:');
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('should include visually hidden severity text for error', () => {
|
|
442
|
+
const { container } = render(
|
|
443
|
+
<Alert open={true} severity="error">
|
|
444
|
+
Test message
|
|
445
|
+
</Alert>
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
const srOnlyText = container.querySelector('.sr-only');
|
|
449
|
+
expect(srOnlyText).toHaveTextContent('Error:');
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('should not include severity text for default severity', () => {
|
|
453
|
+
const { container } = render(
|
|
454
|
+
<Alert open={true} severity="default">
|
|
455
|
+
Test message
|
|
456
|
+
</Alert>
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
const srOnlyText = container.querySelector('.sr-only');
|
|
460
|
+
expect(srOnlyText).not.toBeInTheDocument();
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
describe('Configurable Heading Level', () => {
|
|
465
|
+
it('should render h2 when titleLevel is 2', () => {
|
|
466
|
+
const { container } = render(
|
|
467
|
+
<Alert open={true} severity="info" title="Test Title" titleLevel={2}>
|
|
468
|
+
Test message
|
|
469
|
+
</Alert>
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
const heading = container.querySelector('h2.alert-title');
|
|
473
|
+
expect(heading).toBeInTheDocument();
|
|
474
|
+
expect(heading).toHaveTextContent('Test Title');
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('should render h3 when titleLevel is 3', () => {
|
|
478
|
+
const { container } = render(
|
|
479
|
+
<Alert open={true} severity="info" title="Test Title" titleLevel={3}>
|
|
480
|
+
Test message
|
|
481
|
+
</Alert>
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
const heading = container.querySelector('h3.alert-title');
|
|
485
|
+
expect(heading).toBeInTheDocument();
|
|
486
|
+
expect(heading).toHaveTextContent('Test Title');
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('should render h4 when titleLevel is 4', () => {
|
|
490
|
+
const { container } = render(
|
|
491
|
+
<Alert open={true} severity="info" title="Test Title" titleLevel={4}>
|
|
492
|
+
Test message
|
|
493
|
+
</Alert>
|
|
494
|
+
);
|
|
495
|
+
|
|
496
|
+
const heading = container.querySelector('h4.alert-title');
|
|
497
|
+
expect(heading).toBeInTheDocument();
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('should render h5 when titleLevel is 5', () => {
|
|
501
|
+
const { container } = render(
|
|
502
|
+
<Alert open={true} severity="info" title="Test Title" titleLevel={5}>
|
|
503
|
+
Test message
|
|
504
|
+
</Alert>
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
const heading = container.querySelector('h5.alert-title');
|
|
508
|
+
expect(heading).toBeInTheDocument();
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it('should render h6 when titleLevel is 6', () => {
|
|
512
|
+
const { container } = render(
|
|
513
|
+
<Alert open={true} severity="info" title="Test Title" titleLevel={6}>
|
|
514
|
+
Test message
|
|
515
|
+
</Alert>
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
const heading = container.querySelector('h6.alert-title');
|
|
519
|
+
expect(heading).toBeInTheDocument();
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
it('should render default heading element when titleLevel is undefined', () => {
|
|
523
|
+
const { container } = render(
|
|
524
|
+
<Alert open={true} severity="info" title="Test Title">
|
|
525
|
+
Test message
|
|
526
|
+
</Alert>
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
// The UI component wraps the title, check that title is rendered
|
|
530
|
+
const titleElement = container.querySelector('.alert-title');
|
|
531
|
+
expect(titleElement).toBeInTheDocument();
|
|
532
|
+
expect(titleElement).toHaveTextContent('Test Title');
|
|
533
|
+
// The element should be rendered (exact tag depends on UI component implementation)
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
it('should not render title element when title prop is not provided', () => {
|
|
537
|
+
const { container } = render(
|
|
538
|
+
<Alert open={true} severity="info">
|
|
539
|
+
Test message
|
|
540
|
+
</Alert>
|
|
541
|
+
);
|
|
542
|
+
|
|
543
|
+
expect(container.querySelector('.alert-title')).not.toBeInTheDocument();
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
it('should apply alert-title class to all heading levels', () => {
|
|
547
|
+
const levels = [2, 3, 4, 5, 6] as const;
|
|
548
|
+
|
|
549
|
+
levels.forEach((level) => {
|
|
550
|
+
const { container, unmount } = render(
|
|
551
|
+
<Alert open={true} severity="info" title="Test" titleLevel={level}>
|
|
552
|
+
Message
|
|
553
|
+
</Alert>
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
const heading = container.querySelector(`h${level}`);
|
|
557
|
+
expect(heading).toHaveClass('alert-title');
|
|
558
|
+
unmount();
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
describe('Pause on Hover/Focus', () => {
|
|
564
|
+
it('should have mouse enter and leave handlers when pauseOnHover is true', () => {
|
|
565
|
+
render(
|
|
566
|
+
<Alert
|
|
567
|
+
open={true}
|
|
568
|
+
severity="info"
|
|
569
|
+
autoHideDuration={3000}
|
|
570
|
+
pauseOnHover={true}
|
|
571
|
+
>
|
|
572
|
+
Test message
|
|
573
|
+
</Alert>
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
const alert = screen.getByRole('alert');
|
|
577
|
+
|
|
578
|
+
// Verify the handlers are attached by checking we can dispatch events without errors
|
|
579
|
+
expect(() => {
|
|
580
|
+
alert.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
|
|
581
|
+
alert.dispatchEvent(new MouseEvent('mouseleave', { bubbles: true }));
|
|
582
|
+
}).not.toThrow();
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it('should have focus and blur handlers when pauseOnHover is true', () => {
|
|
586
|
+
render(
|
|
587
|
+
<Alert
|
|
588
|
+
open={true}
|
|
589
|
+
severity="info"
|
|
590
|
+
autoHideDuration={3000}
|
|
591
|
+
autoFocus={true}
|
|
592
|
+
pauseOnHover={true}
|
|
593
|
+
>
|
|
594
|
+
Test message
|
|
595
|
+
</Alert>
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
const alert = screen.getByRole('alert');
|
|
599
|
+
|
|
600
|
+
// Verify the handlers are attached
|
|
601
|
+
expect(() => {
|
|
602
|
+
alert.dispatchEvent(new FocusEvent('focus', { bubbles: true }));
|
|
603
|
+
alert.dispatchEvent(new FocusEvent('blur', { bubbles: true }));
|
|
604
|
+
}).not.toThrow();
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
it('should accept pauseOnHover prop with default true', () => {
|
|
608
|
+
const { rerender } = render(
|
|
609
|
+
<Alert open={true} severity="info" autoHideDuration={3000}>
|
|
610
|
+
Test message
|
|
611
|
+
</Alert>
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
// Just verify component renders without issues
|
|
615
|
+
expect(screen.getByRole('alert')).toBeInTheDocument();
|
|
616
|
+
|
|
617
|
+
rerender(
|
|
618
|
+
<Alert open={true} severity="info" autoHideDuration={3000} pauseOnHover={false}>
|
|
619
|
+
Test message
|
|
620
|
+
</Alert>
|
|
621
|
+
);
|
|
622
|
+
|
|
623
|
+
expect(screen.getByRole('alert')).toBeInTheDocument();
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
describe('Touch Target Size', () => {
|
|
628
|
+
it('should apply alert-dismiss class to dismiss button', () => {
|
|
629
|
+
const { container } = render(
|
|
630
|
+
<Alert open={true} severity="info" dismissible={true}>
|
|
631
|
+
Test message
|
|
632
|
+
</Alert>
|
|
633
|
+
);
|
|
634
|
+
|
|
635
|
+
const dismissButton = container.querySelector('.alert-dismiss');
|
|
636
|
+
expect(dismissButton).toBeInTheDocument();
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
describe('Focus Indicators', () => {
|
|
641
|
+
it('should be focusable when autoFocus is true', () => {
|
|
642
|
+
render(
|
|
643
|
+
<Alert open={true} severity="info" autoFocus={true}>
|
|
644
|
+
Test message
|
|
645
|
+
</Alert>
|
|
646
|
+
);
|
|
647
|
+
|
|
648
|
+
const alert = screen.getByRole('alert');
|
|
649
|
+
expect(alert).toHaveAttribute('tabIndex', '-1');
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it('should not have tabIndex when autoFocus is false', () => {
|
|
653
|
+
render(
|
|
654
|
+
<Alert open={true} severity="info" autoFocus={false}>
|
|
655
|
+
Test message
|
|
656
|
+
</Alert>
|
|
657
|
+
);
|
|
658
|
+
|
|
659
|
+
const alert = screen.getByRole('alert');
|
|
660
|
+
expect(alert).not.toHaveAttribute('tabIndex');
|
|
661
|
+
});
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
describe('Content Type', () => {
|
|
665
|
+
it('should wrap children in paragraph tag when contentType is "text" (default)', () => {
|
|
666
|
+
const { container } = render(
|
|
667
|
+
<Alert open={true} severity="info">
|
|
668
|
+
Simple text content
|
|
669
|
+
</Alert>
|
|
670
|
+
);
|
|
671
|
+
|
|
672
|
+
const paragraph = container.querySelector('.alert-message p');
|
|
673
|
+
expect(paragraph).toBeInTheDocument();
|
|
674
|
+
expect(paragraph).toHaveTextContent('Simple text content');
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
it('should wrap children in paragraph tag when contentType is explicitly set to "text"', () => {
|
|
678
|
+
const { container } = render(
|
|
679
|
+
<Alert open={true} severity="warning" contentType="text">
|
|
680
|
+
Explicit text content
|
|
681
|
+
</Alert>
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
const paragraph = container.querySelector('.alert-message p');
|
|
685
|
+
expect(paragraph).toBeInTheDocument();
|
|
686
|
+
expect(paragraph).toHaveTextContent('Explicit text content');
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
it('should render children directly without paragraph wrapper when contentType is "node"', () => {
|
|
690
|
+
const { container } = render(
|
|
691
|
+
<Alert open={true} severity="error" contentType="node">
|
|
692
|
+
<div className="custom-content">Custom layout</div>
|
|
693
|
+
</Alert>
|
|
694
|
+
);
|
|
695
|
+
|
|
696
|
+
// Should not have a paragraph wrapper
|
|
697
|
+
const paragraph = container.querySelector('.alert-message > p');
|
|
698
|
+
expect(paragraph).not.toBeInTheDocument();
|
|
699
|
+
|
|
700
|
+
// Should have direct custom content
|
|
701
|
+
const customContent = container.querySelector('.alert-message .custom-content');
|
|
702
|
+
expect(customContent).toBeInTheDocument();
|
|
703
|
+
expect(customContent).toHaveTextContent('Custom layout');
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
it('should render complex content with lists when contentType is "node"', () => {
|
|
707
|
+
const { container } = render(
|
|
708
|
+
<Alert open={true} severity="warning" contentType="node">
|
|
709
|
+
<ul>
|
|
710
|
+
<li>First item</li>
|
|
711
|
+
<li>Second item</li>
|
|
712
|
+
<li>Third item</li>
|
|
713
|
+
</ul>
|
|
714
|
+
</Alert>
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
const list = container.querySelector('.alert-message ul');
|
|
718
|
+
expect(list).toBeInTheDocument();
|
|
719
|
+
|
|
720
|
+
const listItems = container.querySelectorAll('.alert-message li');
|
|
721
|
+
expect(listItems).toHaveLength(3);
|
|
722
|
+
expect(listItems[0]).toHaveTextContent('First item');
|
|
723
|
+
expect(listItems[1]).toHaveTextContent('Second item');
|
|
724
|
+
expect(listItems[2]).toHaveTextContent('Third item');
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
it('should render multiple child elements when contentType is "node"', () => {
|
|
728
|
+
const { container } = render(
|
|
729
|
+
<Alert open={true} severity="success" contentType="node">
|
|
730
|
+
<p>First paragraph</p>
|
|
731
|
+
<p>Second paragraph</p>
|
|
732
|
+
<div>Additional content</div>
|
|
733
|
+
</Alert>
|
|
734
|
+
);
|
|
735
|
+
|
|
736
|
+
const messageDiv = container.querySelector('.alert-message');
|
|
737
|
+
const paragraphs = messageDiv?.querySelectorAll('p') || [];
|
|
738
|
+
const divs = messageDiv?.querySelectorAll('div') || [];
|
|
739
|
+
|
|
740
|
+
expect(paragraphs.length).toBeGreaterThanOrEqual(2);
|
|
741
|
+
expect(divs.length).toBeGreaterThanOrEqual(1);
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
it('should maintain accessibility with contentType="node"', () => {
|
|
745
|
+
render(
|
|
746
|
+
<Alert open={true} severity="info" contentType="node">
|
|
747
|
+
<div>
|
|
748
|
+
<p>Complex content structure</p>
|
|
749
|
+
<ul>
|
|
750
|
+
<li>Item 1</li>
|
|
751
|
+
</ul>
|
|
752
|
+
</div>
|
|
753
|
+
</Alert>
|
|
754
|
+
);
|
|
755
|
+
|
|
756
|
+
const alert = screen.getByRole('alert');
|
|
757
|
+
expect(alert).toHaveAttribute('aria-live', 'polite');
|
|
758
|
+
expect(alert).toHaveAttribute('aria-atomic', 'true');
|
|
759
|
+
});
|
|
760
|
+
});
|
|
761
|
+
});
|
|
762
|
+
});
|