[Discover] Add focus to h1 on navigate for single document and surrounding document views (#134942)

[Discover] Add focus to h1 on navigate for single document and surrounding document views
This commit is contained in:
Davis McPhee 2022-06-27 14:06:13 -03:00 committed by GitHub
parent 158e4f693d
commit d12db42360
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 162 additions and 1 deletions

View file

@ -15,6 +15,7 @@ import { cloneDeep } from 'lodash';
import { DataView, DataViewField } from '@kbn/data-views-plugin/public';
import { useExecutionContext } from '@kbn/kibana-react-plugin/public';
import { generateFilters } from '@kbn/data-plugin/public';
import { i18n } from '@kbn/i18n';
import { DOC_TABLE_LEGACY, SEARCH_FIELDS_FROM_SOURCE } from '../../../common';
import { ContextErrorMessage } from './components/context_error_message';
import { LoadingStatus } from './services/context_query_state';
@ -143,12 +144,29 @@ export const ContextApp = ({ indexPattern, anchorId }: ContextAppProps) => {
};
};
const contextAppTitle = useRef<HTMLHeadingElement>(null);
useEffect(() => {
contextAppTitle.current?.focus();
}, []);
return (
<Fragment>
{fetchedState.anchorStatus.value === LoadingStatus.FAILED ? (
<ContextErrorMessage status={fetchedState.anchorStatus} />
) : (
<Fragment>
<h1
id="contextAppTitle"
className="euiScreenReaderOnly"
data-test-subj="discoverContextAppTitle"
tabIndex={-1}
ref={contextAppTitle}
>
{i18n.translate('discover.context.pageTitle', {
defaultMessage: 'Documents surrounding #{anchorId}',
values: { anchorId },
})}
</h1>
<TopNavMenu {...getNavBarProps()} />
<EuiPage className={classNames({ dscDocsPage: !isLegacy })}>
<EuiPageContent paddingSize="s" className="dscDocsContent">

View file

@ -6,10 +6,11 @@
* Side Public License, v 1.
*/
import React from 'react';
import React, { useEffect, useRef } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiCallOut, EuiLink, EuiLoadingSpinner, EuiPageContent, EuiPage } from '@elastic/eui';
import type { DataView } from '@kbn/data-views-plugin/public';
import { i18n } from '@kbn/i18n';
import { DocViewer } from '../../../services/doc_views/components/doc_viewer';
import { ElasticRequestState } from '../types';
import { useEsDocSearch } from '../../../hooks/use_es_doc_search';
@ -39,8 +40,26 @@ export function Doc(props: DocProps) {
const [reqState, hit] = useEsDocSearch(props);
const { docLinks } = useDiscoverServices();
const indexExistsLink = docLinks.links.apis.indexExists;
const singleDocTitle = useRef<HTMLHeadingElement>(null);
useEffect(() => {
singleDocTitle.current?.focus();
}, []);
return (
<EuiPage>
<h1
id="singleDocTitle"
className="euiScreenReaderOnly"
data-test-subj="discoverSingleDocTitle"
tabIndex={-1}
ref={singleDocTitle}
>
{i18n.translate('discover.doc.pageTitle', {
defaultMessage: 'Single document - #{id}',
values: { id: props.id },
})}
</h1>
<EuiPageContent>
{reqState === ElasticRequestState.NotFoundIndexPattern && (
<EuiCallOut

View file

@ -21,6 +21,7 @@ exports[`Render <DocViewer/> with 3 different tabs 1`] = `
}
title="Render function"
/>,
"data-test-subj": "docViewerTab-0",
"id": "kbn_doc_viewer_tab_0",
"name": "Render function",
},
@ -35,6 +36,7 @@ exports[`Render <DocViewer/> with 3 different tabs 1`] = `
}
title="React component"
/>,
"data-test-subj": "docViewerTab-1",
"id": "kbn_doc_viewer_tab_1",
"name": "React component",
},
@ -48,6 +50,7 @@ exports[`Render <DocViewer/> with 3 different tabs 1`] = `
}
title="Invalid doc view"
/>,
"data-test-subj": "docViewerTab-2",
"id": "kbn_doc_viewer_tab_2",
"name": "Invalid doc view",
},

View file

@ -35,6 +35,7 @@ export function DocViewer(renderProps: DocViewRenderProps) {
render={render}
/>
),
['data-test-subj']: `docViewerTab-${idx}`,
};
});

View file

@ -0,0 +1,55 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const browser = getService('browser');
const kibanaServer = getService('kibanaServer');
const find = getService('find');
const testSubjects = getService('testSubjects');
const dataGrid = getService('dataGrid');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker', 'context']);
describe('context accessibility', () => {
before(async () => {
await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings();
await kibanaServer.uiSettings.update({
defaultIndex: 'logstash-*',
});
await PageObjects.common.navigateToApp('discover');
await PageObjects.header.waitUntilLoadingHasFinished();
});
after(async function () {
await kibanaServer.uiSettings.replace({});
});
it('should navigate to the single doc view and give focus to the title h1 on navigate', async () => {
await dataGrid.clickRowToggle({ rowIndex: 0 });
const rowActions = await dataGrid.getRowActions({ rowIndex: 0 });
await rowActions[1].click();
const titleElement = await testSubjects.find('discoverContextAppTitle');
const activeElement = await find.activeElement();
expect(await titleElement.getAttribute('data-test-subj')).to.eql(
await activeElement.getAttribute('data-test-subj')
);
});
it('should give focus to the table tab link when Tab is pressed', async () => {
await browser.pressKeys(browser.keys.TAB);
const dataViewSwitchLink = await testSubjects.find('showQueryBarMenu');
const activeElement = await find.activeElement();
expect(await dataViewSwitchLink.getAttribute('data-test-subj')).to.eql(
await activeElement.getAttribute('data-test-subj')
);
});
});
}

View file

@ -29,6 +29,7 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid
);
});
loadTestFile(require.resolve('./_context_accessibility'));
loadTestFile(require.resolve('./_context_navigation'));
loadTestFile(require.resolve('./_discover_navigation'));
loadTestFile(require.resolve('./classic/_discover_navigation'));

View file

@ -0,0 +1,63 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const browser = getService('browser');
const log = getService('log');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const find = getService('find');
const testSubjects = getService('testSubjects');
const dataGrid = getService('dataGrid');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker', 'context']);
const defaultSettings = {
defaultIndex: 'logstash-*',
};
describe('discover doc accessibility', () => {
before(async () => {
log.debug('load kibana index with default index pattern');
await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover');
// and load a set of makelogs data
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
await kibanaServer.uiSettings.replace(defaultSettings);
await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings();
await PageObjects.common.navigateToApp('discover');
await PageObjects.header.waitUntilLoadingHasFinished();
});
after(async () => {
await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] });
});
it('should navigate to the single doc view and give focus to the title h1 on navigate', async () => {
await dataGrid.clickRowToggle({ rowIndex: 0 });
const rowActions = await dataGrid.getRowActions({ rowIndex: 0 });
await rowActions[0].click();
const titleElement = await testSubjects.find('discoverSingleDocTitle');
const activeElement = await find.activeElement();
expect(await titleElement.getAttribute('data-test-subj')).to.eql(
await activeElement.getAttribute('data-test-subj')
);
});
it('should give focus to the first tab link when Tab is pressed', async () => {
await browser.pressKeys(browser.keys.TAB);
const tableTab = await testSubjects.find('docViewerTab-0');
const activeElement = await find.activeElement();
expect(await tableTab.getAttribute('data-test-subj')).to.eql(
await activeElement.getAttribute('data-test-subj')
);
});
});
}

View file

@ -30,6 +30,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./_discover'));
loadTestFile(require.resolve('./_discover_accessibility'));
loadTestFile(require.resolve('./_discover_histogram'));
loadTestFile(require.resolve('./_doc_accessibility'));
loadTestFile(require.resolve('./classic/_doc_table'));
loadTestFile(require.resolve('./classic/_doc_table_newline'));
loadTestFile(require.resolve('./_filter_editor'));