Skip to content
Closed
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 @@
{
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕵🏾‍♀️ visual regressions to review in the fluentuiv9 Visual Regression Report

Avatar Converged 2 screenshots
Image Name Diff(in Pixels) Image Type
Avatar Converged.badgeMask.normal.chromium.png 2 Changed
Avatar Converged.basic - High Contrast.normal.chromium.png 0 Removed
SwatchPicker Converged 1 screenshots
Image Name Diff(in Pixels) Image Type
SwatchPicker Converged.default - Dark Mode.default.chromium.png 0 Removed

"type": "prerelease",
"comment": "chore: update types",
"packageName": "@fluentui/global-context",
"email": "olfedias@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "chore: update types",
"packageName": "@fluentui/react-accordion",
"email": "olfedias@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "chore: update types",
"packageName": "@fluentui/react-avatar",
"email": "olfedias@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "chore: update types",
"packageName": "@fluentui/react-combobox",
"email": "olfedias@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "chore: rework useContextSelector()",
"packageName": "@fluentui/react-context-selector",
"email": "olfedias@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "chore: update types",
"packageName": "@fluentui/react-dialog",
"email": "olfedias@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "chore: update types",
"packageName": "@fluentui/react-menu",
"email": "olfedias@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "chore: update types",
"packageName": "@fluentui/react-popover",
"email": "olfedias@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "chore: update types",
"packageName": "@fluentui/react-swatch-picker",
"email": "olfedias@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "chore: update types",
"packageName": "@fluentui/react-table",
"email": "olfedias@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "chore: update types",
"packageName": "@fluentui/react-tabs",
"email": "olfedias@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "chore: update types",
"packageName": "@fluentui/react-tree",
"email": "olfedias@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const createContext = <T>(defaultValue: T, name: string, packageName: str
const globalSymbols = Object.getOwnPropertySymbols(globalObject);
if (!globalSymbols.includes(sym)) {
// eslint-disable-next-line @fluentui/no-context-default-value
globalObject[sym] = baseCreateContext<unknown>(defaultValue);
globalObject[sym] = baseCreateContext<unknown>(defaultValue) as React.Context<unknown>;
}

return globalObject[sym] as React.Context<T>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import type { ARIAButtonSlotProps } from '@fluentui/react-aria';
import type { ComponentProps } from '@fluentui/react-utilities';
import type { ComponentState } from '@fluentui/react-utilities';
import { ContextSelector } from '@fluentui/react-context-selector';
import { FC } from 'react';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import { JSXElementConstructor } from 'react';
import type { PresenceMotionSlotProps } from '@fluentui/react-motion';
import { Provider } from 'react';
import { ProviderProps } from 'react';
import * as React_2 from 'react';
import { ReactElement } from 'react';
import type { Slot } from '@fluentui/react-utilities';
import type { SlotClassNames } from '@fluentui/react-utilities';

Expand Down Expand Up @@ -158,7 +158,7 @@ export type AccordionProps<Value = AccordionItemValue> = ComponentProps<Accordio
};

// @public (undocumented)
export const AccordionProvider: Provider<AccordionContextValue<unknown>> & FC<ProviderProps<AccordionContextValue<unknown>>>;
export const AccordionProvider: (props: ProviderProps<AccordionContextValue<unknown>>) => ReactElement<any, string | JSXElementConstructor<any>>;

// @public (undocumented)
export type AccordionSlots = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
import type { ComponentProps } from '@fluentui/react-utilities';
import type { ComponentState } from '@fluentui/react-utilities';
import { ContextSelector } from '@fluentui/react-context-selector';
import { FC } from 'react';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import { JSXElementConstructor } from 'react';
import type { PopoverProps } from '@fluentui/react-popover';
import type { PopoverSurface } from '@fluentui/react-popover';
import { PresenceBadge } from '@fluentui/react-badge';
import { Provider } from 'react';
import { ProviderProps } from 'react';
import * as React_2 from 'react';
import { ReactElement } from 'react';
import type { Slot } from '@fluentui/react-utilities';
import type { SlotClassNames } from '@fluentui/react-utilities';
import type { TooltipProps } from '@fluentui/react-tooltip';
Expand Down Expand Up @@ -113,7 +113,7 @@ export type AvatarGroupProps = ComponentProps<AvatarGroupSlots> & {
};

