From a17c428e073721e981aaa402339d37b87fd8d385 Mon Sep 17 00:00:00 2001 From: Xu Gao Date: Tue, 15 Sep 2020 13:38:09 -0700 Subject: [PATCH] Add missing ref prop typing (#15027) * fix ref typing * fixes * Change files * change changeType to minor --- ...20-09-14-17-37-05-xgao-fix-ref-typing.json | 8 + ...20-09-14-17-37-05-xgao-fix-ref-typing.json | 8 + ...20-09-14-17-37-05-xgao-fix-ref-typing.json | 8 + ...20-09-14-17-37-05-xgao-fix-ref-typing.json | 8 + ...20-09-14-17-37-05-xgao-fix-ref-typing.json | 8 + ...20-09-14-17-37-05-xgao-fix-ref-typing.json | 8 + .../react-checkbox/etc/react-checkbox.api.md | 6 +- .../src/components/Checkbox/Checkbox.base.tsx | 244 ++++++++-------- .../src/components/Checkbox/Checkbox.tsx | 4 +- .../src/components/Checkbox/Checkbox.types.ts | 4 +- packages/react-checkbox/src/next/Checkbox.tsx | 6 +- .../react-checkbox/src/next/Checkbox.types.ts | 4 +- packages/react-link/etc/react-link.api.md | 4 +- .../src/components/Link/Link.base.tsx | 2 +- .../src/components/Link/Link.types.ts | 4 +- packages/react-next/etc/react-next.api.md | 62 ++-- .../src/components/Callout/Callout.tsx | 4 +- .../src/components/Callout/Callout.types.ts | 2 +- .../Callout/CalloutContent.base.tsx | 4 +- .../ChoiceGroup/ChoiceGroup.base.tsx | 183 ++++++------ .../ChoiceGroup/ChoiceGroup.types.ts | 4 +- .../src/components/Coachmark/Beak/Beak.tsx | 124 ++++---- .../components/Coachmark/Beak/Beak.types.ts | 3 +- .../components/Coachmark/Coachmark.base.tsx | 228 +++++++-------- .../components/Coachmark/Coachmark.types.ts | 2 +- .../PositioningContainer.tsx | 145 +++++----- .../PositioningContainer.types.ts | 6 +- .../src/components/Dropdown/Dropdown.base.tsx | 8 +- .../src/components/Dropdown/Dropdown.types.ts | 4 +- .../src/components/Fabric/Fabric.base.tsx | 26 +- .../src/components/Fabric/Fabric.types.ts | 2 +- .../src/components/Image/Image.base.tsx | 126 ++++---- .../src/components/Image/Image.types.ts | 2 +- .../OverflowSet/OverflowSet.base.tsx | 5 +- .../src/components/Persona/Persona.base.tsx | 9 +- .../src/components/Persona/Persona.types.ts | 2 +- .../Persona/PersonaCoin/PersonaCoin.base.tsx | 178 ++++++------ .../PersonaPresence/PersonaPresence.base.tsx | 128 +++++---- .../react-next/src/components/Popup/Popup.tsx | 88 +++--- .../src/components/Popup/Popup.types.ts | 2 +- .../src/components/Rating/Rating.base.tsx | 218 +++++++------- .../src/components/Rating/Rating.types.ts | 2 +- .../ResizeGroup/ResizeGroup.base.tsx | 5 +- .../ResizeGroup/ResizeGroup.types.ts | 2 +- .../components/SearchBox/SearchBox.base.tsx | 5 +- .../components/SearchBox/SearchBox.types.ts | 4 +- .../src/components/Shimmer/Shimmer.base.tsx | 140 ++++----- .../src/components/Shimmer/Shimmer.types.ts | 2 +- .../SwatchColorPicker.base.tsx | 5 +- .../SwatchColorPicker.types.ts | 2 +- .../TeachingBubble/TeachingBubble.base.tsx | 89 +++--- .../TeachingBubbleContent.base.tsx | 271 +++++++++--------- .../utilities/ButtonGrid/ButtonGrid.base.tsx | 7 +- .../src/utilities/ButtonGrid/ButtonGrid.tsx | 11 +- .../utilities/ButtonGrid/ButtonGrid.types.ts | 4 +- packages/react-slider/etc/react-slider.api.md | 4 +- .../src/components/Slider/Slider.base.tsx | 56 ++-- .../src/components/Slider/Slider.types.ts | 4 +- packages/react-tabs/etc/react-tabs.api.md | 2 +- .../src/components/Pivot/Pivot.base.tsx | 4 +- .../src/components/Pivot/Pivot.types.ts | 2 +- packages/react-toggle/etc/react-toggle.api.md | 8 +- .../src/components/Toggle/Toggle.base.tsx | 253 ++++++++-------- .../src/components/Toggle/Toggle.tsx | 2 +- .../src/components/Toggle/Toggle.types.ts | 9 +- packages/react-toggle/src/next/Toggle.tsx | 2 +- .../react-toggle/src/next/Toggle.types.ts | 2 +- packages/react-toggle/src/next/ToggleBase.tsx | 2 +- 68 files changed, 1458 insertions(+), 1332 deletions(-) create mode 100644 change/@fluentui-react-checkbox-2020-09-14-17-37-05-xgao-fix-ref-typing.json create mode 100644 change/@fluentui-react-link-2020-09-14-17-37-05-xgao-fix-ref-typing.json create mode 100644 change/@fluentui-react-next-2020-09-14-17-37-05-xgao-fix-ref-typing.json create mode 100644 change/@fluentui-react-slider-2020-09-14-17-37-05-xgao-fix-ref-typing.json create mode 100644 change/@fluentui-react-tabs-2020-09-14-17-37-05-xgao-fix-ref-typing.json create mode 100644 change/@fluentui-react-toggle-2020-09-14-17-37-05-xgao-fix-ref-typing.json diff --git a/change/@fluentui-react-checkbox-2020-09-14-17-37-05-xgao-fix-ref-typing.json b/change/@fluentui-react-checkbox-2020-09-14-17-37-05-xgao-fix-ref-typing.json new file mode 100644 index 00000000000000..ab241a4ec8f7a3 --- /dev/null +++ b/change/@fluentui-react-checkbox-2020-09-14-17-37-05-xgao-fix-ref-typing.json @@ -0,0 +1,8 @@ +{ + "type": "minor", + "comment": "Add missing ref prop typing.", + "packageName": "@fluentui/react-checkbox", + "email": "xgao@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-09-15T00:36:31.365Z" +} diff --git a/change/@fluentui-react-link-2020-09-14-17-37-05-xgao-fix-ref-typing.json b/change/@fluentui-react-link-2020-09-14-17-37-05-xgao-fix-ref-typing.json new file mode 100644 index 00000000000000..08271572e90fdf --- /dev/null +++ b/change/@fluentui-react-link-2020-09-14-17-37-05-xgao-fix-ref-typing.json @@ -0,0 +1,8 @@ +{ + "type": "minor", + "comment": "Add missing ref prop typing.", + "packageName": "@fluentui/react-link", + "email": "xgao@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-09-15T00:36:39.376Z" +} diff --git a/change/@fluentui-react-next-2020-09-14-17-37-05-xgao-fix-ref-typing.json b/change/@fluentui-react-next-2020-09-14-17-37-05-xgao-fix-ref-typing.json new file mode 100644 index 00000000000000..7a0a60035e8b3b --- /dev/null +++ b/change/@fluentui-react-next-2020-09-14-17-37-05-xgao-fix-ref-typing.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Add missing ref prop typing.", + "packageName": "@fluentui/react-next", + "email": "xgao@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-09-15T00:36:46.296Z" +} diff --git a/change/@fluentui-react-slider-2020-09-14-17-37-05-xgao-fix-ref-typing.json b/change/@fluentui-react-slider-2020-09-14-17-37-05-xgao-fix-ref-typing.json new file mode 100644 index 00000000000000..4d28c230fcbfec --- /dev/null +++ b/change/@fluentui-react-slider-2020-09-14-17-37-05-xgao-fix-ref-typing.json @@ -0,0 +1,8 @@ +{ + "type": "minor", + "comment": "Add missing ref prop typing.", + "packageName": "@fluentui/react-slider", + "email": "xgao@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-09-15T00:36:53.058Z" +} diff --git a/change/@fluentui-react-tabs-2020-09-14-17-37-05-xgao-fix-ref-typing.json b/change/@fluentui-react-tabs-2020-09-14-17-37-05-xgao-fix-ref-typing.json new file mode 100644 index 00000000000000..4b4ab10e5b2f5d --- /dev/null +++ b/change/@fluentui-react-tabs-2020-09-14-17-37-05-xgao-fix-ref-typing.json @@ -0,0 +1,8 @@ +{ + "type": "minor", + "comment": "Add missing ref prop typing.", + "packageName": "@fluentui/react-tabs", + "email": "xgao@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-09-15T00:37:01.696Z" +} diff --git a/change/@fluentui-react-toggle-2020-09-14-17-37-05-xgao-fix-ref-typing.json b/change/@fluentui-react-toggle-2020-09-14-17-37-05-xgao-fix-ref-typing.json new file mode 100644 index 00000000000000..1b421f9d655b03 --- /dev/null +++ b/change/@fluentui-react-toggle-2020-09-14-17-37-05-xgao-fix-ref-typing.json @@ -0,0 +1,8 @@ +{ + "type": "minor", + "comment": "Add missing ref prop typing.", + "packageName": "@fluentui/react-toggle", + "email": "xgao@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-09-15T00:37:05.302Z" +} diff --git a/packages/react-checkbox/etc/react-checkbox.api.md b/packages/react-checkbox/etc/react-checkbox.api.md index abf5dd210b6864..2f917e63c0842f 100644 --- a/packages/react-checkbox/etc/react-checkbox.api.md +++ b/packages/react-checkbox/etc/react-checkbox.api.md @@ -14,10 +14,10 @@ import { ITheme } from '@uifabric/styling'; import * as React from 'react'; // @public (undocumented) -export const Checkbox: React.FunctionComponent>; +export const Checkbox: React.FunctionComponent; // @public (undocumented) -export const CheckboxBase: React.ForwardRefExoticComponent>; +export const CheckboxBase: React.FunctionComponent; // @public export interface ICheckbox { @@ -27,7 +27,7 @@ export interface ICheckbox { } // @public -export interface ICheckboxProps extends React.ButtonHTMLAttributes { +export interface ICheckboxProps extends React.ButtonHTMLAttributes, React.RefAttributes { ariaDescribedBy?: string; ariaLabel?: string; ariaLabelledBy?: string; diff --git a/packages/react-checkbox/src/components/Checkbox/Checkbox.base.tsx b/packages/react-checkbox/src/components/Checkbox/Checkbox.base.tsx index a9859871af100e..38ab3217adf0f6 100644 --- a/packages/react-checkbox/src/components/Checkbox/Checkbox.base.tsx +++ b/packages/react-checkbox/src/components/Checkbox/Checkbox.base.tsx @@ -6,130 +6,132 @@ import { Icon } from 'office-ui-fabric-react/lib/Icon'; const getClassNames = classNamesFunction(); -export const CheckboxBase = React.forwardRef((props: ICheckboxProps, forwardedRef: React.Ref) => { - const { - disabled, - inputProps, - name, - ariaLabel, - ariaLabelledBy, - ariaDescribedBy, - ariaPositionInSet, - ariaSetSize, - title, - label, - checkmarkIconProps, - styles, - theme, - className, - boxSide = 'start', - } = props; - - const id = useId('checkbox-', props.id); - - const rootRef = React.useRef(null); - const mergedRootRefs: React.Ref = useMergedRefs(rootRef, forwardedRef); - const inputRef = React.useRef(null); - - const [isChecked, setIsChecked] = useControllableValue(props.checked, props.defaultChecked, props.onChange); - const [isIndeterminate, setIsIndeterminate] = useControllableValue(props.indeterminate, props.defaultIndeterminate); - - useFocusRects(rootRef); - useDebugWarning(props); - useComponentRef(props, isChecked, isIndeterminate, inputRef); - - const classNames = getClassNames(styles!, { - theme: theme!, - className, - disabled, - indeterminate: isIndeterminate, - checked: isChecked, - reversed: boxSide !== 'start', - isUsingCustomLabelRender: !!props.onRenderLabel, - }); - - const onChange = (ev: React.ChangeEvent): void => { - if (isIndeterminate) { - // If indeterminate, clicking the checkbox *only* removes the indeterminate state (or if - // controlled, lets the consumer know to change it by calling onChange). It doesn't - // change the checked state. - setIsChecked(!!isChecked, ev); - setIsIndeterminate(false); - } else { - setIsChecked(!isChecked, ev); - } - }; - - const defaultLabelRenderer = React.useCallback( - (checkboxProps?: ICheckboxProps): JSX.Element | null => { - if (!checkboxProps) { - return null; - } - return checkboxProps.label ? ( - - ) : null; - }, - [classNames.text], - ); - - const onRenderLabel = props.onRenderLabel || defaultLabelRenderer; - - const ariaChecked: React.InputHTMLAttributes['aria-checked'] = isIndeterminate - ? 'mixed' - : isChecked - ? 'true' - : 'false'; - - const slotProps = { - root: { - className: classNames.root, - title, - ref: mergedRootRefs, - }, - input: { - className: classNames.input, - type: 'checkbox' as React.InputHTMLAttributes['type'], - ...inputProps, - ref: inputRef, - checked: !!isChecked, +export const CheckboxBase: React.FunctionComponent = React.forwardRef( + (props, forwardedRef) => { + const { disabled, + inputProps, name, - id, + ariaLabel, + ariaLabelledBy, + ariaDescribedBy, + ariaPositionInSet, + ariaSetSize, title, - onChange, - 'data-ktp-execute-target': true, - 'aria-disabled': disabled, - 'aria-label': ariaLabel || label, - 'aria-labelledby': ariaLabelledBy, - 'aria-describedby': ariaDescribedBy, - 'aria-posinset': ariaPositionInSet, - 'aria-setsize': ariaSetSize, - 'aria-checked': ariaChecked, - }, - checkbox: { - className: classNames.checkbox, - 'data-ktp-target': true, - }, - container: { - className: classNames.label, - htmlFor: id, - }, - }; - - return ( -
- - -
- ); -}); + label, + checkmarkIconProps, + styles, + theme, + className, + boxSide = 'start', + } = props; + + const id = useId('checkbox-', props.id); + + const rootRef = React.useRef(null); + const mergedRootRefs: React.Ref = useMergedRefs(rootRef, forwardedRef); + const inputRef = React.useRef(null); + + const [isChecked, setIsChecked] = useControllableValue(props.checked, props.defaultChecked, props.onChange); + const [isIndeterminate, setIsIndeterminate] = useControllableValue(props.indeterminate, props.defaultIndeterminate); + + useFocusRects(rootRef); + useDebugWarning(props); + useComponentRef(props, isChecked, isIndeterminate, inputRef); + + const classNames = getClassNames(styles!, { + theme: theme!, + className, + disabled, + indeterminate: isIndeterminate, + checked: isChecked, + reversed: boxSide !== 'start', + isUsingCustomLabelRender: !!props.onRenderLabel, + }); + + const onChange = (ev: React.ChangeEvent): void => { + if (isIndeterminate) { + // If indeterminate, clicking the checkbox *only* removes the indeterminate state (or if + // controlled, lets the consumer know to change it by calling onChange). It doesn't + // change the checked state. + setIsChecked(!!isChecked, ev); + setIsIndeterminate(false); + } else { + setIsChecked(!isChecked, ev); + } + }; + + const defaultLabelRenderer = React.useCallback( + (checkboxProps?: ICheckboxProps): JSX.Element | null => { + if (!checkboxProps) { + return null; + } + return checkboxProps.label ? ( + + ) : null; + }, + [classNames.text], + ); + + const onRenderLabel = props.onRenderLabel || defaultLabelRenderer; + + const ariaChecked: React.InputHTMLAttributes['aria-checked'] = isIndeterminate + ? 'mixed' + : isChecked + ? 'true' + : 'false'; + + const slotProps = { + root: { + className: classNames.root, + title, + ref: mergedRootRefs, + }, + input: { + className: classNames.input, + type: 'checkbox' as React.InputHTMLAttributes['type'], + ...inputProps, + ref: inputRef, + checked: !!isChecked, + disabled, + name, + id, + title, + onChange, + 'data-ktp-execute-target': true, + 'aria-disabled': disabled, + 'aria-label': ariaLabel || label, + 'aria-labelledby': ariaLabelledBy, + 'aria-describedby': ariaDescribedBy, + 'aria-posinset': ariaPositionInSet, + 'aria-setsize': ariaSetSize, + 'aria-checked': ariaChecked, + }, + checkbox: { + className: classNames.checkbox, + 'data-ktp-target': true, + }, + container: { + className: classNames.label, + htmlFor: id, + }, + }; + + return ( +
+ + +
+ ); + }, +); CheckboxBase.displayName = 'CheckboxBase'; diff --git a/packages/react-checkbox/src/components/Checkbox/Checkbox.tsx b/packages/react-checkbox/src/components/Checkbox/Checkbox.tsx index 549eeee47c15b5..42c8aac5bb3fda 100644 --- a/packages/react-checkbox/src/components/Checkbox/Checkbox.tsx +++ b/packages/react-checkbox/src/components/Checkbox/Checkbox.tsx @@ -4,8 +4,8 @@ import { CheckboxBase } from './Checkbox.base'; import { getStyles } from './Checkbox.styles'; import { ICheckboxProps, ICheckboxStyleProps, ICheckboxStyles } from './Checkbox.types'; -export const Checkbox = styled< - ICheckboxProps & React.RefAttributes, +export const Checkbox: React.FunctionComponent = styled< + ICheckboxProps, ICheckboxStyleProps, ICheckboxStyles >(CheckboxBase, getStyles, undefined, { scope: 'Checkbox' }); diff --git a/packages/react-checkbox/src/components/Checkbox/Checkbox.types.ts b/packages/react-checkbox/src/components/Checkbox/Checkbox.types.ts index 5c11977032cd15..3dba7a30c4981c 100644 --- a/packages/react-checkbox/src/components/Checkbox/Checkbox.types.ts +++ b/packages/react-checkbox/src/components/Checkbox/Checkbox.types.ts @@ -25,7 +25,9 @@ export interface ICheckbox { * Checkbox properties. * {@docCategory Checkbox} */ -export interface ICheckboxProps extends React.ButtonHTMLAttributes { +export interface ICheckboxProps + extends React.ButtonHTMLAttributes, + React.RefAttributes { /** * Optional callback to access the ICheckbox interface. Use this instead of ref for accessing * the public methods and properties of the component. diff --git a/packages/react-checkbox/src/next/Checkbox.tsx b/packages/react-checkbox/src/next/Checkbox.tsx index 16d75acddada68..2bd14c0b84fba9 100644 --- a/packages/react-checkbox/src/next/Checkbox.tsx +++ b/packages/react-checkbox/src/next/Checkbox.tsx @@ -1,3 +1,4 @@ +import * as React from 'react'; import { compose, ComposeOptions } from '@fluentui/react-compose'; import { CheckMarkIcon } from '@fluentui/react-icons'; import { CheckboxBase } from './Checkbox.base'; @@ -12,4 +13,7 @@ const composeOptions: ComposeOptions = { }, }; -export const Checkbox = compose<'div', ICheckboxProps, {}, ICheckboxProps, {}>(CheckboxBase, composeOptions); +export const Checkbox: React.FunctionComponent = compose<'div', ICheckboxProps, {}, ICheckboxProps, {}>( + CheckboxBase, + composeOptions, +); diff --git a/packages/react-checkbox/src/next/Checkbox.types.ts b/packages/react-checkbox/src/next/Checkbox.types.ts index e0767619ea2ee5..fa93bca594afcd 100644 --- a/packages/react-checkbox/src/next/Checkbox.types.ts +++ b/packages/react-checkbox/src/next/Checkbox.types.ts @@ -26,7 +26,9 @@ export interface ICheckbox { * Checkbox properties. * {@docCategory Checkbox} */ -export interface ICheckboxProps extends React.ButtonHTMLAttributes { +export interface ICheckboxProps + extends React.ButtonHTMLAttributes, + React.RefAttributes { /** * Render the root element as another type. */ diff --git a/packages/react-link/etc/react-link.api.md b/packages/react-link/etc/react-link.api.md index 85c0306082c5ce..398b520db1ec4a 100644 --- a/packages/react-link/etc/react-link.api.md +++ b/packages/react-link/etc/react-link.api.md @@ -63,7 +63,7 @@ export interface ILinkOptions { } // @public (undocumented) -export interface ILinkProps extends ILinkHTMLAttributes { +export interface ILinkProps extends ILinkHTMLAttributes, React.RefAttributes { componentRef?: IRefObject; disabled?: boolean; // @deprecated @@ -98,7 +98,7 @@ export interface ILinkStyles { export const Link: React.FunctionComponent; // @public (undocumented) -export const LinkBase: React.ForwardRefExoticComponent & React.RefAttributes>; +export const LinkBase: React.FunctionComponent; // @public (undocumented) export type LinkSlotProps = { diff --git a/packages/react-link/src/components/Link/Link.base.tsx b/packages/react-link/src/components/Link/Link.base.tsx index 1b7b6855ad8ef9..f0fbd285719942 100644 --- a/packages/react-link/src/components/Link/Link.base.tsx +++ b/packages/react-link/src/components/Link/Link.base.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { ILinkProps } from './Link.types'; import { useLink } from './useLink'; -export const LinkBase = React.forwardRef((props: ILinkProps, ref: React.Ref) => { +export const LinkBase: React.FunctionComponent = React.forwardRef((props, ref) => { const { slots, slotProps } = useLink(props, ref); return ; diff --git a/packages/react-link/src/components/Link/Link.types.ts b/packages/react-link/src/components/Link/Link.types.ts index 4d04cb3d02e666..6be98900b5c78b 100644 --- a/packages/react-link/src/components/Link/Link.types.ts +++ b/packages/react-link/src/components/Link/Link.types.ts @@ -51,7 +51,9 @@ export interface ILinkHTMLAttributes extends React.HTMLAttributes { /** * {@docCategory Link} */ -export interface ILinkProps extends ILinkHTMLAttributes { +export interface ILinkProps + extends ILinkHTMLAttributes, + React.RefAttributes { /** * Optional callback to access the ILink interface. Use this instead of ref for accessing * the public methods and properties of the component. diff --git a/packages/react-next/etc/react-next.api.md b/packages/react-next/etc/react-next.api.md index a51351225290ae..162baec0fdc222 100644 --- a/packages/react-next/etc/react-next.api.md +++ b/packages/react-next/etc/react-next.api.md @@ -110,13 +110,13 @@ export class BaseSelectedItemsList> } // @public (undocumented) -export const ButtonGrid: import("react").ForwardRefExoticComponent>; +export const ButtonGrid: React.FunctionComponent; // @public (undocumented) export const ButtonGridCell: >(props: IButtonGridCellProps) => JSX.Element; // @public (undocumented) -export const Callout: React.ForwardRefExoticComponent>; +export const Callout: React.FunctionComponent; // @public export function canAnyMenuItemsCheck(items: IContextualMenuItem[]): boolean; @@ -128,7 +128,7 @@ export const Coachmark: React.FunctionComponent; export const COACHMARK_ATTRIBUTE_NAME = "data-coachmarkid"; // @public (undocumented) -export const CoachmarkBase: React.ForwardRefExoticComponent>; +export const CoachmarkBase: React.FunctionComponent; // @public (undocumented) export const ColorPickerGridCell: React.FunctionComponent; @@ -205,7 +205,7 @@ export { DirectionalHint } export const Dropdown: React.FunctionComponent; // @public (undocumented) -export const DropdownBase: React.ForwardRefExoticComponent>; +export const DropdownBase: React.FunctionComponent; export { DropdownMenuItemType } @@ -222,7 +222,7 @@ export class ExtendedSelectedItem extends React.Component; // @public (undocumented) -export const FabricBase: React.ForwardRefExoticComponent>; +export const FabricBase: React.FunctionComponent; // @public export const FocusTrapCallout: React.FunctionComponent; @@ -337,7 +337,7 @@ export interface IButtonGridCellProps { } // @public (undocumented) -export interface IButtonGridProps extends React.TableHTMLAttributes { +export interface IButtonGridProps extends React.TableHTMLAttributes, React.RefAttributes { ariaPosInSet?: number; ariaSetSize?: number; columnCount: number; @@ -391,7 +391,7 @@ export interface ICalloutContentStyles { } // @public (undocumented) -export interface ICalloutProps extends React.HTMLAttributes { +export interface ICalloutProps extends React.HTMLAttributes, React.RefAttributes { alignTargetEdge?: boolean; ariaDescribedBy?: string; ariaLabel?: string; @@ -449,7 +449,7 @@ export interface ICoachmark { } // @public -export interface ICoachmarkProps { +export interface ICoachmarkProps extends React.RefAttributes { ariaAlertText?: string; ariaDescribedBy?: string; ariaDescribedByText?: string; @@ -970,7 +970,7 @@ export interface IDropdownOption extends ISelectableOption { } // @public (undocumented) -export interface IDropdownProps extends ISelectableDroppableTextProps { +export interface IDropdownProps extends ISelectableDroppableTextProps, React.RefAttributes { defaultSelectedKeys?: string[] | number[]; dropdownWidth?: number; // @deprecated @@ -1088,7 +1088,7 @@ export interface IExtendedPersonaProps extends IPersonaProps { } // @public (undocumented) -export interface IFabricProps extends React.HTMLAttributes { +export interface IFabricProps extends React.HTMLAttributes, React.RefAttributes { applyTheme?: boolean; applyThemeToBody?: boolean; as?: React.ElementType; @@ -1148,7 +1148,7 @@ export interface IImage { } // @public (undocumented) -export interface IImageProps extends React.ImgHTMLAttributes { +export interface IImageProps extends React.ImgHTMLAttributes, React.RefAttributes { className?: string; coverStyle?: ImageCoverStyle; // @deprecated @@ -1210,7 +1210,7 @@ export interface ILine extends IShimmerElement { export const Image: React.FunctionComponent; // @public (undocumented) -export const ImageBase: React.ForwardRefExoticComponent>; +export const ImageBase: React.FunctionComponent; // @public export enum ImageCoverStyle { @@ -1484,7 +1484,7 @@ export interface IPersonaProps extends IPersonaSharedProps { } // @public (undocumented) -export interface IPersonaSharedProps extends React.HTMLAttributes { +export interface IPersonaSharedProps extends React.HTMLAttributes, React.RefAttributes { allowPhoneInitials?: boolean; coinProps?: IPersonaCoinProps; coinSize?: number; @@ -1556,7 +1556,7 @@ export interface IPersonaStyles { } // @public (undocumented) -export interface IPopupProps extends React.HTMLAttributes { +export interface IPopupProps extends React.HTMLAttributes, React.RefAttributes { ariaDescribedBy?: string; ariaLabel?: string; ariaLabelledBy?: string; @@ -1576,7 +1576,7 @@ export interface IPositioningContainer { } // @public (undocumented) -export interface IPositioningContainerProps extends IBaseProps { +export interface IPositioningContainerProps extends IBaseProps, React.RefAttributes { ariaDescribedBy?: string; ariaLabel?: string; ariaLabelledBy?: string; @@ -1617,7 +1617,7 @@ export interface IRating { } // @public -export interface IRatingProps extends React.HTMLAttributes { +export interface IRatingProps extends React.HTMLAttributes, React.RefAttributes { allowZeroStars?: boolean; ariaLabelFormat?: string; componentRef?: IRefObject; @@ -1680,7 +1680,7 @@ export interface IResizeGroup { } // @public (undocumented) -export interface IResizeGroupProps extends React.HTMLAttributes { +export interface IResizeGroupProps extends React.HTMLAttributes, React.RefAttributes { className?: string; componentRef?: IRefObject; data: any; @@ -1720,7 +1720,7 @@ export interface ISearchBox { } // @public (undocumented) -export interface ISearchBoxProps extends React.InputHTMLAttributes { +export interface ISearchBoxProps extends React.InputHTMLAttributes, React.RefAttributes { ariaLabel?: string; className?: string; clearButtonProps?: IButtonProps_2; @@ -1937,7 +1937,7 @@ export interface IShimmerLineStyles { } // @public -export interface IShimmerProps extends React.AllHTMLAttributes { +export interface IShimmerProps extends React.AllHTMLAttributes, React.RefAttributes { ariaLabel?: string; className?: string; customElementsGroup?: React.ReactNode; @@ -2042,7 +2042,7 @@ export interface ISpinButtonStyles { } // @public (undocumented) -export interface ISwatchColorPickerProps { +export interface ISwatchColorPickerProps extends React.RefAttributes { ariaPosInSet?: number; ariaSetSize?: number; cellBorderWidth?: number; @@ -2242,19 +2242,19 @@ export class ModalBase extends React.Component implem export const OverflowSet: React.FunctionComponent; // @public (undocumented) -export const OverflowSetBase: React.ForwardRefExoticComponent & React.RefAttributes>; +export const OverflowSetBase: React.FunctionComponent; // @public export const Persona: React.FunctionComponent; // @public -export const PersonaBase: React.ForwardRefExoticComponent>; +export const PersonaBase: React.FunctionComponent; // @public export const PersonaCoin: React.FunctionComponent; // @public -export const PersonaCoinBase: React.ForwardRefExoticComponent>; +export const PersonaCoinBase: React.FunctionComponent; // @public (undocumented) export enum PersonaInitialsColor { @@ -2408,10 +2408,10 @@ export namespace personaSize { } // @public -export const Popup: React.ForwardRefExoticComponent>; +export const Popup: React.FunctionComponent; // @public (undocumented) -export const PositioningContainer: React.ForwardRefExoticComponent>; +export const PositioningContainer: React.FunctionComponent; // @public (undocumented) export const presenceBoolean: (presence: PersonaPresence) => { @@ -2427,7 +2427,7 @@ export const presenceBoolean: (presence: PersonaPresence) => { export const Rating: React.FunctionComponent; // @public (undocumented) -export const RatingBase: React.ForwardRefExoticComponent>; +export const RatingBase: React.FunctionComponent; // @public (undocumented) export enum RatingSize { @@ -2438,10 +2438,10 @@ export enum RatingSize { } // @public (undocumented) -export const ResizeGroup: import("react").ForwardRefExoticComponent>; +export const ResizeGroup: import("react").FunctionComponent; // @public (undocumented) -export const ResizeGroupBase: React.ForwardRefExoticComponent>; +export const ResizeGroupBase: React.FunctionComponent; // @public (undocumented) export enum ResizeGroupDirection { @@ -2457,7 +2457,7 @@ export { ResponsiveMode } export const SearchBox: React.FunctionComponent; // @public (undocumented) -export const SearchBoxBase: React.ForwardRefExoticComponent>; +export const SearchBoxBase: React.FunctionComponent; // @public export class SelectedPeopleList extends BasePeopleSelectedItemsList { @@ -2471,7 +2471,7 @@ export class SelectedPeopleList extends BasePeopleSelectedItemsList { export const Shimmer: React.FunctionComponent; // @public (undocumented) -export const ShimmerBase: React.ForwardRefExoticComponent>; +export const ShimmerBase: React.FunctionComponent; // @public (undocumented) export const ShimmerCircle: React.FunctionComponent; @@ -2539,7 +2539,7 @@ export const SpinButton: React.FunctionComponent; export const SwatchColorPicker: React.FunctionComponent; // @public (undocumented) -export const SwatchColorPickerBase: React.ForwardRefExoticComponent>; +export const SwatchColorPickerBase: React.FunctionComponent; export { Target } diff --git a/packages/react-next/src/components/Callout/Callout.tsx b/packages/react-next/src/components/Callout/Callout.tsx index 96469cb9edfb41..f7457a7928f7d1 100644 --- a/packages/react-next/src/components/Callout/Callout.tsx +++ b/packages/react-next/src/components/Callout/Callout.tsx @@ -3,8 +3,8 @@ import { ICalloutProps } from './Callout.types'; import { CalloutContent } from './CalloutContent'; import { Layer } from '../../Layer'; -export const Callout = React.forwardRef( - ({ layerProps, doNotLayer, ...rest }: ICalloutProps, forwardedRef: React.Ref) => { +export const Callout: React.FunctionComponent = React.forwardRef( + ({ layerProps, doNotLayer, ...rest }, forwardedRef) => { const content = ; return doNotLayer ? content : {content}; }, diff --git a/packages/react-next/src/components/Callout/Callout.types.ts b/packages/react-next/src/components/Callout/Callout.types.ts index a278dd63b1e3af..b2b9366310d3fb 100644 --- a/packages/react-next/src/components/Callout/Callout.types.ts +++ b/packages/react-next/src/components/Callout/Callout.types.ts @@ -11,7 +11,7 @@ export { Target }; /** * {@docCategory Callout} */ -export interface ICalloutProps extends React.HTMLAttributes { +export interface ICalloutProps extends React.HTMLAttributes, React.RefAttributes { /** * The target that the Callout should try to position itself based on. * It can be either an Element a querySelector string of a valid Element diff --git a/packages/react-next/src/components/Callout/CalloutContent.base.tsx b/packages/react-next/src/components/Callout/CalloutContent.base.tsx index 3261c51b4a56ab..1cf54965a16db3 100644 --- a/packages/react-next/src/components/Callout/CalloutContent.base.tsx +++ b/packages/react-next/src/components/Callout/CalloutContent.base.tsx @@ -379,8 +379,8 @@ function useDismissHandlers( return mouseDownHandlers; } -export const CalloutContentBase = React.memo( - React.forwardRef((propsWithoutDefaults: ICalloutProps, forwardedRef: React.Ref) => { +export const CalloutContentBase: React.FunctionComponent = React.memo( + React.forwardRef((propsWithoutDefaults, forwardedRef) => { const props = getPropsWithDefaults(DEFAULT_PROPS, propsWithoutDefaults); const { diff --git a/packages/react-next/src/components/ChoiceGroup/ChoiceGroup.base.tsx b/packages/react-next/src/components/ChoiceGroup/ChoiceGroup.base.tsx index b909a3a32616f5..05e1286ea9193c 100644 --- a/packages/react-next/src/components/ChoiceGroup/ChoiceGroup.base.tsx +++ b/packages/react-next/src/components/ChoiceGroup/ChoiceGroup.base.tsx @@ -62,97 +62,98 @@ function useDebugWarnings(props: IChoiceGroupProps) { /** * {@docCategory ChoiceGroup} */ -export const ChoiceGroupBase: React.FunctionComponent = React.forwardRef( - (props: IChoiceGroupProps, forwardedRef: React.Ref) => { - const { - className, - theme, - styles, - options = [], - label, - required, - disabled, - name, - defaultSelectedKey, - componentRef, - onChange, - } = props; - const id = useId('ChoiceGroup'); - const labelId = useId('ChoiceGroupLabel'); - - const divProps = getNativeProps>(props, divProperties, [ - 'onChange', - 'className', - 'required', - ]); - - const classNames = getClassNames(styles!, { - theme: theme!, - className, - optionsContainIconOrImage: options.some(option => !!(option.iconProps || option.imageSrc)), - }); - - const ariaLabelledBy = props.ariaLabelledBy || (label ? labelId : props['aria-labelledby']); - - const [keyChecked, setKeyChecked] = useControllableValue(props.selectedKey, defaultSelectedKey); - const [keyFocused, setKeyFocused] = React.useState(); - - useDebugWarnings(props); - useComponentRef(options, keyChecked, id, componentRef); - - const onFocus = React.useCallback((ev: React.FocusEvent, option: IChoiceGroupOptionProps) => { - setKeyFocused(option.itemKey); - }, []); - - const onBlur = React.useCallback((ev: React.FocusEvent) => { - setKeyFocused(undefined); - }, []); - - const onOptionChange = React.useCallback( - (evt: React.FormEvent, option: IChoiceGroupOptionProps) => { - setKeyChecked(option.itemKey); +export const ChoiceGroupBase: React.FunctionComponent = React.forwardRef< + HTMLDivElement, + IChoiceGroupProps +>((props, forwardedRef) => { + const { + className, + theme, + styles, + options = [], + label, + required, + disabled, + name, + defaultSelectedKey, + componentRef, + onChange, + } = props; + const id = useId('ChoiceGroup'); + const labelId = useId('ChoiceGroupLabel'); + + const divProps = getNativeProps>(props, divProperties, [ + 'onChange', + 'className', + 'required', + ]); + + const classNames = getClassNames(styles!, { + theme: theme!, + className, + optionsContainIconOrImage: options.some(option => !!(option.iconProps || option.imageSrc)), + }); + + const ariaLabelledBy = props.ariaLabelledBy || (label ? labelId : props['aria-labelledby']); + + const [keyChecked, setKeyChecked] = useControllableValue(props.selectedKey, defaultSelectedKey); + const [keyFocused, setKeyFocused] = React.useState(); + + useDebugWarnings(props); + useComponentRef(options, keyChecked, id, componentRef); + + const onFocus = React.useCallback((ev: React.FocusEvent, option: IChoiceGroupOptionProps) => { + setKeyFocused(option.itemKey); + }, []); + + const onBlur = React.useCallback((ev: React.FocusEvent) => { + setKeyFocused(undefined); + }, []); + + const onOptionChange = React.useCallback( + (evt: React.FormEvent, option: IChoiceGroupOptionProps) => { + setKeyChecked(option.itemKey); + + if (onChange) { + onChange( + evt, + find(options || [], (value: IChoiceGroupOption) => value.key === option.itemKey), + ); + } + }, + [onChange, options, setKeyChecked], + ); - if (onChange) { - onChange( - evt, - find(options || [], (value: IChoiceGroupOption) => value.key === option.itemKey), - ); - } - }, - [onChange, options, setKeyChecked], - ); - - return ( -
-
- {label && ( - - )} -
- {options.map((option: IChoiceGroupOption) => { - return ( - - ); - })} -
+ return ( +
+
+ {label && ( + + )} +
+ {options.map((option: IChoiceGroupOption) => { + return ( + + ); + })}
- ); - }, -); +
+ ); +}); diff --git a/packages/react-next/src/components/ChoiceGroup/ChoiceGroup.types.ts b/packages/react-next/src/components/ChoiceGroup/ChoiceGroup.types.ts index 1d2aa4f4e111e3..b9b35b1366c013 100644 --- a/packages/react-next/src/components/ChoiceGroup/ChoiceGroup.types.ts +++ b/packages/react-next/src/components/ChoiceGroup/ChoiceGroup.types.ts @@ -23,7 +23,9 @@ export interface IChoiceGroup { /** * {@docCategory ChoiceGroup} */ -export interface IChoiceGroupProps extends React.InputHTMLAttributes { +export interface IChoiceGroupProps + extends React.InputHTMLAttributes, + React.RefAttributes { /** * Optional callback to access the IChoiceGroup interface. Use this instead of ref for accessing * the public methods and properties of the component. diff --git a/packages/react-next/src/components/Coachmark/Beak/Beak.tsx b/packages/react-next/src/components/Coachmark/Beak/Beak.tsx index 6c1091ccedbee3..7cc62e50fb466b 100644 --- a/packages/react-next/src/components/Coachmark/Beak/Beak.tsx +++ b/packages/react-next/src/components/Coachmark/Beak/Beak.tsx @@ -8,71 +8,73 @@ import { RectangleEdge } from 'office-ui-fabric-react/lib/utilities/positioning' export const BEAK_HEIGHT = 10; export const BEAK_WIDTH = 18; -export const Beak = React.forwardRef((props: IBeakProps, forwardedRef: React.Ref) => { - const { left, top, bottom, right, color, direction = RectangleEdge.top } = props; +export const Beak: React.FunctionComponent = React.forwardRef( + (props, forwardedRef) => { + const { left, top, bottom, right, color, direction = RectangleEdge.top } = props; - let svgHeight: number; - let svgWidth: number; + let svgHeight: number; + let svgWidth: number; - if (direction === RectangleEdge.top || direction === RectangleEdge.bottom) { - svgHeight = BEAK_HEIGHT; - svgWidth = BEAK_WIDTH; - } else { - svgHeight = BEAK_WIDTH; - svgWidth = BEAK_HEIGHT; - } + if (direction === RectangleEdge.top || direction === RectangleEdge.bottom) { + svgHeight = BEAK_HEIGHT; + svgWidth = BEAK_WIDTH; + } else { + svgHeight = BEAK_WIDTH; + svgWidth = BEAK_HEIGHT; + } - let pointOne: string; - let pointTwo: string; - let pointThree: string; - let transform: string; + let pointOne: string; + let pointTwo: string; + let pointThree: string; + let transform: string; - switch (direction) { - case RectangleEdge.top: - default: - pointOne = `${BEAK_WIDTH / 2}, 0`; - pointTwo = `${BEAK_WIDTH}, ${BEAK_HEIGHT}`; - pointThree = `0, ${BEAK_HEIGHT}`; - transform = 'translateY(-100%)'; - break; - case RectangleEdge.right: - pointOne = `0, 0`; - pointTwo = `${BEAK_HEIGHT}, ${BEAK_HEIGHT}`; - pointThree = `0, ${BEAK_WIDTH}`; - transform = 'translateX(100%)'; - break; - case RectangleEdge.bottom: - pointOne = `0, 0`; - pointTwo = `${BEAK_WIDTH}, 0`; - pointThree = `${BEAK_WIDTH / 2}, ${BEAK_HEIGHT}`; - transform = 'translateY(100%)'; - break; - case RectangleEdge.left: - pointOne = `${BEAK_HEIGHT}, 0`; - pointTwo = `0, ${BEAK_HEIGHT}`; - pointThree = `${BEAK_HEIGHT}, ${BEAK_WIDTH}`; - transform = 'translateX(-100%)'; - break; - } + switch (direction) { + case RectangleEdge.top: + default: + pointOne = `${BEAK_WIDTH / 2}, 0`; + pointTwo = `${BEAK_WIDTH}, ${BEAK_HEIGHT}`; + pointThree = `0, ${BEAK_HEIGHT}`; + transform = 'translateY(-100%)'; + break; + case RectangleEdge.right: + pointOne = `0, 0`; + pointTwo = `${BEAK_HEIGHT}, ${BEAK_HEIGHT}`; + pointThree = `0, ${BEAK_WIDTH}`; + transform = 'translateX(100%)'; + break; + case RectangleEdge.bottom: + pointOne = `0, 0`; + pointTwo = `${BEAK_WIDTH}, 0`; + pointThree = `${BEAK_WIDTH / 2}, ${BEAK_HEIGHT}`; + transform = 'translateY(100%)'; + break; + case RectangleEdge.left: + pointOne = `${BEAK_HEIGHT}, 0`; + pointTwo = `0, ${BEAK_HEIGHT}`; + pointThree = `${BEAK_HEIGHT}, ${BEAK_WIDTH}`; + transform = 'translateX(-100%)'; + break; + } - const getClassNames = classNamesFunction(); - const classNames = getClassNames(getStyles, { - left, - top, - bottom, - right, - height: `${svgHeight}px`, - width: `${svgWidth}px`, - transform: transform, - color, - }); + const getClassNames = classNamesFunction(); + const classNames = getClassNames(getStyles, { + left, + top, + bottom, + right, + height: `${svgHeight}px`, + width: `${svgWidth}px`, + transform: transform, + color, + }); - return ( -
- - - -
- ); -}); + return ( +
+ + + +
+ ); + }, +); Beak.displayName = 'Beak'; diff --git a/packages/react-next/src/components/Coachmark/Beak/Beak.types.ts b/packages/react-next/src/components/Coachmark/Beak/Beak.types.ts index 3dd058890a3ce3..c8b7e036778e65 100644 --- a/packages/react-next/src/components/Coachmark/Beak/Beak.types.ts +++ b/packages/react-next/src/components/Coachmark/Beak/Beak.types.ts @@ -1,6 +1,7 @@ +import * as React from 'react'; import { RectangleEdge } from 'office-ui-fabric-react/lib/utilities/positioning'; -export interface IBeakProps { +export interface IBeakProps extends React.RefAttributes { /** * Beak width. * @defaultvalue 18 diff --git a/packages/react-next/src/components/Coachmark/Coachmark.base.tsx b/packages/react-next/src/components/Coachmark/Coachmark.base.tsx index 4bdaff030305bf..d50dcc8a9dafeb 100644 --- a/packages/react-next/src/components/Coachmark/Coachmark.base.tsx +++ b/packages/react-next/src/components/Coachmark/Coachmark.base.tsx @@ -401,126 +401,128 @@ function useDeprecationWarning(props: ICoachmarkProps) { } const COMPONENT_NAME = 'CoachmarkBase'; -export const CoachmarkBase = React.forwardRef( - (propsWithoutDefaults: ICoachmarkProps, forwardedRef: React.Ref) => { - const props = getPropsWithDefaults(DEFAULT_PROPS, propsWithoutDefaults); - - const entityInnerHostElementRef = React.useRef(null); - const translateAnimationContainer = React.useRef(null); - - const [targetAlignment, targetPosition, onPositioned] = usePositionedData(); - const [isCollapsed, openCoachmark] = useCollapsedState(props, entityInnerHostElementRef); - const [beakPositioningProps, transformOrigin] = useBeakPosition(props, targetAlignment, targetPosition); - const [isMeasuring, entityInnerHostRect] = useEntityHostMeasurements(props, entityInnerHostElementRef); - const alertText = useAriaAlert(props); - const entityHost = useAutoFocus(props); - - useListeners(props, translateAnimationContainer, openCoachmark); - useComponentRef(props); - useProximityHandlers(props, translateAnimationContainer, openCoachmark); - useDeprecationWarning(props); - - const { - beaconColorOne, - beaconColorTwo, - children, - target, - color, - positioningContainerProps, - ariaDescribedBy, - ariaDescribedByText, - ariaLabelledBy, - ariaLabelledByText, - ariaAlertText, - delayBeforeCoachmarkAnimation, - styles, - theme, - className, - persistentBeak, - } = props; - - // Defaulting the main background before passing it to the styles because it is used for `Beak` too. - let defaultColor = color; - if (!defaultColor && theme) { - defaultColor = theme.semanticColors.primaryButtonBackground; - } - const classNames = getClassNames(styles, { - theme, - beaconColorOne, - beaconColorTwo, - className, - isCollapsed, - isMeasuring, - color: defaultColor, - transformOrigin, - entityHostHeight: entityInnerHostRect.height === undefined ? undefined : `${entityInnerHostRect.height}px`, - entityHostWidth: entityInnerHostRect.width === undefined ? undefined : `${entityInnerHostRect.width}px`, - width: `${COACHMARK_WIDTH}px`, - height: `${COACHMARK_HEIGHT}px`, - delayBeforeCoachmarkAnimation: `${delayBeforeCoachmarkAnimation}ms`, - }); +export const CoachmarkBase: React.FunctionComponent = React.forwardRef< + HTMLDivElement, + ICoachmarkProps +>((propsWithoutDefaults, forwardedRef) => { + const props = getPropsWithDefaults(DEFAULT_PROPS, propsWithoutDefaults); + + const entityInnerHostElementRef = React.useRef(null); + const translateAnimationContainer = React.useRef(null); + + const [targetAlignment, targetPosition, onPositioned] = usePositionedData(); + const [isCollapsed, openCoachmark] = useCollapsedState(props, entityInnerHostElementRef); + const [beakPositioningProps, transformOrigin] = useBeakPosition(props, targetAlignment, targetPosition); + const [isMeasuring, entityInnerHostRect] = useEntityHostMeasurements(props, entityInnerHostElementRef); + const alertText = useAriaAlert(props); + const entityHost = useAutoFocus(props); + + useListeners(props, translateAnimationContainer, openCoachmark); + useComponentRef(props); + useProximityHandlers(props, translateAnimationContainer, openCoachmark); + useDeprecationWarning(props); + + const { + beaconColorOne, + beaconColorTwo, + children, + target, + color, + positioningContainerProps, + ariaDescribedBy, + ariaDescribedByText, + ariaLabelledBy, + ariaLabelledByText, + ariaAlertText, + delayBeforeCoachmarkAnimation, + styles, + theme, + className, + persistentBeak, + } = props; + + // Defaulting the main background before passing it to the styles because it is used for `Beak` too. + let defaultColor = color; + if (!defaultColor && theme) { + defaultColor = theme.semanticColors.primaryButtonBackground; + } - const finalHeight: number | undefined = isCollapsed ? COACHMARK_HEIGHT : entityInnerHostRect.height; - - return ( - -
- {ariaAlertText && ( -
- {alertText} -
- )} -
-
-
-
- {(isCollapsed || persistentBeak) && } -
- {isCollapsed && [ - ariaLabelledBy && ( -

- {ariaLabelledByText} -

- ), - ariaDescribedBy && ( -

- {ariaDescribedByText} -

- ), - ]} - -
-
- {children} -
+ const classNames = getClassNames(styles, { + theme, + beaconColorOne, + beaconColorTwo, + className, + isCollapsed, + isMeasuring, + color: defaultColor, + transformOrigin, + entityHostHeight: entityInnerHostRect.height === undefined ? undefined : `${entityInnerHostRect.height}px`, + entityHostWidth: entityInnerHostRect.width === undefined ? undefined : `${entityInnerHostRect.width}px`, + width: `${COACHMARK_WIDTH}px`, + height: `${COACHMARK_HEIGHT}px`, + delayBeforeCoachmarkAnimation: `${delayBeforeCoachmarkAnimation}ms`, + }); + + const finalHeight: number | undefined = isCollapsed ? COACHMARK_HEIGHT : entityInnerHostRect.height; + + return ( + +
+ {ariaAlertText && ( +
+ {alertText} +
+ )} +
+
+
+
+ {(isCollapsed || persistentBeak) && } +
+ {isCollapsed && [ + ariaLabelledBy && ( +

+ {ariaLabelledByText} +

+ ), + ariaDescribedBy && ( +

+ {ariaDescribedByText} +

+ ), + ]} + +
+
+ {children}
- -
+
+
- - ); - }, -); +
+
+ ); +}); CoachmarkBase.displayName = COMPONENT_NAME; function getBounds({ isPositionForced, positioningContainerProps }: ICoachmarkProps): IRectangle | undefined { diff --git a/packages/react-next/src/components/Coachmark/Coachmark.types.ts b/packages/react-next/src/components/Coachmark/Coachmark.types.ts index eb8a3e598ede31..bd85c568b3c54d 100644 --- a/packages/react-next/src/components/Coachmark/Coachmark.types.ts +++ b/packages/react-next/src/components/Coachmark/Coachmark.types.ts @@ -18,7 +18,7 @@ export interface ICoachmark { * Coachmark component props * {@docCategory Coachmark} */ -export interface ICoachmarkProps { +export interface ICoachmarkProps extends React.RefAttributes { /** * Optional callback to access the ICoachmark interface. Use this instead of ref for accessing * the public methods and properties of the component. diff --git a/packages/react-next/src/components/Coachmark/PositioningContainer/PositioningContainer.tsx b/packages/react-next/src/components/Coachmark/PositioningContainer/PositioningContainer.tsx index 72392dcce493a8..0cb86a6236447b 100644 --- a/packages/react-next/src/components/Coachmark/PositioningContainer/PositioningContainer.tsx +++ b/packages/react-next/src/components/Coachmark/PositioningContainer/PositioningContainer.tsx @@ -283,81 +283,82 @@ export function useHeightOffset( return heightOffset; } -export const PositioningContainer = React.forwardRef( - (propsWithoutDefaults: IPositioningContainerProps, forwardedRef: React.Ref) => { - const props = getPropsWithDefaults(DEFAULT_PROPS, propsWithoutDefaults); - - // @TODO rename to reflect the name of this class - const contentHost = React.useRef(null); - /** - * The primary positioned div. - */ - const positionedHost = React.useRef(null); - const rootRef = useMergedRefs(forwardedRef, positionedHost); - - const [targetRef, targetWindow] = useTarget(props.target, positionedHost); - const getCachedBounds = useCachedBounds(props, targetWindow); - const [positions, updateAsyncPosition] = usePositionState( - props, - positionedHost, - contentHost, - targetRef, - getCachedBounds, - ); - const getCachedMaxHeight = useMaxHeight(props, targetRef, getCachedBounds); - const heightOffset = useHeightOffset(props, contentHost); - - useSetInitialFocus(props, contentHost, positions); - useAutoDismissEvents(props, positionedHost, targetWindow, targetRef, positions, updateAsyncPosition); - - // eslint-disable-next-line react-hooks/exhaustive-deps -- should only run on initial render - React.useEffect(() => props.onLayerMounted?.(), []); - - // If there is no target window then we are likely in server side rendering and we should not render anything. - if (!targetWindow) { - return null; - } +export const PositioningContainer: React.FunctionComponent = React.forwardRef< + HTMLDivElement, + IPositioningContainerProps +>((propsWithoutDefaults, forwardedRef) => { + const props = getPropsWithDefaults(DEFAULT_PROPS, propsWithoutDefaults); + + // @TODO rename to reflect the name of this class + const contentHost = React.useRef(null); + /** + * The primary positioned div. + */ + const positionedHost = React.useRef(null); + const rootRef = useMergedRefs(forwardedRef, positionedHost); + + const [targetRef, targetWindow] = useTarget(props.target, positionedHost); + const getCachedBounds = useCachedBounds(props, targetWindow); + const [positions, updateAsyncPosition] = usePositionState( + props, + positionedHost, + contentHost, + targetRef, + getCachedBounds, + ); + const getCachedMaxHeight = useMaxHeight(props, targetRef, getCachedBounds); + const heightOffset = useHeightOffset(props, contentHost); + + useSetInitialFocus(props, contentHost, positions); + useAutoDismissEvents(props, positionedHost, targetWindow, targetRef, positions, updateAsyncPosition); + + // eslint-disable-next-line react-hooks/exhaustive-deps -- should only run on initial render + React.useEffect(() => props.onLayerMounted?.(), []); + + // If there is no target window then we are likely in server side rendering and we should not render anything. + if (!targetWindow) { + return null; + } - const { className, positioningContainerWidth, positioningContainerMaxHeight, children } = props; - - const styles = getClassNames(); - - const directionalClassName = - positions && positions.targetEdge ? AnimationClassNames[SLIDE_ANIMATIONS[positions.targetEdge]] : ''; - - const getContentMaxHeight: number = getCachedMaxHeight() + heightOffset!; - const contentMaxHeight: number = - positioningContainerMaxHeight! && positioningContainerMaxHeight! > getContentMaxHeight - ? getContentMaxHeight - : positioningContainerMaxHeight!; - const content = ( -
-
- {children} - { - // @TODO apply to the content container - contentMaxHeight - } -
+ const { className, positioningContainerWidth, positioningContainerMaxHeight, children } = props; + + const styles = getClassNames(); + + const directionalClassName = + positions && positions.targetEdge ? AnimationClassNames[SLIDE_ANIMATIONS[positions.targetEdge]] : ''; + + const getContentMaxHeight: number = getCachedMaxHeight() + heightOffset!; + const contentMaxHeight: number = + positioningContainerMaxHeight! && positioningContainerMaxHeight! > getContentMaxHeight + ? getContentMaxHeight + : positioningContainerMaxHeight!; + const content = ( +
+
+ {children} + { + // @TODO apply to the content container + contentMaxHeight + }
- ); +
+ ); - return props.doNotLayer ? content : {content}; - }, -); + return props.doNotLayer ? content : {content}; +}); PositioningContainer.displayName = 'PositioningContainer'; function arePositionsEqual(positions: IPositionedData, newPosition: IPositionedData): boolean { diff --git a/packages/react-next/src/components/Coachmark/PositioningContainer/PositioningContainer.types.ts b/packages/react-next/src/components/Coachmark/PositioningContainer/PositioningContainer.types.ts index 5efb5607b6417e..a7e90ec1ce71a7 100644 --- a/packages/react-next/src/components/Coachmark/PositioningContainer/PositioningContainer.types.ts +++ b/packages/react-next/src/components/Coachmark/PositioningContainer/PositioningContainer.types.ts @@ -1,3 +1,4 @@ +import * as React from 'react'; import { DirectionalHint } from '../../../common/DirectionalHint'; import { IRefObject, IBaseProps, Point, IRectangle } from '../../../Utilities'; import { IPositionedData } from 'office-ui-fabric-react/lib/utilities/positioning'; @@ -11,11 +12,14 @@ export interface IPositioningContainer {} /** * {@docCategory Coachmark} */ -export interface IPositioningContainerProps extends IBaseProps { +export interface IPositioningContainerProps + extends IBaseProps, + React.RefAttributes { /** * All props for your component are to be defined here. */ componentRef?: IRefObject; + /** * The target that the positioningContainer should try to position itself based on. * It can be either an HTMLElement a querySelector string of a valid HTMLElement diff --git a/packages/react-next/src/components/Dropdown/Dropdown.base.tsx b/packages/react-next/src/components/Dropdown/Dropdown.base.tsx index c4c4320d58bdcd..8d424efb6ae076 100644 --- a/packages/react-next/src/components/Dropdown/Dropdown.base.tsx +++ b/packages/react-next/src/components/Dropdown/Dropdown.base.tsx @@ -54,7 +54,7 @@ import { useMergedRefs, usePrevious } from '@uifabric/react-hooks'; const getClassNames = classNamesFunction(); /** Internal only props interface to support mixing in responsive mode */ -interface IDropdownInternalProps extends IDropdownProps, IWithResponsiveModeState { +interface IDropdownInternalProps extends Omit, IWithResponsiveModeState { hoisted: { rootRef: React.Ref; selectedIndices: number[]; @@ -159,8 +159,8 @@ function useSelectedItemsState({ return [selectedIndices, setSelectedIndices] as const; } -export const DropdownBase = React.forwardRef( - (propsWithoutDefaults: IDropdownProps, forwardedRef: React.Ref) => { +export const DropdownBase: React.FunctionComponent = React.forwardRef( + (propsWithoutDefaults, forwardedRef) => { const props = getPropsWithDefaults(DEFAULT_PROPS, propsWithoutDefaults); const rootRef = React.useRef(null); @@ -171,7 +171,7 @@ export const DropdownBase = React.forwardRef( return ( )} responsiveMode={responsiveMode} hoisted={{ rootRef: mergedRootRef, selectedIndices, setSelectedIndices }} /> diff --git a/packages/react-next/src/components/Dropdown/Dropdown.types.ts b/packages/react-next/src/components/Dropdown/Dropdown.types.ts index 692415d063d426..93b6d378f5c8a8 100644 --- a/packages/react-next/src/components/Dropdown/Dropdown.types.ts +++ b/packages/react-next/src/components/Dropdown/Dropdown.types.ts @@ -29,7 +29,9 @@ export interface IDropdown { /** * {@docCategory Dropdown} */ -export interface IDropdownProps extends ISelectableDroppableTextProps { +export interface IDropdownProps + extends ISelectableDroppableTextProps, + React.RefAttributes { /** * Input placeholder text. Displayed until option is selected. * @deprecated Use `placeholder` diff --git a/packages/react-next/src/components/Fabric/Fabric.base.tsx b/packages/react-next/src/components/Fabric/Fabric.base.tsx index a8cd803f44944e..69b29352a80593 100644 --- a/packages/react-next/src/components/Fabric/Fabric.base.tsx +++ b/packages/react-next/src/components/Fabric/Fabric.base.tsx @@ -32,21 +32,23 @@ const getDir = ({ theme, dir }: IFabricProps) => { }; }; -export const FabricBase = React.forwardRef((props: IFabricProps, ref: React.Ref) => { - const { className, theme, applyTheme, applyThemeToBody, styles } = props; +export const FabricBase: React.FunctionComponent = React.forwardRef( + (props, ref) => { + const { className, theme, applyTheme, applyThemeToBody, styles } = props; - const classNames = getClassNames(styles, { - theme: theme!, - applyTheme: applyTheme, - className, - }); + const classNames = getClassNames(styles, { + theme: theme!, + applyTheme: applyTheme, + className, + }); - const rootElement = React.useRef(null); - useApplyThemeToBody(applyThemeToBody, classNames, rootElement); - useFocusRects(rootElement); + const rootElement = React.useRef(null); + useApplyThemeToBody(applyThemeToBody, classNames, rootElement); + useFocusRects(rootElement); - return <>{useRenderedContent(props, classNames, rootElement, ref)}; -}); + return <>{useRenderedContent(props, classNames, rootElement, ref)}; + }, +); FabricBase.displayName = 'FabricBase'; function useRenderedContent( diff --git a/packages/react-next/src/components/Fabric/Fabric.types.ts b/packages/react-next/src/components/Fabric/Fabric.types.ts index c5aa9576d050d8..f88c8883766aa9 100644 --- a/packages/react-next/src/components/Fabric/Fabric.types.ts +++ b/packages/react-next/src/components/Fabric/Fabric.types.ts @@ -2,7 +2,7 @@ import * as React from 'react'; import { IStyle, ITheme } from '../../Styling'; import { IRefObject, IStyleFunctionOrObject } from '../../Utilities'; -export interface IFabricProps extends React.HTMLAttributes { +export interface IFabricProps extends React.HTMLAttributes, React.RefAttributes { componentRef?: IRefObject<{}>; /** diff --git a/packages/react-next/src/components/Image/Image.base.tsx b/packages/react-next/src/components/Image/Image.base.tsx index 84089323f70ff8..7d69d2742d4c06 100644 --- a/packages/react-next/src/components/Image/Image.base.tsx +++ b/packages/react-next/src/components/Image/Image.base.tsx @@ -74,68 +74,70 @@ function useLoadState( return [loadState, onImageLoaded, onImageError] as const; } -export const ImageBase = React.forwardRef((props: IImageProps, forwardedRef: React.Ref) => { - const frameElement = React.useRef() as React.RefObject; - const imageElement = React.useRef() as React.RefObject; - const [loadState, onImageLoaded, onImageError] = useLoadState(props, imageElement); - - const imageProps = getNativeProps>(props, imgProperties, [ - 'width', - 'height', - ]); - const { - src, - alt, - width, - height, - shouldFadeIn = true, - shouldStartVisible, - className, - imageFit, - role, - maximizeFrame, - styles, - theme, - } = props; - const coverStyle = useCoverStyle(props, loadState, imageElement, frameElement); - const classNames = getClassNames(styles!, { - theme: theme!, - className, - width, - height, - maximizeFrame, - shouldFadeIn, - shouldStartVisible, - isLoaded: - loadState === ImageLoadState.loaded || (loadState === ImageLoadState.notLoaded && props.shouldStartVisible), - isLandscape: coverStyle === ImageCoverStyle.landscape, - isCenter: imageFit === ImageFit.center, - isCenterContain: imageFit === ImageFit.centerContain, - isCenterCover: imageFit === ImageFit.centerCover, - isContain: imageFit === ImageFit.contain, - isCover: imageFit === ImageFit.cover, - isNone: imageFit === ImageFit.none, - isError: loadState === ImageLoadState.error, - isNotImageFit: imageFit === undefined, - }); - - // If image dimensions aren't specified, the natural size of the image is used. - return ( -
- {alt} -
- ); -}); +export const ImageBase: React.FunctionComponent = React.forwardRef( + (props, forwardedRef) => { + const frameElement = React.useRef() as React.RefObject; + const imageElement = React.useRef() as React.RefObject; + const [loadState, onImageLoaded, onImageError] = useLoadState(props, imageElement); + + const imageProps = getNativeProps>(props, imgProperties, [ + 'width', + 'height', + ]); + const { + src, + alt, + width, + height, + shouldFadeIn = true, + shouldStartVisible, + className, + imageFit, + role, + maximizeFrame, + styles, + theme, + } = props; + const coverStyle = useCoverStyle(props, loadState, imageElement, frameElement); + const classNames = getClassNames(styles!, { + theme: theme!, + className, + width, + height, + maximizeFrame, + shouldFadeIn, + shouldStartVisible, + isLoaded: + loadState === ImageLoadState.loaded || (loadState === ImageLoadState.notLoaded && props.shouldStartVisible), + isLandscape: coverStyle === ImageCoverStyle.landscape, + isCenter: imageFit === ImageFit.center, + isCenterContain: imageFit === ImageFit.centerContain, + isCenterCover: imageFit === ImageFit.centerCover, + isContain: imageFit === ImageFit.contain, + isCover: imageFit === ImageFit.cover, + isNone: imageFit === ImageFit.none, + isError: loadState === ImageLoadState.error, + isNotImageFit: imageFit === undefined, + }); + + // If image dimensions aren't specified, the natural size of the image is used. + return ( +
+ {alt} +
+ ); + }, +); ImageBase.displayName = 'ImageBase'; function useCoverStyle( diff --git a/packages/react-next/src/components/Image/Image.types.ts b/packages/react-next/src/components/Image/Image.types.ts index 9f7142ebb2ae81..0f17946bf0cdf7 100644 --- a/packages/react-next/src/components/Image/Image.types.ts +++ b/packages/react-next/src/components/Image/Image.types.ts @@ -10,7 +10,7 @@ export interface IImage {} /** * {@docCategory Image} */ -export interface IImageProps extends React.ImgHTMLAttributes { +export interface IImageProps extends React.ImgHTMLAttributes, React.RefAttributes { /** * Call to provide customized styling that will layer on top of the variant rules */ diff --git a/packages/react-next/src/components/OverflowSet/OverflowSet.base.tsx b/packages/react-next/src/components/OverflowSet/OverflowSet.base.tsx index 0cfab1586bb0a4..67b9778fee459c 100644 --- a/packages/react-next/src/components/OverflowSet/OverflowSet.base.tsx +++ b/packages/react-next/src/components/OverflowSet/OverflowSet.base.tsx @@ -35,7 +35,10 @@ const useComponentRef = (props: IOverflowSetProps, divContainer: React.RefObject ); }; -export const OverflowSetBase = React.forwardRef((props: IOverflowSetProps, forwardedRef: React.Ref) => { +export const OverflowSetBase: React.FunctionComponent = React.forwardRef< + HTMLDivElement, + IOverflowSetProps +>((props, forwardedRef) => { const divContainer = React.useRef(null); const mergedRef = useMergedRefs(divContainer, forwardedRef); useComponentRef(props, divContainer); diff --git a/packages/react-next/src/components/Persona/Persona.base.tsx b/packages/react-next/src/components/Persona/Persona.base.tsx index 53a8563ca4bace..992e8990cd1d7a 100644 --- a/packages/react-next/src/components/Persona/Persona.base.tsx +++ b/packages/react-next/src/components/Persona/Persona.base.tsx @@ -16,7 +16,7 @@ import { PersonaSize, IPersonaCoinProps, } from './Persona.types'; -import { useWarnings } from '@uifabric/react-hooks'; +import { useWarnings, useMergedRefs } from '@uifabric/react-hooks'; const getClassNames = classNamesFunction(); @@ -41,11 +41,13 @@ function useDebugWarnings(props: IPersonaProps) { * Persona with no default styles. * [Use the `styles` API to add your own styles.](https://github.com/microsoft/fluentui/wiki/Styling) */ -export const PersonaBase = React.forwardRef( - (propsWithoutDefaults: IPersonaProps, forwardedRef: React.Ref) => { +export const PersonaBase: React.FunctionComponent = React.forwardRef( + (propsWithoutDefaults, forwardedRef) => { const props = getPropsWithDefaults(DEFAULT_PROPS, propsWithoutDefaults); useDebugWarnings(props); + const rootRef = React.useRef(null); + const mergedRootRef = useMergedRefs(forwardedRef, rootRef); /** * Deprecation helper for getting text. @@ -189,6 +191,7 @@ export const PersonaBase = React.forwardRef( return (
diff --git a/packages/react-next/src/components/Persona/Persona.types.ts b/packages/react-next/src/components/Persona/Persona.types.ts index 988a0aab948968..35a41b58a54d97 100644 --- a/packages/react-next/src/components/Persona/Persona.types.ts +++ b/packages/react-next/src/components/Persona/Persona.types.ts @@ -12,7 +12,7 @@ export interface IPersona {} /** * {@docCategory Persona} */ -export interface IPersonaSharedProps extends React.HTMLAttributes { +export interface IPersonaSharedProps extends React.HTMLAttributes, React.RefAttributes { /** * Primary text to display, usually the name of the person. */ diff --git a/packages/react-next/src/components/Persona/PersonaCoin/PersonaCoin.base.tsx b/packages/react-next/src/components/Persona/PersonaCoin/PersonaCoin.base.tsx index 1ad4e88651c657..b72c233aefa627 100644 --- a/packages/react-next/src/components/Persona/PersonaCoin/PersonaCoin.base.tsx +++ b/packages/react-next/src/components/Persona/PersonaCoin/PersonaCoin.base.tsx @@ -66,102 +66,100 @@ function useImageLoadState({ onPhotoLoadingStateChange, imageUrl }: IPersonaCoin * PersonaCoin with no default styles. * [Use the `getStyles` API to add your own styles.](https://github.com/microsoft/fluentui/wiki/Styling) */ -export const PersonaCoinBase = React.forwardRef( - (propsWithoutDefaults: IPersonaCoinProps, forwardedRef: React.Ref) => { - const props = getPropsWithDefaults(DEFAULT_PROPS, propsWithoutDefaults); +export const PersonaCoinBase: React.FunctionComponent = React.forwardRef< + HTMLDivElement, + IPersonaCoinProps +>((propsWithoutDefaults, forwardedRef) => { + const props = getPropsWithDefaults(DEFAULT_PROPS, propsWithoutDefaults); - useDebugWarnings(props); + useDebugWarnings(props); - const [imageLoadState, onLoadingStateChange] = useImageLoadState(props); + const [imageLoadState, onLoadingStateChange] = useImageLoadState(props); - const renderCoin = getCoinRenderer(onLoadingStateChange); + const renderCoin = getCoinRenderer(onLoadingStateChange); - const { - className, - coinProps, - showUnknownPersonaCoin, - coinSize, - styles, - imageUrl, - isOutOfOffice, - // eslint-disable-next-line deprecation/deprecation - onRenderCoin = renderCoin, - // eslint-disable-next-line deprecation/deprecation - onRenderPersonaCoin = onRenderCoin, - onRenderInitials = renderPersonaCoinInitials, - presence, - presenceTitle, - presenceColors, - showInitialsUntilImageLoads, - theme, - size, - } = props; - - const divProps = getNativeProps>(props, divProperties); - const divCoinProps = getNativeProps>(coinProps || {}, divProperties); - const coinSizeStyle = coinSize ? { width: coinSize, height: coinSize } : undefined; - const hideImage = showUnknownPersonaCoin; - - const personaPresenceProps: IPersonaPresenceProps = { - coinSize, - isOutOfOffice, - presence, - presenceTitle, - presenceColors, - size, - theme, - }; - - // Use getStyles from props, or fall back to getStyles from styles file. - const classNames = getClassNames(styles, { - theme: theme!, - className: coinProps && coinProps.className ? coinProps.className : className, - size, - coinSize, - showUnknownPersonaCoin, - }); + const { + className, + coinProps, + showUnknownPersonaCoin, + coinSize, + styles, + imageUrl, + isOutOfOffice, + // eslint-disable-next-line deprecation/deprecation + onRenderCoin = renderCoin, + // eslint-disable-next-line deprecation/deprecation + onRenderPersonaCoin = onRenderCoin, + onRenderInitials = renderPersonaCoinInitials, + presence, + presenceTitle, + presenceColors, + showInitialsUntilImageLoads, + theme, + size, + } = props; + + const divProps = getNativeProps>(props, divProperties); + const divCoinProps = getNativeProps>(coinProps || {}, divProperties); + const coinSizeStyle = coinSize ? { width: coinSize, height: coinSize } : undefined; + const hideImage = showUnknownPersonaCoin; + + const personaPresenceProps: IPersonaPresenceProps = { + coinSize, + isOutOfOffice, + presence, + presenceTitle, + presenceColors, + size, + theme, + }; - const shouldRenderInitials = Boolean( - imageLoadState !== ImageLoadState.loaded && - ((showInitialsUntilImageLoads && imageUrl) || - !imageUrl || - imageLoadState === ImageLoadState.error || - hideImage), - ); - - return ( -
- {// Render PersonaCoin if size is not size8. size10 and tiny need to removed after a deprecation cleanup. - // eslint-disable-next-line deprecation/deprecation - size !== PersonaSize.size8 && size !== PersonaSize.size10 && size !== PersonaSize.tiny ? ( -
- {shouldRenderInitials && ( - - )} - {!hideImage && onRenderPersonaCoin(props, renderCoin)} - -
- ) : // Otherwise, render just PersonaPresence. - props.presence ? ( + // Use getStyles from props, or fall back to getStyles from styles file. + const classNames = getClassNames(styles, { + theme: theme!, + className: coinProps && coinProps.className ? coinProps.className : className, + size, + coinSize, + showUnknownPersonaCoin, + }); + + const shouldRenderInitials = Boolean( + imageLoadState !== ImageLoadState.loaded && + ((showInitialsUntilImageLoads && imageUrl) || !imageUrl || imageLoadState === ImageLoadState.error || hideImage), + ); + + return ( +
+ {// Render PersonaCoin if size is not size8. size10 and tiny need to removed after a deprecation cleanup. + // eslint-disable-next-line deprecation/deprecation + size !== PersonaSize.size8 && size !== PersonaSize.size10 && size !== PersonaSize.tiny ? ( +
+ {shouldRenderInitials && ( + + )} + {!hideImage && onRenderPersonaCoin(props, renderCoin)} - ) : ( - // Just render Contact Icon if there isn't a Presence prop. - - )} - {props.children} -
- ); - }, -); +
+ ) : // Otherwise, render just PersonaPresence. + props.presence ? ( + + ) : ( + // Just render Contact Icon if there isn't a Presence prop. + + )} + {props.children} +
+ ); +}); PersonaCoinBase.displayName = 'PersonaCoinBase'; const getCoinRenderer = (onLoadingStateChange: (loadState: ImageLoadState) => void) => ({ diff --git a/packages/react-next/src/components/Persona/PersonaPresence/PersonaPresence.base.tsx b/packages/react-next/src/components/Persona/PersonaPresence/PersonaPresence.base.tsx index 1f21ba5cb0a215..bd68f9c5da77c2 100644 --- a/packages/react-next/src/components/Persona/PersonaPresence/PersonaPresence.base.tsx +++ b/packages/react-next/src/components/Persona/PersonaPresence/PersonaPresence.base.tsx @@ -9,6 +9,7 @@ import { PersonaSize, } from '../Persona.types'; import { sizeBoolean } from '../PersonaConsts'; +import { useMergedRefs } from '@uifabric/react-hooks'; const coinSizeFontScaleFactor = 6; const coinSizePresenceScaleFactor = 3; @@ -25,73 +26,76 @@ const getClassNames = classNamesFunction) => { - const { - coinSize, - isOutOfOffice, - styles, // Use getStyles from props. - presence, - theme, - presenceTitle, - presenceColors, - } = props; - const size = sizeBoolean(props.size as PersonaSize); +export const PersonaPresenceBase: React.FunctionComponent = React.forwardRef< + HTMLDivElement, + IPersonaPresenceProps +>((props, forwardedRef) => { + const { + coinSize, + isOutOfOffice, + styles, // Use getStyles from props. + presence, + theme, + presenceTitle, + presenceColors, + } = props; - // Render Presence Icon if Persona is above size 32. - const renderIcon = - !(size.isSize8 || size.isSize10 || size.isSize16 || size.isSize24 || size.isSize28 || size.isSize32) && - (coinSize ? coinSize > 32 : true); + const rootRef = React.useRef(null); + const mergedRootRef = useMergedRefs(forwardedRef, rootRef); - const presenceHeightWidth: string = coinSize - ? coinSize / coinSizePresenceScaleFactor < presenceMaxSize - ? coinSize / coinSizePresenceScaleFactor + 'px' - : presenceMaxSize + 'px' - : ''; - const presenceFontSize: string = coinSize - ? coinSize / coinSizeFontScaleFactor < presenceFontMaxSize - ? coinSize / coinSizeFontScaleFactor + 'px' - : presenceFontMaxSize + 'px' - : ''; - const coinSizeWithPresenceIconStyle = coinSize - ? { fontSize: presenceFontSize, lineHeight: presenceHeightWidth } - : undefined; - const coinSizeWithPresenceStyle = coinSize - ? { width: presenceHeightWidth, height: presenceHeightWidth } - : undefined; + const size = sizeBoolean(props.size as PersonaSize); - // Use getStyles from props, or fall back to getStyles from styles file. - const classNames = getClassNames(styles, { - theme: theme!, - presence, - size: props.size, - isOutOfOffice, - presenceColors, - }); + // Render Presence Icon if Persona is above size 32. + const renderIcon = + !(size.isSize8 || size.isSize10 || size.isSize16 || size.isSize24 || size.isSize28 || size.isSize32) && + (coinSize ? coinSize > 32 : true); - if (presence === PersonaPresenceEnum.none) { - return null; - } + const presenceHeightWidth: string = coinSize + ? coinSize / coinSizePresenceScaleFactor < presenceMaxSize + ? coinSize / coinSizePresenceScaleFactor + 'px' + : presenceMaxSize + 'px' + : ''; + const presenceFontSize: string = coinSize + ? coinSize / coinSizeFontScaleFactor < presenceFontMaxSize + ? coinSize / coinSizeFontScaleFactor + 'px' + : presenceFontMaxSize + 'px' + : ''; + const coinSizeWithPresenceIconStyle = coinSize + ? { fontSize: presenceFontSize, lineHeight: presenceHeightWidth } + : undefined; + const coinSizeWithPresenceStyle = coinSize ? { width: presenceHeightWidth, height: presenceHeightWidth } : undefined; - return ( -
- {renderIcon && ( - - )} -
- ); - }, -); + // Use getStyles from props, or fall back to getStyles from styles file. + const classNames = getClassNames(styles, { + theme: theme!, + presence, + size: props.size, + isOutOfOffice, + presenceColors, + }); + + if (presence === PersonaPresenceEnum.none) { + return null; + } + + return ( +
+ {renderIcon && ( + + )} +
+ ); +}); PersonaPresenceBase.displayName = 'PersonaPresenceBase'; function determineIcon( diff --git a/packages/react-next/src/components/Popup/Popup.tsx b/packages/react-next/src/components/Popup/Popup.tsx index fdd6b159bb6129..7f63d0242c2f4e 100644 --- a/packages/react-next/src/components/Popup/Popup.tsx +++ b/packages/react-next/src/components/Popup/Popup.tsx @@ -107,52 +107,54 @@ function useRestoreFocus(props: IPopupProps, root: React.RefObject) => { - // Default props - // eslint-disable-next-line deprecation/deprecation - props = { shouldRestoreFocus: true, ...props }; +export const Popup: React.FunctionComponent = React.forwardRef( + (props, forwardedRef) => { + // Default props + // eslint-disable-next-line deprecation/deprecation + props = { shouldRestoreFocus: true, ...props }; - const root = React.useRef(); - const mergedRootRef = useMergedRefs(root, forwardedRef) as React.Ref; + const root = React.useRef(); + const mergedRootRef = useMergedRefs(root, forwardedRef) as React.Ref; - useRestoreFocus(props, root); + useRestoreFocus(props, root); - const { role, className, ariaLabel, ariaLabelledBy, ariaDescribedBy, style, children, onDismiss } = props; - const needsVerticalScrollBar = useScrollbarAsync(props, root); + const { role, className, ariaLabel, ariaLabelledBy, ariaDescribedBy, style, children, onDismiss } = props; + const needsVerticalScrollBar = useScrollbarAsync(props, root); - const onKeyDown = React.useCallback( - (ev: React.KeyboardEvent | KeyboardEvent): void => { - // eslint-disable-next-line deprecation/deprecation - switch (ev.which) { - case KeyCodes.escape: - if (onDismiss) { - onDismiss(ev); + const onKeyDown = React.useCallback( + (ev: React.KeyboardEvent | KeyboardEvent): void => { + // eslint-disable-next-line deprecation/deprecation + switch (ev.which) { + case KeyCodes.escape: + if (onDismiss) { + onDismiss(ev); - ev.preventDefault(); - ev.stopPropagation(); - } + ev.preventDefault(); + ev.stopPropagation(); + } - break; - } - }, - [onDismiss], - ); - - useOnEvent(getWindow(root.current), 'keydown', onKeyDown as (ev: Event) => void); - - return ( -
- {children} -
- ); -}); + break; + } + }, + [onDismiss], + ); + + useOnEvent(getWindow(root.current), 'keydown', onKeyDown as (ev: Event) => void); + + return ( +
+ {children} +
+ ); + }, +); diff --git a/packages/react-next/src/components/Popup/Popup.types.ts b/packages/react-next/src/components/Popup/Popup.types.ts index 214cc11c7cf267..6968571059635e 100644 --- a/packages/react-next/src/components/Popup/Popup.types.ts +++ b/packages/react-next/src/components/Popup/Popup.types.ts @@ -3,7 +3,7 @@ import * as React from 'react'; /** * {@docCategory Popup} */ -export interface IPopupProps extends React.HTMLAttributes { +export interface IPopupProps extends React.HTMLAttributes, React.RefAttributes { /** * Aria role for popup */ diff --git a/packages/react-next/src/components/Rating/Rating.base.tsx b/packages/react-next/src/components/Rating/Rating.base.tsx index 221987fe72038b..b96345552e7541 100644 --- a/packages/react-next/src/components/Rating/Rating.base.tsx +++ b/packages/react-next/src/components/Rating/Rating.base.tsx @@ -78,113 +78,115 @@ const getStarId = (id: string, starNum: number) => { return `${id}-star-${starNum - 1}`; }; -export const RatingBase = React.forwardRef((props, ref) => { - const id = useId('Rating'); - const labelId = useId('RatingLabel'); - const { - ariaLabelFormat, - disabled, - getAriaLabel, - styles, - // eslint-disable-next-line deprecation/deprecation - min: minFromProps = props.allowZeroStars ? 0 : 1, - max = 5, - readOnly, - size, - theme, - icon = 'FavoriteStarFill', - unselectedIcon = 'FavoriteStar', - } = props; - - // Ensure min is >= 0 to avoid issues elsewhere - const min = Math.max(minFromProps, 0); - - const [rating, setRating] = useControllableValue(props.rating, props.defaultRating, props.onChange); - /** Rating clamped within valid range. Will be `min` if `rating` is undefined. */ - const displayRating = getClampedRating(rating, min, max); - - useDebugWarnings(props); - - useComponentRef(props.componentRef, displayRating); - - const divProps = getNativeProps>(props, divProperties); - - const classNames = getClassNames(styles!, { - disabled, - readOnly, - theme: theme!, - }); - - const ariaLabel = getAriaLabel?.(displayRating, max); - - const stars: JSX.Element[] = []; - - for (let starNum = 1; starNum <= max; starNum++) { - const fillPercentage = getFillingPercentage(starNum, displayRating); - - const onSelectStar = (ev: React.SyntheticEvent): void => { - // Use the actual rating (not display value) here, to ensure that we update if the actual - // rating is undefined and the user clicks the first star. - if (rating === undefined || Math.ceil(rating) !== starNum) { - setRating(starNum, ev); - } - }; - - stars.push( - , - ); - } - - const rootSizeClass = size === RatingSize.Large ? classNames.rootIsLarge : classNames.rootIsSmall; +export const RatingBase: React.FunctionComponent = React.forwardRef( + (props, ref) => { + const id = useId('Rating'); + const labelId = useId('RatingLabel'); + const { + ariaLabelFormat, + disabled, + getAriaLabel, + styles, + // eslint-disable-next-line deprecation/deprecation + min: minFromProps = props.allowZeroStars ? 0 : 1, + max = 5, + readOnly, + size, + theme, + icon = 'FavoriteStarFill', + unselectedIcon = 'FavoriteStar', + } = props; + + // Ensure min is >= 0 to avoid issues elsewhere + const min = Math.max(minFromProps, 0); + + const [rating, setRating] = useControllableValue(props.rating, props.defaultRating, props.onChange); + /** Rating clamped within valid range. Will be `min` if `rating` is undefined. */ + const displayRating = getClampedRating(rating, min, max); + + useDebugWarnings(props); + + useComponentRef(props.componentRef, displayRating); + + const divProps = getNativeProps>(props, divProperties); + + const classNames = getClassNames(styles!, { + disabled, + readOnly, + theme: theme!, + }); - return ( -
- ): void => { + // Use the actual rating (not display value) here, to ensure that we update if the actual + // rating is undefined and the user clicks the first star. + if (rating === undefined || Math.ceil(rating) !== starNum) { + setRating(starNum, ev); + } + }; + + stars.push( + , + ); + } + + const rootSizeClass = size === RatingSize.Large ? classNames.rootIsLarge : classNames.rootIsSmall; + + return ( +
- {stars} - -
- ); -}); + + {stars} + +
+ ); + }, +); diff --git a/packages/react-next/src/components/Rating/Rating.types.ts b/packages/react-next/src/components/Rating/Rating.types.ts index 0ca38a8015692c..ee6e9601971156 100644 --- a/packages/react-next/src/components/Rating/Rating.types.ts +++ b/packages/react-next/src/components/Rating/Rating.types.ts @@ -14,7 +14,7 @@ export interface IRating { * Rating component props. * {@docCategory Rating} */ -export interface IRatingProps extends React.HTMLAttributes { +export interface IRatingProps extends React.HTMLAttributes, React.RefAttributes { /** * Optional callback to access the IRating interface. Use this instead of ref for accessing * the public methods and properties of the component. diff --git a/packages/react-next/src/components/ResizeGroup/ResizeGroup.base.tsx b/packages/react-next/src/components/ResizeGroup/ResizeGroup.base.tsx index 7adce0fd15a3bc..defdfd2af41a41 100644 --- a/packages/react-next/src/components/ResizeGroup/ResizeGroup.base.tsx +++ b/packages/react-next/src/components/ResizeGroup/ResizeGroup.base.tsx @@ -464,7 +464,10 @@ function useDebugWarnings(props: IResizeGroupProps) { } } -export const ResizeGroupBase = React.forwardRef((props: IResizeGroupProps, forwardedRef: React.Ref) => { +export const ResizeGroupBase: React.FunctionComponent = React.forwardRef< + HTMLDivElement, + IResizeGroupProps +>((props, forwardedRef) => { const rootRef = React.useRef(null); // The root div which is the container inside of which we are trying to fit content. const mergedRootRef = useMergedRefs(rootRef, forwardedRef); diff --git a/packages/react-next/src/components/ResizeGroup/ResizeGroup.types.ts b/packages/react-next/src/components/ResizeGroup/ResizeGroup.types.ts index d79830e787ab57..c427c4d2ac1b87 100644 --- a/packages/react-next/src/components/ResizeGroup/ResizeGroup.types.ts +++ b/packages/react-next/src/components/ResizeGroup/ResizeGroup.types.ts @@ -23,7 +23,7 @@ export interface IResizeGroup { /** * {@docCategory ResizeGroup} */ -export interface IResizeGroupProps extends React.HTMLAttributes { +export interface IResizeGroupProps extends React.HTMLAttributes, React.RefAttributes { /** * Optional callback to access the IResizeGroup interface. Use this instead of ref for accessing * the public methods and properties of the component. diff --git a/packages/react-next/src/components/SearchBox/SearchBox.base.tsx b/packages/react-next/src/components/SearchBox/SearchBox.base.tsx index 93c84216ef449f..67cc432224e874 100644 --- a/packages/react-next/src/components/SearchBox/SearchBox.base.tsx +++ b/packages/react-next/src/components/SearchBox/SearchBox.base.tsx @@ -27,7 +27,10 @@ const useComponentRef = ( ); }; -export const SearchBoxBase = React.forwardRef((props: ISearchBoxProps, forwardedRef: React.Ref) => { +export const SearchBoxBase: React.FunctionComponent = React.forwardRef< + HTMLDivElement, + ISearchBoxProps +>((props, forwardedRef) => { const [hasFocus, setHasFocus] = React.useState(false); const [value = '', setValue] = useControllableValue(props.value, props.defaultValue, props.onChange); const rootElementRef = React.useRef(null); diff --git a/packages/react-next/src/components/SearchBox/SearchBox.types.ts b/packages/react-next/src/components/SearchBox/SearchBox.types.ts index e7302e367c49c8..834b60e9a1034c 100644 --- a/packages/react-next/src/components/SearchBox/SearchBox.types.ts +++ b/packages/react-next/src/components/SearchBox/SearchBox.types.ts @@ -22,7 +22,9 @@ export interface ISearchBox { /** * {@docCategory SearchBox} */ -export interface ISearchBoxProps extends React.InputHTMLAttributes { +export interface ISearchBoxProps + extends React.InputHTMLAttributes, + React.RefAttributes { /** * Optional callback to access the ISearchBox interface. Use this instead of ref for accessing * the public methods and properties of the component. diff --git a/packages/react-next/src/components/Shimmer/Shimmer.base.tsx b/packages/react-next/src/components/Shimmer/Shimmer.base.tsx index 751547b4a4ad9e..4ab5204dc944a7 100644 --- a/packages/react-next/src/components/Shimmer/Shimmer.base.tsx +++ b/packages/react-next/src/components/Shimmer/Shimmer.base.tsx @@ -12,83 +12,85 @@ const getClassNames = classNamesFunction(); /** * {@docCategory Shimmer} */ -export const ShimmerBase = React.forwardRef((props, ref) => { - const { - styles, - shimmerElements, - children, - width, - className, - customElementsGroup, - theme, - ariaLabel, - shimmerColors, - isDataLoaded = false, - } = props; +export const ShimmerBase: React.FunctionComponent = React.forwardRef( + (props, ref) => { + const { + styles, + shimmerElements, + children, + width, + className, + customElementsGroup, + theme, + ariaLabel, + shimmerColors, + isDataLoaded = false, + } = props; - const divProps = getNativeProps>(props, divProperties); + const divProps = getNativeProps>(props, divProperties); - const classNames: { [key in keyof IShimmerStyles]: string } = getClassNames(styles!, { - theme: theme!, - isDataLoaded, - className, - transitionAnimationInterval: TRANSITION_ANIMATION_INTERVAL, - shimmerColor: shimmerColors && shimmerColors.shimmer, - shimmerWaveColor: shimmerColors && shimmerColors.shimmerWave, - }); + const classNames: { [key in keyof IShimmerStyles]: string } = getClassNames(styles!, { + theme: theme!, + isDataLoaded, + className, + transitionAnimationInterval: TRANSITION_ANIMATION_INTERVAL, + shimmerColor: shimmerColors && shimmerColors.shimmer, + shimmerWaveColor: shimmerColors && shimmerColors.shimmerWave, + }); - const internalState = useConst({ - lastTimeoutId: 0, - }); + const internalState = useConst({ + lastTimeoutId: 0, + }); - const { setTimeout, clearTimeout } = useSetTimeout(); + const { setTimeout, clearTimeout } = useSetTimeout(); - /** - * Flag for knowing when to remove the shimmerWrapper from the DOM. - */ - const [contentLoaded, setContentLoaded] = React.useState(isDataLoaded); + /** + * Flag for knowing when to remove the shimmerWrapper from the DOM. + */ + const [contentLoaded, setContentLoaded] = React.useState(isDataLoaded); - const divStyleProp = { width: width ? width : '100%' }; + const divStyleProp = { width: width ? width : '100%' }; - React.useEffect(() => { - if (isDataLoaded !== contentLoaded) { - if (isDataLoaded) { - internalState.lastTimeoutId = setTimeout(() => { - setContentLoaded(true); - }, TRANSITION_ANIMATION_INTERVAL); + React.useEffect(() => { + if (isDataLoaded !== contentLoaded) { + if (isDataLoaded) { + internalState.lastTimeoutId = setTimeout(() => { + setContentLoaded(true); + }, TRANSITION_ANIMATION_INTERVAL); - return () => clearTimeout(internalState.lastTimeoutId); - } else { - setContentLoaded(false); + return () => clearTimeout(internalState.lastTimeoutId); + } else { + setContentLoaded(false); + } } - } - // eslint-disable-next-line react-hooks/exhaustive-deps -- Should only run when isDataLoaded changes. - }, [isDataLoaded]); + // eslint-disable-next-line react-hooks/exhaustive-deps -- Should only run when isDataLoaded changes. + }, [isDataLoaded]); - return ( -
- {!contentLoaded && ( -
-
- {customElementsGroup ? ( - customElementsGroup - ) : ( - - )} -
- )} - {children &&
{children}
} - {ariaLabel && !isDataLoaded && ( -
- -
{ariaLabel}
-
-
- )} -
- ); -}); + return ( +
+ {!contentLoaded && ( +
+
+ {customElementsGroup ? ( + customElementsGroup + ) : ( + + )} +
+ )} + {children &&
{children}
} + {ariaLabel && !isDataLoaded && ( +
+ +
{ariaLabel}
+
+
+ )} +
+ ); + }, +); ShimmerBase.displayName = COMPONENT_NAME; diff --git a/packages/react-next/src/components/Shimmer/Shimmer.types.ts b/packages/react-next/src/components/Shimmer/Shimmer.types.ts index 33fb813e52a123..4e1c63fccbbe09 100644 --- a/packages/react-next/src/components/Shimmer/Shimmer.types.ts +++ b/packages/react-next/src/components/Shimmer/Shimmer.types.ts @@ -6,7 +6,7 @@ import { IStyleFunctionOrObject } from '../../Utilities'; * Shimmer component props. * {@docCategory Shimmer} */ -export interface IShimmerProps extends React.AllHTMLAttributes { +export interface IShimmerProps extends React.AllHTMLAttributes, React.RefAttributes { /** * Sets the width value of the shimmer wave wrapper. * @defaultvalue 100% diff --git a/packages/react-next/src/components/SwatchColorPicker/SwatchColorPicker.base.tsx b/packages/react-next/src/components/SwatchColorPicker/SwatchColorPicker.base.tsx index 2006e5bada08ee..6723c036948def 100644 --- a/packages/react-next/src/components/SwatchColorPicker/SwatchColorPicker.base.tsx +++ b/packages/react-next/src/components/SwatchColorPicker/SwatchColorPicker.base.tsx @@ -34,7 +34,10 @@ function useDebugWarnings(props: ISwatchColorPickerProps) { } } -export const SwatchColorPickerBase = React.forwardRef((props, ref) => { +export const SwatchColorPickerBase: React.FunctionComponent = React.forwardRef< + HTMLElement, + ISwatchColorPickerProps +>((props, ref) => { const defaultId = useId('swatchColorPicker'); const id = props.id || defaultId; diff --git a/packages/react-next/src/components/SwatchColorPicker/SwatchColorPicker.types.ts b/packages/react-next/src/components/SwatchColorPicker/SwatchColorPicker.types.ts index 4feb96e2316965..1858c37c755148 100644 --- a/packages/react-next/src/components/SwatchColorPicker/SwatchColorPicker.types.ts +++ b/packages/react-next/src/components/SwatchColorPicker/SwatchColorPicker.types.ts @@ -10,7 +10,7 @@ import { /** * {@docCategory SwatchColorPicker} */ -export interface ISwatchColorPickerProps { +export interface ISwatchColorPickerProps extends React.RefAttributes { /** * Number of columns for the swatch color picker */ diff --git a/packages/react-next/src/components/TeachingBubble/TeachingBubble.base.tsx b/packages/react-next/src/components/TeachingBubble/TeachingBubble.base.tsx index 589caf357bf124..07250a1e984b8b 100644 --- a/packages/react-next/src/components/TeachingBubble/TeachingBubble.base.tsx +++ b/packages/react-next/src/components/TeachingBubble/TeachingBubble.base.tsx @@ -35,53 +35,54 @@ const useComponentRef = ( ); }; -export const TeachingBubbleBase = React.forwardRef( - (props: ITeachingBubbleProps, forwardedRef: React.Ref) => { - const rootElementRef = React.useRef(null); - const mergedRootRef = useMergedRefs(rootElementRef, forwardedRef); - const { - calloutProps: setCalloutProps, - // eslint-disable-next-line deprecation/deprecation - targetElement, - onDismiss, - // eslint-disable-next-line deprecation/deprecation - hasCloseButton = props.hasCloseIcon, - isWide, - styles, - theme, - target, - } = props; +export const TeachingBubbleBase: React.FunctionComponent = React.forwardRef< + HTMLDivElement, + ITeachingBubbleProps +>((props, forwardedRef) => { + const rootElementRef = React.useRef(null); + const mergedRootRef = useMergedRefs(rootElementRef, forwardedRef); + const { + calloutProps: setCalloutProps, + // eslint-disable-next-line deprecation/deprecation + targetElement, + onDismiss, + // eslint-disable-next-line deprecation/deprecation + hasCloseButton = props.hasCloseIcon, + isWide, + styles, + theme, + target, + } = props; - const calloutProps = { ...defaultCalloutProps, ...setCalloutProps }; + const calloutProps = { ...defaultCalloutProps, ...setCalloutProps }; - const stylesProps: ITeachingBubbleStyleProps = { - theme: theme!, - isWide, - calloutProps: { ...calloutProps, theme: calloutProps.theme! }, - hasCloseButton, - }; + const stylesProps: ITeachingBubbleStyleProps = { + theme: theme!, + isWide, + calloutProps: { ...calloutProps, theme: calloutProps.theme! }, + hasCloseButton, + }; - const classNames = getClassNames(styles, stylesProps); - const calloutStyles = classNames.subComponentStyles - ? (classNames.subComponentStyles as ITeachingBubbleSubComponentStyles).callout - : undefined; + const classNames = getClassNames(styles, stylesProps); + const calloutStyles = classNames.subComponentStyles + ? (classNames.subComponentStyles as ITeachingBubbleSubComponentStyles).callout + : undefined; - useComponentRef(props.componentRef, rootElementRef); + useComponentRef(props.componentRef, rootElementRef); - return ( - -
- -
-
- ); - }, -); + return ( + +
+ +
+
+ ); +}); TeachingBubbleBase.displayName = COMPONENT_NAME; diff --git a/packages/react-next/src/components/TeachingBubble/TeachingBubbleContent.base.tsx b/packages/react-next/src/components/TeachingBubble/TeachingBubbleContent.base.tsx index cc0e7f3c06e8c9..4e973ede80f08c 100644 --- a/packages/react-next/src/components/TeachingBubble/TeachingBubbleContent.base.tsx +++ b/packages/react-next/src/components/TeachingBubble/TeachingBubbleContent.base.tsx @@ -28,146 +28,147 @@ const useComponentRef = ( ); }; -export const TeachingBubbleContentBase: React.FunctionComponent = React.forwardRef( - (props, forwardedRef: React.Ref) => { - const rootElementRef = React.useRef(null); - const documentRef = useDocument(); - const mergedRootRef = useMergedRefs(rootElementRef, forwardedRef); - - const { - illustrationImage, - primaryButtonProps, - secondaryButtonProps, - headline, - hasCondensedHeadline, - // eslint-disable-next-line deprecation/deprecation - hasCloseButton = props.hasCloseIcon, - onDismiss, - closeButtonAriaLabel, - hasSmallHeadline, - isWide, - styles, - theme, - ariaDescribedBy, - ariaLabelledBy, - footerContent: customFooterContent, - focusTrapZoneProps, - } = props; - - const classNames = getClassNames(styles, { - theme: theme!, - hasCondensedHeadline, - hasSmallHeadline, - hasCloseButton, - hasHeadline: !!headline, - isWide, - primaryButtonClassName: primaryButtonProps ? primaryButtonProps.className : undefined, - secondaryButtonClassName: secondaryButtonProps ? secondaryButtonProps.className : undefined, - }); - - const onKeyDown = React.useCallback( - (ev: React.KeyboardEvent | KeyboardEvent): void => { - if (onDismiss) { - // eslint-disable-next-line deprecation/deprecation - if (ev.which === KeyCodes.escape) { - onDismiss(ev); - } +export const TeachingBubbleContentBase: React.FunctionComponent = React.forwardRef< + HTMLDivElement, + ITeachingBubbleProps +>((props, forwardedRef) => { + const rootElementRef = React.useRef(null); + const documentRef = useDocument(); + const mergedRootRef = useMergedRefs(rootElementRef, forwardedRef); + + const { + illustrationImage, + primaryButtonProps, + secondaryButtonProps, + headline, + hasCondensedHeadline, + // eslint-disable-next-line deprecation/deprecation + hasCloseButton = props.hasCloseIcon, + onDismiss, + closeButtonAriaLabel, + hasSmallHeadline, + isWide, + styles, + theme, + ariaDescribedBy, + ariaLabelledBy, + footerContent: customFooterContent, + focusTrapZoneProps, + } = props; + + const classNames = getClassNames(styles, { + theme: theme!, + hasCondensedHeadline, + hasSmallHeadline, + hasCloseButton, + hasHeadline: !!headline, + isWide, + primaryButtonClassName: primaryButtonProps ? primaryButtonProps.className : undefined, + secondaryButtonClassName: secondaryButtonProps ? secondaryButtonProps.className : undefined, + }); + + const onKeyDown = React.useCallback( + (ev: React.KeyboardEvent | KeyboardEvent): void => { + if (onDismiss) { + // eslint-disable-next-line deprecation/deprecation + if (ev.which === KeyCodes.escape) { + onDismiss(ev); } - }, - [onDismiss], - ); + } + }, + [onDismiss], + ); - useOnEvent(documentRef, 'keydown', onKeyDown as (ev: Event) => void); - - let imageContent: JSX.Element | undefined; - let headerContent: JSX.Element | undefined; - let bodyContent: JSX.Element | undefined; - let footerContent: JSX.Element | undefined; - let closeButton: JSX.Element | undefined; - - if (illustrationImage && illustrationImage.src) { - imageContent = ( -
- -
- ); - } + useOnEvent(documentRef, 'keydown', onKeyDown as (ev: Event) => void); + + let imageContent: JSX.Element | undefined; + let headerContent: JSX.Element | undefined; + let bodyContent: JSX.Element | undefined; + let footerContent: JSX.Element | undefined; + let closeButton: JSX.Element | undefined; + + if (illustrationImage && illustrationImage.src) { + imageContent = ( +
+ +
+ ); + } - if (headline) { - const HeaderWrapperAs = typeof headline === 'string' ? 'p' : 'div'; + if (headline) { + const HeaderWrapperAs = typeof headline === 'string' ? 'p' : 'div'; - headerContent = ( -
- - {headline} - -
- ); - } + headerContent = ( +
+ + {headline} + +
+ ); + } - if (props.children) { - const BodyContentWrapperAs = typeof props.children === 'string' ? 'p' : 'div'; + if (props.children) { + const BodyContentWrapperAs = typeof props.children === 'string' ? 'p' : 'div'; - bodyContent = ( -
- - {props.children} - -
- ); - } - - if (primaryButtonProps || secondaryButtonProps || customFooterContent) { - footerContent = ( - - {{customFooterContent}} - - {secondaryButtonProps && } - {primaryButtonProps && } - - - ); - } - - if (hasCloseButton) { - closeButton = ( - - ); - } - - useComponentRef(props.componentRef, rootElementRef); - - return ( -
- {imageContent} - -
- {headerContent} - {bodyContent} - {footerContent} - {closeButton} -
-
+ bodyContent = ( +
+ + {props.children} +
); - }, -); + } + + if (primaryButtonProps || secondaryButtonProps || customFooterContent) { + footerContent = ( + + {{customFooterContent}} + + {secondaryButtonProps && } + {primaryButtonProps && } + + + ); + } + + if (hasCloseButton) { + closeButton = ( + + ); + } + + useComponentRef(props.componentRef, rootElementRef); + + return ( +
+ {imageContent} + +
+ {headerContent} + {bodyContent} + {footerContent} + {closeButton} +
+
+
+ ); +}); diff --git a/packages/react-next/src/utilities/ButtonGrid/ButtonGrid.base.tsx b/packages/react-next/src/utilities/ButtonGrid/ButtonGrid.base.tsx index d24da99aac6238..a207e03f088315 100644 --- a/packages/react-next/src/utilities/ButtonGrid/ButtonGrid.base.tsx +++ b/packages/react-next/src/utilities/ButtonGrid/ButtonGrid.base.tsx @@ -6,7 +6,10 @@ import { useId } from '@uifabric/react-hooks'; const getClassNames = classNamesFunction(); -export const ButtonGridBase = React.forwardRef((props, ref) => { +export const ButtonGridBase: React.FunctionComponent = React.forwardRef< + HTMLElement, + IButtonGridProps +>((props, forwardedRef) => { const id = useId(undefined, props.id); const { @@ -65,7 +68,7 @@ export const ButtonGridBase = React.forwardRef((p content ) : ( ( - ButtonGridBase, - getStyles, -); +export const ButtonGrid: React.FunctionComponent = styled< + IButtonGridProps, + IButtonGridStyleProps, + IButtonGridStyles, + HTMLElement +>(ButtonGridBase, getStyles); diff --git a/packages/react-next/src/utilities/ButtonGrid/ButtonGrid.types.ts b/packages/react-next/src/utilities/ButtonGrid/ButtonGrid.types.ts index 0d5ef23af502ae..e11d8cfb2718a3 100644 --- a/packages/react-next/src/utilities/ButtonGrid/ButtonGrid.types.ts +++ b/packages/react-next/src/utilities/ButtonGrid/ButtonGrid.types.ts @@ -4,7 +4,9 @@ import { IRefObject, IStyleFunctionOrObject } from '../../Utilities'; export interface IButtonGrid {} -export interface IButtonGridProps extends React.TableHTMLAttributes { +export interface IButtonGridProps + extends React.TableHTMLAttributes, + React.RefAttributes { /** * Gets the component ref. */ diff --git a/packages/react-slider/etc/react-slider.api.md b/packages/react-slider/etc/react-slider.api.md index 3003b98d5e1160..441892d31d1489 100644 --- a/packages/react-slider/etc/react-slider.api.md +++ b/packages/react-slider/etc/react-slider.api.md @@ -19,7 +19,7 @@ export interface ISlider { } // @public (undocumented) -export interface ISliderProps extends Omit, 'defaultValue' | 'onChange'> { +export interface ISliderProps extends Omit, 'defaultValue' | 'onChange'>, React.RefAttributes { 'aria-label'?: string; // @deprecated ariaLabel?: string; @@ -74,7 +74,7 @@ export const ONKEYDOWN_TIMEOUT_DURATION = 1000; export const Slider: React.FunctionComponent; // @public (undocumented) -export const SliderBase: React.ForwardRefExoticComponent>; +export const SliderBase: React.FunctionComponent; // (No @packageDocumentation comment for this package) diff --git a/packages/react-slider/src/components/Slider/Slider.base.tsx b/packages/react-slider/src/components/Slider/Slider.base.tsx index 6ff42d495ef755..c51001a75d95c0 100644 --- a/packages/react-slider/src/components/Slider/Slider.base.tsx +++ b/packages/react-slider/src/components/Slider/Slider.base.tsx @@ -8,37 +8,37 @@ import { useWarnings } from '@uifabric/react-hooks'; const COMPONENT_NAME = 'SliderBase'; export const ONKEYDOWN_TIMEOUT_DURATION = 1000; -export const SliderBase = React.forwardRef((props: ISliderProps, ref: React.Ref) => { - // Ensure that value is always a number and is clamped by min/max. +export const SliderBase: React.FunctionComponent = React.forwardRef( + (props, ref) => { + const slotProps = useSlider(props, ref); - const slotProps = useSlider(props, ref); + if (process.env.NODE_ENV !== 'production') { + // eslint-disable-next-line react-hooks/rules-of-hooks -- build-time conditional + useWarnings({ + name: COMPONENT_NAME, + props, + mutuallyExclusive: { value: 'defaultValue' }, + }); + } - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line react-hooks/rules-of-hooks -- build-time conditional - useWarnings({ - name: COMPONENT_NAME, - props, - mutuallyExclusive: { value: 'defaultValue' }, - }); - } - - return ( -
- {slotProps &&