From 6f068c2ef572425e4f90e3ec003cfac72a2dd894 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 19:33:48 +0000 Subject: [PATCH] build(deps-dev): bump follow-redirects from 1.15.11 to 1.16.0 --- .editorconfig | 66 +- .github/PULL_REQUEST_TEMPLATE.md | 48 +- .github/copilot-instructions.md | 162 +- .github/workflows/chromatic.yml | 66 +- .github/workflows/claude-code-review.yml | 88 +- .github/workflows/claude.yml | 100 +- .github/workflows/code-analysis.yml | 96 +- .github/workflows/code-reviewer.yml | 90 +- .github/workflows/publish-public-build.yml | 112 +- .github/workflows/publish.yml | 56 +- .github/workflows/storybook-deployment.yml | 78 +- .github/workflows/storybook-tests.yml | 64 +- .github/workflows/tag-release.yml | 94 +- .gitignore | 37 +- .storybook/main.ts | 152 +- .storybook/preview.tsx | 60 +- .storybook/test-runner.js | 60 +- .storybook/vitest.setup.ts | 6 +- README.md | 678 +- changelog.txt | 440 +- component-data.json | 6136 +-- package-lock.json | 45983 ++++++++-------- package.json | 294 +- .../accordion/accordion.stories.tsx | 290 +- src/components/accordion/accordion.tsx | 648 +- src/components/alert/alert.stories.tsx | 282 +- src/components/alert/alert.tsx | 346 +- .../area-chart/area-chart.stories.tsx | 866 +- src/components/area-chart/area-chart.tsx | 544 +- .../area-chart/chart-tooltip-content.tsx | 282 +- src/components/avatar/avatar.stories.tsx | 172 +- src/components/badge/badge.stories.tsx | 194 +- src/components/badge/badge.tsx | 370 +- .../bar-chart/bar-chart.stories.tsx | 1320 +- src/components/bar-chart/bar-chart.tsx | 570 +- .../breadcrumb/breadcrumb.stories.tsx | 136 +- src/components/breadcrumb/breadcrumb.tsx | 316 +- src/components/button-group/button-group.tsx | 428 +- src/components/button/button.stories.tsx | 258 +- src/components/button/button.tsx | 326 +- src/components/checkbox/checkbox.stories.tsx | 74 +- src/components/checkbox/checkbox.tsx | 414 +- .../container/container.stories.tsx | 272 +- .../datepicker/datepicker-component.tsx | 1534 +- .../datepicker/datepicker.stories.tsx | 358 +- src/components/dialog/dialog.stories.tsx | 388 +- src/components/dialog/dialog.tsx | 1186 +- src/components/drawer/drawer-backdrop.tsx | 120 +- src/components/drawer/drawer-close-button.tsx | 168 +- src/components/drawer/drawer-description.tsx | 102 +- src/components/drawer/drawer-panel.tsx | 252 +- src/components/drawer/drawer-portal.tsx | 34 +- src/components/drawer/drawer-title.tsx | 100 +- src/components/drawer/drawer.stories.tsx | 494 +- src/components/drawer/drawer.tsx | 444 +- .../dropdown-menu/dropdown-menu.stories.tsx | 282 +- .../dropdown-menu/dropdown-menu.tsx | 594 +- .../dropdown-menu/dropdown-types.ts | 166 +- src/components/dropzone/dropzone.stories.tsx | 124 +- src/components/dropzone/dropzone.tsx | 634 +- .../character-limit.tsx | 142 +- .../character-limit-plugin/index.ts | 2 +- .../editor-input/editor-input-style.ts | 68 +- .../editor-input/editor-input.stories.tsx | 148 +- src/components/editor-input/editor-input.tsx | 408 +- .../mention-plugin/mention-combobox.tsx | 166 +- .../mention-plugin/mention-component.tsx | 324 +- .../mention-plugin/mention-hooks.ts | 302 +- .../mention-plugin/mention-plugin.tsx | 802 +- .../override-editor-style-plugin/index.ts | 2 +- .../override-editor-style.tsx | 60 +- .../file-preview/file-preview.stories.tsx | 226 +- src/components/file-preview/file-preview.tsx | 270 +- .../hamburger-menu/hamburger-menu.stories.tsx | 254 +- .../hamburger-menu/hamburger-menu.tsx | 886 +- src/components/index.ts | 88 +- src/components/input/input.stories.tsx | 182 +- src/components/input/input.tsx | 718 +- src/components/label/label.stories.tsx | 128 +- .../line-chart/chart-tooltip-content.tsx | 268 +- .../line-chart/line-chart.stories.tsx | 482 +- src/components/line-chart/line-chart.tsx | 556 +- src/components/loader/loader.stories.tsx | 142 +- src/components/loader/loader.tsx | 96 +- src/components/menu-item/menu-item.tsx | 542 +- src/components/menu-item/menu.stories.tsx | 210 +- .../pagination/pagination.stories.tsx | 194 +- src/components/pagination/pagination.tsx | 472 +- .../pie-chart/pie-chart.stories.tsx | 264 +- src/components/pie-chart/pie-chart.tsx | 318 +- .../progress-bar/progress-bar.stories.tsx | 96 +- .../progress-steps/progress-steps.stories.tsx | 284 +- .../radio-button/radio-button.stories.tsx | 752 +- src/components/radio-button/radio-button.tsx | 1288 +- src/components/search/search.stories.tsx | 254 +- src/components/search/search.tsx | 1606 +- src/components/select/component-style.ts | 130 +- src/components/select/readme.md | 370 +- src/components/select/select-atom.stories.tsx | 870 +- src/components/select/select-types.ts | 352 +- src/components/select/select.tsx | 2212 +- src/components/select/utils.ts | 88 +- src/components/sidebar/sidebar.stories.tsx | 298 +- src/components/sidebar/sidebar.tsx | 380 +- src/components/skeleton/skeleton.stories.tsx | 64 +- src/components/skeleton/skeleton.tsx | 80 +- src/components/switch/switch.stories.tsx | 144 +- src/components/switch/switch.tsx | 532 +- src/components/table/table.stories.tsx | 572 +- src/components/tabs/tabs.stories.tsx | 316 +- src/components/tabs/tabs.tsx | 850 +- src/components/text/index.ts | 2 +- src/components/text/styles.ts | 126 +- src/components/text/text.stories.tsx | 206 +- src/components/text/text.tsx | 264 +- src/components/textarea/textarea.stories.tsx | 124 +- src/components/textarea/textarea.tsx | 222 +- src/components/title/title.stories.tsx | 130 +- src/components/toaster/component-style.ts | 62 +- src/components/toaster/toaster.stories.tsx | 254 +- src/components/toaster/toaster.tsx | 592 +- src/components/toaster/utils.tsx | 406 +- src/components/tooltip/tooltip.stories.tsx | 212 +- src/components/topbar/topbar.stories.tsx | 446 +- src/components/topbar/topbar.tsx | 216 +- .../admin-settings-Spectra.stories.jsx | 1222 +- .../admin-settings-SureCart.stories.jsx | 1432 +- .../admin-settings-SureRank.stories.jsx | 1102 +- .../assets/surerank-logo.svg | 36 +- .../assets/upgrade-illustration.svg | 122 +- .../sureforms-dashboard.stories.jsx | 1472 +- .../uae-dashboard/assets/surerank-logo.svg | 36 +- .../uae-dashboard/assets/uae-logo-bg.svg | 6 +- .../assets/uae-logo-header-bg.svg | 6 +- .../uae-dashboard/assets/uae-logo-header.svg | 28 +- .../uae-dashboard/assets/uae-logo.svg | 28 +- .../uae-dashboard/assets/upgrade-banner.svg | 450 +- .../uae-dashboard/assets/video-background.svg | 130 +- .../uae-dashboard/uae-dashboard.stories.jsx | 1076 +- .../uae-widgets/assets/upgrade-banner.svg | 112 +- .../uae-widgets/uae-widgets.stories.jsx | 1084 +- src/theme/default-config.js | 486 +- src/utilities/functions.js | 288 +- tailwind.config.js | 13 +- version.json | 6 +- vite.config.ts | 172 +- vitest.config.ts | 64 +- 147 files changed, 51358 insertions(+), 51587 deletions(-) diff --git a/.editorconfig b/.editorconfig index cb7133ce..15961146 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,33 +1,33 @@ -# This file is for unifying the coding style for different editors and IDEs -# editorconfig.org - -# WordPress Coding Standards -# https://make.wordpress.org/core/handbook/coding-standards/ - -root = true - -[*] -charset = utf-8 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true -indent_style = tab -indent_size = 4 - -[{.jshintrc,*.json,*.yml}] -indent_style = space -indent_size = 2 - -[{.editorconfig,*.md,*.txt,*.xml,*.yml}] -indent_style = space -indent_size = 2 - -[{.gitattributes,*.gitignore,*.gitmodules}] -indent_style = space -indent_size = 2 - -[package.json] -indent_style = tab - -[*.md] -trim_trailing_whitespace = false +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +# WordPress Coding Standards +# https://make.wordpress.org/core/handbook/coding-standards/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = tab +indent_size = 4 + +[{.jshintrc,*.json,*.yml}] +indent_style = space +indent_size = 2 + +[{.editorconfig,*.md,*.txt,*.xml,*.yml}] +indent_style = space +indent_size = 2 + +[{.gitattributes,*.gitignore,*.gitmodules}] +indent_style = space +indent_size = 2 + +[package.json] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 91903a40..b0c9f34c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,24 +1,24 @@ -### Description - - -### Screenshots | Video with voice-over - - -### Link to Figma (If applicable) - - -### How has this been tested? - - -### Checklist: - -- [ ] My code is tested -- [ ] Screenshot added in PR -- [ ] Figma design linked -- [ ] My code follows accessibility standards -- [ ] My code has proper inline documentation -- [ ] I have resolved all lint issues -- [ ] My templates are responsive -- [ ] I've added conditional class names use cn() -- [ ] I've followed theme color usage -- [ ] I have used clear and relevant file paths +### Description + + +### Screenshots | Video with voice-over + + +### Link to Figma (If applicable) + + +### How has this been tested? + + +### Checklist: + +- [ ] My code is tested +- [ ] Screenshot added in PR +- [ ] Figma design linked +- [ ] My code follows accessibility standards +- [ ] My code has proper inline documentation +- [ ] I have resolved all lint issues +- [ ] My templates are responsive +- [ ] I've added conditional class names use cn() +- [ ] I've followed theme color usage +- [ ] I have used clear and relevant file paths diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 16599f7e..7547c93d 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,82 +1,82 @@ -# Force-UI Storybook Template Generator - -## System Prompt - -You are a React/TypeScript developer specializing in creating Storybook templates for the force-ui component library. - -### Your Task -Generate a complete Storybook template using force-ui components based on user requirements following process steps and output requirements outlined below. You should check reference files and documentation to ensure the generated template meets the design prompt, correct component usages, and follows best practices. - -### Required Inputs -- `base_folder`: The root directory for templates (default: `src/templates/`) -- `template_title`: Human-readable name for the template (e.g., "Cross Promotion") -- `design_prompt`: Detailed description of the desired UI/UX design - -### Process Steps - -1. **Create Folder Structure** - - Convert `template_title` to kebab-case for folder naming - - Example: "Cross Promotion" → `cross-promotion` - - Remove special characters and spaces - - Create subfolder: `{base_folder}/{kebab-case}/` - -2. **Component Selection** - - Reference `component-data.json` for available force-ui components and their props - - Check available components before starting the template - - Choose appropriate components based on the design requirements - - Prioritize semantic HTML structure and accessibility - -3. **File Generation** - - Create `{template_name}.stories.tsx` in the new subfolder - - Follow existing template patterns from `src/templates/**/*.stories.tsx` - - Include proper TypeScript types and Storybook metadata - - Must use Force-UI components and TailwindCSS for styling - -4. **Responsive Design** - - Ensure the template is responsive using and using TailwindCSS and Force-UI components - - Use responsive utility classes from TailwindCSS - -5. **Accessibility** - - Add ARIA attributes and roles where applicable - - -### Output Requirements - -**File Structure:** -``` -src/templates/{kebab-case}/ -└── {kebab-case}.stories.tsx -``` - -**Code Requirements (must follow):** -- Use React functional components with TypeScript -- Import only necessary force-ui components -- Include proper Storybook story configuration -- Add responsive design considerations -- Use Text component for any text content, ensuring proper typography and spacing -- Use Title component for the main title of the template -- Use Label component for any form labels -- Include accessibility attributes where applicable -- Ensure components are imported and used correctly according to the component documentation -- Before using a component, check the component documentation in `src/components/**/*.stories.{tsx,ts}` for examples or check reference files. -- Don't use className until it's necessary -- Use colors from the Force-UI color palette -- For links use Text component with `as` prop set to `a` and `href` prop for URLs. If the link design is similar to a button, use Button component instead with `tag` prop set to `a` and `href` prop for URLs -- Review the template once generated to ensure it meets the design prompt and follows best practices, and fix lint errors and warnings if any. -- Repeat the review process until the template is complete and meets all requirements and no warnings or errors are present. - -### Example Usage -``` -base_folder: src/templates/ -template_title : Cross Promotion -design_prompt: design a screen where we will promote surerank in suremails. Please add a descption about the benifits of surerank and add a plugin install and activate link as well. -``` - -**Expected Output:** File at `src/templates/cross_promotion/cross_promotion.stories.tsx` - -### Reference Files / Documentation / Examples -- Component library component props details: `component-data.json` -- Example templates: `src/templates/**/*.stories.tsx` -- Component documentation: `src/components/**/*.stories.{tsx,ts}` -- Components: `src/components/**/*.tsx` +# Force-UI Storybook Template Generator + +## System Prompt + +You are a React/TypeScript developer specializing in creating Storybook templates for the force-ui component library. + +### Your Task +Generate a complete Storybook template using force-ui components based on user requirements following process steps and output requirements outlined below. You should check reference files and documentation to ensure the generated template meets the design prompt, correct component usages, and follows best practices. + +### Required Inputs +- `base_folder`: The root directory for templates (default: `src/templates/`) +- `template_title`: Human-readable name for the template (e.g., "Cross Promotion") +- `design_prompt`: Detailed description of the desired UI/UX design + +### Process Steps + +1. **Create Folder Structure** + - Convert `template_title` to kebab-case for folder naming + - Example: "Cross Promotion" → `cross-promotion` + - Remove special characters and spaces + - Create subfolder: `{base_folder}/{kebab-case}/` + +2. **Component Selection** + - Reference `component-data.json` for available force-ui components and their props + - Check available components before starting the template + - Choose appropriate components based on the design requirements + - Prioritize semantic HTML structure and accessibility + +3. **File Generation** + - Create `{template_name}.stories.tsx` in the new subfolder + - Follow existing template patterns from `src/templates/**/*.stories.tsx` + - Include proper TypeScript types and Storybook metadata + - Must use Force-UI components and TailwindCSS for styling + +4. **Responsive Design** + - Ensure the template is responsive using and using TailwindCSS and Force-UI components + - Use responsive utility classes from TailwindCSS + +5. **Accessibility** + - Add ARIA attributes and roles where applicable + + +### Output Requirements + +**File Structure:** +``` +src/templates/{kebab-case}/ +└── {kebab-case}.stories.tsx +``` + +**Code Requirements (must follow):** +- Use React functional components with TypeScript +- Import only necessary force-ui components +- Include proper Storybook story configuration +- Add responsive design considerations +- Use Text component for any text content, ensuring proper typography and spacing +- Use Title component for the main title of the template +- Use Label component for any form labels +- Include accessibility attributes where applicable +- Ensure components are imported and used correctly according to the component documentation +- Before using a component, check the component documentation in `src/components/**/*.stories.{tsx,ts}` for examples or check reference files. +- Don't use className until it's necessary +- Use colors from the Force-UI color palette +- For links use Text component with `as` prop set to `a` and `href` prop for URLs. If the link design is similar to a button, use Button component instead with `tag` prop set to `a` and `href` prop for URLs +- Review the template once generated to ensure it meets the design prompt and follows best practices, and fix lint errors and warnings if any. +- Repeat the review process until the template is complete and meets all requirements and no warnings or errors are present. + +### Example Usage +``` +base_folder: src/templates/ +template_title : Cross Promotion +design_prompt: design a screen where we will promote surerank in suremails. Please add a descption about the benifits of surerank and add a plugin install and activate link as well. +``` + +**Expected Output:** File at `src/templates/cross_promotion/cross_promotion.stories.tsx` + +### Reference Files / Documentation / Examples +- Component library component props details: `component-data.json` +- Example templates: `src/templates/**/*.stories.tsx` +- Component documentation: `src/components/**/*.stories.{tsx,ts}` +- Components: `src/components/**/*.tsx` - Theme and color palette: `src/theme/default-config.js` \ No newline at end of file diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index 278d745e..f1bbb324 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -1,33 +1,33 @@ -name: 'Chromatic' - -on: - push: - branches: - - staging - -permissions: - contents: read - -jobs: - chromatic: - name: Run Chromatic - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: actions/setup-node@v4 - with: - node-version: 20 - - name: Install dependencies - # ⚠️ See your package manager's documentation for the correct command to install dependencies in a CI environment. - run: npm ci && npm run build - - - name: Run Chromatic - uses: chromaui/action@5ec258af08deb3e8c36653bd618cb7fe52090031 # v15.2.0 - with: - # ⚠️ Make sure to configure a `CHROMATIC_PROJECT_TOKEN` repository secret - projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} - zip: true - onlyChanged: true # 👈 Required option to enable TurboSnap +name: 'Chromatic' + +on: + push: + branches: + - staging + +permissions: + contents: read + +jobs: + chromatic: + name: Run Chromatic + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Install dependencies + # ⚠️ See your package manager's documentation for the correct command to install dependencies in a CI environment. + run: npm ci && npm run build + + - name: Run Chromatic + uses: chromaui/action@5ec258af08deb3e8c36653bd618cb7fe52090031 # v15.2.0 + with: + # ⚠️ Make sure to configure a `CHROMATIC_PROJECT_TOKEN` repository secret + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + zip: true + onlyChanged: true # 👈 Required option to enable TurboSnap diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index b5e8cfd4..800eeadd 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -1,44 +1,44 @@ -name: Claude Code Review - -on: - pull_request: - types: [opened, synchronize, ready_for_review, reopened] - # Optional: Only run on specific file changes - # paths: - # - "src/**/*.ts" - # - "src/**/*.tsx" - # - "src/**/*.js" - # - "src/**/*.jsx" - -jobs: - claude-review: - # Optional: Filter by PR author - # if: | - # github.event.pull_request.user.login == 'external-contributor' || - # github.event.pull_request.user.login == 'new-developer' || - # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' - - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - issues: read - id-token: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Run Claude Code Review - id: claude-review - uses: anthropics/claude-code-action@v1 - with: - claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - plugin_marketplaces: 'https://github.com/anthropics/claude-code.git' - plugins: 'code-review@claude-code-plugins' - prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}' - # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md - # or https://code.claude.com/docs/en/cli-reference for available options - +name: Claude Code Review + +on: + pull_request: + types: [opened, synchronize, ready_for_review, reopened] + # Optional: Only run on specific file changes + # paths: + # - "src/**/*.ts" + # - "src/**/*.tsx" + # - "src/**/*.js" + # - "src/**/*.jsx" + +jobs: + claude-review: + # Optional: Filter by PR author + # if: | + # github.event.pull_request.user.login == 'external-contributor' || + # github.event.pull_request.user.login == 'new-developer' || + # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' + + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + issues: read + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Claude Code Review + id: claude-review + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + plugin_marketplaces: 'https://github.com/anthropics/claude-code.git' + plugins: 'code-review@claude-code-plugins' + prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}' + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://code.claude.com/docs/en/cli-reference for available options + diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index d300267f..8233826b 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -1,50 +1,50 @@ -name: Claude Code - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - issues: - types: [opened, assigned] - pull_request_review: - types: [submitted] - -jobs: - claude: - if: | - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || - (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - issues: read - id-token: write - actions: read # Required for Claude to read CI results on PRs - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Run Claude Code - id: claude - uses: anthropics/claude-code-action@v1 - with: - claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - - # This is an optional setting that allows Claude to read CI results on PRs - additional_permissions: | - actions: read - - # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. - # prompt: 'Update the pull request description to include a summary of changes.' - - # Optional: Add claude_args to customize behavior and configuration - # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md - # or https://code.claude.com/docs/en/cli-reference for available options - # claude_args: '--allowed-tools Bash(gh pr:*)' - +name: Claude Code + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, assigned] + pull_request_review: + types: [submitted] + +jobs: + claude: + if: | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || + (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + issues: read + id-token: write + actions: read # Required for Claude to read CI results on PRs + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + + # This is an optional setting that allows Claude to read CI results on PRs + additional_permissions: | + actions: read + + # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. + # prompt: 'Update the pull request description to include a summary of changes.' + + # Optional: Add claude_args to customize behavior and configuration + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://code.claude.com/docs/en/cli-reference for available options + # claude_args: '--allowed-tools Bash(gh pr:*)' + diff --git a/.github/workflows/code-analysis.yml b/.github/workflows/code-analysis.yml index 1a262a41..b075ac03 100644 --- a/.github/workflows/code-analysis.yml +++ b/.github/workflows/code-analysis.yml @@ -1,48 +1,48 @@ -name: Code Analysis - -on: pull_request - -permissions: - contents: read - -# Cancels all previous workflow runs for pull requests that have not completed. -concurrency: - # The concurrency group contains the workflow name and the branch name for pull requests - # or the commit hash for any other events. - group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} - cancel-in-progress: true - -jobs: - analysis: - runs-on: ubuntu-latest - - strategy: - matrix: - node: ['20'] - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Use desired version of NodeJS - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node }} - - - name: Cache NPM packages - uses: actions/cache@v4 - with: - # npm cache files are stored in `~/.npm` on Linux/macOS - path: ~/.npm - key: ${{ runner.os }}-node-${{ matrix.node }}-npm-cache-${{ hashFiles('**/package-lock.json') }} - - - name: Install dependencies - run: npm ci - - - name: Lint JS - if: always() - run: npm run lint:js - - - name: Lint CSS - if: always() - run: npm run lint:css +name: Code Analysis + +on: pull_request + +permissions: + contents: read + +# Cancels all previous workflow runs for pull requests that have not completed. +concurrency: + # The concurrency group contains the workflow name and the branch name for pull requests + # or the commit hash for any other events. + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +jobs: + analysis: + runs-on: ubuntu-latest + + strategy: + matrix: + node: ['20'] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Use desired version of NodeJS + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + + - name: Cache NPM packages + uses: actions/cache@v4 + with: + # npm cache files are stored in `~/.npm` on Linux/macOS + path: ~/.npm + key: ${{ runner.os }}-node-${{ matrix.node }}-npm-cache-${{ hashFiles('**/package-lock.json') }} + + - name: Install dependencies + run: npm ci + + - name: Lint JS + if: always() + run: npm run lint:js + + - name: Lint CSS + if: always() + run: npm run lint:css diff --git a/.github/workflows/code-reviewer.yml b/.github/workflows/code-reviewer.yml index e972c963..34eb40e8 100644 --- a/.github/workflows/code-reviewer.yml +++ b/.github/workflows/code-reviewer.yml @@ -1,45 +1,45 @@ -name: BSF Code Reviewer - -on: - pull_request: - types: [opened, synchronize, edited] - -permissions: - contents: read - pull-requests: write - -jobs: - CHECK_SHORTCODE: - if: contains(github.event.pull_request.body, '[BSF-PR-SUMMARY]') - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: WRITE PR SUMMARY - uses: brainstormforce/pull-request-reviewer@d38b1e07527dd20ad58fd2407648e9689edc54de # master - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - ACTION_CONTEXT: 'CHECK_SHORTCODE' - EXCLUDE_EXTENSIONS: 'md, yml, lock' - INCLUDE_EXTENSIONS: 'php, js, jsx, ts, tsx, css, scss, html, json' - EXCLUDE_PATHS: 'node_modules/' - - CODE_REVIEW: - needs: CHECK_SHORTCODE - if: always() - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: AI CODE REVIEW - uses: brainstormforce/pull-request-reviewer@d38b1e07527dd20ad58fd2407648e9689edc54de # master - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - ACTION_CONTEXT: 'CODE_REVIEW' - EXCLUDE_EXTENSIONS: 'md, yml, lock' - INCLUDE_EXTENSIONS: 'php, js, jsx, ts, tsx, css, scss, html, json' - EXCLUDE_PATHS: 'node_modules/' +name: BSF Code Reviewer + +on: + pull_request: + types: [opened, synchronize, edited] + +permissions: + contents: read + pull-requests: write + +jobs: + CHECK_SHORTCODE: + if: contains(github.event.pull_request.body, '[BSF-PR-SUMMARY]') + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: WRITE PR SUMMARY + uses: brainstormforce/pull-request-reviewer@d38b1e07527dd20ad58fd2407648e9689edc54de # master + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + ACTION_CONTEXT: 'CHECK_SHORTCODE' + EXCLUDE_EXTENSIONS: 'md, yml, lock' + INCLUDE_EXTENSIONS: 'php, js, jsx, ts, tsx, css, scss, html, json' + EXCLUDE_PATHS: 'node_modules/' + + CODE_REVIEW: + needs: CHECK_SHORTCODE + if: always() + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: AI CODE REVIEW + uses: brainstormforce/pull-request-reviewer@d38b1e07527dd20ad58fd2407648e9689edc54de # master + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + ACTION_CONTEXT: 'CODE_REVIEW' + EXCLUDE_EXTENSIONS: 'md, yml, lock' + INCLUDE_EXTENSIONS: 'php, js, jsx, ts, tsx, css, scss, html, json' + EXCLUDE_PATHS: 'node_modules/' diff --git a/.github/workflows/publish-public-build.yml b/.github/workflows/publish-public-build.yml index daef2f54..0c74e290 100644 --- a/.github/workflows/publish-public-build.yml +++ b/.github/workflows/publish-public-build.yml @@ -1,56 +1,56 @@ -name: Publish Public Build - -on: - push: - branches: - - master - -jobs: - publish-build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version: '18' - - # Build the library - - name: Install and Build - run: | - npm ci - npm run build - - # Push to public mirror repo - - name: Push Build to Public Mirror - env: - FORCE_UI_TOKEN: ${{ secrets.FORCE_UI_TOKEN }} - run: | - # Clone public mirror repo - git clone https://x-access-token:${{ secrets.FORCE_UI_TOKEN }}@github.com/brainstormforce/bsf-admin-ui.git build-repo - - cd build-repo - - # Clear old dist - rm -rf dist - - # Copy new build - cp -r ../dist ./ - - # Update version in package.json - VERSION=$(node -p "require('../package.json').version") - node -e " - const pkg = require('./package.json'); - pkg.version = process.argv[1]; - require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2)); - " "$VERSION" - - # Commit and tag - git config user.name github-actions - git config user.email github-actions@github.com - git add dist package.json - git commit -m "Release v$VERSION - build artifacts only" - git tag v$VERSION - git push origin master - git push origin v$VERSION +name: Publish Public Build + +on: + push: + branches: + - master + +jobs: + publish-build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '18' + + # Build the library + - name: Install and Build + run: | + npm ci + npm run build + + # Push to public mirror repo + - name: Push Build to Public Mirror + env: + FORCE_UI_TOKEN: ${{ secrets.FORCE_UI_TOKEN }} + run: | + # Clone public mirror repo + git clone https://x-access-token:${{ secrets.FORCE_UI_TOKEN }}@github.com/brainstormforce/bsf-admin-ui.git build-repo + + cd build-repo + + # Clear old dist + rm -rf dist + + # Copy new build + cp -r ../dist ./ + + # Update version in package.json + VERSION=$(node -p "require('../package.json').version") + node -e " + const pkg = require('./package.json'); + pkg.version = process.argv[1]; + require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2)); + " "$VERSION" + + # Commit and tag + git config user.name github-actions + git config user.email github-actions@github.com + git add dist package.json + git commit -m "Release v$VERSION - build artifacts only" + git tag v$VERSION + git push origin master + git push origin v$VERSION diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e287f670..3572b159 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,28 +1,28 @@ -name: Publish Package - -on: - push: - branches: - - master - -jobs: - publish: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version: '18' - registry-url: 'https://npm.pkg.github.com' - scope: '@brainstormforce' - - - run: npm ci - - run: npm run build - - - run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} +name: Publish Package + +on: + push: + branches: + - master + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '18' + registry-url: 'https://npm.pkg.github.com' + scope: '@brainstormforce' + + - run: npm ci + - run: npm run build + + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/storybook-deployment.yml b/.github/workflows/storybook-deployment.yml index 92ee496c..360a8b42 100644 --- a/.github/workflows/storybook-deployment.yml +++ b/.github/workflows/storybook-deployment.yml @@ -1,39 +1,39 @@ -# Workflow name -name: Build and Publish Storybook to GitHub Pages - -on: - # Event for the workflow to run on - push: - branches: - - master - -permissions: - contents: read - pages: write - id-token: write - -# List of jobs -jobs: - deploy: - environment: - name: github-pages - url: ${{ steps.build-publish.outputs.page_url }} - runs-on: ubuntu-latest - # Job steps - steps: - # Manual Checkout - - uses: actions/checkout@v4 - - # Set up Node - - uses: actions/setup-node@v4 - with: - node-version: '20.x' - - #👇 Add Storybook build and deploy to GitHub Pages as a step in the workflow - - id: build-publish - uses: bitovi/github-actions-storybook-to-github-pages@v1.0.3 - with: - install_command: npm ci - build_command: npm run build && npm run build-storybook # default: npm run build-storybook - path: storybook-static # default: dist/storybook - checkout: false # default: true +# Workflow name +name: Build and Publish Storybook to GitHub Pages + +on: + # Event for the workflow to run on + push: + branches: + - master + +permissions: + contents: read + pages: write + id-token: write + +# List of jobs +jobs: + deploy: + environment: + name: github-pages + url: ${{ steps.build-publish.outputs.page_url }} + runs-on: ubuntu-latest + # Job steps + steps: + # Manual Checkout + - uses: actions/checkout@v4 + + # Set up Node + - uses: actions/setup-node@v4 + with: + node-version: '20.x' + + #👇 Add Storybook build and deploy to GitHub Pages as a step in the workflow + - id: build-publish + uses: bitovi/github-actions-storybook-to-github-pages@v1.0.3 + with: + install_command: npm ci + build_command: npm run build && npm run build-storybook # default: npm run build-storybook + path: storybook-static # default: dist/storybook + checkout: false # default: true diff --git a/.github/workflows/storybook-tests.yml b/.github/workflows/storybook-tests.yml index 90bd012b..08aa5807 100644 --- a/.github/workflows/storybook-tests.yml +++ b/.github/workflows/storybook-tests.yml @@ -1,32 +1,32 @@ -name: 'Storybook Tests' - -on: push - -permissions: - contents: read - -jobs: - test: - timeout-minutes: 60 - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: actions/setup-node@v4 - with: - node-version: 20 - - name: Install dependencies - run: npm ci - - name: Install Playwright - run: npx playwright install --with-deps - - name: Build force-ui - run: npm run build --silent - - name: Build Storybook - run: npm run build-storybook --silent - - name: Serve Storybook and run tests - run: | - npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \ - "npx http-server storybook-static --port 6006 --silent" \ - "npx wait-on tcp:127.0.0.1:6006 && yarn test-storybook" +name: 'Storybook Tests' + +on: push + +permissions: + contents: read + +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Install dependencies + run: npm ci + - name: Install Playwright + run: npx playwright install --with-deps + - name: Build force-ui + run: npm run build --silent + - name: Build Storybook + run: npm run build-storybook --silent + - name: Serve Storybook and run tests + run: | + npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \ + "npx http-server storybook-static --port 6006 --silent" \ + "npx wait-on tcp:127.0.0.1:6006 && yarn test-storybook" diff --git a/.github/workflows/tag-release.yml b/.github/workflows/tag-release.yml index 907c2997..537d8c0c 100644 --- a/.github/workflows/tag-release.yml +++ b/.github/workflows/tag-release.yml @@ -1,47 +1,47 @@ -name: Create a tag - -on: - push: - branches: - - master - -permissions: - contents: write - -# Cancels all previous workflow runs for pull requests that have not completed. -concurrency: - # The concurrency group contains the workflow name and the branch name for pull requests - # or the commit hash for any other events. - group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} - cancel-in-progress: true - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Use desired version of NodeJS - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: 'npm' - - - name: Build the release - run: npm ci && npm run release - - - name: Maybe create a tag - run: bash bin/release.sh - - - name: Release - if: ${{ env.TAG_CREATED == 'true' }} - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ env.BRAINSTORM_FORCE_RELEASE }} - body: '' - name: v${{ env.BRAINSTORM_FORCE_RELEASE }} - draft: false - files: force-ui.zip - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +name: Create a tag + +on: + push: + branches: + - master + +permissions: + contents: write + +# Cancels all previous workflow runs for pull requests that have not completed. +concurrency: + # The concurrency group contains the workflow name and the branch name for pull requests + # or the commit hash for any other events. + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Use desired version of NodeJS + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + + - name: Build the release + run: npm ci && npm run release + + - name: Maybe create a tag + run: bash bin/release.sh + + - name: Release + if: ${{ env.TAG_CREATED == 'true' }} + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ env.BRAINSTORM_FORCE_RELEASE }} + body: '' + name: v${{ env.BRAINSTORM_FORCE_RELEASE }} + draft: false + files: force-ui.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index d0bf53e9..dac4932e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,19 @@ -node_modules -dist/* -.DS_Store -.cursor -.vscode -.npmrc -*storybook.log -storybook-static/* -*.zip -/test-results/ -/playwright-report/ -/blob-report/ -/playwright/.cache/ -tsconfig.app.tsbuildinfo -tsconfig.node.tsbuildinfo -*.app.tsbuildinfo -*.node.tsbuildinfo -tsconfig.app.tsbuildinfo +node_modules +dist/* +.DS_Store +.cursor +.vscode +.npmrc +*storybook.log +storybook-static/* +*.zip +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +tsconfig.app.tsbuildinfo +tsconfig.node.tsbuildinfo +*.app.tsbuildinfo +*.node.tsbuildinfo +tsconfig.app.tsbuildinfo +config.bat diff --git a/.storybook/main.ts b/.storybook/main.ts index d6e28d58..8f12c045 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,76 +1,76 @@ -// This file has been automatically migrated to valid ESM format by Storybook. -import { fileURLToPath } from "node:url"; -import type { StorybookConfig } from '@storybook/react-vite'; -import path, { dirname } from 'path'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -/** @type { import('@storybook/react-webpack5').StorybookConfig } */ -const config: StorybookConfig = { - stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], - addons: [ - '@storybook/addon-onboarding', - '@storybook/addon-links', - '@chromatic-com/storybook', - '@storybook/addon-a11y', - '@storybook/addon-docs', - '@storybook/addon-mcp', - '@storybook/addon-vitest' - ], - framework: { - name: '@storybook/react-vite', - options: { - builder: { - viteConfigPath: path.resolve(__dirname, '..', 'vite.config.ts'), - }, - }, - }, - core: { - builder: '@storybook/builder-vite', - }, - viteFinal: async (config) => { - // Merge custom configuration into the default config - const { mergeConfig } = await import('vite'); - - // Remove the dts plugin from the default config. - config.plugins = [ - ...(config.plugins ?? []).filter((plugin) => { - return ( - (plugin as typeof plugin & Record).name !== - 'vite:dts' - ); - }), - ]; - - return mergeConfig(config, { - optimizeDeps: { - ...config?.optimizeDeps, - }, - resolve: { - ...config.resolve, - alias: { - ...config.resolve?.alias, - // 👇 Internal modules - '@/icons': path.resolve( - __dirname, - '..', - 'src/ui/icons.jsx' - ), - '@/utilities': path.resolve( - __dirname, - '..', - 'src/utilities' - ), - '@/components': path.resolve( - __dirname, - '..', - 'src/components' - ), - '@': path.resolve(__dirname, '..', 'src'), - }, - }, - }); - }, -}; -export default config; +// This file has been automatically migrated to valid ESM format by Storybook. +import { fileURLToPath } from "node:url"; +import type { StorybookConfig } from '@storybook/react-vite'; +import path, { dirname } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +/** @type { import('@storybook/react-webpack5').StorybookConfig } */ +const config: StorybookConfig = { + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + addons: [ + '@storybook/addon-onboarding', + '@storybook/addon-links', + '@chromatic-com/storybook', + '@storybook/addon-a11y', + '@storybook/addon-docs', + '@storybook/addon-mcp', + '@storybook/addon-vitest' + ], + framework: { + name: '@storybook/react-vite', + options: { + builder: { + viteConfigPath: path.resolve(__dirname, '..', 'vite.config.ts'), + }, + }, + }, + core: { + builder: '@storybook/builder-vite', + }, + viteFinal: async (config) => { + // Merge custom configuration into the default config + const { mergeConfig } = await import('vite'); + + // Remove the dts plugin from the default config. + config.plugins = [ + ...(config.plugins ?? []).filter((plugin) => { + return ( + (plugin as typeof plugin & Record).name !== + 'vite:dts' + ); + }), + ]; + + return mergeConfig(config, { + optimizeDeps: { + ...config?.optimizeDeps, + }, + resolve: { + ...config.resolve, + alias: { + ...config.resolve?.alias, + // 👇 Internal modules + '@/icons': path.resolve( + __dirname, + '..', + 'src/ui/icons.jsx' + ), + '@/utilities': path.resolve( + __dirname, + '..', + 'src/utilities' + ), + '@/components': path.resolve( + __dirname, + '..', + 'src/components' + ), + '@': path.resolve(__dirname, '..', 'src'), + }, + }, + }); + }, +}; +export default config; diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 9f47d941..bd01d679 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,30 +1,30 @@ -import React from 'react'; -import type { Preview } from '@storybook/react-vite'; -import '../src/tailwind.css'; -/** @type { import('@storybook/react-vite').Preview } */ - -const preview: Preview = { - parameters: { - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/i, - }, - }, - // Disable color contrast checks for the entire storybook. - a11y: { - config: { - rules: [{ id: 'color-contrast', enabled: false }], - }, - }, - }, - decorators: [ - (Story) => ( -
- -
- ), - ], -}; - -export default preview; +import React from 'react'; +import type { Preview } from '@storybook/react-vite'; +import '../src/tailwind.css'; +/** @type { import('@storybook/react-vite').Preview } */ + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + // Disable color contrast checks for the entire storybook. + a11y: { + config: { + rules: [{ id: 'color-contrast', enabled: false }], + }, + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}; + +export default preview; diff --git a/.storybook/test-runner.js b/.storybook/test-runner.js index 0645107b..ee7690d4 100644 --- a/.storybook/test-runner.js +++ b/.storybook/test-runner.js @@ -1,30 +1,30 @@ -import { getStoryContext } from '@storybook/test-runner'; -import { injectAxe, checkA11y, configureAxe } from 'axe-playwright'; - -export default { - async preVisit(page) { - await injectAxe(page); - }, - async postVisit(page, context) { - // Get the entire context of a story, including parameters, args, argTypes, etc. - const storyContext = await getStoryContext(page, context); - // Apply story-level a11y rules - await configureAxe(page, { - rules: storyContext.parameters?.a11y?.config?.rules, - }); - - // Do not run a11y tests on disabled stories. - if (storyContext.parameters?.a11y?.disable) { - return; - } - - const element = - storyContext.parameters?.a11y?.element ?? '#storybook-root'; - await checkA11y(page, element, { - detailedReport: true, - detailedReportOptions: { - html: true, - }, - }); - }, -}; +import { getStoryContext } from '@storybook/test-runner'; +import { injectAxe, checkA11y, configureAxe } from 'axe-playwright'; + +export default { + async preVisit(page) { + await injectAxe(page); + }, + async postVisit(page, context) { + // Get the entire context of a story, including parameters, args, argTypes, etc. + const storyContext = await getStoryContext(page, context); + // Apply story-level a11y rules + await configureAxe(page, { + rules: storyContext.parameters?.a11y?.config?.rules, + }); + + // Do not run a11y tests on disabled stories. + if (storyContext.parameters?.a11y?.disable) { + return; + } + + const element = + storyContext.parameters?.a11y?.element ?? '#storybook-root'; + await checkA11y(page, element, { + detailedReport: true, + detailedReportOptions: { + html: true, + }, + }); + }, +}; diff --git a/.storybook/vitest.setup.ts b/.storybook/vitest.setup.ts index 7dda3c30..b8b45ee8 100644 --- a/.storybook/vitest.setup.ts +++ b/.storybook/vitest.setup.ts @@ -1,3 +1,3 @@ -// Project annotations (decorators, parameters) from preview.tsx are -// automatically applied by @storybook/addon-vitest since Storybook 10.3. -// Add any custom global test setup here if needed. +// Project annotations (decorators, parameters) from preview.tsx are +// automatically applied by @storybook/addon-vitest since Storybook 10.3. +// Add any custom global test setup here if needed. diff --git a/README.md b/README.md index a06c1cc6..158ae011 100644 --- a/README.md +++ b/README.md @@ -1,339 +1,339 @@ -## Getting Started - Development Repo - -Learn how to use @bsf/force-ui components to quickly and easily create elegant and flexible pages using Tailwind CSS. - -@bsf/force-ui is working with Tailwind CSS classes and you need to have Tailwind CSS installed on your project - Tailwind CSS Installation. - -
- -1. Install `@bsf/force-ui`. - -Force UI library can be installed using npm package manager. Since this library is still in it's alpha phase, we need to use the staging branch. - -Using Force UI as a dependency in package.json - - -```json -"dependencies": { - "@bsf/force-ui": "git+https://github.com/brainstormforce/force-ui#1.7.11" -} -``` - -And run the following command to install the package - - - -```bash -npm install -``` - -Or you can directly run the following command to install the package - - -```bash -npm i -S @bsf/force-ui@git+https://github.com/brainstormforce/force-ui.git#1.7.11 -``` - -
- -2. Once you install @bsf/force-ui you need to wrap your tailwind css configurations with the `withTW()` function coming from @bsf/force-ui/withTW. - -```js -const withTW = require( '@bsf/force-ui/withTW' ); - -module.exports = withTW( { - content: [ './src/**/*.{js, jsx}' ], - theme: { - extend: { - colors: { - 'button-primary': '#6B21A8', - 'button-primary-hover': '#7E22CE', - 'brand-800': '#6B21A8', - 'brand-50': '#FAF5FF', - 'border-interactive': '#6B21A8', - focus: '#9333EA', - 'focus-border': '#D8B4FE', - 'toggle-on': '#6B21A8', - 'toggle-on-border': '#C084FC', - 'toggle-on-hover': '#A855F7', - }, - fontSize: { - xxs: '0.6875rem', // 11px - }, - lineHeight: { - 2.6: '0.6875rem', // 11px - }, - boxShadow: { - 'content-wrapper': - '0px 1px 1px 0px #0000000F, 0px 1px 2px 0px #0000001A', - }, - }, - }, - plugins: [], - corePlugins: { - preflight: false, - }, - important: '.surerank-styles', -} ); -``` - -
- -3. @bsf/force-ui comes with a default tailwind theme settings that set's the default theme/styles for components or to provide your own theme/styles to your components. You can override these give below variables in your tailwind.config.js file. - -```jsx -theme: { - extend: { - colors: { - // brand - 'brand-background-50': '#EFF6FF', - 'brand-background-hover-100': '#DBEAFE', - 'brand-200': '#BFDBFE', - 'brand-border-300': '#93C5FD', - 'brand-400': '#60A5FA', - 'brand-500': '#3B82F6', - 'brand-primary-600': '#2563EB', - 'brand-hover-700': '#1D4ED8', - 'brand-800': '#1E40AF', - 'brand-900': '#1E3A8A', - 'brand-text-950': '#172554', - // background - 'background-primary': '#FFFFFF', - 'background-secondary': '#F3F4F6', - 'background-inverse': '#111827', - 'background-brand': '#2563EB', - 'background-important': '#DC2626', - // field - 'field-primary-background': '#F9FAFB', - 'field-secondary-background': '#FFFFFF', - 'field-primary-hover': '#F3F4F6', - 'field-secondary-hover': '#F3F4F6', - 'field-dropzone-background': '#FFFFFF', - 'field-border': '#E5E7EB', - 'field-dropzone-background-hover': '#F9FAFB', - 'field-dropzone-color': '#2563EB', - 'field-label': '#111827', - 'field-input': '#111827', - 'field-helper': '#9CA3AF', - 'field-background-disabled': '#F9FAFB', - 'field-color-disabled': '#D1D5DB', - 'field-placeholder': '#6B7280', - 'field-border-disabled': '#F3F4F6', - 'field-color-error': '#DC2626', - 'field-border-error': '#FECACA', - 'field-background-error': '#FEF2F2', - 'field-required': '#DC2626', - // border - 'border-interactive': '#2563EB', - 'border-subtle': '#E5E7EB', - 'border-strong': '#6B7280', - 'border-inverse': '#374151', - 'border-disabled': '#E5E7EB', - 'border-muted': '#E5E7EB', - 'border-error': '#DC2626', - 'border-transparent-subtle': '#37415114', - 'border-white': '#FFFFFF', - // text - 'text-primary': '#111827', - 'text-secondary': '#4B5563', - 'text-tertiary': '#9CA3AF', - 'text-on-color': '#FFFFFF', - 'text-error': '#DC2626', - 'text-error-inverse': '#F87171', - 'text-inverse': '#FFFFFF', - 'text-disabled': '#D1D5DB', - 'text-on-button-disabled': '#9CA3AF', - // link - 'link-primary': '#2563EB', - 'link-primary-hover': '#1D4ED8', - 'link-inverse': '#38BDF8', - 'link-visited': '#7C3AED', - 'link-visited-inverse': '#A78BFA', - 'link-inverse-hover': '#7DD3FC', - // icon - 'icon-primary': '#111827', - 'icon-secondary': '#4B5563', - 'icon-on-color': '#FFFFFF', - 'icon-inverse': '#FFFFFF', - 'icon-interactive': '#2563EB', - 'icon-on-color-disabled': '#9CA3AF', - 'icon-disabled': '#D1D5DB', - // support - 'support-error': '#DC2626', - 'support-success': '#16A34A', - 'support-warning': '#EAB308', - 'support-info': '#0284C7', - 'support-error-inverse': '#F87171', - 'support-success-inverse': '#4ADE80', - 'support-warning-inverse': '#FDE047', - 'support-info-inverse': '#38BDF8', - // button - 'button-primary': '#2563EB', - 'button-primary-hover': '#1D4ED8', - 'button-secondary': '#1F2937', - 'button-secondary-hover': '#374151', - 'button-tertiary': '#FFFFFF', - 'button-tertiary-hover': '#F9FAFB', - 'button-danger': '#DC2626', - 'button-danger-secondary': '#DC2626', - 'button-danger-hover': '#B91C1C', - 'button-disabled': '#F3F4F6', - 'button-tertiary-border': '#E5E7EB', - 'button-tertiary-color': '#111827', - // focus - focus: '#2563EB', - 'focus-inset': '#FFFFFF', - 'focus-inverse': '#38BDF8', - 'focus-inverse-inset': '#111827', - 'focus-error': '#DC2626', - 'focus-border': '#BFDBFE', - 'focus-error-border': '#FECACA', - // misc - 'misc-highlight': '#BFDBFE', - 'misc-overlay': '#11182780', - 'misc-skeleton-background': '#F3F4F6', - 'misc-skeleton-element': '#D1D5DB', - 'misc-popup-button-hover': '#1118270D', - 'misc-tab-item-hover': '#E5E7EB', - 'misc-dropdown-hover': '#F3F4F6', - 'misc-loader-base': '#1118270D', - 'misc-loader-color': '#2563EB', - 'misc-progress-background': '#E5E7EB', - // badge - 'badge-background-gray': '#F9FAFB', - 'badge-color-gray': '#1F2937', - 'badge-hover-gray': '#F3F4F6', - 'badge-border-gray': '#E5E7EB', - 'badge-background-red': '#FEF2F2', - 'badge-color-red': '#B91C1C', - 'badge-hover-red': '#FEE2E2', - 'badge-border-red': '#FECACA', - 'badge-background-yellow': '#FEFCE8', - 'badge-color-yellow': '#A16207', - 'badge-hover-yellow': '#FEF9C3', - 'badge-border-yellow': '#FEF08A', - 'badge-hover-green': '#DCFCE7', - 'badge-border-green': '#BBF7D0', - 'badge-background-green': '#F0FDF4', - 'badge-color-green': '#15803D', - 'badge-background-sky': '#F0F9FF', - 'badge-color-sky': '#0369A1', - 'badge-hover-sky': '#E0F2FE', - 'badge-border-sky': '#BAE6FD', - 'badge-background-disabled': '#F3F4F6', - 'badge-color-disabled': '#D1D5DB', - 'badge-hover-disabled': '#F3F4F6', - 'badge-border-disabled': '#E5E7EB', - 'badge-background-important': '#DC2626', - // alert - 'alert-background-neutral': '#FFFFFF', - 'alert-border-neutral': '#E5E7EB', - 'alert-background-danger': '#FEF2F2', - 'alert-border-danger': '#FECACA', - 'alert-background-warning': '#FEFCE8', - 'alert-border-warning': '#FEF08A', - 'alert-background-green': '#F0FDF4', - 'alert-border-green': '#BBF7D0', - 'alert-background-info': '#F0F9FF', - 'alert-border-info': '#BAE6FD', - // tab - 'tab-background': '#F3F4F6', - 'tab-border': '#E5E7EB', - // tooltip - 'tooltip-background-light': '#FFFFFF', - 'tooltip-background-dark': '#111827', - // toggle - 'toggle-off': '#E5E7EB', - 'toggle-on': '#2563EB', - 'toggle-dial-background': '#FFFFFF', - 'toggle-off-hover': '#D1D5DB', - 'toggle-off-border': '#D1D5DB', - 'toggle-on-hover': '#3B82F6', - 'toggle-on-border': '#60A5FA', - 'toggle-off-disabled': '#F3F4F6', - }, - width: { - '1/7': '14.2857143%', - '1/8': '12.5%', - '1/9': '11.1111111%', - '1/10': '10%', - '1/11': '9.0909091%', - '1/12': '8.3333333%', - }, - boxShadow: { - 'soft-shadow-sm': - '0px 6px 32px -12px rgba(149, 160, 178, 0.12)', - 'soft-shadow': '0px 8px 32px -12px rgba(149, 160, 178, 0.16)', - 'soft-shadow-md': - '0px 10px 32px -12px rgba(149, 160, 178, 0.2)', - 'soft-shadow-lg': - '0px 12px 32px -12px rgba(149, 160, 178, 0.24)', - 'soft-shadow-xl': - '0px 16px 32px -12px rgba(149, 160, 178, 0.32)', - 'soft-shadow-2xl': - '0px 24px 64px -12px rgba(149, 160, 178, 0.32)', - 'soft-shadow-inner': '0px 1px 1px 0px rgba(0, 0, 0, 0.05)', - }, - fontSize: { - tiny: '0.625rem', - }, - spacing: { - 120: '30rem', // 480px - 95: '23.75rem', // 380px - 141.5: '35.375rem', // 566px - 188: '47rem', // 752px - }, - zIndex: { - 999999: '999999', - }, - }, -} - -``` - -
- -4. Great 🥳, now you're ready to use @bsf/force-ui. - -```jsx -import { Button } from "@bsf/force-ui"; - -export default function Example() { - return ; -} -``` - -
- -## MCP Setup - -Force UI provides an MCP server that gives AI assistants accurate component usage context correct props, patterns, and examples. So you get reliable implementations without guesswork. - -```bash -npx mcp-add --type http --url "https://brainstormforce.github.io/force-ui/mcp" --scope project # use `global` instead of `project` for making it accessible globally -``` - -When prompted, use the following configuration: - -| Prompt | Value | -| --- | --- | -| **What is the server name?** | `force-ui-mcp` | -| **HTTP headers? (comma-separated Key=value, or leave empty)** | Leave empty | -| **Which clients should be configured?** | Select your preferred AI client(s). Ex. Claude | -| **claude code OAuth client ID? (leave empty if not needed)** | Leave empty | - -
- -Now you are ready to use Force-UI MCP in your project. - -
- -## @bsf/force-ui Documentation - -Visit https://github.com/brainstormforce/force-ui/wiki for full documentation. - - -## Contributing - -Contributions are always welcome! - -See `CONTRIBUTING.md` for ways to get started. - -Please adhere to this project's `CODE_OF_CONDUCT.md`. +## Getting Started - Development Repo + +Learn how to use @bsf/force-ui components to quickly and easily create elegant and flexible pages using Tailwind CSS. + +@bsf/force-ui is working with Tailwind CSS classes and you need to have Tailwind CSS installed on your project - Tailwind CSS Installation. + +
+ +1. Install `@bsf/force-ui`. + +Force UI library can be installed using npm package manager. Since this library is still in it's alpha phase, we need to use the staging branch. + +Using Force UI as a dependency in package.json - + +```json +"dependencies": { + "@bsf/force-ui": "git+https://github.com/brainstormforce/force-ui#1.7.11" +} +``` + +And run the following command to install the package - + + +```bash +npm install +``` + +Or you can directly run the following command to install the package - + +```bash +npm i -S @bsf/force-ui@git+https://github.com/brainstormforce/force-ui.git#1.7.11 +``` + +
+ +2. Once you install @bsf/force-ui you need to wrap your tailwind css configurations with the `withTW()` function coming from @bsf/force-ui/withTW. + +```js +const withTW = require( '@bsf/force-ui/withTW' ); + +module.exports = withTW( { + content: [ './src/**/*.{js, jsx}' ], + theme: { + extend: { + colors: { + 'button-primary': '#6B21A8', + 'button-primary-hover': '#7E22CE', + 'brand-800': '#6B21A8', + 'brand-50': '#FAF5FF', + 'border-interactive': '#6B21A8', + focus: '#9333EA', + 'focus-border': '#D8B4FE', + 'toggle-on': '#6B21A8', + 'toggle-on-border': '#C084FC', + 'toggle-on-hover': '#A855F7', + }, + fontSize: { + xxs: '0.6875rem', // 11px + }, + lineHeight: { + 2.6: '0.6875rem', // 11px + }, + boxShadow: { + 'content-wrapper': + '0px 1px 1px 0px #0000000F, 0px 1px 2px 0px #0000001A', + }, + }, + }, + plugins: [], + corePlugins: { + preflight: false, + }, + important: '.surerank-styles', +} ); +``` + +
+ +3. @bsf/force-ui comes with a default tailwind theme settings that set's the default theme/styles for components or to provide your own theme/styles to your components. You can override these give below variables in your tailwind.config.js file. + +```jsx +theme: { + extend: { + colors: { + // brand + 'brand-background-50': '#EFF6FF', + 'brand-background-hover-100': '#DBEAFE', + 'brand-200': '#BFDBFE', + 'brand-border-300': '#93C5FD', + 'brand-400': '#60A5FA', + 'brand-500': '#3B82F6', + 'brand-primary-600': '#2563EB', + 'brand-hover-700': '#1D4ED8', + 'brand-800': '#1E40AF', + 'brand-900': '#1E3A8A', + 'brand-text-950': '#172554', + // background + 'background-primary': '#FFFFFF', + 'background-secondary': '#F3F4F6', + 'background-inverse': '#111827', + 'background-brand': '#2563EB', + 'background-important': '#DC2626', + // field + 'field-primary-background': '#F9FAFB', + 'field-secondary-background': '#FFFFFF', + 'field-primary-hover': '#F3F4F6', + 'field-secondary-hover': '#F3F4F6', + 'field-dropzone-background': '#FFFFFF', + 'field-border': '#E5E7EB', + 'field-dropzone-background-hover': '#F9FAFB', + 'field-dropzone-color': '#2563EB', + 'field-label': '#111827', + 'field-input': '#111827', + 'field-helper': '#9CA3AF', + 'field-background-disabled': '#F9FAFB', + 'field-color-disabled': '#D1D5DB', + 'field-placeholder': '#6B7280', + 'field-border-disabled': '#F3F4F6', + 'field-color-error': '#DC2626', + 'field-border-error': '#FECACA', + 'field-background-error': '#FEF2F2', + 'field-required': '#DC2626', + // border + 'border-interactive': '#2563EB', + 'border-subtle': '#E5E7EB', + 'border-strong': '#6B7280', + 'border-inverse': '#374151', + 'border-disabled': '#E5E7EB', + 'border-muted': '#E5E7EB', + 'border-error': '#DC2626', + 'border-transparent-subtle': '#37415114', + 'border-white': '#FFFFFF', + // text + 'text-primary': '#111827', + 'text-secondary': '#4B5563', + 'text-tertiary': '#9CA3AF', + 'text-on-color': '#FFFFFF', + 'text-error': '#DC2626', + 'text-error-inverse': '#F87171', + 'text-inverse': '#FFFFFF', + 'text-disabled': '#D1D5DB', + 'text-on-button-disabled': '#9CA3AF', + // link + 'link-primary': '#2563EB', + 'link-primary-hover': '#1D4ED8', + 'link-inverse': '#38BDF8', + 'link-visited': '#7C3AED', + 'link-visited-inverse': '#A78BFA', + 'link-inverse-hover': '#7DD3FC', + // icon + 'icon-primary': '#111827', + 'icon-secondary': '#4B5563', + 'icon-on-color': '#FFFFFF', + 'icon-inverse': '#FFFFFF', + 'icon-interactive': '#2563EB', + 'icon-on-color-disabled': '#9CA3AF', + 'icon-disabled': '#D1D5DB', + // support + 'support-error': '#DC2626', + 'support-success': '#16A34A', + 'support-warning': '#EAB308', + 'support-info': '#0284C7', + 'support-error-inverse': '#F87171', + 'support-success-inverse': '#4ADE80', + 'support-warning-inverse': '#FDE047', + 'support-info-inverse': '#38BDF8', + // button + 'button-primary': '#2563EB', + 'button-primary-hover': '#1D4ED8', + 'button-secondary': '#1F2937', + 'button-secondary-hover': '#374151', + 'button-tertiary': '#FFFFFF', + 'button-tertiary-hover': '#F9FAFB', + 'button-danger': '#DC2626', + 'button-danger-secondary': '#DC2626', + 'button-danger-hover': '#B91C1C', + 'button-disabled': '#F3F4F6', + 'button-tertiary-border': '#E5E7EB', + 'button-tertiary-color': '#111827', + // focus + focus: '#2563EB', + 'focus-inset': '#FFFFFF', + 'focus-inverse': '#38BDF8', + 'focus-inverse-inset': '#111827', + 'focus-error': '#DC2626', + 'focus-border': '#BFDBFE', + 'focus-error-border': '#FECACA', + // misc + 'misc-highlight': '#BFDBFE', + 'misc-overlay': '#11182780', + 'misc-skeleton-background': '#F3F4F6', + 'misc-skeleton-element': '#D1D5DB', + 'misc-popup-button-hover': '#1118270D', + 'misc-tab-item-hover': '#E5E7EB', + 'misc-dropdown-hover': '#F3F4F6', + 'misc-loader-base': '#1118270D', + 'misc-loader-color': '#2563EB', + 'misc-progress-background': '#E5E7EB', + // badge + 'badge-background-gray': '#F9FAFB', + 'badge-color-gray': '#1F2937', + 'badge-hover-gray': '#F3F4F6', + 'badge-border-gray': '#E5E7EB', + 'badge-background-red': '#FEF2F2', + 'badge-color-red': '#B91C1C', + 'badge-hover-red': '#FEE2E2', + 'badge-border-red': '#FECACA', + 'badge-background-yellow': '#FEFCE8', + 'badge-color-yellow': '#A16207', + 'badge-hover-yellow': '#FEF9C3', + 'badge-border-yellow': '#FEF08A', + 'badge-hover-green': '#DCFCE7', + 'badge-border-green': '#BBF7D0', + 'badge-background-green': '#F0FDF4', + 'badge-color-green': '#15803D', + 'badge-background-sky': '#F0F9FF', + 'badge-color-sky': '#0369A1', + 'badge-hover-sky': '#E0F2FE', + 'badge-border-sky': '#BAE6FD', + 'badge-background-disabled': '#F3F4F6', + 'badge-color-disabled': '#D1D5DB', + 'badge-hover-disabled': '#F3F4F6', + 'badge-border-disabled': '#E5E7EB', + 'badge-background-important': '#DC2626', + // alert + 'alert-background-neutral': '#FFFFFF', + 'alert-border-neutral': '#E5E7EB', + 'alert-background-danger': '#FEF2F2', + 'alert-border-danger': '#FECACA', + 'alert-background-warning': '#FEFCE8', + 'alert-border-warning': '#FEF08A', + 'alert-background-green': '#F0FDF4', + 'alert-border-green': '#BBF7D0', + 'alert-background-info': '#F0F9FF', + 'alert-border-info': '#BAE6FD', + // tab + 'tab-background': '#F3F4F6', + 'tab-border': '#E5E7EB', + // tooltip + 'tooltip-background-light': '#FFFFFF', + 'tooltip-background-dark': '#111827', + // toggle + 'toggle-off': '#E5E7EB', + 'toggle-on': '#2563EB', + 'toggle-dial-background': '#FFFFFF', + 'toggle-off-hover': '#D1D5DB', + 'toggle-off-border': '#D1D5DB', + 'toggle-on-hover': '#3B82F6', + 'toggle-on-border': '#60A5FA', + 'toggle-off-disabled': '#F3F4F6', + }, + width: { + '1/7': '14.2857143%', + '1/8': '12.5%', + '1/9': '11.1111111%', + '1/10': '10%', + '1/11': '9.0909091%', + '1/12': '8.3333333%', + }, + boxShadow: { + 'soft-shadow-sm': + '0px 6px 32px -12px rgba(149, 160, 178, 0.12)', + 'soft-shadow': '0px 8px 32px -12px rgba(149, 160, 178, 0.16)', + 'soft-shadow-md': + '0px 10px 32px -12px rgba(149, 160, 178, 0.2)', + 'soft-shadow-lg': + '0px 12px 32px -12px rgba(149, 160, 178, 0.24)', + 'soft-shadow-xl': + '0px 16px 32px -12px rgba(149, 160, 178, 0.32)', + 'soft-shadow-2xl': + '0px 24px 64px -12px rgba(149, 160, 178, 0.32)', + 'soft-shadow-inner': '0px 1px 1px 0px rgba(0, 0, 0, 0.05)', + }, + fontSize: { + tiny: '0.625rem', + }, + spacing: { + 120: '30rem', // 480px + 95: '23.75rem', // 380px + 141.5: '35.375rem', // 566px + 188: '47rem', // 752px + }, + zIndex: { + 999999: '999999', + }, + }, +} + +``` + +
+ +4. Great 🥳, now you're ready to use @bsf/force-ui. + +```jsx +import { Button } from "@bsf/force-ui"; + +export default function Example() { + return ; +} +``` + +
+ +## MCP Setup + +Force UI provides an MCP server that gives AI assistants accurate component usage context correct props, patterns, and examples. So you get reliable implementations without guesswork. + +```bash +npx mcp-add --type http --url "https://brainstormforce.github.io/force-ui/mcp" --scope project # use `global` instead of `project` for making it accessible globally +``` + +When prompted, use the following configuration: + +| Prompt | Value | +| --- | --- | +| **What is the server name?** | `force-ui-mcp` | +| **HTTP headers? (comma-separated Key=value, or leave empty)** | Leave empty | +| **Which clients should be configured?** | Select your preferred AI client(s). Ex. Claude | +| **claude code OAuth client ID? (leave empty if not needed)** | Leave empty | + +
+ +Now you are ready to use Force-UI MCP in your project. + +
+ +## @bsf/force-ui Documentation + +Visit https://github.com/brainstormforce/force-ui/wiki for full documentation. + + +## Contributing + +Contributions are always welcome! + +See `CONTRIBUTING.md` for ways to get started. + +Please adhere to this project's `CODE_OF_CONDUCT.md`. diff --git a/changelog.txt b/changelog.txt index 279c4b11..53dd7a3e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,220 +1,220 @@ -Version 1.7.11 - 8th April, 2026 -- New: Added Storybook MCP compatibility support for smoother integration with MCP-based tooling and workflows. -- Improvement: Atom - Alert: Added `role="alert"` and improved focus-visible ring on the close button. -- Improvement: Atom - Badge: Converted closable element to a semantic ` - - ); -}; - -AccordionTrigger.displayName = 'Accordion.Trigger'; - -// Define AccordionContent-specific props -export interface AccordionContentProps extends CommonProps { - /** Determines if the content is open */ - isOpen?: boolean; - /** Accordion type (same as parent) */ - type?: 'simple' | 'separator' | 'boxed'; - /** Internal ID for this content region (linked from AccordionTrigger aria-controls) */ - contentId?: string; - /** Internal ID of the trigger button, used for aria-labelledby on this region */ - triggerId?: string; -} - -export const AccordionContent = ( { - isOpen, - disabled = false, - type = 'simple', - children, - className, - contentId, - triggerId, -}: AccordionContentProps ) => { - const contentVariants = { - open: { - height: 'auto', - opacity: 1, - overflow: 'unset', - transition: { - overflow: { - delay: 1, - }, - }, - }, - closed: { - height: 0, - opacity: 0, - overflow: 'hidden', - }, - }; - - const contentPaddingClasses = { - simple: 'px-2 pb-3', - separator: 'px-2 pb-4', - boxed: 'px-3 pb-4', - }?.[ type ]; - - return ( - -
{ children }
-
- ); -}; - -AccordionContent.displayName = 'Accordion.Content'; - -export default Object.assign( Accordion, { - Item: AccordionItem, - Trigger: AccordionTrigger, - Content: AccordionContent, -} ); +import React, { + type ReactNode, + type ElementType, + type MouseEventHandler, + useMemo, + useState, +} from 'react'; +import { nanoid } from 'nanoid'; +import { Plus, Minus, ChevronDown } from 'lucide-react'; +import { motion } from 'framer-motion'; +import { callAll, cn } from '@/utilities/functions'; + +// Define common props to be shared by all components +export interface CommonProps { + /** Custom class names for additional styling */ + className?: string; + /** Disables the component */ + disabled?: boolean; + /** Children components */ + children: ReactNode; +} + +// Define Accordion-specific props +export interface AccordionProps extends CommonProps { + /** Type of accordion: 'simple', 'separator', or 'boxed' */ + type?: 'simple' | 'separator' | 'boxed'; + /** Initial active item(s) */ + defaultValue?: string | string[]; + /** Automatically close other items when one is opened */ + autoClose?: boolean; +} + +export const Accordion = ( { + type = 'simple', + defaultValue = [], + autoClose = false, + disabled = false, + children, + className, +}: AccordionProps ) => { + const [ activeItems, setActiveItems ] = useState( + Array.isArray( defaultValue ) ? defaultValue : [ defaultValue ] + ); + + const handleToggle = ( value: string ) => { + setActiveItems( ( prev ) => { + if ( autoClose ) { + return prev.includes( value ) ? [] : [ value ]; + } + return prev.includes( value ) + ? prev.filter( ( item ) => item !== value ) + : [ ...prev, value ]; + } ); + }; + + const typeClasses = type === 'boxed' ? 'space-y-3' : ''; + + return ( +
+ { React.Children.map( children, ( child ) => { + if ( React.isValidElement( child ) && 'value' in child.props ) { + const isCollapsible = child.props.collapsible !== false; // default true + const open = isCollapsible + ? activeItems.includes( child.props.value ) + : true; + + return React.cloneElement( + child as React.ReactElement, + { + isOpen: open, + onToggle: isCollapsible + ? () => handleToggle( child.props.value ) + : undefined, + type, + disabled: disabled || child.props.disabled, + } + ); + } + return child; + } ) } +
+ ); +}; + +Accordion.displayName = 'Accordion'; + +// Define AccordionItem-specific props +/** + * Props for an AccordionItem component. + */ +export interface AccordionItemProps extends CommonProps { + /** Determines if the item is open */ + isOpen?: boolean; + /** Callback to toggle the item's state */ + onToggle?: () => void; + /** Accordion type (same as parent) */ + type?: 'simple' | 'separator' | 'boxed'; + /** The value associated with the accordion item */ + value?: string; + /** Internal ID linking trigger to content for aria-controls */ + contentId?: string; + /** Internal ID for the trigger button, used by content region's aria-labelledby */ + triggerId?: string; +} + +export const AccordionItem = ( { + isOpen, + onToggle, + type = 'simple', + disabled = false, + children, + className, +}: AccordionItemProps ) => { + const contentId = useMemo( () => `accordion-content-${ nanoid() }`, [] ); + const triggerId = useMemo( () => `accordion-trigger-${ nanoid() }`, [] ); + + const typeClasses = { + simple: 'border-0', + separator: 'border-0 border-b border-solid border-border-subtle', + boxed: 'border border-solid border-border-subtle rounded-md', + }?.[ type ]; + + return ( +
+ { React.Children.map( children, ( child ) => + React.isValidElement( child ) + ? React.cloneElement( child, { + isOpen, + onToggle, + type, + disabled, + contentId, + triggerId, + } ) + : child + ) } +
+ ); +}; + +AccordionItem.displayName = 'Accordion.Item'; + +// Define AccordionTrigger-specific props +export interface AccordionTriggerProps extends CommonProps { + /** OnClick handler for the accordion trigger. This works only when collapsible is set to `false`. */ + onClick?: () => void; + /** Callback for toggling item state */ + onToggle?: () => void; + /** Indicates if the item is open */ + isOpen?: boolean; + /** Type of icon to display */ + iconType?: 'arrow' | 'plus-minus' | 'none'; + /** Element to render trigger as */ + tag?: ElementType; + /** Accordion type (same as parent) */ + type?: 'simple' | 'separator' | 'boxed'; + /** Specifies whether the accordion item can be collapsed. */ + collapsible?: boolean; + /** Internal ID for aria-controls linking to content panel */ + contentId?: string; + /** Internal ID for this trigger button, used by content region's aria-labelledby */ + triggerId?: string; +} + +export const AccordionTrigger = ( { + onClick, + onToggle, + isOpen, + iconType = 'arrow', + collapsible = true, + disabled = false, + tag = 'h3', + type = 'simple', + children, + className, + contentId, + triggerId, + ...props +}: AccordionTriggerProps ) => { + const paddingClasses = { + simple: 'px-2 py-3', + separator: 'px-2 py-4', + boxed: 'px-3 py-4', + }?.[ type ]; + + const renderIcon = () => { + if ( ! collapsible ) { + return null; + } + + if ( iconType === 'arrow' ) { + return ( +