Skip to content

Controls: Add maxPresetColors option to ColorControl#33998

Open
mixelburg wants to merge 6 commits intostorybookjs:nextfrom
mixelburg:feat/color-preset-limit
Open

Controls: Add maxPresetColors option to ColorControl#33998
mixelburg wants to merge 6 commits intostorybookjs:nextfrom
mixelburg:feat/color-preset-limit

Conversation

@mixelburg
Copy link
Contributor

@mixelburg mixelburg commented Mar 3, 2026

What does this PR do?

Closes #20298

Adds a new optional maxPresetColors prop to ColorConfig (and by extension ColorControl) that allows users to configure or remove the hardcoded 27-color preset limit.

Problem

The ColorControl component has a hardcoded limit of 27 preset colors:

return initialPresets.concat(selectedColors).filter(Boolean).slice(-27);

This prevents users with design systems that have more than 27 colors from displaying all their presets in the color picker control.

Solution

Add a maxPresetColors?: number option to ColorConfig:

  • Defaults to 27 for backward compatibility
  • Setting to 0 or Infinity removes the limit entirely
  • Any other positive number limits presets to that count

Usage

// In your story argTypes or meta
color: {
  control: {
    type: 'color',
    presetColors: myDesignSystemColors, // More than 27 colors!
    maxPresetColors: 50, // Show up to 50
    // or:
    maxPresetColors: 0, // Show all, no limit
  },
},

Changes

  • types.ts: Added maxPresetColors?: number to ColorConfig interface with JSDoc
  • Color.tsx: Updated usePresets hook to accept and use maxPresetColors parameter; updated ColorControl to pass it through
  • Color.stories.tsx: Added two new stories demonstrating the feature (WithMaxPresetColors and WithUnlimitedPresetColors)

Summary by CodeRabbit

  • New Features

    • Added maxPresetColors to color controls to limit visible preset colors (default 27; 0 or Infinity disables the limit). Oldest presets are dropped in most-recently-used order.
  • Examples

    • Added three example stories: capped preset list (5 visible), unlimited preset list (40 presets), and an example showing fallback behavior for an invalid negative setting.
  • Documentation

    • Exposed maxPresetColors option in config docs with default and behavior described.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduce a configurable maxPresetColors (default 27) across types, hook, and Color control to limit or disable displayed preset colors; add three stories demonstrating limited, unlimited, and invalid-limit fallback behaviors; and expand @sveltejs/vite-plugin-svelte peerDependency to include ^7.0.0.

Changes

