Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(combobox): update to match ARIA 1.2 criteria #7777

Merged
merged 23 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b18204e
fix(combobox): update to match WCAG 2.1 AA criteria
joshblack Feb 10, 2021
3afb036
Merge branch 'master' into 6938-take-2
joshblack Feb 10, 2021
de066c5
Merge branch 'master' into 6938-take-2
joshblack Feb 11, 2021
69983a1
 fix(combobox): updated styling on ComboBox
andreancardona Feb 24, 2021
afdb12e
 fix(combobox): updated styling on ComboBox -pt.2
andreancardona Feb 24, 2021
37b1b62
fix(comobobox): update aria-selected prop attribute
andreancardona Feb 25, 2021
4529907
fix(comobobox): update aria-selected prop attribute
andreancardona Feb 25, 2021
8e3bb44
fix(combobox): remove aria-owns prop - fix lint
andreancardona Feb 26, 2021
bb93f4e
fix(combobox): remove aria-owns prop - remove aria-labelledby fix lint
andreancardona Feb 26, 2021
ab77c70
fix(combobox): add aria commentary
andreancardona Feb 26, 2021
b459f9a
Merge branch 'master' into 6938-take-2
andreancardona Mar 1, 2021
3f65224
fix(combobox): aria-labelledby refactor
andreancardona Mar 1, 2021
a127010
fix(combobox): pass ariaLabel to getMenuProps
andreancardona Mar 2, 2021
f770b3d
Merge branch 'master' into 6938-take-2
andreancardona Mar 2, 2021
b7e9416
fix(combobox): update ariaLabel
andreancardona Mar 2, 2021
5ea67cd
Merge branch '6938-take-2' of github.com:joshblack/carbon into 6938-t…
andreancardona Mar 2, 2021
f21ef6f
fix(combobox): updated selected ui color token
andreancardona Mar 2, 2021
9acac96
Update packages/components/src/components/list-box/_list-box.scss
andreancardona Mar 3, 2021
06b2034
fix(combobox): update aria-selected & aria-current
andreancardona Mar 3, 2021
48a61a5
Merge branch '6938-take-2' of github.com:joshblack/carbon into 6938-t…
andreancardona Mar 3, 2021
94def0d
fix(combobox): update ariaLabel
andreancardona Mar 3, 2021
72acb1c
Merge branch 'master' into 6938-take-2
joshblack Mar 3, 2021
9581a5c
Merge branch 'master' into 6938-take-2
kodiakhq[bot] Mar 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/components/src/components/list-box/_list-box.scss
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ $list-box-menu-width: rem(300px);

// Menu status inside of a `list-box__field`
.#{$prefix}--list-box__menu-icon {
@include button-reset($width: false);

position: absolute;
top: 0;
right: $carbon--spacing-05;
Expand Down Expand Up @@ -421,6 +423,8 @@ $list-box-menu-width: rem(300px);

// Selection indicator for a `list-box__field`
.#{$prefix}--list-box__selection {
@include button-reset($width: false);

position: absolute;
top: 50%;
/* to preserve .5rem space between icons according to spec top/transform used to center the combobox clear selection icon in IE11 */
Expand Down
4 changes: 3 additions & 1 deletion packages/react/src/components/ComboBox/ComboBox-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
findListBoxNode,
findMenuNode,
findMenuItemNode,
openMenu,
assertMenuOpen,
assertMenuClosed,
generateItems,
Expand All @@ -28,6 +27,9 @@ const downshiftActions = {
};
const clearInput = (wrapper) =>
wrapper.instance().handleOnStateChange({ inputValue: '' }, downshiftActions);
const openMenu = (wrapper) => {
wrapper.find(`[role="combobox"]`).simulate('click');
};

