Skip to content

Commit

Permalink
fix: filterable multiselect readonly state implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
Gururajj77 committed Oct 7, 2024
1 parent 29a4646 commit 2bd89ed
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ export interface FilterableMultiSelectProps<ItemType>
*/
placeholder?: string;

/**
* Whether or not the filterable multiselect is readonly
*/
readOnly?: boolean;

/**
* Specify feedback (mode) of the selection.
* `top`: selected item jumps to top
Expand Down Expand Up @@ -334,6 +339,7 @@ const FilterableMultiSelect = React.forwardRef(function FilterableMultiSelect<
onChange,
onMenuChange,
placeholder,
readOnly,
titleText,
type,
selectionFeedback = 'top-after-reopen',
Expand Down Expand Up @@ -504,9 +510,11 @@ const FilterableMultiSelect = React.forwardRef(function FilterableMultiSelect<
};

function handleMenuChange(forceIsOpen: boolean): void {
const nextIsOpen = forceIsOpen ?? !isOpen;
setIsOpen(nextIsOpen);
validateHighlightFocus();
if (!readOnly) {
const nextIsOpen = forceIsOpen ?? !isOpen;
setIsOpen(nextIsOpen);
validateHighlightFocus();
}
}

useEffect(() => {
Expand Down Expand Up @@ -688,6 +696,7 @@ const FilterableMultiSelect = React.forwardRef(function FilterableMultiSelect<
[`${prefix}--multi-select--selected`]:
controlledSelectedItems?.length > 0,
[`${prefix}--multi-select--filterable--input-focused`]: inputFocused,
[`${prefix}--multi-select--readonly`]: readOnly,
}
);

Expand All @@ -699,6 +708,14 @@ const FilterableMultiSelect = React.forwardRef(function FilterableMultiSelect<
handleMenuChange(!isOpen);
textInput.current?.focus();
},
// onClick: (event) => {
// if (!readOnly) {
// handleMenuChange(!isOpen);
// textInput.current?.focus();
// } else {
// event.preventDefault();
// }
// },
// When we moved the "root node" of Downshift to the <input> for
// ARIA 1.2 compliance, we unfortunately hit this branch for the
// "mouseup" event that downshift listens to:
Expand Down Expand Up @@ -727,7 +744,7 @@ const FilterableMultiSelect = React.forwardRef(function FilterableMultiSelect<
placeholder,
preventKeyAction: isOpen,

onClick: () => handleMenuChange(true),
onClick: () => !readOnly && handleMenuChange(true),
onKeyDown(event: KeyboardEvent<HTMLElement>) {
const $input = event.target as HTMLInputElement;
const $value = $input.value;
Expand All @@ -736,6 +753,11 @@ const FilterableMultiSelect = React.forwardRef(function FilterableMultiSelect<
event.stopPropagation();
}

if (readOnly) {
event.preventDefault();
return;
}

Check warning on line 759 in packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx

View check run for this annotation

Codecov / codecov/patch

packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx#L758-L759

Added lines #L758 - L759 were not covered by tests

if (match(event, keys.Enter)) {
handleMenuChange(true);
}
Expand Down Expand Up @@ -797,6 +819,28 @@ const FilterableMultiSelect = React.forwardRef(function FilterableMultiSelect<
}
};

const mergedRef = mergeRefs(textInput, inputProps.ref);

const readOnlyEventHandlers = readOnly
? {
onClick: (evt: React.MouseEvent<HTMLInputElement>) => {
// NOTE: does not prevent click

Check warning on line 827 in packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx

View check run for this annotation

Codecov / codecov/patch

packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx#L827

Added line #L827 was not covered by tests
evt.preventDefault();
// focus on the element as per readonly input behavior

Check warning on line 829 in packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx

View check run for this annotation

Codecov / codecov/patch

packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx#L829

Added line #L829 was not covered by tests
if (mergedRef.current !== undefined) {
mergedRef.current.focus();
}

Check warning on line 832 in packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx

View check run for this annotation

Codecov / codecov/patch

packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx#L832

Added line #L832 was not covered by tests
},
onKeyDown: (evt: React.KeyboardEvent<HTMLInputElement>) => {
const selectAccessKeys = ['ArrowDown', 'ArrowUp', ' ', 'Enter'];
// This prevents the select from opening for the above keys

Check warning on line 836 in packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx

View check run for this annotation

Codecov / codecov/patch

packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx#L835-L836

Added lines #L835 - L836 were not covered by tests
if (selectAccessKeys.includes(evt.key)) {
evt.preventDefault();
}

Check warning on line 839 in packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx

View check run for this annotation

Codecov / codecov/patch

packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx#L839

Added line #L839 was not covered by tests
},
}
: {};

const clearSelectionContent =
controlledSelectedItems.length > 0 ? (
<span className={`${prefix}--visually-hidden`}>
Expand Down Expand Up @@ -831,7 +875,7 @@ const FilterableMultiSelect = React.forwardRef(function FilterableMultiSelect<
invalidText={invalidText}
warn={warn}
warnText={warnText}
isOpen={isOpen}
isOpen={!readOnly && isOpen}
size={size}>
<div
className={`${prefix}--list-box__field`}
Expand All @@ -853,6 +897,8 @@ const FilterableMultiSelect = React.forwardRef(function FilterableMultiSelect<
className={inputClasses}
{...inputProps}
ref={mergeRefs(textInput, inputProps.ref)}
{...readOnlyEventHandlers}
readOnly={readOnly}
/>
{invalid && (
<WarningFilled className={`${prefix}--list-box__invalid-icon`} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import React from 'react';
import { act, render, screen } from '@testing-library/react';
import { getByText } from '@carbon/test-utils/dom';
import userEvent from '@testing-library/user-event';
import FilterableMultiSelect from '../FilterableMultiSelect';
import {
Expand Down Expand Up @@ -63,6 +64,28 @@ describe('FilterableMultiSelect', () => {
expect(mockProps.onMenuChange).toHaveBeenCalledWith(false);
});

it('should not be interactive if readonly', async () => {
const items = generateItems(4, generateGenericItem);
const label = 'test-label';
const { container } = render(
<FilterableMultiSelect
id="test"
readOnly={true}
label={label}
items={items}
/>
);
await waitForPosition();

// eslint-disable-next-line testing-library/prefer-screen-queries
const labelNode = getByText(container, label);
await userEvent.click(labelNode);

expect(
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
container.querySelector('[aria-expanded="true"][aria-haspopup="listbox"]')
).toBeFalsy();
});
it('should initially have the menu open when open prop is provided', async () => {
render(<FilterableMultiSelect {...mockProps} open />);
await waitForPosition();
Expand Down

0 comments on commit 2bd89ed

Please sign in to comment.