From dcda649d32def06339d3eff484ac0fb6bbb56338 Mon Sep 17 00:00:00 2001 From: Arthurk12 Date: Tue, 14 Jul 2026 13:45:31 -0300 Subject: [PATCH 1/5] docs: add JSDoc to component props and story descriptions Documents every public prop across all component types.ts/type.ts files so IDE autocomplete surfaces what each prop does, reusing the wording already written for Storybook's argTypes where available. Also adds a one-line description above every named story export explaining what it demonstrates. Adds eslint-plugin-jsdoc with jsdoc/multiline-blocks and jsdoc/lines-before-block, scoped to types.ts/type.ts, to keep prop JSDoc single-line with a blank line above going forward. --- eslint.config.mjs | 19 ++ package-lock.json | 229 +++++++++++++++++- package.json | 7 +- .../Accordion/component.stories.tsx | 2 + src/components/Accordion/types.ts | 15 ++ src/components/Button/component.stories.tsx | 13 + src/components/Button/type.ts | 49 ++++ src/components/Checkbox/component.stories.tsx | 2 + src/components/Checkbox/types.ts | 7 + src/components/Divider/component.stories.tsx | 1 + src/components/Hint/component.stories.tsx | 1 + src/components/Hint/types.ts | 9 + src/components/Input/types.ts | 20 +- src/components/Modal/component.stories.tsx | 1 + src/components/Modal/types.ts | 21 ++ .../Navigation/component.stories.tsx | 1 + src/components/Navigation/types.ts | 17 ++ src/components/Search/types.ts | 30 +-- src/components/Select/component.stories.tsx | 1 + src/components/Select/types.ts | 7 + src/components/Spinner/component.stories.tsx | 2 + src/components/Spinner/types.ts | 5 + .../TextAreaInput/component.stories.tsx | 2 + src/components/TextAreaInput/types.ts | 13 + .../TextInput/component.stories.tsx | 2 + src/components/TextInput/types.ts | 5 + src/components/Toggle/component.stories.tsx | 2 + src/components/Toggle/types.ts | 11 + .../Typography/component.stories.tsx | 2 + src/components/Typography/types.ts | 5 + 30 files changed, 457 insertions(+), 44 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 4af6df1..529bff2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -6,6 +6,7 @@ import reactRecommended from 'eslint-plugin-react/configs/recommended.js'; import hooksPlugin from 'eslint-plugin-react-hooks'; import importPlugin from 'eslint-plugin-import'; import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'; +import jsdocPlugin from 'eslint-plugin-jsdoc'; import globals from 'globals'; export default tseslint.config( @@ -71,4 +72,22 @@ export default tseslint.config( }, }, }, + + { + // Prop JSDoc in component `types.ts`/`type.ts` files: single-line blocks only, + // each preceded by a blank line (but not the first prop right after `{`). + files: ['src/components/**/types.ts', 'src/components/**/type.ts'], + plugins: { + jsdoc: jsdocPlugin, + }, + rules: { + 'jsdoc/multiline-blocks': ['error', { + noMultilineBlocks: true, + allowMultipleTags: false, + }], + 'jsdoc/lines-before-block': ['error', { + ignoreSingleLines: false, + }], + }, + }, ); diff --git a/package-lock.json b/package-lock.json index 6aa8e05..aea570a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "@mconf/bbb-ui-components-react", - "version": "2.2.0", + "version": "2.3.0", "license": "LGPL-3.0", "dependencies": { "@tippyjs/react": "^4.2.6", @@ -38,6 +38,7 @@ "eslint": "^9.33.0", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jsdoc": "^62.9.0", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", @@ -2027,6 +2028,43 @@ "license": "MIT", "peer": true }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.86.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.86.0.tgz", + "integrity": "sha512-ukZmRQ81WiTpDWO6D/cTBM7XbrNtutHKvAVnZN/8pldAwLoJArGOvkNyxPTBGsPjsoaQBJxlH+tE2TNA/92Qgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@typescript-eslint/types": "^8.58.0", + "comment-parser": "1.4.6", + "esquery": "^1.7.0", + "jsdoc-type-pratt-parser": "~7.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@es-joy/jsdoccomment/node_modules/jsdoc-type-pratt-parser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.2.0.tgz", + "integrity": "sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@es-joy/resolve.exports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@es-joy/resolve.exports/-/resolve.exports-1.2.0.tgz", + "integrity": "sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -3326,6 +3364,7 @@ "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.5.0.tgz", "integrity": "sha512-VPuPqXqbBPlcVSA0BmnoE4knW4/xG6Thazo8vCLWkOKusko6DtwFV6B665MMWJ9j0KFohTIf3yx2zYtYacvG1g==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.26.0" }, @@ -3345,8 +3384,7 @@ "@types/react": { "optional": true } - }, - "peer": true + } }, "node_modules/@mui/material": { "version": "6.5.0", @@ -3610,6 +3648,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@sindresorhus/base62": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/base62/-/base62-1.0.0.tgz", + "integrity": "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@storybook/addon-a11y": { "version": "8.6.18", "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-8.6.18.tgz", @@ -5928,6 +5979,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -6930,6 +6991,16 @@ "node": ">= 6" } }, + "node_modules/comment-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.6.tgz", + "integrity": "sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/common-path-prefix": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", @@ -8279,6 +8350,79 @@ "strip-bom": "^3.0.0" } }, + "node_modules/eslint-plugin-jsdoc": { + "version": "62.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.9.0.tgz", + "integrity": "sha512-PY7/X4jrVgoIDncUmITlUqK546Ltmx/Pd4Hdsu4CvSjryQZJI2mEV4vrdMufyTetMiZ5taNSqvK//BTgVUlNkA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.86.0", + "@es-joy/resolve.exports": "1.2.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.6", + "debug": "^4.4.3", + "escape-string-regexp": "^4.0.0", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "html-entities": "^2.6.0", + "object-deep-merge": "^2.0.0", + "parse-imports-exports": "^0.2.4", + "semver": "^7.7.4", + "spdx-expression-parse": "^4.0.0", + "to-valid-identifier": "^1.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", @@ -11500,6 +11644,13 @@ "node": ">=0.10.0" } }, + "node_modules/object-deep-merge": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-2.0.1.tgz", + "integrity": "sha512-aKttDKcU3pyZqKcCkDhsMn70WmZFG2JGDQLP9EcLyTSIFQRCPWLAmBZRLJnrVUrhPG1jETEEbfdgbNtJf1LyMg==", + "dev": true, + "license": "MIT" + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -11820,6 +11971,16 @@ "node": ">=6" } }, + "node_modules/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-statements": "1.0.11" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -11838,6 +11999,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true, + "license": "MIT" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -12781,6 +12949,19 @@ "dev": true, "license": "MIT" }, + "node_modules/reserved-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", + "integrity": "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -13468,6 +13649,31 @@ "node": ">=0.10.0" } }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -14171,6 +14377,23 @@ "node": ">=8.0" } }, + "node_modules/to-valid-identifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-valid-identifier/-/to-valid-identifier-1.0.0.tgz", + "integrity": "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/base62": "^1.0.0", + "reserved-identifiers": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", diff --git a/package.json b/package.json index c867055..48b774d 100644 --- a/package.json +++ b/package.json @@ -240,8 +240,8 @@ "peerDependencies": { "@emotion/react": "^11.13.0", "@emotion/styled": "^11.13.0", - "@mui/material": "^6.1.4 || ^7.0.0", "@mui/icons-material": "^6.1.4 || ^7.0.0", + "@mui/material": "^6.1.4 || ^7.0.0", "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -257,6 +257,8 @@ "@babel/preset-typescript": "^7.24.7", "@babel/register": "^7.24.6", "@eslint/js": "^9.33.0", + "@storybook/addon-a11y": "^8.6.18", + "@storybook/addon-actions": "^8.6.18", "@storybook/addon-docs": "^8.6.18", "@storybook/addon-links": "^8.6.18", "@storybook/addon-webpack5-compiler-babel": "^3.0.5", @@ -272,6 +274,7 @@ "eslint": "^9.33.0", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jsdoc": "^62.9.0", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", @@ -279,8 +282,6 @@ "globals": "^16.3.0", "path": "^0.12.7", "storybook": "^8.6.18", - "@storybook/addon-a11y": "^8.6.18", - "@storybook/addon-actions": "^8.6.18", "style-loader": "^4.0.0", "styled-components": "6.1.13", "ts-loader": "^9.5.1", diff --git a/src/components/Accordion/component.stories.tsx b/src/components/Accordion/component.stories.tsx index b8a8501..7b54464 100644 --- a/src/components/Accordion/component.stories.tsx +++ b/src/components/Accordion/component.stories.tsx @@ -54,6 +54,7 @@ const meta = { export default meta; type Story = StoryObj; +/** Basic accordion with a title and simple content, expanded/collapsed via the header. */ export const Default: Story = { args: { title: 'Accordion Title', @@ -65,6 +66,7 @@ export const Default: Story = { }, }; +/** Shows the header tooltip enabled via `tooltipLabel` and `tooltipPlacement`. */ export const WithTooltip: Story = { args: { title: 'With Tooltip', diff --git a/src/components/Accordion/types.ts b/src/components/Accordion/types.ts index 24b2e0c..87b09e4 100644 --- a/src/components/Accordion/types.ts +++ b/src/components/Accordion/types.ts @@ -13,12 +13,27 @@ export interface StyledAccordionContent { type TooltipPlacementType = typeof TOOLTIP_PLACEMENT_VALUES[number]; export interface AccordionProps { + /** The text to be displayed in the accordion header. */ title: string; + + /** Optional label for a tooltip that appears when hovering the header; no tooltip is shown when omitted. */ tooltipLabel?: string; + + /** Placement of the tooltip when `tooltipLabel` is provided. @default 'top' */ tooltipPlacement?: TooltipPlacementType; + + /** Accessible name for the expand button. */ ariaLabel?: string; + + /** ID of the element that labels the expand button. */ ariaLabelledBy?: string; + + /** ID of the element that describes the expand button. */ ariaDescribedBy?: string; + + /** Optional React node rendered inside the button header, alongside the title. @default null */ buttonHeader?: React.ReactNode; + + /** Content shown when the accordion is expanded. */ children?: React.ReactNode; }; diff --git a/src/components/Button/component.stories.tsx b/src/components/Button/component.stories.tsx index d393508..50a6554 100644 --- a/src/components/Button/component.stories.tsx +++ b/src/components/Button/component.stories.tsx @@ -115,6 +115,7 @@ const meta = { export default meta; type Story = StoryObj; +/** Renders the `primary` variant across all available colors. */ export const Primary: Story = { name: 'Primary', args: { @@ -134,6 +135,7 @@ export const Primary: Story = { ), }; +/** Renders the `secondary` variant across all available colors. */ export const Secondary: Story = { name: 'Secondary', args: { @@ -153,6 +155,7 @@ export const Secondary: Story = { ), }; +/** Renders the `tertiary` variant across all available colors. */ export const Tertiary: Story = { name: 'Tertiary', args: { @@ -172,6 +175,7 @@ export const Tertiary: Story = { ), }; +/** Renders the `subtle` variant across all available colors. */ export const Subtle: Story = { name: 'Subtle', args: { @@ -191,6 +195,7 @@ export const Subtle: Story = { ), }; +/** Renders the `stacked` layout with an icon and label across all available colors. */ export const Stacked: Story = { name: 'Stacked', args: { @@ -212,6 +217,7 @@ export const Stacked: Story = { ), }; +/** Demonstrates the `stacked` layout with a clickable helper icon separate from the main action. */ export const StackedWithHelperClick: Story = { name: 'Stacked with Helper Click', args: { @@ -225,6 +231,7 @@ export const StackedWithHelperClick: Story = { }, }; +/** Renders the icon-only `circle` layout across all available colors, each with a tooltip. */ export const Circle: Story = { name: 'Circle', args: { @@ -247,6 +254,7 @@ export const Circle: Story = { ), }; +/** Renders the icon-only `squared` layout across all available colors, each with a tooltip. */ export const Squared: Story = { name: 'Squared', args: { @@ -269,6 +277,7 @@ export const Squared: Story = { ), }; +/** Shows a labeled button with a leading icon via `iconStart`. */ export const WithIcon: Story = { args: { label: 'Like', @@ -277,6 +286,7 @@ export const WithIcon: Story = { }, }; +/** Renders every available `size` value side by side. */ export const AllSizes: Story = { name: 'All Sizes', args: { @@ -296,6 +306,7 @@ export const AllSizes: Story = { ), }; +/** Shows the button in its disabled state. */ export const Disabled: Story = { args: { label: 'Disabled Button', @@ -304,6 +315,7 @@ export const Disabled: Story = { }, }; +/** Renders the same button with a tooltip in each of the available `tooltipPlacement` positions. */ export const WithTooltip: Story = { name: 'With Tooltip (All Placements)', args: { @@ -324,6 +336,7 @@ export const WithTooltip: Story = { ), }; +/** Compares buttons using `iconStart`, `iconEnd`, and both together. */ export const WithStartAndEndIcons: Story = { name: 'With Start and End Icons', args: { diff --git a/src/components/Button/type.ts b/src/components/Button/type.ts index 29857ef..8b63960 100644 --- a/src/components/Button/type.ts +++ b/src/components/Button/type.ts @@ -61,44 +61,93 @@ export interface StyledButtonProps { } type BaseButtonProps = { + /** HTML `id` for the root element; auto-generated via `useId` when omitted, and used to derive the label's id and default `data-test` value. @default '' */ id?: string; + + /** Value for the `data-test` attribute, used for test selectors; falls back to an auto-generated id based on `id`/`label` when omitted. */ dataTest?: string; + + /** Text label for the button; rendered in layouts that display a label (e.g. `default`, `stacked`). */ label?: string; + + /** Text shown in a tooltip; the tooltip only renders when this is provided. */ tooltipLabel?: string; + + /** Placement of the tooltip relative to the button; only used when `tooltipLabel` is set. @default 'top' */ tooltipPlacement?: TooltipPlacementType; + + /** Click handler for the button's primary action (required); for the `stacked` layout this is distinct from `helperOnClick`. */ onClick: React.MouseEventHandler; + + /** Keydown handler for the button element, for custom keyboard interactions. */ onKeyDown?: React.KeyboardEventHandler; + + /** Id of the element that labels the button (`aria-labelledby`); when omitted, falls back to the id of the button's own rendered label element, if any. */ ariaLabelledBy?: string; + + /** Id of the element that describes the button, wired to `aria-describedby`, when provided. */ ariaDescribedBy?: string; + + /** Accessible name for the button (`aria-label`); falls back to `label` when not provided. */ ariaLabel?: string; + + /** Color theme for the button (affects background/border/text depending on `variant`). @default 'default' */ color?: ColorType; + + /** Visual variant controlling emphasis and background/border styling. @default 'primary' */ variant?: VariantType; + + /** Button size; affects padding and, for the `circle` layout, the overall diameter. @default 'md' */ size?: SizeType; + + /** Disables interaction and applies disabled styling. @default false */ disabled?: boolean; + + /** Additional content rendered inside the button, after the label (only used in the `default` layout). */ children?: React.ReactNode; } type DefaultLayoutProps = BaseButtonProps & { + /** Layout mode for this variant — always `'default'`, the standard inline label/icon button; also the overall default. @default 'default' */ layout?: typeof LAYOUTS.DEFAULT; + + /** Icon rendered before the label (left side); effective when a label is present. */ iconStart?: React.ReactNode; + + /** Icon rendered after the label (right side); effective when a label is present. */ iconEnd?: React.ReactNode; }; type StackedLayoutProps = BaseButtonProps & { + /** Layout mode for this variant — must be `'stacked'`, rendering a main icon with a label underneath plus an optional helper icon. */ layout: typeof LAYOUTS.STACKED; + + /** Main icon for the button, shown above the label. */ icon?: React.ReactNode; + + /** Auxiliary icon for a secondary action, shown next to the main icon; clickable via `helperOnClick`. @default MdSettings icon */ helperIcon?: React.ReactNode; + + /** Hides the helper icon entirely when true. @default false */ hideHelperIcon?: boolean; + + /** Click handler for the helper icon, triggering a secondary action separate from `onClick`. @default null */ helperOnClick?: React.MouseEventHandler; }; type CircleLayoutProps = BaseButtonProps & { + /** Layout mode for this variant — must be `'circle'`, rendering an icon-only circular button. */ layout: typeof LAYOUTS.CIRCLE; + + /** Icon rendered inside the circular button (no label is shown in this layout). */ icon?: React.ReactNode; }; type SquaredLayoutProps = BaseButtonProps & { + /** Layout mode for this variant — must be `'squared'`, rendering an icon-only square button. */ layout: typeof LAYOUTS.SQUARED; + + /** Icon rendered inside the square button (no label is shown in this layout). */ icon?: React.ReactNode; }; diff --git a/src/components/Checkbox/component.stories.tsx b/src/components/Checkbox/component.stories.tsx index 65c029f..d5d14ac 100644 --- a/src/components/Checkbox/component.stories.tsx +++ b/src/components/Checkbox/component.stories.tsx @@ -31,12 +31,14 @@ const meta = { export default meta; type Story = StoryObj; +/** Basic checkbox with a label, using the default layout. */ export const Default: Story = { args: { label: 'Checkbox label', }, }; +/** Renders the checkbox as a round radio-style control via the `round` prop. */ export const Round: Story = { args: { label: 'Round Checkbox', diff --git a/src/components/Checkbox/types.ts b/src/components/Checkbox/types.ts index a4c6032..bd7298c 100644 --- a/src/components/Checkbox/types.ts +++ b/src/components/Checkbox/types.ts @@ -4,8 +4,15 @@ import { LAYOUTS } from './constants'; type Layout = typeof LAYOUTS[keyof typeof LAYOUTS]; export interface CheckboxProps extends MuiCheckboxProps { + /** Text label displayed next to the checkbox. */ label?: string; + + /** Position of the label relative to the checkbox. @default 'right' */ layout?: Layout; + + /** When true, renders the checkbox as a round radio-style control. @default false */ round?: boolean; + + /** Accessible name for the checkbox. */ ariaLabel?: string; } diff --git a/src/components/Divider/component.stories.tsx b/src/components/Divider/component.stories.tsx index fb8d12f..d89e4b5 100644 --- a/src/components/Divider/component.stories.tsx +++ b/src/components/Divider/component.stories.tsx @@ -10,4 +10,5 @@ const meta = { export default meta; type Story = StoryObj; +/** Renders the divider with its default styles and no custom props. */ export const Default: Story = {}; diff --git a/src/components/Hint/component.stories.tsx b/src/components/Hint/component.stories.tsx index 5879041..65a8a14 100644 --- a/src/components/Hint/component.stories.tsx +++ b/src/components/Hint/component.stories.tsx @@ -32,6 +32,7 @@ const meta = { export default meta; type Story = StoryObj; +/** Basic hint rendering with only a label and the default info icon. */ export const Default: Story = { args: { label: 'Helpful hint', diff --git a/src/components/Hint/types.ts b/src/components/Hint/types.ts index 347e096..eddf53e 100644 --- a/src/components/Hint/types.ts +++ b/src/components/Hint/types.ts @@ -1,7 +1,16 @@ export interface HintProps extends React.HTMLAttributes { + /** Main text content of the hint. */ label: string; + + /** Optional title; if provided, shows a close button. */ title?: string; + + /** Optional icon node displayed next to the title or label. @default */ icon?: React.ReactNode; + + /** Callback fired when the close button is clicked. */ onRequestClose?: () => void; + + /** Optional additional content rendered under the label. */ children?: React.ReactNode; } diff --git a/src/components/Input/types.ts b/src/components/Input/types.ts index 8b7e8b1..2d516b0 100644 --- a/src/components/Input/types.ts +++ b/src/components/Input/types.ts @@ -25,10 +25,7 @@ export interface BBBInputProps { /** Text label rendered by the action button, instead of (or alongside) `buttonIcon`. */ buttonLabel?: string; - /** - * Accessible name for the action button. Falls back to `buttonLabel` when set. - * Required for the button to be accessible when only `buttonIcon` is provided. - */ + /** Accessible name for the action button. Falls back to `buttonLabel` when set. Required for the button to be accessible when only `buttonIcon` is provided. */ buttonAriaLabel?: string; /** Visual variant of the action button (same variants as the shared `Button`). @default 'primary' */ @@ -46,18 +43,10 @@ export interface BBBInputProps { /** Extra content (icon, button, or any other element) rendered to the right of the action button. */ afterButton?: React.ReactNode; - /** - * When `true`, pressing Enter submits the field (Shift+Enter still inserts a newline). - * When `false`, Enter always inserts a newline and only the button submits. - * @default true - */ + /** When `true`, pressing Enter submits the field (Shift+Enter still inserts a newline). When `false`, Enter always inserts a newline and only the button submits. @default true */ submitOnEnter?: boolean; - /** - * When `true`, shows `sentFeedbackContent` in place of `helperText` for `sentFeedbackDuration` - * after a successful submit. - * @default false - */ + /** When `true`, shows `sentFeedbackContent` in place of `helperText` for `sentFeedbackDuration` after a successful submit. @default false */ showSentFeedback?: boolean; /** Content rendered while the sent feedback is visible. @default 'Message sent' */ @@ -90,10 +79,13 @@ export interface BBBInputProps { /** Ref forwarded to the underlying `