Skip to content

Commit 33a4d53

Browse files
committed
Merge branch 'LF-2884-lookup-answerOptions' into 'master'
LF-2884 - Added autocomplete lookup functionality to the answerOptions. See merge request lfor/formbuilder!127
2 parents d26a05b + 7420ec1 commit 33a4d53

19 files changed

Lines changed: 827 additions & 97 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
This project follows [Semantic Versioning](http://semver.org/).
44

5+
## [10.3.13] 2025-12-17
6+
### Added
7+
- Added autocomplete lookup functionality to the answerOptions.
8+
59
## [10.3.12] 2025-12-06
610
### Fixed
711
- Fixed issue with variable deletion not updating the list.

cypress/e2e/item-level/answer-list-source.cy.ts

Lines changed: 149 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ describe('Home page', () => {
107107
.should('contain.text', 'SNOMED ECL is not set. The lookup feature will not be available. Initial values can still be manually typed in.');
108108

109109
// An initial value can still be typed in manually.
110+
cy.get('[id^="initial.0.valueCoding.system"]').type('http://example.org');
110111
cy.get('[id^="initial.0.valueCoding.display"]').type('example');
111112
cy.get('[id^="initial.0.valueCoding.code"]').type('123');
112-
cy.get('[id^="initial.0.valueCoding.system"]').type('http://example.org');
113113

114114
// Enter the 'SNOMED answer value set' URI.
115115
cy.get('#answerValueSet_ecl').type("< 429019009 |Finding related to biological sex|");
@@ -183,9 +183,9 @@ describe('Home page', () => {
183183
.should('contain.text', 'The Answer value set URL is not set. The lookup feature will not be available. Initial values can still be manually typed in.');
184184

185185
// An initial value can still be typed in manually.
186+
cy.get('[id^="initial.0.valueCoding.system"]').type('http://example.org');
186187
cy.get('[id^="initial.0.valueCoding.display"]').type('example');
187188
cy.get('[id^="initial.0.valueCoding.code"]').type('123');
188-
cy.get('[id^="initial.0.valueCoding.system"]').type('http://example.org');
189189

190190
// Enter the 'Answer value set' URI.
191191
cy.get('#answerValueSet_non-snomed').type('http://clinicaltables.nlm.nih.gov/fhir/R4/ValueSet/conditions');
@@ -244,14 +244,14 @@ describe('Home page', () => {
244244
cy.get('lfb-answer-option table > tbody > tr:nth-of-type(1)').as('firstOption');
245245
cy.get('lfb-answer-option table > tbody > tr:nth-of-type(2)').as('secondOption');
246246

247-
cy.get('@firstOption').find('td:nth-child(1) input').should('have.value', 'd1');
248-
cy.get('@firstOption').find('td:nth-child(2) input').should('have.value', 'a');
249-
cy.get('@firstOption').find('td:nth-child(3) input').should('have.value', 's');
247+
cy.get('@firstOption').find('td:nth-child(1) input').should('have.value', 's');
248+
cy.get('@firstOption').find('td:nth-child(2) input').should('have.value', 'd1');
249+
cy.get('@firstOption').find('td:nth-child(3) input').should('have.value', 'a');
250250
cy.get('@firstOption').find('td:nth-child(4) input').should('have.value', '1');
251251

252-
cy.get('@secondOption').find('td:nth-child(1) input').should('have.value', 'd2');
253-
cy.get('@secondOption').find('td:nth-child(2) input').should('have.value', 'b');
254-
cy.get('@secondOption').find('td:nth-child(3) input').should('have.value', 's');
252+
cy.get('@secondOption').find('td:nth-child(1) input').should('have.value', 's');
253+
cy.get('@secondOption').find('td:nth-child(2) input').should('have.value', 'd2');
254+
cy.get('@secondOption').find('td:nth-child(3) input').should('have.value', 'b');
255255
cy.get('@secondOption').find('td:nth-child(4) input').as('secondScore');
256256
cy.get('@secondScore').should('have.value', '2');
257257
cy.getPickInitialValueValueMethodClick();
@@ -266,9 +266,9 @@ describe('Home page', () => {
266266

267267
cy.get('lfb-answer-option table+button').click();
268268
cy.get('lfb-answer-option table > tbody > tr:nth-of-type(3)').as('thirdOption').should('be.visible');
269-
cy.get('@thirdOption').find('td:nth-child(1) input').type('d3');
270-
cy.get('@thirdOption').find('td:nth-child(2) input').type('c');
271-
cy.get('@thirdOption').find('td:nth-child(3) input').type('s');
269+
cy.get('@thirdOption').find('td:nth-child(1) input').type('s');
270+
cy.get('@thirdOption').find('td:nth-child(2) input').type('d3');
271+
cy.get('@thirdOption').find('td:nth-child(3) input').type('c');
272272
cy.get('@thirdOption').find('td:nth-child(4) input').type('33');
273273
cy.get('@pickAnswer').click();
274274
cy.get('#lhc-tools-searchResults ul > li').should('have.length', 3);
@@ -278,15 +278,15 @@ describe('Home page', () => {
278278
cy.questionnaireJSON().should((qJson) => {
279279
expect(qJson.item[0].answerOption).to.deep.equal([
280280
{
281-
valueCoding: {display: 'd1', code: 'a', system: 's'},
281+
valueCoding: {system: 's', display: 'd1', code: 'a'},
282282
extension: [{url: SCORE_URI, valueDecimal: 1}]
283283
},
284284
{
285-
valueCoding: {display: 'd2', code: 'b', system: 's'},
285+
valueCoding: {system: 's', display: 'd2', code: 'b'},
286286
extension: [{url: SCORE_URI, valueDecimal: 22}]
287287
},
288288
{
289-
valueCoding: {display: 'd3', code: 'c', system: 's'},
289+
valueCoding: {system: 's', display: 'd3', code: 'c'},
290290
extension: [{url: SCORE_URI, valueDecimal: 33}],
291291
initialSelected: true
292292
},
@@ -431,6 +431,139 @@ describe('Home page', () => {
431431
});
432432
});
433433

434+
435+
it('should display the answerOptions lookup', () => {
436+
const sampleFile = 'answer-option-lookup-sample.json';
437+
let fixtureJson;
438+
cy.readFile('cypress/fixtures/'+sampleFile).should((json) => {fixtureJson = json});
439+
cy.uploadFile(sampleFile, true);
440+
cy.getFormTitleField().should('have.value', 'Answer options lookup form');
441+
cy.contains('button', 'Edit questions').click();
442+
cy.get('.spinner-border').should('not.exist');
443+
444+
// The Data type should be 'coding'
445+
cy.getItemTypeField().should('contain.value', 'coding');
446+
// 'Create answer list' should be set to 'yes'
447+
cy.getRadioButton('Create answer list', 'Yes').should('be.checked');
448+
449+
cy.get('lfb-answer-option table > tbody > tr:nth-of-type(1)').as('firstOption');
450+
cy.get('lfb-answer-option table > tbody > tr:nth-of-type(2)').as('secondOption');
451+
cy.get('lfb-answer-option table > tbody > tr:nth-of-type(3)').as('thirdOption');
452+
453+
cy.get('@firstOption').find('td:nth-child(1) input').should('have.value', 'http://snomed.info/sct');
454+
cy.get('@firstOption').find('td:nth-child(2) input').should('have.value', 'Heart beat');
455+
cy.get('@firstOption').find('td:nth-child(3) input').should('have.value', '248646004');
456+
457+
cy.get('@secondOption').find('td:nth-child(1) input').should('have.value', 'http://loinc.org');
458+
cy.get('@secondOption').find('td:nth-child(2) input').should('have.value', 'Newborn hearing screening panel');
459+
cy.get('@secondOption').find('td:nth-child(3) input').should('have.value', '54111-0');
460+
461+
cy.get('@thirdOption').find('td:nth-child(1) input').should('have.value', 'http://example.com');
462+
cy.get('@thirdOption').find('td:nth-child(2) input').should('have.value', 'visceral fat');
463+
cy.get('@thirdOption').find('td:nth-child(3) input').should('have.value', 'vf');
464+
465+
// Pick initial value should be selected for the Value method section.
466+
cy.contains('div', 'Value method').as('valueMethod').should('be.visible');
467+
cy.get('@valueMethod').find('[id^="__$valueMethod_pick-initial"]').as('pickInitialRadio');
468+
cy.get('@pickInitialRadio').should('be.visible').and('be.checked');
469+
470+
cy.get('[id^="pick-answer_"]').as('pickAnswer');
471+
cy.get('@pickAnswer').should('have.value', 'Newborn hearing screening panel');
472+
473+
cy.questionnaireJSON().should((qJson) => {
474+
expect(qJson.item[0].answerOption).to.deep.equal(fixtureJson.item[0].answerOption);
475+
});
476+
});
477+
478+
it('should create answerOptions lookup', () => {
479+
cy.selectDataType('coding');
480+
cy.getRadioButtonLabel('Create answer list', 'Yes').click();
481+
482+
// Select 'http://loinc.org'
483+
cy.get('[id^="answerOption.0.valueCoding.system"]').click().type('{downarrow}{downarrow}{enter}');
484+
cy.get('[id^="answerOption.0.valueCoding.system"]').should('have.value', 'http://loinc.org');
485+
cy.get('[id^="answerOption.0.valueCoding.display"]').type('heart');
486+
487+
// Autocomplete should show options.
488+
cy.get('span#completionOptions > ul > li').should('have.length.greaterThan', 0);
489+
// Select 'Heart rate'
490+
cy.get('[id^="answerOption.0.valueCoding.display"]').type('{downarrow}{downarrow}{downarrow}{enter}');
491+
492+
// Verify that code and system are filled in.
493+
cy.get('[id^="answerOption.0.valueCoding.system"]').should('have.value', 'http://loinc.org');
494+
cy.get('[id^="answerOption.0.valueCoding.display"]').should('have.value', 'Heart rate');
495+
cy.get('[id^="answerOption.0.valueCoding.code"]').should('have.value', '18708-8');
496+
497+
// Add another answerOption value.
498+
cy.contains('button', 'Add another answer').as('addAnswerButton');
499+
cy.get('@addAnswerButton').click();
500+
501+
// Select 'http://unitsofmeasure.org'
502+
cy.get('[id^="answerOption.1.valueCoding.system"]').click().type('{downarrow}{downarrow}{downarrow}{enter}');
503+
cy.get('[id^="answerOption.1.valueCoding.system"]').should('have.value', 'http://unitsofmeasure.org');
504+
cy.get('[id^="answerOption.1.valueCoding.display"]').type('kat');
505+
506+
// Autocomplete should show options.
507+
cy.get('span#completionOptions > ul > li').should('have.length.greaterThan', 0);
508+
// Select 'katal'
509+
cy.get('[id^="answerOption.1.valueCoding.display"]').type('{downarrow}{enter}');
510+
511+
// Verify that code and system are filled in.
512+
cy.get('[id^="answerOption.1.valueCoding.system"]').should('have.value', 'http://unitsofmeasure.org');
513+
cy.get('[id^="answerOption.1.valueCoding.display"]').should('have.value', 'kat - katal');
514+
cy.get('[id^="answerOption.1.valueCoding.code"]').should('have.value', 'kat');
515+
516+
// Add another answerOption value.
517+
cy.contains('button', 'Add another answer').as('addAnswerButton');
518+
cy.get('@addAnswerButton').click();
519+
520+
// Type your own 'http://example.org'
521+
cy.get('[id^="answerOption.2.valueCoding.system"]').click().type('http://example.org{enter}');
522+
cy.get('[id^="answerOption.2.valueCoding.display"]').click().type('abcd123');
523+
cy.get('[id^="answerOption.2.valueCoding.code"]').click().type('123');
524+
525+
// Add another answerOption value.
526+
cy.contains('button', 'Add another answer').as('addAnswerButton');
527+
cy.get('@addAnswerButton').click();
528+
529+
// Select 'http://snomed.info/sct'
530+
cy.get('[id^="answerOption.3.valueCoding.system"]').click().type('{downarrow}{enter}');
531+
cy.get('[id^="answerOption.3.valueCoding.system"]').should('have.value', 'http://snomed.info/sct');
532+
533+
// Use mock data for the SNOMED ECL expression request.
534+
cy.intercept('https://snowstorm.ihtsdotools.org/fhir/ValueSet/**', { fixture: 'snomed-ecl-expression-mock.json' }).as('snomedReq');
535+
cy.get('[id^="answerOption.3.valueCoding.display"]').click().type('Intersex');
536+
cy.wait('@snomedReq');
537+
538+
// Autocomplete should show options.
539+
cy.get('span#completionOptions > ul > li').should('have.length.greaterThan', 0);
540+
cy.get('[id^="answerOption.3.valueCoding.display"]').type('{downarrow}{enter}');
541+
542+
// Verify that code and system are filled in.
543+
cy.get('[id^="answerOption.3.valueCoding.system"]').should('have.value', 'http://snomed.info/sct');
544+
cy.get('[id^="answerOption.3.valueCoding.display"]').should('have.value', 'Intersex');
545+
cy.get('[id^="answerOption.3.valueCoding.code"]').should('have.value', '32570691000036108');
546+
547+
cy.questionnaireJSON().should((qJson) => {
548+
expect(qJson.item[0].answerOption[0].valueCoding.system).to.equal('http://loinc.org');
549+
expect(qJson.item[0].answerOption[0].valueCoding.display).to.equal('Heart rate');
550+
expect(qJson.item[0].answerOption[0].valueCoding.code).to.equal('18708-8');
551+
552+
expect(qJson.item[0].answerOption[1].valueCoding.system).to.equal('http://unitsofmeasure.org');
553+
expect(qJson.item[0].answerOption[1].valueCoding.display).to.equal('kat - katal');
554+
expect(qJson.item[0].answerOption[1].valueCoding.code).to.equal('kat');
555+
556+
expect(qJson.item[0].answerOption[2].valueCoding.system).to.equal('http://example.org');
557+
expect(qJson.item[0].answerOption[2].valueCoding.display).to.equal('abcd123');
558+
expect(qJson.item[0].answerOption[2].valueCoding.code).to.equal('123');
559+
560+
expect(qJson.item[0].answerOption[3].valueCoding.system).to.equal('http://snomed.info/sct');
561+
expect(qJson.item[0].answerOption[3].valueCoding.display).to.equal('Intersex');
562+
expect(qJson.item[0].answerOption[3].valueCoding.code).to.equal('32570691000036108');
563+
});
564+
});
565+
566+
434567
it('should fix initial input box when switched data type from coding to decimal', () => {
435568
const sampleFile = 'initial-component-bugfix.json';
436569
let fixtureJson;
@@ -480,8 +613,8 @@ describe('Home page', () => {
480613
cy.get('#answerValueSet_non-snomed').should('not.exist');
481614
cy.get('lfb-answer-option').should('be.visible');
482615
const aOptions = [
483-
{display: 'display 1', code: 'c1', system: 's1'},
484-
{display: 'display 2', code: 'c2', system: 's2'}
616+
{system: 's1', display: 'display 1', code: 'c1'},
617+
{system: 's2', display: 'display 2', code: 'c2'}
485618
];
486619
cy.enterAnswerOptions(aOptions);
487620
cy.questionnaireJSON().should((q) => {

cypress/e2e/item-level/item-level.cy.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,15 +1132,15 @@ describe('Home page', () => {
11321132
cy.getRadioButtonLabel('Create answer list', 'Yes').click();
11331133
cy.getRadioButtonLabel('Answer constraint', 'Restrict to the list').click();
11341134
cy.enterAnswerOptions([
1135-
{display: 'display 1', code: 'c1', system: 's1', __$score: 1},
1136-
{display: 'display 2', code: 'c2', system: 's2', __$score: 2}
1135+
{system: 's1', display: 'display 1', code: 'c1', __$score: 1},
1136+
{system: 's2', display: 'display 2', code: 'c2', __$score: 2}
11371137
]);
11381138
cy.contains('Add new item').scrollIntoView().click();
11391139
cy.getItemTextField().should('have.value', 'New item 1');
11401140
cy.enterAnswerOptions([
1141-
{display: 'display 1', code: 'c1', system: 's1', __$score: 1},
1142-
{display: 'display 2', code: 'c2', system: 's2', __$score: 2},
1143-
{display: 'display 3', code: 'c3', system: 's3', __$score: 3}
1141+
{system: 's1', display: 'display 1', code: 'c1', __$score: 1},
1142+
{system: 's2', display: 'display 2', code: 'c2', __$score: 2},
1143+
{system: 's3', display: 'display 3', code: 'c3', __$score: 3}
11441144
]);
11451145
cy.contains('Add new item').scrollIntoView().click();
11461146
cy.getItemTextField().should('have.value', 'New item 2');
@@ -1199,15 +1199,15 @@ describe('Home page', () => {
11991199

12001200
it('should work with operator exists value in conditional display', () => {
12011201
cy.enterAnswerOptions([
1202-
{display: 'display 1', code: 'c1', system: 's1', __$score: 1},
1203-
{display: 'display 2', code: 'c2', system: 's2', __$score: 2}
1202+
{system: 's1', display: 'display 1', code: 'c1', __$score: 1},
1203+
{system: 's2', display: 'display 2', code: 'c2', __$score: 2}
12041204
]);
12051205
cy.contains('Add new item').scrollIntoView().click();
12061206
cy.getItemTextField().should('have.value', 'New item 1');
12071207
cy.enterAnswerOptions([
1208-
{display: 'display 1', code: 'c1', system: 's1', __$score: 1},
1209-
{display: 'display 2', code: 'c2', system: 's2', __$score: 2},
1210-
{display: 'display 3', code: 'c3', system: 's3', __$score: 3}
1208+
{system: 's1', display: 'display 1', code: 'c1', __$score: 1},
1209+
{system: 's2', display: 'display 2', code: 'c2', __$score: 2},
1210+
{system: 's3', display: 'display 3', code: 'c3', __$score: 3}
12111211
]);
12121212
cy.contains('Add new item').scrollIntoView().click();
12131213
cy.getItemTextField().should('have.value', 'New item 2');

cypress/e2e/item-level/value-method.cy.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -201,17 +201,17 @@ describe('Home page', () => {
201201

202202
// Answer Option field is empty. Add 3 options.
203203
cy.contains('button', 'Add another answer').as('addAnswerButton');
204+
cy.get('[id^="answerOption.0.valueCoding.system"]').type('http://loinc.org');
204205
cy.get('[id^="answerOption.0.valueCoding.display"]').type('Example 1');
205206
cy.get('[id^="answerOption.0.valueCoding.code"]').type('MD11871-1');
206-
cy.get('[id^="answerOption.0.valueCoding.system"]').type('http://loinc.org');
207207
cy.get('@addAnswerButton').click();
208+
cy.get('[id^="answerOption.1.valueCoding.system"]').type('http://loinc.org');
208209
cy.get('[id^="answerOption.1.valueCoding.display"]').type('Example 2');
209210
cy.get('[id^="answerOption.1.valueCoding.code"]').type('MD11871-2');
210-
cy.get('[id^="answerOption.1.valueCoding.system"]').type('http://loinc.org');
211211
cy.get('@addAnswerButton').click();
212+
cy.get('[id^="answerOption.2.valueCoding.system"]').type('http://loinc.org{enter}');
212213
cy.get('[id^="answerOption.2.valueCoding.display"]').type('Example 3');
213214
cy.get('[id^="answerOption.2.valueCoding.code"]').type('MD11871-3');
214-
cy.get('[id^="answerOption.2.valueCoding.system"]').type('http://loinc.org{enter}');
215215
cy.get('[id^="answerOption.2.valueCoding.__$score"]').click();
216216

217217
// The error on the Pick Answer field should go away
@@ -271,17 +271,17 @@ describe('Home page', () => {
271271

272272
// Answer Option field is empty. Add 3 options.
273273
cy.contains('button', 'Add another answer').as('addAnswerButton');
274+
cy.get('[id^="answerOption.0.valueCoding.system"]').type('http://loinc.org');
274275
cy.get('[id^="answerOption.0.valueCoding.display"]').type('Example 1');
275276
cy.get('[id^="answerOption.0.valueCoding.code"]').type('MD11871-1');
276-
cy.get('[id^="answerOption.0.valueCoding.system"]').type('http://loinc.org');
277277
cy.get('@addAnswerButton').click();
278+
cy.get('[id^="answerOption.1.valueCoding.system"]').type('http://loinc.org');
278279
cy.get('[id^="answerOption.1.valueCoding.display"]').type('Example 2');
279280
cy.get('[id^="answerOption.1.valueCoding.code"]').type('MD11871-2');
280-
cy.get('[id^="answerOption.1.valueCoding.system"]').type('http://loinc.org');
281281
cy.get('@addAnswerButton').click();
282+
cy.get('[id^="answerOption.2.valueCoding.system"]').type('http://loinc.org');
282283
cy.get('[id^="answerOption.2.valueCoding.display"]').type('Example 3');
283284
cy.get('[id^="answerOption.2.valueCoding.code"]').type('MD11871-3');
284-
cy.get('[id^="answerOption.2.valueCoding.system"]').type('http://loinc.org{enter}');
285285
cy.get('[id^="answerOption.2.valueCoding.__$score"]').click();
286286

287287
cy.get('[id^="pick-answer_"]').as('pickAnswer');
@@ -933,17 +933,17 @@ describe('Home page', () => {
933933

934934
// Answer Option field is empty. Add 3 options.
935935
cy.contains('button', 'Add another answer').as('addAnswerButton');
936+
cy.get('[id^="answerOption.0.valueCoding.system"]').type('http://loinc.org');
936937
cy.get('[id^="answerOption.0.valueCoding.display"]').type('Example 1');
937938
cy.get('[id^="answerOption.0.valueCoding.code"]').type('MD11871-1');
938-
cy.get('[id^="answerOption.0.valueCoding.system"]').type('http://loinc.org');
939939
cy.get('@addAnswerButton').click();
940+
cy.get('[id^="answerOption.1.valueCoding.system"]').type('http://loinc.org');
940941
cy.get('[id^="answerOption.1.valueCoding.display"]').type('Example 2');
941942
cy.get('[id^="answerOption.1.valueCoding.code"]').type('MD11871-2');
942-
cy.get('[id^="answerOption.1.valueCoding.system"]').type('http://loinc.org');
943943
cy.get('@addAnswerButton').click();
944+
cy.get('[id^="answerOption.2.valueCoding.system"]').type('http://loinc.org');
944945
cy.get('[id^="answerOption.2.valueCoding.display"]').type('Example 3');
945946
cy.get('[id^="answerOption.2.valueCoding.code"]').type('MD11871-3');
946-
cy.get('[id^="answerOption.2.valueCoding.system"]').type('http://loinc.org{enter}');
947947
cy.get('[id^="answerOption.2.valueCoding.__$score"]').click();
948948

949949
// The error on the Pick Answer field should go away

0 commit comments

Comments
 (0)