Skip to content

Commit

Permalink
Readonly MultiSelect (#12435)
Browse files Browse the repository at this point in the history
* chore: wip

* feat: readonly multi select

* chore: remove unnecessary scss vars

* chore: adjust aria-disabled

* fix: cursor following review

* docs(multiselect): scope readOnly control to playground

* Update packages/styles/scss/components/multiselect/_multiselect.scss

Co-authored-by: Taylor Jones <taylor.jones826@gmail.com>
Co-authored-by: Taylor Jones <tay1orjones@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored Nov 8, 2022
1 parent 2ba3910 commit d7ffd1f
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 6 deletions.
3 changes: 3 additions & 0 deletions packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4929,6 +4929,9 @@ Map {
"open": Object {
"type": "bool",
},
"readOnly": Object {
"type": "bool",
},
"selectedItems": Object {
"type": "array",
},
Expand Down
13 changes: 10 additions & 3 deletions packages/react/src/components/ListBox/ListBoxSelection.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ function ListBoxSelection({
translateWithId: t,
disabled,
onClearSelection,
readOnly,
}) {
const prefix = usePrefix();
const className = cx(`${prefix}--list-box__selection`, {
Expand All @@ -31,7 +32,7 @@ function ListBoxSelection({
});
const handleOnClick = (event) => {
event.stopPropagation();
if (disabled) {
if (disabled || readOnly) {
return;
}
clearSelection(event);
Expand All @@ -41,7 +42,7 @@ function ListBoxSelection({
};
const handleOnKeyDown = (event) => {
event.stopPropagation();
if (disabled) {
if (disabled || readOnly) {
return;
}

Expand Down Expand Up @@ -75,7 +76,8 @@ function ListBoxSelection({
onKeyDown={handleOnKeyDown}
disabled={disabled}
aria-label={t('clear.all')}
title={description}>
title={description}
aria-disabled={readOnly ? true : undefined}>
<Close />
</div>
</div>
Expand Down Expand Up @@ -134,6 +136,11 @@ ListBoxSelection.propTypes = {
*/
onKeyDown: PropTypes.func,

/**
* Whether or not the Dropdown is readonly
*/
readOnly: PropTypes.bool,

/**
* Specify an optional `selectionCount` value that will be used to determine
* whether the selection should display a badge or a single clear icon.
Expand Down
34 changes: 31 additions & 3 deletions packages/react/src/components/MultiSelect/MultiSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const MultiSelect = React.forwardRef(function MultiSelect(
onMenuChange,
direction,
selectedItems: selected,
readOnly,
},
ref
) {
Expand Down Expand Up @@ -169,6 +170,7 @@ const MultiSelect = React.forwardRef(function MultiSelect(
[`${prefix}--multi-select--selected`]:
selectedItems && selectedItems.length > 0,
[`${prefix}--list-box--up`]: direction === 'top',
[`${prefix}--multi-select--readonly`]: readOnly,
}
);

Expand Down Expand Up @@ -234,6 +236,24 @@ const MultiSelect = React.forwardRef(function MultiSelect(
: setIsFocused(evt.type === 'focus' ? true : false);
};

const readOnlyEventHandlers = readOnly
? {
onClick: (evt) => {
// NOTE: does not prevent click
evt.preventDefault();
// focus on the element as per readonly input behavior
evt.target.focus();
},
onKeyDown: (evt) => {
const selectAccessKeys = ['ArrowDown', 'ArrowUp', ' ', 'Enter'];
// This prevents the select from opening for the above keys
if (selectAccessKeys.includes(evt.key)) {
evt.preventDefault();
}
},
}
: {};

return (
<div className={wrapperClasses}>
<label className={titleClasses} {...getLabelProps()}>
Expand All @@ -252,6 +272,7 @@ const MultiSelect = React.forwardRef(function MultiSelect(
size={size}
className={className}
disabled={disabled}
readOnly={readOnly}
light={light}
invalid={invalid}
invalidText={invalidText}
Expand All @@ -271,13 +292,15 @@ const MultiSelect = React.forwardRef(function MultiSelect(
type="button"
className={`${prefix}--list-box__field`}
disabled={disabled}
aria-disabled={disabled}
aria-disabled={disabled || readOnly}
{...toggleButtonProps}
ref={mergeRefs(toggleButtonProps.ref, ref)}
onKeyDown={onKeyDown}>
onKeyDown={onKeyDown}
{...readOnlyEventHandlers}>
{selectedItems.length > 0 && (
<ListBox.Selection
clearSelection={!disabled ? clearSelection : noop}
readOnly={readOnly}
clearSelection={!disabled && !readOnly ? clearSelection : noop}
selectionCount={selectedItems.length}
translateWithId={translateWithId}
disabled={disabled}
Expand Down Expand Up @@ -449,6 +472,11 @@ MultiSelect.propTypes = {
*/
open: PropTypes.bool,

/**
* Whether or not the Dropdown is readonly
*/
readOnly: PropTypes.bool,

/**
* For full control of the selected items
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ Playground.argTypes = {
},
defaultValue: 'To clear selection, press Delete or Backspace,',
},
readOnly: {
control: { type: 'boolean' },
},
};

export const Default = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,20 @@ describe('MultiSelect', () => {
).toBeFalsy();
});

it('should not be interactive if readonly', () => {
const items = generateItems(4, generateGenericItem);
const label = 'test-label';
const { container } = render(
<MultiSelect id="test" readOnly={true} label={label} items={items} />
);
const labelNode = getByText(container, label);
Simulate.click(labelNode);

expect(
container.querySelector('[aria-expanded="true"][aria-haspopup="listbox"]')
).toBeFalsy();
});

describe('Component API', () => {
it('should set the default selected items with the `initialSelectedItems` prop', () => {
const items = generateItems(4, generateGenericItem);
Expand Down
38 changes: 38 additions & 0 deletions packages/styles/scss/components/multiselect/_multiselect.scss
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,42 @@
.#{$prefix}--list-box__menu {
visibility: hidden;
}

.#{$prefix}--multi-select.#{$prefix}--multi-select--readonly,
.#{$prefix}--multi-select.#{$prefix}--multi-select--readonly:hover {
background-color: transparent;
}

.#{$prefix}--multi-select.#{$prefix}--multi-select--readonly
.#{$prefix}--list-box__menu-icon
svg {
fill: $icon-disabled;
}

.#{$prefix}--multi-select.#{$prefix}--multi-select--readonly
.#{$prefix}--tag--filter,
.#{$prefix}--multi-select.#{$prefix}--multi-select--readonly
.#{$prefix}--tag__close-icon:hover {
background-color: transparent;
color: $text-primary;
cursor: default;
}

.#{$prefix}--multi-select.#{$prefix}--multi-select--readonly
.#{$prefix}--tag--filter {
box-shadow: 0 0 0 1px $background-inverse;
}

.#{$prefix}--multi-select.#{$prefix}--multi-select--readonly
.#{$prefix}--tag--filter
svg {
fill: $icon-disabled;
}

.#{$prefix}--multi-select.#{$prefix}--multi-select--readonly
.#{$prefix}--list-box__field,
.#{$prefix}--multi-select.#{$prefix}--multi-select--readonly
.#{$prefix}--list-box__menu-icon {
cursor: default;
}
}

0 comments on commit d7ffd1f

Please sign in to comment.