diff --git a/src/ui/components/orchestrator/Orchestrator.tsx b/src/ui/components/orchestrator/Orchestrator.tsx index 30b12d72..f48e0309 100644 --- a/src/ui/components/orchestrator/Orchestrator.tsx +++ b/src/ui/components/orchestrator/Orchestrator.tsx @@ -233,6 +233,7 @@ const useStyles = tss.create(({ theme }) => ({ backgroundColor: theme.palette.background.default, paddingTop: '60px', flex: 1, + width: 'calc(100% - 60px)', }, mainContainer: { flex: 1, @@ -250,9 +251,9 @@ const useStyles = tss.create(({ theme }) => ({ height: '100%', }, '& > div:first-of-type > div': { - width: '80%', + maxWidth: 'calc(100% - 100px)', marginLeft: '100px', - marginTop: '3em', + marginTop: '4em', flexGrow: 1, }, }, @@ -263,7 +264,10 @@ const useStyles = tss.create(({ theme }) => ({ minHeight: '2.3em', }, navBarContainer: { - position: 'relative', + backgroundColor: theme.palette.background.default, + height: 'calc(100vh - 65px - 2em)', + right: '0', + position: 'fixed', justifyContent: 'flex-end', gap: '2em', paddingBottom: '2em', diff --git a/src/ui/components/orchestrator/lunaticStyle.tsx b/src/ui/components/orchestrator/lunaticStyle.tsx index ac2fa036..e1d3b697 100644 --- a/src/ui/components/orchestrator/lunaticStyle.tsx +++ b/src/ui/components/orchestrator/lunaticStyle.tsx @@ -33,6 +33,9 @@ export const useLunaticStyles = tss.create(({ theme }) => ({ padding: '0.2em 1em', }, }, + '&.Question .lunatic-table .lunatic-table-td > .field-container': { + marginTop: '1em', + }, '&.Loop': { display: 'block', @@ -110,42 +113,61 @@ export const useLunaticStyles = tss.create(({ theme }) => ({ }, // declarations - '& .declarations-lunatic': { - padding: '0.5em', - fontSize: '92%', - }, + '& .declarations-lunatic, &:is(.Sequence, .Subsequence) .label-description': + { + padding: '0.5em', + fontSize: '92%', + }, '& .declaration-lunatic': { marginBottom: '1em', + }, + '&:not(.Question) .declaration-lunatic': { '&.declaration-help': { color: theme.palette.declarations.help, }, - '&.declaration-instruction': { - color: theme.palette.declarations.instruction, - }, - '&.declaration-statement': { + '&.declaration-instruction, &.declaration-statement, &.declaration-codecard': + { + color: theme.palette.declarations.instruction, + }, + }, + '&.Question > .declarations-lunatic': { + '.declaration-help': { color: theme.palette.declarations.instruction, }, - '&.declaration-codecard': { - color: theme.palette.declarations.instruction, + }, + '&.Question > .field-container .declarations-lunatic': { + '.declaration-help': { + color: theme.palette.declarations.help, }, + '.declaration-instruction, .declaration-statement, .declaration-codecard': + { + color: theme.palette.declarations.instruction, + }, + }, + '&.Subsequence > .field-container > .field > .label-description': { + color: theme.palette.declarations.instruction, }, '& .label-top label': { fontWeight: 'bold', }, - '& .lunatic-component fieldset legend, fieldset legend': { - fontWeight: 'bold', - maxWidth: '90%', - color: 'initial', - backgroundColor: 'initial', - fontSize: 'initial', - marginBottom: '1.5em', - lineHeight: '1.3em', - }, - '& .lunatic-component .field-container, .field-container': { + '&:not(.Loop) .lunatic-component fieldset legend, &:not(.Question, .Loop) fieldset legend': + { + fontWeight: 'bold', + maxWidth: '90%', + color: 'initial', + backgroundColor: 'initial', + fontSize: 'initial', + marginBottom: '1.5em', + lineHeight: '1.3em', + }, + '&:not(.Question) .lunatic-component .field-container': { marginTop: '1em', }, + '&.Question .lunatic-component .field-container': { + marginTop: '0', + }, '& .lunatic-textarea textarea': { padding: '0.5em', fontSize: '100%', @@ -186,6 +208,14 @@ export const useLunaticStyles = tss.create(({ theme }) => ({ borderRadius: '15px', }, }, + '&.Question .lunatic-table fieldset:is(.lunatic-radio-group, .lunatic-checkbox-one) .code-modality': + { + borderRadius: '5px', + }, + '&.Question fieldset:is(.lunatic-radio-group, .lunatic-checkbox-one) .code-modality': + { + borderRadius: '15px', + }, '& .lunatic-component fieldset, fieldset': { padding: 0, @@ -193,7 +223,7 @@ export const useLunaticStyles = tss.create(({ theme }) => ({ border: 'none', // checkbox & radio - '& .lunatic-input-checkbox, & .lunatic-input-checkbox': { + '&:not(:has(.lunatic-checkbox-boolean)) .lunatic-input-checkbox': { borderRadius: '5px', border: `1px solid ${borderColorCheckbox}`, backgroundColor: `${backgroundColorCheckbox}`, @@ -287,11 +317,59 @@ export const useLunaticStyles = tss.create(({ theme }) => ({ padding: '0.5em', }, }, + '&:is(.Question, .Loop) .field-container > .field > fieldset > legend': { + '> h3': { + backgroundColor: 'transparent', + fontSize: '1em', + color: 'black', + display: 'block', + marginBottom: '1em', + fontWeight: 'bold', + padding: '0.5em', + marginTop: '0', + }, + '> p:not(:has(*))': { + display: 'none', + }, + }, + '&.Loop .field-container > .field > fieldset > legend': { + '> h3': { marginBottom: '0' }, + }, // missing response buttons css override // roll-back some changes when Missing override is available in lunatic-v2 // such as shortcut and checked selectors + '&.Question, &.Loop': { + '&:has(> .declarations-lunatic)': { + display: 'grid', + gridTemplateRows: 'auto 1fr', + gridRowGap: '1em', + }, + '.field-container, .field': { + height: '100%', + }, + '.field fieldset': { + height: '100%', + display: 'grid', + gridTemplateRows: 'auto auto 1fr', + legend: { + gridRow: '1', + '&:not(:has(*))': { + display: 'none', + }, + }, + '.declarations-lunatic': { + gridRow: '2', + }, + '.lunatic.lunatic-component': { + gridRow: '3', + display: 'grid', + gridTemplateRows: 'auto 1fr', + }, + }, + }, + '& .lunatic-component .missing-buttons, .missing-buttons': { display: 'flex', gap: '1em', @@ -512,5 +590,27 @@ export const useLunaticStyles = tss.create(({ theme }) => ({ }, }, }, + '&.Question .lunatic-combo-box-container:has(.lunatic-combo-box-fab)': { + display: 'grid', + gridTemplateColumns: '70% auto', + gridTemplateRows: 'auto auto', + columnGap: '0.5em', + '.lunatic-combo-box': { + gridColumn: 1, + '.lunatic-combo-box-content': { + width: 'unset', + }, + }, + '.lunatic-combo-box-fab': { + gridColumn: 2, + position: 'unset', + left: 'unset', + alignSelf: 'center', + }, + '.lunatic-errors': { + gridRow: 2, + gridColumn: 1 / 3, + }, + }, }, })) diff --git a/src/ui/components/orchestrator/tools/functions.test.ts b/src/ui/components/orchestrator/tools/functions.test.ts new file mode 100644 index 00000000..cf9bb4a5 --- /dev/null +++ b/src/ui/components/orchestrator/tools/functions.test.ts @@ -0,0 +1,71 @@ +import type { LunaticComponentProps } from '@inseefr/lunatic/components/type' +import { describe, expect, it, vi } from 'vitest' + +import { shouldAutoNext } from './functions' + +describe('Should auto next', () => { + it('returns false with non-radio and non-checkbox components', () => { + const components: LunaticComponentProps[] = [ + { + componentType: 'Text', + id: 'a', + value: 'v', + handleChanges: vi.fn(), + missingResponse: { name: 'a_MISSING' }, + }, + ] + const valueChange = [{ name: 'a', value: 'v2' }] + expect(shouldAutoNext(components, valueChange)).toBeFalsy() + }) + it('returns true with radio components', () => { + const components: LunaticComponentProps[] = [ + { + componentType: 'Radio', + id: 'a', + value: '', + options: [], + handleChanges: vi.fn(), + response: { name: 'a' }, + missingResponse: { name: 'a_MISSING' }, + }, + ] + const valueChange = [{ name: 'a', value: 'v2' }] + expect(shouldAutoNext(components, valueChange)).toBeTruthy() + }) + it('returns true with missing value', () => { + const components: LunaticComponentProps[] = [ + { + componentType: 'Radio', + id: 'a', + value: '', + options: [], + handleChanges: vi.fn(), + response: { name: 'a' }, + missingResponse: { name: 'a_MISSING' }, + }, + ] + const valueChange = [{ name: 'a_MISSING', value: 'DK' }] + expect(shouldAutoNext(components, valueChange)).toBeTruthy() + }) + it('returns true with a radio in a question component', () => { + const components: LunaticComponentProps[] = [ + { + componentType: 'Question', + id: 'q', + components: [ + { + componentType: 'Radio', + id: 'a', + value: '', + options: [], + handleChanges: vi.fn(), + response: { name: 'a' }, + missingResponse: { name: 'a_MISSING' }, + }, + ], + }, + ] + const valueChange = [{ name: 'a', value: 'v2' }] + expect(shouldAutoNext(components, valueChange)).toBeTruthy() + }) +}) diff --git a/src/ui/components/orchestrator/tools/functions.tsx b/src/ui/components/orchestrator/tools/functions.tsx index bae35f3e..9957c04c 100644 --- a/src/ui/components/orchestrator/tools/functions.tsx +++ b/src/ui/components/orchestrator/tools/functions.tsx @@ -40,12 +40,52 @@ function countMissingResponseInComponent(component: Component): number { /** * temporary : should be handle by Lunatic */ -export function countMissingResponseInPage(components: Components) { +function countMissingResponseInPage(components: Components) { return components.reduce((total, component) => { return total + countMissingResponseInComponent(component) }, 0) } +/** + * Whether or not we should go on next page on a value has changed. + * + * It is true if we have only one component that is a radio or checkbox, + * or if the response is a "don't know" / "refusal". + */ +export function shouldAutoNext( + components: Components, + valueChange: { + name: string + value: any + iteration?: number[] + }[], +): boolean { + const firstComponent = components[0] + // If it's a question we need to look at its components instead + if (firstComponent.componentType === 'Question') { + return shouldAutoNext(firstComponent.components, valueChange) + } + // at least one missing value has been selected + const hasMissingValue = valueChange.some( + (variable) => + variable.name.includes('_MISSING') && + ['DK', 'RF'].includes(variable.value), + ) + if ( + // There is only one "don't know / refusal" variable on the page + countMissingResponseInPage(components) === 1 && + // One of the values changed is a "don't know / refusal" response, or the current component is a radio or checkbox + (hasMissingValue || + (firstComponent.componentType && + ['Radio', 'CheckboxBoolean', 'CheckboxOne'].includes( + firstComponent.componentType, + ))) + ) { + return true + } + return false +} + // check if the first subPage of an iteration is before lastReachedPage export function isIterationReachable( currentPage: number, diff --git a/src/ui/components/orchestrator/tools/useAutoNext.ts b/src/ui/components/orchestrator/tools/useAutoNext.ts index 672c0b16..ac44fa21 100644 --- a/src/ui/components/orchestrator/tools/useAutoNext.ts +++ b/src/ui/components/orchestrator/tools/useAutoNext.ts @@ -2,7 +2,7 @@ import { useLunatic } from '@inseefr/lunatic' import { useCallback, useRef } from 'react' -import { countMissingResponseInPage } from './functions' +import { shouldAutoNext } from './functions' type PartialLunatic = Pick< ReturnType, @@ -26,23 +26,7 @@ export function useAutoNext() { if (ref.current === null) return const { getComponents, goNextPage } = ref.current const components = getComponents() - const firstComponent = components[0] - // at least one missing value has been selected - const hasMissingValue = valueChange.some( - (variable) => - variable.name.includes('_MISSING') && - ['DK', 'RF'].includes(variable.value), - ) - if ( - // There is only one "don't know / refusal" variable on the page - countMissingResponseInPage(components) === 1 && - // One of the values changed is a "don't know / refusal" response, or the current component is a radio or checkbox - (hasMissingValue || - (firstComponent.componentType && - ['Radio', 'CheckboxBoolean', 'CheckboxOne'].includes( - firstComponent.componentType, - ))) - ) { + if (shouldAutoNext(components, valueChange)) { goNextPage() } },