Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Checkbox components #370

Merged
merged 47 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
fa2255b
add checkbox
robphoenix Jun 6, 2024
f97edd5
focus state
robphoenix Jun 6, 2024
7d0c5ee
disabled state
robphoenix Jun 6, 2024
8836967
label & helperText
robphoenix Jun 6, 2024
35d5949
start on checkbox tile
robphoenix Jun 7, 2024
cc28d2a
update checkboxtile styles
robphoenix Jun 10, 2024
01cb7d0
start on checkboxgroup
robphoenix Jun 10, 2024
593fe87
roving tabindex
robphoenix Jun 11, 2024
7dcfe58
still working
robphoenix Jun 11, 2024
23d8f58
styling
robphoenix Jun 11, 2024
3747bbb
update
robphoenix Jun 11, 2024
1a71aa6
update naming
robphoenix Jun 11, 2024
2a674aa
Strip out radix internals where possible
robphoenix Jun 13, 2024
189aaea
moving things around
robphoenix Jun 13, 2024
0f7919d
share base components
robphoenix Jun 13, 2024
b279acc
Remove old components
robphoenix Jun 14, 2024
609c4bb
stuff
robphoenix Jun 14, 2024
2ca35ad
reflect native behaviour
robphoenix Jun 14, 2024
6cccddb
remove roving tabindex
robphoenix Jun 14, 2024
64e9c59
update behaviour
robphoenix Jun 17, 2024
af5dc93
Add GridGroup
robphoenix Jun 17, 2024
ba64290
move to separate dirs
robphoenix Jun 19, 2024
28ca0c6
Merge branch 'main' into rphoenix-uwds-599
robphoenix Jun 19, 2024
e1594a8
update props
robphoenix Jun 19, 2024
c2019da
start adding docs
robphoenix Jun 19, 2024
cdf7ba3
add changeset
robphoenix Jun 19, 2024
8ba33fc
fix
robphoenix Jun 19, 2024
4572683
update error message spacing
robphoenix Jun 21, 2024
0498a3a
Merge branch 'main' into rphoenix-uwds-599
robphoenix Jun 24, 2024
ba86807
add CheckboxGroup docs
robphoenix Jun 24, 2024
9cc285b
docs updates
robphoenix Jun 24, 2024
462888d
disable text selection on checkboxes
robphoenix Jun 25, 2024
9eb389d
Merge branch 'main' into rphoenix-uwds-599
robphoenix Jun 25, 2024
2e65bf6
Merge branch 'main' into rphoenix-uwds-599
robphoenix Jun 25, 2024
37bb60f
Merge branch 'main' into rphoenix-uwds-599
robphoenix Jun 25, 2024
58f217c
fix
robphoenix Jun 25, 2024
799553d
a11y review
robphoenix Jun 26, 2024
ff48c32
add changesets
robphoenix Jun 26, 2024
0d4be6f
docs updates
robphoenix Jun 26, 2024
b4e0316
Merge branch 'main' into rphoenix-uwds-599
robphoenix Jun 26, 2024
21c5478
fix HelperText
robphoenix Jun 27, 2024
687d4c5
update checkbox/radio group layout
robphoenix Jun 27, 2024
fd3f36d
Fix Checkbox & Radio group layouts, again
robphoenix Jun 27, 2024
83b11c6
Merge branch 'main' into rphoenix-uwds-599
robphoenix Jun 27, 2024
4b14faa
Update a11y docs
robphoenix Jun 28, 2024
e9e06c4
HelperText updates
robphoenix Jun 28, 2024
292c9b3
Merge branch 'main' into rphoenix-uwds-599
robphoenix Jul 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/neat-rockets-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@utilitywarehouse/web-ui': patch
---

Add `disableUserSelect` prop to Label & HelperText components
5 changes: 5 additions & 0 deletions .changeset/plenty-bats-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@utilitywarehouse/web-ui': patch
---

Change cursor to pointer for Radio & Checkbox components
5 changes: 5 additions & 0 deletions .changeset/proud-cups-divide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@utilitywarehouse/web-ui': patch
---

Update HelperText icon size & text colour
5 changes: 5 additions & 0 deletions .changeset/twelve-buses-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@utilitywarehouse/web-ui': patch
---

Fix `HelperText` fontsize & lineheight
5 changes: 5 additions & 0 deletions .changeset/weak-tips-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@utilitywarehouse/web-ui': minor
---

