From 028f284f366b4bb57c2b254326464ce2dd403839 Mon Sep 17 00:00:00 2001 From: Esteban Munoz Date: Mon, 16 Aug 2021 20:26:49 -0600 Subject: [PATCH] refactor(react-checkbox): Removing mergeProps and refactoring Checkbox. (#19225) * Refactoring Checkbox to use new Slots API * Refactoring Checkbox, removing mergeProps, and indicator styles * removing temp style * Change files * updating default icons and their styling * updating checked, mixed, and unchecked styles to be applied from root" * Update packages/react-checkbox/src/components/Checkbox/useCheckboxStyles.ts * updating snapshots * updating snapshots and added checking if indicator has been set before applying default icons * renaming checkboxClassName to containerClassName and fixing unchecked styles * updating checkbox api file --- ...-4da078c0-b510-4088-aec3-f33c521c1303.json | 7 + .../react-checkbox/etc/react-checkbox.api.md | 48 +-- .../react-checkbox/src/Checkbox.stories.tsx | 22 +- .../src/components/Checkbox/Checkbox.test.tsx | 16 +- .../src/components/Checkbox/Checkbox.types.ts | 49 ++- .../src/components/Checkbox/DefaultIcons.tsx | 24 +- .../__snapshots__/Checkbox.test.tsx.snap | 146 ++++----- .../components/Checkbox/renderCheckbox.tsx | 15 +- .../src/components/Checkbox/useCheckbox.tsx | 87 +++-- .../components/Checkbox/useCheckboxStyles.ts | 299 +++++++++--------- 10 files changed, 346 insertions(+), 367 deletions(-) create mode 100644 change/@fluentui-react-checkbox-4da078c0-b510-4088-aec3-f33c521c1303.json diff --git a/change/@fluentui-react-checkbox-4da078c0-b510-4088-aec3-f33c521c1303.json b/change/@fluentui-react-checkbox-4da078c0-b510-4088-aec3-f33c521c1303.json new file mode 100644 index 00000000000000..75eb773dd61e53 --- /dev/null +++ b/change/@fluentui-react-checkbox-4da078c0-b510-4088-aec3-f33c521c1303.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Refactoring Checkbox, removing mergeProps, and fixing indicator styles.", + "packageName": "@fluentui/react-checkbox", + "email": "esteban.230@hotmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-checkbox/etc/react-checkbox.api.md b/packages/react-checkbox/etc/react-checkbox.api.md index 790606ae5ad073..5d7024542d5f4f 100644 --- a/packages/react-checkbox/etc/react-checkbox.api.md +++ b/packages/react-checkbox/etc/react-checkbox.api.md @@ -4,58 +4,58 @@ ```ts -import { ComponentPropsCompat } from '@fluentui/react-utilities'; -import { ComponentStateCompat } from '@fluentui/react-utilities'; +import { ComponentProps } from '@fluentui/react-utilities'; +import { ComponentState } from '@fluentui/react-utilities'; import { LabelProps } from '@fluentui/react-label'; import * as React_2 from 'react'; -import { ShorthandProps } from '@fluentui/react-utilities'; // @public export const Checkbox: React_2.ForwardRefExoticComponent>; // @public -export type CheckboxDefaultedProps = 'label' | 'indicator' | 'input' | 'size' | 'labelPosition'; - -// @public -export interface CheckboxOnChangeData { - // (undocumented) - checked: 'mixed' | boolean; -} - -// @public -export interface CheckboxProps extends Omit, Omit, 'defaultChecked' | 'onChange'> { +export interface CheckboxCommons extends Omit { checked?: 'mixed' | boolean; circular?: boolean; defaultChecked?: 'mixed' | boolean; disabled?: boolean; id?: string; - indicator?: ShorthandProps; - input?: ShorthandProps & React_2.RefAttributes>; - label?: ShorthandProps; - labelPosition?: 'before' | 'after'; + labelPosition: 'before' | 'after'; onChange?: (ev: React_2.FormEvent, data: CheckboxOnChangeData) => void; required?: boolean; rootId?: string; - size?: 'medium' | 'large'; + size: 'medium' | 'large'; } // @public -export type CheckboxShorthandProps = 'label' | 'indicator' | 'input'; +export interface CheckboxOnChangeData { + // (undocumented) + checked: 'mixed' | boolean; +} + +// @public +export interface CheckboxProps extends ComponentProps>, Partial { +} // @public -export const checkboxShorthandProps: CheckboxShorthandProps[]; +export const checkboxShorthandProps: (keyof CheckboxSlots)[]; + +// @public (undocumented) +export type CheckboxSlots = { + input: React_2.InputHTMLAttributes & React_2.RefAttributes; + indicator: React_2.HtmlHTMLAttributes; +}; // @public -export interface CheckboxState extends ComponentStateCompat { - checkboxClassName?: string; +export interface CheckboxState extends ComponentState, CheckboxCommons { + containerClassName?: string; ref: React_2.Ref; } -// @public +// @public (undocumented) export const renderCheckbox: (state: CheckboxState) => JSX.Element; // @public -export const useCheckbox: (props: CheckboxProps, ref: React_2.Ref, defaultProps?: CheckboxProps | undefined) => CheckboxState; +export const useCheckbox: (props: CheckboxProps, ref: React_2.Ref) => CheckboxState; // @public export const useCheckboxStyles: (state: CheckboxState) => CheckboxState; diff --git a/packages/react-checkbox/src/Checkbox.stories.tsx b/packages/react-checkbox/src/Checkbox.stories.tsx index 7a994400b7f1f7..37d2ee5c7d036f 100644 --- a/packages/react-checkbox/src/Checkbox.stories.tsx +++ b/packages/react-checkbox/src/Checkbox.stories.tsx @@ -3,20 +3,24 @@ import { Checkbox } from './index'; export const CheckboxVariations = () => (
- - - - - + Simple Checkbox + Circular Checkbox + Checkbox with label positioned before + Required Checkbox + Large Checkbox + + + +
); export const CheckboxStates = () => (
- - - - + Unchecked Checkbox + Checked Checkbox + Mixed Checkbox + Disabled Checkbox
); diff --git a/packages/react-checkbox/src/components/Checkbox/Checkbox.test.tsx b/packages/react-checkbox/src/components/Checkbox/Checkbox.test.tsx index 27642c8c0bf868..4d8e0f2fbcc05c 100644 --- a/packages/react-checkbox/src/components/Checkbox/Checkbox.test.tsx +++ b/packages/react-checkbox/src/components/Checkbox/Checkbox.test.tsx @@ -34,32 +34,36 @@ describe('Checkbox', () => { }); it('renders a default state', () => { - renderedComponent = render(); + renderedComponent = render(Default Checkbox); expect(renderedComponent.container).toMatchSnapshot(); }); it('renders unchecked correctly', () => { - renderedComponent = render(); + renderedComponent = render(Default Checkbox); expect(renderedComponent).toMatchSnapshot(); }); it('renders checked correctly', () => { - renderedComponent = render(); + renderedComponent = render(Default Checkbox); expect(renderedComponent).toMatchSnapshot(); }); it('renders mixed correctly', () => { - renderedComponent = render(); + renderedComponent = render(Default Checkbox); expect(renderedComponent).toMatchSnapshot(); }); it('respects id prop', () => { - component = mount(); + component = mount( + + Default Checkbox + , + ); expect(component.find('input').prop('id')).toEqual('checkbox'); }); it('defaults to unchecked non-mixed', () => { - component = mount(); + component = mount(Default Checkbox); const input = component.find('input'); expect(input.prop('checked')).toBe(false); diff --git a/packages/react-checkbox/src/components/Checkbox/Checkbox.types.ts b/packages/react-checkbox/src/components/Checkbox/Checkbox.types.ts index be1007857a1f25..59f79c50594cb4 100644 --- a/packages/react-checkbox/src/components/Checkbox/Checkbox.types.ts +++ b/packages/react-checkbox/src/components/Checkbox/Checkbox.types.ts @@ -1,28 +1,25 @@ import * as React from 'react'; -import { ComponentPropsCompat, ComponentStateCompat, ShorthandProps } from '@fluentui/react-utilities'; +import { ComponentProps, ComponentState } from '@fluentui/react-utilities'; import { LabelProps } from '@fluentui/react-label'; -/** - * Checkbox Props - */ -export interface CheckboxProps - extends Omit, - Omit, 'defaultChecked' | 'onChange'> { +export type CheckboxSlots = { /** - * Label that will be rendered next to the checkbox. - */ - label?: ShorthandProps; - - /** - * Indicator to be rendered as the checkbox icon. + * Hidden input that handles the checkbox's functionality. */ - indicator?: ShorthandProps; + input: React.InputHTMLAttributes & React.RefAttributes; /** - * Hidden input that handles the checkbox's functionality. + * Renders the checkbox, with the checkmark icon as its child when checked. */ - input?: ShorthandProps & React.RefAttributes>; + indicator: React.HtmlHTMLAttributes; +}; +/** + * TODO: + * - Remove as from Omit. Currently it's needed since checkbox Commons shouldn't have as. + * - Instead of extending LabelProps, extend LabelCommons once it's added. + */ +export interface CheckboxCommons extends Omit { /** * Disabled state of the checkbox. */ @@ -53,13 +50,13 @@ export interface CheckboxProps * Checkbox supports two different checkbox sizes. * @defaultvalue 'medium' */ - size?: 'medium' | 'large'; + size: 'medium' | 'large'; /** * Determines whether the label should be positioned before or after the checkbox. * @defaultvalue 'after' */ - labelPosition?: 'before' | 'after'; + labelPosition: 'before' | 'after'; /** * ID of the root element that wraps the checkbox and label. @@ -85,27 +82,21 @@ export interface CheckboxOnChangeData { } /** - * Names of the shorthand properties in CheckboxProps - */ -export type CheckboxShorthandProps = 'label' | 'indicator' | 'input'; - -/** - * Names of CheckboxProps that have a default value in useCheckbox + * Checkbox Props */ -export type CheckboxDefaultedProps = 'label' | 'indicator' | 'input' | 'size' | 'labelPosition'; +export interface CheckboxProps extends ComponentProps>, Partial {} /** * State used in rendering Checkbox */ -export interface CheckboxState - extends ComponentStateCompat { +export interface CheckboxState extends ComponentState, CheckboxCommons { /** * Ref to the root element. */ ref: React.Ref; /** - * CSS class for the checkbox element. + * CSS class for the container of the input element and indicator slot. */ - checkboxClassName?: string; + containerClassName?: string; } diff --git a/packages/react-checkbox/src/components/Checkbox/DefaultIcons.tsx b/packages/react-checkbox/src/components/Checkbox/DefaultIcons.tsx index e0d3cd8811a0de..f3801f31e1959a 100644 --- a/packages/react-checkbox/src/components/Checkbox/DefaultIcons.tsx +++ b/packages/react-checkbox/src/components/Checkbox/DefaultIcons.tsx @@ -1,13 +1,25 @@ import * as React from 'react'; -export const DefaultMixedIcon = () => ( - - +export const Mixed12Regular = () => ( + + ); -export const DefaultCheckmarkIcon = () => ( - - +export const Mixed16Regular = () => ( + + + +); + +export const Checkmark12Regular = () => ( + + + +); + +export const Checkmark16Regular = () => ( + + ); diff --git a/packages/react-checkbox/src/components/Checkbox/__snapshots__/Checkbox.test.tsx.snap b/packages/react-checkbox/src/components/Checkbox/__snapshots__/Checkbox.test.tsx.snap index 15fc15371471cc..1a6544d2b7a1c3 100644 --- a/packages/react-checkbox/src/components/Checkbox/__snapshots__/Checkbox.test.tsx.snap +++ b/packages/react-checkbox/src/components/Checkbox/__snapshots__/Checkbox.test.tsx.snap @@ -2,22 +2,23 @@ exports[`Checkbox renders a default state 1`] = `
-
@@ -27,13 +28,8 @@ exports[`Checkbox renders a default state 1`] = ` type="checkbox" />
- -
+ Default Checkbox +
`; @@ -42,22 +38,23 @@ Object { "asFragment": [Function], "baseElement":
-
@@ -68,32 +65,28 @@ Object { type="checkbox" />
- -
+ Default Checkbox +
, "container":
-
@@ -104,13 +97,8 @@ Object { type="checkbox" />
- -
+ Default Checkbox +
, "debug": [Function], "findAllByAltText": [Function], @@ -171,23 +159,27 @@ Object { "asFragment": [Function], "baseElement":
-
@@ -197,33 +189,32 @@ Object { type="checkbox" />
- -
+ Default Checkbox +
, "container":
-
@@ -233,13 +224,8 @@ Object { type="checkbox" />
- -
+ Default Checkbox +
, "debug": [Function], "findAllByAltText": [Function], @@ -300,22 +286,23 @@ Object { "asFragment": [Function], "baseElement":
-
@@ -325,32 +312,28 @@ Object { type="checkbox" />
- -
+ Default Checkbox +
, "container":
-
@@ -360,13 +343,8 @@ Object { type="checkbox" />
- -
+ Default Checkbox +
, "debug": [Function], "findAllByAltText": [Function], diff --git a/packages/react-checkbox/src/components/Checkbox/renderCheckbox.tsx b/packages/react-checkbox/src/components/Checkbox/renderCheckbox.tsx index f9c81dcf1f1748..eca30e9c44ba97 100644 --- a/packages/react-checkbox/src/components/Checkbox/renderCheckbox.tsx +++ b/packages/react-checkbox/src/components/Checkbox/renderCheckbox.tsx @@ -1,22 +1,19 @@ import * as React from 'react'; -import { getSlotsCompat } from '@fluentui/react-utilities'; -import { CheckboxState } from './Checkbox.types'; +import { getSlots } from '@fluentui/react-utilities'; +import { CheckboxState, CheckboxSlots } from './Checkbox.types'; import { checkboxShorthandProps } from './useCheckbox'; -/** - * Render the final JSX of Checkbox - */ export const renderCheckbox = (state: CheckboxState) => { - const { slots, slotProps } = getSlotsCompat(state, checkboxShorthandProps); + const { slots, slotProps } = getSlots(state, checkboxShorthandProps); return ( - {state.labelPosition === 'before' && } -
+ {state.labelPosition === 'before' && state.children} +
- {state.labelPosition === 'after' && } + {state.labelPosition === 'after' && state.children} ); }; diff --git a/packages/react-checkbox/src/components/Checkbox/useCheckbox.tsx b/packages/react-checkbox/src/components/Checkbox/useCheckbox.tsx index 31ddb1734bb411..363535b9c54739 100644 --- a/packages/react-checkbox/src/components/Checkbox/useCheckbox.tsx +++ b/packages/react-checkbox/src/components/Checkbox/useCheckbox.tsx @@ -1,22 +1,20 @@ import * as React from 'react'; import { - makeMergeProps, - resolveShorthandProps, + resolveShorthand, useControllableState, useId, useIsomorphicLayoutEffect, useMergedRefs, + useEventCallback, } from '@fluentui/react-utilities'; -import { CheckboxProps, CheckboxShorthandProps, CheckboxState } from './Checkbox.types'; import { Label } from '@fluentui/react-label'; -import { DefaultCheckmarkIcon, DefaultMixedIcon } from './DefaultIcons'; +import { CheckboxProps, CheckboxState, CheckboxSlots } from './Checkbox.types'; +import { Mixed12Regular, Mixed16Regular, Checkmark12Regular, Checkmark16Regular } from './DefaultIcons'; /** - * Array of all shorthand properties listed in CheckboxShorthandProps + * Array of all shorthand properties listed as the keys of CheckboxSlots */ -export const checkboxShorthandProps: CheckboxShorthandProps[] = ['label', 'indicator', 'input']; - -const mergeProps = makeMergeProps({ deepMerge: checkboxShorthandProps }); +export const checkboxShorthandProps: (keyof CheckboxSlots)[] = ['indicator', 'input']; /** * Create the state required to render Checkbox. @@ -26,34 +24,28 @@ const mergeProps = makeMergeProps({ deepMerge: checkboxShorthandP * * @param props - props from this instance of Checkbox * @param ref - reference to root HTMLElement of Checkbox - * @param defaultProps - (optional) default prop values provided by the implementing type */ -export const useCheckbox = ( - props: CheckboxProps, - ref: React.Ref, - defaultProps?: CheckboxProps, -): CheckboxState => { - const state = mergeProps( - { - ref, - id: useId('checkbox-'), - size: 'medium', - labelPosition: 'after', - label: { - as: Label, - }, - indicator: { - as: 'div', - }, - input: { - as: 'input', - type: 'checkbox', - children: null, - }, +export const useCheckbox = (props: CheckboxProps, ref: React.Ref): CheckboxState => { + const state: CheckboxState = { + ref, + id: useId('checkbox-'), + size: 'medium', + labelPosition: 'after', + + ...props, + + components: { + root: props.children !== undefined ? Label : 'span', + indicator: 'div', + input: 'input', }, - defaultProps && resolveShorthandProps(defaultProps, checkboxShorthandProps), - resolveShorthandProps(props, checkboxShorthandProps), - ); + + input: resolveShorthand(props.input, { + type: 'checkbox', + children: null, + }), + indicator: resolveShorthand(props.indicator), + }; const [checked, setCheckedInternal] = useControllableState({ defaultState: props.defaultChecked, @@ -69,33 +61,32 @@ export const useCheckbox = ( }, [state.onChange, setCheckedInternal], ); + state.input.checked = checked === true; state.checked = checked ? checked : false; - state.indicator.children = checked === 'mixed' ? : ; + + if (!state.indicator.children) { + if (state.size === 'medium') { + state.indicator.children = checked === 'mixed' ? : ; + } else { + state.indicator.children = checked === 'mixed' ? : ; + } + } const userOnChange = state.input.onChange; - state.input.onChange = React.useCallback( - ev => { - userOnChange?.(ev); - setChecked(ev, ev.currentTarget.indeterminate ? 'mixed' : ev.currentTarget.checked); - }, - [userOnChange, setChecked], - ); + state.input.onChange = useEventCallback(ev => { + userOnChange?.(ev); + setChecked(ev, ev.currentTarget.indeterminate ? 'mixed' : ev.currentTarget.checked); + }); if (state.disabled !== undefined) { - state.label.disabled = state.disabled; state.input.disabled = state.disabled; } if (state.required !== undefined) { - state.label.required = state.required; state.input.required = state.required; } - if (!state.label.htmlFor) { - state.label.htmlFor = state.id; - } - state.input.id = state.id; state.id = state.rootId; diff --git a/packages/react-checkbox/src/components/Checkbox/useCheckboxStyles.ts b/packages/react-checkbox/src/components/Checkbox/useCheckboxStyles.ts index 2c457c2c9a768b..1524557de44c75 100644 --- a/packages/react-checkbox/src/components/Checkbox/useCheckboxStyles.ts +++ b/packages/react-checkbox/src/components/Checkbox/useCheckboxStyles.ts @@ -12,188 +12,187 @@ const useStyles = makeStyles({ alignSelf: 'flex-start', alignItems: 'center', padding: '4px', - }), - - focusIndictor: createFocusIndicatorStyleRule( - theme => ({ - ':after': { - content: "''", - position: 'absolute', - width: '100%', - height: '100%', - border: `2px solid ${theme.alias.color.neutral.neutralForeground1}`, - borderRadius: '4px', - margin: '-6px', - }, - }), - { selector: 'focus-within' }, - ), -}); - -const useInputStyle = makeStyles({ - input: { - opacity: 0, - margin: 0, - padding: 0, + userSelect: 'none', cursor: 'pointer', - }, - - disabled: { - cursor: 'default', - }, -}); - -const useBoxStyles = makeStyles({ - box: theme => ({ - display: 'flex', - flexShrink: 0, - alignItems: 'center', - justifyContent: 'center', - boxSizing: 'border-box', - borderStyle: 'solid', - borderRadius: theme.global.borderRadius.small, }), - medium: { - width: '16px', - height: '16px', - }, + disabled: theme => ({ + color: theme.alias.color.neutral.neutralForegroundDisabled, + cursor: 'default', - large: { - width: '20px', - height: '20px', - }, + '& .ms-checkbox-indicator': { + borderColor: theme.alias.color.neutral.neutralStrokeDisabled, + color: theme.alias.color.neutral.neutralForegroundDisabled, + backgroundColor: theme.alias.color.neutral.neutralBackground1, + }, - // TODO: change marginLeft to Spacing horizontal M once it's added - before: theme => ({ - marginLeft: '12px', - }), + ':hover': { + '& .ms-checkbox-indicator': { + borderColor: theme.alias.color.neutral.neutralStrokeDisabled, + color: theme.alias.color.neutral.neutralForegroundDisabled, + backgroundColor: theme.alias.color.neutral.neutralBackground1, + }, + }, - // TODO: change marginRight to Spacing horizontal M once it's added - after: theme => ({ - marginRight: '12px', + ':active': { + '& .ms-checkbox-indicator': { + borderColor: theme.alias.color.neutral.neutralStrokeDisabled, + color: theme.alias.color.neutral.neutralForegroundDisabled, + backgroundColor: theme.alias.color.neutral.neutralBackground1, + }, + }, }), - circular: theme => ({ - borderRadius: theme.global.borderRadius.circular, - }), + unchecked: theme => ({ + color: theme.alias.color.neutral.neutralForeground3, - disabled: theme => ({ - borderWidth: theme.global.strokeWidth.thin, - borderColor: theme.alias.color.neutral.neutralStrokeDisabled, - }), + '& .ms-checkbox-indicator': { + borderColor: theme.alias.color.neutral.neutralStrokeAccessible, + '& > *': { + opacity: 0, + }, + }, - unchecked: theme => ({ - borderColor: theme.alias.color.neutral.neutralStrokeAccessible, - borderWidth: theme.global.strokeWidth.thin, ':hover': { - borderColor: theme.alias.color.neutral.neutralStrokeAccessibleHover, + color: theme.alias.color.neutral.neutralForeground2, + + '& .ms-checkbox-indicator': { + borderColor: theme.alias.color.neutral.neutralStrokeAccessibleHover, + }, }, + ':active': { - borderColor: theme.alias.color.neutral.neutralStrokeAccessiblePressed, + color: theme.alias.color.neutral.neutralForeground1, + + '& .ms-checkbox-indicator': { + borderColor: theme.alias.color.neutral.neutralStrokeAccessiblePressed, + }, }, }), checked: theme => ({ - backgroundColor: theme.alias.color.neutral.compoundBrandBackground, - borderWidth: 0, - ':hover': { - backgroundColor: theme.alias.color.neutral.compoundBrandBackgroundHover, + color: theme.alias.color.neutral.neutralForeground1, + + // TODO: neutralForegroundInverted change to NeutralForegroundOnBrand once it's added + '& .ms-checkbox-indicator': { + backgroundColor: theme.alias.color.neutral.compoundBrandBackground, + color: theme.alias.color.neutral.neutralForegroundInverted, + borderColor: theme.alias.color.neutral.brandBackground, }, + ':active': { - backgroundColor: theme.alias.color.neutral.compoundBrandBackgroundPressed, + '& .ms-checkbox-indicator': { + backgroundColor: theme.alias.color.neutral.compoundBrandBackgroundPressed, + }, + }, + + ':hover': { + '& .ms-checkbox-indicator': { + backgroundColor: theme.alias.color.neutral.compoundBrandBackgroundHover, + }, }, }), mixed: theme => ({ - borderColor: theme.alias.color.neutral.compoundBrandStroke, - borderWidth: theme.global.strokeWidth.thin, - ':hover': { - borderColor: theme.alias.color.neutral.compoundBrandStrokeHover, + color: theme.alias.color.neutral.neutralForeground1, + + '& .ms-checkbox-indicator': { + borderColor: theme.alias.color.neutral.compoundBrandStroke, + color: theme.alias.color.neutral.compoundBrandForeground1, }, + ':active': { - borderColor: theme.alias.color.neutral.compoundBrandStrokePressed, + '& .ms-checkbox-indicator': { + borderColor: theme.alias.color.neutral.compoundBrandStrokePressed, + color: theme.alias.color.neutral.compoundBrandForeground1Pressed, + }, + }, + + ':hover': { + '& .ms-checkbox-indicator': { + borderColor: theme.alias.color.neutral.compoundBrandStrokeHover, + color: theme.alias.color.neutral.compoundBrandForeground1Hover, + }, }, }), + + focusIndictor: createFocusIndicatorStyleRule( + theme => ({ + ':after': { + content: "''", + position: 'absolute', + width: '100%', + height: '100%', + border: `2px solid ${theme.alias.color.neutral.neutralForeground1}`, + borderRadius: '4px', + margin: '-6px', + }, + }), + { selector: 'focus-within' }, + ), }); -const useIndicatorStyles = makeStyles({ - indicator: { - position: 'absolute', +const useContainerStyles = makeStyles({ + container: { + position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center', - fill: 'currentColor', }, - // TODO: Remove fontSize once checkbox uses react-icons medium: { - fontSize: '8px', - width: '8px', - height: '8px', + width: '16px', + height: '16px', }, - // TODO: Remove fontSize once checkbox uses react-icons large: { - fontSize: '10px', - width: '10px', - height: '10px', - }, - - disabled: theme => ({ - opacity: 1, - color: theme.alias.color.neutral.neutralForegroundDisabled, - }), - - unchecked: { - opacity: 0, + width: '20px', + height: '20px', }, - // TODO: neutralForegroundInverted change to NeutralForegroundOnBrand once it's added - checked: theme => ({ - opacity: 1, - color: theme.alias.color.neutral.neutralForegroundInverted, + // TODO: change marginLeft to Spacing horizontal M once it's added + before: theme => ({ + marginLeft: '12px', }), - mixed: theme => ({ - opacity: 1, - color: theme.alias.color.neutral.compoundBrandForeground1, - ':hover': { - color: theme.alias.color.neutral.compoundBrandForeground1Hover, - }, - ':active': { - color: theme.alias.color.neutral.compoundBrandForeground1Pressed, - }, + // TODO: change marginRight to Spacing horizontal M once it's added + after: theme => ({ + marginRight: '12px', }), }); -const useLabelStyles = makeStyles({ - label: { - userSelect: 'none', +const useInputStyles = makeStyles({ + input: { + opacity: 0, + position: 'absolute', + margin: 0, + padding: 0, cursor: 'pointer', }, - disabled: theme => ({ - color: theme.alias.color.neutral.neutralForegroundDisabled, + disabled: { cursor: 'default', - }), - - unchecked: theme => ({ - color: theme.alias.color.neutral.neutralForeground3, - ':hover': { - color: theme.alias.color.neutral.neutralForeground2, - }, - ':active': { - color: theme.alias.color.neutral.neutralForeground1, - }, - }), + }, +}); - checked: theme => ({ - color: theme.alias.color.neutral.neutralForeground1, +const useIndicatorStyles = makeStyles({ + box: theme => ({ + width: '100%', + height: '100%', + fill: 'currentColor', + overflow: 'hidden', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + position: 'absolute', + boxSizing: 'border-box', + borderStyle: 'solid', + borderRadius: theme.global.borderRadius.small, + borderWidth: theme.global.strokeWidth.thin, }), - mixed: theme => ({ - color: theme.alias.color.neutral.neutralForeground1, + circular: theme => ({ + borderRadius: theme.global.borderRadius.circular, }), }); @@ -203,42 +202,38 @@ const useLabelStyles = makeStyles({ export const useCheckboxStyles = (state: CheckboxState): CheckboxState => { const checkedState = state.checked === 'mixed' ? 'mixed' : state.checked ? 'checked' : 'unchecked'; const indicatorStyles = useIndicatorStyles(); - const labelStyles = useLabelStyles(); - const inputStyles = useInputStyle(); - const boxStyles = useBoxStyles(); + const inputStyles = useInputStyles(); + const containerStyles = useContainerStyles(); const styles = useStyles(); - state.className = mergeClasses(styles.root, styles.focusIndictor, state.className); + state.className = mergeClasses( + styles.root, + styles.focusIndictor, + styles[checkedState], + state.disabled && styles.disabled, + state.className, + ); state.input.className = mergeClasses( - boxStyles[state.size], + containerStyles[state.size], inputStyles.input, state.disabled && inputStyles.disabled, state.input.className, ); - state.checkboxClassName = mergeClasses( - boxStyles.box, - boxStyles[state.size], - !!state.label.children && boxStyles[state.labelPosition], - !state.disabled && boxStyles[checkedState], - state.disabled && boxStyles.disabled, - state.circular && boxStyles.circular, + state.containerClassName = mergeClasses( + containerStyles.container, + containerStyles[state.size], + !!state.children && containerStyles[state.labelPosition], ); state.indicator.className = mergeClasses( - indicatorStyles[state.size], - indicatorStyles.indicator, - state.disabled && indicatorStyles.disabled, - !state.disabled && indicatorStyles[checkedState], + indicatorStyles.box, + containerStyles[state.size], + state.circular && indicatorStyles.circular, + 'ms-checkbox-indicator', state.indicator.className, ); - state.label.className = mergeClasses( - labelStyles.label, - !state.disabled && labelStyles[checkedState], - state.disabled && labelStyles.disabled, - ); - return state; };