Skip to content

Commit 2556ac2

Browse files
dmytrokirpaclaude
andcommitted
feat(react-tag-picker): add base hooks for TagPicker components
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 89bba45 commit 2556ac2

23 files changed

+383
-88
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "feat: add base hooks for TagPicker components",
4+
"packageName": "@fluentui/react-tag-picker",
5+
"email": "dmytrokirpa@microsoft.com",
6+
"dependentChangeType": "patch"
7+
}

packages/react-components/react-tag-picker/library/etc/react-tag-picker.api.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { ComboboxState } from '@fluentui/react-combobox';
1212
import { ComponentProps } from '@fluentui/react-utilities';
1313
import { ComponentState } from '@fluentui/react-utilities';
1414
import { ContextSelector } from '@fluentui/react-context-selector';
15+
import type { DistributiveOmit } from '@fluentui/react-utilities';
1516
import { DropdownProps } from '@fluentui/react-combobox';
1617
import type { EventData } from '@fluentui/react-utilities';
1718
import type { EventHandler } from '@fluentui/react-utilities';
@@ -28,6 +29,7 @@ import { OptionState } from '@fluentui/react-combobox';
2829
import * as React_2 from 'react';
2930
import { Slot } from '@fluentui/react-utilities';
3031
import type { SlotClassNames } from '@fluentui/react-utilities';
32+
import type { TagGroupBaseState } from '@fluentui/react-tags';
3133
import { TagGroupContextValues } from '@fluentui/react-tags';
3234
import type { TagGroupSlots } from '@fluentui/react-tags';
3335
import type { TagGroupState } from '@fluentui/react-tags';
@@ -59,9 +61,21 @@ export const renderTagPickerOptionGroup: (state: TagPickerOptionGroupState) => J
5961
// @public
6062
export const TagPicker: React_2.FC<TagPickerProps>;
6163

64+
// @public
65+
export type TagPickerBaseProps = DistributiveOmit<TagPickerProps, 'size' | 'appearance'>;
66+
67+
// @public
68+
export type TagPickerBaseState = DistributiveOmit<TagPickerState, 'size' | 'appearance'>;
69+
6270
// @public
6371
export const TagPickerButton: ForwardRefComponent<TagPickerButtonProps>;
6472

73+
// @public
74+
export type TagPickerButtonBaseProps = DistributiveOmit<TagPickerButtonProps, 'size' | 'appearance'>;
75+
76+
// @public
77+
export type TagPickerButtonBaseState = DistributiveOmit<TagPickerButtonState, 'size'>;
78+
6579
// @public (undocumented)
6680
export const tagPickerButtonClassNames: SlotClassNames<TagPickerButtonSlots>;
6781

