1
0
mirror of https://gitlab.nic.cz/turris/reforis/foris-js.git synced 2025-06-15 13:36:35 +02:00

Compare commits

...

89 Commits

Author SHA1 Message Date
21bbfb6d2e Merge branch 'dev' into 'master'
Dev

See merge request turris/reforis/foris-js!43
2019-11-20 10:22:05 +00:00
1dcba1dfa8 Merge branch 'api-polling-catch-error' into 'dev'
Catch error state in API polling.

See merge request turris/reforis/foris-js!44
2019-11-19 16:56:38 +00:00
fd8acd1ceb Catch error and sending state in API polling. 2019-11-19 16:56:38 +00:00
48d5cf0119 Merge branch 'clickable-checkbox-helptext' into 'dev'
Make checkBox help text clickabe.

See merge request turris/reforis/foris-js!45
2019-11-19 12:43:15 +00:00
1a82f8e225 Update checkbox snapshots. 2019-11-19 13:37:43 +01:00
638821d025 Make checkBox help text clickabe. 2019-11-19 13:36:49 +01:00
fbaa73e378 Merge branch 'global-alert-context' into 'dev'
Use global AlertContext

See merge request turris/reforis/foris-js!42
2019-11-18 12:51:43 +00:00
e54db2c577 Use global AlertContext 2019-11-18 12:51:43 +00:00
a6866a0673 Merge branch 'api-polling' into 'dev'
Add hook for API polling.

See merge request turris/reforis/foris-js!41
2019-11-15 15:59:44 +00:00
429814ebb5 Add until param to API polling hook. 2019-11-15 14:21:19 +01:00
73e213c467 Add default delay. 2019-11-15 14:20:14 +01:00
2e68e56e44 Add hook for API polling. 2019-11-15 14:20:07 +01:00
b3bb4de646 Merge branch 'with-either-props' into 'dev'
Fix for withEither

See merge request turris/reforis/foris-js!40
2019-11-15 08:55:24 +00:00
b30f9f59b4 Fix for withEither 2019-11-15 08:55:24 +00:00
158bd1bb24 Merge branch 'dev' into 'master'
Release 1.3.1

See merge request turris/reforis/foris-js!38
2019-11-14 12:58:31 +00:00
d935f78d6e Merge branch '13-packages-required-for-translations' into 'dev'
Added virtual environment and packages required for translations.

Closes #13

See merge request turris/reforis/foris-js!39
2019-11-14 12:51:58 +00:00
73f4ab48c3 Added virtual environment and packages required for translations. 2019-11-14 11:28:28 +01:00
7075592f24 Merge branch 'timeout' into 'dev'
Timeout handling

See merge request turris/reforis/foris-js!36
2019-11-13 14:45:24 +00:00
23029470b9 Improve error handling + small refactoring. 2019-11-13 15:28:26 +01:00
7e6e6f8c87 Use generic error message in the ForisForm. 2019-11-13 14:49:52 +01:00
b831d664a9 Add timout handling. 2019-11-13 14:49:52 +01:00
0f5b35a3ba Merge branch 'shared-lint-configs' into 'dev'
Shared lint configs

See merge request turris/reforis/foris-js!37
2019-11-13 10:11:11 +00:00
6e02f1d194 Shared lint configs 2019-11-13 10:11:11 +00:00
13ff8221ca Merge branch 'improve-alert' into 'dev'
Move alert to portal

See merge request turris/reforis/foris-js!35
2019-11-12 12:50:29 +00:00
a51ba0630d Move alert to portal 2019-11-12 12:50:29 +00:00
ee5cf07614 Merge branch 'loading-and-errors' into 'dev'
Loading and errors HOCs

See merge request turris/reforis/foris-js!34
2019-11-07 17:21:14 +00:00
8b39bf4193 Loading and errors HOCs 2019-11-07 17:21:14 +00:00
644726a0fc Merge branch '6-api-hooks-fix' into 'dev'
Added missing hook to index.js

Closes #6

See merge request turris/reforis/foris-js!33
2019-11-05 12:03:43 +00:00
654ae6914a Added missing hook to index.js 2019-11-05 13:00:44 +01:00
81dedf59cd Merge branch '6-api-hooks' into 'dev'
Resolve "Discuss and implement proper API methods."

Closes #6

See merge request turris/reforis/foris-js!32
2019-11-05 11:10:50 +00:00
7f8aaea7b8 Resolve "Discuss and implement proper API methods." 2019-11-05 11:10:50 +00:00
dfa80e64ec Merge branch 'publish-fix' into 'dev'
Fixed publish script

See merge request turris/reforis/foris-js!31
2019-11-01 11:08:05 +00:00
031b53f03c Fixed publish script 2019-11-01 10:49:45 +01:00
ca23b2d335 Merge branch 'alert-context-fix' into 'dev'
Flat structure of published package

See merge request turris/reforis/foris-js!30
2019-11-01 09:28:28 +00:00
0984c45161 Flat structure of published package 2019-11-01 09:28:28 +00:00
6835bc7a28 Merge branch 'global-alert' into 'dev'
Global alert

See merge request turris/reforis/foris-js!29
2019-10-30 16:06:53 +00:00
0915d477fe Global alert 2019-10-30 16:06:53 +00:00
c114579b7f Merge branch 'dev' into 'master'
Release 1.2.0

See merge request turris/reforis/foris-js!28
2019-10-24 13:52:35 +00:00
22b2bc9c09 Merge branch 'release-1.2.0' into 'dev'
Release 1.2.0

See merge request turris/reforis/foris-js!27

[skip ci]
2019-10-24 11:54:36 +00:00
cab2cfa068 Release 1.2.0 2019-10-24 13:47:25 +02:00
a137a0d4cf Merge branch 'dev' into 'master'
Release 1.1.1

See merge request turris/reforis/foris-js!26
2019-10-24 11:24:57 +00:00
54cf7e3c06 Merge branch 'master' into dev 2019-10-24 10:47:13 +02:00
18eb28f368 Merge branch 'radio-checkbox-whitespace' into 'dev'
Radio checkbox whitespace

See merge request turris/reforis/foris-js!25

[skip ci]
2019-10-24 08:12:39 +00:00
88f3836485 Radio checkbox whitespace 2019-10-24 08:12:39 +00:00
8f88b09e66 Merge branch 'dev' into 'master'
Release 1.1.0

See merge request turris/reforis/foris-js!24
2019-10-22 08:24:10 +00:00
f2aa28f172 Release 1.1.0 2019-10-22 08:24:10 +00:00
a88fbf63e9 Merge branch 'shell-quotes' into 'dev'
Added quotes to shell variables

See merge request turris/reforis/foris-js!23
2019-10-15 08:50:53 +00:00
ff9e8fdeb1 Added quotes to shell variables 2019-10-15 10:10:33 +02:00
8cd4ac8b44 Merge branch 'ws-connection-closed' into 'dev'
Download button

See merge request turris/reforis/foris-js!22
2019-10-11 14:11:14 +00:00
760e6d9243 Download button 2019-10-11 14:11:14 +00:00
2429f4662c Merge branch '5-versioning' into 'dev'
Changed beta versioning procedure

Closes #5

See merge request turris/reforis/foris-js!21
2019-10-11 08:25:06 +00:00
b320e6a860 Changed beta versioning procedure 2019-10-11 10:15:53 +02:00
1e3e9433ec Merge branch 'client-configuration' into 'dev'
Client configuration

See merge request turris/reforis/foris-js!20
2019-10-10 15:25:00 +00:00
e3a795e40d Client configuration 2019-10-10 15:25:00 +00:00
dccc9e5769 Merge branch 'dev' into 'master'
Release 1.0.0

See merge request turris/reforis/foris-js!19
2019-10-07 15:49:42 +00:00
30748fab12 Merge branch 'master' into dev
[skip ci]
2019-10-07 17:36:04 +02:00
a17ecc6060 Merge branch 'release-1.0.0' into 'dev'
Release 1.0.0

See merge request turris/reforis/foris-js!18

[skip ci]
2019-10-07 15:31:02 +00:00
75ce94d3ee Release 1.0.0 2019-10-07 17:24:03 +02:00
96263af618 Merge branch 'dev' into 'master'
Release 0.1.0

See merge request turris/reforis/foris-js!17
2019-10-07 15:16:27 +00:00
50a1bfd9b5 Release 0.1.0 2019-10-07 15:16:27 +00:00
b360431f38 Merge branch 'release-0.1.0' into 'dev'
Release 0.1.0

See merge request turris/reforis/foris-js!16

[skip ci]
2019-10-07 15:01:29 +00:00
9eebb35e65 Release 0.1.0 2019-10-07 16:53:54 +02:00
cf556e3518 Merge branch '1-automatic-publish' into 'dev'
Resolve "Automatic NPM publish."

Closes #1

