diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2019-12-23 13:59:18 +0100 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2020-01-02 20:46:12 +0100 |
commit | c8d4cd257944527198147e53aaa017b6aaa55822 (patch) | |
tree | 182d2f6c13a78a381c050dc03e83948221987a12 | |
parent | dbd57ddfa8ca3eab4814b66c49ca3552727cbc61 (diff) | |
download | sonarqube-c8d4cd257944527198147e53aaa017b6aaa55822.tar.gz sonarqube-c8d4cd257944527198147e53aaa017b6aaa55822.zip |
SONAR-12880 Improve tag select accessibility
5 files changed, 142 insertions, 130 deletions
diff --git a/server/sonar-web/src/main/js/components/common/MultiSelect.tsx b/server/sonar-web/src/main/js/components/common/MultiSelect.tsx index 9a167fd29fe..dd348bb4691 100644 --- a/server/sonar-web/src/main/js/components/common/MultiSelect.tsx +++ b/server/sonar-web/src/main/js/components/common/MultiSelect.tsx @@ -119,7 +119,7 @@ export default class MultiSelect extends React.PureComponent<Props, State> { } } - handleSelectChange = (item: string, selected: boolean) => { + handleSelectChange = (selected: boolean, item: string) => { if (selected) { this.onSelectItem(item); } else { diff --git a/server/sonar-web/src/main/js/components/common/MultiSelectOption.tsx b/server/sonar-web/src/main/js/components/common/MultiSelectOption.tsx index 309cdd503f4..9ae37b8f55c 100644 --- a/server/sonar-web/src/main/js/components/common/MultiSelectOption.tsx +++ b/server/sonar-web/src/main/js/components/common/MultiSelectOption.tsx @@ -19,51 +19,47 @@ */ import * as classNames from 'classnames'; import * as React from 'react'; +import Checkbox from 'sonar-ui-common/components/controls/Checkbox'; +import { translate } from 'sonar-ui-common/helpers/l10n'; -interface Props { +export interface MultiSelectOptionProps { active?: boolean; custom?: boolean; disabled?: boolean; element: string; - onHover: (elem: string) => void; - onSelectChange: (elem: string, selected: boolean) => void; + onHover: (element: string) => void; + onSelectChange: (selected: boolean, element: string) => void; renderLabel: (element: string) => React.ReactNode; selected?: boolean; } -export default class MultiSelectOption extends React.PureComponent<Props> { - handleSelect = (evt: React.SyntheticEvent<HTMLAnchorElement>) => { - evt.stopPropagation(); - evt.preventDefault(); - evt.currentTarget.blur(); +export default function MultiSelectOption(props: MultiSelectOptionProps) { + const { active, custom, disabled, element, selected } = props; + const onHover = () => props.onHover(element); + const className = classNames({ active, disabled }); + const label = props.renderLabel(element); - if (!this.props.disabled) { - this.props.onSelectChange(this.props.element, !this.props.selected); - } - }; - - handleHover = () => this.props.onHover(this.props.element); - - render() { - const { selected, disabled } = this.props; - const className = classNames('icon-checkbox', { - 'icon-checkbox-checked': selected, - 'icon-checkbox-invisible': disabled - }); - const activeClass = classNames({ active: this.props.active, disabled }); - - return ( - <li> - <a - className={activeClass} - href="#" - onClick={this.handleSelect} - onFocus={this.handleHover} - onMouseOver={this.handleHover}> - <i className={className} /> {this.props.custom && '+ '} - {this.props.renderLabel(this.props.element)} - </a> - </li> - ); - } + return ( + <li onFocus={onHover} onMouseOver={onHover}> + <Checkbox + checked={Boolean(selected)} + className={className} + disabled={disabled} + id={element} + onCheck={props.onSelectChange}> + {custom ? ( + <span + aria-label={`${translate('create_new_element')}: ${label}`} + className="little-spacer-left"> + <span aria-hidden={true} className="little-spacer-right"> + + + </span> + {label} + </span> + ) : ( + <span className="little-spacer-left">{label}</span> + )} + </Checkbox> + </li> + ); } diff --git a/server/sonar-web/src/main/js/components/common/__tests__/MultiSelectOption-test.tsx b/server/sonar-web/src/main/js/components/common/__tests__/MultiSelectOption-test.tsx index 0213f79b8f6..f3e706a5020 100644 --- a/server/sonar-web/src/main/js/components/common/__tests__/MultiSelectOption-test.tsx +++ b/server/sonar-web/src/main/js/components/common/__tests__/MultiSelectOption-test.tsx @@ -19,31 +19,24 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import MultiSelectOption from '../MultiSelectOption'; - -const props = { - element: 'mytag', - onSelectChange: () => {}, - onHover: () => {}, - renderLabel: (element: string) => element -}; +import MultiSelectOption, { MultiSelectOptionProps } from '../MultiSelectOption'; it('should render standard element', () => { - expect(shallow(<MultiSelectOption {...props} />)).toMatchSnapshot(); -}); - -it('should render selected element', () => { - expect(shallow(<MultiSelectOption {...props} selected={true} />)).toMatchSnapshot(); -}); - -it('should render custom element', () => { - expect(shallow(<MultiSelectOption {...props} custom={true} />)).toMatchSnapshot(); -}); - -it('should render active element', () => { - expect(shallow(<MultiSelectOption {...props} active={true} selected={true} />)).toMatchSnapshot(); + expect(shallowRender()).toMatchSnapshot('default element'); + expect(shallowRender({ selected: true })).toMatchSnapshot('selected element'); + expect(shallowRender({ custom: true })).toMatchSnapshot('custom element'); + expect(shallowRender({ active: true, selected: true })).toMatchSnapshot('active element'); + expect(shallowRender({ disabled: true })).toMatchSnapshot('disabled element'); }); -it('should render disabled element', () => { - expect(shallow(<MultiSelectOption {...props} disabled={true} />)).toMatchSnapshot(); -}); +function shallowRender(props: Partial<MultiSelectOptionProps> = {}) { + return shallow( + <MultiSelectOption + element="mytag" + onHover={jest.fn()} + onSelectChange={jest.fn()} + renderLabel={(element: string) => element} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelectOption-test.tsx.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelectOption-test.tsx.snap index ff5048c163c..ee6ce1bf244 100644 --- a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelectOption-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelectOption-test.tsx.snap @@ -1,92 +1,114 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should render active element 1`] = ` -<li> - <a +exports[`should render standard element: active element 1`] = ` +<li + onFocus={[Function]} + onMouseOver={[Function]} +> + <Checkbox + checked={true} className="active" - href="#" - onClick={[Function]} - onFocus={[Function]} - onMouseOver={[Function]} + id="mytag" + onCheck={[MockFunction]} + thirdState={false} > - <i - className="icon-checkbox icon-checkbox-checked" - /> - - mytag - </a> + <span + className="little-spacer-left" + > + mytag + </span> + </Checkbox> </li> `; -exports[`should render custom element 1`] = ` -<li> - <a +exports[`should render standard element: custom element 1`] = ` +<li + onFocus={[Function]} + onMouseOver={[Function]} +> + <Checkbox + checked={false} className="" - href="#" - onClick={[Function]} - onFocus={[Function]} - onMouseOver={[Function]} + id="mytag" + onCheck={[MockFunction]} + thirdState={false} > - <i - className="icon-checkbox" - /> - - + - mytag - </a> + <span + aria-label="create_new_element: mytag" + className="little-spacer-left" + > + <span + aria-hidden={true} + className="little-spacer-right" + > + + + </span> + mytag + </span> + </Checkbox> </li> `; -exports[`should render disabled element 1`] = ` -<li> - <a - className="disabled" - href="#" - onClick={[Function]} - onFocus={[Function]} - onMouseOver={[Function]} +exports[`should render standard element: default element 1`] = ` +<li + onFocus={[Function]} + onMouseOver={[Function]} +> + <Checkbox + checked={false} + className="" + id="mytag" + onCheck={[MockFunction]} + thirdState={false} > - <i - className="icon-checkbox icon-checkbox-invisible" - /> - - mytag - </a> + <span + className="little-spacer-left" + > + mytag + </span> + </Checkbox> </li> `; -exports[`should render selected element 1`] = ` -<li> - <a - className="" - href="#" - onClick={[Function]} - onFocus={[Function]} - onMouseOver={[Function]} +exports[`should render standard element: disabled element 1`] = ` +<li + onFocus={[Function]} + onMouseOver={[Function]} +> + <Checkbox + checked={false} + className="disabled" + disabled={true} + id="mytag" + onCheck={[MockFunction]} + thirdState={false} > - <i - className="icon-checkbox icon-checkbox-checked" - /> - - mytag - </a> + <span + className="little-spacer-left" + > + mytag + </span> + </Checkbox> </li> `; -exports[`should render standard element 1`] = ` -<li> - <a +exports[`should render standard element: selected element 1`] = ` +<li + onFocus={[Function]} + onMouseOver={[Function]} +> + <Checkbox + checked={true} className="" - href="#" - onClick={[Function]} - onFocus={[Function]} - onMouseOver={[Function]} + id="mytag" + onCheck={[MockFunction]} + thirdState={false} > - <i - className="icon-checkbox" - /> - - mytag - </a> + <span + className="little-spacer-left" + > + mytag + </span> + </Checkbox> </li> `; diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 331c0984a35..f016798e583 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -47,6 +47,7 @@ confirm=Confirm continue=Continue copy=Copy create=Create +create_new_element=Create new element created=Created created_on=Created on critical=Critical |