diff --git a/.changeset/proud-items-do.md b/.changeset/proud-items-do.md new file mode 100644 index 00000000000..e5cc17ae927 --- /dev/null +++ b/.changeset/proud-items-do.md @@ -0,0 +1,5 @@ +--- +'@shopify/polaris': minor +--- + +Added a `disabled` prop to `ResourceItem` diff --git a/polaris-react/src/components/ResourceItem/ResourceItem.module.scss b/polaris-react/src/components/ResourceItem/ResourceItem.module.scss index fcbaafee50e..d2eb1fb3e88 100644 --- a/polaris-react/src/components/ResourceItem/ResourceItem.module.scss +++ b/polaris-react/src/components/ResourceItem/ResourceItem.module.scss @@ -84,6 +84,15 @@ margin-right: 0; } +.disabled { + cursor: default; + color: var(--p-color-text-secondary); + + &:hover { + background-color: transparent; + } +} + // Item actions .Actions { > * { diff --git a/polaris-react/src/components/ResourceItem/ResourceItem.stories.tsx b/polaris-react/src/components/ResourceItem/ResourceItem.stories.tsx index 8d15bc2683c..7d2b6263d60 100644 --- a/polaris-react/src/components/ResourceItem/ResourceItem.stories.tsx +++ b/polaris-react/src/components/ResourceItem/ResourceItem.stories.tsx @@ -301,3 +301,69 @@ export function WithVerticalAlignment() { ); } + +export function WithDisabledState() { + const [selectedItems, setSelectedItems] = useState< + ResourceListProps['selectedItems'] + >([]); + + return ( + + { + const {id, url, avatarSource, name, location} = item; + + return ( + + } + accessibilityLabel={`View details for ${name}`} + name={name} + > +

+ + {name} + +

+
{location}
+
+ ); + }} + /> +
+ ); +} diff --git a/polaris-react/src/components/ResourceItem/ResourceItem.tsx b/polaris-react/src/components/ResourceItem/ResourceItem.tsx index 7e9e47ee6de..e8a97112398 100644 --- a/polaris-react/src/components/ResourceItem/ResourceItem.tsx +++ b/polaris-react/src/components/ResourceItem/ResourceItem.tsx @@ -30,6 +30,8 @@ import styles from './ResourceItem.module.scss'; type Alignment = 'leading' | 'trailing' | 'center' | 'fill' | 'baseline'; interface BaseProps { + /** Whether or not interaction is disabled */ + disabled?: boolean; /** Visually hidden text for screen readers used for item link */ accessibilityLabel?: string; /** Individual item name used by various text labels */ @@ -159,6 +161,7 @@ class BaseResourceItem extends Component { dataHref, breakpoints, onMouseOver, + disabled, } = this.props; const {actionsMenuVisible, focused, focusedInner, selected} = this.state; @@ -183,7 +186,7 @@ class BaseResourceItem extends Component { label={checkboxAccessibilityLabel} labelHidden checked={selected} - disabled={loading} + disabled={loading || disabled} bleedInlineStart="300" bleedInlineEnd="300" bleedBlockStart="300" @@ -219,6 +222,7 @@ class BaseResourceItem extends Component { selectMode && styles.selectMode, persistActions && styles.persistActions, focusedInner && styles.focusedInner, + disabled && styles.disabled, ); const listItemClassName = classNames( @@ -351,7 +355,7 @@ class BaseResourceItem extends Component {
{} : this.handleClick} onFocus={this.handleFocus} onBlur={this.handleBlur} onKeyUp={this.handleKeyUp} @@ -359,7 +363,7 @@ class BaseResourceItem extends Component { onMouseOut={this.handleMouseOut} data-href={url} > - {accessibleMarkup} + {disabled ? null : accessibleMarkup} {containerMarkup}
@@ -458,12 +462,13 @@ class BaseResourceItem extends Component { // This fires onClick when there is a URL on the item private handleKeyUp = (event: React.KeyboardEvent) => { const { + disabled, onClick = noop, context: {selectMode}, } = this.props; const {key} = event; - if (key === 'Enter' && this.props.url && !selectMode) { + if (key === 'Enter' && this.props.url && !selectMode && !disabled) { onClick(); } }; diff --git a/polaris-react/src/components/ResourceItem/tests/ResourceItem.test.tsx b/polaris-react/src/components/ResourceItem/tests/ResourceItem.test.tsx index c04320590b4..70bc7cc05c0 100644 --- a/polaris-react/src/components/ResourceItem/tests/ResourceItem.test.tsx +++ b/polaris-react/src/components/ResourceItem/tests/ResourceItem.test.tsx @@ -227,6 +227,22 @@ describe('', () => { expect(element).toContainReactComponent('div', {'data-href': url} as any); }); + + it('does not render an when disabled prop is true', () => { + const element = mountWithApp( + + + , + ); + + expect(element).not.toContainReactComponent(UnstyledLink); + }); }); describe('external', () => { @@ -388,6 +404,38 @@ describe('', () => { expect(onClick).not.toHaveBeenCalled(); }); + it('does not call onClick when hitting keyUp on the item when onClick exists, url exists and is disabled', () => { + const onClick = jest.fn(); + const wrapper = mountWithApp( + + + , + ); + + findResourceItem(wrapper)!.trigger('onKeyUp', {key: 'Enter'}); + expect(onClick).not.toHaveBeenCalled(); + }); + + it('does not call onClick when clicking on the item when onClick exists and is disabled', () => { + const onClick = jest.fn(); + const wrapper = mountWithApp( + + + , + ); + + findResourceItem(wrapper)!.trigger('onClick', { + stopPropagation: () => {}, + nativeEvent: {}, + }); + expect(onClick).not.toHaveBeenCalledWith(itemId); + }); + it('calls window.open on ctrlKey + click', () => { const wrapper = mountWithApp( @@ -448,6 +496,15 @@ describe('', () => { false, ); }); + + it('renders a disabled Checkbox if the item is disabled', () => { + const wrapper = mountWithApp( + + + , + ); + expect(wrapper).toContainReactComponent(Checkbox, {disabled: true}); + }); }); describe('SelectMode', () => {