Browse Source

SONAR-12082 Adjust keyboard navigation instruction visibility

tags/8.9.0.43852
Wouter Admiraal 3 years ago
parent
commit
c3b09a7d49
21 changed files with 640 additions and 374 deletions
  1. 13
    0
      server/sonar-web/src/main/js/app/styles/components/ui.css
  2. 9
    22
      server/sonar-web/src/main/js/apps/coding-rules/components/PageActions.tsx
  3. 10
    17
      server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/PageActions-test.tsx
  4. 63
    0
      server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/PageActions-test.tsx.snap
  5. 2
    25
      server/sonar-web/src/main/js/apps/issues/components/App.tsx
  6. 24
    43
      server/sonar-web/src/main/js/apps/issues/components/PageActions.tsx
  7. 0
    4
      server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx
  8. 1
    11
      server/sonar-web/src/main/js/apps/issues/components/__tests__/PageActions-test.tsx
  9. 0
    1
      server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/App-test.tsx.snap
  10. 0
    23
      server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/LocationNavigationKeyboardShortcuts-test.tsx.snap
  11. 6
    41
      server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/PageActions-test.tsx.snap
  12. 7
    11
      server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueBox.tsx
  13. 14
    19
      server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssuesListHeader.tsx
  14. 15
    13
      server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssuesListHeader-test.tsx
  15. 0
    135
      server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueBox-test.tsx.snap
  16. 56
    0
      server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssuesListHeader-test.tsx.snap
  17. 0
    9
      server/sonar-web/src/main/js/apps/issues/styles.css
  18. 109
    0
      server/sonar-web/src/main/js/components/ui/PageShortcutsTooltip.tsx
  19. 36
    0
      server/sonar-web/src/main/js/components/ui/__tests__/PageShortcutsTooltip-test.tsx
  20. 268
    0
      server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/PageShortcutsTooltip-test.tsx.snap
  21. 7
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 13
- 0
server/sonar-web/src/main/js/app/styles/components/ui.css View File

@@ -41,6 +41,19 @@
margin-right: 4px;
}

.shortcut-button-tiny {
width: 14px;
min-width: auto;
padding: 0;
height: 14px;
line-height: inherit;
font-size: 6px;
}

.page-shortcuts-tooltip {
line-height: 12px;
}

.identity-provider {
display: inline-block;
line-height: 14px;

+ 9
- 22
server/sonar-web/src/main/js/apps/coding-rules/components/PageActions.tsx View File

@@ -22,18 +22,23 @@ import ReloadButton from 'sonar-ui-common/components/controls/ReloadButton';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate } from 'sonar-ui-common/helpers/l10n';
import PageCounter from '../../../components/common/PageCounter';
import PageShortcutsTooltip from '../../../components/ui/PageShortcutsTooltip';

