Skip to content

Commit b69a15b

Browse files
docs(eds-core-react): integrate React Native docs into Storybook (#4868)
* docs(eds-core-react): integrate React Native docs into Storybook * chore(eds-core-react): refactor PlatformTabs and update mobile Storybook setup * refactor(eds-core-react): replace DOM mutation in PlatformTabs with children wrapping Replace the fragile document.querySelector sibling-hiding approach with a children prop pattern. PlatformTabs now owns all content, renders React or React Native tab conditionally, uses EDS tokens for colors, and adds proper ARIA roles with keyboard navigation. Also moves Typography mobile docs from the legacy Typography page to Typography/TypographyNext, and reverts prerequisites.json to React 19. * refactor(eds-core-react): complete ARIA tabs contract in PlatformTabs and fix stale links Complete the WAI-ARIA tabpanel contract: add id and aria-controls to each tab button, wrap content in role="tabpanel" with id, aria-labelledby and tabIndex, and add Home/End key handling. Also replace blob/develop with blob/main in Checkbox, Radio, Input and Switch docs sourceUrls introduced on this branch. * fix(eds-core-react): address PR review feedback on mobile platform tabs - Fix Typography link in Components.mdx to point to TypographyNext docs - Hardcode inactive tab color in PlatformTabs (#6F6F6F) — EDS semantic tokens resolve incorrectly in the Storybook docs MDX context; hardcoded value is intentional for this Storybook-only utility component - Add font weight distinction (700 active / 400 inactive) to improve tab state contrast per reviewer feedback - Remove snyk.advanced.autoSelectOrganization from shared .vscode/settings.json - Update Intro.mdx to mention React Native alongside React - Update README Node.js prerequisite to match .nvmrc (24.16.0)
1 parent b2631e7 commit b69a15b

21 files changed

Lines changed: 4167 additions & 1097 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ Before you begin, ensure you have the following installed:
154154
Source: prerequisites.json (generated from .nvmrc and package.json)
155155
-->
156156

157-
* **Node.js** — Version 22.12.0 or compatible
157+
* **Node.js** — Version 24.16.0 or compatible
158158
* **pnpm** — Version 10.15.0 or higher (install globally with `npm install -g pnpm@10.15.0`)
159159
* **Git** — For version control
160160

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { useState, useRef, type ReactNode, type KeyboardEvent } from 'react'
2+
3+
type Tab = 'react' | 'native'
4+
5+
type PlatformTabsProps = {
6+
mobile?: ReactNode
7+
children?: ReactNode
8+
}
9+
10+
const TABS: { id: Tab; label: string }[] = [
11+
{ id: 'react', label: 'React' },
12+
{ id: 'native', label: 'React Native' },
13+
]
14+
15+
export const PlatformTabs = ({ mobile, children }: PlatformTabsProps) => {
16+
const [activeTab, setActiveTab] = useState<Tab>('react')
17+
const tabRefs = useRef<(HTMLButtonElement | null)[]>([])
18+
19+
if (!mobile) return <>{children}</>
20+
21+
const handleKeyDown = (e: KeyboardEvent, index: number) => {
22+
if (e.key === 'ArrowRight') {
23+
const next = (index + 1) % TABS.length
24+
tabRefs.current[next]?.focus()
25+
setActiveTab(TABS[next].id)
26+
} else if (e.key === 'ArrowLeft') {
27+
const prev = (index - 1 + TABS.length) % TABS.length
28+
tabRefs.current[prev]?.focus()
29+
setActiveTab(TABS[prev].id)
30+
} else if (e.key === 'Home') {
31+
e.preventDefault()
32+
tabRefs.current[0]?.focus()
33+
setActiveTab(TABS[0].id)
34+
} else if (e.key === 'End') {
35+
e.preventDefault()
36+
tabRefs.current[TABS.length - 1]?.focus()
37+
setActiveTab(TABS[TABS.length - 1].id)
38+
}
39+
}
40+
41+
return (
42+
<div>
43+
<div
44+
role="tablist"
45+
aria-label="Platform"
46+
style={{
47+
display: 'flex',
48+
borderBottom: '1px solid var(--eds-color-border-medium, #DCDCDC)',
49+
marginBottom: '1.5rem',
50+
}}
51+
>
52+
{TABS.map(({ id, label }, index) => (
53+
<button
54+
key={id}
55+
id={`platform-tab-${id}`}
56+
ref={(el) => {
57+
tabRefs.current[index] = el
58+
}}
59+
role="tab"
60+
aria-selected={activeTab === id}
61+
aria-controls={`platform-tabpanel-${id}`}
62+
tabIndex={activeTab === id ? 0 : -1}
63+
onClick={() => setActiveTab(id)}
64+
onKeyDown={(e) => handleKeyDown(e, index)}
65+
style={{
66+
padding: '0.5rem 1rem',
67+
border: 'none',
68+
background: 'none',
69+
cursor: 'pointer',
70+
fontFamily:
71+
'var(--eds-typography-ui-body-font-family), sans-serif',
72+
fontSize: '0.875rem',
73+
fontWeight: activeTab === id ? 700 : 400,
74+
color:
75+
activeTab === id
76+
? 'var(--eds-color-interactive-primary, #007079)'
77+
: '#585858',
78+
borderBottom:
79+
activeTab === id
80+
? '1px solid var(--eds-color-interactive-primary, #007079)'
81+
: '1px solid transparent',
82+
marginBottom: '-1px',
83+
}}
84+
>
85+
{label}
86+
</button>
87+
))}
88+
</div>
89+
<div
90+
role="tabpanel"
91+
id={`platform-tabpanel-${activeTab}`}
92+
aria-labelledby={`platform-tab-${activeTab}`}
93+
tabIndex={0}
94+
>
95+
{activeTab === 'react' ? children : mobile}
96+
</div>
97+
</div>
98+
)
99+
}

packages/eds-core-react/.storybook/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export { Story } from './Story'
22
export { Stack } from './Stack'
33
export { Links } from './Links'
44
export { InfoCard } from './InfoCard'
5+
export { PlatformTabs } from './PlatformTabs'

packages/eds-core-react/.storybook/main.mjs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import remarkGfm from 'remark-gfm'
2+
13
const config = {
24
typescript: {
35
reactDocgen: 'react-docgen-typescript',
@@ -11,8 +13,16 @@ const config = {
1113
addons: [
1214
'@storybook/addon-a11y',
1315
'@storybook/addon-links',
14-
'./remark-gfm-preset.mjs',
15-
'@storybook/addon-docs',
16+
{
17+
name: '@storybook/addon-docs',
18+
options: {
19+
mdxPluginOptions: {
20+
mdxCompileOptions: {
21+
remarkPlugins: [remarkGfm],
22+
},
23+
},
24+
},
25+
},
1626
],
1727

1828
features: {

packages/eds-core-react/.storybook/preview.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ const preview = {
7171
'EdsProvider',
7272
'EDS 2.0 (beta)',
7373
['About', 'Icon', 'Inputs'],
74+
'Mobile',
75+
['About', 'Components'],
7476
'Data Display',
7577
'Feedback',
7678
'Inputs',

packages/eds-core-react/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"react"
5454
],
5555
"devDependencies": {
56+
"@equinor/eds-mobile-components": "^0.2.0",
5657
"@figma/code-connect": "^1.4.4",
5758
"@playwright/test": "^1.59.1",
5859
"@rollup/plugin-babel": "^7.0.0",

packages/eds-core-react/src/components/EdsProvider/EdsProvider.docs.mdx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
1-
import { Links } from './../../../.storybook/components'
1+
import { Links, PlatformTabs } from './../../../.storybook/components'
22
import { Primary, Canvas, Controls, Meta } from '@storybook/addon-docs/blocks'
33
import * as ComponentStories from './EdsProvider.stories'
4+
import MobileDocs from '@equinor/eds-mobile-components/docs/EDSProvider.mdx'
45

56
<Meta of={ComponentStories} />
67

78
# EdsProvider
89
Provider used to set global values for EDS react components.
910

11+
<PlatformTabs mobile={<>
12+
<Links
13+
sourceUrl="https://github.com/equinor/design-system-mobile/blob/main/packages/components/src/components/EDSProvider/EDSProvider.tsx"
14+
npmUrl="https://www.npmjs.com/package/@equinor/eds-mobile-components"
15+
/>
16+
<MobileDocs />
17+
</>}>
18+
1019
<Links
1120
figmaUrl="https://www.figma.com/file/luNobpDIWTuQ9rhyovGLBI/Desktop?node-id=4950%3A0"
1221
npmUrl="https://www.npmjs.com/package/@equinor/eds-core-react"
@@ -56,3 +65,5 @@ If you have your own state managment you can just set the `density` prop on `Eds
5665
<Canvas of={ComponentStories.CustomState} />
5766

5867

68+
69+
</PlatformTabs>

packages/eds-core-react/src/components/Typography/Typography.docs.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as ComponentStories from './Typography.stories'
66

77
# Typography
88

9+
910
Presents hierarchy and organises information as clearly and efficiently as possible,
1011
therefore text sizes and styles were developed to balance content density and reading comfort.
1112

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { Primary, Canvas, Controls, Meta } from '@storybook/addon-docs/blocks'
2+
import * as TypographyNewStories from './Typography.new.stories'
3+
import { Links, PlatformTabs } from './../../../.storybook/components'
4+
import MobileDocs from '@equinor/eds-mobile-components/docs/Typography.mdx'
5+
6+
<Meta of={TypographyNewStories} />
7+
8+
# TypographyNext
9+
10+
<PlatformTabs mobile={<>
11+
<Links
12+
sourceUrl="https://github.com/equinor/design-system-mobile/blob/main/packages/components/src/components/Typography/Typography.tsx"
13+
npmUrl="https://www.npmjs.com/package/@equinor/eds-mobile-components"
14+
/>
15+
<MobileDocs />
16+
</>}>
17+
18+
The next generation of the Typography component system. It will replace the existing Typography component in the next major version.
19+
20+
## Features
21+
22+
The new typography system provides baseline grid alignment for consistent vertical rhythm.
23+
24+
## Usage
25+
26+
Import the TypographyNext component with alias for a seamless experience:
27+
28+
```tsx
29+
import { TypographyNext as Typography, Heading, Paragraph } from '@equinor/eds-core-react'
30+
31+
<Heading as="h1">Welcome</Heading>
32+
<Paragraph size="lg">This is a paragraph with the new typography system.</Paragraph>
33+
<Typography family="ui" size="md" lineHeight="default" baseline="grid" weight="normal" tracking="normal">
34+
Flexible inline text
35+
</Typography>
36+
```
37+
38+
## Proposed migration timeline
39+
40+
1. **Now**: Use the new components with aliasing ( `TypographyNext as Typography` )
41+
2. **Parallel support**: 6-12 months of both systems supported
42+
43+
## Key Differences from current typography
44+
45+
- **Component names**: Two opinionated semantic components - `Heading` , and `Paragraph`
46+
- **Baseline alignment**: Built-in support for baseline grid alignment
47+
- **Props**: More explicit control over typography properties
48+
- **Semantic HTML**: `Heading` renders proper heading tags, `Paragraph` renders `<p>` tags
49+
50+
<Primary />
51+
<Controls />
52+
53+
## Stories
54+
55+
### Playground
56+
57+
The TypographyNext component provides full control over typography properties. Use it for inline text with specific styling needs.
58+
59+
<Canvas of={TypographyNewStories.Playground} />
60+
61+
### As Link
62+
63+
TypographyNext can be used as a link by setting `as="a"` and providing an `href` prop. All standard anchor attributes are supported, such as `target` and `rel` for external links.
64+
65+
<Canvas of={TypographyNewStories.AsLink} />
66+
67+
</PlatformTabs>

packages/eds-core-react/src/components/next/Button/Button.docs.mdx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Primary, Canvas, Controls, Meta } from '@storybook/addon-docs/blocks'
22
import * as ComponentStories from './Button.stories'
3+
import { Links, PlatformTabs } from './../../../../.storybook/components'
4+
import MobileDocs from '@equinor/eds-mobile-components/docs/Button.mdx'
35

46
<Meta of={ComponentStories} />
57

@@ -9,6 +11,15 @@ Button component for triggering actions.
911

1012
**⚠️ Beta Component** - This component is under active development and may have breaking changes.
1113

14+
15+
<PlatformTabs mobile={<>
16+
<Links
17+
sourceUrl="https://github.com/equinor/design-system-mobile/blob/main/packages/components/src/components/Button.tsx"
18+
npmUrl="https://www.npmjs.com/package/@equinor/eds-mobile-components"
19+
/>
20+
<MobileDocs />
21+
</>}>
22+
1223
```bash
1324
npm install @equinor/eds-core-react@beta
1425
```
@@ -130,3 +141,5 @@ Complete overview of all variants, sizes, and color appearances.
130141
Multi-line button labels are an anti-pattern — they are harder to scan and weaker as call-to-action elements. Shorten the label instead. The `multiline` prop exists only as a safety valve for edge cases where a long label cannot be avoided.
131142

132143
<Canvas of={ComponentStories.MultilineAntiPattern} />
144+
145+
</PlatformTabs>

0 commit comments

Comments
 (0)