@tribepad/themis 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/CHANGELOG.md +17 -0
- package/LICENSE +21 -0
- package/README.md +114 -0
- package/dist/Carousel-NTZX5TOW.js +16 -0
- package/dist/Carousel-NTZX5TOW.js.map +1 -0
- package/dist/Carousel-YH3DOQJU.mjs +7 -0
- package/dist/Carousel-YH3DOQJU.mjs.map +1 -0
- package/dist/chunk-2HIUTHMU.mjs +234 -0
- package/dist/chunk-2HIUTHMU.mjs.map +1 -0
- package/dist/chunk-34GTFTDO.js +431 -0
- package/dist/chunk-34GTFTDO.js.map +1 -0
- package/dist/chunk-3H7ASYR7.js +250 -0
- package/dist/chunk-3H7ASYR7.js.map +1 -0
- package/dist/chunk-3IEN7JOP.js +316 -0
- package/dist/chunk-3IEN7JOP.js.map +1 -0
- package/dist/chunk-3JHN4GAL.js +326 -0
- package/dist/chunk-3JHN4GAL.js.map +1 -0
- package/dist/chunk-3MJPASQU.js +232 -0
- package/dist/chunk-3MJPASQU.js.map +1 -0
- package/dist/chunk-3XD2JUL3.js +572 -0
- package/dist/chunk-3XD2JUL3.js.map +1 -0
- package/dist/chunk-3YOY2VJ6.js +189 -0
- package/dist/chunk-3YOY2VJ6.js.map +1 -0
- package/dist/chunk-4DU5JSXB.js +408 -0
- package/dist/chunk-4DU5JSXB.js.map +1 -0
- package/dist/chunk-4E4E2GSS.js +352 -0
- package/dist/chunk-4E4E2GSS.js.map +1 -0
- package/dist/chunk-4NHAP4AN.mjs +3 -0
- package/dist/chunk-4NHAP4AN.mjs.map +1 -0
- package/dist/chunk-4S33J5NY.mjs +415 -0
- package/dist/chunk-4S33J5NY.mjs.map +1 -0
- package/dist/chunk-5SMGRT3G.mjs +354 -0
- package/dist/chunk-5SMGRT3G.mjs.map +1 -0
- package/dist/chunk-5SVLJN2C.mjs +22 -0
- package/dist/chunk-5SVLJN2C.mjs.map +1 -0
- package/dist/chunk-66WTU4EB.mjs +299 -0
- package/dist/chunk-66WTU4EB.mjs.map +1 -0
- package/dist/chunk-6S25NMOT.mjs +335 -0
- package/dist/chunk-6S25NMOT.mjs.map +1 -0
- package/dist/chunk-6SP7UB3D.js +4 -0
- package/dist/chunk-6SP7UB3D.js.map +1 -0
- package/dist/chunk-6TYWWQHM.mjs +565 -0
- package/dist/chunk-6TYWWQHM.mjs.map +1 -0
- package/dist/chunk-A3YUJA6W.mjs +384 -0
- package/dist/chunk-A3YUJA6W.mjs.map +1 -0
- package/dist/chunk-A6KEDVUR.js +61 -0
- package/dist/chunk-A6KEDVUR.js.map +1 -0
- package/dist/chunk-A77RUEWL.js +730 -0
- package/dist/chunk-A77RUEWL.js.map +1 -0
- package/dist/chunk-AA4IKMPE.mjs +3 -0
- package/dist/chunk-AA4IKMPE.mjs.map +1 -0
- package/dist/chunk-AKIA6GW6.mjs +163 -0
- package/dist/chunk-AKIA6GW6.mjs.map +1 -0
- package/dist/chunk-AL6P275L.mjs +435 -0
- package/dist/chunk-AL6P275L.mjs.map +1 -0
- package/dist/chunk-AZ3RJYTB.js +37 -0
- package/dist/chunk-AZ3RJYTB.js.map +1 -0
- package/dist/chunk-B5Q4UPL6.js +32 -0
- package/dist/chunk-B5Q4UPL6.js.map +1 -0
- package/dist/chunk-B6DHPMDP.mjs +335 -0
- package/dist/chunk-B6DHPMDP.mjs.map +1 -0
- package/dist/chunk-BDXKKMBZ.mjs +184 -0
- package/dist/chunk-BDXKKMBZ.mjs.map +1 -0
- package/dist/chunk-BL6E2DLZ.mjs +52 -0
- package/dist/chunk-BL6E2DLZ.mjs.map +1 -0
- package/dist/chunk-CGFDS4XS.mjs +121 -0
- package/dist/chunk-CGFDS4XS.mjs.map +1 -0
- package/dist/chunk-CJIW5TKI.js +139 -0
- package/dist/chunk-CJIW5TKI.js.map +1 -0
- package/dist/chunk-CKNISJOQ.js +314 -0
- package/dist/chunk-CKNISJOQ.js.map +1 -0
- package/dist/chunk-D6CBOECS.mjs +1757 -0
- package/dist/chunk-D6CBOECS.mjs.map +1 -0
- package/dist/chunk-DDWEVC2S.js +166 -0
- package/dist/chunk-DDWEVC2S.js.map +1 -0
- package/dist/chunk-DZ556D2F.mjs +176 -0
- package/dist/chunk-DZ556D2F.mjs.map +1 -0
- package/dist/chunk-E2KQFV3O.mjs +10 -0
- package/dist/chunk-E2KQFV3O.mjs.map +1 -0
- package/dist/chunk-EMMLADSC.js +126 -0
- package/dist/chunk-EMMLADSC.js.map +1 -0
- package/dist/chunk-EP4WOI5D.mjs +926 -0
- package/dist/chunk-EP4WOI5D.mjs.map +1 -0
- package/dist/chunk-FJRXLJC2.mjs +160 -0
- package/dist/chunk-FJRXLJC2.mjs.map +1 -0
- package/dist/chunk-FKQI434R.js +345 -0
- package/dist/chunk-FKQI434R.js.map +1 -0
- package/dist/chunk-FPKEAJRZ.mjs +100 -0
- package/dist/chunk-FPKEAJRZ.mjs.map +1 -0
- package/dist/chunk-FWQYB22U.js +183 -0
- package/dist/chunk-FWQYB22U.js.map +1 -0
- package/dist/chunk-GD5GHTMA.js +189 -0
- package/dist/chunk-GD5GHTMA.js.map +1 -0
- package/dist/chunk-GE5XTSDZ.js +447 -0
- package/dist/chunk-GE5XTSDZ.js.map +1 -0
- package/dist/chunk-GVE47ZAX.mjs +32 -0
- package/dist/chunk-GVE47ZAX.mjs.map +1 -0
- package/dist/chunk-HK46BT5U.mjs +18 -0
- package/dist/chunk-HK46BT5U.mjs.map +1 -0
- package/dist/chunk-HQVRMR6N.js +365 -0
- package/dist/chunk-HQVRMR6N.js.map +1 -0
- package/dist/chunk-HSGBJPJO.mjs +398 -0
- package/dist/chunk-HSGBJPJO.mjs.map +1 -0
- package/dist/chunk-I3AUTOMZ.mjs +125 -0
- package/dist/chunk-I3AUTOMZ.mjs.map +1 -0
- package/dist/chunk-IEI5LD5C.mjs +1161 -0
- package/dist/chunk-IEI5LD5C.mjs.map +1 -0
- package/dist/chunk-IIPTC2X7.mjs +118 -0
- package/dist/chunk-IIPTC2X7.mjs.map +1 -0
- package/dist/chunk-J7TLHF2Q.js +4 -0
- package/dist/chunk-J7TLHF2Q.js.map +1 -0
- package/dist/chunk-JJOWXFXQ.mjs +765 -0
- package/dist/chunk-JJOWXFXQ.mjs.map +1 -0
- package/dist/chunk-JPTSS2OA.mjs +3 -0
- package/dist/chunk-JPTSS2OA.mjs.map +1 -0
- package/dist/chunk-KFXXRLTP.js +396 -0
- package/dist/chunk-KFXXRLTP.js.map +1 -0
- package/dist/chunk-KPRRBSG6.mjs +272 -0
- package/dist/chunk-KPRRBSG6.mjs.map +1 -0
- package/dist/chunk-NFSBGRDB.mjs +57 -0
- package/dist/chunk-NFSBGRDB.mjs.map +1 -0
- package/dist/chunk-NGJVCFTM.js +219 -0
- package/dist/chunk-NGJVCFTM.js.map +1 -0
- package/dist/chunk-NSQ6MZJ6.mjs +728 -0
- package/dist/chunk-NSQ6MZJ6.mjs.map +1 -0
- package/dist/chunk-NYQYHT76.mjs +296 -0
- package/dist/chunk-NYQYHT76.mjs.map +1 -0
- package/dist/chunk-OLJJGI5B.js +1193 -0
- package/dist/chunk-OLJJGI5B.js.map +1 -0
- package/dist/chunk-Q3572X2J.js +292 -0
- package/dist/chunk-Q3572X2J.js.map +1 -0
- package/dist/chunk-QH7N7D4I.mjs +210 -0
- package/dist/chunk-QH7N7D4I.mjs.map +1 -0
- package/dist/chunk-R7XUIV25.js +466 -0
- package/dist/chunk-R7XUIV25.js.map +1 -0
- package/dist/chunk-RFFO4KPM.js +135 -0
- package/dist/chunk-RFFO4KPM.js.map +1 -0
- package/dist/chunk-RFX7QKA7.mjs +180 -0
- package/dist/chunk-RFX7QKA7.mjs.map +1 -0
- package/dist/chunk-SN5LFAP3.js +940 -0
- package/dist/chunk-SN5LFAP3.js.map +1 -0
- package/dist/chunk-T4COXKQ3.js +24 -0
- package/dist/chunk-T4COXKQ3.js.map +1 -0
- package/dist/chunk-TS54QM27.js +125 -0
- package/dist/chunk-TS54QM27.js.map +1 -0
- package/dist/chunk-UE2S4PCX.mjs +220 -0
- package/dist/chunk-UE2S4PCX.mjs.map +1 -0
- package/dist/chunk-UTW3QX2A.mjs +282 -0
- package/dist/chunk-UTW3QX2A.mjs.map +1 -0
- package/dist/chunk-V74LGMAE.js +1767 -0
- package/dist/chunk-V74LGMAE.js.map +1 -0
- package/dist/chunk-VIREG536.js +12 -0
- package/dist/chunk-VIREG536.js.map +1 -0
- package/dist/chunk-VY7M7346.js +4 -0
- package/dist/chunk-VY7M7346.js.map +1 -0
- package/dist/chunk-W3TJOO7H.mjs +319 -0
- package/dist/chunk-W3TJOO7H.mjs.map +1 -0
- package/dist/chunk-WIUOB36M.js +54 -0
- package/dist/chunk-WIUOB36M.js.map +1 -0
- package/dist/chunk-WJGLM4CY.js +291 -0
- package/dist/chunk-WJGLM4CY.js.map +1 -0
- package/dist/chunk-WNURH5OO.mjs +453 -0
- package/dist/chunk-WNURH5OO.mjs.map +1 -0
- package/dist/chunk-X25TNRSD.mjs +364 -0
- package/dist/chunk-X25TNRSD.mjs.map +1 -0
- package/dist/chunk-Y3GT7ETK.js +108 -0
- package/dist/chunk-Y3GT7ETK.js.map +1 -0
- package/dist/chunk-Z4FRNOF6.mjs +115 -0
- package/dist/chunk-Z4FRNOF6.mjs.map +1 -0
- package/dist/chunk-ZMYLD3BN.js +166 -0
- package/dist/chunk-ZMYLD3BN.js.map +1 -0
- package/dist/chunk-ZP2KV6EX.js +815 -0
- package/dist/chunk-ZP2KV6EX.js.map +1 -0
- package/dist/chunk-ZVKXFELU.js +366 -0
- package/dist/chunk-ZVKXFELU.js.map +1 -0
- package/dist/elements/Accordion/Accordion.d.ts +139 -0
- package/dist/elements/Accordion/Accordion.d.ts.map +1 -0
- package/dist/elements/Accordion/Accordion.types.d.ts +143 -0
- package/dist/elements/Accordion/Accordion.types.d.ts.map +1 -0
- package/dist/elements/Accordion/index.d.ts +13 -0
- package/dist/elements/Accordion/index.d.ts.map +1 -0
- package/dist/elements/Accordion/index.js +78 -0
- package/dist/elements/Accordion/index.js.map +1 -0
- package/dist/elements/Accordion/index.mjs +5 -0
- package/dist/elements/Accordion/index.mjs.map +1 -0
- package/dist/elements/Avatar/Avatar.d.ts +51 -0
- package/dist/elements/Avatar/Avatar.d.ts.map +1 -0
- package/dist/elements/Avatar/Avatar.types.d.ts +145 -0
- package/dist/elements/Avatar/Avatar.types.d.ts.map +1 -0
- package/dist/elements/Avatar/AvatarGroup.d.ts +32 -0
- package/dist/elements/Avatar/AvatarGroup.d.ts.map +1 -0
- package/dist/elements/Avatar/index.d.ts +11 -0
- package/dist/elements/Avatar/index.d.ts.map +1 -0
- package/dist/elements/Avatar/index.js +54 -0
- package/dist/elements/Avatar/index.js.map +1 -0
- package/dist/elements/Avatar/index.mjs +5 -0
- package/dist/elements/Avatar/index.mjs.map +1 -0
- package/dist/elements/Badge/Badge.d.ts +39 -0
- package/dist/elements/Badge/Badge.d.ts.map +1 -0
- package/dist/elements/Badge/Badge.types.d.ts +76 -0
- package/dist/elements/Badge/Badge.types.d.ts.map +1 -0
- package/dist/elements/Badge/index.d.ts +18 -0
- package/dist/elements/Badge/index.d.ts.map +1 -0
- package/dist/elements/Badge/index.js +43 -0
- package/dist/elements/Badge/index.js.map +1 -0
- package/dist/elements/Badge/index.mjs +6 -0
- package/dist/elements/Badge/index.mjs.map +1 -0
- package/dist/elements/Breadcrumbs/Breadcrumbs.d.ts +91 -0
- package/dist/elements/Breadcrumbs/Breadcrumbs.d.ts.map +1 -0
- package/dist/elements/Breadcrumbs/Breadcrumbs.types.d.ts +114 -0
- package/dist/elements/Breadcrumbs/Breadcrumbs.types.d.ts.map +1 -0
- package/dist/elements/Breadcrumbs/index.d.ts +14 -0
- package/dist/elements/Breadcrumbs/index.d.ts.map +1 -0
- package/dist/elements/Breadcrumbs/index.js +54 -0
- package/dist/elements/Breadcrumbs/index.js.map +1 -0
- package/dist/elements/Breadcrumbs/index.mjs +9 -0
- package/dist/elements/Breadcrumbs/index.mjs.map +1 -0
- package/dist/elements/Button/Button.d.ts +92 -0
- package/dist/elements/Button/Button.d.ts.map +1 -0
- package/dist/elements/Button/Button.types.d.ts +54 -0
- package/dist/elements/Button/Button.types.d.ts.map +1 -0
- package/dist/elements/Button/index.d.ts +18 -0
- package/dist/elements/Button/index.d.ts.map +1 -0
- package/dist/elements/Button/index.js +27 -0
- package/dist/elements/Button/index.js.map +1 -0
- package/dist/elements/Button/index.mjs +6 -0
- package/dist/elements/Button/index.mjs.map +1 -0
- package/dist/elements/ButtonGroup/ButtonGroup.d.ts +53 -0
- package/dist/elements/ButtonGroup/ButtonGroup.d.ts.map +1 -0
- package/dist/elements/ButtonGroup/ButtonGroup.types.d.ts +98 -0
- package/dist/elements/ButtonGroup/ButtonGroup.types.d.ts.map +1 -0
- package/dist/elements/ButtonGroup/ButtonGroup.utils.d.ts +60 -0
- package/dist/elements/ButtonGroup/ButtonGroup.utils.d.ts.map +1 -0
- package/dist/elements/ButtonGroup/ButtonGroup.variants.d.ts +39 -0
- package/dist/elements/ButtonGroup/ButtonGroup.variants.d.ts.map +1 -0
- package/dist/elements/ButtonGroup/ButtonGroupContext.d.ts +42 -0
- package/dist/elements/ButtonGroup/ButtonGroupContext.d.ts.map +1 -0
- package/dist/elements/ButtonGroup/index.d.ts +35 -0
- package/dist/elements/ButtonGroup/index.d.ts.map +1 -0
- package/dist/elements/ButtonGroup/index.js +66 -0
- package/dist/elements/ButtonGroup/index.js.map +1 -0
- package/dist/elements/ButtonGroup/index.mjs +5 -0
- package/dist/elements/ButtonGroup/index.mjs.map +1 -0
- package/dist/elements/Card/Card.d.ts +104 -0
- package/dist/elements/Card/Card.d.ts.map +1 -0
- package/dist/elements/Card/Card.types.d.ts +227 -0
- package/dist/elements/Card/Card.types.d.ts.map +1 -0
- package/dist/elements/Card/index.d.ts +38 -0
- package/dist/elements/Card/index.d.ts.map +1 -0
- package/dist/elements/Card/index.js +85 -0
- package/dist/elements/Card/index.js.map +1 -0
- package/dist/elements/Card/index.mjs +8 -0
- package/dist/elements/Card/index.mjs.map +1 -0
- package/dist/elements/Carousel/Carousel.d.ts +13 -0
- package/dist/elements/Carousel/Carousel.d.ts.map +1 -0
- package/dist/elements/Carousel/Carousel.types.d.ts +65 -0
- package/dist/elements/Carousel/Carousel.types.d.ts.map +1 -0
- package/dist/elements/Carousel/LazyCarousel.d.ts +46 -0
- package/dist/elements/Carousel/LazyCarousel.d.ts.map +1 -0
- package/dist/elements/Carousel/index.d.ts +5 -0
- package/dist/elements/Carousel/index.d.ts.map +1 -0
- package/dist/elements/Carousel/index.js +23 -0
- package/dist/elements/Carousel/index.js.map +1 -0
- package/dist/elements/Carousel/index.mjs +10 -0
- package/dist/elements/Carousel/index.mjs.map +1 -0
- package/dist/elements/Chart/Chart.d.ts +44 -0
- package/dist/elements/Chart/Chart.d.ts.map +1 -0
- package/dist/elements/Chart/Chart.types.d.ts +254 -0
- package/dist/elements/Chart/Chart.types.d.ts.map +1 -0
- package/dist/elements/Chart/ChartAnnouncer.d.ts +24 -0
- package/dist/elements/Chart/ChartAnnouncer.d.ts.map +1 -0
- package/dist/elements/Chart/ChartAxis.d.ts +42 -0
- package/dist/elements/Chart/ChartAxis.d.ts.map +1 -0
- package/dist/elements/Chart/ChartBarSeries.d.ts +54 -0
- package/dist/elements/Chart/ChartBarSeries.d.ts.map +1 -0
- package/dist/elements/Chart/ChartContext.d.ts +46 -0
- package/dist/elements/Chart/ChartContext.d.ts.map +1 -0
- package/dist/elements/Chart/ChartDataPoint.d.ts +56 -0
- package/dist/elements/Chart/ChartDataPoint.d.ts.map +1 -0
- package/dist/elements/Chart/ChartDataTable.d.ts +35 -0
- package/dist/elements/Chart/ChartDataTable.d.ts.map +1 -0
- package/dist/elements/Chart/ChartGrid.d.ts +32 -0
- package/dist/elements/Chart/ChartGrid.d.ts.map +1 -0
- package/dist/elements/Chart/ChartLegend.d.ts +32 -0
- package/dist/elements/Chart/ChartLegend.d.ts.map +1 -0
- package/dist/elements/Chart/ChartLineSeries.d.ts +52 -0
- package/dist/elements/Chart/ChartLineSeries.d.ts.map +1 -0
- package/dist/elements/Chart/ChartSVG.d.ts +62 -0
- package/dist/elements/Chart/ChartSVG.d.ts.map +1 -0
- package/dist/elements/Chart/ChartTooltip.d.ts +45 -0
- package/dist/elements/Chart/ChartTooltip.d.ts.map +1 -0
- package/dist/elements/Chart/chart.constants.d.ts +108 -0
- package/dist/elements/Chart/chart.constants.d.ts.map +1 -0
- package/dist/elements/Chart/chart.variants.d.ts +45 -0
- package/dist/elements/Chart/chart.variants.d.ts.map +1 -0
- package/dist/elements/Chart/index.d.ts +12 -0
- package/dist/elements/Chart/index.d.ts.map +1 -0
- package/dist/elements/Chart/index.js +47 -0
- package/dist/elements/Chart/index.js.map +1 -0
- package/dist/elements/Chart/index.mjs +6 -0
- package/dist/elements/Chart/index.mjs.map +1 -0
- package/dist/elements/Chart/useChartDimensions.d.ts +18 -0
- package/dist/elements/Chart/useChartDimensions.d.ts.map +1 -0
- package/dist/elements/Chart/useChartKeyboard.d.ts +42 -0
- package/dist/elements/Chart/useChartKeyboard.d.ts.map +1 -0
- package/dist/elements/Chart/useRovingTabIndex.d.ts +46 -0
- package/dist/elements/Chart/useRovingTabIndex.d.ts.map +1 -0
- package/dist/elements/Checkbox/Checkbox.d.ts +94 -0
- package/dist/elements/Checkbox/Checkbox.d.ts.map +1 -0
- package/dist/elements/Checkbox/Checkbox.types.d.ts +82 -0
- package/dist/elements/Checkbox/Checkbox.types.d.ts.map +1 -0
- package/dist/elements/Checkbox/index.d.ts +7 -0
- package/dist/elements/Checkbox/index.d.ts.map +1 -0
- package/dist/elements/Checkbox/index.js +47 -0
- package/dist/elements/Checkbox/index.js.map +1 -0
- package/dist/elements/Checkbox/index.mjs +6 -0
- package/dist/elements/Checkbox/index.mjs.map +1 -0
- package/dist/elements/CheckboxGroup/CheckboxGroup.d.ts +130 -0
- package/dist/elements/CheckboxGroup/CheckboxGroup.d.ts.map +1 -0
- package/dist/elements/CheckboxGroup/CheckboxGroup.types.d.ts +142 -0
- package/dist/elements/CheckboxGroup/CheckboxGroup.types.d.ts.map +1 -0
- package/dist/elements/CheckboxGroup/index.d.ts +10 -0
- package/dist/elements/CheckboxGroup/index.d.ts.map +1 -0
- package/dist/elements/CheckboxGroup/index.js +71 -0
- package/dist/elements/CheckboxGroup/index.js.map +1 -0
- package/dist/elements/CheckboxGroup/index.mjs +6 -0
- package/dist/elements/CheckboxGroup/index.mjs.map +1 -0
- package/dist/elements/DatePicker/Calendar.d.ts +16 -0
- package/dist/elements/DatePicker/Calendar.d.ts.map +1 -0
- package/dist/elements/DatePicker/DateField.d.ts +13 -0
- package/dist/elements/DatePicker/DateField.d.ts.map +1 -0
- package/dist/elements/DatePicker/DatePicker.d.ts +16 -0
- package/dist/elements/DatePicker/DatePicker.d.ts.map +1 -0
- package/dist/elements/DatePicker/DatePicker.types.d.ts +390 -0
- package/dist/elements/DatePicker/DatePicker.types.d.ts.map +1 -0
- package/dist/elements/DatePicker/DatePicker.variants.d.ts +42 -0
- package/dist/elements/DatePicker/DatePicker.variants.d.ts.map +1 -0
- package/dist/elements/DatePicker/DateRangePicker.d.ts +13 -0
- package/dist/elements/DatePicker/DateRangePicker.d.ts.map +1 -0
- package/dist/elements/DatePicker/index.d.ts +14 -0
- package/dist/elements/DatePicker/index.d.ts.map +1 -0
- package/dist/elements/DatePicker/index.js +123 -0
- package/dist/elements/DatePicker/index.js.map +1 -0
- package/dist/elements/DatePicker/index.mjs +6 -0
- package/dist/elements/DatePicker/index.mjs.map +1 -0
- package/dist/elements/Dropdown/Dropdown.d.ts +197 -0
- package/dist/elements/Dropdown/Dropdown.d.ts.map +1 -0
- package/dist/elements/Dropdown/Dropdown.types.d.ts +175 -0
- package/dist/elements/Dropdown/Dropdown.types.d.ts.map +1 -0
- package/dist/elements/Dropdown/index.d.ts +12 -0
- package/dist/elements/Dropdown/index.d.ts.map +1 -0
- package/dist/elements/Dropdown/index.js +41 -0
- package/dist/elements/Dropdown/index.js.map +1 -0
- package/dist/elements/Dropdown/index.mjs +4 -0
- package/dist/elements/Dropdown/index.mjs.map +1 -0
- package/dist/elements/FileField/FileField.d.ts +46 -0
- package/dist/elements/FileField/FileField.d.ts.map +1 -0
- package/dist/elements/FileField/FileField.types.d.ts +99 -0
- package/dist/elements/FileField/FileField.types.d.ts.map +1 -0
- package/dist/elements/FileField/FileField.variants.d.ts +49 -0
- package/dist/elements/FileField/FileField.variants.d.ts.map +1 -0
- package/dist/elements/FileField/FilePreview.d.ts +27 -0
- package/dist/elements/FileField/FilePreview.d.ts.map +1 -0
- package/dist/elements/FileField/FileProgress.d.ts +35 -0
- package/dist/elements/FileField/FileProgress.d.ts.map +1 -0
- package/dist/elements/FileField/FileProgress.variants.d.ts +55 -0
- package/dist/elements/FileField/FileProgress.variants.d.ts.map +1 -0
- package/dist/elements/FileField/index.d.ts +41 -0
- package/dist/elements/FileField/index.d.ts.map +1 -0
- package/dist/elements/FileField/index.js +138 -0
- package/dist/elements/FileField/index.js.map +1 -0
- package/dist/elements/FileField/index.mjs +9 -0
- package/dist/elements/FileField/index.mjs.map +1 -0
- package/dist/elements/FileField/useFilePreview.d.ts +46 -0
- package/dist/elements/FileField/useFilePreview.d.ts.map +1 -0
- package/dist/elements/FileField/utils.d.ts +134 -0
- package/dist/elements/FileField/utils.d.ts.map +1 -0
- package/dist/elements/FormLayout/FormLayout.d.ts +51 -0
- package/dist/elements/FormLayout/FormLayout.d.ts.map +1 -0
- package/dist/elements/FormLayout/FormLayout.types.d.ts +66 -0
- package/dist/elements/FormLayout/FormLayout.types.d.ts.map +1 -0
- package/dist/elements/FormLayout/index.d.ts +3 -0
- package/dist/elements/FormLayout/index.d.ts.map +1 -0
- package/dist/elements/FormLayout/index.js +17 -0
- package/dist/elements/FormLayout/index.js.map +1 -0
- package/dist/elements/FormLayout/index.mjs +4 -0
- package/dist/elements/FormLayout/index.mjs.map +1 -0
- package/dist/elements/Modal/Modal.d.ts +168 -0
- package/dist/elements/Modal/Modal.d.ts.map +1 -0
- package/dist/elements/Modal/Modal.types.d.ts +265 -0
- package/dist/elements/Modal/Modal.types.d.ts.map +1 -0
- package/dist/elements/Modal/index.d.ts +14 -0
- package/dist/elements/Modal/index.d.ts.map +1 -0
- package/dist/elements/Modal/index.js +52 -0
- package/dist/elements/Modal/index.js.map +1 -0
- package/dist/elements/Modal/index.mjs +3 -0
- package/dist/elements/Modal/index.mjs.map +1 -0
- package/dist/elements/NumberField/NumberField.d.ts +43 -0
- package/dist/elements/NumberField/NumberField.d.ts.map +1 -0
- package/dist/elements/NumberField/NumberField.types.d.ts +112 -0
- package/dist/elements/NumberField/NumberField.types.d.ts.map +1 -0
- package/dist/elements/NumberField/NumberField.variants.d.ts +82 -0
- package/dist/elements/NumberField/NumberField.variants.d.ts.map +1 -0
- package/dist/elements/NumberField/index.d.ts +25 -0
- package/dist/elements/NumberField/index.d.ts.map +1 -0
- package/dist/elements/NumberField/index.js +57 -0
- package/dist/elements/NumberField/index.js.map +1 -0
- package/dist/elements/NumberField/index.mjs +8 -0
- package/dist/elements/NumberField/index.mjs.map +1 -0
- package/dist/elements/OTPInput/OTPInput.d.ts +105 -0
- package/dist/elements/OTPInput/OTPInput.d.ts.map +1 -0
- package/dist/elements/OTPInput/OTPInput.styles.d.ts +83 -0
- package/dist/elements/OTPInput/OTPInput.styles.d.ts.map +1 -0
- package/dist/elements/OTPInput/OTPInput.types.d.ts +189 -0
- package/dist/elements/OTPInput/OTPInput.types.d.ts.map +1 -0
- package/dist/elements/OTPInput/components/OTPDigit.d.ts +51 -0
- package/dist/elements/OTPInput/components/OTPDigit.d.ts.map +1 -0
- package/dist/elements/OTPInput/hooks/useOTPInput.d.ts +36 -0
- package/dist/elements/OTPInput/hooks/useOTPInput.d.ts.map +1 -0
- package/dist/elements/OTPInput/hooks/useOTPKeyboard.d.ts +59 -0
- package/dist/elements/OTPInput/hooks/useOTPKeyboard.d.ts.map +1 -0
- package/dist/elements/OTPInput/index.d.ts +27 -0
- package/dist/elements/OTPInput/index.d.ts.map +1 -0
- package/dist/elements/OTPInput/index.js +13 -0
- package/dist/elements/OTPInput/index.js.map +1 -0
- package/dist/elements/OTPInput/index.mjs +4 -0
- package/dist/elements/OTPInput/index.mjs.map +1 -0
- package/dist/elements/Panel/Panel.d.ts +111 -0
- package/dist/elements/Panel/Panel.d.ts.map +1 -0
- package/dist/elements/Panel/Panel.types.d.ts +243 -0
- package/dist/elements/Panel/Panel.types.d.ts.map +1 -0
- package/dist/elements/Panel/index.d.ts +4 -0
- package/dist/elements/Panel/index.d.ts.map +1 -0
- package/dist/elements/Panel/index.js +33 -0
- package/dist/elements/Panel/index.js.map +1 -0
- package/dist/elements/Panel/index.mjs +4 -0
- package/dist/elements/Panel/index.mjs.map +1 -0
- package/dist/elements/Progress/Progress.d.ts +44 -0
- package/dist/elements/Progress/Progress.d.ts.map +1 -0
- package/dist/elements/Progress/Progress.types.d.ts +64 -0
- package/dist/elements/Progress/Progress.types.d.ts.map +1 -0
- package/dist/elements/Progress/index.d.ts +11 -0
- package/dist/elements/Progress/index.d.ts.map +1 -0
- package/dist/elements/Progress/index.js +30 -0
- package/dist/elements/Progress/index.js.map +1 -0
- package/dist/elements/Progress/index.mjs +5 -0
- package/dist/elements/Progress/index.mjs.map +1 -0
- package/dist/elements/RadioGroup/RadioGroup.d.ts +110 -0
- package/dist/elements/RadioGroup/RadioGroup.d.ts.map +1 -0
- package/dist/elements/RadioGroup/RadioGroup.types.d.ts +143 -0
- package/dist/elements/RadioGroup/RadioGroup.types.d.ts.map +1 -0
- package/dist/elements/RadioGroup/index.d.ts +10 -0
- package/dist/elements/RadioGroup/index.d.ts.map +1 -0
- package/dist/elements/RadioGroup/index.js +47 -0
- package/dist/elements/RadioGroup/index.js.map +1 -0
- package/dist/elements/RadioGroup/index.mjs +6 -0
- package/dist/elements/RadioGroup/index.mjs.map +1 -0
- package/dist/elements/Resizable/Resizable.types.d.ts +188 -0
- package/dist/elements/Resizable/Resizable.types.d.ts.map +1 -0
- package/dist/elements/Resizable/components/ResizableHandle.d.ts +28 -0
- package/dist/elements/Resizable/components/ResizableHandle.d.ts.map +1 -0
- package/dist/elements/Resizable/components/ResizablePanel.d.ts +24 -0
- package/dist/elements/Resizable/components/ResizablePanel.d.ts.map +1 -0
- package/dist/elements/Resizable/components/ResizablePanelGroup.d.ts +26 -0
- package/dist/elements/Resizable/components/ResizablePanelGroup.d.ts.map +1 -0
- package/dist/elements/Resizable/components/ResizablePopover.d.ts +45 -0
- package/dist/elements/Resizable/components/ResizablePopover.d.ts.map +1 -0
- package/dist/elements/Resizable/hooks/useResizable.d.ts +50 -0
- package/dist/elements/Resizable/hooks/useResizable.d.ts.map +1 -0
- package/dist/elements/Resizable/hooks/useResizableContext.d.ts +36 -0
- package/dist/elements/Resizable/hooks/useResizableContext.d.ts.map +1 -0
- package/dist/elements/Resizable/index.d.ts +37 -0
- package/dist/elements/Resizable/index.d.ts.map +1 -0
- package/dist/elements/Resizable/index.js +65 -0
- package/dist/elements/Resizable/index.js.map +1 -0
- package/dist/elements/Resizable/index.mjs +8 -0
- package/dist/elements/Resizable/index.mjs.map +1 -0
- package/dist/elements/Select/Select.d.ts +184 -0
- package/dist/elements/Select/Select.d.ts.map +1 -0
- package/dist/elements/Select/Select.types.d.ts +166 -0
- package/dist/elements/Select/Select.types.d.ts.map +1 -0
- package/dist/elements/Select/index.d.ts +35 -0
- package/dist/elements/Select/index.d.ts.map +1 -0
- package/dist/elements/Select/index.js +33 -0
- package/dist/elements/Select/index.js.map +1 -0
- package/dist/elements/Select/index.mjs +4 -0
- package/dist/elements/Select/index.mjs.map +1 -0
- package/dist/elements/Skeleton/Skeleton.d.ts +31 -0
- package/dist/elements/Skeleton/Skeleton.d.ts.map +1 -0
- package/dist/elements/Skeleton/Skeleton.types.d.ts +36 -0
- package/dist/elements/Skeleton/Skeleton.types.d.ts.map +1 -0
- package/dist/elements/Skeleton/index.d.ts +11 -0
- package/dist/elements/Skeleton/index.d.ts.map +1 -0
- package/dist/elements/Skeleton/index.js +22 -0
- package/dist/elements/Skeleton/index.js.map +1 -0
- package/dist/elements/Skeleton/index.mjs +5 -0
- package/dist/elements/Skeleton/index.mjs.map +1 -0
- package/dist/elements/Switch/Switch.d.ts +39 -0
- package/dist/elements/Switch/Switch.d.ts.map +1 -0
- package/dist/elements/Switch/Switch.types.d.ts +53 -0
- package/dist/elements/Switch/Switch.types.d.ts.map +1 -0
- package/dist/elements/Switch/index.d.ts +8 -0
- package/dist/elements/Switch/index.d.ts.map +1 -0
- package/dist/elements/Switch/index.js +49 -0
- package/dist/elements/Switch/index.js.map +1 -0
- package/dist/elements/Switch/index.mjs +31 -0
- package/dist/elements/Switch/index.mjs.map +1 -0
- package/dist/elements/Table/Table.d.ts +123 -0
- package/dist/elements/Table/Table.d.ts.map +1 -0
- package/dist/elements/Table/Table.types.d.ts +356 -0
- package/dist/elements/Table/Table.types.d.ts.map +1 -0
- package/dist/elements/Table/index.d.ts +5 -0
- package/dist/elements/Table/index.d.ts.map +1 -0
- package/dist/elements/Table/index.js +76 -0
- package/dist/elements/Table/index.js.map +1 -0
- package/dist/elements/Table/index.mjs +7 -0
- package/dist/elements/Table/index.mjs.map +1 -0
- package/dist/elements/Tabs/Tabs.d.ts +129 -0
- package/dist/elements/Tabs/Tabs.d.ts.map +1 -0
- package/dist/elements/Tabs/Tabs.types.d.ts +179 -0
- package/dist/elements/Tabs/Tabs.types.d.ts.map +1 -0
- package/dist/elements/Tabs/index.d.ts +12 -0
- package/dist/elements/Tabs/index.d.ts.map +1 -0
- package/dist/elements/Tabs/index.js +74 -0
- package/dist/elements/Tabs/index.js.map +1 -0
- package/dist/elements/Tabs/index.mjs +5 -0
- package/dist/elements/Tabs/index.mjs.map +1 -0
- package/dist/elements/TextField/TextField.d.ts +155 -0
- package/dist/elements/TextField/TextField.d.ts.map +1 -0
- package/dist/elements/TextField/TextField.types.d.ts +258 -0
- package/dist/elements/TextField/TextField.types.d.ts.map +1 -0
- package/dist/elements/TextField/index.css +23 -0
- package/dist/elements/TextField/index.css.map +1 -0
- package/dist/elements/TextField/index.d.ts +13 -0
- package/dist/elements/TextField/index.d.ts.map +1 -0
- package/dist/elements/TextField/index.js +260 -0
- package/dist/elements/TextField/index.js.map +1 -0
- package/dist/elements/TextField/index.mjs +207 -0
- package/dist/elements/TextField/index.mjs.map +1 -0
- package/dist/elements/TimeField/TimeField.d.ts +34 -0
- package/dist/elements/TimeField/TimeField.d.ts.map +1 -0
- package/dist/elements/TimeField/TimeField.types.d.ts +151 -0
- package/dist/elements/TimeField/TimeField.types.d.ts.map +1 -0
- package/dist/elements/TimeField/index.d.ts +24 -0
- package/dist/elements/TimeField/index.d.ts.map +1 -0
- package/dist/elements/TimeField/index.js +45 -0
- package/dist/elements/TimeField/index.js.map +1 -0
- package/dist/elements/TimeField/index.mjs +4 -0
- package/dist/elements/TimeField/index.mjs.map +1 -0
- package/dist/elements/Toast/Toast.d.ts +105 -0
- package/dist/elements/Toast/Toast.d.ts.map +1 -0
- package/dist/elements/Toast/Toast.types.d.ts +209 -0
- package/dist/elements/Toast/Toast.types.d.ts.map +1 -0
- package/dist/elements/Toast/ToastProvider.d.ts +37 -0
- package/dist/elements/Toast/ToastProvider.d.ts.map +1 -0
- package/dist/elements/Toast/Toaster.d.ts +18 -0
- package/dist/elements/Toast/Toaster.d.ts.map +1 -0
- package/dist/elements/Toast/index.d.ts +27 -0
- package/dist/elements/Toast/index.d.ts.map +1 -0
- package/dist/elements/Toast/index.js +60 -0
- package/dist/elements/Toast/index.js.map +1 -0
- package/dist/elements/Toast/index.mjs +7 -0
- package/dist/elements/Toast/index.mjs.map +1 -0
- package/dist/elements/Tooltip/Tooltip.d.ts +108 -0
- package/dist/elements/Tooltip/Tooltip.d.ts.map +1 -0
- package/dist/elements/Tooltip/Tooltip.types.d.ts +135 -0
- package/dist/elements/Tooltip/Tooltip.types.d.ts.map +1 -0
- package/dist/elements/Tooltip/index.d.ts +11 -0
- package/dist/elements/Tooltip/index.d.ts.map +1 -0
- package/dist/elements/Tooltip/index.js +59 -0
- package/dist/elements/Tooltip/index.js.map +1 -0
- package/dist/elements/Tooltip/index.mjs +6 -0
- package/dist/elements/Tooltip/index.mjs.map +1 -0
- package/dist/elements/index.css +23 -0
- package/dist/elements/index.css.map +1 -0
- package/dist/elements/index.d.ts +42 -0
- package/dist/elements/index.d.ts.map +1 -0
- package/dist/elements/index.js +839 -0
- package/dist/elements/index.js.map +1 -0
- package/dist/elements/index.mjs +42 -0
- package/dist/elements/index.mjs.map +1 -0
- package/dist/index.css +23 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +865 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +44 -0
- package/dist/index.mjs.map +1 -0
- package/dist/schemas/BaseComponentProps.d.ts +25 -0
- package/dist/schemas/BaseComponentProps.d.ts.map +1 -0
- package/dist/schemas/RegistryItem.d.ts +55 -0
- package/dist/schemas/RegistryItem.d.ts.map +1 -0
- package/dist/schemas/index.d.ts +3 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +29 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/index.mjs +4 -0
- package/dist/schemas/index.mjs.map +1 -0
- package/dist/styles/defaults.css +174 -0
- package/dist/styles/index.d.ts +7 -0
- package/dist/styles/index.d.ts.map +1 -0
- package/dist/styles/index.js +153 -0
- package/dist/styles/index.js.map +1 -0
- package/dist/styles/index.mjs +4 -0
- package/dist/styles/index.mjs.map +1 -0
- package/dist/styles/interaction-states.d.ts +96 -0
- package/dist/styles/interaction-states.d.ts.map +1 -0
- package/dist/styles/shared-variants.d.ts +120 -0
- package/dist/styles/shared-variants.d.ts.map +1 -0
- package/dist/styles/tokens.css +89 -0
- package/dist/utils/cn.d.ts +13 -0
- package/dist/utils/cn.d.ts.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +13 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +4 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +228 -0
- package/src/elements/Accordion/Accordion.stories.tsx +793 -0
- package/src/elements/Avatar/Avatar.stories.tsx +408 -0
- package/src/elements/Badge/Badge.stories.tsx +509 -0
- package/src/elements/Breadcrumbs/Breadcrumbs.stories.tsx +623 -0
- package/src/elements/Button/Button.stories.tsx +670 -0
- package/src/elements/ButtonGroup/ButtonGroup.stories.tsx +658 -0
- package/src/elements/Card/Card.stories.tsx +570 -0
- package/src/elements/Carousel/Carousel.stories.tsx +597 -0
- package/src/elements/Chart/Chart.stories.tsx +616 -0
- package/src/elements/Checkbox/Checkbox.stories.tsx +514 -0
- package/src/elements/CheckboxGroup/CheckboxGroup.stories.tsx +1514 -0
- package/src/elements/DatePicker/DatePicker.stories.tsx +341 -0
- package/src/elements/Dropdown/Dropdown.stories.tsx +397 -0
- package/src/elements/FileField/FileField.stories.tsx +1021 -0
- package/src/elements/FileField/FileProgress.stories.tsx +359 -0
- package/src/elements/FormLayout/FormLayout.stories.tsx +637 -0
- package/src/elements/Modal/Modal.stories.tsx +640 -0
- package/src/elements/NumberField/NumberField.stories.tsx +678 -0
- package/src/elements/OTPInput/OTPInput.stories.tsx +404 -0
- package/src/elements/Panel/Panel.stories.tsx +769 -0
- package/src/elements/Progress/Progress.stories.tsx +668 -0
- package/src/elements/RadioGroup/RadioGroup.stories.tsx +1153 -0
- package/src/elements/Resizable/Resizable.stories.tsx +374 -0
- package/src/elements/Select/Select.stories.tsx +362 -0
- package/src/elements/Skeleton/Skeleton.stories.tsx +284 -0
- package/src/elements/Switch/Switch.stories.tsx +441 -0
- package/src/elements/Table/Table.stories.tsx +790 -0
- package/src/elements/Tabs/Tabs.stories.tsx +661 -0
- package/src/elements/TextField/TextField.stories.tsx +1878 -0
- package/src/elements/TimeField/TimeField.stories.tsx +671 -0
- package/src/elements/Toast/Toast.stories.tsx +971 -0
- package/src/elements/Tooltip/Tooltip.stories.tsx +748 -0
- package/tailwind.config.js +10 -0
- package/tailwind.config.v3.js +10 -0
|
@@ -0,0 +1,1878 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TextField Component Stories
|
|
3
|
+
* Storybook stories for TextField component demonstrating all variants, sizes, and states
|
|
4
|
+
*
|
|
5
|
+
* @see spec.md FR-009 to FR-014 (Accessibility Requirements - WCAG 2.2 AAA)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Meta, StoryObj } from '@storybook/nextjs';
|
|
9
|
+
import { TextField } from './TextField';
|
|
10
|
+
|
|
11
|
+
const meta = {
|
|
12
|
+
title: 'Elements/TextField',
|
|
13
|
+
component: TextField,
|
|
14
|
+
parameters: {
|
|
15
|
+
layout: 'centered',
|
|
16
|
+
docs: {
|
|
17
|
+
description: {
|
|
18
|
+
component:
|
|
19
|
+
'Accessible text input component with label, description, and error handling. Built with React Aria TextField primitive, CVA variant styling, and WCAG 2.2 AAA compliance (7:1 contrast ratio, proper ARIA attributes).',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
tags: ['autodocs'],
|
|
24
|
+
argTypes: {
|
|
25
|
+
size: {
|
|
26
|
+
control: 'select',
|
|
27
|
+
options: ['sm', 'default', 'lg'],
|
|
28
|
+
description: 'TextField size',
|
|
29
|
+
table: {
|
|
30
|
+
defaultValue: { summary: 'default' },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
label: {
|
|
34
|
+
control: 'text',
|
|
35
|
+
description: 'Label text for the input',
|
|
36
|
+
},
|
|
37
|
+
description: {
|
|
38
|
+
control: 'text',
|
|
39
|
+
description: 'Helper text displayed below the input',
|
|
40
|
+
},
|
|
41
|
+
errorMessage: {
|
|
42
|
+
control: 'text',
|
|
43
|
+
description: 'Error message displayed when isInvalid is true',
|
|
44
|
+
},
|
|
45
|
+
type: {
|
|
46
|
+
control: 'select',
|
|
47
|
+
options: ['text', 'email', 'password', 'tel', 'url', 'search'],
|
|
48
|
+
description: 'HTML input type',
|
|
49
|
+
table: {
|
|
50
|
+
defaultValue: { summary: 'text' },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
isRequired: {
|
|
54
|
+
control: 'boolean',
|
|
55
|
+
description: 'Shows required indicator and sets aria-required',
|
|
56
|
+
table: {
|
|
57
|
+
defaultValue: { summary: 'false' },
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
isReadOnly: {
|
|
61
|
+
control: 'boolean',
|
|
62
|
+
description: 'Makes input read-only',
|
|
63
|
+
table: {
|
|
64
|
+
defaultValue: { summary: 'false' },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
isDisabled: {
|
|
68
|
+
control: 'boolean',
|
|
69
|
+
description: 'Disables the input',
|
|
70
|
+
table: {
|
|
71
|
+
defaultValue: { summary: 'false' },
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
isInvalid: {
|
|
75
|
+
control: 'boolean',
|
|
76
|
+
description: 'Shows error styling and displays error message',
|
|
77
|
+
table: {
|
|
78
|
+
defaultValue: { summary: 'false' },
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
placeholder: {
|
|
82
|
+
control: 'text',
|
|
83
|
+
description: 'Placeholder text for the input',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
} satisfies Meta<typeof TextField>;
|
|
87
|
+
|
|
88
|
+
export default meta;
|
|
89
|
+
type Story = StoryObj<typeof meta>;
|
|
90
|
+
|
|
91
|
+
// Basic stories
|
|
92
|
+
export const Default: Story = {
|
|
93
|
+
args: {
|
|
94
|
+
label: 'Username',
|
|
95
|
+
placeholder: 'Enter your username',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const WithDescription: Story = {
|
|
100
|
+
args: {
|
|
101
|
+
label: 'Email',
|
|
102
|
+
description: "We'll never share your email with anyone.",
|
|
103
|
+
placeholder: 'you@example.com',
|
|
104
|
+
type: 'email',
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const WithError: Story = {
|
|
109
|
+
args: {
|
|
110
|
+
label: 'Username',
|
|
111
|
+
errorMessage: 'Username is required',
|
|
112
|
+
isInvalid: true,
|
|
113
|
+
},
|
|
114
|
+
parameters: {
|
|
115
|
+
docs: {
|
|
116
|
+
description: {
|
|
117
|
+
story: 'Error state with validation message. Description text is hidden when error is shown.',
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const Required: Story = {
|
|
124
|
+
args: {
|
|
125
|
+
label: 'Email',
|
|
126
|
+
description: 'This field is required',
|
|
127
|
+
isRequired: true,
|
|
128
|
+
type: 'email',
|
|
129
|
+
},
|
|
130
|
+
parameters: {
|
|
131
|
+
docs: {
|
|
132
|
+
description: {
|
|
133
|
+
story: 'Required field with visual indicator (*) and aria-required attribute.',
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export const Disabled: Story = {
|
|
140
|
+
args: {
|
|
141
|
+
label: 'Username',
|
|
142
|
+
value: 'john.doe',
|
|
143
|
+
isDisabled: true,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const ReadOnly: Story = {
|
|
148
|
+
args: {
|
|
149
|
+
label: 'Username',
|
|
150
|
+
value: 'john.doe',
|
|
151
|
+
isReadOnly: true,
|
|
152
|
+
description: 'This field cannot be edited',
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const WithoutLabel: Story = {
|
|
157
|
+
args: {
|
|
158
|
+
placeholder: 'Search...',
|
|
159
|
+
type: 'search',
|
|
160
|
+
'aria-label': 'Search',
|
|
161
|
+
},
|
|
162
|
+
parameters: {
|
|
163
|
+
docs: {
|
|
164
|
+
description: {
|
|
165
|
+
story: 'TextField without visible label. aria-label is required for accessibility.',
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Size variants
|
|
172
|
+
export const Small: Story = {
|
|
173
|
+
args: {
|
|
174
|
+
label: 'Username',
|
|
175
|
+
size: 'sm',
|
|
176
|
+
placeholder: 'Small input',
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export const Large: Story = {
|
|
181
|
+
args: {
|
|
182
|
+
label: 'Username',
|
|
183
|
+
size: 'lg',
|
|
184
|
+
placeholder: 'Large input',
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export const AllSizes: Story = {
|
|
189
|
+
render: () => (
|
|
190
|
+
<div className="flex flex-col gap-6 w-80">
|
|
191
|
+
<TextField label="Small" size="sm" placeholder="Small input" />
|
|
192
|
+
<TextField label="Default" size="default" placeholder="Default input" />
|
|
193
|
+
<TextField label="Large" size="lg" placeholder="Large input" />
|
|
194
|
+
</div>
|
|
195
|
+
),
|
|
196
|
+
parameters: {
|
|
197
|
+
docs: {
|
|
198
|
+
description: {
|
|
199
|
+
story: 'All 3 size variants: sm (h-9), default (h-10), lg (h-11).',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// Input type variants
|
|
206
|
+
export const TextType: Story = {
|
|
207
|
+
args: {
|
|
208
|
+
label: 'Full Name',
|
|
209
|
+
type: 'text',
|
|
210
|
+
placeholder: 'John Doe',
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export const EmailType: Story = {
|
|
215
|
+
args: {
|
|
216
|
+
label: 'Email Address',
|
|
217
|
+
type: 'email',
|
|
218
|
+
placeholder: 'you@example.com',
|
|
219
|
+
description: 'We respect your privacy',
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const PasswordType: Story = {
|
|
224
|
+
args: {
|
|
225
|
+
label: 'Password',
|
|
226
|
+
type: 'password',
|
|
227
|
+
placeholder: 'Enter your password',
|
|
228
|
+
description: 'At least 8 characters',
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// NOTE: TelType removed (breaking change) - use TelField component instead
|
|
233
|
+
|
|
234
|
+
export const UrlType: Story = {
|
|
235
|
+
args: {
|
|
236
|
+
label: 'Website',
|
|
237
|
+
type: 'url',
|
|
238
|
+
placeholder: 'https://example.com',
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export const SearchType: Story = {
|
|
243
|
+
args: {
|
|
244
|
+
label: 'Search',
|
|
245
|
+
type: 'search',
|
|
246
|
+
placeholder: 'Search...',
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// AutoComplete Examples (Phase 2)
|
|
251
|
+
export const AutoCompleteEmail: Story = {
|
|
252
|
+
args: {
|
|
253
|
+
label: 'Email Address',
|
|
254
|
+
type: 'email',
|
|
255
|
+
autoComplete: 'email',
|
|
256
|
+
placeholder: 'you@example.com',
|
|
257
|
+
description: 'Browser will suggest previously entered emails',
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export const AutoCompleteName: Story = {
|
|
262
|
+
args: {
|
|
263
|
+
label: 'Full Name',
|
|
264
|
+
type: 'text',
|
|
265
|
+
autoComplete: 'name',
|
|
266
|
+
placeholder: 'John Doe',
|
|
267
|
+
description: 'Browser will suggest your name from previous entries',
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
export const AutoCompleteCurrentPassword: Story = {
|
|
272
|
+
args: {
|
|
273
|
+
label: 'Password',
|
|
274
|
+
type: 'password',
|
|
275
|
+
autoComplete: 'current-password',
|
|
276
|
+
placeholder: 'Enter your password',
|
|
277
|
+
description: 'Password manager will offer to fill this field',
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
export const AutoCompleteNewPassword: Story = {
|
|
282
|
+
args: {
|
|
283
|
+
label: 'New Password',
|
|
284
|
+
type: 'password',
|
|
285
|
+
autoComplete: 'new-password',
|
|
286
|
+
placeholder: 'Create a strong password',
|
|
287
|
+
description: 'Password manager will offer to generate and save',
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
export const AutoCompleteAddressLine1: Story = {
|
|
292
|
+
args: {
|
|
293
|
+
label: 'Street Address',
|
|
294
|
+
type: 'text',
|
|
295
|
+
autoComplete: 'address-line1',
|
|
296
|
+
placeholder: '123 Main St',
|
|
297
|
+
description: 'Browser will suggest street address',
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
export const AutoCompleteCreditCard: Story = {
|
|
302
|
+
args: {
|
|
303
|
+
label: 'Card Number',
|
|
304
|
+
type: 'text',
|
|
305
|
+
autoComplete: 'cc-number',
|
|
306
|
+
placeholder: '1234 5678 9012 3456',
|
|
307
|
+
description: 'Browser may offer saved card numbers',
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
export const AutoCompleteOff: Story = {
|
|
312
|
+
args: {
|
|
313
|
+
label: 'One-Time Code',
|
|
314
|
+
type: 'text',
|
|
315
|
+
autoComplete: 'off',
|
|
316
|
+
placeholder: 'Enter code',
|
|
317
|
+
description: 'Browser autocomplete disabled for security',
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
export const LoginFormWithAutoComplete: Story = {
|
|
322
|
+
render: () => (
|
|
323
|
+
<div className="space-y-4 max-w-sm">
|
|
324
|
+
<h3 className="text-lg font-semibold">Login Form with AutoComplete</h3>
|
|
325
|
+
<TextField
|
|
326
|
+
label="Email"
|
|
327
|
+
type="email"
|
|
328
|
+
autoComplete="email"
|
|
329
|
+
placeholder="you@example.com"
|
|
330
|
+
isRequired
|
|
331
|
+
/>
|
|
332
|
+
<TextField
|
|
333
|
+
label="Password"
|
|
334
|
+
type="password"
|
|
335
|
+
autoComplete="current-password"
|
|
336
|
+
placeholder="Enter your password"
|
|
337
|
+
isRequired
|
|
338
|
+
/>
|
|
339
|
+
<p className="text-sm text-muted-foreground">
|
|
340
|
+
Browser will remember and suggest your credentials
|
|
341
|
+
</p>
|
|
342
|
+
</div>
|
|
343
|
+
),
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
export const SignupFormWithAutoComplete: Story = {
|
|
347
|
+
render: () => (
|
|
348
|
+
<div className="space-y-4 max-w-sm">
|
|
349
|
+
<h3 className="text-lg font-semibold">Signup Form with AutoComplete</h3>
|
|
350
|
+
<TextField
|
|
351
|
+
label="Full Name"
|
|
352
|
+
type="text"
|
|
353
|
+
autoComplete="name"
|
|
354
|
+
placeholder="John Doe"
|
|
355
|
+
isRequired
|
|
356
|
+
/>
|
|
357
|
+
<TextField
|
|
358
|
+
label="Email"
|
|
359
|
+
type="email"
|
|
360
|
+
autoComplete="email"
|
|
361
|
+
placeholder="you@example.com"
|
|
362
|
+
isRequired
|
|
363
|
+
/>
|
|
364
|
+
<TextField
|
|
365
|
+
label="Create Password"
|
|
366
|
+
type="password"
|
|
367
|
+
autoComplete="new-password"
|
|
368
|
+
placeholder="Create a strong password"
|
|
369
|
+
description="Password manager will offer to generate and save"
|
|
370
|
+
isRequired
|
|
371
|
+
/>
|
|
372
|
+
</div>
|
|
373
|
+
),
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
export const AddressFormWithAutoComplete: Story = {
|
|
377
|
+
render: () => (
|
|
378
|
+
<div className="space-y-4 max-w-sm">
|
|
379
|
+
<h3 className="text-lg font-semibold">Address Form with AutoComplete</h3>
|
|
380
|
+
<TextField
|
|
381
|
+
label="Street Address"
|
|
382
|
+
type="text"
|
|
383
|
+
autoComplete="address-line1"
|
|
384
|
+
placeholder="123 Main St"
|
|
385
|
+
isRequired
|
|
386
|
+
/>
|
|
387
|
+
<TextField
|
|
388
|
+
label="Apartment/Suite"
|
|
389
|
+
type="text"
|
|
390
|
+
autoComplete="address-line2"
|
|
391
|
+
placeholder="Apt 4B"
|
|
392
|
+
/>
|
|
393
|
+
<TextField
|
|
394
|
+
label="City"
|
|
395
|
+
type="text"
|
|
396
|
+
autoComplete="address-level2"
|
|
397
|
+
placeholder="New York"
|
|
398
|
+
isRequired
|
|
399
|
+
/>
|
|
400
|
+
<TextField
|
|
401
|
+
label="Postal Code"
|
|
402
|
+
type="text"
|
|
403
|
+
autoComplete="postal-code"
|
|
404
|
+
placeholder="10001"
|
|
405
|
+
isRequired
|
|
406
|
+
/>
|
|
407
|
+
<p className="text-sm text-muted-foreground">
|
|
408
|
+
Browser will suggest your saved address details
|
|
409
|
+
</p>
|
|
410
|
+
</div>
|
|
411
|
+
),
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// Copy/Paste Restriction Examples (Phase 3)
|
|
415
|
+
export const PasswordConfirmationNoPaste: Story = {
|
|
416
|
+
args: {
|
|
417
|
+
label: 'Confirm Password',
|
|
418
|
+
type: 'password',
|
|
419
|
+
disableCopyPaste: true,
|
|
420
|
+
description: 'Please type your password again (pasting is not allowed)',
|
|
421
|
+
isRequired: true,
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
export const EmailConfirmationNoPaste: Story = {
|
|
426
|
+
args: {
|
|
427
|
+
label: 'Confirm Email',
|
|
428
|
+
type: 'email',
|
|
429
|
+
disableCopyPaste: true,
|
|
430
|
+
description: 'Please type your email again to confirm',
|
|
431
|
+
isRequired: true,
|
|
432
|
+
},
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
export const SecureFieldWithShakeDemo: Story = {
|
|
436
|
+
args: {
|
|
437
|
+
label: 'One-Time Code',
|
|
438
|
+
type: 'text',
|
|
439
|
+
disableCopyPaste: true,
|
|
440
|
+
description: 'Try to paste - the field will shake to indicate pasting is not allowed',
|
|
441
|
+
placeholder: 'Enter code manually',
|
|
442
|
+
},
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
export const CopyPastePasswordConfirmation: Story = {
|
|
446
|
+
render: () => (
|
|
447
|
+
<div className="space-y-4 max-w-sm">
|
|
448
|
+
<h3 className="text-lg font-semibold">Password Confirmation Example</h3>
|
|
449
|
+
<TextField
|
|
450
|
+
label="New Password"
|
|
451
|
+
type="password"
|
|
452
|
+
autoComplete="new-password"
|
|
453
|
+
placeholder="Create password"
|
|
454
|
+
description="Create a strong password"
|
|
455
|
+
isRequired
|
|
456
|
+
/>
|
|
457
|
+
<TextField
|
|
458
|
+
label="Confirm Password"
|
|
459
|
+
type="password"
|
|
460
|
+
disableCopyPaste={true}
|
|
461
|
+
placeholder="Type password again"
|
|
462
|
+
description="Please type your password again (pasting is not allowed for security)"
|
|
463
|
+
isRequired
|
|
464
|
+
/>
|
|
465
|
+
<p className="text-sm text-muted-foreground">
|
|
466
|
+
The confirmation field prevents pasting to ensure you typed the password correctly.
|
|
467
|
+
If you try to paste, the field will shake and announce the restriction to screen readers.
|
|
468
|
+
</p>
|
|
469
|
+
</div>
|
|
470
|
+
),
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
export const EmailConfirmationForm: Story = {
|
|
474
|
+
render: () => (
|
|
475
|
+
<div className="space-y-4 max-w-sm">
|
|
476
|
+
<h3 className="text-lg font-semibold">Email Confirmation Example</h3>
|
|
477
|
+
<TextField
|
|
478
|
+
label="Email Address"
|
|
479
|
+
type="email"
|
|
480
|
+
autoComplete="email"
|
|
481
|
+
placeholder="you@example.com"
|
|
482
|
+
isRequired
|
|
483
|
+
/>
|
|
484
|
+
<TextField
|
|
485
|
+
label="Confirm Email"
|
|
486
|
+
type="email"
|
|
487
|
+
disableCopyPaste={true}
|
|
488
|
+
placeholder="Type email again"
|
|
489
|
+
description="Re-enter your email address to confirm"
|
|
490
|
+
isRequired
|
|
491
|
+
/>
|
|
492
|
+
<p className="text-sm text-muted-foreground">
|
|
493
|
+
Pasting is disabled in the confirmation field to prevent accidental errors.
|
|
494
|
+
</p>
|
|
495
|
+
</div>
|
|
496
|
+
),
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// Pattern Validation Examples (Phase 4)
|
|
500
|
+
export const USZipCodePattern: Story = {
|
|
501
|
+
args: {
|
|
502
|
+
label: 'ZIP Code',
|
|
503
|
+
type: 'text',
|
|
504
|
+
pattern: String.raw`\d{5}`,
|
|
505
|
+
patternDescription: '5-digit ZIP code (e.g., 12345)',
|
|
506
|
+
placeholder: '12345',
|
|
507
|
+
isRequired: true,
|
|
508
|
+
},
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
export const UsernamePattern: Story = {
|
|
512
|
+
args: {
|
|
513
|
+
label: 'Username',
|
|
514
|
+
type: 'text',
|
|
515
|
+
pattern: '[a-zA-Z0-9_]{3,16}',
|
|
516
|
+
patternDescription: '3-16 characters: letters, numbers, or underscores',
|
|
517
|
+
placeholder: 'john_doe123',
|
|
518
|
+
isRequired: true,
|
|
519
|
+
},
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
export const HexColorPattern: Story = {
|
|
523
|
+
args: {
|
|
524
|
+
label: 'Brand Color',
|
|
525
|
+
type: 'text',
|
|
526
|
+
pattern: '^#[0-9A-Fa-f]{6}$',
|
|
527
|
+
patternDescription: 'Hex color code (e.g., #FF5733)',
|
|
528
|
+
placeholder: '#FF5733',
|
|
529
|
+
defaultValue: '#',
|
|
530
|
+
},
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
export const PhoneNumberPattern: Story = {
|
|
534
|
+
args: {
|
|
535
|
+
label: 'Phone Number',
|
|
536
|
+
type: 'text',
|
|
537
|
+
pattern: String.raw`\d{3}-\d{3}-\d{4}`,
|
|
538
|
+
patternDescription: 'Format: 123-456-7890',
|
|
539
|
+
placeholder: '123-456-7890',
|
|
540
|
+
isRequired: true,
|
|
541
|
+
},
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
export const CanadianPostalCodePattern: Story = {
|
|
545
|
+
args: {
|
|
546
|
+
label: 'Postal Code',
|
|
547
|
+
type: 'text',
|
|
548
|
+
pattern: '[A-Z]\\d[A-Z] \\d[A-Z]\\d',
|
|
549
|
+
patternDescription: 'Format: A1A 1A1 (uppercase letters and digits)',
|
|
550
|
+
placeholder: 'K1A 0B1',
|
|
551
|
+
isRequired: true,
|
|
552
|
+
},
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
export const SlugPattern: Story = {
|
|
556
|
+
args: {
|
|
557
|
+
label: 'URL Slug',
|
|
558
|
+
type: 'text',
|
|
559
|
+
pattern: '^[a-z0-9]+(?:-[a-z0-9]+)*$',
|
|
560
|
+
patternDescription: 'Lowercase letters, numbers, and hyphens (e.g., my-blog-post)',
|
|
561
|
+
placeholder: 'my-article-title',
|
|
562
|
+
description: 'This will be used in your article URL',
|
|
563
|
+
},
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
export const CreditCardPattern: Story = {
|
|
567
|
+
args: {
|
|
568
|
+
label: 'Card Number',
|
|
569
|
+
type: 'text',
|
|
570
|
+
pattern: String.raw`\d{4} \d{4} \d{4} \d{4}`,
|
|
571
|
+
patternDescription: '16 digits in groups of 4 (e.g., 1234 5678 9012 3456)',
|
|
572
|
+
placeholder: '1234 5678 9012 3456',
|
|
573
|
+
autoComplete: 'cc-number',
|
|
574
|
+
isRequired: true,
|
|
575
|
+
},
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
export const PatternValidationForm: Story = {
|
|
579
|
+
render: () => (
|
|
580
|
+
<div className="space-y-4 max-w-md">
|
|
581
|
+
<h3 className="text-lg font-semibold">Registration Form with Pattern Validation</h3>
|
|
582
|
+
<TextField
|
|
583
|
+
label="Username"
|
|
584
|
+
pattern="[a-z0-9_]{3,16}"
|
|
585
|
+
patternDescription="3-16 lowercase letters, numbers, or underscores"
|
|
586
|
+
placeholder="john_doe"
|
|
587
|
+
description="Choose a unique username"
|
|
588
|
+
isRequired
|
|
589
|
+
/>
|
|
590
|
+
<TextField
|
|
591
|
+
label="Phone Number"
|
|
592
|
+
pattern={String.raw`\(\d{3}\) \d{3}-\d{4}`}
|
|
593
|
+
patternDescription="Format: (555) 123-4567"
|
|
594
|
+
placeholder="(555) 123-4567"
|
|
595
|
+
autoComplete="tel"
|
|
596
|
+
isRequired
|
|
597
|
+
/>
|
|
598
|
+
<TextField
|
|
599
|
+
label="Website"
|
|
600
|
+
type="url"
|
|
601
|
+
pattern="https?://.+"
|
|
602
|
+
patternDescription="Must start with http:// or https://"
|
|
603
|
+
placeholder="https://example.com"
|
|
604
|
+
autoComplete="url"
|
|
605
|
+
/>
|
|
606
|
+
<TextField
|
|
607
|
+
label="Promo Code"
|
|
608
|
+
pattern="[A-Z]{4}\\d{4}"
|
|
609
|
+
patternDescription="4 uppercase letters followed by 4 digits (e.g., SAVE2024)"
|
|
610
|
+
placeholder="SAVE2024"
|
|
611
|
+
/>
|
|
612
|
+
<p className="text-sm text-muted-foreground">
|
|
613
|
+
All fields use HTML5 pattern validation. Invalid entries will be highlighted when you submit or move to the next field.
|
|
614
|
+
</p>
|
|
615
|
+
</div>
|
|
616
|
+
),
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
// Success Validation Examples (Phase 5)
|
|
620
|
+
export const SuccessfulEmailValidation: Story = {
|
|
621
|
+
args: {
|
|
622
|
+
label: 'Email Address',
|
|
623
|
+
type: 'email',
|
|
624
|
+
isValid: true,
|
|
625
|
+
successMessage: 'Email is available!',
|
|
626
|
+
defaultValue: 'john@example.com',
|
|
627
|
+
},
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
export const SuccessfulUsernameCheck: Story = {
|
|
631
|
+
args: {
|
|
632
|
+
label: 'Username',
|
|
633
|
+
type: 'text',
|
|
634
|
+
pattern: '[a-z0-9_]{3,16}',
|
|
635
|
+
patternDescription: '3-16 lowercase letters, numbers, or underscores',
|
|
636
|
+
isValid: true,
|
|
637
|
+
successMessage: 'Username is available!',
|
|
638
|
+
defaultValue: 'john_doe',
|
|
639
|
+
},
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
export const SuccessfulPasswordStrength: Story = {
|
|
643
|
+
args: {
|
|
644
|
+
label: 'Password',
|
|
645
|
+
type: 'password',
|
|
646
|
+
isValid: true,
|
|
647
|
+
successMessage: 'Strong password!',
|
|
648
|
+
description: 'Use at least 8 characters with letters, numbers, and symbols',
|
|
649
|
+
},
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
export const SuccessfulZipCodeValidation: Story = {
|
|
653
|
+
args: {
|
|
654
|
+
label: 'ZIP Code',
|
|
655
|
+
pattern: String.raw`\d{5}`,
|
|
656
|
+
patternDescription: '5-digit ZIP code',
|
|
657
|
+
isValid: true,
|
|
658
|
+
successMessage: 'Valid ZIP code',
|
|
659
|
+
defaultValue: '94102',
|
|
660
|
+
},
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
export const SuccessWithoutMessage: Story = {
|
|
664
|
+
args: {
|
|
665
|
+
label: 'Email',
|
|
666
|
+
type: 'email',
|
|
667
|
+
isValid: true,
|
|
668
|
+
defaultValue: 'verified@example.com',
|
|
669
|
+
description: 'This field is validated',
|
|
670
|
+
},
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
export const RegistrationFormWithValidation: Story = {
|
|
674
|
+
render: () => (
|
|
675
|
+
<div className="space-y-4 max-w-md">
|
|
676
|
+
<h3 className="text-lg font-semibold">Registration Form with Success States</h3>
|
|
677
|
+
<TextField
|
|
678
|
+
label="Username"
|
|
679
|
+
pattern="[a-z0-9_]{3,16}"
|
|
680
|
+
patternDescription="3-16 lowercase letters, numbers, or underscores"
|
|
681
|
+
isValid={true}
|
|
682
|
+
successMessage="Username is available!"
|
|
683
|
+
defaultValue="john_doe"
|
|
684
|
+
isRequired
|
|
685
|
+
/>
|
|
686
|
+
<TextField
|
|
687
|
+
label="Email Address"
|
|
688
|
+
type="email"
|
|
689
|
+
isValid={true}
|
|
690
|
+
successMessage="Email verified!"
|
|
691
|
+
defaultValue="john@example.com"
|
|
692
|
+
autoComplete="email"
|
|
693
|
+
isRequired
|
|
694
|
+
/>
|
|
695
|
+
<TextField
|
|
696
|
+
label="Password"
|
|
697
|
+
type="password"
|
|
698
|
+
isValid={true}
|
|
699
|
+
successMessage="Strong password!"
|
|
700
|
+
autoComplete="new-password"
|
|
701
|
+
isRequired
|
|
702
|
+
/>
|
|
703
|
+
<TextField
|
|
704
|
+
label="Referral Code"
|
|
705
|
+
pattern="[A-Z]{4}\\d{4}"
|
|
706
|
+
patternDescription="4 letters + 4 digits (e.g., SAVE2024)"
|
|
707
|
+
placeholder="SAVE2024"
|
|
708
|
+
/>
|
|
709
|
+
<p className="text-sm text-muted-foreground">
|
|
710
|
+
Fields with green borders and checkmarks indicate successful validation.
|
|
711
|
+
</p>
|
|
712
|
+
</div>
|
|
713
|
+
),
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
// Expand on Focus Examples (Phase 6)
|
|
717
|
+
export const SearchFieldExpandOnFocus: Story = {
|
|
718
|
+
args: {
|
|
719
|
+
label: 'Search',
|
|
720
|
+
type: 'search',
|
|
721
|
+
expandOnFocus: true,
|
|
722
|
+
collapsedWidth: '150px',
|
|
723
|
+
placeholder: 'Search...',
|
|
724
|
+
},
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
export const CompactEmailField: Story = {
|
|
728
|
+
args: {
|
|
729
|
+
label: 'Email',
|
|
730
|
+
type: 'email',
|
|
731
|
+
expandOnFocus: true,
|
|
732
|
+
collapsedWidth: '200px',
|
|
733
|
+
placeholder: 'you@example.com',
|
|
734
|
+
autoComplete: 'email',
|
|
735
|
+
},
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
export const CustomCollapsedWidth: Story = {
|
|
739
|
+
args: {
|
|
740
|
+
label: 'Username',
|
|
741
|
+
expandOnFocus: true,
|
|
742
|
+
collapsedWidth: '120px',
|
|
743
|
+
placeholder: 'Enter username',
|
|
744
|
+
},
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
export const ExpandWithDefaultValue: Story = {
|
|
748
|
+
args: {
|
|
749
|
+
label: 'Search Query',
|
|
750
|
+
type: 'search',
|
|
751
|
+
expandOnFocus: true,
|
|
752
|
+
collapsedWidth: '150px',
|
|
753
|
+
defaultValue: 'React components',
|
|
754
|
+
description: 'Field stays expanded when it has a value',
|
|
755
|
+
},
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
export const MultipleExpandableFields: Story = {
|
|
759
|
+
render: () => (
|
|
760
|
+
<div className="space-y-4 max-w-md">
|
|
761
|
+
<h3 className="text-lg font-semibold">Compact Form with Expand on Focus</h3>
|
|
762
|
+
<div className="flex gap-2">
|
|
763
|
+
<TextField
|
|
764
|
+
label="First Name"
|
|
765
|
+
expandOnFocus={true}
|
|
766
|
+
collapsedWidth="100px"
|
|
767
|
+
placeholder="John"
|
|
768
|
+
/>
|
|
769
|
+
<TextField
|
|
770
|
+
label="Last Name"
|
|
771
|
+
expandOnFocus={true}
|
|
772
|
+
collapsedWidth="100px"
|
|
773
|
+
placeholder="Doe"
|
|
774
|
+
/>
|
|
775
|
+
</div>
|
|
776
|
+
<TextField
|
|
777
|
+
label="Email Address"
|
|
778
|
+
type="email"
|
|
779
|
+
expandOnFocus={true}
|
|
780
|
+
collapsedWidth="150px"
|
|
781
|
+
placeholder="john@example.com"
|
|
782
|
+
autoComplete="email"
|
|
783
|
+
/>
|
|
784
|
+
<TextField
|
|
785
|
+
label="Search"
|
|
786
|
+
type="search"
|
|
787
|
+
expandOnFocus={true}
|
|
788
|
+
collapsedWidth="120px"
|
|
789
|
+
placeholder="Search..."
|
|
790
|
+
/>
|
|
791
|
+
<p className="text-sm text-muted-foreground">
|
|
792
|
+
Click on any field to see it smoothly expand. Fields collapse when you leave them empty.
|
|
793
|
+
</p>
|
|
794
|
+
</div>
|
|
795
|
+
),
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
// State combinations
|
|
799
|
+
export const RequiredWithDescription: Story = {
|
|
800
|
+
args: {
|
|
801
|
+
label: 'Email',
|
|
802
|
+
description: 'Required field - please provide your email address',
|
|
803
|
+
isRequired: true,
|
|
804
|
+
type: 'email',
|
|
805
|
+
placeholder: 'you@example.com',
|
|
806
|
+
},
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
export const ErrorWithDescription: Story = {
|
|
810
|
+
args: {
|
|
811
|
+
label: 'Password',
|
|
812
|
+
description: 'At least 8 characters, one uppercase, one number',
|
|
813
|
+
errorMessage: 'Password must contain at least one uppercase letter',
|
|
814
|
+
isInvalid: true,
|
|
815
|
+
type: 'password',
|
|
816
|
+
},
|
|
817
|
+
parameters: {
|
|
818
|
+
docs: {
|
|
819
|
+
description: {
|
|
820
|
+
story: 'When both description and error are provided, error takes precedence.',
|
|
821
|
+
},
|
|
822
|
+
},
|
|
823
|
+
},
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
export const RequiredAndInvalid: Story = {
|
|
827
|
+
args: {
|
|
828
|
+
label: 'Email',
|
|
829
|
+
errorMessage: 'Email is required',
|
|
830
|
+
isRequired: true,
|
|
831
|
+
isInvalid: true,
|
|
832
|
+
type: 'email',
|
|
833
|
+
},
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
export const AllStates: Story = {
|
|
837
|
+
render: () => (
|
|
838
|
+
<div className="flex flex-col gap-6 w-80">
|
|
839
|
+
<TextField label="Default" placeholder="Default state" />
|
|
840
|
+
<TextField label="Required" isRequired description="This field is required" />
|
|
841
|
+
<TextField
|
|
842
|
+
label="Invalid"
|
|
843
|
+
errorMessage="This field is invalid"
|
|
844
|
+
isInvalid
|
|
845
|
+
/>
|
|
846
|
+
<TextField label="Disabled" isDisabled value="Disabled value" />
|
|
847
|
+
<TextField label="Read-only" isReadOnly value="Read-only value" />
|
|
848
|
+
</div>
|
|
849
|
+
),
|
|
850
|
+
parameters: {
|
|
851
|
+
docs: {
|
|
852
|
+
description: {
|
|
853
|
+
story: 'All state variants in one view.',
|
|
854
|
+
},
|
|
855
|
+
},
|
|
856
|
+
},
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
// Theme integration stories
|
|
860
|
+
export const LightTheme: Story = {
|
|
861
|
+
args: {
|
|
862
|
+
label: 'Username',
|
|
863
|
+
description: 'Light theme styling',
|
|
864
|
+
placeholder: 'Enter username',
|
|
865
|
+
},
|
|
866
|
+
parameters: {
|
|
867
|
+
backgrounds: { default: 'light' },
|
|
868
|
+
},
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
export const DarkTheme: Story = {
|
|
872
|
+
args: {
|
|
873
|
+
label: 'Username',
|
|
874
|
+
description: 'Dark theme styling with Themis semantic tokens',
|
|
875
|
+
placeholder: 'Enter username',
|
|
876
|
+
},
|
|
877
|
+
parameters: {
|
|
878
|
+
backgrounds: { default: 'dark' },
|
|
879
|
+
docs: {
|
|
880
|
+
description: {
|
|
881
|
+
story: 'TextField adapts to dark theme via semantic tokens.',
|
|
882
|
+
},
|
|
883
|
+
},
|
|
884
|
+
},
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
export const HighContrast: Story = {
|
|
888
|
+
args: {
|
|
889
|
+
label: 'Username',
|
|
890
|
+
description: 'Enhanced contrast for accessibility',
|
|
891
|
+
placeholder: 'Enter username',
|
|
892
|
+
},
|
|
893
|
+
parameters: {
|
|
894
|
+
docs: {
|
|
895
|
+
description: {
|
|
896
|
+
story: 'TextField with enhanced contrast for high contrast mode (WCAG 2.2 AAA).',
|
|
897
|
+
},
|
|
898
|
+
},
|
|
899
|
+
},
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
export const ColorblindTheme: Story = {
|
|
903
|
+
args: {
|
|
904
|
+
label: 'Email',
|
|
905
|
+
errorMessage: 'Invalid email format',
|
|
906
|
+
isInvalid: true,
|
|
907
|
+
},
|
|
908
|
+
parameters: {
|
|
909
|
+
docs: {
|
|
910
|
+
description: {
|
|
911
|
+
story:
|
|
912
|
+
'Error styling visible in colorblind mode (deuteranopia, protanopia, tritanopia support).',
|
|
913
|
+
},
|
|
914
|
+
},
|
|
915
|
+
},
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
// Real-world examples
|
|
919
|
+
export const RealWorldLoginForm: Story = {
|
|
920
|
+
render: () => (
|
|
921
|
+
<form
|
|
922
|
+
className="flex flex-col gap-4 w-80"
|
|
923
|
+
onSubmit={(e) => {
|
|
924
|
+
e.preventDefault();
|
|
925
|
+
alert('Login submitted!');
|
|
926
|
+
}}
|
|
927
|
+
>
|
|
928
|
+
<TextField
|
|
929
|
+
label="Email"
|
|
930
|
+
type="email"
|
|
931
|
+
placeholder="you@example.com"
|
|
932
|
+
isRequired
|
|
933
|
+
description="We'll never share your email"
|
|
934
|
+
/>
|
|
935
|
+
<TextField
|
|
936
|
+
label="Password"
|
|
937
|
+
type="password"
|
|
938
|
+
placeholder="Enter your password"
|
|
939
|
+
isRequired
|
|
940
|
+
description="At least 8 characters"
|
|
941
|
+
/>
|
|
942
|
+
<button
|
|
943
|
+
type="submit"
|
|
944
|
+
className="mt-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground"
|
|
945
|
+
>
|
|
946
|
+
Sign In
|
|
947
|
+
</button>
|
|
948
|
+
</form>
|
|
949
|
+
),
|
|
950
|
+
parameters: {
|
|
951
|
+
docs: {
|
|
952
|
+
description: {
|
|
953
|
+
story: 'Complete login form with email and password fields.',
|
|
954
|
+
},
|
|
955
|
+
},
|
|
956
|
+
},
|
|
957
|
+
};
|
|
958
|
+
|
|
959
|
+
export const ContactForm: Story = {
|
|
960
|
+
render: () => (
|
|
961
|
+
<form
|
|
962
|
+
className="flex flex-col gap-4 w-80"
|
|
963
|
+
onSubmit={(e) => {
|
|
964
|
+
e.preventDefault();
|
|
965
|
+
alert('Contact form submitted!');
|
|
966
|
+
}}
|
|
967
|
+
>
|
|
968
|
+
<TextField label="Name" isRequired placeholder="John Doe" />
|
|
969
|
+
<TextField
|
|
970
|
+
label="Email"
|
|
971
|
+
type="email"
|
|
972
|
+
isRequired
|
|
973
|
+
placeholder="you@example.com"
|
|
974
|
+
/>
|
|
975
|
+
<TextField
|
|
976
|
+
label="Website"
|
|
977
|
+
type="url"
|
|
978
|
+
placeholder="https://example.com"
|
|
979
|
+
description="Optional"
|
|
980
|
+
/>
|
|
981
|
+
<button
|
|
982
|
+
type="submit"
|
|
983
|
+
className="mt-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground"
|
|
984
|
+
>
|
|
985
|
+
Submit
|
|
986
|
+
</button>
|
|
987
|
+
</form>
|
|
988
|
+
),
|
|
989
|
+
parameters: {
|
|
990
|
+
docs: {
|
|
991
|
+
description: {
|
|
992
|
+
story: 'Contact form with multiple field types and validation.',
|
|
993
|
+
},
|
|
994
|
+
},
|
|
995
|
+
},
|
|
996
|
+
};
|
|
997
|
+
|
|
998
|
+
export const ValidationExample: Story = {
|
|
999
|
+
render: () => {
|
|
1000
|
+
const [email, setEmail] = useState('');
|
|
1001
|
+
const [isInvalid, setIsInvalid] = useState(false);
|
|
1002
|
+
|
|
1003
|
+
const validateEmail = (value: string) => {
|
|
1004
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1005
|
+
setIsInvalid(!emailRegex.test(value) && value.length > 0);
|
|
1006
|
+
};
|
|
1007
|
+
|
|
1008
|
+
return (
|
|
1009
|
+
<div className="w-80">
|
|
1010
|
+
<TextField
|
|
1011
|
+
label="Email"
|
|
1012
|
+
type="email"
|
|
1013
|
+
value={email}
|
|
1014
|
+
onChange={(value) => {
|
|
1015
|
+
setEmail(value);
|
|
1016
|
+
validateEmail(value);
|
|
1017
|
+
}}
|
|
1018
|
+
isInvalid={isInvalid}
|
|
1019
|
+
errorMessage="Please enter a valid email address"
|
|
1020
|
+
description="We'll send you a confirmation email"
|
|
1021
|
+
isRequired
|
|
1022
|
+
/>
|
|
1023
|
+
</div>
|
|
1024
|
+
);
|
|
1025
|
+
},
|
|
1026
|
+
parameters: {
|
|
1027
|
+
docs: {
|
|
1028
|
+
description: {
|
|
1029
|
+
story: 'Live validation example with controlled component pattern.',
|
|
1030
|
+
},
|
|
1031
|
+
},
|
|
1032
|
+
},
|
|
1033
|
+
};
|
|
1034
|
+
|
|
1035
|
+
export const MultiFieldForm: Story = {
|
|
1036
|
+
render: () => (
|
|
1037
|
+
<div className="flex flex-col gap-6 w-96">
|
|
1038
|
+
<div className="grid grid-cols-2 gap-4">
|
|
1039
|
+
<TextField label="First Name" isRequired placeholder="John" />
|
|
1040
|
+
<TextField label="Last Name" isRequired placeholder="Doe" />
|
|
1041
|
+
</div>
|
|
1042
|
+
<TextField
|
|
1043
|
+
label="Email"
|
|
1044
|
+
type="email"
|
|
1045
|
+
isRequired
|
|
1046
|
+
placeholder="you@example.com"
|
|
1047
|
+
/>
|
|
1048
|
+
<TextField
|
|
1049
|
+
label="Company"
|
|
1050
|
+
placeholder="Acme Inc."
|
|
1051
|
+
description="Optional"
|
|
1052
|
+
/>
|
|
1053
|
+
<div className="grid grid-cols-2 gap-4">
|
|
1054
|
+
<TextField label="City" placeholder="New York" />
|
|
1055
|
+
<TextField label="ZIP Code" placeholder="10001" />
|
|
1056
|
+
</div>
|
|
1057
|
+
</div>
|
|
1058
|
+
),
|
|
1059
|
+
parameters: {
|
|
1060
|
+
docs: {
|
|
1061
|
+
description: {
|
|
1062
|
+
story: 'Multi-field form layout with grid positioning.',
|
|
1063
|
+
},
|
|
1064
|
+
},
|
|
1065
|
+
},
|
|
1066
|
+
};
|
|
1067
|
+
|
|
1068
|
+
// Accessibility stories
|
|
1069
|
+
export const FocusVisible: Story = {
|
|
1070
|
+
args: {
|
|
1071
|
+
label: 'Focus Me',
|
|
1072
|
+
placeholder: 'Tab to see focus ring',
|
|
1073
|
+
},
|
|
1074
|
+
parameters: {
|
|
1075
|
+
docs: {
|
|
1076
|
+
description: {
|
|
1077
|
+
story:
|
|
1078
|
+
'Press Tab to see the focus ring. Meets WCAG 2.2 AAA standards with visible focus indicator.',
|
|
1079
|
+
},
|
|
1080
|
+
},
|
|
1081
|
+
},
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
export const KeyboardNavigation: Story = {
|
|
1085
|
+
render: () => (
|
|
1086
|
+
<div className="flex flex-col gap-4 w-80">
|
|
1087
|
+
<TextField label="First Field" placeholder="Tab to next" />
|
|
1088
|
+
<TextField label="Second Field" placeholder="Tab to next" />
|
|
1089
|
+
<TextField label="Third Field" placeholder="Tab to next" />
|
|
1090
|
+
<TextField label="Disabled Field" isDisabled placeholder="Skipped" />
|
|
1091
|
+
<TextField label="Fourth Field" placeholder="Last field" />
|
|
1092
|
+
</div>
|
|
1093
|
+
),
|
|
1094
|
+
parameters: {
|
|
1095
|
+
docs: {
|
|
1096
|
+
description: {
|
|
1097
|
+
story:
|
|
1098
|
+
'Use Tab/Shift+Tab to navigate. Disabled fields are skipped automatically.',
|
|
1099
|
+
},
|
|
1100
|
+
},
|
|
1101
|
+
},
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
export const ScreenReaderSupport: Story = {
|
|
1105
|
+
args: {
|
|
1106
|
+
label: 'Email Address',
|
|
1107
|
+
description: 'This description is announced by screen readers',
|
|
1108
|
+
isRequired: true,
|
|
1109
|
+
type: 'email',
|
|
1110
|
+
},
|
|
1111
|
+
parameters: {
|
|
1112
|
+
docs: {
|
|
1113
|
+
description: {
|
|
1114
|
+
story:
|
|
1115
|
+
'All TextField elements are properly linked via ARIA: label, description (aria-describedby), error (aria-errormessage), required (aria-required).',
|
|
1116
|
+
},
|
|
1117
|
+
},
|
|
1118
|
+
},
|
|
1119
|
+
};
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Phase 8: Prefix/Suffix Feature Stories
|
|
1123
|
+
* Demonstrates prefix and suffix for currency, units, and icons
|
|
1124
|
+
*/
|
|
1125
|
+
|
|
1126
|
+
// Currency prefix
|
|
1127
|
+
export const CurrencyInput: Story = {
|
|
1128
|
+
args: {
|
|
1129
|
+
label: 'Price',
|
|
1130
|
+
prefix: '$',
|
|
1131
|
+
type: 'text',
|
|
1132
|
+
placeholder: '0.00',
|
|
1133
|
+
description: 'Enter the price in USD',
|
|
1134
|
+
},
|
|
1135
|
+
parameters: {
|
|
1136
|
+
docs: {
|
|
1137
|
+
description: {
|
|
1138
|
+
story: 'Use prefix for currency symbols. The input automatically adds padding to accommodate the prefix.',
|
|
1139
|
+
},
|
|
1140
|
+
},
|
|
1141
|
+
},
|
|
1142
|
+
};
|
|
1143
|
+
|
|
1144
|
+
// Unit suffix
|
|
1145
|
+
export const UnitInput: Story = {
|
|
1146
|
+
args: {
|
|
1147
|
+
label: 'Weight',
|
|
1148
|
+
suffix: 'kg',
|
|
1149
|
+
type: 'text',
|
|
1150
|
+
placeholder: '0',
|
|
1151
|
+
description: 'Enter the weight in kilograms',
|
|
1152
|
+
},
|
|
1153
|
+
parameters: {
|
|
1154
|
+
docs: {
|
|
1155
|
+
description: {
|
|
1156
|
+
story: 'Use suffix for units of measurement. The input automatically adds padding to accommodate the suffix.',
|
|
1157
|
+
},
|
|
1158
|
+
},
|
|
1159
|
+
},
|
|
1160
|
+
};
|
|
1161
|
+
|
|
1162
|
+
// Both prefix and suffix
|
|
1163
|
+
export const PrefixAndSuffix: Story = {
|
|
1164
|
+
args: {
|
|
1165
|
+
label: 'Amount',
|
|
1166
|
+
prefix: '$',
|
|
1167
|
+
suffix: 'USD',
|
|
1168
|
+
type: 'text',
|
|
1169
|
+
placeholder: '0.00',
|
|
1170
|
+
description: 'Enter the amount with currency code',
|
|
1171
|
+
},
|
|
1172
|
+
parameters: {
|
|
1173
|
+
docs: {
|
|
1174
|
+
description: {
|
|
1175
|
+
story: 'Combine prefix and suffix for complex input formats like currency with codes.',
|
|
1176
|
+
},
|
|
1177
|
+
},
|
|
1178
|
+
},
|
|
1179
|
+
};
|
|
1180
|
+
|
|
1181
|
+
// Icon prefix (search)
|
|
1182
|
+
export const IconPrefix: Story = {
|
|
1183
|
+
render: () => {
|
|
1184
|
+
const SearchIcon = () => (
|
|
1185
|
+
<svg
|
|
1186
|
+
width="16"
|
|
1187
|
+
height="16"
|
|
1188
|
+
viewBox="0 0 16 16"
|
|
1189
|
+
fill="none"
|
|
1190
|
+
stroke="currentColor"
|
|
1191
|
+
strokeWidth="2"
|
|
1192
|
+
strokeLinecap="round"
|
|
1193
|
+
strokeLinejoin="round"
|
|
1194
|
+
>
|
|
1195
|
+
<circle cx="7" cy="7" r="5" />
|
|
1196
|
+
<path d="M11 11l3 3" />
|
|
1197
|
+
</svg>
|
|
1198
|
+
);
|
|
1199
|
+
|
|
1200
|
+
return (
|
|
1201
|
+
<TextField
|
|
1202
|
+
label="Search"
|
|
1203
|
+
prefix={<SearchIcon />}
|
|
1204
|
+
prefixSize={16}
|
|
1205
|
+
type="search"
|
|
1206
|
+
placeholder="Search..."
|
|
1207
|
+
/>
|
|
1208
|
+
);
|
|
1209
|
+
},
|
|
1210
|
+
parameters: {
|
|
1211
|
+
docs: {
|
|
1212
|
+
description: {
|
|
1213
|
+
story: 'Use ReactNode (like SVG icons) as prefix. Set prefixSize to control icon dimensions.',
|
|
1214
|
+
},
|
|
1215
|
+
},
|
|
1216
|
+
},
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1219
|
+
// Icon suffix (status indicator)
|
|
1220
|
+
export const IconSuffix: Story = {
|
|
1221
|
+
render: () => {
|
|
1222
|
+
const CheckIcon = () => (
|
|
1223
|
+
<svg
|
|
1224
|
+
width="16"
|
|
1225
|
+
height="16"
|
|
1226
|
+
viewBox="0 0 16 16"
|
|
1227
|
+
fill="none"
|
|
1228
|
+
stroke="currentColor"
|
|
1229
|
+
strokeWidth="2"
|
|
1230
|
+
strokeLinecap="round"
|
|
1231
|
+
strokeLinejoin="round"
|
|
1232
|
+
>
|
|
1233
|
+
<path d="M13 4L6 11L3 8" />
|
|
1234
|
+
</svg>
|
|
1235
|
+
);
|
|
1236
|
+
|
|
1237
|
+
return (
|
|
1238
|
+
<TextField
|
|
1239
|
+
label="Username"
|
|
1240
|
+
suffix={<CheckIcon />}
|
|
1241
|
+
suffixSize={16}
|
|
1242
|
+
type="text"
|
|
1243
|
+
placeholder="username"
|
|
1244
|
+
isValid={true}
|
|
1245
|
+
successMessage="Username is available"
|
|
1246
|
+
/>
|
|
1247
|
+
);
|
|
1248
|
+
},
|
|
1249
|
+
parameters: {
|
|
1250
|
+
docs: {
|
|
1251
|
+
description: {
|
|
1252
|
+
story: 'Use ReactNode (like SVG icons) as suffix for status indicators or actions.',
|
|
1253
|
+
},
|
|
1254
|
+
},
|
|
1255
|
+
},
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
// Multiple fields with prefixes/suffixes
|
|
1259
|
+
export const FormWithPrefixSuffix: Story = {
|
|
1260
|
+
render: () => (
|
|
1261
|
+
<div className="space-y-4 max-w-md">
|
|
1262
|
+
<h3 className="text-lg font-semibold">Product Details Form</h3>
|
|
1263
|
+
<TextField
|
|
1264
|
+
label="Product Name"
|
|
1265
|
+
type="text"
|
|
1266
|
+
placeholder="Enter product name"
|
|
1267
|
+
isRequired={true}
|
|
1268
|
+
/>
|
|
1269
|
+
<TextField
|
|
1270
|
+
label="Price"
|
|
1271
|
+
prefix="$"
|
|
1272
|
+
suffix="USD"
|
|
1273
|
+
type="text"
|
|
1274
|
+
placeholder="0.00"
|
|
1275
|
+
description="Retail price in US dollars"
|
|
1276
|
+
/>
|
|
1277
|
+
<TextField
|
|
1278
|
+
label="Weight"
|
|
1279
|
+
suffix="kg"
|
|
1280
|
+
type="text"
|
|
1281
|
+
placeholder="0.0"
|
|
1282
|
+
description="Shipping weight"
|
|
1283
|
+
/>
|
|
1284
|
+
<TextField
|
|
1285
|
+
label="Discount"
|
|
1286
|
+
suffix="%"
|
|
1287
|
+
type="text"
|
|
1288
|
+
placeholder="0"
|
|
1289
|
+
description="Percentage discount (0-100)"
|
|
1290
|
+
/>
|
|
1291
|
+
<TextField
|
|
1292
|
+
label="SKU"
|
|
1293
|
+
prefix="#"
|
|
1294
|
+
type="text"
|
|
1295
|
+
placeholder="ABC123"
|
|
1296
|
+
description="Stock keeping unit"
|
|
1297
|
+
/>
|
|
1298
|
+
</div>
|
|
1299
|
+
),
|
|
1300
|
+
parameters: {
|
|
1301
|
+
docs: {
|
|
1302
|
+
description: {
|
|
1303
|
+
story: 'A complete form demonstrating various prefix/suffix combinations for different data types.',
|
|
1304
|
+
},
|
|
1305
|
+
},
|
|
1306
|
+
},
|
|
1307
|
+
};
|
|
1308
|
+
|
|
1309
|
+
// Prefix/suffix with different sizes
|
|
1310
|
+
export const PrefixSuffixSizes: Story = {
|
|
1311
|
+
render: () => (
|
|
1312
|
+
<div className="space-y-4 max-w-md">
|
|
1313
|
+
<h3 className="text-lg font-semibold">Prefix/Suffix with Size Variants</h3>
|
|
1314
|
+
<TextField
|
|
1315
|
+
label="Small Amount"
|
|
1316
|
+
prefix="$"
|
|
1317
|
+
size="sm"
|
|
1318
|
+
type="text"
|
|
1319
|
+
placeholder="0.00"
|
|
1320
|
+
/>
|
|
1321
|
+
<TextField
|
|
1322
|
+
label="Default Amount"
|
|
1323
|
+
prefix="$"
|
|
1324
|
+
size="default"
|
|
1325
|
+
type="text"
|
|
1326
|
+
placeholder="0.00"
|
|
1327
|
+
/>
|
|
1328
|
+
<TextField
|
|
1329
|
+
label="Large Amount"
|
|
1330
|
+
prefix="$"
|
|
1331
|
+
size="lg"
|
|
1332
|
+
type="text"
|
|
1333
|
+
placeholder="0.00"
|
|
1334
|
+
/>
|
|
1335
|
+
</div>
|
|
1336
|
+
),
|
|
1337
|
+
parameters: {
|
|
1338
|
+
docs: {
|
|
1339
|
+
description: {
|
|
1340
|
+
story: 'Prefix and suffix work seamlessly with all input size variants (sm, default, lg).',
|
|
1341
|
+
},
|
|
1342
|
+
},
|
|
1343
|
+
},
|
|
1344
|
+
};
|
|
1345
|
+
|
|
1346
|
+
/**
|
|
1347
|
+
* Phase 7: Password Toggle Feature Stories
|
|
1348
|
+
* Demonstrates password visibility toggle for password fields
|
|
1349
|
+
*/
|
|
1350
|
+
|
|
1351
|
+
// Basic password with toggle
|
|
1352
|
+
export const PasswordWithToggle: Story = {
|
|
1353
|
+
args: {
|
|
1354
|
+
label: 'Password',
|
|
1355
|
+
type: 'password',
|
|
1356
|
+
showPasswordToggle: true,
|
|
1357
|
+
placeholder: 'Enter your password',
|
|
1358
|
+
description: 'Click the eye icon to toggle password visibility',
|
|
1359
|
+
},
|
|
1360
|
+
parameters: {
|
|
1361
|
+
docs: {
|
|
1362
|
+
description: {
|
|
1363
|
+
story: 'Enable showPasswordToggle to add a visibility toggle button. Click the eye icon or use keyboard (Tab + Space/Enter) to toggle.',
|
|
1364
|
+
},
|
|
1365
|
+
},
|
|
1366
|
+
},
|
|
1367
|
+
};
|
|
1368
|
+
|
|
1369
|
+
// Password toggle with validation
|
|
1370
|
+
export const PasswordToggleWithValidation: Story = {
|
|
1371
|
+
render: () => {
|
|
1372
|
+
const [password, setPassword] = useState('');
|
|
1373
|
+
const [isValid, setIsValid] = useState<boolean | undefined>(undefined);
|
|
1374
|
+
const [errorMessage, setErrorMessage] = useState('');
|
|
1375
|
+
|
|
1376
|
+
const validatePassword = (value: string) => {
|
|
1377
|
+
if (value.length === 0) {
|
|
1378
|
+
setIsValid(undefined);
|
|
1379
|
+
setErrorMessage('');
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
if (value.length < 8) {
|
|
1384
|
+
setIsValid(false);
|
|
1385
|
+
setErrorMessage('Password must be at least 8 characters');
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
if (!/[A-Z]/.test(value)) {
|
|
1390
|
+
setIsValid(false);
|
|
1391
|
+
setErrorMessage('Password must contain at least one uppercase letter');
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
if (!/[0-9]/.test(value)) {
|
|
1396
|
+
setIsValid(false);
|
|
1397
|
+
setErrorMessage('Password must contain at least one number');
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
setIsValid(true);
|
|
1402
|
+
setErrorMessage('');
|
|
1403
|
+
};
|
|
1404
|
+
|
|
1405
|
+
return (
|
|
1406
|
+
<TextField
|
|
1407
|
+
label="New Password"
|
|
1408
|
+
type="password"
|
|
1409
|
+
showPasswordToggle={true}
|
|
1410
|
+
value={password}
|
|
1411
|
+
onChange={(val) => {
|
|
1412
|
+
setPassword(val);
|
|
1413
|
+
validatePassword(val);
|
|
1414
|
+
}}
|
|
1415
|
+
isInvalid={isValid === false}
|
|
1416
|
+
isValid={isValid === true}
|
|
1417
|
+
errorMessage={errorMessage}
|
|
1418
|
+
successMessage="Password meets all requirements"
|
|
1419
|
+
description="Must be 8+ characters with uppercase and number"
|
|
1420
|
+
/>
|
|
1421
|
+
);
|
|
1422
|
+
},
|
|
1423
|
+
parameters: {
|
|
1424
|
+
docs: {
|
|
1425
|
+
description: {
|
|
1426
|
+
story: 'Password toggle works seamlessly with validation. Toggle visibility to verify password while typing.',
|
|
1427
|
+
},
|
|
1428
|
+
},
|
|
1429
|
+
},
|
|
1430
|
+
};
|
|
1431
|
+
|
|
1432
|
+
// Password confirmation form
|
|
1433
|
+
export const PasswordToggleConfirmation: Story = {
|
|
1434
|
+
render: () => {
|
|
1435
|
+
const [password, setPassword] = useState('');
|
|
1436
|
+
const [confirmPassword, setConfirmPassword] = useState('');
|
|
1437
|
+
const [passwordsMatch, setPasswordsMatch] = useState<boolean | undefined>(undefined);
|
|
1438
|
+
|
|
1439
|
+
useEffect(() => {
|
|
1440
|
+
if (confirmPassword.length > 0) {
|
|
1441
|
+
setPasswordsMatch(password === confirmPassword);
|
|
1442
|
+
} else {
|
|
1443
|
+
setPasswordsMatch(undefined);
|
|
1444
|
+
}
|
|
1445
|
+
}, [password, confirmPassword]);
|
|
1446
|
+
|
|
1447
|
+
return (
|
|
1448
|
+
<div className="space-y-4 max-w-md">
|
|
1449
|
+
<h3 className="text-lg font-semibold">Create Account</h3>
|
|
1450
|
+
<TextField
|
|
1451
|
+
label="Password"
|
|
1452
|
+
type="password"
|
|
1453
|
+
showPasswordToggle={true}
|
|
1454
|
+
value={password}
|
|
1455
|
+
onChange={setPassword}
|
|
1456
|
+
placeholder="Enter password"
|
|
1457
|
+
description="At least 8 characters"
|
|
1458
|
+
/>
|
|
1459
|
+
<TextField
|
|
1460
|
+
label="Confirm Password"
|
|
1461
|
+
type="password"
|
|
1462
|
+
showPasswordToggle={true}
|
|
1463
|
+
value={confirmPassword}
|
|
1464
|
+
onChange={setConfirmPassword}
|
|
1465
|
+
placeholder="Re-enter password"
|
|
1466
|
+
isInvalid={passwordsMatch === false}
|
|
1467
|
+
isValid={passwordsMatch === true}
|
|
1468
|
+
errorMessage="Passwords do not match"
|
|
1469
|
+
successMessage="Passwords match"
|
|
1470
|
+
/>
|
|
1471
|
+
</div>
|
|
1472
|
+
);
|
|
1473
|
+
},
|
|
1474
|
+
parameters: {
|
|
1475
|
+
docs: {
|
|
1476
|
+
description: {
|
|
1477
|
+
story: 'Common use case: password confirmation. Toggle visibility on either field independently.',
|
|
1478
|
+
},
|
|
1479
|
+
},
|
|
1480
|
+
},
|
|
1481
|
+
};
|
|
1482
|
+
|
|
1483
|
+
// Password toggle with size variants
|
|
1484
|
+
export const PasswordToggleSizes: Story = {
|
|
1485
|
+
render: () => (
|
|
1486
|
+
<div className="space-y-4 max-w-md">
|
|
1487
|
+
<h3 className="text-lg font-semibold">Password Toggle with Size Variants</h3>
|
|
1488
|
+
<TextField
|
|
1489
|
+
label="Small Password"
|
|
1490
|
+
type="password"
|
|
1491
|
+
showPasswordToggle={true}
|
|
1492
|
+
size="sm"
|
|
1493
|
+
placeholder="Enter password"
|
|
1494
|
+
/>
|
|
1495
|
+
<TextField
|
|
1496
|
+
label="Default Password"
|
|
1497
|
+
type="password"
|
|
1498
|
+
showPasswordToggle={true}
|
|
1499
|
+
size="default"
|
|
1500
|
+
placeholder="Enter password"
|
|
1501
|
+
/>
|
|
1502
|
+
<TextField
|
|
1503
|
+
label="Large Password"
|
|
1504
|
+
type="password"
|
|
1505
|
+
showPasswordToggle={true}
|
|
1506
|
+
size="lg"
|
|
1507
|
+
placeholder="Enter password"
|
|
1508
|
+
/>
|
|
1509
|
+
</div>
|
|
1510
|
+
),
|
|
1511
|
+
parameters: {
|
|
1512
|
+
docs: {
|
|
1513
|
+
description: {
|
|
1514
|
+
story: 'Password toggle works with all input size variants (sm, default, lg).',
|
|
1515
|
+
},
|
|
1516
|
+
},
|
|
1517
|
+
},
|
|
1518
|
+
};
|
|
1519
|
+
|
|
1520
|
+
// Password with disabled state
|
|
1521
|
+
export const PasswordToggleDisabled: Story = {
|
|
1522
|
+
args: {
|
|
1523
|
+
label: 'Password',
|
|
1524
|
+
type: 'password',
|
|
1525
|
+
showPasswordToggle: true,
|
|
1526
|
+
isDisabled: true,
|
|
1527
|
+
value: 'disabled-password',
|
|
1528
|
+
},
|
|
1529
|
+
parameters: {
|
|
1530
|
+
docs: {
|
|
1531
|
+
description: {
|
|
1532
|
+
story: 'When disabled, the password field and toggle button are both non-interactive.',
|
|
1533
|
+
},
|
|
1534
|
+
},
|
|
1535
|
+
},
|
|
1536
|
+
};
|
|
1537
|
+
|
|
1538
|
+
// Login form example
|
|
1539
|
+
export const PasswordToggleLoginForm: Story = {
|
|
1540
|
+
render: () => {
|
|
1541
|
+
const [email, setEmail] = useState('');
|
|
1542
|
+
const [password, setPassword] = useState('');
|
|
1543
|
+
|
|
1544
|
+
return (
|
|
1545
|
+
<div className="space-y-4 max-w-md">
|
|
1546
|
+
<h3 className="text-lg font-semibold">Login</h3>
|
|
1547
|
+
<TextField
|
|
1548
|
+
label="Email"
|
|
1549
|
+
type="email"
|
|
1550
|
+
value={email}
|
|
1551
|
+
onChange={setEmail}
|
|
1552
|
+
placeholder="you@example.com"
|
|
1553
|
+
autoComplete="email"
|
|
1554
|
+
isRequired={true}
|
|
1555
|
+
/>
|
|
1556
|
+
<TextField
|
|
1557
|
+
label="Password"
|
|
1558
|
+
type="password"
|
|
1559
|
+
showPasswordToggle={true}
|
|
1560
|
+
value={password}
|
|
1561
|
+
onChange={setPassword}
|
|
1562
|
+
placeholder="Enter your password"
|
|
1563
|
+
autoComplete="current-password"
|
|
1564
|
+
isRequired={true}
|
|
1565
|
+
/>
|
|
1566
|
+
<button
|
|
1567
|
+
type="button"
|
|
1568
|
+
className="w-full bg-[var(--themis-primary)] text-[var(--themis-primary-foreground)] py-2 px-4 rounded-md hover:opacity-90 transition-opacity"
|
|
1569
|
+
>
|
|
1570
|
+
Log In
|
|
1571
|
+
</button>
|
|
1572
|
+
</div>
|
|
1573
|
+
);
|
|
1574
|
+
},
|
|
1575
|
+
parameters: {
|
|
1576
|
+
docs: {
|
|
1577
|
+
description: {
|
|
1578
|
+
story: 'Complete login form with password toggle. Notice proper autoComplete attributes for password managers.',
|
|
1579
|
+
},
|
|
1580
|
+
},
|
|
1581
|
+
},
|
|
1582
|
+
};
|
|
1583
|
+
|
|
1584
|
+
/**
|
|
1585
|
+
* Phase 9: Integration & Polish Stories
|
|
1586
|
+
* Comprehensive examples combining multiple features
|
|
1587
|
+
*/
|
|
1588
|
+
|
|
1589
|
+
// All features combined
|
|
1590
|
+
export const AllFeaturesCombined: Story = {
|
|
1591
|
+
render: () => {
|
|
1592
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
1593
|
+
const [email, setEmail] = useState('');
|
|
1594
|
+
const [password, setPassword] = useState('');
|
|
1595
|
+
const [amount, setAmount] = useState('');
|
|
1596
|
+
const [emailValid, setEmailValid] = useState<boolean | undefined>(undefined);
|
|
1597
|
+
|
|
1598
|
+
useEffect(() => {
|
|
1599
|
+
if (email.length > 0) {
|
|
1600
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1601
|
+
setEmailValid(emailRegex.test(email));
|
|
1602
|
+
} else {
|
|
1603
|
+
setEmailValid(undefined);
|
|
1604
|
+
}
|
|
1605
|
+
}, [email]);
|
|
1606
|
+
|
|
1607
|
+
return (
|
|
1608
|
+
<div className="space-y-6 max-w-2xl">
|
|
1609
|
+
<h2 className="text-2xl font-bold">All TextField Features</h2>
|
|
1610
|
+
<p className="text-sm text-muted-foreground">
|
|
1611
|
+
This showcase demonstrates all TextField enhancement features working together:
|
|
1612
|
+
AutoComplete, Copy/Paste Restriction, Pattern Validation, Success Validation,
|
|
1613
|
+
Expand on Focus, Prefix/Suffix, and Password Toggle.
|
|
1614
|
+
</p>
|
|
1615
|
+
|
|
1616
|
+
<div className="space-y-4">
|
|
1617
|
+
{/* Feature 2: AutoComplete + Feature 6: Expand on Focus */}
|
|
1618
|
+
<TextField
|
|
1619
|
+
label="Search"
|
|
1620
|
+
type="search"
|
|
1621
|
+
prefix={
|
|
1622
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2">
|
|
1623
|
+
<circle cx="7" cy="7" r="5" />
|
|
1624
|
+
<path d="M11 11l3 3" />
|
|
1625
|
+
</svg>
|
|
1626
|
+
}
|
|
1627
|
+
value={searchQuery}
|
|
1628
|
+
onChange={setSearchQuery}
|
|
1629
|
+
placeholder="Search..."
|
|
1630
|
+
expandOnFocus={true}
|
|
1631
|
+
collapsedWidth="200px"
|
|
1632
|
+
description="Expands when focused, collapses when empty"
|
|
1633
|
+
autoComplete="off"
|
|
1634
|
+
/>
|
|
1635
|
+
|
|
1636
|
+
{/* Feature 2: AutoComplete + Feature 4: Pattern Validation + Feature 5: Success Validation */}
|
|
1637
|
+
<TextField
|
|
1638
|
+
label="Email"
|
|
1639
|
+
type="email"
|
|
1640
|
+
value={email}
|
|
1641
|
+
onChange={setEmail}
|
|
1642
|
+
placeholder="you@example.com"
|
|
1643
|
+
autoComplete="email"
|
|
1644
|
+
pattern="[^\s@]+@[^\s@]+\.[^\s@]+"
|
|
1645
|
+
patternDescription="Must be a valid email format"
|
|
1646
|
+
isValid={emailValid === true}
|
|
1647
|
+
isInvalid={emailValid === false}
|
|
1648
|
+
successMessage="Valid email address"
|
|
1649
|
+
errorMessage="Please enter a valid email"
|
|
1650
|
+
isRequired={true}
|
|
1651
|
+
/>
|
|
1652
|
+
|
|
1653
|
+
{/* Feature 7: Password Toggle + Feature 3: Copy/Paste Restriction + Feature 2: AutoComplete */}
|
|
1654
|
+
<TextField
|
|
1655
|
+
label="Password"
|
|
1656
|
+
type="password"
|
|
1657
|
+
showPasswordToggle={true}
|
|
1658
|
+
value={password}
|
|
1659
|
+
onChange={setPassword}
|
|
1660
|
+
placeholder="Enter secure password"
|
|
1661
|
+
autoComplete="new-password"
|
|
1662
|
+
description="Password cannot be copied or pasted for security"
|
|
1663
|
+
disableCopyPaste={true}
|
|
1664
|
+
isRequired={true}
|
|
1665
|
+
/>
|
|
1666
|
+
|
|
1667
|
+
{/* Feature 8: Prefix/Suffix + Feature 4: Pattern Validation */}
|
|
1668
|
+
<TextField
|
|
1669
|
+
label="Amount"
|
|
1670
|
+
type="text"
|
|
1671
|
+
prefix="$"
|
|
1672
|
+
suffix="USD"
|
|
1673
|
+
value={amount}
|
|
1674
|
+
onChange={setAmount}
|
|
1675
|
+
placeholder="0.00"
|
|
1676
|
+
pattern="^\d+(\.\d{1,2})?$"
|
|
1677
|
+
patternDescription="Enter a valid amount (e.g., 10.50)"
|
|
1678
|
+
description="Currency amount with validation"
|
|
1679
|
+
/>
|
|
1680
|
+
</div>
|
|
1681
|
+
|
|
1682
|
+
<div className="mt-6 p-4 bg-muted rounded-md">
|
|
1683
|
+
<h3 className="font-semibold mb-2">Feature Summary:</h3>
|
|
1684
|
+
<ul className="text-sm space-y-1 list-disc list-inside">
|
|
1685
|
+
<li>Search field: Prefix icon, expand on focus, autocomplete</li>
|
|
1686
|
+
<li>Email: Pattern validation, success/error states, autocomplete</li>
|
|
1687
|
+
<li>Password: Visibility toggle, copy/paste prevention, autocomplete</li>
|
|
1688
|
+
<li>Amount: Prefix/suffix, pattern validation</li>
|
|
1689
|
+
</ul>
|
|
1690
|
+
</div>
|
|
1691
|
+
</div>
|
|
1692
|
+
);
|
|
1693
|
+
},
|
|
1694
|
+
parameters: {
|
|
1695
|
+
docs: {
|
|
1696
|
+
description: {
|
|
1697
|
+
story: 'Comprehensive showcase of all TextField enhancement features working together seamlessly.',
|
|
1698
|
+
},
|
|
1699
|
+
},
|
|
1700
|
+
},
|
|
1701
|
+
};
|
|
1702
|
+
|
|
1703
|
+
// Complex form with all features
|
|
1704
|
+
export const CompleteRegistrationForm: Story = {
|
|
1705
|
+
render: () => {
|
|
1706
|
+
const [formData, setFormData] = useState({
|
|
1707
|
+
username: '',
|
|
1708
|
+
email: '',
|
|
1709
|
+
password: '',
|
|
1710
|
+
confirmPassword: '',
|
|
1711
|
+
amount: '',
|
|
1712
|
+
code: '',
|
|
1713
|
+
});
|
|
1714
|
+
|
|
1715
|
+
const [validation, setValidation] = useState({
|
|
1716
|
+
usernameValid: undefined as boolean | undefined,
|
|
1717
|
+
emailValid: undefined as boolean | undefined,
|
|
1718
|
+
passwordValid: undefined as boolean | undefined,
|
|
1719
|
+
passwordsMatch: undefined as boolean | undefined,
|
|
1720
|
+
codeValid: undefined as boolean | undefined,
|
|
1721
|
+
});
|
|
1722
|
+
|
|
1723
|
+
const updateField = (field: string, value: string) => {
|
|
1724
|
+
setFormData(prev => ({ ...prev, [field]: value }));
|
|
1725
|
+
};
|
|
1726
|
+
|
|
1727
|
+
useEffect(() => {
|
|
1728
|
+
// Username validation
|
|
1729
|
+
if (formData.username.length > 0) {
|
|
1730
|
+
setValidation(prev => ({
|
|
1731
|
+
...prev,
|
|
1732
|
+
usernameValid: /^[a-zA-Z0-9_]{3,16}$/.test(formData.username),
|
|
1733
|
+
}));
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// Email validation
|
|
1737
|
+
if (formData.email.length > 0) {
|
|
1738
|
+
setValidation(prev => ({
|
|
1739
|
+
...prev,
|
|
1740
|
+
emailValid: /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email),
|
|
1741
|
+
}));
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
// Password validation
|
|
1745
|
+
if (formData.password.length > 0) {
|
|
1746
|
+
setValidation(prev => ({
|
|
1747
|
+
...prev,
|
|
1748
|
+
passwordValid: formData.password.length >= 8 && /[A-Z]/.test(formData.password) && /[0-9]/.test(formData.password),
|
|
1749
|
+
}));
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
// Password match
|
|
1753
|
+
if (formData.confirmPassword.length > 0) {
|
|
1754
|
+
setValidation(prev => ({
|
|
1755
|
+
...prev,
|
|
1756
|
+
passwordsMatch: formData.password === formData.confirmPassword,
|
|
1757
|
+
}));
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
// Code validation
|
|
1761
|
+
if (formData.code.length > 0) {
|
|
1762
|
+
setValidation(prev => ({
|
|
1763
|
+
...prev,
|
|
1764
|
+
codeValid: /^[A-Z]{3}$/.test(formData.code),
|
|
1765
|
+
}));
|
|
1766
|
+
}
|
|
1767
|
+
}, [formData]);
|
|
1768
|
+
|
|
1769
|
+
return (
|
|
1770
|
+
<div className="space-y-4 max-w-md">
|
|
1771
|
+
<h3 className="text-lg font-semibold">Complete Registration</h3>
|
|
1772
|
+
|
|
1773
|
+
<TextField
|
|
1774
|
+
label="Username"
|
|
1775
|
+
type="text"
|
|
1776
|
+
value={formData.username}
|
|
1777
|
+
onChange={(val) => updateField('username', val)}
|
|
1778
|
+
placeholder="username"
|
|
1779
|
+
pattern="^[a-zA-Z0-9_]{3,16}$"
|
|
1780
|
+
patternDescription="3-16 characters, letters, numbers, underscore"
|
|
1781
|
+
isValid={validation.usernameValid === true}
|
|
1782
|
+
isInvalid={validation.usernameValid === false}
|
|
1783
|
+
successMessage="Username available"
|
|
1784
|
+
errorMessage="Invalid username format"
|
|
1785
|
+
autoComplete="username"
|
|
1786
|
+
isRequired={true}
|
|
1787
|
+
/>
|
|
1788
|
+
|
|
1789
|
+
<TextField
|
|
1790
|
+
label="Email"
|
|
1791
|
+
type="email"
|
|
1792
|
+
value={formData.email}
|
|
1793
|
+
onChange={(val) => updateField('email', val)}
|
|
1794
|
+
placeholder="you@example.com"
|
|
1795
|
+
isValid={validation.emailValid === true}
|
|
1796
|
+
isInvalid={validation.emailValid === false}
|
|
1797
|
+
successMessage="Valid email"
|
|
1798
|
+
errorMessage="Invalid email format"
|
|
1799
|
+
autoComplete="email"
|
|
1800
|
+
isRequired={true}
|
|
1801
|
+
/>
|
|
1802
|
+
|
|
1803
|
+
<TextField
|
|
1804
|
+
label="Password"
|
|
1805
|
+
type="password"
|
|
1806
|
+
showPasswordToggle={true}
|
|
1807
|
+
value={formData.password}
|
|
1808
|
+
onChange={(val) => updateField('password', val)}
|
|
1809
|
+
placeholder="Enter password"
|
|
1810
|
+
description="8+ chars, uppercase, number"
|
|
1811
|
+
isValid={validation.passwordValid === true}
|
|
1812
|
+
isInvalid={validation.passwordValid === false}
|
|
1813
|
+
successMessage="Strong password"
|
|
1814
|
+
errorMessage="Password too weak"
|
|
1815
|
+
autoComplete="new-password"
|
|
1816
|
+
isRequired={true}
|
|
1817
|
+
/>
|
|
1818
|
+
|
|
1819
|
+
<TextField
|
|
1820
|
+
label="Confirm Password"
|
|
1821
|
+
type="password"
|
|
1822
|
+
showPasswordToggle={true}
|
|
1823
|
+
value={formData.confirmPassword}
|
|
1824
|
+
onChange={(val) => updateField('confirmPassword', val)}
|
|
1825
|
+
placeholder="Re-enter password"
|
|
1826
|
+
disableCopyPaste={true}
|
|
1827
|
+
description="Type password again (no paste)"
|
|
1828
|
+
isValid={validation.passwordsMatch === true}
|
|
1829
|
+
isInvalid={validation.passwordsMatch === false}
|
|
1830
|
+
successMessage="Passwords match"
|
|
1831
|
+
errorMessage="Passwords don't match"
|
|
1832
|
+
autoComplete="new-password"
|
|
1833
|
+
isRequired={true}
|
|
1834
|
+
/>
|
|
1835
|
+
|
|
1836
|
+
<TextField
|
|
1837
|
+
label="Referral Code"
|
|
1838
|
+
type="text"
|
|
1839
|
+
prefix="#"
|
|
1840
|
+
value={formData.code}
|
|
1841
|
+
onChange={(val) => updateField('code', val.toUpperCase())}
|
|
1842
|
+
placeholder="ABC"
|
|
1843
|
+
pattern="^[A-Z]{3}$"
|
|
1844
|
+
patternDescription="3 uppercase letters"
|
|
1845
|
+
description="Optional referral code"
|
|
1846
|
+
/>
|
|
1847
|
+
|
|
1848
|
+
<TextField
|
|
1849
|
+
label="Initial Deposit"
|
|
1850
|
+
type="text"
|
|
1851
|
+
prefix="$"
|
|
1852
|
+
suffix="USD"
|
|
1853
|
+
value={formData.amount}
|
|
1854
|
+
onChange={(val) => updateField('amount', val)}
|
|
1855
|
+
placeholder="0.00"
|
|
1856
|
+
description="Minimum $10.00"
|
|
1857
|
+
/>
|
|
1858
|
+
|
|
1859
|
+
<button
|
|
1860
|
+
type="button"
|
|
1861
|
+
className="w-full bg-[var(--themis-primary)] text-[var(--themis-primary-foreground)] py-2 px-4 rounded-md hover:opacity-90 transition-opacity"
|
|
1862
|
+
>
|
|
1863
|
+
Create Account
|
|
1864
|
+
</button>
|
|
1865
|
+
</div>
|
|
1866
|
+
);
|
|
1867
|
+
},
|
|
1868
|
+
parameters: {
|
|
1869
|
+
docs: {
|
|
1870
|
+
description: {
|
|
1871
|
+
story: 'Complete registration form showcasing real-world usage of all TextField features.',
|
|
1872
|
+
},
|
|
1873
|
+
},
|
|
1874
|
+
},
|
|
1875
|
+
};
|
|
1876
|
+
|
|
1877
|
+
// Import React for validation example
|
|
1878
|
+
import { useState, useEffect } from 'react';
|