Skip to content

Commit

Permalink
fix: Accessibility Bugs (#7264)
Browse files Browse the repository at this point in the history
#### Details

This PR fixes two accessibility bugs:
* `QuickAssessToAssessmentDialog` does not redirect focus to the trigger
button after it closes
* [before
video](https://www.loom.com/share/6c19f10e23044a3a97064d79029b5e30)
* [after
video](https://www.loom.com/share/3b1799677d114a80ab54deb140b0e5d6)
* Failure instance visual helper checkbox in issues table does not
correctly update `aria-checked`/`checked` when it is toggled
* [before
video](https://www.loom.com/share/9ecd18aca30a44dc86697bea37238fde)
* [after
video](https://www.loom.com/share/02fdc17ca946461fb2d119c8de759150)

##### Motivation

Accessibility Compliance

##### Context

For the dialog close focus bug, I moved the
`QuickAssessToAssessmentDialog` to be inside the `DetailsViewCommandBar`
like the other buttons that open dialogs. I managed focus the same way
we do for the `ReportExportDialog`.

For the visual helper checkbox bug, we were only setting `aria-checked`
and not `checked` on the checkbox, so now we explicitly set the
`checked` attribute (which in turn updates `aria-checked`).

#### Pull request checklist
<!-- If a checklist item is not applicable to this change, write "n/a"
in the checkbox -->
- [N/A] Addresses an existing issue: #0000
- [x] Ran `yarn fastpass`
- [x] Added/updated relevant unit test(s) (and ran `yarn test`)
- [x] Verified code coverage for the changes made. Check coverage report
at: `<rootDir>/test-results/unit/coverage`
- [x] PR title *AND* final merge commit title both start with a semantic
tag (`fix:`, `chore:`, `feat(feature-name):`, `refactor:`). See
`CONTRIBUTING.md`.
- [x] (UI changes only) Added screenshots/GIFs to description above
- [x] (UI changes only) Verified usability with NVDA/JAWS
  • Loading branch information
madalynrose authored Mar 11, 2024
1 parent 532ceb1 commit cf4814b
Show file tree
Hide file tree
Showing 14 changed files with 620 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class AssessmentInstanceSelectedButton extends React.Component<Assessment
disabled={!isVisible}
onClick={this.onButtonClicked}
role="checkbox"
aria-checked={isVisualizationEnabled}
checked={isVisualizationEnabled}
aria-label="Visualization"
/>
);
Expand Down
33 changes: 28 additions & 5 deletions src/DetailsView/components/details-view-command-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
LoadAssessmentDialogDeps,
} from 'DetailsView/components/load-assessment-dialog';
import { NarrowModeStatus } from 'DetailsView/components/narrow-mode-detector';
import { QuickAssessToAssessmentDialog } from 'DetailsView/components/quick-assess-to-assessment-dialog';
import { ReportExportButton } from 'DetailsView/components/report-export-button';
import {
ReportExportDialogFactoryDeps,
Expand All @@ -48,6 +49,7 @@ import {
TransferToAssessmentButtonDeps,
TransferToAssessmentButtonProps,
} from 'DetailsView/components/transfer-to-assessment-button';
import { DataTransferViewStoreData } from 'DetailsView/data-transfer-view-store';
import * as React from 'react';
import { ReportGenerator } from 'reports/report-generator';
import { AssessmentStoreData } from '../../common/types/store-data/assessment-result-data';
Expand Down Expand Up @@ -105,20 +107,23 @@ export interface DetailsViewCommandBarProps {
tabStopRequirementData: TabStopRequirementState;
userConfigurationStoreData: UserConfigurationStoreData;
featureFlagStoreData: FeatureFlagStoreData;
dataTransferViewStoreData: DataTransferViewStoreData;
}
export class DetailsViewCommandBar extends React.Component<
DetailsViewCommandBarProps,
DetailsViewCommandBarState
> {
public exportDialogCloseFocus?: IButton;
public startOverDialogCloseFocus?: IButton;
public transferToAssessmentDialogCloseFocus?: IButton;

public constructor(props) {
super(props);
this.state = {
isInvalidLoadAssessmentDialogOpen: false,
isLoadAssessmentDialogOpen: false,
isReportExportDialogOpen: false,
isMoveToAssessmentDialogOpen: false,
loadedAssessmentData: {} as VersionedAssessmentData,
startOverDialogState: 'none',
};
Expand All @@ -137,6 +142,7 @@ export class DetailsViewCommandBar extends React.Component<
{this.renderInvalidLoadAssessmentDialog()}
{this.renderLoadAssessmentDialog()}
{this.renderStartOverDialog()}
{this.renderTransferToAssessmentDialog()}
</div>
);
}
Expand Down Expand Up @@ -208,6 +214,7 @@ export class DetailsViewCommandBar extends React.Component<
buttonRef={ref => {
this.exportDialogCloseFocus = ref ?? undefined;
this.startOverDialogCloseFocus = ref ?? undefined;
this.transferToAssessmentDialogCloseFocus = ref ?? undefined;
}}
/>
);
Expand All @@ -219,6 +226,9 @@ export class DetailsViewCommandBar extends React.Component<

private focusReportExportButton = () => this.exportDialogCloseFocus?.focus();

private focusTransferToAssessmentButton = () =>
this.transferToAssessmentDialogCloseFocus?.focus();

private renderExportButton = () => {
const shouldShowReportExportButtonProps: ShouldShowReportExportButtonProps = {
visualizationConfigurationFactory: this.props.visualizationConfigurationFactory,
Expand Down Expand Up @@ -256,16 +266,29 @@ export class DetailsViewCommandBar extends React.Component<
});
};

private renderLoadAssessmentButton = (): JSX.Element | null => {
return this.props.switcherNavConfiguration.LoadAssessmentButton({
private renderTransferToAssessmentButton = (): JSX.Element | null => {
return this.props.switcherNavConfiguration.TransferToAssessmentButton({
...this.props,
handleLoadAssessmentButtonClick: this.handleLoadAssessmentButtonClick,
buttonRef: ref => (this.transferToAssessmentDialogCloseFocus = ref ?? undefined),
});
};

private renderTransferToAssessmentButton = (): JSX.Element | null => {
return this.props.switcherNavConfiguration.TransferToAssessmentButton({
private renderTransferToAssessmentDialog(): JSX.Element {
return (
<QuickAssessToAssessmentDialog
isShown={
this.props.dataTransferViewStoreData.showQuickAssessToAssessmentConfirmDialog
}
afterDialogDismissed={this.focusTransferToAssessmentButton}
{...this.props}
/>
);
}

private renderLoadAssessmentButton = (): JSX.Element | null => {
return this.props.switcherNavConfiguration.LoadAssessmentButton({
...this.props,
handleLoadAssessmentButtonClick: this.handleLoadAssessmentButtonClick,
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type QuickAssessToAssessmentDialogDeps = {
export interface QuickAssessToAssessmentDialogProps {
deps: QuickAssessToAssessmentDialogDeps;
isShown: boolean;
afterDialogDismissed: () => void;
}

export const continueToAssessmentButtonAutomationId = 'continue-to-assessment-button';
Expand All @@ -29,7 +30,10 @@ export const QuickAssessToAssessmentDialog = NamedFC<QuickAssessToAssessmentDial
hidden: !props.isShown,
title: 'Continue to Assessment',
onDismiss: dataTransferViewController.hideQuickAssessToAssessmentConfirmDialog,
containerClassName: commonDialogStyles.insightsDialogMainOverride,
modalProps: {
onDismissed: props.afterDialogDismissed,
containerClassName: commonDialogStyles.insightsDialogMainOverride,
},
};

const descriptionText =
Expand Down
3 changes: 3 additions & 0 deletions src/DetailsView/components/transfer-to-assessment-button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { IRefObject, IButton } from '@fluentui/react';
import { InsightsCommandButton } from 'common/components/controls/insights-command-button';
import { NamedFC } from 'common/react/named-fc';
import { DataTransferViewController } from 'DetailsView/data-transfer-view-controller';
Expand All @@ -12,6 +13,7 @@ export type TransferToAssessmentButtonDeps = {

export interface TransferToAssessmentButtonProps {
deps: TransferToAssessmentButtonDeps;
buttonRef?: IRefObject<IButton>;
}

export const transferToAssessmentButtonAutomationId = 'transfer-to-assessment-button';
Expand All @@ -26,6 +28,7 @@ export const TransferToAssessmentButton = NamedFC<TransferToAssessmentButtonProp
onClick={
props.deps.dataTransferViewController.showQuickAssessToAssessmentConfirmDialog
}
componentRef={props.buttonRef}
>
Move to assessment
</InsightsCommandButton>
Expand Down
17 changes: 1 addition & 16 deletions src/DetailsView/details-view-body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ import { FluentSideNav, FluentSideNavDeps } from 'DetailsView/components/left-na
import { NarrowModeStatus } from 'DetailsView/components/narrow-mode-detector';
import { OverviewHeadingIntroFactory } from 'DetailsView/components/overview-content/overview-heading-intro';
import { OverviewHelpSectionAboutFactory } from 'DetailsView/components/overview-content/overview-help-section-about';
import {
QuickAssessToAssessmentDialog,
QuickAssessToAssessmentDialogDeps,
} from 'DetailsView/components/quick-assess-to-assessment-dialog';
import { QuickAssessToAssessmentDialogDeps } from 'DetailsView/components/quick-assess-to-assessment-dialog';
import { TabStopsViewStoreData } from 'DetailsView/components/tab-stops/tab-stops-view-store-data';
import { TestViewContainerProvider } from 'DetailsView/components/test-view-container-provider';
import { DataTransferViewStoreData } from 'DetailsView/data-transfer-view-store';
Expand Down Expand Up @@ -103,7 +100,6 @@ export class DetailsViewBody extends React.Component<DetailsViewBodyProps> {
<div className={styles.detailsViewBodyContentPane}>
{this.getTargetPageHiddenBar()}
<div className={styles.view} role="main">
{this.renderQuickAssessToAssessmentDialog()}
{this.renderRightPanel()}
</div>
</div>
Expand Down Expand Up @@ -149,15 +145,4 @@ export class DetailsViewBody extends React.Component<DetailsViewBodyProps> {
private renderRightPanel(): JSX.Element {
return <this.props.rightPanelConfiguration.RightPanel {...this.props} />;
}

private renderQuickAssessToAssessmentDialog(): JSX.Element {
return (
<QuickAssessToAssessmentDialog
isShown={
this.props.dataTransferViewStoreData.showQuickAssessToAssessmentConfirmDialog
}
deps={this.props.deps}
/>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,7 @@ exports[`DetailsViewBody render render 1`] = `
<div
class="view"
role="main"
>
<mock-quickassesstoassessmentconfirmdialog
deps="[object Object]"
isshown="true"
/>
</div>
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -1908,15 +1903,6 @@ exports[`DetailsViewBody render render: QuickAssessCommandBar props 1`] = `
}
`;

exports[`DetailsViewBody render render: QuickAssessToAssessmentConfirmDialog props 1`] = `
{
"deps": {
"detailsViewActionMessageCreator": {},
},
"isShown": true,
}
`;

exports[`DetailsViewBody render render: TargetPageHiddenBar props 1`] = `
{
"isTargetPageHidden": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
exports[`AssessmentInstanceSelectedButton render when element is not selected and is not visible 1`] = `
<DocumentFragment>
<mock-customizediconbutton
aria-checked="false"
aria-label="Visualization"
checked="false"
classname="instanceVisibilityButton testInstanceSelectedHiddenButton"
disabled="true"
iconprops="[object Object]"
Expand All @@ -16,8 +16,8 @@ exports[`AssessmentInstanceSelectedButton render when element is not selected an
exports[`AssessmentInstanceSelectedButton render when element is not selected and is visible 1`] = `
<DocumentFragment>
<mock-customizediconbutton
aria-checked="false"
aria-label="Visualization"
checked="false"
classname="instanceVisibilityButton"
disabled="false"
iconprops="[object Object]"
Expand All @@ -29,8 +29,8 @@ exports[`AssessmentInstanceSelectedButton render when element is not selected an
exports[`AssessmentInstanceSelectedButton render when element is selected and visible 1`] = `
<DocumentFragment>
<mock-customizediconbutton
aria-checked="true"
aria-label="Visualization"
checked="true"
classname="instanceVisibilityButton"
disabled="false"
iconprops="[object Object]"
Expand All @@ -42,8 +42,8 @@ exports[`AssessmentInstanceSelectedButton render when element is selected and vi
exports[`AssessmentInstanceSelectedButton render when element is selected but hidden 1`] = `
<DocumentFragment>
<mock-customizediconbutton
aria-checked="true"
aria-label="Visualization"
checked="true"
classname="instanceVisibilityButton testInstanceSelectedHiddenButton"
disabled="true"
iconprops="[object Object]"
Expand Down
Loading

0 comments on commit cf4814b

Please sign in to comment.