@codecademy/styleguide 78.5.6-alpha.a75de2.0 → 78.5.6-alpha.afe04a.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.
Files changed (37) hide show
  1. package/.storybook/components/Elements/DocsContainer.tsx +4 -0
  2. package/.storybook/components/Elements/Markdown.tsx +0 -1
  3. package/.storybook/preview.ts +14 -0
  4. package/.storybook/theming/GamutThemeProvider.tsx +7 -1
  5. package/CHANGELOG.md +1 -1
  6. package/package.json +2 -2
  7. package/src/lib/Atoms/FormElements/FormGroup/FormGroup.mdx +0 -6
  8. package/src/lib/Atoms/FormElements/FormGroup/FormGroup.stories.tsx +0 -11
  9. package/src/lib/Foundations/System/About.mdx +1 -1
  10. package/src/lib/Foundations/System/Props/About.mdx +81 -0
  11. package/src/lib/Foundations/System/Props/Background.mdx +30 -0
  12. package/src/lib/Foundations/System/Props/Border.mdx +33 -0
  13. package/src/lib/Foundations/System/Props/Color.mdx +28 -0
  14. package/src/lib/Foundations/System/Props/Flex.mdx +28 -0
  15. package/src/lib/Foundations/System/Props/Grid.mdx +31 -0
  16. package/src/lib/Foundations/System/Props/Layout.mdx +34 -0
  17. package/src/lib/Foundations/System/Props/List.mdx +38 -0
  18. package/src/lib/Foundations/System/Props/Positioning.mdx +29 -0
  19. package/src/lib/Foundations/System/Props/Shadow.mdx +31 -0
  20. package/src/lib/Foundations/System/Props/Space.mdx +44 -0
  21. package/src/lib/Foundations/System/Props/Space.stories.tsx +48 -0
  22. package/src/lib/Foundations/System/Props/Typography.mdx +28 -0
  23. package/src/lib/Foundations/System/ResponsiveProperties/ResponsiveProperties.mdx +3 -3
  24. package/src/lib/Foundations/System/ResponsiveProperties/ResponsiveProperties.stories.tsx +1 -0
  25. package/src/lib/Foundations/shared/elements.tsx +69 -19
  26. package/src/lib/Meta/About.mdx +3 -1
  27. package/src/lib/Meta/Logical and physical CSS properties.mdx +123 -0
  28. package/src/lib/Meta/Usage Guide.mdx +6 -1
  29. package/src/lib/Molecules/Tips/InfoTip/InfoTip.mdx +8 -24
  30. package/src/lib/Molecules/Tips/InfoTip/InfoTip.stories.tsx +34 -86
  31. package/src/lib/Organisms/ConnectedForm/ConnectedFormGroup/ConnectedFormGroup.mdx +0 -20
  32. package/src/lib/Organisms/ConnectedForm/ConnectedFormGroup/ConnectedFormGroup.stories.tsx +0 -84
  33. package/src/lib/Organisms/GridForm/Fields.mdx +0 -20
  34. package/src/lib/Organisms/GridForm/Fields.stories.tsx +1 -73
  35. package/src/lib/Organisms/GridForm/Layout.mdx +1 -1
  36. package/src/static/meta/toolbar.png +0 -0
  37. package/src/lib/Foundations/System/Props.mdx +0 -230
@@ -6,17 +6,17 @@ import { breakpoint } from '../../shared/elements';
6
6
  import * as ResponsivePropertiesStories from './ResponsiveProperties.stories';
7
7
 
8
8
  export const parameters = {
9
- title: 'Responsive Properties',
9
+ title: 'Responsive properties',
10
10
  subtitle:
11
11
  'All system props accept a syntax to generate responsive styles on a per prop basis',
12
12
  source: {
13
13
  repo: 'variance',
14
14
  githubLink:
15
- 'https://github.com/Codecademy/gamut/blob/cass-gm-842/packages/variance/src/utils/responsive.ts',
15
+ 'https://github.com/Codecademy/gamut/blob/main/packages/gamut-styles/src/variables/responsive.ts',
16
16
  },
17
17
  };
