Skip to content

Commit

Permalink
feat(checkbox): ✨ update simple method with state & more
Browse files Browse the repository at this point in the history
  • Loading branch information
navin-moorthy committed Aug 31, 2021
1 parent 466969b commit 2a2323f
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 81 deletions.
66 changes: 27 additions & 39 deletions src/checkboxNew/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import {
CheckboxStateReturn,
CheckboxInitialState,
} from "./CheckboxState";
import { RenderProp } from "../utils/types";
import { CheckboxText } from "./CheckboxText";
import { CheckboxLabel } from "./CheckboxLabel";
import { USE_CHECKBOX_STATE_KEYS } from "./__keys";
import { getValidChildren, runIfFn } from "../utils";
import { runIfFn, runIfFnChildren } from "../utils";
import { getCheckboxComponentProps } from "./helpers";
import { CheckboxDescription } from "./CheckboxDescription";
import { CheckboxIcon, CheckboxDefaultIcon } from "./CheckboxIcon";
import { CheckboxInput, CheckboxInputHTMLProps } from "./CheckboxInput";
import { Dict } from "../utils/types";

export type CheckboxOwnProps = CheckboxInputHTMLProps & {
/**
Expand All @@ -32,7 +33,9 @@ export type CheckboxOwnProps = CheckboxInputHTMLProps & {
description?: React.ReactNode | ((args: CheckboxStateReturn) => JSX.Element);
};

export type CheckboxProps = CheckboxInitialState & CheckboxOwnProps;
export type CheckboxProps = CheckboxInitialState &
CheckboxOwnProps &
RenderProp<CheckboxStateReturn>;

export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
(props, ref) => {
Expand All @@ -46,8 +49,9 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
children,
...inputProps
} = checkboxProps;

const componentProps = getCheckboxComponentProps(children);
const componentProps = getCheckboxComponentProps(
runIfFnChildren(children, state),
);

return (
<CheckboxLabel
Expand All @@ -62,25 +66,31 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
{...inputProps}
{...componentProps?.inputProps}
/>
<CheckboxIcon {...state} label={label} {...componentProps?.iconProps}>
{runIfFn(icon, state)}
</CheckboxIcon>
<CheckboxIcon
{...state}
label={label}
children={runIfFn(icon, state)}
{...componentProps?.iconProps}
></CheckboxIcon>
{label && !description ? (
<CheckboxText {...state} {...componentProps?.textProps}>
{runIfFn(label, state)}
</CheckboxText>
<CheckboxText
{...state}
children={runIfFn(label, state)}
{...componentProps?.textProps}
/>
) : null}
{label && description ? (
<div className="flex flex-col">
<CheckboxText {...state} {...componentProps?.textProps}>
{runIfFn(label, state)}
</CheckboxText>
<CheckboxText
{...state}
children={runIfFn(label, state)}
{...componentProps?.textProps}
/>
<CheckboxDescription
{...state}
children={runIfFn(description, state)}
{...componentProps?.descriptionProps}
>
{runIfFn(description, state)}
</CheckboxDescription>
/>
</div>
) : null}
</CheckboxLabel>
Expand All @@ -103,25 +113,3 @@ export const useCheckboxProps = (props: CheckboxProps) => {
CheckboxInitialState,
];
};

const ComponentPropsMap = {
CheckboxLabel: "labelProps",
CheckboxInput: "inputProps",
CheckboxIcon: "iconProps",
CheckboxText: "textProps",
CheckboxDescription: "descriptionProps",
};

export const getCheckboxComponentProps = (children: React.ReactNode) => {
const validChildren = getValidChildren(children);
const props: Dict = {};

validChildren.forEach(child => {
props[
// @ts-ignore
ComponentPropsMap[child.type.displayName]
] = child.props;
});

return props;
};
2 changes: 1 addition & 1 deletion src/checkboxNew/CheckboxIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type CheckboxIconOptions = BoxOptions &
Pick<
CheckboxStateReturn,
"isChecked" | "isIndeterminate" | "isUnchecked" | "size"
> & { label: CheckboxProps["label"] };
> & { label?: CheckboxProps["label"] };

export type CheckboxIconHTMLProps = BoxHTMLProps;

Expand Down
24 changes: 24 additions & 0 deletions src/checkboxNew/helpers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Dict } from "../utils/types";
import { getValidChildren } from "../utils";

const ComponentPropsMap = {
CheckboxLabel: "labelProps",
CheckboxInput: "inputProps",
CheckboxIcon: "iconProps",
CheckboxText: "textProps",
CheckboxDescription: "descriptionProps",
};

export const getCheckboxComponentProps = (children: React.ReactNode) => {
const validChildren = getValidChildren(children);
const props: Dict = {};

validChildren.forEach(child => {
props[
// @ts-ignore
ComponentPropsMap[child.type.displayName]
] = child.props;
});

return props;
};
81 changes: 40 additions & 41 deletions src/checkboxNew/stories/Checkbox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
CheckboxIcon,
CheckboxLabel,
CheckboxInput,
useCheckboxState,
CheckboxState,
CheckboxInitialState,
CheckboxText,
Expand All @@ -17,7 +16,7 @@ import {
import { Button } from "../../button";
import { withIconA11y } from "../../utils";
import { CheckboxInputHTMLProps } from "../CheckboxInput";
import { CloseIcon, EyeClose, EyeOpen } from "../../icons";
import { EyeClose, EyeOpen } from "../../icons";
import { createControls } from "../../../.storybook/storybookUtils";
import { Checkbox, CheckboxProps, useCheckboxProps } from "../Checkbox";

Expand Down Expand Up @@ -490,39 +489,50 @@ export const CustomIcon = () => {
CustomIcon.parameters = { options: { showPanel: false } };

export const CustomSimple = () => {
const state = useCheckboxState();

return (
<CheckboxLabel {...state}>
<CheckboxInput {...state} />
<CheckboxIcon
{...state}
className={cx(
state.isChecked
? "bg-red-500 peer-hover:bg-red-400 peer-active:bg-red-600 "
: "",
"peer-focus-visible:ring-orange-400",
)}
>
{state.isChecked ? withIconA11y(<CloseIcon />) : null}
</CheckboxIcon>
<div className="flex">
<CheckboxText {...state} className="text-pink-600">
Custom Checkbox
</CheckboxText>
<CheckboxDescription
as="span"
{...state}
className="self-center mt-0 ml-2 text-xs text-emarald-600"
>
Custom Description
</CheckboxDescription>
</div>
</CheckboxLabel>
<Checkbox label="Checkbox" description="Fruits in the basket">
<CheckboxLabel className="p-2 border-2 border-blue-500 rounded" />
<CheckboxIcon className="bg-red-500" />
<CheckboxText className="text-green-500" />
<CheckboxDescription className="text-orange-500" />
</Checkbox>
);
};
CustomSimple.parameters = { options: { showPanel: false } };

export const CustomSimpleV2 = () => {
return (
<Checkbox label="Checkbox" description="Fruits in the basket">
{state => {
return (
<>
<CheckboxLabel className="p-2 border-2 border-blue-500 rounded" />
<CheckboxIcon
className={
state.isChecked
? "bg-red-500 peer-hover:bg-red-400"
: "bg-green-500 peer-hover:bg-green-400"
}
>
<>
{state.isUnchecked ? withIconA11y(<EyeClose />) : null}
{state.isChecked ? withIconA11y(<EyeOpen />) : null}
</>
</CheckboxIcon>
<CheckboxText className="text-green-500">
Overidden Label
</CheckboxText>
<CheckboxDescription className="text-orange-500">
Overridden Description
</CheckboxDescription>
</>
);
}}
</Checkbox>
);
};
CustomSimpleV2.parameters = { options: { showPanel: false } };

type CustomCheckboxProps = CheckboxInputHTMLProps & CheckboxInitialState;

const CustomCheckbox: React.FC<CustomCheckboxProps> = props => {
Expand Down Expand Up @@ -559,17 +569,6 @@ const CustomCheckbox: React.FC<CustomCheckboxProps> = props => {
);
};

export const CustomSimpleV2 = () => {
return (
<Checkbox label="Checkbox" description="Fruits in the basket">
<CheckboxLabel className="p-2 border-2 border-blue-500 rounded" />
<CheckboxIcon className="bg-red-500" />
<CheckboxText className="text-green-500" />
<CheckboxDescription className="text-orange-500" />
</Checkbox>
);
};

export const CustomAdvanced = () => {
const [state, onStateChange] = useState<CheckboxState["state"]>([]);

Expand Down
8 changes: 8 additions & 0 deletions src/utils/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ export function runIfFn<T, U>(
return isFunction(valueOrFn) ? valueOrFn(...args) : valueOrFn;
}

export function runIfFnChildren<T, U>(
valueOrFn: T | ((...fnArgs: U[]) => T),
...args: U[]
): T {
// @ts-ignore
return isFunction(valueOrFn) ? valueOrFn(...args).props.children : valueOrFn;
}

/**
* Gets only the valid children of a component,
* and ignores any nullish or falsy child.
Expand Down

0 comments on commit 2a2323f

Please sign in to comment.