-
Notifications
You must be signed in to change notification settings - Fork 2.9k
feat(react-tags): decouple useTagGroupBase_unstable from Tabster + export contexts #36228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "type": "minor", | ||
| "comment": "feat: export contexts for headless", | ||
| "packageName": "@fluentui/react-tags", | ||
| "email": "vgenaev@gmail.com", | ||
| "dependentChangeType": "patch" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,12 @@ import { useArrowNavigationGroup, useFocusFinders } from '@fluentui/react-tabste | |
| import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; | ||
| import { interactionTagSecondaryClassNames } from '../InteractionTagSecondary/useInteractionTagSecondaryStyles.styles'; | ||
| import type { TagValue } from '../../utils/types'; | ||
| import type { TabsterDOMAttribute } from '@fluentui/react-tabster'; | ||
|
|
||
| type UseTagGroupBaseOptions = { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why cant we extend BaseProps type with this and have it all passed through props without need to change hook api pattern (which we probably should not do) ? |
||
| arrowNavigationProps?: TabsterDOMAttribute; | ||
| onAfterTagDismiss?: (container: HTMLElement | null) => void; | ||
| }; | ||
|
|
||
| /** | ||
| * Create the base state required to render TagGroup, without design-only props. | ||
|
|
@@ -24,6 +30,7 @@ import type { TagValue } from '../../utils/types'; | |
| export const useTagGroupBase_unstable = ( | ||
| props: TagGroupBaseProps, | ||
| ref: React.Ref<HTMLDivElement>, | ||
| options?: UseTagGroupBaseOptions, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding third argument is an architecture change. I would like to avoid it
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good callout , do we have this api change in any of our existing base hooks ? if not this should be avoided
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. true, but it leaked from the similar PR #36087 and should be undone
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PR with a fix for useMenuTrigger #36237
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we will ship lint rule for this to prevent these regressions |
||
| ): TagGroupBaseState => { | ||
| const { | ||
| onDismiss, | ||
|
|
@@ -37,8 +44,6 @@ export const useTagGroupBase_unstable = ( | |
| } = props; | ||
|
|
||
| const innerRef = React.useRef<HTMLElement>(undefined); | ||
| const { targetDocument } = useFluent(); | ||
| const { findNextFocusable, findPrevFocusable } = useFocusFinders(); | ||
|
|
||
| const [items, setItems] = useControllableState<Array<TagValue>>({ | ||
| defaultState: defaultSelectedValues, | ||
|
|
@@ -48,26 +53,7 @@ export const useTagGroupBase_unstable = ( | |
|
|
||
| const handleTagDismiss: TagGroupBaseState['handleTagDismiss'] = useEventCallback((e, data) => { | ||
| onDismiss?.(e, data); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| // set focus after tag dismiss | ||
| const activeElement = targetDocument?.activeElement; | ||
| if (innerRef.current?.contains(activeElement as HTMLElement)) { | ||
| // focus on next tag only if the active element is within the current tag group | ||
| const next = findNextFocusable(activeElement as HTMLElement, { container: innerRef.current }); | ||
| if (next) { | ||
| next.focus(); | ||
| return; | ||
| } | ||
|
|
||
| // if there is no next focusable, focus on the previous focusable | ||
| if (activeElement?.className.includes(interactionTagSecondaryClassNames.root)) { | ||
| const prev = findPrevFocusable(activeElement.parentElement as HTMLElement, { container: innerRef.current }); | ||
| prev?.focus(); | ||
| } else { | ||
| const prev = findPrevFocusable(activeElement as HTMLElement, { container: innerRef.current }); | ||
| prev?.focus(); | ||
| } | ||
| } | ||
| options?.onAfterTagDismiss?.(innerRef.current ?? null); | ||
| }); | ||
|
|
||
| const handleTagSelect: TagGroupBaseState['handleTagSelect'] = useEventCallback( | ||
|
|
@@ -80,12 +66,6 @@ export const useTagGroupBase_unstable = ( | |
| }), | ||
| ); | ||
|
|
||
| const arrowNavigationProps = useArrowNavigationGroup({ | ||
| circular: true, | ||
| axis: 'both', | ||
| memorizeCurrent: true, | ||
| }); | ||
|
|
||
| return { | ||
| handleTagDismiss, | ||
| handleTagSelect: onTagSelect ? handleTagSelect : undefined, | ||
|
|
@@ -105,7 +85,7 @@ export const useTagGroupBase_unstable = ( | |
| ref: useMergedRefs(ref, innerRef) as React.Ref<HTMLDivElement>, | ||
| role, | ||
| 'aria-disabled': disabled, | ||
| ...arrowNavigationProps, | ||
| ...options?.arrowNavigationProps, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be just spreaded to |
||
| ...rest, | ||
| }), | ||
| { elementType: 'div' }, | ||
|
|
@@ -124,8 +104,39 @@ export const useTagGroupBase_unstable = ( | |
| */ | ||
| export const useTagGroup_unstable = (props: TagGroupProps, ref: React.Ref<HTMLDivElement>): TagGroupState => { | ||
| const { size = 'medium', appearance = 'filled' } = props; | ||
|
|
||
| const { targetDocument } = useFluent(); | ||
| const { findNextFocusable, findPrevFocusable } = useFocusFinders(); | ||
|
|
||
| const arrowNavigationProps = useArrowNavigationGroup({ | ||
| circular: true, | ||
| axis: 'both', | ||
| memorizeCurrent: true, | ||
| }); | ||
|
|
||
| const onAfterTagDismiss = useEventCallback((container: HTMLElement | null) => { | ||
| const activeElement = targetDocument?.activeElement; | ||
| if (container?.contains(activeElement as HTMLElement)) { | ||
| // focus on next tag only if the active element is within the current tag group | ||
| const next = findNextFocusable(activeElement as HTMLElement, { container }); | ||
| if (next) { | ||
| next.focus(); | ||
| return; | ||
| } | ||
|
|
||
| // if there is no next focusable, focus on the previous focusable | ||
| if (activeElement?.className.includes(interactionTagSecondaryClassNames.root)) { | ||
| const prev = findPrevFocusable(activeElement.parentElement as HTMLElement, { container }); | ||
| prev?.focus(); | ||
| } else { | ||
| const prev = findPrevFocusable(activeElement as HTMLElement, { container }); | ||
| prev?.focus(); | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| return { | ||
| ...useTagGroupBase_unstable(props, ref), | ||
| ...useTagGroupBase_unstable(props, ref, { arrowNavigationProps, onAfterTagDismiss }), | ||
| size, | ||
| appearance, | ||
| }; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π΅πΎββοΈ visual changes to review in the Visual Change Report
vr-tests-react-components/Avatar Converged 1 screenshots
vr-tests-react-components/Charts-DonutChart 1 screenshots
vr-tests-react-components/Menu Converged - submenuIndicator slotted content 2 screenshots
vr-tests-react-components/Positioning 2 screenshots
vr-tests-react-components/ProgressBar converged 2 screenshots
vr-tests-react-components/TagPicker 1 screenshots
There were 1 duplicate changes discarded. Check the build logs for more information.