1
0
mirror of https://gitlab.nic.cz/turris/reforis/foris-js.git synced 2024-12-25 00:11:36 +01:00

Client configuration

This commit is contained in:
Maciej Lenartowicz 2019-10-10 15:25:00 +00:00
parent 30748fab12
commit e3a795e40d
14 changed files with 170 additions and 120 deletions

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "foris", "name": "foris",
"version": "1.0.0", "version": "1.1.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "foris", "name": "foris",
"version": "1.0.0", "version": "1.1.0",
"description": "Set of components and utils for Foris and its plugins.", "description": "Set of components and utils for Foris and its plugins.",
"author": "CZ.NIC, z.s.p.o.", "author": "CZ.NIC, z.s.p.o.",
"repository": { "repository": {

View File

@ -12,7 +12,7 @@ import {
API_ACTIONS, TIMEOUT, HEADERS, APIReducer, getErrorMessage, API_ACTIONS, TIMEOUT, HEADERS, APIReducer, getErrorMessage,
} from "./utils"; } from "./utils";
export function useAPIPost(url) { export function useAPIPost(url, contentType) {
const [state, dispatch] = useReducer(APIReducer, { const [state, dispatch] = useReducer(APIReducer, {
isSending: false, isSending: false,
isError: false, isError: false,
@ -20,12 +20,17 @@ export function useAPIPost(url) {
data: null, data: null,
}); });
const headers = { ...HEADERS };
if (contentType) {
headers["Content-Type"] = contentType;
}
const post = async (data) => { const post = async (data) => {
dispatch({ type: API_ACTIONS.INIT }); dispatch({ type: API_ACTIONS.INIT });
try { try {
const result = await axios.post(url, data, { const result = await axios.post(url, data, {
timeout: TIMEOUT, timeout: TIMEOUT,
headers: HEADERS, headers,
}); });
dispatch({ type: API_ACTIONS.SUCCESS, payload: result.data }); dispatch({ type: API_ACTIONS.SUCCESS, payload: result.data });
} catch (error) { } catch (error) {

View File

@ -7,7 +7,7 @@
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useUID } from "react-uid/dist/es5/index"; import { useUID } from "react-uid";
import { formFieldsSize } from "./constants"; import { formFieldsSize } from "./constants";

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

View File

@ -7,7 +7,7 @@
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useUID } from "react-uid/dist/es5/index"; import { useUID } from "react-uid";
import { formFieldsSize } from "./constants"; import { formFieldsSize } from "./constants";

View File

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

View File

@ -2,10 +2,7 @@
exports[`<NumberInput/> Render number input 1`] = ` exports[`<NumberInput/> Render number input 1`] = `
<div <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 <label
for="1" for="1"
@ -50,5 +47,4 @@ exports[`<NumberInput/> Render number input 1`] = `
Some help text Some help text
</small> </small>
</div> </div>
</div>
`; `;

View File

@ -2,10 +2,7 @@
exports[`<PasswordInput/> Render password input 1`] = ` exports[`<PasswordInput/> Render password input 1`] = `
<div <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 <label
for="1" for="1"
@ -29,5 +26,4 @@ exports[`<PasswordInput/> Render password input 1`] = `
Some help text Some help text
</small> </small>
</div> </div>
</div>
`; `;

View File

@ -2,10 +2,7 @@
exports[`<TextInput/> Render text input 1`] = ` exports[`<TextInput/> Render text input 1`] = `
<div <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 <label
for="1" for="1"
@ -28,5 +25,4 @@ exports[`<TextInput/> Render text input 1`] = `
Some help text Some help text
</small> </small>
</div> </div>
</div>
`; `;

View File

@ -17,7 +17,7 @@ const FORM_ACTIONS = {
resetData: 2, resetData: 2,
}; };
export function useForm(validator, prepData) { export function useForm(validator, dataPreprocessor) {
const [state, dispatch] = useReducer(formReducer, { const [state, dispatch] = useReducer(formReducer, {
data: null, data: null,
initialData: null, initialData: null,
@ -28,10 +28,10 @@ export function useForm(validator, prepData) {
dispatch({ dispatch({
type: FORM_ACTIONS.resetData, type: FORM_ACTIONS.resetData,
data, data,
prepData, dataPreprocessor,
validator, validator,
}); });
}, [prepData, validator]); }, [dataPreprocessor, validator]);
const onFormChangeHandler = useCallback((updateRule) => (event) => { const onFormChangeHandler = useCallback((updateRule) => (event) => {
dispatch({ dispatch({
@ -41,6 +41,7 @@ export function useForm(validator, prepData) {
validator, validator,
}); });
}, [validator]); }, [validator]);
return [ return [
state, state,
onFormChangeHandler, onFormChangeHandler,
@ -61,12 +62,15 @@ function formReducer(state, action) {
}; };
} }
case FORM_ACTIONS.resetData: { case FORM_ACTIONS.resetData: {
if (!action.data) return { ...state, initialData: state.data }; if (!action.data) {
const prepData = action.prepData ? action.prepData(action.data) : action.data; return { ...state, initialData: state.data };
}
const data = action.dataPreprocessor ? action.dataPreprocessor(action.data) : action.data;
return { return {
data: prepData, data,
initialData: prepData, initialData: data,
errors: action.data ? action.validator(prepData) : undefined, errors: action.data ? action.validator(data) : undefined,
}; };
} }
default: { default: {
@ -82,6 +86,9 @@ function getChangedValue(target) {
} else if (target.type === "number") { } else if (target.type === "number") {
const parsedValue = parseInt(value); const parsedValue = parseInt(value);
value = Number.isNaN(parsedValue) ? value : parsedValue; 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; return value;
} }

View File

@ -15,15 +15,16 @@ export { useAPIPatch } from "api/patch";
export { Alert } from "bootstrap/Alert"; export { Alert } from "bootstrap/Alert";
export { Button } from "bootstrap/Button"; export { Button } from "bootstrap/Button";
export { CheckBox } from "bootstrap/CheckBox"; export { CheckBox } from "bootstrap/CheckBox";
export { formFieldsSize } from "bootstrap/constants";
export { DataTimeInput } from "bootstrap/DataTimeInput"; export { DataTimeInput } from "bootstrap/DataTimeInput";
export { EmailInput } from "bootstrap/EmailInput"; export { EmailInput } from "bootstrap/EmailInput";
export { FileInput } from "bootstrap/FileInput";
export { Input } from "bootstrap/Input"; export { Input } from "bootstrap/Input";
export { NumberInput } from "bootstrap/NumberInput"; export { NumberInput } from "bootstrap/NumberInput";
export { PasswordInput } from "bootstrap/PasswordInput"; export { PasswordInput } from "bootstrap/PasswordInput";
export { RadioSet } from "bootstrap/RadioSet"; export { RadioSet } from "bootstrap/RadioSet";
export { Select } from "bootstrap/Select"; export { Select } from "bootstrap/Select";
export { TextInput } from "bootstrap/TextInput"; export { TextInput } from "bootstrap/TextInput";
export { formFieldsSize } from "bootstrap/constants";
export { export {
Spinner, Spinner,