Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "refactor: use shared MotionRefForwarder from react-motion",
"packageName": "@fluentui/react-dialog",
"email": "robertpenner@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "refactor: use shared MotionRefForwarder from react-motion",
"packageName": "@fluentui/react-message-bar",
"email": "robertpenner@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: export MotionRefForwarder and useMotionForwardedRef for shared motion ref forwarding",
"packageName": "@fluentui/react-motion",
"email": "robertpenner@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { assertSlots } from '@fluentui/react-utilities';
import type { JSXElement } from '@fluentui/react-utilities';
import * as React from 'react';

import { MotionRefForwarder } from '../MotionRefForwarder';
import { MotionRefForwarder } from '@fluentui/react-motion';
import { DialogProvider, DialogSurfaceProvider } from '../../contexts';
import type { DialogState, DialogContextValues, InternalDialogSlots } from './Dialog.types';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import * as React from 'react';
import { useDialogContext_unstable, useDialogBackdropContext_unstable } from '../../contexts';
import { useDisableBodyScroll } from '../../utils/useDisableBodyScroll';
import { DialogBackdropMotion } from '../DialogBackdropMotion';
import { useMotionForwardedRef } from '../MotionRefForwarder';
import { useMotionForwardedRef } from '@fluentui/react-motion';
import type { DialogSurfaceElement, DialogSurfaceProps, DialogSurfaceState } from './DialogSurface.types';

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { MessageBarProps, MessageBarState } from './MessageBar.types';
import { getIntentIcon } from './getIntentIcon';
import { useMessageBarReflow } from './useMessageBarReflow';
import { useMessageBarTransitionContext } from '../../contexts/messageBarTransitionContext';
import { useMotionForwardedRef } from '../MotionRefForwarder';
import { useMotionForwardedRef } from '@fluentui/react-motion';

/**
* Create the state required to render MessageBar.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { JSXElement } from '@fluentui/react-utilities';
import type { MessageBarGroupState, MessageBarGroupSlots } from './MessageBarGroup.types';
import { PresenceGroup } from '@fluentui/react-motion';
import { MessageBarMotion } from './MessageBarGroup.motions';
import { MotionRefForwarder } from '../MotionRefForwarder';
import { MotionRefForwarder } from '@fluentui/react-motion';

/**
* Render the final JSX of MessageBarGroup
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ export type MotionImperativeRef = {
// @public
export type MotionParam = boolean | number | string;

// @internal
export const MotionRefForwarder: React_2.ForwardRefExoticComponent<{
children: React_2.ReactElement;
} & React_2.RefAttributes<HTMLElement>>;

// @public (undocumented)
export const motionTokens: {
curveAccelerateMax: "cubic-bezier(0.9,0.1,1,0.2)";
Expand Down Expand Up @@ -179,6 +184,9 @@ export type PresenceMotionSlotProps<MotionParams extends Record<string, MotionPa
}>;
};

// @internal
export function useMotionForwardedRef(): React_2.Ref<HTMLElement> | undefined;

// @public (undocumented)
export const usePresenceGroupChildContext: () => PresenceGroupChildContextValue | undefined;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { render } from '@testing-library/react';
import * as React from 'react';

import { MotionRefForwarder, useMotionForwardedRef } from './MotionRefForwarder';

const TestConsumer: React.FC = () => {
const ref = useMotionForwardedRef();

return <div data-testid="consumer" ref={ref as React.Ref<HTMLDivElement>} />;
};

describe('MotionRefForwarder', () => {
it('should provide a ref to children via context', () => {
const ref = React.createRef<HTMLElement>();

const { getByTestId } = render(
<MotionRefForwarder ref={ref}>
<TestConsumer />
</MotionRefForwarder>,
);

expect(getByTestId('consumer')).toBe(ref.current);
});

it('should provide undefined when not wrapped in MotionRefForwarder', () => {
let capturedRef: React.Ref<HTMLElement> | undefined;

const RefCapture: React.FC = () => {
capturedRef = useMotionForwardedRef();
return null;
};

render(<RefCapture />);

expect(capturedRef).toBeUndefined();
});

it('should forward callback refs', () => {
const callbackRef = jest.fn();

const { getByTestId } = render(
<MotionRefForwarder ref={callbackRef}>
<TestConsumer />
</MotionRefForwarder>,
);

const element = getByTestId('consumer');
// The callback ref is passed via context to TestConsumer, which applies it to its div.
expect(callbackRef).toHaveBeenCalledWith(element);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import * as React from 'react';
const MotionRefForwarderContext = React.createContext<React.Ref<HTMLElement> | undefined>(undefined);

/**
* A hook that reads the ref forwarded by `MotionRefForwarder` from context.
* Used in child components to merge the motion ref into the root slot ref.
*
* @internal
*/
export function useMotionForwardedRef(): React.Ref<HTMLElement> | undefined {
Expand All @@ -13,9 +16,13 @@ export function useMotionForwardedRef(): React.Ref<HTMLElement> | undefined {

/**
* A component that forwards a ref to its children via a React context.
* This is used to pass a motion component's ref through to the actual surface element,
* since motion components wrap their children and the ref needs to reach the inner element.
*
* @internal
*/
export const MotionRefForwarder = React.forwardRef<HTMLElement, { children: React.ReactElement }>((props, ref) => {
return <MotionRefForwarderContext.Provider value={ref}>{props.children}</MotionRefForwarderContext.Provider>;
});

MotionRefForwarder.displayName = 'MotionRefForwarder';
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export {
export { createPresenceComponentVariant } from './factories/createPresenceComponentVariant';

export { PresenceGroup } from './components/PresenceGroup';
export { MotionRefForwarder, useMotionForwardedRef } from './components/MotionRefForwarder';

export { presenceMotionSlot, type PresenceMotionSlotProps } from './slots/presenceMotionSlot';

Expand Down