// @public (undocumented)
export const AvatarGroupProvider: Provider<AvatarGroupContextValue> & FC<ProviderProps<AvatarGroupContextValue>>;
export const AvatarGroupProvider: (props: ProviderProps<AvatarGroupContextValue>) => ReactElement<any, string | JSXElementConstructor<any>>;

// @public (undocumented)
export type AvatarGroupSlots = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import { ContextSelector } from '@fluentui/react-context-selector';
import { EventData } from '@fluentui/react-utilities';
import { EventHandler } from '@fluentui/react-utilities';
import type { ExtractSlotProps } from '@fluentui/react-utilities';
import { FC } from 'react';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import { JSXElementConstructor } from 'react';
import { PortalProps } from '@fluentui/react-portal';
import type { PositioningShorthand } from '@fluentui/react-positioning';
import { Provider } from 'react';
import { ProviderProps } from 'react';
import * as React_2 from 'react';
import { ReactElement } from 'react';
import type { Slot } from '@fluentui/react-utilities';
import { SlotClassNames } from '@fluentui/react-utilities';
import type { SlotComponentType } from '@fluentui/react-utilities';
Expand Down Expand Up @@ -87,7 +87,7 @@ export type ComboboxProps = Omit<ComponentProps<Partial<ComboboxSlots>, 'input'>
};

// @public @deprecated (undocumented)
export const ComboboxProvider: Provider<ComboboxContextValue> & FC<ProviderProps<ComboboxContextValue>>;
export const ComboboxProvider: (props: ProviderProps<ComboboxContextValue>) => ReactElement<any, string | JSXElementConstructor<any>>;

