Skip to content

Commit 4692aee

Browse files
authored
IDE — Enable keyboard navigation, move alternativeText to individual files and chat (#890)
* moved alt text to individual tabs and chat * allow tab switch before animation completes * add useTabs hook * simplified useTabs hook * implement useTabs hook in IDE * fix tests * remove alt text from IDE usage * fix my bug * fix failing test suite * update docs * add changeset * typo * minor fixes * replace usage of Idx with Index * update changeset * rename variables * replace i with index
1 parent 872bdcf commit 4692aee

File tree

12 files changed

+619
-347
lines changed

12 files changed

+619
-347
lines changed

.changeset/warm-ads-return.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
'@primer/react-brand': minor
3+
---
4+
5+
Enabled keyboard navigation in the `IDE` component and made the contents navigable by screen readers.
6+
7+
⚠️ Breaking changes
8+
9+
The `alternativeText` prop on the `IDE` component has been removed in favour of more granular descriptive text.
10+
11+
<table width="100%">
12+
<tr>
13+
<th> Before</th>
14+
</tr>
15+
<tr>
16+
<td valign="top">
17+
18+
```tsx
19+
<IDE alternativeText="A user asks how to concatenate arrays in JavaScript, Copilot demonstrates using the concat method, and the user confirms it worked.">
20+
<IDE.Chat />
21+
</IDE>
22+
```
23+
24+
</td>
25+
</tr>
26+
<tr>
27+
<th> After</th>
28+
</tr>
29+
<tr>
30+
<td valign="top">
31+
32+
```tsx
33+
<IDE>
34+
<IDE.Chat alternativeText="A user asks how to concatenate arrays in JavaScript, Copilot demonstrates using the concat method, and the user confirms it worked." />
35+
</IDE>
36+
```
37+
38+
</td>
39+
</tr>
40+
</table>
41+
42+
<table width="100%">
43+
<tr>
44+
<th> Before</th>
45+
</tr>
46+
<tr>
47+
<td valign="top">
48+
49+
```tsx
50+
<IDE alternativeText="TypeScript sentiment analysis function with D3.js visualization.">
51+
<IDE.Editor
52+
files={[
53+
{
54+
name: 'index.js',
55+
},
56+
]}
57+
/>
58+
</IDE>
59+
```
60+
61+
</td>
62+
</tr>
63+
<tr>
64+
<th> After</th>
65+
</tr>
66+
<tr>
67+
<td valign="top">
68+
69+
```tsx
70+
<IDE>
71+
<IDE.Editor
72+
files={[
73+
{
74+
name: 'index.js',
75+
alternativeText: 'TypeScript sentiment analysis function with D3.js visualization.',
76+
// ...
77+
},
78+
]}
79+
/>
80+
</IDE>
81+
```
82+
83+
</td>
84+
</tr>
85+
</table>
86+
87+
🔗 [See the documentation for example usage, and more information on accessibility in the `IDE` component](https://primer.style/brand/components/IDE#accessibility)

apps/docs/content/components/IDE.mdx

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,14 @@ See Storybook for examples of `IDE` using `highlight.js` syntax highlighter.
3434
</Note>
3535

3636
```jsx live
37-
<IDE
38-
alternativeText="An interactive development environment displaying two code files: 'index.js' and 'parse_expenses.py'. The 'index.js' file contains JavaScript code for a function that fetches data and draws a scatterplot. The 'parse_expenses.py' file contains Python code for a function that parses expense data."
39-
height={500}
40-
>
37+
<IDE height={500}>
4138
<IDE.Editor
4239
showReplayButton={false}
4340
files={[
4441
{
4542
name: 'index.js',
43+
alternativeText:
44+
'TypeScript sentiment analysis function with D3.js visualization.',
4645
code: `import { fetch } from "fetch-h2";
4746
4847
async function isPositive(text: string): Promise<boolean> {
@@ -60,8 +59,10 @@ async function isPositive(text: string): Promise<boolean> {
6059
},
6160
{
6261
name: 'parse_expenses.py',
62+
alternativeText:
63+
'Python function that converts financial transaction strings into structured data, parsing dates, amounts, and currencies while skipping comments',
6364
code: `import datetime
64-
65+
6566
def parse_expenses(expenses_string):
6667
Ignore lines starting with #.
6768
Parse the date using datetime.
@@ -90,14 +91,14 @@ return expenses`,
9091
Line-by-line animations can be enabled using pre-formatted prop values. A replay button is enabled by default.
9192

9293
```jsx live
93-
<IDE
94-
alternativeText="An interactive development environment displaying one code file: 'index.js'. The 'index.js' file contains JavaScript code for a function that fetches data and draws a scatterplot. Each line animates into view line-by-line."
95-
height={500}
96-
>
94+
<IDE height={500}>
9795
<IDE.Editor
9896
files={[
9997
{
10098
name: 'index.js',
99+
alternativeText:
100+
'TypeScript sentiment analysis function with D3.js visualization.',
101+
101102
code: `import { fetch } from "fetch-h2";
102103
103104
async function isPositive(text: string): Promise<boolean> {
@@ -125,16 +126,16 @@ async function isPositive(text: string): Promise<boolean> {
125126
Simulate GitHub Copilot suggestions using the `suggestedLineStart` property.
126127

127128
```jsx live
128-
<IDE
129-
alternativeText="An interactive development environment displaying one code file: 'index.js'. The 'index.js' file contains JavaScript code for a function that fetches data and draws a scatterplot. Line 6 onwards contains a GitHub Copilot auto suggestion."
130-
height={500}
131-
>
129+
<IDE height={500}>
132130
<IDE.Editor
133131
files={[
134132
{
135133
name: 'index.js',
136134
// Line number at which the suggestion will apply
137135
suggestedLineStart: 6,
136+
alternativeText:
137+
'TypeScript sentiment analysis function with D3.js visualization. Copilot suggests completing the scatterplot with SVG creation and circle attributes.',
138+
138139
code: `import { fetch } from "fetch-h2";
139140
140141
async function isPositive(text: string): Promise<boolean> {
@@ -172,11 +173,9 @@ See Storybook for a comprehensive example of the Chat feature, inclusive of synt
172173
</Note>
173174

174175
```jsx live
175-
<IDE
176-
alternativeText="A chat interface showing a conversation between a user named 'monalisa' and an assistant named 'GitHub Copilot'. Monalisa asks 'How do I concatenate two arrays in JavaScript"
177-
height={700}
178-
>
176+
<IDE height={700}>
179177
<IDE.Chat
178+
alternativeText="A user asks how to concatenate arrays in JavaScript, Copilot demonstrates using the concat method, and the user confirms it worked."
180179
script={[
181180
{
182181
role: 'user',
@@ -223,6 +222,8 @@ A `glass` variant is available to simplify placement of the `IDE` on background
223222
files={[
224223
{
225224
name: 'index.js',
225+
alternativeText:
226+
'TypeScript sentiment analysis function with D3.js visualization.',
226227
code: `import { fetch } from "fetch-h2";
227228
228229
async function isPositive(text: string): Promise<boolean> {
@@ -240,8 +241,11 @@ async function isPositive(text: string): Promise<boolean> {
240241
},
241242
{
242243
name: 'parse_expenses.py',
244+
alternativeText:
245+
'Python function description and docstring showing how to parse financial transactions with example date, amount, and currency formats.',
246+
243247
code: `import datetime
244-
248+
245249
def parse_expenses(expenses_string):
246250
Ignore lines starting with #.
247251
Parse the date using datetime.
@@ -257,15 +261,25 @@ Example expenses_string:
257261
</Box>
258262
```
259263

264+
## Accessibility
265+
266+
The full contents of `IDE.Chat` and `IDE.Editor` are intentionally not made available to users of assistive technologies. Summaries of the content — which must be provided using the `alternativeText` prop — are made available instead.
267+
268+
When writing `alternativeText`, consider the following:
269+
270+
- The `IDE.Chat` script should be summarized in a way that conveys the essential information of the conversation.
271+
- The `IDE.Editor` code should be summarized in a way that conveys the essential information of the code snippet, specifically noting any suggestions made by Copilot.
272+
273+
In both cases, the goal is to provide a meaningful summary of the content that will allow users of assistive technologies to understand the purpose and scope of the conversation or code snippet, without needing to understand the full content.
274+
260275
## Component props
261276

262277
### IDE <Label>Required</Label>
263278

264-
| name | type | default | required | description |
265-
| ----------------- | --------------------------------------------------------------- | --------- | -------- | --------------------------------------------- |
266-
| `alternativeText` | `string` | | `true` | Alternative description of the IDE |
267-
| `height` | `number` | `800` | `false` | The optionally configurable height of the IDE |
268-
| `variant` | <PropTableValues values={['default', 'glass']} addLineBreaks /> | `default` | `false` | Alternative presentation of the IDE |
279+
| name | type | default | required | description |
280+
| --------- | --------------------------------------------------------------- | --------- | -------- | --------------------------------------------- |
281+
| `height` | `number` | `800` | `false` | The optionally configurable height of the IDE |
282+
| `variant` | <PropTableValues values={['default', 'glass']} addLineBreaks /> | `default` | `false` | Alternative presentation of the IDE |
269283

270284
### IDE.Editor
271285

@@ -282,7 +296,8 @@ Supported file extensions in the editor include: <PropTableValues values={IDEFil
282296

283297
### IDE.Chat
284298

285-
| name | type | default | required | description |
286-
| ---------------- | ----------------------- | ------- | -------- | ---------------------------- |
287-
| `script` | [`IDEChatMessage[]`](#) | | `false` | Array of chat messages |
288-
| `animationDelay` | `number` | `3000` | `false` | Delay for the chat animation |
299+
| name | type | default | required | description |
300+
| ----------------- | ----------------------- | ------- | -------- | ------------------------------------------------------------------------------ |
301+
| `script` | [`IDEChatMessage[]`](#) | | `true` | Array of chat messages |
302+
| `alternativeText` | `string` | | `true` | Alternative description of the chat script for users of assistive technologies |
303+
| `animationDelay` | `number` | `3000` | `false` | Delay for the chat animation |

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/react/src/IDE/IDE.features.stories.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {River, RiverBreakout} from '../river'
1717
import {Text} from '../Text'
1818
import {ThemeProvider, useTheme} from '../ThemeProvider'
1919
import {Timeline} from '../Timeline'
20-
import {chatScript, files, singleFile} from './fixtures/content'
20+
import {chatScript, chatScriptAlt, files, singleFile} from './fixtures/content'
2121
import storyStyles from './IDE.stories.module.css'
2222

2323
import './IDE.stories.hljs.theme.css'
@@ -93,7 +93,7 @@ EditorNoReplayButton.storyName = 'Editor Only (no replay button)'
9393
export const ChatOnly = args => {
9494
return (
9595
<IDE {...args}>
96-
<IDE.Chat script={chatScript}></IDE.Chat>
96+
<IDE.Chat script={chatScript} alternativeText={chatScriptAlt}></IDE.Chat>
9797
</IDE>
9898
)
9999
}
@@ -184,7 +184,7 @@ export const WithRiver = args => {
184184
className={storyStyles.riverVisual}
185185
>
186186
<IDE {...args} height={700} variant="glass">
187-
<IDE.Chat script={chatScript}></IDE.Chat>
187+
<IDE.Chat script={chatScript} alternativeText={chatScriptAlt}></IDE.Chat>
188188
</IDE>
189189
</Box>
190190
</River.Visual>
@@ -237,7 +237,7 @@ PerspectiveExampleLight.decorators = [
237237

238238
export const AllGlass = args => (
239239
<IDE {...args} variant="glass">
240-
<IDE.Chat script={chatScript}></IDE.Chat>
240+
<IDE.Chat script={chatScript} alternativeText={chatScriptAlt}></IDE.Chat>
241241
<IDE.Editor size="large" files={files} showReplayButton={false} />
242242
</IDE>
243243
)

packages/react/src/IDE/IDE.module.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@
6666
}
6767
}
6868

69+
.IDE__Chat-wrapper {
70+
display: contents;
71+
}
72+
6973
.IDE__Chat-message-user {
7074
display: inline-flex;
7175
gap: var(--base-size-8);
@@ -206,6 +210,7 @@
206210

207211
.IDE__Editor-tab.active {
208212
color: var(--brand-color-text-default);
213+
z-index: 1;
209214
}
210215

211216
.IDE--default .IDE__Editor-tab.active {

packages/react/src/IDE/IDE.module.css.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ declare const styles: {
55
readonly "IDE--default": string;
66
readonly "IDE__Chat": string;
77
readonly "IDE--full-exp": string;
8+
readonly "IDE__Chat-wrapper": string;
89
readonly "IDE__Chat-message-user": string;
910
readonly "IDE__Chat-messages": string;
1011
readonly "IDE__Chat-input-area": string;

packages/react/src/IDE/IDE.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import python from 'highlight.js/lib/languages/python'
88

99
import './IDE.stories.hljs.theme.css'
1010

11-
import {chatScript, defaultFiles} from './fixtures/content'
11+
import {chatScript, chatScriptAlt, defaultFiles} from './fixtures/content'
1212

1313
hljs.registerLanguage('javascript', javascript)
1414
hljs.registerLanguage('python', python)
@@ -81,7 +81,7 @@ type StoryProps = {
8181
const Template: StoryFn<StoryProps> = ({showChat, showReplayButton, ...args}) => {
8282
return (
8383
<IDE {...args}>
84-
{showChat && <IDE.Chat script={chatScript}></IDE.Chat>}
84+
{showChat && <IDE.Chat script={chatScript} alternativeText={chatScriptAlt}></IDE.Chat>}
8585
<IDE.Editor files={defaultFiles} showReplayButton={showReplayButton} />
8686
</IDE>
8787
)

0 commit comments

Comments
 (0)