Skip to content

Commit

Permalink
feature: introduces noPopover property to TagPicker
Browse files Browse the repository at this point in the history
  • Loading branch information
bsunderhus committed Aug 1, 2024
1 parent bf7891f commit 7cb9ad0
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feature: introduces noPopover property to TagPicker",
"packageName": "@fluentui/react-tag-picker",
"email": "bernardo.sunderhus@gmail.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ export type TagPickerOptionState = ComponentState<TagPickerOptionSlots> & Pick<O

// @public
export type TagPickerProps = ComponentProps<TagPickerSlots> & Pick<ComboboxProps, 'positioning' | 'disabled' | 'defaultOpen' | 'selectedOptions' | 'defaultSelectedOptions' | 'open'> & Pick<Partial<TagPickerContextValue>, 'size' | 'appearance'> & {
noPopover?: boolean;
onOpenChange?: EventHandler<TagPickerOnOpenChangeData>;
onOptionSelect?: EventHandler<TagPickerOnOptionSelectData>;
children: [JSX.Element, JSX.Element] | JSX.Element;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ export type TagPickerProps = ComponentProps<TagPickerSlots> &
'positioning' | 'disabled' | 'defaultOpen' | 'selectedOptions' | 'defaultSelectedOptions' | 'open'
> &
Pick<Partial<TagPickerContextValue>, 'size' | 'appearance'> & {
/**
* By default, when a single children is provided, the TagPicker will assume that the children
* is a popover. By setting this prop to true, the children will be treated as a trigger instead.
*
* @default false
*/
noPopover?: boolean;
onOpenChange?: EventHandler<TagPickerOnOpenChangeData>;
onOptionSelect?: EventHandler<TagPickerOnOptionSelectData>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const useTagPicker_unstable = (props: TagPickerProps): TagPickerState =>
const triggerInnerRef = React.useRef<HTMLInputElement | HTMLButtonElement>(null);
const secondaryActionRef = React.useRef<HTMLSpanElement>(null);
const tagPickerGroupRef = React.useRef<HTMLDivElement>(null);
const { positioning, size = 'medium', inline = false } = props;
const { positioning, size = 'medium', inline = false, noPopover = false } = props;

const { targetRef, containerRef } = usePositioning({
position: 'below' as const,
Expand Down Expand Up @@ -69,7 +69,7 @@ export const useTagPicker_unstable = (props: TagPickerProps): TagPickerState =>
size: 'medium',
});

const { trigger, popover } = childrenToTriggerAndPopover(props.children);
const { trigger, popover } = childrenToTriggerAndPopover(props.children, noPopover);

return {
activeDescendantController,
Expand Down Expand Up @@ -105,18 +105,18 @@ export const useTagPicker_unstable = (props: TagPickerProps): TagPickerState =>
};
};

const childrenToTriggerAndPopover = (children?: React.ReactNode) => {
const childrenToTriggerAndPopover = (children: React.ReactNode, noPopover: boolean) => {
const childrenArray = React.Children.toArray(children) as React.ReactElement[];

if (process.env.NODE_ENV !== 'production') {
if (childrenArray.length === 0) {
// eslint-disable-next-line no-console
console.warn('Picker must contain at least one child');
console.warn('TagPicker must contain at least one child');
}

if (childrenArray.length > 2) {
// eslint-disable-next-line no-console
console.warn('Picker must contain at most two children');
console.warn('TagPicker must contain at most two children');
}
}

Expand All @@ -126,7 +126,11 @@ const childrenToTriggerAndPopover = (children?: React.ReactNode) => {
trigger = childrenArray[0];
popover = childrenArray[1];
} else if (childrenArray.length === 1) {
popover = childrenArray[0];
if (noPopover) {
trigger = childrenArray[0];
} else {
popover = childrenArray[0];
}
}
return { trigger, popover };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as React from 'react';
import {
TagPicker,
TagPickerInput,
TagPickerControl,
TagPickerProps,
TagPickerGroup,
} from '@fluentui/react-components';
import { Tag, Avatar, Field } from '@fluentui/react-components';

export const NoPopover = () => {
const [selectedOptions, setSelectedOptions] = React.useState<string[]>([]);

const onOptionSelect: TagPickerProps['onOptionSelect'] = (_, data) => {
setSelectedOptions(data.selectedOptions);
};
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(event.currentTarget.value);
};
const handleKeyDown = (event: React.KeyboardEvent) => {
if (event.key === 'Enter' && inputValue) {
setInputValue('');
setSelectedOptions(curr => [...curr, inputValue]);
}
};

const [inputValue, setInputValue] = React.useState('');

return (
<Field label="Add Employees" style={{ maxWidth: 400 }}>
<TagPicker noPopover onOptionSelect={onOptionSelect} selectedOptions={selectedOptions}>
<TagPickerControl expandIcon={null}>
<TagPickerGroup>
{selectedOptions.map((option, index) => (
<Tag
key={index}
shape="rounded"
media={<Avatar aria-hidden name={option} color="colorful" />}
value={option}
>
{option}
</Tag>
))}
</TagPickerGroup>
<TagPickerInput
value={inputValue}
onChange={handleChange}
onKeyDown={handleKeyDown}
aria-label="Add Employees"
/>
</TagPickerControl>
</TagPicker>
</Field>
);
};

NoPopover.parameters = {
docs: {
description: {
story: `
You can use the \`TagPicker\` without the popover with the list of options by providing the \`noPopover\` property. This is useful when you want to allow users to input their own tags. All you have to do is control the \`TagPickerInput\` value and handle the \`onKeyDown\` event to add the tag to the \`TagPicker\` when the user presses the Enter key.
`,
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export { SecondaryAction } from './TagPickerSecondaryAction.stories';
export { Grouped } from './TagPickerGrouped.stories';
export { TruncatedText } from './TagPickerTruncatedText.stories';
export { SingleSelect } from './TagPickerSingleSelect.stories';
export { NoPopover } from './TagPickerNoPopover.stories';

export default {
title: 'Components/TagPicker',
Expand Down

0 comments on commit 7cb9ad0

Please sign in to comment.