From bd9677eb419fab03ef6d70618965e66d40b5b030 Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Thu, 6 Jun 2024 22:03:54 +0200 Subject: [PATCH 1/8] chore(react-utilities): slot API react v17/18 support --- ...-8f78fc18-0062-4b31-943b-49d1f8fb792c.json | 7 + ...-40e91e26-ec95-4c11-8598-3e6bf2be277d.json | 7 + ...-12473640-484d-4699-a36c-5d9e9c7a1622.json | 7 + ...-625c3222-142a-430e-b5de-4b0d6ecd800c.json | 7 + ...-e5bd1559-048d-4423-8339-c1ef4cd8aa29.json | 7 + ...-0dbfdec5-c535-461c-979a-dedf2e792f13.json | 7 + ...-b21a0221-810f-4b38-b928-13e54251c56b.json | 7 + ...-536a8e63-1156-4ebc-8595-e72351ff0d29.json | 7 + ...-ba1e922d-1d50-4b25-97af-f776b995023a.json | 7 + ...-199bf58f-2421-486a-bcf1-535db8cb98cf.json | 7 + ...-852e0ed2-030e-44aa-b7f5-c16ea6283aa2.json | 7 + .../react-aria/library/etc/react-aria.api.md | 5 +- .../react-aria/library/src/button/types.ts | 7 +- .../src/button/useARIAButtonShorthand.ts | 14 +- .../etc/react-datepicker-compat.api.md | 2 +- .../components/DatePicker/DatePicker.types.ts | 4 +- .../components/DatePicker/useDatePicker.tsx | 10 +- .../src/createElement.test.tsx | 2 + .../react-jsx-runtime/src/interop.test.tsx | 3 + .../src/jsx-runtime.test.tsx | 2 + .../src/jsx/createElementFromSlotComponent.ts | 5 +- .../react-jsx-runtime/src/jsx/jsxDEVSlot.ts | 5 +- .../react-jsx-runtime/src/jsx/jsxSlot.ts | 5 +- .../react-jsx-runtime/src/jsx/jsxsSlot.ts | 5 +- .../src/utils/getMetadataFromSlotComponent.ts | 7 +- .../react-jsx-runtime/src/utils/types.ts | 16 ++- .../src/components/MenuItem/useMenuItem.tsx | 15 ++- .../etc/react-migration-v0-v9.api.md | 8 +- .../etc/react-migration-v8-v9.api.md | 2 +- .../TableCellLayout/useTableCellLayout.ts | 3 +- .../library/src/components/Tab/useTab.ts | 6 +- .../ToastContainer/useToastContainer.ts | 1 + .../ToolbarGroup/useToolbarGroup.ts | 2 +- .../etc/react-utilities.api.md | 61 ++++----- .../src/compose/deprecated/getSlots.ts | 8 +- .../compose/deprecated/getSlotsNext.test.tsx | 1 + .../src/compose/deprecated/getSlotsNext.ts | 8 +- .../compose/deprecated/resolveShorthand.ts | 19 +-- .../src/compose/getIntrinsicElementProps.ts | 6 +- .../src/compose/isResolvedShorthand.ts | 4 +- .../react-utilities/src/compose/slot.ts | 18 ++- .../react-utilities/src/compose/types.ts | 123 +++++++++++------- .../react-utilities/src/index.ts | 1 + .../trigger/applyTriggerPropsToChildren.ts | 2 +- .../react-utilities/src/utils/types.ts | 56 ++++++++ 45 files changed, 366 insertions(+), 147 deletions(-) create mode 100644 change/@fluentui-react-aria-8f78fc18-0062-4b31-943b-49d1f8fb792c.json create mode 100644 change/@fluentui-react-datepicker-compat-40e91e26-ec95-4c11-8598-3e6bf2be277d.json create mode 100644 change/@fluentui-react-jsx-runtime-12473640-484d-4699-a36c-5d9e9c7a1622.json create mode 100644 change/@fluentui-react-menu-625c3222-142a-430e-b5de-4b0d6ecd800c.json create mode 100644 change/@fluentui-react-migration-v0-v9-e5bd1559-048d-4423-8339-c1ef4cd8aa29.json create mode 100644 change/@fluentui-react-migration-v8-v9-0dbfdec5-c535-461c-979a-dedf2e792f13.json create mode 100644 change/@fluentui-react-table-b21a0221-810f-4b38-b928-13e54251c56b.json create mode 100644 change/@fluentui-react-tabs-536a8e63-1156-4ebc-8595-e72351ff0d29.json create mode 100644 change/@fluentui-react-toast-ba1e922d-1d50-4b25-97af-f776b995023a.json create mode 100644 change/@fluentui-react-toolbar-199bf58f-2421-486a-bcf1-535db8cb98cf.json create mode 100644 change/@fluentui-react-utilities-852e0ed2-030e-44aa-b7f5-c16ea6283aa2.json diff --git a/change/@fluentui-react-aria-8f78fc18-0062-4b31-943b-49d1f8fb792c.json b/change/@fluentui-react-aria-8f78fc18-0062-4b31-943b-49d1f8fb792c.json new file mode 100644 index 00000000000000..1403653d7f3a89 --- /dev/null +++ b/change/@fluentui-react-aria-8f78fc18-0062-4b31-943b-49d1f8fb792c.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: slot API react v17/18 support", + "packageName": "@fluentui/react-aria", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-datepicker-compat-40e91e26-ec95-4c11-8598-3e6bf2be277d.json b/change/@fluentui-react-datepicker-compat-40e91e26-ec95-4c11-8598-3e6bf2be277d.json new file mode 100644 index 00000000000000..edf33f876a4283 --- /dev/null +++ b/change/@fluentui-react-datepicker-compat-40e91e26-ec95-4c11-8598-3e6bf2be277d.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: slot API react v17/18 support", + "packageName": "@fluentui/react-datepicker-compat", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-jsx-runtime-12473640-484d-4699-a36c-5d9e9c7a1622.json b/change/@fluentui-react-jsx-runtime-12473640-484d-4699-a36c-5d9e9c7a1622.json new file mode 100644 index 00000000000000..10deaee299fa5c --- /dev/null +++ b/change/@fluentui-react-jsx-runtime-12473640-484d-4699-a36c-5d9e9c7a1622.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: slot API react v17/18 support", + "packageName": "@fluentui/react-jsx-runtime", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-menu-625c3222-142a-430e-b5de-4b0d6ecd800c.json b/change/@fluentui-react-menu-625c3222-142a-430e-b5de-4b0d6ecd800c.json new file mode 100644 index 00000000000000..033f99fc8f6f75 --- /dev/null +++ b/change/@fluentui-react-menu-625c3222-142a-430e-b5de-4b0d6ecd800c.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: slot API react v17/18 support", + "packageName": "@fluentui/react-menu", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-migration-v0-v9-e5bd1559-048d-4423-8339-c1ef4cd8aa29.json b/change/@fluentui-react-migration-v0-v9-e5bd1559-048d-4423-8339-c1ef4cd8aa29.json new file mode 100644 index 00000000000000..532068cc9d04b8 --- /dev/null +++ b/change/@fluentui-react-migration-v0-v9-e5bd1559-048d-4423-8339-c1ef4cd8aa29.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: slot API react v17/18 support", + "packageName": "@fluentui/react-migration-v0-v9", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-migration-v8-v9-0dbfdec5-c535-461c-979a-dedf2e792f13.json b/change/@fluentui-react-migration-v8-v9-0dbfdec5-c535-461c-979a-dedf2e792f13.json new file mode 100644 index 00000000000000..b64f6fd7cc2dea --- /dev/null +++ b/change/@fluentui-react-migration-v8-v9-0dbfdec5-c535-461c-979a-dedf2e792f13.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: slot API react v17/18 support", + "packageName": "@fluentui/react-migration-v8-v9", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-table-b21a0221-810f-4b38-b928-13e54251c56b.json b/change/@fluentui-react-table-b21a0221-810f-4b38-b928-13e54251c56b.json new file mode 100644 index 00000000000000..26a546220a4758 --- /dev/null +++ b/change/@fluentui-react-table-b21a0221-810f-4b38-b928-13e54251c56b.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: slot API react v17/18 support", + "packageName": "@fluentui/react-table", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-tabs-536a8e63-1156-4ebc-8595-e72351ff0d29.json b/change/@fluentui-react-tabs-536a8e63-1156-4ebc-8595-e72351ff0d29.json new file mode 100644 index 00000000000000..b6d7ee4594564a --- /dev/null +++ b/change/@fluentui-react-tabs-536a8e63-1156-4ebc-8595-e72351ff0d29.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: slot API react v17/18 support", + "packageName": "@fluentui/react-tabs", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-toast-ba1e922d-1d50-4b25-97af-f776b995023a.json b/change/@fluentui-react-toast-ba1e922d-1d50-4b25-97af-f776b995023a.json new file mode 100644 index 00000000000000..d935266f701ec7 --- /dev/null +++ b/change/@fluentui-react-toast-ba1e922d-1d50-4b25-97af-f776b995023a.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: slot API react v17/18 support", + "packageName": "@fluentui/react-toast", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-toolbar-199bf58f-2421-486a-bcf1-535db8cb98cf.json b/change/@fluentui-react-toolbar-199bf58f-2421-486a-bcf1-535db8cb98cf.json new file mode 100644 index 00000000000000..1768e5829fcfea --- /dev/null +++ b/change/@fluentui-react-toolbar-199bf58f-2421-486a-bcf1-535db8cb98cf.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: slot API react v17/18 support", + "packageName": "@fluentui/react-toolbar", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-utilities-852e0ed2-030e-44aa-b7f5-c16ea6283aa2.json b/change/@fluentui-react-utilities-852e0ed2-030e-44aa-b7f5-c16ea6283aa2.json new file mode 100644 index 00000000000000..5130827b8da53c --- /dev/null +++ b/change/@fluentui-react-utilities-852e0ed2-030e-44aa-b7f5-c16ea6283aa2.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "chore: slot API react v17/18 support", + "packageName": "@fluentui/react-utilities", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-aria/library/etc/react-aria.api.md b/packages/react-components/react-aria/library/etc/react-aria.api.md index 7ae0174c42a8b1..0c1aed6549f588 100644 --- a/packages/react-components/react-aria/library/etc/react-aria.api.md +++ b/packages/react-components/react-aria/library/etc/react-aria.api.md @@ -5,6 +5,7 @@ ```ts import type { AnnounceContextValue } from '@fluentui/react-shared-contexts'; +import type { DistributiveOmit } from '@fluentui/react-utilities'; import type { ExtractSlotProps } from '@fluentui/react-utilities'; import * as React_2 from 'react'; import type { ResolveShorthandFunction } from '@fluentui/react-utilities'; @@ -67,7 +68,7 @@ export type ARIAButtonElement = H export type ARIAButtonElementIntersection = UnionToIntersection>; // @public -export type ARIAButtonProps = React_2.PropsWithRef & { +export type ARIAButtonProps = DistributiveOmit, 'children'> & { disabled?: boolean; disabledFocusable?: boolean; }; @@ -108,7 +109,7 @@ export const useActiveDescendantContext: () => ActiveDescendantContextValue; export function useARIAButtonProps>(type?: Type, props?: Props): ARIAButtonResultProps; // @internal @deprecated (undocumented) -export const useARIAButtonShorthand: ResolveShorthandFunction; +export const useARIAButtonShorthand: ResolveShorthandFunction; // @public (undocumented) export const useAriaLiveAnnouncer_unstable: (props: AriaLiveAnnouncerProps) => AriaLiveAnnouncerState; diff --git a/packages/react-components/react-aria/library/src/button/types.ts b/packages/react-components/react-aria/library/src/button/types.ts index 478b894dadb0c1..8e0ae86460c03b 100644 --- a/packages/react-components/react-aria/library/src/button/types.ts +++ b/packages/react-components/react-aria/library/src/button/types.ts @@ -1,4 +1,4 @@ -import type { ExtractSlotProps, Slot, UnionToIntersection } from '@fluentui/react-utilities'; +import type { DistributiveOmit, ExtractSlotProps, Slot, UnionToIntersection } from '@fluentui/react-utilities'; import * as React from 'react'; export type ARIAButtonType = 'button' | 'a' | 'div'; @@ -18,8 +18,9 @@ export type ARIAButtonElementIntersection = React.PropsWithRef< - JSX.IntrinsicElements[Type] +export type ARIAButtonProps = DistributiveOmit< + React.PropsWithRef, + 'children' > & { disabled?: boolean; /** diff --git a/packages/react-components/react-aria/library/src/button/useARIAButtonShorthand.ts b/packages/react-components/react-aria/library/src/button/useARIAButtonShorthand.ts index b5b18bae1c3146..4cdcdb1093bd30 100644 --- a/packages/react-components/react-aria/library/src/button/useARIAButtonShorthand.ts +++ b/packages/react-components/react-aria/library/src/button/useARIAButtonShorthand.ts @@ -1,7 +1,7 @@ import { resolveShorthand } from '@fluentui/react-utilities'; import { useARIAButtonProps } from './useARIAButtonProps'; import type { ResolveShorthandFunction } from '@fluentui/react-utilities'; -import type { ARIAButtonProps, ARIAButtonSlotProps, ARIAButtonType } from './types'; +import type { ARIAButtonProps, ARIAButtonType } from './types'; /** * @internal @@ -14,10 +14,14 @@ import type { ARIAButtonProps, ARIAButtonSlotProps, ARIAButtonType } from './typ * for multiple scenarios of shorthand properties. Ensuring 1st rule of ARIA for cases * where no attribute addition is required. */ -// eslint-disable-next-line deprecation/deprecation -export const useARIAButtonShorthand: ResolveShorthandFunction = (value, options) => { +// eslint-disable-next-line deprecation/deprecation, @typescript-eslint/no-explicit-any +export const useARIAButtonShorthand: ResolveShorthandFunction = ((value, options) => { // eslint-disable-next-line deprecation/deprecation const shorthand = resolveShorthand(value, options); - const shorthandARIAButton = useARIAButtonProps(shorthand?.as ?? 'button', shorthand); + const shorthandARIAButton = useARIAButtonProps( + shorthand?.as ?? 'button', + shorthand as ARIAButtonProps, + ); return shorthand && shorthandARIAButton; -}; + // eslint-disable-next-line deprecation/deprecation, @typescript-eslint/no-explicit-any +}) as ResolveShorthandFunction; diff --git a/packages/react-components/react-datepicker-compat/library/etc/react-datepicker-compat.api.md b/packages/react-components/react-datepicker-compat/library/etc/react-datepicker-compat.api.md index 495068c61bae03..31abc94e4f5775 100644 --- a/packages/react-components/react-datepicker-compat/library/etc/react-datepicker-compat.api.md +++ b/packages/react-components/react-datepicker-compat/library/etc/react-datepicker-compat.api.md @@ -6,7 +6,7 @@ /// -import type { CalendarProps } from '@fluentui/react-calendar-compat'; +import type { Calendar } from '@fluentui/react-calendar-compat'; import { CalendarStrings } from '@fluentui/react-calendar-compat'; import type { ComponentProps } from '@fluentui/react-utilities'; import type { ComponentState } from '@fluentui/react-utilities'; diff --git a/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/DatePicker.types.ts b/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/DatePicker.types.ts index 2b6b11b75ee202..55d780701f5d80 100644 --- a/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/DatePicker.types.ts +++ b/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/DatePicker.types.ts @@ -1,13 +1,13 @@ import { DayOfWeek, FirstWeekOfYear } from '@fluentui/react-calendar-compat'; import { Input } from '@fluentui/react-input'; import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; -import type { CalendarProps, CalendarStrings, DateFormatting } from '@fluentui/react-calendar-compat'; +import type { Calendar, CalendarStrings, DateFormatting } from '@fluentui/react-calendar-compat'; import type { PortalProps } from '@fluentui/react-portal'; import type { PositioningProps } from '@fluentui/react-positioning'; export type DatePickerSlots = { root: NonNullable>; - calendar: NonNullable>>; + calendar: NonNullable>; popupSurface?: Slot<'div'>; }; diff --git a/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/useDatePicker.tsx b/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/useDatePicker.tsx index 0da92ebd934a75..c4a112b7955532 100644 --- a/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/useDatePicker.tsx +++ b/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/useDatePicker.tsx @@ -13,13 +13,19 @@ import { useOnClickOutside, useOnScrollOutside, slot, + ExtractSlotProps, } from '@fluentui/react-utilities'; import { useFieldContext_unstable as useFieldContext } from '@fluentui/react-field'; import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; import { useModalAttributes } from '@fluentui/react-tabster'; import { usePopupPositioning } from '../../utils/usePopupPositioning'; import type { CalendarProps, ICalendar } from '@fluentui/react-calendar-compat'; -import type { DatePickerProps, DatePickerState, DatePickerValidationResultData } from './DatePicker.types'; +import type { + DatePickerProps, + DatePickerSlots, + DatePickerState, + DatePickerValidationResultData, +} from './DatePicker.types'; import type { InputProps, InputOnChangeData } from '@fluentui/react-input'; function isDateOutOfBounds(date: Date, minDate?: Date, maxDate?: Date): boolean { @@ -450,7 +456,7 @@ export const useDatePicker_unstable = (props: DatePickerProps, ref: React.Ref>(props.calendar, { defaultProps: { allFocusable, componentRef: calendar, diff --git a/packages/react-components/react-jsx-runtime/src/createElement.test.tsx b/packages/react-components/react-jsx-runtime/src/createElement.test.tsx index 59cd16ff8b906e..44b82244e2733b 100644 --- a/packages/react-components/react-jsx-runtime/src/createElement.test.tsx +++ b/packages/react-components/react-jsx-runtime/src/createElement.test.tsx @@ -70,6 +70,7 @@ describe('createElement with getSlotsNext', () => { describe('custom behavior tests', () => { it('keeps children from "defaultProps" in a render callback', () => { type TestComponentSlots = { slot: Slot<'div'> }; + // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; type TestComponentProps = ComponentProps>; @@ -113,6 +114,7 @@ describe('createElement with getSlotsNext', () => { it('keeps children from a render template in a render callback', () => { type TestComponentSlots = { outer: Slot<'div'>; inner: Slot<'div'> }; + // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; type TestComponentProps = ComponentProps>; diff --git a/packages/react-components/react-jsx-runtime/src/interop.test.tsx b/packages/react-components/react-jsx-runtime/src/interop.test.tsx index d6c46a016b4ef4..a5f7d52b0ae6fa 100644 --- a/packages/react-components/react-jsx-runtime/src/interop.test.tsx +++ b/packages/react-components/react-jsx-runtime/src/interop.test.tsx @@ -14,6 +14,7 @@ describe('resolveShorthand with assertSlots', () => { someSlot: NonNullable>; }; type TestComponentProps = ComponentProps>; + // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; const TestComponent = (props: TestComponentProps) => { @@ -58,6 +59,7 @@ describe('resolveShorthand with assertSlots', () => { it('keeps children from a render template in a render callback', () => { const consoleWarnMock = jest.spyOn(console, 'warn').mockImplementation(); type TestComponentSlots = { outer: NonNullable>; inner: NonNullable> }; + // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; type TestComponentProps = ComponentProps>; @@ -119,6 +121,7 @@ describe('resolveShorthand with assertSlots', () => { it("should support 'as' property to opt-out of base element type", () => { const consoleWarnMock = jest.spyOn(console, 'warn').mockImplementation(); type TestComponentSlots = { slot: NonNullable> }; + // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; type TestComponentProps = ComponentProps>; diff --git a/packages/react-components/react-jsx-runtime/src/jsx-runtime.test.tsx b/packages/react-components/react-jsx-runtime/src/jsx-runtime.test.tsx index 66d5a5343aa3ca..b6f7ec348c06e6 100644 --- a/packages/react-components/react-jsx-runtime/src/jsx-runtime.test.tsx +++ b/packages/react-components/react-jsx-runtime/src/jsx-runtime.test.tsx @@ -64,6 +64,7 @@ describe('createElement with getSlotsNext', () => { describe('custom behavior tests', () => { it('keeps children from "defaultProps" in a render callback', () => { type TestComponentSlots = { slot: Slot<'div'> }; + // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; type TestComponentProps = ComponentProps>; @@ -107,6 +108,7 @@ describe('createElement with getSlotsNext', () => { it('keeps children from a render template in a render callback', () => { type TestComponentSlots = { outer: Slot<'div'>; inner: Slot<'div'> }; + // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; type TestComponentProps = ComponentProps>; diff --git a/packages/react-components/react-jsx-runtime/src/jsx/createElementFromSlotComponent.ts b/packages/react-components/react-jsx-runtime/src/jsx/createElementFromSlotComponent.ts index 3c9b4e73fbafc4..02fcb4a93541ff 100644 --- a/packages/react-components/react-jsx-runtime/src/jsx/createElementFromSlotComponent.ts +++ b/packages/react-components/react-jsx-runtime/src/jsx/createElementFromSlotComponent.ts @@ -1,12 +1,13 @@ import * as React from 'react'; -import type { SlotComponentType, UnknownSlotProps } from '@fluentui/react-utilities'; +import type { SlotComponentType } from '@fluentui/react-utilities'; import { getMetadataFromSlotComponent } from '../utils/getMetadataFromSlotComponent'; +import type { SlotPropsDataType } from '../utils/types'; /** * @internal * creates a ReactElement from a slot declaration */ -export function createElementFromSlotComponent( +export function createElementFromSlotComponent( type: SlotComponentType, overrideChildren: React.ReactNode[], ): React.ReactElement { diff --git a/packages/react-components/react-jsx-runtime/src/jsx/jsxDEVSlot.ts b/packages/react-components/react-jsx-runtime/src/jsx/jsxDEVSlot.ts index 49f3e1378ff8a6..09b62996706817 100644 --- a/packages/react-components/react-jsx-runtime/src/jsx/jsxDEVSlot.ts +++ b/packages/react-components/react-jsx-runtime/src/jsx/jsxDEVSlot.ts @@ -1,9 +1,10 @@ import * as React from 'react'; -import { SlotComponentType, UnknownSlotProps } from '@fluentui/react-utilities'; +import { SlotComponentType } from '@fluentui/react-utilities'; import { getMetadataFromSlotComponent } from '../utils/getMetadataFromSlotComponent'; import { DevRuntime } from '../utils/DevRuntime'; +import type { SlotPropsDataType } from '../utils/types'; -export const jsxDEVSlot = ( +export const jsxDEVSlot = ( type: SlotComponentType, overrideProps: Props | null, key?: React.Key, diff --git a/packages/react-components/react-jsx-runtime/src/jsx/jsxSlot.ts b/packages/react-components/react-jsx-runtime/src/jsx/jsxSlot.ts index ebfa184124b92d..7b3825d74eecc3 100644 --- a/packages/react-components/react-jsx-runtime/src/jsx/jsxSlot.ts +++ b/packages/react-components/react-jsx-runtime/src/jsx/jsxSlot.ts @@ -1,9 +1,10 @@ import * as React from 'react'; -import { SlotComponentType, UnknownSlotProps } from '@fluentui/react-utilities'; +import { SlotComponentType } from '@fluentui/react-utilities'; import { getMetadataFromSlotComponent } from '../utils/getMetadataFromSlotComponent'; import { Runtime } from '../utils/Runtime'; +import type { SlotPropsDataType } from '../utils/types'; -export const jsxSlot = ( +export const jsxSlot = ( type: SlotComponentType, overrideProps: Props | null, key?: React.Key, diff --git a/packages/react-components/react-jsx-runtime/src/jsx/jsxsSlot.ts b/packages/react-components/react-jsx-runtime/src/jsx/jsxsSlot.ts index 756df8bc6531b9..90e20de6a911a8 100644 --- a/packages/react-components/react-jsx-runtime/src/jsx/jsxsSlot.ts +++ b/packages/react-components/react-jsx-runtime/src/jsx/jsxsSlot.ts @@ -1,9 +1,10 @@ import * as React from 'react'; -import { SlotComponentType, UnknownSlotProps } from '@fluentui/react-utilities'; +import { SlotComponentType } from '@fluentui/react-utilities'; import { getMetadataFromSlotComponent } from '../utils/getMetadataFromSlotComponent'; import { Runtime } from '../utils/Runtime'; +import type { SlotPropsDataType } from '../utils/types'; -export const jsxsSlot = ( +export const jsxsSlot = ( type: SlotComponentType, overrideProps: Props | null, key?: React.Key, diff --git a/packages/react-components/react-jsx-runtime/src/utils/getMetadataFromSlotComponent.ts b/packages/react-components/react-jsx-runtime/src/utils/getMetadataFromSlotComponent.ts index 8c95fd3ba80049..ce958b6a194a46 100644 --- a/packages/react-components/react-jsx-runtime/src/utils/getMetadataFromSlotComponent.ts +++ b/packages/react-components/react-jsx-runtime/src/utils/getMetadataFromSlotComponent.ts @@ -1,18 +1,19 @@ import type * as React from 'react'; import { SLOT_ELEMENT_TYPE_SYMBOL, SLOT_RENDER_FUNCTION_SYMBOL } from '@fluentui/react-utilities'; -import type { SlotComponentType, UnknownSlotProps } from '@fluentui/react-utilities'; +import type { SlotComponentType } from '@fluentui/react-utilities'; +import type { SlotPropsDataType } from './types'; /** * @internal */ -export function getMetadataFromSlotComponent(type: SlotComponentType) { +export function getMetadataFromSlotComponent(type: SlotComponentType) { const { as, [SLOT_ELEMENT_TYPE_SYMBOL]: baseElementType, [SLOT_RENDER_FUNCTION_SYMBOL]: renderFunction, ...propsWithoutMetadata } = type; - const props = propsWithoutMetadata as UnknownSlotProps as Props; + const props = propsWithoutMetadata as SlotPropsDataType as Props; const elementType = ( typeof baseElementType === 'string' ? as ?? baseElementType : baseElementType diff --git a/packages/react-components/react-jsx-runtime/src/utils/types.ts b/packages/react-components/react-jsx-runtime/src/utils/types.ts index 218d95aa9e587d..0ee58809359ecf 100644 --- a/packages/react-components/react-jsx-runtime/src/utils/types.ts +++ b/packages/react-components/react-jsx-runtime/src/utils/types.ts @@ -1,5 +1,5 @@ import type * as React from 'react'; -import type { SlotComponentType, UnknownSlotProps } from '@fluentui/react-utilities'; +import type { SlotComponentType } from '@fluentui/react-utilities'; export type JSXRuntime =

( type: React.ElementType

, @@ -9,10 +9,22 @@ export type JSXRuntime =

( self?: unknown, ) => React.ReactElement

; -export type JSXSlotRuntime = ( +export type JSXSlotRuntime = ( type: SlotComponentType, overrideProps: Props | null, key?: React.Key, source?: unknown, self?: unknown, ) => React.ReactElement; + +/** + * @internal + * + * This should ONLY be used in type templates as in `extends SlotPropsDataType`; + * it shouldn't be used as a component's Slot props type. + */ +export type SlotPropsDataType = { + as?: keyof JSX.IntrinsicElements; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + children?: any; +}; diff --git a/packages/react-components/react-menu/library/src/components/MenuItem/useMenuItem.tsx b/packages/react-components/react-menu/library/src/components/MenuItem/useMenuItem.tsx index 7794817ba8e39b..a2df551af767b4 100644 --- a/packages/react-components/react-menu/library/src/components/MenuItem/useMenuItem.tsx +++ b/packages/react-components/react-menu/library/src/components/MenuItem/useMenuItem.tsx @@ -30,7 +30,14 @@ const ChevronLeftIcon = bundleIcon(ChevronLeftFilled, ChevronLeftRegular); export const useMenuItem_unstable = (props: MenuItemProps, ref: React.Ref>): MenuItemState => { const isSubmenuTrigger = useMenuTriggerContext_unstable(); const persistOnClickContext = useMenuContext_unstable(context => context.persistOnItemClick); - const { as = 'div', disabled = false, hasSubmenu = isSubmenuTrigger, persistOnClick = persistOnClickContext } = props; + const { + as = 'div', + disabled = false, + hasSubmenu = isSubmenuTrigger, + persistOnClick = persistOnClickContext, + content, + ...rest + } = props; const hasIcons = useMenuListContext_unstable(context => context.hasIcons); const hasCheckmarks = useMenuListContext_unstable(context => context.hasCheckmarks); const setOpen = useMenuContext_unstable(context => context.setOpen); @@ -52,11 +59,11 @@ export const useMenuItem_unstable = (props: MenuItemProps, ref: React.Ref( + 'div', useARIAButtonProps<'div', ARIAButtonProps<'div'>>(as, { role: 'menuitem', - ...props, + ...rest, disabled: false, disabledFocusable: disabled, ref: useMergedRefs(ref, innerRef) as React.Ref>, diff --git a/packages/react-components/react-migration-v0-v9/etc/react-migration-v0-v9.api.md b/packages/react-components/react-migration-v0-v9/etc/react-migration-v0-v9.api.md index 931031824e7dc3..9eb92c1978c4b8 100644 --- a/packages/react-components/react-migration-v0-v9/etc/react-migration-v0-v9.api.md +++ b/packages/react-components/react-migration-v0-v9/etc/react-migration-v0-v9.api.md @@ -81,14 +81,16 @@ export const input: { }; // @public (undocumented) -export const ItemLayout: React_2.ForwardRefExoticComponent & Omit<{ +export const ItemLayout: React_2.ForwardRefExoticComponent & Omit, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { +} & Omit, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject | null | undefined; -} & { +}, "children"> & { children?: React_2.ReactNode | SlotRenderFunction, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject | null | undefined; }>; +}, "children"> & { + children?: React_2.ReactNode; }, "ref"> & React_2.RefAttributes>; // @public (undocumented) diff --git a/packages/react-components/react-migration-v8-v9/etc/react-migration-v8-v9.api.md b/packages/react-components/react-migration-v8-v9/etc/react-migration-v8-v9.api.md index 1893b881734b64..b6a1b61f712ef0 100644 --- a/packages/react-components/react-migration-v8-v9/etc/react-migration-v8-v9.api.md +++ b/packages/react-components/react-migration-v8-v9/etc/react-migration-v8-v9.api.md @@ -42,7 +42,7 @@ export const brandWeb: BrandVariants; export const ButtonShim: React_2.ForwardRefExoticComponent>; // @public (undocumented) -export const CheckboxShim: React_2.ForwardRefExoticComponent & React_2.RefAttributes>; +export const CheckboxShim: React_2.ForwardRefExoticComponent & React_2.RefAttributes>; // @public export type ColorVariants = { diff --git a/packages/react-components/react-table/library/src/components/TableCellLayout/useTableCellLayout.ts b/packages/react-components/react-table/library/src/components/TableCellLayout/useTableCellLayout.ts index bbf2e096734a5a..d537e6ccb085ef 100644 --- a/packages/react-components/react-table/library/src/components/TableCellLayout/useTableCellLayout.ts +++ b/packages/react-components/react-table/library/src/components/TableCellLayout/useTableCellLayout.ts @@ -22,6 +22,7 @@ export const useTableCellLayout_unstable = ( props: TableCellLayoutProps, ref: React.Ref, ): TableCellLayoutState => { + const { content, ...rest } = props; const { size } = useTableContext(); return { @@ -38,7 +39,7 @@ export const useTableCellLayout_unstable = ( // `ref` is wrongly assigned to be `HTMLElement` instead of `HTMLDivElement` // but since it would be a breaking change to fix it, we are casting ref to it's proper type ref: ref as React.Ref, - ...props, + ...rest, }), { elementType: 'div' }, ), diff --git a/packages/react-components/react-tabs/library/src/components/Tab/useTab.ts b/packages/react-components/react-tabs/library/src/components/Tab/useTab.ts index b36d04b23130f3..643c04f07c74cd 100644 --- a/packages/react-components/react-tabs/library/src/components/Tab/useTab.ts +++ b/packages/react-components/react-tabs/library/src/components/Tab/useTab.ts @@ -20,7 +20,7 @@ import { SelectTabEvent } from '../TabList/TabList.types'; * @param ref - reference to root HTMLElement of Tab */ export const useTab_unstable = (props: TabProps, ref: React.Ref): TabState => { - const { content, disabled: tabDisabled = false, icon, onClick, onFocus, value } = props; + const { content, disabled: tabDisabled = false, icon, onClick, onFocus, value, ...rest } = props; const appearance = useTabListContext_unstable(ctx => ctx.appearance); const reserveSelectedTabSpace = useTabListContext_unstable(ctx => ctx.reserveSelectedTabSpace); @@ -69,7 +69,9 @@ export const useTab_unstable = (props: TabProps, ref: React.Ref): T // aria-selected undefined indicates it is not selectable // according to https://www.w3.org/TR/wai-aria-1.1/#aria-selected 'aria-selected': disabled ? undefined : (`${selected}` as 'true' | 'false'), - ...props, + // FIXME: value is not a valid prop for button + value, + ...rest, disabled, onClick: onTabClick, onFocus: selectTabOnFocus ? onTabFocus : onFocus, diff --git a/packages/react-components/react-toast/library/src/components/ToastContainer/useToastContainer.ts b/packages/react-components/react-toast/library/src/components/ToastContainer/useToastContainer.ts index 3de56e5f4c1c62..c0a6d6f4f5ec0f 100644 --- a/packages/react-components/react-toast/library/src/components/ToastContainer/useToastContainer.ts +++ b/packages/react-components/react-toast/library/src/components/ToastContainer/useToastContainer.ts @@ -50,6 +50,7 @@ export const useToastContainer_unstable = ( pauseOnWindowBlur, imperativeRef, tryRestoreFocus, + content, ...rest } = props; const titleId = useId('toast-title'); diff --git a/packages/react-components/react-toolbar/library/src/components/ToolbarGroup/useToolbarGroup.ts b/packages/react-components/react-toolbar/library/src/components/ToolbarGroup/useToolbarGroup.ts index a93d5166447dfa..c584667de83852 100644 --- a/packages/react-components/react-toolbar/library/src/components/ToolbarGroup/useToolbarGroup.ts +++ b/packages/react-components/react-toolbar/library/src/components/ToolbarGroup/useToolbarGroup.ts @@ -16,7 +16,7 @@ export const useToolbarGroup_unstable = ( root: 'div', }, root: slot.always( - getIntrinsicElementProps>('div', { + getIntrinsicElementProps('div', { ref, role: 'presentation', ...props, diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index 985292f0c166af..c94af442fca157 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -8,7 +8,7 @@ import { DispatchWithoutAction } from 'react'; import * as React_2 from 'react'; // @public -function always(value: Props | SlotShorthandValue | undefined, options: SlotOptions): SlotComponentType; +function always(value: Props | SlotShorthandValue | undefined, options: SlotOptions): SlotComponentType; // @internal export function applyTriggerPropsToChildren(children: TriggerProps['children'], triggerChildProps: TriggerChildProps): React_2.ReactElement | null; @@ -23,15 +23,15 @@ export function canUseDOM(): boolean; export const clamp: (value: number, min: number, max: number) => number; // @public -export type ComponentProps = Omit & PropsWithoutRef>; +export type ComponentProps = Omit & PropsWithoutRef>>; // @public export type ComponentState = { components: { - [Key in keyof Slots]-?: React_2.ComponentType> | (ExtractSlotProps extends AsIntrinsicElement ? As : keyof JSX.IntrinsicElements); + [Key in keyof Slots]-?: React_2.ElementType; }; } & { - [Key in keyof Slots]: ReplaceNullWithUndefined>; + [Key in keyof Slots]: ReplaceNullWithUndefined>>; }; // @internal (undocumented) @@ -64,7 +64,7 @@ export type FluentTriggerComponent = { }; // @public -export type ForwardRefComponent = React_2.ForwardRefExoticComponent>>; +export type ForwardRefComponent = FunctionComponent>>; // @public export function getEventClientCoords(event: TouchOrMouseEvent): { @@ -73,7 +73,7 @@ export function getEventClientCoords(event: TouchOrMouseEvent): { }; // @public -export const getIntrinsicElementProps: = never>(tagName: NonNullable, props: Props & React_2.RefAttributes>, excludedPropNames?: ExcludedPropKeys[] | undefined) => DistributiveOmit>; +export const getIntrinsicElementProps: = never>(tagName: NonNullable, props: Props & React_2.RefAttributes>, excludedPropNames?: ExcludedPropKeys[] | undefined) => DistributiveOmit>; // @public @deprecated export function getNativeElementProps>(tagName: string, props: {}, excludedPropNames?: string[]): TAttributes; @@ -98,13 +98,13 @@ export const getPartitionedNativeProps: string; // @public @deprecated -export function getSlots(state: ComponentState): { +export function getSlots(state: unknown): { slots: Slots; slotProps: ObjectSlotProps; }; // @internal @deprecated -export function getSlotsNext(state: ComponentState): { +export function getSlotsNext(state: unknown): { slots: Slots; slotProps: ObjectSlotProps; }; @@ -135,7 +135,7 @@ export function isInteractiveHTMLElement(element: unknown): boolean; export function isMouseEvent(event: TouchOrMouseEvent): event is MouseEvent | React_2.MouseEvent; // @public -export function isResolvedShorthand>(shorthand?: Shorthand): shorthand is ExtractSlotProps; +export function isResolvedShorthand>(shorthand?: Shorthand): shorthand is ExtractSlotProps; // @public export function isSlot(element: unknown): element is SlotComponentType; @@ -161,7 +161,7 @@ export type OnSelectionChangeData = { }; // @public -function optional(value: Props | SlotShorthandValue | undefined | null, options: { +function optional(value: Props | SlotShorthandValue | undefined | null, options: { renderByDefault?: boolean; } & SlotOptions): SlotComponentType | undefined; @@ -195,15 +195,15 @@ export type RefObjectFunction = React_2.RefObject & ((value: T) => void); export function resetIdsForTests(): void; // @public @deprecated -export const resolveShorthand: ResolveShorthandFunction; +export const resolveShorthand: ResolveShorthandFunction; // @public -function resolveShorthand_2(value: Props | SlotShorthandValue): Props; +function resolveShorthand_2(value: Props | SlotShorthandValue): Props; // @public @deprecated (undocumented) -export type ResolveShorthandFunction = { -

(value: P | SlotShorthandValue | undefined, options: ResolveShorthandOptions): P; -

(value: P | SlotShorthandValue | null | undefined, options?: ResolveShorthandOptions): P | undefined; +export type ResolveShorthandFunction = { +

(value: P | SlotShorthandValue | undefined, options: ResolveShorthandOptions): WithoutSlotRenderFunction

; +

(value: P | SlotShorthandValue | null | undefined, options?: ResolveShorthandOptions): WithoutSlotRenderFunction

| undefined; }; // @public @deprecated (undocumented) @@ -250,13 +250,11 @@ export { SelectionMode_2 as SelectionMode } export function setVirtualParent(child: Node, parent?: Node): void; // @public -export type Slot = IsSingleton> extends true ? WithSlotShorthandValue | SlotPropsDataType, AlternateAs extends keyof JSX.IntrinsicElements = never> = IsSingleton> extends true ? WithSlotShorthandValue> : Type extends React_2.ComponentType ? WithSlotRenderFunction : Type> | { - [As in AlternateAs]: { - as: As; - } & WithSlotRenderFunction>; -}[AlternateAs] | null : 'Error: First parameter to Slot must not be not a union of types. See documentation of Slot type.'; +} & WithSlotRenderFunction> : Type extends ComponentType ? Props extends SlotPropsDataType ? Props : WithSlotRenderFunction : Type> | (AlternateAs extends unknown ? { + as: AlternateAs; +} & WithSlotRenderFunction> : never) | null : 'Error: First parameter to Slot must not be not a union of types. See documentation of Slot type.'; declare namespace slot { export { @@ -280,23 +278,26 @@ export type SlotClassNames = { }; // @public -export type SlotComponentType = Props & { - (props: React_2.PropsWithChildren<{}>): React_2.ReactElement | null; +export type SlotComponentType = WithoutSlotRenderFunction & FunctionComponent<{ + children?: ReactNode; +}> & { [SLOT_RENDER_FUNCTION_SYMBOL]?: SlotRenderFunction; - [SLOT_ELEMENT_TYPE_SYMBOL]: React_2.ComponentType | (Props extends AsIntrinsicElement ? As : keyof JSX.IntrinsicElements); + [SLOT_ELEMENT_TYPE_SYMBOL]: ComponentType | (Props extends AsIntrinsicElement ? As : keyof JSX.IntrinsicElements); }; // @public (undocumented) -export type SlotOptions = { +export type SlotOptions = { elementType: React_2.ComponentType | (Props extends AsIntrinsicElement ? As : keyof JSX.IntrinsicElements); - defaultProps?: Partial; + defaultProps?: Partial>; + }>; }; // @public -export type SlotPropsRecord = Record; +export type SlotPropsRecord = Record; // @public (undocumented) -export type SlotRenderFunction = (Component: React_2.ElementType, props: Omit) => React_2.ReactNode; +export type SlotRenderFunction = (Component: React_2.ElementType, props: Omit) => ReactNode; // @public @deprecated (undocumented) export type Slots = { @@ -304,7 +305,7 @@ export type Slots = { }; // @public -export type SlotShorthandValue = React_2.ReactChild | React_2.ReactNode[] | React_2.ReactPortal; +export type SlotShorthandValue = React_2.ReactElement | string | number | Iterable | React_2.ReactPortal; // @public export const SSRProvider: React_2.FC<{ @@ -322,7 +323,7 @@ export type TriggerProps = { // @public export type UnionToIntersection = (U extends unknown ? (x: U) => U : never) extends (x: infer I) => U ? I : never; -// @public +// @public @deprecated (undocumented) export type UnknownSlotProps = Pick, 'children' | 'className' | 'style'> & { as?: keyof JSX.IntrinsicElements; }; diff --git a/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts b/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts index 0a3c64a7bd3349..2d0ee6bfe238da 100644 --- a/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts +++ b/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts @@ -57,20 +57,21 @@ export type ObjectSlotProps = { * @returns An object containing the `slots` map and `slotProps` map. */ export function getSlots( - state: ComponentState, + state: unknown, ): { // eslint-disable-next-line deprecation/deprecation slots: Slots; // eslint-disable-next-line deprecation/deprecation slotProps: ObjectSlotProps; } { + const typeState = state as ComponentState; // eslint-disable-next-line deprecation/deprecation const slots = {} as Slots; const slotProps = {} as R; - const slotNames: (keyof R)[] = Object.keys(state.components); + const slotNames: (keyof R)[] = Object.keys(typeState.components); for (const slotName of slotNames) { - const [slot, props] = getSlot(state, slotName); + const [slot, props] = getSlot(typeState, slotName); // eslint-disable-next-line deprecation/deprecation slots[slotName] = slot as Slots[typeof slotName]; slotProps[slotName] = props; @@ -112,6 +113,7 @@ function getSlot( } const shouldOmitAsProp = typeof slot === 'string' && asProp; + // eslint-disable-next-line deprecation/deprecation const slotProps = (shouldOmitAsProp ? omit(props, ['as']) : (props as UnknownSlotProps)) as R[K]; return [slot, slotProps]; } diff --git a/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.test.tsx b/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.test.tsx index 054a02b7a6d6bd..578579831a5d1d 100644 --- a/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.test.tsx +++ b/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.test.tsx @@ -4,6 +4,7 @@ import type { ExtractSlotProps, Slot, SlotComponentType, UnknownSlotProps } from import { resolveShorthand } from './resolveShorthand'; import { SLOT_RENDER_FUNCTION_SYMBOL } from '../constants'; +// eslint-disable-next-line deprecation/deprecation const resolveShorthandMock = (props: Props): SlotComponentType => { // casting is required here as SlotComponent is a callable return { ...props } as SlotComponentType; diff --git a/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.ts b/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.ts index 0bd1a54b951cd0..ec25b36840060c 100644 --- a/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.ts +++ b/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.ts @@ -13,21 +13,22 @@ import { ObjectSlotProps, Slots } from './getSlots'; * @deprecated use slot.always or slot.optional combined with assertSlots instead */ export function getSlotsNext( - state: ComponentState, + state: unknown, ): { // eslint-disable-next-line deprecation/deprecation slots: Slots; // eslint-disable-next-line deprecation/deprecation slotProps: ObjectSlotProps; } { + const typedState = state as ComponentState; // eslint-disable-next-line deprecation/deprecation const slots = {} as Slots; const slotProps = {} as R; - const slotNames: (keyof R)[] = Object.keys(state.components); + const slotNames: (keyof R)[] = Object.keys(typedState.components); for (const slotName of slotNames) { // eslint-disable-next-line deprecation/deprecation - const [slot, props] = getSlotNext(state, slotName); + const [slot, props] = getSlotNext(typedState, slotName); // eslint-disable-next-line deprecation/deprecation slots[slotName] = slot as Slots[typeof slotName]; slotProps[slotName] = props; @@ -60,6 +61,7 @@ function getSlotNext( ) as React.ElementType; const shouldOmitAsProp = typeof slot === 'string' && asProp; + // eslint-disable-next-line deprecation/deprecation const slotProps: UnknownSlotProps = shouldOmitAsProp ? propsWithoutAs : props; return [slot, slotProps as R[K]]; diff --git a/packages/react-components/react-utilities/src/compose/deprecated/resolveShorthand.ts b/packages/react-components/react-utilities/src/compose/deprecated/resolveShorthand.ts index d6c5063ed58b61..9e2e96eb15cdfa 100644 --- a/packages/react-components/react-utilities/src/compose/deprecated/resolveShorthand.ts +++ b/packages/react-components/react-utilities/src/compose/deprecated/resolveShorthand.ts @@ -1,5 +1,5 @@ import * as slot from '../slot'; -import type { SlotShorthandValue, UnknownSlotProps } from '../types'; +import type { SlotPropsDataType, SlotShorthandValue, WithoutSlotRenderFunction } from '../types'; /** * @deprecated - use slot.always or slot.optional combined with assertSlots instead @@ -11,12 +11,15 @@ export type ResolveShorthandOptions = R /** * @deprecated use slot.always or slot.optional combined with assertSlots instead */ -export type ResolveShorthandFunction = { - // eslint-disable-next-line deprecation/deprecation -

(value: P | SlotShorthandValue | undefined, options: ResolveShorthandOptions): P; +export type ResolveShorthandFunction = { +

( + value: P | SlotShorthandValue | undefined, + // eslint-disable-next-line deprecation/deprecation + options: ResolveShorthandOptions, + ): WithoutSlotRenderFunction

; // eslint-disable-next-line deprecation/deprecation

(value: P | SlotShorthandValue | null | undefined, options?: ResolveShorthandOptions): - | P + | WithoutSlotRenderFunction

| undefined; }; @@ -30,11 +33,11 @@ export type ResolveShorthandFunction = (value, options) => - slot.optional(value, { +export const resolveShorthand: ResolveShorthandFunction = (value, options) => + slot.optional(value, { ...options, renderByDefault: options?.required, // elementType as undefined is the way to identify between a slot and a resolveShorthand call // in the case elementType is undefined assertSlots will fail, ensuring it'll only work with slot method. elementType: undefined!, - }); + }) as WithoutSlotRenderFunction; diff --git a/packages/react-components/react-utilities/src/compose/getIntrinsicElementProps.ts b/packages/react-components/react-utilities/src/compose/getIntrinsicElementProps.ts index 93fdc5ab0674e5..847be6cc3ba6ef 100644 --- a/packages/react-components/react-utilities/src/compose/getIntrinsicElementProps.ts +++ b/packages/react-components/react-utilities/src/compose/getIntrinsicElementProps.ts @@ -1,6 +1,6 @@ import * as React from 'react'; import { getNativeElementProps } from '../utils/getNativeElementProps'; -import type { InferredElementRefType, UnknownSlotProps } from './types'; +import type { InferredElementRefType, SlotPropsDataType } from './types'; import type { DistributiveOmit } from '../utils/types'; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -13,7 +13,7 @@ type HTMLAttributes = React.HTMLAttributes; * Equivalent to {@link getNativeElementProps}, but more type-safe. */ export const getIntrinsicElementProps = < - Props extends UnknownSlotProps, + Props extends SlotPropsDataType, ExcludedPropKeys extends Extract = never, >( /** The slot's default element type (e.g. 'div') */ @@ -25,6 +25,6 @@ export const getIntrinsicElementProps = < ) => { // eslint-disable-next-line deprecation/deprecation return getNativeElementProps< - DistributiveOmit | ExcludedPropKeys> + DistributiveOmit | ExcludedPropKeys> >(props.as ?? tagName, props, excludedPropNames); }; diff --git a/packages/react-components/react-utilities/src/compose/isResolvedShorthand.ts b/packages/react-components/react-utilities/src/compose/isResolvedShorthand.ts index fdddc5adb4d18b..63b3ae6f96e6f1 100644 --- a/packages/react-components/react-utilities/src/compose/isResolvedShorthand.ts +++ b/packages/react-components/react-utilities/src/compose/isResolvedShorthand.ts @@ -1,5 +1,5 @@ import { isValidElement } from 'react'; -import type { ExtractSlotProps, Slot, UnknownSlotProps } from './types'; +import type { ExtractSlotProps, Slot, SlotPropsDataType } from './types'; /** * Guard method that validates if a shorthand is a slot @@ -31,7 +31,7 @@ import type { ExtractSlotProps, Slot, UnknownSlotProps } from './types'; * }) * ``` */ -export function isResolvedShorthand>( +export function isResolvedShorthand>( shorthand?: Shorthand, ): shorthand is ExtractSlotProps { return shorthand !== null && typeof shorthand === 'object' && !Array.isArray(shorthand) && !isValidElement(shorthand); diff --git a/packages/react-components/react-utilities/src/compose/slot.ts b/packages/react-components/react-utilities/src/compose/slot.ts index 6520b3fb4d4252..fca167d583a111 100644 --- a/packages/react-components/react-utilities/src/compose/slot.ts +++ b/packages/react-components/react-utilities/src/compose/slot.ts @@ -3,16 +3,17 @@ import type { SlotComponentType, SlotRenderFunction, SlotShorthandValue, - UnknownSlotProps, + SlotPropsDataType, + InferredElementRefType, } from './types'; import * as React from 'react'; import { SLOT_ELEMENT_TYPE_SYMBOL, SLOT_RENDER_FUNCTION_SYMBOL } from './constants'; -export type SlotOptions = { +export type SlotOptions = { elementType: | React.ComponentType | (Props extends AsIntrinsicElement ? As : keyof JSX.IntrinsicElements); - defaultProps?: Partial; + defaultProps?: Partial> }>; }; /** @@ -23,7 +24,7 @@ export type SlotOptions = { * * `elementType` - the base element type of a slot, defaults to `'div'` * * `defaultProps` - similar to a React component declaration, you can provide a slot default properties to be merged with the shorthand/properties provided. */ -export function always( +export function always( value: Props | SlotShorthandValue | undefined, options: SlotOptions, ): SlotComponentType { @@ -64,7 +65,7 @@ export function always( * with the values provided by `options.defaultProps` (or `{}`). This is useful for cases such as providing a default content * in case no shorthand is provided, like the case of the `expandIcon` slot for the `AccordionHeader` */ -export function optional( +export function optional( value: Props | SlotShorthandValue | undefined | null, options: { renderByDefault?: boolean } & SlotOptions, ): SlotComponentType | undefined { @@ -79,13 +80,13 @@ export function optional( * The main difference between this function and `slot` is that this function does not return the metadata required for a slot to be considered a properly renderable slot, it only converts the value to a slot properties object * @param value - the value of the slot, it can be a slot shorthand or a slot properties object */ -export function resolveShorthand( +export function resolveShorthand( value: Props | SlotShorthandValue, ): Props { if ( typeof value === 'string' || typeof value === 'number' || - Array.isArray(value) || + isIterable(value) || // eslint-disable-next-line @typescript-eslint/no-explicit-any React.isValidElement(value) ) { @@ -104,3 +105,6 @@ export function resolveShorthand => + typeof value === 'object' && value !== null && Symbol.iterator in value; diff --git a/packages/react-components/react-utilities/src/compose/types.ts b/packages/react-components/react-utilities/src/compose/types.ts index 59c2d1f9d86ac2..d7e497e498804e 100644 --- a/packages/react-components/react-utilities/src/compose/types.ts +++ b/packages/react-components/react-utilities/src/compose/types.ts @@ -1,11 +1,25 @@ import * as React from 'react'; import { SLOT_ELEMENT_TYPE_SYMBOL, SLOT_RENDER_FUNCTION_SYMBOL } from './constants'; -import { DistributiveOmit, ReplaceNullWithUndefined } from '../utils/types'; +import { + ComponentType, + DistributiveOmit, + FunctionComponent, + ReactNode, + ReplaceNullWithUndefined, +} from '../utils/types'; -export type SlotRenderFunction = ( - Component: React.ElementType, - props: Omit, -) => React.ReactNode; +/** + * @internal + * + * This should ONLY be used in type templates as in `extends SlotPropsDataType`; + * it shouldn't be used as a component's Slot props type. + */ +export type SlotPropsDataType = { + as?: keyof JSX.IntrinsicElements; + children?: ReactNode; +}; + +export type SlotRenderFunction = (Component: React.ElementType, props: Omit) => ReactNode; /** * Matches any component's Slots type (such as ButtonSlots). @@ -13,14 +27,15 @@ export type SlotRenderFunction = ( * This should ONLY be used in type templates as in `extends SlotPropsRecord`; * it shouldn't be used as a component's Slots type. */ -export type SlotPropsRecord = Record; +export type SlotPropsRecord = Record; /** * The shorthand value of a slot allows specifying its child */ -export type SlotShorthandValue = React.ReactChild | React.ReactNode[] | React.ReactPortal; +export type SlotShorthandValue = React.ReactElement | string | number | Iterable | React.ReactPortal; /** + * @deprecated - SlotPropsDataType instead * Matches any slot props type. * * This should ONLY be used in type templates as in `extends UnknownSlotProps`; @@ -38,13 +53,23 @@ type WithSlotShorthandValue = | Extract; /** + * @internal * Helper type for {@link Slot}. Takes the props we want to support for a slot and adds the ability for `children` * to be a render function that takes those props. */ -type WithSlotRenderFunction = Props & { - children?: (Props extends { children?: unknown } ? Props['children'] : never) | SlotRenderFunction; +export type WithSlotRenderFunction = Omit & { + children?: ('children' extends keyof Props ? Props['children'] : never) | SlotRenderFunction; }; +/** + * @internal + */ +export type WithoutSlotRenderFunction = Props extends unknown + ? 'children' extends keyof Props + ? Omit & { children?: Exclude } + : Props + : never; + /** * HTML element types that are not allowed to have children. * @@ -98,20 +123,23 @@ type IntrinsicElementProps = Type exte * ``` */ export type Slot< - Type extends keyof JSX.IntrinsicElements | React.ComponentType | React.VoidFunctionComponent | UnknownSlotProps, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Type extends keyof JSX.IntrinsicElements | ComponentType | SlotPropsDataType, AlternateAs extends keyof JSX.IntrinsicElements = never, > = IsSingleton> extends true ? | WithSlotShorthandValue< Type extends keyof JSX.IntrinsicElements // Intrinsic elements like `div` ? { as?: Type } & WithSlotRenderFunction> - : Type extends React.ComponentType // Component types like `typeof Button` - ? WithSlotRenderFunction + : Type extends ComponentType // Component types like `typeof Button` + ? Props extends SlotPropsDataType + ? Props + : WithSlotRenderFunction : Type // Props types like `ButtonProps` > - | { - [As in AlternateAs]: { as: As } & WithSlotRenderFunction>; - }[AlternateAs] + | (AlternateAs extends unknown + ? { as: AlternateAs } & WithSlotRenderFunction> + : never) | null : 'Error: First parameter to Slot must not be not a union of types. See documentation of Slot type.'; @@ -164,30 +192,33 @@ export type ExtractSlotProps = Exclude = // Include a prop for each slot (see note below about the Omit) + // Note: the `Omit` here is a little tricky. Here's what it's doing: + // * If the Primary slot is 'root', then omit the `root` slot prop. + // * Otherwise, don't omit any props: include *both* the Primary and `root` props. + // We need both props to allow the user to specify native props for either slot because the `root` slot is + // special and always gets className and style props, per RFC https://github.com/microsoft/fluentui/pull/18983 Omit & // Include all of the props of the primary slot inline in the component's props - PropsWithoutRef>; - -// Note: the `Omit` above is a little tricky. Here's what it's doing: -// * If the Primary slot is 'root', then omit the `root` slot prop. -// * Otherwise, don't omit any props: include *both* the Primary and `root` props. -// We need both props to allow the user to specify native props for either slot because the `root` slot is -// special and always gets className and style props, per RFC https://github.com/microsoft/fluentui/pull/18983 + PropsWithoutRef>>; /** * Defines the State object of a component given its slots. */ export type ComponentState = { + /** + * @deprecated + * The base element type for each slot. + * This property is deprecated and will be removed in a future version. + * The slot base element type is declared through `slot.*(slotShorthand, {elementType: ElementType})` instead. + */ components: { - [Key in keyof Slots]-?: - | React.ComponentType> - | (ExtractSlotProps extends AsIntrinsicElement ? As : keyof JSX.IntrinsicElements); + [Key in keyof Slots]-?: React.ElementType; }; } & { // Include a prop for each slot, with the shorthand resolved to a props object // The root slot can never be null, so also exclude null from it [Key in keyof Slots]: ReplaceNullWithUndefined< - Exclude + WithoutSlotRenderFunction> >; }; @@ -216,15 +247,10 @@ export type InferredElementRefType = ObscureEventName extends keyof Props /** * Return type for `React.forwardRef`, including inference of the proper typing for the ref. + * + * Note: {@link React.RefAttributes} is {@link https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/69756 | leaking string references} into forwardRef components, forwardRef component do not support string refs. */ -export type ForwardRefComponent = React.ForwardRefExoticComponent< - Props & React.RefAttributes> ->; -// A definition like this would also work, but typescript is more likely to unnecessarily expand -// the props type with this version (and it's likely much more expensive to evaluate) -// export type ForwardRefComponent = Props extends React.DOMAttributes -// ? React.ForwardRefExoticComponent & React.RefAttributes -// : never; +export type ForwardRefComponent = FunctionComponent>>; /** * Helper type to correctly define the slot class names object. @@ -237,22 +263,19 @@ export type SlotClassNames = { * A definition of a slot, as a component, very similar to how a React component is declared, * but with some additional metadata that is used to determine how to render the slot. */ -export type SlotComponentType = Props & { - /** - * **NOTE**: Slot components are not callable. - */ - (props: React.PropsWithChildren<{}>): React.ReactElement | null; - /** - * @internal - */ - [SLOT_RENDER_FUNCTION_SYMBOL]?: SlotRenderFunction; - /** - * @internal - */ - [SLOT_ELEMENT_TYPE_SYMBOL]: - | React.ComponentType - | (Props extends AsIntrinsicElement ? As : keyof JSX.IntrinsicElements); -}; +export type SlotComponentType = WithoutSlotRenderFunction & + FunctionComponent<{ children?: ReactNode }> & { + /** + * @internal + */ + [SLOT_RENDER_FUNCTION_SYMBOL]?: SlotRenderFunction; + /** + * @internal + */ + [SLOT_ELEMENT_TYPE_SYMBOL]: + | ComponentType + | (Props extends AsIntrinsicElement ? As : keyof JSX.IntrinsicElements); + }; /** * Data type for event handlers. It makes data a discriminated union, where each object requires `event` and `type` property. diff --git a/packages/react-components/react-utilities/src/index.ts b/packages/react-components/react-utilities/src/index.ts index 61a014c8ed2473..8c6945aaab853e 100644 --- a/packages/react-components/react-utilities/src/index.ts +++ b/packages/react-components/react-utilities/src/index.ts @@ -29,6 +29,7 @@ export type { SlotPropsRecord, SlotRenderFunction, SlotShorthandValue, + // eslint-disable-next-line deprecation/deprecation UnknownSlotProps, SlotComponentType, SlotOptions, diff --git a/packages/react-components/react-utilities/src/trigger/applyTriggerPropsToChildren.ts b/packages/react-components/react-utilities/src/trigger/applyTriggerPropsToChildren.ts index d13a4d3b8225db..12f3b5dbaf0780 100644 --- a/packages/react-components/react-utilities/src/trigger/applyTriggerPropsToChildren.ts +++ b/packages/react-components/react-utilities/src/trigger/applyTriggerPropsToChildren.ts @@ -27,7 +27,7 @@ export function applyTriggerPropsToChildren( * a FluentTriggerComponent or React Fragment (the same element returned by {@link getTriggerChild}). */ function cloneTriggerTree( - child: React.ReactNode, + child: TriggerProps['children'], triggerProps: TriggerChildProps, ): React.ReactElement { if (!React.isValidElement(child) || child.type === React.Fragment) { diff --git a/packages/react-components/react-utilities/src/utils/types.ts b/packages/react-components/react-utilities/src/utils/types.ts index 3d0f96236b358e..136c6003ca6adc 100644 --- a/packages/react-components/react-utilities/src/utils/types.ts +++ b/packages/react-components/react-utilities/src/utils/types.ts @@ -1,3 +1,5 @@ +import * as React from 'react'; + /** * Helper type that works similar to Omit, * but when modifying an union type it will distribute the omission to all the union members. @@ -31,3 +33,57 @@ export type UnionToIntersection = (U extends unknown ? (x: U) => U : never) e * If type T includes `null`, remove it and add `undefined` instead. */ export type ReplaceNullWithUndefined = T extends null ? Exclude | undefined : T; + +/** + * @internal + * With react 18, our `children` type starts leaking everywhere and that causes conflicts on component declaration, specially in the `propTypes` property of + * both `ComponentClass` and `FunctionComponent`. + * + * This type substitutes `React.ComponentType` only keeping the function signature, it omits `propTypes`, `displayName` and other properties that are not + * required for the inference. + */ +export type ComponentType

= ComponentClass

| FunctionComponent

; + +/** + * @internal + * + * On types/react 18 there are two types being delivered, + * they rely on the typescript version to decide which will be consumed {@link https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b59dc3ac1e2770fbd6cdbb90ba52abe04c168196/types/react/package.json#L10} + * + * If TS is higher than 5.0 then the `FunctionComponent` will be returning ReactNode (which we don't support) + * If TS is below or equal to 5.0 then the `FunctionComponent` will be returning ReactElement | null (which we support) + * + * Since it's not possible to have a single type that works for both cases + * (as ReactNode is more specific, and this will break while evaluating functions), + * we need to create our own `FunctionComponent` type + * that will work for both cases. + * + * **THIS TYPE IS INTERNAL AND SHOULD NEVER BE EXPOSED** + */ +export interface FunctionComponent

{ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (props: P): any; + defaultProps?: Partial

; + displayName?: string; +} + +/** + * @internal + * **THIS TYPE IS INTERNAL AND SHOULD NEVER BE EXPOSED** + */ +export interface ComponentClass

extends React.StaticLifecycle { + new (props: P): React.Component; +} + +/** + * @internal + * + * on types/react 18 ReactNode becomes a more strict type, which is not compatible with our current implementation. to avoid any issues we are creating our own ReactNode type which allows anything. + * + * This type should only be used for inference purposes, and should never be exposed. + * + * **THIS TYPE IS INTERNAL AND SHOULD NEVER BE EXPOSED** + * + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type ReactNode = any; From f7caf230dfbe4f86156a6f9498dfdc1db4218d5a Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Fri, 7 Jun 2024 09:38:27 +0000 Subject: [PATCH 2/8] chore: deprecates ComponentState.components property --- ...t-drawer-70c3f806-65d7-4b1f-aaba-3a92ade08fee.json | 7 +++++++ ...t-search-f1e14f72-5b28-44d0-bb5e-7cc3260e00a4.json | 7 +++++++ ...g-picker-e9aa92f6-cda0-4905-8d4d-13d63785c30b.json | 7 +++++++ ...tualizer-672c17bd-9097-418a-af72-9283de7b72dd.json | 7 +++++++ .../useDrawerHeaderTitleStyles.styles.ts | 5 +++++ .../react-jsx-runtime/src/createElement.test.tsx | 2 -- .../react-jsx-runtime/src/interop.test.tsx | 4 +--- .../react-jsx-runtime/src/jsx-runtime.test.tsx | 2 -- .../src/components/MenuItemLink/useMenuItemLink.ts | 1 + .../components/MenuItemSwitch/useMenuItemSwitch.tsx | 1 + .../MenuItemSwitch/useMenuItemSwitchStyles.styles.ts | 1 + .../library/src/components/SearchBox/useSearchBox.tsx | 1 + .../src/components/DataGridRow/useDataGridRow.tsx | 1 + .../TableSelectionCell/useTableSelectionCell.tsx | 1 + .../components/TagPickerOption/useTagPickerOption.ts | 1 + .../react-utilities/src/compose/assertSlots.ts | 6 +++++- .../src/compose/deprecated/getSlots.ts | 11 ++++++++--- .../src/compose/deprecated/getSlotsNext.ts | 11 ++++++++--- .../src/compose/deprecated/resolveShorthand.ts | 2 +- .../VirtualizerScrollView/useVirtualizerScrollView.ts | 1 + .../useVirtualizerScrollViewDynamic.tsx | 1 + 21 files changed, 65 insertions(+), 15 deletions(-) create mode 100644 change/@fluentui-react-drawer-70c3f806-65d7-4b1f-aaba-3a92ade08fee.json create mode 100644 change/@fluentui-react-search-f1e14f72-5b28-44d0-bb5e-7cc3260e00a4.json create mode 100644 change/@fluentui-react-tag-picker-e9aa92f6-cda0-4905-8d4d-13d63785c30b.json create mode 100644 change/@fluentui-react-virtualizer-672c17bd-9097-418a-af72-9283de7b72dd.json diff --git a/change/@fluentui-react-drawer-70c3f806-65d7-4b1f-aaba-3a92ade08fee.json b/change/@fluentui-react-drawer-70c3f806-65d7-4b1f-aaba-3a92ade08fee.json new file mode 100644 index 00000000000000..103e38996d6acf --- /dev/null +++ b/change/@fluentui-react-drawer-70c3f806-65d7-4b1f-aaba-3a92ade08fee.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: deprecates ComponentState.components property", + "packageName": "@fluentui/react-drawer", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-search-f1e14f72-5b28-44d0-bb5e-7cc3260e00a4.json b/change/@fluentui-react-search-f1e14f72-5b28-44d0-bb5e-7cc3260e00a4.json new file mode 100644 index 00000000000000..9bd15e00b2e816 --- /dev/null +++ b/change/@fluentui-react-search-f1e14f72-5b28-44d0-bb5e-7cc3260e00a4.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: deprecates ComponentState.components property", + "packageName": "@fluentui/react-search", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-tag-picker-e9aa92f6-cda0-4905-8d4d-13d63785c30b.json b/change/@fluentui-react-tag-picker-e9aa92f6-cda0-4905-8d4d-13d63785c30b.json new file mode 100644 index 00000000000000..34c5c83f97f826 --- /dev/null +++ b/change/@fluentui-react-tag-picker-e9aa92f6-cda0-4905-8d4d-13d63785c30b.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: deprecates ComponentState.components property", + "packageName": "@fluentui/react-tag-picker", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-virtualizer-672c17bd-9097-418a-af72-9283de7b72dd.json b/change/@fluentui-react-virtualizer-672c17bd-9097-418a-af72-9283de7b72dd.json new file mode 100644 index 00000000000000..1e2f10400c4ba1 --- /dev/null +++ b/change/@fluentui-react-virtualizer-672c17bd-9097-418a-af72-9283de7b72dd.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "chore: deprecates ComponentState.components property", + "packageName": "@fluentui/react-virtualizer", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-drawer/library/src/components/DrawerHeaderTitle/useDrawerHeaderTitleStyles.styles.ts b/packages/react-components/react-drawer/library/src/components/DrawerHeaderTitle/useDrawerHeaderTitleStyles.styles.ts index d82e284944a7ac..60a74f8659f9f9 100644 --- a/packages/react-components/react-drawer/library/src/components/DrawerHeaderTitle/useDrawerHeaderTitleStyles.styles.ts +++ b/packages/react-components/react-drawer/library/src/components/DrawerHeaderTitle/useDrawerHeaderTitleStyles.styles.ts @@ -33,6 +33,11 @@ const useStyles = makeStyles({ export const useDrawerHeaderTitleStyles_unstable = (state: DrawerHeaderTitleState): DrawerHeaderTitleState => { const styles = useStyles(); + // We should not use components to pass along the base element type of a slot + // but there's no way to retrieve the element type of a slot from the slot definition + // right now without using SLOT_ELEMENT_TYPE_SYMBOL + // TODO: create a method to retrieve the element type of a slot + // eslint-disable-next-line deprecation/deprecation const { heading: root = {}, action, components } = state; useDialogTitleStyles_unstable({ diff --git a/packages/react-components/react-jsx-runtime/src/createElement.test.tsx b/packages/react-components/react-jsx-runtime/src/createElement.test.tsx index 44b82244e2733b..59cd16ff8b906e 100644 --- a/packages/react-components/react-jsx-runtime/src/createElement.test.tsx +++ b/packages/react-components/react-jsx-runtime/src/createElement.test.tsx @@ -70,7 +70,6 @@ describe('createElement with getSlotsNext', () => { describe('custom behavior tests', () => { it('keeps children from "defaultProps" in a render callback', () => { type TestComponentSlots = { slot: Slot<'div'> }; - // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; type TestComponentProps = ComponentProps>; @@ -114,7 +113,6 @@ describe('createElement with getSlotsNext', () => { it('keeps children from a render template in a render callback', () => { type TestComponentSlots = { outer: Slot<'div'>; inner: Slot<'div'> }; - // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; type TestComponentProps = ComponentProps>; diff --git a/packages/react-components/react-jsx-runtime/src/interop.test.tsx b/packages/react-components/react-jsx-runtime/src/interop.test.tsx index a5f7d52b0ae6fa..3181827f74d17b 100644 --- a/packages/react-components/react-jsx-runtime/src/interop.test.tsx +++ b/packages/react-components/react-jsx-runtime/src/interop.test.tsx @@ -14,7 +14,6 @@ describe('resolveShorthand with assertSlots', () => { someSlot: NonNullable>; }; type TestComponentProps = ComponentProps>; - // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; const TestComponent = (props: TestComponentProps) => { @@ -59,7 +58,6 @@ describe('resolveShorthand with assertSlots', () => { it('keeps children from a render template in a render callback', () => { const consoleWarnMock = jest.spyOn(console, 'warn').mockImplementation(); type TestComponentSlots = { outer: NonNullable>; inner: NonNullable> }; - // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; type TestComponentProps = ComponentProps>; @@ -121,7 +119,6 @@ describe('resolveShorthand with assertSlots', () => { it("should support 'as' property to opt-out of base element type", () => { const consoleWarnMock = jest.spyOn(console, 'warn').mockImplementation(); type TestComponentSlots = { slot: NonNullable> }; - // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; type TestComponentProps = ComponentProps>; @@ -174,6 +171,7 @@ describe('resolveShorthand with assertSlots', () => { const TestComponent = (props: TestComponentProps) => { const higherOrderState = useHigherOrderStateHook(props); const state: TestComponentState = { + // eslint-disable-next-line deprecation/deprecation components: { ...higherOrderState.components, slot: 'span' }, slot: { ...higherOrderState.slot, diff --git a/packages/react-components/react-jsx-runtime/src/jsx-runtime.test.tsx b/packages/react-components/react-jsx-runtime/src/jsx-runtime.test.tsx index b6f7ec348c06e6..66d5a5343aa3ca 100644 --- a/packages/react-components/react-jsx-runtime/src/jsx-runtime.test.tsx +++ b/packages/react-components/react-jsx-runtime/src/jsx-runtime.test.tsx @@ -64,7 +64,6 @@ describe('createElement with getSlotsNext', () => { describe('custom behavior tests', () => { it('keeps children from "defaultProps" in a render callback', () => { type TestComponentSlots = { slot: Slot<'div'> }; - // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; type TestComponentProps = ComponentProps>; @@ -108,7 +107,6 @@ describe('createElement with getSlotsNext', () => { it('keeps children from a render template in a render callback', () => { type TestComponentSlots = { outer: Slot<'div'>; inner: Slot<'div'> }; - // eslint-disable-next-line deprecation/deprecation type TestComponentState = ComponentState; type TestComponentProps = ComponentProps>; diff --git a/packages/react-components/react-menu/library/src/components/MenuItemLink/useMenuItemLink.ts b/packages/react-components/react-menu/library/src/components/MenuItemLink/useMenuItemLink.ts index d9aaa4f21d54ed..88cbe16e7bc07b 100644 --- a/packages/react-components/react-menu/library/src/components/MenuItemLink/useMenuItemLink.ts +++ b/packages/react-components/react-menu/library/src/components/MenuItemLink/useMenuItemLink.ts @@ -27,6 +27,7 @@ export const useMenuItemLink_unstable = ( return { ...baseState, components: { + // eslint-disable-next-line deprecation/deprecation ...baseState.components, root: 'a', }, diff --git a/packages/react-components/react-menu/library/src/components/MenuItemSwitch/useMenuItemSwitch.tsx b/packages/react-components/react-menu/library/src/components/MenuItemSwitch/useMenuItemSwitch.tsx index 0099c7f05ebc9e..29e8dd6219265d 100644 --- a/packages/react-components/react-menu/library/src/components/MenuItemSwitch/useMenuItemSwitch.tsx +++ b/packages/react-components/react-menu/library/src/components/MenuItemSwitch/useMenuItemSwitch.tsx @@ -29,6 +29,7 @@ export const useMenuItemSwitch_unstable = ( }, }), components: { + // eslint-disable-next-line deprecation/deprecation ...baseState.components, switchIndicator: 'span', }, diff --git a/packages/react-components/react-menu/library/src/components/MenuItemSwitch/useMenuItemSwitchStyles.styles.ts b/packages/react-components/react-menu/library/src/components/MenuItemSwitch/useMenuItemSwitchStyles.styles.ts index 19262e27571dea..eb7e46e312f837 100644 --- a/packages/react-components/react-menu/library/src/components/MenuItemSwitch/useMenuItemSwitchStyles.styles.ts +++ b/packages/react-components/react-menu/library/src/components/MenuItemSwitch/useMenuItemSwitchStyles.styles.ts @@ -119,6 +119,7 @@ export const useMenuItemSwitchStyles_unstable = (state: MenuItemSwitchState): Me useMenuItemStyles_unstable({ ...state, components: { + // eslint-disable-next-line deprecation/deprecation ...state.components, checkmark: 'span', submenuIndicator: 'span', diff --git a/packages/react-components/react-search/library/src/components/SearchBox/useSearchBox.tsx b/packages/react-components/react-search/library/src/components/SearchBox/useSearchBox.tsx index 4e80dbc6bd2c9f..92b5039e928e9d 100644 --- a/packages/react-components/react-search/library/src/components/SearchBox/useSearchBox.tsx +++ b/packages/react-components/react-search/library/src/components/SearchBox/useSearchBox.tsx @@ -106,6 +106,7 @@ export const useSearchBox_unstable = (props: SearchBoxProps, ref: React.Ref(state: unknown): asse */ if (process.env.NODE_ENV !== 'production') { const typedState = state as ComponentState; + // eslint-disable-next-line deprecation/deprecation for (const slotName of Object.keys(typedState.components)) { const slotElement = typedState[slotName]; if (slotElement === undefined) { @@ -44,6 +45,7 @@ export function assertSlots(state: unknown): asse // FIXME: this slot will still fail to support child render function scenario if (!isSlot(slotElement)) { typedState[slotName as keyof ComponentState] = slot.always(slotElement, { + // eslint-disable-next-line deprecation/deprecation elementType: typedState.components[slotName] as React.ComponentType<{}>, }) as ComponentState[keyof ComponentState]; // eslint-disable-next-line no-console @@ -56,13 +58,15 @@ export function assertSlots(state: unknown): asse // This means a slot is being declared by using resolveShorthand on the state hook, // but the render method is using the new `assertSlots` method. That scenario can be solved by simply updating the slot element with the proper element type const { [SLOT_ELEMENT_TYPE_SYMBOL]: elementType } = slotElement; + // eslint-disable-next-line deprecation/deprecation if (elementType !== typedState.components[slotName]) { + // eslint-disable-next-line deprecation/deprecation slotElement[SLOT_ELEMENT_TYPE_SYMBOL] = typedState.components[slotName] as React.ComponentType<{}>; // eslint-disable-next-line no-console console.warn(/** #__DE-INDENT__ */ ` @fluentui/react-utilities [${assertSlots.name}]: "state.${slotName}" element type differs from "state.components.${slotName}", - ${elementType} !== ${typedState.components[slotName]}. + ${elementType} !== ${typedState.components[slotName] /* eslint-disable-line deprecation/deprecation */}. Be sure to create slots properly by using "slot.always" or "slot.optional" with the correct elementType. `); } diff --git a/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts b/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts index 2d0ee6bfe238da..d61cbfa831f4bd 100644 --- a/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts +++ b/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts @@ -69,6 +69,7 @@ export function getSlots( const slots = {} as Slots; const slotProps = {} as R; + // eslint-disable-next-line deprecation/deprecation const slotNames: (keyof R)[] = Object.keys(typeState.components); for (const slotName of slotNames) { const [slot, props] = getSlot(typeState, slotName); @@ -97,9 +98,13 @@ function getSlot( const renderFunction = isSlot(props) ? props[SLOT_RENDER_FUNCTION_SYMBOL] : undefined; const slot = ( - state.components?.[slotName] === undefined || typeof state.components[slotName] === 'string' - ? asProp || state.components?.[slotName] || 'div' - : state.components[slotName] + state.components?.[slotName] === undefined || // eslint-disable-line deprecation/deprecation + // eslint-disable-next-line deprecation/deprecation + typeof state.components[slotName] === 'string' + ? // eslint-disable-next-line deprecation/deprecation + asProp || state.components?.[slotName] || 'div' + : // eslint-disable-next-line deprecation/deprecation + state.components[slotName] ) as React.ElementType; if (renderFunction || typeof children === 'function') { diff --git a/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.ts b/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.ts index ec25b36840060c..58b91cc21d5680 100644 --- a/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.ts +++ b/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.ts @@ -25,6 +25,7 @@ export function getSlotsNext( const slots = {} as Slots; const slotProps = {} as R; + // eslint-disable-next-line deprecation/deprecation const slotNames: (keyof R)[] = Object.keys(typedState.components); for (const slotName of slotNames) { // eslint-disable-next-line deprecation/deprecation @@ -55,9 +56,13 @@ function getSlotNext( const { as: asProp, ...propsWithoutAs } = props as NonUndefined; const slot = ( - state.components?.[slotName] === undefined || typeof state.components[slotName] === 'string' - ? asProp || state.components?.[slotName] || 'div' - : state.components[slotName] + state.components?.[slotName] === undefined || // eslint-disable-line deprecation/deprecation + // eslint-disable-next-line deprecation/deprecation + typeof state.components[slotName] === 'string' + ? // eslint-disable-next-line deprecation/deprecation + asProp || state.components?.[slotName] || 'div' + : // eslint-disable-next-line deprecation/deprecation + state.components[slotName] ) as React.ElementType; const shouldOmitAsProp = typeof slot === 'string' && asProp; diff --git a/packages/react-components/react-utilities/src/compose/deprecated/resolveShorthand.ts b/packages/react-components/react-utilities/src/compose/deprecated/resolveShorthand.ts index 9e2e96eb15cdfa..668b3efb620ab6 100644 --- a/packages/react-components/react-utilities/src/compose/deprecated/resolveShorthand.ts +++ b/packages/react-components/react-utilities/src/compose/deprecated/resolveShorthand.ts @@ -30,7 +30,7 @@ export type ResolveShorthandFunction = (value, options) => diff --git a/packages/react-components/react-virtualizer/library/src/components/VirtualizerScrollView/useVirtualizerScrollView.ts b/packages/react-components/react-virtualizer/library/src/components/VirtualizerScrollView/useVirtualizerScrollView.ts index a1aebaca2b7829..8789a6f3422758 100644 --- a/packages/react-components/react-virtualizer/library/src/components/VirtualizerScrollView/useVirtualizerScrollView.ts +++ b/packages/react-components/react-virtualizer/library/src/components/VirtualizerScrollView/useVirtualizerScrollView.ts @@ -69,6 +69,7 @@ export function useVirtualizerScrollView_unstable(props: VirtualizerScrollViewPr return { ...virtualizerState, components: { + // eslint-disable-next-line deprecation/deprecation ...virtualizerState.components, container: 'div', }, diff --git a/packages/react-components/react-virtualizer/library/src/components/VirtualizerScrollViewDynamic/useVirtualizerScrollViewDynamic.tsx b/packages/react-components/react-virtualizer/library/src/components/VirtualizerScrollViewDynamic/useVirtualizerScrollViewDynamic.tsx index d49243312fa1d7..339b29a5a28445 100644 --- a/packages/react-components/react-virtualizer/library/src/components/VirtualizerScrollViewDynamic/useVirtualizerScrollViewDynamic.tsx +++ b/packages/react-components/react-virtualizer/library/src/components/VirtualizerScrollViewDynamic/useVirtualizerScrollViewDynamic.tsx @@ -169,6 +169,7 @@ export function useVirtualizerScrollViewDynamic_unstable( return { ...virtualizerState, components: { + // eslint-disable-next-line deprecation/deprecation ...virtualizerState.components, container: 'div', }, From d99b342961a22c7f3cd84f29533f331d1367801d Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Fri, 7 Jun 2024 12:54:22 +0000 Subject: [PATCH 3/8] chore(react-utilities): updates useMergedRefs to accept LegacyRef --- .../react-menu/library/etc/react-menu.api.md | 5 +- .../src/components/MenuItem/MenuItem.types.ts | 49 +++++++++---------- .../src/components/MenuItem/useMenuItem.tsx | 10 +--- .../etc/react-utilities.api.md | 2 +- .../src/compose/deprecated/getSlots.ts | 2 +- .../src/hooks/useMergedRefs.ts | 24 +++++++-- 6 files changed, 50 insertions(+), 42 deletions(-) diff --git a/packages/react-components/react-menu/library/etc/react-menu.api.md b/packages/react-components/react-menu/library/etc/react-menu.api.md index 10ce069c886e84..675e5cb104857b 100644 --- a/packages/react-components/react-menu/library/etc/react-menu.api.md +++ b/packages/react-components/react-menu/library/etc/react-menu.api.md @@ -12,6 +12,7 @@ import { ARIAButtonType } from '@fluentui/react-aria'; import type { ComponentProps } from '@fluentui/react-utilities'; import type { ComponentState } from '@fluentui/react-utilities'; import type { ContextSelector } from '@fluentui/react-context-selector'; +import type { ExtractSlotProps } from '@fluentui/react-utilities'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; import type { PortalProps } from '@fluentui/react-portal'; import type { PositioningShorthand } from '@fluentui/react-positioning'; @@ -157,7 +158,7 @@ export type MenuItemLinkSlots = { export type MenuItemLinkState = ComponentState; // @public (undocumented) -export type MenuItemProps = Omit>, 'content'> & Pick, 'content'> & { +export type MenuItemProps = ComponentProps> & { hasSubmenu?: boolean; persistOnClick?: boolean; disabled?: boolean; @@ -189,7 +190,7 @@ export type MenuItemSelectableState = MenuItemSelectableProps & { // @public (undocumented) export type MenuItemSlots = { - root: Slot<'div'>; + root: Slot>, 'content'>>; icon?: Slot<'span'>; checkmark?: Slot<'span'>; submenuIndicator?: Slot<'span'>; diff --git a/packages/react-components/react-menu/library/src/components/MenuItem/MenuItem.types.ts b/packages/react-components/react-menu/library/src/components/MenuItem/MenuItem.types.ts index 91b79e12ab3fb0..fde30440116273 100644 --- a/packages/react-components/react-menu/library/src/components/MenuItem/MenuItem.types.ts +++ b/packages/react-components/react-menu/library/src/components/MenuItem/MenuItem.types.ts @@ -1,7 +1,7 @@ -import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; +import type { ComponentProps, ComponentState, ExtractSlotProps, Slot } from '@fluentui/react-utilities'; export type MenuItemSlots = { - root: Slot<'div'>; + root: Slot>, 'content'>>; /** * Icon slot rendered before children content @@ -31,29 +31,28 @@ export type MenuItemSlots = { secondaryContent?: Slot<'span'>; }; -export type MenuItemProps = Omit>, 'content'> & - Pick, 'content'> & { - /** - * If the menu item is a trigger for a submenu - * - * @default false - */ - hasSubmenu?: boolean; - - /** - * Clicking on the menu item will not dismiss an open menu - * - * @default false - */ - persistOnClick?: boolean; - - disabled?: boolean; - /** - * @deprecated this property does nothing. - * disabled focusable is by default by simply using `disabled` property - */ - disabledFocusable?: boolean; - }; +export type MenuItemProps = ComponentProps> & { + /** + * If the menu item is a trigger for a submenu + * + * @default false + */ + hasSubmenu?: boolean; + + /** + * Clicking on the menu item will not dismiss an open menu + * + * @default false + */ + persistOnClick?: boolean; + + disabled?: boolean; + /** + * @deprecated this property does nothing. + * disabled focusable is by default by simply using `disabled` property + */ + disabledFocusable?: boolean; +}; export type MenuItemState = ComponentState & Required>; diff --git a/packages/react-components/react-menu/library/src/components/MenuItem/useMenuItem.tsx b/packages/react-components/react-menu/library/src/components/MenuItem/useMenuItem.tsx index a2df551af767b4..ae749a3ae9b85e 100644 --- a/packages/react-components/react-menu/library/src/components/MenuItem/useMenuItem.tsx +++ b/packages/react-components/react-menu/library/src/components/MenuItem/useMenuItem.tsx @@ -13,12 +13,7 @@ import { import { useMenuListContext_unstable } from '../../contexts/menuListContext'; import { useMenuContext_unstable } from '../../contexts/menuContext'; import type { MenuItemProps, MenuItemState } from './MenuItem.types'; -import { - ARIAButtonElement, - ARIAButtonElementIntersection, - ARIAButtonProps, - useARIAButtonProps, -} from '@fluentui/react-aria'; +import { ARIAButtonElement, ARIAButtonElementIntersection, useARIAButtonProps } from '@fluentui/react-aria'; import { Enter, Space } from '@fluentui/keyboard-keys'; const ChevronRightIcon = bundleIcon(ChevronRightFilled, ChevronRightRegular); @@ -31,7 +26,6 @@ export const useMenuItem_unstable = (props: MenuItemProps, ref: React.Ref context.persistOnItemClick); const { - as = 'div', disabled = false, hasSubmenu = isSubmenuTrigger, persistOnClick = persistOnClickContext, @@ -61,7 +55,7 @@ export const useMenuItem_unstable = (props: MenuItemProps, ref: React.Ref( 'div', - useARIAButtonProps<'div', ARIAButtonProps<'div'>>(as, { + useARIAButtonProps('div', { role: 'menuitem', ...rest, disabled: false, diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index c94af442fca157..8a2f7b1dcd7c4f 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -360,7 +360,7 @@ export const useIsomorphicLayoutEffect: typeof React_2.useEffect; export function useIsSSR(): boolean; // @public -export function useMergedRefs(...refs: (React_2.Ref | undefined)[]): RefObjectFunction; +export function useMergedRefs(...refs: (React_2.LegacyRef | undefined)[]): RefObjectFunction; // @internal (undocumented) export type UseOnClickOrScrollOutsideOptions = { diff --git a/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts b/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts index d61cbfa831f4bd..ed3eeb85f1f2c6 100644 --- a/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts +++ b/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts @@ -110,7 +110,7 @@ function getSlot( if (renderFunction || typeof children === 'function') { const render = (renderFunction || children) as SlotRenderFunction; return [ - React.Fragment, + React.Fragment as React.ElementType, { children: render(slot, rest as Omit), } as unknown as R[K], diff --git a/packages/react-components/react-utilities/src/hooks/useMergedRefs.ts b/packages/react-components/react-utilities/src/hooks/useMergedRefs.ts index b2ae3599b3faf7..5ca23dbe9c82bf 100644 --- a/packages/react-components/react-utilities/src/hooks/useMergedRefs.ts +++ b/packages/react-components/react-utilities/src/hooks/useMergedRefs.ts @@ -6,30 +6,44 @@ import * as React from 'react'; */ export type RefObjectFunction = React.RefObject & ((value: T) => void); +/** @internal */ +type MutableRefObjectFunction = React.MutableRefObject & ((value: T) => void); + /** * React hook to merge multiple React refs (either MutableRefObjects or ref callbacks) into a single ref callback that * updates all provided refs * @param refs - Refs to collectively update with one ref value. * @returns A function with an attached "current" prop, so that it can be treated like a RefObject. */ -export function useMergedRefs(...refs: (React.Ref | undefined)[]): RefObjectFunction { - const mergedCallback: RefObjectFunction = React.useCallback( +// LegacyRef is actually not supported, but in React v18 types this is leaking directly from forwardRef component declaration +export function useMergedRefs(...refs: (React.LegacyRef | undefined)[]): RefObjectFunction { + const mergedCallback = React.useCallback( (value: T) => { // Update the "current" prop hanging on the function. - (mergedCallback as unknown as React.MutableRefObject).current = value; + mergedCallback.current = value; for (const ref of refs) { + if (typeof ref === 'string' && process.env.NODE_ENV !== 'production') { + // eslint-disable-next-line no-console + console.error(/** #__DE-INDENT__ */ ` + @fluentui/react-utilities [useMergedRefs]: + This hook does not support the usage of string refs. Please use React.useRef instead. + + For more info on 'React.useRef', see https://react.dev/reference/react/useRef. + For more info on string refs, see https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-string-refs. + `); + } if (typeof ref === 'function') { ref(value); } else if (ref) { // work around the immutability of the React.Ref type - (ref as unknown as React.MutableRefObject).current = value; + (ref as React.MutableRefObject).current = value; } } }, // eslint-disable-next-line react-hooks/exhaustive-deps -- already exhaustive [...refs], - ) as unknown as RefObjectFunction; + ) as MutableRefObjectFunction; return mergedCallback; } From 4da2e2c1ea21057577c01d3aa9f01ec5e8ab88ee Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Mon, 10 Jun 2024 09:00:07 +0000 Subject: [PATCH 4/8] chore: loosen types for deprecated API --- .../etc/react-utilities.api.md | 2 +- .../src/compose/deprecated/getSlots.ts | 27 ++++--------------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index 8a2f7b1dcd7c4f..80366a12319871 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -301,7 +301,7 @@ export type SlotRenderFunction = (Component: React_2.ElementType, // @public @deprecated (undocumented) export type Slots = { - [K in keyof S]: ExtractSlotProps extends AsIntrinsicElement ? As : ExtractSlotProps extends React_2.ComponentType ? React_2.ElementType> : React_2.ElementType>; + [K in keyof S]: React_2.ElementType; }; // @public diff --git a/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts b/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts index ed3eeb85f1f2c6..cc71f4f22e2e72 100644 --- a/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts +++ b/packages/react-components/react-utilities/src/compose/deprecated/getSlots.ts @@ -1,40 +1,23 @@ import * as React from 'react'; import { omit } from '../../utils/omit'; -import type { - AsIntrinsicElement, - ComponentState, - ExtractSlotProps, - SlotPropsRecord, - SlotRenderFunction, - UnknownSlotProps, -} from '../types'; +import type { ComponentState, SlotPropsRecord, SlotRenderFunction, UnknownSlotProps } from '../types'; import { isSlot } from '../isSlot'; import { SLOT_RENDER_FUNCTION_SYMBOL } from '../constants'; -import { UnionToIntersection } from '../../utils/types'; /** * @deprecated - use slot.always or slot.optional combined with assertSlots instead */ export type Slots = { - [K in keyof S]: ExtractSlotProps extends AsIntrinsicElement - ? // for slots with an `as` prop, the slot will be any one of the possible values of `as` - As - : ExtractSlotProps extends React.ComponentType - ? React.ElementType> - : React.ElementType>; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [K in keyof S]: React.ElementType; }; /** * @deprecated - use slot.always or slot.optional combined with assertSlots instead */ export type ObjectSlotProps = { - [K in keyof S]-?: ExtractSlotProps extends AsIntrinsicElement - ? // For intrinsic element types, return the intersection of all possible - // element's props, to be compatible with the As type returned by Slots<> - UnionToIntersection // Slot<'div', 'span'> - : ExtractSlotProps extends React.ComponentType - ? P // Slot - : ExtractSlotProps; // Slot + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [K in keyof S]-?: any; // Slot }; /** From ae94e978a8db9b5a241a5a62531c0ff5793c1f4a Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Tue, 11 Jun 2024 09:54:47 +0000 Subject: [PATCH 5/8] chore: updates WithSlotShorthandValue internals --- .../react-components/react-utilities/src/compose/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-utilities/src/compose/types.ts b/packages/react-components/react-utilities/src/compose/types.ts index d7e497e498804e..e94e53579c262b 100644 --- a/packages/react-components/react-utilities/src/compose/types.ts +++ b/packages/react-components/react-utilities/src/compose/types.ts @@ -48,9 +48,9 @@ export type UnknownSlotProps = Pick, 'children /** * Helper type for {@link Slot}. Adds shorthand types that are assignable to the slot's `children`. */ -type WithSlotShorthandValue = +type WithSlotShorthandValue = | Props - | Extract; + | ('children' extends keyof Props ? Extract : never); /** * @internal From 5fb2a827fef12380aaa686a491ac469b646e2901 Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Tue, 11 Jun 2024 11:49:39 +0000 Subject: [PATCH 6/8] chore: updates getSlotsNext tests --- .../src/compose/deprecated/getSlotsNext.test.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.test.tsx b/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.test.tsx index 578579831a5d1d..017d38241ce4df 100644 --- a/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.test.tsx +++ b/packages/react-components/react-utilities/src/compose/deprecated/getSlotsNext.test.tsx @@ -1,11 +1,10 @@ import * as React from 'react'; import { getSlotsNext } from './getSlotsNext'; -import type { ExtractSlotProps, Slot, SlotComponentType, UnknownSlotProps } from '../types'; +import type { ExtractSlotProps, Slot, SlotComponentType, SlotPropsDataType } from '../types'; import { resolveShorthand } from './resolveShorthand'; import { SLOT_RENDER_FUNCTION_SYMBOL } from '../constants'; -// eslint-disable-next-line deprecation/deprecation -const resolveShorthandMock = (props: Props): SlotComponentType => { +const resolveShorthandMock = (props: Props): SlotComponentType => { // casting is required here as SlotComponent is a callable return { ...props } as SlotComponentType; }; From 01f4809452b7e062c18396840b3f6798da1d8715 Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Tue, 18 Jun 2024 17:58:58 +0000 Subject: [PATCH 7/8] feature(react-utilities): support for data-* attributes --- ...-b650b8ef-a676-48a6-a90f-a0a5c12d4d40.json | 7 ++ ...-75d223db-26c4-4e67-9efc-27d2c87d4746.json | 7 ++ ...-0675581f-93c2-4eb6-8777-245169e63ccc.json | 7 ++ ...-9c771276-6832-439e-9536-8a5a65c93638.json | 7 ++ ...-77636efd-b545-46f3-8d29-4fdf7974fbe4.json | 7 ++ ...-0129d42b-1550-4862-a3fd-cc8ad57f7e3c.json | 7 ++ ...-1db35c3c-7f69-412b-ab0f-c279a9f7132e.json | 7 ++ ...-63fb5fd8-d730-4e34-ba24-a909c8178866.json | 7 ++ ...-7cc19258-0545-4a21-b262-6d1dc8d513b3.json | 7 ++ ...-8ad435b5-7fc5-4716-bf03-35ea896e2148.json | 7 ++ .../react-aria/library/etc/react-aria.api.md | 6 +- .../react-aria/library/src/button/types.ts | 14 +++- .../library/etc/react-breadcrumb.api.md | 6 +- .../BreadcrumbButton/BreadcrumbButton.tsx | 3 +- .../BreadcrumbButton.types.ts | 6 +- .../library/etc/react-button.api.md | 6 +- .../src/components/Button/useButton.ts | 16 +++-- .../components/SplitButton/SplitButton.tsx | 3 +- .../SplitButton/SplitButton.types.ts | 11 ++- .../components/SplitButton/useSplitButton.ts | 5 +- .../CarouselNavButton/useCarouselNavButton.ts | 4 +- .../useCarouselNavImageButton.ts | 4 +- .../src/components/Combobox/Combobox.test.tsx | 6 +- .../etc/react-datepicker-compat.api.md | 7 +- .../components/DatePicker/DatePicker.types.ts | 5 +- .../DatePicker/renderDatePicker.tsx | 5 +- .../components/DatePicker/useDatePicker.tsx | 56 +++++++-------- .../DatePicker/useDatePickerStyles.styles.ts | 2 +- .../library/etc/react-dialog.api.md | 6 +- .../DialogTrigger/DialogTrigger.types.ts | 14 ++-- .../components/InfoButton/useInfoButton.tsx | 2 +- .../react-link/library/etc/react-link.api.md | 3 +- .../library/src/components/Link/Link.types.ts | 4 +- .../library/src/components/Link/useLink.ts | 2 +- .../react-menu/library/etc/react-menu.api.md | 15 ++-- .../MenuTrigger/MenuTrigger.types.ts | 31 ++++----- .../components/MenuTrigger/useMenuTrigger.ts | 2 +- .../etc/react-migration-v0-v9.api.md | 5 +- .../src/components/NavDrawer/useNavDrawer.ts | 7 +- .../library/etc/react-popover.api.md | 15 ++-- .../PopoverTrigger/PopoverTrigger.types.ts | 25 ++++--- .../components/ColorSwatch/useColorSwatch.tsx | 2 +- .../src/components/DataGrid/DataGrid.tsx | 3 +- .../library/src/components/Tab/useTab.ts | 2 +- .../useInteractionTagSecondary.tsx | 2 +- .../library/src/components/Tag/Tag.tsx | 3 +- .../TeachingPopoverCarouselFooterButton.tsx | 3 +- .../etc/react-utilities.api.md | 21 ++++-- .../src/compose/getIntrinsicElementProps.ts | 16 ++--- .../react-utilities/src/compose/types.ts | 69 +++++++------------ .../react-utilities/src/index.ts | 3 +- .../src/trigger/getTriggerChild.ts | 2 +- .../react-utilities/src/trigger/types.ts | 3 +- .../react-utilities/src/utils/types.ts | 34 +++++++++ 54 files changed, 316 insertions(+), 213 deletions(-) create mode 100644 change/@fluentui-react-breadcrumb-b650b8ef-a676-48a6-a90f-a0a5c12d4d40.json create mode 100644 change/@fluentui-react-button-75d223db-26c4-4e67-9efc-27d2c87d4746.json create mode 100644 change/@fluentui-react-dialog-0675581f-93c2-4eb6-8777-245169e63ccc.json create mode 100644 change/@fluentui-react-infolabel-9c771276-6832-439e-9536-8a5a65c93638.json create mode 100644 change/@fluentui-react-link-77636efd-b545-46f3-8d29-4fdf7974fbe4.json create mode 100644 change/@fluentui-react-nav-preview-0129d42b-1550-4862-a3fd-cc8ad57f7e3c.json create mode 100644 change/@fluentui-react-popover-1db35c3c-7f69-412b-ab0f-c279a9f7132e.json create mode 100644 change/@fluentui-react-swatch-picker-63fb5fd8-d730-4e34-ba24-a909c8178866.json create mode 100644 change/@fluentui-react-tags-7cc19258-0545-4a21-b262-6d1dc8d513b3.json create mode 100644 change/@fluentui-react-teaching-popover-8ad435b5-7fc5-4716-bf03-35ea896e2148.json diff --git a/change/@fluentui-react-breadcrumb-b650b8ef-a676-48a6-a90f-a0a5c12d4d40.json b/change/@fluentui-react-breadcrumb-b650b8ef-a676-48a6-a90f-a0a5c12d4d40.json new file mode 100644 index 00000000000000..29a861d0f8e519 --- /dev/null +++ b/change/@fluentui-react-breadcrumb-b650b8ef-a676-48a6-a90f-a0a5c12d4d40.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feature: support for data-* attributes", + "packageName": "@fluentui/react-breadcrumb", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-button-75d223db-26c4-4e67-9efc-27d2c87d4746.json b/change/@fluentui-react-button-75d223db-26c4-4e67-9efc-27d2c87d4746.json new file mode 100644 index 00000000000000..28604c60b1d9d7 --- /dev/null +++ b/change/@fluentui-react-button-75d223db-26c4-4e67-9efc-27d2c87d4746.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feature: support for data-* attributes", + "packageName": "@fluentui/react-button", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-dialog-0675581f-93c2-4eb6-8777-245169e63ccc.json b/change/@fluentui-react-dialog-0675581f-93c2-4eb6-8777-245169e63ccc.json new file mode 100644 index 00000000000000..23639605d853c4 --- /dev/null +++ b/change/@fluentui-react-dialog-0675581f-93c2-4eb6-8777-245169e63ccc.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feature: support for data-* attributes", + "packageName": "@fluentui/react-dialog", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-infolabel-9c771276-6832-439e-9536-8a5a65c93638.json b/change/@fluentui-react-infolabel-9c771276-6832-439e-9536-8a5a65c93638.json new file mode 100644 index 00000000000000..44d43e4475d112 --- /dev/null +++ b/change/@fluentui-react-infolabel-9c771276-6832-439e-9536-8a5a65c93638.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feature: support for data-* attributes", + "packageName": "@fluentui/react-infolabel", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-link-77636efd-b545-46f3-8d29-4fdf7974fbe4.json b/change/@fluentui-react-link-77636efd-b545-46f3-8d29-4fdf7974fbe4.json new file mode 100644 index 00000000000000..48103686a59d91 --- /dev/null +++ b/change/@fluentui-react-link-77636efd-b545-46f3-8d29-4fdf7974fbe4.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feature: support for data-* attributes", + "packageName": "@fluentui/react-link", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-nav-preview-0129d42b-1550-4862-a3fd-cc8ad57f7e3c.json b/change/@fluentui-react-nav-preview-0129d42b-1550-4862-a3fd-cc8ad57f7e3c.json new file mode 100644 index 00000000000000..ba3748a358071c --- /dev/null +++ b/change/@fluentui-react-nav-preview-0129d42b-1550-4862-a3fd-cc8ad57f7e3c.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feature: support for data-* attributes", + "packageName": "@fluentui/react-nav-preview", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-popover-1db35c3c-7f69-412b-ab0f-c279a9f7132e.json b/change/@fluentui-react-popover-1db35c3c-7f69-412b-ab0f-c279a9f7132e.json new file mode 100644 index 00000000000000..2ac995ba2da6ea --- /dev/null +++ b/change/@fluentui-react-popover-1db35c3c-7f69-412b-ab0f-c279a9f7132e.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feature: support for data-* attributes", + "packageName": "@fluentui/react-popover", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-swatch-picker-63fb5fd8-d730-4e34-ba24-a909c8178866.json b/change/@fluentui-react-swatch-picker-63fb5fd8-d730-4e34-ba24-a909c8178866.json new file mode 100644 index 00000000000000..5e96c5eba5af38 --- /dev/null +++ b/change/@fluentui-react-swatch-picker-63fb5fd8-d730-4e34-ba24-a909c8178866.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feature: support for data-* attributes", + "packageName": "@fluentui/react-swatch-picker", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-tags-7cc19258-0545-4a21-b262-6d1dc8d513b3.json b/change/@fluentui-react-tags-7cc19258-0545-4a21-b262-6d1dc8d513b3.json new file mode 100644 index 00000000000000..318416c8263225 --- /dev/null +++ b/change/@fluentui-react-tags-7cc19258-0545-4a21-b262-6d1dc8d513b3.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feature: support for data-* attributes", + "packageName": "@fluentui/react-tags", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-teaching-popover-8ad435b5-7fc5-4716-bf03-35ea896e2148.json b/change/@fluentui-react-teaching-popover-8ad435b5-7fc5-4716-bf03-35ea896e2148.json new file mode 100644 index 00000000000000..09d50a70c0fa15 --- /dev/null +++ b/change/@fluentui-react-teaching-popover-8ad435b5-7fc5-4716-bf03-35ea896e2148.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feature: support for data-* attributes", + "packageName": "@fluentui/react-teaching-popover", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-aria/library/etc/react-aria.api.md b/packages/react-components/react-aria/library/etc/react-aria.api.md index 0c1aed6549f588..30c23a58c1159a 100644 --- a/packages/react-components/react-aria/library/etc/react-aria.api.md +++ b/packages/react-components/react-aria/library/etc/react-aria.api.md @@ -6,7 +6,9 @@ import type { AnnounceContextValue } from '@fluentui/react-shared-contexts'; import type { DistributiveOmit } from '@fluentui/react-utilities'; +import type { DistributivePick } from '@fluentui/react-utilities'; import type { ExtractSlotProps } from '@fluentui/react-utilities'; +import type { HTMLDataAttributes } from '@fluentui/react-utilities'; import * as React_2 from 'react'; import type { ResolveShorthandFunction } from '@fluentui/react-utilities'; import type { Slot } from '@fluentui/react-utilities'; @@ -74,10 +76,10 @@ export type ARIAButtonProps = Dist }; // @public -export type ARIAButtonResultProps = Props & UnionToIntersection>; +export type ARIAButtonResultProps = Props & UnionToIntersection> & HTMLDataAttributes; // @public (undocumented) -export type ARIAButtonSlotProps = ExtractSlotProps> & Pick, 'disabled' | 'disabledFocusable'>; +export type ARIAButtonSlotProps = ExtractSlotProps> & DistributivePick, 'disabled' | 'disabledFocusable'>; // @public (undocumented) export type ARIAButtonType = 'button' | 'a' | 'div'; diff --git a/packages/react-components/react-aria/library/src/button/types.ts b/packages/react-components/react-aria/library/src/button/types.ts index 8e0ae86460c03b..a9a360b5fac48e 100644 --- a/packages/react-components/react-aria/library/src/button/types.ts +++ b/packages/react-components/react-aria/library/src/button/types.ts @@ -1,4 +1,11 @@ -import type { DistributiveOmit, ExtractSlotProps, Slot, UnionToIntersection } from '@fluentui/react-utilities'; +import type { + DistributiveOmit, + DistributivePick, + ExtractSlotProps, + HTMLDataAttributes, + Slot, + UnionToIntersection, +} from '@fluentui/react-utilities'; import * as React from 'react'; export type ARIAButtonType = 'button' | 'a' | 'div'; @@ -37,7 +44,7 @@ export type ARIAButtonProps = Dist export type ARIAButtonSlotProps = ExtractSlotProps< Slot<'button', AlternateAs> > & - Pick, 'disabled' | 'disabledFocusable'>; + DistributivePick, 'disabled' | 'disabledFocusable'>; /** * Props that will be modified internally by `useARIAButtonProps` by each case. @@ -64,4 +71,5 @@ export type ARIAButtonAlteredProps = * Merge of props provided by the user and props provided internally. */ export type ARIAButtonResultProps = Props & - UnionToIntersection>; + UnionToIntersection> & + HTMLDataAttributes; diff --git a/packages/react-components/react-breadcrumb/library/etc/react-breadcrumb.api.md b/packages/react-components/react-breadcrumb/library/etc/react-breadcrumb.api.md index 305bb41522a653..3c8553478bdc95 100644 --- a/packages/react-components/react-breadcrumb/library/etc/react-breadcrumb.api.md +++ b/packages/react-components/react-breadcrumb/library/etc/react-breadcrumb.api.md @@ -11,6 +11,8 @@ import { ButtonSlots } from '@fluentui/react-button'; import { ButtonState } from '@fluentui/react-button'; import type { ComponentProps } from '@fluentui/react-utilities'; import type { ComponentState } from '@fluentui/react-utilities'; +import type { DistributiveOmit } from '@fluentui/react-utilities'; +import type { DistributivePick } from '@fluentui/react-utilities'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; import * as React_2 from 'react'; import type { Slot } from '@fluentui/react-utilities'; @@ -26,7 +28,7 @@ export const BreadcrumbButton: ForwardRefComponent; export const breadcrumbButtonClassNames: SlotClassNames; // @public -export type BreadcrumbButtonProps = ComponentProps & Pick & Pick & { +export type BreadcrumbButtonProps = ComponentProps & Pick & DistributivePick & { current?: boolean; }; @@ -34,7 +36,7 @@ export type BreadcrumbButtonProps = ComponentProps & Pick export type BreadcrumbButtonSlots = ButtonSlots; // @public -export type BreadcrumbButtonState = ComponentState & Omit & Required>; +export type BreadcrumbButtonState = ComponentState & DistributiveOmit & Required>; // @public (undocumented) export const breadcrumbClassNames: SlotClassNames; diff --git a/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/BreadcrumbButton.tsx b/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/BreadcrumbButton.tsx index 1fecd7be6c6750..0176b8a996ff7a 100644 --- a/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/BreadcrumbButton.tsx +++ b/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/BreadcrumbButton.tsx @@ -16,6 +16,7 @@ export const BreadcrumbButton: ForwardRefComponent = Reac useCustomStyleHook_unstable('useBreadcrumbButtonStyles_unstable')(state); return renderBreadcrumbButton_unstable(state); -}); + // Casting is required due to lack of distributive union to support unions on @types/react +}) as ForwardRefComponent; BreadcrumbButton.displayName = 'BreadcrumbButton'; diff --git a/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/BreadcrumbButton.types.ts b/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/BreadcrumbButton.types.ts index edf9dcff71e0b5..c344c08076af0b 100644 --- a/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/BreadcrumbButton.types.ts +++ b/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/BreadcrumbButton.types.ts @@ -1,4 +1,4 @@ -import type { ComponentProps, ComponentState } from '@fluentui/react-utilities'; +import type { ComponentProps, ComponentState, DistributiveOmit, DistributivePick } from '@fluentui/react-utilities'; import { ButtonProps, ButtonSlots, ButtonState } from '@fluentui/react-button'; import { BreadcrumbProps } from '../Breadcrumb/Breadcrumb.types'; @@ -9,7 +9,7 @@ export type BreadcrumbButtonSlots = ButtonSlots; */ export type BreadcrumbButtonProps = ComponentProps & Pick & - Pick & { + DistributivePick & { /** * Defines current sate of BreadcrumbButton. * @@ -22,5 +22,5 @@ export type BreadcrumbButtonProps = ComponentProps & * State used in rendering BreadcrumbButton */ export type BreadcrumbButtonState = ComponentState & - Omit & + DistributiveOmit & Required>; diff --git a/packages/react-components/react-button/library/etc/react-button.api.md b/packages/react-components/react-button/library/etc/react-button.api.md index 2a565c1a7b2060..49ff3127f0d5cd 100644 --- a/packages/react-components/react-button/library/etc/react-button.api.md +++ b/packages/react-components/react-button/library/etc/react-button.api.md @@ -106,7 +106,7 @@ export const SplitButton: ForwardRefComponent; export const splitButtonClassNames: SlotClassNames; // @public (undocumented) -export type SplitButtonProps = ComponentProps & Omit & Omit; +export type SplitButtonProps = ComponentProps & Pick & Pick; // @public (undocumented) export type SplitButtonSlots = { @@ -116,7 +116,7 @@ export type SplitButtonSlots = { }; // @public (undocumented) -export type SplitButtonState = ComponentState & Omit & Omit; +export type SplitButtonState = ComponentState & Pick; // @public export const ToggleButton: ForwardRefComponent; @@ -155,7 +155,7 @@ export const useMenuButton_unstable: ({ menuIcon, ...props }: MenuButtonProps, r export const useMenuButtonStyles_unstable: (state: MenuButtonState) => MenuButtonState; // @public -export const useSplitButton_unstable: (props: SplitButtonProps, ref: React_2.Ref) => SplitButtonState; +export const useSplitButton_unstable: (props: SplitButtonProps, ref: React_2.Ref) => SplitButtonState; // @public (undocumented) export const useSplitButtonStyles_unstable: (state: SplitButtonState) => SplitButtonState; diff --git a/packages/react-components/react-button/library/src/components/Button/useButton.ts b/packages/react-components/react-button/library/src/components/Button/useButton.ts index 90851fd2238044..feb21c4018793b 100644 --- a/packages/react-components/react-button/library/src/components/Button/useButton.ts +++ b/packages/react-components/react-button/library/src/components/Button/useButton.ts @@ -16,7 +16,6 @@ export const useButton_unstable = ( const { size: contextSize } = useButtonContext(); const { appearance = 'secondary', - as = 'button', disabled = false, disabledFocusable = false, icon, @@ -35,13 +34,16 @@ export const useButton_unstable = ( size, // State calculated from a set of props iconOnly: Boolean(iconShorthand?.children && !props.children), // Slots definition components: { root: 'button', icon: 'span' }, - root: slot.always>(getIntrinsicElementProps(as, useARIAButtonProps(props.as, props)), { - elementType: 'button', - defaultProps: { - ref: ref as React.Ref, - type: 'button', + root: slot.always>( + getIntrinsicElementProps('button', useARIAButtonProps(props.as, props)), + { + elementType: 'button', + defaultProps: { + ref: ref as React.Ref, + type: 'button', + }, }, - }), + ), icon: iconShorthand, }; }; diff --git a/packages/react-components/react-button/library/src/components/SplitButton/SplitButton.tsx b/packages/react-components/react-button/library/src/components/SplitButton/SplitButton.tsx index 6896cad7436621..793bff67b22bcd 100644 --- a/packages/react-components/react-button/library/src/components/SplitButton/SplitButton.tsx +++ b/packages/react-components/react-button/library/src/components/SplitButton/SplitButton.tsx @@ -18,7 +18,6 @@ export const SplitButton: ForwardRefComponent = React.forwardR useCustomStyleHook_unstable('useSplitButtonStyles_unstable')(state); return renderSplitButton_unstable(state); - // Casting is required due to lack of distributive union to support unions on @types/react -}) as ForwardRefComponent; +}); SplitButton.displayName = 'SplitButton'; diff --git a/packages/react-components/react-button/library/src/components/SplitButton/SplitButton.types.ts b/packages/react-components/react-button/library/src/components/SplitButton/SplitButton.types.ts index 6d67668ddef3b2..e62a01eaf207e7 100644 --- a/packages/react-components/react-button/library/src/components/SplitButton/SplitButton.types.ts +++ b/packages/react-components/react-button/library/src/components/SplitButton/SplitButton.types.ts @@ -1,8 +1,8 @@ import { Button } from '../Button/Button'; import { MenuButton } from '../MenuButton/MenuButton'; import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; -import type { ButtonProps, ButtonState } from '../Button/Button.types'; -import type { MenuButtonProps, MenuButtonState } from '../MenuButton/MenuButton.types'; +import type { ButtonProps } from '../Button/Button.types'; +import { MenuButtonProps } from '../MenuButton/MenuButton.types'; export type SplitButtonSlots = { /** @@ -21,9 +21,8 @@ export type SplitButtonSlots = { }; export type SplitButtonProps = ComponentProps & - Omit & - Omit; + Pick & + Pick; export type SplitButtonState = ComponentState & - Omit & - Omit; + Pick; diff --git a/packages/react-components/react-button/library/src/components/SplitButton/useSplitButton.ts b/packages/react-components/react-button/library/src/components/SplitButton/useSplitButton.ts index d554f6de11ee25..ab5784557af433 100644 --- a/packages/react-components/react-button/library/src/components/SplitButton/useSplitButton.ts +++ b/packages/react-components/react-button/library/src/components/SplitButton/useSplitButton.ts @@ -9,10 +9,7 @@ import type { SplitButtonProps, SplitButtonState } from './SplitButton.types'; * @param props - User provided props to the SplitButton component. * @param ref - User provided ref to be passed to the SplitButton component. */ -export const useSplitButton_unstable = ( - props: SplitButtonProps, - ref: React.Ref, -): SplitButtonState => { +export const useSplitButton_unstable = (props: SplitButtonProps, ref: React.Ref): SplitButtonState => { const { appearance = 'secondary', children, diff --git a/packages/react-components/react-carousel-preview/library/src/components/CarouselNavButton/useCarouselNavButton.ts b/packages/react-components/react-carousel-preview/library/src/components/CarouselNavButton/useCarouselNavButton.ts index bddb16c605be71..784e4601f6038a 100644 --- a/packages/react-components/react-carousel-preview/library/src/components/CarouselNavButton/useCarouselNavButton.ts +++ b/packages/react-components/react-carousel-preview/library/src/components/CarouselNavButton/useCarouselNavButton.ts @@ -20,7 +20,7 @@ export const useCarouselNavButton_unstable = ( props: CarouselNavButtonProps, ref: React.Ref, ): CarouselNavButtonState => { - const { onClick, as = 'button' } = props; + const { onClick } = props; const value = useCarouselNavContext(); @@ -40,7 +40,7 @@ export const useCarouselNavButton_unstable = ( }); const _carouselButton = slot.always( - getIntrinsicElementProps(as, useARIAButtonProps(props.as, props)), + getIntrinsicElementProps('button', useARIAButtonProps(props.as, props)), { elementType: 'button', defaultProps: { diff --git a/packages/react-components/react-carousel-preview/library/src/components/CarouselNavImageButton/useCarouselNavImageButton.ts b/packages/react-components/react-carousel-preview/library/src/components/CarouselNavImageButton/useCarouselNavImageButton.ts index 330e85805f36f7..65c96c78673aa9 100644 --- a/packages/react-components/react-carousel-preview/library/src/components/CarouselNavImageButton/useCarouselNavImageButton.ts +++ b/packages/react-components/react-carousel-preview/library/src/components/CarouselNavImageButton/useCarouselNavImageButton.ts @@ -20,7 +20,7 @@ export const useCarouselNavImageButton_unstable = ( props: CarouselNavImageButtonProps, ref: React.Ref, ): CarouselNavImageButtonState => { - const { onClick, as = 'button' } = props; + const { onClick } = props; const value = useCarouselNavContext(); @@ -40,7 +40,7 @@ export const useCarouselNavImageButton_unstable = ( }); const _carouselButton = slot.always( - getIntrinsicElementProps(as, useARIAButtonProps(props.as, props)), + getIntrinsicElementProps('button', useARIAButtonProps(props.as, props)), { elementType: 'button', defaultProps: { diff --git a/packages/react-components/react-combobox/library/src/components/Combobox/Combobox.test.tsx b/packages/react-components/react-combobox/library/src/components/Combobox/Combobox.test.tsx index a34c0af0faa837..5d956d31d44967 100644 --- a/packages/react-components/react-combobox/library/src/components/Combobox/Combobox.test.tsx +++ b/packages/react-components/react-combobox/library/src/components/Combobox/Combobox.test.tsx @@ -1100,7 +1100,7 @@ describe('Combobox', () => { it('opens the popup on expand icon click', () => { const { getByRole, getByTestId } = render( - }> + @@ -1113,7 +1113,7 @@ describe('Combobox', () => { it('closes the popup on expand icon click', () => { const { getByTestId, queryByRole } = render( - }> + @@ -1129,7 +1129,7 @@ describe('Combobox', () => { it('closes the popup on blur/outside click after clicking on the expand icon', () => { const { getByTestId, queryByRole } = render( <> - }> + diff --git a/packages/react-components/react-datepicker-compat/library/etc/react-datepicker-compat.api.md b/packages/react-components/react-datepicker-compat/library/etc/react-datepicker-compat.api.md index 31abc94e4f5775..261ed63d9f016a 100644 --- a/packages/react-components/react-datepicker-compat/library/etc/react-datepicker-compat.api.md +++ b/packages/react-components/react-datepicker-compat/library/etc/react-datepicker-compat.api.md @@ -6,7 +6,7 @@ /// -import type { Calendar } from '@fluentui/react-calendar-compat'; +import type { CalendarProps } from '@fluentui/react-calendar-compat'; import { CalendarStrings } from '@fluentui/react-calendar-compat'; import type { ComponentProps } from '@fluentui/react-utilities'; import type { ComponentState } from '@fluentui/react-utilities'; @@ -27,13 +27,16 @@ export { CalendarStrings } export const DatePicker: ForwardRefComponent; // @public (undocumented) -export const datePickerClassNames: SlotClassNames; +export const datePickerClassNames: SlotClassNames & { + calendar: string; +}; // @public export type DatePickerErrorType = 'invalid-input' | 'out-of-bounds' | 'required-input'; // @public (undocumented) export type DatePickerProps = Omit>, 'defaultValue' | 'value'> & Pick & { + calendar?: CalendarProps; onSelectDate?: (date: Date | null | undefined) => void; required?: boolean; disabled?: boolean; diff --git a/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/DatePicker.types.ts b/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/DatePicker.types.ts index 55d780701f5d80..d48e76f377b9c0 100644 --- a/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/DatePicker.types.ts +++ b/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/DatePicker.types.ts @@ -1,18 +1,18 @@ import { DayOfWeek, FirstWeekOfYear } from '@fluentui/react-calendar-compat'; import { Input } from '@fluentui/react-input'; import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; -import type { Calendar, CalendarStrings, DateFormatting } from '@fluentui/react-calendar-compat'; +import type { CalendarProps, CalendarStrings, DateFormatting } from '@fluentui/react-calendar-compat'; import type { PortalProps } from '@fluentui/react-portal'; import type { PositioningProps } from '@fluentui/react-positioning'; export type DatePickerSlots = { root: NonNullable>; - calendar: NonNullable>; popupSurface?: Slot<'div'>; }; export type DatePickerProps = Omit>, 'defaultValue' | 'value'> & Pick & { + calendar?: CalendarProps; /** * Callback issued when a date is selected */ @@ -222,6 +222,7 @@ export type DatePickerProps = Omit>, 'de */ export type DatePickerState = ComponentState & Pick & { + calendar: CalendarProps; disabled: boolean; inlinePopup: boolean; }; diff --git a/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/renderDatePicker.tsx b/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/renderDatePicker.tsx index 5b5a3dde4d030a..e10dd16404a62b 100644 --- a/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/renderDatePicker.tsx +++ b/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/renderDatePicker.tsx @@ -4,6 +4,7 @@ import { Portal } from '@fluentui/react-portal'; import { assertSlots } from '@fluentui/react-utilities'; import type { DatePickerSlots, DatePickerState } from './DatePicker.types'; +import { Calendar } from '@fluentui/react-calendar-compat'; /** * Render the final JSX of DatePicker @@ -18,12 +19,12 @@ export const renderDatePicker_unstable = (state: DatePickerState) => { {state.popupSurface && (inlinePopup ? ( - + ) : ( - + ))} diff --git a/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/useDatePicker.tsx b/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/useDatePicker.tsx index c4a112b7955532..4feb0214b64eaa 100644 --- a/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/useDatePicker.tsx +++ b/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/useDatePicker.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { ArrowDown, Enter, Escape } from '@fluentui/keyboard-keys'; -import { Calendar, compareDatePart, DayOfWeek, FirstWeekOfYear } from '@fluentui/react-calendar-compat'; +import { compareDatePart, DayOfWeek, FirstWeekOfYear } from '@fluentui/react-calendar-compat'; import { CalendarMonthRegular } from '@fluentui/react-icons'; import { defaultDatePickerStrings } from './defaults'; import { Input } from '@fluentui/react-input'; @@ -13,19 +13,13 @@ import { useOnClickOutside, useOnScrollOutside, slot, - ExtractSlotProps, } from '@fluentui/react-utilities'; import { useFieldContext_unstable as useFieldContext } from '@fluentui/react-field'; import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; import { useModalAttributes } from '@fluentui/react-tabster'; import { usePopupPositioning } from '../../utils/usePopupPositioning'; -import type { CalendarProps, ICalendar } from '@fluentui/react-calendar-compat'; -import type { - DatePickerProps, - DatePickerSlots, - DatePickerState, - DatePickerValidationResultData, -} from './DatePicker.types'; +import type { ICalendar } from '@fluentui/react-calendar-compat'; +import type { DatePickerProps, DatePickerState, DatePickerValidationResultData } from './DatePicker.types'; import type { InputProps, InputOnChangeData } from '@fluentui/react-input'; function isDateOutOfBounds(date: Date, minDate?: Date, maxDate?: Date): boolean { @@ -456,34 +450,32 @@ export const useDatePicker_unstable = (props: DatePickerProps, ref: React.Ref>(props.calendar, { - defaultProps: { - allFocusable, - componentRef: calendar, - dateTimeFormatter, - firstDayOfWeek, - firstWeekOfYear, - highlightCurrentMonth, - highlightSelectedMonth, - isMonthPickerVisible, - maxDate, - minDate, - showCloseButton, - showGoToToday, - showMonthPickerAsOverlay, - showWeekNumbers, - strings, - today, - value: selectedDate || initialPickerDate, - }, - elementType: Calendar, - }); + const calendarShorthand = { + allFocusable, + componentRef: calendar, + dateTimeFormatter, + firstDayOfWeek, + firstWeekOfYear, + highlightCurrentMonth, + highlightSelectedMonth, + isMonthPickerVisible, + maxDate, + minDate, + showCloseButton, + showGoToToday, + showMonthPickerAsOverlay, + showWeekNumbers, + strings, + today, + value: selectedDate || initialPickerDate, + ...props.calendar, + }; calendarShorthand.onDismiss = useEventCallback(mergeCallbacks(calendarShorthand.onDismiss, calendarDismissed)); calendarShorthand.onSelectDate = useEventCallback(mergeCallbacks(calendarShorthand.onSelectDate, calendarDismissed)); const state: DatePickerState = { disabled: !!props.disabled, inlinePopup, - components: { root: Input, calendar: Calendar as React.FC>, popupSurface: 'div' }, + components: { root: Input, popupSurface: 'div' }, calendar: calendarShorthand, mountNode, root, diff --git a/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/useDatePickerStyles.styles.ts b/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/useDatePickerStyles.styles.ts index 7780e7c2f9ee76..29e7a1f7d79610 100644 --- a/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/useDatePickerStyles.styles.ts +++ b/packages/react-components/react-datepicker-compat/library/src/components/DatePicker/useDatePickerStyles.styles.ts @@ -3,7 +3,7 @@ import { tokens, typographyStyles } from '@fluentui/react-theme'; import type { SlotClassNames } from '@fluentui/react-utilities'; import type { DatePickerSlots, DatePickerState } from './DatePicker.types'; -export const datePickerClassNames: SlotClassNames = { +export const datePickerClassNames: SlotClassNames & { calendar: string } = { root: 'fui-DatePicker', calendar: 'fui-DatePicker__calendar', popupSurface: 'fui-DatePicker__popupSurface', diff --git a/packages/react-components/react-dialog/library/etc/react-dialog.api.md b/packages/react-components/react-dialog/library/etc/react-dialog.api.md index d2a77f0e0bd479..58061c12380e92 100644 --- a/packages/react-components/react-dialog/library/etc/react-dialog.api.md +++ b/packages/react-components/react-dialog/library/etc/react-dialog.api.md @@ -6,7 +6,6 @@ /// -import { ARIAButtonResultProps } from '@fluentui/react-aria'; import { ARIAButtonType } from '@fluentui/react-aria'; import type { ComponentProps } from '@fluentui/react-utilities'; import type { ComponentState } from '@fluentui/react-utilities'; @@ -205,9 +204,10 @@ export const DialogTrigger: React_2.FC; export type DialogTriggerAction = 'open' | 'close'; // @public -export type DialogTriggerChildProps = ARIAButtonResultProps = { 'aria-haspopup'?: 'dialog'; -}>; + onClick?: React_2.MouseEventHandler; +}; // @public (undocumented) export type DialogTriggerProps = TriggerProps & { diff --git a/packages/react-components/react-dialog/library/src/components/DialogTrigger/DialogTrigger.types.ts b/packages/react-components/react-dialog/library/src/components/DialogTrigger/DialogTrigger.types.ts index 8b7d1254bdc410..d95dcdde9379db 100644 --- a/packages/react-components/react-dialog/library/src/components/DialogTrigger/DialogTrigger.types.ts +++ b/packages/react-components/react-dialog/library/src/components/DialogTrigger/DialogTrigger.types.ts @@ -1,4 +1,4 @@ -import { ARIAButtonResultProps, ARIAButtonType } from '@fluentui/react-aria'; +import { ARIAButtonType } from '@fluentui/react-aria'; import type { TriggerProps } from '@fluentui/react-utilities'; import * as React from 'react'; @@ -24,12 +24,12 @@ export type DialogTriggerProps = TriggerProps & { /** * Props that are passed to the child of the DialogTrigger when cloned to ensure correct behaviour for the Dialog */ -export type DialogTriggerChildProps = ARIAButtonResultProps< - Type, - Props & { - 'aria-haspopup'?: 'dialog'; - } ->; +export type DialogTriggerChildProps = { + 'aria-haspopup'?: 'dialog'; + /* eslint-disable @nx/workspace-consistent-callback-type -- can't change type of existing callback */ + onClick?: React.MouseEventHandler; + /* eslint-enable @nx/workspace-consistent-callback-type */ +}; export type DialogTriggerState = { children: React.ReactElement | null; diff --git a/packages/react-components/react-infolabel/library/src/components/InfoButton/useInfoButton.tsx b/packages/react-components/react-infolabel/library/src/components/InfoButton/useInfoButton.tsx index 9c6400e906fcbd..59f55a08573039 100644 --- a/packages/react-components/react-infolabel/library/src/components/InfoButton/useInfoButton.tsx +++ b/packages/react-components/react-infolabel/library/src/components/InfoButton/useInfoButton.tsx @@ -56,7 +56,7 @@ export const useInfoButton_unstable = (props: InfoButtonProps, ref: React.Ref & Required> & { +export type LinkState = ComponentState & Required> & { backgroundAppearance?: BackgroundAppearanceContextValue; }; diff --git a/packages/react-components/react-link/library/src/components/Link/Link.types.ts b/packages/react-components/react-link/library/src/components/Link/Link.types.ts index 13e221fad5bf3a..d37024f8c754fc 100644 --- a/packages/react-components/react-link/library/src/components/Link/Link.types.ts +++ b/packages/react-components/react-link/library/src/components/Link/Link.types.ts @@ -1,4 +1,4 @@ -import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; +import type { ComponentProps, ComponentState, DistributivePick, Slot } from '@fluentui/react-utilities'; import { BackgroundAppearanceContextValue } from '@fluentui/react-shared-contexts'; export type LinkSlots = { @@ -37,6 +37,6 @@ export type LinkProps = ComponentProps & { }; export type LinkState = ComponentState & - Required> & { + Required> & { backgroundAppearance?: BackgroundAppearanceContextValue; }; diff --git a/packages/react-components/react-link/library/src/components/Link/useLink.ts b/packages/react-components/react-link/library/src/components/Link/useLink.ts index 87d70432975c52..d8dee8ba2e2a69 100644 --- a/packages/react-components/react-link/library/src/components/Link/useLink.ts +++ b/packages/react-components/react-link/library/src/components/Link/useLink.ts @@ -39,7 +39,7 @@ export const useLink_unstable = ( }, root: slot.always( - getIntrinsicElementProps(elementType, { + getIntrinsicElementProps(elementType, { ref, ...propsWithAssignedAs, } as const), diff --git a/packages/react-components/react-menu/library/etc/react-menu.api.md b/packages/react-components/react-menu/library/etc/react-menu.api.md index 675e5cb104857b..a0478da5f10149 100644 --- a/packages/react-components/react-menu/library/etc/react-menu.api.md +++ b/packages/react-components/react-menu/library/etc/react-menu.api.md @@ -7,7 +7,6 @@ /// import { ARIAButtonElement } from '@fluentui/react-aria'; -import { ARIAButtonResultProps } from '@fluentui/react-aria'; import { ARIAButtonType } from '@fluentui/react-aria'; import type { ComponentProps } from '@fluentui/react-utilities'; import type { ComponentState } from '@fluentui/react-utilities'; @@ -387,16 +386,18 @@ export type MenuState = ComponentState & Required; // @public -export type MenuTriggerChildProps = ARIAButtonResultProps = { 'aria-haspopup'?: 'menu'; 'aria-expanded'?: boolean; id: string; ref: React_2.Ref; - onMouseEnter: React_2.MouseEventHandler; - onMouseLeave: React_2.MouseEventHandler; - onMouseMove: React_2.MouseEventHandler; - onContextMenu: React_2.MouseEventHandler; -}>; + onMouseEnter: React_2.MouseEventHandler; + onMouseLeave: React_2.MouseEventHandler; + onMouseMove: React_2.MouseEventHandler; + onContextMenu: React_2.MouseEventHandler; + onClick?: React_2.MouseEventHandler; + onKeyDown?: React_2.KeyboardEventHandler; +}; // @public (undocumented) export const MenuTriggerContextProvider: React_2.Provider; diff --git a/packages/react-components/react-menu/library/src/components/MenuTrigger/MenuTrigger.types.ts b/packages/react-components/react-menu/library/src/components/MenuTrigger/MenuTrigger.types.ts index 177dfdb9859841..994b70626eae47 100644 --- a/packages/react-components/react-menu/library/src/components/MenuTrigger/MenuTrigger.types.ts +++ b/packages/react-components/react-menu/library/src/components/MenuTrigger/MenuTrigger.types.ts @@ -1,4 +1,4 @@ -import { ARIAButtonResultProps, ARIAButtonType } from '@fluentui/react-aria'; +import { ARIAButtonType } from '@fluentui/react-aria'; import type { TriggerProps } from '@fluentui/react-utilities'; import * as React from 'react'; @@ -13,21 +13,20 @@ export type MenuTriggerProps = TriggerProps & { /** * Props that are passed to the child of the MenuTrigger when cloned to ensure correct behaviour for the Menu */ -export type MenuTriggerChildProps = ARIAButtonResultProps< - Type, - Props & { - 'aria-haspopup'?: 'menu'; - 'aria-expanded'?: boolean; - id: string; - ref: React.Ref; - /* eslint-disable @nx/workspace-consistent-callback-type -- can't change type of existing callback */ - onMouseEnter: React.MouseEventHandler; - onMouseLeave: React.MouseEventHandler; - onMouseMove: React.MouseEventHandler; - onContextMenu: React.MouseEventHandler; - /* eslint-enable @nx/workspace-consistent-callback-type */ - } ->; +export type MenuTriggerChildProps = { + 'aria-haspopup'?: 'menu'; + 'aria-expanded'?: boolean; + id: string; + ref: React.Ref; + /* eslint-disable @nx/workspace-consistent-callback-type -- can't change type of existing callback */ + onMouseEnter: React.MouseEventHandler; + onMouseLeave: React.MouseEventHandler; + onMouseMove: React.MouseEventHandler; + onContextMenu: React.MouseEventHandler; + onClick?: React.MouseEventHandler; + onKeyDown?: React.KeyboardEventHandler; + /* eslint-enable @nx/workspace-consistent-callback-type */ +}; export type MenuTriggerState = { children: React.ReactElement | null; diff --git a/packages/react-components/react-menu/library/src/components/MenuTrigger/useMenuTrigger.ts b/packages/react-components/react-menu/library/src/components/MenuTrigger/useMenuTrigger.ts index 14a983e3450d6f..7fd7e971d8cfed 100644 --- a/packages/react-components/react-menu/library/src/components/MenuTrigger/useMenuTrigger.ts +++ b/packages/react-components/react-menu/library/src/components/MenuTrigger/useMenuTrigger.ts @@ -143,7 +143,7 @@ export const useMenuTrigger_unstable = (props: MenuTriggerProps): MenuTriggerSta } as const; const ariaButtonTriggerChildProps = useARIAButtonProps( - child?.type === 'button' || child?.type === 'a' ? child.type : 'div', + child?.type === 'button' || child?.type === 'a' ? child?.type : 'div', triggerChildProps, ); diff --git a/packages/react-components/react-migration-v0-v9/etc/react-migration-v0-v9.api.md b/packages/react-components/react-migration-v0-v9/etc/react-migration-v0-v9.api.md index 9eb92c1978c4b8..a931e6719bf5b8 100644 --- a/packages/react-components/react-migration-v0-v9/etc/react-migration-v0-v9.api.md +++ b/packages/react-components/react-migration-v0-v9/etc/react-migration-v0-v9.api.md @@ -11,6 +11,7 @@ import type { ComponentProps as ComponentProps_2 } from '@fluentui/react-utiliti import type { ComponentState } from '@fluentui/react-utilities'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; import { GriffelStyle } from '@fluentui/react-components'; +import { HTMLDataAttributes } from '@fluentui/react-utilities'; import { ObjectOf } from '@fluentui/react-northstar'; import type { ObjectShorthandValue } from '@fluentui/react-northstar'; import * as React_2 from 'react'; @@ -85,10 +86,10 @@ export const ItemLayout: React_2.ForwardRefExoticComponent, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject | null | undefined; -}, "children"> & { +} & HTMLDataAttributes, "children"> & { children?: React_2.ReactNode | SlotRenderFunction, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject | null | undefined; - }>; + } & HTMLDataAttributes>; }, "children"> & { children?: React_2.ReactNode; }, "ref"> & React_2.RefAttributes>; diff --git a/packages/react-components/react-nav-preview/library/src/components/NavDrawer/useNavDrawer.ts b/packages/react-components/react-nav-preview/library/src/components/NavDrawer/useNavDrawer.ts index 5ec217dd052788..5a6aa85f1f0c21 100644 --- a/packages/react-components/react-nav-preview/library/src/components/NavDrawer/useNavDrawer.ts +++ b/packages/react-components/react-nav-preview/library/src/components/NavDrawer/useNavDrawer.ts @@ -36,13 +36,10 @@ export const useNavDrawer_unstable = (props: NavDrawerProps, ref: React.Ref( - { - ref, - ...props, - ...focusAttributes, - }, + { ...props, ...focusAttributes }, { elementType: Drawer, + defaultProps: { ref }, }, ), }; diff --git a/packages/react-components/react-popover/library/etc/react-popover.api.md b/packages/react-components/react-popover/library/etc/react-popover.api.md index e6cca50af5bddc..f5e5898a9a440f 100644 --- a/packages/react-components/react-popover/library/etc/react-popover.api.md +++ b/packages/react-components/react-popover/library/etc/react-popover.api.md @@ -4,7 +4,6 @@ ```ts -import { ARIAButtonResultProps } from '@fluentui/react-aria'; import { ARIAButtonType } from '@fluentui/react-aria'; import type { ComponentProps } from '@fluentui/react-utilities'; import type { ComponentState } from '@fluentui/react-utilities'; @@ -106,13 +105,15 @@ export type PopoverSurfaceState = ComponentState & Pick; // @public -export type PopoverTriggerChildProps = ARIAButtonResultProps = { 'aria-expanded'?: 'true' | 'false'; - ref: React_2.Ref; - onMouseEnter: React_2.MouseEventHandler; - onMouseLeave: React_2.MouseEventHandler; - onContextMenu: React_2.MouseEventHandler; -}>; + ref: React_2.Ref; + onMouseEnter: React_2.MouseEventHandler; + onMouseLeave: React_2.MouseEventHandler; + onContextMenu: React_2.MouseEventHandler; + onClick?: React_2.MouseEventHandler; + onKeyDown?: React_2.KeyboardEventHandler; +}; // @public export type PopoverTriggerProps = TriggerProps & { diff --git a/packages/react-components/react-popover/library/src/components/PopoverTrigger/PopoverTrigger.types.ts b/packages/react-components/react-popover/library/src/components/PopoverTrigger/PopoverTrigger.types.ts index 8f41c82e703ffa..05a7f4737a4f9b 100644 --- a/packages/react-components/react-popover/library/src/components/PopoverTrigger/PopoverTrigger.types.ts +++ b/packages/react-components/react-popover/library/src/components/PopoverTrigger/PopoverTrigger.types.ts @@ -1,4 +1,4 @@ -import { ARIAButtonResultProps, ARIAButtonType } from '@fluentui/react-aria'; +import { ARIAButtonType } from '@fluentui/react-aria'; import type { TriggerProps } from '@fluentui/react-utilities'; import * as React from 'react'; @@ -23,15 +23,14 @@ export type PopoverTriggerState = { /** * Props that are passed to the child of the DialogTrigger when cloned to ensure correct behaviour for the Dialog */ -export type PopoverTriggerChildProps = ARIAButtonResultProps< - Type, - Props & { - 'aria-expanded'?: 'true' | 'false'; - ref: React.Ref; - /* eslint-disable @nx/workspace-consistent-callback-type -- can't change type of existing callback */ - onMouseEnter: React.MouseEventHandler; - onMouseLeave: React.MouseEventHandler; - onContextMenu: React.MouseEventHandler; - /* eslint-enable @nx/workspace-consistent-callback-type */ - } ->; +export type PopoverTriggerChildProps = { + 'aria-expanded'?: 'true' | 'false'; + ref: React.Ref; + /* eslint-disable @nx/workspace-consistent-callback-type -- can't change type of existing callback */ + onMouseEnter: React.MouseEventHandler; + onMouseLeave: React.MouseEventHandler; + onContextMenu: React.MouseEventHandler; + onClick?: React.MouseEventHandler; + onKeyDown?: React.KeyboardEventHandler; + /* eslint-enable @nx/workspace-consistent-callback-type */ +}; diff --git a/packages/react-components/react-swatch-picker/library/src/components/ColorSwatch/useColorSwatch.tsx b/packages/react-components/react-swatch-picker/library/src/components/ColorSwatch/useColorSwatch.tsx index 29525fcbab0b1f..4a24c4969a9d97 100644 --- a/packages/react-components/react-swatch-picker/library/src/components/ColorSwatch/useColorSwatch.tsx +++ b/packages/react-components/react-swatch-picker/library/src/components/ColorSwatch/useColorSwatch.tsx @@ -76,7 +76,7 @@ export const useColorSwatch_unstable = ( ...rootVariables, ...style, }, - }), + } as const), { elementType: 'button' }, ), icon: iconShorthand, diff --git a/packages/react-components/react-table/library/src/components/DataGrid/DataGrid.tsx b/packages/react-components/react-table/library/src/components/DataGrid/DataGrid.tsx index 143a2e4f40f5c5..5d30f10fb20050 100644 --- a/packages/react-components/react-table/library/src/components/DataGrid/DataGrid.tsx +++ b/packages/react-components/react-table/library/src/components/DataGrid/DataGrid.tsx @@ -18,6 +18,7 @@ export const DataGrid: ForwardRefComponent = React.forwardRef((pr useCustomStyleHook_unstable('useDataGridStyles_unstable')(state); return renderDataGrid_unstable(state, useDataGridContextValues_unstable(state)); -}); + // Casting is required due to lack of distributive union to support unions on @types/react +}) as ForwardRefComponent; DataGrid.displayName = 'DataGrid'; diff --git a/packages/react-components/react-tabs/library/src/components/Tab/useTab.ts b/packages/react-components/react-tabs/library/src/components/Tab/useTab.ts index 643c04f07c74cd..7df53fcadfd257 100644 --- a/packages/react-components/react-tabs/library/src/components/Tab/useTab.ts +++ b/packages/react-components/react-tabs/library/src/components/Tab/useTab.ts @@ -75,7 +75,7 @@ export const useTab_unstable = (props: TabProps, ref: React.Ref): T disabled, onClick: onTabClick, onFocus: selectTabOnFocus ? onTabFocus : onFocus, - }), + } as const), { elementType: 'button' }, ), icon: iconSlot, diff --git a/packages/react-components/react-tags/library/src/components/InteractionTagSecondary/useInteractionTagSecondary.tsx b/packages/react-components/react-tags/library/src/components/InteractionTagSecondary/useInteractionTagSecondary.tsx index 9cd6de0a565599..e47e4de38be2fc 100644 --- a/packages/react-components/react-tags/library/src/components/InteractionTagSecondary/useInteractionTagSecondary.tsx +++ b/packages/react-components/react-tags/library/src/components/InteractionTagSecondary/useInteractionTagSecondary.tsx @@ -57,7 +57,7 @@ export const useInteractionTagSecondary_unstable = ( id, onClick, onKeyDown, - }), + } as const), { elementType: 'button' }, ), }; diff --git a/packages/react-components/react-tags/library/src/components/Tag/Tag.tsx b/packages/react-components/react-tags/library/src/components/Tag/Tag.tsx index 78bb2568ca48b6..85a7fe425851d5 100644 --- a/packages/react-components/react-tags/library/src/components/Tag/Tag.tsx +++ b/packages/react-components/react-tags/library/src/components/Tag/Tag.tsx @@ -19,6 +19,7 @@ export const Tag: ForwardRefComponent = React.forwardRef((props, ref) useCustomStyleHook_unstable('useTagStyles_unstable')(state); return renderTag_unstable(state, useTagAvatarContextValues_unstable(state)); -}); + // Casting is required due to lack of distributive union to support unions on @types/react +}) as ForwardRefComponent; Tag.displayName = 'Tag'; diff --git a/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarouselFooterButton/TeachingPopoverCarouselFooterButton.tsx b/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarouselFooterButton/TeachingPopoverCarouselFooterButton.tsx index 65b3f413b5c0dc..c577ef34d40e37 100644 --- a/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarouselFooterButton/TeachingPopoverCarouselFooterButton.tsx +++ b/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarouselFooterButton/TeachingPopoverCarouselFooterButton.tsx @@ -18,6 +18,7 @@ export const TeachingPopoverCarouselFooterButton: ForwardRefComponent; TeachingPopoverCarouselFooterButton.displayName = 'TeachingPopoverCarouselFooterButton'; diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index 80366a12319871..178c43467c20a5 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -40,6 +40,9 @@ export function createPriorityQueue(compare: PriorityQueueCompareFn): Prio // @public export type DistributiveOmit = T extends unknown ? Omit : T; +// @public (undocumented) +export type DistributivePick = T extends unknown ? Pick : never; + // @internal export function elementContains(parent: Node | null, child: Node | null): boolean; @@ -73,7 +76,7 @@ export function getEventClientCoords(event: TouchOrMouseEvent): { }; // @public -export const getIntrinsicElementProps: = never>(tagName: NonNullable, props: Props & React_2.RefAttributes>, excludedPropNames?: ExcludedPropKeys[] | undefined) => DistributiveOmit>; +export const getIntrinsicElementProps: (tagName: NonNullable, props: Props & React_2.RefAttributes>, excludedPropNames?: ExcludedPropKeys[] | undefined) => DistributiveOmit, ExcludedPropKeys>; // @public @deprecated export function getNativeElementProps>(tagName: string, props: {}, excludedPropNames?: string[]): TAttributes; @@ -110,15 +113,21 @@ export function getSlotsNext(state: unknown): { }; // @internal -export function getTriggerChild(children: TriggerProps['children']): (React_2.ReactElement> & { +export function getTriggerChild(children: TriggerProps['children']): (React_2.ReactElement & { ref?: React_2.Ref; }) | null; +// @public (undocumented) +export interface HTMLDataAttributes { + // (undocumented) + [data: `data-${string}`]: any; +} + // @public export const IdPrefixProvider: React_2.Provider; // @public -export type InferredElementRefType = ObscureEventName extends keyof Props ? Required[ObscureEventName] extends React_2.PointerEventHandler ? Element : never : never; +export type InferredElementRefType = Props extends unknown ? ObscureEventName extends keyof Props ? Required[ObscureEventName] extends React_2.PointerEventHandler ? Element : never : never : never; // @internal export function isFluentTrigger(element: React_2.ReactElement): element is React_2.ReactElement; @@ -252,7 +261,7 @@ export function setVirtualParent(child: Node, parent?: Node): void; // @public export type Slot | SlotPropsDataType, AlternateAs extends keyof JSX.IntrinsicElements = never> = IsSingleton> extends true ? WithSlotShorthandValue> : Type extends ComponentType ? Props extends SlotPropsDataType ? Props : WithSlotRenderFunction : Type> | (AlternateAs extends unknown ? { +} & WithSlotRenderFunction> : Type extends ComponentType ? Props : Type> | (AlternateAs extends unknown ? { as: AlternateAs; } & WithSlotRenderFunction> : never) | null : 'Error: First parameter to Slot must not be not a union of types. See documentation of Slot type.'; @@ -305,7 +314,7 @@ export type Slots = { }; // @public -export type SlotShorthandValue = React_2.ReactElement | string | number | Iterable | React_2.ReactPortal; +export type SlotShorthandValue = React_2.ReactElement | string | number | Iterable; // @public export const SSRProvider: React_2.FC<{ @@ -316,7 +325,7 @@ export const SSRProvider: React_2.FC<{ export type TouchOrMouseEvent = NativeTouchOrMouseEvent | ReactTouchOrMouseEvent; // @public -export type TriggerProps = { +export type TriggerProps = { children?: React_2.ReactElement | ((props: TriggerChildProps) => React_2.ReactElement | null) | null; }; diff --git a/packages/react-components/react-utilities/src/compose/getIntrinsicElementProps.ts b/packages/react-components/react-utilities/src/compose/getIntrinsicElementProps.ts index 847be6cc3ba6ef..6fcc0af2cae724 100644 --- a/packages/react-components/react-utilities/src/compose/getIntrinsicElementProps.ts +++ b/packages/react-components/react-utilities/src/compose/getIntrinsicElementProps.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import { getNativeElementProps } from '../utils/getNativeElementProps'; import type { InferredElementRefType, SlotPropsDataType } from './types'; -import type { DistributiveOmit } from '../utils/types'; +import type { DistributiveOmit, DistributivePick } from '../utils/types'; // eslint-disable-next-line @typescript-eslint/no-explicit-any type HTMLAttributes = React.HTMLAttributes; @@ -12,19 +12,17 @@ type HTMLAttributes = React.HTMLAttributes; * * Equivalent to {@link getNativeElementProps}, but more type-safe. */ -export const getIntrinsicElementProps = < - Props extends SlotPropsDataType, - ExcludedPropKeys extends Extract = never, ->( +export const getIntrinsicElementProps = ( /** The slot's default element type (e.g. 'div') */ tagName: NonNullable, /** The component's props object */ props: Props & React.RefAttributes>, /** List of native props to exclude from the returned value */ excludedPropNames?: ExcludedPropKeys[], -) => { +): DistributiveOmit< + DistributivePick, + ExcludedPropKeys +> => { // eslint-disable-next-line deprecation/deprecation - return getNativeElementProps< - DistributiveOmit | ExcludedPropKeys> - >(props.as ?? tagName, props, excludedPropNames); + return getNativeElementProps(props.as ?? tagName, props, excludedPropNames); }; diff --git a/packages/react-components/react-utilities/src/compose/types.ts b/packages/react-components/react-utilities/src/compose/types.ts index e94e53579c262b..a76c65008fcd9d 100644 --- a/packages/react-components/react-utilities/src/compose/types.ts +++ b/packages/react-components/react-utilities/src/compose/types.ts @@ -2,8 +2,10 @@ import * as React from 'react'; import { SLOT_ELEMENT_TYPE_SYMBOL, SLOT_RENDER_FUNCTION_SYMBOL } from './constants'; import { ComponentType, - DistributiveOmit, FunctionComponent, + IsSingleton, + PropsWithoutChildren, + PropsWithoutRef, ReactNode, ReplaceNullWithUndefined, } from '../utils/types'; @@ -32,7 +34,7 @@ export type SlotPropsRecord = Record | React.ReactPortal; +export type SlotShorthandValue = React.ReactElement | string | number | Iterable; /** * @deprecated - SlotPropsDataType instead @@ -48,18 +50,20 @@ export type UnknownSlotProps = Pick, 'children /** * Helper type for {@link Slot}. Adds shorthand types that are assignable to the slot's `children`. */ -type WithSlotShorthandValue = - | Props - | ('children' extends keyof Props ? Extract : never); +type WithSlotShorthandValue = Props extends unknown + ? Props | ('children' extends keyof Props ? Extract : never) + : never; /** * @internal * Helper type for {@link Slot}. Takes the props we want to support for a slot and adds the ability for `children` * to be a render function that takes those props. */ -export type WithSlotRenderFunction = Omit & { - children?: ('children' extends keyof Props ? Props['children'] : never) | SlotRenderFunction; -}; +export type WithSlotRenderFunction = Props extends unknown + ? Omit & { + children?: ('children' extends keyof Props ? Props['children'] : never) | SlotRenderFunction; + } + : never; /** * @internal @@ -97,8 +101,13 @@ type EmptyIntrinsicElements = * * Disallows children for empty tags like 'img'. */ type IntrinsicElementProps = Type extends EmptyIntrinsicElements - ? PropsWithoutChildren> - : React.PropsWithRef; + ? PropsWithoutChildren> & HTMLDataAttributes + : React.PropsWithRef & HTMLDataAttributes; + +export interface HTMLDataAttributes { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [data: `data-${string}`]: any; +} /** * The props type and shorthand value for a slot. Type is either a single intrinsic element like `'div'`, @@ -132,9 +141,7 @@ export type Slot< Type extends keyof JSX.IntrinsicElements // Intrinsic elements like `div` ? { as?: Type } & WithSlotRenderFunction> : Type extends ComponentType // Component types like `typeof Button` - ? Props extends SlotPropsDataType - ? Props - : WithSlotRenderFunction + ? Props : Type // Props types like `ButtonProps` > | (AlternateAs extends unknown @@ -143,16 +150,6 @@ export type Slot< | null : 'Error: First parameter to Slot must not be not a union of types. See documentation of Slot type.'; -/** - * Evaluates to true if the given type contains exactly one string, or false if it is a union of strings. - * - * ``` - * IsSingleton<'a'> // true - * IsSingleton<'a' | 'b' | 'c'> // false - * ``` - */ -export type IsSingleton = { [K in T]: Exclude extends never ? true : false }[T]; - /** * Helper type for inferring the type of the as prop from a Props type. * @@ -163,24 +160,6 @@ export type IsSingleton = { [K in T]: Exclude extends ne */ export type AsIntrinsicElement = { as?: As }; -/** - * Removes the 'ref' prop from the given Props type, leaving unions intact (such as the discriminated union created by - * IntrinsicSlotProps). This allows IntrinsicSlotProps to be used with React.forwardRef. - * - * The conditional "extends unknown" (always true) exploits a quirk in the way TypeScript handles conditional - * types, to prevent unions from being expanded. - */ -export type PropsWithoutRef

= 'ref' extends keyof P ? DistributiveOmit : P; - -/** - * Removes the 'ref' prop from the given Props type, leaving unions intact (such as the discriminated union created by - * IntrinsicSlotProps). This allows IntrinsicSlotProps to be used with React.forwardRef. - * - * The conditional "extends unknown" (always true) exploits a quirk in the way TypeScript handles conditional - * types, to prevent unions from being expanded. - */ -export type PropsWithoutChildren

= 'children' extends keyof P ? DistributiveOmit : P; - /** * Removes SlotShorthandValue and null from the slot type, extracting just the slot's Props object. */ @@ -239,9 +218,11 @@ type ObscureEventName = 'onLostPointerCaptureCapture'; /** * Infers the element type from props that are declared using ComponentProps. */ -export type InferredElementRefType = ObscureEventName extends keyof Props - ? Required[ObscureEventName] extends React.PointerEventHandler - ? Element +export type InferredElementRefType = Props extends unknown + ? ObscureEventName extends keyof Props + ? Required[ObscureEventName] extends React.PointerEventHandler + ? Element + : never : never : never; diff --git a/packages/react-components/react-utilities/src/index.ts b/packages/react-components/react-utilities/src/index.ts index 8c6945aaab853e..61ac9f701270fa 100644 --- a/packages/react-components/react-utilities/src/index.ts +++ b/packages/react-components/react-utilities/src/index.ts @@ -36,6 +36,7 @@ export type { InferredElementRefType, EventData, EventHandler, + HTMLDataAttributes, } from './compose/index'; export { @@ -72,7 +73,7 @@ export { createPriorityQueue, } from './utils/index'; -export type { DistributiveOmit, UnionToIntersection } from './utils/types'; +export type { DistributiveOmit, DistributivePick, UnionToIntersection } from './utils/types'; export type { PriorityQueue } from './utils/priorityQueue'; diff --git a/packages/react-components/react-utilities/src/trigger/getTriggerChild.ts b/packages/react-components/react-utilities/src/trigger/getTriggerChild.ts index 30c2981e21e0b3..8130ff966da422 100644 --- a/packages/react-components/react-utilities/src/trigger/getTriggerChild.ts +++ b/packages/react-components/react-utilities/src/trigger/getTriggerChild.ts @@ -27,7 +27,7 @@ import type { TriggerProps } from './types'; export function getTriggerChild( children: TriggerProps['children'], // eslint-disable-next-line @typescript-eslint/no-explicit-any -): (React.ReactElement> & { ref?: React.Ref }) | null { +): (React.ReactElement & { ref?: React.Ref }) | null { if (!React.isValidElement(children)) { return null; } diff --git a/packages/react-components/react-utilities/src/trigger/types.ts b/packages/react-components/react-utilities/src/trigger/types.ts index 161a500e0642cc..66637478bf9ad3 100644 --- a/packages/react-components/react-utilities/src/trigger/types.ts +++ b/packages/react-components/react-utilities/src/trigger/types.ts @@ -24,6 +24,7 @@ export type FluentTriggerComponent = { * 2. A render function that will receive properties and must return a valid element or null * 3. null or undefined */ -export type TriggerProps = { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type TriggerProps = { children?: React.ReactElement | ((props: TriggerChildProps) => React.ReactElement | null) | null; }; diff --git a/packages/react-components/react-utilities/src/utils/types.ts b/packages/react-components/react-utilities/src/utils/types.ts index 136c6003ca6adc..23e74555f21104 100644 --- a/packages/react-components/react-utilities/src/utils/types.ts +++ b/packages/react-components/react-utilities/src/utils/types.ts @@ -1,5 +1,37 @@ import * as React from 'react'; +/** + * Evaluates to true if the given type contains exactly one string, or false if it is a union of strings. + * + * ``` + * IsSingleton<'a'> // true + * IsSingleton<'a' | 'b' | 'c'> // false + * ``` + */ +export type IsSingleton = { [K in T]: Exclude extends never ? true : false }[T]; + +/** + * Removes the 'ref' prop from the given Props type, leaving unions intact (such as the discriminated union created by + * IntrinsicSlotProps). This allows IntrinsicSlotProps to be used with React.forwardRef. + * + * The conditional "extends unknown" (always true) exploits a quirk in the way TypeScript handles conditional + * types, to prevent unions from being expanded. + */ +export type PropsWithoutRef

= P extends unknown ? ('ref' extends keyof P ? Omit : P) : never; + +/** + * Removes the 'ref' prop from the given Props type, leaving unions intact (such as the discriminated union created by + * IntrinsicSlotProps). This allows IntrinsicSlotProps to be used with React.forwardRef. + * + * The conditional "extends unknown" (always true) exploits a quirk in the way TypeScript handles conditional + * types, to prevent unions from being expanded. + */ +export type PropsWithoutChildren

= P extends unknown + ? 'children' extends keyof P + ? Omit + : P + : never; + /** * Helper type that works similar to Omit, * but when modifying an union type it will distribute the omission to all the union members. @@ -23,6 +55,8 @@ import * as React from 'react'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type DistributiveOmit = T extends unknown ? Omit : T; +export type DistributivePick = T extends unknown ? Pick : never; + /** * Converts a union type (`A | B | C`) to an intersection type (`A & B & C`) */ From b0b3212931818daabafe170d4ba33ed8c27f6e5b Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Tue, 18 Jun 2024 18:50:17 +0000 Subject: [PATCH 8/8] chore(ts-minbar-test-react-components): updates minimal ts version --- apps/ts-minbar-test-react-components/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ts-minbar-test-react-components/src/index.ts b/apps/ts-minbar-test-react-components/src/index.ts index cc783f58401558..fbc6c0de73f688 100644 --- a/apps/ts-minbar-test-react-components/src/index.ts +++ b/apps/ts-minbar-test-react-components/src/index.ts @@ -9,7 +9,7 @@ import { packProjectPackages, } from '@fluentui/scripts-projects-test'; -const tsVersion = '3.9'; +const tsVersion = '4.5'; const testName = 'ts-minbar-react-components'; async function performTest() {