// @public (undocumented)
export type ComboboxSlots = {
Expand Down Expand Up @@ -162,7 +162,7 @@ export type ListboxProps = ComponentProps<ListboxSlots> & SelectionProps & {
};

// @public (undocumented)
export const ListboxProvider: React_2.Provider<ListboxContextValue | undefined> & React_2.FC<React_2.ProviderProps<ListboxContextValue | undefined>>;
export const ListboxProvider: (props: React_2.ProviderProps<ListboxContextValue | undefined>) => React_2.ReactElement<any, string | React_2.JSXElementConstructor<any>>;

// @public (undocumented)
export type ListboxSlots = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,20 @@
import * as React_2 from 'react';

// @internal (undocumented)
export type Context<Value> = React_2.Context<Value> & {
Provider: React_2.FC<React_2.ProviderProps<Value>>;
Consumer: never;
export type Context<Value> = Omit<React_2.Context<ContextValue<Value>>, 'Consumer' | 'Provider'> & {
Provider: (props: React_2.ProviderProps<Value>) => React_2.ReactElement;
};

// @public (undocumented)
export type ContextSelector<Value, SelectedValue> = (value: Value) => SelectedValue;

// @internal (undocumented)
export type ContextValue<Value> = {
listeners: ((payload: readonly [ContextVersion, Value]) => void)[];
value: React_2.MutableRefObject<Value>;
version: React_2.MutableRefObject<ContextVersion>;
value: Value;
subscribe: (listener: () => void) => () => void;
notify?: () => void;
};

// @internal (undocumented)
export type ContextValues<Value> = ContextValue<Value> & {
listeners: ((payload: readonly [ContextVersion, Record<string, Value>]) => void)[];
};

// @internal (undocumented)
export type ContextVersion = number;

// @internal (undocumented)
export const createContext: <Value>(defaultValue: Value) => Context<Value>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as ReactIs from 'react-is';
describe('createContext', () => {
it('creates a Provider component', () => {
const Context = createContext(null);

expect(ReactIs.isValidElementType(Context.Provider)).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -1,64 +1,74 @@
import { useIsomorphicLayoutEffect } from '@fluentui/react-utilities';
import * as React from 'react';
import { unstable_NormalPriority as NormalPriority, unstable_runWithPriority as runWithPriority } from 'scheduler';

import { Context, ContextValue } from './types';
import type { Context, ContextValue } from './types';

const createProvider = <Value>(Original: React.Provider<ContextValue<Value>>) => {
const Provider: React.FC<React.ProviderProps<Value>> = props => {
// Holds an actual "props.value"
const valueRef = React.useRef(props.value);
// Used to sync context updates and avoid stale values, can be considered as render/effect counter of Provider.
const versionRef = React.useRef(0);
'use no memo';

// A stable object, is used to avoid context updates via mutation of its values.
const contextValue = React.useRef<ContextValue<Value>>();
const [store] = React.useState(() => {
const listeners = new Set<Function>();

if (!contextValue.current) {
contextValue.current = {
value: valueRef,
version: versionRef,
listeners: [],
return {
value: props.value,

subscribe: (listener: Function) => {
listeners.add(listener);

return () => {
listeners.delete(listener);
};
},

notify: () => {
for (const listener of listeners) {
listener();
}
},
};
}
});

useIsomorphicLayoutEffect(() => {
valueRef.current = props.value;
versionRef.current += 1;
store.value = props.value;

runWithPriority(NormalPriority, () => {
(contextValue.current as ContextValue<Value>).listeners.forEach(listener => {
listener([versionRef.current, props.value]);
});
});
}, [props.value]);
useIsomorphicLayoutEffect(
() => {
// if (!Object.is(store.value, props.value)) {
store.value = props.value;
store.notify();
// }
},
// "store" is a constant object, so it's safe to omit it from the dependencies
// eslint-disable-next-line react-hooks/exhaustive-deps
[props.value],
);

return React.createElement(Original, { value: contextValue.current }, props.children);
return React.createElement(Original, { value: store }, props.children);
};

/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
Provider.displayName = 'ContextSelector.Provider';
}

return Provider as unknown as React.Provider<ContextValue<Value>>;
return Provider;
};

/**
* @internal
*/
export const createContext = <Value>(defaultValue: Value): Context<Value> => {
// eslint-disable-next-line @fluentui/no-context-default-value
const context = React.createContext<ContextValue<Value>>({
value: { current: defaultValue },
version: { current: -1 },
listeners: [],
const originalContext = React.createContext<ContextValue<Value>>({
value: defaultValue,
subscribe: () => () => {
/* noop */
},
});

context.Provider = createProvider<Value>(context.Provider);

// We don't support Consumer API
delete (context as unknown as Context<Value>).Consumer;

return context as unknown as Context<Value>;
return Object.assign(originalContext, {
Provider: createProvider<Value>(originalContext.Provider),
// We don't support Consumer API
Consumer: undefined,
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ export { createContext } from './createContext';
export { useContextSelector } from './useContextSelector';
export { useHasParentContext } from './useHasParentContext';
// eslint-disable-next-line @fluentui/ban-context-export
export type { Context, ContextSelector, ContextValue, ContextValues, ContextVersion } from './types';
export type { Context, ContextSelector, ContextValue } from './types';
30 changes: 6 additions & 24 deletions packages/react-components/react-context-selector/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,18 @@ import * as React from 'react';
/**
* @internal
*/
export type Context<Value> = React.Context<Value> & {
Provider: React.FC<React.ProviderProps<Value>>;
Consumer: never;
export type Context<Value> = Omit<React.Context<ContextValue<Value>>, 'Consumer' | 'Provider'> & {
// eslint-disable-next-line @typescript-eslint/naming-convention
Provider: (props: React.ProviderProps<Value>) => React.ReactElement;
};

export type ContextSelector<Value, SelectedValue> = (value: Value) => SelectedValue;

/**
* @internal
*/
export type ContextVersion = number;

/**
* @internal
*/
export type ContextValue<Value> = {
/** Holds a set of subscribers from components. */
listeners: ((payload: readonly [ContextVersion, Value]) => void)[];

/** Holds an actual value of React's context that will be propagated down for computations. */
value: React.MutableRefObject<Value>;

/** A version field is used to sync a context value and consumers. */
version: React.MutableRefObject<ContextVersion>;
};

/**
* @internal
*/
export type ContextValues<Value> = ContextValue<Value> & {
/** List of listners to publish changes */
listeners: ((payload: readonly [ContextVersion, Record<string, Value>]) => void)[];
value: Value;
subscribe: (listener: () => void) => () => void;
notify?: () => void;
};
Loading