-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e1c70ad
commit 357b771
Showing
12 changed files
with
4,088 additions
and
5,142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { ComponentPropsWithoutRef, ReactNode } from 'react'; | ||
import { CheckboxGroupContextValue } from './CheckboxGroup.context'; | ||
import { BoxProps } from '../Box'; | ||
|
||
export interface BaseCheckboxGroupProps extends ComponentPropsWithoutRef<'fieldset'> { | ||
name?: CheckboxGroupContextValue['name']; | ||
required?: boolean; | ||
disabled?: boolean; | ||
defaultValue?: Array<string>; | ||
value?: CheckboxGroupContextValue['value']; | ||
onValueChange?: (value: Array<string>) => void; | ||
/** | ||
* The label for the radio group. This should contain the question being | ||
* answered by the radio 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 radio | ||
* group. | ||
*/ | ||
label?: ReactNode; | ||
/** | ||
* Helper text for the radio group. Provides a hint such as specific | ||
* requirements for what to choose. When displayed, child `Radio` or | ||
* `RadioTile` 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 RadioGroup children, separate to the width of the | ||
* entire RadioGroup. | ||
*/ | ||
contentWidth?: BoxProps['width']; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import * as React from 'react'; | ||
import { useControllableState } from '@radix-ui/react-use-controllable-state'; | ||
import { Fieldset } from '../Fieldset'; | ||
import { FieldsetLegend } from '../FieldsetLegend'; | ||
import { HelperText } from '../HelperText'; | ||
import { Flex } from '../Flex'; | ||
import { mergeIds } from '../utils'; | ||
import { CheckboxGroupContext } from './CheckboxGroup.context'; | ||
import { useIds } from '../hooks'; | ||
import { CheckboxGroupProps } from './CheckboxGroup.props'; | ||
import { BaseCheckboxGroupProps } from './BaseCheckboxGroup.props'; | ||
|
||
const componentName = 'BaseCheckboxGroup'; | ||
|
||
const BaseCheckboxGroup = React.forwardRef<HTMLFieldSetElement, BaseCheckboxGroupProps>( | ||
( | ||
{ | ||
name, | ||
defaultValue, | ||
value: valueProp, | ||
required = false, | ||
disabled = false, | ||
onValueChange, | ||
id: providedId, | ||
label, | ||
helperText, | ||
helperTextPosition = 'top', | ||
showHelperTextIcon, | ||
error, | ||
errorMessage, | ||
showErrorMessageIcon, | ||
'aria-labelledby': ariaLabelledby, | ||
'aria-describedby': ariaDescribedby, | ||
'aria-errormessage': ariaErrorMessage, | ||
children, | ||
...props | ||
}, | ||
ref | ||
) => { | ||
const { id, labelId, helperTextId, errorMessageId } = useIds({ | ||
providedId, | ||
componentPrefix: 'checkboxgroup', | ||
}); | ||
const showErrorMessage = Boolean(error && errorMessage); | ||
const fieldDirection = helperTextPosition === 'top' ? 'column' : 'column-reverse'; | ||
|
||
// With useControllableState, you can pass an initial state (using | ||
// defaultValue) implying the component is uncontrolled, or you can pass a | ||
// controlled value (using value) implying the component is controlled. | ||
const [value = [], setValue] = useControllableState({ | ||
prop: valueProp, | ||
defaultProp: defaultValue, | ||
onChange: onValueChange, | ||
}); | ||
|
||
const handleItemCheck = React.useCallback( | ||
(itemValue: string) => setValue((prevValue = []) => [...prevValue, itemValue]), | ||
[setValue] | ||
); | ||
|
||
const handleItemUncheck = React.useCallback( | ||
(itemValue: string) => | ||
setValue((prevValue = []) => prevValue.filter(value => value !== itemValue)), | ||
[setValue] | ||
); | ||
|
||
const providerValue = { | ||
name, | ||
required, | ||
disabled, | ||
value, | ||
onItemCheck: handleItemCheck, | ||
onItemUncheck: handleItemUncheck, | ||
hasGroupHelperText: !!helperText, | ||
'aria-describedby': mergeIds( | ||
ariaDescribedby || !!helperText ? helperTextId : undefined, | ||
ariaErrorMessage || showErrorMessage ? errorMessageId : undefined | ||
), | ||
}; | ||
|
||
return ( | ||
<Fieldset | ||
ref={ref} | ||
{...props} | ||
disabled={disabled} | ||
id={id} | ||
data-disabled={disabled ? '' : undefined} | ||
aria-errormessage={ariaErrorMessage || showErrorMessage ? errorMessageId : undefined} | ||
aria-labelledby={ariaLabelledby || !!label ? labelId : undefined} | ||
aria-invalid={showErrorMessage} | ||
> | ||
{label ? ( | ||
<FieldsetLegend id={labelId} disabled={disabled}> | ||
{label} | ||
</FieldsetLegend> | ||
) : null} | ||
<Flex gap={2} direction={fieldDirection}> | ||
{helperText ? ( | ||
<HelperText id={helperTextId} disabled={disabled} showIcon={showHelperTextIcon}> | ||
{helperText} | ||
</HelperText> | ||
) : null} | ||
|
||
<CheckboxGroupContext.Provider value={providerValue}> | ||
{children} | ||
</CheckboxGroupContext.Provider> | ||
</Flex> | ||
{showErrorMessage ? ( | ||
<HelperText | ||
validationStatus="invalid" | ||
showIcon={showErrorMessageIcon} | ||
id={errorMessageId} | ||
> | ||
{errorMessage} | ||
</HelperText> | ||
) : null} | ||
</Fieldset> | ||
); | ||
} | ||
); | ||
|
||
BaseCheckboxGroup.displayName = componentName; | ||
|
||
export { BaseCheckboxGroup }; | ||
export type { CheckboxGroupProps }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { StackProps } from '../Stack'; | ||
import { BaseCheckboxGroupProps } from './BaseCheckboxGroup.props'; | ||
|
||
export interface CheckboxGridGroupProps extends Omit<BaseCheckboxGroupProps, 'direction'> { | ||
/** Sets the number of columns to display the contents in. */ | ||
columns?: StackProps['spacing']; | ||
} |
58 changes: 58 additions & 0 deletions
58
packages/web-ui/src/Checkbox/CheckboxGridGroup.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import * as React from 'react'; | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
import { CheckboxGridGroup } from './CheckboxGridGroup'; | ||
import { CheckboxTile } from './CheckboxTile'; | ||
|
||
const meta: Meta<typeof CheckboxGridGroup> = { | ||
title: 'Web UI / Components / Checkbox / CheckboxGridGroup', | ||
component: CheckboxGridGroup, | ||
argTypes: { | ||
direction: { | ||
options: ['column', 'row'], | ||
control: { type: 'radio' }, | ||
}, | ||
helperText: { control: { type: 'text' } }, | ||
helperTextPosition: { options: ['top', 'bottom'], control: { type: 'radio' } }, | ||
showHelperTextIcon: { control: { type: 'boolean' } }, | ||
label: { control: { type: 'text' } }, | ||
error: { control: { type: 'boolean' } }, | ||
errorMessage: { control: { type: 'text' } }, | ||
showErrorMessageIcon: { control: { type: 'boolean' } }, | ||
disabled: { control: { type: 'boolean' } }, | ||
contentWidth: { control: { type: 'text' } }, | ||
columns: { control: { type: 'number' } }, | ||
}, | ||
args: { | ||
label: 'Label', | ||
defaultValue: ['1', '2'], | ||
columns: 2, | ||
direction: 'column', | ||
disabled: false, | ||
helperText: 'Helper text', | ||
helperTextPosition: 'top', | ||
showHelperTextIcon: false, | ||
error: false, | ||
errorMessage: 'There is an error', | ||
showErrorMessageIcon: true, | ||
contentWidth: undefined, | ||
}, | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof CheckboxGridGroup>; | ||
|
||
export const Workshop: Story = { | ||
name: 'CheckboxGridGroup', | ||
render: args => ( | ||
<form> | ||
<CheckboxGridGroup {...args} name="checkbox-tiles-story"> | ||
<CheckboxTile value="1" label="One" /> | ||
<CheckboxTile value="2" label="Two" /> | ||
<CheckboxTile value="3" label="Three" /> | ||
<CheckboxTile value="4" label="Four" /> | ||
<CheckboxTile value="5" label="Five" /> | ||
<CheckboxTile value="6" label="Six" /> | ||
</CheckboxGridGroup> | ||
</form> | ||
), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import * as React from 'react'; | ||
import { getColumns, withGlobalPrefix } from '../utils'; | ||
import clsx from 'clsx'; | ||
import { BaseCheckboxGroup } from './BaseCheckboxGroup'; | ||
import { Box } from '../Box'; | ||
import { CheckboxGridGroupProps } from './CheckboxGridGroup.props'; | ||
|
||
const componentName = 'CheckboxGridGroup'; | ||
const componentClassName = withGlobalPrefix(componentName); | ||
|
||
export const CheckboxGridGroup = React.forwardRef<HTMLFieldSetElement, CheckboxGridGroupProps>( | ||
({ children, contentWidth = 'fit-content', columns = 2, className, ...props }, ref) => { | ||
return ( | ||
<BaseCheckboxGroup ref={ref} className={clsx(componentClassName, className)} {...props}> | ||
<Box | ||
display="grid" | ||
gap={2} | ||
gridTemplateColumns={getColumns(columns)} | ||
minWidth="fit-content" | ||
width={contentWidth} | ||
> | ||
{children} | ||
</Box> | ||
</BaseCheckboxGroup> | ||
); | ||
} | ||
); | ||
|
||
CheckboxGridGroup.displayName = componentName; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,6 @@ | ||
import { ComponentPropsWithoutRef, ReactNode } from 'react'; | ||
import { CheckboxGroupContextValue } from './CheckboxGroup.context'; | ||
import { BoxProps } from '../Box'; | ||
import { BaseCheckboxGroupProps } from './BaseCheckboxGroup.props'; | ||
|
||
export interface CheckboxGroupProps extends ComponentPropsWithoutRef<'fieldset'> { | ||
name?: CheckboxGroupContextValue['name']; | ||
required?: boolean; | ||
disabled?: boolean; | ||
defaultValue?: Array<string>; | ||
value?: CheckboxGroupContextValue['value']; | ||
onValueChange?: (value: Array<string>) => void; | ||
export interface CheckboxGroupProps extends BaseCheckboxGroupProps { | ||
/** The direction of the radios, will also set the aria-orientation value. */ | ||
direction?: 'column' | 'row'; | ||
/** | ||
* Set the width of the RadioGroup children, separate to the width of the | ||
* entire RadioGroup. | ||
*/ | ||
contentWidth?: BoxProps['width']; | ||
/** | ||
* The label for the radio group. This should contain the question being | ||
* answered by the radio 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 radio | ||
* group. | ||
*/ | ||
label?: ReactNode; | ||
/** | ||
* Helper text for the radio group. Provides a hint such as specific | ||
* requirements for what to choose. When displayed, child `Radio` or | ||
* `RadioTile` 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; | ||
} |
Oops, something went wrong.