Skip to content

Commit afccabd

Browse files
authored
Accessibility fixes for the HTML report (#6329)
* Accessibility fixes for the HTML report * Update formatting. * Fix build issue * Improve formatting
1 parent 61d8439 commit afccabd

File tree

16 files changed

+97
-88
lines changed

16 files changed

+97
-88
lines changed

src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/App.tsx

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import { StrictMode } from 'react'
55
import { createRoot } from 'react-dom/client'
6-
import App from '../../components/App.tsx';
6+
import { App } from '../../components/App.tsx';
77
import './index.css'
88
import { init, ready, getAccessToken, getConfiguration } from "azure-devops-extension-sdk";
99
import { getClient } from "./azure-devops-extension-api";

src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/App.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,5 @@ The .NET Foundation licenses this file to you under the MIT license.
55

66
#root {
77
margin: 0 auto;
8-
padding: 0rem 2rem 2rem 2rem;
98
background-color: white;
109
}

src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/App.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const useStyles = makeStyles({
2020
position: 'sticky',
2121
top: 0,
2222
zIndex: 1,
23-
paddingBottom: '12px',
23+
padding: '0rem 2rem 1rem 2rem',
2424
backgroundColor: tokens.colorNeutralBackground1,
2525
borderBottom: `1px solid ${tokens.colorNeutralStroke2}`,
2626
marginBottom: '1rem',
@@ -30,6 +30,9 @@ const useStyles = makeStyles({
3030
justifyContent: 'space-between',
3131
alignItems: 'center',
3232
},
33+
body: {
34+
padding: '0rem 2rem',
35+
},
3336
headerActions: {
3437
display: 'flex',
3538
alignItems: 'center',
@@ -45,7 +48,7 @@ const useStyles = makeStyles({
4548
drawerBody: { paddingTop: '1rem' },
4649
});
4750

48-
function App() {
51+
export const App = () => {
4952
const classes = useStyles();
5053
const { dataset, scoreSummary, selectedTags, clearFilters, searchValue, setSearchValue } = useReportContext();
5154
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
@@ -82,14 +85,14 @@ function App() {
8285
<Button icon={<FilterDismissRegular />} appearance="subtle" onClick={clearFilters} />
8386
</Tooltip>
8487
)}
85-
<SearchBox placeholder="Search / Filter " value={searchValue} type="text"
86-
style={{width: "16rem"}}
88+
<SearchBox placeholder="Search / Filter " value={searchValue} type="text"
89+
style={{ width: "16rem" }}
8790
onChange={(_ev, data) => setSearchValue(data.value)} />
8891
<Tooltip content="Download Data as JSON" relationship="description">
89-
<Button icon={<ArrowDownloadRegular />} appearance="subtle" onClick={downloadDataset} />
92+
<Button icon={<ArrowDownloadRegular />} appearance="subtle" onClick={downloadDataset} aria-label="Download Data as JSON" />
9093
</Tooltip>
9194
<Tooltip content="Settings" relationship="description">
92-
<Button icon={<Settings28Regular />} appearance="subtle" onClick={toggleSettings} />
95+
<Button icon={<Settings28Regular />} appearance="subtle" onClick={toggleSettings} aria-label="Settings" />
9396
</Tooltip>
9497
</div>
9598
</div>
@@ -102,10 +105,12 @@ function App() {
102105
<ScoreNodeHistory />
103106
</div>
104107

105-
<ScenarioGroup
106-
node={scoreSummary.primaryResult}
107-
scoreSummary={scoreSummary}
108-
/>
108+
<div className={classes.body}>
109+
<ScenarioGroup
110+
node={scoreSummary.primaryResult}
111+
scoreSummary={scoreSummary}
112+
/>
113+
</div>
109114

110115
<p className={classes.footerText}>
111116
Generated at {dataset.createdAt} by Microsoft.Extensions.AI.Evaluation.Reporting version {dataset.generatorVersion}
@@ -126,6 +131,4 @@ function App() {
126131
</Drawer>
127132
</>
128133
);
129-
}
130-
131-
export default App;
134+
};

src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ChatDetailsSection.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ export const ChatDetailsSection = ({ chatDetails }: { chatDetails: ChatDetails;
2424
navigator.clipboard.writeText(text);
2525
};
2626
return (
27-
<div className={classes.section}>
27+
<div className={classes.section} tabIndex={0}
28+
onKeyUp={e => e.key === 'Enter' && setIsExpanded(!isExpanded)}>
2829
<div className={classes.sectionHeader} onClick={() => setIsExpanded(!isExpanded)}>
2930
{isExpanded ? <ChevronDown12Regular /> : <ChevronRight12Regular />}
3031
<h3 className={classes.sectionHeaderText}>Diagnostic Data</h3>

src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ConversationDetails.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export const ConversationDetails = ({ messages, model, usage, selectedMetric }:
6767
}
6868

6969
const contextGroups: { key: string, contents: AIContent[] }[] = [];
70-
70+
7171
for (const [key, contents] of Object.entries(selectedMetric.context)) {
7272
if (contents && contents.length > 0) {
7373
contextGroups.push({
@@ -84,7 +84,8 @@ export const ConversationDetails = ({ messages, model, usage, selectedMetric }:
8484
const contextGroups = getContextGroups();
8585

8686
return (
87-
<div className={classes.section}>
87+
<div className={classes.section} tabIndex={0}
88+
onKeyUp={e => e.key === 'Enter' && setIsExpanded(!isExpanded)}>
8889
<div className={classes.sectionHeader} onClick={() => setIsExpanded(!isExpanded)}>
8990
{isExpanded ? <ChevronDown12Regular /> : <ChevronRight12Regular />}
9091
<h3 className={classes.sectionHeaderText}>Conversation</h3>
@@ -113,7 +114,7 @@ export const ConversationDetails = ({ messages, model, usage, selectedMetric }:
113114
</div>
114115
);
115116
})}
116-
117+
117118
{contextGroups.map((group, index) => (
118119
<div key={`context-${index}`} className={mergeClasses(classes.messageRow, classes.userMessageRow)}>
119120
<div className={classes.messageParticipantName}>{`supplied evaluation context (${group.key})`}</div>

src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/DiagnosticsContent.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
import { DismissCircle16Regular, Warning16Regular, Info16Regular, Copy16Regular } from "@fluentui/react-icons";
5-
import { Table, TableHeader, TableRow, TableHeaderCell, TableBody, TableCell } from "@fluentui/react-components";
5+
import { Table, TableHeader, TableRow, TableHeaderCell, TableBody, TableCell, mergeClasses } from "@fluentui/react-components";
66
import { useStyles } from "./Styles";
77

88
export const DiagnosticsContent = ({ diagnostics }: { diagnostics: EvaluationDiagnostic[]; }) => {
@@ -39,13 +39,13 @@ export const DiagnosticsContent = ({ diagnostics }: { diagnostics: EvaluationDia
3939
};
4040

4141
return (
42-
<div className={classes.tableContainer}>
42+
<div className={classes.tableContainer} tabIndex={0}>
4343
<Table className={classes.autoWidthTable}>
4444
<TableHeader>
4545
<TableRow>
46-
<TableHeaderCell className={`${classes.tableHeaderCell} ${classes.diagnosticSeverityCell}`}>Severity</TableHeaderCell>
47-
<TableHeaderCell className={`${classes.tableHeaderCell} ${classes.diagnosticMessageCell}`}>Message</TableHeaderCell>
48-
<TableHeaderCell className={`${classes.tableHeaderCell} ${classes.diagnosticCopyButtonCell}`}></TableHeaderCell>
46+
<TableHeaderCell className={mergeClasses(classes.tableHeaderCell, classes.diagnosticSeverityCell)}>Severity</TableHeaderCell>
47+
<TableHeaderCell className={mergeClasses(classes.tableHeaderCell, classes.diagnosticMessageCell)}>Message</TableHeaderCell>
48+
<TableHeaderCell className={mergeClasses(classes.tableHeaderCell, classes.diagnosticCopyButtonCell)}></TableHeaderCell>
4949
</TableRow>
5050
</TableHeader>
5151
<TableBody>

src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/MetricCard.tsx

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
import { makeStyles, mergeClasses, tokens, Tooltip } from "@fluentui/react-components";
5-
import { DismissCircle16Regular, Info16Regular, Warning16Regular } from "@fluentui/react-icons";
5+
import { DismissCircle16Regular, Info16Regular, Warning16Regular, RadioButtonFilled, RadioButtonRegular } from "@fluentui/react-icons";
66

77
const useCardListStyles = makeStyles({
88
metricCardList: {
@@ -13,20 +13,20 @@ const useCardListStyles = makeStyles({
1313
},
1414
});
1515

16-
export const MetricCardList = ({ scenario, onMetricSelect, selectedMetric }: {
17-
scenario: ScenarioRunResult,
18-
onMetricSelect: (metric: MetricType | null) => void,
19-
selectedMetric: MetricType | null
16+
export const MetricCardList = ({ scenario, onMetricSelect, selectedMetric }: {
17+
scenario: ScenarioRunResult,
18+
onMetricSelect: (metric: MetricType | null) => void,
19+
selectedMetric: MetricType | null
2020
}) => {
2121
const classes = useCardListStyles();
2222
return (
2323
<div className={classes.metricCardList}>
2424
{Object.values(scenario.evaluationResult.metrics).map((metric, index) => (
25-
<MetricCard
26-
metric={metric}
27-
key={index}
28-
onClick={() => onMetricSelect(selectedMetric === metric ? null : metric)}
29-
isSelected={selectedMetric === metric}
25+
<MetricCard
26+
metric={metric}
27+
key={index}
28+
onClick={() => onMetricSelect(selectedMetric === metric ? null : metric)}
29+
isSelected={selectedMetric === metric}
3030
/>
3131
))}
3232
</div>
@@ -35,11 +35,11 @@ export const MetricCardList = ({ scenario, onMetricSelect, selectedMetric }: {
3535

3636
const useCardStyles = makeStyles({
3737
card: {
38-
display: 'flex',
39-
flexDirection: 'column',
40-
alignItems: 'center',
38+
display: 'flex',
39+
flexDirection: 'column',
40+
alignItems: 'center',
4141
gap: '0.5rem',
42-
padding: '.75rem',
42+
padding: '.75rem',
4343
border: `1px solid ${tokens.colorNeutralStroke2}`,
4444
borderRadius: '4px',
4545
width: '12.5rem',
@@ -54,12 +54,9 @@ const useCardStyles = makeStyles({
5454
selectedCard: {
5555
zIndex: 1,
5656
boxShadow: tokens.shadow8,
57-
outline: `2px solid ${tokens.colorNeutralForeground3}`,
58-
outlineOffset: '0px',
59-
border: 'none'
6057
},
61-
metricNameText: {
62-
fontSize: '1rem',
58+
metricNameText: {
59+
fontSize: '1rem',
6360
fontWeight: 'normal',
6461
width: '80%',
6562
textAlign: 'center',
@@ -75,15 +72,19 @@ const useCardStyles = makeStyles({
7572
height: '4px',
7673
width: '100%',
7774
position: 'relative',
78-
marginBottom: '0',
75+
},
76+
selectionIcon: {
77+
position: 'absolute',
78+
top: '-0.25rem',
79+
left: '-0.25rem',
7980
},
8081
metricIcon: {
8182
position: 'absolute',
8283
top: '-0.25rem',
8384
right: '-0.25rem',
8485
},
85-
metricValueText: {
86-
fontSize: '1rem',
86+
metricValueText: {
87+
fontSize: '1rem',
8788
fontWeight: 'bold',
8889
width: '80%',
8990
textAlign: 'center',
@@ -114,7 +115,7 @@ const useCardStyles = makeStyles({
114115
fontWeight: '500',
115116
padding: '0.25rem 0.5rem',
116117
borderRadius: '4px',
117-
border: '1px solid ' + tokens.colorNeutralStroke2,
118+
border: '1px solid ' + tokens.colorNeutralStroke2,
118119
}
119120
});
120121

@@ -161,7 +162,7 @@ const getMetricDisplayValue = (metric: MetricType): string => {
161162
case "string":
162163
return metric?.value ?? "??";
163164
case "boolean":
164-
return !metric || metric.value === undefined || metric.value === null ?
165+
return !metric || metric.value === undefined || metric.value === null ?
165166
'??' :
166167
metric.value ? 'Yes' : 'No';
167168
case "numeric":
@@ -173,34 +174,34 @@ const getMetricDisplayValue = (metric: MetricType): string => {
173174
}
174175
};
175176

176-
export const MetricCard = ({
177-
metric,
177+
export const MetricCard = ({
178+
metric,
178179
onClick,
179180
isSelected
180-
}: {
181-
metric: MetricType,
181+
}: {
182+
metric: MetricType,
182183
onClick: () => void,
183184
isSelected: boolean
184185
}) => {
185186

186187
const metricValue = getMetricDisplayValue(metric);
187188
const classes = useCardStyles();
188189
const { fg, bg } = useCardColors(metric.interpretation);
189-
190+
190191
const hasReasons = metric.reason != null || metric.interpretation?.reason != null;
191192
const hasInformationalMessages = metric.diagnostics && metric.diagnostics.some((d: EvaluationDiagnostic) => d.severity == "informational");
192193
const hasWarningMessages = metric.diagnostics && metric.diagnostics.some((d: EvaluationDiagnostic) => d.severity == "warning");
193194
const hasErrorMessages = metric.diagnostics && metric.diagnostics.some((d: EvaluationDiagnostic) => d.severity == "error");
194-
195+
195196
const cardClass = mergeClasses(
196-
bg,
197-
classes.card,
197+
bg,
198+
classes.card,
198199
isSelected ? classes.selectedCard : undefined
199200
);
200-
201+
201202
let statusIcon = null;
202203
let statusTooltip = '';
203-
204+
204205
if (hasErrorMessages) {
205206
statusIcon = <DismissCircle16Regular className={classes.metricIcon} />;
206207
statusTooltip = 'This metric has errors. Click the card to view more details.';
@@ -211,10 +212,12 @@ export const MetricCard = ({
211212
statusIcon = <Info16Regular className={classes.metricIcon} />;
212213
statusTooltip = 'This metric has additional information. Click the card to view more details.';
213214
}
214-
215+
215216
return (
216-
<div className={cardClass} onClick={onClick}>
217+
<div className={cardClass} onClick={onClick} tabIndex={0}
218+
onKeyUp={e => e.key === 'Enter' && onClick()} >
217219
<div className={classes.iconPlaceholder}>
220+
{isSelected ? <RadioButtonFilled className={classes.selectionIcon} /> : <RadioButtonRegular className={classes.selectionIcon} />}
218221
{statusIcon && (
219222
<Tooltip content={statusTooltip} relationship="description">
220223
<span>{statusIcon}</span>
@@ -231,7 +234,7 @@ export const MetricCard = ({
231234
);
232235
};
233236

234-
export const MetricDisplay = ({metric}: {metric: MetricWithNoValue | NumericMetric | BooleanMetric | StringMetric}) => {
237+
export const MetricDisplay = ({ metric }: { metric: MetricWithNoValue | NumericMetric | BooleanMetric | StringMetric }) => {
235238
const metricValue = getMetricDisplayValue(metric);
236239
const classes = useCardStyles();
237240
const { fg, bg } = useCardColors(metric.interpretation);

src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/MetricDetailsSection.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ export const MetricDetailsSection = ({ metric }: { metric: MetricType; }) => {
2525
if (!hasReason && !hasInterpretationReason && !hasDiagnostics && !hasMetadata) return null;
2626

2727
return (
28-
<div className={classes.section}>
28+
<div className={classes.section} tabIndex={0}
29+
onKeyUp={e => e.key === 'Enter' && setIsExpanded(!isExpanded)}>
2930
<div className={classes.sectionHeader} onClick={() => setIsExpanded(!isExpanded)}>
3031
{isExpanded ? <ChevronDown12Regular /> : <ChevronRight12Regular />}
3132
<h3 className={classes.sectionHeaderText}>Metric Details: {metric.name}</h3>

src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScenarioRunHistory.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export const ScenarioRunHistory = ({ scoreSummary, scenario }: { scoreSummary: S
3333
const latestExecution = mergeClasses(classes.verticalText, classes.currentExecutionForeground);
3434

3535
return (
36-
<div className={classes.section}>
36+
<div className={classes.section} tabIndex={0}
37+
onKeyUp={e => e.key === 'Enter' && setIsExpanded(!isExpanded)}>
3738
<div className={classes.sectionHeader} onClick={() => setIsExpanded(!isExpanded)}>
3839
{isExpanded ? <ChevronDown12Regular /> : <ChevronRight12Regular />}
3940
<h3 className={classes.sectionHeaderText}>Trends</h3>

0 commit comments

Comments
 (0)