[Monitoring] Address multiple accessibility issues (#20619)

* Add screen reader only text for monitoring charts

* Add aria label for completed recoveries

* Ensure we have aria label coverage for monitoring chart tooltips

* Ensure table rows are tabbable and include the number of results at the top

* Use EuiScreenReaderOnly

* Updated copy

* Use EUI

* Remove kui usage

* Use an id instead of aria-label

* Show results in the table footer

* Revert "Show results in the table footer"

This reverts commit d622eb7eb4.

* Show total row count within the monitoring table

* PR feedback

* PR feedback

* Ensure all charts show an interval in the tooltip

* Fix padding issue with the cluster status
This commit is contained in:
Chris Roberson 2018-08-08 10:35:10 -04:00 committed by GitHub
parent cdc4ab6f67
commit dd40ac3dc2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 74 additions and 44 deletions

View file

@ -5,11 +5,8 @@
*/ */
import React from 'react'; import React from 'react';
import { first, get } from 'lodash';
export function InfoTooltip({ series }) { export function InfoTooltip({ series, bucketSize }) {
const bucketSize = get(first(series), 'bucket_size'); // bucket size will be the same for all metrics in all series
const tableRows = series.map((item, index) => { const tableRows = series.map((item, index) => {
return ( return (
<tr <tr

View file

@ -4,14 +4,15 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import React from 'react'; import React, { Fragment } from 'react';
import { get, first } from 'lodash';
import { getTitle } from './get_title'; import { getTitle } from './get_title';
import { getUnits } from './get_units'; import { getUnits } from './get_units';
import { MonitoringTimeseries } from './monitoring_timeseries'; import { MonitoringTimeseries } from './monitoring_timeseries';
import { InfoTooltip } from './info_tooltip'; import { InfoTooltip } from './info_tooltip';
import { import {
EuiIconTip, EuiFlexGroup, EuiFlexItem, EuiTitle EuiIconTip, EuiFlexGroup, EuiFlexItem, EuiTitle, EuiScreenReaderOnly
} from '@elastic/eui'; } from '@elastic/eui';
export function MonitoringTimeseriesContainer({ series, onBrush }) { export function MonitoringTimeseriesContainer({ series, onBrush }) {
@ -19,27 +20,43 @@ export function MonitoringTimeseriesContainer({ series, onBrush }) {
return null; // still loading return null; // still loading
} }
const title = getTitle(series);
const titleForAriaIds = title.replace(/\s+/, '--');
const units = getUnits(series); const units = getUnits(series);
const bucketSize = get(first(series), 'bucket_size'); // bucket size will be the same for all metrics in all series
const seriesScreenReaderTextList = [`Interval: ${bucketSize}`]
.concat(series.map(item => `${item.metric.label}: ${item.metric.description}`));
return ( return (
<EuiFlexGroup direction="column" gutterSize="s"> <EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem> <EuiFlexItem>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center" style={{ flexGrow: 0 }}> <EuiFlexGroup justifyContent="spaceBetween" alignItems="center" style={{ flexGrow: 0 }}>
<EuiFlexItem> <EuiFlexItem>
<EuiTitle> <EuiTitle tabIndex="0">
<h2> <Fragment>
{ getTitle(series) }{ units ? ` (${units})` : '' } <EuiScreenReaderOnly><span>This chart is not screen reader accessible</span></EuiScreenReaderOnly>
</h2> <h2>
{ getTitle(series) }{ units ? ` (${units})` : '' }
</h2>
</Fragment>
</EuiTitle> </EuiTitle>
</EuiFlexItem> </EuiFlexItem>
<EuiFlexItem grow={false}> <EuiFlexItem grow={false}>
<EuiIconTip <Fragment>
anchorClassName="eui-textRight eui-alignMiddle monitoring-chart-tooltip__trigger" <EuiIconTip
className="monitoring-chart-tooltip__wrapper" anchorClassName="eui-textRight eui-alignMiddle monitoring-chart-tooltip__trigger"
type="iInCircle" className="monitoring-chart-tooltip__wrapper"
position="right" type="iInCircle"
content={<InfoTooltip series={series}/>} position="right"
/> content={<InfoTooltip series={series} bucketSize={bucketSize}/>}
/>
<EuiScreenReaderOnly>
<span id={`monitoringChart${titleForAriaIds}`}>
{seriesScreenReaderTextList.join('. ')}
</span>
</EuiScreenReaderOnly>
</Fragment>
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>
</EuiFlexItem> </EuiFlexItem>

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import React from 'react'; import React, { Fragment } from 'react';
import { ClusterStatus } from '../cluster_status'; import { ClusterStatus } from '../cluster_status';
import { ShardActivity } from '../shard_activity'; import { ShardActivity } from '../shard_activity';
import { MonitoringTimeseriesContainer } from '../../chart'; import { MonitoringTimeseriesContainer } from '../../chart';
@ -24,24 +24,24 @@ export function ElasticsearchOverview({
]; ];
return ( return (
<EuiPage style={{ backgroundColor: 'white' }}> <Fragment>
<EuiPageBody> <ClusterStatus stats={clusterStatus} />
<ClusterStatus stats={clusterStatus} /> <EuiPage style={{ backgroundColor: 'white' }}>
<EuiPageBody>
<EuiFlexGrid columns={2} gutterSize="none"> <EuiFlexGrid columns={2} gutterSize="none">
{metricsToShow.map((metric, index) => ( {metricsToShow.map((metric, index) => (
<EuiFlexItem key={index} style={{ width: '50%' }}> <EuiFlexItem key={index} style={{ width: '50%' }}>
<MonitoringTimeseriesContainer <MonitoringTimeseriesContainer
series={metric} series={metric}
{...props} {...props}
/> />
<EuiSpacer size="m"/> <EuiSpacer size="m"/>
</EuiFlexItem> </EuiFlexItem>
))} ))}
</EuiFlexGrid> </EuiFlexGrid>
<ShardActivity data={shardActivity} {...props} />
<ShardActivity data={shardActivity} {...props} /> </EuiPageBody>
</EuiPageBody> </EuiPage>
</EuiPage> </Fragment>
); );
} }

View file

@ -56,6 +56,7 @@ const ToggleCompletedSwitch = ({ toggleHistory, showHistory }) => (
<KuiToolBarSection> <KuiToolBarSection>
<KuiToolBarText> <KuiToolBarText>
<EuiSwitch <EuiSwitch
id="monitoring_completed_recoveries"
label="Completed recoveries" label="Completed recoveries"
onChange={toggleHistory} onChange={toggleHistory}
checked={showHistory} checked={showHistory}

View file

@ -256,6 +256,7 @@ export class MonitoringTable extends React.Component {
pageIndexFirstRow={numVisibleRows ? firstRow + 1 : 0} pageIndexFirstRow={numVisibleRows ? firstRow + 1 : 0}
pageIndexLastRow={numVisibleRows ? numVisibleRows + firstRow : 0} pageIndexLastRow={numVisibleRows ? numVisibleRows + firstRow : 0}
rowsFiltered={numAvailableRows} rowsFiltered={numAvailableRows}
totalRows={this.state.rows.length}
filterText={this.state.filterText} filterText={this.state.filterText}
paginationControls={this.getPaginationControls(numAvailableRows, this.props.alwaysShowPageControls)} paginationControls={this.getPaginationControls(numAvailableRows, this.props.alwaysShowPageControls)}
onFilterChange={this.onFilterChange} onFilterChange={this.onFilterChange}

View file

@ -37,9 +37,18 @@ export function MonitoringTableToolBar(props) {
) )
: null; : null;
const totalRows = Boolean(props.showTotalRows)
? (
<p tabIndex="0" className="monitoringTableToolbarTotalRows">
{props.totalRows} in total
</p>
)
: null;
return ( return (
<KuiToolBar> <KuiToolBar>
{ searchBox } { searchBox }
{ totalRows }
{ props.renderToolBarSections(props) } { props.renderToolBarSections(props) }
{ paginationSection } { paginationSection }
</KuiToolBar> </KuiToolBar>
@ -47,5 +56,6 @@ export function MonitoringTableToolBar(props) {
} }
MonitoringTableToolBar.defaultProps = { MonitoringTableToolBar.defaultProps = {
renderToolBarSections: noop, renderToolBarSections: noop,
showSearchBox: true showSearchBox: true,
showTotalRows: true
}; };

View file

@ -54,17 +54,17 @@ const alertRowFactory = (scope, kbnUrl) => {
return ( return (
<KuiTableRow> <KuiTableRow>
<KuiTableRowCell> <KuiTableRowCell tabIndex="0">
<Tooltip text={severityIcon.title} placement="bottom" trigger="hover"> <Tooltip text={severityIcon.title} placement="bottom" trigger="hover">
<EuiHealth color={severityIcon.color} data-test-subj="alertIcon" aria-label={severityIcon.title}> <EuiHealth color={severityIcon.color} data-test-subj="alertIcon" aria-label={severityIcon.title}>
{ capitalize(severityIcon.value) } { capitalize(severityIcon.value) }
</EuiHealth> </EuiHealth>
</Tooltip> </Tooltip>
</KuiTableRowCell> </KuiTableRowCell>
<KuiTableRowCell> <KuiTableRowCell tabIndex="0">
{ resolution.icon } { resolution.text } { resolution.icon } { resolution.text }
</KuiTableRowCell> </KuiTableRowCell>
<KuiTableRowCell> <KuiTableRowCell tabIndex="0">
<FormattedMessage <FormattedMessage
prefix={props.prefix} prefix={props.prefix}
suffix={props.suffix} suffix={props.suffix}
@ -73,13 +73,13 @@ const alertRowFactory = (scope, kbnUrl) => {
changeUrl={changeUrl} changeUrl={changeUrl}
/> />
</KuiTableRowCell> </KuiTableRowCell>
<KuiTableRowCell> <KuiTableRowCell tabIndex="0">
{ linkToCategories[props.metadata.link] ? linkToCategories[props.metadata.link] : 'General' } { linkToCategories[props.metadata.link] ? linkToCategories[props.metadata.link] : 'General' }
</KuiTableRowCell> </KuiTableRowCell>
<KuiTableRowCell> <KuiTableRowCell tabIndex="0">
{ formatDateTimeLocal(props.update_timestamp) } { formatDateTimeLocal(props.update_timestamp) }
</KuiTableRowCell> </KuiTableRowCell>
<KuiTableRowCell> <KuiTableRowCell tabIndex="0">
{ formatTimestampToDuration(props.timestamp, CALCULATE_DURATION_SINCE) } ago { formatTimestampToDuration(props.timestamp, CALCULATE_DURATION_SINCE) } ago
</KuiTableRowCell> </KuiTableRowCell>
</KuiTableRow> </KuiTableRow>

View file

@ -60,3 +60,7 @@
color: #666977; color: #666977;
font-size: 12px; font-size: 12px;
} }
.monitoringTableToolbarTotalRows {
flex: 2; /* This is to ensure this shows directly next to the search bar in the table toolbar */
}