See merge request turris/reforis/foris-js!15
2019-10-07 14:43:57 +00:00
5977e675cf Resolve "Automatic NPM publish." 2019-10-07 14:43:57 +00:00
ee5f3da093 Merge branch '4-increment-decrement' into 'dev'
Resolve "Increment/decrement value in NumberInput when +/- button is kept pushed."

Closes #4

See merge request turris/reforis/foris-js!14
2019-10-07 09:18:25 +00:00
afa8c160a3 Resolve "Increment/decrement value in NumberInput when +/- button is kept pushed." 2019-10-07 09:18:25 +00:00
0af56ec84c Merge branch 'non-json-payload' into 'dev'
Filter non-JSON payload

See merge request turris/reforis/foris-js!13
2019-10-01 09:38:18 +00:00
96eed02d32 Filter non-JSON payload 2019-10-01 09:38:18 +00:00
8479518fab Merge branch 'docs' into 'dev'
Fix docs, extract CSS to Foris JS.

See merge request turris/reforis/foris-js!10
2019-09-30 13:01:00 +00:00
800dedbcfd Merge branch 'reforis-125-ignore-non-json-errors' into 'dev'
Ignore non-JSON error payload

See merge request turris/reforis/foris-js!12
2019-09-30 12:58:41 +00:00
44e45499c6 Ignore non-JSON error payload 2019-09-30 12:58:41 +00:00
5457f68c70 Download docs css via NPM. 2019-09-30 11:27:13 +02:00
680871de62 Core review docs fixes. 2019-09-30 11:26:06 +02:00
9641fbcb50 Version bump 2019-09-30 10:09:52 +02:00
1708f026a4 Merge branch 'filter-keys-fix' into 'dev'
Added missing export

See merge request turris/reforis/foris-js!11
2019-09-30 08:04:05 +00:00
7516cfdbfe Added missing export 2019-09-30 09:58:11 +02:00
cbb1382c66 Merge branch 'filter-keys' into 'dev'
Added function to filter object by keys

See merge request turris/reforis/foris-js!9
2019-09-30 07:51:59 +00:00
e4e28dbce4 Move css to the library. 2019-09-26 16:10:52 +02:00
7a14ed1b60 Copyright. 2019-09-26 12:23:22 +02:00
497a1a0ad3 Added function to filter object keys 2019-09-26 09:39:07 +02:00
4a481ea642 Add styleguidist docs. 2019-09-25 16:14:29 +02:00
8296c6129e Merge branch '89-arrows-number-input' into 'dev'
Disable default styles for number input

See merge request turris/reforis/foris-js!5
2019-09-25 11:59:45 +00:00
7253319b61 Disable default styles for number input 2019-09-25 11:59:45 +00:00
7371fbcac0 Merge branch '3-handle-patch-request' into 'dev'
Resolve "Handle PATCH request"

Closes #3

See merge request turris/reforis/foris-js!8
2019-09-25 11:56:08 +00:00
d658f0b112 Resolve "Handle PATCH request" 2019-09-25 11:56:08 +00:00
edc5c92421 Merge branch '2-alert-context' into 'dev'
Resolve "Extract alert context from OpenVPN plugin"

Closes #2

See merge request turris/reforis/foris-js!7
2019-09-24 07:52:09 +00:00
d4bc5a6998 Resolve "Extract alert context from OpenVPN plugin" 2019-09-24 07:52:09 +00:00
b938dd85ff Merge branch 'api-delete-method' into 'dev'
DELETE method in API hooks

See merge request turris/reforis/foris-js!6
2019-09-10 08:30:42 +00:00
4ff3e33411 DELETE method in API hooks 2019-09-10 08:30:42 +00:00
89 changed files with 4106 additions and 1029 deletions

View File

@ -1,66 +1,6 @@
const path = require("path");
module.exports = {
"env": {
"browser": true,
"node": true,
"es6": true,
"jest": true
},
"extends": [
"airbnb",
"airbnb/hooks"
],
"globals": {
"_": "readonly",
"babel": "readonly",
"ForisTranslations": "readonly",
"ngettext": "readonly",
"ForisPlugins": "readonly"
},
"parser": "babel-eslint",
"rules": {
"quotes": ["error", "double"],
"indent": ["error", 4],
"react/jsx-indent": ["error", 4],
"react/jsx-indent-props": ["error", 4],
"react/prop-types": "warn",
"react/no-array-index-key": "warn",
"react/button-has-type": "warn",
extends: "eslint-config-reforis",
rules: {
"import/prefer-default-export": "off",
"import/no-unresolved": [
"error",
// Ignore imports used only in tests
{ ignore: ["customTestRender"] }
],
"import/no-cycle": "warn",
"no-console": "error",
"no-use-before-define": ["error", {
functions: false,
classes: true,
variables: true
}],
"no-restricted-syntax": "warn",
// Should be enabled in the future
"camelcase": "off",
"no-param-reassign": "off",
"react/jsx-props-no-spreading": "off",
"react/require-default-props": "off",
"react/default-props-match-prop-types": "off",
"react/forbid-prop-types": "off",
// Permanently disabled
"react/jsx-filename-extension": "off",
"no-plusplus": "off",
"consistent-return": "off",
"radix": "off",
"no-continue": "off",
"react/no-danger": "off",
},
"settings": {
"import/resolver": {
"node": {
"paths": ["src"]
}
}
}
};

5
.gitignore vendored
View File

@ -4,6 +4,9 @@
logs
*.log
# Python
venv/
# NodeJS
## Logs
npm-debug.log*
@ -43,8 +46,8 @@ coverage.xml
## Translations
*.mo
/js/styleguide/
.gitignore
dist/
foris-*.tgz
styleguide/

View File

@ -3,24 +3,42 @@ image: node:8-alpine
stages:
- test
- build
- publish
before_script:
- apk add make
- npm install
test:
stage: test
script:
- npm test
- make test
lint:
stage: test
script:
- npm run lint
- make lint
build:
stage: build
script:
- npm pack
- make pack
artifacts:
paths:
- foris-*.tgz
- dist/foris-*.tgz
publish_beta:
stage: publish
only:
refs:
- dev
script:
- make publish-beta
publish_latest:
stage: publish
only:
refs:
- master
script:
- make publish-latest

View File

@ -1,17 +1,71 @@
.PHONY: all create-messages update-messages clean
.PHONY: all install-js watch-js build-js collect-files pack publish-beta publish-latest lint test test-js-update-snapshots create-messages update-messages docs docs-watch clean
DEV_PYTHON=python3.7
VENV_NAME?=venv
VENV_BIN=$(shell pwd)/$(VENV_NAME)/bin
all:
@echo "make install-js"
@echo " Install dependencies"
@echo "make watch-js"
@echo " Compile JS in watch mode."
@echo "make build-js"
@echo " Compile JS."
@echo "make lint-js"
@echo " Run linter"
@echo "make test-js"
@echo " Run tests"
@echo "make create-messages"
@echo " Create locale messages (.pot)."
@echo "make update-messages"
@echo " Update locale messages from .pot file."
@echo "make docs"
@echo " Build project documentation."
@echo "make docs-watch"
@echo " Start styleguidist server."
@echo "make clean"
@echo " Remove python artifacts and virtualenv."
create-messages:
pybabel extract -F babel.cfg -o ./translations/forisjs.pot .
update-messages:
pybabel update -i translations/forisjs.pot -d translations
venv: $(VENV_NAME)/bin/activate
$(VENV_NAME)/bin/activate:
test -d $(VENV_NAME) || $(DEV_PYTHON) -m virtualenv -p $(DEV_PYTHON) $(VENV_NAME)
$(VENV_BIN)/$(DEV_PYTHON) -m pip install -r requirements.txt
touch $(VENV_NAME)/bin/activate
install-js: package.json
npm install --save-dev
watch-js:
npm run build:watch
build-js:
npm run build
collect-files:
sh scripts/collect_files.sh
pack: collect-files
cd dist && npm pack
publish-beta: collect-files
sh scripts/publish.sh beta
publish-latest: collect-files
sh scripts/publish.sh latest
lint:
npm run lint
test:
npm test
test-js-update-snapshots:
npm test -- -u
create-messages: venv
$(VENV_BIN)/pybabel extract -F babel.cfg -o ./translations/forisjs.pot .
update-messages: venv
$(VENV_BIN)/pybabel update -i ./translations/forisjs.pot -d ./translations -D forisjs
docs:
npm run-script docs
docs-watch:
npm run-script docs:watch
clean:
rm -rf node_modules dist

17
README.md Normal file
View File

@ -0,0 +1,17 @@
# foris-js
## Publishing package
### Beta versions
Each commit to `dev` branch will result in publishing a new version of library
tagged `beta`. Versions names are based on commit SHA, e.g.
`foris@0.1.0-beta.d9073aa4`.
### Preparing a release
1. Crete a merge request to `dev` branch with version bumped
2. When merging add `[skip ci]` to commit message to prevent publishing
unnecessary version
3. Create a merge request from `dev` to `master` branch
4. New version should be published automatically

View File

@ -14,4 +14,12 @@ module.exports = {
},
}],
],
env: {
development: {
ignore: ["**/__tests__/**", "**/__mocks__/**"],
},
test: {
ignore: [],
},
},
};

