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

Pc 33527 checkbox group with border #15709

Merged
merged 9 commits into from
Jan 13, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Button } from 'ui-kit/Button/Button'
import { ButtonVariant, IconPositionEnum } from 'ui-kit/Button/types'
import { Checkbox } from 'ui-kit/form/Checkbox/Checkbox'
import { PriceInput } from 'ui-kit/form/PriceInput/PriceInput'
import { CheckboxVariant } from 'ui-kit/form/shared/BaseCheckbox/BaseCheckbox'
import { TextInput } from 'ui-kit/form/TextInput/TextInput'
import { InfoBox } from 'ui-kit/InfoBox/InfoBox'

Expand Down Expand Up @@ -232,7 +233,7 @@ export const PriceCategoriesForm = ({
label="Accepter les réservations “Duo“"
name="isDuo"
disabled={isDisabled}
withBorder
variant={CheckboxVariant.BOX}
/>
</FormLayout.Row>
</FormLayout.Section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
Quantity,
QuantityInput,
} from 'ui-kit/form/QuantityInput/QuantityInput'
import { CheckboxVariant } from 'ui-kit/form/shared/BaseCheckbox/BaseCheckbox'
import { TextInput } from 'ui-kit/form/TextInput/TextInput'
import { InfoBox } from 'ui-kit/InfoBox/InfoBox'

Expand Down Expand Up @@ -85,7 +86,7 @@

// eslint-disable-next-line @typescript-eslint/no-floating-promises
loadStocks()
}, [])

Check warning on line 89 in pro/src/components/IndividualOffer/StocksThing/StocksThing.tsx

View workflow job for this annotation

GitHub Actions / Tests pro / Type check / Quality check / Style quality check

React Hook useEffect has missing dependencies: 'formik', 'offer', and 'useOffererAddressAsDataSourceEnabled'. Either include them or remove the dependency array

// validation is tested in getValidationSchema
// and it's not possible as is to test it here
Expand Down Expand Up @@ -469,7 +470,7 @@
label="Accepter les réservations “Duo“"
name="isDuo"
disabled={isDisabled}
withBorder
variant={CheckboxVariant.BOX}
/>
</FormLayout.Row>
</FormLayout.Section>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
@use "styles/mixins/_rem.scss" as rem;

.accessibility-checkbox-group {
legend {
margin-bottom: rem.torem(24px);
}
}

