Skip to content

Commit

Permalink
Merge pull request #458 from omnifed/453-feat-checkbox-styling-fine-t…
Browse files Browse the repository at this point in the history
…uning-updates

453 feat checkbox styling fine tuning updates
  • Loading branch information
caseybaggz committed Sep 9, 2024
2 parents ff54309 + 192c67f commit 1c1573b
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 15 deletions.
8 changes: 1 addition & 7 deletions docs/app/components/code-builder/builder-snippet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@ import { useMemo, type PropsWithChildren } from 'react'
import { css } from '@cerberus/styled-system/css'

function isFormState(key: string) {
return [
'disabled',
'required',
'readOnly',
'invalid',
'indeterminate',
].includes(key)
return ['disabled', 'required', 'readOnly', 'invalid', 'mixed'].includes(key)
}

function isProgressKey(key: string) {
Expand Down
18 changes: 18 additions & 0 deletions docs/app/react/checkbox/components/checkbox-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,24 @@ export function OverviewPreview() {
I would like to receive marketing emails
</Label>
</Field>
<Field>
<Label
className={hstack({
justify: 'flex-start',
})}
htmlFor="legal"
size="sm"
>
<Checkbox
defaultChecked={true}
id="mixed"
name="mixed"
mixed
size="md"
/>
Mixed state
</Label>
</Field>
</div>
)
}
8 changes: 6 additions & 2 deletions docs/app/react/checkbox/components/live-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const api = {
size: builder.Enum('size', ['md', 'lg']),
text: builder.Text('text', 'Add your label text here'),
id: builder.Text('id', 'add-uuid'),
mixed: builder.Boolean('mixed', false),
disabled: builder.Boolean('disabled', false),
invalid: builder.Boolean('invalid', false),
readOnly: builder.Boolean('readOnly', false),
Expand All @@ -33,7 +34,7 @@ export function LivePlaygroundWithCode() {
import { hstack } from '@cerberus/styled-system/patterns'
export function MyCheckbox(props: CheckboxProps) {
const { describedBy, size, ...nativeProps } = props
const { describedBy, size, mixed, ...nativeProps } = props
const labelSize = size === 'md' ? 'sm' : 'md'
return (
Expand All @@ -55,6 +56,7 @@ export function MyCheckbox(props: CheckboxProps) {
{...nativeProps}
{...(describedBy && { describedBy: describedBy })}
id={{id}}
mixed={{mixed}}
size={{size}}
/>
{{text}}
Expand All @@ -70,7 +72,7 @@ export function MyCheckbox(props: CheckboxProps) {

export function CheckboxPreview() {
const { selectedProps } = useCodeBuilder()
const { size, text, id, ...fieldState } = selectedProps
const { size, text, id, mixed, ...fieldState } = selectedProps
const [checked, setChecked] = useState<boolean>(false)

const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
Expand All @@ -92,6 +94,7 @@ export function CheckboxPreview() {
<Checkbox
checked={checked}
id={id as string}
mixed={mixed as boolean}
name={id as string}
onChange={handleChange}
size="lg"
Expand All @@ -101,6 +104,7 @@ export function CheckboxPreview() {
<Checkbox
checked={checked}
id={id as string}
mixed={mixed as boolean}
name={id as string}
onChange={handleChange}
size="md"
Expand Down
2 changes: 2 additions & 0 deletions docs/app/react/checkbox/dev.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ define function Field(props: PropsWithChildren<FieldProps>): ReactNode
export interface CheckboxProps extends InputHTMLAttributes<HTMLInputElement> {
describedBy?: string
id: string
mixed?: boolean
size?: 'md' | 'lg'
}
Expand All @@ -56,4 +57,5 @@ The Checkbox component accepts the following props:
| -------- | ------- | ------------------------------------------------------------- |
| describedBy | | The ID of the FieldMessage element that describes the input field. |
| id | | An identifier that is shared with the Label `htmlFor` attribute. |
| mixed | | An [indeterminate state](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-checked#mixed) of a checkbox. |
| size | `md` | The size of the input field. |
3 changes: 2 additions & 1 deletion packages/panda-preset/src/recipes/slots/checkbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ export const checkbox: Partial<SlotRecipeConfig> = defineSlotRecipe({
color: 'action.text.initial',
display: 'inline-block',
zIndex: 'decorator',
w: 'full',
_peerInvalid: {
color: 'danger.text.inverse',
color: 'danger.text.initial',
},
_peerDisabled: {
opacity: formStates._disabled.opacity,
Expand Down
14 changes: 11 additions & 3 deletions packages/react/src/components/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type CheckboxProps = CheckboxVariantProps &
Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'id'> & {
describedBy?: string
id: string
mixed?: boolean
}

/**
Expand All @@ -35,10 +36,11 @@ export type CheckboxProps = CheckboxVariantProps &
* ```
*/
export function Checkbox(props: CheckboxProps) {
const { describedBy, size, checked, ...nativeProps } = props
const { describedBy, size, checked, mixed, ...nativeProps } = props
const { invalid, ...fieldStates } = useFieldContext()
const styles = checkbox({ size })
const { checkbox: CheckIcon } = $cerberusIcons
const { checkbox: CheckIcon, indeterminate: IndeterminateIcon } =
$cerberusIcons

return (
<div
Expand All @@ -55,14 +57,20 @@ export function Checkbox(props: CheckboxProps) {
{...fieldStates}
{...(describedBy && { 'aria-describedby': describedBy })}
{...(invalid && { 'aria-invalid': true })}
{...(mixed && { 'aria-checked': 'mixed' })}
className={cx('peer', nativeProps.className, styles.input)}
type="checkbox"
/>
<Show when={checked}>
<Show when={checked && !mixed}>
<span className={styles.icon}>
<CheckIcon />
</span>
</Show>
<Show when={mixed}>
<span className={styles.icon}>
<IndeterminateIcon />
</span>
</Show>
</div>
)
}
5 changes: 4 additions & 1 deletion packages/react/src/config/cerbIcons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
type CarbonIconType,
} from '@cerberus/icons'
import type { ElementType } from 'react'
import { CheckmarkIcon, IndeterminateIcon } from './icons/checkbox.icons'

export type IconType = CarbonIconType | ElementType

Expand All @@ -18,6 +19,7 @@ export interface DefinedIcons {
confirmModal?: IconType
promptModal?: IconType
fileUploader?: IconType
indeterminate?: IconType
infoNotification?: IconType
successNotification?: IconType
warningNotification?: IconType
Expand All @@ -28,10 +30,11 @@ export interface DefinedIcons {
}

export const defaultIcons: DefinedIcons = {
checkbox: Checkmark,
checkbox: CheckmarkIcon,
confirmModal: Information,
promptModal: Information,
fileUploader: CloudUpload,
indeterminate: IndeterminateIcon,
infoNotification: InformationFilled,
successNotification: CheckmarkFilled,
warningNotification: WarningFilled,
Expand Down
49 changes: 49 additions & 0 deletions packages/react/src/config/icons/checkbox.icons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { SVGProps } from 'react'

/**
* This module contains substitute icons for the Checkbox component.
* @module
*/

interface CheckboxIconProps extends SVGProps<SVGSVGElement> {}

/**
* Checkmark icon for Checkbox component
*/
export function CheckmarkIcon(props: CheckboxIconProps) {
return (
<svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
role="img"
viewBox="0 0 24 24"
{...props}
>
<path
fill="currentColor"
d="M9.714 18 4 12.335l1.818-1.764 3.896 3.824L18.181 6 20 7.803 9.714 18Z"
/>
</svg>
)
}

interface IndeterminateIconProps extends SVGProps<SVGSVGElement> {}

/**
* Indeterminate icon for Checkbox component
*/
export function IndeterminateIcon(props: IndeterminateIconProps) {
return (
<svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
role="img"
fill="none"
viewBox="0 0 24 24"
{...props}
>
<path fill="currentColor" d="M4 11h16v2.667H4z" />
</svg>
)
}
3 changes: 2 additions & 1 deletion tests/panda-preset/recipes/slots/checkbox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ describe('checkbox recipe', () => {
bottom: '0',
color: 'action.text.initial',
display: 'inline-block',
w: 'full',
zIndex: 'decorator',
_peerInvalid: {
color: 'danger.text.inverse',
color: 'danger.text.initial',
},
_peerDisabled: {
opacity: '0.5',
Expand Down
28 changes: 28 additions & 0 deletions tests/react/components/checkbox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,32 @@ describe('Checkbox', () => {
.classList.contains('cerberus-checkbox__input--size_lg'),
).toBeTruthy()
})

test('should render a checkbox icon when checked', () => {
render(
<Field>
<Label htmlFor="test">Test Label</Label>,
<Checkbox id="test" checked />
</Field>,
)
expect(
screen.getByRole('img', {
hidden: true,
}),
).toBeTruthy()
})

test('should render a mixed icon when mixed', () => {
render(
<Field>
<Label htmlFor="test">Test Label</Label>,
<Checkbox id="test" mixed />
</Field>,
)
expect(
screen.getByRole('img', {
hidden: true,
}),
).toBeTruthy()
})
})

0 comments on commit 1c1573b

Please sign in to comment.