[Security Solution] Expandable flyout - add accessibility support (#166996)

## Summary

This PR adds accessibility support in the new expandably flyout, namely:
- Added recommended aria-label to components
- Adjusted heading sizes to follow "Heading level should only increase
by one" rule


### Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
This commit is contained in:
christineweng 2023-09-26 14:15:30 -05:00 committed by GitHub
parent 14ed446c23
commit c9a98a7846
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 119 additions and 29 deletions

View file

@ -28,11 +28,11 @@ const EnrichmentSectionHeader: React.FC<{ type?: ENRICHMENT_TYPES }> = ({ type }
<EuiFlexGroup direction="row" gutterSize="xs" alignItems="baseline">
<EuiFlexItem grow={false}>
<EuiTitle size="xxxs">
<h5>
<h3>
{type === ENRICHMENT_TYPES.IndicatorMatchRule
? i18n.INDICATOR_ENRICHMENT_TITLE
: i18n.INVESTIGATION_ENRICHMENT_TITLE}
</h5>
</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>

View file

@ -231,12 +231,12 @@ export const HostDetails: React.FC<HostDetailsProps> = ({ hostName, timestamp, s
return (
<>
<EuiTitle size="xs">
<h4>
<h3>
<FormattedMessage
id="xpack.securitySolution.flyout.left.insights.entities.hostDetailsTitle"
defaultMessage="Host"
/>
</h4>
</h3>
</EuiTitle>
<EuiSpacer size="s" />
<ExpandablePanel
@ -249,12 +249,12 @@ export const HostDetails: React.FC<HostDetailsProps> = ({ hostName, timestamp, s
data-test-subj={HOST_DETAILS_TEST_ID}
>
<EuiTitle size="xxs">
<h5>
<h4>
<FormattedMessage
id="xpack.securitySolution.flyout.left.insights.entities.hostDetailsInfoTitle"
defaultMessage="Host information"
/>
</h5>
</h4>
</EuiTitle>
<EuiSpacer size="s" />
<AnomalyTableProvider
@ -289,12 +289,12 @@ export const HostDetails: React.FC<HostDetailsProps> = ({ hostName, timestamp, s
<EuiFlexGroup direction="row" gutterSize="xs" alignItems="center">
<EuiFlexItem grow={false}>
<EuiTitle size="xxs">
<h5>
<h4>
<FormattedMessage
id="xpack.securitySolution.flyout.left.insights.entities.relatedUsersTitle"
defaultMessage="Related users"
/>
</h5>
</h4>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>

View file

@ -10,6 +10,7 @@ import type { EuiBasicTableColumn } from '@elastic/eui';
import { EuiInMemoryTable, EuiSkeletonText } from '@elastic/eui';
import type { RelatedCase } from '@kbn/cases-plugin/common';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { CellTooltipWrapper } from '../../shared/components/cell_tooltip_wrapper';
import { CaseDetailsLink } from '../../../common/components/links';
import {
@ -65,7 +66,19 @@ export const RelatedCases: React.VFC<RelatedCasesProps> = ({ eventId }) => {
const { loading, error, data, dataCount } = useFetchRelatedCases({ eventId });
if (loading) {
return <EuiSkeletonText lines={1} size="m" isLoading={loading} contentAriaLabel="Loading" />;
return (
<EuiSkeletonText
lines={1}
size="m"
isLoading={loading}
contentAriaLabel={i18n.translate(
'xpack.securitySolution.flyout.left.insights.correlations.relatedCasesLoadingAriaLabel',
{
defaultMessage: 'Related cases is loading',
}
)}
/>
);
}
if (error) {

View file

@ -232,12 +232,12 @@ export const UserDetails: React.FC<UserDetailsProps> = ({ userName, timestamp, s
return (
<>
<EuiTitle size="xs">
<h4>
<h3>
<FormattedMessage
id="xpack.securitySolution.flyout.left.insights.entities.userDetailsTitle"
defaultMessage="User"
/>
</h4>
</h3>
</EuiTitle>
<EuiSpacer size="s" />
<ExpandablePanel
@ -253,12 +253,12 @@ export const UserDetails: React.FC<UserDetailsProps> = ({ userName, timestamp, s
data-test-subj={USER_DETAILS_TEST_ID}
>
<EuiTitle size="xxs">
<h5>
<h4>
<FormattedMessage
id="xpack.securitySolution.flyout.left.insights.entities.userDetailsInfoTitle"
defaultMessage="User information"
/>
</h5>
</h4>
</EuiTitle>
<EuiSpacer size="s" />
<AnomalyTableProvider
@ -292,12 +292,12 @@ export const UserDetails: React.FC<UserDetailsProps> = ({ userName, timestamp, s
<EuiFlexGroup direction="row" gutterSize="xs" alignItems="center">
<EuiFlexItem grow={false}>
<EuiTitle size="xxs">
<h5>
<h4>
<FormattedMessage
id="xpack.securitySolution.flyout.left.insights.entities.relatedHostsTitle"
defaultMessage="Related hosts"
/>
</h5>
</h4>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>

View file

@ -113,7 +113,7 @@ export const InsightsTab: React.FC = memo(() => {
color="primary"
name="coarsness"
legend={i18n.translate(
'xpack.securitySolution.flyout.left.insights.buttonGroupButtonLabel',
'xpack.securitySolution.flyout.left.insights.buttonGroupLegendLabel',
{
defaultMessage: 'Insights options',
}

View file

@ -91,7 +91,7 @@ export const VisualizeTab: FC = memo(() => {
color="primary"
name="coarsness"
legend={i18n.translate(
'xpack.securitySolution.flyout.left.visualize.buttonGroupButtonLabel',
'xpack.securitySolution.flyout.left.visualize.buttonGroupLegendLabel',
{
defaultMessage: 'Visualize options',
}

View file

@ -11,6 +11,7 @@ import React, { useMemo, useCallback } from 'react';
import { isEmpty } from 'lodash';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { useRightPanelContext } from '../context';
import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers';
import {
@ -65,6 +66,12 @@ export const Description: FC = () => {
onClick={openRulePreview}
iconSide="right"
data-test-subj={RULE_SUMMARY_BUTTON_TEST_ID}
aria-label={i18n.translate(
'xpack.securitySolution.flyout.right.about.description.ruleSummaryButtonAriaLabel',
{
defaultMessage: 'Show rule summary',
}
)}
>
<FormattedMessage
id="xpack.securitySolution.flyout.right.about.description.ruleSummaryButtonLabel"

View file

@ -10,6 +10,7 @@ import type { FC } from 'react';
import React, { memo, useCallback } from 'react';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { COLLAPSE_DETAILS_BUTTON_TEST_ID, EXPAND_DETAILS_BUTTON_TEST_ID } from './test_ids';
import { LeftPanelKey } from '../../left';
import { useRightPanelContext } from '../context';
@ -42,6 +43,12 @@ export const ExpandDetailButton: FC = memo(() => {
onClick={collapseDetails}
iconType="arrowEnd"
data-test-subj={COLLAPSE_DETAILS_BUTTON_TEST_ID}
aria-label={i18n.translate(
'xpack.securitySolution.flyout.right.header.collapseDetailButtonAriaLabel',
{
defaultMessage: 'Collapse details',
}
)}
>
<FormattedMessage
id="xpack.securitySolution.flyout.right.header.collapseDetailButtonLabel"
@ -54,6 +61,12 @@ export const ExpandDetailButton: FC = memo(() => {
onClick={expandDetails}
iconType="arrowStart"
data-test-subj={EXPAND_DETAILS_BUTTON_TEST_ID}
aria-label={i18n.translate(
'xpack.securitySolution.flyout.right.header.expandDetailButtonAriaLabel',
{
defaultMessage: 'Expand details',
}
)}
>
<FormattedMessage
id="xpack.securitySolution.flyout.right.header.expandDetailButtonLabel"

View file

@ -86,7 +86,7 @@ export const HeaderTitle: VFC<HeaderTitleProps> = memo(({ flyoutIsExpandable })
)}
<EuiSpacer size="s" />
<EuiTitle size="s">
<h4 data-test-subj={FLYOUT_HEADER_TITLE_TEST_ID}>
<h2 data-test-subj={FLYOUT_HEADER_TITLE_TEST_ID}>
{isAlert && !isEmpty(ruleName) ? (
ruleName
) : (
@ -95,7 +95,7 @@ export const HeaderTitle: VFC<HeaderTitleProps> = memo(({ flyoutIsExpandable })
defaultMessage="Event details"
/>
)}
</h4>
</h2>
</EuiTitle>
<EuiSpacer size="s" />
<EuiFlexGroup direction="row" gutterSize={isAlert ? 'm' : 'none'}>

View file

@ -9,6 +9,7 @@ import type { ReactElement, VFC } from 'react';
import React from 'react';
import { css } from '@emotion/react';
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiHealth, EuiSkeletonText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedCount } from '../../../common/components/formatted_number';
export interface InsightsSummaryRowProps {
@ -64,7 +65,13 @@ export const InsightsSummaryRow: VFC<InsightsSummaryRowProps> = ({
lines={1}
size="m"
isLoading={loading}
contentAriaLabel="Loading"
contentAriaLabel={i18n.translate(
'xpack.securitySolution.flyout.right.insights.insightSummaryLoadingAriaLabel',
{
defaultMessage: 'Loading insights for {value}',
values: { value },
}
)}
data-test-subj={loadingDataTestSubj}
/>
);
@ -83,7 +90,12 @@ export const InsightsSummaryRow: VFC<InsightsSummaryRowProps> = ({
<EuiFlexItem grow={false}>
<EuiButtonIcon
data-test-subj={iconDataTestSubj}
aria-label={'entity-icon'}
aria-label={i18n.translate(
'xpack.securitySolution.flyout.right.insights.insightSummaryButtonIconAriaLabel',
{
defaultMessage: 'Insight summary row icon',
}
)}
color="text"
display="empty"
iconType={icon}

View file

@ -8,6 +8,7 @@ import React, { useCallback } from 'react';
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiTitle } from '@elastic/eui';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { useInvestigationGuide } from '../../shared/hooks/use_investigation_guide';
import { useRightPanelContext } from '../context';
import { LeftPanelKey, LeftPanelInvestigationTab } from '../../left';
@ -75,6 +76,12 @@ export const InvestigationGuide: React.FC = () => {
onClick={goToInvestigationsTab}
iconType="documentation"
data-test-subj={INVESTIGATION_GUIDE_BUTTON_TEST_ID}
aria-label={i18n.translate(
'xpack.securitySolution.flyout.right.investigation.investigationGuide.investigationGuideButtonAriaLabel',
{
defaultMessage: 'Show investigation guide',
}
)}
>
<FormattedMessage
id="xpack.securitySolution.flyout.right.investigation.investigationGuide.investigationGuideButtonLabel"

View file

@ -11,6 +11,7 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eu
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { ALERT_REASON } from '@kbn/rule-data-utils';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { getField } from '../../shared/utils';
import { AlertReasonPreviewPanel, PreviewPanelKey } from '../../preview';
import {
@ -62,6 +63,12 @@ export const Reason: FC = () => {
onClick={openRulePreview}
iconSide="right"
data-test-subj={REASON_DETAILS_PREVIEW_BUTTON_TEST_ID}
aria-label={i18n.translate(
'xpack.securitySolution.flyout.right.about.reason.alertReasonButtonAriaLabel',
{
defaultMessage: 'Show full reason',
}
)}
>
<FormattedMessage
id="xpack.securitySolution.flyout.right.about.reason.alertReasonButtonLabel"

View file

@ -8,6 +8,7 @@ import React, { useCallback } from 'react';
import { EuiButton } from '@elastic/eui';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { expandDottedObject } from '../../../../common/utils/expand_dotted';
import type {
ExpandedEventFieldsObject,
@ -56,6 +57,12 @@ export const ResponseButton: React.FC = () => {
onClick={goToResponseTab}
iconType="documentation"
data-test-subj={RESPONSE_BUTTON_TEST_ID}
aria-label={i18n.translate(
'xpack.securitySolution.flyout.right.response.responseButtonAriaLabel',
{
defaultMessage: 'Response',
}
)}
>
<FormattedMessage
id="xpack.securitySolution.flyout.right.response.responseButtonLabel"

View file

@ -37,12 +37,12 @@ export const RiskScore: FC = memo(() => {
<EuiFlexGroup alignItems="center" direction="row" gutterSize="xs">
<EuiFlexItem grow={false}>
<EuiTitle size="xxs" data-test-subj={RISK_SCORE_TITLE_TEST_ID}>
<h5>
<h3>
<FormattedMessage
id="xpack.securitySolution.flyout.right.header.riskScoreTitle"
defaultMessage="Risk score:"
/>
</h5>
</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>

View file

@ -46,12 +46,12 @@ export const DocumentSeverity: FC = memo(() => {
<EuiFlexGroup alignItems="center" direction="row" gutterSize="xs">
<EuiFlexItem grow={false}>
<EuiTitle size="xxs" data-test-subj={SEVERITY_TITLE_TEST_ID}>
<h5>
<h3>
<FormattedMessage
id="xpack.securitySolution.flyout.right.header.severityTitle"
defaultMessage="Severity:"
/>
</h5>
</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>

View file

@ -9,6 +9,7 @@ import { copyToClipboard, EuiButtonEmpty, EuiCopy } from '@elastic/eui';
import type { FC } from 'react';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { FLYOUT_URL_PARAM } from '../../shared/hooks/url/use_sync_flyout_state_with_url';
import { SHARE_BUTTON_TEST_ID } from './test_ids';
@ -40,6 +41,12 @@ export const ShareButton: FC<ShareButtonProps> = ({ alertUrl }) => {
}}
iconType="share"
data-test-subj={SHARE_BUTTON_TEST_ID}
aria-label={i18n.translate(
'xpack.securitySolution.flyout.right.header.shareButtonAriaLabel',
{
defaultMessage: 'Share Alert',
}
)}
>
<FormattedMessage
id="xpack.securitySolution.flyout.right.header.shareButtonLabel"

View file

@ -7,7 +7,8 @@
import type { FC } from 'react';
import React, { memo } from 'react';
import { EuiHorizontalRule } from '@elastic/eui';
import { EuiPanel, EuiHorizontalRule } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { ResponseSection } from '../components/response_section';
import { InvestigationSection } from '../components/investigation_section';
import { AboutSection } from '../components/about_section';
@ -19,7 +20,17 @@ import { VisualizationsSection } from '../components/visualizations_section';
*/
export const OverviewTab: FC = memo(() => {
return (
<>
<EuiPanel
hasBorder={false}
hasShadow={false}
paddingSize="none"
aria-label={i18n.translate(
'xpack.securitySolution.flyout.right.overview.overviewContentAriaLabel',
{
defaultMessage: 'Alert overview',
}
)}
>
<AboutSection />
<EuiHorizontalRule margin="l" />
<InvestigationSection />
@ -29,7 +40,7 @@ export const OverviewTab: FC = memo(() => {
<InsightsSection />
<EuiHorizontalRule margin="l" />
<ResponseSection />
</>
</EuiPanel>
);
});

View file

@ -22,6 +22,7 @@ import {
} from '@elastic/eui';
import type { IconType } from '@elastic/eui';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
export interface ExpandablePanelPanelProps {
header: {
@ -99,7 +100,12 @@ export const ExpandablePanel: React.FC<ExpandablePanelPanelProps> = ({
() => (
<EuiButtonIcon
data-test-subj={`${dataTestSubj}ToggleIcon`}
aria-label={`entity-toggle`}
aria-label={i18n.translate(
'xpack.securitySolution.flyout.shared.ExpandablePanelButtonIconAriaLabel',
{
defaultMessage: 'Expandable panel toggle',
}
)}
color="text"
display="empty"
iconType={toggleStatus ? 'arrowDown' : 'arrowRight'}