Add `Checkbox`, `CheckboxTile`, `CheckboxGroup`, `CheckboxGridGroup` components
8 changes: 5 additions & 3 deletions packages/web-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@mui/system": "5.15.15",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-use-controllable-state": "^1.0.1",
"@utilitywarehouse/colour-system": "workspace:^",
"@utilitywarehouse/react-icons": "^1.1.1",
"clsx": "^2.0.0"
Expand All @@ -52,17 +54,17 @@
"@storybook/testing-library": "^0.0.14-next.1",
"@types/node": "^18.15.11",
"@types/react": "^17.0.0 || ^18.0.0",
"@types/react-syntax-highlighter": "^15.5.11",
"@utilitywarehouse/css-reset": "workspace:^",
"@utilitywarehouse/fontsource": "^0.0.5",
"chromatic": "^11.3.5",
"prop-types": "^15.8.1",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0",
"react-syntax-highlighter": "^15.5.0",
"remark-gfm": "^3.0.0",
"storybook": "^7.6.3",
"tsup": "^8.0.1",
"@types/react-syntax-highlighter": "^15.5.11",
"react-syntax-highlighter": "^15.5.0"
"tsup": "^8.0.1"
},
"peerDependencies": {
"@mui/material": "5.11.14",
Expand Down
25 changes: 25 additions & 0 deletions packages/web-ui/src/BaseCheckbox/BaseCheckbox.props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { ReactNode } from 'react';
import type { CheckboxProps as RadixCheckboxProps } from '@radix-ui/react-checkbox';

export interface BaseCheckboxProps
extends Omit<
RadixCheckboxProps,
'asChild' | 'value' | 'onCheckedChange' | 'defaultChecked' | 'defaultValue'
> {
/** The value given as data when submitted with a `name`. */
value?: string;
defaultValue?: string;
/**
* The label for the Checkbox. If not using please properly associate the
* checkbox with a label using the `aria-label` or `aria-labelledby` props.
*/
label?: ReactNode;
/** Helper text for the Checkbox. Will not display if the checkbox group has `helperText` set. */
helperText?: ReactNode;
/** The controlled checked state of the checkbox. Must be used in conjunction with onCheckedChange. */
checked?: boolean;
/** Event handler called when the checked state of the checkbox changes. */
onCheckedChange?: (checked: boolean) => void;
/** The checked state of the checkbox when it is initially rendered. Use when you do not need to control its checked state. */
defaultChecked?: boolean;
}
119 changes: 119 additions & 0 deletions packages/web-ui/src/BaseCheckbox/BaseCheckbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import * as React from 'react';
import * as RadixCheckbox from '@radix-ui/react-checkbox';
import { styled } from '../theme';
import { TickMediumIcon } from '@utilitywarehouse/react-icons';
import { BaseCheckboxProps } from './BaseCheckbox.props';
import { colors, colorsCommon } from '@utilitywarehouse/colour-system';
import { px } from '../utils';
import { useBaseCheckboxGroup } from '../BaseCheckboxGroup';

const componentName = 'BaseCheckbox';

export const StyledIndicator = styled(RadixCheckbox.Indicator)({
position: 'absolute',
});

const StyledCheckboxRoot = styled(RadixCheckbox.Root)({
position: 'relative',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
verticalAlign: 'top',
flexShrink: 0,
padding: 0,
height: 24,
width: 24,
border: 'none',
borderRadius: '50%',
'--checkbox-color': colors.cyan1000,
'--checkbox-color-disabled': colors.grey400,
'--checkbox-background-color-unchecked': colorsCommon.brandWhite,
'--checkbox-background-color-checked': colors.cyan500,
'--checkbox-background-color-unchecked-disabled': colorsCommon.brandWhite,
'--checkbox-background-color-checked-disabled': colors.grey150,
'--checkbox-border-width': px(2),
'--checkbox-border-color-unchecked': colors.grey500,
'--checkbox-border-color-checked': colors.cyan500,
'--checkbox-border-color-hover': colors.cyan500,
'--checkbox-border-color-focus': colors.cyan500,
'--checkbox-border-color-unchecked-disabled': colors.grey300,
'--checkbox-border-color-checked-disabled': colors.grey150,
'--checkbox-outline-color': 'transparent',
'--checkbox-outline-color-focus': colors.cyan700,
color: 'var(--checkbox-color)',
backgroundColor: 'var(--checkbox-background-color)',
outline: 'none',
'&::before': {
content: '""',
display: 'block',
height: 24,
width: 24,
borderRadius: px(3),
backgroundColor: 'inherit',
boxShadow: 'inset 0 0 0 var(--checkbox-border-width) var(--checkbox-border-color)',
outline: `2px solid var(--checkbox-outline-color)`,
},
':where([data-state="unchecked"])': {
'--checkbox-background-color': 'var(--checkbox-background-color-unchecked)',
'--checkbox-border-color': 'var(--checkbox-border-color-unchecked)',
'--checkbox-border-color-disabled': 'var(--checkbox-border-color-unchecked-disabled)',
'--checkbox-background-color-disabled': 'var(--checkbox-background-color-unchecked-disabled)',
},
':where([data-state="checked"])': {
'--checkbox-background-color': 'var(--checkbox-background-color-checked)',
'--checkbox-border-color': 'var(--checkbox-border-color-checked)',
'--checkbox-border-color-disabled': 'var(--checkbox-border-color-checked-disabled)',
'--checkbox-background-color-disabled': 'var(--checkbox-background-color-checked-disabled)',
},
'@media (hover: hover)': {
':where(:hover:enabled)': {
boxShadow: `0 0 0 8px ${colors.cyan75}`,
'--checkbox-border-color': 'var(--checkbox-border-color-hover)',
},
},
':where([data-disabled],[data-disabled] &)': {
'--checkbox-color': 'var(--checkbox-color-disabled)',
'--checkbox-border-color': 'var(--checkbox-border-color-disabled)',
'--checkbox-background-color': 'var(--checkbox-background-color-disabled)',
},
':where(:focus-visible)': {
'--checkbox-border-color': 'var(--checkbox-border-color-focus)',
},
});

export const BaseCheckbox = React.forwardRef<HTMLButtonElement, BaseCheckboxProps>(
({ onCheckedChange, value = 'on', ...props }, ref) => {
const context = useBaseCheckboxGroup();
const checked = context?.value?.includes(value);

return (
<StyledCheckboxRoot
ref={ref}
name={context?.name}
disabled={context?.disabled}
required={context?.required}
checked={checked}
value={value}
{...props}
onCheckedChange={(checked: boolean) => {
if (context) {
if (checked) {
context?.onItemCheck(value);
} else {
context?.onItemUncheck(value);
}
}
if (onCheckedChange) {
onCheckedChange(checked);
}
}}
>
<StyledIndicator asChild>
<TickMediumIcon />
</StyledIndicator>
</StyledCheckboxRoot>
);
}
);

BaseCheckbox.displayName = componentName;
2 changes: 2 additions & 0 deletions packages/web-ui/src/BaseCheckbox/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { BaseCheckbox } from './BaseCheckbox';
export type { BaseCheckboxProps } from './BaseCheckbox.props';
20 changes: 20 additions & 0 deletions packages/web-ui/src/BaseCheckboxGroup/BaseCheckboxGroup.context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createContext, useContext } from 'react';

