diff --git a/src/components/form/fields/FieldSetField.tsx b/src/components/form/fields/FieldSetField.tsx index 453cc3ced..1ace0cdb8 100644 --- a/src/components/form/fields/FieldSetField.tsx +++ b/src/components/form/fields/FieldSetField.tsx @@ -248,7 +248,11 @@ export function FieldSetField({ }; } - if (field.isVisible === false || field.deprecated) { + if ( + field.isVisible === false || + field.deprecated || + field.hidden + ) { return null; // Skip hidden or deprecated fields } diff --git a/src/flows/Onboarding/api.ts b/src/flows/Onboarding/api.ts index 70353d843..12e8f1ec4 100644 --- a/src/flows/Onboarding/api.ts +++ b/src/flows/Onboarding/api.ts @@ -59,6 +59,7 @@ import { getContractDetailsSchemaVersion, getBasicInformationSchemaVersion, getBenefitOffersSchemaVersion, + withJobTitleEligibilityRiskAcknowledgement, } from '@/src/flows/Onboarding/utils'; // oxlint-disable-next-line no-restricted-imports -- TODO: move onto useHeadlessForm, see docs/USE_HEADLESS_FORM_ROLLOUT.md import { createHeadlessForm } from '@/src/common/createHeadlessForm'; @@ -317,7 +318,8 @@ export const useLegacyContractDetailsSchema = ({ return response; }, enabled: options?.queryOptions?.enabled, - select: ({ data }) => data?.data || {}, + select: ({ data }) => + withJobTitleEligibilityRiskAcknowledgement(data?.data || {}), }); const { form } = useHeadlessForm({ @@ -385,7 +387,8 @@ export const useContractDetailsSchema = ({ return response; }, enabled: options?.queryOptions?.enabled, - select: ({ data }) => data?.data, + select: ({ data }) => + data?.data && withJobTitleEligibilityRiskAcknowledgement(data.data), }); const { form, handleValidation, parseFormValues } = useHeadlessForm({ diff --git a/src/flows/Onboarding/tests/OnboardingFlowJobTitleEligibility.test.tsx b/src/flows/Onboarding/tests/OnboardingFlowJobTitleEligibility.test.tsx index 5deff9e16..8a4eb2a38 100644 --- a/src/flows/Onboarding/tests/OnboardingFlowJobTitleEligibility.test.tsx +++ b/src/flows/Onboarding/tests/OnboardingFlowJobTitleEligibility.test.tsx @@ -13,7 +13,10 @@ import { contractDetailsSchemaV1JobTitleEligibility, employmentDefaultResponse, } from '@/src/flows/Onboarding/tests/fixtures'; -import { OnboardingRenderProps } from '@/src/flows/Onboarding/types'; +import { + OnboardingFlowProps, + OnboardingRenderProps, +} from '@/src/flows/Onboarding/types'; import { fillRadio, queryClient, TestProviders } from '@/src/tests/testHelpers'; import { server } from '@/src/tests/server'; import { $TSFixMe } from '@/src/types/remoteFlows'; @@ -114,13 +117,17 @@ describe.each(schemaPaths)( vi.restoreAllMocks(); }); - const renderContractDetailsStep = async () => { + const renderContractDetailsStep = async ({ + features = ['job_title_eligibility'], + }: { + features?: NonNullable['features']; + } = {}) => { render( , { wrapper: TestProviders }, @@ -148,6 +155,17 @@ describe.each(schemaPaths)( await user.tab(); }; + it('keeps the risk acknowledgement hidden when the check is not enabled', async () => { + await renderContractDetailsStep({ features: [] }); + + await fillRoleFields(); + + expect(jobTitleEligibilityCheckSpy).not.toHaveBeenCalled(); + expect( + screen.queryByLabelText(/I acknowledge the risks/i), + ).not.toBeInTheDocument(); + }); + it('calls the job title eligibility check once the role fields are filled and blurred', async () => { await renderContractDetailsStep(); diff --git a/src/flows/Onboarding/tests/fixtures/contractDetails/v1-job-title-eligibility.ts b/src/flows/Onboarding/tests/fixtures/contractDetails/v1-job-title-eligibility.ts index 27a0d31e3..ef98742c5 100644 --- a/src/flows/Onboarding/tests/fixtures/contractDetails/v1-job-title-eligibility.ts +++ b/src/flows/Onboarding/tests/fixtures/contractDetails/v1-job-title-eligibility.ts @@ -1,8 +1,11 @@ /** - * Trimmed down contract details schema exposing only the fields the job title - * eligibility check cares about: the role fields sent to the check, the hidden - * slug/result fields it fills, and the risk acknowledgement that the - * yes_with_ack result reveals. Served as jsf v1 and as jsf v0. + * Trimmed down contract details schema exposing only the Role Requirements + * fieldset, as the gateway serves it to API partners for DEU, ITA and PRT: the + * role fields sent to the job title eligibility check, the hidden slug field, + * and the risk acknowledgement. The server-owned check result field and the + * yes_with_ack conditional are withheld from the served schema, so the SDK has + * to add them. role_description drops its minLength to keep the tests short. + * Served as jsf v1 and as jsf v0. */ const jobTitleEligibilitySchema = { additionalProperties: false, @@ -64,34 +67,28 @@ const jobTitleEligibilitySchema = { inputType: 'hidden', }, }, - additional_job_title_eligibility_check_result: { - type: ['string', 'null'], - 'x-jsf-presentation': { - inputType: 'hidden', - }, - }, }, - allOf: [ - { - if: { - properties: { - additional_job_title_eligibility_check_result: { - const: 'yes_with_ack', - }, - }, - required: ['additional_job_title_eligibility_check_result'], - }, - then: { - required: ['employer_acknowledges_risk'], - }, - else: { - properties: { - employer_acknowledges_risk: false, - }, - }, - }, - ], + required: ['role_description'], type: 'object', + 'x-jsf-order': [ + 'role_description', + 'role_is_onsite', + 'role_requires_license', + 'employer_acknowledges_risk', + 'additional_job_title_eligibility_check_slug', + ], + 'x-rmt-flatFieldsets': { + additional_job_title_eligibility_check: { + propertiesByName: [ + 'additional_job_title_eligibility_check_slug', + 'role_description', + 'role_is_onsite', + 'role_requires_license', + 'employer_acknowledges_risk', + ], + title: 'Role Requirements', + }, + }, }; export const contractDetailsSchemaV1JobTitleEligibility = { diff --git a/src/flows/Onboarding/utils.ts b/src/flows/Onboarding/utils.ts index 6f9be66f9..28d70c103 100644 --- a/src/flows/Onboarding/utils.ts +++ b/src/flows/Onboarding/utils.ts @@ -234,6 +234,61 @@ export const JOB_TITLE_ELIGIBILITY_SLUG_FIELD = export const JOB_TITLE_ELIGIBILITY_RESULT_FIELD = 'additional_job_title_eligibility_check_result'; +const EMPLOYER_ACKNOWLEDGES_RISK_FIELD = 'employer_acknowledges_risk'; + +type ContractDetailsJsonSchema = { + properties?: Record; + allOf?: unknown[]; + 'x-jsf-order'?: string[]; +}; + +export const withJobTitleEligibilityRiskAcknowledgement = < + T extends ContractDetailsJsonSchema, +>( + schema: T, +): T => { + const properties = schema.properties ?? {}; + if ( + !properties[JOB_TITLE_ELIGIBILITY_SLUG_FIELD] || + !properties[EMPLOYER_ACKNOWLEDGES_RISK_FIELD] || + properties[JOB_TITLE_ELIGIBILITY_RESULT_FIELD] + ) { + return schema; + } + + return { + ...schema, + properties: { + ...properties, + [JOB_TITLE_ELIGIBILITY_RESULT_FIELD]: { + type: ['string', 'null'], + 'x-jsf-presentation': { inputType: 'hidden' }, + }, + }, + allOf: [ + ...(schema.allOf ?? []), + { + if: { + properties: { + [JOB_TITLE_ELIGIBILITY_RESULT_FIELD]: { const: 'yes_with_ack' }, + }, + required: [JOB_TITLE_ELIGIBILITY_RESULT_FIELD], + }, + then: { required: [EMPLOYER_ACKNOWLEDGES_RISK_FIELD] }, + else: { properties: { [EMPLOYER_ACKNOWLEDGES_RISK_FIELD]: false } }, + }, + ], + ...(schema['x-jsf-order'] + ? { + 'x-jsf-order': [ + ...schema['x-jsf-order'], + JOB_TITLE_ELIGIBILITY_RESULT_FIELD, + ], + } + : {}), + }; +}; + const JOB_TITLE_ELIGIBILITY_PARAM_FIELDS = [ 'role_description', 'role_is_onsite',