diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 00000000..eefd1082
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,33 @@
+FROM centos:centos7.9.2009
+
+# root Installations
+RUN yum install -y libpng12 make gcc-c++ sudo
+RUN yum install -y https://packages.endpointdev.com/rhel/7/os/x86_64/endpoint-repo.x86_64.rpm
+RUN yum install -y git
+
+# User Creation
+ARG USERNAME=vscode
+ARG USER_UID=1000
+ARG USER_GID=$USER_UID
+
+RUN groupadd --gid $USER_GID $USERNAME \
+ && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
+ && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
+ && chmod 0440 /etc/sudoers.d/$USERNAME
+
+USER $USERNAME
+
+# User Installations
+RUN touch /home/vscode/.bashrc
+
+ENV NVM_DIR /home/vscode/.nvm
+ENV NODE_VERSION 10.24.1
+
+RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash \
+ && . $NVM_DIR/nvm.sh \
+ && nvm install $NODE_VERSION \
+ && nvm alias default $NODE_VERSION \
+ && nvm use default \
+ && npm install -g yarn
+
+CMD ["/bin/sleep", "infinity"]
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 00000000..ba0c5621
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,9 @@
+{
+ "dockerComposeFile": "docker-compose.yml",
+ "service": "env",
+ "remoteUser": "vscode",
+ "workspaceFolder": "/workspace",
+ "forwardPorts": [
+ 9876
+ ]
+}
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
new file mode 100644
index 00000000..f6efbec1
--- /dev/null
+++ b/.devcontainer/docker-compose.yml
@@ -0,0 +1,13 @@
+version: '3'
+
+services:
+ env:
+ image: johnny94/form-controls-env:v1.0.0
+ #build:
+ # context: .
+ # dockerfile: Dockerfile
+ volumes:
+ - ..:/workspace:cached
+ - $SSH_AUTH_SOCK:/ssh-agent
+ environment:
+ - SSH_AUTH_SOCK=/ssh-agent
diff --git a/README.md b/README.md
index 1521c455..58916a85 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Form Controls
### Setup Steps
1. Install nvm
-2. Install node
+2. Install node (Tested version: 10.X.Y)
3. Install yarn - https://yarnpkg.com/en/docs/install
### Build
@@ -18,3 +18,9 @@ Form Controls
1. Install dependencies - `yarn`
2. Build - `yarn build`
3. Test - `yarn test`
+
+Note: OS library libpng12 maybe needs to be installed in order to make dependency installation succeed.
+
+### Documentation
+
+https://bahmni.atlassian.net/wiki/spaces/BAH/pages/714276866/Creating+a+Sample+Form-Controls+Property+Component
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..543796e6
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,357 @@
+{
+ "name": "bahmni-form-controls",
+ "version": "0.93.11",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "requires": {
+ "@jridgewell/set-array": "1.1.2",
+ "@jridgewell/sourcemap-codec": "1.4.14",
+ "@jridgewell/trace-mapping": "0.3.17"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
+ },
+ "@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
+ },
+ "@jridgewell/source-map": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
+ "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
+ "requires": {
+ "@jridgewell/gen-mapping": "0.3.2",
+ "@jridgewell/trace-mapping": "0.3.17"
+ }
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.17",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
+ "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+ "requires": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "@types/html-minifier-terser": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+ "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg=="
+ },
+ "acorn": {
+ "version": "8.8.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+ "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw=="
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+ },
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+ },
+ "buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+ },
+ "camel-case": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+ "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+ "requires": {
+ "pascal-case": "3.1.2",
+ "tslib": "2.5.0"
+ }
+ },
+ "clean-css": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz",
+ "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==",
+ "requires": {
+ "source-map": "0.6.1"
+ }
+ },
+ "commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
+ },
+ "css-select": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+ "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+ "requires": {
+ "boolbase": "1.0.0",
+ "css-what": "6.1.0",
+ "domhandler": "4.3.1",
+ "domutils": "2.8.0",
+ "nth-check": "2.1.1"
+ }
+ },
+ "css-what": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="
+ },
+ "dom-converter": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
+ "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+ "requires": {
+ "utila": "0.4.0"
+ }
+ },
+ "dom-serializer": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+ "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+ "requires": {
+ "domelementtype": "2.3.0",
+ "domhandler": "4.3.1",
+ "entities": "2.2.0"
+ }
+ },
+ "domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
+ },
+ "domhandler": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+ "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "requires": {
+ "domelementtype": "2.3.0"
+ }
+ },
+ "domutils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+ "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "requires": {
+ "dom-serializer": "1.4.1",
+ "domelementtype": "2.3.0",
+ "domhandler": "4.3.1"
+ }
+ },
+ "dot-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+ "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "requires": {
+ "no-case": "3.0.4",
+ "tslib": "2.5.0"
+ }
+ },
+ "entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
+ },
+ "html-minifier-terser": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+ "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+ "requires": {
+ "camel-case": "4.1.2",
+ "clean-css": "5.3.2",
+ "commander": "8.3.0",
+ "he": "1.2.0",
+ "param-case": "3.0.4",
+ "relateurl": "0.2.7",
+ "terser": "5.16.3"
+ }
+ },
+ "html-webpack-plugin": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
+ "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==",
+ "requires": {
+ "@types/html-minifier-terser": "6.1.0",
+ "html-minifier-terser": "6.1.0",
+ "lodash": "4.17.21",
+ "pretty-error": "4.0.0",
+ "tapable": "2.2.1"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ }
+ }
+ },
+ "htmlparser2": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+ "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+ "requires": {
+ "domelementtype": "2.3.0",
+ "domhandler": "4.3.1",
+ "domutils": "2.8.0",
+ "entities": "2.2.0"
+ }
+ },
+ "lower-case": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+ "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "requires": {
+ "tslib": "2.5.0"
+ }
+ },
+ "no-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+ "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "requires": {
+ "lower-case": "2.0.2",
+ "tslib": "2.5.0"
+ }
+ },
+ "nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "requires": {
+ "boolbase": "1.0.0"
+ }
+ },
+ "param-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+ "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+ "requires": {
+ "dot-case": "3.0.4",
+ "tslib": "2.5.0"
+ }
+ },
+ "pascal-case": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+ "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+ "requires": {
+ "no-case": "3.0.4",
+ "tslib": "2.5.0"
+ }
+ },
+ "pretty-error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
+ "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
+ "requires": {
+ "lodash": "4.17.21",
+ "renderkid": "3.0.0"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ }
+ }
+ },
+ "relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog=="
+ },
+ "renderkid": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
+ "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
+ "requires": {
+ "css-select": "4.3.0",
+ "dom-converter": "0.2.0",
+ "htmlparser2": "6.1.0",
+ "lodash": "4.17.21",
+ "strip-ansi": "6.0.1"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ },
+ "source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "requires": {
+ "buffer-from": "1.1.2",
+ "source-map": "0.6.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "requires": {
+ "ansi-regex": "5.0.1"
+ }
+ },
+ "tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
+ },
+ "terser": {
+ "version": "5.16.3",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.3.tgz",
+ "integrity": "sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q==",
+ "requires": {
+ "@jridgewell/source-map": "0.3.2",
+ "acorn": "8.8.2",
+ "commander": "2.20.3",
+ "source-map-support": "0.5.21"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ }
+ }
+ },
+ "tslib": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
+ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
+ },
+ "utila": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
+ "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA=="
+ }
+ }
+}
diff --git a/package.json b/package.json
index 4e3b80ab..5c74c76f 100644
--- a/package.json
+++ b/package.json
@@ -90,7 +90,7 @@
"sinon-as-promised": "^4.0.3",
"whatwg-fetch": "^1.0.0"
},
- "resolutions": {
+ "resolutions": {
"**/**/lodash": "^4.17.12",
"**/**/lodash-es": "^4.17.14",
"**/**/mixin-deep": "^1.3.2",
diff --git a/src/components/ImageView.jsx b/src/components/ImageView.jsx
new file mode 100644
index 00000000..59f7fa82
--- /dev/null
+++ b/src/components/ImageView.jsx
@@ -0,0 +1,74 @@
+/* eslint-disable react/prefer-stateless-function */
+/* Needs this to attach refs as they cannot be attached to stateless functions. */
+
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import ComponentStore from 'src/helpers/componentStore';
+import { IntlShape } from 'react-intl';
+
+export class ImageView extends Component {
+
+ _computeImageViewContainerStyle(position) {
+
+ let textAlignPosition;
+
+ switch(position) {
+ case "center":
+ textAlignPosition = "center";
+ break;
+ case "right":
+ textAlignPosition = "right";
+ break;
+ case "left":
+ default:
+ textAlignPosition = "left";
+ break;
+ }
+
+ return {
+ textAlign: textAlignPosition,
+ padding: '15px',
+ };
+ }
+
+ _computeImageViewStyle(maxHeight, maxWidth) {
+
+ const maxHeightPercentage = isNaN(maxHeight) ? 100 : maxHeight;
+ const maxWidthPercentage = isNaN(maxWidth) ? 100 : maxWidth;
+
+ return {
+ maxHeight: maxHeightPercentage + 'vh',
+ maxWidth: maxWidthPercentage + '%'
+ };
+ }
+
+ render() {
+ const { intl, enabled, metadata: { label: { value }, properties: { maxHeight, maxWidth, position } } } = this.props;
+
+ const imageUrl = `/bahmni/images/clinical_forms/${value}`;
+
+ const disableClass = enabled ? '' : 'disabled-label';
+ return (
+
+
+ );
+ }
+}
+
+ImageView.propTypes = {
+ intl: IntlShape,
+ metadata: PropTypes.shape({
+ type: PropTypes.string.isRequired,
+ label: PropTypes.object,
+ translationKey: PropTypes.string,
+ properties: PropTypes.object,
+ }),
+};
+
+ImageView.defaultProps = {
+};
+
+ComponentStore.registerComponent('imageView', ImageView);
+/* eslint-enable react/prefer-stateless-function */
diff --git a/src/components/Link.jsx b/src/components/Link.jsx
new file mode 100644
index 00000000..d892f078
--- /dev/null
+++ b/src/components/Link.jsx
@@ -0,0 +1,41 @@
+/* eslint-disable react/prefer-stateless-function */
+/* Needs this to attach refs as they cannot be attached to stateless functions. */
+
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import ComponentStore from 'src/helpers/componentStore';
+import { IntlShape } from 'react-intl';
+
+export class Link extends Component {
+
+ render() {
+ const { intl, enabled, metadata: { label: { value: displayName}, link: { value: url } } } = this.props;
+
+ return (
+
+ );
+
+ }
+}
+
+Link.propTypes = {
+ intl: IntlShape,
+ metadata: PropTypes.shape({
+ type: PropTypes.string.isRequired,
+ label: PropTypes.object,
+ link: PropTypes.object,
+ translationKey: PropTypes.string,
+ properties: PropTypes.object,
+ }),
+};
+
+Link.defaultProps = {
+};
+
+ComponentStore.registerComponent('link', Link);
+/* eslint-enable react/prefer-stateless-function */
diff --git a/src/components/designer/ImageView.jsx b/src/components/designer/ImageView.jsx
new file mode 100644
index 00000000..a42f6182
--- /dev/null
+++ b/src/components/designer/ImageView.jsx
@@ -0,0 +1,166 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import ComponentStore from 'src/helpers/componentStore';
+import { SectionMapper } from '../../mapper/SectionMapper';
+import { LabelDesigner } from 'components/designer/Label.jsx';
+
+export class ImageViewDesigner extends Component {
+
+ constructor(props) {
+ super(props);
+ this.metadata = props.metadata;
+ this.mapper = new SectionMapper();
+ this.storeLabelRef = this.storeLabelRef.bind(this);
+ this.deleteControl = this.deleteControl.bind(this);
+ }
+
+ getJsonDefinition() {
+ const labelJsonDefinition = this.labelControl && this.labelControl.getJsonDefinition();
+ return Object.assign({}, this.props.metadata, {}, { label: labelJsonDefinition });
+ }
+
+ storeLabelRef(ref) {
+ this.labelControl = ref;
+ }
+
+ displayLabel() {
+ const { metadata: { label, id } } = this.props;
+ const data = Object.assign({}, label, { id });
+ return (
+
+ );
+ }
+
+ deleteControl(event) {
+ this.props.deleteControl();
+ this.props.clearSelectedControl(event);
+ }
+
+ showDeleteButton() {
+ if (this.props.showDeleteButton) {
+ return (
+
+ );
+ }
+ return null;
+ }
+
+ stopEventPropagation(event) {
+ this.props.dispatch();
+ event.stopPropagation();
+ }
+
+ render() {
+ const { metadata } = this.props;
+ return (
+ {
+ this.stopEventPropagation(event);
+ this.props.onSelect(event, metadata);
+ }}
+ >
+ {this.showDeleteButton()}
+
+
+ Filename: |
+ {this.displayLabel()} |
+
+
+
+ );
+ }
+}
+
+ImageViewDesigner.propTypes = {
+ clearSelectedControl: PropTypes.func.isRequired,
+ deleteControl: PropTypes.func.isRequired,
+ dispatch: PropTypes.func,
+ metadata: PropTypes.shape({
+ displayType: PropTypes.string,
+ id: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+ label: PropTypes.object,
+ properties: PropTypes.shape({
+ location: PropTypes.shape({
+ row: PropTypes.number,
+ column: PropTypes.number,
+ }),
+ }),
+ type: PropTypes.string.isRequired,
+ }),
+ onSelect: PropTypes.func.isRequired,
+ showDeleteButton: PropTypes.bool,
+};
+
+const descriptor = {
+ control: ImageViewDesigner,
+ designProperties: {
+ displayName: 'Image View',
+ isTopLevelComponent: true,
+ },
+ metadata: {
+ attributes: [
+ {
+ name: 'type',
+ dataType: 'text',
+ defaultValue: 'imageView',
+ },
+ {
+ name: 'value',
+ dataType: 'text',
+ defaultValue: 'ImageView',
+ },
+ {
+ name: 'label',
+ dataType: 'complex',
+ attributes: [
+ {
+ name: 'type',
+ dataType: 'text',
+ defaultValue: 'label',
+ },
+ {
+ name: 'value',
+ dataType: 'text',
+ defaultValue: 'ImageView',
+ },
+ ],
+ },
+ {
+ name: 'properties',
+ dataType: 'complex',
+ attributes: [
+ {
+ name: 'position',
+ elementType: 'dropdown',
+ defaultValue: 'left',
+ options: [
+ 'left',
+ 'center',
+ 'right',
+ ],
+ },
+ {
+ name: 'maxWidth',
+ elementType: 'number',
+ defaultValue: 100,
+ },
+ {
+ name: 'maxHeight',
+ elementType: 'number',
+ defaultValue: 100,
+ },
+ ],
+ },
+ ],
+ },
+};
+
+ComponentStore.registerDesignerComponent('imageView', descriptor);
diff --git a/src/components/designer/Link.jsx b/src/components/designer/Link.jsx
new file mode 100644
index 00000000..a4c320e5
--- /dev/null
+++ b/src/components/designer/Link.jsx
@@ -0,0 +1,179 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import ComponentStore from 'src/helpers/componentStore';
+import { SectionMapper } from '../../mapper/SectionMapper';
+import { LabelDesigner } from 'components/designer/Label.jsx';
+
+export class LinkDesigner extends Component {
+
+ constructor(props) {
+ super(props);
+ this.metadata = props.metadata;
+ this.mapper = new SectionMapper();
+ this.storeNameLabelRef = this.storeNameLabelRef.bind(this);
+ this.storeUrlLabelRef = this.storeUrlLabelRef.bind(this);
+ this.deleteControl = this.deleteControl.bind(this);
+ }
+
+ getJsonDefinition() {
+ const namelabelJsonDefinition = this.nameLabelControl && this.nameLabelControl.getJsonDefinition();
+ const urllabelJsonDefinition = this.urlLabelControl && this.urlLabelControl.getJsonDefinition();
+
+ return Object.assign({}, this.props.metadata, {}, { label: namelabelJsonDefinition, link: urllabelJsonDefinition });
+ }
+
+ storeNameLabelRef(ref) {
+ this.nameLabelControl = ref;
+ }
+
+ storeUrlLabelRef(ref) {
+ this.urlLabelControl = ref;
+ }
+
+ displayNameLabel() {
+ const { metadata: { label, id } } = this.props;
+ const data = Object.assign({}, label, { id });
+ return (
+
+ );
+ }
+
+ displayUrlLabel() {
+ const { metadata: { link, id } } = this.props;
+ const data = Object.assign({}, link, { id });
+ return (
+
+ );
+ }
+
+ deleteControl(event) {
+ this.props.deleteControl();
+ this.props.clearSelectedControl(event);
+ }
+
+ showDeleteButton() {
+ if (this.props.showDeleteButton) {
+ return (
+
+ );
+ }
+ return null;
+ }
+
+ stopEventPropagation(event) {
+ this.props.dispatch();
+ event.stopPropagation();
+ }
+
+ render() {
+ const { metadata } = this.props;
+ return (
+ {
+ this.stopEventPropagation(event);
+ this.props.onSelect(event, metadata);
+ }}
+ >
+ {this.showDeleteButton()}
+
+
+ Display Name: |
+ {this.displayNameLabel()} |
+
+
+ Url: |
+ {this.displayUrlLabel()} |
+
+
+
+ );
+ }
+}
+
+LinkDesigner.propTypes = {
+ clearSelectedControl: PropTypes.func.isRequired,
+ deleteControl: PropTypes.func.isRequired,
+ dispatch: PropTypes.func,
+ metadata: PropTypes.shape({
+ displayType: PropTypes.string,
+ id: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+ label: PropTypes.object,
+ properties: PropTypes.shape({
+ location: PropTypes.shape({
+ row: PropTypes.number,
+ column: PropTypes.number,
+ }),
+ }),
+ type: PropTypes.string.isRequired,
+ }),
+ onSelect: PropTypes.func.isRequired,
+ showDeleteButton: PropTypes.bool,
+};
+
+const descriptor = {
+ control: LinkDesigner,
+ designProperties: {
+ displayName: 'Link',
+ isTopLevelComponent: true,
+ },
+ metadata: {
+ attributes: [
+ {
+ name: 'type',
+ dataType: 'text',
+ defaultValue: 'link',
+ },
+ {
+ name: 'value',
+ dataType: 'text',
+ defaultValue: 'link',
+ },
+ {
+ name: 'label',
+ dataType: 'complex',
+ attributes: [
+ {
+ name: 'type',
+ dataType: 'text',
+ defaultValue: 'label',
+ },
+ {
+ name: 'value',
+ dataType: 'text',
+ defaultValue: 'name',
+ },
+ ],
+ },
+ {
+ name: 'link',
+ dataType: 'complex',
+ attributes: [
+ {
+ name: 'type',
+ dataType: 'text',
+ defaultValue: 'label',
+ },
+ {
+ name: 'value',
+ dataType: 'text',
+ defaultValue: 'url',
+ },
+ ],
+ },
+ ],
+ },
+};
+
+ComponentStore.registerDesignerComponent('link', descriptor);
diff --git a/src/components/designer/TableDesigner.jsx b/src/components/designer/TableDesigner.jsx
index 5269fefe..f9f26667 100644
--- a/src/components/designer/TableDesigner.jsx
+++ b/src/components/designer/TableDesigner.jsx
@@ -6,7 +6,7 @@ import { LabelDesigner } from 'components/designer/Label.jsx';
import { GridDesigner } from 'components/designer/Grid.jsx';
import { CellDesigner } from 'components/designer/Cell.jsx';
-const supportedControlTypes = ['obsControl'];
+const supportedControlTypes = ['obsControl', 'label', 'imageView', 'link'];
const unsupportedProperties = ['addMore'];
const NO_OF_TABLE_COLUMNS = 2;
diff --git a/src/index.jsx b/src/index.jsx
index 3891f1ed..51eae8a9 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -1,6 +1,8 @@
export { Container } from 'components/Container.jsx';
export { ObsControl } from 'components/ObsControl.jsx';
export { Label } from 'components/Label.jsx';
+export { Link } from 'components/Link.jsx';
+export { ImageView } from 'components/ImageView.jsx';
export { TextBox } from 'components/TextBox.jsx';
export { NumericBox } from 'components/NumericBox.jsx';
export { ComplexControl } from 'components/ComplexControl.jsx';
@@ -41,6 +43,8 @@ export { DateDesigner } from 'components/designer/Date.jsx';
export { DateTimeDesigner } from 'components/designer/DateTime.jsx';
export { SectionDesigner } from 'components/designer/Section.jsx';
export { TableDesigner } from 'components/designer/TableDesigner.jsx';
+export { LinkDesigner } from 'components/designer/Link.jsx';
+export { ImageViewDesigner } from 'components/designer/ImageView.jsx';
export { ImageDesigner } from 'components/designer/Image.jsx';
export { VideoDesigner } from 'components/designer/Video.jsx';
export { LocationDesigner } from 'components/designer/Location.jsx';
diff --git a/styles/bahmniAppsFormBuilder/_form.scss b/styles/bahmniAppsFormBuilder/_form.scss
index 5e0d6d83..0d367c3e 100644
--- a/styles/bahmniAppsFormBuilder/_form.scss
+++ b/styles/bahmniAppsFormBuilder/_form.scss
@@ -620,3 +620,27 @@ input.computed-value{
border: 0 !important;
pointer-events: none;
}
+
+a.link {
+ background-color: $bahmniSecondaryColor;
+ color: white;
+ box-shadow: 1px 1px 1px #ccc;
+ border-radius: 10px;
+ font-size: 20px;
+ font-family: inherit;
+ padding: 5px 5px 5px 10px;
+ font-weight: 800;
+}
+
+.link-container {
+ width: 100%;
+ text-align: center;
+}
+
+table.link, table.image-view {
+ float: left;
+}
+
+table.link td, table.image-view td {
+ padding: 1px 5px 1px 5px;
+}