1
docs/intro.md Normal file
View File

@ -0,0 +1 @@
Foris JS library is set of componets and utils for Foris JS application and plugins.

View File

@ -24,4 +24,8 @@ module.exports = {
globals: {
TZ: "utc",
},
transform: {
"^.+\\.js$": "babel-jest",
"^.+\\.css$": "jest-transform-css",
},
};

2230
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "foris",
"version": "0.0.7",
"version": "1.3.2",
"description": "Set of components and utils for Foris and its plugins.",
"author": "CZ.NIC, z.s.p.o.",
"repository": {
@ -12,60 +12,63 @@
"reforis"
],
"license": "GPL-3.0",
"main": "./dist/index.js",
"main": "index.js",
"dependencies": {
"axios": "^0.19.0",
"immutability-helper": "^3.0.1",
"jest-transform-css": "^2.0.0",
"moment": "^2.24.0",
"moment-timezone": "^0.5.25",
"prop-types": "^15.7.2",
"react-datetime": "^2.16.3",
"react-router": "^5.0.1",
"react-uid": "^2.2.0",
"moment": "^2.24.0",
"moment-timezone": "^0.5.25"
"react-uid": "^2.2.0"
},
"peerDependencies": {
"react": "^16.9.0",
"react-dom": "^16.9.0"
"immutability-helper": "3.0.1",
"react": "16.9.0",
"react-dom": "16.9.0"
},
"devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.5",
"@babel/plugin-proposal-class-properties": "^7.4.4",
"@babel/plugin-syntax-export-default-from": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.4.4",
"@babel/preset-env": "^7.4.5",
"@babel/preset-react": "^7.0.0",
"@testing-library/react": "^8.0.1",
"babel-eslint": "^9.0.0",
"@fortawesome/fontawesome-free": "^5.11.2",
"@testing-library/react": "^8.0.9",
"babel-jest": "^24.8.0",
"babel-loader": "^8.0.6",
"babel-plugin-module-resolver": "^3.2.0",
"babel-plugin-react-transform": "^3.0.0",
"babel-polyfill": "^6.26.0",
"eslint": "^5.16.0",
"eslint-config-airbnb": "^18.0.1",
"eslint-plugin-import": "^2.17.3",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.13.0",
"eslint-plugin-react-hooks": "^1.6.0",
"bootstrap": "^4.3.1",
"copy-webpack-plugin": "^5.0.4",
"css-loader": "^3.2.0",
"eslint": "^6.1.0",
"eslint-config-reforis": "^1.0.0",
"file-loader": "^4.2.0",
"immutability-helper": "3.0.1",
"jest": "^24.8.0",
"jest-mock-axios": "^3.0.0",
"moment": "^2.24.0",
"moment-timezone": "^0.5.25",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-styleguidist": "^9.1.11",
"snapshot-diff": "^0.5.1"
"react": "16.9.0",
"react-dom": "16.9.0",
"react-styleguidist": "^9.1.16",
"snapshot-diff": "^0.5.1",
"style-loader": "^1.0.0",
"webpack": "^4.41.0"
},
"scripts": {
"build": "rm -rf dist; babel src --out-dir dist --ignore '**/__tests__' --source-maps inline",
"prepare": "rm -rf ./dist && npm run build",
"build": "rm -rf dist; babel src --out-dir dist --source-maps inline --copy-files",
"build:watch": "babel src --verbose --watch --out-dir dist --source-maps inline --copy-files",
"lint": "eslint src",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage --colors"
},
"files": [
"dist/**",
"translations"
]
"test:coverage": "jest --coverage --colors",
"docs": "npx styleguidist build ",
"docs:watch": "styleguidist server"
}
}

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
Babel

9
scripts/collect_files.sh Normal file
View File

@ -0,0 +1,9 @@
#!/bin/sh
# Collect files
npm run build
cp package.json dist
cp -rf translations dist
# Remove unwanted files
rm -rf dist/**/__tests__
rm -rf dist/__mocks__

24
scripts/publish.sh Executable file
View File

@ -0,0 +1,24 @@
#!/bin/sh
if test -z "$NPM_TOKEN"
then
echo "\$NPM_TOKEN is not set"
exit 1
else
cd dist
# Need to replace "_" with "-" as GitLab CI won't accept secret vars with "-"
echo "//registry.npmjs.org/:_authToken=$(echo "$NPM_TOKEN" | tr _ -)" > .npmrc
echo "unsafe-perm = true" >> ~/.npmrc
if test "$1" = "beta"
then
BETA_VERSION=$(npx -c 'echo "$npm_package_version"')-beta.$CI_COMMIT_SHORT_SHA
npm version "$BETA_VERSION" --git-tag-version false
npm publish --tag beta
elif test "$1" = "latest"
then
npm publish
else
echo "Usage: publish.sh [ beta | latest ]"
exit 1
fi
fi

View File

@ -0,0 +1,52 @@
/*
* 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, useContext, useCallback } from "react";
import PropTypes from "prop-types";
import { Alert, ALERT_TYPES } from "bootstrap/Alert";
import { Portal } from "utils/Portal";
AlertContextProvider.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]),
};
function AlertContextProvider({ children }) {
const { AlertContext } = window;
const [alert, setAlert] = useState(null);
const setAlertWrapper = useCallback((message, type = ALERT_TYPES.DANGER) => {
setAlert({ message, type });
}, [setAlert]);
const dismissAlert = useCallback(() => setAlert(null), [setAlert]);
return (
<>
{alert && (
<Portal containerId="alert-container">
<Alert type={alert.type} onDismiss={dismissAlert}>
{alert.message}
</Alert>
</Portal>
)}
<AlertContext.Provider value={[setAlertWrapper, dismissAlert]}>
{ children }
</AlertContext.Provider>
</>
);
}
function useAlert() {
const { AlertContext } = window;
return useContext(AlertContext);
}
export { AlertContextProvider, useAlert };

View File

@ -0,0 +1,65 @@
/*
* 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, getByText, queryByText, fireEvent } from "customTestRender";
import { useAlert, AlertContextProvider } from "../AlertContext";
function AlertTest() {
const [setAlert, dismissAlert] = useAlert();
// alert-container serves as an output for Portal which renders Alert
return (
<>
<div id="alert-container" />
<button onClick={() => setAlert("Alert content")}>Set alert</button>
<button onClick={dismissAlert}>Dismiss alert</button>
</>
);
};
describe("AlertContext", () => {
let componentContainer;
beforeEach(() => {
const { container } = render(
<AlertContextProvider>
<AlertTest />
</AlertContextProvider>
);
componentContainer = container;
});
it("should render component without alert", () => {
expect(componentContainer).toMatchSnapshot();
});
it("should render alert", () => {
fireEvent.click(getByText(componentContainer, "Set alert"));
expect(componentContainer).toMatchSnapshot();
});
it("should dismiss alert with alert button", () => {
fireEvent.click(getByText(componentContainer, "Set alert"));
// Alert is present
expect(getByText(componentContainer, "Alert content")).toBeDefined();
fireEvent.click(componentContainer.querySelector(".close"));
// Alert is gone
expect(queryByText(componentContainer, "Alert content")).toBeNull();
});
it("should dismiss alert with external button", () => {
fireEvent.click(getByText(componentContainer, "Set alert"));
// Alert is present
expect(getByText(componentContainer, "Alert content")).toBeDefined();
fireEvent.click(getByText(componentContainer, "Dismiss alert"));
// Alert is gone
expect(queryByText(componentContainer, "Alert content")).toBeNull();
});
});

View File

@ -0,0 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AlertContext should render alert 1`] = `
<div>
<div
id="alert-container"
>
<div
class="alert alert-dismissible alert-danger"
>
<button
class="close"
type="button"
>
×
</button>
Alert content
</div>
</div>
<button>
Set alert
</button>
<button>
Dismiss alert
</button>
</div>
`;
exports[`AlertContext should render component without alert 1`] = `
<div>
<div
id="alert-container"
/>
<button>
Set alert
</button>
<button>
Dismiss alert
</button>
</div>
`;

View File

@ -5,149 +5,110 @@
* See /LICENSE for more information.
*/
import axios from "axios";
import { useCallback, useReducer } from "react";
import {
useCallback, useEffect, useReducer, useState,
} from "react";
import { ForisURLs } from "forisUrls";
import {
API_ACTIONS, API_METHODS, API_STATE, getErrorPayload, HEADERS, TIMEOUT,
} from "./utils";
const DATA_METHODS = ["POST", "PATCH", "PUT"];
const POST_HEADERS = {
Accept: "application/json",
"Content-Type": "application/json",
"X-CSRFToken": getCookie("_csrf_token"),
};
function createAPIHook(method) {
return (url, contentType) => {
const [state, dispatch] = useReducer(APIReducer, {
state: API_STATE.INIT,
data: null,
});
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;
const sendRequest = useCallback(async (data) => {
const headers = { ...HEADERS };
if (contentType) {
headers["Content-Type"] = contentType;
}
}
}
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();
}
};
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,
});
}
dispatch({ type: API_ACTIONS.INIT });
try {
const request = API_METHODS[method];
const config = {
timeout: TIMEOUT,
headers,
};
let result;
if (DATA_METHODS.includes(method)) {
result = await request(url, data, config);
} else {
result = await request(url, config);
}
dispatch({
type: API_ACTIONS.SUCCESS,
payload: result.data,
});
} catch (error) {
dispatch({
type: API_ACTIONS.FAILURE,
status: error.response && error.response.status,
payload: getErrorPayload(error),
});
}
}, [url, contentType]);
return [state, sendRequest];
};
return [state, post];
}
function APIReducer(state, action) {
switch (action.type) {
case API_ACTIONS.INIT:
return {
...state,
state: API_STATE.SENDING,
};
case API_ACTIONS.SUCCESS:
return {
state: API_STATE.SUCCESS,
data: action.payload,
};
case API_ACTIONS.FAILURE:
if (action.status === 403) {
window.location.assign(ForisURLs.login);
}
return {
state: API_STATE.ERROR,
data: action.payload,
};
default:
throw new Error();
}
}
const useAPIGet = createAPIHook("GET");
const useAPIPost = createAPIHook("POST");
const useAPIPatch = createAPIHook("PATCH");
const useAPIPut = createAPIHook("PUT");
const useAPIDelete = createAPIHook("DELETE");
export {
useAPIGet, useAPIPost, useAPIPatch, useAPIPut, useAPIDelete,
};
export function useAPIPolling(endpoint, delay = 1000, until) { // delay ms
const [state, setState] = useState({ state: API_STATE.INIT });
const [getResponse, get] = useAPIGet(endpoint);
useEffect(() => {
if (getResponse.state !== API_STATE.INIT) {
setState(getResponse);
}
}, [getResponse]);
useEffect(() => {
if (until) {
const interval = setInterval(get, delay);
return () => clearInterval(interval);
}
}, [until, delay, get]);
return [state];
}

