From 42a91f7215d0503fa48ade9cef47ff4accb57e00 Mon Sep 17 00:00:00 2001 From: moathabuhamad-cengage Date: Wed, 16 Oct 2024 13:00:18 +0200 Subject: [PATCH] feat(Select): Support disabling individual items --- .changeset/feat-select-disabled-items.md | 5 +++ .../src/components/Select/ItemsList.tsx | 28 +++++++++----- .../src/components/Select/Select.stories.tsx | 16 ++++---- .../src/components/Select/Select.test.js | 29 ++++++++++++--- .../src/components/Select/components.tsx | 13 +++++-- .../src/components/Select/index.tsx | 30 +++++++++------ .../src/components/Select/shared.ts | 37 +++++++++++++------ .../react-magma-docs/src/pages/api/select.mdx | 23 ++++++++++++ 8 files changed, 132 insertions(+), 49 deletions(-) create mode 100644 .changeset/feat-select-disabled-items.md diff --git a/.changeset/feat-select-disabled-items.md b/.changeset/feat-select-disabled-items.md new file mode 100644 index 0000000000..d08948f689 --- /dev/null +++ b/.changeset/feat-select-disabled-items.md @@ -0,0 +1,5 @@ +--- +'react-magma-dom': minor +--- + +feat(Select): Support disabling individual items \ No newline at end of file diff --git a/packages/react-magma-dom/src/components/Select/ItemsList.tsx b/packages/react-magma-dom/src/components/Select/ItemsList.tsx index 57d1c052e6..d4cb64bca0 100644 --- a/packages/react-magma-dom/src/components/Select/ItemsList.tsx +++ b/packages/react-magma-dom/src/components/Select/ItemsList.tsx @@ -1,21 +1,24 @@ -import React from 'react'; -import { ThemeContext } from '../../theme/ThemeContext'; -import { I18nContext } from '../../i18n'; -import { StyledCard, StyledItem, StyledList } from './shared'; +import styled from '@emotion/styled'; +import { ReferenceType } from '@floating-ui/react-dom'; import { UseSelectGetItemPropsOptions, UseSelectGetMenuPropsOptions, } from 'downshift'; -import { instanceOfToBeCreatedItemObject } from '.'; +import React from 'react'; +import { + instanceOfItemWithOptionalDisabled, + instanceOfToBeCreatedItemObject, +} from '.'; +import { I18nContext } from '../../i18n'; +import { ThemeContext } from '../../theme/ThemeContext'; +import { convertStyleValueToString } from '../../utils'; +import { Spinner } from '../Spinner'; import { defaultComponents, ItemRenderOptions, SelectComponents, } from './components'; -import { convertStyleValueToString } from '../../utils'; -import { Spinner } from '../Spinner'; -import styled from '@emotion/styled'; -import { ReferenceType } from '@floating-ui/react-dom'; +import { StyledCard, StyledItem, StyledList } from './shared'; interface ItemsListProps { customComponents?: SelectComponents; @@ -98,7 +101,7 @@ export function ItemsList(props: ItemsListProps) { } return ( -
+
(props: ItemsListProps) { const itemString = instanceOfToBeCreatedItemObject(item) ? item.label : itemToString(item); + const isItemDisabled = instanceOfItemWithOptionalDisabled(item) + ? item.disabled + : false; const { ref, ...otherDownshiftItemProps } = getItemProps({ item, index, + disabled: isItemDisabled, }); const key = `${itemString}${index}`; @@ -133,6 +140,7 @@ export function ItemsList(props: ItemsListProps) { itemString, key, theme, + isDisabled:isItemDisabled, ...otherDownshiftItemProps, }; diff --git a/packages/react-magma-dom/src/components/Select/Select.stories.tsx b/packages/react-magma-dom/src/components/Select/Select.stories.tsx index cbe6c49f49..b52a54327e 100644 --- a/packages/react-magma-dom/src/components/Select/Select.stories.tsx +++ b/packages/react-magma-dom/src/components/Select/Select.stories.tsx @@ -1,13 +1,13 @@ +import { Meta, Story } from '@storybook/react/types-6-0'; import React from 'react'; -import { Story, Meta } from '@storybook/react/types-6-0'; -import { Select, SelectOptions, SelectProps, MultiSelectProps } from './'; -import { LabelPosition } from '../Label'; +import { HelpIcon } from 'react-magma-icons'; +import { ButtonSize, ButtonType, ButtonVariant } from '../Button'; import { Card } from '../Card'; import { CardBody } from '../Card/CardBody'; -import { Tooltip } from '../Tooltip'; import { IconButton } from '../IconButton'; -import { HelpIcon } from 'react-magma-icons'; -import { ButtonSize, ButtonType, ButtonVariant } from '../Button'; +import { LabelPosition } from '../Label'; +import { Tooltip } from '../Tooltip'; +import { MultiSelectProps, Select, SelectOptions, SelectProps } from './'; const Template: Story> = args => ( + ); + + const renderedSelect = getByLabelText(labelText, { selector: 'div' }); + fireEvent.click(renderedSelect); + + expect(getByText('Red')).toHaveAttribute('aria-disabled', 'true'); + expect(getByText('Red')).toHaveStyleRule('cursor', 'not-allowed'); + expect(getByText('Blue')).toHaveAttribute('aria-disabled', 'false'); + expect(getByText('Green')).toHaveAttribute('aria-disabled', 'false'); + }); }); describe('events', () => { diff --git a/packages/react-magma-dom/src/components/Select/components.tsx b/packages/react-magma-dom/src/components/Select/components.tsx index 847689d464..ee5f20fcd5 100644 --- a/packages/react-magma-dom/src/components/Select/components.tsx +++ b/packages/react-magma-dom/src/components/Select/components.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; +import { ArrowDropDownIcon, IconProps } from 'react-magma-icons'; +import { ThemeInterface } from '../../theme/magma'; import { IconButton, IconButtonProps } from '../IconButton'; import { Spinner, SpinnerProps } from '../Spinner'; -import { IconProps, ArrowDropDownIcon } from 'react-magma-icons'; import { StyledItem } from './shared'; -import { ThemeInterface } from '../../theme/magma'; export type ItemRenderOptions = { key: string; @@ -46,10 +46,17 @@ export function DefaultItem({ itemRef, itemString, isInverse, + isDisabled, ...props }: ItemRenderOptions) { return ( - + {itemString} ); diff --git a/packages/react-magma-dom/src/components/Select/index.tsx b/packages/react-magma-dom/src/components/Select/index.tsx index 0443eec60c..d863b9b66e 100644 --- a/packages/react-magma-dom/src/components/Select/index.tsx +++ b/packages/react-magma-dom/src/components/Select/index.tsx @@ -1,10 +1,3 @@ -import * as React from 'react'; -import { - useMultipleSelection, - UseMultipleSelectionProps, - useSelect, - UseSelectProps, -} from 'downshift'; import { AlignedPlacement, autoUpdate, @@ -12,16 +5,23 @@ import { useFloating, } from '@floating-ui/react-dom'; import { ReferenceType } from '@floating-ui/react-dom/dist/floating-ui.react-dom'; -import { Select as InternalSelect } from './Select'; -import { MultiSelect } from './MultiSelect'; -import { SelectComponents } from './components'; +import { + useMultipleSelection, + UseMultipleSelectionProps, + useSelect, + UseSelectProps, +} from 'downshift'; +import * as React from 'react'; +import { useIsInverse } from '../../inverse'; import { Omit, useGenerateId, XOR } from '../../utils'; import { LabelPosition } from '../Label'; -import { useIsInverse } from '../../inverse'; +import { MultiSelect } from './MultiSelect'; +import { Select as InternalSelect } from './Select'; +import { SelectComponents } from './components'; export type SelectOptions = | string - | { value: string; label: string; [key: string]: any } + | { value: string; label: string; [key: string]: any; disabled?: boolean } | any; export interface InternalSelectProps { @@ -214,6 +214,12 @@ export function instanceOfToBeCreatedItemObject(object: any): object is { ); } +export function instanceOfItemWithOptionalDisabled( + object: any +): object is { label: string; value: string; disabled?: boolean } { + return typeof object !== 'string' && object && 'disabled' in object; +} + export type XORSelectProps = XOR, MultiSelectProps>; export const SelectStateChangeTypes = useSelect.stateChangeTypes; diff --git a/packages/react-magma-dom/src/components/Select/shared.ts b/packages/react-magma-dom/src/components/Select/shared.ts index cbc9c4113a..f93961e94e 100644 --- a/packages/react-magma-dom/src/components/Select/shared.ts +++ b/packages/react-magma-dom/src/components/Select/shared.ts @@ -1,9 +1,9 @@ -import { inputBaseStyles } from '../InputBase'; -import { Card } from '../Card'; -import { transparentize } from 'polished'; +import { css } from '@emotion/react'; import styled from '@emotion/styled'; +import { transparentize } from 'polished'; import { ThemeInterface } from '../../theme/magma'; -import { css } from '@emotion/react'; +import { Card } from '../Card'; +import { inputBaseStyles } from '../InputBase'; function buildListHoverColor(props) { if (props.isFocused) { @@ -25,6 +25,19 @@ function buildListFocusColor(props) { return 'transparent'; } +function buildListItemColor(props) { + if (props.isDisabled) { + if (props.isInverse) { + return transparentize(0.6, props.theme.colors.neutral100); + } + return transparentize(0.4, props.theme.colors.neutral500); + } + if (props.isInverse) { + return props.theme.colors.neutral100; + } + return props.theme.colors.neutral700; +} + export const SelectContainer = styled.div` position: relative; `; @@ -55,9 +68,12 @@ export const SelectText = styled.span<{ : props.theme.colors.neutral500; } }}; - ${props => props.isDisabled && props.isShowPlaceholder && css` - opacity: ${props.isInverse ? 0.4 : 0.6} - `} + ${props => + props.isDisabled && + props.isShowPlaceholder && + css` + opacity: ${props.isInverse ? 0.4 : 0.6}; + `} `; export const StyledCard = styled(Card)<{ @@ -91,19 +107,18 @@ export const StyledList = styled('ul')<{ isOpen?: boolean; maxHeight: string }>` export const StyledItem = styled('li')<{ isInverse?: boolean; isFocused?: boolean; + isDisabled?: boolean; }>` align-self: center; background: ${props => buildListHoverColor(props)}; border: 2px solid; border-color: ${props => buildListFocusColor(props)}; cursor: default; - color: ${props => - props.isInverse - ? props.theme.colors.neutral100 - : props.theme.colors.neutral700}; + color: ${props => buildListItemColor(props)}; line-height: 24px; margin: 0; padding: 8px 16px; + cursor: ${props => (props.isDisabled ? 'not-allowed' : 'pointer')}; &:hover { background: ${props => buildListHoverColor(props)}; border-color: transparent; diff --git a/website/react-magma-docs/src/pages/api/select.mdx b/website/react-magma-docs/src/pages/api/select.mdx index 2d84e5e460..5b31485db4 100644 --- a/website/react-magma-docs/src/pages/api/select.mdx +++ b/website/react-magma-docs/src/pages/api/select.mdx @@ -159,6 +159,29 @@ export function Example() { } ``` +## Disabled Items + +You can disable specific items in the `Select` component by adding a `disabled: true` property to the item object. For example: + +```tsx +import React from 'react'; +import { Select } from 'react-magma-dom'; + +export function Example() { + return ( +