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
11 changes: 10 additions & 1 deletion apps/vr-tests-react-components/src/stories/Input.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { storiesOf } from '@storybook/react';
import { Input } from '@fluentui/react-input';
import { SearchRegular, DismissRegular } from '@fluentui/react-icons';
import { TestWrapperDecoratorFixedWidth } from '../utilities/TestWrapperDecorator';
import { FluentProvider } from '@fluentui/react-provider';

storiesOf('Input Converged', module)
.addDecorator(TestWrapperDecoratorFixedWidth)
Expand Down Expand Up @@ -65,4 +66,12 @@ storiesOf('Input Converged', module)
'contentAfter',
() => <Input contentAfter={<DismissRegular style={{ fontSize: '20px' }} />} placeholder="Placeholder" />,
{ includeRtl: true },
);
)
.addStory('With appearance override', () => (
<FluentProvider overrides_unstable={{ inputDefaultAppearance: 'filled-darker' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
<Input placeholder="Default overriden appearance" />
<Input appearance="outline" placeholder="Outline appearance" />
</div>
</FluentProvider>
));
13 changes: 13 additions & 0 deletions apps/vr-tests-react-components/src/stories/Select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Steps, StoryWright } from 'storywright';
import { storiesOf } from '@storybook/react';
import { Select } from '@fluentui/react-select';
import { TestWrapperDecoratorFixedWidth } from '../utilities/TestWrapperDecorator';
import { FluentProvider } from '@fluentui/react-provider';

storiesOf('Select Converged', module)
.addDecorator(TestWrapperDecoratorFixedWidth)
Expand Down Expand Up @@ -102,4 +103,16 @@ storiesOf('Select Converged', module)
<Select icon="+">
<option>text</option>
</Select>
))
.addStory('With appearance override', () => (
<FluentProvider overrides_unstable={{ inputDefaultAppearance: 'filled-darker' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
<Select>
<option>Default overriden appearance</option>
</Select>
<Select appearance="outline">
<option>Outline appearance</option>
</Select>
</div>
</FluentProvider>
));
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Steps, StoryWright } from 'storywright';
import { storiesOf } from '@storybook/react';
import { SpinButton, spinButtonClassNames } from '@fluentui/react-spinbutton';
import { TestWrapperDecoratorFixedWidth } from '../utilities/TestWrapperDecorator';
import { FluentProvider } from '@fluentui/react-provider';

const cropTo = '.testWrapper';

Expand Down Expand Up @@ -172,4 +173,12 @@ storiesOf('SpinButton Converged', module)
includeHighContrast: true,
includeDarkMode: true,
})
.addStory('Size: medium (default)', () => <SpinButton value={10} />, { includeRtl: true });
.addStory('Size: medium (default)', () => <SpinButton value={10} />, { includeRtl: true })
.addStory('With appearance override', () => (
<FluentProvider overrides_unstable={{ inputDefaultAppearance: 'filled-darker' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
<SpinButton value={10} />
<SpinButton value={10} appearance="outline" />
</div>
</FluentProvider>
));
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Steps, StoryWright } from 'storywright';
import { storiesOf } from '@storybook/react';
import { Textarea } from '@fluentui/react-textarea';
import { TestWrapperDecoratorFixedWidth } from '../utilities/TestWrapperDecorator';
import { FluentProvider } from '@fluentui/react-provider';

storiesOf('Textarea Converged', module)
.addDecorator(TestWrapperDecoratorFixedWidth)
Expand Down Expand Up @@ -47,4 +48,12 @@ storiesOf('Textarea Converged', module)
))
.addStory('Size: small', () => <Textarea size="small" placeholder="Placeholder" />)
.addStory('Size: medium', () => <Textarea size="medium" placeholder="Placeholder" />)
.addStory('Size: large', () => <Textarea size="large" placeholder="Placeholder" />);
.addStory('Size: large', () => <Textarea size="large" placeholder="Placeholder" />)
.addStory('With appearance override', () => (
<FluentProvider overrides_unstable={{ inputDefaultAppearance: 'filled-darker' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
<Textarea placeholder="Default overriden appearance" />
<Textarea appearance="outline" placeholder="Outline appearance" />
</div>
</FluentProvider>
));
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: add support for appearance overrides (unstable)",
"packageName": "@fluentui/react-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "chore: fix a typo in jsdoc",
"packageName": "@fluentui/react-context-selector",
"email": "[email protected]",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: Allow default appearance override",
"packageName": "@fluentui/react-input",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "Lint based re-format",
"packageName": "@fluentui/react-portal",
"email": "[email protected]",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: add support for overrides (unstable)",
"packageName": "@fluentui/react-provider",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "feat: Allow default appearance override",
"packageName": "@fluentui/react-select",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: Overrides context",
"packageName": "@fluentui/react-shared-contexts",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: Allow default appearance override",
"packageName": "@fluentui/react-spinbutton",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: Allow default appearance override",
"packageName": "@fluentui/react-textarea",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ try {
felaDevMode = !!window.localStorage.felaDevMode;
} catch {}

if (typeof window !== "undefined" && process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
if (typeof window !== 'undefined' && process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
if (felaDevMode) {
/* eslint-disable-next-line no-console */
console.warn(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type ContextReducer<Value, SelectedValue> = React.Reducer<
* @internal
* This hook returns context selected value by selector.
* It will only accept context created by `createContext`.
* It will trigger re-render if only the selected value is referencially changed.
* It will trigger re-render if only the selected value is referentially changed.
*/
export const useContextSelector = <Value, SelectedValue>(
context: Context<Value>,
Expand Down
1 change: 1 addition & 0 deletions packages/react-components/react-input/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
},
"dependencies": {
"@fluentui/react-field": "9.0.0-alpha.16",
"@fluentui/react-shared-contexts": "^9.1.5",
"@fluentui/react-theme": "^9.1.5",
"@fluentui/react-utilities": "^9.4.0",
"@griffel/react": "^1.5.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
useEventCallback,
} from '@fluentui/react-utilities';
import type { InputProps, InputState } from './Input.types';
import { useOverrides_unstable as useOverrides } from '@fluentui/react-shared-contexts';

/**
* Create the state required to render Input.
Expand All @@ -17,7 +18,9 @@ import type { InputProps, InputState } from './Input.types';
* @param ref - reference to `<input>` element of Input
*/
export const useInput_unstable = (props: InputProps, ref: React.Ref<HTMLInputElement>): InputState => {
const { size = 'medium', appearance = 'outline', onChange } = props;
const overrides = useOverrides();

const { size = 'medium', appearance = overrides.inputDefaultAppearance ?? 'outline', onChange } = props;

if (
process.env.NODE_ENV !== 'production' &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export const usePortal_unstable = (props: PortalProps): PortalState => {
setVirtualParent(state.mountNode, state.virtualParentRootRef.current);
}
return () => {
if (state.mountNode) {setVirtualParent(state.mountNode, undefined);}
if (state.mountNode) {
setVirtualParent(state.mountNode, undefined);
}
};
}, [state.virtualParentRootRef, state.mountNode]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { ComponentProps } from '@fluentui/react-utilities';
import type { ComponentState } from '@fluentui/react-utilities';
import { OverridesContextValue_unstable } from '@fluentui/react-shared-contexts';
import type { PartialTheme } from '@fluentui/react-theme';
import type { ProviderContextValue_unstable } from '@fluentui/react-shared-contexts';
import * as React_2 from 'react';
Expand All @@ -22,13 +23,14 @@ export const FluentProvider: React_2.ForwardRefExoticComponent<Omit<ComponentPro
dir?: "ltr" | "rtl" | undefined;
targetDocument?: Document | undefined;
theme?: Partial<Theme> | undefined;
overrides_unstable?: OverridesContextValue_unstable | undefined;
} & React_2.RefAttributes<HTMLElement>>;

// @public (undocumented)
export const fluentProviderClassNames: SlotClassNames<FluentProviderSlots>;

// @public (undocumented)
export type FluentProviderContextValues = Pick<FluentProviderState, 'theme'> & {
export type FluentProviderContextValues = Pick<FluentProviderState, 'theme' | 'overrides_unstable'> & {
provider: ProviderContextValue_unstable;
themeClassName: ThemeClassNameContextValue_unstable;
textDirection: 'ltr' | 'rtl';
Expand All @@ -41,6 +43,7 @@ export type FluentProviderProps = Omit<ComponentProps<FluentProviderSlots>, 'dir
dir?: 'ltr' | 'rtl';
targetDocument?: Document;
theme?: PartialTheme;
overrides_unstable?: OverridesContextValue_unstable;
};

// @public (undocumented)
Expand All @@ -49,7 +52,7 @@ export type FluentProviderSlots = {
};

// @public (undocumented)
export type FluentProviderState = ComponentState<FluentProviderSlots> & Pick<FluentProviderProps, 'targetDocument'> & Required<Pick<FluentProviderProps, 'applyStylesToPortals' | 'dir'>> & {
export type FluentProviderState = ComponentState<FluentProviderSlots> & Pick<FluentProviderProps, 'targetDocument'> & Required<Pick<FluentProviderProps, 'applyStylesToPortals' | 'dir' | 'overrides_unstable'>> & {
theme: ThemeContextValue_unstable;
themeClassName: string;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {
OverridesContextValue_unstable as OverridesContextValue,
ProviderContextValue_unstable as ProviderContextValue,
TooltipVisibilityContextValue_unstable as TooltipVisibilityContextValue,
ThemeClassNameContextValue_unstable as ThemeClassNameContextValue,
Expand Down Expand Up @@ -26,16 +27,19 @@ export type FluentProviderProps = Omit<ComponentProps<FluentProviderSlots>, 'dir

/** Sets the theme used in a scope. */
theme?: PartialTheme;

// eslint-disable-next-line @typescript-eslint/naming-convention
overrides_unstable?: OverridesContextValue;
};

export type FluentProviderState = ComponentState<FluentProviderSlots> &
Pick<FluentProviderProps, 'targetDocument'> &
Required<Pick<FluentProviderProps, 'applyStylesToPortals' | 'dir'>> & {
Required<Pick<FluentProviderProps, 'applyStylesToPortals' | 'dir' | 'overrides_unstable'>> & {
theme: ThemeContextValue;
themeClassName: string;
};

export type FluentProviderContextValues = Pick<FluentProviderState, 'theme'> & {
export type FluentProviderContextValues = Pick<FluentProviderState, 'theme' | 'overrides_unstable'> & {
provider: ProviderContextValue;
themeClassName: ThemeClassNameContextValue;
textDirection: 'ltr' | 'rtl';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { TextDirectionProvider } from '@griffel/react';
import {
OverridesProvider_unstable as OverridesProvider,
Provider_unstable as Provider,
TooltipVisibilityProvider_unstable as TooltipVisibilityProvider,
ThemeProvider_unstable as ThemeProvider,
Expand All @@ -24,7 +25,9 @@ export const renderFluentProvider_unstable = (
<ThemeClassNameProvider value={contextValues.themeClassName}>
<TooltipVisibilityProvider value={contextValues.tooltip}>
<TextDirectionProvider dir={contextValues.textDirection}>
<slots.root {...slotProps.root}>{state.root.children}</slots.root>
<OverridesProvider value={contextValues.overrides_unstable}>
<slots.root {...slotProps.root}>{state.root.children}</slots.root>
</OverridesProvider>
</TextDirectionProvider>
</TooltipVisibilityProvider>
</ThemeClassNameProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as React from 'react';
import { FluentProvider } from './FluentProvider';
import { useFluentProvider_unstable } from './useFluentProvider';
import type { PartialTheme } from '@fluentui/react-theme';
import { OverridesContextValue_unstable } from '@fluentui/react-shared-contexts';

describe('useFluentProvider_unstable', () => {
// eslint-disable-next-line @typescript-eslint/no-empty-function
Expand Down Expand Up @@ -50,4 +51,34 @@ describe('useFluentProvider_unstable', () => {
}
`);
});

it('should merge overrides', () => {
const overridesA: OverridesContextValue_unstable = {
inputDefaultAppearance: 'filled-lighter',
// currently the overrides object contains a single value, adding one more to test the merging
customValue: 'shouldNotBeOverridden',
} as OverridesContextValue_unstable;
const overridesB: OverridesContextValue_unstable = {
inputDefaultAppearance: 'filled-darker',
};

const Wrapper: React.FC = ({ children }) => (
<FluentProvider overrides_unstable={overridesA}>{children}</FluentProvider>
);

const { result } = renderHook(
// eslint-disable-next-line @typescript-eslint/naming-convention
() => useFluentProvider_unstable({ overrides_unstable: overridesB }, React.createRef()),
{
wrapper: Wrapper,
},
);

expect(result.current.overrides_unstable).toMatchInlineSnapshot(`
Object {
"customValue": "shouldNotBeOverridden",
"inputDefaultAppearance": "filled-darker",
}
`);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useFocusVisible } from '@fluentui/react-tabster';
import {
ThemeContext_unstable as ThemeContext,
useFluent_unstable as useFluent,
useOverrides_unstable as useOverrides,
} from '@fluentui/react-shared-contexts';
import type { ThemeContextValue_unstable as ThemeContextValue } from '@fluentui/react-shared-contexts';
import { getNativeElementProps, useMergedRefs } from '@fluentui/react-utilities';
Expand All @@ -24,6 +25,7 @@ export const useFluentProvider_unstable = (
): FluentProviderState => {
const parentContext = useFluent();
const parentTheme = useTheme();
const parentOverrides = useOverrides();

/**
* TODO: add merge functions to "dir" merge,
Expand All @@ -35,8 +37,11 @@ export const useFluentProvider_unstable = (
dir = parentContext.dir,
targetDocument = parentContext.targetDocument,
theme,
overrides_unstable: overrides = {},
} = props;
const mergedTheme = mergeThemes(parentTheme, theme);
const mergedTheme = shallowMerge(parentTheme, theme);

const mergedOverrides = shallowMerge(parentOverrides, overrides);

React.useEffect(() => {
if (process.env.NODE_ENV !== 'production' && mergedTheme === undefined) {
Expand All @@ -55,6 +60,8 @@ export const useFluentProvider_unstable = (
dir,
targetDocument,
theme: mergedTheme,
// eslint-disable-next-line @typescript-eslint/naming-convention
overrides_unstable: mergedOverrides,
themeClassName: useFluentProviderThemeStyleTag({ theme: mergedTheme, targetDocument }),

components: {
Expand All @@ -69,7 +76,7 @@ export const useFluentProvider_unstable = (
};
};

function mergeThemes(a: ThemeContextValue, b: ThemeContextValue): ThemeContextValue {
function shallowMerge<T>(a: T, b: T): T {
// Merge impacts perf: we should like to avoid it if it's possible
if (a && b) {
return { ...a, ...b };
Expand Down
Loading