diff --git a/change/@fluentui-react-avatar-5d8641dc-c661-41bc-bbe3-8b0c8a82b68e.json b/change/@fluentui-react-avatar-5d8641dc-c661-41bc-bbe3-8b0c8a82b68e.json new file mode 100644 index 0000000000000..aa3aeca6c8eef --- /dev/null +++ b/change/@fluentui-react-avatar-5d8641dc-c661-41bc-bbe3-8b0c8a82b68e.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: drop unnecessary dependencies from base hooks", + "packageName": "@fluentui/react-avatar", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-breadcrumb-59f990c7-d36b-4b22-b659-4152e463b83e.json b/change/@fluentui-react-breadcrumb-59f990c7-d36b-4b22-b659-4152e463b83e.json new file mode 100644 index 0000000000000..76ba873d105bc --- /dev/null +++ b/change/@fluentui-react-breadcrumb-59f990c7-d36b-4b22-b659-4152e463b83e.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: update types for render function", + "packageName": "@fluentui/react-breadcrumb", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-button-6561155a-5cdf-4b9a-ae91-69fa32f9650d.json b/change/@fluentui-react-button-6561155a-5cdf-4b9a-ae91-69fa32f9650d.json new file mode 100644 index 0000000000000..358cf1fba67b2 --- /dev/null +++ b/change/@fluentui-react-button-6561155a-5cdf-4b9a-ae91-69fa32f9650d.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: update types for render function", + "packageName": "@fluentui/react-button", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-field-90597e60-8f13-4477-b049-1f63aaf11da4.json b/change/@fluentui-react-field-90597e60-8f13-4477-b049-1f63aaf11da4.json new file mode 100644 index 0000000000000..e1b8bbc721769 --- /dev/null +++ b/change/@fluentui-react-field-90597e60-8f13-4477-b049-1f63aaf11da4.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add missing base hook export", + "packageName": "@fluentui/react-field", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-progress-7b34e90c-ceac-4cef-ae58-1e00f71233fc.json b/change/@fluentui-react-progress-7b34e90c-ceac-4cef-ae58-1e00f71233fc.json new file mode 100644 index 0000000000000..1fb6770fbada3 --- /dev/null +++ b/change/@fluentui-react-progress-7b34e90c-ceac-4cef-ae58-1e00f71233fc.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: drop unnecessary dependencies from base hooks", + "packageName": "@fluentui/react-progress", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-rating-49b1cc95-f0a0-4864-bf2f-e7087f636601.json b/change/@fluentui-react-rating-49b1cc95-f0a0-4864-bf2f-e7087f636601.json new file mode 100644 index 0000000000000..0d6f4c260dec5 --- /dev/null +++ b/change/@fluentui-react-rating-49b1cc95-f0a0-4864-bf2f-e7087f636601.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: add mising base hook export", + "packageName": "@fluentui/react-rating", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-skeleton-8eae8004-fa9d-4754-bc51-fd06d5eb2c06.json b/change/@fluentui-react-skeleton-8eae8004-fa9d-4754-bc51-fd06d5eb2c06.json new file mode 100644 index 0000000000000..bc3c232bc37cb --- /dev/null +++ b/change/@fluentui-react-skeleton-8eae8004-fa9d-4754-bc51-fd06d5eb2c06.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: update types for render function", + "packageName": "@fluentui/react-skeleton", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-slider-391fdebc-4979-4df3-a493-c2a794fb6d93.json b/change/@fluentui-react-slider-391fdebc-4979-4df3-a493-c2a794fb6d93.json new file mode 100644 index 0000000000000..ee2a5c475e985 --- /dev/null +++ b/change/@fluentui-react-slider-391fdebc-4979-4df3-a493-c2a794fb6d93.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: drop unnecessary dependencies from base hooks", + "packageName": "@fluentui/react-slider", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-spinner-bb2b5be4-201f-437b-ae3b-41e239e31ad7.json b/change/@fluentui-react-spinner-bb2b5be4-201f-437b-ae3b-41e239e31ad7.json new file mode 100644 index 0000000000000..236bfe0ab5eb0 --- /dev/null +++ b/change/@fluentui-react-spinner-bb2b5be4-201f-437b-ae3b-41e239e31ad7.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: drop unnecessary dependencies from base hooks", + "packageName": "@fluentui/react-spinner", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-switch-3be4ecac-34fe-4680-897d-7bd62fbec7b9.json b/change/@fluentui-react-switch-3be4ecac-34fe-4680-897d-7bd62fbec7b9.json new file mode 100644 index 0000000000000..6f09edbc8807b --- /dev/null +++ b/change/@fluentui-react-switch-3be4ecac-34fe-4680-897d-7bd62fbec7b9.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: update Switch component to conditionally render CircleFilled in indicator", + "packageName": "@fluentui/react-switch", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-textarea-13fe61b7-e8e0-479f-87f7-566a49be4b26.json b/change/@fluentui-react-textarea-13fe61b7-e8e0-479f-87f7-566a49be4b26.json new file mode 100644 index 0000000000000..744b9a06fbfa8 --- /dev/null +++ b/change/@fluentui-react-textarea-13fe61b7-e8e0-479f-87f7-566a49be4b26.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: update types for render function", + "packageName": "@fluentui/react-textarea", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-avatar/library/src/components/AvatarGroupPopover/useAvatarGroupPopover.tsx b/packages/react-components/react-avatar/library/src/components/AvatarGroupPopover/useAvatarGroupPopover.tsx index a4a16df9d5bd2..f8fa23c34d4c4 100644 --- a/packages/react-components/react-avatar/library/src/components/AvatarGroupPopover/useAvatarGroupPopover.tsx +++ b/packages/react-components/react-avatar/library/src/components/AvatarGroupPopover/useAvatarGroupPopover.tsx @@ -103,11 +103,11 @@ export const useAvatarGroupPopoverBase_unstable = (props: AvatarGroupPopoverBase popoverOpen, components: { - root: Popover, + root: 'div', triggerButton: 'button', content: 'ul', - popoverSurface: PopoverSurface, - tooltip: Tooltip, + popoverSurface: 'div', + tooltip: 'div', }, root: slot.always( { 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 f990baebbe804..db7fefeeca654 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 @@ -139,16 +139,16 @@ export type PartitionBreadcrumbItemsOptions = { }; // @public -export const renderBreadcrumb_unstable: (state: BreadcrumbState, contextValues: BreadcrumbContextValues) => JSXElement; +export const renderBreadcrumb_unstable: (state: BreadcrumbBaseState, contextValues: BreadcrumbContextValues) => JSXElement; // @public -export const renderBreadcrumbButton_unstable: (state: BreadcrumbButtonState) => JSXElement; +export const renderBreadcrumbButton_unstable: (state: BreadcrumbButtonBaseState) => JSXElement; // @public -export const renderBreadcrumbDivider_unstable: (state: BreadcrumbDividerState) => JSXElement; +export const renderBreadcrumbDivider_unstable: (state: BreadcrumbDividerBaseState) => JSXElement; // @public -export const renderBreadcrumbItem_unstable: (state: BreadcrumbItemState) => JSXElement; +export const renderBreadcrumbItem_unstable: (state: BreadcrumbItemBaseState) => JSXElement; // @public (undocumented) export const truncateBreadcrumbLongName: (content: string, maxLength?: number) => string; @@ -177,6 +177,9 @@ export const useBreadcrumbButtonStyles_unstable: (state: BreadcrumbButtonState) // @internal (undocumented) export const useBreadcrumbContext_unstable: () => BreadcrumbContextValues; +// @public (undocumented) +export function useBreadcrumbContextValues_unstable(state: BreadcrumbState): BreadcrumbContextValues; + // @public export const useBreadcrumbDivider_unstable: (props: BreadcrumbDividerProps, ref: React_2.Ref) => BreadcrumbDividerState; diff --git a/packages/react-components/react-breadcrumb/library/src/Breadcrumb.ts b/packages/react-components/react-breadcrumb/library/src/Breadcrumb.ts index fdd3135d7e98f..0a29653613794 100644 --- a/packages/react-components/react-breadcrumb/library/src/Breadcrumb.ts +++ b/packages/react-components/react-breadcrumb/library/src/Breadcrumb.ts @@ -15,6 +15,7 @@ export { useBreadcrumbContext_unstable, useBreadcrumbStyles_unstable, useBreadcrumb_unstable, + useBreadcrumbContextValues_unstable, useBreadcrumbBase_unstable, useBreadcrumbA11yBehavior_unstable, } from './components/Breadcrumb/index'; diff --git a/packages/react-components/react-breadcrumb/library/src/components/Breadcrumb/index.ts b/packages/react-components/react-breadcrumb/library/src/components/Breadcrumb/index.ts index 6b3b07b704f11..ea0517cf980a2 100644 --- a/packages/react-components/react-breadcrumb/library/src/components/Breadcrumb/index.ts +++ b/packages/react-components/react-breadcrumb/library/src/components/Breadcrumb/index.ts @@ -14,4 +14,5 @@ export { useBreadcrumbBase_unstable, useBreadcrumbA11yBehavior_unstable, } from './useBreadcrumb'; +export { useBreadcrumbContextValues_unstable } from './useBreadcrumbContextValue'; export { breadcrumbClassNames, useBreadcrumbStyles_unstable } from './useBreadcrumbStyles.styles'; diff --git a/packages/react-components/react-breadcrumb/library/src/components/Breadcrumb/renderBreadcrumb.tsx b/packages/react-components/react-breadcrumb/library/src/components/Breadcrumb/renderBreadcrumb.tsx index df905a95ab0a2..f1a8d96137875 100644 --- a/packages/react-components/react-breadcrumb/library/src/components/Breadcrumb/renderBreadcrumb.tsx +++ b/packages/react-components/react-breadcrumb/library/src/components/Breadcrumb/renderBreadcrumb.tsx @@ -5,12 +5,12 @@ import { assertSlots } from '@fluentui/react-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; import { BreadcrumbProvider } from './BreadcrumbContext'; -import type { BreadcrumbState, BreadcrumbSlots, BreadcrumbContextValues } from './Breadcrumb.types'; +import type { BreadcrumbBaseState, BreadcrumbSlots, BreadcrumbContextValues } from './Breadcrumb.types'; /** * Render the final JSX of Breadcrumb */ export const renderBreadcrumb_unstable = ( - state: BreadcrumbState, + state: BreadcrumbBaseState, contextValues: BreadcrumbContextValues, ): JSXElement => { assertSlots(state); diff --git a/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/renderBreadcrumbButton.tsx b/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/renderBreadcrumbButton.tsx index c3e9b4b3bdcee..56e6544ba2b29 100644 --- a/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/renderBreadcrumbButton.tsx +++ b/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/renderBreadcrumbButton.tsx @@ -1,4 +1,4 @@ -import type { BreadcrumbButtonState } from './BreadcrumbButton.types'; +import type { BreadcrumbButtonBaseState } from './BreadcrumbButton.types'; import type { JSXElement } from '@fluentui/react-utilities'; import { renderButton_unstable } from '@fluentui/react-button'; @@ -6,6 +6,6 @@ import { renderButton_unstable } from '@fluentui/react-button'; /** * Render the final JSX of BreadcrumbButton */ -export const renderBreadcrumbButton_unstable = (state: BreadcrumbButtonState): JSXElement => { +export const renderBreadcrumbButton_unstable = (state: BreadcrumbButtonBaseState): JSXElement => { return renderButton_unstable(state); }; diff --git a/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbDivider/renderBreadcrumbDivider.tsx b/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbDivider/renderBreadcrumbDivider.tsx index c7f5c0e21e873..f1093d7bc61a2 100644 --- a/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbDivider/renderBreadcrumbDivider.tsx +++ b/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbDivider/renderBreadcrumbDivider.tsx @@ -4,12 +4,12 @@ import { assertSlots } from '@fluentui/react-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; -import type { BreadcrumbDividerState, BreadcrumbDividerSlots } from './BreadcrumbDivider.types'; +import type { BreadcrumbDividerBaseState, BreadcrumbDividerSlots } from './BreadcrumbDivider.types'; /** * Render the final JSX of BreadcrumbDivider */ -export const renderBreadcrumbDivider_unstable = (state: BreadcrumbDividerState): JSXElement => { +export const renderBreadcrumbDivider_unstable = (state: BreadcrumbDividerBaseState): JSXElement => { assertSlots(state); return ; diff --git a/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbItem/renderBreadcrumbItem.tsx b/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbItem/renderBreadcrumbItem.tsx index a0acb3968fabd..4186b732edc78 100644 --- a/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbItem/renderBreadcrumbItem.tsx +++ b/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbItem/renderBreadcrumbItem.tsx @@ -4,12 +4,12 @@ import { assertSlots } from '@fluentui/react-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; -import type { BreadcrumbItemState, BreadcrumbItemSlots } from './BreadcrumbItem.types'; +import type { BreadcrumbItemBaseState, BreadcrumbItemSlots } from './BreadcrumbItem.types'; /** * Render the final JSX of BreadcrumbItem */ -export const renderBreadcrumbItem_unstable = (state: BreadcrumbItemState): JSXElement => { +export const renderBreadcrumbItem_unstable = (state: BreadcrumbItemBaseState): JSXElement => { assertSlots(state); return {state.root.children}; diff --git a/packages/react-components/react-breadcrumb/library/src/index.ts b/packages/react-components/react-breadcrumb/library/src/index.ts index 34d462af12c6e..a6be25d0a9ee2 100644 --- a/packages/react-components/react-breadcrumb/library/src/index.ts +++ b/packages/react-components/react-breadcrumb/library/src/index.ts @@ -5,6 +5,7 @@ export { useBreadcrumbBase_unstable, useBreadcrumbA11yBehavior_unstable, useBreadcrumbStyles_unstable, + useBreadcrumbContextValues_unstable, breadcrumbClassNames, } from './Breadcrumb'; export type { 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 b5a6596d4b672..f19f122c6c2f9 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 @@ -92,7 +92,7 @@ export type MenuButtonSlots = ButtonSlots & { export type MenuButtonState = ComponentState & Omit; // @public -const renderButton_unstable: (state: ButtonState) => JSXElement; +const renderButton_unstable: (state: ButtonBaseState) => JSXElement; export { renderButton_unstable } export { renderButton_unstable as renderToggleButton_unstable } diff --git a/packages/react-components/react-button/library/src/components/Button/renderButton.tsx b/packages/react-components/react-button/library/src/components/Button/renderButton.tsx index 3ce555193c83c..8a11905f58eef 100644 --- a/packages/react-components/react-button/library/src/components/Button/renderButton.tsx +++ b/packages/react-components/react-button/library/src/components/Button/renderButton.tsx @@ -4,12 +4,12 @@ import { assertSlots } from '@fluentui/react-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; -import type { ButtonSlots, ButtonState } from './Button.types'; +import type { ButtonSlots, ButtonBaseState } from './Button.types'; /** * Renders a Button component by passing the state defined props to the appropriate slots. */ -export const renderButton_unstable = (state: ButtonState): JSXElement => { +export const renderButton_unstable = (state: ButtonBaseState): JSXElement => { assertSlots(state); const { iconOnly, iconPosition } = state; diff --git a/packages/react-components/react-field/library/etc/react-field.api.md b/packages/react-components/react-field/library/etc/react-field.api.md index 5b456ff2b4445..ebdaced16d5a2 100644 --- a/packages/react-components/react-field/library/etc/react-field.api.md +++ b/packages/react-components/react-field/library/etc/react-field.api.md @@ -6,6 +6,7 @@ import type { ComponentProps } from '@fluentui/react-utilities'; import type { ComponentState } from '@fluentui/react-utilities'; +import type { DistributiveOmit } from '@fluentui/react-utilities'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; import { Label } from '@fluentui/react-label'; @@ -16,6 +17,12 @@ import type { SlotClassNames } from '@fluentui/react-utilities'; // @public (undocumented) export const Field: ForwardRefComponent; +// @public (undocumented) +export type FieldBaseProps = DistributiveOmit; + +// @public (undocumented) +export type FieldBaseState = DistributiveOmit; + // @public (undocumented) export const fieldClassNames: SlotClassNames; @@ -74,11 +81,14 @@ export type FieldState = ComponentState> & Required JSXElement; +export const renderField_unstable: (state: FieldBaseState, contextValues: FieldContextValues) => JSXElement; // @public export const useField_unstable: (props: FieldProps, ref: React_2.Ref) => FieldState; +// @public +export const useFieldBase_unstable: (props: FieldBaseProps, ref: React_2.Ref) => FieldBaseState; + // @public (undocumented) export const useFieldContext_unstable: () => FieldContextValue | undefined; diff --git a/packages/react-components/react-field/library/src/Field.ts b/packages/react-components/react-field/library/src/Field.ts index bfed0c836526c..fbd271612bd18 100644 --- a/packages/react-components/react-field/library/src/Field.ts +++ b/packages/react-components/react-field/library/src/Field.ts @@ -5,6 +5,8 @@ export type { FieldProps, FieldSlots, FieldState, + FieldBaseProps, + FieldBaseState, } from './components/Field/index'; export { Field, @@ -12,4 +14,5 @@ export { renderField_unstable, useFieldStyles_unstable, useField_unstable, + useFieldBase_unstable, } from './components/Field/index'; diff --git a/packages/react-components/react-field/library/src/components/Field/index.ts b/packages/react-components/react-field/library/src/components/Field/index.ts index 3ebf540ebfb89..a26024fcff330 100644 --- a/packages/react-components/react-field/library/src/components/Field/index.ts +++ b/packages/react-components/react-field/library/src/components/Field/index.ts @@ -10,5 +10,6 @@ export type { } from './Field.types'; export { Field } from './Field'; export { renderField_unstable } from './renderField'; -export { useField_unstable, useFieldBase_unstable } from './useField'; +export { useField_unstable } from './useField'; +export { useFieldBase_unstable } from './useFieldBase'; export { fieldClassNames, useFieldStyles_unstable } from './useFieldStyles.styles'; diff --git a/packages/react-components/react-field/library/src/components/Field/renderField.tsx b/packages/react-components/react-field/library/src/components/Field/renderField.tsx index 5e60db7c30f1d..11b7db65523d2 100644 --- a/packages/react-components/react-field/library/src/components/Field/renderField.tsx +++ b/packages/react-components/react-field/library/src/components/Field/renderField.tsx @@ -4,12 +4,12 @@ import { assertSlots } from '@fluentui/react-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; import { FieldContextProvider, getFieldControlProps } from '../../contexts/index'; -import type { FieldContextValues, FieldSlots, FieldState } from './Field.types'; +import type { FieldContextValues, FieldSlots, FieldBaseState } from './Field.types'; /** * Render the final JSX of Field */ -export const renderField_unstable = (state: FieldState, contextValues: FieldContextValues): JSXElement => { +export const renderField_unstable = (state: FieldBaseState, contextValues: FieldContextValues): JSXElement => { assertSlots(state); let { children } = state; diff --git a/packages/react-components/react-field/library/src/components/Field/useField.test.tsx b/packages/react-components/react-field/library/src/components/Field/useField.test.tsx index cebcf7224dabb..c5faca305955c 100644 --- a/packages/react-components/react-field/library/src/components/Field/useField.test.tsx +++ b/packages/react-components/react-field/library/src/components/Field/useField.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { renderHook } from '@testing-library/react-hooks'; -import { useField_unstable, useFieldBase_unstable } from './useField'; +import { useField_unstable } from './useField'; describe('useField_unstable', () => { const ref = React.createRef(); @@ -43,47 +43,3 @@ describe('useField_unstable', () => { }); }); }); - -describe('useFieldBase_unstable', () => { - const ref = React.createRef(); - - it('should return default state when no props are provided', () => { - const { result } = renderHook(() => useFieldBase_unstable({}, ref)); - - expect(result.current).toMatchObject({ - children: undefined, - components: { - // Plain HTML element is expected here, as base hook shouldn't know about the "styled" components - label: 'label', - }, - validationMessage: undefined, - validationMessageIcon: undefined, - validationState: 'none', - }); - }); - - it('should return default state when props are provided', () => { - const { result } = renderHook(() => - useFieldBase_unstable( - { - label: 'Test Label', - validationMessage: 'Test Validation Message', - validationMessageIcon: 'Test Icon', - validationState: 'error', - }, - ref, - ), - ); - - expect(result.current).toMatchObject({ - children: undefined, - components: { - // Plain HTML element is expected here, as base hook shouldn't know about the "styled" components - label: 'label', - }, - validationMessage: expect.objectContaining({ children: 'Test Validation Message' }), - validationMessageIcon: expect.objectContaining({ children: 'Test Icon' }), - validationState: 'error', - }); - }); -}); diff --git a/packages/react-components/react-field/library/src/components/Field/useField.tsx b/packages/react-components/react-field/library/src/components/Field/useField.tsx index 25d36c6265380..add8cb321031e 100644 --- a/packages/react-components/react-field/library/src/components/Field/useField.tsx +++ b/packages/react-components/react-field/library/src/components/Field/useField.tsx @@ -4,8 +4,9 @@ import * as React from 'react'; import { CheckmarkCircle12Filled, DiamondDismiss12Filled, Warning12Filled } from '@fluentui/react-icons'; import { Label } from '@fluentui/react-label'; -import { getIntrinsicElementProps, useId, slot } from '@fluentui/react-utilities'; -import type { FieldBaseProps, FieldBaseState, FieldProps, FieldState } from './Field.types'; +import { slot } from '@fluentui/react-utilities'; +import type { FieldProps, FieldState } from './Field.types'; +import { useFieldBase_unstable } from './useFieldBase'; const validationMessageIcons = { error: , @@ -46,50 +47,3 @@ export const useField_unstable = (props: FieldProps, ref: React.Ref): FieldBaseState => { - const { children, required = false, validationState = props.validationMessage ? 'error' : 'none' } = props; - - const baseId = useId('field-'); - const generatedControlId = baseId + '__control'; - - const root = slot.always(getIntrinsicElementProps('div', { ...props, ref }, /*excludedPropNames:*/ ['children']), { - elementType: 'div', - }); - const label = slot.optional(props.label, { - defaultProps: { htmlFor: generatedControlId, id: baseId + '__label', required }, - elementType: 'label', - }); - const validationMessage = slot.optional(props.validationMessage, { - defaultProps: { - id: baseId + '__validationMessage', - role: validationState === 'error' || validationState === 'warning' ? 'alert' : undefined, - }, - elementType: 'div', - }); - const hint = slot.optional(props.hint, { defaultProps: { id: baseId + '__hint' }, elementType: 'div' }); - const validationMessageIcon = slot.optional(props.validationMessageIcon, { - renderByDefault: false, - elementType: 'span', - }); - - return { - children, - generatedControlId, - required, - validationState, - components: { root: 'div', label: 'label', validationMessage: 'div', validationMessageIcon: 'span', hint: 'div' }, - root, - label, - validationMessageIcon, - validationMessage, - hint, - }; -}; diff --git a/packages/react-components/react-field/library/src/components/Field/useFieldBase.test.tsx b/packages/react-components/react-field/library/src/components/Field/useFieldBase.test.tsx new file mode 100644 index 0000000000000..b982891f115d3 --- /dev/null +++ b/packages/react-components/react-field/library/src/components/Field/useFieldBase.test.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { renderHook } from '@testing-library/react-hooks'; +import { useFieldBase_unstable } from './useFieldBase'; + +describe('useFieldBase_unstable', () => { + const ref = React.createRef(); + + it('should return default state when no props are provided', () => { + const { result } = renderHook(() => useFieldBase_unstable({}, ref)); + + expect(result.current).toMatchObject({ + children: undefined, + components: { + // Plain HTML element is expected here, as base hook shouldn't know about the "styled" components + label: 'label', + }, + validationMessage: undefined, + validationMessageIcon: undefined, + validationState: 'none', + }); + }); + + it('should return default state when props are provided', () => { + const { result } = renderHook(() => + useFieldBase_unstable( + { + label: 'Test Label', + validationMessage: 'Test Validation Message', + validationMessageIcon: 'Test Icon', + validationState: 'error', + }, + ref, + ), + ); + + expect(result.current).toMatchObject({ + children: undefined, + components: { + // Plain HTML element is expected here, as base hook shouldn't know about the "styled" components + label: 'label', + }, + validationMessage: expect.objectContaining({ children: 'Test Validation Message' }), + validationMessageIcon: expect.objectContaining({ children: 'Test Icon' }), + validationState: 'error', + }); + }); +}); diff --git a/packages/react-components/react-field/library/src/components/Field/useFieldBase.tsx b/packages/react-components/react-field/library/src/components/Field/useFieldBase.tsx new file mode 100644 index 0000000000000..3b899f8b61c1d --- /dev/null +++ b/packages/react-components/react-field/library/src/components/Field/useFieldBase.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; + +import { getIntrinsicElementProps, useId, slot } from '@fluentui/react-utilities'; +import type { FieldBaseProps, FieldBaseState } from './Field.types'; + +/** + * Base hook for Field component, which manages state related to validation, ARIA attributes, + * ID generation, and slot structure without design props. + * + * @param props - Props passed to this field + * @param ref - Ref to the root + */ +export const useFieldBase_unstable = (props: FieldBaseProps, ref: React.Ref): FieldBaseState => { + const { children, required = false, validationState = props.validationMessage ? 'error' : 'none' } = props; + + const baseId = useId('field-'); + const generatedControlId = baseId + '__control'; + + const root = slot.always(getIntrinsicElementProps('div', { ...props, ref }, /*excludedPropNames:*/ ['children']), { + elementType: 'div', + }); + const label = slot.optional(props.label, { + defaultProps: { htmlFor: generatedControlId, id: baseId + '__label', required }, + elementType: 'label', + }); + const validationMessage = slot.optional(props.validationMessage, { + defaultProps: { + id: baseId + '__validationMessage', + role: validationState === 'error' || validationState === 'warning' ? 'alert' : undefined, + }, + elementType: 'div', + }); + const hint = slot.optional(props.hint, { defaultProps: { id: baseId + '__hint' }, elementType: 'div' }); + const validationMessageIcon = slot.optional(props.validationMessageIcon, { + renderByDefault: false, + elementType: 'span', + }); + + return { + children, + generatedControlId, + required, + validationState, + components: { root: 'div', label: 'label', validationMessage: 'div', validationMessageIcon: 'span', hint: 'div' }, + root, + label, + validationMessageIcon, + validationMessage, + hint, + }; +}; diff --git a/packages/react-components/react-field/library/src/index.ts b/packages/react-components/react-field/library/src/index.ts index a3ed2913e05a1..5c2ff33539575 100644 --- a/packages/react-components/react-field/library/src/index.ts +++ b/packages/react-components/react-field/library/src/index.ts @@ -1,4 +1,11 @@ -export { Field, fieldClassNames, renderField_unstable, useFieldStyles_unstable, useField_unstable } from './Field'; +export { + Field, + fieldClassNames, + renderField_unstable, + useFieldStyles_unstable, + useField_unstable, + useFieldBase_unstable, +} from './Field'; export type { FieldContextValue, FieldContextValues, @@ -6,6 +13,8 @@ export type { FieldProps, FieldSlots, FieldState, + FieldBaseProps, + FieldBaseState, } from './Field'; export { FieldContextProvider, diff --git a/packages/react-components/react-progress/library/src/components/ProgressBar/useProgressBar.tsx b/packages/react-components/react-progress/library/src/components/ProgressBar/useProgressBar.tsx index ccd0193c5cbca..8711b249f91c6 100644 --- a/packages/react-components/react-progress/library/src/components/ProgressBar/useProgressBar.tsx +++ b/packages/react-components/react-progress/library/src/components/ProgressBar/useProgressBar.tsx @@ -38,6 +38,11 @@ export const useProgressBar_unstable = (props: ProgressBarProps, ref: React.Ref< return { ...state, + components: { + // eslint-disable-next-line @typescript-eslint/no-deprecated + ...state.components, + indeterminateMotion: ProgressBarIndeterminateMotion, + }, color, shape, thickness, @@ -94,7 +99,7 @@ export const useProgressBarBase_unstable = ( return { max, value, - components: { root: 'div', bar: 'div', indeterminateMotion: ProgressBarIndeterminateMotion }, + components: { root: 'div', bar: 'div', indeterminateMotion: 'div' }, root, bar, }; diff --git a/packages/react-components/react-rating/library/etc/react-rating.api.md b/packages/react-components/react-rating/library/etc/react-rating.api.md index c02bcf14f3b25..f99d3c533b3c7 100644 --- a/packages/react-components/react-rating/library/etc/react-rating.api.md +++ b/packages/react-components/react-rating/library/etc/react-rating.api.md @@ -167,6 +167,9 @@ export const useRatingDisplayStyles_unstable: (state: RatingDisplayState) => Rat // @public export const useRatingItem_unstable: (props: RatingItemProps, ref: React_2.Ref) => RatingItemState; +// @public +export const useRatingItemBase_unstable: (props: RatingItemBaseProps, ref: React_2.Ref) => RatingItemBaseState; + // @public export const useRatingItemContextValue_unstable: () => RatingItemContextValue; diff --git a/packages/react-components/react-rating/library/src/components/Rating/useRating.tsx b/packages/react-components/react-rating/library/src/components/Rating/useRating.tsx index 33816ac01982e..fc20522cf92fb 100644 --- a/packages/react-components/react-rating/library/src/components/Rating/useRating.tsx +++ b/packages/react-components/react-rating/library/src/components/Rating/useRating.tsx @@ -28,16 +28,17 @@ export const useRating_unstable = (props: RatingProps, ref: React.Ref { + return Array.from(Array(max), (_, i) => ); + }, [max]); + + state.root.children ??= rootChildren; return { ...state, @@ -56,15 +57,7 @@ export const useRating_unstable = (props: RatingProps, ref: React.Ref): RatingBaseState => { const generatedName = useId('rating-'); - const { - iconFilled = 'span', - iconOutline = 'span', - max = 5, - name = generatedName, - onChange, - step = 1, - itemLabel, - } = props; + const { iconFilled = 'span', iconOutline = 'span', name = generatedName, onChange, step = 1, itemLabel } = props; const [value, setValue] = useControllableState({ state: props.value, @@ -76,12 +69,6 @@ export const useRatingBase_unstable = (props: RatingBaseProps, ref: React.Ref(undefined); - - // Generate the child RatingItems and memoize them to prevent unnecessary re-rendering - const rootChildren = React.useMemo(() => { - return Array.from(Array(max), (_, i) => ); - }, [max]); - const state: RatingBaseState = { iconFilled, iconOutline, @@ -98,7 +85,6 @@ export const useRatingBase_unstable = (props: RatingBaseProps, ref: React.Ref { + return compact ? ( + + ) : ( + Array.from(Array(max), (_, i) => ) + ); + }, [compact, max]); + return { ...state, + root: { + children: rootChildren, + ...state.root, + }, icon, color, size, @@ -52,15 +66,6 @@ export const useRatingDisplayBase_unstable = ( const valueTextId = useId('rating-value-'); const countTextId = useId('rating-count-'); - // Generate the child RatingItems and memoize them to prevent unnecessary re-rendering - const rootChildren = React.useMemo(() => { - return compact ? ( - - ) : ( - Array.from(Array(max), (_, i) => ) - ); - }, [compact, max]); - const state: RatingDisplayBaseState = { compact, icon, @@ -74,7 +79,6 @@ export const useRatingDisplayBase_unstable = ( root: slot.always( getIntrinsicElementProps('div', { ref, - children: rootChildren, role: 'img', ...props, }), diff --git a/packages/react-components/react-rating/library/src/contexts/RatingItemContext.tsx b/packages/react-components/react-rating/library/src/contexts/RatingItemContext.tsx index c11adf12d3138..7a436ddb15349 100644 --- a/packages/react-components/react-rating/library/src/contexts/RatingItemContext.tsx +++ b/packages/react-components/react-rating/library/src/contexts/RatingItemContext.tsx @@ -1,8 +1,7 @@ 'use client'; import * as React from 'react'; -import { RatingItemContextValue } from '../RatingItem'; -import { StarFilled, StarRegular } from '@fluentui/react-icons'; +import type { RatingItemContextValue } from '../RatingItem'; /** * RatingContext is provided by Rating, and is consumed by Rating to determine default values of some props. @@ -10,8 +9,8 @@ import { StarFilled, StarRegular } from '@fluentui/react-icons'; export const RatingItemContext = React.createContext(undefined); const ratingItemContextDefaultValue: RatingItemContextValue = { color: 'neutral', - iconFilled: StarFilled, - iconOutline: StarRegular, + iconFilled: 'span', + iconOutline: 'span', step: 1, size: 'medium', }; diff --git a/packages/react-components/react-rating/library/src/index.ts b/packages/react-components/react-rating/library/src/index.ts index e003b69417de3..c28c415bd7c9a 100644 --- a/packages/react-components/react-rating/library/src/index.ts +++ b/packages/react-components/react-rating/library/src/index.ts @@ -22,6 +22,7 @@ export { renderRatingItem_unstable, useRatingItemStyles_unstable, useRatingItem_unstable, + useRatingItemBase_unstable, } from './RatingItem'; export type { RatingItemProps, diff --git a/packages/react-components/react-skeleton/library/etc/react-skeleton.api.md b/packages/react-components/react-skeleton/library/etc/react-skeleton.api.md index df793d91ed18c..54b10df52a0a1 100644 --- a/packages/react-components/react-skeleton/library/etc/react-skeleton.api.md +++ b/packages/react-components/react-skeleton/library/etc/react-skeleton.api.md @@ -13,7 +13,7 @@ import type { Slot } from '@fluentui/react-utilities'; import type { SlotClassNames } from '@fluentui/react-utilities'; // @public -export const renderSkeleton_unstable: (state: SkeletonState, contextValues: SkeletonContextValues) => JSXElement; +export const renderSkeleton_unstable: (state: SkeletonBaseState, contextValues: SkeletonContextValues) => JSXElement; // @public export const renderSkeletonItem_unstable: (state: SkeletonItemBaseState) => JSXElement; @@ -45,6 +45,11 @@ export interface SkeletonContextValue { size?: SkeletonItemSize; } +// @public (undocumented) +export type SkeletonContextValues = { + skeletonGroup: SkeletonContextValue; +}; + // @public (undocumented) export const SkeletonItem: ForwardRefComponent; @@ -97,6 +102,9 @@ export const useSkeletonBase_unstable: (props: SkeletonBaseProps, ref: React_2.R // @public (undocumented) export const useSkeletonContext: () => SkeletonContextValue; +// @public (undocumented) +export const useSkeletonContextValues: (state: SkeletonState) => SkeletonContextValues; + // @public export const useSkeletonItem_unstable: (props: SkeletonItemProps, ref: React_2.Ref) => SkeletonItemState; diff --git a/packages/react-components/react-skeleton/library/src/components/Skeleton/renderSkeleton.tsx b/packages/react-components/react-skeleton/library/src/components/Skeleton/renderSkeleton.tsx index 404a060ea1409..a2641234e8a1e 100644 --- a/packages/react-components/react-skeleton/library/src/components/Skeleton/renderSkeleton.tsx +++ b/packages/react-components/react-skeleton/library/src/components/Skeleton/renderSkeleton.tsx @@ -4,12 +4,12 @@ import { assertSlots } from '@fluentui/react-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; import { SkeletonContextProvider } from '../../contexts/SkeletonContext'; -import type { SkeletonContextValues, SkeletonSlots, SkeletonState } from './Skeleton.types'; +import type { SkeletonContextValues, SkeletonSlots, SkeletonBaseState } from './Skeleton.types'; /** * Render the final JSX of Skeleton */ -export const renderSkeleton_unstable = (state: SkeletonState, contextValues: SkeletonContextValues): JSXElement => { +export const renderSkeleton_unstable = (state: SkeletonBaseState, contextValues: SkeletonContextValues): JSXElement => { assertSlots(state); return ( diff --git a/packages/react-components/react-skeleton/library/src/index.ts b/packages/react-components/react-skeleton/library/src/index.ts index a6f8ca5e3626f..4b1ac8e53825d 100644 --- a/packages/react-components/react-skeleton/library/src/index.ts +++ b/packages/react-components/react-skeleton/library/src/index.ts @@ -2,11 +2,19 @@ export { Skeleton, renderSkeleton_unstable, skeletonClassNames, + useSkeletonContextValues, useSkeletonStyles_unstable, useSkeleton_unstable, useSkeletonBase_unstable, } from './Skeleton'; -export type { SkeletonBaseProps, SkeletonProps, SkeletonSlots, SkeletonBaseState, SkeletonState } from './Skeleton'; +export type { + SkeletonContextValues, + SkeletonBaseProps, + SkeletonProps, + SkeletonSlots, + SkeletonBaseState, + SkeletonState, +} from './Skeleton'; export { SkeletonItem, renderSkeletonItem_unstable, diff --git a/packages/react-components/react-slider/library/src/components/Slider/Slider.constants.ts b/packages/react-components/react-slider/library/src/components/Slider/Slider.constants.ts new file mode 100644 index 0000000000000..491277f2a9f70 --- /dev/null +++ b/packages/react-components/react-slider/library/src/components/Slider/Slider.constants.ts @@ -0,0 +1,11 @@ +export const sliderCSSVars = { + sliderDirectionVar: `--fui-Slider--direction`, + sliderInnerThumbRadiusVar: `--fui-Slider__inner-thumb--radius`, + sliderProgressVar: `--fui-Slider--progress`, + sliderProgressColorVar: `--fui-Slider__progress--color`, + sliderRailSizeVar: `--fui-Slider__rail--size`, + sliderRailColorVar: `--fui-Slider__rail--color`, + sliderStepsPercentVar: `--fui-Slider--steps-percent`, + sliderThumbColorVar: `--fui-Slider__thumb--color`, + sliderThumbSizeVar: `--fui-Slider__thumb--size`, +}; diff --git a/packages/react-components/react-slider/library/src/components/Slider/index.ts b/packages/react-components/react-slider/library/src/components/Slider/index.ts index c4dc9f06f56b1..0aeb08c9549f9 100644 --- a/packages/react-components/react-slider/library/src/components/Slider/index.ts +++ b/packages/react-components/react-slider/library/src/components/Slider/index.ts @@ -1,4 +1,5 @@ export { Slider } from './Slider'; +export { sliderCSSVars } from './Slider.constants'; export type { SliderBaseProps, SliderBaseState, @@ -10,4 +11,4 @@ export type { export { renderSlider_unstable } from './renderSlider'; export { useSlider_unstable, useSliderBase_unstable } from './useSlider'; export { useSliderState_unstable } from './useSliderState'; -export { sliderClassNames, sliderCSSVars, useSliderStyles_unstable } from './useSliderStyles.styles'; +export { sliderClassNames, useSliderStyles_unstable } from './useSliderStyles.styles'; diff --git a/packages/react-components/react-slider/library/src/components/Slider/useSliderState.tsx b/packages/react-components/react-slider/library/src/components/Slider/useSliderState.tsx index c63a57634a3ae..e0f5299056698 100644 --- a/packages/react-components/react-slider/library/src/components/Slider/useSliderState.tsx +++ b/packages/react-components/react-slider/library/src/components/Slider/useSliderState.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { clamp, useControllableState, useEventCallback } from '@fluentui/react-utilities'; import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; -import { sliderCSSVars } from './useSliderStyles.styles'; +import { sliderCSSVars } from './Slider.constants'; import type { SliderBaseState, SliderBaseProps } from './Slider.types'; const { sliderStepsPercentVar, sliderProgressVar, sliderDirectionVar } = sliderCSSVars; diff --git a/packages/react-components/react-slider/library/src/components/Slider/useSliderStyles.styles.ts b/packages/react-components/react-slider/library/src/components/Slider/useSliderStyles.styles.ts index d3204f9bb2f89..9340a7fbfecf0 100644 --- a/packages/react-components/react-slider/library/src/components/Slider/useSliderStyles.styles.ts +++ b/packages/react-components/react-slider/library/src/components/Slider/useSliderStyles.styles.ts @@ -5,6 +5,7 @@ import { createFocusOutlineStyle } from '@fluentui/react-tabster'; import { tokens } from '@fluentui/react-theme'; import type { SliderState, SliderSlots } from './Slider.types'; import type { SlotClassNames } from '@fluentui/react-utilities'; +import { sliderCSSVars } from './Slider.constants'; export const sliderClassNames: SlotClassNames = { root: 'fui-Slider', @@ -16,18 +17,6 @@ export const sliderClassNames: SlotClassNames = { // Internal CSS variables const thumbPositionVar = `--fui-Slider__thumb--position`; -export const sliderCSSVars = { - sliderDirectionVar: `--fui-Slider--direction`, - sliderInnerThumbRadiusVar: `--fui-Slider__inner-thumb--radius`, - sliderProgressVar: `--fui-Slider--progress`, - sliderProgressColorVar: `--fui-Slider__progress--color`, - sliderRailSizeVar: `--fui-Slider__rail--size`, - sliderRailColorVar: `--fui-Slider__rail--color`, - sliderStepsPercentVar: `--fui-Slider--steps-percent`, - sliderThumbColorVar: `--fui-Slider__thumb--color`, - sliderThumbSizeVar: `--fui-Slider__thumb--size`, -}; - const { sliderDirectionVar, sliderInnerThumbRadiusVar, diff --git a/packages/react-components/react-spinner/library/src/components/Spinner/useSpinner.tsx b/packages/react-components/react-spinner/library/src/components/Spinner/useSpinner.tsx index bc90b619e1617..ea3d0502f7457 100644 --- a/packages/react-components/react-spinner/library/src/components/Spinner/useSpinner.tsx +++ b/packages/react-components/react-spinner/library/src/components/Spinner/useSpinner.tsx @@ -25,6 +25,15 @@ export const useSpinner_unstable = (props: SpinnerProps, ref: React.Ref; + return { ...baseState, + components: { + // eslint-disable-next-line @typescript-eslint/no-deprecated + ...baseState.components, + label: Label, + }, size, + label: slot.optional(props.label, { + defaultProps: { size: 'medium', ...baseState.label }, + elementType: Label, + }), }; }; @@ -61,7 +72,7 @@ export const useSwitchBase_unstable = (props: SwitchBaseProps, ref?: React.Ref }, + defaultProps: { 'aria-hidden': true }, elementType: 'div', }); const input = slot.always(props.input, { @@ -90,13 +101,13 @@ export const useSwitchBase_unstable = (props: SwitchBaseProps, ref?: React.Ref JSXElement; +export const renderTextarea_unstable: (state: TextareaBaseState) => JSXElement; // @public export const Textarea: ForwardRefComponent; diff --git a/packages/react-components/react-textarea/library/src/components/Textarea/renderTextarea.tsx b/packages/react-components/react-textarea/library/src/components/Textarea/renderTextarea.tsx index 2c7a1593ad76c..e5e64655ad4d5 100644 --- a/packages/react-components/react-textarea/library/src/components/Textarea/renderTextarea.tsx +++ b/packages/react-components/react-textarea/library/src/components/Textarea/renderTextarea.tsx @@ -3,12 +3,12 @@ import { assertSlots } from '@fluentui/react-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; -import type { TextareaState, TextareaSlots } from './Textarea.types'; +import type { TextareaBaseState, TextareaSlots } from './Textarea.types'; /** * Render the final JSX of Textarea */ -export const renderTextarea_unstable = (state: TextareaState): JSXElement => { +export const renderTextarea_unstable = (state: TextareaBaseState): JSXElement => { assertSlots(state); return (