Skip to content

Commit

Permalink
Backport choice_filter xlsforms fix to 3.14.x (#7558)
Browse files Browse the repository at this point in the history
  • Loading branch information
m5r authored Mar 16, 2022
1 parent 62d12a8 commit 41375d9
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 21 deletions.
35 changes: 34 additions & 1 deletion tests/e2e/forms/repeat-form.wdio-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const reportsPage = require('../../page-objects/reports/reports.wdio.page');

const countFormDocument = readFormDocument('repeat-translation-count');
const buttonFormDocument = readFormDocument('repeat-translation-button');
const selectFormDocument = readFormDocument('repeat-translation-select');
const userContactDoc = {
_id: constants.USER_CONTACT_ID,
name: 'Jack',
Expand All @@ -25,7 +26,7 @@ const userContactDoc = {

describe('RepeatForm', () => {
before(async () => {
await utils.seedTestData(userContactDoc, [countFormDocument, buttonFormDocument]);
await utils.seedTestData(userContactDoc, [countFormDocument, buttonFormDocument, selectFormDocument]);
});

afterEach(async () => {
Expand Down Expand Up @@ -133,6 +134,38 @@ describe('RepeatForm', () => {
}
});

describe('Repeat form with select', () => {
it('should display the initial form and its repeated content in the default language', async () => {
const swUserName = 'Jina la mtumizi';
await loginPage.changeLanguage('sw', swUserName);
await login();
await openRepeatForm(selectFormDocument.internalId);

const { input: washingtonInput, label: washingtonLabel } = await getField('selected_state', 'washington');
expect(await washingtonLabel.getText()).to.equal('Washington');

await washingtonInput.click();
const { input: kingInput, label: kingLabel } = await getField('selected_county', 'king');
expect(await kingLabel.getText()).to.equal('King');

await kingInput.click();
const { label: seattleLabel } = await getField('selected_city', 'seattle');
const { label: redmondLabel } = await getField('selected_city', 'redmond');
expect(await seattleLabel.getText()).to.equal('Seattle');
expect(await redmondLabel.getText()).to.equal('Redmond');
});

async function getField(fieldName, fieldValue) {
const fieldInputPath = `#report-form input[name="/cascading_select/${fieldName}"][value="${fieldValue}"]`;
const fieldLabelPath = `${fieldInputPath} ~ .option-label.active`;

return {
input: await $(fieldInputPath),
label: await $(fieldLabelPath),
};
}
});

async function assertLabels({ selector, count, labelText }) {
const labels = await $$(selector);
expect(labels.length).to.equal(count);
Expand Down
166 changes: 166 additions & 0 deletions tests/forms/repeat-translation-select.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?xml version="1.0"?>
<h:html xmlns="http://www.w3.org/2002/xforms" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:jr="http://openrosa.org/javarosa" xmlns:orx="http://openrosa.org/xforms">
<h:head>
<h:title>Cascading select example</h:title>
<model>
<itext>
<translation lang="en">
<text id="static_instance-cities-0">
<value>Brownsville</value>
</text>
<text id="static_instance-cities-1">
<value>Harlingen</value>
</text>
<text id="static_instance-cities-2">
<value>Seattle</value>
</text>
<text id="static_instance-cities-3">
<value>Redmond</value>
</text>
<text id="static_instance-cities-4">
<value>Tacoma</value>
</text>
<text id="static_instance-cities-5">
<value>Puyallup</value>
</text>
<text id="static_instance-counties-0">
<value>King</value>
</text>
<text id="static_instance-counties-1">
<value>Pierce</value>
</text>
<text id="static_instance-counties-2">
<value>Brewster</value>
</text>
<text id="static_instance-counties-3">
<value>Cameron</value>
</text>
<text id="static_instance-states-0">
<value>Texas</value>
</text>
<text id="static_instance-states-1">
<value>Washington</value>
</text>
</translation>
</itext>
<instance>
<cascading_select id="cascading_select" prefix="J1!cascading_select!" delimiter="#">
<selected_state/>
<selected_county/>
<selected_city/>
<meta tag="hidden">
<instanceID/>
</meta>
</cascading_select>
</instance>
<instance id="contact-summary"/>
<instance id="states">
<root>
<item>
<itextId>static_instance-states-0</itextId>
<name>texas</name>
</item>
<item>
<itextId>static_instance-states-1</itextId>
<name>washington</name>
</item>
</root>
</instance>
<instance id="counties">
<root>
<item>
<itextId>static_instance-counties-0</itextId>
<state>washington</state>
<name>king</name>
</item>
<item>
<itextId>static_instance-counties-1</itextId>
<state>washington</state>
<name>pierce</name>
</item>
<item>
<itextId>static_instance-counties-2</itextId>
<state>texas</state>
<name>brewster</name>
</item>
<item>
<itextId>static_instance-counties-3</itextId>
<state>texas</state>
<name>cameron</name>
</item>
</root>
</instance>
<instance id="cities">
<root>
<item>
<itextId>static_instance-cities-0</itextId>
<name>brownsville</name>
<county>cameron</county>
<state>texas</state>
</item>
<item>
<itextId>static_instance-cities-1</itextId>
<name>harlingen</name>
<county>cameron</county>
<state>texas</state>
</item>
<item>
<itextId>static_instance-cities-2</itextId>
<name>seattle</name>
<county>king</county>
<state>washington</state>
</item>
<item>
<itextId>static_instance-cities-3</itextId>
<name>redmond</name>
<county>king</county>
<state>washington</state>
</item>
<item>
<itextId>static_instance-cities-4</itextId>
<name>tacoma</name>
<county>pierce</county>
<state>washington</state>
</item>
<item>
<itextId>static_instance-cities-5</itextId>
<name>puyallup</name>
<county>pierce</county>
<state>washington</state>
</item>
</root>
</instance>
<bind nodeset="/cascading_select/selected_state" type="select1"/>
<bind nodeset="/cascading_select/selected_county" type="select1"/>
<bind nodeset="/cascading_select/selected_city" type="select1"/>
<bind nodeset="/cascading_select/meta/instanceID" type="string" readonly="true()" calculate="concat('uuid:', uuid())"/>
</model>
</h:head>
<h:body>
<select1 ref="/cascading_select/selected_state">
<label>Select a state</label>
<item>
<label>Texas</label>
<value>texas</value>
</item>
<item>
<label>Washington</label>
<value>washington</value>
</item>
</select1>
<select1 ref="/cascading_select/selected_county">
<label>Select a county</label>
<itemset nodeset="instance('counties')/root/item[state= /cascading_select/selected_state ]">
<value ref="name"/>
<label ref="jr:itext(itextId)"/>
</itemset>
</select1>
<select1 ref="/cascading_select/selected_city">
<label>Select a city</label>
<itemset nodeset="instance('cities')/root/item[county= /cascading_select/selected_county ]">
<value ref="name"/>
<label ref="jr:itext(itextId)"/>
</itemset>
</select1>
</h:body>
</h:html>
57 changes: 37 additions & 20 deletions webapp/src/ts/services/enketo.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ export class EnketoService {
this.languageService.get()
])
.then(([ instanceStr, contactSummary, language ]) => {
const options:any = {
const options: EnketoOptions = {
modelStr: doc.model,
instanceStr: instanceStr,
language: language
Expand Down Expand Up @@ -291,25 +291,8 @@ export class EnketoService {
if (loadErrors?.length) {
return Promise.reject(new Error(JSON.stringify(loadErrors)));
}
const language = options.language;
this.currentForm.langs.$formLanguages.val(language).trigger('change');
// re-set the enketo form's language when a DOM node is added to the form and the form has been edited
// TODO: remove this once the enketo uplift gets merged https://github.com/medic/cht-core/pull/7256
this.currentForm.view.$.on(
'click',
'button.add-repeat-btn:enabled',
() => this.currentForm.langs.setAll(language),
);
let hasFormChanged = false;
this.currentForm.view.$.on('change', () => hasFormChanged = true);
const observer = new MutationObserver((mutations) => {
const hasNewNodes = mutations.some(mutation => mutation.addedNodes.length > 0);
if (hasFormChanged && hasNewNodes) {
this.currentForm.langs.setAll(language);
}
hasFormChanged = false;
});
observer.observe(this.currentForm.view.html, { childList: true, subtree: true });

this.setupFormLanguage(options.language);
})
.then(() => this.getFormTitle(titleKey, doc))
.then((title) => {
Expand All @@ -328,6 +311,28 @@ export class EnketoService {
});
}

// set the enketo form's language, re-set it when a DOM node is added to the form and the form has been edited
// TODO: remove this method once the enketo uplift gets merged https://github.com/medic/cht-core/pull/7256
private setupFormLanguage(language: string) {
const setFormLanguage = (language: string) => this.currentForm.langs.$formLanguages.val(language).trigger('change');
setFormLanguage(language);
this.currentForm.view.$.on(
'click',
'button.add-repeat-btn:enabled',
() => setFormLanguage(language),
);
let hasFormChanged = false;
this.currentForm.view.$.on('change', () => hasFormChanged = true);
const observer = new MutationObserver((mutations) => {
const hasNewNodes = mutations.some(mutation => mutation.addedNodes.length > 0);
if (hasFormChanged && hasNewNodes) {
setFormLanguage(language);
}
hasFormChanged = false;
});
observer.observe(this.currentForm.view.html, { childList: true, subtree: true });
}

private getFormTitle(titleKey, doc) {
if (titleKey) {
// using translation key
Expand Down Expand Up @@ -775,6 +780,18 @@ export class EnketoService {
}
}

interface ContactSummary {
id: string;
xmlStr: string;
}

interface EnketoOptions {
modelStr: string;
instanceStr: string;
language: string;
external?: ContactSummary[];
}

interface XmlFormContext {
doc: {
html: JQuery;
Expand Down

0 comments on commit 41375d9

Please sign in to comment.