@codecademy/styleguide 79.0.1-alpha.d74da5.0 → 79.0.1-alpha.dda0e7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,10 +46,14 @@ const preview: Preview = {
46
46
  [
47
47
  'About',
48
48
  'Best Practices',
49
- 'ESLint rules',
50
49
  'Contributing',
50
+ 'ESLint rules',
51
51
  'FAQs',
52
52
  'Stories',
53
+ 'Brand',
54
+ 'Installation',
55
+ 'Usage Guide',
56
+ 'MCP',
53
57
  ],
54
58
  'Foundations',
55
59
  'Layouts',
package/CHANGELOG.md CHANGED
@@ -3,7 +3,7 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ### [79.0.1-alpha.d74da5.0](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@79.0.0...@codecademy/styleguide@79.0.1-alpha.d74da5.0) (2026-02-02)
6
+ ### [79.0.1-alpha.dda0e7.0](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@79.0.0...@codecademy/styleguide@79.0.1-alpha.dda0e7.0) (2026-02-06)
7
7
 
8
8
  **Note:** Version bump only for package @codecademy/styleguide
9
9
 
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@codecademy/styleguide",
3
3
  "description": "Styleguide & Component library for codecademy.com",
4
- "version": "79.0.1-alpha.d74da5.0",
4
+ "version": "79.0.1-alpha.dda0e7.0",
5
5
  "author": "Codecademy Engineering",
6
6
  "license": "MIT",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
10
10
  "repository": "git@github.com:Codecademy/gamut.git",
11
- "gitHead": "0b1c876fb2b1eeb6f7fcbbb443109e28013bc7a6"
11
+ "gitHead": "9cf2788574551506b4a0b54e7eb9b711c73c3775"
12
12
  }
@@ -124,7 +124,7 @@ Use `sm` when the `Badge` is inline with other components such as <LinkTo id='Ty
124
124
 
125
125
  ## Leading icon
126
126
 
127
- Include the `icon` property when the icon reinforces the badges message and improves scannability. Since badges are inherently small, it's recommended to use Gamut's mini icons. Below are examples of using icons with the different `Badge` sizes.
127
+ Include the `icon` property when the icon reinforces the badge's message and improves scannability. Since badges are inherently small, it's recommended to use Gamut's mini icons. Below are examples of using icons with the different `Badge` sizes.
128
128
 
129
129
  ### Default size with icon
130
130
 
@@ -13,6 +13,7 @@ import { parameters as deepControlsParameters } from './Deep Controls Add-On.mdx
13
13
  import { parameters as eslintRulesParameters } from './ESLint rules.mdx';
14
14
  import { parameters as faqsParameters } from './FAQs.mdx';
15
15
  import { parameters as installationParameters } from './Installation.mdx';
16
+ import { parameters as mcpAboutParameters } from './MCP/About.mdx';
16
17
  import { parameters as storiesParameters } from './Stories.mdx';
17
18
  import { parameters as usageGuideParameters } from './Usage Guide.mdx';
18
19
 
@@ -38,5 +39,6 @@ export const parameters = {
38
39
  brandParameters,
39
40
  installationParameters,
40
41
  usageGuideParameters,
42
+ mcpAboutParameters,
41
43
  ])}
42
44
  />