describe('ComboBox', () => {
let mockProps;
Expand Down
250 changes: 142 additions & 108 deletions packages/react/src/components/ComboBox/ComboBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ import {
WarningFilled16,
} from '@carbon/icons-react';
import ListBox, { PropTypes as ListBoxPropTypes } from '../ListBox';
import { ListBoxTrigger, ListBoxSelection } from '../ListBox/next';
import { match, keys } from '../../internal/keyboard';
import setupGetInstanceId from '../../tools/setupGetInstanceId';
import { mapDownshiftProps } from '../../tools/createPropAdapter';
import mergeRefs from '../../tools/mergeRefs';

const { prefix } = settings;

Expand Down Expand Up @@ -242,13 +244,11 @@ export default class ComboBox extends React.Component {
constructor(props) {
super(props);

this.textInput = React.createRef();

this.comboBoxInstanceId = getInstanceId();

this.state = {
inputValue: getInputValue(props, {}),
};
this.textInput = React.createRef();
}

filterItems = (items, itemToString, inputValue) =>
Expand Down Expand Up @@ -317,6 +317,10 @@ export default class ComboBox extends React.Component {
event.preventDownshiftDefault = true;
event.persist();
}

if (this.textInput.current) {
this.textInput.current.focus();
}
};

render() {
Expand Down Expand Up @@ -382,126 +386,156 @@ export default class ComboBox extends React.Component {
inputId={id}
selectedItem={selectedItem}>
{({
getToggleButtonProps,
getInputProps,
getItemProps,
getLabelProps,
getMenuProps,
getRootProps,
getToggleButtonProps,
isOpen,
inputValue,
selectedItem,
highlightedIndex,
clearSelection,
toggleMenu,
getMenuProps,
}) => (
<div className={wrapperClasses}>
{titleText && (
<label className={titleClasses} {...getLabelProps()}>
{titleText}
</label>
)}
<ListBox
className={className}
disabled={disabled}
invalid={invalid}
aria-label={ariaLabel}
invalidText={invalidText}
isOpen={isOpen}
light={light}
size={size}
warn={warn}
warnText={warnText}>
<ListBox.Field
{...getToggleButtonProps({
disabled,
onClick: this.onToggleClick(isOpen),
})}>
<input
disabled={disabled}
className={inputClasses}
type="text"
tabIndex="0"
aria-autocomplete="list"
ref={this.textInput}
{...rest}
{...getInputProps({
disabled,
placeholder,
onKeyDown: (event) => {
if (match(event, keys.Space)) {
event.stopPropagation();
}

if (match(event, keys.Enter)) {
toggleMenu();
}
},
})}
/>
{invalid && (
<WarningFilled16
className={`${prefix}--list-box__invalid-icon`}
/>
)}
{showWarning && (
<WarningAltFilled16
className={`${prefix}--list-box__invalid-icon ${prefix}--list-box__invalid-icon--warning`}
}) => {
const rootProps = getRootProps(
{},
{
suppressRefError: true,
}
);
const labelProps = getLabelProps();
const buttonProps = getToggleButtonProps({
disabled,
onClick: this.onToggleClick(isOpen),
// When we moved the "root node" of Downshift to the <input> for
// WCAG 2.1 compliance, we unfortunately hit this branch for the
andreancardona marked this conversation as resolved.
Show resolved Hide resolved
// "mouseup" event that downshift listens to:
// https://github.com/downshift-js/downshift/blob/v5.2.1/src/downshift.js#L1051-L1065
//
// As a result, it will reset the state of the component and so we
// stop the event from propagating to prevent this. This allows the
// toggleMenu behavior for the toggleButton to correctly open and
// close the menu.
onMouseUp(event) {
event.stopPropagation();
},
});
const inputProps = getInputProps({
disabled,
placeholder,
onClick() {
toggleMenu();
},
onKeyDown: (event) => {
if (match(event, keys.Space)) {
event.stopPropagation();
}

if (match(event, keys.Enter)) {
toggleMenu();
}
},
});

return (
<div className={wrapperClasses}>
{titleText && (
<label className={titleClasses} {...labelProps}>
{titleText}
</label>
)}
<ListBox
className={className}
disabled={disabled}
invalid={invalid}
invalidText={invalidText}
isOpen={isOpen}
light={light}
size={size}
warn={warn}
warnText={warnText}>
<div className={`${prefix}--list-box__field`}>
<input
disabled={disabled}
className={inputClasses}
type="text"
tabIndex="0"
aria-autocomplete="list"
{...rest}
{...rootProps}
{...inputProps}
ref={mergeRefs(this.textInput, rootProps.ref)}
/>
)}
{inputValue && (
<ListBox.Selection
clearSelection={clearSelection}
{invalid && (
<WarningFilled16
className={`${prefix}--list-box__invalid-icon`}
/>
)}
{showWarning && (
<WarningAltFilled16
className={`${prefix}--list-box__invalid-icon ${prefix}--list-box__invalid-icon--warning`}
/>
)}
{inputValue && (
<ListBoxSelection
clearSelection={clearSelection}
translateWithId={translateWithId}
disabled={disabled}
onClearSelection={this.handleSelectionClear}
/>
)}
<ListBoxTrigger
{...buttonProps}
isOpen={isOpen}
translateWithId={translateWithId}
disabled={disabled}
onClearSelection={this.handleSelectionClear}
/>
)}
<ListBox.MenuIcon
isOpen={isOpen}
translateWithId={translateWithId}
/>
</ListBox.Field>
{isOpen && (
</div>
<ListBox.Menu {...getMenuProps({ 'aria-label': ariaLabel })}>
{this.filterItems(items, itemToString, inputValue).map(
(item, index) => {
const itemProps = getItemProps({ item, index });
return (
<ListBox.MenuItem
key={itemProps.id}
isActive={selectedItem === item}
tabIndex="-1"
isHighlighted={
highlightedIndex === index ||
(selectedItem && selectedItem.id === item.id) ||
false
}
title={itemToElement ? item.text : itemToString(item)}
{...itemProps}>
{itemToElement ? (
<ItemToElement key={itemProps.id} {...item} />
) : (
itemToString(item)
)}
{selectedItem === item && (
<Checkmark16
className={`${prefix}--list-box__menu-item__selected-icon`}
/>
)}
</ListBox.MenuItem>
);
}
)}
{isOpen
? this.filterItems(items, itemToString, inputValue).map(
(item, index) => {
const itemProps = getItemProps({ item, index });
return (
<ListBox.MenuItem
key={itemProps.id}
isActive={selectedItem === item}
tabIndex="-1"
isHighlighted={
highlightedIndex === index ||
(selectedItem && selectedItem.id === item.id) ||
false
}
title={
itemToElement ? item.text : itemToString(item)
}
{...itemProps}>
{itemToElement ? (
<ItemToElement key={itemProps.id} {...item} />
) : (
itemToString(item)
)}
{selectedItem === item && (
<Checkmark16
className={`${prefix}--list-box__menu-item__selected-icon`}
/>
)}
</ListBox.MenuItem>
);
}
)
: null}
</ListBox.Menu>
</ListBox>
{helperText && !invalid && !warn && (
<div id={comboBoxHelperId} className={helperClasses}>
{helperText}
</div>
)}
</ListBox>
{helperText && !invalid && !warn && (
<div id={comboBoxHelperId} className={helperClasses}>
{helperText}
</div>
)}
</div>
)}
</div>
);
}}
</Downshift>
);
}
Expand Down
Loading