Browse Source

SONAR-12880 Improve tag select accessibility

tags/8.2.0.32929
Wouter Admiraal 4 years ago
parent
commit
c8d4cd2579

+ 1
- 1
server/sonar-web/src/main/js/components/common/MultiSelect.tsx View File

@@ -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 {

+ 33
- 37
server/sonar-web/src/main/js/components/common/MultiSelectOption.tsx View File

@@ -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>
);
}

+ 17
- 24
server/sonar-web/src/main/js/components/common/__tests__/MultiSelectOption-test.tsx View File

@@ -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}
/>
);
}

+ 90
- 68
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelectOption-test.tsx.snap View File

@@ -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>
`;

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

@@ -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

Loading…
Cancel
Save