@openmrs/esm-form-engine-lib 2.1.0-pre.1501 → 2.1.0-pre.1502

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,48 @@
1
+ {
2
+ "encounterType": "e22e39fd-7db2-45e7-80f1-60fa0d5a4378",
3
+ "name": "Sample UI Select",
4
+ "processor": "EncounterFormProcessor",
5
+ "referencedForms": [],
6
+ "uuid": "f7768d34-8e41-4f6b-a276-12c12e023165",
7
+ "version": "1.0",
8
+ "pages": [
9
+ {
10
+ "label": "First Page",
11
+ "sections": [
12
+ {
13
+ "label": "A Section",
14
+ "isExpanded": "true",
15
+ "questions": [
16
+ {
17
+ "label": "Transfer Location",
18
+ "type": "obs",
19
+ "questionOptions": {
20
+ "rendering": "ui-select-extended",
21
+ "concept": "160540AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
22
+ "datasource": {
23
+ "name": "location_datasource",
24
+ "config": {
25
+ "tag": "test-tag"
26
+ }
27
+ }
28
+ },
29
+ "meta": {},
30
+ "id": "patient_transfer_location"
31
+ },
32
+ {
33
+ "label": "Problem",
34
+ "type": "obs",
35
+ "questionOptions": {
36
+ "isSearchable": true,
37
+ "rendering": "problem",
38
+ "concept": "4b59ac07-cf72-4f46-b8c0-4f62b1779f7e"
39
+ },
40
+ "id": "problem"
41
+ }
42
+ ]
43
+ }
44
+ ]
45
+ }
46
+ ],
47
+ "description": "Sample UI Select"
48
+ }
@@ -1 +1 @@
1
- var _openmrs_esm_form_engine_lib;(()=>{"use strict";var e,r,t,n,o,i,a,l,s,u,f,p,d,c,h,m,v,g,b={8008:(e,r,t)=>{var n={"./start":()=>Promise.all([t.e(901),t.e(501),t.e(72),t.e(385),t.e(572)]).then((()=>()=>t(8572)))},o=(e,r)=>(t.R=r,r=t.o(n,e)?n[e]():Promise.resolve().then((()=>{throw new Error('Module "'+e+'" does not exist in container.')})),t.R=void 0,r),i=(e,r)=>{if(t.S){var n="default",o=t.S[n];if(o&&o!==e)throw new Error("Container initialization failed as it has already been initialized with a different share scope");return t.S[n]=e,t.I(n,r)}};t.d(r,{get:()=>o,init:()=>i})}},y={};function w(e){var r=y[e];if(void 0!==r)return r.exports;var t=y[e]={id:e,loaded:!1,exports:{}};return b[e].call(t.exports,t,t.exports,w),t.loaded=!0,t.exports}w.m=b,w.c=y,w.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return w.d(r,{a:r}),r},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,w.t=function(t,n){if(1&n&&(t=this(t)),8&n)return t;if("object"==typeof t&&t){if(4&n&&t.__esModule)return t;if(16&n&&"function"==typeof t.then)return t}var o=Object.create(null);w.r(o);var i={};e=e||[null,r({}),r([]),r(r)];for(var a=2&n&&t;"object"==typeof a&&!~e.indexOf(a);a=r(a))Object.getOwnPropertyNames(a).forEach((e=>i[e]=()=>t[e]));return i.default=()=>t,w.d(o,i),o},w.d=(e,r)=>{for(var t in r)w.o(r,t)&&!w.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},w.f={},w.e=e=>Promise.all(Object.keys(w.f).reduce(((r,t)=>(w.f[t](e,r),r)),[])),w.u=e=>e+".js",w.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),w.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),t={},n="@openmrs/esm-form-engine-lib:",w.l=(e,r,o,i)=>{if(t[e])t[e].push(r);else{var a,l;if(void 0!==o)for(var s=document.getElementsByTagName("script"),u=0;u<s.length;u++){var f=s[u];if(f.getAttribute("src")==e||f.getAttribute("data-webpack")==n+o){a=f;break}}a||(l=!0,(a=document.createElement("script")).charset="utf-8",a.timeout=120,w.nc&&a.setAttribute("nonce",w.nc),a.setAttribute("data-webpack",n+o),a.src=e),t[e]=[r];var p=(r,n)=>{a.onerror=a.onload=null,clearTimeout(d);var o=t[e];if(delete t[e],a.parentNode&&a.parentNode.removeChild(a),o&&o.forEach((e=>e(n))),r)return r(n)},d=setTimeout(p.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=p.bind(null,a.onerror),a.onload=p.bind(null,a.onload),l&&document.head.appendChild(a)}},w.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},w.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{w.S={};var e={},r={};w.I=(t,n)=>{n||(n=[]);var o=r[t];if(o||(o=r[t]={}),!(n.indexOf(o)>=0)){if(n.push(o),e[t])return e[t];w.o(w.S,t)||(w.S[t]={});var i=w.S[t],a="@openmrs/esm-form-engine-lib",l=(e,r,t,n)=>{var o=i[e]=i[e]||{},l=o[r];(!l||!l.loaded&&(!n!=!l.eager?n:a>l.from))&&(o[r]={get:t,from:a,eager:!!n})},s=[];return"default"===t&&(l("@openmrs/esm-framework","5.8.2-pre.2368",(()=>Promise.all([w.e(151),w.e(72),w.e(766)]).then((()=>()=>w(5151))))),l("@openmrs/esm-patient-common-lib","8.1.1-pre.5183",(()=>Promise.all([w.e(617),w.e(901),w.e(72),w.e(465),w.e(385),w.e(70)]).then((()=>()=>w(8617))))),l("dayjs","1.11.11",(()=>w.e(353).then((()=>()=>w(4353))))),l("i18next","23.11.4",(()=>w.e(635).then((()=>()=>w(2635))))),l("react-i18next","11.18.6",(()=>Promise.all([w.e(422),w.e(72)]).then((()=>()=>w(4422))))),l("react","18.3.1",(()=>w.e(540).then((()=>()=>w(6540))))),l("swr/_internal","2.2.5",(()=>Promise.all([w.e(993),w.e(72)]).then((()=>()=>w(4993))))),l("swr/immutable","2.2.5",(()=>Promise.all([w.e(225),w.e(72),w.e(465)]).then((()=>()=>w(4225))))),l("swr/infinite","2.2.5",(()=>Promise.all([w.e(41),w.e(72),w.e(465)]).then((()=>()=>w(3041)))))),e[t]=s.length?Promise.all(s).then((()=>e[t]=1)):1}}})(),(()=>{var e;w.g.importScripts&&(e=w.g.location+"");var r=w.g.document;if(!e&&r&&(r.currentScript&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var n=t.length-1;n>-1&&(!e||!/^http(s?):/.test(e));)e=t[n--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),w.p=e})(),o=e=>{var r=e=>e.split(".").map((e=>+e==e?+e:e)),t=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(e),n=t[1]?r(t[1]):[];return t[2]&&(n.length++,n.push.apply(n,r(t[2]))),t[3]&&(n.push([]),n.push.apply(n,r(t[3]))),n},i=(e,r)=>{e=o(e),r=o(r);for(var t=0;;){if(t>=e.length)return t<r.length&&"u"!=(typeof r[t])[0];var n=e[t],i=(typeof n)[0];if(t>=r.length)return"u"==i;var a=r[t],l=(typeof a)[0];if(i!=l)return"o"==i&&"n"==l||"s"==l||"u"==i;if("o"!=i&&"u"!=i&&n!=a)return n<a;t++}},a=e=>{var r=e[0],t="";if(1===e.length)return"*";if(r+.5){t+=0==r?">=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var n=1,o=1;o<e.length;o++)n--,t+="u"==(typeof(l=e[o]))[0]?"-":(n>0?".":"")+(n=2,l);return t}var i=[];for(o=1;o<e.length;o++){var l=e[o];i.push(0===l?"not("+s()+")":1===l?"("+s()+" || "+s()+")":2===l?i.pop()+" "+i.pop():a(l))}return s();function s(){return i.pop().replace(/^\((.+)\)$/,"$1")}},l=(e,r)=>{if(0 in e){r=o(r);var t=e[0],n=t<0;n&&(t=-t-1);for(var i=0,a=1,s=!0;;a++,i++){var u,f,p=a<e.length?(typeof e[a])[0]:"";if(i>=r.length||"o"==(f=(typeof(u=r[i]))[0]))return!s||("u"==p?a>t&&!n:""==p!=n);if("u"==f){if(!s||"u"!=p)return!1}else if(s)if(p==f)if(a<=t){if(u!=e[a])return!1}else{if(n?u>e[a]:u<e[a])return!1;u!=e[a]&&(s=!1)}else if("s"!=p&&"n"!=p){if(n||a<=t)return!1;s=!1,a--}else{if(a<=t||f<p!=n)return!1;s=!1}else"s"!=p&&"n"!=p&&(s=!1,a--)}}var d=[],c=d.pop.bind(d);for(i=1;i<e.length;i++){var h=e[i];d.push(1==h?c()|c():2==h?c()&c():h?l(h,r):!c())}return!!c()},s=(e,r)=>{var t=e[r];return Object.keys(t).reduce(((e,r)=>!e||!t[e].loaded&&i(e,r)?r:e),0)},u=(e,r,t,n)=>"Unsatisfied version "+t+" from "+(t&&e[r][t].from)+" of shared singleton module "+r+" (required "+a(n)+")",f=(e,r,t,n)=>{var o=s(e,t);return l(n,o)||p(u(e,t,o,n)),d(e[t][o])},p=e=>{"undefined"!=typeof console&&console.warn&&console.warn(e)},d=e=>(e.loaded=1,e.get()),c=(e=>function(r,t,n,o){var i=w.I(r);return i&&i.then?i.then(e.bind(e,r,w.S[r],t,n,o)):e(0,w.S[r],t,n,o)})(((e,r,t,n,o)=>r&&w.o(r,t)?f(r,0,t,n):o())),h={},m={6072:()=>c("default","react",[1,18],(()=>w.e(540).then((()=>()=>w(6540))))),6766:()=>c("default","i18next",[1,23],(()=>w.e(635).then((()=>()=>w(2635))))),8465:()=>c("default","swr/_internal",[1,2],(()=>w.e(993).then((()=>()=>w(4993))))),3941:()=>c("default","react-i18next",[1,11],(()=>w.e(422).then((()=>()=>w(4422))))),5972:()=>c("default","@openmrs/esm-framework",[1,5],(()=>Promise.all([w.e(151),w.e(766)]).then((()=>()=>w(5151))))),6656:()=>c("default","@openmrs/esm-patient-common-lib",[1,8],(()=>Promise.all([w.e(617),w.e(465)]).then((()=>()=>w(8617))))),4209:()=>c("default","swr/immutable",[1,2],(()=>Promise.all([w.e(225),w.e(465)]).then((()=>()=>w(4225))))),231:()=>c("default","dayjs",[1,1],(()=>w.e(353).then((()=>()=>w(4353))))),6339:()=>c("default","swr/infinite",[1,2],(()=>Promise.all([w.e(41),w.e(465)]).then((()=>()=>w(3041)))))},v={70:[4209],72:[6072],385:[3941,5972,6656],465:[8465],572:[231,4209,6339],766:[6766]},g={},w.f.consumes=(e,r)=>{w.o(v,e)&&v[e].forEach((e=>{if(w.o(h,e))return r.push(h[e]);if(!g[e]){var t=r=>{h[e]=0,w.m[e]=t=>{delete w.c[e],t.exports=r()}};g[e]=!0;var n=r=>{delete h[e],w.m[e]=t=>{throw delete w.c[e],r}};try{var o=m[e]();o.then?r.push(h[e]=o.then(t).catch(n)):t(o)}catch(e){n(e)}}}))},(()=>{var e={719:0};w.f.j=(r,t)=>{var n=w.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else if(/^(385|465|72|766)$/.test(r))e[r]=0;else{var o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var i=w.p+w.u(r),a=new Error;w.l(i,(t=>{if(w.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;a.message="Loading chunk "+r+" failed.\n("+o+": "+i+")",a.name="ChunkLoadError",a.type=o,a.request=i,n[1](a)}}),"chunk-"+r,r)}};var r=(r,t)=>{var n,o,[i,a,l]=t,s=0;if(i.some((r=>0!==e[r]))){for(n in a)w.o(a,n)&&(w.m[n]=a[n]);l&&l(w)}for(r&&r(t);s<i.length;s++)o=i[s],w.o(e,o)&&e[o]&&e[o][0](),e[o]=0},t=globalThis.webpackChunk_openmrs_esm_form_engine_lib=globalThis.webpackChunk_openmrs_esm_form_engine_lib||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),w.nc=void 0;var _=w(8008);_openmrs_esm_form_engine_lib=_})();
1
+ var _openmrs_esm_form_engine_lib;(()=>{"use strict";var e,r,t,n,o,i,a,l,s,u,f,p,d,c,h,m,v,g,b={8008:(e,r,t)=>{var n={"./start":()=>Promise.all([t.e(901),t.e(420),t.e(72),t.e(385),t.e(514)]).then((()=>()=>t(5514)))},o=(e,r)=>(t.R=r,r=t.o(n,e)?n[e]():Promise.resolve().then((()=>{throw new Error('Module "'+e+'" does not exist in container.')})),t.R=void 0,r),i=(e,r)=>{if(t.S){var n="default",o=t.S[n];if(o&&o!==e)throw new Error("Container initialization failed as it has already been initialized with a different share scope");return t.S[n]=e,t.I(n,r)}};t.d(r,{get:()=>o,init:()=>i})}},y={};function w(e){var r=y[e];if(void 0!==r)return r.exports;var t=y[e]={id:e,loaded:!1,exports:{}};return b[e].call(t.exports,t,t.exports,w),t.loaded=!0,t.exports}w.m=b,w.c=y,w.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return w.d(r,{a:r}),r},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,w.t=function(t,n){if(1&n&&(t=this(t)),8&n)return t;if("object"==typeof t&&t){if(4&n&&t.__esModule)return t;if(16&n&&"function"==typeof t.then)return t}var o=Object.create(null);w.r(o);var i={};e=e||[null,r({}),r([]),r(r)];for(var a=2&n&&t;"object"==typeof a&&!~e.indexOf(a);a=r(a))Object.getOwnPropertyNames(a).forEach((e=>i[e]=()=>t[e]));return i.default=()=>t,w.d(o,i),o},w.d=(e,r)=>{for(var t in r)w.o(r,t)&&!w.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},w.f={},w.e=e=>Promise.all(Object.keys(w.f).reduce(((r,t)=>(w.f[t](e,r),r)),[])),w.u=e=>e+".js",w.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),w.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),t={},n="@openmrs/esm-form-engine-lib:",w.l=(e,r,o,i)=>{if(t[e])t[e].push(r);else{var a,l;if(void 0!==o)for(var s=document.getElementsByTagName("script"),u=0;u<s.length;u++){var f=s[u];if(f.getAttribute("src")==e||f.getAttribute("data-webpack")==n+o){a=f;break}}a||(l=!0,(a=document.createElement("script")).charset="utf-8",a.timeout=120,w.nc&&a.setAttribute("nonce",w.nc),a.setAttribute("data-webpack",n+o),a.src=e),t[e]=[r];var p=(r,n)=>{a.onerror=a.onload=null,clearTimeout(d);var o=t[e];if(delete t[e],a.parentNode&&a.parentNode.removeChild(a),o&&o.forEach((e=>e(n))),r)return r(n)},d=setTimeout(p.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=p.bind(null,a.onerror),a.onload=p.bind(null,a.onload),l&&document.head.appendChild(a)}},w.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},w.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{w.S={};var e={},r={};w.I=(t,n)=>{n||(n=[]);var o=r[t];if(o||(o=r[t]={}),!(n.indexOf(o)>=0)){if(n.push(o),e[t])return e[t];w.o(w.S,t)||(w.S[t]={});var i=w.S[t],a="@openmrs/esm-form-engine-lib",l=(e,r,t,n)=>{var o=i[e]=i[e]||{},l=o[r];(!l||!l.loaded&&(!n!=!l.eager?n:a>l.from))&&(o[r]={get:t,from:a,eager:!!n})},s=[];return"default"===t&&(l("@openmrs/esm-framework","5.8.2-pre.2368",(()=>Promise.all([w.e(151),w.e(72),w.e(766)]).then((()=>()=>w(5151))))),l("@openmrs/esm-patient-common-lib","8.1.1-pre.5183",(()=>Promise.all([w.e(617),w.e(901),w.e(72),w.e(465),w.e(385),w.e(70)]).then((()=>()=>w(8617))))),l("dayjs","1.11.11",(()=>w.e(353).then((()=>()=>w(4353))))),l("i18next","23.11.4",(()=>w.e(635).then((()=>()=>w(2635))))),l("react-i18next","11.18.6",(()=>Promise.all([w.e(422),w.e(72)]).then((()=>()=>w(4422))))),l("react","18.3.1",(()=>w.e(540).then((()=>()=>w(6540))))),l("swr/_internal","2.2.5",(()=>Promise.all([w.e(993),w.e(72)]).then((()=>()=>w(4993))))),l("swr/immutable","2.2.5",(()=>Promise.all([w.e(225),w.e(72),w.e(465)]).then((()=>()=>w(4225))))),l("swr/infinite","2.2.5",(()=>Promise.all([w.e(41),w.e(72),w.e(465)]).then((()=>()=>w(3041)))))),e[t]=s.length?Promise.all(s).then((()=>e[t]=1)):1}}})(),(()=>{var e;w.g.importScripts&&(e=w.g.location+"");var r=w.g.document;if(!e&&r&&(r.currentScript&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var n=t.length-1;n>-1&&(!e||!/^http(s?):/.test(e));)e=t[n--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),w.p=e})(),o=e=>{var r=e=>e.split(".").map((e=>+e==e?+e:e)),t=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(e),n=t[1]?r(t[1]):[];return t[2]&&(n.length++,n.push.apply(n,r(t[2]))),t[3]&&(n.push([]),n.push.apply(n,r(t[3]))),n},i=(e,r)=>{e=o(e),r=o(r);for(var t=0;;){if(t>=e.length)return t<r.length&&"u"!=(typeof r[t])[0];var n=e[t],i=(typeof n)[0];if(t>=r.length)return"u"==i;var a=r[t],l=(typeof a)[0];if(i!=l)return"o"==i&&"n"==l||"s"==l||"u"==i;if("o"!=i&&"u"!=i&&n!=a)return n<a;t++}},a=e=>{var r=e[0],t="";if(1===e.length)return"*";if(r+.5){t+=0==r?">=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var n=1,o=1;o<e.length;o++)n--,t+="u"==(typeof(l=e[o]))[0]?"-":(n>0?".":"")+(n=2,l);return t}var i=[];for(o=1;o<e.length;o++){var l=e[o];i.push(0===l?"not("+s()+")":1===l?"("+s()+" || "+s()+")":2===l?i.pop()+" "+i.pop():a(l))}return s();function s(){return i.pop().replace(/^\((.+)\)$/,"$1")}},l=(e,r)=>{if(0 in e){r=o(r);var t=e[0],n=t<0;n&&(t=-t-1);for(var i=0,a=1,s=!0;;a++,i++){var u,f,p=a<e.length?(typeof e[a])[0]:"";if(i>=r.length||"o"==(f=(typeof(u=r[i]))[0]))return!s||("u"==p?a>t&&!n:""==p!=n);if("u"==f){if(!s||"u"!=p)return!1}else if(s)if(p==f)if(a<=t){if(u!=e[a])return!1}else{if(n?u>e[a]:u<e[a])return!1;u!=e[a]&&(s=!1)}else if("s"!=p&&"n"!=p){if(n||a<=t)return!1;s=!1,a--}else{if(a<=t||f<p!=n)return!1;s=!1}else"s"!=p&&"n"!=p&&(s=!1,a--)}}var d=[],c=d.pop.bind(d);for(i=1;i<e.length;i++){var h=e[i];d.push(1==h?c()|c():2==h?c()&c():h?l(h,r):!c())}return!!c()},s=(e,r)=>{var t=e[r];return Object.keys(t).reduce(((e,r)=>!e||!t[e].loaded&&i(e,r)?r:e),0)},u=(e,r,t,n)=>"Unsatisfied version "+t+" from "+(t&&e[r][t].from)+" of shared singleton module "+r+" (required "+a(n)+")",f=(e,r,t,n)=>{var o=s(e,t);return l(n,o)||p(u(e,t,o,n)),d(e[t][o])},p=e=>{"undefined"!=typeof console&&console.warn&&console.warn(e)},d=e=>(e.loaded=1,e.get()),c=(e=>function(r,t,n,o){var i=w.I(r);return i&&i.then?i.then(e.bind(e,r,w.S[r],t,n,o)):e(0,w.S[r],t,n,o)})(((e,r,t,n,o)=>r&&w.o(r,t)?f(r,0,t,n):o())),h={},m={6072:()=>c("default","react",[1,18],(()=>w.e(540).then((()=>()=>w(6540))))),6766:()=>c("default","i18next",[1,23],(()=>w.e(635).then((()=>()=>w(2635))))),8465:()=>c("default","swr/_internal",[1,2],(()=>w.e(993).then((()=>()=>w(4993))))),3941:()=>c("default","react-i18next",[1,11],(()=>w.e(422).then((()=>()=>w(4422))))),5972:()=>c("default","@openmrs/esm-framework",[1,5],(()=>Promise.all([w.e(151),w.e(766)]).then((()=>()=>w(5151))))),6656:()=>c("default","@openmrs/esm-patient-common-lib",[1,8],(()=>Promise.all([w.e(617),w.e(465)]).then((()=>()=>w(8617))))),4209:()=>c("default","swr/immutable",[1,2],(()=>Promise.all([w.e(225),w.e(465)]).then((()=>()=>w(4225))))),231:()=>c("default","dayjs",[1,1],(()=>w.e(353).then((()=>()=>w(4353))))),6339:()=>c("default","swr/infinite",[1,2],(()=>Promise.all([w.e(41),w.e(465)]).then((()=>()=>w(3041)))))},v={70:[4209],72:[6072],385:[3941,5972,6656],465:[8465],514:[231,4209,6339],766:[6766]},g={},w.f.consumes=(e,r)=>{w.o(v,e)&&v[e].forEach((e=>{if(w.o(h,e))return r.push(h[e]);if(!g[e]){var t=r=>{h[e]=0,w.m[e]=t=>{delete w.c[e],t.exports=r()}};g[e]=!0;var n=r=>{delete h[e],w.m[e]=t=>{throw delete w.c[e],r}};try{var o=m[e]();o.then?r.push(h[e]=o.then(t).catch(n)):t(o)}catch(e){n(e)}}}))},(()=>{var e={719:0};w.f.j=(r,t)=>{var n=w.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else if(/^(385|465|72|766)$/.test(r))e[r]=0;else{var o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var i=w.p+w.u(r),a=new Error;w.l(i,(t=>{if(w.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;a.message="Loading chunk "+r+" failed.\n("+o+": "+i+")",a.name="ChunkLoadError",a.type=o,a.request=i,n[1](a)}}),"chunk-"+r,r)}};var r=(r,t)=>{var n,o,[i,a,l]=t,s=0;if(i.some((r=>0!==e[r]))){for(n in a)w.o(a,n)&&(w.m[n]=a[n]);l&&l(w)}for(r&&r(t);s<i.length;s++)o=i[s],w.o(e,o)&&e[o]&&e[o][0](),e[o]=0},t=globalThis.webpackChunk_openmrs_esm_form_engine_lib=globalThis.webpackChunk_openmrs_esm_form_engine_lib||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),w.nc=void 0;var _=w(8008);_openmrs_esm_form_engine_lib=_})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-form-engine-lib",
3
- "version": "2.1.0-pre.1501",
3
+ "version": "2.1.0-pre.1502",
4
4
  "description": "React Form Engine for O3",
5
5
  "browser": "dist/openmrs-esm-form-engine-lib.js",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,5 @@
1
1
  import React from 'react';
2
2
  import { act, fireEvent, render, screen } from '@testing-library/react';
3
- import { type EncounterContext, FormContext } from '../../../form-context';
4
3
  import Dropdown from './dropdown.component';
5
4
  import { type FormField } from '../../../types';
6
5
 
@@ -29,7 +28,7 @@ const question: FormField = {
29
28
  id: 'patient-past-program',
30
29
  };
31
30
 
32
- const encounterContext: EncounterContext = {
31
+ const encounterContext = {
33
32
  patient: {
34
33
  id: '833db896-c1f0-11eb-8529-0242ac130003',
35
34
  },
@@ -154,4 +153,4 @@ describe.skip('dropdown input field', () => {
154
153
  });
155
154
  });
156
155
  });
157
- });
156
+ });
@@ -1,87 +1,89 @@
1
- import React, { useEffect, useMemo, useRef, useState } from 'react';
1
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import debounce from 'lodash-es/debounce';
3
- import { ComboBox, DropdownSkeleton, Layer } from '@carbon/react';
3
+ import { ComboBox, DropdownSkeleton, Layer, InlineLoading } from '@carbon/react';
4
4
  import { isTrue } from '../../../utils/boolean-utils';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { getRegisteredDataSource } from '../../../registry/registry';
