Skip to content

Commit

Permalink
Add missing ref prop typing (#15027)
Browse files Browse the repository at this point in the history
* fix ref typing

* fixes

* Change files

* change changeType to minor
  • Loading branch information
xugao authored Sep 15, 2020
1 parent 7846244 commit a17c428
Show file tree
Hide file tree
Showing 68 changed files with 1,458 additions and 1,332 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "minor",
"comment": "Add missing ref prop typing.",
"packageName": "@fluentui/react-checkbox",
"email": "[email protected]",
"dependentChangeType": "patch",
"date": "2020-09-15T00:36:31.365Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "minor",
"comment": "Add missing ref prop typing.",
"packageName": "@fluentui/react-link",
"email": "[email protected]",
"dependentChangeType": "patch",
"date": "2020-09-15T00:36:39.376Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "prerelease",
"comment": "Add missing ref prop typing.",
"packageName": "@fluentui/react-next",
"email": "[email protected]",
"dependentChangeType": "patch",
"date": "2020-09-15T00:36:46.296Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "minor",
"comment": "Add missing ref prop typing.",
"packageName": "@fluentui/react-slider",
"email": "[email protected]",
"dependentChangeType": "patch",
"date": "2020-09-15T00:36:53.058Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "minor",
"comment": "Add missing ref prop typing.",
"packageName": "@fluentui/react-tabs",
"email": "[email protected]",
"dependentChangeType": "patch",
"date": "2020-09-15T00:37:01.696Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "minor",
"comment": "Add missing ref prop typing.",
"packageName": "@fluentui/react-toggle",
"email": "[email protected]",
"dependentChangeType": "patch",
"date": "2020-09-15T00:37:05.302Z"
}
6 changes: 3 additions & 3 deletions packages/react-checkbox/etc/react-checkbox.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import { ITheme } from '@uifabric/styling';
import * as React from 'react';

// @public (undocumented)
export const Checkbox: React.FunctionComponent<ICheckboxProps & React.RefAttributes<HTMLDivElement>>;
export const Checkbox: React.FunctionComponent<ICheckboxProps>;

// @public (undocumented)
export const CheckboxBase: React.ForwardRefExoticComponent<ICheckboxProps & React.RefAttributes<HTMLDivElement>>;
export const CheckboxBase: React.FunctionComponent<ICheckboxProps>;

// @public
export interface ICheckbox {
Expand All @@ -27,7 +27,7 @@ export interface ICheckbox {
}

// @public
export interface ICheckboxProps extends React.ButtonHTMLAttributes<HTMLElement | HTMLInputElement> {
export interface ICheckboxProps extends React.ButtonHTMLAttributes<HTMLElement | HTMLInputElement>, React.RefAttributes<HTMLDivElement> {
ariaDescribedBy?: string;
ariaLabel?: string;
ariaLabelledBy?: string;
Expand Down
244 changes: 123 additions & 121 deletions packages/react-checkbox/src/components/Checkbox/Checkbox.base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,130 +6,132 @@ import { Icon } from 'office-ui-fabric-react/lib/Icon';

const getClassNames = classNamesFunction<ICheckboxStyleProps, ICheckboxStyles>();

export const CheckboxBase = React.forwardRef((props: ICheckboxProps, forwardedRef: React.Ref<HTMLDivElement>) => {
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<HTMLDivElement | null>(null);
const mergedRootRefs: React.Ref<HTMLDivElement> = useMergedRefs(rootRef, forwardedRef);
const inputRef = React.useRef<HTMLInputElement>(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<HTMLElement>): 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 ? (
<span aria-hidden="true" className={classNames.text} title={checkboxProps.title}>
{checkboxProps.label}
</span>
) : null;
},
[classNames.text],
);

const onRenderLabel = props.onRenderLabel || defaultLabelRenderer;

const ariaChecked: React.InputHTMLAttributes<HTMLInputElement>['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<HTMLInputElement>['type'],
...inputProps,
ref: inputRef,
checked: !!isChecked,
export const CheckboxBase: React.FunctionComponent<ICheckboxProps> = React.forwardRef<HTMLDivElement, ICheckboxProps>(
(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 (
<div {...slotProps.root}>
<input {...slotProps.input} />
<label {...slotProps.container}>
<div {...slotProps.checkbox}>
<Icon iconName="CheckMark" {...checkmarkIconProps} className={classNames.checkmark} />
</div>
{onRenderLabel(props, defaultLabelRenderer)}
</label>
</div>
);
});
label,
checkmarkIconProps,
styles,
theme,
className,
boxSide = 'start',
} = props;

const id = useId('checkbox-', props.id);

const rootRef = React.useRef<HTMLDivElement | null>(null);
const mergedRootRefs: React.Ref<HTMLDivElement> = useMergedRefs(rootRef, forwardedRef);
const inputRef = React.useRef<HTMLInputElement>(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<HTMLElement>): 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 ? (
<span aria-hidden="true" className={classNames.text} title={checkboxProps.title}>
{checkboxProps.label}
</span>
) : null;
},
[classNames.text],
);

const onRenderLabel = props.onRenderLabel || defaultLabelRenderer;

const ariaChecked: React.InputHTMLAttributes<HTMLInputElement>['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<HTMLInputElement>['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 (
<div {...slotProps.root}>
<input {...slotProps.input} />
<label {...slotProps.container}>
<div {...slotProps.checkbox}>
<Icon iconName="CheckMark" {...checkmarkIconProps} className={classNames.checkmark} />
</div>
{onRenderLabel(props, defaultLabelRenderer)}
</label>
</div>
);
},
);

