Browse Source

SONAR-11645 Disable sibling navigation in Measures

tags/7.7
Wouter Admiraal 5 years ago
parent
commit
46a60dac2e

+ 0
- 1
server/sonar-docs/src/pages/user-guide/keyboard-shortcuts.md View File

@@ -42,7 +42,6 @@ url: /user-guide/keyboard-shortcuts/
| `↑` `↓` | select files |
| `→` | open file |
| `←` | return back to the list |
| `j` `k` | switch between files |

## Rules Page


+ 27
- 35
server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx View File

@@ -25,7 +25,7 @@ import MeasureHeader from './MeasureHeader';
import MeasureViewSelect from './MeasureViewSelect';
import PageActions from '../../../components/ui/PageActions';
import { complementary } from '../config/complementary';
import CodeView from '../drilldown/CodeView';
import SourceViewer from '../../../components/SourceViewer/SourceViewer';
import FilesView from '../drilldown/FilesView';
import TreeMapView from '../drilldown/TreeMapView';
import { Query, View, isFileType, enhanceComponent, isViewType } from '../utils';
@@ -263,21 +263,6 @@ export default class MeasureContent extends React.PureComponent<Props, State> {
return index !== -1 ? index : undefined;
};

renderCode() {
return (
<div className="measure-details-viewer">
<CodeView
branchLike={this.props.branchLike}
component={this.state.baseComponent!}
components={this.state.components}
leakPeriod={this.props.leakPeriod}
selectedIdx={this.getSelectedIndex()}
updateSelected={this.updateSelected}
/>
</div>
);
}

