Skip to content

Commit

Permalink
refactor(react-checkbox): Removing mergeProps and refactoring Checkbo…
Browse files Browse the repository at this point in the history
…x. (#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
  • Loading branch information
sopranopillow authored Aug 17, 2021
1 parent d09247f commit 028f284
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 367 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Refactoring Checkbox, removing mergeProps, and fixing indicator styles.",
"packageName": "@fluentui/react-checkbox",
"email": "[email protected]",
"dependentChangeType": "patch"
}
48 changes: 24 additions & 24 deletions packages/react-checkbox/etc/react-checkbox.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<CheckboxProps & React_2.RefAttributes<HTMLElement>>;

// @public
export type CheckboxDefaultedProps = 'label' | 'indicator' | 'input' | 'size' | 'labelPosition';

// @public
export interface CheckboxOnChangeData {
// (undocumented)
checked: 'mixed' | boolean;
}

// @public
export interface CheckboxProps extends Omit<ComponentPropsCompat, 'children'>, Omit<React_2.HTMLAttributes<HTMLElement>, 'defaultChecked' | 'onChange'> {
export interface CheckboxCommons extends Omit<LabelProps, 'defaultChecked' | 'onChange' | 'as'> {
checked?: 'mixed' | boolean;
circular?: boolean;
defaultChecked?: 'mixed' | boolean;
disabled?: boolean;
id?: string;
indicator?: ShorthandProps<ComponentPropsCompat>;
input?: ShorthandProps<React_2.InputHTMLAttributes<HTMLInputElement> & React_2.RefAttributes<HTMLInputElement>>;
label?: ShorthandProps<LabelProps>;
labelPosition?: 'before' | 'after';
labelPosition: 'before' | 'after';
onChange?: (ev: React_2.FormEvent<HTMLInputElement>, 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<CheckboxSlots>>, Partial<CheckboxCommons> {
}

// @public
export const checkboxShorthandProps: CheckboxShorthandProps[];
export const checkboxShorthandProps: (keyof CheckboxSlots)[];

// @public (undocumented)
export type CheckboxSlots = {
input: React_2.InputHTMLAttributes<HTMLInputElement> & React_2.RefAttributes<HTMLInputElement>;
indicator: React_2.HtmlHTMLAttributes<HTMLDivElement>;
};

// @public
export interface CheckboxState extends ComponentStateCompat<CheckboxProps, CheckboxShorthandProps, CheckboxDefaultedProps> {
checkboxClassName?: string;
export interface CheckboxState extends ComponentState<CheckboxSlots>, CheckboxCommons {
containerClassName?: string;
ref: React_2.Ref<HTMLElement>;
}

// @public
// @public (undocumented)
export const renderCheckbox: (state: CheckboxState) => JSX.Element;

// @public
export const useCheckbox: (props: CheckboxProps, ref: React_2.Ref<HTMLElement>, defaultProps?: CheckboxProps | undefined) => CheckboxState;
export const useCheckbox: (props: CheckboxProps, ref: React_2.Ref<HTMLElement>) => CheckboxState;

// @public
export const useCheckboxStyles: (state: CheckboxState) => CheckboxState;
Expand Down
22 changes: 13 additions & 9 deletions packages/react-checkbox/src/Checkbox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@ import { Checkbox } from './index';

export const CheckboxVariations = () => (
<div style={{ display: 'flex', gap: '14px', flexDirection: 'column' }}>
<Checkbox label="Simple Checkbox" />
<Checkbox label="Circular Checkbox" circular />
<Checkbox label="Checkbox with label positioned before" labelPosition="before" />
<Checkbox label="Required Checkbox" required />
<Checkbox label="Large Checkbox" size="large" />
<Checkbox>Simple Checkbox</Checkbox>
<Checkbox circular>Circular Checkbox</Checkbox>
<Checkbox labelPosition="before">Checkbox with label positioned before</Checkbox>
<Checkbox required>Required Checkbox</Checkbox>
<Checkbox size="large">Large Checkbox</Checkbox>
<Checkbox />
<Checkbox disabled />
<Checkbox disabled defaultChecked="mixed" />
<Checkbox disabled defaultChecked={true} />
</div>
);

export const CheckboxStates = () => (
<div style={{ display: 'flex', gap: '14px', flexDirection: 'column' }}>
<Checkbox label="Unchecked Checkbox" />
<Checkbox label="Checked Checkbox" defaultChecked />
<Checkbox label="Mixed Checkbox" defaultChecked="mixed" />
<Checkbox label="Disabled Checkbox" disabled />
<Checkbox>Unchecked Checkbox</Checkbox>
<Checkbox defaultChecked>Checked Checkbox</Checkbox>
<Checkbox defaultChecked="mixed">Mixed Checkbox</Checkbox>
<Checkbox disabled>Disabled Checkbox</Checkbox>
</div>
);

Expand Down
16 changes: 10 additions & 6 deletions packages/react-checkbox/src/components/Checkbox/Checkbox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,36 @@ describe('Checkbox', () => {
});

it('renders a default state', () => {
renderedComponent = render(<Checkbox label="Default Checkbox" />);
renderedComponent = render(<Checkbox>Default Checkbox</Checkbox>);
expect(renderedComponent.container).toMatchSnapshot();
});

it('renders unchecked correctly', () => {
renderedComponent = render(<Checkbox label="Default Checkbox" input={{ ref: checkboxRef }} />);
renderedComponent = render(<Checkbox input={{ ref: checkboxRef }}>Default Checkbox</Checkbox>);
expect(renderedComponent).toMatchSnapshot();
});

it('renders checked correctly', () => {
renderedComponent = render(<Checkbox checked label="Default Checkbox" />);
renderedComponent = render(<Checkbox checked>Default Checkbox</Checkbox>);
expect(renderedComponent).toMatchSnapshot();
});

it('renders mixed correctly', () => {
renderedComponent = render(<Checkbox checked="mixed" label="Default Checkbox" />);
renderedComponent = render(<Checkbox checked="mixed">Default Checkbox</Checkbox>);
expect(renderedComponent).toMatchSnapshot();
});

it('respects id prop', () => {
component = mount(<Checkbox aria-describedby="descriptionID" id="checkbox" label="Default Checkbox" />);
component = mount(
<Checkbox aria-describedby="descriptionID" id="checkbox">
Default Checkbox
</Checkbox>,
);
expect(component.find('input').prop('id')).toEqual('checkbox');
});

it('defaults to unchecked non-mixed', () => {
component = mount(<Checkbox label="Default Checkbox" input={{ ref: checkboxRef }} />);
component = mount(<Checkbox input={{ ref: checkboxRef }}>Default Checkbox</Checkbox>);

const input = component.find('input');
expect(input.prop('checked')).toBe(false);
Expand Down
49 changes: 20 additions & 29 deletions packages/react-checkbox/src/components/Checkbox/Checkbox.types.ts
Original file line number Diff line number Diff line change
@@ -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<ComponentPropsCompat, 'children'>,
Omit<React.HTMLAttributes<HTMLElement>, 'defaultChecked' | 'onChange'> {
export type CheckboxSlots = {
/**
* Label that will be rendered next to the checkbox.
*/
label?: ShorthandProps<LabelProps>;

/**
* Indicator to be rendered as the checkbox icon.
* Hidden input that handles the checkbox's functionality.
*/
indicator?: ShorthandProps<ComponentPropsCompat>;
input: React.InputHTMLAttributes<HTMLInputElement> & React.RefAttributes<HTMLInputElement>;

/**
* Hidden input that handles the checkbox's functionality.
* Renders the checkbox, with the checkmark icon as its child when checked.
*/
input?: ShorthandProps<React.InputHTMLAttributes<HTMLInputElement> & React.RefAttributes<HTMLInputElement>>;
indicator: React.HtmlHTMLAttributes<HTMLDivElement>;
};

/**
* 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<LabelProps, 'defaultChecked' | 'onChange' | 'as'> {
/**
* Disabled state of the checkbox.
*/
Expand Down Expand Up @@ -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.
Expand All @@ -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<CheckboxSlots>>, Partial<CheckboxCommons> {}

/**
* State used in rendering Checkbox
*/
export interface CheckboxState
extends ComponentStateCompat<CheckboxProps, CheckboxShorthandProps, CheckboxDefaultedProps> {
export interface CheckboxState extends ComponentState<CheckboxSlots>, CheckboxCommons {
/**
* Ref to the root element.
*/
ref: React.Ref<HTMLElement>;

/**
* CSS class for the checkbox element.
* CSS class for the container of the input element and indicator slot.
*/
checkboxClassName?: string;
containerClassName?: string;
}
24 changes: 18 additions & 6 deletions packages/react-checkbox/src/components/Checkbox/DefaultIcons.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import * as React from 'react';

export const DefaultMixedIcon = () => (
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" focusable="false">
<rect width="100%" height="100%" />
export const Mixed12Regular = () => (
<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" focusable="false">
<rect width="8" height="8" x="2" y="2" />
</svg>
);

export const DefaultCheckmarkIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2048 2048" focusable="false">
<path d="M1837 557L768 1627l-557-558 90-90 467 466 979-978 90 90z" />
export const Mixed16Regular = () => (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" focusable="false">
<rect width="10" height="10" x="3" y="3" />
</svg>
);

export const Checkmark12Regular = () => (
<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
<path d="M9.85355 3.14645C10.0488 3.34171 10.0488 3.65829 9.85355 3.85355L5.35355 8.35355C5.15829 8.54882 4.84171 8.54882 4.64645 8.35355L2.64645 6.35355C2.45118 6.15829 2.45118 5.84171 2.64645 5.64645C2.84171 5.45118 3.15829 5.45118 3.35355 5.64645L5 7.29289L9.14645 3.14645C9.34171 2.95118 9.65829 2.95118 9.85355 3.14645Z" />
</svg>
);

export const Checkmark16Regular = () => (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M13.8639 3.65609C14.0533 3.85704 14.0439 4.17348 13.8429 4.36288L5.91309 11.8368C5.67573 12.0605 5.30311 12.0536 5.07417 11.8213L2.39384 9.10093C2.20003 8.90422 2.20237 8.58765 2.39907 8.39384C2.59578 8.20003 2.91235 8.20237 3.10616 8.39908L5.51192 10.8407L13.1571 3.63517C13.358 3.44577 13.6745 3.45513 13.8639 3.65609Z" />
</svg>
);
Loading

0 comments on commit 028f284

Please sign in to comment.