Cohort / File(s) Summary
Type Definitions
code/addons/docs/src/blocks/controls/types.ts
Added optional maxPresetColors?: number to ColorConfig with docs (default 27, MRU removal when exceeded, disable with 0 or Infinity).
Control Implementation
code/addons/docs/src/blocks/controls/Color.tsx
Extended usePresets(...) signature to accept maxPresetColors (default 27), added limit-slicing logic (special-case 0/Infinity to return all), included maxPresetColors in deps, and forwarded prop from ColorControl.
Stories / Examples
code/addons/docs/src/blocks/controls/Color.stories.tsx
Added three stories: WithMaxPresetColors (limit 5 on an 8-item list), WithUnlimitedPresetColors (limit disabled via 0, generates 40 HSL presets), and WithInvalidMaxPresetColors (negative limit falls back and shows descriptive name).
Package metadata
code/frameworks/svelte-vite/package.json
Expanded peerDependency range for @sveltejs/vite-plugin-svelte to include ^7.0.0.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/addons/docs/src/blocks/controls/Color.tsx`:
- Around line 335-338: The slice branch must guard against negative,
non-integer, NaN, or Infinity values for maxPresetColors before calling
combined.slice; update the logic where maxPresetColors is used (the block
referencing combined and maxPresetColors in Color.tsx) to coerce and validate it
into a safe positive integer (e.g., if typeof maxPresetColors !== 'number' ||
!isFinite(maxPresetColors) || maxPresetColors <= 0 return combined; otherwise
use const n = Math.floor(maxPresetColors) and return combined.slice(-Math.min(n,
combined.length))). Ensure you reference and use the existing combined and
maxPresetColors identifiers when implementing the checks.

In `@code/core/src/actions/containers/ActionLogger/index.tsx`:
- Around line 49-51: The retrieved expandLevel from
api.getCurrentParameter<ActionsParameters['actions']>(PARAM_KEY) must be
normalized before calling this.setState; validate that the value is a finite
number, coerce to an integer (e.g. Math.floor), clamp to a safe range (minimum 0
or 1 as your UI expects), and fall back to the default (1) for NaN/invalid
inputs, then pass that sanitized value to this.setState({ expandLevel }); apply
the same normalization logic to the other assignment site around the 68-70 block
so both places use the same validated, clamped integer for expandLevel.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e23262b and 04577eb.

📒 Files selected for processing (6)
  • code/addons/docs/src/blocks/controls/Color.stories.tsx
  • code/addons/docs/src/blocks/controls/Color.tsx
  • code/addons/docs/src/blocks/controls/types.ts
  • code/core/src/actions/components/ActionLogger/index.tsx
  • code/core/src/actions/containers/ActionLogger/index.tsx
  • code/core/src/actions/types.ts

Comment on lines +49 to +51
const expandLevel =
api.getCurrentParameter<ActionsParameters['actions']>(PARAM_KEY)?.expandLevel ?? 1;
this.setState({ expandLevel });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Normalize expandLevel before storing it in state.

expandLevel is user-configurable, so invalid numeric values (e.g. negative, fractional, NaN) can leak into state and produce inconsistent expansion behavior.

Suggested fix
+  private getExpandLevel = () => {
+    const raw = this.props.api.getCurrentParameter<ActionsParameters['actions']>(PARAM_KEY)?.expandLevel;
+    return Number.isInteger(raw) && raw >= 0 ? raw : 1;
+  };
+
   override componentDidMount() {
@@
-    const expandLevel =
-      api.getCurrentParameter<ActionsParameters['actions']>(PARAM_KEY)?.expandLevel ?? 1;
-    this.setState({ expandLevel });
+    this.setState({ expandLevel: this.getExpandLevel() });
   }
@@
-    const expandLevel =
-      api.getCurrentParameter<ActionsParameters['actions']>(PARAM_KEY)?.expandLevel ?? 1;
-    this.setState({ expandLevel });
+    this.setState({ expandLevel: this.getExpandLevel() });
   };

Also applies to: 68-70

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/actions/containers/ActionLogger/index.tsx` around lines 49 -
51, The retrieved expandLevel from
api.getCurrentParameter<ActionsParameters['actions']>(PARAM_KEY) must be
normalized before calling this.setState; validate that the value is a finite
number, coerce to an integer (e.g. Math.floor), clamp to a safe range (minimum 0
or 1 as your UI expects), and fall back to the default (1) for NaN/invalid
inputs, then pass that sanitized value to this.setState({ expandLevel }); apply
the same normalization logic to the other assignment site around the 68-70 block
so both places use the same validated, clamped integer for expandLevel.

@Sidnioulz Sidnioulz changed the title Controls: add maxPresetColors option to ColorControl Controls: Add maxPresetColors option to ColorControl Mar 4, 2026
@Sidnioulz Sidnioulz self-assigned this Mar 4, 2026
@Sidnioulz Sidnioulz self-requested a review March 4, 2026 08:38
Copy link
Member

@Sidnioulz Sidnioulz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for this PR @mixelburg! I'll be very happy to approve it once we've cleared the mixup with the ActionLogger. See also improvements you can make to the stories!

Code LGTM besides that.

The ColorControl preset limit was previously hardcoded to 27 colors.
This prevented users with large design systems (more than 27 colors)
from displaying all their preset colors.

Add a new optional maxPresetColors prop to ColorConfig/ColorControl
that allows users to configure or remove this limit:
- Defaults to 27 for backward compatibility
- Setting to 0 or Infinity removes the limit entirely
- Any other positive number limits presets to that count

Add two new stories demonstrating the feature:
- WithMaxPresetColors (limit to 5)
- WithUnlimitedPresetColors (maxPresetColors: 0, 40 colors)

Fixes storybookjs#20298
@mixelburg mixelburg force-pushed the feat/color-preset-limit branch from 04577eb to 7383be3 Compare March 8, 2026 15:25
@mixelburg
Copy link
Contributor Author

Hi @Sidnioulz — the ActionLogger mixup is now resolved. The branch was forked before #33977 merged, so it included those commits in the diff. I've rebased onto the latest next and force-pushed; the PR now contains only the 3 ColorControl files (Color.tsx, Color.stories.tsx, types.ts). Happy to address any story improvements you had in mind!

