Skip to content

Commit fad19e2

Browse files
committed
feat(react-headless-components-preview): add Provider component
1 parent 3d2d9bf commit fad19e2

File tree

11 files changed

+175
-0
lines changed

11 files changed

+175
-0
lines changed

packages/react-components/react-headless-components-preview/library/etc/react-headless-components-preview.api.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ import type { FieldBaseProps } from '@fluentui/react-field';
5252
import { FieldBaseState } from '@fluentui/react-field';
5353
import { FieldContextValues } from '@fluentui/react-field';
5454
import type { FieldSlots as FieldSlots_2 } from '@fluentui/react-field';
55+
import type { FluentProviderContextValues } from '@fluentui/react-provider';
56+
import type { FluentProviderProps } from '@fluentui/react-provider';
57+
import type { FluentProviderState } from '@fluentui/react-provider';
5558
import type { ForwardRefComponent } from '@fluentui/react-utilities';
5659
import type { InputBaseProps } from '@fluentui/react-input';
5760
import { InputBaseState } from '@fluentui/react-input';
@@ -382,6 +385,15 @@ export type ProgressBarSlots = ProgressBarSlots_2;
382385
// @public
383386
export type ProgressBarState = ProgressBarBaseState;
384387

388+
// @public (undocumented)
389+
export const Provider: React_2.ForwardRefExoticComponent<ProviderProps & React_2.RefAttributes<HTMLDivElement>>;
390+
391+
// @public
392+
export type ProviderProps = Omit<FluentProviderProps, 'applyStylesToPortals' | 'theme' | 'customStyleHooks_unstable' | 'overrides_unstable'>;
393+
394+
// @public
395+
export type ProviderState = Omit<FluentProviderState, 'applyStylesToPortals' | 'theme' | 'themeClassName' | 'customStyleHooks_unstable' | 'overrides_unstable' | 'serverStyleProps'>;
396+
385397
// @public
386398
export const Radio: React_2.ForwardRefExoticComponent<Omit<ComponentProps<Partial<RadioSlots_2>, "input">, "onChange" | "size"> & {
387399
value?: string;
@@ -512,6 +524,9 @@ export const renderLink: (state: LinkBaseState) => JSXElement;
512524
// @public
513525
export const renderProgressBar: (state: ProgressBarState) => JSXElement;
514526

527+
// @public
528+
export const renderProvider: (state: ProviderState, contextValues: FluentProviderContextValues) => JSXElement;
529+
515530
// @public
516531
export const renderRadio: (state: RadioBaseState) => JSXElement;
517532

@@ -819,6 +834,9 @@ export const useLink: (props: LinkProps, ref: React_2.Ref<HTMLElement>) => LinkS
819834
// @public
820835
export const useProgressBar: (props: ProgressBarProps, ref: React_2.Ref<HTMLDivElement>) => ProgressBarState;
821836

837+
// @public (undocumented)
838+
export const useProvider: (props: ProviderProps, ref: React_2.Ref<HTMLDivElement>) => ProviderState;
839+
822840
// @public
823841
export const useRadio: (props: RadioProps, ref: React_2.Ref<HTMLInputElement>) => RadioState;
824842

packages/react-components/react-headless-components-preview/library/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@fluentui/react-link": "^9.8.0",
3535
"@fluentui/react-persona": "^9.7.1",
3636
"@fluentui/react-progress": "^9.4.17",
37+
"@fluentui/react-provider": "^9.22.15",
3738
"@fluentui/react-radio": "^9.6.0",
3839
"@fluentui/react-rating": "^9.4.0",
3940
"@fluentui/react-search": "^9.4.0",
@@ -45,6 +46,7 @@
4546
"@fluentui/react-spinner": "^9.8.0",
4647
"@fluentui/react-switch": "^9.7.0",
4748
"@fluentui/react-tabs": "^9.11.2",
49+
"@fluentui/react-tabster": "^9.26.13",
4850
"@fluentui/react-tags": "^9.7.19",
4951
"@fluentui/react-textarea": "^9.7.0",
5052
"@fluentui/react-utilities": "^9.26.2",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { Provider, renderProvider, useProvider } from './components/Provider/index';
2+
export type { ProviderProps, ProviderState } from './components/Provider/index';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import * as React from 'react';
2+
import { render } from '@testing-library/react';
3+
import { isConformant } from '../../testing/isConformant';
4+
import { Provider } from './Provider';
5+
6+
describe('Provider', () => {
7+
isConformant({
8+
Component: Provider,
9+
displayName: 'Provider',
10+
});
11+
12+
it('renders a default state', () => {
13+
const result = render(
14+
<Provider>
15+
<div>Content</div>
16+
</Provider>,
17+
);
18+
expect(result.container).toHaveTextContent('Content');
19+
expect(result.container.firstChild).toHaveAttribute('dir', 'ltr');
20+
});
21+
22+
it('renders with custom dir', () => {
23+
const result = render(
24+
<Provider dir="rtl">
25+
<div>Content</div>
26+
</Provider>,
27+
);
28+
expect(result.container).toHaveTextContent('Content');
29+
expect(result.container.firstChild).toHaveAttribute('dir', 'rtl');
30+
});
31+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use client';
2+
3+
import * as React from 'react';
4+
5+
import { renderProvider } from './renderProvider';
6+
import { useProvider } from './useProvider';
7+
import type { ProviderProps } from './Provider.types';
8+
import { useProviderContextValues } from './useProviderContextValues';
9+
10+
/**
11+
* Renders required context providers for Fluent Headless Components.
12+
*/
13+
export const Provider = React.forwardRef<HTMLDivElement, ProviderProps>((props, ref) => {
14+
const state = useProvider(props, ref);
15+
const contextValues = useProviderContextValues(state);
16+
17+
return renderProvider(state, contextValues);
18+
});
19+
20+
Provider.displayName = 'Provider';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type {
2+
FluentProviderContextValues,
3+
FluentProviderSlots,
4+
FluentProviderProps,
5+
FluentProviderState,
6+
} from '@fluentui/react-provider';
7+
8+
export type ProviderSlots = FluentProviderSlots;
9+
10+
/**
11+
* Provider Props
12+
*/
13+
export type ProviderProps = Omit<
14+
FluentProviderProps,
15+
'applyStylesToPortals' | 'theme' | 'customStyleHooks_unstable' | 'overrides_unstable'
16+
>;
17+
18+
/**
19+
* State used in rendering Provider
20+
*/
21+
export type ProviderState = Omit<
22+
FluentProviderState,
23+
| 'applyStylesToPortals'
24+
| 'theme'
25+
| 'themeClassName'
26+
| 'customStyleHooks_unstable'
27+
| 'overrides_unstable'
28+
| 'serverStyleProps'
29+
>;
30+
31+
export type ProviderContextValues = FluentProviderContextValues;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export { Provider } from './Provider';
2+
export type { ProviderProps, ProviderState } from './Provider.types';
3+
export { renderProvider } from './renderProvider';
4+
export { useProvider } from './useProvider';
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/** @jsxRuntime automatic */
2+
/** @jsxImportSource @fluentui/react-jsx-runtime */
3+
4+
import { assertSlots } from '@fluentui/react-utilities';
5+
import type { JSXElement } from '@fluentui/react-utilities';
6+
import {
7+
Provider_unstable as Provider,
8+
TooltipVisibilityProvider_unstable as TooltipVisibilityProvider,
9+
} from '@fluentui/react-shared-contexts';
10+
import type { FluentProviderContextValues, FluentProviderSlots } from '@fluentui/react-provider';
11+
import type { ProviderState } from './Provider.types';
12+
13+
/**
14+
* Render the final JSX of Provider
15+
*/
16+
export const renderProvider = (state: ProviderState, contextValues: FluentProviderContextValues): JSXElement => {
17+
assertSlots<FluentProviderSlots>(state);
18+
19+
return (
20+
<Provider value={contextValues.provider}>
21+
<TooltipVisibilityProvider value={contextValues.tooltip}>
22+
<state.root />
23+
</TooltipVisibilityProvider>
24+
</Provider>
25+
);
26+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use client';
2+
3+
import type * as React from 'react';
4+
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
5+
import { useFocusVisible } from '@fluentui/react-tabster';
6+
import { getIntrinsicElementProps, slot, useMergedRefs } from '@fluentui/react-utilities';
7+
8+
import type { ProviderProps, ProviderState } from './Provider.types';
9+
10+
export const useProvider = (props: ProviderProps, ref: React.Ref<HTMLDivElement>): ProviderState => {
11+
const parentContext = useFluent();
12+
13+
const { dir = parentContext.dir, targetDocument = parentContext.targetDocument } = props;
14+
15+
return {
16+
dir,
17+
targetDocument,
18+
components: {
19+
root: 'div',
20+
},
21+
root: slot.always(
22+
getIntrinsicElementProps('div', {
23+
...props,
24+
dir,
25+
ref: useMergedRefs(ref, useFocusVisible<HTMLDivElement>({ targetDocument })),
26+
}),
27+
{ elementType: 'div' },
28+
),
29+
};
30+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use client';
2+
3+
import { useFluentProviderContextValues_unstable as useFluentProviderContextValues } from '@fluentui/react-provider';
4+
import type { ProviderContextValues, ProviderState } from './Provider.types';
5+
6+
export const useProviderContextValues = useFluentProviderContextValues as (
7+
state: ProviderState,
8+
) => ProviderContextValues;

0 commit comments

Comments
 (0)