78
src/api/utils.js Normal file
View File

@ -0,0 +1,78 @@
/*
* 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";
export const HEADERS = {
Accept: "application/json",
"Content-Type": "application/json",
"X-CSRFToken": getCookie("_csrf_token"),
};
export const TIMEOUT = 5000;
export const API_ACTIONS = {
INIT: 1,
SUCCESS: 2,
FAILURE: 3,
};
export const API_STATE = {
INIT: "init",
SENDING: "sending",
SUCCESS: "success",
ERROR: "error",
};
export const API_METHODS = {
GET: axios.get,
POST: axios.post,
PATCH: axios.patch,
PUT: axios.put,
DELETE: axios.delete,
};
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 function getErrorPayload(error) {
if (error.response) {
if (error.response.status === 403) {
return _("The session is expired. Please log in again.");
}
return getJSONErrorMessage(error);
}
if (error.code === "ECONNABORTED") {
return _("Timeout error occurred.");
}
if (error.request) {
return _("No response received.");
}
/* eslint no-console: "off" */
console.error(error);
return _("An unknown error occurred. Check the console for more info.");
}
export function getJSONErrorMessage(error) {
if (error.response.headers["content-type"] === "application/json") {
return error.response.data;
}
return _("An unknown API error occurred.");
}

View File

@ -8,11 +8,20 @@
import React from "react";
import PropTypes from "prop-types";
export const ALERT_TYPES = Object.freeze({
PRIMARY: "primary",
SECONDARY: "secondary",
SUCCESS: "success",
DANGER: "danger",
WARNING: "warning",
INFO: "info",
LIGHT: "light",
DARK: "dark",
});
Alert.propTypes = {
/** Type of the alert it adds as `alert-${type}` class. */
type: PropTypes.string.isRequired,
/** Alert message. */
message: PropTypes.string,
type: PropTypes.oneOf(Object.values(ALERT_TYPES)),
/** Alert content. */
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
@ -22,13 +31,16 @@ Alert.propTypes = {
onDismiss: PropTypes.func,
};
Alert.defaultProps = {
type: ALERT_TYPES.DANGER,
};
export function Alert({
type, message, onDismiss, children,
type, onDismiss, children,
}) {
return (
<div className={`alert alert-dismissible alert-${type}`}>
{onDismiss ? <button type="button" className="close" onClick={onDismiss}>&times;</button> : false}
{message}
{children}
</div>
);

View File

@ -7,7 +7,7 @@
import React from "react";
import PropTypes from "prop-types";
import { useUID } from "react-uid/dist/es5/index";
import { useUID } from "react-uid";
import { formFieldsSize } from "./constants";
@ -32,8 +32,8 @@ export function CheckBox({
}) {
const uid = useUID();
return (
<div className={useDefaultSize ? formFieldsSize : ""} style={{ marginBottom: "1rem" }}>
<div className="custom-control custom-checkbox" style={{ marginBottom: "0" }}>
<div className={`form-group ${useDefaultSize ? formFieldsSize : ""}`.trim()}>
<div className="custom-control custom-checkbox ">
<input
className="custom-control-input"
type="checkbox"
@ -42,9 +42,11 @@ export function CheckBox({
{...props}
/>
<label className="custom-control-label" htmlFor={uid} style={helpText ? { marginBottom: "0" } : null}>{label}</label>
<label className="custom-control-label" htmlFor={uid}>
{label}
{helpText && <small className="form-text text-muted">{helpText}</small>}
</label>
</div>
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
</div>
);
}

View File

@ -11,6 +11,6 @@ const [value, setValue] = useState(false);
value={value}
label="Some label"
helpText="Read the small text!"
onChange={value => setValue(value)}
onChange={event =>setValue(event.target.value)}
/>
```

View File

@ -9,6 +9,7 @@ import React from "react";
import PropTypes from "prop-types";
import Datetime from "react-datetime/DateTime";
import moment from "moment/moment";
import "react-datetime/css/react-datetime.css";
import { Input } from "./Input";

View 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.
*/
import React from "react";
import PropTypes from "prop-types";
DownloadButton.propTypes = {
href: PropTypes.string.isRequired,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]),
};
export function DownloadButton({ href, children }) {
return <a href={href} className="btn btn-primary" download>{children}</a>;
}

View File

@ -0,0 +1,7 @@
Hyperlink with apperance of a button.
It has `download` attribute, which prevents closing WebSocket connection on Firefox. See [related issue](https://bugzilla.mozilla.org/show_bug.cgi?id=858538) for more details.
```js
<DownloadButton href="example.zip">Download</DownloadButton>
```

View File

@ -12,7 +12,6 @@ import { Input } from "./Input";
export const EmailInput = ({ ...props }) => <Input type="email" {...props} />;
EmailInput.propTypes = {
/** Field label. */
label: PropTypes.string.isRequired,

View File

@ -11,7 +11,7 @@ const [email, setEmail] = useState('Wrong email');
value={email}
label="Some label"
helpText="Read the small text!"
onChange={target => setEmail(target.value)}
onChange={event =>setEmail(event.target.value)}
/>
<button type="submit">Try to submit</button>
</form>

View File

@ -0,0 +1,34 @@
/*
* 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";
FileInput.propTypes = {
/** Field label. */
label: PropTypes.string.isRequired,
/** Error message. */
error: PropTypes.string,
/** Help text message. */
helpText: PropTypes.string,
/** Email value. */
value: PropTypes.string,
};
export function FileInput({ ...props }) {
return (
<Input
type="file"
className="custom-file-input"
labelClassName="custom-file-label"
groupClassName="custom-file"
{...props}
/>
);
}

View File

@ -0,0 +1,15 @@
Bootstrap component for file input. Includes label and has predefined sizes and structure for using in foris forms.
All additional `props` are passed to the `<input type="file">` HTML component.
```js
import {useState} from 'react';
const [files, setFiles] = useState([]);
<FileInput
files={files}
label="Some file"
helpText="Will be uploaded"
onChange={event =>setFiles(event.target.files)}
/>
```

View File

@ -6,7 +6,7 @@
*/
import React from "react";
import { useUID } from "react-uid/dist/es5/index";
import { useUID } from "react-uid";
import PropTypes from "prop-types";
import { formFieldsSize } from "./constants";
@ -21,31 +21,31 @@ Input.propTypes = {
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]),
labelClassName: PropTypes.string,
groupClassName: PropTypes.string,
};
/** Base bootstrap input component. */
export function Input({
type, label, helpText, error, className, children, ...props
type, label, helpText, error, className, children, labelClassName, groupClassName, ...props
}) {
const uid = useUID();
const inputClassName = `form-control ${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}
<div className={`form-group ${formFieldsSize}`}>
<label className={labelClassName} htmlFor={uid}>{label}</label>
<div className={`input-group ${groupClassName || ""}`.trim()}>
<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}
{...props}
/>
{children}
</div>
{error ? <div className="invalid-feedback">{error}</div> : null}
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
</div>
);
}

View File

@ -37,11 +37,10 @@ export function Modal({ shown, setShown, children }) {
};
}, [setShown]);
return (
<Portal containerId="modal-container">
<div className={`modal fade ${shown ? "show" : ""}`} role="dialog">
<div ref={dialogRef} className="modal-dialog" role="document">
<div ref={dialogRef} className="modal-dialog modal-dialog-centered" role="document">
<div className="modal-content">
{children}
</div>

View File

@ -1,6 +1,13 @@
Bootstrap modal component.
I have no idea why example doesn't work here but you can investigate HTML code...
it's required to have an element `<div id={"modal-container"}/>` somewhere on the page since modals are rendered in portals.
```js
<div id="modal-container"/>
```
I have no idea why example doesn't work here but you can investigate HTML code and Foris project.
```js
import {ModalHeader, ModalBody, ModalFooter} from './Modal';
@ -8,7 +15,7 @@ import {useState} from 'react';
const [shown, setShown] = useState(false);
<>
<Modal shown={shown}>
<Modal setShown={setShown} shown={shown}>
<ModalHeader setShown={setShown} title='Warning!'/>
<ModalBody><p>Bla bla bla...</p></ModalBody>
<ModalFooter>
@ -19,9 +26,8 @@ const [shown, setShown] = useState(false);
</ModalFooter>
</Modal>
<button
className='btn btn-secondary'
onClick={()=>setShown(true)}
>Show modal</button>
<button className='btn btn-secondary' onClick={()=>setShown(true)}>
Show modal
</button>
</>
```

View File

@ -0,0 +1,10 @@
input[type="number"] {
-webkit-appearance: textfield;
-moz-appearance: textfield;
appearance: textfield;
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
}

View File

@ -6,11 +6,11 @@
*/
import React from "react";
import PropTypes from "prop-types";
import { Input } from "./Input";
export const NumberInput = ({ ...props }) => <Input type="number" {...props} />;
import { useConditionalTimeout } from "utils/hooks";
import { Input } from "./Input";
import "./NumberInput.css";
NumberInput.propTypes = {
/** Field label. */
@ -24,4 +24,49 @@ NumberInput.propTypes = {
PropTypes.string,
PropTypes.number,
]),
/** Function called when value changes. */
onChange: PropTypes.func.isRequired,
/** Additional description dispaled to the right of input value. */
inlineText: PropTypes.string,
};
NumberInput.defaultProps = {
value: 0,
};
export function NumberInput({
onChange, inlineText, value, ...props
}) {
function updateValue(initialValue, difference) {
onChange({ target: { value: initialValue + difference } });
}
const enableIncrease = useConditionalTimeout({ callback: updateValue }, value, 1);
const enableDecrease = useConditionalTimeout({ callback: updateValue }, value, -1);
return (
<Input type="number" onChange={onChange} value={value} {...props}>
<div className="input-group-append">
{inlineText && <p className="input-group-text">{inlineText}</p>}
<button
type="button"
className="btn btn-outline-secondary"
onMouseDown={() => enableIncrease(true)}
onMouseUp={() => enableIncrease(false)}
aria-label="Increase"
>
<i className="fas fa-plus" />
</button>
<button
type="button"
className="btn btn-outline-secondary"
onMouseDown={() => enableDecrease(true)}
onMouseUp={() => enableDecrease(false)}
aria-label="Decrease"
>
<i className="fas fa-minus" />
</button>
</div>
</Input>
);
}

