Skip to content

Commit

Permalink
fix: resolve checkbox issue to persist the checked state tckt-365
Browse files Browse the repository at this point in the history
  • Loading branch information
kalasgarov committed Dec 9, 2024
1 parent 491d00c commit 928f945
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 54 deletions.
56 changes: 20 additions & 36 deletions packages/design/src/Form/components/GenderId/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import classNames from 'classnames';
import { useFormContext } from 'react-hook-form';
import { useFormContext, useWatch } from 'react-hook-form';
import { type GenderIdProps } from '@atj/forms';

import { type PatternComponent } from '../../index.js';

const GenderIdPattern: PatternComponent<GenderIdProps> = ({
Expand All @@ -11,7 +10,7 @@ const GenderIdPattern: PatternComponent<GenderIdProps> = ({
label,
required,
error,
value,
value = '',
preferNotToAnswerText,
preferNotToAnswerChecked: initialPreferNotToAnswerChecked = false,
}) => {
Expand All @@ -22,25 +21,17 @@ const GenderIdPattern: PatternComponent<GenderIdProps> = ({

const errorId = `input-error-message-${genderId}`;
const hintId = `hint-${genderId}`;
const preferNotToAnswerId = `prefer-not-to-answer-${genderId}`;
const preferNotToAnswerId = `${genderId}.preferNotToAnswer`;
const inputId = `${genderId}.input`;

useEffect(() => {
if (preferNotToAnswerChecked) {
setValue(genderId, preferNotToAnswerText, { shouldValidate: true });
} else {
setValue(genderId, value, { shouldValidate: true });
}
}, [
preferNotToAnswerChecked,
setValue,
genderId,
preferNotToAnswerText,
value,
]);
const watchedValue = useWatch({ name: inputId, defaultValue: value });

const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setPreferNotToAnswerChecked(event.target.checked);
setValue(preferNotToAnswerId, event.target.checked);
const isChecked = event.target.checked;
setPreferNotToAnswerChecked(isChecked);
setValue(genderId, isChecked ? preferNotToAnswerText : value, {
shouldValidate: true,
});
};

return (
Expand All @@ -66,35 +57,28 @@ const GenderIdPattern: PatternComponent<GenderIdProps> = ({
</div>
)}
<input
className={classNames('usa-input', {
className={classNames('usa-input usa-input--xl', {
'usa-input--error': error,
})}
style={
preferNotToAnswerChecked
? {
backgroundColor: '#e9ecef',
pointerEvents: 'none',
opacity: 0.65,
}
: {}
}
id={genderId}
id={inputId}
type="text"
defaultValue={value}
{...register(genderId, { required })}
readOnly={preferNotToAnswerChecked}
disabled={preferNotToAnswerChecked}
defaultValue={preferNotToAnswerChecked ? '' : watchedValue}
{...register(inputId, { required })}
aria-describedby={
`${hint ? `${hintId}` : ''}${error ? ` ${errorId}` : ''}`.trim() ||
undefined
}
/>
{preferNotToAnswerText && (
<div className="usa-checkbox">
<div className="usa-checkbox usa-input--xl">
<input
className="usa-checkbox__input"
id={preferNotToAnswerId}
type="checkbox"
value="prefer-not-to-answer"
checked={preferNotToAnswerChecked}
defaultValue={preferNotToAnswerText}
defaultChecked={preferNotToAnswerChecked}
{...register(preferNotToAnswerId)}
onChange={handleCheckboxChange}
/>
Expand Down
20 changes: 14 additions & 6 deletions packages/forms/src/patterns/gender-id/gender-id.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
createGenderIdSchema,
genderIdConfig,
type GenderIdPattern,
type GenderIdPatternOutput,
} from './gender-id';

describe('GenderIdPattern tests', () => {
Expand All @@ -11,14 +12,19 @@ describe('GenderIdPattern tests', () => {
const data: GenderIdPattern['data'] = {
label: 'Test Gender Identity Label',
required: true,
preferNotToAnswerText: 'Prefer not to share my gender identity',
};

const schema = createGenderIdSchema(data);
const validInput = 'Test Gender';
const invalidInput = '';
const validInput = { input: 'Test Gender' };
const invalidInput = { input: '' };
const preferNotToAnswerInput = {
preferNotToAnswer: 'Prefer not to share my gender identity',
};

expect(schema.safeParse(validInput).success).toBe(true);
expect(schema.safeParse(invalidInput).success).toBe(false);
expect(schema.safeParse(preferNotToAnswerInput).success).toBe(true);
});

it('should create schema for optional gender identity input', () => {
Expand All @@ -28,8 +34,8 @@ describe('GenderIdPattern tests', () => {
};

const schema = createGenderIdSchema(data);
const validInput = 'Test Gender';
const emptyInput = '';
const validInput = { input: 'Test Gender' };
const emptyInput = { input: '' };

expect(schema.safeParse(validInput).success).toBe(true);
expect(schema.safeParse(emptyInput).success).toBe(true);
Expand All @@ -44,10 +50,11 @@ describe('GenderIdPattern tests', () => {
data: {
label: 'Test Gender Identity Label',
required: true,
preferNotToAnswerText: 'Prefer not to share my gender identity',
},
};

const inputValue = 'Test Gender';
const inputValue = { input: 'Test Gender' };
if (!genderIdConfig.parseUserInput) {
expect.fail('genderIdConfig.parseUserInput is undefined');
}
Expand All @@ -66,10 +73,11 @@ describe('GenderIdPattern tests', () => {
data: {
label: 'Test Gender Identity Label',
required: true,
preferNotToAnswerText: 'Prefer not to share my gender identity',
},
};

const inputValue = '';
const inputValue = { input: '' };
if (!genderIdConfig.parseUserInput) {
expect.fail('genderIdConfig.parseUserInput is undefined');
}
Expand Down
37 changes: 25 additions & 12 deletions packages/forms/src/patterns/gender-id/gender-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,26 @@ export type GenderIdPatternOutput = z.infer<
>;

export const createGenderIdSchema = (data: GenderIdPattern['data']) => {
return z.string().superRefine((value, ctx) => {
if (value === data.preferNotToAnswerText) {
return;
}
if (data.required && value.trim() === '') {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'This field is required',
});
}
});
return z
.object({
input: z.string().optional(),
preferNotToAnswer: z.string().optional(),
})
.superRefine((value, ctx) => {
const { input, preferNotToAnswer } = value;

if (
data.required &&
!input?.trim() &&
preferNotToAnswer !== data.preferNotToAnswerText
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'This field is required',
});
return;
}
});
};

export const genderIdConfig: PatternConfig<
Expand Down Expand Up @@ -65,6 +74,9 @@ export const genderIdConfig: PatternConfig<
createPrompt(_, session, pattern, options) {
const extraAttributes: Record<string, any> = {};
const sessionValue = getFormSessionValue(session, pattern.id);
const value = sessionValue?.input || '';
const preferNotToAnswerChecked =
sessionValue?.preferNotToAnswer === pattern.data.preferNotToAnswerText;
const error = session.data.errors[pattern.id];

return {
Expand All @@ -76,7 +88,8 @@ export const genderIdConfig: PatternConfig<
required: pattern.data.required,
hint: pattern.data.hint,
preferNotToAnswerText: pattern.data.preferNotToAnswerText,
value: sessionValue,
preferNotToAnswerChecked,
value,
error,
...extraAttributes,
} as GenderIdProps,
Expand Down

0 comments on commit 928f945

Please sign in to comment.