Skip to content

Commit

Permalink
feat: enable to always display detail value (#1187)
Browse files Browse the repository at this point in the history
  • Loading branch information
QRuhier authored Dec 10, 2024
1 parent f2355de commit 68ecffc
Show file tree
Hide file tree
Showing 24 changed files with 254 additions and 35 deletions.
4 changes: 3 additions & 1 deletion docs/docs/hook/parameters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ Toutes les options sont **optionnelles**.
- **missingShortcut** raccourcis clavier déclanchant les boutons missing (défaut `{ dontKnow: '', refused: '' }`)
- **dontKnowButton** label du bouton "Ne sait pas" (défaut `{ fr: 'Ne sais pas', en: "Don't know" }`)
- **refusedButton** label du bouton "Refus" (défaut `{ fr: 'Refus', en: 'Refused' }`)
- **trackChanges**, active le [suivi des changements](#trackChanges) (défaut `false`)
- **trackChanges**, active le [suivi des changements](#trackChanges) (défaut `false`),
- **componentsOptions** permet de définir des options sur le fonctionnement des composants. Toutes les options sont optionnelles (défaut objet contenant la valeur par défaut de chaque paramètre)
- **detailAlwaysDisplayed** permet d'afficher par défaut les modalités 'detail' associées aux modalités de réponse

### Vue d'ensemble {#vue-densemble}

Expand Down
2 changes: 1 addition & 1 deletion e2e/checkbox.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ test.describe('Checkboxes', () => {
});

test('Can select arbitrary value', async ({ page }) => {
await goToStory(page, 'components-checkboxgroup--arbitrary');
await goToStory(page, 'components-checkboxgroup--with-detail');
const selector = page.getByRole('checkbox', { name: 'Autre préciser' });
await expect(selector).toBeVisible();
await selector.click();
Expand Down
3 changes: 3 additions & 0 deletions src/components/CheckboxGroup/CheckboxGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { LunaticComponentProps } from '../type';
import { CustomCheckboxGroup } from './CustomCheckboxGroup';
import { getComponentErrors } from '../shared/ComponentErrors/ComponentErrors';
import { useLunaticComponentsOptions } from '../../use-lunatic/lunatic-context';

export function CheckboxGroup({
id,
Expand All @@ -14,6 +15,7 @@ export function CheckboxGroup({
declarations,
orientation,
}: LunaticComponentProps<'CheckboxGroup'>) {
const { detailAlwaysDisplayed } = useLunaticComponentsOptions();
return (
<CustomCheckboxGroup
id={id}
Expand All @@ -26,6 +28,7 @@ export function CheckboxGroup({
readOnly={readOnly}
declarations={declarations}
orientation={orientation}
detailAlwaysDisplayed={detailAlwaysDisplayed}
/>
);
}
17 changes: 3 additions & 14 deletions src/components/CheckboxGroup/CustomCheckboxGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { CheckboxOption } from '../shared/Checkbox/CheckboxOption';
import { getShortcutKey } from '../shared/Checkbox/getShortcutKey';
import type { LunaticComponentProps } from '../type';
import { Declarations } from '../shared/Declarations/Declarations';
import { CustomInput } from '../Input/Input';

type Props = Pick<
LunaticComponentProps<'CheckboxGroup'>,
Expand All @@ -19,6 +18,7 @@ type Props = Pick<
| 'disabled'
| 'options'
| 'orientation'
| 'detailAlwaysDisplayed'
> & {
errors?: LunaticError[];
};
Expand All @@ -35,6 +35,7 @@ export const CustomCheckboxGroup = slottableComponent<Props>(
readOnly,
declarations,
orientation,
detailAlwaysDisplayed,
}: Props) => {
return (
<Fieldset
Expand All @@ -59,20 +60,8 @@ export const CustomCheckboxGroup = slottableComponent<Props>(
codeModality={
shortcut ? getShortcutKey(index, options.length) : undefined
}
detailAlwaysDisplayed={detailAlwaysDisplayed}
/>
{option.onDetailChange && option.checked && (
<CustomInput
id="detailId"
label={option.detailLabel ?? 'Précisez :'}
value={
typeof option.detailValue === 'string'
? option.detailValue
: ''
}
onChange={option.onDetailChange}
disabled={disabled}
/>
)}
</div>
);
})}
Expand Down
3 changes: 3 additions & 0 deletions src/components/CheckboxOne/CheckboxOne.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { RadioGroup } from '../shared/Radio/RadioGroup';
import type { LunaticComponentProps } from '../type';
import { getComponentErrors } from '../shared/ComponentErrors/ComponentErrors';
import { useLunaticComponentsOptions } from '../../use-lunatic/lunatic-context';

/**
* Checkbox acting as a radio (only one option can be checked at a time)
Expand All @@ -18,6 +19,7 @@ export function CheckboxOne({
declarations,
orientation,
}: LunaticComponentProps<'CheckboxOne'>) {
const { detailAlwaysDisplayed } = useLunaticComponentsOptions();
return (
<RadioGroup
id={id}
Expand All @@ -33,6 +35,7 @@ export function CheckboxOne({
shortcut={shortcut}
declarations={declarations}
orientation={orientation ?? 'vertical'}
detailAlwaysDisplayed={detailAlwaysDisplayed}
clearable
/>
);
Expand Down
3 changes: 3 additions & 0 deletions src/components/Radio/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { LunaticComponentProps } from '../type';
import { getComponentErrors } from '../shared/ComponentErrors/ComponentErrors';
import { RadioGroup } from '../shared/Radio/RadioGroup';
import { slottableComponent } from '../shared/HOC/slottableComponent';
import { useLunaticComponentsOptions } from '../../use-lunatic/lunatic-context';

function LunaticRadio(props: LunaticComponentProps<'Radio'>) {
const {
Expand All @@ -19,6 +20,7 @@ function LunaticRadio(props: LunaticComponentProps<'Radio'>) {
declarations,
orientation,
} = props;
const { detailAlwaysDisplayed } = useLunaticComponentsOptions();
return (
<RadioGroup
id={id}
Expand All @@ -34,6 +36,7 @@ function LunaticRadio(props: LunaticComponentProps<'Radio'>) {
readOnly={readOnly}
declarations={declarations}
orientation={orientation ?? 'vertical'}
detailAlwaysDisplayed={detailAlwaysDisplayed}
/>
);
}
Expand Down
21 changes: 21 additions & 0 deletions src/components/shared/Checkbox/CheckboxOption.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ describe('CheckboxOption', () => {
id: 'test-checkbox',
onCheck: vi.fn(),
label: 'Test checkbox',
detailLabel: 'My detail',
onDetailChange: () => {},
};

it('renders the component correctly', () => {
Expand Down Expand Up @@ -55,4 +57,23 @@ describe('CheckboxOption', () => {
fireEvent.keyDown(checkbox, { code: 'Space' });
expect(defaultProps.onCheck).toHaveBeenCalledWith(true);
});

it('renders the detail when checked', () => {
const { getByText } = render(
<CheckboxOption {...defaultProps} checked={true} />
);
expect(getByText(defaultProps.detailLabel)).toBeInTheDocument();
});
it('does not render the detail when unchecked', () => {
const { queryByText } = render(
<CheckboxOption {...defaultProps} checked={false} />
);
expect(queryByText(defaultProps.detailLabel)).toBeNull();
});
it('renders the details when unchecked with the detail always displayed attribute', () => {
const { getByText } = render(
<CheckboxOption {...defaultProps} checked={false} detailAlwaysDisplayed />
);
expect(getByText(defaultProps.detailLabel)).toBeInTheDocument();
});
});
19 changes: 19 additions & 0 deletions src/components/shared/Checkbox/CheckboxOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { LunaticBaseProps } from '../../type';
import { slottableComponent } from '../HOC/slottableComponent';
import { Label } from '../Label/Label';
import { useKeyboardKey } from '../../../hooks/useKeyboardKey';
import { CustomInput } from '../../Input/Input';

export type CheckboxOptionProps = {
disabled?: boolean;
Expand All @@ -15,6 +16,10 @@ export type CheckboxOptionProps = {
codeModality?: string;
shortcut?: boolean;
invalid?: boolean;
detailAlwaysDisplayed?: boolean;
detailLabel?: ReactNode;
detailValue?: string | null;
onDetailChange?: (value: string) => void;
};

function LunaticCheckboxOption({
Expand All @@ -25,11 +30,16 @@ function LunaticCheckboxOption({
onCheck,
label,
description,
detailAlwaysDisplayed,
detailLabel,
detailValue,
onDetailChange,
codeModality,
shortcut,
invalid,
}: CheckboxOptionProps) {
const isEnabled = !readOnly && !disabled;
const hasDetail = !!onDetailChange;
const hasKeyboardShortcut = Boolean(shortcut && codeModality && isEnabled);
const onClickOption = () => {
if (isEnabled) {
Expand Down Expand Up @@ -88,6 +98,15 @@ function LunaticCheckboxOption({
)}{' '}
{label}
</Label>
{hasDetail && (checked || detailAlwaysDisplayed) && (
<CustomInput
id="detailId"
label={detailLabel ?? 'Précisez :'}
value={typeof detailValue === 'string' ? detailValue : ''}
onChange={onDetailChange}
disabled={disabled}
/>
)}
</div>
);
}
Expand Down
3 changes: 3 additions & 0 deletions src/components/shared/Radio/RadioGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export type RadioGroupProps = Pick<
| 'description'
| 'declarations'
| 'orientation'
| 'detailAlwaysDisplayed'
> & {
errors?: LunaticError[];
clearable?: boolean;
Expand All @@ -44,6 +45,7 @@ function LunaticRadioGroup({
readOnly,
declarations,
orientation,
detailAlwaysDisplayed,
}: RadioGroupProps) {
const onKeyDown = useListKeyboardHandler(options);
const maxIndex = options.length;
Expand All @@ -61,6 +63,7 @@ function LunaticRadioGroup({
id={radioId}
index={index}
checked={value === option.value}
detailAlwaysDisplayed={detailAlwaysDisplayed}
onKeyDown={onKeyDown}
checkboxStyle={checkboxStyle}
codeModality={shortcut ? codeModality : undefined}
Expand Down
36 changes: 36 additions & 0 deletions src/components/shared/Radio/RadioOption.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,40 @@ describe('RadioOption', () => {
fireEvent.keyDown(option, { key: 'Enter', code: 'Enter' });
expect(onKeyDownMock).toHaveBeenCalled();
});

it('renders the detail when checked', () => {
const { getByText } = render(
<RadioOption
id="radio-option"
label="Test Option"
onDetailChange={() => {}}
detailLabel="My detail"
checked
/>
);
expect(getByText('My detail')).toBeInTheDocument();
});
it('does not render the detail when unchecked', () => {
const { queryByText } = render(
<RadioOption
id="radio-option"
label="Test Option"
onDetailChange={() => {}}
detailLabel="My detail"
/>
);
expect(queryByText('My detail')).toBeNull();
});
it('renders the details when unchecked with the detail always displayed attribute', () => {
const { getByText } = render(
<RadioOption
id="radio-option"
label="Test Option"
onDetailChange={() => {}}
detailLabel="My detail"
detailAlwaysDisplayed
/>
);
expect(getByText('My detail')).toBeInTheDocument();
});
});
6 changes: 5 additions & 1 deletion src/components/shared/Radio/RadioOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type Props = {
labelledBy?: string;
codeModality?: string;
invalid?: boolean;
detailAlwaysDisplayed?: boolean;
} & InterpretedOption;

function LunaticRadioOption({
Expand All @@ -30,10 +31,12 @@ function LunaticRadioOption({
shortcut,
codeModality,
id,
invalid,
labelledBy,
description,
label,
onDetailChange,
detailAlwaysDisplayed,
detailLabel,
detailValue,
onCheck,
Expand Down Expand Up @@ -80,6 +83,7 @@ function LunaticRadioOption({
<div
id={id}
role="radio"
aria-invalid={invalid}
aria-disabled={disabled}
className={classnames(
'lunatic-input-checkbox',
Expand Down Expand Up @@ -113,7 +117,7 @@ function LunaticRadioOption({
{label}
</Label>
</div>
{hasDetail && checked && (
{hasDetail && (checked || detailAlwaysDisplayed) && (
<CustomInput
id="detailId"
label={detailLabel ?? 'Précisez :'}
Expand Down
3 changes: 3 additions & 0 deletions src/components/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,15 @@ export type ComponentPropsByType = {
detailLabel?: ReactNode;
}[];
orientation?: 'horizontal' | 'vertical';
detailAlwaysDisplayed?: boolean;
componentType?: 'CheckboxGroup';
};
CheckboxOne: LunaticBaseProps<string | null> &
LunaticExtraProps & {
options: Array<InterpretedOption>;
componentType?: 'CheckboxOne';
orientation?: 'horizontal' | 'vertical';
detailAlwaysDisplayed?: boolean;
};
Switch: LunaticBaseProps<boolean> &
LunaticExtraProps & {
Expand All @@ -217,6 +219,7 @@ export type ComponentPropsByType = {
response: { name: string };
componentType?: 'Radio';
orientation?: 'horizontal' | 'vertical';
detailAlwaysDisplayed?: boolean;
};
Roundabout: LunaticBaseProps<string> &
LunaticExtraProps & {
Expand Down
9 changes: 9 additions & 0 deletions src/stories/behaviour/missing/missing.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,20 @@ const stories = {
component: Orchestrator,
argTypes: {
...defaultArgTypes,
shortcut: {
table: { disable: false },
control: 'boolean',
defaultValue: true,
},
missing: {
table: { disable: false },
control: 'boolean',
defaultValue: true,
},
missingStrategy: {
table: { disable: false },
control: 'object',
},
activeGoNextForMissing: {
table: { disable: false },
control: 'boolean',
Expand Down
Loading

0 comments on commit 68ecffc

Please sign in to comment.