@@ -110,6 +124,12 @@ export type TagPickerContextValues = {
110124
// @public
111125
export const TagPickerControl: ForwardRefComponent<TagPickerControlProps>;
112126

127+
// @public
128+
export type TagPickerControlBaseProps = TagPickerControlProps;
129+
130+
// @public
131+
export type TagPickerControlBaseState = DistributiveOmit<TagPickerControlState, 'size' | 'appearance'>;
132+
113133
// @public (undocumented)
114134
export const tagPickerControlClassNames: SlotClassNames<TagPickerControlSlots & TagPickerControlInternalSlots>;
115135

@@ -132,6 +152,14 @@ export type TagPickerControlState = ComponentState<TagPickerControlSlots & TagPi
132152
// @public
133153
export const TagPickerGroup: ForwardRefComponent<TagPickerGroupProps>;
134154

155+
// @public
156+
export type TagPickerGroupBaseProps = TagPickerGroupProps;
157+
158+
// @public
159+
export type TagPickerGroupBaseState = TagGroupBaseState & {
160+
hasSelectedOptions: boolean;
161+
};
162+
135163
// @public (undocumented)
136164
export const tagPickerGroupClassNames: SlotClassNames<TagPickerGroupSlots>;
137165

@@ -149,6 +177,12 @@ export type TagPickerGroupState = TagGroupState & {
149177
// @public
150178
export const TagPickerInput: ForwardRefComponent<TagPickerInputProps>;
151179

180+
// @public
181+
export type TagPickerInputBaseProps = DistributiveOmit<TagPickerInputProps, 'appearance'>;
182+
183+
// @public
184+
export type TagPickerInputBaseState = DistributiveOmit<TagPickerInputState, 'size'>;
185+
152186
// @public (undocumented)
153187
export const tagPickerInputClassNames: SlotClassNames<TagPickerInputSlots>;
154188

@@ -262,9 +296,15 @@ export type TagPickerState = ComponentState<TagPickerSlots> & Pick<ComboboxState
262296
// @public
263297
export const useTagPicker_unstable: (props: TagPickerProps) => TagPickerState;
264298

299+
// @public
300+
export const useTagPickerBase_unstable: (props: TagPickerBaseProps) => TagPickerBaseState;
301+
265302
// @public
266303
export const useTagPickerButton_unstable: (props: TagPickerButtonProps, ref: React_2.Ref<HTMLButtonElement>) => TagPickerButtonState;
267304

305+
// @public
306+
export const useTagPickerButtonBase_unstable: (props: TagPickerButtonBaseProps, ref: React_2.Ref<HTMLButtonElement>) => TagPickerButtonBaseState;
307+
268308
// @public
269309
export const useTagPickerButtonStyles_unstable: (state: TagPickerButtonState) => TagPickerButtonState;
270310

@@ -274,6 +314,9 @@ export const useTagPickerContext_unstable: <T>(selector: ContextSelector<TagPick
274314
// @public
275315
export const useTagPickerControl_unstable: (props: TagPickerControlProps, ref: React_2.Ref<HTMLDivElement>) => TagPickerControlState;
276316

317+
// @public
318+
export const useTagPickerControlBase_unstable: (props: TagPickerControlBaseProps, ref: React_2.Ref<HTMLDivElement>) => TagPickerControlBaseState;
319+
277320
// @public
278321
export const useTagPickerControlStyles_unstable: (state: TagPickerControlState) => TagPickerControlState;
279322

@@ -283,12 +326,18 @@ export function useTagPickerFilter({ filter: filterOverride, noOptionsElement, r
283326
// @public
284327
export const useTagPickerGroup_unstable: (props: TagPickerGroupProps, ref: React_2.Ref<HTMLDivElement>) => TagPickerGroupState;
285328

329+
// @public
330+
export const useTagPickerGroupBase_unstable: (props: TagPickerGroupBaseProps, ref: React_2.Ref<HTMLDivElement>) => TagPickerGroupBaseState;
331+
286332
// @public
287333
export const useTagPickerGroupStyles_unstable: (state: TagPickerGroupState) => TagPickerGroupState;
288334

289335
// @public
290336
export const useTagPickerInput_unstable: (propsArg: TagPickerInputProps, ref: React_2.Ref<HTMLInputElement>) => TagPickerInputState;
291337

338+
// @public
339+
export const useTagPickerInputBase_unstable: (propsArg: TagPickerInputBaseProps, ref: React_2.Ref<HTMLInputElement>) => TagPickerInputBaseState;
340+
292341
// @public
293342
export const useTagPickerInputStyles_unstable: (state: TagPickerInputState) => TagPickerInputState;
294343

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export type {
2+
TagPickerBaseProps,
3+
TagPickerBaseState,
24
TagPickerContextValues,
35
TagPickerOnOpenChangeData,
46
TagPickerOnOptionSelectData,
@@ -7,4 +9,9 @@ export type {
79
TagPickerSlots,
810
TagPickerState,
911
} from './components/TagPicker/index';
10-
export { TagPicker, renderTagPicker_unstable, useTagPicker_unstable } from './components/TagPicker/index';
12+
export {
13+
TagPicker,
14+
renderTagPicker_unstable,
15+
useTagPickerBase_unstable,
16+
useTagPicker_unstable,
17+
} from './components/TagPicker/index';

packages/react-components/react-tag-picker/library/src/TagPickerButton.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export type {
2+
TagPickerButtonBaseProps,
3+
TagPickerButtonBaseState,
24
TagPickerButtonProps,
35
TagPickerButtonSlots,
46
TagPickerButtonState,
@@ -8,5 +10,6 @@ export {
810
renderTagPickerButton_unstable,
911
tagPickerButtonClassNames,
1012
useTagPickerButtonStyles_unstable,
13+
useTagPickerButtonBase_unstable,
1114
useTagPickerButton_unstable,
1215
} from './components/TagPickerButton/index';

packages/react-components/react-tag-picker/library/src/TagPickerControl.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export type {
2+
TagPickerControlBaseProps,
3+
TagPickerControlBaseState,
24
TagPickerControlCSSProperties,
35
TagPickerControlInternalSlots,
46
TagPickerControlProps,
@@ -12,5 +14,6 @@ export {
1214
tagPickerControlAsideWidthToken,
1315
tagPickerControlClassNames,
1416
useTagPickerControlStyles_unstable,
17+
useTagPickerControlBase_unstable,
1518
useTagPickerControl_unstable,
1619
} from './components/TagPickerControl/index';
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
export type { TagPickerGroupProps, TagPickerGroupSlots, TagPickerGroupState } from './components/TagPickerGroup/index';
1+
export type {
2+
TagPickerGroupBaseProps,
3+
TagPickerGroupBaseState,
4+
TagPickerGroupProps,
5+
TagPickerGroupSlots,
6+
TagPickerGroupState,
7+
} from './components/TagPickerGroup/index';
28
export {
39
TagPickerGroup,
410
renderTagPickerGroup_unstable,
511
tagPickerGroupClassNames,
612
useTagPickerGroupStyles_unstable,
13+
useTagPickerGroupBase_unstable,
714
useTagPickerGroup_unstable,
815
} from './components/TagPickerGroup/index';
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
export type { TagPickerInputProps, TagPickerInputSlots, TagPickerInputState } from './components/TagPickerInput/index';
1+
export type {
2+
TagPickerInputBaseProps,
3+
TagPickerInputBaseState,
4+
TagPickerInputProps,
5+
TagPickerInputSlots,
6+
TagPickerInputState,
7+
} from './components/TagPickerInput/index';
28
export {
39
TagPickerInput,
410
renderTagPickerInput_unstable,
511
tagPickerInputClassNames,
612
useTagPickerInputStyles_unstable,
13+
useTagPickerInputBase_unstable,
714
useTagPickerInput_unstable,
815
} from './components/TagPickerInput/index';

packages/react-components/react-tag-picker/library/src/components/TagPicker/TagPicker.types.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import type * as React from 'react';
2-
import type { ComponentProps, ComponentState, EventData, EventHandler, JSXElement } from '@fluentui/react-utilities';
2+
import type {
3+
ComponentProps,
4+
ComponentState,
5+
DistributiveOmit,
6+
EventData,
7+
EventHandler,
8+
JSXElement,
9+
} from '@fluentui/react-utilities';
310
import type { ComboboxProps, ComboboxState, ListboxContextValue } from '@fluentui/react-combobox';
411
import type { TagPickerContextValue } from '../../contexts/TagPickerContext';
512
import type { ActiveDescendantContextValue } from '@fluentui/react-aria';
@@ -106,3 +113,13 @@ export type TagPickerContextValues = {
106113
activeDescendant: ActiveDescendantContextValue;
107114
listbox: ListboxContextValue;
108115
};
116+
117+
/**
118+
* TagPicker Base Props - omits design-only props
119+
*/
120+
export type TagPickerBaseProps = DistributiveOmit<TagPickerProps, 'size' | 'appearance'>;
121+
122+
/**
123+
* TagPicker Base State - omits design-only state
124+
*/
125+
export type TagPickerBaseState = DistributiveOmit<TagPickerState, 'size' | 'appearance'>;

packages/react-components/react-tag-picker/library/src/components/TagPicker/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export { TagPicker } from './TagPicker';
22
export type {
3+
TagPickerBaseProps,
4+
TagPickerBaseState,
35
TagPickerContextValues,
46
TagPickerOnOpenChangeData,
57
TagPickerOnOptionSelectData,
@@ -9,4 +11,4 @@ export type {
911
TagPickerState,
1012
} from './TagPicker.types';
1113
export { renderTagPicker_unstable } from './renderTagPicker';
12-
export { useTagPicker_unstable } from './useTagPicker';
14+
export { useTagPickerBase_unstable, useTagPicker_unstable } from './useTagPicker';

packages/react-components/react-tag-picker/library/src/components/TagPicker/useTagPicker.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import * as React from 'react';
44
import { elementContains, useEventCallback, useId, useMergedRefs } from '@fluentui/react-utilities';
55
import type {
6+
TagPickerBaseProps,
7+
TagPickerBaseState,
68
TagPickerOnOpenChangeData,
79
TagPickerOnOptionSelectData,
810
TagPickerProps,
@@ -17,19 +19,16 @@ import { useComboboxBaseState } from '@fluentui/react-combobox';
1719
const fallbackPositions: PositioningShorthandValue[] = ['above', 'after', 'after-top', 'before', 'before-top'];
1820

1921
/**
20-
* Create the state required to render Picker.
21-
*
22-
* The returned state can be modified with hooks such as usePickerStyles_unstable,
23-
* before being passed to renderPicker_unstable.
22+
* Create the base state required to render TagPicker, without design-only props.
2423
*
25-
* @param props - props from this instance of Picker
24+
* @param props - props from this instance of TagPicker (without size, appearance)
2625
*/
27-
export const useTagPicker_unstable = (props: TagPickerProps): TagPickerState => {
26+
export const useTagPickerBase_unstable = (props: TagPickerBaseProps): TagPickerBaseState => {
2827
const popoverId = useId('picker-listbox');
2928
const triggerInnerRef = React.useRef<HTMLInputElement | HTMLButtonElement>(null);
3029
const secondaryActionRef = React.useRef<HTMLSpanElement>(null);
3130
const tagPickerGroupRef = React.useRef<HTMLDivElement>(null);
32-
const { positioning, size = 'medium', inline = false, noPopover = false, disableAutoFocus } = props;
31+
const { positioning, inline = false, noPopover = false, disableAutoFocus } = props;
3332

3433
const { targetRef, containerRef } = usePositioning({
3534
position: 'below' as const,
@@ -86,15 +85,13 @@ export const useTagPicker_unstable = (props: TagPickerProps): TagPickerState =>
8685
secondaryActionRef,
8786
tagPickerGroupRef,
8887
targetRef,
89-
size,
9088
inline,
9189
open: comboboxState.open,
9290
mountNode: comboboxState.mountNode,
9391
onOptionClick: useEventCallback(event => {
9492
comboboxState.onOptionClick(event);
9593
comboboxState.setOpen(event, false);
9694
}),
97-
appearance: comboboxState.appearance,
9895
clearSelection: comboboxState.clearSelection,
9996
getOptionById: comboboxState.getOptionById,
10097
getOptionsMatchingValue: comboboxState.getOptionsMatchingValue,
@@ -124,6 +121,23 @@ export const useTagPicker_unstable = (props: TagPickerProps): TagPickerState =>
124121
};
125122
};
126123

124+
/**
125+
* Create the state required to render Picker.
126+
*
127+
* The returned state can be modified with hooks such as usePickerStyles_unstable,
128+
* before being passed to renderPicker_unstable.
129+
*
130+
* @param props - props from this instance of Picker
131+
*/
132+
export const useTagPicker_unstable = (props: TagPickerProps): TagPickerState => {
133+
const { size = 'medium', appearance = 'outline' } = props;
134+
return {
135+
...useTagPickerBase_unstable(props),
136+
size,
137+
appearance,
138+
};
139+
};
140+
127141
const childrenToTriggerAndPopover = (children: React.ReactNode, noPopover: boolean) => {
128142
const childrenArray = React.Children.toArray(children) as React.ReactElement[];
129143

0 commit comments

Comments
 (0)