diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e406801..2e8ddb27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ This log documents significant changes for the [@aehrc/smart-forms-renderer](htt Changelog only includes changes from version 0.36.0 onwards. +## [0.44.1] - 2024-10-14 +### Fixed +- Fixed unexpected behaviour of open-choice's open label field clearing the answers of previously selected options. +- Fixed an issue where enableWhen logic was not working properly with repeating items. + ## [0.44.0] - 2024-10-09 ### Added - Added support for the [preferredTerminologyServer](https://hl7.org/fhir/uv/sdc/STU3/StructureDefinition-sdc-questionnaire-preferredTerminologyServer.html) SDC extension. diff --git a/apps/demo-renderer-app/package-lock.json b/apps/demo-renderer-app/package-lock.json index 1bb4b6db..4c0aa8b6 100644 --- a/apps/demo-renderer-app/package-lock.json +++ b/apps/demo-renderer-app/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@aehrc/sdc-populate": "^2.3.1", - "@aehrc/smart-forms-renderer": "^0.44.0", + "@aehrc/smart-forms-renderer": "^0.44.1", "@radix-ui/react-collapsible": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.1.0", @@ -56,9 +56,9 @@ } }, "node_modules/@aehrc/smart-forms-renderer": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@aehrc/smart-forms-renderer/-/smart-forms-renderer-0.44.0.tgz", - "integrity": "sha512-A80bYDLDBovDiXarvH3dlzxlzd2stiFEyBHrzF7zvlVdqIU/yS10zllMhJQsrxQ3/JPNk3j/5/C7h5MvHz8noA==", + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@aehrc/smart-forms-renderer/-/smart-forms-renderer-0.44.1.tgz", + "integrity": "sha512-ytRzHlOm6WVGn6sIP/zWwmNTDUPQDpAfE7fB0NFzApaDXOJkPHVfQAbLldmp3fffEi8l5wuRPkCVXv/jMF0jPQ==", "dependencies": { "@aehrc/sdc-populate": "^2.3.1", "@iconify/react": "^4.1.1", @@ -71,6 +71,7 @@ "lodash.clonedeep": "^4.5.0", "lodash.debounce": "^4.0.8", "lodash.difference": "^4.5.0", + "lodash.differencewith": "^4.5.0", "lodash.intersection": "^4.4.0", "lodash.isequal": "^4.5.0", "nanoid": "^5.0.7", @@ -15787,6 +15788,11 @@ "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" }, + "node_modules/lodash.differencewith": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.differencewith/-/lodash.differencewith-4.5.0.tgz", + "integrity": "sha512-/8JFjydAS+4bQuo3CpLMBv7WxGFyk7/etOAsrQUCu0a9QVDemxv0YQ0rFyeZvqlUD314SERfNlgnlqqHmaQ0Cg==" + }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", diff --git a/apps/demo-renderer-app/package.json b/apps/demo-renderer-app/package.json index 61deb342..325fe317 100644 --- a/apps/demo-renderer-app/package.json +++ b/apps/demo-renderer-app/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@aehrc/sdc-populate": "^2.3.1", - "@aehrc/smart-forms-renderer": "^0.44.0", + "@aehrc/smart-forms-renderer": "^0.44.1", "@radix-ui/react-collapsible": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.1.0", diff --git a/apps/smart-forms-app/e2e/items.spec.ts b/apps/smart-forms-app/e2e/items.spec.ts index c65044b5..c1286bd7 100644 --- a/apps/smart-forms-app/e2e/items.spec.ts +++ b/apps/smart-forms-app/e2e/items.spec.ts @@ -18,6 +18,17 @@ import { expect, test } from '@playwright/test'; import { PLAYWRIGHT_APP_URL, PLAYWRIGHT_FORMS_SERVER_URL } from './globals'; +test.beforeEach(async ({ page }) => { + // Go to playground + const fetchQPromise = page.waitForResponse( + `${PLAYWRIGHT_FORMS_SERVER_URL}/Questionnaire?_count=100&_sort=-date&` + ); + const launchUrl = `${PLAYWRIGHT_APP_URL}/playground`; + await page.goto(launchUrl); + const fetchQResponse = await fetchQPromise; + expect(fetchQResponse.status()).toBe(200); +}); + const stringInput = 'Test string input'; const textInput = 'Test text input'; const dateInput = '25/12/2023'; @@ -33,15 +44,6 @@ const choiceAnswerOptionInput = 'option A'; const choiceAnswerValueSetInput = 'Tasmania'; test('enter inputs into BitOfEverything questionnaire', async ({ page }) => { - // Go to playground - const fetchQPromise = page.waitForResponse( - `${PLAYWRIGHT_FORMS_SERVER_URL}/Questionnaire?_count=100&_sort=-date&` - ); - const launchUrl = `${PLAYWRIGHT_APP_URL}/playground`; - await page.goto(launchUrl); - const fetchQResponse = await fetchQPromise; - expect(fetchQResponse.status()).toBe(200); - // Select BitOfEverything questionnaire await page .getByTestId('questionnaire-picker-playground') @@ -177,3 +179,149 @@ test('enter inputs into BitOfEverything questionnaire', async ({ page }) => { expect(debugViewerText.includes(`"code": "6"`)).toBeTruthy(); expect(debugViewerText.includes(`"display": "${choiceAnswerValueSetInput}"`)).toBeTruthy(); }); + +test('enter inputs into OpenChoiceCheckboxAVS questionnaire', async ({ page }) => { + // Select OpenChoiceCheckboxAVS questionnaire + await page + .getByTestId('questionnaire-picker-playground') + .locator('input') + .fill('OpenChoiceCheckboxAVS'); + await page.keyboard.press('Enter'); + await expect(page.getByTestId('questionnaire-details-playground')).toContainText( + 'Open Choice Checkbox - Answer Value Set' + ); + await expect(page.getByTestId('questionnaire-details-playground')).toContainText( + 'https://smartforms.csiro.au/docs/advanced/control/itemcontrol/question/open-choice-checkbox-avs' + ); + + // Build OpenChoiceCheckboxAVS questionnaire + await page.getByTestId('picker-build-form-button-playground').click(); + await expect(page.getByText('"resourceType": "Questionnaire"')).toBeInViewport(); + await expect(page.getByText('"id": "OpenChoiceCheckboxAVS"')).toBeInViewport(); + + const openChoiceCheckboxLinkId = 'state-multi'; + + // Test multi-select checkbox + // Check on a defined option followed by the open label and see if it removes the initial option + // Check two checkboxes + await page + .locator( + `div[data-test="q-item-open-choice-checkbox-answer-value-set-box"][data-linkid="${openChoiceCheckboxLinkId}"]` + ) + .locator('label:has-text("Australian Capital Territory")') + .check(); + + await page + .locator( + `div[data-test="q-item-open-choice-checkbox-answer-value-set-box"][data-linkid="${openChoiceCheckboxLinkId}"]` + ) + .locator('label:has-text("Queensland")') + .check(); + + // Assert that both checkboxes are checked + const isACTChecked = await page + .locator( + `div[data-test="q-item-open-choice-checkbox-answer-value-set-box"][data-linkid="${openChoiceCheckboxLinkId}"]` + ) + .locator('label:has-text("Australian Capital Territory")') + .locator('input[type="checkbox"]') // Assuming the checkbox is the input element inside the label + .isChecked(); + + const isQueenslandChecked = await page + .locator( + `div[data-test="q-item-open-choice-checkbox-answer-value-set-box"][data-linkid="${openChoiceCheckboxLinkId}"]` + ) + .locator('label:has-text("Queensland")') + .locator('input[type="checkbox"]') + .isChecked(); + + expect(isACTChecked).toBe(true); + expect(isQueenslandChecked).toBe(true); + + // Check Open Label checkbox + await page + .locator( + `div[data-test="q-item-open-choice-checkbox-answer-value-set-box"][data-linkid="${openChoiceCheckboxLinkId}"]` + ) + .locator('label:has-text("Overseas state, please specify")') + .check(); + + // Assert that both checkboxes are checked once more, to verify the open label checkbox did not affect the other checkboxes + const isACTCheckedAgain = await page + .locator( + `div[data-test="q-item-open-choice-checkbox-answer-value-set-box"][data-linkid="${openChoiceCheckboxLinkId}"]` + ) + .locator('label:has-text("Australian Capital Territory")') + .locator('input[type="checkbox"]') + .isChecked(); + + const isQueenslandCheckedAgain = await page + .locator( + `div[data-test="q-item-open-choice-checkbox-answer-value-set-box"][data-linkid="${openChoiceCheckboxLinkId}"]` + ) + .locator('label:has-text("Queensland")') + .locator('input[type="checkbox"]') + .isChecked(); + + expect(isACTCheckedAgain).toBe(true); + expect(isQueenslandCheckedAgain).toBe(true); +}); + +test('enableWhen multi-checkbox (also for repeating items)', async ({ page }) => { + // Select EnableWhenMultiCheckbox questionnaire + await page + .getByTestId('questionnaire-picker-playground') + .locator('input') + .fill('EnableWhenMultiCheckbox'); + await page.keyboard.press('Enter'); + await expect(page.getByTestId('questionnaire-details-playground')).toContainText( + 'EnableWhen Multi-select Checkbox' + ); + await expect(page.getByTestId('questionnaire-details-playground')).toContainText( + 'https://smartforms.csiro.au/docs/behavior/other/enable-when-multi-checkbox' + ); + + // Build OpenChoiceCheckboxAVS questionnaire + await page.getByTestId('picker-build-form-button-playground').click(); + await expect(page.getByText('"resourceType": "Questionnaire"')).toBeInViewport(); + await expect(page.getByText('"id": "EnableWhenMultiCheckbox"')).toBeInViewport(); + + const openChoiceCheckboxLinkId = 'select-conditions-list'; + + // Check on a single option, then check for displayed enableWhen display question + await page + .locator( + `div[data-test="q-item-open-choice-checkbox-answer-option-box"][data-linkid="${openChoiceCheckboxLinkId}"]` + ) + .locator('label:has-text("Condition A (Displays Clinical guidance: Condition A question)")') + .check(); + + await expect(page.getByTestId('q-item-display-box')).toContainText( + 'Clinical guidance: Condition A' + ); + + // Check on options A. B. C, then check for all displayed enableWhen display questions + await page + .locator( + `div[data-test="q-item-open-choice-checkbox-answer-option-box"][data-linkid="${openChoiceCheckboxLinkId}"]` + ) + .locator('label:has-text("Condition B (Displays Clinical guidance: Condition B question)")') + .check(); + + await page + .locator( + `div[data-test="q-item-open-choice-checkbox-answer-option-box"][data-linkid="${openChoiceCheckboxLinkId}"]` + ) + .locator('label:has-text("Condition C (Displays Clinical guidance: Condition C question)")') + .check(); + + await expect(page.getByTestId('q-item-display-box').first()).toContainText( + 'Clinical guidance: Condition A' + ); + await expect(page.getByTestId('q-item-display-box').nth(1)).toContainText( + 'Clinical guidance: Condition B' + ); + await expect(page.getByTestId('q-item-display-box').nth(2)).toContainText( + 'Clinical guidance: Condition C' + ); +}); diff --git a/apps/smart-forms-app/package.json b/apps/smart-forms-app/package.json index 7d1a13c6..bd5bcc6a 100644 --- a/apps/smart-forms-app/package.json +++ b/apps/smart-forms-app/package.json @@ -28,7 +28,7 @@ "dependencies": { "@aehrc/sdc-assemble": "^1.3.1", "@aehrc/sdc-populate": "^2.3.1", - "@aehrc/smart-forms-renderer": "^0.44.0", + "@aehrc/smart-forms-renderer": "^0.44.1", "@emotion/react": "^11.13.0", "@emotion/styled": "^11.13.0", "@fontsource/material-icons": "^5.0.18", diff --git a/documentation/package-lock.json b/documentation/package-lock.json index 0804189f..174393bc 100644 --- a/documentation/package-lock.json +++ b/documentation/package-lock.json @@ -8,7 +8,7 @@ "name": "@aehrc/smart-forms-documentation", "version": "0.0.0", "dependencies": { - "@aehrc/smart-forms-renderer": "^0.44.0", + "@aehrc/smart-forms-renderer": "^0.44.1", "@docusaurus/core": "^3.4.0", "@docusaurus/preset-classic": "^3.4.0", "@docusaurus/theme-live-codeblock": "^3.4.0", diff --git a/documentation/package.json b/documentation/package.json index 28870fa0..7f6c60f1 100644 --- a/documentation/package.json +++ b/documentation/package.json @@ -15,7 +15,7 @@ "typecheck": "tsc" }, "dependencies": { - "@aehrc/smart-forms-renderer": "^0.44.0", + "@aehrc/smart-forms-renderer": "^0.44.1", "@docusaurus/core": "^3.4.0", "@docusaurus/preset-classic": "^3.4.0", "@docusaurus/theme-live-codeblock": "^3.4.0", diff --git a/package-lock.json b/package-lock.json index 5f1fee03..07923fe1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11114,6 +11114,15 @@ "@types/lodash": "*" } }, + "node_modules/@types/lodash.differencewith": { + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/@types/lodash.differencewith/-/lodash.differencewith-4.5.9.tgz", + "integrity": "sha512-nMaREKoe7J3WvnsO7HDRxvnPT3mWmZD3EAECpy7gBGJ6S5nQ66uVlkRe+ZXs6261ZNb2fH9Ny4oUUiSOCmTnLw==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/lodash.intersection": { "version": "4.4.9", "resolved": "https://registry.npmjs.org/@types/lodash.intersection/-/lodash.intersection-4.4.9.tgz", @@ -22208,6 +22217,11 @@ "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" }, + "node_modules/lodash.differencewith": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.differencewith/-/lodash.differencewith-4.5.0.tgz", + "integrity": "sha512-/8JFjydAS+4bQuo3CpLMBv7WxGFyk7/etOAsrQUCu0a9QVDemxv0YQ0rFyeZvqlUD314SERfNlgnlqqHmaQ0Cg==" + }, "node_modules/lodash.flow": { "version": "3.5.0", "license": "MIT" @@ -39546,6 +39560,7 @@ "lodash.clonedeep": "^4.5.0", "lodash.debounce": "^4.0.8", "lodash.difference": "^4.5.0", + "lodash.differencewith": "^4.5.0", "lodash.intersection": "^4.4.0", "lodash.isequal": "^4.5.0", "nanoid": "^5.0.7", @@ -39579,6 +39594,7 @@ "@types/lodash.clonedeep": "^4.5.9", "@types/lodash.debounce": "^4.0.9", "@types/lodash.difference": "^4.5.9", + "@types/lodash.differencewith": "^4.5.9", "@types/lodash.intersection": "^4.4.9", "@types/lodash.isequal": "^4.5.8", "@types/react": ">=17.0.0", diff --git a/packages/smart-forms-renderer/package.json b/packages/smart-forms-renderer/package.json index 06980e77..abb48957 100644 --- a/packages/smart-forms-renderer/package.json +++ b/packages/smart-forms-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@aehrc/smart-forms-renderer", - "version": "0.44.0", + "version": "0.44.1", "description": "FHIR Structured Data Captured (SDC) rendering engine for Smart Forms", "main": "lib/index.js", "scripts": { @@ -38,6 +38,7 @@ "lodash.clonedeep": "^4.5.0", "lodash.debounce": "^4.0.8", "lodash.difference": "^4.5.0", + "lodash.differencewith": "^4.5.0", "lodash.intersection": "^4.4.0", "lodash.isequal": "^4.5.0", "nanoid": "^5.0.7", @@ -82,6 +83,7 @@ "@types/lodash.clonedeep": "^4.5.9", "@types/lodash.debounce": "^4.0.9", "@types/lodash.difference": "^4.5.9", + "@types/lodash.differencewith": "^4.5.9", "@types/lodash.intersection": "^4.4.9", "@types/lodash.isequal": "^4.5.8", "@types/react": ">=17.0.0", diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.tsx index b39c728e..5ed32aa8 100644 --- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.tsx +++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.tsx @@ -166,7 +166,7 @@ function OpenChoiceCheckboxAnswerValueSetItem(props: OpenChoiceCheckboxAnswerVal return ( onFocusLinkId(qItem.linkId)}> diff --git a/packages/smart-forms-renderer/src/stories/assets/questionnaires/QBehaviorOther.ts b/packages/smart-forms-renderer/src/stories/assets/questionnaires/QBehaviorOther.ts index dd710b97..d772ad02 100644 --- a/packages/smart-forms-renderer/src/stories/assets/questionnaires/QBehaviorOther.ts +++ b/packages/smart-forms-renderer/src/stories/assets/questionnaires/QBehaviorOther.ts @@ -315,6 +315,98 @@ export const qEnableWhen: Questionnaire = { ] }; +export const qEnableWhenMultiCheckbox: Questionnaire = { + resourceType: 'Questionnaire', + id: 'EnableWhenMultiCheckbox', + name: 'EnableWhenMultiCheckbox', + title: 'EnableWhen Multi-select Checkbox', + version: '0.1.0', + status: 'draft', + date: '2024-05-01', + url: 'https://smartforms.csiro.au/docs/behavior/other/enable-when-multi-checkbox', + item: [ + { + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'check-box' + } + ] + } + }, + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-openLabel', + valueString: 'Other, please specify' + } + ], + linkId: 'select-conditions-list', + text: 'Select one or more conditions', + type: 'open-choice', + repeats: true, + answerOption: [ + { + valueString: 'Condition A (Displays Clinical guidance: Condition A question)' + }, + { + valueString: 'Condition B (Displays Clinical guidance: Condition B question)' + }, + { + valueString: 'Condition C (Displays Clinical guidance: Condition C question)' + }, + { + valueString: 'Condition D' + }, + { + valueString: 'Condition E' + }, + { + valueString: 'Condition F' + } + ] + }, + { + linkId: 'clinical-guidance-a', + text: 'Clinical guidance: Condition A', + type: 'display', + enableWhen: [ + { + question: 'select-conditions-list', + operator: '=', + answerString: 'Condition A (Displays Clinical guidance: Condition A question)' + } + ] + }, + { + linkId: 'clinical-guidance-b', + text: 'Clinical guidance: Condition B', + type: 'display', + enableWhen: [ + { + question: 'select-conditions-list', + operator: '=', + answerString: 'Condition B (Displays Clinical guidance: Condition B question)' + } + ] + }, + { + linkId: 'clinical-guidance-c', + text: 'Clinical guidance: Condition C', + type: 'display', + enableWhen: [ + { + question: 'select-conditions-list', + operator: '=', + answerString: 'Condition C (Displays Clinical guidance: Condition C question)' + } + ] + } + ] +}; + export const qEnableBehaviorAll: Questionnaire = { resourceType: 'Questionnaire', id: 'EnableBehaviorAll', diff --git a/packages/smart-forms-renderer/src/stories/sdc/BehaviorOther.stories.tsx b/packages/smart-forms-renderer/src/stories/sdc/BehaviorOther.stories.tsx index 339e19d7..bf040d82 100644 --- a/packages/smart-forms-renderer/src/stories/sdc/BehaviorOther.stories.tsx +++ b/packages/smart-forms-renderer/src/stories/sdc/BehaviorOther.stories.tsx @@ -23,6 +23,7 @@ import { qEnableWhen, qEnableWhenExpressionSimple, qEnableWhenExpressionTabs, + qEnableWhenMultiCheckbox, qInitialRepeats, qInitialSingle, qText @@ -59,6 +60,12 @@ export const EnableWhen: Story = { } }; +export const EnableWhenMultiCheckbox: Story = { + args: { + questionnaire: qEnableWhenMultiCheckbox + } +}; + export const EnableBehaviorAll: Story = { args: { questionnaire: qEnableBehaviorAll diff --git a/packages/smart-forms-renderer/src/utils/enableWhen.ts b/packages/smart-forms-renderer/src/utils/enableWhen.ts index 5c2e30d4..b5bb7793 100644 --- a/packages/smart-forms-renderer/src/utils/enableWhen.ts +++ b/packages/smart-forms-renderer/src/utils/enableWhen.ts @@ -339,19 +339,29 @@ export function checkItemIsEnabledSingle( ): boolean { const checkedIsEnabledItems: boolean[] = []; + // Check if linked item satisfies enableWhen condition for (const linkedItem of enableWhenItemProperties.linked) { + let isEnabledForThisLinkedItem = false; + + // Linked item has answers if (linkedItem.answer && linkedItem.answer.length > 0) { + // Check if linked answer within item satisfies enableWhen condition + // Exit early once a linked answer is found to satisfy the condition for (const answer of linkedItem.answer) { - const isEnabledForThisLinkedItem = isEnabledAnswerTypeSwitcher( + const isEnabledForThisLinkedAnswer = isEnabledAnswerTypeSwitcher( linkedItem.enableWhen, answer ); - // In a repeat item, if at least one answer satisfies the condition, the item is enabled - // FIXME need to look further at this - checkedIsEnabledItems.push(isEnabledForThisLinkedItem); - break; + if (isEnabledForThisLinkedAnswer) { + isEnabledForThisLinkedItem = true; + break; + } } + + // Push result of the linked item to the checkedIsEnabledItems array + checkedIsEnabledItems.push(isEnabledForThisLinkedItem); + continue; } diff --git a/packages/smart-forms-renderer/src/utils/openChoice.ts b/packages/smart-forms-renderer/src/utils/openChoice.ts index d27df003..21e4f824 100644 --- a/packages/smart-forms-renderer/src/utils/openChoice.ts +++ b/packages/smart-forms-renderer/src/utils/openChoice.ts @@ -24,6 +24,7 @@ import type { import { OpenChoiceItemControl } from '../interfaces/choice.enum'; import { isSpecificItemControl } from './itemControl'; import isEqual from 'lodash.isequal'; +import differenceWith from 'lodash.differencewith'; /** * Update open choice answer based on open label value @@ -121,10 +122,15 @@ export function getOldOpenLabelAnswer( answers: QuestionnaireResponseItemAnswer[], options: QuestionnaireItemAnswerOption[] ): QuestionnaireResponseItemAnswer | null { - const openLabelAnswer = answers.find( - (answer) => !options.some((option) => isEqual(option, answer)) - ); - return openLabelAnswer ?? null; + const answersWithoutId = answers.map((answer) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id, ...rest } = answer; + return rest as QuestionnaireResponseItemAnswer; + }); + + const outliers = differenceWith(answersWithoutId, options, isEqual); + + return outliers?.[0] ?? null; } /**