aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorWouter Admiraal <wouter.admiraal@sonarsource.com>2022-08-10 14:29:08 +0200
committersonartech <sonartech@sonarsource.com>2022-08-11 20:03:49 +0000
commit53e5a51f1cecc1e17a00bfa2981eba583f2d3f4a (patch)
tree059640fc332c1728ac3bca8317ebcd13a7722c01 /server/sonar-web
parentf33601b3524d0b6285bcd7510f937195b0b7db08 (diff)
downloadsonarqube-53e5a51f1cecc1e17a00bfa2981eba583f2d3f4a.tar.gz
sonarqube-53e5a51f1cecc1e17a00bfa2981eba583f2d3f4a.zip
SONAR-16782 [893362] Function cannot be performed by keyboard alone
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewer-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/Line.css4
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplicationBlock.tsx11
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesIndicator.tsx13
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/LineNumber.tsx9
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/LineOptionsPopup.tsx13
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/LineSCM.tsx5
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssuesIndicator-test.tsx3
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineIssuesIndicator-test.tsx.snap24
-rw-r--r--server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx21
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/ActionsDropdown-test.tsx11
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ActionsDropdown-test.tsx.snap41
-rw-r--r--server/sonar-web/src/main/js/components/controls/buttons.css23
-rw-r--r--server/sonar-web/src/main/js/components/controls/buttons.tsx4
14 files changed, 67 insertions, 119 deletions
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewer-it.tsx b/server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewer-it.tsx
index df8ed428928..0c958bb6500 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewer-it.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewer-it.tsx
@@ -54,7 +54,7 @@ it('should show a permalink on line number', async () => {
})
);
await user.click(
- rowScreen.getByRole('link', {
+ rowScreen.getByRole('button', {
name: 'component_viewer.copy_permalink'
})
);
@@ -89,7 +89,7 @@ it('should show a permalink on line number', async () => {
);
expect(
- lowerRowScreen.getByRole('link', {
+ lowerRowScreen.getByRole('button', {
name: 'component_viewer.copy_permalink'
})
).toBeInTheDocument();
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/Line.css b/server/sonar-web/src/main/js/components/SourceViewer/components/Line.css
index 24016edd24d..985473db381 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/Line.css
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/Line.css
@@ -186,7 +186,9 @@
display: block;
}
-.source-line-scm [role='button'] {
+.source-line-scm button {
+ display: block;
+ width: 100%;
height: 18px;
}
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplicationBlock.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplicationBlock.tsx
index 49dc0765919..038acbf733b 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplicationBlock.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplicationBlock.tsx
@@ -25,6 +25,7 @@ import Tooltip from '../../../components/controls/Tooltip';
import { PopupPlacement } from '../../../components/ui/popups';
import { translate } from '../../../helpers/l10n';
import { SourceLine } from '../../../types/types';
+import { ButtonPlain } from '../../controls/buttons';
export interface LineDuplicationBlockProps {
blocksLoaded: boolean;
@@ -43,11 +44,11 @@ export function LineDuplicationBlock(props: LineDuplicationBlockProps) {
'source-line-duplicated': duplicated
});
+ const tooltip = dropdownOpen ? undefined : translate('source_viewer.tooltip.duplicated_block');
+
return duplicated ? (
<td className={className} data-index={index} data-line-number={line.line}>
- <Tooltip
- overlay={dropdownOpen ? undefined : translate('source_viewer.tooltip.duplicated_block')}
- placement="right">
+ <Tooltip overlay={tooltip} placement="right">
<div>
<Toggler
onRequestClose={() => setDropdownOpen(false)}
@@ -57,7 +58,7 @@ export function LineDuplicationBlock(props: LineDuplicationBlockProps) {
{props.renderDuplicationPopup(index, line.line)}
</DropdownOverlay>
}>
- <div
+ <ButtonPlain
aria-label={translate('source_viewer.tooltip.duplicated_block')}
className="source-line-bar"
onClick={() => {
@@ -66,8 +67,6 @@ export function LineDuplicationBlock(props: LineDuplicationBlockProps) {
props.onClick(line);
}
}}
- role="button"
- tabIndex={0}
/>
</Toggler>
</div>
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesIndicator.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesIndicator.tsx
index 666f5c527d8..d1ddbdb2247 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesIndicator.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesIndicator.tsx
@@ -25,6 +25,7 @@ import IssueIcon from '../../../components/icons/IssueIcon';
import { sortByType } from '../../../helpers/issues';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { Issue, SourceLine } from '../../../types/types';
+import { ButtonPlain } from '../../controls/buttons';
export interface LineIssuesIndicatorProps {
issues: Issue[];
@@ -65,20 +66,14 @@ export function LineIssuesIndicator(props: LineIssuesIndicatorProps) {
return (
<td className={className} data-line-number={line.line}>
- <span
+ <ButtonPlain
aria-label={translate('source_viewer.issues_on_line', issuesOpen ? 'hide' : 'show')}
- onClick={(e: React.MouseEvent<HTMLElement>) => {
- e.preventDefault();
- e.currentTarget.blur();
- props.onClick();
- }}
- role="button"
- tabIndex={0}>
+ onClick={props.onClick}>
<Tooltip overlay={tooltipContent}>
<IssueIcon type={mostImportantIssue.type} />
</Tooltip>
{issues.length > 1 && <span className="source-line-issues-counter">{issues.length}</span>}
- </span>
+ </ButtonPlain>
</td>
);
}
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineNumber.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/LineNumber.tsx
index 348a8cae165..55ba93354d3 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineNumber.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineNumber.tsx
@@ -21,6 +21,7 @@ import * as React from 'react';
import Toggler from '../../../components/controls/Toggler';
import { translateWithParameters } from '../../../helpers/l10n';
import { SourceLine } from '../../../types/types';
+import { ButtonPlain } from '../../controls/buttons';
import LineOptionsPopup from './LineOptionsPopup';
export interface LineNumberProps {
@@ -42,15 +43,13 @@ export function LineNumber({ displayOptions, firstLineNumber, line }: LineNumber
onRequestClose={() => setOpen(false)}
open={isOpen}
overlay={<LineOptionsPopup firstLineNumber={firstLineNumber} line={line} />}>
- <span
+ <ButtonPlain
aria-expanded={isOpen}
aria-haspopup={true}
aria-label={translateWithParameters('source_viewer.line_X', lineNumber)}
- onClick={() => setOpen(true)}
- role="button"
- tabIndex={0}>
+ onClick={() => setOpen(true)}>
{lineNumber}
- </span>
+ </ButtonPlain>
</Toggler>
) : (
lineNumber
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineOptionsPopup.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/LineOptionsPopup.tsx
index 975e2a7e41e..ef523ceb15f 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineOptionsPopup.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineOptionsPopup.tsx
@@ -23,7 +23,7 @@ import { PopupPlacement } from '../../../components/ui/popups';
import { translate } from '../../../helpers/l10n';
import { getCodeUrl, getPathUrlAsString } from '../../../helpers/urls';
import { SourceLine } from '../../../types/types';
-import { ActionsDropdownItem } from '../../controls/ActionsDropdown';
+import { ClipboardButton } from '../../controls/clipboard';
import { SourceViewerContext } from '../SourceViewerContext';
export interface LineOptionsPopupProps {
@@ -43,11 +43,14 @@ export function LineOptionsPopup({ firstLineNumber, line }: LineOptionsPopupProp
className="big-spacer-left"
noPadding={true}
placement={isAtTop ? PopupPlacement.BottomLeft : PopupPlacement.TopLeft}>
- <ul className="padded source-viewer-bubble-popup nowrap">
- <ActionsDropdownItem copyValue={codeUrl}>
+ <div className="padded source-viewer-bubble-popup nowrap">
+ <ClipboardButton
+ className="button-link"
+ copyValue={codeUrl}
+ aria-label={translate('component_viewer.copy_permalink')}>
{translate('component_viewer.copy_permalink')}
- </ActionsDropdownItem>
- </ul>
+ </ClipboardButton>
+ </div>
</DropdownOverlay>
);
}}
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineSCM.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/LineSCM.tsx
index 713f02a6c06..c06c6e41c5e 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineSCM.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineSCM.tsx
@@ -22,6 +22,7 @@ import Dropdown from '../../../components/controls/Dropdown';
import { PopupPlacement } from '../../../components/ui/popups';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { SourceLine } from '../../../types/types';
+import { ButtonPlain } from '../../controls/buttons';
import SCMPopup from './SCMPopup';
export interface LineSCMProps {
@@ -49,9 +50,7 @@ export function LineSCM({ line, previousLine }: LineSCMProps) {
return (
<td className="source-meta source-line-scm" data-line-number={line.line}>
<Dropdown overlay={<SCMPopup line={line} />} overlayPlacement={PopupPlacement.RightTop}>
- <div aria-label={ariaLabel} role="button">
- {cell}
- </div>
+ <ButtonPlain aria-label={ariaLabel}>{cell}</ButtonPlain>
</Dropdown>
</td>
);
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssuesIndicator-test.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssuesIndicator-test.tsx
index 72ad28cd99b..21d20fbb05b 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssuesIndicator-test.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssuesIndicator-test.tsx
@@ -21,6 +21,7 @@ import { shallow } from 'enzyme';
import * as React from 'react';
import { mockIssue } from '../../../../helpers/testMocks';
import { click } from '../../../../helpers/testUtils';
+import { ButtonPlain } from '../../../controls/buttons';
import { LineIssuesIndicator, LineIssuesIndicatorProps } from '../LineIssuesIndicator';
it('should render correctly', () => {
@@ -43,7 +44,7 @@ it('should correctly handle click', () => {
const onClick = jest.fn();
const wrapper = shallowRender({ onClick });
- click(wrapper.find('span[role="button"]'));
+ click(wrapper.find(ButtonPlain));
expect(onClick).toHaveBeenCalled();
});
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineIssuesIndicator-test.tsx.snap b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineIssuesIndicator-test.tsx.snap
index 5f22c660993..dc67d02a1f9 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineIssuesIndicator-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineIssuesIndicator-test.tsx.snap
@@ -5,11 +5,9 @@ exports[`should render correctly: default 1`] = `
className="source-meta source-line-issues source-line-with-issues"
data-line-number={3}
>
- <span
+ <ButtonPlain
aria-label="source_viewer.issues_on_line.show"
- onClick={[Function]}
- role="button"
- tabIndex={0}
+ onClick={[MockFunction]}
>
<Tooltip
overlay="source_viewer.issues_on_line.multiple_issues"
@@ -23,7 +21,7 @@ exports[`should render correctly: default 1`] = `
>
2
</span>
- </span>
+ </ButtonPlain>
</td>
`;
@@ -32,11 +30,9 @@ exports[`should render correctly: multiple issues, same type 1`] = `
className="source-meta source-line-issues source-line-with-issues"
data-line-number={3}
>
- <span
+ <ButtonPlain
aria-label="source_viewer.issues_on_line.show"
- onClick={[Function]}
- role="button"
- tabIndex={0}
+ onClick={[MockFunction]}
>
<Tooltip
overlay="source_viewer.issues_on_line.X_issues_of_type_Y.2.issue.type.VULNERABILITY.plural"
@@ -50,7 +46,7 @@ exports[`should render correctly: multiple issues, same type 1`] = `
>
2
</span>
- </span>
+ </ButtonPlain>
</td>
`;
@@ -66,11 +62,9 @@ exports[`should render correctly: single issue 1`] = `
className="source-meta source-line-issues source-line-with-issues"
data-line-number={3}
>
- <span
+ <ButtonPlain
aria-label="source_viewer.issues_on_line.show"
- onClick={[Function]}
- role="button"
- tabIndex={0}
+ onClick={[MockFunction]}
>
<Tooltip
overlay="source_viewer.issues_on_line.issue_of_type_X.issue.type.VULNERABILITY"
@@ -79,6 +73,6 @@ exports[`should render correctly: single issue 1`] = `
type="VULNERABILITY"
/>
</Tooltip>
- </span>
+ </ButtonPlain>
</td>
`;
diff --git a/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx b/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx
index 1a87832a2f2..e0184dcb0f1 100644
--- a/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx
+++ b/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx
@@ -20,14 +20,11 @@
import classNames from 'classnames';
import * as React from 'react';
import { Link, To } from 'react-router-dom';
-import { translate } from '../../helpers/l10n';
import DropdownIcon from '../icons/DropdownIcon';
import SettingsIcon from '../icons/SettingsIcon';
import { PopupPlacement } from '../ui/popups';
import { Button } from './buttons';
-import { ClipboardBase } from './clipboard';
import Dropdown from './Dropdown';
-import Tooltip from './Tooltip';
export interface ActionsDropdownProps {
className?: string;
@@ -60,8 +57,6 @@ export default function ActionsDropdown(props: ActionsDropdownProps) {
interface ItemProps {
className?: string;
children: React.ReactNode;
- /** used to pass a string to copy to clipboard */
- copyValue?: string;
destructive?: boolean;
/** used to pass a name of downloaded file */
download?: string;
@@ -106,22 +101,6 @@ export class ActionsDropdownItem extends React.PureComponent<ItemProps> {
);
}
- if (this.props.copyValue) {
- return (
- <ClipboardBase>
- {({ setCopyButton, copySuccess }) => (
- <Tooltip overlay={translate('copied_action')} visible={copySuccess}>
- <li data-clipboard-text={this.props.copyValue} ref={setCopyButton}>
- <a className={className} href="#" id={this.props.id} onClick={this.handleClick}>
- {this.props.children}
- </a>
- </li>
- </Tooltip>
- )}
- </ClipboardBase>
- );
- }
-
return (
<li>
<a className={className} href="#" id={this.props.id} onClick={this.handleClick}>
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/ActionsDropdown-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/ActionsDropdown-test.tsx
index 8eed909458c..611920edcfd 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/ActionsDropdown-test.tsx
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/ActionsDropdown-test.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { mount, shallow } from 'enzyme';
+import { shallow } from 'enzyme';
import * as React from 'react';
import { click } from '../../../helpers/testUtils';
import { PopupPlacement } from '../../ui/popups';
@@ -62,19 +62,10 @@ describe('ActionsDropdownItem', () => {
expect(onClick).toBeCalled();
});
- it('should render correctly copy item', () => {
- const wrapper = mountRender({ copyValue: 'my content to copy to clipboard' });
- expect(wrapper).toMatchSnapshot();
- });
-
function shallowRender(props: Partial<ActionsDropdownItem['props']> = {}) {
return shallow(renderContent(props));
}
- function mountRender(props: Partial<ActionsDropdownItem['props']> = {}) {
- return mount(renderContent(props));
- }
-
function renderContent(props: Partial<ActionsDropdownItem['props']> = {}) {
return (
<ActionsDropdownItem className="foo" {...props}>
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ActionsDropdown-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ActionsDropdown-test.tsx.snap
index 37677005279..2ebe48a75c0 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ActionsDropdown-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ActionsDropdown-test.tsx.snap
@@ -103,44 +103,3 @@ exports[`ActionsDropdownItem should render correctly 3`] = `
</a>
</li>
`;
-
-exports[`ActionsDropdownItem should render correctly copy item 1`] = `
-<ActionsDropdownItem
- className="foo"
- copyValue="my content to copy to clipboard"
->
- <ClipboardBase>
- <Tooltip
- overlay="copied_action"
- visible={false}
- >
- <TooltipInner
- mouseEnterDelay={0.1}
- overlay="copied_action"
- visible={false}
- >
- <li
- aria-describedby="tooltip-1"
- aria-labelledby="tooltip-1"
- data-clipboard-text="my content to copy to clipboard"
- onBlur={[Function]}
- onFocus={[Function]}
- onPointerEnter={[Function]}
- onPointerLeave={[Function]}
- tabIndex={0}
- >
- <a
- className="foo"
- href="#"
- onClick={[Function]}
- >
- <span>
- Hello world
- </span>
- </a>
- </li>
- </TooltipInner>
- </Tooltip>
- </ClipboardBase>
-</ActionsDropdownItem>
-`;
diff --git a/server/sonar-web/src/main/js/components/controls/buttons.css b/server/sonar-web/src/main/js/components/controls/buttons.css
index 2a78bdffbac..4e4f215dab0 100644
--- a/server/sonar-web/src/main/js/components/controls/buttons.css
+++ b/server/sonar-web/src/main/js/components/controls/buttons.css
@@ -118,6 +118,29 @@
/* #endregion */
+/* #region .button-plain */
+.button-plain {
+ display: inline-flex;
+ height: auto;
+ line-height: inherit;
+ margin: 0;
+ padding: 0;
+ border: none;
+ border-radius: 0;
+ background: transparent;
+ color: inherit;
+ border-bottom: 0;
+ font-weight: inherit;
+ font-size: inherit;
+}
+
+.button-plain:hover {
+ color: var(--blue);
+ background-color: transparent;
+}
+
+/* #endregion */
+
/* #region .button-link */
.button-link {
display: inline-flex;
diff --git a/server/sonar-web/src/main/js/components/controls/buttons.tsx b/server/sonar-web/src/main/js/components/controls/buttons.tsx
index e4cf225e3f1..67548395ccb 100644
--- a/server/sonar-web/src/main/js/components/controls/buttons.tsx
+++ b/server/sonar-web/src/main/js/components/controls/buttons.tsx
@@ -92,6 +92,10 @@ export function ButtonLink({ className, ...props }: ButtonProps) {
return <Button {...props} className={classNames('button-link', className)} />;
}
+export function ButtonPlain({ className, ...props }: ButtonProps) {
+ return <Button {...props} className={classNames('button-plain', className)} />;
+}
+
export function SubmitButton(props: Omit<ButtonProps, 'type'>) {
// do not prevent default to actually submit a form
return <Button {...props} preventDefault={false} type="submit" />;