mirror of
https://gitlab.nic.cz/turris/reforis/foris-js.git
synced 2025-06-15 13:36:35 +02:00
Fix lint.
This commit is contained in:
@ -5,27 +5,31 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
Alert.propTypes = {
|
||||
/** Type of the alert it adds as `alert-${type}` class.*/
|
||||
/** Type of the alert it adds as `alert-${type}` class. */
|
||||
type: PropTypes.string.isRequired,
|
||||
/** Alert message.*/
|
||||
/** Alert message. */
|
||||
message: PropTypes.string,
|
||||
/** Alert content.*/
|
||||
/** Alert content. */
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node
|
||||
PropTypes.node,
|
||||
]),
|
||||
/** onDismiss handler.*/
|
||||
onDismiss: PropTypes.func
|
||||
/** onDismiss handler. */
|
||||
onDismiss: PropTypes.func,
|
||||
};
|
||||
|
||||
export function Alert({type, message, onDismiss, children}) {
|
||||
return <div className={`alert alert-dismissible alert-${type}`}>
|
||||
{onDismiss ? <button type="button" className="close" onClick={onDismiss}>×</button> : false}
|
||||
{message}
|
||||
{children}
|
||||
</div>
|
||||
export default function Alert({
|
||||
type, message, onDismiss, children,
|
||||
}) {
|
||||
return (
|
||||
<div className={`alert alert-dismissible alert-${type}`}>
|
||||
{onDismiss ? <button type="button" className="close" onClick={onDismiss}>×</button> : false}
|
||||
{message}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -5,13 +5,13 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const OFFSET = 8;
|
||||
const SIZE = 3;
|
||||
const SIZE_CLASS = ' offset-lg-' + OFFSET + ' col-lg-' + SIZE;
|
||||
const SIZE_CLASS_SM = ' col-sm-12';
|
||||
const SIZE_CLASS = ` offset-lg-${OFFSET} col-lg-${SIZE}`;
|
||||
const SIZE_CLASS_SM = " col-sm-12";
|
||||
|
||||
Button.propTypes = {
|
||||
/** Additional class name. */
|
||||
@ -26,18 +26,25 @@ Button.propTypes = {
|
||||
PropTypes.element,
|
||||
PropTypes.node,
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
]).isRequired
|
||||
]).isRequired,
|
||||
};
|
||||
|
||||
export function Button({className, loading, forisFormSize, children, ...props}) {
|
||||
className = className ? 'btn ' + className : 'btn btn-primary ';
|
||||
if (forisFormSize)
|
||||
className += SIZE_CLASS + SIZE_CLASS_SM;
|
||||
export default function Button({
|
||||
className, loading, forisFormSize, children, ...props
|
||||
}) {
|
||||
className = className ? `btn ${className}` : "btn btn-primary ";
|
||||
if (forisFormSize) className += SIZE_CLASS + SIZE_CLASS_SM;
|
||||
|
||||
const span = loading ?
|
||||
<span className='spinner-border spinner-border-sm' role='status' aria-hidden='true'/> : null;
|
||||
const span = loading
|
||||
? <span className="spinner-border spinner-border-sm" role="status" aria-hidden="true" /> : null;
|
||||
|
||||
return <button className={className} {...props}>
|
||||
{span} {span ? ' ' : null} {children}
|
||||
</button>;
|
||||
return (
|
||||
<button type="button" className={className} {...props}>
|
||||
{span}
|
||||
{" "}
|
||||
{span ? " " : null}
|
||||
{" "}
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { useUID } from "react-uid";
|
||||
import { useUID } from "react-uid/dist/es5/index";
|
||||
|
||||
import { formFieldsSize } from "./constants";
|
||||
|
||||
@ -27,7 +27,7 @@ CheckBox.defaultProps = {
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
export function CheckBox({
|
||||
export default function CheckBox({
|
||||
label, helpText, useDefaultSize, disabled, ...props
|
||||
}) {
|
||||
const uid = useUID();
|
||||
|
@ -5,53 +5,59 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Datetime from 'react-datetime/DateTime';
|
||||
import moment from 'moment/moment';
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Datetime from "react-datetime/DateTime";
|
||||
import moment from "moment/moment";
|
||||
|
||||
import {Input} from './Input';
|
||||
import Input from "./Input";
|
||||
|
||||
DataTimeInput.propTypes = {
|
||||
/** Field label. */
|
||||
label: PropTypes.string.isRequired,
|
||||
/** Error message. */
|
||||
error: PropTypes.string,
|
||||
/** DataTime or Data or Time value. Can be `moment` or string.*/
|
||||
/** DataTime or Data or Time value. Can be `moment` or string. */
|
||||
value: PropTypes.oneOfType([PropTypes.objectOf(moment), PropTypes.string]),
|
||||
/** Help text message. */
|
||||
helpText: PropTypes.string,
|
||||
/** Content. */
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node
|
||||
PropTypes.node,
|
||||
]),
|
||||
isValidDate: PropTypes.bool,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
dateFormat: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
|
||||
timeFormat: PropTypes.string
|
||||
timeFormat: PropTypes.string,
|
||||
};
|
||||
|
||||
const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD';
|
||||
const DEFAULT_TIME_FORMAT = 'HH:mm:ss';
|
||||
const DEFAULT_DATE_FORMAT = "YYYY-MM-DD";
|
||||
const DEFAULT_TIME_FORMAT = "HH:mm:ss";
|
||||
|
||||
export function DataTimeInput({value, onChange, isValidDate, dateFormat, timeFormat, children, ...props}) {
|
||||
export default function DataTimeInput({
|
||||
value, onChange, isValidDate, dateFormat, timeFormat, children, ...props
|
||||
}) {
|
||||
function renderInput(datetimeProps) {
|
||||
return <Input
|
||||
{...props}
|
||||
{...datetimeProps}
|
||||
>
|
||||
{children}
|
||||
</Input>
|
||||
return (
|
||||
<Input
|
||||
{...props}
|
||||
{...datetimeProps}
|
||||
>
|
||||
{children}
|
||||
</Input>
|
||||
);
|
||||
}
|
||||
|
||||
return <Datetime
|
||||
locale={ForisTranslations.locale}
|
||||
dateFormat={dateFormat !== undefined ? dateFormat : DEFAULT_DATE_FORMAT}
|
||||
timeFormat={timeFormat !== undefined ? timeFormat : DEFAULT_TIME_FORMAT}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
isValidDate={isValidDate}
|
||||
renderInput={renderInput}
|
||||
/>;
|
||||
return (
|
||||
<Datetime
|
||||
locale={ForisTranslations.locale}
|
||||
dateFormat={dateFormat !== undefined ? dateFormat : DEFAULT_DATE_FORMAT}
|
||||
timeFormat={timeFormat !== undefined ? timeFormat : DEFAULT_TIME_FORMAT}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
isValidDate={isValidDate}
|
||||
renderInput={renderInput}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -5,12 +5,13 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
import {Input} from './Input';
|
||||
import PropTypes from 'prop-types';
|
||||
import PropTypes from "prop-types";
|
||||
import Input from "./Input";
|
||||
|
||||
const EmailInput = ({ ...props }) => <Input type="email" {...props} />;
|
||||
|
||||
export const EmailInput = ({...props}) => <Input type="email" {...props}/>;
|
||||
|
||||
EmailInput.propTypes = {
|
||||
/** Field label. */
|
||||
@ -22,3 +23,5 @@ EmailInput.propTypes = {
|
||||
/** Email value. */
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
export default EmailInput;
|
||||
|
@ -5,10 +5,10 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {useUID} from 'react-uid';
|
||||
import {formFieldsSize} from './constants';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from "react";
|
||||
import { useUID } from "react-uid/dist/es5/index";
|
||||
import PropTypes from "prop-types";
|
||||
import { formFieldsSize } from "./constants";
|
||||
|
||||
Input.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
@ -18,29 +18,33 @@ Input.propTypes = {
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node
|
||||
])
|
||||
}
|
||||
PropTypes.node,
|
||||
]),
|
||||
};
|
||||
|
||||
/** Base bootstrap input component. */
|
||||
export function Input({type, label, helpText, error, className, children, ...props}) {
|
||||
export default function Input({
|
||||
type, label, helpText, error, className, children, ...props
|
||||
}) {
|
||||
const uid = useUID();
|
||||
const inputClassName = `form-control ${className ? className : ''} ${(error ? 'is-invalid' : '')}`.trim();
|
||||
return <div className={formFieldsSize}>
|
||||
<div className='form-group'>
|
||||
<label htmlFor={uid}>{label}</label>
|
||||
<div className='input-group'>
|
||||
<input
|
||||
className={inputClassName}
|
||||
type={type}
|
||||
id={uid}
|
||||
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}
|
||||
|
||||
{...props}
|
||||
/>
|
||||
{children}
|
||||
{...props}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
{error ? <div className="invalid-feedback">{error}</div> : null}
|
||||
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
|
||||
</div>
|
||||
{error ? <div className='invalid-feedback'>{error}</div> : null}
|
||||
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
}
|
||||
|
@ -5,10 +5,9 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React, {useEffect, useRef} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {Portal} from 'utils/Portal';
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Portal from "utils/Portal";
|
||||
|
||||
Modal.propTypes = {
|
||||
/** Is modal shown value */
|
||||
@ -19,17 +18,16 @@ Modal.propTypes = {
|
||||
/** Modal content use following: `ModalHeader`, `ModalBody`, `ModalFooter` */
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node
|
||||
]).isRequired
|
||||
PropTypes.node,
|
||||
]).isRequired,
|
||||
};
|
||||
|
||||
export function Modal({shown, setShown, children}) {
|
||||
export function Modal({ shown, setShown, children }) {
|
||||
const dialogRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
function handleClickOutsideDialog(e) {
|
||||
if (!dialogRef.current.contains(e.target))
|
||||
setShown(false);
|
||||
if (!dialogRef.current.contains(e.target)) setShown(false);
|
||||
}
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutsideDialog);
|
||||
@ -39,15 +37,17 @@ 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 className="modal-content">
|
||||
{children}
|
||||
return (
|
||||
<Portal containerId="modal-container">
|
||||
<div className={`modal fade ${shown ? "show" : ""}`} role="dialog">
|
||||
<div ref={dialogRef} className="modal-dialog" role="document">
|
||||
<div className="modal-content">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Portal>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
|
||||
ModalHeader.propTypes = {
|
||||
@ -55,35 +55,39 @@ ModalHeader.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export function ModalHeader({setShown, title}) {
|
||||
return <div className="modal-header">
|
||||
<h5 className="modal-title">{title}</h5>
|
||||
<button type="button" className="close" onClick={() => setShown(false)}>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
export function ModalHeader({ setShown, title }) {
|
||||
return (
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">{title}</h5>
|
||||
<button type="button" className="close" onClick={() => setShown(false)}>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ModalBody.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node
|
||||
]).isRequired
|
||||
PropTypes.node,
|
||||
]).isRequired,
|
||||
};
|
||||
|
||||
export function ModalBody({children}) {
|
||||
return <div className="modal-body">{children}</div>
|
||||
export function ModalBody({ children }) {
|
||||
return <div className="modal-body">{children}</div>;
|
||||
}
|
||||
|
||||
ModalFooter.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node
|
||||
]).isRequired
|
||||
PropTypes.node,
|
||||
]).isRequired,
|
||||
};
|
||||
|
||||
export function ModalFooter({children}) {
|
||||
return <div className="modal-footer">
|
||||
{children}
|
||||
</div>
|
||||
export function ModalFooter({ children }) {
|
||||
return (
|
||||
<div className="modal-footer">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -5,12 +5,12 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
import {Input} from './Input';
|
||||
import PropTypes from 'prop-types';
|
||||
import PropTypes from "prop-types";
|
||||
import Input from "./Input";
|
||||
|
||||
export const NumberInput = ({...props}) => <Input type="number" {...props}/>;
|
||||
const NumberInput = ({ ...props }) => <Input type="number" {...props} />;
|
||||
|
||||
NumberInput.propTypes = {
|
||||
/** Field label. */
|
||||
@ -25,3 +25,5 @@ NumberInput.propTypes = {
|
||||
PropTypes.number,
|
||||
]),
|
||||
};
|
||||
|
||||
export default NumberInput;
|
||||
|
@ -5,10 +5,10 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React, {useState} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import {Input} from './Input';
|
||||
import Input from "./Input";
|
||||
|
||||
PasswordInput.propTypes = {
|
||||
/** Field label. */
|
||||
@ -23,22 +23,30 @@ PasswordInput.propTypes = {
|
||||
withEye: PropTypes.bool,
|
||||
};
|
||||
|
||||
export function PasswordInput({withEye, ...props}) {
|
||||
export default function PasswordInput({ withEye, ...props }) {
|
||||
const [isHidden, setHidden] = useState(true);
|
||||
return <Input
|
||||
type={withEye ? isHidden ? 'password' : 'text' : 'password'}
|
||||
autoComplete={isHidden ? 'new-password' : null}
|
||||
{...props}
|
||||
>
|
||||
{withEye ?
|
||||
<div className="input-group-append">
|
||||
<button className="input-group-text" onClick={e => {
|
||||
e.preventDefault();
|
||||
setHidden(isHidden => !isHidden);
|
||||
}}>
|
||||
<i className={'fa ' + (isHidden ? 'fa-eye' : 'fa-eye-slash')}/>
|
||||
</button>
|
||||
</div>
|
||||
: null}
|
||||
</Input>
|
||||
return (
|
||||
<Input
|
||||
type={withEye && !isHidden ? "text" : "password"}
|
||||
autoComplete={isHidden ? "new-password" : null}
|
||||
{...props}
|
||||
>
|
||||
{withEye
|
||||
? (
|
||||
<div className="input-group-append">
|
||||
<button
|
||||
type="button"
|
||||
className="input-group-text"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setHidden((shouldBeHidden) => !shouldBeHidden);
|
||||
}}
|
||||
>
|
||||
<i className={`fa ${isHidden ? "fa-eye" : "fa-eye-slash"}`} />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
: null}
|
||||
</Input>
|
||||
);
|
||||
}
|
||||
|
@ -5,76 +5,89 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {useUID} from 'react-uid';
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { useUID } from "react-uid/dist/es5/index";
|
||||
|
||||
import {formFieldsSize} from './constants';
|
||||
import { formFieldsSize } from "./constants";
|
||||
|
||||
|
||||
RadioSet.propTypes = {
|
||||
/** Name attribute of the input HTML tag.*/
|
||||
/** Name attribute of the input HTML tag. */
|
||||
name: PropTypes.string.isRequired,
|
||||
/** RadioSet label .*/
|
||||
/** RadioSet label . */
|
||||
label: PropTypes.string,
|
||||
/** Choices .*/
|
||||
/** Choices . */
|
||||
choices: PropTypes.arrayOf(PropTypes.shape({
|
||||
/** Choice lable .*/
|
||||
/** Choice lable . */
|
||||
label: PropTypes.string.isRequired,
|
||||
/** Choice value .*/
|
||||
/** Choice value . */
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||
})).isRequired,
|
||||
/** Initial value .*/
|
||||
/** Initial value . */
|
||||
value: PropTypes.string,
|
||||
/** Help text message .*/
|
||||
/** Help text message . */
|
||||
helpText: PropTypes.string,
|
||||
};
|
||||
|
||||
export function RadioSet({name, label, choices, value, helpText, ...props}) {
|
||||
export default function RadioSet({
|
||||
name, label, choices, value, helpText, ...props
|
||||
}) {
|
||||
const uid = useUID();
|
||||
const radios = choices.map((choice, key) =>
|
||||
<Radio
|
||||
id={`${name}-${key}`}
|
||||
key={key}
|
||||
name={name}
|
||||
label={choice.label}
|
||||
value={choice.value}
|
||||
helpText={choice.helpText}
|
||||
checked={choice.value === value}
|
||||
const radios = choices.map((choice, key) => {
|
||||
const id = `${name}-${key}`;
|
||||
return (
|
||||
<Radio
|
||||
id={id}
|
||||
key={id}
|
||||
name={name}
|
||||
label={choice.label}
|
||||
value={choice.value}
|
||||
helpText={choice.helpText}
|
||||
checked={choice.value === value}
|
||||
|
||||
{...props}
|
||||
/>
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={`form-group ${formFieldsSize}`} style={{ marginBottom: "1rem" }}>
|
||||
{label
|
||||
? (
|
||||
<label className="col-12" htmlFor={uid} style={{ paddingLeft: "0" }}>
|
||||
{label}
|
||||
</label>
|
||||
)
|
||||
: null}
|
||||
{radios}
|
||||
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
return <div className={`form-group ${formFieldsSize}`} style={{marginBottom: '1rem'}}>
|
||||
{label ?
|
||||
<label className='col-12' htmlFor={uid} style={{paddingLeft: '0'}}>
|
||||
{label}
|
||||
</label>
|
||||
: null}
|
||||
{radios}
|
||||
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
|
||||
</div>;
|
||||
}
|
||||
|
||||
Radio.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
helpText: PropTypes.string
|
||||
helpText: PropTypes.string,
|
||||
};
|
||||
|
||||
function Radio({label, id, helpText, ...props}) {
|
||||
return <>
|
||||
<div className='custom-control custom-radio custom-control-inline'>
|
||||
<input
|
||||
id={id}
|
||||
className='custom-control-input'
|
||||
type='radio'
|
||||
function Radio({
|
||||
label, id, helpText, ...props
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<div className="custom-control custom-radio custom-control-inline">
|
||||
<input
|
||||
id={id}
|
||||
className="custom-control-input"
|
||||
type="radio"
|
||||
|
||||
{...props}
|
||||
/>
|
||||
<label className='custom-control-label' htmlFor={id}>{label}</label>
|
||||
</div>
|
||||
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
|
||||
</>
|
||||
{...props}
|
||||
/>
|
||||
<label className="custom-control-label" htmlFor={id}>{label}</label>
|
||||
</div>
|
||||
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -5,15 +5,15 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {useUID} from 'react-uid';
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { useUID } from "react-uid/dist/es5/index";
|
||||
|
||||
|
||||
Select.propTypes = {
|
||||
/** Select field Label. */
|
||||
label: PropTypes.string.isRequired,
|
||||
/** Choices if form of {value : "Label",...}.*/
|
||||
/** Choices if form of {value : "Label",...}. */
|
||||
choices: PropTypes.object.isRequired,
|
||||
/** Current value. */
|
||||
value: PropTypes.oneOfType([
|
||||
@ -24,22 +24,26 @@ Select.propTypes = {
|
||||
helpText: PropTypes.string,
|
||||
};
|
||||
|
||||
export function Select({label, choices, helpText, ...props}) {
|
||||
export default function Select({
|
||||
label, choices, helpText, ...props
|
||||
}) {
|
||||
const uid = useUID();
|
||||
|
||||
const options = Object.keys(choices).map(
|
||||
key => <option key={key} value={key}>{choices[key]}</option>
|
||||
(key) => <option key={key} value={key}>{choices[key]}</option>,
|
||||
);
|
||||
|
||||
return <div className='form-group col-sm-12 offset-lg-1 col-lg-10'>
|
||||
<label htmlFor={uid}>{label}</label>
|
||||
<select
|
||||
className='custom-select'
|
||||
id={uid}
|
||||
{...props}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
|
||||
</div>;
|
||||
return (
|
||||
<div className="form-group col-sm-12 offset-lg-1 col-lg-10">
|
||||
<label htmlFor={uid}>{label}</label>
|
||||
<select
|
||||
className="custom-select"
|
||||
id={uid}
|
||||
{...props}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -5,13 +5,13 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import {Input} from './Input';
|
||||
import Input from "./Input";
|
||||
|
||||
|
||||
export const TextInput = ({...props}) => <Input type="text" {...props}/>;
|
||||
const TextInput = ({ ...props }) => <Input type="text" {...props} />;
|
||||
|
||||
|
||||
TextInput.propTypes = {
|
||||
@ -22,3 +22,5 @@ TextInput.propTypes = {
|
||||
/** Help text message. */
|
||||
helpText: PropTypes.string,
|
||||
};
|
||||
|
||||
export default TextInput;
|
||||
|
@ -9,7 +9,7 @@ import React from 'react';
|
||||
|
||||
import {render} from 'customTestRender';
|
||||
|
||||
import {Button} from '../Button'
|
||||
import Button from '../Button'
|
||||
|
||||
describe('<Button />', () => {
|
||||
it('Render button correctly', () => {
|
||||
|
@ -9,7 +9,7 @@ import React from 'react';
|
||||
|
||||
import {render} from 'customTestRender';
|
||||
|
||||
import {CheckBox} from '../Checkbox'
|
||||
import CheckBox from '../Checkbox'
|
||||
|
||||
describe('<Checkbox/>', () => {
|
||||
it('Render checkbox', () => {
|
||||
|
@ -9,7 +9,7 @@ import React from 'react';
|
||||
|
||||
import {render} from 'customTestRender';
|
||||
|
||||
import {NumberInput} from '../NumberInput';
|
||||
import NumberInput from '../NumberInput';
|
||||
|
||||
|
||||
describe('<NumberInput/>', () => {
|
||||
|
@ -9,7 +9,7 @@ import React from 'react';
|
||||
|
||||
import {render} from 'customTestRender';
|
||||
|
||||
import {PasswordInput} from '../PasswordInput';
|
||||
import PasswordInput from '../PasswordInput';
|
||||
|
||||
describe('<PasswordInput/>', () => {
|
||||
it('Render password input', () => {
|
||||
|
@ -9,7 +9,7 @@ import React from 'react';
|
||||
|
||||
import {render} from 'customTestRender';
|
||||
|
||||
import {RadioSet} from '../RadioSet';
|
||||
import RadioSet from '../RadioSet';
|
||||
|
||||
const TEST_CHOICES = [
|
||||
{label: 'label', value: 'value'},
|
||||
|
@ -9,7 +9,7 @@ import React from 'react';
|
||||
|
||||
import {fireEvent, getByDisplayValue, getByText, render} from 'customTestRender';
|
||||
|
||||
import {Select} from '../Select';
|
||||
import Select from '../Select';
|
||||
|
||||
const TEST_CHOICES = {
|
||||
'1': 'one',
|
||||
|
@ -9,7 +9,7 @@ import React from 'react';
|
||||
|
||||
import {render} from 'customTestRender';
|
||||
|
||||
import {TextInput} from '../TextInput';
|
||||
import TextInput from '../TextInput';
|
||||
|
||||
describe('<TextInput/>', () => {
|
||||
it('Render text input', () => {
|
||||
|
@ -3,6 +3,7 @@
|
||||
exports[`<Button /> Render button correctly 1`] = `
|
||||
<button
|
||||
class="btn btn-primary "
|
||||
type="button"
|
||||
>
|
||||
|
||||
|
||||
@ -13,6 +14,7 @@ exports[`<Button /> Render button correctly 1`] = `
|
||||
exports[`<Button /> Render button with custom classes 1`] = `
|
||||
<button
|
||||
class="btn one two three"
|
||||
type="button"
|
||||
>
|
||||
|
||||
|
||||
@ -23,6 +25,7 @@ exports[`<Button /> Render button with custom classes 1`] = `
|
||||
exports[`<Button /> Render button with spinner 1`] = `
|
||||
<button
|
||||
class="btn btn-primary "
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
|
@ -6,4 +6,5 @@
|
||||
*/
|
||||
|
||||
/** Bootstrap column size for form fields */
|
||||
export const formFieldsSize = 'col-sm-12 offset-lg-1 col-lg-10';
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const formFieldsSize = "col-sm-12 offset-lg-1 col-lg-10";
|
||||
|
@ -5,22 +5,25 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {render} from 'customTestRender';
|
||||
import React from "react";
|
||||
import { render } from "customTestRender";
|
||||
|
||||
import {STATES, SubmitButton} from '../components/SubmitButton';
|
||||
import SubmitButton, { STATES } from "../components/SubmitButton";
|
||||
|
||||
describe('<SubmitButton/>', () => {
|
||||
it('Render ready', () => {
|
||||
const {container} = render(<SubmitButton state={STATES.READY}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
describe("<SubmitButton/>", () => {
|
||||
it("Render ready", () => {
|
||||
const { container } = render(<SubmitButton state={STATES.READY}/>);
|
||||
expect(container)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
it('Render saving', () => {
|
||||
const {container} = render(<SubmitButton state={STATES.SAVING}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
it("Render saving", () => {
|
||||
const { container } = render(<SubmitButton state={STATES.SAVING}/>);
|
||||
expect(container)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
it('Render load', () => {
|
||||
const {container} = render(<SubmitButton state={STATES.LOAD}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
it("Render load", () => {
|
||||
const { container } = render(<SubmitButton state={STATES.LOAD}/>);
|
||||
expect(container)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -5,6 +5,7 @@ exports[`<SubmitButton/> Render load 1`] = `
|
||||
<button
|
||||
class="btn btn-primary offset-lg-8 col-lg-3 col-sm-12"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
@ -23,6 +24,7 @@ exports[`<SubmitButton/> Render ready 1`] = `
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-primary offset-lg-8 col-lg-3 col-sm-12"
|
||||
type="button"
|
||||
>
|
||||
|
||||
|
||||
@ -36,6 +38,7 @@ exports[`<SubmitButton/> Render saving 1`] = `
|
||||
<button
|
||||
class="btn btn-primary offset-lg-8 col-lg-3 col-sm-12"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
|
@ -9,8 +9,8 @@ import React from 'react';
|
||||
|
||||
import {act, fireEvent, render, waitForElement} from 'customTestRender';
|
||||
import mockAxios from 'jest-mock-axios';
|
||||
import ForisForm from "../components/ForisForm";
|
||||
|
||||
import {ForisForm} from '../components/ForisForm';
|
||||
|
||||
// It's possible to unittest each hooks via react-hooks-testing-library.
|
||||
// But it's better and easier to test it by test components which uses this hooks.
|
||||
@ -76,39 +76,39 @@ describe('useForm hook.', () => {
|
||||
expect(Child.mock.calls[1][0].formErrors).toMatchObject({field: 'Error'});
|
||||
});
|
||||
|
||||
// it('Update text value.', () => {
|
||||
// fireEvent.change(input, {target: {value: 'newValue', type: 'text'}})
|
||||
// expect(input.value).toBe('newValue');
|
||||
// });
|
||||
//
|
||||
// it('Update text value.', () => {
|
||||
// fireEvent.change(input, {target: {value: 123, type: 'number'}})
|
||||
// expect(input.value).toBe('123');
|
||||
// });
|
||||
//
|
||||
// it('Update checkbox value.', () => {
|
||||
// fireEvent.change(input, {target: {checked: true, type: 'checkbox'}})
|
||||
// expect(input.checked).toBe(true);
|
||||
// });
|
||||
//
|
||||
// it('Fetch data.', () => {
|
||||
// expect(mockAxios.get).toHaveBeenCalledWith('/api/wan', expect.anything());
|
||||
// expect(mockPrepData).toHaveBeenCalledTimes(1);
|
||||
// expect(Child.mock.calls[0][0].formData).toMatchObject({field: 'preparedData'});
|
||||
// });
|
||||
//
|
||||
// it('Submit.', () => {
|
||||
// expect(mockAxios.get).toHaveBeenCalledTimes(1);
|
||||
// expect(mockPrepDataToSubmit).toHaveBeenCalledTimes(0);
|
||||
//
|
||||
// fireEvent.submit(form);
|
||||
//
|
||||
// expect(mockPrepDataToSubmit).toHaveBeenCalledTimes(1);
|
||||
// expect(mockAxios.post).toHaveBeenCalledTimes(1);
|
||||
// expect(mockAxios.post).toHaveBeenCalledWith(
|
||||
// '/api/wan',
|
||||
// {'field': 'preparedDataToSubmit'},
|
||||
// expect.anything(),
|
||||
// );
|
||||
// });
|
||||
it('Update text value.', () => {
|
||||
fireEvent.change(input, {target: {value: 'newValue', type: 'text'}})
|
||||
expect(input.value).toBe('newValue');
|
||||
});
|
||||
|
||||
it('Update text value.', () => {
|
||||
fireEvent.change(input, {target: {value: 123, type: 'number'}})
|
||||
expect(input.value).toBe('123');
|
||||
});
|
||||
|
||||
it('Update checkbox value.', () => {
|
||||
fireEvent.change(input, {target: {checked: true, type: 'checkbox'}})
|
||||
expect(input.checked).toBe(true);
|
||||
});
|
||||
|
||||
it('Fetch data.', () => {
|
||||
expect(mockAxios.get).toHaveBeenCalledWith('testEndpoint', expect.anything());
|
||||
expect(mockPrepData).toHaveBeenCalledTimes(1);
|
||||
expect(Child.mock.calls[0][0].formData).toMatchObject({field: 'preparedData'});
|
||||
});
|
||||
|
||||
it('Submit.', () => {
|
||||
expect(mockAxios.get).toHaveBeenCalledTimes(1);
|
||||
expect(mockPrepDataToSubmit).toHaveBeenCalledTimes(0);
|
||||
|
||||
fireEvent.submit(form);
|
||||
|
||||
expect(mockPrepDataToSubmit).toHaveBeenCalledTimes(1);
|
||||
expect(mockAxios.post).toHaveBeenCalledTimes(1);
|
||||
expect(mockAxios.post).toHaveBeenCalledWith(
|
||||
'testEndpoint',
|
||||
{'field': 'preparedDataToSubmit'},
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -5,16 +5,16 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Prompt} from 'react-router';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import {Spinner} from 'bootstrap/Spinner';
|
||||
import {useAPIPost} from 'api/hooks';
|
||||
import { Spinner } from "bootstrap/Spinner";
|
||||
import { useAPIPost } from "api/hooks";
|
||||
|
||||
import {useForisModule, useForm} from '../hooks';
|
||||
import {STATES as SUBMIT_BUTTON_STATES, SubmitButton} from './SubmitButton';
|
||||
import {FailAlert, SuccessAlert} from './alerts';
|
||||
import { Prompt } from "react-router";
|
||||
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`. */
|
||||
@ -27,7 +27,7 @@ ForisForm.propTypes = {
|
||||
* If it's not passed then WebSockets aren't used
|
||||
* */
|
||||
wsModule: PropTypes.string,
|
||||
/**`foris-controller` action name to be used via WebSockets.
|
||||
/** `foris-controller` action name to be used via WebSockets.
|
||||
* If it's not passed then `update_settings` is used. see `src/common/WebSocketHooks.js`
|
||||
* */
|
||||
wsAction: PropTypes.string,
|
||||
@ -45,42 +45,41 @@ ForisForm.propTypes = {
|
||||
/** reForis form components. */
|
||||
children: PropTypes.node.isRequired,
|
||||
/** Optional override of form submit callback */
|
||||
onSubmitOverridden: PropTypes.func
|
||||
onSubmitOverridden: PropTypes.func,
|
||||
};
|
||||
|
||||
ForisForm.defaultProps = {
|
||||
prepData: data => data,
|
||||
prepDataToSubmit: data => data,
|
||||
prepData: (data) => data,
|
||||
prepDataToSubmit: (data) => data,
|
||||
postCallback: () => undefined,
|
||||
validator: () => undefined,
|
||||
disabled: false
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
/** Serves as HOC for all foris forms components. */
|
||||
export function ForisForm({
|
||||
ws,
|
||||
forisConfig,
|
||||
prepData,
|
||||
prepDataToSubmit,
|
||||
postCallback,
|
||||
validator,
|
||||
disabled,
|
||||
onSubmitOverridden,
|
||||
children
|
||||
}) {
|
||||
export default function ForisForm({
|
||||
ws,
|
||||
forisConfig,
|
||||
prepData,
|
||||
prepDataToSubmit,
|
||||
postCallback,
|
||||
validator,
|
||||
disabled,
|
||||
onSubmitOverridden,
|
||||
children,
|
||||
}) {
|
||||
const [formState, onFormChangeHandler, resetFormData] = useForm(validator, prepData);
|
||||
|
||||
const [forisModuleState] = useForisModule(ws, forisConfig);
|
||||
useEffect(() => {
|
||||
if (forisModuleState.data) {
|
||||
resetFormData(forisModuleState.data)
|
||||
resetFormData(forisModuleState.data);
|
||||
}
|
||||
}, [forisModuleState.data, resetFormData, prepData]);
|
||||
|
||||
const [postState, post] = useAPIPost(forisConfig.endpoint);
|
||||
useEffect(() => {
|
||||
if (postState.isSuccess)
|
||||
postCallback();
|
||||
if (postState.isSuccess) postCallback();
|
||||
}, [postCallback, postState.isSuccess]);
|
||||
|
||||
|
||||
@ -93,56 +92,57 @@ export function ForisForm({
|
||||
}
|
||||
|
||||
function getSubmitButtonState() {
|
||||
if (postState.isSending)
|
||||
return SUBMIT_BUTTON_STATES.SAVING;
|
||||
else if (forisModuleState.isLoading)
|
||||
return SUBMIT_BUTTON_STATES.LOAD;
|
||||
if (postState.isSending) return SUBMIT_BUTTON_STATES.SAVING;
|
||||
if (forisModuleState.isLoading) return SUBMIT_BUTTON_STATES.LOAD;
|
||||
return SUBMIT_BUTTON_STATES.READY;
|
||||
}
|
||||
|
||||
const [alertIsDismissed, setAlertIsDismissed] = useState(false);
|
||||
|
||||
if (!formState.data)
|
||||
return <Spinner className='row justify-content-center'/>;
|
||||
if (!formState.data) return <Spinner className="row justify-content-center"/>;
|
||||
|
||||
const formIsDisabled = disabled || forisModuleState.isLoading || postState.isSending;
|
||||
const submitButtonIsDisabled = disabled || !!formState.errors;
|
||||
|
||||
const childrenWithFormProps =
|
||||
React.Children.map(children, child =>
|
||||
React.cloneElement(child, {
|
||||
formData: formState.data,
|
||||
formErrors: formState.errors,
|
||||
setFormValue: onFormChangeHandler,
|
||||
disabled: formIsDisabled,
|
||||
})
|
||||
);
|
||||
const childrenWithFormProps = React.Children.map(
|
||||
children,
|
||||
(child) => React.cloneElement(child, {
|
||||
formData: formState.data,
|
||||
formErrors: formState.errors,
|
||||
setFormValue: onFormChangeHandler,
|
||||
disabled: formIsDisabled,
|
||||
}),
|
||||
);
|
||||
|
||||
const onSubmit = onSubmitOverridden ?
|
||||
onSubmitOverridden(formState.data, onFormChangeHandler, onSubmitHandler) : onSubmitHandler;
|
||||
const onSubmit = onSubmitOverridden
|
||||
? onSubmitOverridden(formState.data, onFormChangeHandler, onSubmitHandler)
|
||||
: onSubmitHandler;
|
||||
|
||||
function getMessageOnLeavingPage() {
|
||||
if (JSON.stringify(formState.data) === JSON.stringify(formState.initialData))
|
||||
return true;
|
||||
return _('Changes you made may not be saved. Are you sure you want to leave?')
|
||||
if (JSON.stringify(formState.data) === JSON.stringify(formState.initialData)) return true;
|
||||
return _("Changes you made may not be saved. Are you sure you want to leave?");
|
||||
}
|
||||
|
||||
return <>
|
||||
<Prompt message={getMessageOnLeavingPage}/>
|
||||
{!alertIsDismissed ?
|
||||
postState.isSuccess ?
|
||||
<SuccessAlert onDismiss={() => setAlertIsDismissed(true)}/>
|
||||
: postState.isError ?
|
||||
<FailAlert onDismiss={() => setAlertIsDismissed(true)}/>
|
||||
: null
|
||||
: null
|
||||
let alert = null;
|
||||
if (!alertIsDismissed) {
|
||||
if (postState.isSuccess) {
|
||||
alert = <SuccessAlert onDismiss={() => setAlertIsDismissed(true)}/>;
|
||||
} else if (postState.isError) {
|
||||
alert = <FailAlert onDismiss={() => setAlertIsDismissed(true)}/>;
|
||||
}
|
||||
<form onSubmit={onSubmit}>
|
||||
{childrenWithFormProps}
|
||||
<SubmitButton
|
||||
state={getSubmitButtonState()}
|
||||
disabled={submitButtonIsDisabled}
|
||||
/>
|
||||
</form>
|
||||
</>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Prompt message={getMessageOnLeavingPage}/>
|
||||
{alert}
|
||||
<form onSubmit={onSubmit}>
|
||||
{childrenWithFormProps}
|
||||
<SubmitButton
|
||||
state={getSubmitButtonState()}
|
||||
disabled={submitButtonIsDisabled}
|
||||
/>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { Button } from "bootstrap/Button";
|
||||
import Button from "bootstrap/Button";
|
||||
|
||||
export const STATES = {
|
||||
READY: 1,
|
||||
|
@ -5,38 +5,42 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import {Alert} from 'bootstrap/Alert';
|
||||
import {Portal} from 'utils/Portal';
|
||||
import Alert from "bootstrap/Alert";
|
||||
import Portal from "utils/Portal";
|
||||
|
||||
SuccessAlert.propTypes = {
|
||||
onDismiss: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const ALERT_CONTAINER_ID = 'alert_container';
|
||||
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>;
|
||||
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>
|
||||
export function FailAlert({ onDismiss }) {
|
||||
return (
|
||||
<Portal containerId={ALERT_CONTAINER_ID}>
|
||||
<Alert
|
||||
type="danger"
|
||||
message={_("Settings update was failed.")}
|
||||
onDismiss={onDismiss}
|
||||
/>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import { useCallback, useEffect, useReducer } from "react";
|
||||
import update from "immutability-helper";
|
||||
|
||||
import { useAPIGet } from "api/hooks";
|
||||
import { useWSForisModule } from "webSockets/hooks";
|
||||
import useWSForisModule from "webSockets/hooks";
|
||||
|
||||
|
||||
const FORM_ACTIONS = {
|
||||
@ -18,7 +18,6 @@ const FORM_ACTIONS = {
|
||||
};
|
||||
|
||||
export function useForm(validator, prepData) {
|
||||
|
||||
const [state, dispatch] = useReducer(formReducer, {
|
||||
data: null,
|
||||
initialData: null,
|
||||
@ -82,7 +81,7 @@ function getChangedValue(target) {
|
||||
value = target.checked;
|
||||
} else if (target.type === "number") {
|
||||
const parsedValue = parseInt(value);
|
||||
value = isNaN(parsedValue) ? value : parsedValue;
|
||||
value = Number.isNaN(parsedValue) ? value : parsedValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
62
src/index.js
62
src/index.js
@ -1,41 +1,53 @@
|
||||
//API
|
||||
export {useAPIGet, useAPIPost} from "./api/hooks";
|
||||
// API
|
||||
export { useAPIGet, useAPIPost } from "./api/hooks";
|
||||
|
||||
// Bootstrap
|
||||
export {Alert} from "bootstrap/Alert";
|
||||
export {Button} from "bootstrap/Button";
|
||||
export {CheckBox} from "bootstrap/Checkbox";
|
||||
export {formFieldsSize} from "bootstrap/constants";
|
||||
export {DataTimeInput} from "bootstrap/DataTimeInput";
|
||||
export {EmailInput} from "bootstrap/EmailInput";
|
||||
export {Input} from "bootstrap/Input";
|
||||
export {Modal, ModalBody, ModalFooter, ModalHeader} from "bootstrap/Modal";
|
||||
export {NumberInput} from "bootstrap/NumberInput";
|
||||
export {PasswordInput} from "bootstrap/PasswordInput";
|
||||
export {RadioSet} from "bootstrap/RadioSet";
|
||||
export {Select} from "bootstrap/Select";
|
||||
export {Spinner, SpinnerElement} from "bootstrap/Spinner";
|
||||
export {TextInput} from "bootstrap/TextInput";
|
||||
export * as Alert from "bootstrap/Alert";
|
||||
export * as Button from "bootstrap/Button";
|
||||
export * as CheckBox from "bootstrap/Checkbox";
|
||||
export * as formFieldsSize from "bootstrap/constants";
|
||||
export * as DataTimeInput from "bootstrap/DataTimeInput";
|
||||
export * as EmailInput from "bootstrap/EmailInput";
|
||||
export * as Input from "bootstrap/Input";
|
||||
export * as NumberInput from "bootstrap/NumberInput";
|
||||
export * as PasswordInput from "bootstrap/PasswordInput";
|
||||
export * as RadioSet from "bootstrap/RadioSet";
|
||||
export * as Select from "bootstrap/Select";
|
||||
export * as TextInput from "bootstrap/TextInput";
|
||||
export {
|
||||
Spinner,
|
||||
SpinnerElement,
|
||||
} from "bootstrap/Spinner";
|
||||
export {
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
} from "bootstrap/Modal";
|
||||
|
||||
|
||||
// Form
|
||||
export {ForisForm} from "form/components/ForisForm";
|
||||
export {SubmitButton, STATES as SUBMIT_BUTTON_STATES} from "form/components/SubmitButton";
|
||||
export {useForisModule, useForm} from "form/hooks";
|
||||
export ForisForm from "form/components/ForisForm";
|
||||
export { SubmitButton, STATES as SUBMIT_BUTTON_STATES } from "form/components/SubmitButton";
|
||||
export { useForisModule, useForm } from "form/hooks";
|
||||
|
||||
|
||||
// Test Utils
|
||||
export {mockedWS} from "testUtils/mockWS";
|
||||
export { mockedWS } from "testUtils/mockWS";
|
||||
|
||||
|
||||
// WebSockets
|
||||
export {useWSForisModule} from "webSockets/hooks";
|
||||
export {WebSockets} from "webSockets/WebSockets";
|
||||
export * as useWSForisModule from "webSockets/hooks";
|
||||
export * as WebSockets from "webSockets/WebSockets";
|
||||
|
||||
|
||||
// Utils
|
||||
export {Portal} from "utils/Portal";
|
||||
export * as Portal from "utils/Portal";
|
||||
|
||||
|
||||
// Foris URL
|
||||
export {ForisURLs, REFORIS_URL_PREFIX} from "./forisUrls"
|
||||
export { ForisURLs, REFORIS_URL_PREFIX } from "./forisUrls";
|
||||
|
||||
|
||||
// Validation
|
||||
export {
|
||||
@ -46,4 +58,4 @@ export {
|
||||
validateDUID,
|
||||
validateMAC,
|
||||
validateMultipleEmails,
|
||||
} from "validations"
|
||||
} from "validations";
|
||||
|
@ -5,11 +5,10 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
export function Portal({containerId, children}) {
|
||||
export default function Portal({ containerId, children }) {
|
||||
const container = document.getElementById(containerId);
|
||||
if (container)
|
||||
return ReactDOM.createPortal(children, container);
|
||||
if (container) return ReactDOM.createPortal(children, container);
|
||||
return null;
|
||||
}
|
||||
|
@ -5,7 +5,9 @@
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import { ForisUrls } from "forisUrls";
|
||||
/* eslint no-console: "off" */
|
||||
|
||||
import { ForisURLs } from "forisUrls";
|
||||
|
||||
const PROTOCOL = window.location.protocol === "http:" ? "ws" : "wss";
|
||||
|
||||
@ -15,13 +17,13 @@ const URL = process.env.LIGHTTPD
|
||||
|
||||
const WAITING_FOR_CONNECTION_TIMEOUT = 500;
|
||||
|
||||
export class WebSockets {
|
||||
export default class WebSockets {
|
||||
constructor() {
|
||||
this.ws = new WebSocket(URL);
|
||||
this.ws.onerror = (e) => {
|
||||
if (window.location.pathname !== ForisUrls.login) {
|
||||
if (window.location.pathname !== ForisURLs.login) {
|
||||
console.error("WS: Error observed, you aren't logged probably.");
|
||||
window.location.replace(ForisUrls.login);
|
||||
window.location.replace(ForisURLs.login);
|
||||
}
|
||||
console.log(`WS: Error: ${e}`);
|
||||
};
|
||||
|
@ -7,14 +7,15 @@
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function useWSForisModule(ws, module, action = "update_settings") {
|
||||
export default function useWSForisModule(ws, module, action = "update_settings") {
|
||||
const [data, setData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (ws && module) {
|
||||
ws.subscribe(module).bind(module, action, (msg) => {
|
||||
setData(msg.data);
|
||||
});
|
||||
ws.subscribe(module)
|
||||
.bind(module, action, (msg) => {
|
||||
setData(msg.data);
|
||||
});
|
||||
}
|
||||
}, [action, module, ws]);
|
||||
|
||||
|
Reference in New Issue
Block a user