@@ -0,0 +1,28 @@
1
+ import { Meta } from '@storybook/blocks';
2
+
3
+ import {
4
+ AboutHeader,
5
+ addParentPath,
6
+ TableOfContents,
7
+ } from '~styleguide/blocks';
8
+
9
+ import { parameters as codeConnectParameters } from './Code Connect.mdx';
10
+ import { parameters as figmaMcpParameters } from './Figma MCP.mdx';
11
+
12
+ export const parameters = {
13
+ id: 'Meta/MCP',
14
+ title: 'MCP',
15
+ subtitle: 'Documentation related to the Figma Model Context Protocol exclusive to Codecademy + Skillsoft employees.',
16
+ status: 'static',
17
+ };
18
+
19
+ <Meta title="Meta/MCP/About" />
20
+
21
+ <AboutHeader {...parameters} />
22
+
23
+ <TableOfContents
24
+ links={addParentPath(parameters.id, [
25
+ codeConnectParameters,
26
+ figmaMcpParameters,
27
+ ])}
28
+ />
@@ -0,0 +1,38 @@
1
+ import { Meta } from '@storybook/blocks';
2
+
3
+ import { AboutHeader, Callout, ImageWrapper } from '~styleguide/blocks';
4
+
5
+ export const parameters = {
6
+ id: 'Code Connect',
7
+ title: 'Code Connect',
8
+ subtitle: 'Exploring Code Connect features in Figma.',
9
+ status: 'static',
10
+ };
11
+
12
+ <Meta title="Meta/MCP/Code Connect" />
13
+
14
+ <AboutHeader {...parameters} />
15
+
16
+ Code Connect provides Figma with the actual code for Figma components. In providing accurate code, this helps the Figma MCP generate accurate code snippets for designs that use these components.
17
+
18
+ More information can be found at [Figma Code Connect documentation](https://www.figma.com/code-connect-docs/)
19
+
20
+ <Callout text="Any changes to Code Connect files should be requested through the Gamut team and NOT done on an individual or a separate team." />
21
+
22
+ ## Code Connected components
23
+
24
+ When visiting the Figma file, you'll know if a component is set up with Code Connect if you select the component and see the sidebar includes a "Code Connect" section.
25
+
26
+ <ImageWrapper
27
+ src="./mcp/component_with_code_connect.png"
28
+ alt="A Figma component, Badge, with the Code Connect sidebar open."
29
+ />
30
+
31
+ ### Exploring behavior
32
+
33
+ For components with Code Connect, you can also click on the "Explore behavior" button to open a modal that allows you to edit the component's props. Editing these props will automatically update the code in the modal's Code Connect section to give you a sense of how to adapt your component accordingly.
34
+
35
+ <ImageWrapper
36
+ src="./mcp/component_playground.png"
37
+ alt="The 'Component playground' modal, with the Badge component's props editable."
38
+ />
@@ -0,0 +1,89 @@
1
+ import { Meta } from '@storybook/blocks';
2
+
3
+ import { AboutHeader, Callout } from '~styleguide/blocks';
4
+
5
+ export const parameters = {
6
+ id: 'Figma MCP',
7
+ title: 'Figma MCP',
8
+ subtitle:
9
+ 'Set up the Figma MCP locally to enable design to code generation, exclusive to Codecademy + Skillsoft employees.',
10
+ status: 'static',
11
+ };
12
+
13
+ <Meta title="Meta/MCP/Figma MCP" />
14
+
15
+ <AboutHeader {...parameters} />
16
+
17
+ <Callout text="Any MCP generated code is EXPERIMENTAL! You should always validate the code generated and adapt it to your own needs — do NOT simply use the MCP to generate code and push it to production! Additionally, you must have a Dev or Full account to be able to be able to use the Figma MCP." />
18
+
19
+ The offical guidance and documentation comes from Figma. Please reference [Figma Dev Mode MCP documentation](https://help.figma.com/hc/en-us/articles/32132100833559-Guide-to-the-Dev-Mode-MCP-Server) for the most up to date information. This documentation below has been adapted to fit the context of the Gamut repository.
20
+
21
+ ## Figma (desktop client app)
22
+
23
+ Go to [Figma's download page](https://www.figma.com/downloads/) to download the appropriate version of the Figma desktop client for your OS.
24
+
25
+ ### Start up local Figma MCP server
26
+
27
+ 1. In the Figma desktop client, check that you have dev mode enabled.
28
+ 2. The right-hand sidebar should have a "MCP server" section
29
+ 3. Click on the "Open settings modal" button
30
+ 4. In the modal, toggle the status to "On", the the color of the toggle should be green.
31
+ OR
32
+ 5. Click on the Figma icon in the top left-hand corner and select "Actions"
33
+ 6. Type "Enable desktop MCP server"
34
+ 7. Click on the checkbox, if it's not already checked, to enable the MCP server.
35
+
36
+ ## Set up MCP Client for your text editor/IDE
37
+
38
+ We currently support Figma MCP for Cursor. Please let the Gamut team know if you'd like support for other editors.
39
+
40
+ ### Cursor
41
+
42
+ 1. Open **Cursor** → **Settings** → **Cursor Settings**.
43
+ 2. Go to the **MCP** tab (might be called **MCP & Integrations**) from the left-hand sidebar.
44
+ 3. Under **MCP Tools**, click **+ Add MCP server**.
45
+ 4. Enter the following configuration and save:
46
+
47
+ ```
48
+ {
49
+ "mcpServers": {
50
+ "Figma": {
51
+ "url": "http://127.0.0.1:3845/mcp"
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ ### Verify your MCP server is running
58
+
59
+ To check if your MCP server is running, you can visit the endpoint: `http://127.0.0.1:3845/mcp`. You should see a response, e.g.: `{"jsonrpc":"2.0","error":{"code":-32001,"message":"Invalid sessionId"},"id":null}`.
60
+ Yes, it's an odd response, but it indicates that the MCP server is running — otherwise if it's not running you'll see an error "This site can’t be reached" on the page.
61
+
62
+ Similarly you can test the end point using:
63
+
64
+ ```bash
65
+ curl http://127.0.0.1:3845/mcp
66
+ ```
67
+
68
+ ## Prompt your MCP client
69
+
70
+ You can prompt the MCP in two main ways:
71
+
72
+ 1. Provide a link to the node of the design you'd like to generate code for, e.g.:
73
+
74
+ > Generate the code for this node: https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=79202-13690&m=dev
75
+
76
+ 2. Selecting the node in the Figma interface, e.g.:
77
+ > Generate the code for the current selection
78
+
79
+ In the chat, you may be prompted to allow the Figma commands like `get_code()` to run. You can also add these commands to an allow list in your code editor's settings.
80
+
81
+ ## Feedback for the Gamut team
82
+
83
+ Please let the Gamut team of any feedback you have, things like:
84
+
85
+ - If you find that a new rule that you'd like implemented in this repo, please let the Gamut team know.
86
+ - Support for another text editor/IDE.
87
+ - Incorrect code generation
88
+
89
+ If you have any other feedback or suggestions, feel free to share those as well!
@@ -62,7 +62,7 @@ Use an alert to display an important, succinct message with actions for users to
62
62
  4. Dismiss button (optional)
63
63
 
64
64
  - Use to allow users to remove the alert after they've acknowledged it or when it's no longer needed
65
- - Only exclude when the information must remain visible until specific conditions are met to ensure users dont miss critical information
65
+ - Only exclude when the information must remain visible until specific conditions are met to ensure users don't miss critical information
66
66
 
67
67
  ## Variants
68
68
 
@@ -1,455 +0,0 @@
1
- import { Canvas, Controls, Meta } from '@storybook/blocks';
2
-
3
- import {
4
- Callout,
5
- ComponentHeader,
6
- ImageWrapper,
7
- LinkTo,
8
- } from '~styleguide/blocks';
9
-
10
- import * as BarChartStories from './BarChart.stories';
11
-
12
- export const parameters = {
13
- title: 'BarChart',
14
- subtitle: `A horizontal bar chart for visualizing comparative data`,
15
- design: {
16
- type: 'figma',
17
- url: 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=55123-4176',
18
- },
19
- status: 'updating',
20
- source: {
21
- repo: 'gamut',
22
- githubLink:
23
- 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/BarChart',
24
- },
25
- };
26
-
27
- <Meta of={BarChartStories} />
28
-
29
- <ComponentHeader {...parameters} />
30
-
31
- ## Usage
32
-
33
- Use BarChart to display comparative data across categories, such as skills progress, XP earned, or any quantitative metrics that benefit from visual comparison.
34
-
35
- ### Best practices:
36
-
37
- - Use consistent units across all bars in a chart
38
- - Limit the number of bars to maintain readability (5-10 is optimal)
39
- - Consider using the stacked variant to show progress toward a goal
40
- - Sort bars by value (descending) when ranking is important
41
- - Ensure bar colors maintain at least a 3:1 contrast ratio with the background
42
-
43
- ### When NOT to use:
44
-
45
- - **Progress tracking** – for displaying the completion status of a task, use the <LinkTo id="Atoms/ProgressBar">ProgressBar</LinkTo> component.
46
-
47
- ## Anatomy
48
-
49
- <ImageWrapper
50
- src="./organisms/barchart.png"
51
- alt="The anatomy of the BarChart component, detailed below."
52
- />
53
-
54
- 1. **Bar row**
55
-
56
- - Represents a single category and its values.
57
-
58
- 2. **Category icon** (optional)
59
-
60
- - Use to clarify or reinforce the category name.
61
-
62
- 3. **Category label**
63
-
64
- - Displays gridlines and labels to indicate the scale of the data values.
65
- - The number of dividers, as well as the minimum and maximum values on the value axis, are adjustable.
66
- - The minimum value should always be 0.
67
-
68
- 4. **Series one value**
69
-
70
- - Displays the numerical value of the series one bar for clarity and precise data representation.
71
-
72
- 5. **Series two value** (optional)
73
-
74
- - Displays the numerical value of the series two bar for clarity and precise data representation.
75
- - Include a second series value by using the stacked property. Stacked bar charts compare parts of a whole and are best suited for displaying discrete data segmented into meaningful categories.
76
-
77
- 6. ** Series one bar**
78
-
79
- - A visual representation of the first value for the associated category.
80
- - By default, the first series bars use the text color, however, it can be overridden to any color.
81
- - Bars include a required border that uses white or navy, chosen automatically to ensure the highest contrast with the bar color.
82
-
83
- 8. **Series two bar** _(bars with zero value)_
84
-
85
- - A visual representation of the second value for the associated category.
86
- - By default, the second series bars use the primary color, however, it can be overridden to any color.
87
- - Bars include a required border that uses white or navy, chosen automatically to ensure the highest contrast with the bar color.
88
- - Bars include a required border that uses white or navy, chosen automatically to ensure the highest contrast with the bar color.
89
-
90
- ## Variants
91
-
92
- ### Simple (Non-stacked)
93
-
94
- Use the simple variant when showing single values per category. Only `seriesOneValue` is provided.
95
-
96
- <Canvas of={BarChartStories.Default} />
97
-
98
- ### Stacked
99
-
100
- Use the stacked variant when showing progress within a total. Provide both `seriesOneValue` (progress) and `seriesTwoValue` (total).
101
-
102
- <Canvas of={BarChartStories.Stacked} />
103
-
104
- ### With Icons
105
-
106
- Add icons to labels for better visual identification of categories.
107
-
108
- <Canvas of={BarChartStories.WithIcons} />
109
-
110
- ### Animated
111
-
112
- Enable entrance animations for a more engaging experience.
113
-
114
- <Canvas of={BarChartStories.Animated} />
115
-
116
- ### Interactive
117
-
118
- Rows can be made interactive with `onClick` handlers or `href` links.
119
-
120
- <Canvas of={BarChartStories.Interactive} />
121
-
122
- ### Sorting
123
-
124
- BarChart includes an optional sorting dropdown in the title area. The dropdown only renders when the `sortFns` prop is provided. You can control which sort options appear by including string literals or custom sort functions in the array.
125
-
126
- **String Literals:**
127
-
128
- - `'alphabetically'` - Adds both "Label (A-Z)" and "Label (Z-A)" options
129
- - `'numerically'` - Adds both "Value (Low-High)" and "Value (High-Low)" options
130
- - `'none'` - Adds "None" option (preserves original order)
131
-
132
- **Custom Sort Functions:**
133
- You can also provide custom sort functions as objects with:
134
-
135
- - `label`: The text displayed in the dropdown
136
- - `value`: A unique identifier for the sort option
137
- - `sortFn`: A function that takes an array of bars and returns a sorted array
138
-
139
- **Type Definition:**
140
-
141
- ```typescript
142
- type CustomSortOption<TBar extends BarProps = BarProps> = {
143
- label: string;
144
- value: string;
145
- sortFn: (bars: TBar[]) => TBar[];
146
- };
147
-
148
- // sortFns prop accepts:
149
- sortFns?: ('alphabetically' | 'numerically' | 'none' | CustomSortOption<TBar>)[]
150
- ```
151
-
152
- **Automatic Type Inference:**
153
- TypeScript automatically infers the bar type from your `barValues` prop. This means custom properties (like `dateAdded`, `category`, etc.) are automatically typed in your sort functions - no type assertions needed!
154
-
155
- ```typescript
156
- const barsWithDates = [
157
- { yLabel: 'Python', seriesOneValue: 1500, dateAdded: new Date('2023-01-15') },
158
- {
159
- yLabel: 'JavaScript',
160
- seriesOneValue: 2000,
161
- dateAdded: new Date('2023-03-20'),
162
- },
163
- // ...
164
- ];
165
-
166
- <BarChart
167
- barValues={barsWithDates}
168
- sortFns={[
169
- {
170
- label: 'Recently Added',
171
- value: 'recent',
172
- sortFn: (bars) => {
173
- // TypeScript automatically knows bars have dateAdded property!
174
- return [...bars].sort((a, b) => {
175
- const aDate = a.dateAdded as Date | undefined;
176
- const bDate = b.dateAdded as Date | undefined;
177
- if (!aDate || !bDate) return 0;
178
- return bDate.getTime() - aDate.getTime();
179
- });
180
- },
181
- },
182
- ]}
183
- />;
184
- ```
185
-
186
- **Note:** Since `BarProps` uses `Record<string, unknown>` for extensibility, custom properties are typed as `unknown` by default. You may need a simple type assertion (`as Date | undefined`) when accessing them, but the property names are fully type-checked.
187
-
188
- <Canvas of={BarChartStories.WithSorting} />
189
-
190
- **Example with Custom Sort Functions:**
191
-
192
- Custom sort functions can access additional properties on `BarProps` for domain-specific sorting, such as sorting by dates or categories.
193
-
194
- <Canvas of={BarChartStories.WithCustomSorting} />
195
-
196
- When a bar has an `onClick` handler or `href` link, an `aria-label` is automatically generated from the bar's data and applied to the button or link element. The label summarizes the bar's values (e.g., "100 XP in Python" for a simple bar, or "200 XP gained - now at 1500 XP in Python" for a stacked bar).
197
-
198
- This ensures that screen reader users receive a clear, data-driven description when interacting with bars, without requiring manual `aria-label` props.
199
-
200
- <Canvas of={BarChartStories.WithLinks} />
201
-
202
- ## Interactive states
203
-
204
- BarChart supports both interactive and static bar rows, each with distinct visual states.
205
-
206
- ### Static bars
207
-
208
- When bars have neither `onClick` handlers nor `href` links, they are rendered as static, non-interactive elements.
209
-
210
- <Canvas of={BarChartStories.Default} />
211
-
212
- ### Interactive bars
213
-
214
- When bars have `onClick` handlers or `href` links, they become interactive. Interactive bars are rendered as buttons or anchor elements, providing full keyboard accessibility and proper semantic HTML.
215
-
216
- <Canvas of={BarChartStories.Interactive} />
217
-
218
- ## Color modes
219
-
220
- BarChart automatically adapts to both light and dark color modes, ensuring proper contrast and readability in all themes. The component uses semantic color tokens that adjust based on the current color mode. The `styleConfig` prop should be provided by semantic color tokens to ensure the `BarChart` is accessible in all color modes.
221
-
222
- ## Custom styling
223
-
224
- BarChart supports custom color styling through the `styleConfig` prop, allowing you to customize the appearance of chart elements to match your design needs.
225
-
226
- ### Style configuration options
227
-
228
- The `styleConfig` prop accepts an object with the following optional properties:
229
-
230
- - **`textColor`**: Color for text labels (y-axis labels). Defaults to `'text'`
231
- - **`foregroundBarColor`**: Color for the foreground/progress bar (used in stacked charts for `seriesOneValue`). Defaults to `'feedback-warning'`
232
- - **`backgroundBarColor`**: Color for the background/total bar (used for `seriesTwoValue` in stacked charts, or `seriesOneValue` in simple charts). Defaults to `'background-primary'`
233
- - **`seriesOneLabel`**: Color for the series one value label (first right label when stacked, or displayValue when not stacked). Defaults to `'text-secondary'`
234
- - **`seriesTwoLabel`**: Color for the series two value label (displayValue when stacked). Defaults to `'primary'`
235
-
236
- All color values should be valid color tokens from the Gamut design system. When customizing colors, ensure they maintain at least a 3:1 contrast ratio with the background for accessibility.
237
-
238
- ### Default values
239
-
240
- If `styleConfig` is not provided, BarChart uses these default colors:
241
-
242
- ```typescript
243
- {
244
- textColor: 'text',
245
- foregroundBarColor: 'feedback-warning',
246
- backgroundBarColor: 'background-primary',
247
- seriesOneLabel: 'text-secondary',
248
- seriesTwoLabel: 'primary',
249
- }
250
- ```
251
-
252
- ### Example
253
-
254
- <Canvas of={BarChartStories.CustomStyles} />
255
-
256
- ## Custom scale
257
-
258
- BarChart allows you to customize the x-axis scale interval using the `xScale` prop, which controls the spacing and granularity of scale markers along the horizontal axis.
259
-
260
- ### Scale configuration
261
-
262
- The `xScale` prop determines the interval between scale markers on the x-axis. By default, BarChart automatically calculates an appropriate scale interval based on the `minRange` and `maxRange` values. However, you can override this with a custom interval to achieve more granular or specific scale markings.
263
-
264
- **When to use custom scale:**
265
-
266
- - When you need specific scale intervals (e.g., increments of 250, 500, or 1000)
267
- - When the automatic scale calculation doesn't provide the desired granularity
268
- - When you want to match scale intervals across multiple charts for consistency
269
- - When you need scale markers at specific values for better data interpretation
270
-
271
- **How it works:**
272
-
273
- The `xScale` value represents the interval between consecutive scale markers. For example:
274
-
275
- - `xScale: 250` creates markers at 0, 250, 500, 750, 1000, etc.
276
- - `xScale: 500` creates markers at 0, 500, 1000, 1500, 2000, etc.
277
-
278
- The number of tick marks is automatically calculated as: `Math.ceil((maxRange - minRange) / xScale) + 1`
279
-
280
- ### Example
281
-
282
- <Canvas of={BarChartStories.CustomScale} />
283
-
284
- ## Accessibility considerations
285
-
286
- ### Chart and row labeling
287
-
288
- **Chart-level labeling:**
289
-
290
- - Always provide either `title` or `aria-labelledby` to describe the chart (see Title and description section below for details)
291
- - The chart is wrapped in a semantic `<figure>` element for proper structure
292
-
293
- **Row-level labeling:**
294
-
295
- - For non-interactive bars: A hidden text element (visible only to screen readers) is automatically added as the first child of each list item, summarizing the bar's values (e.g., "100 XP in Python")
296
- - For interactive bars (with `onClick` or `href`): An `aria-label` is automatically generated from the bar's data and applied to the button or link element, ensuring proper screen reader announcements
297
- - All accessibility labels are automatically generated from the bar's data - no manual `aria-label` props are required
298
-
299
- ## UX writing
300
-
301
- - Keep y-axis labels concise (1-3 words)
302
- - Use consistent unit labels (e.g., "XP", "hours", "points")
303
- - Consider locale-aware number formatting for international audiences
304
-
305
- ### Title and description
306
-
307
- The BarChart component uses semantic HTML to provide context and accessibility. The chart is wrapped in a [`<figure>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure) element, and the description is displayed in a [`<figcaption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption) element. This semantic structure helps screen readers and other assistive technologies understand the relationship between the chart and its description. **Visual title + description is the greatly preferred pattern** for accessibility and user experience.
308
-
309
- **Preferred pattern: Visual title + description**
310
-
311
- Provide both `title` and `description` props to make the chart's purpose and key takeaways clear to all users:
312
-
313
- - `title`: A heading that identifies the chart. Can be a string or an object with `{ as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6', title: string, variant?: Text['variant'] }` to specify the heading level and optionally override the default styling with a <LinkTo id="Typography/Text">Text variant</LinkTo>.
314
- - `description`: A summary of the information or the overall takeaway displayed in the `<figcaption>` element.
315
-
316
- <Canvas of={BarChartStories.WithVisualTitleAndDescription} />
317
-
318
- **Alternative patterns:**
319
- `hideTitle` and `hideDescription` will keep the screenreader text but hide the visual descriptions.
320
-
321
- <Canvas of={BarChartStories.WithHiddenTitleAndDescription} />
322
-
323
- - **External title with visual description**: Use `aria-labelledby` for the title but show the description visually when the title exists elsewhere but you want to display the description.
324
-
325
- <Canvas of={BarChartStories.WithExternalTitle} />
326
-
327
- **Best practices:**
328
-
329
- - Always provide a `description` prop - it's required and should summarize the chart's key information
330
- - Use semantic heading levels for titles (h1-h6) to maintain proper document structure
331
- - When using `aria-labelledby`, ensure the referenced element exists and is properly labeled
332
-
333
- ## Playground
334
-
335
- <Canvas sourceState="shown" of={BarChartStories.Default} />
336
-
337
- <Controls />
338
-
339
- ## Internationalization
340
-
341
- <Callout text="We are currently updating global internationalization in Gamut, so the following features are subject to change." />
342
-
343
- BarChart supports internationalization through the `translations` prop, which allows you to customize all user-facing strings and configure locale-aware number formatting.
344
-
345
- **Translation Structure:**
346
-
347
- ```typescript
348
- type BarChartTranslations = {
349
- sortLabel: string;
350
- sortOptions: {
351
- none: string;
352
- labelAsc: string;
353
- labelDesc: string;
354
- valueAsc: string;
355
- valueDesc: string;
356
- };
357
- accessibility: {
358
- gainedNowAt: string; // Template: "{value} {unit} gained - now at {value} {unit} in {label}"
359
- inLabel: string; // Template: "{value} {unit} in {label}"
360
- inOnly: string; // Template: "{value} {unit} in "
361
- };
362
- locale: string; // For Intl.NumberFormat, e.g., 'en', 'es', 'fr', 'de-DE'
363
- };
364
- ```
365
-
366
- **Basic Usage:**
367
-
368
- The `translations` prop is optional and accepts partial translations that will be merged with the default English translations. This means you only need to provide the translations you want to override.
369
-
370
- ```tsx
371
- <BarChart
372
- title="Habilidades"
373
- barValues={barData}
374
- description="Progreso de habilidades"
375
- minRange={0}
376
- maxRange={200}
377
- sortFns={['alphabetically', 'none']}
378
- translations={{
379
- sortLabel: 'Ordenar por:',
380
- sortOptions: {
381
- none: 'Ninguno',
382
- labelAsc: 'Etiqueta (A-Z)',
383
- labelDesc: 'Etiqueta (Z-A)',
384
- valueAsc: 'Valor (Bajo-Alto)',
385
- valueDesc: 'Valor (Alto-Bajo)',
386
- },
387
- locale: 'es',
388
- }}
389
- />
390
- ```
391
-
392
- **Partial Translations:**
393
-
394
- You can provide only the translations you need to customize. All other strings will use the default English values:
395
-
396
- ```tsx
397
- <BarChart
398
- // ... other props
399
- translations={{
400
- sortLabel: 'Sort by:',
401
- // sortOptions will use default English values
402
- locale: 'en-GB', // Use British English number formatting
403
- }}
404
- />
405
- ```
406
-
407
- **Locale-Aware Number Formatting:**
408
-
409
- The `locale` property controls how numbers are formatted throughout the chart using `Intl.NumberFormat`. This affects:
410
-
411
- - Scale labels on the x-axis
412
- - Value labels on bars
413
- - All numeric displays
414
-
415
- ```tsx
416
- // German locale (1.000 instead of 1,000)
417
- <BarChart
418
- // ... other props
419
- translations={{
420
- locale: 'de-DE',
421
- }}
422
- />
423
-
424
- // French locale
425
- <BarChart
426
- // ... other props
427
- translations={{
428
- locale: 'fr-FR',
429
- }}
430
- />
431
- ```
432
-
433
- **Accessibility Translations:**
434
-
435
- The accessibility strings are used in automatically generated `aria-label` attributes for interactive bars and screen reader text for non-interactive bars. These should be translated to match the language of your application:
436
-
437
- ```tsx
438
- <BarChart
439
- // ... other props
440
- translations={{
441
- accessibility: {
442
- gainedNowAt: 'ganado - ahora en', // Spanish: "gained - now at"
443
- inLabel: 'en', // Spanish: "in"
444
- inOnly: 'en ', // Spanish: "in "
445
- },
446
- }}
447
- />
448
- ```
449
-
450
- **Best Practices:**
451
-
452
- - Always provide complete translations for the language your application uses
453
- - Use appropriate locale codes (e.g., `'en-US'`, `'es-ES'`, `'fr-FR'`) for number formatting
454
- - Test accessibility strings with screen readers in the target language
455
- - Consider creating translation objects that can be reused across multiple BarChart instances
@@ -1,296 +0,0 @@
1
- import { BarChart, BarProps, Box } from '@codecademy/gamut';
2
- import {
3
- BookFlipPageIcon,
4
- DataScienceIcon,
5
- TerminalIcon,
6
- } from '@codecademy/gamut-icons';
7
- import { action } from '@storybook/addon-actions';
8
- import type { Meta, StoryObj } from '@storybook/react';
9
-
10
- const meta: Meta<typeof BarChart> = {
11
- component: BarChart,
12
- args: {
13
- title: 'Skills experience chart',
14
- description: 'Chart showing programming language experience levels',
15
- minRange: 0,
16
- maxRange: 2000,
17
- unit: 'XP',
18
- },
19
- };
20
-
21
- export default meta;
22
- type Story = StoryObj<typeof BarChart>;
23
-
24
- const simpleBarData: BarProps[] = [
25
- { yLabel: 'Python', seriesOneValue: 1500 },
26
- { yLabel: 'JavaScript', seriesOneValue: 2000 },
27
- { yLabel: 'HTML/CSS', seriesOneValue: 800 },
28
- { yLabel: 'SQL', seriesOneValue: 600 },
29
- { yLabel: 'React', seriesOneValue: 450 },
30
- ];
31
-
32
- const stackedBarData: BarProps[] = [
33
- { yLabel: 'Python', seriesOneValue: 200, seriesTwoValue: 1500 },
34
- {
35
- yLabel: 'JavaScript',
36
- icon: TerminalIcon,
37
- seriesOneValue: 1800,
38
- seriesTwoValue: 2000,
39
- },
40
- { yLabel: 'HTML/CSS', seriesOneValue: 600, seriesTwoValue: 800 },
41
- { yLabel: 'SQL', seriesOneValue: 550, seriesTwoValue: 600 },
42
- { yLabel: 'React', seriesOneValue: 300, seriesTwoValue: 450 },
43
- ];
44
-
45
- const barDataWithIcons: BarProps[] = [
46
- {
47
- yLabel: 'Python',
48
- seriesOneValue: 200,
49
- seriesTwoValue: 1500,
50
- icon: TerminalIcon,
51
- },
52
- {
53
- yLabel: 'JavaScript',
54
- seriesOneValue: 150,
55
- seriesTwoValue: 2000,
56
- icon: TerminalIcon,
57
- },
58
- {
59
- yLabel: 'Data Science',
60
- seriesOneValue: 100,
61
- seriesTwoValue: 800,
62
- icon: DataScienceIcon,
63
- },
64
- {
65
- yLabel: 'Backend',
66
- seriesOneValue: 50,
67
- seriesTwoValue: 600,
68
- icon: TerminalIcon,
69
- },
70
- {
71
- yLabel: 'Reading',
72
- seriesOneValue: 75,
73
- seriesTwoValue: 450,
74
- icon: BookFlipPageIcon,
75
- },
76
- ];
77
-
78
- export const Default: Story = {
79
- args: {
80
- barValues: simpleBarData,
81
- title: 'Skills experience chart',
82
- description: 'Chart showing programming language experience levels',
83
- },
84
- };
85
-
86
- export const Stacked: Story = {
87
- args: {
88
- barValues: stackedBarData,
89
- title: 'Skills progress chart',
90
- description: 'Progress toward total goals for each programming language',
91
- },
92
- };
93
-
94
- export const WithIcons: Story = {
95
- args: {
96
- barValues: barDataWithIcons,
97
- title: 'Skills progress with icons',
98
- description: 'Skills progress with visual icons for each category',
99
- },
100
- };
101
-
102
- export const Animated: Story = {
103
- args: {
104
- barValues: stackedBarData,
105
- animate: true,
106
- title: 'Animated skills chart',
107
- description: 'Animated chart showing progress with entrance animations',
108
- },
109
- };
110
-
111
- export const Interactive: Story = {
112
- args: {
113
- barValues: simpleBarData.map((bar) => ({
114
- ...bar,
115
- onClick: action(`Clicked ${bar.yLabel}`),
116
- })),
117
- title: 'Interactive skills chart',
118
- description: 'Click on any row to view detailed course information',
119
- },
120
- };
121
-
122
- export const WithLinks: Story = {
123
- args: {
124
- barValues: simpleBarData.map((bar) => ({
125
- ...bar,
126
- href: `#${bar.yLabel.toLowerCase().replace(/\s+/g, '-')}`,
127
- })),
128
- title: 'Skills chart with links',
129
- description: 'Each row links to its corresponding course page',
130
- },
131
- };
132
-
133
- export const WithVisualTitleAndDescription: Story = {
134
- args: {
135
- barValues: simpleBarData,
136
- title: 'Programming Skills Overview',
137
- description:
138
- 'Experience points earned across different programming languages',
139
- },
140
- };
141
-
142
- export const WithHiddenTitleAndDescription: Story = {
143
- render: () => {
144
- return (
145
- <BarChart
146
- barValues={simpleBarData}
147
- description="Experience points earned across different programming languages"
148
- hideDescription
149
- hideTitle
150
- maxRange={2000}
151
- minRange={0}
152
- title="Programming Skills Overview"
153
- unit="XP"
154
- />
155
- );
156
- },
157
- };
158
-
159
- export const WithExternalTitle: Story = {
160
- render: () => {
161
- return (
162
- <>
163
- <Box
164
- as="h2"
165
- bg="paleBlue"
166
- border={1}
167
- borderRadius="lg"
168
- id="external-chart-title"
169
- p={16}
170
- textAlign="right"
171
- >
172
- Programming Skills Overview
173
- </Box>
174
- <BarChart
175
- aria-labelledby="external-title"
176
- barValues={simpleBarData}
177
- description="Experience points earned across different programming languages"
178
- hideDescription={false}
179
- maxRange={2000}
180
- minRange={0}
181
- unit="XP"
182
- />
183
- </>
184
- );
185
- },
186
- };
187
-
188
- export const WithSorting: Story = {
189
- args: {
190
- barValues: simpleBarData,
191
- sortFns: ['alphabetically', 'numerically', 'none'],
192
- title: 'Skills experience chart',
193
- description: 'Use the dropdown to sort bars by different criteria',
194
- },
195
- };
196
-
197
- const customSortingBarValues = [
198
- {
199
- yLabel: 'Python',
200
- seriesOneValue: 1500,
201
- dateAdded: new Date('2023-01-15'),
202
- },
203
- {
204
- yLabel: 'JavaScript',
205
- seriesOneValue: 2000,
206
- dateAdded: new Date('2023-03-20'),
207
- },
208
- {
209
- yLabel: 'React',
210
- seriesOneValue: 450,
211
- dateAdded: new Date('2023-06-10'),
212
- },
213
- {
214
- yLabel: 'TypeScript',
215
- seriesOneValue: 300,
216
- dateAdded: new Date('2023-08-05'),
217
- },
218
- {
219
- yLabel: 'SQL',
220
- seriesOneValue: 600,
221
- dateAdded: new Date('2023-02-28'),
222
- },
223
- ];
224
-
225
- export const WithCustomSorting: Story = {
226
- args: {
227
- barValues: customSortingBarValues,
228
- sortFns: [
229
- 'none',
230
- {
231
- label: 'Recently Added',
232
- value: 'recent',
233
- sortFn: (bars) => {
234
- return [...bars].sort((a, b) => {
235
- // TypeScript infers the type from barValues, so dateAdded is properly typed
236
- const aDate = a.dateAdded as Date | undefined;
237
- const bDate = b.dateAdded as Date | undefined;
238
- if (!aDate && !bDate) return 0;
239
- if (!aDate) return 1;
240
- if (!bDate) return -1;
241
- return bDate.getTime() - aDate.getTime();
242
- });
243
- },
244
- },
245
- {
246
- label: 'Oldest First',
247
- value: 'oldest',
248
- sortFn: (bars) => {
249
- return [...bars].sort((a, b) => {
250
- // TypeScript infers the type from barValues, so dateAdded is properly typed
251
- const aDate = a.dateAdded as Date | undefined;
252
- const bDate = b.dateAdded as Date | undefined;
253
- if (!aDate && !bDate) return 0;
254
- if (!aDate) return 1;
255
- if (!bDate) return -1;
256
- return aDate.getTime() - bDate.getTime();
257
- });
258
- },
259
- },
260
- ],
261
- title: 'Skills chart with date sorting',
262
- description:
263
- 'Custom sort functions can access additional properties on BarProps, such as dates',
264
- },
265
- };
266
-
267
- /**
268
- * Bar chart with custom styling
269
- */
270
- export const CustomStyles: Story = {
271
- args: {
272
- barValues: stackedBarData,
273
- styleConfig: {
274
- backgroundBarColor: 'text',
275
- foregroundBarColor: 'primary',
276
- textColor: 'primary',
277
- seriesOneLabel: 'feedback-error',
278
- seriesTwoLabel: 'feedback-success',
279
- },
280
- title: 'Custom styled skills chart',
281
- description: 'Custom color scheme applied to chart elements',
282
- },
283
- };
284
-
285
- /**
286
- * Bar chart with custom xScale interval
287
- */
288
- export const CustomScale: Story = {
289
- args: {
290
- barValues: simpleBarData,
291
- maxRange: 2000,
292
- xScale: 250,
293
- title: 'Skills chart with custom scale',
294
- description: 'Custom scale intervals for more granular value display',
295
- },
296
- };