mirror of
				https://gitlab.nic.cz/turris/reforis/foris-js.git
				synced 2025-11-03 23:00:31 +01:00 
			
		
		
		
	Set babel.
This commit is contained in:
		@@ -59,4 +59,4 @@ module.exports = {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -45,4 +45,6 @@ coverage.xml
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/js/styleguide/
 | 
					/js/styleguide/
 | 
				
			||||||
.gitignore
 | 
					.gitignore
 | 
				
			||||||
src/
 | 
					
 | 
				
			||||||
 | 
					dist/
 | 
				
			||||||
 | 
					foris-*.tgz
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1222
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1222
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										35
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								package.json
									
									
									
									
									
								
							@@ -1,5 +1,7 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "foris",
 | 
					  "name": "foris",
 | 
				
			||||||
 | 
					  "version": "0.0.1",
 | 
				
			||||||
 | 
					  "description": "Set of components and utils for Foris and its plugins.",
 | 
				
			||||||
  "author": "CZ.NIC, z.s.p.o.",
 | 
					  "author": "CZ.NIC, z.s.p.o.",
 | 
				
			||||||
  "repository": {
 | 
					  "repository": {
 | 
				
			||||||
    "type": "git",
 | 
					    "type": "git",
 | 
				
			||||||
@@ -10,18 +12,20 @@
 | 
				
			|||||||
    "reforis"
 | 
					    "reforis"
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "license": "GPL-3.0",
 | 
					  "license": "GPL-3.0",
 | 
				
			||||||
  "version": "0.0.1",
 | 
					  "main": "./dist/index.js",
 | 
				
			||||||
  "description": "Set of components and utils for Foris and its plugins.",
 | 
					 | 
				
			||||||
  "main": "./dist/foris.js",
 | 
					 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "axios": "^0.19.0",
 | 
					    "axios": "^0.19.0",
 | 
				
			||||||
    "immutability-helper": "^3.0.1",
 | 
					    "immutability-helper": "^3.0.1",
 | 
				
			||||||
    "prop-types": "^15.7.2",
 | 
					    "prop-types": "^15.7.2",
 | 
				
			||||||
    "react": "^16.9.0-alpha.0",
 | 
					 | 
				
			||||||
    "react-datetime": "^2.16.3",
 | 
					    "react-datetime": "^2.16.3",
 | 
				
			||||||
    "react-dom": "^16.9.0-alpha.0",
 | 
					 | 
				
			||||||
    "react-router": "^5.0.1",
 | 
					    "react-router": "^5.0.1",
 | 
				
			||||||
    "react-uid": "^2.2.0"
 | 
					    "react-uid": "^2.2.0",
 | 
				
			||||||
 | 
					    "moment": "^2.24.0",
 | 
				
			||||||
 | 
					    "moment-timezone": "^0.5.25"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "peerDependencies": {
 | 
				
			||||||
 | 
					    "react": "^16.9.0",
 | 
				
			||||||
 | 
					    "react-dom": "^16.9.0"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@babel/cli": "^7.4.4",
 | 
					    "@babel/cli": "^7.4.4",
 | 
				
			||||||
@@ -36,7 +40,6 @@
 | 
				
			|||||||
    "babel-loader": "^8.0.6",
 | 
					    "babel-loader": "^8.0.6",
 | 
				
			||||||
    "babel-plugin-react-transform": "^3.0.0",
 | 
					    "babel-plugin-react-transform": "^3.0.0",
 | 
				
			||||||
    "babel-polyfill": "^6.26.0",
 | 
					    "babel-polyfill": "^6.26.0",
 | 
				
			||||||
    "css-loader": "^3.0.0",
 | 
					 | 
				
			||||||
    "eslint": "^5.16.0",
 | 
					    "eslint": "^5.16.0",
 | 
				
			||||||
    "eslint-config-react-app": "^4.0.1",
 | 
					    "eslint-config-react-app": "^4.0.1",
 | 
				
			||||||
    "eslint-plugin-flowtype": "^2.50.3",
 | 
					    "eslint-plugin-flowtype": "^2.50.3",
 | 
				
			||||||
@@ -44,22 +47,24 @@
 | 
				
			|||||||
    "eslint-plugin-jsx-a11y": "^6.2.1",
 | 
					    "eslint-plugin-jsx-a11y": "^6.2.1",
 | 
				
			||||||
    "eslint-plugin-react": "^7.13.0",
 | 
					    "eslint-plugin-react": "^7.13.0",
 | 
				
			||||||
    "eslint-plugin-react-hooks": "^1.6.0",
 | 
					    "eslint-plugin-react-hooks": "^1.6.0",
 | 
				
			||||||
    "file-loader": "^4.0.0",
 | 
					 | 
				
			||||||
    "jest": "^24.8.0",
 | 
					    "jest": "^24.8.0",
 | 
				
			||||||
    "jest-mock-axios": "^3.0.0",
 | 
					    "jest-mock-axios": "^3.0.0",
 | 
				
			||||||
 | 
					    "moment": "^2.24.0",
 | 
				
			||||||
    "moment-timezone": "^0.5.25",
 | 
					    "moment-timezone": "^0.5.25",
 | 
				
			||||||
 | 
					    "react": "^16.9.0",
 | 
				
			||||||
 | 
					    "react-dom": "^16.9.0",
 | 
				
			||||||
    "react-styleguidist": "^9.1.11",
 | 
					    "react-styleguidist": "^9.1.11",
 | 
				
			||||||
    "snapshot-diff": "^0.5.1",
 | 
					    "snapshot-diff": "^0.5.1"
 | 
				
			||||||
    "style-loader": "^0.23.1",
 | 
					 | 
				
			||||||
    "webpack": "^4.33.0",
 | 
					 | 
				
			||||||
    "webpack-cli": "^3.3.4"
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "watch": "webpack --watch --mode=development --env.lighttpd",
 | 
					    "build": "babel ./src --out-dir ./dist -s inline",
 | 
				
			||||||
    "build": "webpack --mode=production --env.lighttpd",
 | 
					    "prepare": "rm -rf ./dist && npm run build",
 | 
				
			||||||
    "lint": "eslint src",
 | 
					    "lint": "eslint src",
 | 
				
			||||||
    "test": "jest",
 | 
					    "test": "jest",
 | 
				
			||||||
    "test:watch": "jest --watch",
 | 
					    "test:watch": "jest --watch",
 | 
				
			||||||
    "test:coverage": "jest --coverage --colors"
 | 
					    "test:coverage": "jest --coverage --colors"
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
 | 
					  "files": [
 | 
				
			||||||
 | 
					    "dist"
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								src/api/constants.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/api/constants.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const REFORIS_URL_PREFIX = process.env.LIGHTTPD ? "/reforis" : "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ForisURLs = {
 | 
				
			||||||
 | 
					    login: `${REFORIS_URL_PREFIX}/login`,
 | 
				
			||||||
 | 
					    static: `${REFORIS_URL_PREFIX}/static/reforis`,
 | 
				
			||||||
 | 
					    wifi: `${REFORIS_URL_PREFIX}/network-settings/wifi`,
 | 
				
			||||||
 | 
					    updates: `${REFORIS_URL_PREFIX}/updates`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Notifications links are used with <Link/> inside Router, thus url subdir is not required.
 | 
				
			||||||
 | 
					    notifications: "/notifications",
 | 
				
			||||||
 | 
					    notificationsSettings: "/administration/notifications-settings",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    luci: "/cgi-bin/luci",
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										158
									
								
								src/api/hooks.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								src/api/hooks.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,158 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import axios from "axios";
 | 
				
			||||||
 | 
					import {useCallback, useReducer} from "react";
 | 
				
			||||||
 | 
					import {ForisURLs} from './constants';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const POST_HEADERS = {
 | 
				
			||||||
 | 
					    Accept: "application/json",
 | 
				
			||||||
 | 
					    "Content-Type": "application/json",
 | 
				
			||||||
 | 
					    "X-CSRFToken": getCookie("_csrf_token"),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getCookie(name) {
 | 
				
			||||||
 | 
					    let cookieValue = null;
 | 
				
			||||||
 | 
					    if (document.cookie && document.cookie !== "") {
 | 
				
			||||||
 | 
					        const cookies = document.cookie.split(";");
 | 
				
			||||||
 | 
					        for (let i = 0; i < cookies.length; i++) {
 | 
				
			||||||
 | 
					            const cookie = cookies[i].trim();
 | 
				
			||||||
 | 
					            // Does this cookie string begin with the name we want?
 | 
				
			||||||
 | 
					            if (cookie.substring(0, name.length + 1) === (`${name}=`)) {
 | 
				
			||||||
 | 
					                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return cookieValue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TIMEOUT = 5000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const API_ACTIONS = {
 | 
				
			||||||
 | 
					    INIT: 1,
 | 
				
			||||||
 | 
					    SUCCESS: 2,
 | 
				
			||||||
 | 
					    FAILURE: 3,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const APIGetReducer = (state, action) => {
 | 
				
			||||||
 | 
					    switch (action.type) {
 | 
				
			||||||
 | 
					        case API_ACTIONS.INIT:
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                ...state,
 | 
				
			||||||
 | 
					                isLoading: true,
 | 
				
			||||||
 | 
					                isError: false,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        case API_ACTIONS.SUCCESS:
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                ...state,
 | 
				
			||||||
 | 
					                isLoading: false,
 | 
				
			||||||
 | 
					                isError: false,
 | 
				
			||||||
 | 
					                data: action.payload,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        case API_ACTIONS.FAILURE:
 | 
				
			||||||
 | 
					            if (action.status === 403) window.location.assign(ForisURLs.login);
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                ...state,
 | 
				
			||||||
 | 
					                isLoading: false,
 | 
				
			||||||
 | 
					                isError: true,
 | 
				
			||||||
 | 
					                data: action.payload,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            throw new Error();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This function adds one to its input.
 | 
				
			||||||
 | 
					 * @returns {number} that number, plus one.
 | 
				
			||||||
 | 
					 * @param url
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function useAPIGet(url) {
 | 
				
			||||||
 | 
					    const [state, dispatch] = useReducer(APIGetReducer, {
 | 
				
			||||||
 | 
					        isLoading: false,
 | 
				
			||||||
 | 
					        isError: false,
 | 
				
			||||||
 | 
					        data: null,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const get = useCallback(async () => {
 | 
				
			||||||
 | 
					        dispatch({type: API_ACTIONS.INIT});
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const result = await axios.get(url, {
 | 
				
			||||||
 | 
					                timeout: TIMEOUT,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            dispatch({type: API_ACTIONS.SUCCESS, payload: result.data});
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            dispatch({
 | 
				
			||||||
 | 
					                type: API_ACTIONS.FAILURE,
 | 
				
			||||||
 | 
					                payload: error.response.data,
 | 
				
			||||||
 | 
					                status: error.response.status,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }, [url]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return [state, get];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const APIPostReducer = (state, action) => {
 | 
				
			||||||
 | 
					    switch (action.type) {
 | 
				
			||||||
 | 
					        case API_ACTIONS.INIT:
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                ...state,
 | 
				
			||||||
 | 
					                isSending: true,
 | 
				
			||||||
 | 
					                isError: false,
 | 
				
			||||||
 | 
					                isSuccess: false,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        case API_ACTIONS.SUCCESS:
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                ...state,
 | 
				
			||||||
 | 
					                isSending: false,
 | 
				
			||||||
 | 
					                isError: false,
 | 
				
			||||||
 | 
					                isSuccess: true,
 | 
				
			||||||
 | 
					                data: action.payload,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        case API_ACTIONS.FAILURE:
 | 
				
			||||||
 | 
					            if (action.status === 403) window.location.assign(ForisURLs.login);
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                ...state,
 | 
				
			||||||
 | 
					                isSending: false,
 | 
				
			||||||
 | 
					                isError: true,
 | 
				
			||||||
 | 
					                isSuccess: false,
 | 
				
			||||||
 | 
					                data: action.payload,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            throw new Error();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useAPIPost(url) {
 | 
				
			||||||
 | 
					    const [state, dispatch] = useReducer(APIPostReducer, {
 | 
				
			||||||
 | 
					        isSending: false,
 | 
				
			||||||
 | 
					        isError: false,
 | 
				
			||||||
 | 
					        isSuccess: false,
 | 
				
			||||||
 | 
					        data: null,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const post = async (data) => {
 | 
				
			||||||
 | 
					        dispatch({type: API_ACTIONS.INIT});
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const result = await axios.post(url, data, {
 | 
				
			||||||
 | 
					                timeout: TIMEOUT,
 | 
				
			||||||
 | 
					                headers: POST_HEADERS,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            dispatch({type: API_ACTIONS.SUCCESS, payload: result.data});
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            dispatch({
 | 
				
			||||||
 | 
					                type: API_ACTIONS.FAILURE,
 | 
				
			||||||
 | 
					                payload: error.response.data,
 | 
				
			||||||
 | 
					                status: error.response.status,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    return [state, post];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								src/api/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/api/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export * from "./hooks";
 | 
				
			||||||
							
								
								
									
										29
									
								
								src/bootstrap/__tests__/Button.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/bootstrap/__tests__/Button.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {render} from 'customTestRender';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Button from '../components/Button'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('<Button />', () => {
 | 
				
			||||||
 | 
					    it('Render button correctly', () => {
 | 
				
			||||||
 | 
					        const {container} = render(<Button>Test Button</Button>);
 | 
				
			||||||
 | 
					        expect(container.firstChild).toMatchSnapshot()
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('Render button with custom classes', () => {
 | 
				
			||||||
 | 
					        const {container} = render(<Button className="one two three">Test Button</Button>)
 | 
				
			||||||
 | 
					        expect(container.firstChild).toMatchSnapshot()
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('Render button with spinner', () => {
 | 
				
			||||||
 | 
					        const {container} = render(<Button loading={true}>Test Button</Button>)
 | 
				
			||||||
 | 
					        expect(container.firstChild).toMatchSnapshot()
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/bootstrap/__tests__/Checkbox.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/bootstrap/__tests__/Checkbox.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {render} from 'customTestRender';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Checkbox from '../components/Checkbox'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('<Checkbox/>', () => {
 | 
				
			||||||
 | 
					    it('Render checkbox', () => {
 | 
				
			||||||
 | 
					        const {container} = render(
 | 
				
			||||||
 | 
					            <Checkbox
 | 
				
			||||||
 | 
					                label="Test label"
 | 
				
			||||||
 | 
					                checked
 | 
				
			||||||
 | 
					                helpText="Some help text"
 | 
				
			||||||
 | 
					                onChange={()=>{}}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        expect(container.firstChild).toMatchSnapshot();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('Render uncheked checkbox', () => {
 | 
				
			||||||
 | 
					        const {container} = render(
 | 
				
			||||||
 | 
					            <Checkbox
 | 
				
			||||||
 | 
					                label="Test label"
 | 
				
			||||||
 | 
					                helpText="Some help text"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        expect(container.firstChild).toMatchSnapshot();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/bootstrap/__tests__/NumberInput.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/bootstrap/__tests__/NumberInput.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {render} from 'customTestRender';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import NumberInput from '../components/NumberInput';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('<NumberInput/>', () => {
 | 
				
			||||||
 | 
					    it('Render number input', () => {
 | 
				
			||||||
 | 
					        const {container} = render(
 | 
				
			||||||
 | 
					            <NumberInput
 | 
				
			||||||
 | 
					                label="Test label"
 | 
				
			||||||
 | 
					                helpText="Some help text"
 | 
				
			||||||
 | 
					                value={1123}
 | 
				
			||||||
 | 
					                onChange={() => {
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        expect(container.firstChild).toMatchSnapshot();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/bootstrap/__tests__/PasswordInput.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/bootstrap/__tests__/PasswordInput.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {render} from 'customTestRender';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import PasswordInput from '../components/PasswordInput';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('<PasswordInput/>', () => {
 | 
				
			||||||
 | 
					    it('Render password input', () => {
 | 
				
			||||||
 | 
					        const {container} = render(
 | 
				
			||||||
 | 
					            <PasswordInput
 | 
				
			||||||
 | 
					                label="Test label"
 | 
				
			||||||
 | 
					                helpText="Some help text"
 | 
				
			||||||
 | 
					                value="Some password"
 | 
				
			||||||
 | 
					                onChange={() => {
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        expect(container.firstChild).toMatchSnapshot();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										35
									
								
								src/bootstrap/__tests__/RadioSet.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/bootstrap/__tests__/RadioSet.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {render} from 'customTestRender';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import RadioSet from '../components/RadioSet';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const TEST_CHOICES = [
 | 
				
			||||||
 | 
					    {label: 'label', value: 'value'},
 | 
				
			||||||
 | 
					    {label: 'another label', value: 'another value'},
 | 
				
			||||||
 | 
					    {label: 'another one  label', value: 'another on value'}
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('<RadioSet/>', () => {
 | 
				
			||||||
 | 
					    it('Render radio set', () => {
 | 
				
			||||||
 | 
					        const {container} = render(
 | 
				
			||||||
 | 
					            <RadioSet
 | 
				
			||||||
 | 
					                name={'test_name'}
 | 
				
			||||||
 | 
					                label='Radios set label'
 | 
				
			||||||
 | 
					                value='value'
 | 
				
			||||||
 | 
					                choices={TEST_CHOICES}
 | 
				
			||||||
 | 
					                helpText={'Some help text'}
 | 
				
			||||||
 | 
					                onChange={() => {
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        expect(container.firstChild).toMatchSnapshot();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										51
									
								
								src/bootstrap/__tests__/Select.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/bootstrap/__tests__/Select.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {fireEvent, getByDisplayValue, getByText, render} from 'customTestRender';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Select from '../components/Select';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const TEST_CHOICES = {
 | 
				
			||||||
 | 
					    '1': 'one',
 | 
				
			||||||
 | 
					    '2': 'two',
 | 
				
			||||||
 | 
					    '3': 'three',
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('<Select/>', () => {
 | 
				
			||||||
 | 
					    var selectContainer;
 | 
				
			||||||
 | 
					    const onChangeHandler = jest.fn();
 | 
				
			||||||
 | 
					    beforeEach(() => {
 | 
				
			||||||
 | 
					        const {container} = render(
 | 
				
			||||||
 | 
					            <Select
 | 
				
			||||||
 | 
					                label='Test label'
 | 
				
			||||||
 | 
					                value='1'
 | 
				
			||||||
 | 
					                choices={TEST_CHOICES}
 | 
				
			||||||
 | 
					                helpText='Help text'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                onChange={onChangeHandler}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        selectContainer = container;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('Test with snapshot.', () => {
 | 
				
			||||||
 | 
					        expect(selectContainer).toMatchSnapshot();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('Test onChange handling.', () => {
 | 
				
			||||||
 | 
					        const select = getByDisplayValue(selectContainer, 'one');
 | 
				
			||||||
 | 
					        expect(select.value).toBe('1');
 | 
				
			||||||
 | 
					        fireEvent.change(select, {target: {value: '2'}});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const option = getByText(selectContainer, 'two');
 | 
				
			||||||
 | 
					        expect(onChangeHandler).toBeCalled();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(option.value).toBe('2');
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/bootstrap/__tests__/TextInput.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/bootstrap/__tests__/TextInput.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {render} from 'customTestRender';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import TextInput from '../components/TextInput';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('<TextInput/>', () => {
 | 
				
			||||||
 | 
					    it('Render text input', () => {
 | 
				
			||||||
 | 
					        const {container} = render(
 | 
				
			||||||
 | 
					            <TextInput
 | 
				
			||||||
 | 
					                label="Test label"
 | 
				
			||||||
 | 
					                helpText="Some help text"
 | 
				
			||||||
 | 
					                value="Some text"
 | 
				
			||||||
 | 
					                onChange={() => {
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        expect(container.firstChild).toMatchSnapshot();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										37
									
								
								src/bootstrap/__tests__/__snapshots__/Button.test.js.snap
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/bootstrap/__tests__/__snapshots__/Button.test.js.snap
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					// Jest Snapshot v1, https://goo.gl/fbAQLP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<Button /> Render button correctly 1`] = `
 | 
				
			||||||
 | 
					<button
 | 
				
			||||||
 | 
					  class="btn btn-primary "
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					  Test Button
 | 
				
			||||||
 | 
					</button>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<Button /> Render button with custom classes 1`] = `
 | 
				
			||||||
 | 
					<button
 | 
				
			||||||
 | 
					  class="btn one two three"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					  Test Button
 | 
				
			||||||
 | 
					</button>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<Button /> Render button with spinner 1`] = `
 | 
				
			||||||
 | 
					<button
 | 
				
			||||||
 | 
					  class="btn btn-primary "
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					  <span
 | 
				
			||||||
 | 
					    aria-hidden="true"
 | 
				
			||||||
 | 
					    class="spinner-border spinner-border-sm"
 | 
				
			||||||
 | 
					    role="status"
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					  Test Button
 | 
				
			||||||
 | 
					</button>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
							
								
								
									
										62
									
								
								src/bootstrap/__tests__/__snapshots__/Checkbox.test.js.snap
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/bootstrap/__tests__/__snapshots__/Checkbox.test.js.snap
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					// Jest Snapshot v1, https://goo.gl/fbAQLP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<Checkbox/> Render checkbox 1`] = `
 | 
				
			||||||
 | 
					<div
 | 
				
			||||||
 | 
					  class="col-sm-12 offset-lg-1 col-lg-10"
 | 
				
			||||||
 | 
					  style="margin-bottom: 1rem;"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					  <div
 | 
				
			||||||
 | 
					    class="custom-control custom-checkbox"
 | 
				
			||||||
 | 
					    style="margin-bottom: 0px;"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <input
 | 
				
			||||||
 | 
					      checked=""
 | 
				
			||||||
 | 
					      class="custom-control-input"
 | 
				
			||||||
 | 
					      id="1"
 | 
				
			||||||
 | 
					      type="checkbox"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <label
 | 
				
			||||||
 | 
					      class="custom-control-label"
 | 
				
			||||||
 | 
					      for="1"
 | 
				
			||||||
 | 
					      style="margin-bottom: 0px;"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      Test label
 | 
				
			||||||
 | 
					    </label>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <small
 | 
				
			||||||
 | 
					    class="form-text text-muted"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    Some help text
 | 
				
			||||||
 | 
					  </small>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<Checkbox/> Render uncheked checkbox 1`] = `
 | 
				
			||||||
 | 
					<div
 | 
				
			||||||
 | 
					  class="col-sm-12 offset-lg-1 col-lg-10"
 | 
				
			||||||
 | 
					  style="margin-bottom: 1rem;"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					  <div
 | 
				
			||||||
 | 
					    class="custom-control custom-checkbox"
 | 
				
			||||||
 | 
					    style="margin-bottom: 0px;"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <input
 | 
				
			||||||
 | 
					      class="custom-control-input"
 | 
				
			||||||
 | 
					      id="1"
 | 
				
			||||||
 | 
					      type="checkbox"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <label
 | 
				
			||||||
 | 
					      class="custom-control-label"
 | 
				
			||||||
 | 
					      for="1"
 | 
				
			||||||
 | 
					      style="margin-bottom: 0px;"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      Test label
 | 
				
			||||||
 | 
					    </label>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <small
 | 
				
			||||||
 | 
					    class="form-text text-muted"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    Some help text
 | 
				
			||||||
 | 
					  </small>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					// Jest Snapshot v1, https://goo.gl/fbAQLP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<NumberInput/> Render number input 1`] = `
 | 
				
			||||||
 | 
					<div
 | 
				
			||||||
 | 
					  class="col-sm-12 offset-lg-1 col-lg-10"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					  <div
 | 
				
			||||||
 | 
					    class="form-group"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <label
 | 
				
			||||||
 | 
					      for="1"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      Test label
 | 
				
			||||||
 | 
					    </label>
 | 
				
			||||||
 | 
					    <div
 | 
				
			||||||
 | 
					      class="input-group"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <input
 | 
				
			||||||
 | 
					        class="form-control"
 | 
				
			||||||
 | 
					        id="1"
 | 
				
			||||||
 | 
					        type="number"
 | 
				
			||||||
 | 
					        value="1123"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <small
 | 
				
			||||||
 | 
					      class="form-text text-muted"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      Some help text
 | 
				
			||||||
 | 
					    </small>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					// Jest Snapshot v1, https://goo.gl/fbAQLP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<PasswordInput/> Render password input 1`] = `
 | 
				
			||||||
 | 
					<div
 | 
				
			||||||
 | 
					  class="col-sm-12 offset-lg-1 col-lg-10"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					  <div
 | 
				
			||||||
 | 
					    class="form-group"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <label
 | 
				
			||||||
 | 
					      for="1"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      Test label
 | 
				
			||||||
 | 
					    </label>
 | 
				
			||||||
 | 
					    <div
 | 
				
			||||||
 | 
					      class="input-group"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <input
 | 
				
			||||||
 | 
					        autocomplete="new-password"
 | 
				
			||||||
 | 
					        class="form-control"
 | 
				
			||||||
 | 
					        id="1"
 | 
				
			||||||
 | 
					        type="password"
 | 
				
			||||||
 | 
					        value="Some password"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <small
 | 
				
			||||||
 | 
					      class="form-text text-muted"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      Some help text
 | 
				
			||||||
 | 
					    </small>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
							
								
								
									
										73
									
								
								src/bootstrap/__tests__/__snapshots__/RadioSet.test.js.snap
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/bootstrap/__tests__/__snapshots__/RadioSet.test.js.snap
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					// Jest Snapshot v1, https://goo.gl/fbAQLP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<RadioSet/> Render radio set 1`] = `
 | 
				
			||||||
 | 
					<div
 | 
				
			||||||
 | 
					  class="form-group col-sm-12 offset-lg-1 col-lg-10"
 | 
				
			||||||
 | 
					  style="margin-bottom: 1rem;"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					  <label
 | 
				
			||||||
 | 
					    class="col-12"
 | 
				
			||||||
 | 
					    for="1"
 | 
				
			||||||
 | 
					    style="padding-left: 0px;"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    Radios set label
 | 
				
			||||||
 | 
					  </label>
 | 
				
			||||||
 | 
					  <div
 | 
				
			||||||
 | 
					    class="custom-control custom-radio custom-control-inline"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <input
 | 
				
			||||||
 | 
					      checked=""
 | 
				
			||||||
 | 
					      class="custom-control-input"
 | 
				
			||||||
 | 
					      id="test_name-0"
 | 
				
			||||||
 | 
					      name="test_name"
 | 
				
			||||||
 | 
					      type="radio"
 | 
				
			||||||
 | 
					      value="value"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <label
 | 
				
			||||||
 | 
					      class="custom-control-label"
 | 
				
			||||||
 | 
					      for="test_name-0"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      label
 | 
				
			||||||
 | 
					    </label>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <div
 | 
				
			||||||
 | 
					    class="custom-control custom-radio custom-control-inline"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <input
 | 
				
			||||||
 | 
					      class="custom-control-input"
 | 
				
			||||||
 | 
					      id="test_name-1"
 | 
				
			||||||
 | 
					      name="test_name"
 | 
				
			||||||
 | 
					      type="radio"
 | 
				
			||||||
 | 
					      value="another value"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <label
 | 
				
			||||||
 | 
					      class="custom-control-label"
 | 
				
			||||||
 | 
					      for="test_name-1"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      another label
 | 
				
			||||||
 | 
					    </label>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <div
 | 
				
			||||||
 | 
					    class="custom-control custom-radio custom-control-inline"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <input
 | 
				
			||||||
 | 
					      class="custom-control-input"
 | 
				
			||||||
 | 
					      id="test_name-2"
 | 
				
			||||||
 | 
					      name="test_name"
 | 
				
			||||||
 | 
					      type="radio"
 | 
				
			||||||
 | 
					      value="another on value"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <label
 | 
				
			||||||
 | 
					      class="custom-control-label"
 | 
				
			||||||
 | 
					      for="test_name-2"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      another one  label
 | 
				
			||||||
 | 
					    </label>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <small
 | 
				
			||||||
 | 
					    class="form-text text-muted"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    Some help text
 | 
				
			||||||
 | 
					  </small>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
							
								
								
									
										40
									
								
								src/bootstrap/__tests__/__snapshots__/Select.test.js.snap
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/bootstrap/__tests__/__snapshots__/Select.test.js.snap
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					// Jest Snapshot v1, https://goo.gl/fbAQLP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<Select/> Test with snapshot. 1`] = `
 | 
				
			||||||
 | 
					<div>
 | 
				
			||||||
 | 
					  <div
 | 
				
			||||||
 | 
					    class="form-group col-sm-12 offset-lg-1 col-lg-10"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <label
 | 
				
			||||||
 | 
					      for="1"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      Test label
 | 
				
			||||||
 | 
					    </label>
 | 
				
			||||||
 | 
					    <select
 | 
				
			||||||
 | 
					      class="custom-select"
 | 
				
			||||||
 | 
					      id="1"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <option
 | 
				
			||||||
 | 
					        value="1"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        one
 | 
				
			||||||
 | 
					      </option>
 | 
				
			||||||
 | 
					      <option
 | 
				
			||||||
 | 
					        value="2"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        two
 | 
				
			||||||
 | 
					      </option>
 | 
				
			||||||
 | 
					      <option
 | 
				
			||||||
 | 
					        value="3"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        three
 | 
				
			||||||
 | 
					      </option>
 | 
				
			||||||
 | 
					    </select>
 | 
				
			||||||
 | 
					    <small
 | 
				
			||||||
 | 
					      class="form-text text-muted"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      Help text
 | 
				
			||||||
 | 
					    </small>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/bootstrap/__tests__/__snapshots__/TextInput.test.js.snap
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/bootstrap/__tests__/__snapshots__/TextInput.test.js.snap
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					// Jest Snapshot v1, https://goo.gl/fbAQLP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<TextInput/> Render text input 1`] = `
 | 
				
			||||||
 | 
					<div
 | 
				
			||||||
 | 
					  class="col-sm-12 offset-lg-1 col-lg-10"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					  <div
 | 
				
			||||||
 | 
					    class="form-group"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <label
 | 
				
			||||||
 | 
					      for="1"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      Test label
 | 
				
			||||||
 | 
					    </label>
 | 
				
			||||||
 | 
					    <div
 | 
				
			||||||
 | 
					      class="input-group"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <input
 | 
				
			||||||
 | 
					        class="form-control"
 | 
				
			||||||
 | 
					        id="1"
 | 
				
			||||||
 | 
					        type="text"
 | 
				
			||||||
 | 
					        value="Some text"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <small
 | 
				
			||||||
 | 
					      class="form-text text-muted"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      Some help text
 | 
				
			||||||
 | 
					    </small>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
							
								
								
									
										31
									
								
								src/bootstrap/components/Alert.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/bootstrap/components/Alert.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Alert.propTypes = {
 | 
				
			||||||
 | 
					    /** Type of the alert it adds as `alert-${type}` class.*/
 | 
				
			||||||
 | 
					    type: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    /** Alert message.*/
 | 
				
			||||||
 | 
					    message: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Alert content.*/
 | 
				
			||||||
 | 
					    children: PropTypes.oneOfType([
 | 
				
			||||||
 | 
					        PropTypes.arrayOf(PropTypes.node),
 | 
				
			||||||
 | 
					        PropTypes.node
 | 
				
			||||||
 | 
					    ]),
 | 
				
			||||||
 | 
					    /** onDismiss handler.*/
 | 
				
			||||||
 | 
					    onDismiss: PropTypes.func
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Alert({type, message, onDismiss, children}) {
 | 
				
			||||||
 | 
					    return <div className={`alert alert-dismissible alert-${type}`}>
 | 
				
			||||||
 | 
					        {onDismiss ? <button type="button" className="close" onClick={onDismiss}>×</button> : false}
 | 
				
			||||||
 | 
					        {message}
 | 
				
			||||||
 | 
					        {children}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/bootstrap/components/Alert.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/bootstrap/components/Alert.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					Bootstrap alert component.
 | 
				
			||||||
 | 
					```jsx
 | 
				
			||||||
 | 
					import {useState} from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function AlertExample(){
 | 
				
			||||||
 | 
					    const [alert, setAlert] = useState(true);
 | 
				
			||||||
 | 
					    if (alert)
 | 
				
			||||||
 | 
					        return <Alert 
 | 
				
			||||||
 | 
					            type='warning' 
 | 
				
			||||||
 | 
					            message='Some warning out there!' 
 | 
				
			||||||
 | 
					            onDismiss={()=>setAlert(false)}
 | 
				
			||||||
 | 
					        />;
 | 
				
			||||||
 | 
					    return <button 
 | 
				
			||||||
 | 
					        className='btn btn-secondary' 
 | 
				
			||||||
 | 
					        onClick={()=>setAlert(true)}
 | 
				
			||||||
 | 
					    >Show alert again</button>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					<AlertExample/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										43
									
								
								src/bootstrap/components/Button.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/bootstrap/components/Button.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const OFFSET = 8;
 | 
				
			||||||
 | 
					const SIZE = 3;
 | 
				
			||||||
 | 
					const SIZE_CLASS = ' offset-lg-' + OFFSET + ' col-lg-' + SIZE;
 | 
				
			||||||
 | 
					const SIZE_CLASS_SM = ' col-sm-12';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Button.propTypes = {
 | 
				
			||||||
 | 
					    /** Additional class name. */
 | 
				
			||||||
 | 
					    className: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Use foris form size and offset. */
 | 
				
			||||||
 | 
					    forisFormSize: PropTypes.bool,
 | 
				
			||||||
 | 
					    /** Show loading icon. */
 | 
				
			||||||
 | 
					    loading: PropTypes.bool,
 | 
				
			||||||
 | 
					    /** Button content. */
 | 
				
			||||||
 | 
					    children: PropTypes.oneOfType([
 | 
				
			||||||
 | 
					        PropTypes.string,
 | 
				
			||||||
 | 
					        PropTypes.element,
 | 
				
			||||||
 | 
					        PropTypes.node,
 | 
				
			||||||
 | 
					        PropTypes.arrayOf(PropTypes.node),
 | 
				
			||||||
 | 
					    ]).isRequired
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Button({className, loading, forisFormSize, children, ...props}) {
 | 
				
			||||||
 | 
					    className = className ? 'btn ' + className : 'btn btn-primary ';
 | 
				
			||||||
 | 
					    if (forisFormSize)
 | 
				
			||||||
 | 
					        className += SIZE_CLASS + SIZE_CLASS_SM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const span = loading ?
 | 
				
			||||||
 | 
					        <span className='spinner-border spinner-border-sm' role='status' aria-hidden='true'/> : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <button className={className} {...props}>
 | 
				
			||||||
 | 
					        {span} {span ? ' ' : null} {children}
 | 
				
			||||||
 | 
					    </button>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/bootstrap/components/Button.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/bootstrap/components/Button.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					Bootstrap button component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All additional `props` are passed to the `<button>` HTML component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Can be used without parameters:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```jsx
 | 
				
			||||||
 | 
					<Button>Click</Button>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Using loading spinner:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```jsx
 | 
				
			||||||
 | 
					<Button loading disabled>Loading...</Button>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/bootstrap/components/Checkbox.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/bootstrap/components/Checkbox.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import {useUID} from 'react-uid/dist/es5/index';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {formFieldsSize} from './constants';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CheckBox.propTypes = {
 | 
				
			||||||
 | 
					    /** Label message */
 | 
				
			||||||
 | 
					    label: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    /** Help text message */
 | 
				
			||||||
 | 
					    helpText: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Apply default size (full-width) */
 | 
				
			||||||
 | 
					    useDefaultSize: PropTypes.bool,
 | 
				
			||||||
 | 
					    /** Control if checkbox is clickable */
 | 
				
			||||||
 | 
					    disabled: PropTypes.bool
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CheckBox.defaultProps = {
 | 
				
			||||||
 | 
					    useDefaultSize: true,
 | 
				
			||||||
 | 
					    disabled: false
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function CheckBox({label, helpText, useDefaultSize, disabled, ...props}) {
 | 
				
			||||||
 | 
					    const uid = useUID();
 | 
				
			||||||
 | 
					    return <div className={useDefaultSize ? formFieldsSize : ""} style={{marginBottom: '1rem'}}>
 | 
				
			||||||
 | 
					        <div className='custom-control custom-checkbox' style={{marginBottom: '0'}}>
 | 
				
			||||||
 | 
					            <input
 | 
				
			||||||
 | 
					                className='custom-control-input'
 | 
				
			||||||
 | 
					                type='checkbox'
 | 
				
			||||||
 | 
					                id={uid}
 | 
				
			||||||
 | 
					                disabled={disabled}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                {...props}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <label className='custom-control-label' htmlFor={uid} style={helpText ? {marginBottom: '0'} : null}>{label}</label>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        {helpText ? <small className="form-text text-muted">{helpText}</small> : null}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								src/bootstrap/components/Checkbox.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/bootstrap/components/Checkbox.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					Checkbox with label Bootstrap component with predefined sizes and structure for using in foris forms.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All additional `props` are passed to the `<input type="checkbox">` HTML component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					import {useState} from 'react';
 | 
				
			||||||
 | 
					const [value, setValue] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<CheckBox
 | 
				
			||||||
 | 
					    value={value}
 | 
				
			||||||
 | 
					    label="Some label" 
 | 
				
			||||||
 | 
					    helpText="Read the small text!"
 | 
				
			||||||
 | 
					    onChange={value => setValue(value)}
 | 
				
			||||||
 | 
					/>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										57
									
								
								src/bootstrap/components/DataTimeInput.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/bootstrap/components/DataTimeInput.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import Datetime from 'react-datetime/DateTime';
 | 
				
			||||||
 | 
					import moment from 'moment/moment';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Input from './Input';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DataTimeInput.propTypes = {
 | 
				
			||||||
 | 
					    /** Field label. */
 | 
				
			||||||
 | 
					    label: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    /** Error message. */
 | 
				
			||||||
 | 
					    error: PropTypes.string,
 | 
				
			||||||
 | 
					    /** DataTime or Data or Time value. Can be `moment` or string.*/
 | 
				
			||||||
 | 
					    value: PropTypes.oneOfType([PropTypes.objectOf(moment), PropTypes.string]),
 | 
				
			||||||
 | 
					    /** Help text message. */
 | 
				
			||||||
 | 
					    helpText: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Content. */
 | 
				
			||||||
 | 
					    children: PropTypes.oneOfType([
 | 
				
			||||||
 | 
					        PropTypes.arrayOf(PropTypes.node),
 | 
				
			||||||
 | 
					        PropTypes.node
 | 
				
			||||||
 | 
					    ]),
 | 
				
			||||||
 | 
					    isValidDate: PropTypes.bool,
 | 
				
			||||||
 | 
					    onChange: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    dateFormat: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
 | 
				
			||||||
 | 
					    timeFormat: PropTypes.string
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD';
 | 
				
			||||||
 | 
					const DEFAULT_TIME_FORMAT = 'HH:mm:ss';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function DataTimeInput({value, onChange, isValidDate, dateFormat, timeFormat, children, ...props}) {
 | 
				
			||||||
 | 
					    function renderInput(datetimeProps) {
 | 
				
			||||||
 | 
					        return <Input
 | 
				
			||||||
 | 
					            {...props}
 | 
				
			||||||
 | 
					            {...datetimeProps}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					            {children}
 | 
				
			||||||
 | 
					        </Input>
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <Datetime
 | 
				
			||||||
 | 
					        locale={ForisTranslations.locale}
 | 
				
			||||||
 | 
					        dateFormat={dateFormat !== undefined ? dateFormat : DEFAULT_DATE_FORMAT}
 | 
				
			||||||
 | 
					        timeFormat={timeFormat !== undefined ? timeFormat : DEFAULT_TIME_FORMAT}
 | 
				
			||||||
 | 
					        value={value}
 | 
				
			||||||
 | 
					        onChange={onChange}
 | 
				
			||||||
 | 
					        isValidDate={isValidDate}
 | 
				
			||||||
 | 
					        renderInput={renderInput}
 | 
				
			||||||
 | 
					    />;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/bootstrap/components/DataTimeInput.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/bootstrap/components/DataTimeInput.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					Adopted from `react-datetime/DateTime` datatime picker component.
 | 
				
			||||||
 | 
					It uses `momentjs` see example.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It requires `ForisTranslations.locale` to be defined in order to use right locale.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					ForisTranslations={locale:'en'};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {useState, useEffect} from 'react';
 | 
				
			||||||
 | 
					import moment from 'moment/moment';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const [dataTime, setDataTime] = useState(moment());
 | 
				
			||||||
 | 
					const [error, setError] = useState();
 | 
				
			||||||
 | 
					useEffect(()=>{
 | 
				
			||||||
 | 
					   dataTime.isValid() ? setError(null) : setError('Invalid value!');
 | 
				
			||||||
 | 
					},[dataTime]);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					<DataTimeInput
 | 
				
			||||||
 | 
					    label='Time to sleep'
 | 
				
			||||||
 | 
					    value={dataTime}
 | 
				
			||||||
 | 
					    error={error}
 | 
				
			||||||
 | 
					    helpText='Example helptext...'
 | 
				
			||||||
 | 
					    onChange={value => setDataTime(value)}
 | 
				
			||||||
 | 
					/>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/bootstrap/components/EmailInput.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/bootstrap/components/EmailInput.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Input from './Input';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const EmailInput = ({...props}) => <Input type="email" {...props}/>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EmailInput.propTypes = {
 | 
				
			||||||
 | 
					    /** Field label. */
 | 
				
			||||||
 | 
					    label: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    /** Error message. */
 | 
				
			||||||
 | 
					    error: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Help text message. */
 | 
				
			||||||
 | 
					    helpText: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Email value. */
 | 
				
			||||||
 | 
					    value: PropTypes.string,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default EmailInput;
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/bootstrap/components/EmailInput.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/bootstrap/components/EmailInput.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					Bootstrap component of email input with label with predefined sizes and structure for using in foris forms.
 | 
				
			||||||
 | 
					It use built-in browser email address checking. It's only meaningful using inside `<form>`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All additional `props` are passed to the `<input type="email">` HTML component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					import {useState} from 'react';
 | 
				
			||||||
 | 
					const [email, setEmail] = useState('Wrong email');
 | 
				
			||||||
 | 
					<form onSubmit={e=>e.preventDefault()}>
 | 
				
			||||||
 | 
					    <EmailInput
 | 
				
			||||||
 | 
					        value={email}
 | 
				
			||||||
 | 
					        label="Some label" 
 | 
				
			||||||
 | 
					        helpText="Read the small text!"
 | 
				
			||||||
 | 
					        onChange={target => setEmail(target.value)}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <button type="submit">Try to submit</button>
 | 
				
			||||||
 | 
					</form>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/bootstrap/components/Input.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/bootstrap/components/Input.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import {useUID} from 'react-uid/dist/es5/index';
 | 
				
			||||||
 | 
					import {formFieldsSize} from './constants';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Input.propTypes = {
 | 
				
			||||||
 | 
					    type: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    label: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    helpText: PropTypes.string,
 | 
				
			||||||
 | 
					    error: PropTypes.string,
 | 
				
			||||||
 | 
					    className: PropTypes.string,
 | 
				
			||||||
 | 
					    children: PropTypes.oneOfType([
 | 
				
			||||||
 | 
					        PropTypes.arrayOf(PropTypes.node),
 | 
				
			||||||
 | 
					        PropTypes.node
 | 
				
			||||||
 | 
					    ])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Base bootstrap input component. */
 | 
				
			||||||
 | 
					export default function Input({type, label, helpText, error, className, children, ...props}) {
 | 
				
			||||||
 | 
					    const uid = useUID();
 | 
				
			||||||
 | 
					    const inputClassName = `form-control ${className ? className : ''} ${(error ? 'is-invalid' : '')}`.trim();
 | 
				
			||||||
 | 
					    return <div className={formFieldsSize}>
 | 
				
			||||||
 | 
					        <div className='form-group'>
 | 
				
			||||||
 | 
					            <label htmlFor={uid}>{label}</label>
 | 
				
			||||||
 | 
					            <div className='input-group'>
 | 
				
			||||||
 | 
					                <input
 | 
				
			||||||
 | 
					                    className={inputClassName}
 | 
				
			||||||
 | 
					                    type={type}
 | 
				
			||||||
 | 
					                    id={uid}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    {...props}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                {children}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            {error ? <div className='invalid-feedback'>{error}</div> : null}
 | 
				
			||||||
 | 
					            {helpText ? <small className="form-text text-muted">{helpText}</small> : null}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										88
									
								
								src/bootstrap/components/Modal.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/bootstrap/components/Modal.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React, {useEffect, useRef} from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import Portal from 'utils/Portal';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Modal.propTypes = {
 | 
				
			||||||
 | 
					    /** Is modal shown value */
 | 
				
			||||||
 | 
					    shown: PropTypes.bool.isRequired,
 | 
				
			||||||
 | 
					    /** Callback to manage modal visibility */
 | 
				
			||||||
 | 
					    setShown: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Modal content use following: `ModalHeader`, `ModalBody`, `ModalFooter` */
 | 
				
			||||||
 | 
					    children: PropTypes.oneOfType([
 | 
				
			||||||
 | 
					        PropTypes.arrayOf(PropTypes.node),
 | 
				
			||||||
 | 
					        PropTypes.node
 | 
				
			||||||
 | 
					    ]).isRequired
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function Modal({shown, setShown, children}) {
 | 
				
			||||||
 | 
					    const dialogRef = useRef();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        function handleClickOutsideDialog(e) {
 | 
				
			||||||
 | 
					            if (!dialogRef.current.contains(e.target))
 | 
				
			||||||
 | 
					                setShown(false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        document.addEventListener("mousedown", handleClickOutsideDialog);
 | 
				
			||||||
 | 
					        return () => {
 | 
				
			||||||
 | 
					            document.removeEventListener("mousedown", handleClickOutsideDialog);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }, [setShown]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <Portal containerId="modal_container">
 | 
				
			||||||
 | 
					        <div className={'modal fade ' + (shown ? 'show' : '')} role="dialog">
 | 
				
			||||||
 | 
					            <div ref={dialogRef} className="modal-dialog" role="document">
 | 
				
			||||||
 | 
					                <div className="modal-content">
 | 
				
			||||||
 | 
					                    {children}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </Portal>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ModalHeader.propTypes = {
 | 
				
			||||||
 | 
					    setShown: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    title: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function ModalHeader({setShown, title}) {
 | 
				
			||||||
 | 
					    return <div className="modal-header">
 | 
				
			||||||
 | 
					        <h5 className="modal-title">{title}</h5>
 | 
				
			||||||
 | 
					        <button type="button" className="close" onClick={() => setShown(false)}>
 | 
				
			||||||
 | 
					            <span aria-hidden="true">×</span>
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ModalBody.propTypes = {
 | 
				
			||||||
 | 
					    children: PropTypes.oneOfType([
 | 
				
			||||||
 | 
					        PropTypes.arrayOf(PropTypes.node),
 | 
				
			||||||
 | 
					        PropTypes.node
 | 
				
			||||||
 | 
					    ]).isRequired
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function ModalBody({children}) {
 | 
				
			||||||
 | 
					    return <div className="modal-body">{children}</div>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ModalFooter.propTypes = {
 | 
				
			||||||
 | 
					    children: PropTypes.oneOfType([
 | 
				
			||||||
 | 
					        PropTypes.arrayOf(PropTypes.node),
 | 
				
			||||||
 | 
					        PropTypes.node
 | 
				
			||||||
 | 
					    ]).isRequired
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function ModalFooter({children}) {
 | 
				
			||||||
 | 
					    return <div className="modal-footer">
 | 
				
			||||||
 | 
					        {children}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/bootstrap/components/Modal.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/bootstrap/components/Modal.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					Bootstrap modal component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I have no idea why example doesn't work here but you can investigate HTML code...
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					import {ModalHeader, ModalBody, ModalFooter} from './Modal';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {useState} from 'react';
 | 
				
			||||||
 | 
					const [shown, setShown] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<>
 | 
				
			||||||
 | 
					    <Modal shown={shown}>
 | 
				
			||||||
 | 
					        <ModalHeader setShown={setShown} title='Warning!'/>
 | 
				
			||||||
 | 
					        <ModalBody><p>Bla bla bla...</p></ModalBody>
 | 
				
			||||||
 | 
					        <ModalFooter>
 | 
				
			||||||
 | 
					            <button 
 | 
				
			||||||
 | 
					                className='btn btn-secondary' 
 | 
				
			||||||
 | 
					                onClick={() => setShown(false)}
 | 
				
			||||||
 | 
					            >Skip it</button>
 | 
				
			||||||
 | 
					        </ModalFooter>
 | 
				
			||||||
 | 
					    </Modal>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    <button 
 | 
				
			||||||
 | 
					        className='btn btn-secondary'
 | 
				
			||||||
 | 
					        onClick={()=>setShown(true)}
 | 
				
			||||||
 | 
					    >Show modal</button>
 | 
				
			||||||
 | 
					</>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										29
									
								
								src/bootstrap/components/NumberInput.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/bootstrap/components/NumberInput.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Input from './Input';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const NumberInput = ({...props}) => <Input type="number" {...props}/>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NumberInput.propTypes = {
 | 
				
			||||||
 | 
					    /** Field label. */
 | 
				
			||||||
 | 
					    label: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    /** Error message. */
 | 
				
			||||||
 | 
					    error: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Help text message. */
 | 
				
			||||||
 | 
					    helpText: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Number value. */
 | 
				
			||||||
 | 
					    value: PropTypes.oneOfType([
 | 
				
			||||||
 | 
					        PropTypes.string,
 | 
				
			||||||
 | 
					        PropTypes.number,
 | 
				
			||||||
 | 
					    ]),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default NumberInput;
 | 
				
			||||||
							
								
								
									
										17
									
								
								src/bootstrap/components/NumberInput.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/bootstrap/components/NumberInput.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					Bootstrap component of number input with label with predefined sizes and structure for using in foris forms.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All additional `props` are passed to the `<input type="number">` HTML component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					import {useState} from 'react';
 | 
				
			||||||
 | 
					const [value, setValue] = useState(42);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<NumberInput
 | 
				
			||||||
 | 
					    value={value}
 | 
				
			||||||
 | 
					    label="Some number" 
 | 
				
			||||||
 | 
					    helpText="Read the small text!"
 | 
				
			||||||
 | 
					    min='33'
 | 
				
			||||||
 | 
					    max='54'
 | 
				
			||||||
 | 
					    onChange={target => setValue(target.value)}
 | 
				
			||||||
 | 
					/>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										44
									
								
								src/bootstrap/components/PasswordInput.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/bootstrap/components/PasswordInput.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React, {useState} from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Input from './Input';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PasswordInput.propTypes = {
 | 
				
			||||||
 | 
					    /** Field label. */
 | 
				
			||||||
 | 
					    label: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    /** Error message. */
 | 
				
			||||||
 | 
					    error: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Password value. */
 | 
				
			||||||
 | 
					    value: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Help text message. */
 | 
				
			||||||
 | 
					    helpText: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Use show/hide password button. */
 | 
				
			||||||
 | 
					    withEye: PropTypes.bool,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function PasswordInput({withEye, ...props}) {
 | 
				
			||||||
 | 
					    const [isHidden, setHidden] = useState(true);
 | 
				
			||||||
 | 
					    return <Input
 | 
				
			||||||
 | 
					        type={withEye ? isHidden ? 'password' : 'text' : 'password'}
 | 
				
			||||||
 | 
					        autoComplete={isHidden ? 'new-password' : null}
 | 
				
			||||||
 | 
					        {...props}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					        {withEye ?
 | 
				
			||||||
 | 
					            <div className="input-group-append">
 | 
				
			||||||
 | 
					                <button className="input-group-text" onClick={e => {
 | 
				
			||||||
 | 
					                    e.preventDefault();
 | 
				
			||||||
 | 
					                    setHidden(isHidden => !isHidden);
 | 
				
			||||||
 | 
					                }}>
 | 
				
			||||||
 | 
					                    <i className={'fa ' + (isHidden ? 'fa-eye' : 'fa-eye-slash')}/>
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            : null}
 | 
				
			||||||
 | 
					    </Input>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								src/bootstrap/components/PasswordInput.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/bootstrap/components/PasswordInput.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					Password Bootstrap component input with label and predefined sizes and structure for using in foris forms.
 | 
				
			||||||
 | 
					Can be used with "eye" button, see example.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All additional `props` are passed to the `<input type="password">` HTML component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					import {useState} from 'react';
 | 
				
			||||||
 | 
					const [value, setValue] = useState('secret');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<PasswordInput
 | 
				
			||||||
 | 
					    withEye
 | 
				
			||||||
 | 
					    value={value}
 | 
				
			||||||
 | 
					    label="Some password" 
 | 
				
			||||||
 | 
					    helpText="Read the small text!"
 | 
				
			||||||
 | 
					    onChange={target => setValue(target.value)}
 | 
				
			||||||
 | 
					/>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										80
									
								
								src/bootstrap/components/RadioSet.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/bootstrap/components/RadioSet.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import {useUID} from 'react-uid/dist/es5/index';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {formFieldsSize} from './constants';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RadioSet.propTypes = {
 | 
				
			||||||
 | 
					    /** Name attribute of the input HTML tag.*/
 | 
				
			||||||
 | 
					    name: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    /** RadioSet label .*/
 | 
				
			||||||
 | 
					    label: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Choices .*/
 | 
				
			||||||
 | 
					    choices: PropTypes.arrayOf(PropTypes.shape({
 | 
				
			||||||
 | 
					        /** Choice lable .*/
 | 
				
			||||||
 | 
					        label: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					        /** Choice value .*/
 | 
				
			||||||
 | 
					        value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
 | 
				
			||||||
 | 
					    })).isRequired,
 | 
				
			||||||
 | 
					    /** Initial value .*/
 | 
				
			||||||
 | 
					    value: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Help text message .*/
 | 
				
			||||||
 | 
					    helpText: PropTypes.string,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function RadioSet({name, label, choices, value, helpText, ...props}) {
 | 
				
			||||||
 | 
					    const uid = useUID();
 | 
				
			||||||
 | 
					    const radios = choices.map((choice, key) =>
 | 
				
			||||||
 | 
					        <Radio
 | 
				
			||||||
 | 
					            id={`${name}-${key}`}
 | 
				
			||||||
 | 
					            key={key}
 | 
				
			||||||
 | 
					            name={name}
 | 
				
			||||||
 | 
					            label={choice.label}
 | 
				
			||||||
 | 
					            value={choice.value}
 | 
				
			||||||
 | 
					            helpText={choice.helpText}
 | 
				
			||||||
 | 
					            checked={choice.value === value}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {...props}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <div className={`form-group ${formFieldsSize}`} style={{marginBottom: '1rem'}}>
 | 
				
			||||||
 | 
					        {label ?
 | 
				
			||||||
 | 
					            <label className='col-12' htmlFor={uid} style={{paddingLeft: '0'}}>
 | 
				
			||||||
 | 
					                {label}
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
 | 
					            : null}
 | 
				
			||||||
 | 
					        {radios}
 | 
				
			||||||
 | 
					        {helpText ? <small className="form-text text-muted">{helpText}</small> : null}
 | 
				
			||||||
 | 
					    </div>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Radio.propTypes = {
 | 
				
			||||||
 | 
					    label: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    id: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    helpText: PropTypes.string
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Radio({label, id, helpText, ...props}) {
 | 
				
			||||||
 | 
					    return <>
 | 
				
			||||||
 | 
					        <div className='custom-control custom-radio custom-control-inline'>
 | 
				
			||||||
 | 
					            <input
 | 
				
			||||||
 | 
					                id={id}
 | 
				
			||||||
 | 
					                className='custom-control-input'
 | 
				
			||||||
 | 
					                type='radio'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                {...props}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <label className='custom-control-label' htmlFor={id}>{label}</label>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        {helpText ? <small className="form-text text-muted">{helpText}</small> : null}
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										24
									
								
								src/bootstrap/components/RadioSet.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/bootstrap/components/RadioSet.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					Set of radio Bootstrap component input with label and predefined sizes and structure for using in foris forms.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All additional `props` are passed to the `<input type="number">` HTML component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					import {useState} from 'react';
 | 
				
			||||||
 | 
					const CHOICES=[
 | 
				
			||||||
 | 
					    {value:'one',label:'1'},
 | 
				
			||||||
 | 
					    {value:'two',label:'2'},
 | 
				
			||||||
 | 
					    {value:'three',label:'3'},
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					const [value, setValue] = useState(CHOICES[0].value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<>
 | 
				
			||||||
 | 
					    {/*Yeah, it gets event, not value!*/}
 | 
				
			||||||
 | 
					    <RadioSet
 | 
				
			||||||
 | 
					        value={value}
 | 
				
			||||||
 | 
					        name='some-radio'
 | 
				
			||||||
 | 
					        choices={CHOICES}
 | 
				
			||||||
 | 
					        onChange={event=>setValue(event.target.value)}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <p>Selected value: {value}</p>
 | 
				
			||||||
 | 
					</>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										45
									
								
								src/bootstrap/components/Select.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/bootstrap/components/Select.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import {useUID} from 'react-uid/dist/es5/index';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Select.propTypes = {
 | 
				
			||||||
 | 
					    /** Select field Label. */
 | 
				
			||||||
 | 
					    label: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    /** Choices if form of {value : "Label",...}.*/
 | 
				
			||||||
 | 
					    choices: PropTypes.object.isRequired,
 | 
				
			||||||
 | 
					    /** Current value. */
 | 
				
			||||||
 | 
					    value: PropTypes.oneOfType([
 | 
				
			||||||
 | 
					        PropTypes.string,
 | 
				
			||||||
 | 
					        PropTypes.number,
 | 
				
			||||||
 | 
					    ]).isRequired,
 | 
				
			||||||
 | 
					    /** Help text message. */
 | 
				
			||||||
 | 
					    helpText: PropTypes.string,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Select({label, choices, helpText, ...props}) {
 | 
				
			||||||
 | 
					    const uid = useUID();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const options = Object.keys(choices).map(
 | 
				
			||||||
 | 
					        key => <option key={key} value={key}>{choices[key]}</option>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <div className='form-group col-sm-12 offset-lg-1 col-lg-10'>
 | 
				
			||||||
 | 
					        <label htmlFor={uid}>{label}</label>
 | 
				
			||||||
 | 
					        <select
 | 
				
			||||||
 | 
					            className='custom-select'
 | 
				
			||||||
 | 
					            id={uid}
 | 
				
			||||||
 | 
					            {...props}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					            {options}
 | 
				
			||||||
 | 
					        </select>
 | 
				
			||||||
 | 
					        {helpText ? <small className="form-text text-muted">{helpText}</small> : null}
 | 
				
			||||||
 | 
					    </div>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/bootstrap/components/Select.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/bootstrap/components/Select.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					Select with options Bootstrap component input with label and predefined sizes and structure for using in foris forms.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All additional `props` are passed to the `<select>` HTML component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					import {useState} from 'react';
 | 
				
			||||||
 | 
					const CHOICES={
 | 
				
			||||||
 | 
					    apple:'Apple',
 | 
				
			||||||
 | 
					    banana:'Banana',
 | 
				
			||||||
 | 
					    peach:'Peach',
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					const [value, setValue] = useState(Object.keys(CHOICES)[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<>
 | 
				
			||||||
 | 
					    {/*Yeah, it gets event, not value!*/}
 | 
				
			||||||
 | 
					    <Select
 | 
				
			||||||
 | 
					        label="Fruit"
 | 
				
			||||||
 | 
					        value={value}
 | 
				
			||||||
 | 
					        choices={CHOICES}
 | 
				
			||||||
 | 
					        onChange={event=>setValue(event.target.value)}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <p>Selected choice label: {CHOICES[value]}</p>
 | 
				
			||||||
 | 
					    <p>Selected choice value: {value}</p>
 | 
				
			||||||
 | 
					</>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										57
									
								
								src/bootstrap/components/Spinner.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/bootstrap/components/Spinner.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Spinner.propTypes = {
 | 
				
			||||||
 | 
					    /** Children components put into `div` with "spinner-text" class. */
 | 
				
			||||||
 | 
					    children: PropTypes.oneOfType([
 | 
				
			||||||
 | 
					        PropTypes.arrayOf(PropTypes.node),
 | 
				
			||||||
 | 
					        PropTypes.node
 | 
				
			||||||
 | 
					    ]),
 | 
				
			||||||
 | 
					    /** Render component with full-screen mode (using apropriate `.css` styles) */
 | 
				
			||||||
 | 
					    fullScreen: PropTypes.bool.isRequired,
 | 
				
			||||||
 | 
					    className: PropTypes.string
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Spinner.defaultProps = {
 | 
				
			||||||
 | 
					    fullScreen: false,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Spinner({fullScreen, children, className, ...props}) {
 | 
				
			||||||
 | 
					    if (!fullScreen) {
 | 
				
			||||||
 | 
					        return <div className={'spinner-wrapper ' + (className ? className : '')} {...props}>
 | 
				
			||||||
 | 
					            <SpinnerElement>{children}</SpinnerElement>
 | 
				
			||||||
 | 
					        </div>;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <div className="spinner-fs-wrapper" {...props}>
 | 
				
			||||||
 | 
					        <div className="spinner-fs-background">
 | 
				
			||||||
 | 
					            <SpinnerElement>{children}</SpinnerElement>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SpinnerElement.propTypes = {
 | 
				
			||||||
 | 
					    /** Spinner's size */
 | 
				
			||||||
 | 
					    small: PropTypes.bool,
 | 
				
			||||||
 | 
					    /** Children components */
 | 
				
			||||||
 | 
					    children: PropTypes.oneOfType([
 | 
				
			||||||
 | 
					        PropTypes.arrayOf(PropTypes.node),
 | 
				
			||||||
 | 
					        PropTypes.node
 | 
				
			||||||
 | 
					    ]),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function SpinnerElement({small, children}) {
 | 
				
			||||||
 | 
					    return <>
 | 
				
			||||||
 | 
					        <div className={'spinner-border ' + (small ? 'spinner-border-sm' : '')} role="status">
 | 
				
			||||||
 | 
					            <span className="sr-only"/>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div className="spinner-text">{children}</div>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								src/bootstrap/components/Spinner.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/bootstrap/components/Spinner.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					Spiner Bootstrap component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					<Spinner>You can put text inside or any component you wish.</Spinner>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										26
									
								
								src/bootstrap/components/TextInput.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/bootstrap/components/TextInput.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Input from './Input';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const TextInput = ({...props}) => <Input type="text" {...props}/>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TextInput.propTypes = {
 | 
				
			||||||
 | 
					    /** Field label. */
 | 
				
			||||||
 | 
					    label: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    /** Error text. */
 | 
				
			||||||
 | 
					    error: PropTypes.string,
 | 
				
			||||||
 | 
					    /** Help text message. */
 | 
				
			||||||
 | 
					    helpText: PropTypes.string,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default TextInput;
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/bootstrap/components/TextInput.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/bootstrap/components/TextInput.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					Text Bootstrap component input with label and predefined sizes and structure for using in foris forms.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All additional `props` are passed to the `<input type="text">` HTML component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					import {useState} from 'react';
 | 
				
			||||||
 | 
					const [value, setValue] = useState('Bla bla');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<TextInput
 | 
				
			||||||
 | 
					    value={value}
 | 
				
			||||||
 | 
					    label="Some text" 
 | 
				
			||||||
 | 
					    helpText="Read the small text!"
 | 
				
			||||||
 | 
					    onChange={event => setValue(event.target.value)}
 | 
				
			||||||
 | 
					/>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/bootstrap/components/constants.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/bootstrap/components/constants.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Bootstrap column size for form fields */
 | 
				
			||||||
 | 
					export const formFieldsSize = 'col-sm-12 offset-lg-1 col-lg-10';
 | 
				
			||||||
							
								
								
									
										31
									
								
								src/bootstrap/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/bootstrap/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					import * as Alert from "./components/Alert";
 | 
				
			||||||
 | 
					import * as Button from "./components/Button";
 | 
				
			||||||
 | 
					import * as Checkbox from "./components/Checkbox";
 | 
				
			||||||
 | 
					import * as constants from "./components/constants";
 | 
				
			||||||
 | 
					import * as DataTimeInput from "./components/DataTimeInput";
 | 
				
			||||||
 | 
					import * as EmailInput from "./components/EmailInput";
 | 
				
			||||||
 | 
					import * as Input from "./components/Input";
 | 
				
			||||||
 | 
					import * as Modal from "./components/Modal";
 | 
				
			||||||
 | 
					import * as NumberInput from "./components/NumberInput";
 | 
				
			||||||
 | 
					import * as PasswordInput from "./components/PasswordInput";
 | 
				
			||||||
 | 
					import * as RadioSet from "./components/RadioSet";
 | 
				
			||||||
 | 
					import * as Select from "./components/Select";
 | 
				
			||||||
 | 
					import * as Spinner from "./components/Spinner";
 | 
				
			||||||
 | 
					import * as TextInput from "./components/TextInput";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					    Alert,
 | 
				
			||||||
 | 
					    Button,
 | 
				
			||||||
 | 
					    Checkbox,
 | 
				
			||||||
 | 
					    constants,
 | 
				
			||||||
 | 
					    DataTimeInput,
 | 
				
			||||||
 | 
					    EmailInput,
 | 
				
			||||||
 | 
					    Input,
 | 
				
			||||||
 | 
					    Modal,
 | 
				
			||||||
 | 
					    NumberInput,
 | 
				
			||||||
 | 
					    PasswordInput,
 | 
				
			||||||
 | 
					    RadioSet,
 | 
				
			||||||
 | 
					    Select,
 | 
				
			||||||
 | 
					    Spinner,
 | 
				
			||||||
 | 
					    TextInput,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								src/form/__tests__/SubmitButton.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/form/__tests__/SubmitButton.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import {render} from 'customTestRender';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SubmitButton, {STATES} from '../components/SubmitButton';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('<SubmitButton/>', () => {
 | 
				
			||||||
 | 
					    it('Render ready', () => {
 | 
				
			||||||
 | 
					        const {container} = render(<SubmitButton state={STATES.READY}/>);
 | 
				
			||||||
 | 
					        expect(container).toMatchSnapshot();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('Render saving', () => {
 | 
				
			||||||
 | 
					        const {container} = render(<SubmitButton state={STATES.SAVING}/>);
 | 
				
			||||||
 | 
					        expect(container).toMatchSnapshot();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('Render load', () => {
 | 
				
			||||||
 | 
					        const {container} = render(<SubmitButton state={STATES.LOAD}/>);
 | 
				
			||||||
 | 
					        expect(container).toMatchSnapshot();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										51
									
								
								src/form/__tests__/__snapshots__/SubmitButton.test.js.snap
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/form/__tests__/__snapshots__/SubmitButton.test.js.snap
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					// Jest Snapshot v1, https://goo.gl/fbAQLP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<SubmitButton/> Render load 1`] = `
 | 
				
			||||||
 | 
					<div>
 | 
				
			||||||
 | 
					  <button
 | 
				
			||||||
 | 
					    class="btn btn-primary  offset-lg-8 col-lg-3 col-sm-12"
 | 
				
			||||||
 | 
					    disabled=""
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <span
 | 
				
			||||||
 | 
					      aria-hidden="true"
 | 
				
			||||||
 | 
					      class="spinner-border spinner-border-sm"
 | 
				
			||||||
 | 
					      role="status"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					     
 | 
				
			||||||
 | 
					     
 | 
				
			||||||
 | 
					     
 | 
				
			||||||
 | 
					    Load settings
 | 
				
			||||||
 | 
					  </button>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<SubmitButton/> Render ready 1`] = `
 | 
				
			||||||
 | 
					<div>
 | 
				
			||||||
 | 
					  <button
 | 
				
			||||||
 | 
					    class="btn btn-primary  offset-lg-8 col-lg-3 col-sm-12"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					     
 | 
				
			||||||
 | 
					     
 | 
				
			||||||
 | 
					    Save
 | 
				
			||||||
 | 
					  </button>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`<SubmitButton/> Render saving 1`] = `
 | 
				
			||||||
 | 
					<div>
 | 
				
			||||||
 | 
					  <button
 | 
				
			||||||
 | 
					    class="btn btn-primary  offset-lg-8 col-lg-3 col-sm-12"
 | 
				
			||||||
 | 
					    disabled=""
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <span
 | 
				
			||||||
 | 
					      aria-hidden="true"
 | 
				
			||||||
 | 
					      class="spinner-border spinner-border-sm"
 | 
				
			||||||
 | 
					      role="status"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					     
 | 
				
			||||||
 | 
					     
 | 
				
			||||||
 | 
					     
 | 
				
			||||||
 | 
					    Updating
 | 
				
			||||||
 | 
					  </button>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
							
								
								
									
										115
									
								
								src/form/__tests__/hooks.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/form/__tests__/hooks.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: rewrite this test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {fireEvent, render, waitForElement} from 'customTestRender';
 | 
				
			||||||
 | 
					import mockAxios from 'jest-mock-axios';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ForisForm from '../components/ForisForm';
 | 
				
			||||||
 | 
					import API_URLs from 'common/API';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// It's possible to unittest each hooks via react-hooks-testing-library.
 | 
				
			||||||
 | 
					// But it's better and easier to test it by test components which uses this hooks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const TestForm = ({formData, formErrors, setFormValue}) => {
 | 
				
			||||||
 | 
					    return <>
 | 
				
			||||||
 | 
					        <input
 | 
				
			||||||
 | 
					            data-testid='test-input'
 | 
				
			||||||
 | 
					            value={formData.field}
 | 
				
			||||||
 | 
					            onChange={setFormValue(value => ({field: {$set: value}}))}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <p>{formErrors.field}</p>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('useForm hook.', () => {
 | 
				
			||||||
 | 
					    let mockValidator;
 | 
				
			||||||
 | 
					    let mockPrepData;
 | 
				
			||||||
 | 
					    let mockPrepDataToSubmit;
 | 
				
			||||||
 | 
					    let mockWebSockets;
 | 
				
			||||||
 | 
					    let input;
 | 
				
			||||||
 | 
					    let form;
 | 
				
			||||||
 | 
					    const Child = jest.fn(props => <TestForm {...props}/>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    beforeEach(async () => {
 | 
				
			||||||
 | 
					        mockPrepData = jest.fn(() => ({field: 'preparedData'}));
 | 
				
			||||||
 | 
					        mockPrepDataToSubmit = jest.fn(() => ({field: 'preparedDataToSubmit'}));
 | 
				
			||||||
 | 
					        mockValidator = jest.fn(data => data.field === 'invalidValue' ? {field: 'Error'} : {});
 | 
				
			||||||
 | 
					        const {getByTestId, container} = render(
 | 
				
			||||||
 | 
					            <ForisForm
 | 
				
			||||||
 | 
					                ws={mockWebSockets}
 | 
				
			||||||
 | 
					                // Just some module which exists...
 | 
				
			||||||
 | 
					                forisConfig={{
 | 
				
			||||||
 | 
					                    endpoint: API_URLs.wan,
 | 
				
			||||||
 | 
					                    wsModule: 'wan'
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					                prepData={mockPrepData}
 | 
				
			||||||
 | 
					                prepDataToSubmit={mockPrepDataToSubmit}
 | 
				
			||||||
 | 
					                validator={mockValidator}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <Child/>
 | 
				
			||||||
 | 
					            </ForisForm>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        mockAxios.mockResponse({field: 'fetchedData'});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        input = await waitForElement(() =>
 | 
				
			||||||
 | 
					            getByTestId('test-input')
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        form = container.firstChild
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('Validation on changing.', () => {
 | 
				
			||||||
 | 
					        expect(mockValidator).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					        expect(Child).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					        expect(Child.mock.calls[0][0].formErrors).toMatchObject({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fireEvent.change(input, {target: {value: 'invalidValue', type: 'text'}});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(Child).toHaveBeenCalledTimes(2);
 | 
				
			||||||
 | 
					        expect(mockValidator).toHaveBeenCalledTimes(2);
 | 
				
			||||||
 | 
					        expect(Child.mock.calls[1][0].formErrors).toMatchObject({field: 'Error'});
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('Update text value.', () => {
 | 
				
			||||||
 | 
					        fireEvent.change(input, {target: {value: 'newValue', type: 'text'}})
 | 
				
			||||||
 | 
					        expect(input.value).toBe('newValue');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('Update text value.', () => {
 | 
				
			||||||
 | 
					        fireEvent.change(input, {target: {value: 123, type: 'number'}})
 | 
				
			||||||
 | 
					        expect(input.value).toBe('123');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('Update checkbox value.', () => {
 | 
				
			||||||
 | 
					        fireEvent.change(input, {target: {checked: true, type: 'checkbox'}})
 | 
				
			||||||
 | 
					        expect(input.checked).toBe(true);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('Fetch data.', () => {
 | 
				
			||||||
 | 
					        expect(mockAxios.get).toHaveBeenCalledWith('/api/wan', expect.anything());
 | 
				
			||||||
 | 
					        expect(mockPrepData).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					        expect(Child.mock.calls[0][0].formData).toMatchObject({field: 'preparedData'});
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('Submit.', () => {
 | 
				
			||||||
 | 
					        expect(mockAxios.get).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					        expect(mockPrepDataToSubmit).toHaveBeenCalledTimes(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fireEvent.submit(form);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(mockPrepDataToSubmit).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					        expect(mockAxios.post).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					        expect(mockAxios.post).toHaveBeenCalledWith(
 | 
				
			||||||
 | 
					            '/api/wan',
 | 
				
			||||||
 | 
					            {'field': 'preparedDataToSubmit'},
 | 
				
			||||||
 | 
					            expect.anything(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										84
									
								
								src/form/__tests__/validation.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/form/__tests__/validation.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    validateDomain,
 | 
				
			||||||
 | 
					    validateDUID,
 | 
				
			||||||
 | 
					    validateIPv4Address,
 | 
				
			||||||
 | 
					    validateIPv6Address,
 | 
				
			||||||
 | 
					    validateIPv6Prefix, validateMAC
 | 
				
			||||||
 | 
					} from 'validations';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('Validation functions', () => {
 | 
				
			||||||
 | 
					    it('validateIPv4Address valid', () => {
 | 
				
			||||||
 | 
					        expect(validateIPv4Address('192.168.1.1')).toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv4Address('1.1.1.1')).toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv4Address('0.0.0.0')).toBe(undefined);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('validateIPv4Address invalid', () => {
 | 
				
			||||||
 | 
					        expect(validateIPv4Address('invalid')).not.toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv4Address('192.256.1.1')).not.toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv4Address('192.168.256.1')).not.toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv4Address('192.168.1.256')).not.toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv4Address('192.168.1.256')).not.toBe(undefined);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('validateIPv6Address valid', () => {
 | 
				
			||||||
 | 
					        expect(validateIPv6Address('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv6Address('0:0:0:0:0:0:0:1')).toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv6Address('::1')).toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv6Address('::')).toBe(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('validateIPv6Address invalid', () => {
 | 
				
			||||||
 | 
					        expect(validateIPv6Address('invalid')).not.toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv6Address('1.1.1.1')).not.toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv6Address('1200::AB00:1234::2552:7777:1313')).not.toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv6Address('1200:0000:AB00:1234:O000:2552:7777:1313')).not.toBe(undefined);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('validateIPv6Prefix valid', () => {
 | 
				
			||||||
 | 
					        expect(validateIPv6Prefix('2002:0000::/16')).toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv6Prefix('0::/0')).toBe(undefined);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('validateIPv6Prefix invalid', () => {
 | 
				
			||||||
 | 
					        expect(validateIPv6Prefix('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).not.toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv6Prefix('::1')).not.toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateIPv6Prefix('2002:0000::/999')).not.toBe(undefined);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('validateDomain valid', () => {
 | 
				
			||||||
 | 
					        expect(validateDomain('example.com')).toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateDomain('one.two.three')).toBe(undefined);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('validateDomain invalid', () => {
 | 
				
			||||||
 | 
					        expect(validateDomain('test/')).not.toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateDomain('.')).not.toBe(undefined);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('validateDUID valid', () => {
 | 
				
			||||||
 | 
					        expect(validateDUID('abcdefAB')).toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateDUID('ABCDEF12')).toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateDUID('ABCDEF12AB')).toBe(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('validateDUID invalid', () => {
 | 
				
			||||||
 | 
					        expect(validateDUID('gggggggg')).not.toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateDUID('abcdefABa')).not.toBe(undefined);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('validateMAC valid', () => {
 | 
				
			||||||
 | 
					        expect(validateMAC('00:D0:56:F2:B5:12')).toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateMAC('00:26:DD:14:C4:EE')).toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateMAC('06:00:00:00:00:00')).toBe(undefined);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('validateMAC invalid', () => {
 | 
				
			||||||
 | 
					        expect(validateMAC('00:00:00:00:00:0G')).not.toBe(undefined);
 | 
				
			||||||
 | 
					        expect(validateMAC('06:00:00:00:00:00:00')).not.toBe(undefined);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										148
									
								
								src/form/components/ForisForm.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								src/form/components/ForisForm.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,148 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React, {useEffect, useState} from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import {Prompt} from 'react-router';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Spinner from 'bootstrap/components/Spinner';
 | 
				
			||||||
 | 
					import {useAPIPost} from 'api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {useForisModule, useForm} from '../hooks';
 | 
				
			||||||
 | 
					import SubmitButton, {STATES as SUBMIT_BUTTON_STATES} from './SubmitButton';
 | 
				
			||||||
 | 
					import {FailAlert, SuccessAlert} from './alerts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ForisForm.propTypes = {
 | 
				
			||||||
 | 
					    /** WebSocket object see `scr/common/WebSockets.js`. */
 | 
				
			||||||
 | 
					    ws: PropTypes.object,
 | 
				
			||||||
 | 
					    /** Foris configuration object. See usage in main components. */
 | 
				
			||||||
 | 
					    forisConfig: PropTypes.shape({
 | 
				
			||||||
 | 
					        /** reForis Flask aplication API endpoint from `src/common/API.js`. */
 | 
				
			||||||
 | 
					        endpoint: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					        /** `foris-controller` module name to be used via WebSockets.
 | 
				
			||||||
 | 
					         *  If it's not passed then WebSockets aren't used
 | 
				
			||||||
 | 
					         * */
 | 
				
			||||||
 | 
					        wsModule: PropTypes.string,
 | 
				
			||||||
 | 
					        /**`foris-controller` action name to be used via WebSockets.
 | 
				
			||||||
 | 
					         *  If it's not passed then `update_settings` is used. see `src/common/WebSocketHooks.js`
 | 
				
			||||||
 | 
					         * */
 | 
				
			||||||
 | 
					        wsAction: PropTypes.string,
 | 
				
			||||||
 | 
					    }).isRequired,
 | 
				
			||||||
 | 
					    /** Function to prepare data recived from the API before using in forms. */
 | 
				
			||||||
 | 
					    prepData: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    /** Function to prepare data from form before submitting. */
 | 
				
			||||||
 | 
					    prepDataToSubmit: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    /** Function to handle response to POST request. */
 | 
				
			||||||
 | 
					    postCallback: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    /** Validate data and provide validation object. Then validation errors passed to children. */
 | 
				
			||||||
 | 
					    validator: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    /** Disables form */
 | 
				
			||||||
 | 
					    disabled: PropTypes.bool,
 | 
				
			||||||
 | 
					    /** reForis form components. */
 | 
				
			||||||
 | 
					    children: PropTypes.node.isRequired,
 | 
				
			||||||
 | 
					    /** Optional override of form submit callback */
 | 
				
			||||||
 | 
					    onSubmitOverridden: PropTypes.func
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ForisForm.defaultProps = {
 | 
				
			||||||
 | 
					    prepData: data => data,
 | 
				
			||||||
 | 
					    prepDataToSubmit: data => data,
 | 
				
			||||||
 | 
					    postCallback: () => undefined,
 | 
				
			||||||
 | 
					    validator: () => undefined,
 | 
				
			||||||
 | 
					    disabled: false
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Serves as HOC for all foris forms components. */
 | 
				
			||||||
 | 
					export default function ForisForm({
 | 
				
			||||||
 | 
					                                      ws,
 | 
				
			||||||
 | 
					                                      forisConfig,
 | 
				
			||||||
 | 
					                                      prepData,
 | 
				
			||||||
 | 
					                                      prepDataToSubmit,
 | 
				
			||||||
 | 
					                                      postCallback,
 | 
				
			||||||
 | 
					                                      validator,
 | 
				
			||||||
 | 
					                                      disabled,
 | 
				
			||||||
 | 
					                                      onSubmitOverridden,
 | 
				
			||||||
 | 
					                                      children
 | 
				
			||||||
 | 
					                                  }) {
 | 
				
			||||||
 | 
					    const [formState, onFormChangeHandler, resetFormData] = useForm(validator, prepData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const [forisModuleState] = useForisModule(ws, forisConfig);
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        if (forisModuleState.data) {
 | 
				
			||||||
 | 
					            resetFormData(forisModuleState.data)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }, [forisModuleState.data, resetFormData, prepData]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const [postState, post] = useAPIPost(forisConfig.endpoint);
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        if (postState.isSuccess)
 | 
				
			||||||
 | 
					            postCallback();
 | 
				
			||||||
 | 
					    }, [postCallback, postState.isSuccess]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function onSubmitHandler(e) {
 | 
				
			||||||
 | 
					        e.preventDefault();
 | 
				
			||||||
 | 
					        resetFormData();
 | 
				
			||||||
 | 
					        const copiedFormData = JSON.parse(JSON.stringify(formState.data));
 | 
				
			||||||
 | 
					        const preparedData = prepDataToSubmit(copiedFormData);
 | 
				
			||||||
 | 
					        post(preparedData);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function getSubmitButtonState() {
 | 
				
			||||||
 | 
					        if (postState.isSending)
 | 
				
			||||||
 | 
					            return SUBMIT_BUTTON_STATES.SAVING;
 | 
				
			||||||
 | 
					        else if (forisModuleState.isLoading)
 | 
				
			||||||
 | 
					            return SUBMIT_BUTTON_STATES.LOAD;
 | 
				
			||||||
 | 
					        return SUBMIT_BUTTON_STATES.READY;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const [alertIsDismissed, setAlertIsDismissed] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!formState.data)
 | 
				
			||||||
 | 
					        return <Spinner className='row justify-content-center'/>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const formIsDisabled = disabled || forisModuleState.isLoading || postState.isSending;
 | 
				
			||||||
 | 
					    const submitButtonIsDisabled = disabled || !!formState.errors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const childrenWithFormProps =
 | 
				
			||||||
 | 
					        React.Children.map(children, child =>
 | 
				
			||||||
 | 
					            React.cloneElement(child, {
 | 
				
			||||||
 | 
					                formData: formState.data,
 | 
				
			||||||
 | 
					                formErrors: formState.errors,
 | 
				
			||||||
 | 
					                setFormValue: onFormChangeHandler,
 | 
				
			||||||
 | 
					                disabled: formIsDisabled,
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onSubmit = onSubmitOverridden ?
 | 
				
			||||||
 | 
					        onSubmitOverridden(formState.data, onFormChangeHandler, onSubmitHandler) : onSubmitHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function getMessageOnLeavingPage() {
 | 
				
			||||||
 | 
					        if (JSON.stringify(formState.data) === JSON.stringify(formState.initialData))
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        return _('Changes you made may not be saved. Are you sure you want to leave?')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <>
 | 
				
			||||||
 | 
					        <Prompt message={getMessageOnLeavingPage}/>
 | 
				
			||||||
 | 
					        {!alertIsDismissed ?
 | 
				
			||||||
 | 
					            postState.isSuccess ?
 | 
				
			||||||
 | 
					                <SuccessAlert onDismiss={() => setAlertIsDismissed(true)}/>
 | 
				
			||||||
 | 
					                : postState.isError ?
 | 
				
			||||||
 | 
					                <FailAlert onDismiss={() => setAlertIsDismissed(true)}/>
 | 
				
			||||||
 | 
					                : null
 | 
				
			||||||
 | 
					            : null
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        <form onSubmit={onSubmit}>
 | 
				
			||||||
 | 
					            {childrenWithFormProps}
 | 
				
			||||||
 | 
					            <SubmitButton
 | 
				
			||||||
 | 
					                state={getSubmitButtonState()}
 | 
				
			||||||
 | 
					                disabled={submitButtonIsDisabled}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										49
									
								
								src/form/components/SubmitButton.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/form/components/SubmitButton.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Button from 'bootstrap/components/Button';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const STATES = {
 | 
				
			||||||
 | 
					    READY: 1,
 | 
				
			||||||
 | 
					    SAVING: 2,
 | 
				
			||||||
 | 
					    LOAD: 3,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SubmitButton.propTypes = {
 | 
				
			||||||
 | 
					    disabled: PropTypes.bool,
 | 
				
			||||||
 | 
					    state: PropTypes.oneOf(Object.keys(STATES).map(key => STATES[key]))
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function SubmitButton({disabled, state, ...props}) {
 | 
				
			||||||
 | 
					    const disableSubmitButton = disabled || state !== STATES.READY;
 | 
				
			||||||
 | 
					    const loadingSubmitButton = state !== STATES.READY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let labelSubmitButton;
 | 
				
			||||||
 | 
					    switch (state) {
 | 
				
			||||||
 | 
					        case STATES.SAVING:
 | 
				
			||||||
 | 
					            labelSubmitButton = _('Updating');
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case STATES.LOAD:
 | 
				
			||||||
 | 
					            labelSubmitButton = _('Load settings');
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            labelSubmitButton = _('Save');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <Button
 | 
				
			||||||
 | 
					        loading={loadingSubmitButton}
 | 
				
			||||||
 | 
					        disabled={disableSubmitButton}
 | 
				
			||||||
 | 
					        forisFormSize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {...props}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					        {labelSubmitButton}
 | 
				
			||||||
 | 
					    </Button>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								src/form/components/alerts.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/form/components/alerts.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Alert from 'bootstrap/components/Alert';
 | 
				
			||||||
 | 
					import Portal from 'utils/Portal';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SuccessAlert.propTypes = {
 | 
				
			||||||
 | 
					    onDismiss: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ALERT_CONTAINER_ID = 'alert_container';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function SuccessAlert({onDismiss}) {
 | 
				
			||||||
 | 
					    return <Portal containerId={ALERT_CONTAINER_ID}>
 | 
				
			||||||
 | 
					        <Alert
 | 
				
			||||||
 | 
					            type='success'
 | 
				
			||||||
 | 
					            message={_('Settings were successfully saved.')}
 | 
				
			||||||
 | 
					            onDismiss={onDismiss}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    </Portal>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FailAlert.propTypes = {
 | 
				
			||||||
 | 
					    onDismiss: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function FailAlert({onDismiss}) {
 | 
				
			||||||
 | 
					    return <Portal containerId={ALERT_CONTAINER_ID}>
 | 
				
			||||||
 | 
					        <Alert
 | 
				
			||||||
 | 
					            type='danger'
 | 
				
			||||||
 | 
					            message={_('Settings update was failed.')}
 | 
				
			||||||
 | 
					            onDismiss={onDismiss}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    </Portal>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										100
									
								
								src/form/hooks.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/form/hooks.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {useCallback, useEffect, useReducer} from 'react';
 | 
				
			||||||
 | 
					import update from 'immutability-helper';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {useAPIGet} from 'api/hooks';
 | 
				
			||||||
 | 
					import useWSForisModule from 'webSockets/hooks';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useForm(validator, prepData) {
 | 
				
			||||||
 | 
					    const [state, dispatch] = useReducer(formReducer, {
 | 
				
			||||||
 | 
					        data: null,
 | 
				
			||||||
 | 
					        initialData: null,
 | 
				
			||||||
 | 
					        errors: {},
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onFormReload = useCallback(data => {
 | 
				
			||||||
 | 
					        dispatch({
 | 
				
			||||||
 | 
					            type: FORM_ACTIONS.resetData,
 | 
				
			||||||
 | 
					            data: data,
 | 
				
			||||||
 | 
					            prepData: prepData,
 | 
				
			||||||
 | 
					            validator: validator,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }, [prepData, validator]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onFormChangeHandler = useCallback(updateRule =>
 | 
				
			||||||
 | 
					        event => {
 | 
				
			||||||
 | 
					            dispatch({
 | 
				
			||||||
 | 
					                type: FORM_ACTIONS.updateValue,
 | 
				
			||||||
 | 
					                value: getChangedValue(event.target),
 | 
				
			||||||
 | 
					                updateRule: updateRule,
 | 
				
			||||||
 | 
					                validator: validator,
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        }, [validator]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return [
 | 
				
			||||||
 | 
					        state,
 | 
				
			||||||
 | 
					        onFormChangeHandler,
 | 
				
			||||||
 | 
					        onFormReload,
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const FORM_ACTIONS = {
 | 
				
			||||||
 | 
					    updateValue: 1,
 | 
				
			||||||
 | 
					    resetData: 2,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function formReducer(state, action) {
 | 
				
			||||||
 | 
					    switch (action.type) {
 | 
				
			||||||
 | 
					        case FORM_ACTIONS.updateValue: {
 | 
				
			||||||
 | 
					            const newData = update(state.data, action.updateRule(action.value));
 | 
				
			||||||
 | 
					            const errors = action.validator(newData);
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                ...state,
 | 
				
			||||||
 | 
					                data: newData,
 | 
				
			||||||
 | 
					                errors: errors
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        case FORM_ACTIONS.resetData: {
 | 
				
			||||||
 | 
					            if (!action.data)
 | 
				
			||||||
 | 
					                return {...state, initialData: state.data};
 | 
				
			||||||
 | 
					            const prepData = action.prepData ? action.prepData(action.data) : action.data;
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                data: prepData,
 | 
				
			||||||
 | 
					                initialData: prepData,
 | 
				
			||||||
 | 
					                errors: action.data ? action.validator(prepData) : undefined,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        default: {
 | 
				
			||||||
 | 
					            throw new Error();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getChangedValue(target) {
 | 
				
			||||||
 | 
					    let value = target.value;
 | 
				
			||||||
 | 
					    if (target.type === 'checkbox') {
 | 
				
			||||||
 | 
					        value = target.checked;
 | 
				
			||||||
 | 
					    } else if (target.type === 'number') {
 | 
				
			||||||
 | 
					        const parsedValue = parseInt(value);
 | 
				
			||||||
 | 
					        value = isNaN(parsedValue) ? value : parsedValue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return value
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useForisModule(ws, config) {
 | 
				
			||||||
 | 
					    const [APIGetState, get] = useAPIGet(config.endpoint);
 | 
				
			||||||
 | 
					    const [WSData] = useWSForisModule(ws, config.wsModule, config.wsAction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        get();
 | 
				
			||||||
 | 
					    }, [WSData, get]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return [APIGetState];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								src/form/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/form/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					import * as ForisForm from "./components/ForisForm";
 | 
				
			||||||
 | 
					import * as SubmitButton from "./components/SubmitButton";
 | 
				
			||||||
 | 
					import * as hooks from "./hooks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {ForisForm, SubmitButton, hooks}
 | 
				
			||||||
							
								
								
									
										16
									
								
								src/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					import * as api from './api';
 | 
				
			||||||
 | 
					import * as bootstrap from './bootstrap';
 | 
				
			||||||
 | 
					import * as form from './form';
 | 
				
			||||||
 | 
					import * as testUtils from './testUtils';
 | 
				
			||||||
 | 
					import * as validations from './validations';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {Portal} from './utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					    api,
 | 
				
			||||||
 | 
					    bootstrap,
 | 
				
			||||||
 | 
					    form,
 | 
				
			||||||
 | 
					    testUtils,
 | 
				
			||||||
 | 
					    validations,
 | 
				
			||||||
 | 
					    Portal,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								src/testUtils/customTestRender.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/testUtils/customTestRender.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* eslint import/export: "off" */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import {UIDReset} from 'react-uid';
 | 
				
			||||||
 | 
					import {StaticRouter} from 'react-router';
 | 
				
			||||||
 | 
					import {render} from '@testing-library/react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Wrapper.propTypes = {
 | 
				
			||||||
 | 
					    children: PropTypes.oneOfType([
 | 
				
			||||||
 | 
					        PropTypes.arrayOf(PropTypes.node),
 | 
				
			||||||
 | 
					        PropTypes.node
 | 
				
			||||||
 | 
					    ])
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Wrapper({children}) {
 | 
				
			||||||
 | 
					    return <StaticRouter>
 | 
				
			||||||
 | 
					        <UIDReset>
 | 
				
			||||||
 | 
					            {children}
 | 
				
			||||||
 | 
					        </UIDReset>
 | 
				
			||||||
 | 
					    </StaticRouter>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const customTestRender = (ui, options) => render(ui, {wrapper: Wrapper, ...options});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// re-export everything
 | 
				
			||||||
 | 
					export * from '@testing-library/react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// override render method
 | 
				
			||||||
 | 
					export {customTestRender as render}
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/testUtils/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/testUtils/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					import * as customTestRender from "./customTestRender";
 | 
				
			||||||
 | 
					import * as mockWS from "./mockWS";
 | 
				
			||||||
 | 
					import * as setupGlobals from "./setupGlobals";
 | 
				
			||||||
 | 
					import * as setupTest from "./setupTest";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					    customTestRender,
 | 
				
			||||||
 | 
					    mockWS,
 | 
				
			||||||
 | 
					    setupGlobals,
 | 
				
			||||||
 | 
					    setupTest,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/testUtils/mockWS.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/testUtils/mockWS.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock foris WS
 | 
				
			||||||
 | 
					export class mockedWS {
 | 
				
			||||||
 | 
					    bind() {
 | 
				
			||||||
 | 
					        return this
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    subscribe() {
 | 
				
			||||||
 | 
					        return this
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/testUtils/setupGlobals.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/testUtils/setupGlobals.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fake babel (gettext) used for docs
 | 
				
			||||||
 | 
					global._ = str => str;
 | 
				
			||||||
 | 
					global.babel = {format: (str) => str};
 | 
				
			||||||
 | 
					global.ForisTranslations = {};
 | 
				
			||||||
							
								
								
									
										37
									
								
								src/testUtils/setupTest.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/testUtils/setupTest.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function setUpTest() {
 | 
				
			||||||
 | 
					    const mockAxios = require('jest-mock-axios');
 | 
				
			||||||
 | 
					    const moment = require('moment-timezone');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Setup axios cleanup
 | 
				
			||||||
 | 
					    global.afterEach(() => {
 | 
				
			||||||
 | 
					        mockAxios.reset();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Mock babel (gettext)
 | 
				
			||||||
 | 
					    window._ = str => str;
 | 
				
			||||||
 | 
					    window.babel = {format: (str) => str};
 | 
				
			||||||
 | 
					    window.ForisTranslations = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Mock scrollIntoView
 | 
				
			||||||
 | 
					    window.HTMLElement.prototype.scrollIntoView = () => {
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    jest.doMock('moment', () => {
 | 
				
			||||||
 | 
					        moment.tz.setDefault('UTC');
 | 
				
			||||||
 | 
					        return moment;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Date.now = jest.fn(() => new Date(Date.UTC(2019, 1, 1, 12, 13, 14)).valueOf());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/utils/Portal.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/utils/Portal.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ReactDOM from 'react-dom';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Portal({containerId, children}) {
 | 
				
			||||||
 | 
					    const container = document.getElementById(containerId);
 | 
				
			||||||
 | 
					    if (container)
 | 
				
			||||||
 | 
					        return ReactDOM.createPortal(children, container);
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								src/utils/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/utils/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					import Portal from './Portal';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {Portal}
 | 
				
			||||||
							
								
								
									
										53
									
								
								src/validations.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/validations.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Set of validation helpers and validation error messages.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ERROR_MESSAGES = {
 | 
				
			||||||
 | 
					    IPv4: _("This is not a valid IPv4 address."),
 | 
				
			||||||
 | 
					    IPv6: _("This is not a valid IPv6 address."),
 | 
				
			||||||
 | 
					    IPv6Prefix: _("This is not a valid IPv6 prefix."),
 | 
				
			||||||
 | 
					    domain: _("This is not a valid domain name."),
 | 
				
			||||||
 | 
					    DUID: _("This is not a valid DUID."),
 | 
				
			||||||
 | 
					    MAC: _("This is not a valid MAC address."),
 | 
				
			||||||
 | 
					    MultipleEmails: _("Doesn't contain a list of emails separated by commas."),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const REs = {
 | 
				
			||||||
 | 
					    IPv4: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
 | 
				
			||||||
 | 
					    IPv6: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,
 | 
				
			||||||
 | 
					    IPv6Prefix: /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))$/,
 | 
				
			||||||
 | 
					    domain: /^([a-zA-Z0-9-]{1,63}\.?)*$/,
 | 
				
			||||||
 | 
					    DUID: /^([0-9a-fA-F]{2}){4}([0-9a-fA-F]{2})*$/,
 | 
				
			||||||
 | 
					    MAC: /^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/,
 | 
				
			||||||
 | 
					    MultipleEmails: /^([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)( *, *[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)*$/,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const createValidator = (fieldType) => (value) => {
 | 
				
			||||||
 | 
					    if (value && value !== "") return REs[fieldType].test(value) ? undefined : ERROR_MESSAGES[fieldType];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const validateIPv4Address = createValidator("IPv4");
 | 
				
			||||||
 | 
					const validateIPv6Address = createValidator("IPv6");
 | 
				
			||||||
 | 
					const validateIPv6Prefix = createValidator("IPv6Prefix");
 | 
				
			||||||
 | 
					const validateDomain = createValidator("domain");
 | 
				
			||||||
 | 
					const validateDUID = createValidator("DUID");
 | 
				
			||||||
 | 
					const validateMAC = createValidator("MAC");
 | 
				
			||||||
 | 
					const validateMultipleEmails = createValidator("MultipleEmails");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					    validateIPv4Address,
 | 
				
			||||||
 | 
					    validateIPv6Address,
 | 
				
			||||||
 | 
					    validateIPv6Prefix,
 | 
				
			||||||
 | 
					    validateDomain,
 | 
				
			||||||
 | 
					    validateDUID,
 | 
				
			||||||
 | 
					    validateMAC,
 | 
				
			||||||
 | 
					    validateMultipleEmails,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										99
									
								
								src/webSockets/WebSockets.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/webSockets/WebSockets.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* eslint no-console: "off" */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { ForisURLs } from "./constants";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const PROTOCOL = window.location.protocol === "http:" ? "ws" : "wss";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const URL = process.env.LIGHTTPD
 | 
				
			||||||
 | 
					    ? `${PROTOCOL}://${window.location.hostname}/foris-ws`
 | 
				
			||||||
 | 
					    : `${PROTOCOL}://${window.location.hostname}:${9081}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const WAITING_FOR_CONNECTION_TIMEOUT = 500;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class WebSockets {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        this.ws = new WebSocket(URL);
 | 
				
			||||||
 | 
					        this.ws.onerror = (e) => {
 | 
				
			||||||
 | 
					            if (window.location.pathname !== ForisURLs.login) {
 | 
				
			||||||
 | 
					                console.error("WS: Error observed, you aren't logged probably.");
 | 
				
			||||||
 | 
					                window.location.replace(ForisURLs.login);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            console.log(`WS: Error: ${e}`);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        this.ws.onmessage = (e) => {
 | 
				
			||||||
 | 
					            console.log(`WS: Received Message: ${e.data}`);
 | 
				
			||||||
 | 
					            const data = JSON.parse(e.data);
 | 
				
			||||||
 | 
					            this.dispatch(data);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        this.ws.onopen = () => {
 | 
				
			||||||
 | 
					            console.log("WS: Connection open.");
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        this.ws.onclose = () => {
 | 
				
			||||||
 | 
					            console.log("WS: Connection closed.");
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // callbacks[module][action]
 | 
				
			||||||
 | 
					        this.callbacks = {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    waitForConnection(callback) {
 | 
				
			||||||
 | 
					        if (this.ws.readyState === 1) {
 | 
				
			||||||
 | 
					            callback();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            const that = this;
 | 
				
			||||||
 | 
					            setTimeout(() => {
 | 
				
			||||||
 | 
					                that.waitForConnection(callback);
 | 
				
			||||||
 | 
					            }, WAITING_FOR_CONNECTION_TIMEOUT);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bind(module, action, callback) {
 | 
				
			||||||
 | 
					        this.callbacks[module] = this.callbacks[module] || {};
 | 
				
			||||||
 | 
					        this.callbacks[module][action] = this.callbacks[module][action] || [];
 | 
				
			||||||
 | 
					        this.callbacks[module][action].push(callback);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    subscribe(params) {
 | 
				
			||||||
 | 
					        this.waitForConnection(() => {
 | 
				
			||||||
 | 
					            this.send("subscribe", params);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    send(action, params) {
 | 
				
			||||||
 | 
					        const payload = JSON.stringify({ action, params });
 | 
				
			||||||
 | 
					        this.waitForConnection(() => {
 | 
				
			||||||
 | 
					            this.ws.send(payload);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dispatch(json) {
 | 
				
			||||||
 | 
					        if (!json.module) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let chain;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            chain = this.callbacks[json.module][json.action];
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					            if (e instanceof TypeError) {
 | 
				
			||||||
 | 
					                console.log(`Callback for this message wasn't found:${e.data}`);
 | 
				
			||||||
 | 
					            } else throw e;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (typeof chain === "undefined") return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (let i = 0; i < chain.length; i++) chain[i](json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    close() {
 | 
				
			||||||
 | 
					        this.ws.close();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										22
									
								
								src/webSockets/hooks.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/webSockets/hooks.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is free software, licensed under the GNU General Public License v3.
 | 
				
			||||||
 | 
					 * See /LICENSE for more information.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function useWSForisModule(ws, module, action = "update_settings") {
 | 
				
			||||||
 | 
					    const [data, setData] = useState(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        if (ws && module) {
 | 
				
			||||||
 | 
					            ws.subscribe(module).bind(module, action, (msg) => {
 | 
				
			||||||
 | 
					                setData(msg.data);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }, [action, module, ws]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return [data];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,34 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This is free software, licensed under the GNU General Public License v3.
 | 
					 | 
				
			||||||
 * See /LICENSE for more information.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const path = require('path');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = () => ({
 | 
					 | 
				
			||||||
    mode: 'development',
 | 
					 | 
				
			||||||
    entry: './src/index.js',
 | 
					 | 
				
			||||||
    resolve: {
 | 
					 | 
				
			||||||
        modules: [
 | 
					 | 
				
			||||||
            path.resolve(__dirname, './src'),
 | 
					 | 
				
			||||||
            path.resolve(__dirname, './node_modules')
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        extensions: [".js", ".json"],
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    module: {
 | 
					 | 
				
			||||||
        rules: [{
 | 
					 | 
				
			||||||
            test: /\.js$/,
 | 
					 | 
				
			||||||
            exclude: /node_modules/,
 | 
					 | 
				
			||||||
            loader: 'babel-loader'
 | 
					 | 
				
			||||||
        }]
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    output: {
 | 
					 | 
				
			||||||
        filename: 'foris.js',
 | 
					 | 
				
			||||||
        path: path.join(__dirname, 'dist/'),
 | 
					 | 
				
			||||||
        libraryTarget: 'umd',
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user