diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index d492b1d9a10c..fcb2f8bd02fe 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -1179,6 +1179,9 @@ Map { "placeholder": Object { "type": "string", }, + "readOnly": Object { + "type": "bool", + }, "selectedItem": Object { "args": Array [ Array [ diff --git a/packages/react/src/components/ComboBox/ComboBox-test.js b/packages/react/src/components/ComboBox/ComboBox-test.js index fda420019087..ceda878ac937 100644 --- a/packages/react/src/components/ComboBox/ComboBox-test.js +++ b/packages/react/src/components/ComboBox/ComboBox-test.js @@ -164,6 +164,26 @@ describe('ComboBox', () => { }); }); + describe('when readonly', () => { + it('should not let the user edit the input node', () => { + render(); + + expect(findInputNode()).toHaveAttribute('readonly'); + + expect(findInputNode()).toHaveDisplayValue(''); + + userEvent.type(findInputNode(), 'o'); + + expect(findInputNode()).toHaveDisplayValue(''); + }); + + it('should not let the user expand the menu', () => { + render(); + openMenu(); + expect(findListBoxNode()).not.toHaveClass('cds--list-box--expanded'); + }); + }); + describe('downshift quirks', () => { it('should set `inputValue` to an empty string if a false-y value is given', () => { render(); diff --git a/packages/react/src/components/ComboBox/ComboBox.js b/packages/react/src/components/ComboBox/ComboBox.js index 1e9463c2ed9d..01af0f007378 100644 --- a/packages/react/src/components/ComboBox/ComboBox.js +++ b/packages/react/src/components/ComboBox/ComboBox.js @@ -91,6 +91,7 @@ const ComboBox = React.forwardRef((props, ref) => { onInputChange, onToggleClick, // eslint-disable-line no-unused-vars placeholder, + readOnly, selectedItem, shouldFilterItem, size, @@ -203,6 +204,7 @@ const ComboBox = React.forwardRef((props, ref) => { { [`${prefix}--list-box--up`]: direction === 'top', [`${prefix}--combo-box--warning`]: showWarning, + [`${prefix}--combo-box--readonly`]: readOnly, } ); const titleClasses = cx(`${prefix}--label`, { @@ -264,7 +266,7 @@ const ComboBox = React.forwardRef((props, ref) => { ); const labelProps = getLabelProps(); const buttonProps = getToggleButtonProps({ - disabled, + disabled: disabled || readOnly, onClick: handleToggleClick(isOpen), // When we moved the "root node" of Downshift to the for // ARIA 1.2 compliance, we unfortunately hit this branch for the @@ -308,6 +310,17 @@ const ComboBox = React.forwardRef((props, ref) => { } }; + const readOnlyEventHandlers = readOnly + ? { + onKeyDown: (evt) => { + // This prevents the select from opening for the above keys + if (evt.key !== 'Tab') { + evt.preventDefault(); + } + }, + } + : {}; + return (
{titleText && ( @@ -341,6 +354,8 @@ const ComboBox = React.forwardRef((props, ref) => { title={textInput?.current?.value} {...inputProps} {...rest} + {...readOnlyEventHandlers} + readOnly={readOnly} ref={mergeRefs(textInput, ref)} /> {invalid && ( @@ -357,7 +372,7 @@ const ComboBox = React.forwardRef((props, ref) => { )} @@ -540,6 +555,11 @@ ComboBox.propTypes = { */ placeholder: PropTypes.string, + /** + * Is the ComboBox readonly? + */ + readOnly: PropTypes.bool, + /** * For full control of the selection */ diff --git a/packages/react/src/components/ListBox/next/ListBoxSelection.js b/packages/react/src/components/ListBox/next/ListBoxSelection.js index d70e7235a272..63a722649328 100644 --- a/packages/react/src/components/ListBox/next/ListBoxSelection.js +++ b/packages/react/src/components/ListBox/next/ListBoxSelection.js @@ -92,6 +92,7 @@ function ListBoxSelection({ {...rest} aria-label={description} className={className} + disabled={disabled} onClick={onClick} onKeyDown={onKeyDown} tabIndex={disabled ? -1 : 0} diff --git a/packages/styles/scss/components/combo-box/_combo-box.scss b/packages/styles/scss/components/combo-box/_combo-box.scss index 0fc2593ea1db..da6ef4e2814c 100644 --- a/packages/styles/scss/components/combo-box/_combo-box.scss +++ b/packages/styles/scss/components/combo-box/_combo-box.scss @@ -42,4 +42,20 @@ .#{$prefix}--list-box__field { padding: 0; } + + // readonly + .#{$prefix}--combo-box--readonly, + .#{$prefix}--combo-box--readonly:hover { + background-color: transparent; + } + + .#{$prefix}--combo-box--readonly .#{$prefix}--list-box__menu-icon, + .#{$prefix}--combo-box--readonly .#{$prefix}--list-box__selection { + cursor: default; + } + + .#{$prefix}--combo-box--readonly .#{$prefix}--list-box__menu-icon svg, + .#{$prefix}--combo-box--readonly .#{$prefix}--list-box__selection svg { + fill: $icon-disabled; + } }