7
7
  import { getControlTemplate } from '../../../registry/inbuilt-components/control-templates';
8
- import { type FormFieldInputProps } from '../../../types';
8
+ import { type DataSource, type FormFieldInputProps } from '../../../types';
9
9
  import { isEmpty } from '../../../validators/form-validator';
10
10
  import { shouldUseInlineLayout } from '../../../utils/form-helper';
11
11
  import FieldValueView from '../../value/view/field-value-view.component';
12
12
  import styles from './ui-select-extended.scss';
13
13
  import { useFormProviderContext } from '../../../provider/form-provider';
14
14
  import FieldLabel from '../../field-label/field-label.component';
15
- import useDataSourceDependentValue from '../../../hooks/useDatasourceDependentValue';
16
15
  import { useWatch } from 'react-hook-form';
16
+ import useDataSourceDependentValue from '../../../hooks/useDataSourceDependentValue';
17
+ import { isViewMode } from '../../../utils/common-utils';
18
+ import { type OpenmrsResource } from '@openmrs/esm-framework';
17
19
 
18
20
  const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnings, setFieldValue }) => {
19
21
  const { t } = useTranslation();
20
22
  const [items, setItems] = useState([]);
21
23
  const [isLoading, setIsLoading] = useState(false);
24
+ const [isSearching, setIsSearching] = useState(false);
22
25
  const [searchTerm, setSearchTerm] = useState('');
23
26
  const isProcessingSelection = useRef(false);
24
27
  const [dataSource, setDataSource] = useState(null);
25
28
  const [config, setConfig] = useState({});
26
- const [savedSearchableItem, setSavedSearchableItem] = useState({});
27
29
  const dataSourceDependentValue = useDataSourceDependentValue(field);
30
+ const isSearchable = isTrue(field.questionOptions.isSearchable);
28
31
  const {
29
32
  layoutType,
30
33
  sessionMode,
31
34
  workspaceLayout,
32
- methods: { control },
35
+ methods: { control, getFieldState },
33
36
  } = useFormProviderContext();
34
37
 
35
38
  const value = useWatch({ control, name: field.id, exact: true });
39
+ const { isDirty } = getFieldState(field.id);
36
40
 
37
41
  const isInline = useMemo(() => {
38
- if (['view', 'embedded-view'].includes(sessionMode) || isTrue(field.readonly)) {
42
+ if (isViewMode(sessionMode) || isTrue(field.readonly)) {
39
43
  return shouldUseInlineLayout(field.inlineRendering, layoutType, workspaceLayout, sessionMode);
40
44
  }
41
45
  return false;
42
46
  }, [sessionMode, field.readonly, field.inlineRendering, layoutType, workspaceLayout]);
43
47
 
44
- useEffect(() => {
45
- const dataSource = field.questionOptions?.datasource?.name;
46
- setConfig(
47
- dataSource
48
- ? field.questionOptions.datasource?.config
49
- : getControlTemplate(field.questionOptions.rendering)?.datasource?.config,
50
- );
51
- getRegisteredDataSource(dataSource ? dataSource : field.questionOptions.rendering).then((ds) => setDataSource(ds));
52
- }, [field.questionOptions?.datasource]);
48
+ const selectedItem = useMemo(() => items.find((item) => item.uuid == value) || null, [items, value]);
53
49
 
54
- const selectedItem = useMemo(() => items.find((item) => item.uuid == value), [items, value]);
55
-
56
- const debouncedSearch = debounce((searchTerm, dataSource) => {
57
- setItems([]);
58
- setIsLoading(true);
50
+ const debouncedSearch = debounce((searchTerm: string, dataSource: DataSource<OpenmrsResource>) => {
51
+ setIsSearching(true);
59
52
  dataSource
60
53
  .fetchData(searchTerm, config)
61
54
  .then((dataItems) => {
62
- setItems(dataItems.map(dataSource.toUuidAndDisplay));
63
- setIsLoading(false);
55
+ if (dataItems.length) {
56
+ const currentSelectedItem = items.find((item) => item.uuid == value);
57
+ const newItems = dataItems.map(dataSource.toUuidAndDisplay);
58
+ if (currentSelectedItem && !newItems.some((item) => item.uuid == currentSelectedItem.uuid)) {
59
+ newItems.unshift(currentSelectedItem);
60
+ }
61
+ setItems(newItems);
62
+ }
63
+ setIsSearching(false);
64
64
  })
65
65
  .catch((err) => {
66
66
  console.error(err);
67
- setIsLoading(false);
68
- setItems([]);
67
+ setIsSearching(false);
69
68
  });
70
69
  }, 300);
71
70
 
72
- const processSearchableValues = (value) => {
73
- dataSource
74
- .fetchData(null, config, value)
75
- .then((dataItem) => {
76
- setSavedSearchableItem(dataItem);
77
- setIsLoading(false);
78
- })
79
- .catch((err) => {
80
- console.error(err);
81
- setIsLoading(false);
82
- setItems([]);
83
- });
84
- };
71
+ const searchTermHasMatchingItem = useCallback(
72
+ (searchTerm: string) => {
73
+ return items.some((item) => item.display?.toLowerCase().includes(searchTerm.toLowerCase()));
74
+ },
75
+ [items],
76
+ );
77
+
78
+ useEffect(() => {
79
+ const dataSource = field.questionOptions?.datasource?.name;
80
+ setConfig(
81
+ dataSource
82
+ ? field.questionOptions.datasource?.config
83
+ : getControlTemplate(field.questionOptions.rendering)?.datasource?.config,
84
+ );
85
+ getRegisteredDataSource(dataSource ? dataSource : field.questionOptions.rendering).then((ds) => setDataSource(ds));
86
+ }, [field.questionOptions?.datasource]);
85
87
 
86
88
  useEffect(() => {
87
89
  // If not searchable, preload the items
@@ -103,29 +105,32 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
103
105
  }, [dataSource, config, dataSourceDependentValue]);
104
106
 
105
107
  useEffect(() => {
106
- if (dataSource && isTrue(field.questionOptions.isSearchable) && !isEmpty(searchTerm)) {
108
+ if (dataSource && isSearchable && !isEmpty(searchTerm) && !searchTermHasMatchingItem(searchTerm)) {
107
109
  debouncedSearch(searchTerm, dataSource);
108
110
  }
109
111
  }, [dataSource, searchTerm, config]);
110
112
 
111
113
  useEffect(() => {
112
- if (
113
- dataSource &&
114
- isTrue(field.questionOptions.isSearchable) &&
115
- isEmpty(searchTerm) &&
116
- value &&
117
- !Object.keys(savedSearchableItem).length
118
- ) {
114
+ if (value && !isDirty && dataSource && isSearchable && sessionMode !== 'enter' && !items.length) {
115
+ // While in edit mode, search-based instances should fetch the initial item (previously selected value) to resolve its display property
119
116
  setIsLoading(true);
120
- processSearchableValues(value);
117
+ try {
118
+ dataSource.fetchSingleItem(value).then((item) => {
119
+ setItems([dataSource.toUuidAndDisplay(item)]);
120
+ setIsLoading(false);
121
+ });
122
+ } catch (error) {
123
+ console.error(error);
124
+ setIsLoading(false);
125
+ }
121
126
  }
122
- }, [value]);
127
+ }, [value, isDirty, sessionMode, dataSource, isSearchable, items]);
123
128
 
124
129
  if (isLoading) {
125
130
  return <DropdownSkeleton />;
126
131
  }
127
132
 
128
- return sessionMode == 'view' || sessionMode == 'embedded-view' || isTrue(field.readonly) ? (
133
+ return isViewMode(sessionMode) || isTrue(field.readonly) ? (
129
134
  <FieldValueView
130
135
  label={t(field.label)}
131
136
  value={value ? items.find((item) => item.uuid == value)?.display : value}
@@ -142,6 +147,7 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
142
147
  items={items}
143
148
  itemToString={(item) => item?.display}
144
149
  selectedItem={selectedItem}
150
+ placeholder={isSearchable ? t('search', 'Search') + '...' : null}
145
151
  shouldFilterItem={({ item, inputValue }) => {
146
152
  if (!inputValue) {
147
153
  // Carbon's initial call at component mount
@@ -165,7 +171,7 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
165
171
  isProcessingSelection.current = false;
166
172
  return;
167
173
  }
168
- if (field.questionOptions['isSearchable']) {
174
+ if (field.questionOptions.isSearchable) {
169
175
  setSearchTerm(value);
170
176
  }
171
177
  }}
@@ -178,6 +184,7 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
178
184
  }
179
185
  }}
180
186
  />
187
+ {isSearching && <InlineLoading className={styles.loader} description={t('searching', 'Searching') + '...'} />}
181
188
  </Layer>
182
189
  </div>
183
190
  )
@@ -13,3 +13,7 @@
13
13
  color: colors.$red-60;
14
14
  font-weight: 600;
15
15
  }
16
+
17
+ .loader {
18
+ padding: 0.25rem 0.5rem;
19
+ }
@@ -1,211 +1,281 @@
1
1
  import React from 'react';
2
- import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
3
- import UiSelectExtended from './ui-select-extended.component';
4
- import { type EncounterContext, FormContext } from '../../../form-context';
5
- import { type FormField } from '../../../types';
6
-
7
- const questions: FormField[] = [
8
- {
9
- label: 'Transfer Location',
10
- type: 'obs',
11
- questionOptions: {
12
- rendering: 'ui-select-extended',
13
- concept: '160540AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
14
- datasource: {
15
- name: 'location_datasource',
16
- config: {
17
- tag: 'test-tag',
18
- },
19
- },
20
- },
21
- meta: {},
22
- id: 'patient_transfer_location',
23
- },
24
- {
25
- label: 'Select criteria for new WHO stage:',
26
- type: 'obs',
27
- questionOptions: {
28
- concept: '250e87b6-beb7-44a1-93a1-d3dd74d7e372',
29
- rendering: 'select-concept-answers',
30
- datasource: {
31
- name: 'select_concept_answers_datasource',
32
- config: {
33
- concept: '250e87b6-beb7-44a1-93a1-d3dd74d7e372',
34
- },
35
- },
36
- },
37
- validators: [],
38
- id: '__sq5ELJr7p',
39
- },
40
- ];
41
-
42
- const encounterContext: EncounterContext = {
43
- patient: {
44
- id: '833db896-c1f0-11eb-8529-0242ac130003',
45
- },
46
- location: {
47
- uuid: '41e6e516-c1f0-11eb-8529-0242ac130003',
48
- },
49
- encounter: {
50
- uuid: '873455da-3ec4-453c-b565-7c1fe35426be',
51
- obs: [],
52
- },
53
- sessionMode: 'enter',
54
- encounterDate: new Date(2023, 8, 29),
55
- setEncounterDate: (value) => {},
56
- encounterProvider: '2c95f6f5-788e-4e73-9079-5626911231fa',
57
- setEncounterProvider: jest.fn,
58
- setEncounterLocation: jest.fn,
59
- encounterRole: '8cb3a399-d18b-4b62-aefb-5a0f948a3809',
60
- setEncounterRole: jest.fn,
61
- };
2
+ import { act, render, screen } from '@testing-library/react';
3
+ import { type FormSchema, type SessionMode, type OpenmrsEncounter } from '../../../types';
4
+ import { usePatient, useSession } from '@openmrs/esm-framework';
5
+ import { mockPatient } from '../../../../__mocks__/patient.mock';
6
+ import { mockSessionDataResponse } from '../../../../__mocks__/session.mock';
7
+ import { FormEngine } from '../../..';
8
+ import uiSelectExtForm from '../../../../__mocks__/forms/rfe-forms/sample_ui-select-ext.json';
9
+ import { assertFormHasAllFields, findSelectInput } from '../../../utils/test-utils';
10
+ import userEvent from '@testing-library/user-event';
11
+ import * as api from '../../../api';
62
12
 
63
- const renderForm = (initialValues) => {
64
- render(<></>);
65
- };
13
+ const mockUsePatient = jest.mocked(usePatient);
14
+ const mockUseSession = jest.mocked(useSession);
15
+ global.ResizeObserver = require('resize-observer-polyfill');
16
+
17
+ jest.mock('../../../hooks/useRestMaxResultsCount', () => jest.fn().mockReturnValue({ systemSetting: { value: '50' } }));
18
+ jest.mock('lodash-es/debounce', () => jest.fn((fn) => fn));
19
+
20
+ jest.mock('../../../api', () => {
21
+ const originalModule = jest.requireActual('../../../api');
22
+ return {
23
+ ...originalModule,
24
+ getPreviousEncounter: jest.fn().mockImplementation(() => Promise.resolve(null)),
25
+ getConcept: jest.fn().mockImplementation(() => Promise.resolve(null)),
26
+ saveEncounter: jest.fn(),
27
+ };
28
+ });
29
+
30
+ jest.mock('../../../hooks/useEncounterRole', () => ({
31
+ useEncounterRole: jest.fn().mockReturnValue({
32
+ isLoading: false,
33
+ encounterRole: { name: 'Clinician', uuid: 'clinician-uuid' },
34
+ error: undefined,
35
+ }),
36
+ }));
37
+
38
+ jest.mock('../../../hooks/useEncounter', () => ({
39
+ useEncounter: jest.fn().mockImplementation((formJson: FormSchema) => {
40
+ return {
41
+ encounter: formJson.encounter ? (encounter as OpenmrsEncounter) : null,
42
+ isLoading: false,
43
+ error: undefined,
44
+ };
45
+ }),
46
+ }));
66
47
 
67
- // Mock the data source fetch behavior
68
- jest.mock('../../../registry/registry', () => ({
69
- getRegisteredDataSource: jest.fn().mockResolvedValue({
70
- fetchData: jest.fn().mockImplementation((...args) => {
71
- if (args[1].concept) {
48
+ jest.mock('../../../hooks/useConcepts', () => ({
49
+ useConcepts: jest.fn().mockImplementation((references: Set<string>) => {
50
+ return {
51
+ isLoading: false,
52
+ concepts: [],
53
+ error: undefined,
54
+ };
55
+ }),
56
+ }));
57
+
58
+ jest.mock('../../../registry/registry', () => {
59
+ const originalModule = jest.requireActual('../../../registry/registry');
60
+ return {
61
+ ...originalModule,
62
+ getRegisteredDataSource: jest.fn().mockResolvedValue({
63
+ fetchData: jest.fn().mockImplementation((...args) => {
64
+ if (args[1].class?.length) {
65
+ // concept DS
66
+ return Promise.resolve([
67
+ {
68
+ uuid: 'stage-1-uuid',
69
+ display: 'stage 1',
70
+ },
71
+ {
72
+ uuid: 'stage-2-uuid',
73
+ display: 'stage 2',
74
+ },
75
+ ]);
76
+ }
77
+
78
+ // location DS
72
79
  return Promise.resolve([
73
80
  {
74
- uuid: 'stage-1-uuid',
75
- display: 'stage 1',
81
+ uuid: 'aaa-1',
82
+ display: 'Kololo',
83
+ },
84
+ {
85
+ uuid: 'aaa-2',
86
+ display: 'Naguru',
76
87
  },
77
88
  {
78
- uuid: 'stage-2-uuid',
79
- display: 'stage 2',
89
+ uuid: 'aaa-3',
90
+ display: 'Muyenga',
80
91
  },
81
92
  ]);
82
- }
83
-
84
- return Promise.resolve([
85
- {
86
- uuid: 'aaa-1',
87
- display: 'Kololo',
88
- },
89
- {
90
- uuid: 'aaa-2',
91
- display: 'Naguru',
92
- },
93
- {
94
- uuid: 'aaa-3',
95
- display: 'Muyenga',
96
- },
97
- ]);
93
+ }),
94
+ fetchSingleItem: jest.fn().mockImplementation((uuid: string) => {
95
+ return Promise.resolve({
96
+ uuid,
97
+ display: 'stage 1',
98
+ });
99
+ }),
100
+ toUuidAndDisplay: (data) => data,
98
101
  }),
99
- toUuidAndDisplay: (data) => data,
100
- }),
101
- }));
102
+ };
103
+ });
102
104
 
103
- describe.skip('UiSelectExtended Component', () => {
104
- it('renders with items from the datasource', async () => {
105
- await act(async () => {
106
- await renderForm({});
107
- });
105
+ const encounter = {
106
+ uuid: 'encounter-uuid',
107
+ obs: [
108
+ {
109
+ concept: {
110
+ uuid: '160540AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
111
+ },
112
+ value: 'aaa-2',
113
+ formFieldNamespace: 'rfe-forms',
114
+ formFieldPath: 'rfe-forms-patient_transfer_location',
115
+ uuid: 'obs-uuid-1',
116
+ },
117
+ {
118
+ concept: {
119
+ uuid: '4b59ac07-cf72-4f46-b8c0-4f62b1779f7e',
120
+ },
121
+ value: 'stage-1-uuid',
122
+ formFieldNamespace: 'rfe-forms',
123
+ formFieldPath: 'rfe-forms-problem',
124
+ uuid: 'obs-uuid-2',
125
+ },
126
+ ],
127
+ };
108
128
 
109
- // setup
110
- const uiSelectExtendedWidget = screen.getByLabelText('Transfer Location');
129
+ const renderForm = (mode: SessionMode = 'enter') => {
130
+ render(
131
+ <FormEngine
132
+ formJson={uiSelectExtForm as FormSchema}
133
+ patientUUID="8673ee4f-e2ab-4077-ba55-4980f408773e"
134
+ mode={mode}
135
+ encounterUUID={mode === 'edit' ? 'encounter-uuid' : null}
136
+ />,
137
+ );
138
+ };
139
+
140
+ describe('UiSelectExtended', () => {
141
+ const user = userEvent.setup();
111
142
 
112
- // assert initial values
113
- expect(questions[0].meta.submission).toBe(undefined);
143
+ beforeEach(() => {
144
+ Object.defineProperty(window, 'i18next', {
145
+ writable: true,
146
+ configurable: true,
147
+ value: {
148
+ language: 'en',
149
+ t: jest.fn(),
150
+ },
151
+ });
114
152
 
115
- //Click on the UiSelectExtendedWidget to open the dropdown
116
- fireEvent.click(uiSelectExtendedWidget);
153
+ mockUsePatient.mockImplementation(() => ({
154
+ patient: mockPatient,
155
+ isLoading: false,
156
+ error: undefined,
157
+ patientUuid: mockPatient.id,
158
+ }));
117
159
 
118
- // Assert that all three items are displayed
119
- expect(screen.getByText('Kololo')).toBeInTheDocument();
120
- expect(screen.getByText('Naguru')).toBeInTheDocument();
121
- expect(screen.getByText('Muyenga')).toBeInTheDocument();
160
+ mockUseSession.mockImplementation(() => mockSessionDataResponse.data);
122
161
  });
123
162
 
124
- it('renders with items from the datasource of select-concept-answers rendering', async () => {
125
- await act(async () => {
126
- await renderForm({});
127
- });
163
+ describe('Enter/New mode', () => {
164
+ it('should render comboboxes correctly for both "non-searchable" and "searchable" instances', async () => {
165
+ await act(async () => {
166
+ renderForm();
167
+ });
128
168
 
129
- const uiSelectExtendedWidget = screen.getByLabelText(/Select criteria for new WHO stage:/i);
130
- fireEvent.click(uiSelectExtendedWidget);
169
+ await assertFormHasAllFields(screen, [
170
+ { fieldName: 'Transfer Location', fieldType: 'select' },
171
+ { fieldName: 'Problem', fieldType: 'select' },
172
+ ]);
131
173
 
132
- // Assert that all items are displayed
133
- expect(screen.getByText('stage 1')).toBeInTheDocument();
134
- expect(screen.getByText('stage 2')).toBeInTheDocument();
135
- });
174
+ // Test for "non-searchable" instance
175
+ const transferLocationSelect = await findSelectInput(screen, 'Transfer Location');
176
+ await user.click(transferLocationSelect);
177
+ expect(screen.getByText('Kololo')).toBeInTheDocument();
178
+ expect(screen.getByText('Naguru')).toBeInTheDocument();
179
+ expect(screen.getByText('Muyenga')).toBeInTheDocument();
136
180
 
137
- it('Selects a value from the list', async () => {
138
- await act(async () => {
139
- await renderForm({});
181
+ // Test for "searchable" instance
182
+ const problemSelect = await findSelectInput(screen, 'Problem');
183
+ expect(problemSelect).toHaveAttribute('placeholder', 'Search...');
140
184
  });
141
185
 
142
- // setup
143
- const uiSelectExtendedWidget = screen.getByLabelText('Transfer Location');
186
+ it('should be possible to select an item from the combobox and submit the form', async () => {
187
+ const mockSaveEncounter = jest.spyOn(api, 'saveEncounter');
144
188
 
145
- //Click on the UiSelectExtendedWidget to open the dropdown
146
- fireEvent.click(uiSelectExtendedWidget);
189
+ await act(async () => {
190
+ renderForm();
191
+ });
147
192
 
148
- // Find the list item for 'Naguru' and click it to select
149
- const naguruOption = screen.getByText('Naguru');
150
- fireEvent.click(naguruOption);
193
+ const transferLocationSelect = await findSelectInput(screen, 'Transfer Location');
194
+ await user.click(transferLocationSelect);
195
+ const naguruOption = screen.getByText('Naguru');
196
+ await user.click(naguruOption);
151
197
 
152
- // verify
153
- await act(async () => {
154
- expect(questions[0].meta.submission.newValue).toEqual({
155
- concept: '160540AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
156
- formFieldNamespace: 'rfe-forms',
157
- formFieldPath: 'rfe-forms-patient_transfer_location',
158
- value: 'aaa-2',
159
- });
160
- });
161
- });
198
+ // submit the form
199
+ await user.click(screen.getByRole('button', { name: /save/i }));
162
200
 
163
- it('Filters items based on user input', async () => {
164
- await act(async () => {
165
- await renderForm({});
201
+ expect(mockSaveEncounter).toHaveBeenCalledWith(
202
+ expect.any(AbortController),
203
+ expect.objectContaining({
204
+ obs: [
205
+ {
206
+ concept: '160540AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
207
+ formFieldNamespace: 'rfe-forms',
208
+ formFieldPath: 'rfe-forms-patient_transfer_location',
209
+ value: 'aaa-2',
210
+ },
211
+ ],
212
+ }),
213
+ undefined,
214
+ );
166
215
  });
167
216
 
168
- // setup
169
- const uiSelectExtendedWidget = screen.getByLabelText('Transfer Location');
217
+ it('should be possible to search and select an item from the search-box and submit the form', async () => {
218
+ const mockSaveEncounter = jest.spyOn(api, 'saveEncounter');
170
219
 
171
- //Click on the UiSelectExtendedWidget to open the dropdown
172
- fireEvent.click(uiSelectExtendedWidget);
220
+ await act(async () => {
221
+ renderForm();
222
+ });
173
223
 
174
- // Type 'Nag' in the input field to filter items
175
- fireEvent.change(uiSelectExtendedWidget, { target: { value: 'Nag' } });
224
+ const problemSelect = await findSelectInput(screen, 'Problem');
225
+ await user.click(problemSelect);
226
+ await user.type(problemSelect, 'stage');
227
+ expect(screen.getByText('stage 1')).toBeInTheDocument();
228
+ expect(screen.getByText('stage 2')).toBeInTheDocument();
229
+ // select the first option
230
+ await user.click(screen.getByText('stage 1'));
176
231
 
177
- // Wait for the filtered items to appear in the dropdown
178
- await waitFor(() => {
179
- // Verify that 'Naguru' is in the filtered items
180
- expect(screen.getByText('Naguru')).toBeInTheDocument();
232
+ // submit the form
233
+ await user.click(screen.getByRole('button', { name: /save/i }));
234
+
235
+ expect(mockSaveEncounter).toHaveBeenCalledWith(
236
+ expect.any(AbortController),
237
+ expect.objectContaining({
238
+ obs: [
239
+ {
240
+ concept: '4b59ac07-cf72-4f46-b8c0-4f62b1779f7e',
241
+ formFieldNamespace: 'rfe-forms',
242
+ formFieldPath: 'rfe-forms-problem',
243
+ value: 'stage-1-uuid',
244
+ },
245
+ ],
246
+ }),
247
+ undefined,
248
+ );
249
+ });
250
+
251
+ it('should filter items based on user input', async () => {
252
+ await act(async () => {
253
+ renderForm();
254
+ });
181
255
 
182
- // Verify that 'Kololo' and 'Muyenga' are not in the filtered items
256
+ const transferLocationSelect = await findSelectInput(screen, 'Transfer Location');
257
+ await user.click(transferLocationSelect);
258
+ await user.type(transferLocationSelect, 'Nag');
259
+
260
+ expect(screen.getByText('Naguru')).toBeInTheDocument();
183
261
  expect(screen.queryByText('Kololo')).not.toBeInTheDocument();
184
262
  expect(screen.queryByText('Muyenga')).not.toBeInTheDocument();
185
263
  });
186
264
  });
187
265
 
188
- it('Should set the correct value for the config parameter', async () => {
189
- // Mock the data source fetch behavior
190
- const expectedConfigValue = {
191
- tag: 'test-tag',
192
- };
266
+ describe('Edit mode', () => {
267
+ it('should initialize with the current value for both "non-searchable" and "searchable" instances', async () => {
268
+ await act(async () => {
269
+ renderForm('edit');
270
+ });
193
271
 
194
- // Mock the getRegisteredDataSource function
195
- jest.mock('../../../registry/registry', () => ({
196
- getRegisteredDataSource: jest.fn().mockResolvedValue({
197
- fetchData: jest.fn().mockResolvedValue([]),
198
- toUuidAndDisplay: (data) => data,
199
- config: expectedConfigValue,
200
- }),
201
- }));
272
+ // Non-searchable instance
273
+ const nonSearchableInstance = await findSelectInput(screen, 'Transfer Location');
274
+ expect(nonSearchableInstance).toHaveValue('Naguru');
202
275
 
203
- await act(async () => {
204
- await renderForm({});
276
+ // Searchable instance
277
+ const searchableInstance = await findSelectInput(screen, 'Problem');
278
+ expect(searchableInstance).toHaveValue('stage 1');
205
279
  });
206
- const config = questions[0].questionOptions.datasource.config;
207
-
208
- // Assert that the config is set with the expected configuration value
209
- expect(config).toEqual(expectedConfigValue);
210
280
  });
211
281
  });
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import dayjs from 'dayjs';
3
3
  import { fireEvent, render, screen } from '@testing-library/react';
4
4
  import { OpenmrsDatePicker } from '@openmrs/esm-framework';
5
- import { type FormField, type EncounterContext } from '../../..';
5
+ import { type FormField } from '../../../types';
6
6
  import { findTextOrDateInput } from '../../../utils/test-utils';
7
7
 
8
8
  const mockOpenmrsDatePicker = jest.mocked(OpenmrsDatePicker);
@@ -31,27 +31,6 @@ const question: FormField = {
31
31
  id: 'visit-date',
32
32
  };
33
33
 
34
- const encounterContext: EncounterContext = {
35
- patient: {
36
- id: '833db896-c1f0-11eb-8529-0242ac130003',
37
- },
38
- location: {
39
- uuid: '41e6e516-c1f0-11eb-8529-0242ac130003',
40
- },
41
- encounter: {
42
- uuid: '873455da-3ec4-453c-b565-7c1fe35426be',
43
- obs: [],
44
- },
45
- sessionMode: 'enter',
46
- encounterDate: new Date(2020, 11, 29),
47
- setEncounterDate: (value) => {},
48
- encounterProvider: '2c95f6f5-788e-4e73-9079-5626911231fa',
49
- setEncounterProvider: jest.fn,
50
- setEncounterLocation: jest.fn,
51
- encounterRole: '8cb3a399-d18b-4b62-aefb-5a0f948a3809',
52
- setEncounterRole: jest.fn,
53
- };
54
-
55
34
  const renderForm = (initialValues) => {
56
35
  render(<></>);
57
36
  };
@@ -4,21 +4,21 @@ import { isEmpty } from '../validators/form-validator';
4
4
 
5
5
  export class ConceptDataSource extends BaseOpenMRSDataSource {
6
6
  constructor() {
7
- super(`${restBaseUrl}/concept?name=&searchType=fuzzy&v=custom:(uuid,display,conceptClass:(uuid,display))`);
7
+ super(`${restBaseUrl}/concept?v=custom:(uuid,display,conceptClass:(uuid,display))`);
8
8
  }
9
9
 
10
- fetchData(searchTerm: string, config?: Record<string, any>, uuid?: string): Promise<any[]> {
10
+ fetchData(searchTerm: string, config?: Record<string, any>): Promise<any[]> {
11
11
  if (isEmpty(config?.class) && isEmpty(config?.concept) && !config?.useSetMembersByConcept && isEmpty(searchTerm)) {
12
12
  return Promise.resolve([]);
13
13
  }
14
14
 
15
- let apiUrl = this.url;
15
+ let searchUrl = `${restBaseUrl}/concept?name=&searchType=fuzzy&v=custom:(uuid,display,conceptClass:(uuid,display))`;
16
16
  if (config?.class) {
17
17
  if (typeof config.class == 'string') {
18
- const urlParts = apiUrl.split('searchType=fuzzy');
19
- apiUrl = `${urlParts[0]}searchType=fuzzy&class=${config.class}&${urlParts[1]}`;
18
+ const urlParts = searchUrl.split('searchType=fuzzy');
19
+ searchUrl = `${urlParts[0]}searchType=fuzzy&class=${config.class}&${urlParts[1]}`;
20
20
  } else {
21
- return openmrsFetch(searchTerm ? `${apiUrl}&q=${searchTerm}` : apiUrl).then(({ data }) => {
21
+ return openmrsFetch(searchTerm ? `${searchUrl}&q=${searchTerm}` : searchUrl).then(({ data }) => {
22
22
  return data.results.filter(
23
23
  (concept) => concept.conceptClass && config.class.includes(concept.conceptClass.uuid),
24
24
  );
@@ -27,15 +27,15 @@ export class ConceptDataSource extends BaseOpenMRSDataSource {
27
27
  }
28
28
 
29
29
  if (config?.concept && config?.useSetMembersByConcept) {
30
- let urlParts = apiUrl.split('?name=&searchType=fuzzy&v=');
31
- apiUrl = `${urlParts[0]}/${config.concept}?v=custom:(uuid,setMembers:(uuid,display))`;
32
- return openmrsFetch(searchTerm ? `${apiUrl}&q=${searchTerm}` : apiUrl).then(({ data }) => {
30
+ let urlParts = searchUrl.split('?name=&searchType=fuzzy&v=');
31
+ searchUrl = `${urlParts[0]}/${config.concept}?v=custom:(uuid,setMembers:(uuid,display))`;
32
+ return openmrsFetch(searchTerm ? `${searchUrl}&q=${searchTerm}` : searchUrl).then(({ data }) => {
33
33
  // return the setMembers from the retrieved concept object
34
34
  return data['setMembers'];
35
35
  });
36
36
  }
37
37
 
38
- return openmrsFetch(searchTerm ? `${apiUrl}&q=${searchTerm}` : apiUrl).then(({ data }) => {
38
+ return openmrsFetch(searchTerm ? `${searchUrl}&q=${searchTerm}` : searchUrl).then(({ data }) => {
39
39
  return data.results;
40
40
  });
41
41
  }
@@ -14,6 +14,17 @@ export class BaseOpenMRSDataSource implements DataSource<OpenmrsResource> {
14
14
  });
15
15
  }
16
16
 
17
+ fetchSingleItem(uuid: string): Promise<OpenmrsResource | null> {
18
+ let apiUrl = this.url;
19
+ if (apiUrl.includes('?')) {
20
+ const urlParts = apiUrl.split('?');
21
+ apiUrl = `${urlParts[0]}/${uuid}?${urlParts[1]}`;
22
+ } else {
23
+ apiUrl = `${apiUrl}/${uuid}`;
24
+ }
25
+ return openmrsFetch(apiUrl).then(({ data }) => data);
26
+ }
27
+
17
28
  toUuidAndDisplay(data: OpenmrsResource): OpenmrsResource {
18
29
  if (typeof data.uuid === 'undefined' || typeof data.display === 'undefined') {
19
30
  throw new Error("'uuid' or 'display' not found in the OpenMRS object.");
@@ -41,7 +41,7 @@ export function useFormFieldsMeta(rawFormFields: FormField[], concepts: OpenmrsR
41
41
  return field;
42
42
  });
43
43
  }
44
- return [];
44
+ return rawFormFields ?? [];
45
45
  }, [concepts, rawFormFields]);
46
46
 
47
47
  return formFields;
package/src/index.ts CHANGED
@@ -5,7 +5,6 @@ export * from './constants';
5
5
  export * from './utils/boolean-utils';
6
6
  export * from './validators/form-validator';
7
7
  export * from './utils/form-helper';
8
- export * from './form-context';
9
8
  export * from './components/value/view/field-value-view.component';
10
9
  export * from './components/previous-value-review/previous-value-review.component';
11
10
  export * from './hooks/useFormJson';
@@ -164,6 +164,10 @@ function transformByRendering(question: FormField) {
164
164
  case 'markdown':
165
165
  question.type = 'control';
166
166
  break;
167
+ case 'drug':
168
+ case 'problem':
169
+ question.questionOptions.isSearchable = true;
170
+ break;
167
171
  }
168
172
  return question;
169
173
  }
@@ -193,6 +197,7 @@ function handleLabOrders(question: FormField) {
193
197
  }
194
198
 
195
199
  function handleSelectConceptAnswers(question: FormField) {
200
+ question.questionOptions.isSearchable = true;
196
201
  if (!question.questionOptions.datasource?.config) {
197
202
  question.questionOptions.datasource = {
198
203
  name: 'select_concept_answers_datasource',
@@ -69,7 +69,14 @@ export interface DataSource<T> {
69
69
  /**
70
70
  * Fetches arbitrary data from a data source
71
71
  */
72
- fetchData(searchTerm?: string, config?: Record<string, any>, uuid?: string): Promise<Array<T>>;
72
+ fetchData(searchTerm?: string, config?: Record<string, any>): Promise<Array<T>>;
73
+
74
+ /**
75
+ * Fetches a single item from the data source based on its UUID.
76
+ * This is used for value binding with previously selected values.
77
+ */
78
+ fetchSingleItem(uuid: string): Promise<T | null>;
79
+
73
80
  /**
74
81
  * Maps a data source item to an object with a uuid and display property
75
82
  */
@@ -198,6 +198,7 @@ export type RenderType =
198
198
  | 'content-switcher'
199
199
  | 'date'
200
200
  | 'datetime'
201
+ | 'drug'
201
202
  | 'encounter-location'
202
203
  | 'encounter-provider'
203
204
  | 'encounter-role'
@@ -205,6 +206,7 @@ export type RenderType =
205
206
  | 'file'
206
207
  | 'group'
207
208
  | 'number'
209
+ | 'problem'
208
210
  | 'radio'
209
211
  | 'repeating'
210
212
  | 'select'
@@ -8,7 +8,6 @@ import { DefaultValueValidator } from '../validators/default-value-validator';
8
8
  import { type LayoutType } from '@openmrs/esm-framework';
9
9
  import { ConceptTrue } from '../constants';
10
10
  import { type FormField, type OpenmrsEncounter, type SessionMode } from '../types';
11
- import { type EncounterContext } from '../form-context';
12
11
 
13
12
  jest.mock('../validators/default-value-validator');
14
13
 
@@ -1,42 +0,0 @@
1
- import React from 'react';
2
- import { type LayoutType } from '@openmrs/esm-framework';
3
- import {
4
- type PatientProgram,
5
- type FormField,
6
- type OpenmrsEncounter,
7
- type PatientIdentifier,
8
- type SessionMode,
9
- } from './types';
10
-
11
- type FormContextProps = {
12
- values: Record<string, any>;
13
- setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void;
14
- setEncounterLocation: (value: any) => void;
15
- encounterContext: EncounterContext;
16
- fields: FormField[];
17
- isFieldInitializationComplete: boolean;
18
- isSubmitting: boolean;
19
- layoutType?: LayoutType;
20
- workspaceLayout?: 'minimized' | 'maximized';
21
- };
22
-
23
- export interface EncounterContext {
24
- patient: fhir.Patient;
25
- encounter: OpenmrsEncounter;
26
- previousEncounter?: OpenmrsEncounter;
27
- location: any;
28
- sessionMode: SessionMode;
29
- encounterDate: Date;
30
- setEncounterDate(value: Date): void;
31
- encounterProvider: string;
32
- setEncounterProvider(value: string): void;
33
- setEncounterLocation(value: any): void;
34
- encounterRole: string;
35
- setEncounterRole(value: string): void;
36
- initValues?: Record<string, any>;
37
- patientIdentifier?: PatientIdentifier;
38
- patientPrograms?: Array<PatientProgram>;
39
- getFormField?: (id: string) => FormField;
40
- }
41
-
42
- export const FormContext = React.createContext<FormContextProps | undefined>(undefined);