From 42b9414c51cf710c2bb56b624b7d6533fe914a23 Mon Sep 17 00:00:00 2001 From: Sneha Bagri Date: Thu, 26 Mar 2020 14:20:05 +0530 Subject: [PATCH 01/32] Sneha | FBE-29 | When a form is edited after publish copy transaltion using ref form uuid --- .../components/FormDetailContainer.jsx | 17 ++++++-- .../components/FormDetailContainer.spec.js | 43 +++++++++++++++++-- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/form-builder/components/FormDetailContainer.jsx b/src/form-builder/components/FormDetailContainer.jsx index 0f3857f2..dea5e9a0 100644 --- a/src/form-builder/components/FormDetailContainer.jsx +++ b/src/form-builder/components/FormDetailContainer.jsx @@ -33,7 +33,7 @@ export class FormDetailContainer extends Component { this.state = { formData: undefined, showModal: false, showPreview: false, notification: {}, httpReceived: false, loading: true, formList: [], originalFormName: undefined, formEvents: {}, referenceVersion: undefined, - formPreviewJson: undefined }; + referenceFormUuid: undefined, formPreviewJson: undefined }; this.setState = this.setState.bind(this); this.setErrorMessage = this.setErrorMessage.bind(this); this.onSave = this.onSave.bind(this); @@ -57,8 +57,12 @@ export class FormDetailContainer extends Component { httpInterceptor .get(`${formBuilderConstants.formUrl}/${this.props.match.params.formUuid}?${params}`) .then((data) => { + const parsedFormValue = data.resources.length > 0 ? + JSON.parse(data.resources[0].value) : {}; this.setState({ formData: data, httpReceived: true, - loading: false, originalFormName: data.name, referenceVersion: data.version }); + loading: false, originalFormName: data.name, + referenceVersion: parsedFormValue.referenceVersion, + referenceFormUuid: parsedFormValue.referenceFormUuid }); this.getFormJson(); }) .catch((error) => { @@ -107,6 +111,8 @@ export class FormDetailContainer extends Component { const formResourceUuid = this.state.formData && this.state.formData.resources.length > 0 ? this.state.formData.resources[0].uuid : ''; formJson.translationsUrl = formBuilderConstants.translationsUrl; + formJson.referenceVersion = this.state.referenceVersion; + formJson.referenceFormUuid = this.state.referenceFormUuid; const formResource = { form: { name: formName, @@ -194,8 +200,9 @@ export class FormDetailContainer extends Component { _createTranslationReqObject(container, locale) { const { version, name, uuid } = this.state.formData; const referenceVersion = this.state.referenceVersion; + const referenceFormUuid = this.state.referenceFormUuid; const translations = Object.assign({}, container, { formUuid: uuid, - formName: name, version, locale, referenceVersion }); + formName: name, version, locale, referenceVersion, referenceFormUuid }); return [translations]; } @@ -285,7 +292,9 @@ export class FormDetailContainer extends Component { { editable: true } ); this.props.dispatch(clearTranslations()); - this.setState({ formData: editableFormData }); + this.setState({ formData: editableFormData, + referenceVersion: this.state.formData.version, + referenceFormUuid: this.state.formData.uuid }); } generateFormPreviewJson() { diff --git a/test/form-builder/components/FormDetailContainer.spec.js b/test/form-builder/components/FormDetailContainer.spec.js index b5bec314..3591c4a3 100644 --- a/test/form-builder/components/FormDetailContainer.spec.js +++ b/test/form-builder/components/FormDetailContainer.spec.js @@ -628,14 +628,14 @@ describe('FormDetailContainer', () => { () => { publishButton = wrapper.find('.publish-button'); }); - wrapper.setState({ referenceVersion: '1' }); + wrapper.setState({ referenceVersion: '1', referenceFormUuid: 'ref-uuid' }); publishButton.simulate('click'); setTimeout(() => { sinon.assert.calledTwice(httpInterceptor.post); sinon.assert.callOrder( postStub.withArgs(formBuilderConstants.saveTranslationsUrl, [{ formName: 'someFormName', locale: 'en', version: '1', referenceVersion: '1', - formUuid: 'someUuid' }]), + referenceFormUuid: 'ref-uuid', formUuid: 'someUuid' }]), postStub.withArgs(new UrlHelper().bahmniFormPublishUrl(formData.uuid)) ); postStub.restore(); @@ -671,14 +671,14 @@ describe('FormDetailContainer', () => { () => { publishButton = wrapper.find('.publish-button'); }); - wrapper.setState({ referenceVersion: '1' }); + wrapper.setState({ referenceVersion: '1', referenceFormUuid: 'ref-uuid' }); publishButton.simulate('click'); setTimeout(() => { sinon.assert.calledTwice(httpInterceptor.post); sinon.assert.callOrder( postStub.withArgs(formBuilderConstants.saveTranslationsUrl, [{ formName: 'someFormName', locale: 'fr', version: '1', referenceVersion: '1', - formUuid: 'someUuid' }]), + referenceFormUuid: 'ref-uuid', formUuid: 'someUuid' }]), postStub.withArgs(new UrlHelper().bahmniFormPublishUrl(formData.uuid)) ); postStub.restore(); @@ -912,5 +912,40 @@ describe('FormDetailContainer', () => { const notificationContainer = wrapper.find('NotificationContainer'); expect(notificationContainer.prop('notification').message).to.equal('Section/Table is empty'); }); + + it('should copy the translation to new version after editing', (done) => { + const wrapper = mount( + + , { context } + ); + wrapper.find('FormDetailContainer').setState({ formData: publishedFormData, + httpReceived: true, originalFormName: publishedFormData.name }); + wrapper.update(); + const editButton = wrapper.find('.edit-button'); + editButton.simulate('click'); + wrapper.find('EditModal').find('.btn--highlight').simulate('click'); + expect(wrapper.find('FormDetailContainer').state().referenceVersion) + .to.eq(publishedFormData.version); + expect(wrapper.find('FormDetailContainer').state().referenceFormUuid) + .to.eq(publishedFormData.uuid); + sinon.stub(wrapper.find('FormDetailContainer').instance(), 'getFormJson').returns({}); + sinon.stub(wrapper.find('FormDetailContainer').instance(), 'hasEmptyBlocks').returns(false); + const postStub = sinon.stub(httpInterceptor, 'post'); + postStub.callsFake(() => Promise.resolve(Object.assign({}, + formData, { version: '2', uuid: 'next-uuid' }))); + const saveButton = wrapper.find('.save-button'); + saveButton.simulate('click'); + setTimeout(() => { + expect(postStub.getCall(0).args[0]).to.eq(formBuilderConstants.bahmniFormResourceUrl); + expect(JSON.parse(postStub.getCall(0).args[1].value).referenceVersion) + .to.eq(publishedFormData.version); + expect(JSON.parse(postStub.getCall(0).args[1].value).referenceFormUuid) + .to.eq(publishedFormData.uuid); + done(); + }, 500); + httpInterceptor.post.restore(); + }); }); }); From 1c8a98d83c5d58776f7aa4164ce7e380ad13706a Mon Sep 17 00:00:00 2001 From: vinay-ThoughtWorks Date: Mon, 30 Mar 2020 17:18:20 +0530 Subject: [PATCH 02/32] Vinay | FBE - 32 | Add form name in translations page and save the translations --- src/common/apis/formTranslationApi.js | 10 ++ .../components/FormTranslationsContainer.jsx | 94 +++++++++++++++++-- .../components/FormTranslationsGrid.jsx | 1 + src/form-builder/constants.js | 1 + src/form-builder/helpers/UrlHelper.js | 5 + test/common/formTranslationApi.spec.js | 50 +++++++++- .../FormTranslationsContainer.spec.js | 40 ++++++-- .../components/FormTranslationsGrid.spec.js | 31 ++++-- test/form-builder/helpers/UrlHelper.spec.js | 7 ++ 9 files changed, 216 insertions(+), 23 deletions(-) diff --git a/src/common/apis/formTranslationApi.js b/src/common/apis/formTranslationApi.js index eca3aafc..2599cfd0 100644 --- a/src/common/apis/formTranslationApi.js +++ b/src/common/apis/formTranslationApi.js @@ -11,3 +11,13 @@ export function translationsFor(formName, formVersion, locale, formUuid) { export function saveTranslations(translations) { return httpInterceptor.post(formBuilderConstants.saveTranslationsUrl, translations); } + +export function saveFormNameTranslations(nameTranslations) { + return httpInterceptor.post(formBuilderConstants.saveNameTranslationsUrl, nameTranslations); +} + + +export function getFormNameTranslations(formName, formUuid) { + return httpInterceptor.get(new UrlHelper() + .bahmniFormNameTranslateUrl(formName, formUuid), 'text'); +} diff --git a/src/form-builder/components/FormTranslationsContainer.jsx b/src/form-builder/components/FormTranslationsContainer.jsx index 56fc09ab..72f8f242 100644 --- a/src/form-builder/components/FormTranslationsContainer.jsx +++ b/src/form-builder/components/FormTranslationsContainer.jsx @@ -16,7 +16,13 @@ import forEach from 'lodash/forEach'; import map from 'lodash/map'; import omit from 'lodash/omit'; import { commonConstants } from 'common/constants'; -import { saveTranslations, translationsFor } from 'common/apis/formTranslationApi'; +import { + getFormNameTranslations, + saveFormNameTranslations, + saveTranslations, + translationsFor, +} from 'common/apis/formTranslationApi'; +import { each } from 'lodash'; class FormTranslationsContainer extends Component { @@ -33,9 +39,13 @@ class FormTranslationsContainer extends Component { this.name = undefined; this.version = undefined; this.selectedLocale = undefined; + this.formNameTranslations = undefined; this._updateStore = this._updateStore.bind(this); this._saveTranslations = this._saveTranslations.bind(this); this._createTranslationReqObject = this._createTranslationReqObject.bind(this); + this._createNameTranslationReqObject = this._createNameTranslationReqObject.bind(this); + this._getFormNameTranslationsPromise = this._getFormNameTranslationsPromise.bind(this); + this._generateFormNameTranslationPayload = this._generateFormNameTranslationPayload.bind(this); this._getTranslations = this._getTranslations.bind(this); this._generateTranslation = this._generateTranslation.bind(this); props.dispatch(clearTranslations()); @@ -77,11 +87,27 @@ class FormTranslationsContainer extends Component { _getTranslations(name, version, locale, uuid) { this.setState({ loading: true }); - translationsFor(name, version, locale, uuid) - .then((translations) => { - this._createInitialValue(translations, locale); + const getFormNameTranslationsPromise = this._getFormNameTranslationsPromise(name, uuid); + const getFormTranslationsPromise = translationsFor(name, version, locale, uuid); + + Promise.all([getFormTranslationsPromise, getFormNameTranslationsPromise]) + .then(translationsResponse => { + const formNameTranslationResponse = translationsResponse[1]; + const formTranslationResponse = translationsResponse[0]; + if (formNameTranslationResponse) { + this.formNameTranslations = formNameTranslationResponse; + const nameTranslationJSON = JSON.parse(formNameTranslationResponse.toString()); + const nameTranslation = nameTranslationJSON.find(nameTranslationObj => + locale === nameTranslationObj.locale); + formTranslationResponse.formNames = { + [name]: [nameTranslation && nameTranslation.display || name], + }; + } else { + formTranslationResponse.formNames = { [name]: [name] }; + } + this._createInitialValue(formTranslationResponse, locale); const { allowedLocales } = this.state; - const data = this._getTranslationsInfo(locale, translations, allowedLocales); + const data = this._getTranslationsInfo(locale, formTranslationResponse, allowedLocales); this.setState({ translationData: data, loading: false }); }).catch(() => { const { allowedLocales } = this.state; @@ -90,6 +116,12 @@ class FormTranslationsContainer extends Component { }); } + _getFormNameTranslationsPromise(name, uuid) { + return this.formNameTranslations + ? Promise.resolve(this.formNameTranslations) + : getFormNameTranslations(name, uuid); + } + _getTranslationsInfo(locale, selectedTranslations, allowedLocales) { const defaultLocale = localStorage.getItem('openmrsDefaultLocale'); const headers = this._getHeaders(allowedLocales, locale, defaultLocale); @@ -121,6 +153,7 @@ class FormTranslationsContainer extends Component { _createInitialValue(translations, locale) { this._updateStore(translations, 'concepts', locale); this._updateStore(translations, 'labels', locale); + this._updateStore(translations, 'formNames', locale); } @@ -150,6 +183,17 @@ class FormTranslationsContainer extends Component { _saveTranslations() { this.setState({ loading: true }); const { translations } = this.props; + saveFormNameTranslations(this._createNameTranslationReqObject(translations)).then(() => { + this.formNameTranslations = undefined; + this.setState({ loading: true }); + this._getFormNameTranslationsPromise(this.name, this.props.match.params.formUuid) + .then(resp => { + this.formNameTranslations = resp; + }).catch(() => { + }).finally(() => { + this.setState({ loading: false }); + }); + }); saveTranslations(this._createTranslationReqObject(translations)).then(() => { const message = 'Form translations saved successfully'; @@ -161,14 +205,48 @@ class FormTranslationsContainer extends Component { }); } + _createNameTranslationReqObject(translations) { + const formNameTranslations = this._generateFormNameTranslationPayload(translations); + return { + form: { name: this.name, uuid: this.props.match.params.formUuid }, + value: JSON.stringify(formNameTranslations), + }; + } + + _generateFormNameTranslationPayload(translations) { + const formNameTranslationsObj = {}; + each(translations, (localeTranslation, locale) => { + formNameTranslationsObj[locale] = localeTranslation.formNames[this.name]; + }); + if (this.formNameTranslations) { + const existingTranslations = JSON.parse(this.formNameTranslations); + each(existingTranslations, translation => { + if (!formNameTranslationsObj[translation.locale]) { + formNameTranslationsObj[translation.locale] = translation.display; + } + }); + } + + const formNameTranslationsList = []; + each(formNameTranslationsObj, (display, locale) => { + formNameTranslationsList.push({ display, locale }); + }); + return formNameTranslationsList; + } + _createTranslationReqObject(translations) { const { name, version } = this; - return map(translations, (localeTranslation, locale) => - Object.assign(localeTranslation, { + const translationObj = []; + each(translations, (localeTranslation, locale) => { + const localeTranslationCopy = Object.assign(Object.assign({}, localeTranslation), { formName: name, formUuid: this.props.match.params.formUuid, version, locale, - })); + }); + delete localeTranslationCopy.formNames; + translationObj.push(localeTranslationCopy); + }); + return translationObj; } _generateTranslation(element) { diff --git a/src/form-builder/components/FormTranslationsGrid.jsx b/src/form-builder/components/FormTranslationsGrid.jsx index 169d9d6f..a5bb3404 100644 --- a/src/form-builder/components/FormTranslationsGrid.jsx +++ b/src/form-builder/components/FormTranslationsGrid.jsx @@ -63,6 +63,7 @@ class FormTranslationsGrid extends Component { {this._getHeaders(this.props.translationData.headers)} + {this.getRows('formNames')} {this.getRows('concepts')} {this.getRows('labels')} diff --git a/src/form-builder/constants.js b/src/form-builder/constants.js index 9ccedbc3..c8c31f50 100644 --- a/src/form-builder/constants.js +++ b/src/form-builder/constants.js @@ -20,5 +20,6 @@ export const formBuilderConstants = { translationsUrl: '/openmrs/ws/rest/v1/bahmniie/form/translations', formTranslationUrl: '/openmrs/ws/rest/v1/bahmniie/form/translate', saveTranslationsUrl: '/openmrs/ws/rest/v1/bahmniie/form/saveTranslation', + saveNameTranslationsUrl: '/openmrs/ws/rest/v1/bahmniie/form/name/saveTranslation', exportUrl: '/openmrs/ws/rest/v1/bahmniie/form/export', }; diff --git a/src/form-builder/helpers/UrlHelper.js b/src/form-builder/helpers/UrlHelper.js index f9bc01a6..29d23782 100644 --- a/src/form-builder/helpers/UrlHelper.js +++ b/src/form-builder/helpers/UrlHelper.js @@ -12,4 +12,9 @@ export class UrlHelper { return '/openmrs/ws/rest/v1/bahmniie/form/translate?' + `formName=${formName}&formVersion=${formVersion}&locale=${locale}&formUuid=${formUuid}`; } + + bahmniFormNameTranslateUrl(formName, formUuid) { + return '/openmrs/ws/rest/v1/bahmniie/form/name/translate?' + + `formName=${formName}&formUuid=${formUuid}`; + } } diff --git a/test/common/formTranslationApi.spec.js b/test/common/formTranslationApi.spec.js index 82899cfd..2fae4c69 100644 --- a/test/common/formTranslationApi.spec.js +++ b/test/common/formTranslationApi.spec.js @@ -3,7 +3,11 @@ import chai, { expect } from 'chai'; import sinon from 'sinon'; import { httpInterceptor } from 'common/utils/httpInterceptor'; import { formBuilderConstants } from 'form-builder/constants'; -import { saveTranslations, translationsFor } from 'common/apis/formTranslationApi'; +import { + getFormNameTranslations, saveFormNameTranslations, + saveTranslations, + translationsFor, +} from 'common/apis/formTranslationApi'; import { UrlHelper } from 'form-builder/helpers/UrlHelper'; chai.use(chaiEnzyme()); @@ -57,5 +61,49 @@ describe('formTranslationApi', () => { }); }); }); + + describe('save form name translation', () => { + beforeEach(() => { + sinon.stub(httpInterceptor, 'post').callsFake(() => Promise.resolve()); + }); + afterEach(() => { + httpInterceptor.post.restore(); + }); + it('should call save form name translations endpoint and return a promise', () => { + const translations = { form: { name: 'formName', uuid: 'formUuid' }, value: '' }; + const saveTranslationPromise = saveFormNameTranslations(translations); + expect(saveTranslationPromise).not.to.eq(null); + saveTranslationPromise.then(() => { + sinon.assert.calledWith( + httpInterceptor.post, + formBuilderConstants.saveNameTranslationsUrl, + translations + ); + }); + }); + }); + + describe('fetch form name translation', () => { + const nameTranslations = "[{ display: 'formName_en', locale: 'en'}]"; + beforeEach(() => { + sinon.stub(httpInterceptor, 'get').callsFake(() => Promise.resolve(nameTranslations)); + }); + afterEach(() => { + httpInterceptor.get.restore(); + }); + it('should call form name translate endpoint and return a translations', () => { + const formName = 'formName'; + const formUuid = 'formUuid'; + const translatePromise = getFormNameTranslations(formName, formUuid); + + expect(translatePromise).not.to.eq(null); + translatePromise.then(() => { + sinon.assert.calledWith( + httpInterceptor.get, + new UrlHelper().bahmniFormTranslateUrl(formName, formUuid) + ); + }); + }); + }); }); diff --git a/test/form-builder/components/FormTranslationsContainer.spec.js b/test/form-builder/components/FormTranslationsContainer.spec.js index fdd9da5f..4818b80c 100644 --- a/test/form-builder/components/FormTranslationsContainer.spec.js +++ b/test/form-builder/components/FormTranslationsContainer.spec.js @@ -41,11 +41,14 @@ describe('FormTranslationContainer', () => { ], }, locale: 'en', + formNames: { form_name: ['default_form_name_display_en'] }, }; + const nameTranslations = '[{"display": "form_name_display_es", "locale": "es"},' + + ' {"display": "default_form_name_display_en", "locale": "en"}]'; const defaultProps = { - match: { params: { formUuid: 'form_uuid' } }, + match: { params: { formUuid: 'form_uuid' }, path: '' }, routes: [], defaultLocale: 'en', }; @@ -66,6 +69,8 @@ describe('FormTranslationContainer', () => { mockHttp.get.withArgs('/openmrs/ws/rest/v1/bahmniie/form/translate?formName=' + 'form_name&formVersion=2&locale=en&formUuid=form_uuid') .returns(Promise.resolve(translationData)); + mockHttp.get.withArgs('/openmrs/ws/rest/v1/bahmniie/form/name/translate?formName=' + + 'form_name&formUuid=form_uuid').returns(Promise.resolve(nameTranslations)); breadcrumbsStub = sinon.stub(FormBuilderBreadcrumbs, 'default').returns(
A stub
); }); @@ -105,8 +110,8 @@ describe('FormTranslationContainer', () => { expect(expectedLocaleOptions, localeOptions.find('select').html()); const formTranslationContainer = wrapper.find('FormTranslationsContainer'); - - sinon.assert.callOrder(store.dispatch.withArgs( + sinon.assert.callOrder( + store.dispatch.withArgs( updateTranslations({ value: 'desc Examination Notes', type: 'concepts', @@ -119,7 +124,6 @@ describe('FormTranslationContainer', () => { translationKey: 'SEVERE_UNDERNUTRITION_13', locale: 'en', }))); - expect(formTranslationContainer.find('.info-message').text()).to .have.string('Please save the changes before selecting a locale.'); formTranslationContainer.update(); @@ -129,6 +133,8 @@ describe('FormTranslationContainer', () => { sinon.assert.callOrder( mockHttp.get.withArgs('/bahmni_config/openmrs/apps/home/locale_languages.json'), mockHttp.get.withArgs('/openmrs/ws/rest/v1/form/form_uuid?v=custom:(id,uuid,name,version)'), + mockHttp.get.withArgs('/openmrs/ws/rest/v1/bahmniie/form/name/translate?formName=' + + 'form_name&formUuid=form_uuid'), mockHttp.get.withArgs('/openmrs/ws/rest/v1/bahmniie/form/translate?formName=' + 'form_name&formVersion=2&locale=en&formUuid=form_uuid') ); @@ -145,7 +151,6 @@ describe('FormTranslationContainer', () => { 'form_name&formVersion=2&locale=es&formUuid=form_uuid') .returns(Promise.resolve(localeTranslations)); - const expectedTranslationData = { headers: [ 'Translation Key', @@ -179,6 +184,8 @@ describe('FormTranslationContainer', () => { mockHttp.get.withArgs('/bahmni_config/openmrs/apps/home/locale_languages.json'), mockHttp.get.withArgs('/openmrs/ws/rest/v1/form/form_uuid?v=custom:' + '(id,uuid,name,version)'), + mockHttp.get.withArgs('/openmrs/ws/rest/v1/bahmniie/form/name/translate?formName=' + + 'form_name&formUuid=form_uuid'), mockHttp.get.withArgs('/openmrs/ws/rest/v1/bahmniie/form/translate?formName=' + 'form_name&formVersion=2&locale=en&formUuid=form_uuid'), mockHttp.get.withArgs('/openmrs/ws/rest/v1/bahmniie/form/translate?formName=' + @@ -264,18 +271,25 @@ describe('FormTranslationContainer', () => { it('should save generated translations', (done) => { const localeTranslations = Object.assign({}, translationData); - mockHttp.post.withArgs('/openmrs/ws/rest/v1/bahmniie/form/saveTranslation') .returns(Promise.resolve({})); + mockHttp.post.withArgs('/openmrs/ws/rest/v1/bahmniie/form/name/saveTranslation') + .returns(Promise.resolve({})); localeTranslations.locale = 'es'; localeTranslations.concepts.SEVERE_UNDERNUTRITION_13 = ['Severe Undernutrition es']; + localeTranslations.formNames.form_name = ['default_form_name_display_es']; mockHttp.get.withArgs('/openmrs/ws/rest/v1/bahmniie/form/translate?formName=' + 'form_name&formVersion=2&locale=es&formUuid=form_uuid') .returns(Promise.resolve(localeTranslations)); + mockHttp.get.withArgs('/openmrs/ws/rest/v1/bahmniie/form/name/translate?formName=' + + 'form_name&formUuid=form_uuid') + .returns(Promise.resolve(nameTranslations)); localeTranslations.formName = 'form_name'; localeTranslations.formUuid = 'form_uuid'; localeTranslations.version = '2'; + + delete localeTranslations.formNames; const wrapper = mount( @@ -288,10 +302,24 @@ describe('FormTranslationContainer', () => { setTimeout(() => { setTimeout(() => { wrapper.find('#save-translations-button').simulate('click'); + const nameTranslation = { + form: { name: 'form_name', uuid: 'form_uuid' }, + value: '[{"display":["form_name_display_es"],"locale":"es"},' + + '{"display":"default_form_name_display_en","locale":"en"}]', + }; + sinon.assert.calledOnce( + mockHttp.post.withArgs('/openmrs/ws/rest/v1/bahmniie/form/name/saveTranslation', + nameTranslation) + ); + delete localeTranslations.formNames; sinon.assert.calledOnce(mockHttp.post.withArgs( '/openmrs/ws/rest/v1/bahmniie/form/saveTranslation', [localeTranslations])); setTimeout(() => { + sinon.assert.called( + mockHttp.get.withArgs('/openmrs/ws/rest/v1/bahmniie/form/name/translate?formName=' + + 'form_name&formUuid=form_uuid') + ); const formTranslationContainer = wrapper.find('FormTranslationsContainer'); formTranslationContainer.update(); expect(wrapper.find('NotificationContainer').props().notification) diff --git a/test/form-builder/components/FormTranslationsGrid.spec.js b/test/form-builder/components/FormTranslationsGrid.spec.js index 344abd94..32b01f07 100644 --- a/test/form-builder/components/FormTranslationsGrid.spec.js +++ b/test/form-builder/components/FormTranslationsGrid.spec.js @@ -35,7 +35,9 @@ describe('FormTranslationsGrid', () => { SECTION_12: [ 'SECTION es', ], - + }, + formNames: { + sampleName: ['Sample Name ESPANOL'], }, locale: 'es', }, @@ -53,6 +55,9 @@ describe('FormTranslationsGrid', () => { 'SECTION_12', ], }, + formNames: { + sampleName: ['Sample Name FRENCH'], + }, locale: 'fr', }, ], @@ -76,7 +81,7 @@ describe('FormTranslationsGrid', () => { ); - expect(getTableBody()).to.have.exactly(3).descendants('tr'); + expect(getTableBody()).to.have.exactly(4).descendants('tr'); expect(wrapper.find('table').find('thead')).to.have.exactly(1).descendants('tr'); expect(wrapper.find('table').find('thead')).to.have.exactly(3).descendants('th'); @@ -86,12 +91,15 @@ describe('FormTranslationsGrid', () => { expect(getItem(rowIndex, 1)).to.have.exactly(1).descendants('FreeTextAutoComplete'); } - expect(getData(0, 0)).to.have.string('SEVERE_UNDERNUTRITION_13'); - expect(getData(0, 1)).to.have.string('Undernutrition es'); - expect(getData(0, 2)).to.have.string('Undernutrition'); - expect(getData(2, 0)).to.have.string('SECTION_12'); - expect(getData(2, 1)).to.have.string('SECTION es'); - expect(getData(2, 2)).to.have.string('SECTION_12'); + expect(getData(0, 0)).to.have.string('sampleName'); + expect(getData(0, 1)).to.have.string('Sample Name ESPANOL'); + expect(getData(0, 2)).to.have.string('Sample Name FRENCH'); + expect(getData(1, 0)).to.have.string('SEVERE_UNDERNUTRITION_13'); + expect(getData(1, 1)).to.have.string('Undernutrition es'); + expect(getData(1, 2)).to.have.string('Undernutrition'); + expect(getData(3, 0)).to.have.string('SECTION_12'); + expect(getData(3, 1)).to.have.string('SECTION es'); + expect(getData(3, 2)).to.have.string('SECTION_12'); }); it('should update store on value change', () => { @@ -106,12 +114,19 @@ describe('FormTranslationsGrid', () => { const onChange = freeTextAutocomplete.props().onChange; expect(onChange).to.be.instanceOf(Function); onChange({ value: 'something' }, 'concepts', 'SEVERE_UNDERNUTRITION_13', 'en'); + onChange({ value: 'NEW NAME TRANSLATION' }, 'formNames', 'sampleName', 'en'); sinon.assert.calledOnce(store.dispatch.withArgs(updateTranslations( { value: 'something', type: 'concepts', translationKey: 'SEVERE_UNDERNUTRITION_13', locale: 'en', } ))); + sinon.assert.calledOnce(store.dispatch.withArgs(updateTranslations( + { + value: 'NEW NAME TRANSLATION', type: 'formNames', + translationKey: 'sampleName', locale: 'en', + } + ))); }); }); diff --git a/test/form-builder/helpers/UrlHelper.spec.js b/test/form-builder/helpers/UrlHelper.spec.js index 3fa3c680..74924117 100644 --- a/test/form-builder/helpers/UrlHelper.spec.js +++ b/test/form-builder/helpers/UrlHelper.spec.js @@ -24,4 +24,11 @@ describe('UrlHelper', () => { 'formName=name&formVersion=version&locale=locale&formUuid=formUuid'; expect(conceptRepresentation).to.eql(expectedUrl); }); + + it('should return form name translate url', () => { + const conceptRepresentation = urlHelper.bahmniFormNameTranslateUrl('name', 'formUuid'); + const expectedUrl = '/openmrs/ws/rest/v1/bahmniie/form/name/translate?' + + 'formName=name&formUuid=formUuid'; + expect(conceptRepresentation).to.eql(expectedUrl); + }); }); From e82f368997c0da7797a57f0bd133dd21210801fe Mon Sep 17 00:00:00 2001 From: sowmika Date: Tue, 24 Mar 2020 15:42:09 +0530 Subject: [PATCH 03/32] Sowmika, Alekhya | FBE-70 | Add save button on preview popup --- src/form-builder/components/FormPreviewModal.jsx | 3 +++ test/form-builder/components/FormPreviewModal.spec.js | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/form-builder/components/FormPreviewModal.jsx b/src/form-builder/components/FormPreviewModal.jsx index 8bbcfaf5..6393e6ff 100644 --- a/src/form-builder/components/FormPreviewModal.jsx +++ b/src/form-builder/components/FormPreviewModal.jsx @@ -43,6 +43,9 @@ export default class FormPreviewModal extends React.Component { {this.getContainer(metadata)}
+
-
); diff --git a/test/form-builder/components/FormBuilder.spec.js b/test/form-builder/components/FormBuilder.spec.js index 33a423d6..cecce1d4 100644 --- a/test/form-builder/components/FormBuilder.spec.js +++ b/test/form-builder/components/FormBuilder.spec.js @@ -392,6 +392,7 @@ describe('Import form', () => { referenceFormUuid: null, }, ]; + formJson.nameTranslations = '{"display":"1_EN_Name", "locale": "en"}'; sinon.stub(httpInterceptor, 'post').callsFake(() => { const response = data[0]; return Promise.resolve(Object.assign({}, response, { uuid: 'new_uuid' })); @@ -400,6 +401,7 @@ describe('Import form', () => { setTimeout(() => { const firstArgumentOfSaveFormResource = saveFormResourceSpy.getCall(0).args[0]; const secondArgumentOfSaveFormResource = saveFormResourceSpy.getCall(0).args[1]; + const thirdArgumentOfSaveFormResource = saveFormResourceSpy.getCall(0).args[2]; sinon.assert.calledOnce(saveFormResourceSpy); expect(firstArgumentOfSaveFormResource.form.name).to.eq(data[0].name); expect(firstArgumentOfSaveFormResource.form.uuid).to.eq('new_uuid'); @@ -410,6 +412,9 @@ describe('Import form', () => { expect(secondArgumentOfSaveFormResource[0].version).to.eq(data[0].version); expect(secondArgumentOfSaveFormResource[0].formName).to.eq(data[0].name); expect(secondArgumentOfSaveFormResource[0].formUuid).to.eq('new_uuid'); + expect(thirdArgumentOfSaveFormResource.form.name).to.eq(data[0].name); + expect(thirdArgumentOfSaveFormResource.form.uuid).to.eq('new_uuid'); + expect(thirdArgumentOfSaveFormResource.value).to.eq('{"display":"1_EN_Name", "locale": "en"}'); done(); }, 500); }); @@ -839,7 +844,7 @@ describe('Import Multiple Forms', () => { formJson: { name: formName, resources: [ - { value: '{"a":2}' }, + {value: '{"a":2}'}, {value: '{"display":"sampleForm_EN", "locale": "en"}'}, ], }, translations: [], @@ -848,9 +853,10 @@ describe('Import Multiple Forms', () => { newInstance.validateFormJsonAndConcepts(fileName, formData); setTimeout(() => { - expect(newInstance.formJSONs.length).to.eql(1); + expect(newInstance.formJSONs.length).to.eql(2); expect(newInstance.formJSONs[0].formName).to.eql(formName); expect(newInstance.formJSONs[0].translations).to.eql([]); + expect(newInstance.formJSONs[0].nameTranslations).to.eql('{"display":"sampleForm_EN", "locale": "en"}'); done(); }); }); From e624530e27ba46fce96fe5f4eb655041c6ac16e9 Mon Sep 17 00:00:00 2001 From: vinay-ThoughtWorks Date: Fri, 3 Apr 2020 13:40:51 +0530 Subject: [PATCH 12/32] Vinay, Swetha | FBE-73 | Fix tests --- src/form-builder/components/FormBuilder.jsx | 10 ++++++---- .../components/FormBuilderContainer.jsx | 17 ++++++++--------- .../form-builder/components/FormBuilder.spec.js | 10 ++++++---- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/form-builder/components/FormBuilder.jsx b/src/form-builder/components/FormBuilder.jsx index d032841c..55889116 100644 --- a/src/form-builder/components/FormBuilder.jsx +++ b/src/form-builder/components/FormBuilder.jsx @@ -279,7 +279,8 @@ export default class FormBuilder extends Component { const importFormJsonPromises = []; formJsons.forEach(formJson => { const { form, value, formName, translations, nameTranslations } = formJson; - importFormJsonPromises.push(self.saveFormJson(form, value, formName, translations, nameTranslations)); + importFormJsonPromises.push(self.saveFormJson(form, value, formName, translations, + nameTranslations)); }); Promise.all(importFormJsonPromises) .then(() => self.hideLoader()) @@ -307,9 +308,10 @@ export default class FormBuilder extends Component { value: nameTranslations, uuid: '', }; - const translationsWithFormUuid = translations.map((eachTranslation) => Object.assign({}, - eachTranslation, { formUuid: response.uuid })); - self.props.saveFormResource(formResource, translationsWithFormUuid, formNameTranslationsResource); + const translationsWithFormUuid = translations.map((eachTranslation) => + Object.assign({}, eachTranslation, { formUuid: response.uuid })); + self.props.saveFormResource(formResource, translationsWithFormUuid, + formNameTranslationsResource); }) .catch(() => { const formUuid = self.getFormUuid(formName); diff --git a/src/form-builder/components/FormBuilderContainer.jsx b/src/form-builder/components/FormBuilderContainer.jsx index 2a9948ba..ad5e68d4 100644 --- a/src/form-builder/components/FormBuilderContainer.jsx +++ b/src/form-builder/components/FormBuilderContainer.jsx @@ -12,7 +12,7 @@ import sortBy from 'lodash/sortBy'; import formHelper from '../helpers/formHelper'; import { connect } from 'react-redux'; import { setDefaultLocale } from '../actions/control'; -import {saveFormNameTranslations, saveTranslations} from 'common/apis/formTranslationApi'; +import { saveFormNameTranslations, saveTranslations } from 'common/apis/formTranslationApi'; export class FormBuilderContainer extends Component { @@ -107,17 +107,16 @@ export class FormBuilderContainer extends Component { const self = this; self.setMessage('Importing Translations...', commonConstants.responseType.success); const translationsPromises = [saveTranslations(translations || [])]; - if (formNameTranslations) + if (formNameTranslations) { translationsPromises.push(saveFormNameTranslations(formNameTranslations, null)); + } Promise.all(translationsPromises).then(() => { - self.getFormData(); - self.setMessage('Imported Successfully', - commonConstants.responseType.success); - }) - .catch(() => { - this.setMessage('Error Importing Translations', commonConstants.responseType.error); - }); + self.getFormData(); + self.setMessage('Imported Successfully', commonConstants.responseType.success); + }).catch(() => { + this.setMessage('Error Importing Translations', commonConstants.responseType.error); + }); } saveFormResource(formJson, formTranslations, formNameTranslationsResource) { diff --git a/test/form-builder/components/FormBuilder.spec.js b/test/form-builder/components/FormBuilder.spec.js index cecce1d4..dfd50aec 100644 --- a/test/form-builder/components/FormBuilder.spec.js +++ b/test/form-builder/components/FormBuilder.spec.js @@ -414,7 +414,8 @@ describe('Import form', () => { expect(secondArgumentOfSaveFormResource[0].formUuid).to.eq('new_uuid'); expect(thirdArgumentOfSaveFormResource.form.name).to.eq(data[0].name); expect(thirdArgumentOfSaveFormResource.form.uuid).to.eq('new_uuid'); - expect(thirdArgumentOfSaveFormResource.value).to.eq('{"display":"1_EN_Name", "locale": "en"}'); + expect(thirdArgumentOfSaveFormResource.value) + .to.eq('{"display":"1_EN_Name", "locale": "en"}'); done(); }, 500); }); @@ -844,7 +845,7 @@ describe('Import Multiple Forms', () => { formJson: { name: formName, resources: [ - {value: '{"a":2}'}, {value: '{"display":"sampleForm_EN", "locale": "en"}'}, + { value: '{"a":2}' }, { value: '{"display":"sampleForm_EN", "locale": "en"}' }, ], }, translations: [], @@ -853,10 +854,11 @@ describe('Import Multiple Forms', () => { newInstance.validateFormJsonAndConcepts(fileName, formData); setTimeout(() => { - expect(newInstance.formJSONs.length).to.eql(2); + expect(newInstance.formJSONs.length).to.eql(1); expect(newInstance.formJSONs[0].formName).to.eql(formName); expect(newInstance.formJSONs[0].translations).to.eql([]); - expect(newInstance.formJSONs[0].nameTranslations).to.eql('{"display":"sampleForm_EN", "locale": "en"}'); + expect(newInstance.formJSONs[0].nameTranslations) + .to.eql('{"display":"sampleForm_EN", "locale": "en"}'); done(); }); }); From ae8ea17a069ff0a3fec0e948ddb911ec8b760df6 Mon Sep 17 00:00:00 2001 From: vinay-ThoughtWorks Date: Fri, 3 Apr 2020 13:47:42 +0530 Subject: [PATCH 13/32] Vinay | FBE - 32 | Display recently saved name even on locale change --- .../components/FormTranslationsContainer.jsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/form-builder/components/FormTranslationsContainer.jsx b/src/form-builder/components/FormTranslationsContainer.jsx index 5550fc91..cb808e95 100644 --- a/src/form-builder/components/FormTranslationsContainer.jsx +++ b/src/form-builder/components/FormTranslationsContainer.jsx @@ -189,6 +189,25 @@ class FormTranslationsContainer extends Component { this._getFormNameTranslationsPromise(this.name, this.props.match.params.formUuid) .then(resp => { this.formNameTranslations = resp; + const nameTranslationJSON = JSON.parse(resp.toString()); + if (nameTranslationJSON) { + const updatedTranslationsData = map(this.state.translationData.data, translationObj => { + const translation = Object.assign({}, translationObj); + const nameTranslation = nameTranslationJSON.find(nameTranslationObj => + translation.locale === nameTranslationObj.locale + ); + if (nameTranslation) { + translation.formNames.FORM_NAME[0] = nameTranslation.display; + } + return translation; + }); + this.setState({ + translationsData: { + headers: this.state.translationData.headers, + data: updatedTranslationsData, + }, + }); + } }).catch(() => { }).finally(() => { this.setState({ loading: false }); From 472ad18f22cf5e9d5062f39ff097d1ce4355a3d8 Mon Sep 17 00:00:00 2001 From: vinay-ThoughtWorks Date: Mon, 6 Apr 2020 14:30:42 +0530 Subject: [PATCH 14/32] Vinay | FBE - 66 | Fix scroll issue with minimal resolution for ubuntu --- styles/common/_bahmniGlobal.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/common/_bahmniGlobal.scss b/styles/common/_bahmniGlobal.scss index 3d876c94..c0fb06e2 100644 --- a/styles/common/_bahmniGlobal.scss +++ b/styles/common/_bahmniGlobal.scss @@ -122,7 +122,7 @@ input[type="checkbox"] { &:not(.scrolled) { @media screen and (max-height: 742px) { - max-height: 572px; + max-height: 500px; } @media screen and (max-height: 642px) { max-height: 472px; From 3169ab06b55206fe8340994cb20e3f411a6050aa Mon Sep 17 00:00:00 2001 From: Yamuna Chukka Date: Wed, 1 Apr 2020 17:49:27 +0530 Subject: [PATCH 15/32] Yamuna | FBE - 22 | Add full screen viewer for all form events --- src/form-builder/actions/control.js | 6 +- .../components/FormConditionsContainer.jsx | 51 +++++++ .../components/FormConditionsModal.js | 130 ++++++++++++++++++ src/form-builder/components/FormDetail.jsx | 41 +++++- .../components/FormDetailContainer.jsx | 24 +++- .../components/FormEventContainer.jsx | 4 +- .../components/FormEventEditor.jsx | 14 +- .../components/ScriptEditorComponentModal.js | 105 ++++++++++++++ src/form-builder/reducers/controlDetails.js | 26 +++- src/form-builder/reducers/formDetails.js | 5 + styles/common/_popUp.scss | 19 ++- 11 files changed, 410 insertions(+), 15 deletions(-) create mode 100644 src/form-builder/components/FormConditionsContainer.jsx create mode 100644 src/form-builder/components/FormConditionsModal.js create mode 100644 src/form-builder/components/ScriptEditorComponentModal.js diff --git a/src/form-builder/actions/control.js b/src/form-builder/actions/control.js index 5858710e..cfbc0988 100644 --- a/src/form-builder/actions/control.js +++ b/src/form-builder/actions/control.js @@ -11,7 +11,7 @@ export const addSourceMap = (sourceMap) => ({ type: 'ADD_SOURCE_MAP', sourceMap export const setChangedProperty = (property, id) => ({ type: 'SET_CHANGED_PROPERTY', property, id }); -export const sourceChangedProperty = (source) => ({ type: 'SOURCE_CHANGED', source }); +export const sourceChangedProperty = (source, id) => ({ type: 'SOURCE_CHANGED', source, id }); export const dragSourceUpdate = (cell) => ({ type: 'DRAG_SOURCE_CHANGED', cell }); @@ -35,3 +35,7 @@ export const removeLocaleTranslation = (locale) => ({ type: 'REMOVE_LOCALE_TRANSLATIONS', locale }); export const clearTranslations = () => ({ type: 'CLEAR_TRANSLATIONS' }); + +export const formConditionsEventUpdate = (events) => ({ type: 'FORM_CONDITIONS_CHANGED', events }); + +export const formLoad = (controls) => ({ type: 'FORM_LOAD', controls }); diff --git a/src/form-builder/components/FormConditionsContainer.jsx b/src/form-builder/components/FormConditionsContainer.jsx new file mode 100644 index 00000000..7a6a8a06 --- /dev/null +++ b/src/form-builder/components/FormConditionsContainer.jsx @@ -0,0 +1,51 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { setChangedProperty } from '../actions/control'; + +export class FormConditionsContainer extends Component { + + constructor(props) { + super(props); + this.state = { events: {} }; + } + + componentWillUpdate(newProps) { + const updatedEvents = newProps.formDetails && newProps.formDetails.events; + if (updatedEvents && this.state.events !== updatedEvents) { + this.setState({ events: updatedEvents }); + this.props.updateFormEvents(updatedEvents); + } + } + + updateProperty() { + const properties = { [this.props.eventProperty]: true, formTitle: this.props.formTitle }; + this.props.dispatch(setChangedProperty(properties)); + } + + render() { + return ( +
+ +