CheckboxBase.displayName = 'CheckboxBase';

Expand Down
4 changes: 2 additions & 2 deletions packages/react-checkbox/src/components/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLDivElement>,
export const Checkbox: React.FunctionComponent<ICheckboxProps> = styled<
ICheckboxProps,
ICheckboxStyleProps,
ICheckboxStyles
>(CheckboxBase, getStyles, undefined, { scope: 'Checkbox' });
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export interface ICheckbox {
* Checkbox properties.
* {@docCategory Checkbox}
*/
export interface ICheckboxProps extends React.ButtonHTMLAttributes<HTMLElement | HTMLInputElement> {
export interface ICheckboxProps
extends React.ButtonHTMLAttributes<HTMLElement | HTMLInputElement>,
React.RefAttributes<HTMLDivElement> {
/**
* Optional callback to access the ICheckbox interface. Use this instead of ref for accessing
* the public methods and properties of the component.
Expand Down
6 changes: 5 additions & 1 deletion packages/react-checkbox/src/next/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -12,4 +13,7 @@ const composeOptions: ComposeOptions = {
},
};

export const Checkbox = compose<'div', ICheckboxProps, {}, ICheckboxProps, {}>(CheckboxBase, composeOptions);
export const Checkbox: React.FunctionComponent<ICheckboxProps> = compose<'div', ICheckboxProps, {}, ICheckboxProps, {}>(
CheckboxBase,
composeOptions,
);
4 changes: 3 additions & 1 deletion packages/react-checkbox/src/next/Checkbox.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ export interface ICheckbox {
* Checkbox properties.
* {@docCategory Checkbox}
*/
export interface ICheckboxProps extends React.ButtonHTMLAttributes<HTMLElement | HTMLInputElement> {
export interface ICheckboxProps
extends React.ButtonHTMLAttributes<HTMLElement | HTMLInputElement>,
React.RefAttributes<HTMLElement> {
/**
* Render the root element as another type.
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/react-link/etc/react-link.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export interface ILinkOptions {
}

// @public (undocumented)
export interface ILinkProps extends ILinkHTMLAttributes<HTMLAnchorElement | HTMLButtonElement | HTMLElement> {
export interface ILinkProps extends ILinkHTMLAttributes<HTMLAnchorElement | HTMLButtonElement | HTMLElement>, React.RefAttributes<HTMLElement> {
componentRef?: IRefObject<ILink>;
disabled?: boolean;
// @deprecated
Expand Down Expand Up @@ -98,7 +98,7 @@ export interface ILinkStyles {
export const Link: React.FunctionComponent<ILinkProps>;

// @public (undocumented)
export const LinkBase: React.ForwardRefExoticComponent<Pick<ILinkProps, string | number> & React.RefAttributes<HTMLElement>>;
export const LinkBase: React.FunctionComponent<ILinkProps>;

// @public (undocumented)
export type LinkSlotProps = {
Expand Down
Loading

0 comments on commit a17c428

Please sign in to comment.