View File

@ -12,6 +12,6 @@ const [value, setValue] = useState(42);
helpText="Read the small text!"
min='33'
max='54'
onChange={target => setValue(target.value)}
onChange={event =>setValue(event.target.value)}
/>
```

View File

@ -12,6 +12,6 @@ const [value, setValue] = useState('secret');
value={value}
label="Some password"
helpText="Read the small text!"
onChange={target => setValue(target.value)}
onChange={event =>setValue(event.target.value)}
/>
```

View File

@ -7,11 +7,10 @@
import React from "react";
import PropTypes from "prop-types";
import { useUID } from "react-uid/dist/es5/index";
import { useUID } from "react-uid";
import { formFieldsSize } from "./constants";
RadioSet.propTypes = {
/** Name attribute of the input HTML tag. */
name: PropTypes.string.isRequired,
@ -52,16 +51,10 @@ export function RadioSet({
});
return (
<div className={`form-group ${formFieldsSize}`} style={{ marginBottom: "1rem" }}>
{label
? (
<label className="col-12" htmlFor={uid} style={{ paddingLeft: "0" }}>
{label}
</label>
)
: null}
<div className={`form-group ${formFieldsSize}`}>
{label && <label htmlFor={uid} className="d-block">{label}</label>}
{radios}
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
{helpText && <small className="form-text text-muted">{helpText}</small>}
</div>
);
}
@ -77,7 +70,7 @@ function Radio({
}) {
return (
<>
<div className="custom-control custom-radio custom-control-inline">
<div className={`custom-control custom-radio ${!helpText ? "custom-control-inline" : ""}`.trim()}>
<input
id={id}
className="custom-control-input"
@ -86,8 +79,8 @@ function Radio({
{...props}
/>
<label className="custom-control-label" htmlFor={id}>{label}</label>
{helpText && <small className="form-text text-muted mt-0 mb-3">{helpText}</small>}
</div>
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
</>
);
}

View File

@ -2,6 +2,8 @@ Set of radio Bootstrap component input with label and predefined sizes and struc
All additional `props` are passed to the `<input type="number">` HTML component.
Unless `helpText` is set for one of the options they are displayed inline.
```js
import {useState} from 'react';
const CHOICES=[
@ -17,7 +19,7 @@ const [value, setValue] = useState(CHOICES[0].value);
value={value}
name='some-radio'
choices={CHOICES}
onChange={event=>setValue(event.target.value)}
onChange={event =>setValue(event.target.value)}
/>
<p>Selected value: {value}</p>
</>

View File

@ -7,8 +7,7 @@
import React from "react";
import PropTypes from "prop-types";
import { useUID } from "react-uid/dist/es5/index";
import { useUID } from "react-uid";
Select.propTypes = {
/** Select field Label. */

View File

@ -24,18 +24,18 @@ Spinner.defaultProps = {
};
export function Spinner({
fullScreen, children, className, ...props
fullScreen, children, className,
}) {
if (!fullScreen) {
return (
<div className={`spinner-wrapper ${className || ""}`} {...props}>
<div className={`spinner-wrapper ${className || "my-3 text-center"}`}>
<SpinnerElement>{children}</SpinnerElement>
</div>
);
}
return (
<div className="spinner-fs-wrapper" {...props}>
<div className="spinner-fs-wrapper">
<div className="spinner-fs-background">
<SpinnerElement>{children}</SpinnerElement>
</div>

View File

@ -10,10 +10,8 @@ import PropTypes from "prop-types";
import { Input } from "./Input";
export const TextInput = ({ ...props }) => <Input type="text" {...props} />;
TextInput.propTypes = {
/** Field label. */
label: PropTypes.string.isRequired,

View File

@ -10,6 +10,6 @@ const [value, setValue] = useState('Bla bla');
value={value}
label="Some text"
helpText="Read the small text!"
onChange={event => setValue(event.target.value)}
onChange={event =>setValue(event.target.value)}
/>
```

View File

@ -0,0 +1,19 @@
/*
* 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 { DownloadButton } from "../DownloadButton";
describe("<DownloadButton />", () => {
it("should have download attribute", () => {
const { container } = render(<DownloadButton href="http://example.com">Test Button</DownloadButton>);
expect(container.firstChild.getAttribute("download")).not.toBeNull();
});
});

View File

@ -7,23 +7,39 @@
import React from "react";
import { render } from "customTestRender";
import { render, fireEvent, getByLabelText, wait } from "customTestRender";
import { NumberInput } from "../NumberInput";
describe("<NumberInput/>", () => {
it("Render number input", () => {
const onChangeMock = jest.fn();
let componentContainer;
beforeEach(() => {
const { container } = render(
<NumberInput
label="Test label"
helpText="Some help text"
value={1123}
onChange={() => {
}}
value={1}
onChange={onChangeMock}
/>
);
expect(container.firstChild)
.toMatchSnapshot();
componentContainer = container;
});
it("Render number input", () => {
expect(componentContainer.firstChild).toMatchSnapshot();
});
it("Increase number with button", async () => {
const increaseButton = getByLabelText(componentContainer, "Increase");
fireEvent.mouseDown(increaseButton);
await wait(() => expect(onChangeMock).toHaveBeenCalledWith({"target": {"value": 2}}));
});
it("Decrease number with button", async () => {
const decreaseButton = getByLabelText(componentContainer, "Decrease");
fireEvent.mouseDown(decreaseButton);
await wait(() => expect(onChangeMock).toHaveBeenCalledWith({"target": {"value": 0}}));
});
});

View File

@ -2,12 +2,10 @@
exports[`<Checkbox/> Render checkbox 1`] = `
<div
class="col-sm-12 offset-lg-1 col-lg-10"
style="margin-bottom: 1rem;"
class="form-group col-sm-12 offset-lg-1 col-lg-10"
>
<div
class="custom-control custom-checkbox"
style="margin-bottom: 0px;"
class="custom-control custom-checkbox "
>
<input
checked=""
@ -18,27 +16,24 @@ exports[`<Checkbox/> Render checkbox 1`] = `
<label
class="custom-control-label"
for="1"
style="margin-bottom: 0px;"
>
Test label
<small
class="form-text text-muted"
>
Some help text
</small>
</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;"
class="form-group col-sm-12 offset-lg-1 col-lg-10"
>
<div
class="custom-control custom-checkbox"
style="margin-bottom: 0px;"
class="custom-control custom-checkbox "
>
<input
class="custom-control-input"
@ -48,15 +43,14 @@ exports[`<Checkbox/> Render uncheked checkbox 1`] = `
<label
class="custom-control-label"
for="1"
style="margin-bottom: 0px;"
>
Test label
<small
class="form-text text-muted"
>
Some help text
</small>
</label>
</div>
<small
class="form-text text-muted"
>
Some help text
</small>
</div>
`;

View File

@ -2,31 +2,49 @@
exports[`<NumberInput/> Render number input 1`] = `
<div
class="col-sm-12 offset-lg-1 col-lg-10"
class="form-group col-sm-12 offset-lg-1 col-lg-10"
>
<div
class="form-group"
<label
for="1"
>
<label
for="1"
>
Test label
</label>
Test label
</label>
<div
class="input-group"
>
<input
class="form-control"
id="1"
type="number"
value="1"
/>
<div
class="input-group"
class="input-group-append"
>
<input
class="form-control"
id="1"
type="number"
value="1123"
/>
<button
aria-label="Increase"
class="btn btn-outline-secondary"
type="button"
>
<i
class="fas fa-plus"
/>
</button>
<button
aria-label="Decrease"
class="btn btn-outline-secondary"
type="button"
>
<i
class="fas fa-minus"
/>
</button>
</div>
<small
class="form-text text-muted"
>
Some help text
</small>
</div>
<small
class="form-text text-muted"
>
Some help text
</small>
</div>
`;

View File

@ -2,32 +2,28 @@
exports[`<PasswordInput/> Render password input 1`] = `
<div
class="col-sm-12 offset-lg-1 col-lg-10"
class="form-group col-sm-12 offset-lg-1 col-lg-10"
>
<div
class="form-group"
<label
for="1"
>
<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>
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>
`;

View File

@ -3,12 +3,10 @@
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"
class="d-block"
for="1"
style="padding-left: 0px;"
>
Radios set label
</label>

View File

@ -2,31 +2,27 @@
exports[`<TextInput/> Render text input 1`] = `
<div
class="col-sm-12 offset-lg-1 col-lg-10"
class="form-group col-sm-12 offset-lg-1 col-lg-10"
>
<div
class="form-group"
<label
for="1"
>
<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>
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>
`;

View File

@ -1,3 +1,10 @@
/*
* 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 = {

View File

@ -5,16 +5,19 @@
* See /LICENSE for more information.
*/
import React, { useEffect, useState } from "react";
import React, { useEffect } from "react";
import PropTypes from "prop-types";
import { Spinner } from "bootstrap/Spinner";
import { useAPIPost } from "api/hooks";
import { Prompt } from "react-router";
import { API_STATE } from "api/utils";
import { ErrorMessage } from "utils/ErrorMessage";
import { useAlert } from "alertContext/AlertContext";
import { ALERT_TYPES } from "bootstrap/Alert";
import { useForisModule, useForm } from "../hooks";
import { STATES as SUBMIT_BUTTON_STATES, SubmitButton } from "./SubmitButton";
import { FailAlert, SuccessAlert } from "./alerts";
ForisForm.propTypes = {
/** WebSocket object see `scr/common/WebSockets.js`. */
@ -69,22 +72,34 @@ export function ForisForm({
children,
}) {
const [formState, onFormChangeHandler, resetFormData] = useForm(validator, prepData);
const [setAlert] = useAlert();
const [forisModuleState] = useForisModule(ws, forisConfig);
useEffect(() => {
if (forisModuleState.data) {
if (forisModuleState.state === API_STATE.SUCCESS) {
resetFormData(forisModuleState.data);
}
}, [forisModuleState.data, resetFormData, prepData]);
}, [forisModuleState, resetFormData, prepData]);
const [postState, post] = useAPIPost(forisConfig.endpoint);
useEffect(() => {
if (postState.isSuccess) postCallback();
}, [postCallback, postState.isSuccess]);
if (postState.state === API_STATE.SUCCESS) {
postCallback();
setAlert(_("Settings saved successfully"), ALERT_TYPES.SUCCESS);
} else if (postState.state === API_STATE.ERROR) {
setAlert(postState.data);
}
}, [postCallback, postState.state, postState.data, setAlert]);
if (forisModuleState.state === API_STATE.ERROR) {
return <ErrorMessage />;
}
if (!formState.data) {
return <Spinner />;
}
function onSubmitHandler(e) {
e.preventDefault();
function onSubmitHandler(event) {
event.preventDefault();
resetFormData();
const copiedFormData = JSON.parse(JSON.stringify(formState.data));
const preparedData = prepDataToSubmit(copiedFormData);
@ -92,16 +107,18 @@ export function ForisForm({
}
function getSubmitButtonState() {
if (postState.isSending) return SUBMIT_BUTTON_STATES.SAVING;
if (forisModuleState.isLoading) return SUBMIT_BUTTON_STATES.LOAD;
if (postState.state === API_STATE.SENDING) {
return SUBMIT_BUTTON_STATES.SAVING;
}
if (forisModuleState.state === API_STATE.SENDING) {
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 formIsDisabled = (disabled
|| forisModuleState.state === API_STATE.SENDING
|| postState.state === API_STATE.SENDING);
const submitButtonIsDisabled = disabled || !!formState.errors;
const childrenWithFormProps = React.Children.map(
@ -123,19 +140,9 @@ export function ForisForm({
return _("Changes you made may not be saved. Are you sure you want to leave?");
}
let alert = null;
if (!alertIsDismissed) {
if (postState.isSuccess) {
alert = <SuccessAlert onDismiss={() => setAlertIsDismissed(true)} />;
} else if (postState.isError) {
alert = <FailAlert onDismiss={() => setAlertIsDismissed(true)} />;
}
}
return (
<>
<Prompt message={getMessageOnLeavingPage} />
{alert}
<form onSubmit={onSubmit}>
{childrenWithFormProps}
<SubmitButton

View File

@ -0,0 +1,73 @@
`<ForisForm/>` is Higher-Order Component which encapsulates entire form logic and provides with children required props.
This component structure provides comfort API and allows to create typical Foris module forms easily.
## Example of usage of `<ForisForm/>`
You can pass more forms as children.
```js
<ForisForm
ws={ws}
forisConfig={{
endpoint: API_URLs.wan,
wsModule: "wan",
}}
prepData={prepData}
prepDataToSubmit={prepDataToSubmit}
validator={validator}
>
<WANForm />
<WAN6Form />
<MACForm />
</ForisForm>
```
### Example of children forms `props` usage
```js
export default function MACForm({
formData, formErrors, setFormValue, ...props
}) {
const macSettings = formData.mac_settings;
const errors = (formErrors || {}).mac_settings || {};
return (
<>
<h3>{_("MAC")}</h3>
<CheckBox
label={_("Custom MAC address")}
checked={macSettings.custom_mac_enabled}
helpText={HELP_TEXTS.custom_mac_enabled}
onChange={setFormValue(
(value) => ({ mac_settings: { custom_mac_enabled: { $set: value } } }),
)}
{...props}
/>
{macSettings.custom_mac_enabled
? (
<TextInput
label={_("MAC address")}
value={macSettings.custom_mac || ""}
helpText={HELP_TEXTS.custom_mac}
error={errors.custom_mac}
required
onChange={setFormValue(
(value) => ({ mac_settings: { custom_mac: { $set: value } } }),
)}
{...props}
/>
)
: null}
</>
);
}
```
The <ForisForm/> passes subsequent `props` to the child components.
| Prop | Type | Description |
|----------------|--------|----------------------------------------------------------------------------|
| `formData` | object | Data returned from API. |
| `formErrors` | object | Errors returned after validation via validator. |
| `setFormValue` | func | Function for data update. It takes update rule as arg (see example above). |
| `disabled` | bool | Flag to disable form elements (during updates or loadings e.t.c.). |

View File

@ -1,46 +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.
*/
import React from "react";
import PropTypes from "prop-types";
import { Alert } from "bootstrap/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>
);
}

View File

@ -11,13 +11,12 @@ import update from "immutability-helper";
import { useAPIGet } from "api/hooks";
import { useWSForisModule } from "webSockets/hooks";
const FORM_ACTIONS = {
updateValue: 1,
resetData: 2,
};
export function useForm(validator, prepData) {
export function useForm(validator, dataPreprocessor) {
const [state, dispatch] = useReducer(formReducer, {
data: null,
initialData: null,
@ -28,10 +27,10 @@ export function useForm(validator, prepData) {
dispatch({
type: FORM_ACTIONS.resetData,
data,
prepData,
dataPreprocessor,
validator,
});
}, [prepData, validator]);
}, [dataPreprocessor, validator]);
const onFormChangeHandler = useCallback((updateRule) => (event) => {
dispatch({
@ -41,6 +40,7 @@ export function useForm(validator, prepData) {
validator,
});
}, [validator]);
return [
state,
onFormChangeHandler,
@ -48,7 +48,6 @@ export function useForm(validator, prepData) {
];
}
function formReducer(state, action) {
switch (action.type) {
case FORM_ACTIONS.updateValue: {
@ -61,12 +60,15 @@ function formReducer(state, action) {
};
}
case FORM_ACTIONS.resetData: {
if (!action.data) return { ...state, initialData: state.data };
const prepData = action.prepData ? action.prepData(action.data) : action.data;
if (!action.data) {
return { ...state, initialData: state.data };
}
const data = action.dataPreprocessor ? action.dataPreprocessor(action.data) : action.data;
return {
data: prepData,
initialData: prepData,
errors: action.data ? action.validator(prepData) : undefined,
data,
initialData: data,
errors: action.data ? action.validator(data) : undefined,
};
}
default: {
@ -82,6 +84,9 @@ function getChangedValue(target) {
} else if (target.type === "number") {
const parsedValue = parseInt(value);
value = Number.isNaN(parsedValue) ? value : parsedValue;
} else if (target.type === "file") {
// Return first file (we don't need multiple yet)
[value] = target.files;
}
return value;
}

View File

@ -1,19 +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.
*/
// API
export { useAPIGet, useAPIPost } from "./api/hooks";
export {
useAPIGet,
useAPIPost,
useAPIPatch,
useAPIPut,
useAPIDelete,
useAPIPolling,
} from "api/hooks";
export { API_STATE } from "api/utils";
// Bootstrap
export { Alert } from "bootstrap/Alert";
export { Alert, ALERT_TYPES } from "bootstrap/Alert";
export { Button } from "bootstrap/Button";
export { CheckBox } from "bootstrap/CheckBox";
export { formFieldsSize } from "bootstrap/constants";
export { DownloadButton } from "bootstrap/DownloadButton";
export { DataTimeInput } from "bootstrap/DataTimeInput";
export { EmailInput } from "bootstrap/EmailInput";
export { FileInput } from "bootstrap/FileInput";
export { Input } from "bootstrap/Input";
export { NumberInput } from "bootstrap/NumberInput";
export { PasswordInput } from "bootstrap/PasswordInput";
export { RadioSet } from "bootstrap/RadioSet";
export { Select } from "bootstrap/Select";
export { TextInput } from "bootstrap/TextInput";
export { formFieldsSize } from "bootstrap/constants";
export {
Spinner,
@ -37,6 +54,11 @@ export { WebSockets } from "webSockets/WebSockets";
// Utils
export { Portal } from "utils/Portal";
export { undefinedIfEmpty, withoutUndefinedKeys, onlySpecifiedKeys } from "utils/objectHelpers";
export {
withEither, withSpinner, withSending, withSpinnerOnSending, withError, withErrorMessage,
} from "utils/conditionalHOCs";
export { ErrorMessage } from "utils/ErrorMessage";
// Foris URL
export { ForisURLs, REFORIS_URL_PREFIX } from "forisUrls";
@ -51,3 +73,6 @@ export {
validateMAC,
validateMultipleEmails,
} from "validations";
// Alert context
export { AlertContextProvider, useAlert } from "alertContext/AlertContext";

View File

@ -0,0 +1,23 @@
/*
* 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";
const mockSetAlert = jest.fn();
const mockDismissAlert = jest.fn();
window.AlertContext = React.createContext();
function AlertContextMock({ children }) {
return (
<AlertContext.Provider value={[mockSetAlert, mockDismissAlert]}>
{ children }
</AlertContext.Provider>
);
}
export { AlertContextMock, mockSetAlert, mockDismissAlert };

View File

@ -7,31 +7,37 @@
/* 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'
import React from "react";
import { UIDReset } from "react-uid";
import { StaticRouter } from "react-router";
import { render } from "@testing-library/react";
import PropTypes from "prop-types";
import { AlertContextMock } from "./alertContextMock";
Wrapper.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
])
PropTypes.node,
]),
};
function Wrapper({children}) {
return <StaticRouter>
<UIDReset>
{children}
</UIDReset>
</StaticRouter>
function Wrapper({ children }) {
return (
<AlertContextMock>
<StaticRouter>
<UIDReset>
{children}
</UIDReset>
</StaticRouter>
</AlertContextMock>
);
}
const customTestRender = (ui, options) => render(ui, {wrapper: Wrapper, ...options});
const customTestRender = (ui, options) => render(ui, { wrapper: Wrapper, ...options });
// re-export everything
export * from '@testing-library/react'
export * from "@testing-library/react";
// override render method
export {customTestRender as render}
export { customTestRender as render };

12
src/testUtils/network.js Normal file
View File

@ -0,0 +1,12 @@
/*
* 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 mockAxios from 'jest-mock-axios';
export function mockJSONError(data) {
mockAxios.mockError({ response: { data, headers: { "content-type": "application/json" } } });
}

View File

@ -15,12 +15,15 @@ global.afterEach(() => {
// Mock babel (gettext)
global._ = str => str;
global.ngettext = str => str;
global.babel = {format: (str) => str};
global.ForisTranslations = {};
// Mock web sockets
window.WebSocket = jest.fn();
// Mock scrollIntoView
global.HTMLElement.prototype.scrollIntoView = () => {
};
global.HTMLElement.prototype.scrollIntoView = () => {};
jest.doMock('moment', () => {
moment.tz.setDefault('UTC');

16
src/utils/ErrorMessage.js Normal file
View File

@ -0,0 +1,16 @@
/*
* 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";
export function ErrorMessage() {
return (
<p className="text-center text-danger">
{_("An error occurred while fetching data.")}
</p>
);
}

View File

@ -0,0 +1,61 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`conditional HOCs withError should render error message 1`] = `
<div>
<p
class="text-center text-danger"
>
An error occurred while fetching data.
</p>
</div>
`;
exports[`conditional HOCs withErrorMessage should render error message 1`] = `
<div>
<p
class="text-center text-danger"
>
An error occurred while fetching data.
</p>
</div>
`;
exports[`conditional HOCs withSpinner should render spinner 1`] = `
<div>
<div
class="spinner-wrapper my-3 text-center"
>
<div
class="spinner-border "
role="status"
>
<span
class="sr-only"
/>
</div>
<div
class="spinner-text"
/>
</div>
</div>
`;
exports[`conditional HOCs withSpinnerOnSending should render spinner 1`] = `
<div>
<div
class="spinner-wrapper my-3 text-center"
>
<div
class="spinner-border "
role="status"
>
<span
class="sr-only"
/>
</div>
<div
class="spinner-text"
/>
</div>
</div>
`;

View File

@ -0,0 +1,110 @@
/*
* 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 {
withEither, withSpinner, withSending, withSpinnerOnSending, withError, withErrorMessage,
} from "../conditionalHOCs";
import { API_STATE } from "api/utils";
describe("conditional HOCs", () => {
const First = () => <p>First</p>;
const Alternative = () => <p>Alternative</p>;
describe("withEither", () => {
it("should render First component", () => {
const withAlternative = withEither(() => false, Alternative);
const FirstWithConditional = withAlternative(First);
const { getByText } = render(<FirstWithConditional />);
expect(getByText("First")).toBeDefined();
});
it("should render Alternative component", () => {
const withAlternative = withEither(() => true, Alternative);
const FirstWithConditional = withAlternative(First);
const { getByText } = render(<FirstWithConditional />);
expect(getByText("Alternative")).toBeDefined();
});
});
describe("withSpinner", () => {
it("should render First component", () => {
const withSpinnerHidden = withSpinner(() => false);
const FirstWithConditional = withSpinnerHidden(First);
const { getByText } = render(<FirstWithConditional />);
expect(getByText("First")).toBeDefined();
});
it("should render spinner", () => {
const withSpinnerVisible = withSpinner(() => true);
const FirstWithConditional = withSpinnerVisible(First);
const { container } = render(<FirstWithConditional />);
expect(container).toMatchSnapshot();
});
});
describe("withSending", () => {
it("should render First component", () => {
const withAlternative = withSending(Alternative);
const FirstWithConditional = withAlternative(First);
const { getByText } = render(<FirstWithConditional apiState={API_STATE.SUCCESS} />);
expect(getByText("First")).toBeDefined();
});
it("should render Alternative component", () => {
const withAlternative = withSending(Alternative);
const FirstWithConditional = withAlternative(First);
const { getByText } = render(<FirstWithConditional apiState={API_STATE.SENDING} />);
expect(getByText("Alternative")).toBeDefined();
});
});
describe("withSpinnerOnSending", () => {
it("should render First component", () => {
const FirstWithConditional = withSpinnerOnSending(First);
const { getByText } = render(<FirstWithConditional apiState={API_STATE.SUCCESS} />);
expect(getByText("First")).toBeDefined();
});
it("should render spinner", () => {
const FirstWithConditional = withSpinnerOnSending(First);
const { container } = render(<FirstWithConditional apiState={API_STATE.SENDING} />);
expect(container).toMatchSnapshot();
});
});
describe("withError", () => {
it("should render First component", () => {
const withErrorHidden = withError(() => false);
const FirstWithConditional = withErrorHidden(First);
const { getByText } = render(<FirstWithConditional />);
expect(getByText("First")).toBeDefined();
});
it("should render error message", () => {
const withErrorVisible = withError(() => true);
const FirstWithConditional = withErrorVisible(First);
const { container } = render(<FirstWithConditional />);
expect(container).toMatchSnapshot();
});
});
describe("withErrorMessage", () => {
it("should render First component", () => {
const FirstWithConditional = withErrorMessage(First);
const { getByText } = render(<FirstWithConditional apiState={API_STATE.SUCCESS} />);
expect(getByText("First")).toBeDefined();
});
it("should render error message", () => {
const FirstWithConditional = withErrorMessage(First);
const { container } = render(<FirstWithConditional apiState={API_STATE.ERROR} />);
expect(container).toMatchSnapshot();
});
});
});

View File

@ -0,0 +1,52 @@
/*
* 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 { Spinner } from "bootstrap/Spinner";
import { API_STATE } from "api/utils";
import { ErrorMessage } from "./ErrorMessage";
function withEither(conditionalFn, Either) {
return (Component) => (props) => {
if (conditionalFn(props)) {
return <Either {...props} />;
}
return <Component {...props} />;
};
}
// Loading
function isSending(props) {
if (Array.isArray(props.apiState)) {
return props.apiState.some(
(state) => [API_STATE.INIT, API_STATE.SENDING].includes(state),
);
}
return [API_STATE.INIT, API_STATE.SENDING].includes(props.apiState);
}
const withSpinner = (conditionalFn) => withEither(conditionalFn, Spinner);
const withSending = (Either) => withEither(isSending, Either);
const withSpinnerOnSending = withSpinner(isSending);
// Error handling
const withError = (conditionalFn) => withEither(conditionalFn, ErrorMessage);
const withErrorMessage = withError(
(props) => {
if (Array.isArray(props.apiState)) {
return props.apiState.includes(API_STATE.ERROR);
}
return props.apiState === API_STATE.ERROR;
},
);
export {
withEither, withSpinner, withSending, withSpinnerOnSending, withError, withErrorMessage,
};

20
src/utils/hooks.js Normal file
View File

@ -0,0 +1,20 @@
/*
* 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 { useState, useEffect } from "react";
/** Execute callback when condition is set to true. */
export function useConditionalTimeout({ callback, timeout = 125 }, ...callbackArgs) {
const [condition, setCondition] = useState(false);
useEffect(() => {
if (condition) {
const interval = setTimeout(() => callback(...callbackArgs), timeout);
return () => setTimeout(interval);
}
}, [condition, callback, timeout, callbackArgs]);
return setCondition;
}

View 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.
*/
/** Return undefined if object has no keys, otherwise return object. */
export function undefinedIfEmpty(instance) {
if (Object.keys(instance).length > 0) {
return instance;
}
return undefined;
}
/** Return object without keys that have undefined value. */
export function withoutUndefinedKeys(instance) {
return Object.keys(instance).reduce(
(accumulator, key) => {
if (instance[key] !== undefined) {
accumulator[key] = instance[key];
}
return accumulator;
},
{},
);
}
/** Return copy of passed object that has only desired keys. */
export function onlySpecifiedKeys(object, desiredKeys) {
return desiredKeys.reduce(
(accumulator, key) => { accumulator[key] = object[key]; return accumulator; },
{},
);
}

View File

@ -29,7 +29,6 @@ const REs = {
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];
};

60
styleguide.config.js Normal file
View File

@ -0,0 +1,60 @@
/*
* 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 = {
title: "Foris JS docs",
sections: [
{
name: "Foris JS",
content: "docs/intro.md",
},
{
name: "Foris forms",
components: [
"src/form/components/ForisForm.js",
"src/form/components/alerts.js",
"src/form/components/SubmitButton.js",
],
exampleMode: "expand",
usageMode: "expand",
},
{
name: "Bootstrap components",
description: "Set of bootstrap components.",
components: "src/bootstrap/*.js",
exampleMode: "expand",
usageMode: "expand",
ignore: [
"src/bootstrap/constants.js",
],
},
],
require: [
"babel-polyfill",
path.join(__dirname, "node_modules/bootstrap/dist/css/bootstrap.min.css"),
path.join(__dirname, "node_modules/@fortawesome/fontawesome-free/css/all.min.css"),
],
webpackConfig: {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
}, {
test: /\.css$/,
use: ["style-loader", "css-loader"],
}, {
test: /\.(jpg|jpeg|png|woff|woff2|eot|ttf|svg)$/,
loader: "file-loader",
},
],
},
},
};

View File

@ -1,4 +1,4 @@
# Translations template for PROJECT.
# Czech translations for PROJECT.
# Copyright (C) 2019 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2019.
@ -7,17 +7,16 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 12:55+0000\n"
"Last-Translator: Stepan Henek <stepan+github@henek.name>\n"
"Language-Team: Czech <https://hosted.weblate.org/projects/turris/foris-js/cs/"
">\n"
"Language: cs\n"
"Language-Team: Czech <https://hosted.weblate.org/projects/turris/foris-"
"js/cs/>\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
"X-Generator: Weblate 3.9-dev\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
@ -48,7 +47,31 @@ msgstr "Tohle není platná MAC adresa."
msgid "Doesn't contain a list of emails separated by commas."
msgstr "Neobsahuje seznam e-mailů oddělených čárkou."
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
"Změny, které byly provedeny, nebyly uloženy. Jste si jistý, že chcete "
@ -66,10 +89,13 @@ msgstr "Načítám nastavení"
msgid "Save"
msgstr "Uložit"
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
msgstr "Nastavení bylo úspěšně uloženo."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr "Nastavení bylo úspěšně uloženo."
#~ msgid "Settings update was failed."
#~ msgstr "Ukládání nastavení selhalo."
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr "Ukládání nastavení selhalo."

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:54+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: da\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:54+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:54+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: el\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:21+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:54+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: fi\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:54+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: fo\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -45,7 +45,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -61,11 +85,7 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:54+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: fr\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:55+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: hr\n"
@ -17,7 +17,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -47,7 +47,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -63,11 +87,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:55+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: hu\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:55+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: it\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:55+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: ja\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:55+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: ko\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:55+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: lt\n"
@ -17,7 +17,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -47,7 +47,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -63,11 +87,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:56+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: nb\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:55+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: nb_NO\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:56+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: nl\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:56+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: pl\n"
@ -17,7 +17,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -47,7 +47,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -63,11 +87,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:56+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: ro\n"
@ -17,7 +17,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -47,7 +47,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -63,11 +87,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:56+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: ru\n"
@ -17,7 +17,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -47,7 +47,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -63,11 +87,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:56+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: sk\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-08-28 14:34+0200\n"
"POT-Creation-Date: 2019-11-14 11:13+0100\n"
"PO-Revision-Date: 2019-08-28 17:56+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: sv\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
"Generated-By: Babel 2.7.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
@ -46,7 +46,31 @@ msgstr ""
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/form/components/ForisForm.js:123
#: src/api/utils.js:58
msgid "The session is expired. Please log in again."
msgstr ""
#: src/api/utils.js:63
msgid "Timeout error occurred."
msgstr ""
#: src/api/utils.js:66
msgid "No response received."
msgstr ""
#: src/api/utils.js:70
msgid "An unknown error occurred. Check the console for more info."
msgstr ""
#: src/api/utils.js:77
msgid "An unknown API error occurred."
msgstr ""
#: src/form/components/ForisForm.js:88
msgid "Settings saved successfully"
msgstr ""
#: src/form/components/ForisForm.js:140
msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr ""
@ -62,11 +86,13 @@ msgstr ""
msgid "Save"
msgstr ""
#: src/form/components/alerts.js:25
msgid "Settings were successfully saved."
#: src/utils/ErrorMessage.js:13
msgid "An error occurred while fetching data."
msgstr ""
#: src/form/components/alerts.js:41
msgid "Settings update was failed."
msgstr ""
#~ msgid "Settings were successfully saved."
#~ msgstr ""
#~ msgid "Settings update was failed."
#~ msgstr ""