aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/component-measures
diff options
context:
space:
mode:
authorGuillaume Peoc'h <guillaume.peoch@sonarsource.com>2022-05-03 15:06:43 +0200
committersonartech <sonartech@sonarsource.com>2022-05-10 20:02:47 +0000
commit02519d93219660a558c9ac1837f30e869ce005a5 (patch)
tree70e2ca4bc075ee75b4226d58270a89aed54dc0fc /server/sonar-web/src/main/js/apps/component-measures
parentdafa3fa22dc51bd9d07af693d1015de49ccf3680 (diff)
downloadsonarqube-02519d93219660a558c9ac1837f30e869ce005a5.tar.gz
sonarqube-02519d93219660a558c9ac1837f30e869ce005a5.zip
SONAR-16338 Replace keymaster in Measures
Diffstat (limited to 'server/sonar-web/src/main/js/apps/component-measures')
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/App.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/Breadcrumbs-test.tsx35
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx32
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx54
5 files changed, 90 insertions, 54 deletions
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx
index c44ff041d5f..280b6076506 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx
@@ -18,7 +18,6 @@
* 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';
@@ -104,7 +103,6 @@ export class App extends React.PureComponent<Props, State> {
componentDidMount() {
this.mounted = true;
- key.setScope('measures-files');
getAllMetrics().then(
metrics => {
const byKey = keyBy(metrics, 'key');
@@ -137,7 +135,6 @@ export class App extends React.PureComponent<Props, State> {
this.mounted = false;
removeWhitePageClass();
removeSideBarClass();
- key.deleteScope('measures-files');
}
fetchMeasures(metrics: State['metrics']) {
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.tsx
index 98d6a79748c..9fd2ddcbf7a 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.tsx
@@ -17,10 +17,10 @@
* 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';
@@ -45,7 +45,7 @@ export default class Breadcrumbs extends React.PureComponent<Props, State> {
componentDidMount() {
this.mounted = true;
this.fetchBreadcrumbs();
- this.attachShortcuts();
+ document.addEventListener('keydown', this.handleKeyDown);
}
componentDidUpdate(prevProps: Props) {
@@ -59,23 +59,19 @@ export default class Breadcrumbs extends React.PureComponent<Props, State> {
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;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/Breadcrumbs-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/Breadcrumbs-test.tsx
index ad8796fb13e..0a5167a2723 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/Breadcrumbs-test.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/Breadcrumbs-test.tsx
@@ -17,10 +17,11 @@
* 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', () => ({
@@ -60,26 +61,32 @@ it('should display correctly for the list view', () => {
});
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();
-});
+}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx
index 719c32deda6..74c2a9195ab 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx
@@ -17,12 +17,12 @@
* 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';
@@ -58,8 +58,6 @@ interface State {
showBestMeasures: boolean;
}
-const keyScope = 'measures-files';
-
export default class FilesView extends React.PureComponent<Props, State> {
listContainer?: HTMLElement | null;
@@ -71,7 +69,7 @@ export default class FilesView extends React.PureComponent<Props, State> {
}
componentDidMount() {
- this.attachShortcuts();
+ document.addEventListener('keydown', this.handleKeyDown);
if (this.props.selectedComponent !== undefined) {
this.scrollToElement();
}
@@ -90,27 +88,21 @@ export default class FilesView extends React.PureComponent<Props, State> {
}
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;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx
index b07c8937873..53eb6307322 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx
@@ -19,6 +19,9 @@
*/
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 = [
@@ -33,17 +36,17 @@ 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'
}
@@ -52,8 +55,49 @@ it('should render with best values hidden', () => {
).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}