18
18
 
19
- <Meta title="Foundations/System/Responsive Properties" />
19
+ <Meta of={ResponsivePropertiesStories} />
20
20
 
21
21
  <AboutHeader {...parameters} />
22
22
 
@@ -2,6 +2,7 @@ import { Box, FlexBox } from '@codecademy/gamut';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
3
 
4
4
  const meta: Meta<typeof FlexBox> = {
5
+ title: 'Foundations/System/Responsive properties',
5
6
  component: FlexBox,
6
7
  args: {
7
8
  center: true,
@@ -2,22 +2,32 @@ import { Anchor, Box } from '@codecademy/gamut';
2
2
  import {
3
3
  Background,
4
4
  coreSwatches,
5
+ css,
5
6
  lxStudioColors,
6
7
  theme,
7
8
  trueColors,
8
9
  } from '@codecademy/gamut-styles';
9
10
  // eslint-disable-next-line gamut/import-paths
10
11
  import * as ALL_PROPS from '@codecademy/gamut-styles/src/variance/config';
12
+ import { useTheme } from '@emotion/react';
13
+ import styled from '@emotion/styled';
11
14
  import kebabCase from 'lodash/kebabCase';
12
15
 
13
16
  import { Code, ColorScale, LinkTo, TokenTable } from '~styleguide/blocks';
14
17
 
15
18
  import { applyCorrectNotation } from './applyCorrectNotation';
16
19
 
20
+ const AnchorCode = styled(Code)(
21
+ css({
22
+ textDecoration: 'underline',
23
+ mx: 4,
24
+ })
25
+ );
26
+
17
27
  export const PROP_COLUMN = {
18
28
  key: 'key',
19
29
  name: 'Prop',
20
- size: 'md',
30
+ size: 'lg',
21
31
  render: ({ id }: any) => <Code>{id}</Code>,
22
32
  };
23
33
 
@@ -403,26 +413,61 @@ export const DarkModeTable = () => (
403
413
  );
404
414
  /* eslint-disable gamut/import-paths */
405
415
 
416
+ const PropertiesRenderer = ({
417
+ property,
418
+ properties,
419
+ resolveProperty,
420
+ }: {
421
+ property: string | { physical: string; logical: string };
422
+ properties?: string[] | { physical: string[]; logical: string[] };
423
+ resolveProperty?: (useLogicalProperties: boolean) => 'logical' | 'physical';
424
+ }) => {
425
+ const currentTheme = useTheme() as { useLogicalProperties?: boolean };
426
+ const useLogicalProperties = currentTheme?.useLogicalProperties ?? true;
427
+
428
+ const mode = resolveProperty
429
+ ? resolveProperty(useLogicalProperties)
430
+ : 'physical';
431
+
432
+ const resolvedProperty =
433
+ typeof property === 'string' ? property : property[mode];
434
+
435
+ let resolvedProperties: string[];
436
+ if (!properties) {
437
+ resolvedProperties = [resolvedProperty];
438
+ } else if (Array.isArray(properties)) {
439
+ resolvedProperties = properties;
440
+ } else {
441
+ resolvedProperties = properties[mode];
442
+ }
443
+
444
+ return (
445
+ <>
446
+ {resolvedProperties.map((prop) => (
447
+ <Anchor
448
+ href={`https://developer.mozilla.org/en-US/docs/Web/CSS/${kebabCase(
449
+ prop
450
+ )}`}
451
+ key={prop}
452
+ rel=""
453
+ target="_blank"
454
+ >
455
+ <AnchorCode>{kebabCase(prop)}</AnchorCode>
456
+ </Anchor>
457
+ ))}
458
+ </>
459
+ );
460
+ };
461
+
406
462
  const PROPERTIES_COLUMN = {
407
463
  key: 'properties',
408
464
  name: 'Properties',
409
465
  size: 'xl',
410
- render: ({
411
- property,
412
- properties = [property],
413
- }: {
414
- property: string;
415
- properties: string[];
416
- }) =>
417
- properties.map((property) => (
418
- <Anchor
419
- href={`https://developer.mozilla.org/en-US/docs/Web/CSS/${property}`}
420
- rel=""
421
- target="_blank"
422
- >
423
- <Code key={property}>{kebabCase(property)}</Code>
424
- </Anchor>
425
- )),
466
+ render: (props: {
467
+ property: string | { physical: string; logical: string };
468
+ properties?: string[] | { physical: string[]; logical: string[] };
469
+ resolveProperty?: (useLogicalProperties: boolean) => 'logical' | 'physical';
470
+ }) => <PropertiesRenderer {...props} />,
426
471
  };
427
472
 
428
473
  const SCALE_COLUMN = {
@@ -430,7 +475,7 @@ const SCALE_COLUMN = {
430
475
  name: 'Scale',
431
476
  size: 'lg',
432
477
  render: ({ scale }: { scale: string }) => (
433
- <LinkTo id={`foundations-theme--${kebabCase(scale)}`}>{scale}</LinkTo>
478
+ <LinkTo id="Foundations/Theme/Core Theme">{scale}</LinkTo>
434
479
  ),
435
480
  };
436
481
 
@@ -438,7 +483,12 @@ const TRANSFORM_COLUMN = {
438
483
  key: 'transform',
439
484
  name: 'Transform',
440
485
  size: 'fill',
441
- render: ({ transform }: any) => transform && <Code>{transform?.name}</Code>,
486
+ render: ({ transform, resolveProperty }: any) => (
487
+ <>
488
+ {transform && <Code>{transform?.name}</Code>}
489
+ {resolveProperty && <Code>{resolveProperty?.name}</Code>}
490
+ </>
491
+ ),
442
492
  };
443
493
 
444
494
  export const defaultColumns = [
@@ -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 logicalPhysicalParameters } from './Logical and physical CSS properties.mdx';
16
17
  import { parameters as storiesParameters } from './Stories.mdx';
17
18
  import { parameters as usageGuideParameters } from './Usage Guide.mdx';
18
19
 
@@ -34,9 +35,10 @@ export const parameters = {
34
35
  deepControlsParameters,
35
36
  eslintRulesParameters,
36
37
  faqsParameters,
38
+ installationParameters,
39
+ logicalPhysicalParameters,
37
40
  storiesParameters,
38
41
  brandParameters,
39
- installationParameters,
40
42
  usageGuideParameters,
41
43
  ])}
42
44
  />
@@ -0,0 +1,123 @@
1
+ import { Meta } from '@storybook/blocks';
2
+
3
+ import {
4
+ AboutHeader,
5
+ Callout,
6
+ Code,
7
+ ImageWrapper,
8
+ TokenTable,
9
+ } from '~styleguide/blocks';
10
+
11
+ export const parameters = {
12
+ id: 'Meta/Logical and physical CSS properties',
13
+ title: 'Logical and physical CSS properties',
14
+ subtitle:
15
+ 'Understanding CSS logical and physical properties and how Gamut supports both modes.',
16
+ status: 'static',
17
+ };
18
+
19
+ <Meta title="Meta/Logical and physical CSS properties" />
20
+
21
+ <AboutHeader {...parameters} />
22
+
23
+ ## What are CSS logical properties?
24
+
25
+ CSS logical properties are a modern approach to styling that adapts to the writing mode and text direction of your content, rather than being tied to physical screen directions. More information can be found on[MDN: CSS Logical Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values)
26
+
27
+ ### Physical Properties (Traditional)
28
+
29
+ Physical properties reference the physical dimensions of the viewport. For example:
30
+
31
+ - `margin-left`, `margin-right`, `margin-top`, `margin-bottom`
32
+ - `padding-left`, `padding-right`, `padding-top`, `padding-bottom`
33
+
34
+ These work well for left-to-right (LTR) languages but require manual overrides for right-to-left (RTL) languages like Arabic or Hebrew.
35
+
36
+ ### Logical Properties (Modern)
37
+
38
+ Logical properties reference the flow of content:
39
+
40
+ - **Inline axis** (text direction): `margin-inline-start`, `margin-inline-end`
41
+ - **Block axis** (reading direction): `margin-block-start`, `margin-block-end`
42
+
43
+ ## Using `useLogicalProperties` in Gamut
44
+
45
+ Gamut supports both physical and logical CSS properties through the `useLogicalProperties` prop on `GamutProvider`. This allows you to choose which mode your application uses. By default, `useLogicalProperties` is set to `true`, meaning Gamut will use logical CSS properties. If you want to use physical CSS properties, you have to set `useLogicalProperties` to `false`.
46
+
47
+ ### Affected Props
48
+
49
+ Here are some examples of how physical and logical properties are affected by the `useLogicalProperties` prop:
50
+
51
+ <TokenTable
52
+ idKey="prop"
53
+ columns={[
54
+ {
55
+ key: 'prop',
56
+ name: 'Prop',
57
+ size: 'sm',
58
+ render: ({ prop }) => <Code>{prop}</Code>,
59
+ },
60
+ {
61
+ key: 'physical',
62
+ name: 'Physical',
63
+ size: 'xl',
64
+ render: ({ physical }) =>
65
+ physical.map((p) => (
66
+ <>
67
+ <Code key={p}>{p}</Code>{' '}
68
+ </>
69
+ )),
70
+ },
71
+ {
72
+ key: 'logical',
73
+ name: 'Logical',
74
+ size: 'xl',
75
+ render: ({ logical }) =>
76
+ logical.map((l) => (
77
+ <>
78
+ <Code key={l}>{l}</Code>{' '}
79
+ </>
80
+ )),
81
+ },
82
+ ]}
83
+ rows={[
84
+ {
85
+ prop: 'mx',
86
+ physical: ['margin-left', 'margin-right'],
87
+ logical: ['margin-inline-start', 'margin-inline-end'],
88
+ },
89
+ { prop: 'mt', physical: ['margin-top'], logical: ['margin-block-start'] },
90
+ {
91
+ prop: 'py',
92
+ physical: ['padding-top', 'padding-bottom'],
93
+ logical: ['padding-block-start', 'padding-block-end'],
94
+ },
95
+ {
96
+ prop: 'pb',
97
+ physical: ['padding-bottom'],
98
+ logical: ['padding-block-end'],
99
+ },
100
+ ]}
101
+ />
102
+
103
+ <Callout
104
+ text={
105
+ <>
106
+ Props like <code>m</code> and <code>p</code> (which set all four sides at
107
+ once) are not affected by this setting, as the CSS <code>margin</code> and{' '}
108
+ <code>padding</code> shorthands work identically in both modes.
109
+ </>
110
+ }
111
+ />
112
+
113
+ ## Previewing in Storybook
114
+
115
+ You can toggle between logical and physical properties in Storybook using the **LogicalProps** toolbar button:
116
+
117
+ <ImageWrapper
118
+ src="./meta/toolbar.png"
119
+ alt="The Storybook toolbar with the LogicalProps toggle highlighted."
120
+ height="auto"
121
+ />
122
+
123
+ This allows you to preview how components render with either property mode without changing any code.
@@ -39,7 +39,8 @@ On each page is a toolbar located on the top:
39
39
  1. Grid (the 2x2 collection of squares) - applies a grid to the preview
40
40
  2. Color mode selector (the circle icon) - toggles between light and dark mode for rendered code examples
41
41
  3. Theme switcher (the paintbrush icon) - switches between different design system themes (Core, Admin, LX Studio, Percipio)
42
- 4. Outline (dotted square) - applies outlines to elements in the rendered code examples
42
+ 4. LogicalProps (the two arrows icon) - toggles between physical and logical CSS properties
43
+ 5. Outline (dotted square) - applies outlines to elements in the rendered code examples
43
44
 
44
45
  ### Theme Switcher
45
46
 
@@ -58,6 +59,10 @@ Available themes:
58
59
 
59
60
  The theme switcher works in combination with the color mode selector, so you can test both light and dark variants of each theme.
60
61
 
62
+ ### LogicalProps
63
+
64
+ The LogicalProps button (two arrows icon) provides a menu to select between Logical and Physical CSS properties.
65
+
61
66
  ### Showing code
62
67
 
63
68
  On the bottom right of each canvas (a rendered code example) is a button to show its code:
@@ -28,7 +28,8 @@ export const parameters = {
28
28
  A tip is triggered by clicking on an information icon button and can be closed by clicking outside, pressing <KeyboardKey>Esc</KeyboardKey>, or clicking the info button again.
29
29
 
30
30
  Use an infotip to provide additional info about a nearby element or content.
31
- The info button has low and high emphasis variants and the `Tip` has 4 alignment variants.
31
+
32
+ Infotip consists of an icon button and the .tip-bg subcomponent. The info button has low and high emphasis variants and the `.tip` has 4 alignment variants.
32
33
 
33
34
  ## Variants
34
35
 
@@ -56,19 +57,17 @@ This `floating` variant should only be used as needed.
56
57
 
57
58
  ### InfoTips with links or buttons
58
59
 
59
- Links or buttons within InfoTips should be used sparingly and only when the information is critical to the user's understanding of the content. When an InfoTip opens, focus automatically moves to the tip content, allowing keyboard users to immediately interact with any links or buttons inside.
60
+ Links or buttons within InfoTips should be used sparingly and only when the information is critical to the user's understanding of the content. If an infotip _absolutely requires_ a link or button, it needs to provide a programmatic focus by way of the `onClick` prop. The `onClick` prop accepts a function that can accept an `{isTipHidden}` argument and using that you can set programmatic focus as needed.
60
61
 
61
62
  <Canvas of={InfoTipStories.WithLinksOrButtons} />
62
63
 
63
- ### Automatic Focus Management
64
+ ### Floating placement
64
65
 
65
- InfoTips automatically manage focus for optimal keyboard accessibility:
66
+ When using `placement="floating"`, InfoTips implements focus management for easier navigation:
66
67
 
67
- - **Opening**: Focus automatically moves to the tip content when opened
68
- - ** <KeyboardKey>Tab</KeyboardKey> (Floating)**: Navigate forward through focusable elements (links, buttons) inside the tip. When reaching the last element, wraps back to the InfoTip button
69
- - **<KeyboardKey>Shift </KeyboardKey> + <KeyboardKey>Tab</KeyboardKey> (Floating)**: Navigate backward naturally - from the button, exits to the previous page element
70
- - **<KeyboardKey>Tab</KeyboardKey> or <KeyboardKey>Shift</KeyboardKey> +<KeyboardKey>Tab</KeyboardKey> (Inline)**: Follows normal document flow
71
- - **<KeyboardKey>Escape</KeyboardKey>**: Closes the tip and returns focus to the InfoTip button
68
+ - **<KeyboardKey>Tab</KeyboardKey>**: Navigate forward through focusable elements (links, buttons) inside the tip. When reaching the last element, wraps back to the InfoTip button for convenience
69
+ - **<KeyboardKey>Shift</KeyboardKey>+<KeyboardKey>Tab</KeyboardKey>**: Navigate backward naturally through the page
70
+ - **<KeyboardKey>Esc</KeyboardKey>**: Closes the tip and returns focus to the InfoTip button
72
71
 
73
72
  <Canvas of={InfoTipStories.KeyboardNavigation} />
74
73
 
@@ -83,21 +82,6 @@ InfoTips have intelligent Escape key handling that works correctly both inside a
83
82
 
84
83
  <Canvas of={InfoTipStories.InfoTipInsideModal} />
85
84
 
86
- ## Custom Accessible Labeling
87
-
88
- Provide either `ariaLabel` or `ariaLabelledby` to ensure screen reader users understand the purpose of the InfoTip button.
89
-
90
- The InfoTip button's accessible label can be customized using either prop:
91
-
92
- - **`ariaLabel`**: Directly sets the accessible label text. Useful when you want to provide a custom label without referencing another element.
93
- - **`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.
94
-
95
- ### Custom Role Description
96
-
97
- The `InfoTipButton` uses [`aria-roledescription`](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. This defaults to `"More information button"` but can be customized via the `ariaRoleDescription` prop for translation or other accessibility needs.
98
-
99
- <Canvas of={InfoTipStories.AriaLabel} />
100
-
101
85
  ## InfoTips and zIndex
102
86
 
103
87
  You can change the zIndex of your `InfoTip` with the zIndex property.
@@ -4,20 +4,17 @@ import {
4
4
  FillButton,
5
5
  FlexBox,
6
6
  GridBox,
7
- IconButton,
8
7
  InfoTip,
9
8
  Modal,
10
9
  Text,
11
10
  } from '@codecademy/gamut';
12
- import { SparkleIcon } from '@codecademy/gamut-icons';
13
11
  import type { Meta, StoryObj } from '@storybook/react';
14
- import { useState } from 'react';
12
+ import { useRef, useState } from 'react';
15
13
 
16
14
  const meta: Meta<typeof InfoTip> = {
17
15
  component: InfoTip,
18
16
  args: {
19
17
  alignment: 'top-left',
20
- ariaLabel: 'More information',
21
18
  info: `I am additional information about a nearby element or content.`,
22
19
  },
23
20
  };
@@ -31,10 +28,7 @@ export const Emphasis: Story = {
31
28
  },
32
29
  render: (args) => (
33
30
  <FlexBox center m={24} py={64}>
34
- <Text id="emphasis-text" mr={4}>
35
- Some text that needs info
36
- </Text>
37
- <InfoTip {...args} ariaLabelledby="emphasis-text" />
31
+ <Text mr={4}>Some text that needs info</Text> <InfoTip {...args} />
38
32
  </FlexBox>
39
33
  ),
40
34
  };
@@ -44,15 +38,10 @@ export const Alignments: Story = {
44
38
  <GridBox gap={24} gridTemplateColumns="1fr 1fr" ml={8} py={64}>
45
39
  {(['top-right', 'top-left', 'bottom-right', 'bottom-left'] as const).map(
46
40
  (alignment) => {
47
- const labelId = `alignment-${alignment}`;
48
41
  return (
49
42
  <Box key={alignment}>
50
- <Text id={labelId}>{alignment}</Text>
51
- <InfoTip
52
- {...args}
53
- alignment={alignment}
54
- ariaLabelledby={labelId}
55
- />
43
+ <Text>{alignment}</Text>
44
+ <InfoTip {...args} alignment={alignment} />
56
45
  </Box>
57
46
  );
58
47
  }
@@ -67,51 +56,10 @@ export const Placement: Story = {
67
56
  },
68
57
  render: (args) => (
69
58
  <FlexBox center>
70
- <Text id="placement-text" mr={4}>
59
+ <Text mr={4}>
71
60
  This text is in a small space and needs floating placement
72
61
  </Text>{' '}
73
- <InfoTip {...args} ariaLabelledby="placement-text" />
74
- </FlexBox>
75
- ),
76
- };
77
-
78
- export const AriaLabel: Story = {
79
- render: (args) => (
80
- <FlexBox center column gap={24} my={48} width={1}>
81
- <FlexBox alignItems="center" gap={8}>
82
- <Text fontSize={16} fontWeight="bold">
83
- Using ariaLabel (no visible label text):
84
- </Text>
85
- </FlexBox>
86
- <FlexBox alignItems="center" gap={8}>
87
- <IconButton
88
- icon={SparkleIcon}
89
- tip="This tool needs to be explained in the InfoTip"
90
- tipProps={{ placement: 'floating' }}
91
- onClick={() => null}
92
- />
93
- <InfoTip
94
- {...args}
95
- ariaLabel="Learn more about this tool"
96
- info="This is some helpful info about the tool represented by the IconButton"
97
- />
98
- </FlexBox>
99
-
100
- <FlexBox alignItems="center" gap={8}>
101
- <Text fontSize={16} fontWeight="bold">
102
- Using ariaLabelledby (references visible text):
103
- </Text>
104
- </FlexBox>
105
- <FlexBox alignItems="center" gap={8}>
106
- <Text id="custom-info-id">
107
- I am some helpful yet concise text that needs more explanation
108
- </Text>
109
- <InfoTip
110
- alignment="bottom-left"
111
- ariaLabelledby="custom-info-id"
112
- info="I am clarifying information related to the concise text."
113
- />
114
- </FlexBox>
62
+ <InfoTip {...args} />
115
63
  </FlexBox>
116
64
  ),
117
65
  };
@@ -121,16 +69,19 @@ export const WithLinksOrButtons: Story = {
121
69
  placement: 'floating',
122
70
  },
123
71
  render: function WithLinksOrButtons(args) {
72
+ const ref = useRef<HTMLDivElement>(null);
73
+
74
+ const onClick = ({ isTipHidden }: { isTipHidden: boolean }) => {
75
+ if (!isTipHidden) ref.current?.focus();
76
+ };
77
+
124
78
  return (
125
79
  <FlexBox center py={64}>
126
- <Text id="links-text" mr={4}>
127
- This text is in a small space and needs info
128
- </Text>{' '}
80
+ <Text mr={4}>This text is in a small space and needs info </Text>{' '}
129
81
  <InfoTip
130
82
  {...args}
131
- ariaLabelledby="links-text"
132
83
  info={
133
- <Text tabIndex={-1}>
84
+ <Text ref={ref} tabIndex={-1}>
134
85
  Hey! Here is a{' '}
135
86
  <Anchor href="https://giphy.com/search/nichijou">
136
87
  cool link
@@ -142,6 +93,7 @@ export const WithLinksOrButtons: Story = {
142
93
  that is also super important.
143
94
  </Text>
144
95
  }
96
+ onClick={onClick}
145
97
  />
146
98
  </FlexBox>
147
99
  );
@@ -150,35 +102,42 @@ export const WithLinksOrButtons: Story = {
150
102
 
151
103
  export const KeyboardNavigation: Story = {
152
104
  render: function KeyboardNavigation() {
105
+ const floatingRef = useRef<HTMLDivElement>(null);
106
+ const inlineRef = useRef<HTMLDivElement>(null);
107
+
153
108
  const examples = [
154
109
  {
155
110
  title: 'Floating Placement',
156
111
  placement: 'floating' as const,
112
+ ref: floatingRef,
157
113
  links: ['Link 1', 'Link 2', 'Link 3'],
158
114
  },
159
115
  {
160
116
  title: 'Inline Placement',
161
117
  placement: 'inline' as const,
162
118
  alignment: 'bottom-right' as const,
119
+ ref: inlineRef,
163
120
  links: ['Link A', 'Link B'],
164
121
  },
165
122
  ];
166
123
 
167
124
  return (
168
- <FlexBox center flexDirection="column" gap={24} py={64}>
125
+ <FlexBox center column gap={24} py={64}>
169
126
  <GridBox gap={16} gridTemplateColumns="1fr 1fr">
170
- {examples.map(({ title, placement, alignment, links }) => {
171
- const labelId = `keyboard-nav-${placement}`;
127
+ {examples.map(({ title, placement, alignment, ref, links }) => {
128
+ const onClick = ({ isTipHidden }: { isTipHidden: boolean }) => {
129
+ if (!isTipHidden) ref.current?.focus();
130
+ };
131
+
172
132
  return (
173
133
  <FlexBox gap={8} key={placement}>
174
- <Text fontSize={16} fontWeight="bold" id={labelId}>
134
+ <Text fontSize={16} fontWeight="bold">
175
135
  {title}
176
136
  </Text>
177
137
  <InfoTip
178
138
  alignment={alignment}
179
- ariaLabelledby={labelId}
180
139
  info={
181
- <Text>
140
+ <Text ref={ref} tabIndex={-1}>
182
141
  {links.map((label, idx) => (
183
142
  <>
184
143
  {idx > 0 && ', '}
@@ -191,6 +150,7 @@ export const KeyboardNavigation: Story = {
191
150
  </Text>
192
151
  }
193
152
  placement={placement}
153
+ onClick={onClick}
194
154
  />
195
155
  </FlexBox>
196
156
  );
@@ -202,10 +162,6 @@ export const KeyboardNavigation: Story = {
202
162
  Keyboard Navigation:
203
163
  </Text>
204
164
  <Box as="ul" fontSize={14} pl={16}>
205
- <li>
206
- <strong>Opening:</strong> Focus automatically moves to the tip
207
- content when opened
208
- </li>
209
165
  <li>
210
166
  <strong>Floating - Tab:</strong> Navigates forward through links,
211
167
  then wraps to button (contained)
@@ -268,10 +224,8 @@ export const InfoTipInsideModal: Story = {
268
224
  <Text>This modal contains an InfoTip below:</Text>
269
225
 
270
226
  <FlexBox alignItems="center" gap={8}>
271
- <Text id="modal-infotip-text">
272
- Some text that needs explanation
273
- </Text>
274
- <InfoTip {...args} ariaLabelledby="modal-infotip-text" />
227
+ <Text>Some text that needs explanation</Text>
228
+ <InfoTip {...args} />
275
229
  </FlexBox>
276
230
 
277
231
  <Text color="text-disabled" fontSize={14}>
@@ -299,14 +253,11 @@ export const ZIndex: Story = {
299
253
  <Box bg="paleBlue" zIndex={3}>
300
254
  I will not be behind the infotip, sad + unreadable
301
255
  </Box>
302
- <InfoTip
303
- ariaLabel="z-index example without override"
304
- info="I am inline, cool"
305
- />
256
+ <InfoTip info="I am inline, cool" />
306
257
  <Box bg="paleBlue" zIndex={3}>
307
258
  I will be behind the infotip, nice + great
308
259
  </Box>
309
- <InfoTip {...args} ariaLabel="z-index example with override" />
260
+ <InfoTip {...args} />
310
261
  </FlexBox>
311
262
  ),
312
263
  };
@@ -314,10 +265,7 @@ export const ZIndex: Story = {
314
265
  export const Default: Story = {
315
266
  render: (args) => (
316
267
  <FlexBox center m={24} py={64}>
317
- <Text id="default-text" mr={4}>
318
- Some text that needs info
319
- </Text>
320
- <InfoTip {...args} ariaLabelledby="default-text" />
268
+ <Text mr={4}>Some text that needs info</Text> <InfoTip {...args} />
321
269
  </FlexBox>
322
270
  ),
323
271
  };
@@ -61,26 +61,6 @@ A `ConnectedFormGroup` can be in one of three states: `default`, `error`, or `di
61
61
 
62
62
  <Canvas of={ConnectedFormGroupStories.States} />
63
63
 
64
- ## InfoTip
65
-
66
- A `ConnectedFormGroup` can include an `infotip` prop to provide additional context.
67
-
68
- ### Automatic labelling
69
-
70
- InfoTip buttons are automatically labelled by string field labels for accessibility.
71
-
72
- <Canvas of={ConnectedFormGroupStories.InfoTipAutoLabelling} />
73
-
74
- ### ReactNode labels
75
-
76
- For ReactNode labels (e.g., styled text or icons), you have three options:
77
-
78
- - `labelledByFieldLabel: true` - opt into automatic labelling by the field label
79
- - `ariaLabel` - provide a custom accessible name
80
- - `ariaLabelledby` - reference another element on the page, such as a section heading
81
-
82
- <Canvas of={ConnectedFormGroupStories.InfoTipWithReactNodeLabel} />
83
-
84
64
  ## Playground
85
65
 
86
66
  To see how a `ConnectedFormGroup` can be used in a `ConnectedForm`, check out the <LinkTo id="Organisms/ConnectedForm/ConnectedForm">ConnectedForm</LinkTo> page.