Skip to content

Commit 526a15f

Browse files
authored
fix: api explorer accessibility issues from test pass findings (#3706)
* fix accessibility * fix keyboard select all * narrate number of items * announce row details on narrator * fix unnecessary headings * fix checkbox announcement
1 parent cd845a5 commit 526a15f

File tree

15 files changed

+217
-113
lines changed

15 files changed

+217
-113
lines changed

src/app/services/context/popups-context/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export interface PopupsComponent<Data = {}> {
88
}
99

1010
interface PopupSettings {
11-
title: React.ReactNode | string;
11+
title: string;
1212
subtitle?: string;
1313
width?: width;
1414
renderFooter?: () => JSX.Element;

src/app/views/common/popups/DrawerWrapper.tsx

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import {
44
DrawerBody,
55
DrawerFooter,
66
DrawerHeader,
7+
DrawerHeaderNavigation,
78
DrawerHeaderTitle,
89
Button,
910
Spinner,
1011
makeStyles,
11-
tokens,
1212
Tooltip
1313
} from '@fluentui/react-components';
1414
import { ArrowLeft24Regular, Dismiss24Regular } from '@fluentui/react-icons';
@@ -20,11 +20,23 @@ const useDrawerStyles = makeStyles({
2020
root: {
2121
width: '1100px'
2222
},
23-
button: {
24-
marginInlineEnd: '20px'
23+
closeButton: {
24+
alignSelf: 'flex-end'
2525
},
2626
body: {
2727
padding: 0
28+
},
29+
header: {
30+
display: 'flex',
31+
flexDirection: 'row'
32+
},
33+
headerTitle: {
34+
display: 'flex',
35+
flex: '1',
36+
marginInlineStart: '20px'
37+
},
38+
headerNavigation: {
39+
display: 'flex'
2840
}
2941
});
3042

@@ -69,18 +81,8 @@ export function DrawerWrapper(props: WrapperProps) {
6981
size={getDrawerSize()}
7082
className={drawerStyles.root}
7183
>
72-
<DrawerHeader>
73-
<DrawerHeaderTitle action={
74-
<Tooltip
75-
content={translateMessage('Close')} relationship='label'>
76-
<Button
77-
icon={<Dismiss24Regular />}
78-
appearance='subtle'
79-
onClick={() => dismissPopup()}
80-
aria-label={translateMessage('Close')}
81-
/>
82-
</Tooltip>
83-
}>
84+
<DrawerHeader className={drawerStyles.header}>
85+
<DrawerHeaderNavigation className={drawerStyles.headerNavigation}>
8486
{showBackButton && (
8587
<Tooltip
8688
content={translateMessage('Back')}
@@ -90,11 +92,23 @@ export function DrawerWrapper(props: WrapperProps) {
9092
appearance='subtle'
9193
onClick={() => dismissPopup()}
9294
aria-label={translateMessage('Back')}
93-
className={drawerStyles.button}
9495
/>
9596
</Tooltip>
9697
)}
97-
{title || ''}
98+
</DrawerHeaderNavigation>
99+
<DrawerHeaderTitle className={drawerStyles.headerTitle} action={
100+
<Tooltip
101+
content={translateMessage('Close')} relationship='label'>
102+
<Button
103+
icon={<Dismiss24Regular />}
104+
appearance='subtle'
105+
onClick={() => dismissPopup()}
106+
aria-label={translateMessage('Close')}
107+
className={drawerStyles.closeButton}
108+
/>
109+
</Tooltip>
110+
}>
111+
{translateMessage(title) || ''}
98112
</DrawerHeaderTitle>
99113
</DrawerHeader>
100114

src/app/views/query-response/response/ResponseDisplay.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ const ResponseDisplay = (props: ResponseDisplayProps) => {
2020
case 'text/html':
2121
return <Monaco body={body} language='text/html' readOnly={true} />;
2222

23+
case 'application/json':
24+
return (
25+
<div style={{ flex: 1, height: '100%', display: 'flex' }}>
26+
<Monaco body={body} readOnly language="application/json" />
27+
</div>
28+
);
29+
2330
default:
2431
if (isImageResponse(contentType) && typeof body !== 'string') {
2532
return (
@@ -28,7 +35,7 @@ const ResponseDisplay = (props: ResponseDisplayProps) => {
2835
}
2936
return (
3037
<div style={{ flex: 1, height: '100%', display: 'flex' }}>
31-
<Monaco body={body} readOnly language="application/json" />
38+
<Monaco body={body} readOnly language="text/plain" />
3239
</div>
3340
);
3441
}

src/app/views/query-response/snippets/Snippets.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ const trackLinkClickedEvent = (
127127
telemetry.trackLinkClickEvent(e.currentTarget.href, componentName);
128128
};
129129

130-
const addExtraSnippetInformation = (language: string): JSX.Element => {
130+
const ExtraSnippetInformation: React.FC<{ language: string }> = ({ language }) => {
131131
const styles = useSnippetStyles();
132132
const { sdkDownloadLink, sdkDocLink } = supportedLanguages[language];
133133
const libParagraph =
@@ -253,7 +253,7 @@ const SnippetContent: React.FC<SnippetContentProps> = (
253253
body={snippet}
254254
language={language}
255255
readOnly={true}
256-
extraInfoElement={addExtraSnippetInformation(props.language)}
256+
extraInfoElement={<ExtraSnippetInformation language={props.language} />}
257257
/>
258258
</div>
259259
</div>

src/app/views/query-runner/query-input/QueryInput.tsx

Lines changed: 54 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import React, { useContext } from 'react';
1+
import { useContext } from 'react';
22
import {
33
Dropdown,
44
Field,
5+
Tooltip,
56
Option,
67
Badge,
78
makeStyles,
@@ -112,57 +113,67 @@ const QueryInput = (props: IQueryInputProps) => {
112113
showError ? translateMessage('Sign in to use this method') : undefined
113114
}
114115
validationState={showError ? 'error' : 'none'}
116+
>
117+
<Tooltip
118+
content={translateMessage('HTTP request method')}
119+
relationship='description'
120+
withArrow>
121+
<Dropdown
122+
aria-label={translateMessage('HTTP request method option')}
123+
placeholder='Select method'
124+
value={sampleQuery.selectedVerb}
125+
className={classes.smallDropdown}
126+
button={{ style: { color: selectedBadgeColor } }}
127+
onOptionSelect={(event, data) => {
128+
handleOnMethodChange({
129+
key: data.optionValue,
130+
text: data.optionValue
131+
});
132+
}}
133+
>
134+
{Object.values(httpMethods).map((method) => {
135+
const badgeColor = methodColors[method] || 'brand';
136+
137+
return (
138+
<Option
139+
key={method}
140+
value={method}
141+
text={method}
142+
>
143+
<Badge appearance='ghost' color={badgeColor}>
144+
{method}
145+
</Badge>
146+
</Option>
147+
);
148+
})}
149+
</Dropdown>
150+
</Tooltip>
151+
</Field>
152+
153+
<Tooltip
154+
content={translateMessage('Microsoft Graph API Version')}
155+
relationship='description'
156+
withArrow
115157
>
116158
<Dropdown
117-
aria-label={translateMessage('HTTP request method option')}
118-
placeholder='Select method'
119-
value={sampleQuery.selectedVerb}
120-
className={classes.smallDropdown}
121-
button={{ style: { color: selectedBadgeColor } }}
159+
aria-label={translateMessage('Microsoft Graph API Version option')}
160+
placeholder='Select a version'
161+
value={sampleQuery.selectedVersion || GRAPH_API_VERSIONS[0]}
122162
onOptionSelect={(event, data) => {
123-
handleOnMethodChange({
163+
handleOnVersionChange({
124164
key: data.optionValue,
125165
text: data.optionValue
126166
});
127167
}}
168+
className={classes.smallDropdown}
128169
>
129-
{Object.values(httpMethods).map((method) => {
130-
const badgeColor = methodColors[method] || 'brand';
131-
132-
return (
133-
<Option
134-
key={method}
135-
value={method}
136-
text={method}
137-
>
138-
<Badge appearance='ghost' color={badgeColor}>
139-
{method}
140-
</Badge>
141-
</Option>
142-
);
143-
})}
170+
{GRAPH_API_VERSIONS.map((version) => (
171+
<Option key={version} value={version}>
172+
{version}
173+
</Option>
174+
))}
144175
</Dropdown>
145-
</Field>
146-
147-
<Dropdown
148-
aria-label={translateMessage('Microsoft Graph API Version option')}
149-
placeholder='Select a version'
150-
value={sampleQuery.selectedVersion || GRAPH_API_VERSIONS[0]}
151-
onOptionSelect={(event, data) => {
152-
handleOnVersionChange({
153-
key: data.optionValue,
154-
text: data.optionValue
155-
});
156-
}}
157-
className={classes.smallDropdown}
158-
>
159-
{GRAPH_API_VERSIONS.map((version) => (
160-
<Option key={version} value={version}>
161-
{version}
162-
</Option>
163-
))}
164-
</Dropdown>
165-
176+
</Tooltip>
166177
<AutoComplete contentChanged={contentChanged} runQuery={runQuery} />
167178

168179
<SubmitButton
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { makeStyles, tokens } from '@fluentui/react-components';
2+
3+
export const useSuggestionStyles = makeStyles({
4+
suggestions: {
5+
maxHeight: '25vh',
6+
overflow: 'auto',
7+
position: 'absolute',
8+
minWidth: '40%',
9+
maxWidth: '50%',
10+
zIndex: 1,
11+
cursor: 'pointer',
12+
color: tokens.colorNeutralForeground1,
13+
'@media (max-width: 480px)': {
14+
minWidth: '100%',
15+
maxWidth: '100%'
16+
}
17+
},
18+
suggestionOption: {
19+
backgroundColor: tokens.colorNeutralBackground1,
20+
position: 'relative',
21+
whiteSpace: 'nowrap',
22+
textOverflow: 'ellipsis',
23+
border: `1px solid ${tokens.colorNeutralStroke1}`,
24+
overflow: 'hidden',
25+
'&:hover': {
26+
background: tokens.colorNeutralBackground1Hover
27+
}
28+
},
29+
suggestionActive: {
30+
cursor: 'pointer',
31+
position: 'relative',
32+
whiteSpace: 'nowrap',
33+
textOverflow: 'ellipsis',
34+
border: '1px solid',
35+
overflow: 'hidden',
36+
wordWrap: 'normal',
37+
backgroundColor: tokens.colorNeutralBackground1Selected
38+
}
39+
});

src/app/views/query-runner/query-input/auto-complete/suggestion-list/SuggestionsList.tsx

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,34 @@
1-
import { Label } from '@fluentui/react-components';
1+
import { mergeClasses, Option } from '@fluentui/react-components';
22
import { createRef, useEffect } from 'react';
3-
43
import { ISuggestionsList } from '../../../../../../types/auto-complete';
4+
import { useSuggestionStyles } from './SuggestionsList.styles';
55

6-
const SuggestionsList = ({ filteredSuggestions, activeSuggestion, onSuggestionSelected }: ISuggestionsList) => {
76

8-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
9-
const refs = filteredSuggestions.reduce((ref: any, value: string) => {
10-
const itemIndex = filteredSuggestions.findIndex(k => k === value);
11-
ref[itemIndex] = createRef();
12-
return ref;
13-
}, {});
7+
const SuggestionsList = ({ filteredSuggestions, activeSuggestion, onSuggestionSelected }: ISuggestionsList) => {
8+
const styles = useSuggestionStyles();
9+
const itemRefs = filteredSuggestions.map(() => createRef<HTMLDivElement>());
1410

1511
useEffect(() => {
16-
if (refs && filteredSuggestions.length > 0) {
17-
if (refs[activeSuggestion] && refs[activeSuggestion].current) {
18-
refs[activeSuggestion].current.scrollIntoView({
19-
behavior: 'smooth', block: 'nearest', inline: 'start'
20-
});
21-
}
12+
if (filteredSuggestions.length > 0 && itemRefs[activeSuggestion]?.current) {
13+
itemRefs[activeSuggestion].current.scrollIntoView({
14+
behavior: 'smooth', block: 'nearest', inline: 'start'
15+
});
2216
}
23-
}, [activeSuggestion]);
17+
}, [activeSuggestion, itemRefs, filteredSuggestions]);
2418

2519
return (
26-
<ul tabIndex={-1}>
27-
{filteredSuggestions.map((suggestion: string, index: number) => {
28-
return (
29-
<li
30-
key={index}
31-
ref={refs[index]}
32-
onClick={() => onSuggestionSelected(suggestion)}
33-
>
34-
<Label>
35-
{suggestion}
36-
</Label>
37-
</li>
38-
);
39-
})}
20+
<ul tabIndex={-1} className={styles.suggestions}>
21+
{filteredSuggestions.map((suggestion: string, index: number) => (
22+
<Option
23+
className={mergeClasses(activeSuggestion === index ? styles.suggestionActive : styles.suggestionOption)}
24+
key={suggestion}
25+
ref={itemRefs[index]}
26+
onClick={() => onSuggestionSelected(suggestion)}
27+
aria-selected={activeSuggestion === index}
28+
>
29+
{suggestion}
30+
</Option>
31+
))}
4032
</ul>
4133
);
4234
};

src/app/views/query-runner/request/permissions/Permission.styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const permissionStyles = makeStyles({
88
marginLeft: tokens.spacingHorizontalS
99
},
1010
table: {
11-
tableLayout: 'fixed',
11+
tableLayout: 'auto',
1212
display: 'table-cell'
1313
},
1414
tableHeader: {

0 commit comments

Comments
 (0)