From d04e218aff5728510d3b2bd6daf137b4482c5654 Mon Sep 17 00:00:00 2001 From: Lee Chase Date: Mon, 31 Oct 2022 14:19:40 +0000 Subject: [PATCH 1/6] feat: add readonly dropdown --- .../__snapshots__/PublicAPI-test.js.snap | 3 +++ .../src/components/Dropdown/Dropdown-test.js | 13 ++++++++++ .../react/src/components/Dropdown/Dropdown.js | 26 +++++++++++++++++++ .../scss/components/dropdown/_dropdown.scss | 10 +++++++ 4 files changed, 52 insertions(+) diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index 5634ca994276..f5d2995dc238 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -2612,6 +2612,9 @@ Map { "onChange": Object { "type": "func", }, + "readOnly": Object { + "type": "bool", + }, "renderSelectedItem": Object { "type": "func", }, diff --git a/packages/react/src/components/Dropdown/Dropdown-test.js b/packages/react/src/components/Dropdown/Dropdown-test.js index cd4c19661330..9288db522e19 100644 --- a/packages/react/src/components/Dropdown/Dropdown-test.js +++ b/packages/react/src/components/Dropdown/Dropdown-test.js @@ -144,6 +144,19 @@ describe('Dropdown', () => { }); }); + it('should respect readOnly prop', () => { + render(); + openMenu(); // menu should not open + assertMenuClosed(); + + openMenu(); // menu should not open + expect(screen.queryByText('Item 0')).toBeNull(); + expect(mockProps.onChange).toHaveBeenCalledTimes(0); + assertMenuClosed(); + + mockProps.onChange.mockClear(); + }); + describe('should display initially selected item found in `initialSelectedItem`', () => { it('using an object type for the `initialSelectedItem` prop', () => { render( diff --git a/packages/react/src/components/Dropdown/Dropdown.js b/packages/react/src/components/Dropdown/Dropdown.js index 816e4d180f01..e57703abf9f9 100644 --- a/packages/react/src/components/Dropdown/Dropdown.js +++ b/packages/react/src/components/Dropdown/Dropdown.js @@ -56,6 +56,7 @@ const Dropdown = React.forwardRef(function Dropdown( initialSelectedItem, selectedItem: controlledSelectedItem, downshiftProps, + readOnly, ...other }, ref @@ -102,6 +103,7 @@ const Dropdown = React.forwardRef(function Dropdown( [`${prefix}--dropdown--inline`]: inline, [`${prefix}--dropdown--disabled`]: disabled, [`${prefix}--dropdown--light`]: light, + [`${prefix}--dropdown--readonly`]: readOnly, [`${prefix}--dropdown--${size}`]: size, [`${prefix}--list-box--up`]: direction === 'top', } @@ -152,6 +154,24 @@ const Dropdown = React.forwardRef(function Dropdown( 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 (
{titleText && ( @@ -187,6 +207,7 @@ const Dropdown = React.forwardRef(function Dropdown( aria-disabled={disabled} title={selectedItem ? itemToString(selectedItem) : label} {...toggleButtonProps} + {...readOnlyEventHandlers} ref={mergeRefs(toggleButtonProps.ref, ref)}> {selectedItem @@ -341,6 +362,11 @@ Dropdown.propTypes = { */ onChange: PropTypes.func, + /** + * Whether or not the Dropdown is readonly + */ + readOnly: PropTypes.bool, + /** * An optional callback to render the currently selected item as a react element instead of only * as a string. diff --git a/packages/styles/scss/components/dropdown/_dropdown.scss b/packages/styles/scss/components/dropdown/_dropdown.scss index 651e0626e568..a60f73101227 100644 --- a/packages/styles/scss/components/dropdown/_dropdown.scss +++ b/packages/styles/scss/components/dropdown/_dropdown.scss @@ -473,4 +473,14 @@ .#{$prefix}--list-box__menu-item__selected-icon { @include high-contrast-mode('icon-fill'); } + + $icon-readonly: $icon-disabled; + .#{$prefix}--dropdown--readonly, + .#{$prefix}--dropdown--readonly:hover { + background-color: transparent; + } + + .#{$prefix}--dropdown--readonly .#{$prefix}--list-box__menu-icon svg { + fill: $icon-readonly; + } } From b02a9d971ea793f3cd7155495e371914a9982b00 Mon Sep 17 00:00:00 2001 From: Lee Chase Date: Mon, 31 Oct 2022 15:01:28 +0000 Subject: [PATCH 2/6] feat: read only multiselect --- .../src/components/MultiSelect/MultiSelect.js | 31 ++++++++++++++++-- .../MultiSelect/MultiSelect.stories.js | 3 ++ .../components/multiselect/_multiselect.scss | 32 +++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/packages/react/src/components/MultiSelect/MultiSelect.js b/packages/react/src/components/MultiSelect/MultiSelect.js index ae686e941029..21eb60bc1296 100644 --- a/packages/react/src/components/MultiSelect/MultiSelect.js +++ b/packages/react/src/components/MultiSelect/MultiSelect.js @@ -70,6 +70,7 @@ const MultiSelect = React.forwardRef(function MultiSelect( onMenuChange, direction, selectedItems: selected, + readOnly, }, ref ) { @@ -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, } ); @@ -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 (