* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import styled from '@emotion/styled';
-import key from 'keymaster';
import { debounce, keyBy } from 'lodash';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
componentDidMount() {
this.mounted = true;
- key.setScope('measures-files');
getAllMetrics().then(
metrics => {
const byKey = keyBy(metrics, 'key');
this.mounted = false;
removeWhitePageClass();
removeSideBarClass();
- key.deleteScope('measures-files');
}
fetchMeasures(metrics: State['metrics']) {
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import key from 'keymaster';
import * as React from 'react';
import { getBreadcrumbs } from '../../../api/components';
import { getBranchLikeQuery, isSameBranchLike } from '../../../helpers/branch-like';
+import { KeyboardCodes } from '../../../helpers/keycodes';
import { BranchLike } from '../../../types/branch-like';
import { ComponentMeasure, ComponentMeasureIntern } from '../../../types/types';
import Breadcrumb from './Breadcrumb';
componentDidMount() {
this.mounted = true;
this.fetchBreadcrumbs();
- this.attachShortcuts();
+ document.addEventListener('keydown', this.handleKeyDown);
}
componentDidUpdate(prevProps: Props) {
componentWillUnmount() {
this.mounted = false;
- this.detachShortcuts();
+ document.removeEventListener('keydown', this.handleKeyDown);
}
- attachShortcuts() {
- key('left', 'measures-files', () => {
+ handleKeyDown = (event: KeyboardEvent) => {
+ if (event.code === KeyboardCodes.LeftArrow) {
+ event.preventDefault();
const { breadcrumbs } = this.state;
if (breadcrumbs.length > 1) {
const idx = this.props.backToFirst ? 0 : breadcrumbs.length - 2;
this.props.handleSelect(breadcrumbs[idx]);
}
- return false;
- });
- }
-
- detachShortcuts() {
- key.unbind('left', 'measures-files');
- }
+ }
+ };
fetchBreadcrumbs = () => {
const { branchLike, component, rootComponent } = this.props;
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { mount } from 'enzyme';
+import { mount, shallow } from 'enzyme';
import * as React from 'react';
import { getBreadcrumbs } from '../../../../api/components';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
+import { KeyboardCodes } from '../../../../helpers/keycodes';
+import { keydown, waitAndUpdate } from '../../../../helpers/testUtils';
import Breadcrumbs from '../Breadcrumbs';
jest.mock('../../../../api/components', () => ({
});
it('should display only the root component', () => {
- const wrapper = mount(
- <Breadcrumbs
- backToFirst={false}
- component={componentFoo}
- handleSelect={() => {}}
- rootComponent={componentFoo}
- />
- );
+ const wrapper = shallowRender({ component: componentFoo });
expect(wrapper.state()).toMatchSnapshot();
});
it('should load the breadcrumb from the api', async () => {
- const wrapper = mount(
+ const wrapper = shallowRender();
+ await waitAndUpdate(wrapper);
+ expect(getBreadcrumbs).toHaveBeenCalled();
+});
+
+it('should correctly handle keyboard action', async () => {
+ const handleSelect = jest.fn();
+ const wrapper = shallowRender({ handleSelect });
+ await waitAndUpdate(wrapper);
+ keydown({ code: KeyboardCodes.LeftArrow });
+ expect(handleSelect).toHaveBeenCalled();
+});
+
+function shallowRender(props: Partial<Breadcrumbs['props']> = {}) {
+ return shallow<Breadcrumbs>(
<Breadcrumbs
backToFirst={false}
component={componentBar}
handleSelect={() => {}}
rootComponent={componentFoo}
+ {...props}
/>
);
- await waitAndUpdate(wrapper);
- expect(getBreadcrumbs).toHaveBeenCalled();
-});
+}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import key from 'keymaster';
import { throttle } from 'lodash';
import * as React from 'react';
import { Button } from '../../../components/controls/buttons';
import ListFooter from '../../../components/controls/ListFooter';
import { Alert } from '../../../components/ui/Alert';
+import { KeyboardCodes } from '../../../helpers/keycodes';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { formatMeasure, isDiffMetric, isPeriodBestValue } from '../../../helpers/measures';
import { scrollToElement } from '../../../helpers/scrolling';
showBestMeasures: boolean;
}
-const keyScope = 'measures-files';
-
export default class FilesView extends React.PureComponent<Props, State> {
listContainer?: HTMLElement | null;
}
componentDidMount() {
- this.attachShortcuts();
+ document.addEventListener('keydown', this.handleKeyDown);
if (this.props.selectedComponent !== undefined) {
this.scrollToElement();
}
}
componentWillUnmount() {
- this.detachShortcuts();
+ document.removeEventListener('keydown', this.handleKeyDown);
}
- attachShortcuts() {
- key('up', keyScope, () => {
+ handleKeyDown = (event: KeyboardEvent) => {
+ if (event.code === KeyboardCodes.UpArrow) {
+ event.preventDefault();
this.selectPrevious();
- return false;
- });
- key('down', keyScope, () => {
+ } else if (event.code === KeyboardCodes.DownArrow) {
+ event.preventDefault();
this.selectNext();
- return false;
- });
- key('right', keyScope, () => {
+ } else if (event.code === KeyboardCodes.RightArrow) {
+ event.preventDefault();
this.openSelected();
- return false;
- });
- }
-
- detachShortcuts() {
- ['up', 'down', 'right'].forEach(action => key.unbind(action, keyScope));
- }
+ }
+ };
getVisibleComponents = () => {
const { components } = this.props;
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { KeyboardCodes } from '../../../../helpers/keycodes';
+import { mockMetric } from '../../../../helpers/testMocks';
+import { keydown } from '../../../../helpers/testUtils';
import FilesView from '../FilesView';
const COMPONENTS = [
const METRICS = { coverage: { id: '1', key: 'coverage', type: 'PERCENT', name: 'Coverage' } };
it('should renders correctly', () => {
- expect(getWrapper()).toMatchSnapshot();
+ expect(shallowRender()).toMatchSnapshot();
});
it('should render with best values hidden', () => {
expect(
- getWrapper({
+ shallowRender({
components: [
...COMPONENTS,
{
key: 'bar',
- measures: [{ bestValue: true, metric: { key: 'coverage' } }],
+ measures: [{ bestValue: true, metric: mockMetric({ key: 'coverage' }) }],
name: 'Bar',
qualifier: 'TRK'
}
).toMatchSnapshot();
});
-function getWrapper(props = {}) {
- return shallow(
+it('should correctly bind key events for file navigation', () => {
+ const handleSelect = jest.fn();
+ const handleOpen = jest.fn();
+ const FILES = [
+ {
+ key: 'foo',
+ measures: [],
+ name: 'Foo',
+ qualifier: 'TRK'
+ },
+ {
+ key: 'bar',
+ measures: [],
+ name: 'Bar',
+ qualifier: 'TRK'
+ },
+ {
+ key: 'yoo',
+ measures: [],
+ name: 'Yoo',
+ qualifier: 'TRK'
+ }
+ ];
+
+ shallowRender({
+ handleSelect,
+ handleOpen,
+ selectedComponent: FILES[0],
+ components: FILES
+ });
+
+ keydown({ code: KeyboardCodes.DownArrow });
+ expect(handleSelect).toBeCalledWith(FILES[0]);
+
+ keydown({ code: KeyboardCodes.UpArrow });
+ expect(handleSelect).toBeCalledWith(FILES[2]);
+
+ keydown({ code: KeyboardCodes.RightArrow });
+ expect(handleOpen).toBeCalled();
+});
+
+function shallowRender(props: Partial<FilesView['props']> = {}) {
+ return shallow<FilesView>(
<FilesView
components={COMPONENTS}
defaultShowBestMeasures={false}