@codecademy/styleguide 78.5.5-alpha.86d3e6.0 → 78.5.5-alpha.c15718.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/package.json +2 -2
- package/src/lib/Molecules/Coachmark/Coachmark.mdx +0 -6
- package/src/lib/Molecules/Coachmark/Coachmark.stories.tsx +1 -37
- package/src/lib/Molecules/Modals/Modal/Modal.mdx +0 -10
- package/src/lib/Molecules/Modals/Overlay/Overlay.mdx +0 -6
- package/src/lib/Molecules/Modals/Overlay/Overlay.stories.tsx +2 -67
- package/src/lib/Molecules/Tips/InfoTip/InfoTip.mdx +2 -17
- package/src/lib/Molecules/Tips/InfoTip/InfoTip.stories.tsx +10 -73
- package/src/lib/Molecules/Tips/PreviewTip/PreviewTip.mdx +0 -6
- package/src/lib/Molecules/Tips/PreviewTip/PreviewTip.stories.tsx +1 -68
- package/src/lib/Molecules/Tips/ToolTip/ToolTip.mdx +0 -6
- package/src/lib/Molecules/Tips/ToolTip/ToolTip.stories.tsx +0 -59
- package/src/lib/Organisms/BarChart/BarChart.mdx +92 -0
- package/src/lib/Organisms/BarChart/BarChart.stories.tsx +183 -0
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
|
-
### [78.5.5-alpha.
|
|
6
|
+
### [78.5.5-alpha.c15718.0](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@78.5.4...@codecademy/styleguide@78.5.5-alpha.c15718.0) (2026-01-07)
|
|
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": "78.5.5-alpha.
|
|
4
|
+
"version": "78.5.5-alpha.c15718.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": "
|
|
11
|
+
"gitHead": "558182a086c9a842a64bb7881a8fcb0e790a4f36"
|
|
12
12
|
}
|
|
@@ -94,12 +94,6 @@ Using `popoverProps`, you can customize the look of the rendered popover. For ex
|
|
|
94
94
|
|
|
95
95
|
<Canvas of={CoachmarkStories.Customized} />
|
|
96
96
|
|
|
97
|
-
### Z-Index
|
|
98
|
-
|
|
99
|
-
You can customize the `zIndex` of the Coachmark's popover by passing `zIndex` through `popoverProps`. This is useful when the popover needs to appear above other positioned elements.
|
|
100
|
-
|
|
101
|
-
<Canvas of={CoachmarkStories.ZIndex} />
|
|
102
|
-
|
|
103
97
|
## Playground
|
|
104
98
|
|
|
105
99
|
<Canvas sourceState="shown" of={CoachmarkStories.Default} />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Coachmark, FillButton, FlexBox, Text } from '@codecademy/gamut';
|
|
2
2
|
import * as patterns from '@codecademy/gamut-patterns';
|
|
3
3
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
4
|
import { ComponentProps, useEffect, useState } from 'react';
|
|
@@ -197,39 +197,3 @@ export const Customized: Story = {
|
|
|
197
197
|
);
|
|
198
198
|
},
|
|
199
199
|
};
|
|
200
|
-
|
|
201
|
-
export const ZIndex: Story = {
|
|
202
|
-
args: {
|
|
203
|
-
popoverProps: {
|
|
204
|
-
position: 'below',
|
|
205
|
-
zIndex: 5,
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
render: function ZIndexExample(args) {
|
|
209
|
-
const [shouldShow, setShouldShow] = useState(true);
|
|
210
|
-
|
|
211
|
-
return (
|
|
212
|
-
<FlexBox flexDirection="column" gap={16}>
|
|
213
|
-
<Box bg="paleBlue" p={16} zIndex={3} position="relative">
|
|
214
|
-
Element with z-index: 3
|
|
215
|
-
</Box>
|
|
216
|
-
<Coachmark
|
|
217
|
-
{...args}
|
|
218
|
-
renderPopover={() => (
|
|
219
|
-
<FlexBox alignItems="flex-start" flexDirection="column" p={16}>
|
|
220
|
-
<Text mb={8}>This coachmark has z-index: 5 via popoverProps</Text>
|
|
221
|
-
<FillButton size="small" onClick={() => setShouldShow(false)}>
|
|
222
|
-
Got it
|
|
223
|
-
</FillButton>
|
|
224
|
-
</FlexBox>
|
|
225
|
-
)}
|
|
226
|
-
shouldShow={shouldShow}
|
|
227
|
-
>
|
|
228
|
-
<FillButton onClick={() => setShouldShow(true)}>
|
|
229
|
-
Show Coachmark
|
|
230
|
-
</FillButton>
|
|
231
|
-
</Coachmark>
|
|
232
|
-
</FlexBox>
|
|
233
|
-
);
|
|
234
|
-
},
|
|
235
|
-
};
|
|
@@ -89,16 +89,6 @@ A Modal can be made scrollable by including large content inside.
|
|
|
89
89
|
|
|
90
90
|
<Canvas of={ModalStories.Scrollable} />
|
|
91
91
|
|
|
92
|
-
## Z-Index
|
|
93
|
-
|
|
94
|
-
Modal accepts a `zIndex` prop (defaults to `3`) that controls the stacking order of its underlying Overlay. Use this when the modal needs to appear above other positioned elements like sticky headers or custom floating UI.
|
|
95
|
-
|
|
96
|
-
```tsx
|
|
97
|
-
<Modal zIndex={10} isOpen={isOpen} onRequestClose={handleClose}>
|
|
98
|
-
Content that needs to appear above other positioned elements
|
|
99
|
-
</Modal>
|
|
100
|
-
```
|
|
101
|
-
|
|
102
92
|
## Focus management
|
|
103
93
|
|
|
104
94
|
The `containerFocusRef` prop allows you to programmatically control focus on the Modal container. This is useful for advanced focus management scenarios where you need to override the default focus behavior (the Modal has `data-autofocus` by default).
|
|
@@ -32,12 +32,6 @@ Unlike the legacy `Modal` implementations in the monolith, this:
|
|
|
32
32
|
|
|
33
33
|
- If you need styles such as a background behind content, see `Modal` for general modals and `Dialog` for confirmation flows.
|
|
34
34
|
|
|
35
|
-
## Z-Index
|
|
36
|
-
|
|
37
|
-
The `zIndex` prop controls the stacking order of the overlay. It defaults to `3`, which places it above most common UI elements. Increase this value when the overlay needs to appear above other positioned elements like sticky headers or floating UI.
|
|
38
|
-
|
|
39
|
-
<Canvas of={OverlayStories.ZIndex} />
|
|
40
|
-
|
|
41
35
|
## Playground
|
|
42
36
|
|
|
43
37
|
<Canvas sourceState="shown" of={OverlayStories.Default} />
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { Meta
|
|
1
|
+
import { FillButton, FlexBox, Overlay, Text } from '@codecademy/gamut';
|
|
2
|
+
import type { Meta } from '@storybook/react';
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof Overlay> = {
|
|
@@ -8,7 +8,6 @@ const meta: Meta<typeof Overlay> = {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
export default meta;
|
|
11
|
-
type Story = StoryObj<typeof Overlay>;
|
|
12
11
|
|
|
13
12
|
export const Default: React.FC<React.ComponentProps<typeof Overlay>> = (
|
|
14
13
|
args
|
|
@@ -35,67 +34,3 @@ export const Default: React.FC<React.ComponentProps<typeof Overlay>> = (
|
|
|
35
34
|
</>
|
|
36
35
|
);
|
|
37
36
|
};
|
|
38
|
-
|
|
39
|
-
export const ZIndex: Story = {
|
|
40
|
-
render: function ZIndexExample() {
|
|
41
|
-
const [defaultOpen, setDefaultOpen] = useState(false);
|
|
42
|
-
const [customOpen, setCustomOpen] = useState(false);
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<FlexBox flexDirection="column" gap={16}>
|
|
46
|
-
<FlexBox gap={16}>
|
|
47
|
-
<FillButton onClick={() => setDefaultOpen(true)}>
|
|
48
|
-
Open Overlay (default z-index: 3)
|
|
49
|
-
</FillButton>
|
|
50
|
-
<FillButton onClick={() => setCustomOpen(true)}>
|
|
51
|
-
Open Overlay (z-index: 10)
|
|
52
|
-
</FillButton>
|
|
53
|
-
</FlexBox>
|
|
54
|
-
|
|
55
|
-
<Box position="relative" height="100px">
|
|
56
|
-
<Box
|
|
57
|
-
position="absolute"
|
|
58
|
-
top={0}
|
|
59
|
-
left={0}
|
|
60
|
-
bg="paleYellow"
|
|
61
|
-
p={16}
|
|
62
|
-
zIndex={5}
|
|
63
|
-
>
|
|
64
|
-
<Text>Element with z-index: 5</Text>
|
|
65
|
-
</Box>
|
|
66
|
-
</Box>
|
|
67
|
-
|
|
68
|
-
<Overlay
|
|
69
|
-
isOpen={defaultOpen}
|
|
70
|
-
onRequestClose={() => setDefaultOpen(false)}
|
|
71
|
-
shroud
|
|
72
|
-
>
|
|
73
|
-
<FlexBox bg="white" p={24} borderRadius="md" flexDirection="column" gap={16}>
|
|
74
|
-
<Text>
|
|
75
|
-
This overlay uses the default z-index of 3.
|
|
76
|
-
<br />
|
|
77
|
-
It may appear behind elements with higher z-index values.
|
|
78
|
-
</Text>
|
|
79
|
-
<FillButton onClick={() => setDefaultOpen(false)}>Close</FillButton>
|
|
80
|
-
</FlexBox>
|
|
81
|
-
</Overlay>
|
|
82
|
-
|
|
83
|
-
<Overlay
|
|
84
|
-
isOpen={customOpen}
|
|
85
|
-
onRequestClose={() => setCustomOpen(false)}
|
|
86
|
-
shroud
|
|
87
|
-
zIndex={10}
|
|
88
|
-
>
|
|
89
|
-
<FlexBox bg="white" p={24} borderRadius="md" flexDirection="column" gap={16}>
|
|
90
|
-
<Text>
|
|
91
|
-
This overlay uses z-index: 10.
|
|
92
|
-
<br />
|
|
93
|
-
It will appear above the yellow element with z-index: 5.
|
|
94
|
-
</Text>
|
|
95
|
-
<FillButton onClick={() => setCustomOpen(false)}>Close</FillButton>
|
|
96
|
-
</FlexBox>
|
|
97
|
-
</Overlay>
|
|
98
|
-
</FlexBox>
|
|
99
|
-
);
|
|
100
|
-
},
|
|
101
|
-
};
|
|
@@ -82,24 +82,9 @@ InfoTips have intelligent Escape key handling that works correctly both inside a
|
|
|
82
82
|
|
|
83
83
|
<Canvas of={InfoTipStories.InfoTipInsideModal} />
|
|
84
84
|
|
|
85
|
-
##
|
|
85
|
+
## InfoTips and zIndex
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
The InfoTip button's accessible label can be customized using either prop:
|
|
90
|
-
|
|
91
|
-
- **`ariaLabel`**: Directly sets the accessible label text. Useful when you want to provide a custom label without referencing another element.
|
|
92
|
-
- **`ariaLabelledby`**: References the ID of another element to use as the label. Useful when you want the InfoTip button to be labeled by visible text elsewhere on the page. This is useful for when the `InfoTip` is beside text that contextualizes it.
|
|
93
|
-
|
|
94
|
-
### Custom Role Description
|
|
95
|
-
|
|
96
|
-
The `InfoTipButton` uses [`aria-roledescription="More information button"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-roledescription) to provide additional context to screen reader users about the button's specific purpose.
|
|
97
|
-
|
|
98
|
-
<Canvas of={InfoTipStories.AriaLabel} />
|
|
99
|
-
|
|
100
|
-
## Z-Index
|
|
101
|
-
|
|
102
|
-
You can customize the `zIndex` of the InfoTip's portal with the `zIndex` prop. This works for both `inline` and `floating` placements, and is useful when the tip needs to appear above other positioned elements.
|
|
87
|
+
You can change the zIndex of your `InfoTip` with the zIndex property.
|
|
103
88
|
|
|
104
89
|
<Canvas of={InfoTipStories.ZIndex} />
|
|
105
90
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Anchor,
|
|
3
3
|
Box,
|
|
4
|
-
Coachmark,
|
|
5
4
|
FillButton,
|
|
6
5
|
FlexBox,
|
|
7
6
|
GridBox,
|
|
@@ -196,7 +195,6 @@ export const InfoTipInsideModal: Story = {
|
|
|
196
195
|
},
|
|
197
196
|
render: function InfoTipInsideModal(args) {
|
|
198
197
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
199
|
-
const [showCoachmark, setShowCoachmark] = useState(false);
|
|
200
198
|
|
|
201
199
|
return (
|
|
202
200
|
<FlexBox center flexDirection="column" gap={16} py={64}>
|
|
@@ -235,37 +233,6 @@ export const InfoTipInsideModal: Story = {
|
|
|
235
233
|
closing the modal itself. Inline placement works correctly.
|
|
236
234
|
</Text>
|
|
237
235
|
|
|
238
|
-
<FlexBox alignItems="center" borderTop={1} gap={8} mt={8} pt={16}>
|
|
239
|
-
<Text id="modal-coachmark-text">
|
|
240
|
-
This modal also contains a Coachmark
|
|
241
|
-
</Text>
|
|
242
|
-
<Coachmark
|
|
243
|
-
popoverProps={{ zIndex: 3 }}
|
|
244
|
-
renderPopover={() => (
|
|
245
|
-
<FlexBox
|
|
246
|
-
alignItems="flex-start"
|
|
247
|
-
flexDirection="column"
|
|
248
|
-
p={16}
|
|
249
|
-
>
|
|
250
|
-
<Text mb={8}>
|
|
251
|
-
This Coachmark is inside a Modal. Try pressing Escape!
|
|
252
|
-
</Text>
|
|
253
|
-
<FillButton
|
|
254
|
-
size="small"
|
|
255
|
-
onClick={() => setShowCoachmark(false)}
|
|
256
|
-
>
|
|
257
|
-
Got it
|
|
258
|
-
</FillButton>
|
|
259
|
-
</FlexBox>
|
|
260
|
-
)}
|
|
261
|
-
shouldShow={showCoachmark}
|
|
262
|
-
>
|
|
263
|
-
<FillButton size="small" onClick={() => setShowCoachmark(true)}>
|
|
264
|
-
Show Coachmark
|
|
265
|
-
</FillButton>
|
|
266
|
-
</Coachmark>
|
|
267
|
-
</FlexBox>
|
|
268
|
-
|
|
269
236
|
<FillButton onClick={() => setIsModalOpen(false)}>
|
|
270
237
|
Close Modal
|
|
271
238
|
</FillButton>
|
|
@@ -278,49 +245,19 @@ export const InfoTipInsideModal: Story = {
|
|
|
278
245
|
|
|
279
246
|
export const ZIndex: Story = {
|
|
280
247
|
args: {
|
|
281
|
-
info: 'I
|
|
248
|
+
info: 'I am inline, cool',
|
|
282
249
|
zIndex: 5,
|
|
283
250
|
},
|
|
284
251
|
render: (args) => (
|
|
285
|
-
<FlexBox center flexDirection="column"
|
|
286
|
-
<
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
</Box>
|
|
295
|
-
<InfoTip
|
|
296
|
-
{...args}
|
|
297
|
-
ariaLabel="inline custom"
|
|
298
|
-
info="z-index: 5 (inline)"
|
|
299
|
-
/>
|
|
300
|
-
</FlexBox>
|
|
301
|
-
|
|
302
|
-
<Text variant="p-small" mt={24}>
|
|
303
|
-
Floating placement:
|
|
304
|
-
</Text>
|
|
305
|
-
<FlexBox alignItems="center" gap={8}>
|
|
306
|
-
<Box bg="paleGreen" p={8} zIndex={3} position="relative">
|
|
307
|
-
z-index: 3
|
|
308
|
-
</Box>
|
|
309
|
-
<InfoTip
|
|
310
|
-
ariaLabel="floating default"
|
|
311
|
-
info="Default z-index (floating)"
|
|
312
|
-
placement="floating"
|
|
313
|
-
/>
|
|
314
|
-
<Box bg="paleGreen" p={8} zIndex={3} position="relative">
|
|
315
|
-
z-index: 3
|
|
316
|
-
</Box>
|
|
317
|
-
<InfoTip
|
|
318
|
-
{...args}
|
|
319
|
-
ariaLabel="floating custom"
|
|
320
|
-
info="z-index: 5 (floating)"
|
|
321
|
-
placement="floating"
|
|
322
|
-
/>
|
|
323
|
-
</FlexBox>
|
|
252
|
+
<FlexBox center flexDirection="column" m={24} py={64}>
|
|
253
|
+
<Box bg="paleBlue" zIndex={3}>
|
|
254
|
+
I will not be behind the infotip, sad + unreadable
|
|
255
|
+
</Box>
|
|
256
|
+
<InfoTip info="I am inline, cool" />
|
|
257
|
+
<Box bg="paleBlue" zIndex={3}>
|
|
258
|
+
I will be behind the infotip, nice + great
|
|
259
|
+
</Box>
|
|
260
|
+
<InfoTip {...args} />
|
|
324
261
|
</FlexBox>
|
|
325
262
|
),
|
|
326
263
|
};
|
|
@@ -55,12 +55,6 @@ The `truncateLines` prop allows you to set the maximum number of lines that the
|
|
|
55
55
|
|
|
56
56
|
<Canvas of={PreviewTipStories.Truncation} />
|
|
57
57
|
|
|
58
|
-
## Z-Index
|
|
59
|
-
|
|
60
|
-
You can customize the `zIndex` of the PreviewTip's portal with the `zIndex` prop. This works for both `inline` and `floating` placements, and is useful when the tip needs to appear above other positioned elements.
|
|
61
|
-
|
|
62
|
-
<Canvas of={PreviewTipStories.ZIndex} />
|
|
63
|
-
|
|
64
58
|
## Playground
|
|
65
59
|
|
|
66
60
|
<Canvas sourceState="shown" of={PreviewTipStories.Default} />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box, FlexBox, PreviewTip
|
|
1
|
+
import { Box, FlexBox, PreviewTip } from '@codecademy/gamut';
|
|
2
2
|
import { SmileyIndifferentIcon } from '@codecademy/gamut-icons';
|
|
3
3
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
4
|
import { useState } from 'react';
|
|
@@ -110,70 +110,3 @@ const TruncationExample = (args: React.ComponentProps<typeof PreviewTip>) => {
|
|
|
110
110
|
export const Truncation: Story = {
|
|
111
111
|
render: (args) => <TruncationExample {...args} />,
|
|
112
112
|
};
|
|
113
|
-
|
|
114
|
-
export const ZIndex: Story = {
|
|
115
|
-
args: {
|
|
116
|
-
zIndex: 5,
|
|
117
|
-
},
|
|
118
|
-
render: (args) => (
|
|
119
|
-
<FlexBox center flexDirection="column" gap={16} py={64}>
|
|
120
|
-
<Text variant="p-small">Inline placement:</Text>
|
|
121
|
-
<FlexBox alignItems="center" gap={16}>
|
|
122
|
-
<Box bg="paleBlue" p={8} zIndex={3} position="relative">
|
|
123
|
-
z-index: 3
|
|
124
|
-
</Box>
|
|
125
|
-
<PreviewTip
|
|
126
|
-
{...args}
|
|
127
|
-
alignment="bottom-right"
|
|
128
|
-
href="#"
|
|
129
|
-
linkDescription="Default z-index (inline)"
|
|
130
|
-
>
|
|
131
|
-
Default z-index
|
|
132
|
-
</PreviewTip>
|
|
133
|
-
<Box bg="paleBlue" p={8} zIndex={3} position="relative">
|
|
134
|
-
z-index: 3
|
|
135
|
-
</Box>
|
|
136
|
-
<PreviewTip
|
|
137
|
-
{...args}
|
|
138
|
-
alignment="top-right"
|
|
139
|
-
href="#"
|
|
140
|
-
linkDescription="z-index: 5 (inline)"
|
|
141
|
-
zIndex={5}
|
|
142
|
-
>
|
|
143
|
-
z-index: 5
|
|
144
|
-
</PreviewTip>
|
|
145
|
-
</FlexBox>
|
|
146
|
-
|
|
147
|
-
<Text variant="p-small" mt={24}>
|
|
148
|
-
Floating placement:
|
|
149
|
-
</Text>
|
|
150
|
-
<FlexBox alignItems="center" gap={16}>
|
|
151
|
-
<Box bg="paleGreen" p={8} zIndex={3} position="relative">
|
|
152
|
-
z-index: 3
|
|
153
|
-
</Box>
|
|
154
|
-
<PreviewTip
|
|
155
|
-
{...args}
|
|
156
|
-
alignment="bottom-right"
|
|
157
|
-
href="#"
|
|
158
|
-
linkDescription="Default z-index (floating)"
|
|
159
|
-
placement="floating"
|
|
160
|
-
>
|
|
161
|
-
Default z-index
|
|
162
|
-
</PreviewTip>
|
|
163
|
-
<Box bg="paleGreen" p={8} zIndex={3} position="relative">
|
|
164
|
-
z-index: 3
|
|
165
|
-
</Box>
|
|
166
|
-
<PreviewTip
|
|
167
|
-
{...args}
|
|
168
|
-
alignment="top-right"
|
|
169
|
-
href="#"
|
|
170
|
-
linkDescription="z-index: 5 (floating)"
|
|
171
|
-
placement="floating"
|
|
172
|
-
zIndex={5}
|
|
173
|
-
>
|
|
174
|
-
z-index: 5
|
|
175
|
-
</PreviewTip>
|
|
176
|
-
</FlexBox>
|
|
177
|
-
</FlexBox>
|
|
178
|
-
),
|
|
179
|
-
};
|
|
@@ -63,12 +63,6 @@ When a Button is disabled with a tooltip, you must use the `aria-disabled` prop
|
|
|
63
63
|
|
|
64
64
|
<Canvas of={ToolTipStories.Disabled} />
|
|
65
65
|
|
|
66
|
-
## Z-Index
|
|
67
|
-
|
|
68
|
-
You can customize the `zIndex` of the ToolTip's portal with the `zIndex` prop. This works for both `inline` and `floating` placements, and is useful when the tip needs to appear above other positioned elements.
|
|
69
|
-
|
|
70
|
-
<Canvas of={ToolTipStories.ZIndex} />
|
|
71
|
-
|
|
72
66
|
## Playground
|
|
73
67
|
|
|
74
68
|
<Canvas sourceState="shown" of={ToolTipStories.Default} />
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Box,
|
|
3
2
|
FillButton,
|
|
4
3
|
FlexBox,
|
|
5
4
|
IconButton,
|
|
6
5
|
StrokeButton,
|
|
7
|
-
Text,
|
|
8
6
|
ToolTip,
|
|
9
7
|
} from '@codecademy/gamut';
|
|
10
8
|
import {
|
|
@@ -135,60 +133,3 @@ export const Disabled: Story = {
|
|
|
135
133
|
</FlexBox>
|
|
136
134
|
),
|
|
137
135
|
};
|
|
138
|
-
|
|
139
|
-
export const ZIndex: Story = {
|
|
140
|
-
render: () => (
|
|
141
|
-
<FlexBox center flexDirection="column" gap={16} m={24} py={64}>
|
|
142
|
-
<Text variant="p-small">Inline placement:</Text>
|
|
143
|
-
<FlexBox alignItems="center" gap={16}>
|
|
144
|
-
<Box bg="paleBlue" p={8} zIndex={3} position="relative">
|
|
145
|
-
z-index: 3
|
|
146
|
-
</Box>
|
|
147
|
-
<ToolTip id="zindex-inline-default" info="Default z-index (inline)">
|
|
148
|
-
<FillButton aria-describedby="zindex-inline-default">
|
|
149
|
-
Default
|
|
150
|
-
</FillButton>
|
|
151
|
-
</ToolTip>
|
|
152
|
-
<Box bg="paleBlue" p={8} zIndex={3} position="relative">
|
|
153
|
-
z-index: 3
|
|
154
|
-
</Box>
|
|
155
|
-
<ToolTip id="zindex-inline-custom" info="z-index: 5 (inline)" zIndex={5}>
|
|
156
|
-
<FillButton aria-describedby="zindex-inline-custom">
|
|
157
|
-
z-index: 5
|
|
158
|
-
</FillButton>
|
|
159
|
-
</ToolTip>
|
|
160
|
-
</FlexBox>
|
|
161
|
-
|
|
162
|
-
<Text variant="p-small" mt={24}>
|
|
163
|
-
Floating placement:
|
|
164
|
-
</Text>
|
|
165
|
-
<FlexBox alignItems="center" gap={16}>
|
|
166
|
-
<Box bg="paleGreen" p={8} zIndex={3} position="relative">
|
|
167
|
-
z-index: 3
|
|
168
|
-
</Box>
|
|
169
|
-
<ToolTip
|
|
170
|
-
id="zindex-floating-default"
|
|
171
|
-
info="Default z-index (floating)"
|
|
172
|
-
placement="floating"
|
|
173
|
-
>
|
|
174
|
-
<FillButton aria-describedby="zindex-floating-default">
|
|
175
|
-
Default
|
|
176
|
-
</FillButton>
|
|
177
|
-
</ToolTip>
|
|
178
|
-
<Box bg="paleGreen" p={8} zIndex={3} position="relative">
|
|
179
|
-
z-index: 3
|
|
180
|
-
</Box>
|
|
181
|
-
<ToolTip
|
|
182
|
-
id="zindex-floating-custom"
|
|
183
|
-
info="z-index: 5 (floating)"
|
|
184
|
-
placement="floating"
|
|
185
|
-
zIndex={5}
|
|
186
|
-
>
|
|
187
|
-
<FillButton aria-describedby="zindex-floating-custom">
|
|
188
|
-
z-index: 5
|
|
189
|
-
</FillButton>
|
|
190
|
-
</ToolTip>
|
|
191
|
-
</FlexBox>
|
|
192
|
-
</FlexBox>
|
|
193
|
-
),
|
|
194
|
-
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Canvas, Controls, Meta } from '@storybook/blocks';
|
|
2
|
+
|
|
3
|
+
import { ComponentHeader } from '~styleguide/blocks';
|
|
4
|
+
|
|
5
|
+
import * as BarChartStories from './BarChart.stories';
|
|
6
|
+
|
|
7
|
+
export const parameters = {
|
|
8
|
+
subtitle: `A horizontal bar chart for visualizing comparative data`,
|
|
9
|
+
design: {
|
|
10
|
+
type: 'figma',
|
|
11
|
+
url: 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=55123-4176',
|
|
12
|
+
},
|
|
13
|
+
status: 'current',
|
|
14
|
+
source: {
|
|
15
|
+
repo: 'gamut',
|
|
16
|
+
githubLink:
|
|
17
|
+
'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/BarChart',
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
<Meta of={BarChartStories} />;
|
|
22
|
+
|
|
23
|
+
<ComponentHeader {...parameters} />
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Use BarChart to display comparative data across categories, such as skills progress, XP earned, or any quantitative metrics that benefit from visual comparison.
|
|
28
|
+
|
|
29
|
+
### Best practices:
|
|
30
|
+
|
|
31
|
+
- Use consistent units across all bars in a chart
|
|
32
|
+
- Limit the number of bars to maintain readability (5-10 is optimal)
|
|
33
|
+
- Consider using the stacked variant to show progress toward a goal
|
|
34
|
+
- Sort bars by value (descending) when ranking is important
|
|
35
|
+
|
|
36
|
+
When NOT to use
|
|
37
|
+
|
|
38
|
+
- For showing trends over time - use a line chart instead
|
|
39
|
+
- For showing parts of a whole - use a pie or donut chart
|
|
40
|
+
- For very small datasets (1-2 items) - consider using ProgressBar
|
|
41
|
+
|
|
42
|
+
## Variants
|
|
43
|
+
|
|
44
|
+
### Simple (Non-stacked)
|
|
45
|
+
|
|
46
|
+
Use the simple variant when showing single values per category. Only `seriesOneValue` is provided.
|
|
47
|
+
|
|
48
|
+
<Canvas of={BarChartStories.Default} />
|
|
49
|
+
|
|
50
|
+
### Stacked
|
|
51
|
+
|
|
52
|
+
Use the stacked variant when showing progress within a total. Provide both `seriesOneValue` (progress) and `seriesTwoValue` (total).
|
|
53
|
+
|
|
54
|
+
<Canvas of={BarChartStories.Stacked} />
|
|
55
|
+
|
|
56
|
+
### With Icons
|
|
57
|
+
|
|
58
|
+
Add icons to labels for better visual identification of categories.
|
|
59
|
+
|
|
60
|
+
<Canvas of={BarChartStories.WithIcons} />
|
|
61
|
+
|
|
62
|
+
### Animated
|
|
63
|
+
|
|
64
|
+
Enable entrance animations for a more engaging experience.
|
|
65
|
+
|
|
66
|
+
<Canvas of={BarChartStories.Animated} />
|
|
67
|
+
|
|
68
|
+
### Interactive
|
|
69
|
+
|
|
70
|
+
Rows can be made interactive with `onClick` handlers or `href` links.
|
|
71
|
+
|
|
72
|
+
<Canvas of={BarChartStories.Interactive} />
|
|
73
|
+
|
|
74
|
+
## Playground
|
|
75
|
+
|
|
76
|
+
<Canvas sourceState="shown" of={BarChartStories.Default} />
|
|
77
|
+
|
|
78
|
+
<Controls />
|
|
79
|
+
|
|
80
|
+
## Accessibility considerations
|
|
81
|
+
|
|
82
|
+
- Always provide either `aria-label` or `aria-labelledby` to describe the chart
|
|
83
|
+
- Each row automatically generates an accessible label summarizing its values
|
|
84
|
+
- Interactive rows (with onClick/href) are properly announced as buttons/links
|
|
85
|
+
- Grid lines are marked as decorative and hidden from screen readers
|
|
86
|
+
- The scale header is hidden on small screens and marked as decorative
|
|
87
|
+
|
|
88
|
+
## UX writing
|
|
89
|
+
|
|
90
|
+
- Keep y-axis labels concise (1-3 words)
|
|
91
|
+
- Use consistent unit labels (e.g., "XP", "hours", "points")
|
|
92
|
+
- Consider locale-aware number formatting for international audiences
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { BarChart, BarProps } from '@codecademy/gamut';
|
|
2
|
+
import {
|
|
3
|
+
BookFlipPageIcon,
|
|
4
|
+
CodeIcon,
|
|
5
|
+
DataScienceIcon,
|
|
6
|
+
GameControllerIcon,
|
|
7
|
+
TerminalIcon,
|
|
8
|
+
} from '@codecademy/gamut-icons';
|
|
9
|
+
import { action } from '@storybook/addon-actions';
|
|
10
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
11
|
+
|
|
12
|
+
const meta: Meta<typeof BarChart> = {
|
|
13
|
+
component: BarChart,
|
|
14
|
+
args: {
|
|
15
|
+
'aria-label': 'Skills experience chart',
|
|
16
|
+
minRange: 0,
|
|
17
|
+
maxRange: 2000,
|
|
18
|
+
unit: 'XP',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof BarChart>;
|
|
24
|
+
|
|
25
|
+
// Sample data for non-stacked (simple) bars
|
|
26
|
+
const simpleBarData: BarProps[] = [
|
|
27
|
+
{ yLabel: 'Python', seriesOneValue: 1500 },
|
|
28
|
+
{ yLabel: 'JavaScript', seriesOneValue: 2000 },
|
|
29
|
+
{ yLabel: 'HTML/CSS', seriesOneValue: 800 },
|
|
30
|
+
{ yLabel: 'SQL', seriesOneValue: 600 },
|
|
31
|
+
{ yLabel: 'React', seriesOneValue: 450 },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// Sample data for stacked bars (with seriesTwoValue)
|
|
35
|
+
const stackedBarData: BarProps[] = [
|
|
36
|
+
{ yLabel: 'Python', seriesOneValue: 200, seriesTwoValue: 1500 },
|
|
37
|
+
{ yLabel: 'JavaScript', seriesOneValue: 1800, seriesTwoValue: 2000 },
|
|
38
|
+
{ yLabel: 'HTML/CSS', seriesOneValue: 600, seriesTwoValue: 800 },
|
|
39
|
+
{ yLabel: 'SQL', seriesOneValue: 550, seriesTwoValue: 600 },
|
|
40
|
+
{ yLabel: 'React', seriesOneValue: 300, seriesTwoValue: 450 },
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// Sample data with icons
|
|
44
|
+
const barDataWithIcons: BarProps[] = [
|
|
45
|
+
{
|
|
46
|
+
yLabel: 'Python',
|
|
47
|
+
seriesOneValue: 200,
|
|
48
|
+
seriesTwoValue: 1500,
|
|
49
|
+
icon: CodeIcon,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
yLabel: 'JavaScript',
|
|
53
|
+
seriesOneValue: 150,
|
|
54
|
+
seriesTwoValue: 2000,
|
|
55
|
+
icon: TerminalIcon,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
yLabel: 'Data Science',
|
|
59
|
+
seriesOneValue: 100,
|
|
60
|
+
seriesTwoValue: 800,
|
|
61
|
+
icon: DataScienceIcon,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
yLabel: 'Game Dev',
|
|
65
|
+
seriesOneValue: 50,
|
|
66
|
+
seriesTwoValue: 600,
|
|
67
|
+
icon: GameControllerIcon,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
yLabel: 'Reading',
|
|
71
|
+
seriesOneValue: 75,
|
|
72
|
+
seriesTwoValue: 450,
|
|
73
|
+
icon: BookFlipPageIcon,
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Default non-stacked bar chart showing single values
|
|
79
|
+
*/
|
|
80
|
+
export const Default: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
barValues: simpleBarData,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Stacked bar chart showing progress (seriesOneValue) over total (seriesTwoValue)
|
|
88
|
+
*/
|
|
89
|
+
export const Stacked: Story = {
|
|
90
|
+
args: {
|
|
91
|
+
barValues: stackedBarData,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Bar chart with icons next to labels
|
|
97
|
+
*/
|
|
98
|
+
export const WithIcons: Story = {
|
|
99
|
+
args: {
|
|
100
|
+
barValues: barDataWithIcons,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Animated bar chart with staggered entrance
|
|
106
|
+
*/
|
|
107
|
+
export const Animated: Story = {
|
|
108
|
+
args: {
|
|
109
|
+
barValues: stackedBarData,
|
|
110
|
+
animate: true,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Bar chart sorted by value in descending order
|
|
116
|
+
*/
|
|
117
|
+
export const SortedByValue: Story = {
|
|
118
|
+
args: {
|
|
119
|
+
barValues: simpleBarData,
|
|
120
|
+
sortBy: 'value',
|
|
121
|
+
order: 'descending',
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Bar chart sorted alphabetically by label
|
|
127
|
+
*/
|
|
128
|
+
export const SortedByLabel: Story = {
|
|
129
|
+
args: {
|
|
130
|
+
barValues: simpleBarData,
|
|
131
|
+
sortBy: 'label',
|
|
132
|
+
order: 'ascending',
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Interactive bar chart with clickable rows
|
|
138
|
+
*/
|
|
139
|
+
export const Interactive: Story = {
|
|
140
|
+
args: {
|
|
141
|
+
barValues: simpleBarData.map((bar) => ({
|
|
142
|
+
...bar,
|
|
143
|
+
onClick: action(`Clicked ${bar.yLabel}`),
|
|
144
|
+
})),
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Interactive bar chart with linked rows
|
|
150
|
+
*/
|
|
151
|
+
export const WithLinks: Story = {
|
|
152
|
+
args: {
|
|
153
|
+
barValues: simpleBarData.map((bar) => ({
|
|
154
|
+
...bar,
|
|
155
|
+
href: `#${bar.yLabel.toLowerCase().replace(/\s+/g, '-')}`,
|
|
156
|
+
})),
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Bar chart with custom styling
|
|
162
|
+
*/
|
|
163
|
+
export const CustomStyles: Story = {
|
|
164
|
+
args: {
|
|
165
|
+
barValues: stackedBarData,
|
|
166
|
+
styleConfig: {
|
|
167
|
+
backgroundBarColor: 'paleGreen',
|
|
168
|
+
foregroundBarColor: 'feedback-success',
|
|
169
|
+
textColor: 'navy',
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Bar chart with custom xScale interval
|
|
176
|
+
*/
|
|
177
|
+
export const CustomScale: Story = {
|
|
178
|
+
args: {
|
|
179
|
+
barValues: simpleBarData,
|
|
180
|
+
maxRange: 2000,
|
|
181
|
+
xScale: 250,
|
|
182
|
+
},
|
|
183
|
+
};
|