diff --git a/packages/react-components/react-tag-picker-preview/etc/react-tag-picker-preview.api.md b/packages/react-components/react-tag-picker-preview/etc/react-tag-picker-preview.api.md index e7dbf4aa9070d..c0e7f74499821 100644 --- a/packages/react-components/react-tag-picker-preview/etc/react-tag-picker-preview.api.md +++ b/packages/react-components/react-tag-picker-preview/etc/react-tag-picker-preview.api.md @@ -103,7 +103,7 @@ export type TagPickerControlSlots = { }; // @public -export type TagPickerControlState = ComponentState & Pick; +export type TagPickerControlState = ComponentState & Pick; // @public export const TagPickerGroup: ForwardRefComponent; @@ -141,7 +141,7 @@ export type TagPickerInputSlots = { } & Pick; // @public -export type TagPickerInputState = ComponentState & Pick; +export type TagPickerInputState = ComponentState & Pick; // @public export const TagPickerList: ForwardRefComponent; @@ -167,7 +167,7 @@ export const TagPickerOption: ForwardRefComponent; export const tagPickerOptionClassNames: SlotClassNames; // @public -export type TagPickerOptionProps = ComponentProps & Omit & { +export type TagPickerOptionProps = ComponentProps & Omit & { children: React_2.ReactNode; text?: string; value: string; @@ -183,7 +183,7 @@ export type TagPickerOptionSlots = Omit & { export type TagPickerOptionState = ComponentState & Omit; // @public -export type TagPickerProps = ComponentProps & Pick & Pick, 'size' | 'selectedOptions' | 'appearance'> & { +export type TagPickerProps = ComponentProps & Pick & Pick, 'size' | 'selectedOptions' | 'appearance'> & { children: [JSX.Element, JSX.Element] | JSX.Element; }; @@ -191,7 +191,7 @@ export type TagPickerProps = ComponentProps & Pick & Omit & Pick & { +export type TagPickerState = ComponentState & Omit & Pick & { positioning?: PositioningShorthand; trigger: React_2.ReactNode; popover?: React_2.ReactNode; diff --git a/packages/react-components/react-tag-picker-preview/src/components/TagPicker/TagPicker.types.ts b/packages/react-components/react-tag-picker-preview/src/components/TagPicker/TagPicker.types.ts index e34e4c15cb9a4..3fc1ed6cb2294 100644 --- a/packages/react-components/react-tag-picker-preview/src/components/TagPicker/TagPicker.types.ts +++ b/packages/react-components/react-tag-picker-preview/src/components/TagPicker/TagPicker.types.ts @@ -13,7 +13,7 @@ export type TagPickerSize = 'medium' | 'large' | 'extra-large'; * Picker Props */ export type TagPickerProps = ComponentProps & - Pick & + Pick & Pick, 'size' | 'selectedOptions' | 'appearance'> & { /** * Can contain two children including a trigger and a popover @@ -27,7 +27,7 @@ export type TagPickerProps = ComponentProps & */ export type TagPickerState = ComponentState & Omit & - Pick & { + Pick & { /** * Configures the positioned menu */ diff --git a/packages/react-components/react-tag-picker-preview/src/components/TagPicker/useTagPicker.ts b/packages/react-components/react-tag-picker-preview/src/components/TagPicker/useTagPicker.ts index a48582f1db9ac..48db0215cc55e 100644 --- a/packages/react-components/react-tag-picker-preview/src/components/TagPicker/useTagPicker.ts +++ b/packages/react-components/react-tag-picker-preview/src/components/TagPicker/useTagPicker.ts @@ -17,7 +17,7 @@ import { useComboboxBaseState } from '../../utils/useComboboxBaseState'; export const useTagPicker_unstable = (props: TagPickerProps): TagPickerState => { const popoverId = useId('picker-listbox'); const triggerInnerRef = React.useRef(null); - const { positioning, size = 'medium' } = props; + const { positioning, size = 'medium', disabled = false } = props; // Set a default set of fallback positions to try if the dropdown does not fit on screen const fallbackPositions: PositioningShorthandValue[] = ['above', 'after', 'after-top', 'before', 'before-top']; @@ -47,9 +47,16 @@ export const useTagPicker_unstable = (props: TagPickerProps): TagPickerState => size: 'medium', }); const onOptionClickBase = state.onOptionClick; - state.onOptionClick = useEventCallback(e => { - onOptionClickBase(e); - state.setOpen(e, false); + state.onOptionClick = useEventCallback(event => { + onOptionClickBase(event); + state.setOpen(event, false); + }); + const setOpenBase = state.setOpen; + state.setOpen = useEventCallback((event, newValue) => { + if (disabled) { + return; + } + setOpenBase(event, newValue); }); const children = React.Children.toArray(props.children) as React.ReactElement[]; @@ -81,6 +88,7 @@ export const useTagPicker_unstable = (props: TagPickerProps): TagPickerState => trigger, popover: state.open || state.hasFocus ? popover : undefined, popoverId, + disabled, triggerRef: useMergedRefs(triggerInnerRef, activeParentRef), popoverRef: useMergedRefs(listboxRef, containerRef), targetRef, diff --git a/packages/react-components/react-tag-picker-preview/src/components/TagPicker/useTagPickerContextValues.ts b/packages/react-components/react-tag-picker-preview/src/components/TagPicker/useTagPickerContextValues.ts index d715e4221d9f7..fa5ee86fbcfb4 100644 --- a/packages/react-components/react-tag-picker-preview/src/components/TagPicker/useTagPickerContextValues.ts +++ b/packages/react-components/react-tag-picker-preview/src/components/TagPicker/useTagPickerContextValues.ts @@ -27,6 +27,7 @@ export function useTagPickerContextValues(state: TagPickerState): TagPickerConte getOptionById, open, popoverId, + disabled, } = state; return { activeDescendant: React.useMemo( @@ -60,6 +61,7 @@ export function useTagPickerContextValues(state: TagPickerState): TagPickerConte getOptionById, open, popoverId, + disabled, }, }; } diff --git a/packages/react-components/react-tag-picker-preview/src/components/TagPickerControl/TagPickerControl.types.ts b/packages/react-components/react-tag-picker-preview/src/components/TagPickerControl/TagPickerControl.types.ts index 9b5b5184a15ad..a154ae61b6945 100644 --- a/packages/react-components/react-tag-picker-preview/src/components/TagPickerControl/TagPickerControl.types.ts +++ b/packages/react-components/react-tag-picker-preview/src/components/TagPickerControl/TagPickerControl.types.ts @@ -14,4 +14,4 @@ export type TagPickerControlProps = ComponentProps; * State used in rendering PickerControl */ export type TagPickerControlState = ComponentState & - Pick; + Pick; diff --git a/packages/react-components/react-tag-picker-preview/src/components/TagPickerControl/useTagPickerControl.ts b/packages/react-components/react-tag-picker-preview/src/components/TagPickerControl/useTagPickerControl.ts index 0247c7e651132..de36aff871f09 100644 --- a/packages/react-components/react-tag-picker-preview/src/components/TagPickerControl/useTagPickerControl.ts +++ b/packages/react-components/react-tag-picker-preview/src/components/TagPickerControl/useTagPickerControl.ts @@ -22,6 +22,7 @@ export const useTagPickerControl_unstable = ( const triggerRef = useTagPickerContext_unstable(ctx => ctx.triggerRef); const size = useTagPickerContext_unstable(ctx => ctx.size); const appearance = useTagPickerContext_unstable(ctx => ctx.appearance); + const disabled = useTagPickerContext_unstable(ctx => ctx.disabled); const handleMouseDown = useEventCallback((event: React.MouseEvent) => { if (event.target !== triggerRef.current) { event.preventDefault(); @@ -48,5 +49,6 @@ export const useTagPickerControl_unstable = ( ), size, appearance, + disabled, }; }; diff --git a/packages/react-components/react-tag-picker-preview/src/components/TagPickerControl/useTagPickerControlStyles.styles.ts b/packages/react-components/react-tag-picker-preview/src/components/TagPickerControl/useTagPickerControlStyles.styles.ts index 5a2217bc0438d..1b97b7667cbaa 100644 --- a/packages/react-components/react-tag-picker-preview/src/components/TagPickerControl/useTagPickerControlStyles.styles.ts +++ b/packages/react-components/react-tag-picker-preview/src/components/TagPickerControl/useTagPickerControlStyles.styles.ts @@ -150,14 +150,13 @@ const useStyles = makeStyles({ */ export const useTagPickerControlStyles_unstable = (state: TagPickerControlState): TagPickerControlState => { const styles = useStyles(); - // TODO handle invalid styles state.root.className = mergeClasses( tagPickerControlClassNames.root, styles.root, styles[state.size], styles[state.appearance], - // !state.disabled && state.appearance === 'outline' && styles.outlineInteractive, - // state.disabled && styles.disabled, + !state.disabled && state.appearance === 'outline' && styles.outlineInteractive, + state.disabled && styles.disabled, state.root.className, ); diff --git a/packages/react-components/react-tag-picker-preview/src/components/TagPickerInput/TagPickerInput.types.ts b/packages/react-components/react-tag-picker-preview/src/components/TagPickerInput/TagPickerInput.types.ts index 81e42ba1cce95..cbcbbe531dd80 100644 --- a/packages/react-components/react-tag-picker-preview/src/components/TagPickerInput/TagPickerInput.types.ts +++ b/packages/react-components/react-tag-picker-preview/src/components/TagPickerInput/TagPickerInput.types.ts @@ -22,4 +22,5 @@ export type TagPickerInputProps = Omit< /** * State used in rendering TagPickerInput */ -export type TagPickerInputState = ComponentState & Pick; +export type TagPickerInputState = ComponentState & + Pick; diff --git a/packages/react-components/react-tag-picker-preview/src/components/TagPickerInput/useTagPickerInput.tsx b/packages/react-components/react-tag-picker-preview/src/components/TagPickerInput/useTagPickerInput.tsx index 267d69be94a7e..a352cd15baca1 100644 --- a/packages/react-components/react-tag-picker-preview/src/components/TagPickerInput/useTagPickerInput.tsx +++ b/packages/react-components/react-tag-picker-preview/src/components/TagPickerInput/useTagPickerInput.tsx @@ -4,13 +4,7 @@ import type { TagPickerInputProps, TagPickerInputState } from './TagPickerInput. import { ChevronDownRegular as ChevronDownIcon } from '@fluentui/react-icons'; import { useActiveDescendantContext } from '@fluentui/react-aria'; import { useTagPickerContext_unstable } from '../../contexts/TagPickerContext'; -import { - slot, - useMergedRefs, - getIntrinsicElementProps, - useEventCallback, - mergeCallbacks, -} from '@fluentui/react-utilities'; +import { slot, useMergedRefs, getIntrinsicElementProps, useEventCallback } from '@fluentui/react-utilities'; import { useInputTriggerSlot } from '../../utils/useInputTriggerSlot'; import { Backspace, Enter } from '@fluentui/keyboard-keys'; @@ -29,6 +23,7 @@ export const useTagPickerInput_unstable = ( ): TagPickerInputState => { const { controller: activeDescendantController } = useActiveDescendantContext(); const size = useTagPickerContext_unstable(ctx => ctx.size); + const contextDisabled = useTagPickerContext_unstable(ctx => ctx.disabled); const { triggerRef, clearSelection, @@ -44,7 +39,7 @@ export const useTagPickerInput_unstable = ( value: contextValue, } = usePickerContext(); - const { value = contextValue, disabled } = props; + const { value = contextValue, disabled = contextDisabled } = props; const root = useInputTriggerSlot( { @@ -108,22 +103,10 @@ export const useTagPickerInput_unstable = ( }, elementType: 'span', }), + disabled, size, }; - /* handle open/close + focus change when clicking expandIcon */ - const { onMouseDown: onIconMouseDown } = state.expandIcon || {}; - - const onExpandIconMouseDown = useEventCallback( - mergeCallbacks(onIconMouseDown, (event: React.MouseEvent) => { - event.preventDefault(); - }), - ); - - if (state.expandIcon) { - state.expandIcon.onMouseDown = onExpandIconMouseDown; - } - return state; }; diff --git a/packages/react-components/react-tag-picker-preview/src/components/TagPickerInput/useTagPickerInputStyles.styles.ts b/packages/react-components/react-tag-picker-preview/src/components/TagPickerInput/useTagPickerInputStyles.styles.ts index 7e919ec5fa880..7fa236361b5a6 100644 --- a/packages/react-components/react-tag-picker-preview/src/components/TagPickerInput/useTagPickerInputStyles.styles.ts +++ b/packages/react-components/react-tag-picker-preview/src/components/TagPickerInput/useTagPickerInputStyles.styles.ts @@ -121,7 +121,7 @@ export const useTagPickerInputStyles_unstable = (state: TagPickerInputState): Ta tagPickerInputClassNames.root, styles.root, styles[state.size], - // state.disabled && styles.disabled, + state.disabled && styles.disabled, state.root.className, ); @@ -130,7 +130,7 @@ export const useTagPickerInputStyles_unstable = (state: TagPickerInputState): Ta tagPickerInputClassNames.expandIcon, iconStyles.icon, iconStyles[state.size], - // state.disabled && iconStyles.disabled, + state.disabled && iconStyles.disabled, // state.showClearIcon && iconStyles.visuallyHidden, state.expandIcon.className, ); diff --git a/packages/react-components/react-tag-picker-preview/src/components/TagPickerOption/TagPickerOption.types.ts b/packages/react-components/react-tag-picker-preview/src/components/TagPickerOption/TagPickerOption.types.ts index 27decc23e39cc..6f72d62d3ed1d 100644 --- a/packages/react-components/react-tag-picker-preview/src/components/TagPickerOption/TagPickerOption.types.ts +++ b/packages/react-components/react-tag-picker-preview/src/components/TagPickerOption/TagPickerOption.types.ts @@ -11,7 +11,7 @@ export type TagPickerOptionSlots = Omit & { * TagPickerOption Props */ export type TagPickerOptionProps = ComponentProps & - Omit & { + Omit & { children: React.ReactNode; text?: string; value: string; diff --git a/packages/react-components/react-tag-picker-preview/src/contexts/TagPickerContext.ts b/packages/react-components/react-tag-picker-preview/src/contexts/TagPickerContext.ts index 59d8ce40a13eb..f15a3c068cbf0 100644 --- a/packages/react-components/react-tag-picker-preview/src/contexts/TagPickerContext.ts +++ b/packages/react-components/react-tag-picker-preview/src/contexts/TagPickerContext.ts @@ -24,6 +24,7 @@ export interface TagPickerContextValue popoverId: string; targetRef: React.RefObject; size: TagPickerSize; + disabled: boolean; } /** @@ -46,6 +47,7 @@ export const tagPickerContextDefaultValue: TagPickerContextValue = { popoverId: '', size: 'medium', appearance: 'outline', + disabled: false, }; const TagPickerContext = createContext(undefined); diff --git a/packages/react-components/react-tag-picker-preview/stories/TagPicker/TagPickerDisabled.stories.tsx b/packages/react-components/react-tag-picker-preview/stories/TagPicker/TagPickerDisabled.stories.tsx new file mode 100644 index 0000000000000..849f7f18c5df0 --- /dev/null +++ b/packages/react-components/react-tag-picker-preview/stories/TagPicker/TagPickerDisabled.stories.tsx @@ -0,0 +1,65 @@ +import * as React from 'react'; +import { + TagPicker, + TagPickerList, + TagPickerInput, + TagPickerControl, + TagPickerProps, + TagPickerOption, + TagPickerGroup, +} from '@fluentui/react-tag-picker-preview'; +import { Tag, Avatar } from '@fluentui/react-components'; + +const options = [ + 'John Doe', + 'Jane Doe', + 'Max Mustermann', + 'Erika Mustermann', + 'Pierre Dupont', + 'Amelie Dupont', + 'Mario Rossi', + 'Maria Rossi', +]; + +export const Disabled = () => { + const [selectedOptions, setSelectedOptions] = React.useState([ + options[0], + options[1], + options[2], + options[3], + ]); + const onOptionSelect: TagPickerProps['onOptionSelect'] = (e, data) => { + setSelectedOptions(data.selectedOptions); + }; + + return ( +
+ + + + {selectedOptions.map(option => ( + } value={option}> + {option} + + ))} + + + + + {options + .filter(option => !selectedOptions.includes(option)) + .map(option => ( + } + value={option} + key={option} + > + {option} + + ))} + + +
+ ); +}; diff --git a/packages/react-components/react-tag-picker-preview/stories/TagPicker/index.stories.tsx b/packages/react-components/react-tag-picker-preview/stories/TagPicker/index.stories.tsx index a86f10913a8cf..c4b90a9b907e7 100644 --- a/packages/react-components/react-tag-picker-preview/stories/TagPicker/index.stories.tsx +++ b/packages/react-components/react-tag-picker-preview/stories/TagPicker/index.stories.tsx @@ -8,6 +8,7 @@ export { Button } from './TagPickerButton.stories'; export { Filtering } from './TagPickerFiltering.stories'; export { Size } from './TagPickerSize.stories'; export { Appearance } from './TagPickerAppearance.stories'; +export { Disabled } from './TagPickerDisabled.stories'; export default { title: 'Preview Components/Tag Picker',