]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12082 Adjust keyboard navigation instruction visibility
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Thu, 8 Apr 2021 11:59:54 +0000 (13:59 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 9 Apr 2021 20:03:53 +0000 (20:03 +0000)
23 files changed:
server/sonar-web/src/main/js/app/styles/components/ui.css
server/sonar-web/src/main/js/apps/coding-rules/components/PageActions.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/PageActions-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/PageActions-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/components/App.tsx
server/sonar-web/src/main/js/apps/issues/components/LocationNavigationKeyboardShortcuts.tsx [deleted file]
server/sonar-web/src/main/js/apps/issues/components/PageActions.tsx
server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx
server/sonar-web/src/main/js/apps/issues/components/__tests__/LocationNavigationKeyboardShortcuts-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/issues/components/__tests__/PageActions-test.tsx
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/App-test.tsx.snap
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/LocationNavigationKeyboardShortcuts-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/PageActions-test.tsx.snap
server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueBox.tsx
server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssuesListHeader.tsx
server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssuesListHeader-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueBox-test.tsx.snap
server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssuesListHeader-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/styles.css
server/sonar-web/src/main/js/components/ui/PageShortcutsTooltip.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/ui/__tests__/PageShortcutsTooltip-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/PageShortcutsTooltip-test.tsx.snap [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 268d3b08b62e4922b3fccbfb0902407e8c7fcf0f..7379714e8a605ac1079a53b6d768c510c4da44f3 100644 (file)
   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;
index 6966a81b536226d0e06221dc6a25b551ee9438a1..4c4d8b9ed4d602bfdfc783b9ced9a5c94b329f0d 100644 (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>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/PageActions-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/PageActions-test.tsx
new file mode 100644 (file)
index 0000000..a4b82a0
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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 PageActions, { PageActionsProps } from '../PageActions';
+
+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');
+});
+
+function shallowRender(props: Partial<PageActionsProps> = {}) {
+  return shallow<PageActionsProps>(<PageActions loading={false} onReload={jest.fn()} {...props} />);
+}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/PageActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/PageActions-test.tsx.snap
new file mode 100644 (file)
index 0000000..d31ccfe
--- /dev/null
@@ -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>
+`;
index ed5155c767fed45f708fa563813b681cf5ace259..0c92c8f0eff43cdf407a3b70a303934ae259ec7a 100644 (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" />
diff --git a/server/sonar-web/src/main/js/apps/issues/components/LocationNavigationKeyboardShortcuts.tsx b/server/sonar-web/src/main/js/apps/issues/components/LocationNavigationKeyboardShortcuts.tsx
deleted file mode 100644 (file)
index ebeeb6b..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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 React from 'react';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-
-export interface Props {
-  issue: Pick<T.Issue, 'flows' | 'secondaryLocations'> | undefined;
-}
-
-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>
-  );
-}
index e401c145624dbebebd02247a0b489cc29689c865..09f9fc8a22458c187bfeba4edac1ea73046f4e76 100644 (file)
  * 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>
+  );
 }
index d87c2db51d46ba2a4cb3064e88789545753f635f..f4169cbabd172a6564d63e25a7a693a0eaf5c72a 100644 (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 () => {
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/LocationNavigationKeyboardShortcuts-test.tsx b/server/sonar-web/src/main/js/apps/issues/components/__tests__/LocationNavigationKeyboardShortcuts-test.tsx
deleted file mode 100644 (file)
index ea12624..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 { mockFlowLocation } from '../../../../helpers/testMocks';
-import LocationNavigationKeyboardShortcuts, { Props } from '../LocationNavigationKeyboardShortcuts';
-
-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();
-});
-
-const shallowRender = (props: Partial<Props> = {}) => {
-  return shallow(<LocationNavigationKeyboardShortcuts issue={undefined} {...props} />);
-};
index 5b6088de2139deb40ba5f86768b3461d5ab6cb87..40da1057147fa6c390f03bb5c16003e50eac930a 100644 (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();
 });
index a465c14212fdbff7b0ecee466a0db2f237cba39d..aa9a378fa015f11186183c598361312437a65ae5 100644 (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,
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/LocationNavigationKeyboardShortcuts-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/LocationNavigationKeyboardShortcuts-test.tsx.snap
deleted file mode 100644 (file)
index e992592..0000000
+++ /dev/null
@@ -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>
-`;
index 09f7ccf406edcaab01a71a0760ad7021cd74e373..7424582257abe6090ceb1a681164d243c3526671 100644 (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}
     />
index 6d36de259d829467ed5d3928b486c17a0e9b51b1..587c64ca672d3acd7419d41193dab16ab1dcad0d 100644 (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>
     );
index 9604ebf52db1423feaf7a18f5c25aa52b4ea8a37..bd036c78aafdeda38616a9f889621a902132477f 100644 (file)
  */
 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>
   );
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssuesListHeader-test.tsx b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssuesListHeader-test.tsx
new file mode 100644 (file)
index 0000000..68d0566
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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 ConciseIssuesListHeader, { ConciseIssuesListHeaderProps } from '../ConciseIssuesListHeader';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot('default');
+  expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
+  expect(shallowRender({ displayBackButton: true })).toMatchSnapshot('with back button');
+});
+
+function shallowRender(props: Partial<ConciseIssuesListHeaderProps> = {}) {
+  return shallow<ConciseIssuesListHeaderProps>(
+    <ConciseIssuesListHeader
+      displayBackButton={false}
+      loading={false}
+      onBackClick={jest.fn()}
+      {...props}
+    />
+  );
+}
index 4eb63a1a28c9aa89682e6a4ed1d9cd004a32f068..6947b89f0475dd4fd240515ce74675b7e3eafd63 100644 (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>
 `;
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssuesListHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssuesListHeader-test.tsx.snap
new file mode 100644 (file)
index 0000000..3887851
--- /dev/null
@@ -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>
+`;
index a513369140647ecc1806002db4adfff2fef163ea..2c2f1c8178c9d3e97681cd38a86cb94ab4454fe2 100644 (file)
 .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);
-}
diff --git a/server/sonar-web/src/main/js/components/ui/PageShortcutsTooltip.tsx b/server/sonar-web/src/main/js/components/ui/PageShortcutsTooltip.tsx
new file mode 100644 (file)
index 0000000..5af086c
--- /dev/null
@@ -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>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/PageShortcutsTooltip-test.tsx b/server/sonar-web/src/main/js/components/ui/__tests__/PageShortcutsTooltip-test.tsx
new file mode 100644 (file)
index 0000000..e49323a
--- /dev/null
@@ -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} />);
+}
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/PageShortcutsTooltip-test.tsx.snap b/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/PageShortcutsTooltip-test.tsx.snap
new file mode 100644 (file)
index 0000000..cb4aa6e
--- /dev/null
@@ -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>
+`;
index 70aaea7779285daa2efd48d42f2925964df5e636..8e4490ebcbb76495e209ff51aec030a36c9b4224 100644 (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