@Sidnioulz
Copy link
Member

Hi @Sidnioulz — the ActionLogger mixup is now resolved. The branch was forked before #33977 merged, so it included those commits in the diff. I've rebased onto the latest next and force-pushed; the PR now contains only the 3 ColorControl files (Color.tsx, Color.stories.tsx, types.ts). Happy to address any story improvements you had in mind!

Thank you! We have two more feedback items to go through and then we can merge this:

  • The behaviour when maxPresetColors is invalid must be defined (try a story or unit test with a negative value; and please check if you get type errors when passing non numbers)
  • Could you please make the new stories startOpen: true so we can see the effect in Chromatic captures?

Thanks

…tartOpen to stories

- Normalize maxPresetColors: fall back to default 27 for negative, non-integer, or NaN values
- Add startOpen: true to WithMaxPresetColors and WithUnlimitedPresetColors stories for Chromatic captures
@Sidnioulz Sidnioulz self-requested a review March 12, 2026 09:44
@storybook-app-bot
Copy link

storybook-app-bot bot commented Mar 12, 2026

Package Benchmarks

Commit: f1cf7c8, ran on 15 March 2026 at 01:24:04 UTC

The following packages have significant changes to their size or dependencies:

storybook

Before After Difference
Dependency count 49 49 0
Self size 20.45 MB 20.21 MB 🎉 -245 KB 🎉
Dependency size 16.54 MB 16.54 MB 0 B
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 183 183 0
Self size 779 KB 779 KB 🎉 -116 B 🎉
Dependency size 67.65 MB 67.40 MB 🎉 -245 KB 🎉
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 176 176 0
Self size 32 KB 32 KB 0 B
Dependency size 66.17 MB 65.93 MB 🎉 -245 KB 🎉
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 50 50 0
Self size 1.04 MB 1.04 MB 🎉 -385 B 🎉
Dependency size 37.00 MB 36.75 MB 🎉 -245 KB 🎉
Bundle Size Analyzer node node

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/frameworks/svelte-vite/package.json`:
- Line 70: The change broadened the peerDependency for
"@sveltejs/vite-plugin-svelte" in package.json (the dependency string on that
package) which is outside this PR's stated scope; update the PR by either adding
a clear note in the PR description documenting this peerDependency change and
the "accidental downgrade" fix, or remove the change from this branch and put
the updated "@sveltejs/vite-plugin-svelte" peer range into a separate focused PR
so only the three ColorControl files remain in this PR.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 44b48a5d-60c3-456b-9e16-e567c4ae4bf6

📥 Commits

Reviewing files that changed from the base of the PR and between 42d56f8 and 9ae0cda.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (1)
  • code/frameworks/svelte-vite/package.json

},
"peerDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0",
"@sveltejs/vite-plugin-svelte": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if `@sveltejs/vite-plugin-svelte` version 7.x exists
curl -s "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte" | jq '.versions | keys | map(select(startswith("7."))) | sort | .[-5:]'

Repository: storybookjs/storybook

Length of output: 114


Document or split the peerDependency change.

This package.json change is outside the stated PR scope. According to the comments summary, the PR should contain "only the three ColorControl files," but this extends @sveltejs/vite-plugin-svelte peer compatibility to v7. While the version exists and the change is valid, either document it in the PR description (explaining the "accidental downgrade" fix mentioned in the commit) or split it into a separate PR for clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/frameworks/svelte-vite/package.json` at line 70, The change broadened
the peerDependency for "@sveltejs/vite-plugin-svelte" in package.json (the
dependency string on that package) which is outside this PR's stated scope;
update the PR by either adding a clear note in the PR description documenting
this peerDependency change and the "accidental downgrade" fix, or remove the
change from this branch and put the updated "@sveltejs/vite-plugin-svelte" peer
range into a separate focused PR so only the three ColorControl files remain in
this PR.

Demonstrates fallback to default (27) when negative value is passed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mixelburg
Copy link
Contributor Author

Added a WithInvalidMaxPresetColors story that passes a negative value (-5) — demonstrates the fallback to the default of 27. The guard already handles non-integer, negative, NaN, and Infinity values. All new stories use startOpen: true for Chromatic captures.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request]: Addon-control color picker preset limit

2 participants