From 300fbc0038df28f53a9b653298931f71aa6f0bb5 Mon Sep 17 00:00:00 2001 From: Andreas Bauer Date: Fri, 23 Feb 2024 18:18:23 -0800 Subject: [PATCH] Fixed translation of required Form questions (#73) # Fixed translation of required Form questions ## :recycle: Current situation & Problem As reported in #71 the requiredness of groups is not correctly mapped to ResearchKit Forms. There are two mistakes here: 1. The `isOptional` property of the individual `ORKFormItem` is invested (this controls if the "Continue" button is rendered enabled or disabled) 2. The `isOptional` property of the `ORKFormStep` is never set (controls the appearance of the "Skip" button). ## :gear: Release Notes * Fixed an issue where you could skip required questions in a when rendered as a Form. ## :books: Documentation Added inline docs to explain the different semantics of the two `isOptional` properties. ## :white_check_mark: Testing Added UI tests to verify this behavior. Our existing `Form Example` FHIR questionnaire had a Form with 3 question out of which 2 are required. We ensure that the Skip button doesn't show up and the Next Button is only enabled once all the required question are answered. ### Code of Conduct & Contributing Guidelines By submitting creating this pull request, you agree to follow our [Code of Conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md): - [x] I agree to follow the [Code of Conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md). --- CITATION.cff | 3 +++ CONTRIBUTORS.md | 1 + Example/ExampleUITests/ExampleUITests.swift | 11 +++++++++++ .../QuestionnaireItem+ResearchKit.swift | 13 +++++++++++-- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index d2b1251..b368b4d 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -18,6 +18,9 @@ authors: - family-names: "Aalami" given-names: "Oliver" orcid: "https://orcid.org/0000-0002-7799-2429" +- family-names: "Bauer" + given-names: "Andreas" + orcid: "https://orcid.org/0000-0002-1680-237X" title: "ResearchKitOnFHIR" doi: 10.5281/zenodo.7538169 url: "https://github.com/StanfordBDHG/ResearchKitOnFHIR" diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index f6ad93f..2100e8c 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -15,3 +15,4 @@ ResearchKitOnFHIR contributors * [Vishnu Ravi](https://github.com/vishnuravi) * [Paul Schmiedmayer](https://github.com/PSchmiedmayer) +* [Andreas Bauer](https://github.com/Supereg) diff --git a/Example/ExampleUITests/ExampleUITests.swift b/Example/ExampleUITests/ExampleUITests.swift index 6e0946f..6cf75de 100644 --- a/Example/ExampleUITests/ExampleUITests.swift +++ b/Example/ExampleUITests/ExampleUITests.swift @@ -301,8 +301,19 @@ final class ExampleUITests: XCTestCase { app.buttons["Get Started"].tap() // Answer the three questions + app.tables.staticTexts["Yes"].tap() + + + // The first two questions are required ones, the last one is optional. + // Tests that the button is disabled just before we answer the last required question. + XCTAssert(app.buttons["Next"].exists) + XCTAssertFalse(app.buttons["Next"].isEnabled) + XCTAssertFalse(app.buttons["Skip"].exists) + app.tables.staticTexts["Chocolate"].tap() + XCTAssert(app.buttons["Next"].isEnabled) + app.tables.staticTexts["Sprinkles"].tap() app.tables.staticTexts["Marshmallows"].tap() app.buttons["Next"].tap() diff --git a/Sources/ResearchKitOnFHIR/FHIRToResearchKit/QuestionnaireItem+ResearchKit.swift b/Sources/ResearchKitOnFHIR/FHIRToResearchKit/QuestionnaireItem+ResearchKit.swift index a9ccff5..c29d8cb 100644 --- a/Sources/ResearchKitOnFHIR/FHIRToResearchKit/QuestionnaireItem+ResearchKit.swift +++ b/Sources/ResearchKitOnFHIR/FHIRToResearchKit/QuestionnaireItem+ResearchKit.swift @@ -92,7 +92,9 @@ extension QuestionnaireItem { formStep.title = title formStep.text = text?.value?.string ?? "" var formItems = [ORKFormItem]() - + + var containsRequiredSteps = false + for question in nestedQuestions { guard let questionId = question.linkId.value?.string, let questionText = question.text?.value?.string, @@ -102,13 +104,20 @@ extension QuestionnaireItem { let formItem = ORKFormItem(identifier: questionId, text: questionText, answerFormat: answerFormat) if let required = question.required?.value?.bool { - formItem.isOptional = required + // if !optional, the `Continue` will stay disabled till the question is answered. + formItem.isOptional = !required + + if required { + containsRequiredSteps = true + } } formItems.append(formItem) } formStep.formItems = formItems + // if optional, the `Next` button will appear + formStep.isOptional = !containsRequiredSteps return formStep }