.info-banners {
margin-bottom: rem.torem(24px);
}
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,6 @@ export const UsefulInformationForm = ({
<FormLayout.Section title="Modalités d’accessibilité">
<FormLayout.Row>
<CheckboxGroup
className={styles['accessibility-checkbox-group']}
group={accessibilityOptionsGroups}
groupName="accessibility"
disabled={readOnlyFields.includes('accessibility')}
Expand Down
13 changes: 1 addition & 12 deletions pro/src/ui-kit/form/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import cn from 'classnames'
import { useField } from 'formik'
import { ForwardedRef } from 'react'

import {
BaseCheckbox,
Expand All @@ -15,29 +14,19 @@ interface CheckboxProps extends BaseCheckboxProps {
name: string
className?: string
hideFooter?: boolean
/**
* A forward ref to the input element.
*/
refForInput?: ForwardedRef<HTMLInputElement>
}

export const Checkbox = ({
name,
className,
hideFooter,
refForInput,
...props
}: CheckboxProps): JSX.Element => {
const [field, meta] = useField({ name, type: 'checkbox' })
const hasError = meta.touched && !!meta.error
return (
<div className={cn(styles['checkbox'], className)}>
<BaseCheckbox
ref={refForInput}
hasError={hasError}
{...field}
{...props}
/>
<BaseCheckbox hasError={hasError} {...field} {...props} />
{!hideFooter && (
<div className={styles['checkbox-error']}>
{hasError && <FieldError name={name}>{meta.error}</FieldError>}
Expand Down
15 changes: 7 additions & 8 deletions pro/src/ui-kit/form/CheckboxGroup/CheckboxGroup.module.scss
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
@use "styles/mixins/_rem.scss" as rem;

.checkbox-group {
&-item {
// RomainC: I don't understand why it doesn't exactly match the sketch
margin-bottom: rem.torem(12px);
width: max-content;
display: flex;
flex-direction: column;
gap: rem.torem(8px);
}

&:last-of-type {
margin-bottom: 0;
}
}
.inline {
flex-flow: row wrap;
gap: rem.torem(16px);
}
119 changes: 118 additions & 1 deletion pro/src/ui-kit/form/CheckboxGroup/CheckboxGroup.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
import { Formik } from 'formik'

import { CheckboxVariant } from '../shared/BaseCheckbox/BaseCheckbox'

import { CheckboxGroup } from './CheckboxGroup'

export default {
title: 'ui-kit/forms/CheckboxGroup',
component: CheckboxGroup,
decorators: [
(Story: any) => (
<Formik initialValues={{ accessibility: false }} onSubmit={() => {}}>
<Formik
initialValues={{
checkBoxes: {
foo: true,
bar: false,
baz: false,
'sub-foo-0': true,
'sub-bar-0': false,
'sub-foo-1': false,
'sub-bar-1': false,
'sub-foo-2': false,
'sub-bar-2': false,
},
}}
onSubmit={() => {}}
>
{({ getFieldProps }) => {
return <Story {...getFieldProps('group')} />
}}
Expand All @@ -24,5 +41,105 @@ export const Default = {
})),
groupName: 'checkBoxes',
legend: 'This is the legend',
disabled: false,
},
}

export const Box = {
args: {
...Default.args,
variant: CheckboxVariant.BOX,
},
}

export const BoxInline = {
args: {
...Default.args,
inline: true,
variant: CheckboxVariant.BOX,
},
}

export const BoxWithChildren = {
args: {
...Default.args,
group: ['foo', 'bar', 'baz'].map((item) => ({
label: item,
name: `checkBoxes.${item}`,
childrenOnChecked: <span>Child content for {item}</span>,
})),
variant: CheckboxVariant.BOX,
},
}

export const BoxWithCheckboxGroupChildren = {
args: {
...Default.args,
group: ['foo', 'bar', 'baz'].map((item, i) => ({
label: item,
name: `checkBoxes.${item}`,
childrenOnChecked: (
<CheckboxGroup
legend="Sub group legend"
groupName="sub-name"
group={[
{ label: 'sub-foo', name: `checkBoxes.sub-foo-${i}` },
{ label: 'sub-bar', name: `checkBoxes.sub-bar-${i}` },
]}
variant={CheckboxVariant.BOX}
/>
),
})),
variant: CheckboxVariant.BOX,
},
}

export const BoxWithCheckboxGroupChildrenNoLegend = {
args: {
...Default.args,
group: ['foo', 'bar', 'baz'].map((item, i) => ({
label: <span>{item}</span>,
name: `checkBoxes.${item}`,
childrenOnChecked: (
<CheckboxGroup
describedBy="parent-name-id"
groupName="sub-name"
group={[
{ label: 'sub-foo', name: `checkBoxes.sub-foo-${i}` },
{ label: 'sub-bar', name: `checkBoxes.sub-bar-${i}` },
]}
variant={CheckboxVariant.BOX}
inline={i === 0}
/>
),
})),
variant: CheckboxVariant.BOX,
},
}

export const BoxWithCheckboxGroupChildrenDisabled = {
args: {
...Default.args,
disabled: true,
group: ['foo', 'bar', 'baz'].map((item, i) => ({
label: <span>{item}</span>,
name: `checkBoxes.${item}`,
childrenOnChecked: (
<CheckboxGroup
describedBy="parent-name-id"
groupName="sub-name"
group={[
{ label: 'sub-foo', name: `checkBoxes.sub-foo-${i}` },
{ label: 'sub-bar', name: `checkBoxes.sub-bar-${i}` },
]}
variant={CheckboxVariant.BOX}
inline={i === 0}
disabled={true}
/>
),
})),
variant: CheckboxVariant.BOX,
groupName: 'checkBoxes',
legend: 'This is the legend',
},
}
84 changes: 51 additions & 33 deletions pro/src/ui-kit/form/CheckboxGroup/CheckboxGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,79 @@
import cn from 'classnames'
import classNames from 'classnames'
import { useField } from 'formik'

import { RequireAtLeastOne } from '../RadioGroup/RadioGroup'
import { CheckboxVariant } from '../shared/BaseCheckbox/BaseCheckbox'
import { FieldSetLayout } from '../shared/FieldSetLayout/FieldSetLayout'

import styles from './CheckboxGroup.module.scss'
import { CheckboxGroupItem } from './CheckboxGroupItem'

interface CheckboxGroupProps {
groupName: string
legend: string
group: {
name: string
label: string
description?: string
icon?: string
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
}[]
className?: string
disabled?: boolean
isOptional?: boolean
}
type CheckboxGroupProps = RequireAtLeastOne<
{
groupName: string
legend?: string
describedBy?: string
group: {
name: string
label: string
icon?: string
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
childrenOnChecked?: JSX.Element
}[]
disabled?: boolean
isOptional?: boolean
variant?: CheckboxVariant
inline?: boolean
},
'legend' | 'describedBy'
>

export const CheckboxGroup = ({
group,
groupName,
legend,
className,
describedBy,
disabled,
isOptional,
variant,
inline = false,
}: CheckboxGroupProps): JSX.Element => {
const [, meta, helpers] = useField({ name: groupName })
const hasError = meta.touched && !!meta.error

return (
<FieldSetLayout
className={cn(styles['checkbox-group'], className)}
error={hasError ? meta.error : undefined}
legend={legend}
name={groupName}
ariaDescribedBy={describedBy}
isOptional={isOptional}
hideFooter
>
{group.map((item) => (
<div className={styles['checkbox-group-item']} key={item.name}>
<CheckboxGroupItem
icon={item.icon}
hasError={hasError}
label={item.label}
name={item.name}
setGroupTouched={() =>
!meta.touched ? helpers.setTouched(true) : null
}
disabled={disabled}
onChange={item.onChange}
{...(hasError ? { ariaDescribedBy: `error-${groupName}` } : {})}
/>
</div>
))}
<div
className={classNames(styles['checkbox-group'], {
[styles['inline']]: inline,
})}
>
{group.map((item) => (
<div className={styles['checkbox-group-item']} key={item.name}>
<CheckboxGroupItem
icon={item.icon}
hasError={hasError}
label={item.label}
name={item.name}
setGroupTouched={() =>
!meta.touched ? helpers.setTouched(true) : null
}
disabled={disabled}
onChange={item.onChange}
{...(hasError ? { ariaDescribedBy: `error-${groupName}` } : {})}
variant={variant}
childrenOnChecked={item.childrenOnChecked}
/>
</div>
))}
</div>
</FieldSetLayout>
)
}
Loading
Loading