export type BaseCheckboxGroupContextValue = {
name?: string;
required: boolean;
disabled: boolean;
value?: Array<string>;
onItemCheck(value: string): void;
onItemUncheck(value: string): void;
hasGroupHelperText: boolean;
'aria-describedby'?: string;
};

export const BaseCheckboxGroupContext = createContext<BaseCheckboxGroupContextValue | undefined>(
undefined
);

export const BaseCheckboxGroupProvider = BaseCheckboxGroupContext.Provider;

export const useBaseCheckboxGroup = () => useContext(BaseCheckboxGroupContext);
49 changes: 49 additions & 0 deletions packages/web-ui/src/BaseCheckboxGroup/BaseCheckboxGroup.props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { ComponentPropsWithoutRef, ReactNode } from 'react';
import { BoxProps } from '../Box';
import { BaseCheckboxGroupContextValue } from './BaseCheckboxGroup.context';

export interface BaseCheckboxGroupProps extends ComponentPropsWithoutRef<'fieldset'> {
name?: string;
required?: boolean;
disabled?: boolean;
defaultValue?: Array<string>;
value?: BaseCheckboxGroupContextValue['value'];
onValueChange?: (value: Array<string>) => void;
/**
* The label for the checkbox group. This should contain the question being
* answered by the checkbox group.
*
* If you don't include a label you need to ensure you use the `aria-label`
* or `aria-labelledby` prop to properly associate a label with the checkbox
* group.
*/
label?: ReactNode;
/**
* Helper text for the checkbox group. Provides a hint such as specific
* requirements for what to choose. When displayed, child `Checkbox` or
* `CheckboxTile` components will not display `helperText`.
*/
helperText?: ReactNode;
/**
* Position of the helper text.
* @default 'top'
*/
helperTextPosition?: 'top' | 'bottom';
/**
* Set whether to display the helper text icon.
*/
showHelperTextIcon?: boolean;
/** Controls whether the error message is displayed. */
error?: boolean;
/** The error message to be displayed. */
errorMessage?: ReactNode;
/**
* Set whether to display the error message icon.
*/
showErrorMessageIcon?: boolean;
/**
* Set the width of the CheckboxGroup children, separate to the width of the
* entire CheckboxGroup.
*/
contentWidth?: BoxProps['width'];
}
Loading
Loading