renderMeasure() {
const { view } = this.props;
const { metric } = this.state;
@@ -318,7 +303,7 @@ export default class MeasureContent extends React.PureComponent<Props, State> {

render() {
const { branchLike, rootComponent, view } = this.props;
const { baseComponent, measure, metric, secondaryMeasure } = this.state;
const { baseComponent, measure, metric, paging, secondaryMeasure } = this.state;

if (!baseComponent || !metric) {
return null;
@@ -349,24 +334,25 @@ export default class MeasureContent extends React.PureComponent<Props, State> {
<div className="display-flex-center">
{!isFile &&
metric && (
<MeasureViewSelect
className="measure-view-select big-spacer-right"
handleViewChange={this.updateView}
metric={metric}
view={view}
/>
<>
<MeasureViewSelect
className="measure-view-select big-spacer-right"
handleViewChange={this.updateView}
metric={metric}
view={view}
/>

<PageActions
current={
selectedIdx !== undefined && view !== 'treemap'
? selectedIdx + 1
: undefined
}
showShortcuts={['list', 'tree'].includes(view)}
total={paging && paging.total}
/>
</>
)}
<PageActions
current={
selectedIdx !== undefined && view !== 'treemap'
? selectedIdx + 1
: undefined
}
isFile={isFile}
paging={this.state.paging}
showShortcuts={['list', 'tree'].includes(view)}
totalLoadedComponents={this.state.components.length}
/>
</div>
}
/>
@@ -383,7 +369,13 @@ export default class MeasureContent extends React.PureComponent<Props, State> {
metric={metric}
secondaryMeasure={secondaryMeasure}
/>
{isFile ? this.renderCode() : this.renderMeasure()}
{isFile ? (
<div className="measure-details-viewer">
<SourceViewer branchLike={branchLike} component={baseComponent.key} />
</div>
) : (
this.renderMeasure()
)}
</div>
</div>
);

+ 2
- 3
server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx View File

@@ -134,7 +134,7 @@ export default class MeasureOverview extends React.PureComponent<Props, State> {

render() {
const { branchLike, component, leakPeriod, rootComponent } = this.props;
const isFile = isFileType(component);
const { paging } = this.state;
return (
<div className={this.props.className}>
<div className="layout-page-header-panel layout-page-main-header">
@@ -154,8 +154,7 @@ export default class MeasureOverview extends React.PureComponent<Props, State> {
right={
<PageActions
current={this.state.components.length}
isFile={isFile}
paging={this.state.paging}
total={paging && paging.total}
/>
}
/>

+ 0
- 81
server/sonar-web/src/main/js/apps/component-measures/drilldown/CodeView.tsx View File

@@ -1,81 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import * as key from 'keymaster';
import SourceViewer from '../../../components/SourceViewer/SourceViewer';

interface Props {
branchLike?: T.BranchLike;
component: T.ComponentMeasure;
components: T.ComponentMeasureEnhanced[];
leakPeriod?: T.Period;
selectedIdx?: number;
updateSelected: (component: string) => void;
}

export default class CodeView extends React.PureComponent<Props> {
componentDidMount() {
this.attachShortcuts();
}

componentWillUnmount() {
this.detachShortcuts();
}

attachShortcuts() {
key('j', 'measures-files', () => {
this.selectNext();
return false;
});
key('k', 'measures-files', () => {
this.selectPrevious();
return false;
});
}

detachShortcuts() {
['j', 'k'].forEach(action => key.unbind(action, 'measures-files'));
}

selectPrevious = () => {
const { selectedIdx } = this.props;
if (selectedIdx != null && selectedIdx > 0) {
const prevComponent = this.props.components[selectedIdx - 1];
if (prevComponent) {
this.props.updateSelected(prevComponent.key);
}
}
};

selectNext = () => {
const { components, selectedIdx } = this.props;
if (selectedIdx != null && selectedIdx < components.length - 1) {
const nextComponent = components[selectedIdx + 1];
if (nextComponent) {
this.props.updateSelected(nextComponent.key);
}
}
};

render() {
const { branchLike, component } = this.props;
return <SourceViewer branchLike={branchLike} component={component.key} />;
}
}

+ 0
- 9
server/sonar-web/src/main/js/apps/component-measures/style.css View File

@@ -64,15 +64,6 @@
white-space: nowrap;
}

.measure-details-page-actions {
display: inline-block;
text-align: right;
}

.measure-details-page-actions .spinner {
vertical-align: text-bottom;
}

.measure-details-metric {
display: flex;
align-items: center;

+ 1
- 11
server/sonar-web/src/main/js/components/hoc/__tests__/withKeyboardNavigation-test.tsx View File

@@ -130,7 +130,7 @@ it('should support not cycling through elements, and triggering a callback on re
expect(onHighlight).toBeCalledWith(COMPONENTS[0]);
});

it('should correctly bind key events for sibling navigation', () => {
it('should correctly bind key events for codeview navigation', () => {
const onGoToParent = jest.fn();
const onHighlight = jest.fn();
const onSelect = jest.fn();
@@ -149,26 +149,16 @@ it('should correctly bind key events for sibling navigation', () => {

keydown('down');
expect(onHighlight).not.toBeCalled();
expect(onSelect).not.toBeCalled();

keydown('up');
expect(onHighlight).not.toBeCalled();
expect(onSelect).not.toBeCalled();

keydown('right');
expect(onHighlight).not.toBeCalled();
expect(onSelect).not.toBeCalled();

keydown('enter');
expect(onHighlight).not.toBeCalled();
expect(onSelect).not.toBeCalled();

keydown('j');
expect(onSelect).toBeCalledWith(COMPONENTS[2]);

keydown('k');
expect(onSelect).toBeCalledWith(COMPONENTS[0]);

keydown('left');
expect(onGoToParent).toBeCalled();
});

+ 1
- 25
server/sonar-web/src/main/js/components/hoc/withKeyboardNavigation.tsx View File

@@ -64,12 +64,6 @@ export default function withKeyboardNavigation<P>(
this.handleSelectParent();
return false; // always hijack left
});
key('k', KEY_SCOPE, () => {
return this.skipIfNotFile(this.handleSelectPrevious);
});
key('j', KEY_SCOPE, () => {
return this.skipIfNotFile(this.handleSelectNext);
});
};

detachShortcuts = () => {
@@ -90,15 +84,6 @@ export default function withKeyboardNavigation<P>(
}
};

skipIfNotFile = (handler: () => void) => {
if (this.props.isFile) {
handler();
return false;
} else {
return true;
}
};

handleHighlightNext = () => {
if (this.props.onHighlight === undefined) {
return;
@@ -172,18 +157,9 @@ export default function withKeyboardNavigation<P>(
};

render() {
const { components = [], isFile } = this.props;
const index = this.getCurrentIndex();

return (
<>
<PageActions
current={index > -1 ? index + 1 : undefined}
isFile={isFile}
showPaging={isFile && index > -1}
showShortcuts={true}
totalLoadedComponents={components.length}
/>
<PageActions showShortcuts={!this.props.isFile} />

<WrappedComponent {...this.props} />
</>

+ 20
- 47
server/sonar-web/src/main/js/components/ui/PageActions.tsx View File

@@ -21,64 +21,37 @@ import * as React from 'react';
import FilesCounter from './FilesCounter';
import { translate } from '../../helpers/l10n';

interface Props {
export interface Props {
current?: number;
isFile?: boolean;
paging?: T.Paging;
showPaging?: boolean;
showShortcuts?: boolean;
totalLoadedComponents?: number;
total?: number;
}

export default function PageActions(props: Props) {
const { isFile, paging, showPaging, showShortcuts, totalLoadedComponents } = props;
let total = 0;

if (showPaging && totalLoadedComponents) {
total = totalLoadedComponents;
} else if (paging !== undefined) {
total = isFile && totalLoadedComponents ? totalLoadedComponents : paging.total;
}
const { current, showShortcuts, total = 0 } = props;

return (
<div className="page-actions display-flex-center">
{!isFile && showShortcuts && renderShortcuts()}
{isFile && (paging || showPaging) && renderFileShortcuts()}
{showShortcuts && (
<span className="note nowrap">
<span className="big-spacer-right">
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button little-spacer-right">↓</span>
{translate('component_measures.to_select_files')}
</span>

<span>
<span className="shortcut-button little-spacer-right">←</span>
<span className="shortcut-button little-spacer-right">→</span>
{translate('component_measures.to_navigate')}
</span>
</span>
)}
{total > 0 && (
<div className="measure-details-page-actions nowrap">
<FilesCounter className="big-spacer-left" current={props.current} total={total} />
<div className="nowrap">
<FilesCounter className="big-spacer-left" current={current} total={total} />
</div>
)}
</div>
);
}

function renderShortcuts() {
return (
<span className="note nowrap">
<span className="big-spacer-right">
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button little-spacer-right">↓</span>
{translate('component_measures.to_select_files')}
</span>

<span>
<span className="shortcut-button little-spacer-right">←</span>
<span className="shortcut-button little-spacer-right">→</span>
{translate('component_measures.to_navigate')}
</span>
</span>
);
}

function renderFileShortcuts() {
return (
<span className="note nowrap">
<span>
<span className="shortcut-button little-spacer-right">j</span>
<span className="shortcut-button little-spacer-right">k</span>
{translate('component_measures.to_navigate_files')}
</span>
</span>
);
}

+ 8
- 50
server/sonar-web/src/main/js/components/ui/__tests__/PageActions-test.tsx View File

@@ -19,56 +19,14 @@
*/
import * as React from 'react';
import { shallow } from 'enzyme';
import PageActions from '../PageActions';
import PageActions, { Props } from '../PageActions';

const PAGING = {
pageIndex: 1,
pageSize: 100,
total: 120
};

it('should display correctly for a project', () => {
expect(
shallow(<PageActions isFile={false} showShortcuts={true} totalLoadedComponents={20} />)
).toMatchSnapshot();
});

it('should display correctly for a file', () => {
const wrapper = shallow(
<PageActions isFile={true} showShortcuts={true} totalLoadedComponents={10} />
);
expect(wrapper).toMatchSnapshot();
wrapper.setProps({ paging: { total: 100 } });
expect(wrapper).toMatchSnapshot();
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
expect(shallowRender({ total: 10 })).toMatchSnapshot();
expect(shallowRender({ current: 12, showShortcuts: false, total: 120 })).toMatchSnapshot();
});

it('should not display shortcuts for treemap', () => {
expect(
shallow(<PageActions isFile={false} showShortcuts={false} totalLoadedComponents={20} />)
).toMatchSnapshot();
});

it('should display the total of files', () => {
expect(
shallow(
<PageActions
current={12}
isFile={false}
paging={PAGING}
showShortcuts={false}
totalLoadedComponents={20}
/>
)
).toMatchSnapshot();
expect(
shallow(
<PageActions
current={12}
isFile={true}
paging={PAGING}
showShortcuts={true}
totalLoadedComponents={20}
/>
)
).toMatchSnapshot();
});
function shallowRender(props: Partial<Props> = {}) {
return shallow(<PageActions showShortcuts={true} {...props} />);
}

+ 25
- 55
server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/PageActions-test.tsx.snap View File

@@ -1,44 +1,45 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should display correctly for a file 1`] = `
<div
className="page-actions display-flex-center"
/>
`;

exports[`should display correctly for a file 2`] = `
exports[`should render correctly 1`] = `
<div
className="page-actions display-flex-center"
>
<span
className="note nowrap"
>
<span
className="big-spacer-right"
>
<span
className="shortcut-button little-spacer-right"
>
</span>
<span
className="shortcut-button little-spacer-right"
>
</span>
component_measures.to_select_files
</span>
<span>
<span
className="shortcut-button little-spacer-right"
>
j
</span>
<span
className="shortcut-button little-spacer-right"
>
k
</span>
component_measures.to_navigate_files
component_measures.to_navigate
</span>
</span>
<div
className="measure-details-page-actions nowrap"
>
<FilesCounter
className="big-spacer-left"
total={10}
/>
</div>
</div>
`;

exports[`should display correctly for a project 1`] = `
exports[`should render correctly 2`] = `
<div
className="page-actions display-flex-center"
>
@@ -74,60 +75,29 @@ exports[`should display correctly for a project 1`] = `
component_measures.to_navigate
</span>
</span>
</div>
`;

exports[`should display the total of files 1`] = `
<div
className="page-actions display-flex-center"
>
<div
className="measure-details-page-actions nowrap"
className="nowrap"
>
<FilesCounter
className="big-spacer-left"
current={12}
total={120}
total={10}
/>
</div>
</div>
`;

exports[`should display the total of files 2`] = `
exports[`should render correctly 3`] = `
<div
className="page-actions display-flex-center"
>
<span
className="note nowrap"
>
<span>
<span
className="shortcut-button little-spacer-right"
>
j
</span>
<span
className="shortcut-button little-spacer-right"
>
k
</span>
component_measures.to_navigate_files
</span>
</span>
<div
className="measure-details-page-actions nowrap"
className="nowrap"
>
<FilesCounter
className="big-spacer-left"
current={12}
total={20}
total={120}
/>
</div>
</div>
`;

exports[`should not display shortcuts for treemap 1`] = `
<div
className="page-actions display-flex-center"
/>
`;

+ 1
- 3
server/sonar-web/src/main/js/helpers/testUtils.ts View File

@@ -73,9 +73,7 @@ export const KEYCODE_MAP: { [keycode: number]: string } = {
37: 'left',
38: 'up',
39: 'right',
40: 'down',
74: 'j',
75: 'k'
40: 'down'
};

export function keydown(key: number | string): void {

Loading…
Cancel
Save