mirror of
https://gitlab.nic.cz/turris/reforis/foris-js.git
synced 2025-07-31 19:53:28 +02:00
Fix lint.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user