interface Props {
export interface PageActionsProps {
loading: boolean;
onReload: () => void;
paging?: T.Paging;
selectedIndex?: number;
}

export default function PageActions(props: Props) {
export default function PageActions(props: PageActionsProps) {
return (
<div className="pull-right">
<Shortcuts />
<div className="display-flex-center display-flex-justify-end">
<PageShortcutsTooltip
className="big-spacer-right"
leftAndRightLabel={translate('issues.to_navigate')}
upAndDownLabel={translate('coding_rules.to_select_rules')}
/>

<DeferredSpinner loading={props.loading}>
<ReloadButton onClick={props.onReload} />
@@ -50,21 +55,3 @@ export default function PageActions(props: Props) {
</div>
);
}

function Shortcuts() {
return (
<span className="note big-spacer-right">
<span className="big-spacer-right">
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button little-spacer-right">↓</span>
{translate('coding_rules.to_select_rules')}
</span>

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

server/sonar-web/src/main/js/apps/issues/components/LocationNavigationKeyboardShortcuts.tsx → server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/PageActions-test.tsx View File

@@ -17,24 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import { translate } from 'sonar-ui-common/helpers/l10n';
import PageActions, { PageActionsProps } from '../PageActions';

export interface Props {
issue: Pick<T.Issue, 'flows' | 'secondaryLocations'> | undefined;
}
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
expect(shallowRender({ paging: { total: 100 } as T.Paging })).toMatchSnapshot('with paging');
});

export default function LocationNavigationKeyboardShortcuts({ issue }: Props) {
if (!issue || (!issue.secondaryLocations.length && !issue.flows.length)) {
return null;
}
const hasSeveralFlows = issue.flows.length > 1;
return (
<div className="navigation-keyboard-shortcuts big-spacer-top text-center">
<span>
alt + ↑ ↓ {hasSeveralFlows && <>←→</>}
{translate('issues.to_navigate_issue_locations')}
</span>
</div>
);
function shallowRender(props: Partial<PageActionsProps> = {}) {
return shallow<PageActionsProps>(<PageActions loading={false} onReload={jest.fn()} {...props} />);
}

+ 63
- 0
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/PageActions-test.tsx.snap View File

@@ -0,0 +1,63 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: default 1`] = `
<div
className="display-flex-center display-flex-justify-end"
>
<PageShortcutsTooltip
className="big-spacer-right"
leftAndRightLabel="issues.to_navigate"
upAndDownLabel="coding_rules.to_select_rules"
/>
<DeferredSpinner
loading={false}
>
<ReloadButton
onClick={[MockFunction]}
/>
</DeferredSpinner>
</div>
`;

exports[`should render correctly: loading 1`] = `
<div
className="display-flex-center display-flex-justify-end"
>
<PageShortcutsTooltip
className="big-spacer-right"
leftAndRightLabel="issues.to_navigate"
upAndDownLabel="coding_rules.to_select_rules"
/>
<DeferredSpinner
loading={true}
>
<ReloadButton
onClick={[MockFunction]}
/>
</DeferredSpinner>
</div>
`;

exports[`should render correctly: with paging 1`] = `
<div
className="display-flex-center display-flex-justify-end"
>
<PageShortcutsTooltip
className="big-spacer-right"
leftAndRightLabel="issues.to_navigate"
upAndDownLabel="coding_rules.to_select_rules"
/>
<DeferredSpinner
loading={false}
>
<ReloadButton
onClick={[MockFunction]}
/>
</DeferredSpinner>
<PageCounter
className="spacer-left"
label="coding_rules._rules"
total={100}
/>
</div>
`;

+ 2
- 25
server/sonar-web/src/main/js/apps/issues/components/App.tsx View File

@@ -797,26 +797,6 @@ export default class App extends React.PureComponent<Props, State> {
this.handleCloseBulkChange();
};

handleReload = () => {
this.fetchFirstIssues();
this.refreshBranchStatus();
const { branchLike, onBranchesChange } = this.props;
if (onBranchesChange && isPullRequest(branchLike)) {
onBranchesChange();
}
};

handleReloadAndOpenFirst = () => {
this.fetchFirstIssues().then(
(issues: T.Issue[]) => {
if (issues.length > 0) {
this.openIssue(issues[0].key);
}
},
() => {}
);
};

selectLocation = (index: number) => {
this.setState(actions.selectLocation(index));
};
@@ -946,9 +926,6 @@ export default class App extends React.PureComponent<Props, State> {
displayBackButton={query.issues.length !== 1}
loading={this.state.loading}
onBackClick={this.closeIssue}
onReload={this.handleReloadAndOpenFirst}
paging={paging}
selectedIndex={this.getSelectedIndex()}
/>
<ConciseIssuesList
issues={issues}
@@ -1070,7 +1047,6 @@ export default class App extends React.PureComponent<Props, State> {
<PageActions
canSetHome={!this.props.component}
effortTotal={this.state.effortTotal}
onReload={this.handleReload}
paging={paging}
selectedIndex={selectedIndex}
/>
@@ -1124,7 +1100,8 @@ export default class App extends React.PureComponent<Props, State> {
}

render() {
const { openIssue } = this.state;
const { openIssue, paging } = this.state;
const selectedIndex = this.getSelectedIndex();
return (
<div className="layout-page issues" id="issues-page">
<Suggestions suggestions="issues" />

+ 24
- 43
server/sonar-web/src/main/js/apps/issues/components/PageActions.tsx View File

@@ -18,60 +18,41 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import ReloadButton from 'sonar-ui-common/components/controls/ReloadButton';
import { translate } from 'sonar-ui-common/helpers/l10n';
import HomePageSelect from '../../../components/controls/HomePageSelect';
import PageShortcutsTooltip from '../../../components/ui/PageShortcutsTooltip';
import { isSonarCloud } from '../../../helpers/system';
import IssuesCounter from './IssuesCounter';
import TotalEffort from './TotalEffort';

interface Props {
export interface PageActionsProps {
canSetHome: boolean;
effortTotal: number | undefined;
onReload: () => void;
paging: T.Paging | undefined;
selectedIndex: number | undefined;
paging?: T.Paging;
selectedIndex?: number;
}

export default class PageActions extends React.PureComponent<Props> {
renderShortcuts() {
return (
<span className="note big-spacer-right">
<span className="big-spacer-right">
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button little-spacer-right">↓</span>
{translate('issues.to_select_issues')}
</span>
export default function PageActions(props: PageActionsProps) {
const { canSetHome, effortTotal, paging, selectedIndex } = props;

<span>
<span className="shortcut-button little-spacer-right">←</span>
<span className="shortcut-button little-spacer-right">→</span>
{translate('issues.to_navigate')}
</span>
</span>
);
}
return (
<div className="display-flex-center display-flex-justify-end">
<PageShortcutsTooltip
leftAndRightLabel={translate('issues.to_navigate')}
upAndDownLabel={translate('issues.to_select_issues')}
/>

render() {
const { effortTotal, paging, selectedIndex } = this.props;

return (
<div className="pull-right">
{this.renderShortcuts()}

<div className="issues-page-actions">
<ReloadButton onClick={this.props.onReload} />
{paging != null && <IssuesCounter current={selectedIndex} total={paging.total} />}
{effortTotal !== undefined && <TotalEffort effort={effortTotal} />}
</div>

{this.props.canSetHome && (
<HomePageSelect
className="huge-spacer-left"
currentPage={isSonarCloud() ? { type: 'MY_ISSUES' } : { type: 'ISSUES' }}
/>
)}
<div className="spacer-left issues-page-actions">
{paging != null && <IssuesCounter current={selectedIndex} total={paging.total} />}
{effortTotal !== undefined && <TotalEffort effort={effortTotal} />}
</div>
);
}

{canSetHome && (
<HomePageSelect
className="huge-spacer-left"
currentPage={isSonarCloud() ? { type: 'MY_ISSUES' } : { type: 'ISSUES' }}
/>
)}
</div>
);
}

+ 0
- 4
server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx View File

@@ -467,10 +467,6 @@ it('should refresh branch status if issues are updated', async () => {
fetchBranchStatus.mockClear();
instance.handleBulkChangeDone();
expect(fetchBranchStatus).toBeCalled();

fetchBranchStatus.mockClear();
instance.handleReload();
expect(fetchBranchStatus).toBeCalled();
});

it('should update the open issue when it is changed', async () => {

+ 1
- 11
server/sonar-web/src/main/js/apps/issues/components/__tests__/PageActions-test.tsx View File

@@ -22,15 +22,5 @@ import * as React from 'react';
import PageActions from '../PageActions';

it('should render', () => {
expect(
shallow(
<PageActions
canSetHome={true}
effortTotal={125}
onReload={jest.fn()}
paging={{ pageIndex: 1, pageSize: 100, total: 12345 }}
selectedIndex={5}
/>
)
).toMatchSnapshot();
expect(shallow(<PageActions canSetHome={true} effortTotal={125} />)).toMatchSnapshot();
});

+ 0
- 1
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/App-test.tsx.snap View File

@@ -82,7 +82,6 @@ exports[`should switch to source view if an issue is selected 1`] = `
<PageActions
canSetHome={false}
effortTotal={1}
onReload={[Function]}
paging={
Object {
"pageIndex": 1,

+ 0
- 23
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/LocationNavigationKeyboardShortcuts-test.tsx.snap View File

@@ -1,23 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<div
className="navigation-keyboard-shortcuts big-spacer-top text-center"
>
<span>
alt + ↑ ↓
issues.to_navigate_issue_locations
</span>
</div>
`;

exports[`should render correctly 2`] = `
<div
className="navigation-keyboard-shortcuts big-spacer-top text-center"
>
<span>
alt + ↑ ↓
issues.to_navigate_issue_locations
</span>
</div>
`;

+ 6
- 41
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/PageActions-test.tsx.snap View File

@@ -2,50 +2,15 @@

exports[`should render 1`] = `
<div
className="pull-right"
className="display-flex-center display-flex-justify-end"
>
<span
className="note big-spacer-right"
>
<span
className="big-spacer-right"
>
<span
className="shortcut-button little-spacer-right"
>
</span>
<span
className="shortcut-button little-spacer-right"
>
</span>
issues.to_select_issues
</span>
<span>
<span
className="shortcut-button little-spacer-right"
>
</span>
<span
className="shortcut-button little-spacer-right"
>
</span>
issues.to_navigate
</span>
</span>
<PageShortcutsTooltip
leftAndRightLabel="issues.to_navigate"
upAndDownLabel="issues.to_select_issues"
/>
<div
className="issues-page-actions"
className="spacer-left issues-page-actions"
>
<ReloadButton
onClick={[MockFunction]}
/>
<IssuesCounter
current={5}
total={12345}
/>
<TotalEffort
effort={125}
/>

+ 7
- 11
server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueBox.tsx View File

@@ -20,7 +20,6 @@
import * as classNames from 'classnames';
import * as React from 'react';
import TypeHelper from '../../../components/shared/TypeHelper';
import LocationNavigationKeyboardShortcuts from '../components/LocationNavigationKeyboardShortcuts';
import ConciseIssueLocations from './ConciseIssueLocations';
import ConciseIssueLocationsNavigator from './ConciseIssueLocationsNavigator';

@@ -105,16 +104,13 @@ export default class ConciseIssueBox extends React.PureComponent<Props> {
/>
</div>
{selected && (
<>
<ConciseIssueLocationsNavigator
issue={issue}
onLocationSelect={this.props.onLocationSelect}
scroll={this.props.scroll}
selectedFlowIndex={this.props.selectedFlowIndex}
selectedLocationIndex={this.props.selectedLocationIndex}
/>
<LocationNavigationKeyboardShortcuts issue={issue} />
</>
<ConciseIssueLocationsNavigator
issue={issue}
onLocationSelect={this.props.onLocationSelect}
scroll={this.props.scroll}
selectedFlowIndex={this.props.selectedFlowIndex}
selectedLocationIndex={this.props.selectedLocationIndex}
/>
)}
</div>
);

+ 14
- 19
server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssuesListHeader.tsx View File

@@ -19,33 +19,28 @@
*/
import * as React from 'react';
import BackButton from 'sonar-ui-common/components/controls/BackButton';
import ReloadButton from 'sonar-ui-common/components/controls/ReloadButton';
import IssuesCounter from '../components/IssuesCounter';
import { translate } from 'sonar-ui-common/helpers/l10n';
import PageShortcutsTooltip from '../../../components/ui/PageShortcutsTooltip';

interface Props {
displayBackButton?: boolean;
export interface ConciseIssuesListHeaderProps {
displayBackButton: boolean;
loading: boolean;
onBackClick: () => void;
onReload: () => void;
paging: T.Paging | undefined;
selectedIndex: number | undefined;
}

export default function ConciseIssuesListHeader(props: Props) {
const { displayBackButton = true, paging, selectedIndex } = props;
export default function ConciseIssuesListHeader(props: ConciseIssuesListHeaderProps) {
const { displayBackButton, loading } = props;

return (
<header className="layout-page-header-panel concise-issues-list-header">
<div className="layout-page-header-panel-inner concise-issues-list-header-inner">
{displayBackButton && (
<BackButton className="pull-left" disabled={props.loading} onClick={props.onBackClick} />
)}
{props.loading ? (
<i className="spinner pull-right" />
) : (
<ReloadButton className="pull-right" onClick={props.onReload} />
)}
{paging && <IssuesCounter current={selectedIndex} total={paging.total} />}
<div className="layout-page-header-panel-inner concise-issues-list-header-inner display-flex-center display-flex-space-between">
{displayBackButton && <BackButton disabled={loading} onClick={props.onBackClick} />}
<PageShortcutsTooltip
leftLabel={translate('issues.to_navigate_back')}
upAndDownLabel={translate('issues.to_select_issues')}
metaModifierLabel={translate('issues.to_navigate_issue_locations')}
/>
{loading && <i className="spinner" />}
</div>
</header>
);

server/sonar-web/src/main/js/apps/issues/components/__tests__/LocationNavigationKeyboardShortcuts-test.tsx → server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssuesListHeader-test.tsx View File

@@ -17,22 +17,24 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import { mockFlowLocation } from '../../../../helpers/testMocks';
import LocationNavigationKeyboardShortcuts, { Props } from '../LocationNavigationKeyboardShortcuts';
import ConciseIssuesListHeader, { ConciseIssuesListHeaderProps } from '../ConciseIssuesListHeader';

it('should render correctly', () => {
expect(shallowRender().type()).toBeNull();
expect(shallowRender({ issue: { flows: [], secondaryLocations: [] } }).type()).toBeNull();
expect(
shallowRender({ issue: { flows: [], secondaryLocations: [mockFlowLocation()] } })
).toMatchSnapshot();
expect(
shallowRender({ issue: { flows: [[mockFlowLocation()]], secondaryLocations: [] } })
).toMatchSnapshot();
expect(shallowRender()).toMatchSnapshot('default');
expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
expect(shallowRender({ displayBackButton: true })).toMatchSnapshot('with back button');
});

const shallowRender = (props: Partial<Props> = {}) => {
return shallow(<LocationNavigationKeyboardShortcuts issue={undefined} {...props} />);
};
function shallowRender(props: Partial<ConciseIssuesListHeaderProps> = {}) {
return shallow<ConciseIssuesListHeaderProps>(
<ConciseIssuesListHeader
displayBackButton={false}
loading={false}
onBackClick={jest.fn()}
{...props}
/>
);
}

+ 0
- 135
server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueBox-test.tsx.snap View File

@@ -92,39 +92,6 @@ exports[`should render correctly 1`] = `
selectedFlowIndex={0}
selectedLocationIndex={0}
/>
<LocationNavigationKeyboardShortcuts
issue={
Object {
"actions": Array [],
"component": "main.js",
"componentLongName": "main.js",
"componentQualifier": "FIL",
"componentUuid": "foo1234",
"creationDate": "2017-03-01T09:36:01+0100",
"flows": Array [],
"fromHotspot": false,
"key": "AVsae-CQS-9G3txfbFN2",
"line": 25,
"message": "Reduce the number of conditional operators (4) used in the expression",
"project": "myproject",
"projectKey": "foo",
"projectName": "Foo",
"rule": "javascript:S1067",
"ruleName": "foo",
"secondaryLocations": Array [],
"severity": "MAJOR",
"status": "OPEN",
"textRange": Object {
"endLine": 26,
"endOffset": 15,
"startLine": 25,
"startOffset": 0,
},
"transitions": Array [],
"type": "BUG",
}
}
/>
</div>
`;

@@ -358,107 +325,5 @@ exports[`should render correctly 2`] = `
selectedFlowIndex={0}
selectedLocationIndex={0}
/>
<LocationNavigationKeyboardShortcuts
issue={
Object {
"actions": Array [],
"component": "main.js",
"componentLongName": "main.js",
"componentQualifier": "FIL",
"componentUuid": "foo1234",
"creationDate": "2017-03-01T09:36:01+0100",
"flows": Array [
Array [
Object {
"component": "main.js",
"textRange": Object {
"endLine": 2,
"endOffset": 2,
"startLine": 1,
"startOffset": 1,
},
},
Object {
"component": "main.js",
"textRange": Object {
"endLine": 2,
"endOffset": 2,
"startLine": 1,
"startOffset": 1,
},
},
Object {
"component": "main.js",
"textRange": Object {
"endLine": 2,
"endOffset": 2,
"startLine": 1,
"startOffset": 1,
},
},
],
Array [
Object {
"component": "main.js",
"textRange": Object {
"endLine": 2,
"endOffset": 2,
"startLine": 1,
"startOffset": 1,
},
},
Object {
"component": "main.js",
"textRange": Object {
"endLine": 2,
"endOffset": 2,
"startLine": 1,
"startOffset": 1,
},
},
],
],
"fromHotspot": false,
"key": "AVsae-CQS-9G3txfbFN2",
"line": 25,
"message": "Reduce the number of conditional operators (4) used in the expression",
"project": "myproject",
"projectKey": "foo",
"projectName": "Foo",
"rule": "javascript:S1067",
"ruleName": "foo",
"secondaryLocations": Array [
Object {
"component": "main.js",
"textRange": Object {
"endLine": 2,
"endOffset": 2,
"startLine": 1,
"startOffset": 1,
},
},
Object {
"component": "main.js",
"textRange": Object {
"endLine": 2,
"endOffset": 2,
"startLine": 1,
"startOffset": 1,
},
},
],
"severity": "MAJOR",
"status": "OPEN",
"textRange": Object {
"endLine": 26,
"endOffset": 15,
"startLine": 25,
"startOffset": 0,
},
"transitions": Array [],
"type": "BUG",
}
}
/>
</div>
`;

+ 56
- 0
server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssuesListHeader-test.tsx.snap View File

@@ -0,0 +1,56 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: default 1`] = `
<header
className="layout-page-header-panel concise-issues-list-header"
>
<div
className="layout-page-header-panel-inner concise-issues-list-header-inner display-flex-center display-flex-space-between"
>
<PageShortcutsTooltip
leftLabel="issues.to_navigate_back"
metaModifierLabel="issues.to_navigate_issue_locations"
upAndDownLabel="issues.to_select_issues"
/>
</div>
</header>
`;

exports[`should render correctly: loading 1`] = `
<header
className="layout-page-header-panel concise-issues-list-header"
>
<div
className="layout-page-header-panel-inner concise-issues-list-header-inner display-flex-center display-flex-space-between"
>
<PageShortcutsTooltip
leftLabel="issues.to_navigate_back"
metaModifierLabel="issues.to_navigate_issue_locations"
upAndDownLabel="issues.to_select_issues"
/>
<i
className="spinner"
/>
</div>
</header>
`;

exports[`should render correctly: with back button 1`] = `
<header
className="layout-page-header-panel concise-issues-list-header"
>
<div
className="layout-page-header-panel-inner concise-issues-list-header-inner display-flex-center display-flex-space-between"
>
<BackButton
disabled={false}
onClick={[MockFunction]}
/>
<PageShortcutsTooltip
leftLabel="issues.to_navigate_back"
metaModifierLabel="issues.to_navigate_issue_locations"
upAndDownLabel="issues.to_select_issues"
/>
</div>
</header>
`;

+ 0
- 9
server/sonar-web/src/main/js/apps/issues/styles.css View File

@@ -327,12 +327,3 @@
.bulk-change-radio-button:hover {
background-color: var(--barBackgroundColor);
}

.navigation-keyboard-shortcuts > span {
background-color: var(--transparentGray);
border-radius: 16px;
display: inline-block;
font-size: var(--smallFontSize);
height: 16px;
padding: calc(var(--gridSize) / 2) var(--gridSize);
}

+ 109
- 0
server/sonar-web/src/main/js/components/ui/PageShortcutsTooltip.tsx View File

@@ -0,0 +1,109 @@
/*
* SonarQube
* Copyright (C) 2009-2021 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 classNames from 'classnames';
import * as React from 'react';
import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';

export interface PageShortcutsTooltipProps {
className?: string;
leftAndRightLabel?: string;
leftLabel?: string;
upAndDownLabel?: string;
metaModifierLabel?: string;
}

export default function PageShortcutsTooltip(props: PageShortcutsTooltipProps) {
const { className, leftAndRightLabel, leftLabel, upAndDownLabel, metaModifierLabel } = props;
return (
<Tooltip
overlay={
<div className="small nowrap">
<div>
{upAndDownLabel && (
<span>
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button spacer-right">↓</span>
{upAndDownLabel}
</span>
)}
{leftAndRightLabel && (
<span className={classNames({ 'big-spacer-left': upAndDownLabel })}>
<span className="shortcut-button little-spacer-right">←</span>
<span className="shortcut-button spacer-right">→</span>
{leftAndRightLabel}
</span>
)}
{leftLabel && (
<span className={classNames({ 'big-spacer-left': upAndDownLabel })}>
<span className="shortcut-button spacer-right">←</span>
{leftLabel}
</span>
)}
</div>
{metaModifierLabel && (
<div className="big-spacer-top big-padded-top bordered-top">
<span className="shortcut-button little-spacer-right">alt</span>
<span className="little-spacer-right">+</span>
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button spacer-right">↓</span>
<span className="shortcut-button little-spacer-right">←</span>
<span className="shortcut-button spacer-right">→</span>
{metaModifierLabel}
</div>
)}
</div>
}>
<div
aria-label={`
${translate('shortcuts.on_page.intro')}
${
upAndDownLabel
? translateWithParameters('shortcuts.on_page.up_down_x', upAndDownLabel)
: ''
}
${
leftAndRightLabel
? translateWithParameters('shortcuts.on_page.left_right_x', leftAndRightLabel)
: ''
}
${leftLabel ? translateWithParameters('shortcuts.on_page.left_x', leftLabel) : ''}
${
metaModifierLabel
? translateWithParameters('shortcuts.on_page.meta_x', metaModifierLabel)
: ''
}
`}
className={classNames(
className,
'page-shortcuts-tooltip note text-center display-inline-block'
)}>
<div>
<span className="shortcut-button shortcut-button-tiny">↑</span>
</div>
<div>
<span className="shortcut-button shortcut-button-tiny">←</span>
<span className="shortcut-button shortcut-button-tiny">↓</span>
<span className="shortcut-button shortcut-button-tiny">→</span>
</div>
</div>
</Tooltip>
);
}

+ 36
- 0
server/sonar-web/src/main/js/components/ui/__tests__/PageShortcutsTooltip-test.tsx View File

@@ -0,0 +1,36 @@
/*
* SonarQube
* Copyright (C) 2009-2021 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 { shallow } from 'enzyme';
import * as React from 'react';
import PageShortcutsTooltip, { PageShortcutsTooltipProps } from '../PageShortcutsTooltip';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
expect(shallowRender({ upAndDownLabel: 'foo', leftAndRightLabel: 'bar' })).toMatchSnapshot(
'with up/down and left/right labels'
);
expect(shallowRender({ leftLabel: 'baz' })).toMatchSnapshot('only left label');
expect(shallowRender({ metaModifierLabel: 'funky' })).toMatchSnapshot('with meta label');
});

function shallowRender(props: Partial<PageShortcutsTooltipProps> = {}) {
return shallow<PageShortcutsTooltipProps>(<PageShortcutsTooltip {...props} />);
}

+ 268
- 0
server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/PageShortcutsTooltip-test.tsx.snap View File

@@ -0,0 +1,268 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: default 1`] = `
<Tooltip
overlay={
<div
className="small nowrap"
>
<div />
</div>
}
>
<div
aria-label="
shortcuts.on_page.intro
"
className="page-shortcuts-tooltip note text-center display-inline-block"
>
<div>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
</div>
<div>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
</div>
</div>
</Tooltip>
`;

exports[`should render correctly: only left label 1`] = `
<Tooltip
overlay={
<div
className="small nowrap"
>
<div>
<span
className=""
>
<span
className="shortcut-button spacer-right"
>
</span>
baz
</span>
</div>
</div>
}
>
<div
aria-label="
shortcuts.on_page.intro
shortcuts.on_page.left_x.baz
"
className="page-shortcuts-tooltip note text-center display-inline-block"
>
<div>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
</div>
<div>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
</div>
</div>
</Tooltip>
`;

exports[`should render correctly: with meta label 1`] = `
<Tooltip
overlay={
<div
className="small nowrap"
>
<div />
<div
className="big-spacer-top big-padded-top bordered-top"
>
<span
className="shortcut-button little-spacer-right"
>
alt
</span>
<span
className="little-spacer-right"
>
+
</span>
<span
className="shortcut-button little-spacer-right"
>
</span>
<span
className="shortcut-button spacer-right"
>
</span>
<span
className="shortcut-button little-spacer-right"
>
</span>
<span
className="shortcut-button spacer-right"
>
</span>
funky
</div>
</div>
}
>
<div
aria-label="
shortcuts.on_page.intro
shortcuts.on_page.meta_x.funky
"
className="page-shortcuts-tooltip note text-center display-inline-block"
>
<div>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
</div>
<div>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
</div>
</div>
</Tooltip>
`;

exports[`should render correctly: with up/down and left/right labels 1`] = `
<Tooltip
overlay={
<div
className="small nowrap"
>
<div>
<span>
<span
className="shortcut-button little-spacer-right"
>
</span>
<span
className="shortcut-button spacer-right"
>
</span>
foo
</span>
<span
className="big-spacer-left"
>
<span
className="shortcut-button little-spacer-right"
>
</span>
<span
className="shortcut-button spacer-right"
>
</span>
bar
</span>
</div>
</div>
}
>
<div
aria-label="
shortcuts.on_page.intro
shortcuts.on_page.up_down_x.foo
shortcuts.on_page.left_right_x.bar
"
className="page-shortcuts-tooltip note text-center display-inline-block"
>
<div>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
</div>
<div>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
<span
className="shortcut-button shortcut-button-tiny"
>
</span>
</div>
</div>
</Tooltip>
`;

+ 7
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -863,6 +863,7 @@ issues.select_all_issues=Select all Issues
issues.issues=issues
issues.to_select_issues=to select issues
issues.to_navigate=to navigate
issues.to_navigate_back=to navigate back
issues.to_navigate_issue_locations=to navigate issue locations
issues.to_switch_flows=to switch flows
issues.new_code=New code
@@ -1316,6 +1317,12 @@ shortcuts.section.rules.return_to_list=return back to the list
shortcuts.section.rules.activate=activate selected rule
shortcuts.section.rules.deactivate=deactivate selected rule

shortcuts.on_page.intro=This page allows you to use the following keyboard shortcuts:
shortcuts.on_page.left_x=Left arrow key: {0}
shortcuts.on_page.left_right_x=Left and right arrow keys: {0}
shortcuts.on_page.up_down_x=Up and down arrow keys: {0}
shortcuts.on_page.meta_x=Alt key + arrow keys: {0}

tutorials.onboarding=Analyze a new project
tutorials.skip=Skip this tutorial
tutorials.finish=Finish this tutorial

Loading…
Cancel
Save