mirror of
https://gitlab.nic.cz/turris/reforis/foris-js.git
synced 2025-06-16 13:46:16 +02:00
Set babel.
This commit is contained in:
26
src/form/__tests__/SubmitButton.test.js
Normal file
26
src/form/__tests__/SubmitButton.test.js
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
|
||||
*
|
||||
* This is free software, licensed under the GNU General Public License v3.
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {render} from 'customTestRender';
|
||||
|
||||
import SubmitButton, {STATES} from '../components/SubmitButton';
|
||||
|
||||
describe('<SubmitButton/>', () => {
|
||||
it('Render ready', () => {
|
||||
const {container} = render(<SubmitButton state={STATES.READY}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
it('Render saving', () => {
|
||||
const {container} = render(<SubmitButton state={STATES.SAVING}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
it('Render load', () => {
|
||||
const {container} = render(<SubmitButton state={STATES.LOAD}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
51
src/form/__tests__/__snapshots__/SubmitButton.test.js.snap
Normal file
51
src/form/__tests__/__snapshots__/SubmitButton.test.js.snap
Normal file
@ -0,0 +1,51 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<SubmitButton/> Render load 1`] = `
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-primary offset-lg-8 col-lg-3 col-sm-12"
|
||||
disabled=""
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
Load settings
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<SubmitButton/> Render ready 1`] = `
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-primary offset-lg-8 col-lg-3 col-sm-12"
|
||||
>
|
||||
|
||||
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<SubmitButton/> Render saving 1`] = `
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-primary offset-lg-8 col-lg-3 col-sm-12"
|
||||
disabled=""
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
Updating
|
||||
</button>
|
||||
</div>
|
||||
`;
|
115
src/form/__tests__/hooks.test.js
Normal file
115
src/form/__tests__/hooks.test.js
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
|
||||
*
|
||||
* This is free software, licensed under the GNU General Public License v3.
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
// TODO: rewrite this test
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {fireEvent, render, waitForElement} from 'customTestRender';
|
||||
import mockAxios from 'jest-mock-axios';
|
||||
|
||||
import ForisForm from '../components/ForisForm';
|
||||
import API_URLs from 'common/API';
|
||||
|
||||
// It's possible to unittest each hooks via react-hooks-testing-library.
|
||||
// But it's better and easier to test it by test components which uses this hooks.
|
||||
|
||||
const TestForm = ({formData, formErrors, setFormValue}) => {
|
||||
return <>
|
||||
<input
|
||||
data-testid='test-input'
|
||||
value={formData.field}
|
||||
onChange={setFormValue(value => ({field: {$set: value}}))}
|
||||
/>
|
||||
<p>{formErrors.field}</p>
|
||||
</>
|
||||
};
|
||||
|
||||
describe('useForm hook.', () => {
|
||||
let mockValidator;
|
||||
let mockPrepData;
|
||||
let mockPrepDataToSubmit;
|
||||
let mockWebSockets;
|
||||
let input;
|
||||
let form;
|
||||
const Child = jest.fn(props => <TestForm {...props}/>);
|
||||
|
||||
beforeEach(async () => {
|
||||
mockPrepData = jest.fn(() => ({field: 'preparedData'}));
|
||||
mockPrepDataToSubmit = jest.fn(() => ({field: 'preparedDataToSubmit'}));
|
||||
mockValidator = jest.fn(data => data.field === 'invalidValue' ? {field: 'Error'} : {});
|
||||
const {getByTestId, container} = render(
|
||||
<ForisForm
|
||||
ws={mockWebSockets}
|
||||
// Just some module which exists...
|
||||
forisConfig={{
|
||||
endpoint: API_URLs.wan,
|
||||
wsModule: 'wan'
|
||||
}}
|
||||
prepData={mockPrepData}
|
||||
prepDataToSubmit={mockPrepDataToSubmit}
|
||||
validator={mockValidator}
|
||||
>
|
||||
<Child/>
|
||||
</ForisForm>
|
||||
);
|
||||
mockAxios.mockResponse({field: 'fetchedData'});
|
||||
|
||||
input = await waitForElement(() =>
|
||||
getByTestId('test-input')
|
||||
);
|
||||
form = container.firstChild
|
||||
});
|
||||
|
||||
it('Validation on changing.', () => {
|
||||
expect(mockValidator).toHaveBeenCalledTimes(1);
|
||||
expect(Child).toHaveBeenCalledTimes(1);
|
||||
expect(Child.mock.calls[0][0].formErrors).toMatchObject({});
|
||||
|
||||
fireEvent.change(input, {target: {value: 'invalidValue', type: 'text'}});
|
||||
|
||||
expect(Child).toHaveBeenCalledTimes(2);
|
||||
expect(mockValidator).toHaveBeenCalledTimes(2);
|
||||
expect(Child.mock.calls[1][0].formErrors).toMatchObject({field: 'Error'});
|
||||
});
|
||||
|
||||
it('Update text value.', () => {
|
||||
fireEvent.change(input, {target: {value: 'newValue', type: 'text'}})
|
||||
expect(input.value).toBe('newValue');
|
||||
});
|
||||
|
||||
it('Update text value.', () => {
|
||||
fireEvent.change(input, {target: {value: 123, type: 'number'}})
|
||||
expect(input.value).toBe('123');
|
||||
});
|
||||
|
||||
it('Update checkbox value.', () => {
|
||||
fireEvent.change(input, {target: {checked: true, type: 'checkbox'}})
|
||||
expect(input.checked).toBe(true);
|
||||
});
|
||||
|
||||
it('Fetch data.', () => {
|
||||
expect(mockAxios.get).toHaveBeenCalledWith('/api/wan', expect.anything());
|
||||
expect(mockPrepData).toHaveBeenCalledTimes(1);
|
||||
expect(Child.mock.calls[0][0].formData).toMatchObject({field: 'preparedData'});
|
||||
});
|
||||
|
||||
it('Submit.', () => {
|
||||
expect(mockAxios.get).toHaveBeenCalledTimes(1);
|
||||
expect(mockPrepDataToSubmit).toHaveBeenCalledTimes(0);
|
||||
|
||||
fireEvent.submit(form);
|
||||
|
||||
expect(mockPrepDataToSubmit).toHaveBeenCalledTimes(1);
|
||||
expect(mockAxios.post).toHaveBeenCalledTimes(1);
|
||||
expect(mockAxios.post).toHaveBeenCalledWith(
|
||||
'/api/wan',
|
||||
{'field': 'preparedDataToSubmit'},
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
});
|
84
src/form/__tests__/validation.test.js
Normal file
84
src/form/__tests__/validation.test.js
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
|
||||
*
|
||||
* This is free software, licensed under the GNU General Public License v3.
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import {
|
||||
validateDomain,
|
||||
validateDUID,
|
||||
validateIPv4Address,
|
||||
validateIPv6Address,
|
||||
validateIPv6Prefix, validateMAC
|
||||
} from 'validations';
|
||||
|
||||
describe('Validation functions', () => {
|
||||
it('validateIPv4Address valid', () => {
|
||||
expect(validateIPv4Address('192.168.1.1')).toBe(undefined);
|
||||
expect(validateIPv4Address('1.1.1.1')).toBe(undefined);
|
||||
expect(validateIPv4Address('0.0.0.0')).toBe(undefined);
|
||||
});
|
||||
it('validateIPv4Address invalid', () => {
|
||||
expect(validateIPv4Address('invalid')).not.toBe(undefined);
|
||||
expect(validateIPv4Address('192.256.1.1')).not.toBe(undefined);
|
||||
expect(validateIPv4Address('192.168.256.1')).not.toBe(undefined);
|
||||
expect(validateIPv4Address('192.168.1.256')).not.toBe(undefined);
|
||||
expect(validateIPv4Address('192.168.1.256')).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('validateIPv6Address valid', () => {
|
||||
expect(validateIPv6Address('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe(undefined);
|
||||
expect(validateIPv6Address('0:0:0:0:0:0:0:1')).toBe(undefined);
|
||||
expect(validateIPv6Address('::1')).toBe(undefined);
|
||||
expect(validateIPv6Address('::')).toBe(undefined);
|
||||
|
||||
});
|
||||
it('validateIPv6Address invalid', () => {
|
||||
expect(validateIPv6Address('invalid')).not.toBe(undefined);
|
||||
expect(validateIPv6Address('1.1.1.1')).not.toBe(undefined);
|
||||
expect(validateIPv6Address('1200::AB00:1234::2552:7777:1313')).not.toBe(undefined);
|
||||
expect(validateIPv6Address('1200:0000:AB00:1234:O000:2552:7777:1313')).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('validateIPv6Prefix valid', () => {
|
||||
expect(validateIPv6Prefix('2002:0000::/16')).toBe(undefined);
|
||||
expect(validateIPv6Prefix('0::/0')).toBe(undefined);
|
||||
});
|
||||
it('validateIPv6Prefix invalid', () => {
|
||||
expect(validateIPv6Prefix('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).not.toBe(undefined);
|
||||
expect(validateIPv6Prefix('::1')).not.toBe(undefined);
|
||||
expect(validateIPv6Prefix('2002:0000::/999')).not.toBe(undefined);
|
||||
});
|
||||
|
||||
|
||||
it('validateDomain valid', () => {
|
||||
expect(validateDomain('example.com')).toBe(undefined);
|
||||
expect(validateDomain('one.two.three')).toBe(undefined);
|
||||
});
|
||||
it('validateDomain invalid', () => {
|
||||
expect(validateDomain('test/')).not.toBe(undefined);
|
||||
expect(validateDomain('.')).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('validateDUID valid', () => {
|
||||
expect(validateDUID('abcdefAB')).toBe(undefined);
|
||||
expect(validateDUID('ABCDEF12')).toBe(undefined);
|
||||
expect(validateDUID('ABCDEF12AB')).toBe(undefined);
|
||||
|
||||
});
|
||||
it('validateDUID invalid', () => {
|
||||
expect(validateDUID('gggggggg')).not.toBe(undefined);
|
||||
expect(validateDUID('abcdefABa')).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('validateMAC valid', () => {
|
||||
expect(validateMAC('00:D0:56:F2:B5:12')).toBe(undefined);
|
||||
expect(validateMAC('00:26:DD:14:C4:EE')).toBe(undefined);
|
||||
expect(validateMAC('06:00:00:00:00:00')).toBe(undefined);
|
||||
});
|
||||
it('validateMAC invalid', () => {
|
||||
expect(validateMAC('00:00:00:00:00:0G')).not.toBe(undefined);
|
||||
expect(validateMAC('06:00:00:00:00:00:00')).not.toBe(undefined);
|
||||
});
|
||||
});
|
148
src/form/components/ForisForm.js
Normal file
148
src/form/components/ForisForm.js
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
|
||||
*
|
||||
* This is free software, licensed under the GNU General Public License v3.
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Prompt} from 'react-router';
|
||||
|
||||
import Spinner from 'bootstrap/components/Spinner';
|
||||
import {useAPIPost} from 'api';
|
||||
|
||||
import {useForisModule, useForm} from '../hooks';
|
||||
import SubmitButton, {STATES as SUBMIT_BUTTON_STATES} from './SubmitButton';
|
||||
import {FailAlert, SuccessAlert} from './alerts';
|
||||
|
||||
ForisForm.propTypes = {
|
||||
/** WebSocket object see `scr/common/WebSockets.js`. */
|
||||
ws: PropTypes.object,
|
||||
/** Foris configuration object. See usage in main components. */
|
||||
forisConfig: PropTypes.shape({
|
||||
/** reForis Flask aplication API endpoint from `src/common/API.js`. */
|
||||
endpoint: PropTypes.string.isRequired,
|
||||
/** `foris-controller` module name to be used via WebSockets.
|
||||
* If it's not passed then WebSockets aren't used
|
||||
* */
|
||||
wsModule: PropTypes.string,
|
||||
/**`foris-controller` action name to be used via WebSockets.
|
||||
* If it's not passed then `update_settings` is used. see `src/common/WebSocketHooks.js`
|
||||
* */
|
||||
wsAction: PropTypes.string,
|
||||
}).isRequired,
|
||||
/** Function to prepare data recived from the API before using in forms. */
|
||||
prepData: PropTypes.func.isRequired,
|
||||
/** Function to prepare data from form before submitting. */
|
||||
prepDataToSubmit: PropTypes.func.isRequired,
|
||||
/** Function to handle response to POST request. */
|
||||
postCallback: PropTypes.func.isRequired,
|
||||
/** Validate data and provide validation object. Then validation errors passed to children. */
|
||||
validator: PropTypes.func.isRequired,
|
||||
/** Disables form */
|
||||
disabled: PropTypes.bool,
|
||||
/** reForis form components. */
|
||||
children: PropTypes.node.isRequired,
|
||||
/** Optional override of form submit callback */
|
||||
onSubmitOverridden: PropTypes.func
|
||||
};
|
||||
|
||||
ForisForm.defaultProps = {
|
||||
prepData: data => data,
|
||||
prepDataToSubmit: data => data,
|
||||
postCallback: () => undefined,
|
||||
validator: () => undefined,
|
||||
disabled: false
|
||||
};
|
||||
|
||||
/** Serves as HOC for all foris forms components. */
|
||||
export default function ForisForm({
|
||||
ws,
|
||||
forisConfig,
|
||||
prepData,
|
||||
prepDataToSubmit,
|
||||
postCallback,
|
||||
validator,
|
||||
disabled,
|
||||
onSubmitOverridden,
|
||||
children
|
||||
}) {
|
||||
const [formState, onFormChangeHandler, resetFormData] = useForm(validator, prepData);
|
||||
|
||||
const [forisModuleState] = useForisModule(ws, forisConfig);
|
||||
useEffect(() => {
|
||||
if (forisModuleState.data) {
|
||||
resetFormData(forisModuleState.data)
|
||||
}
|
||||
}, [forisModuleState.data, resetFormData, prepData]);
|
||||
|
||||
const [postState, post] = useAPIPost(forisConfig.endpoint);
|
||||
useEffect(() => {
|
||||
if (postState.isSuccess)
|
||||
postCallback();
|
||||
}, [postCallback, postState.isSuccess]);
|
||||
|
||||
|
||||
function onSubmitHandler(e) {
|
||||
e.preventDefault();
|
||||
resetFormData();
|
||||
const copiedFormData = JSON.parse(JSON.stringify(formState.data));
|
||||
const preparedData = prepDataToSubmit(copiedFormData);
|
||||
post(preparedData);
|
||||
}
|
||||
|
||||
function getSubmitButtonState() {
|
||||
if (postState.isSending)
|
||||
return SUBMIT_BUTTON_STATES.SAVING;
|
||||
else if (forisModuleState.isLoading)
|
||||
return SUBMIT_BUTTON_STATES.LOAD;
|
||||
return SUBMIT_BUTTON_STATES.READY;
|
||||
}
|
||||
|
||||
const [alertIsDismissed, setAlertIsDismissed] = useState(false);
|
||||
|
||||
if (!formState.data)
|
||||
return <Spinner className='row justify-content-center'/>;
|
||||
|
||||
const formIsDisabled = disabled || forisModuleState.isLoading || postState.isSending;
|
||||
const submitButtonIsDisabled = disabled || !!formState.errors;
|
||||
|
||||
const childrenWithFormProps =
|
||||
React.Children.map(children, child =>
|
||||
React.cloneElement(child, {
|
||||
formData: formState.data,
|
||||
formErrors: formState.errors,
|
||||
setFormValue: onFormChangeHandler,
|
||||
disabled: formIsDisabled,
|
||||
})
|
||||
);
|
||||
|
||||
const onSubmit = onSubmitOverridden ?
|
||||
onSubmitOverridden(formState.data, onFormChangeHandler, onSubmitHandler) : onSubmitHandler;
|
||||
|
||||
function getMessageOnLeavingPage() {
|
||||
if (JSON.stringify(formState.data) === JSON.stringify(formState.initialData))
|
||||
return true;
|
||||
return _('Changes you made may not be saved. Are you sure you want to leave?')
|
||||
}
|
||||
|
||||
return <>
|
||||
<Prompt message={getMessageOnLeavingPage}/>
|
||||
{!alertIsDismissed ?
|
||||
postState.isSuccess ?
|
||||
<SuccessAlert onDismiss={() => setAlertIsDismissed(true)}/>
|
||||
: postState.isError ?
|
||||
<FailAlert onDismiss={() => setAlertIsDismissed(true)}/>
|
||||
: null
|
||||
: null
|
||||
}
|
||||
<form onSubmit={onSubmit}>
|
||||
{childrenWithFormProps}
|
||||
<SubmitButton
|
||||
state={getSubmitButtonState()}
|
||||
disabled={submitButtonIsDisabled}
|
||||
/>
|
||||
</form>
|
||||
</>
|
||||
}
|
49
src/form/components/SubmitButton.js
Normal file
49
src/form/components/SubmitButton.js
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
|
||||
*
|
||||
* This is free software, licensed under the GNU General Public License v3.
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Button from 'bootstrap/components/Button';
|
||||
|
||||
export const STATES = {
|
||||
READY: 1,
|
||||
SAVING: 2,
|
||||
LOAD: 3,
|
||||
};
|
||||
|
||||
SubmitButton.propTypes = {
|
||||
disabled: PropTypes.bool,
|
||||
state: PropTypes.oneOf(Object.keys(STATES).map(key => STATES[key]))
|
||||
};
|
||||
|
||||
export default function SubmitButton({disabled, state, ...props}) {
|
||||
const disableSubmitButton = disabled || state !== STATES.READY;
|
||||
const loadingSubmitButton = state !== STATES.READY;
|
||||
|
||||
let labelSubmitButton;
|
||||
switch (state) {
|
||||
case STATES.SAVING:
|
||||
labelSubmitButton = _('Updating');
|
||||
break;
|
||||
case STATES.LOAD:
|
||||
labelSubmitButton = _('Load settings');
|
||||
break;
|
||||
default:
|
||||
labelSubmitButton = _('Save');
|
||||
}
|
||||
|
||||
return <Button
|
||||
loading={loadingSubmitButton}
|
||||
disabled={disableSubmitButton}
|
||||
forisFormSize
|
||||
|
||||
{...props}
|
||||
>
|
||||
{labelSubmitButton}
|
||||
</Button>;
|
||||
}
|
42
src/form/components/alerts.js
Normal file
42
src/form/components/alerts.js
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
|
||||
*
|
||||
* This is free software, licensed under the GNU General Public License v3.
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Alert from 'bootstrap/components/Alert';
|
||||
import Portal from 'utils/Portal';
|
||||
|
||||
SuccessAlert.propTypes = {
|
||||
onDismiss: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const ALERT_CONTAINER_ID = 'alert_container';
|
||||
|
||||
export function SuccessAlert({onDismiss}) {
|
||||
return <Portal containerId={ALERT_CONTAINER_ID}>
|
||||
<Alert
|
||||
type='success'
|
||||
message={_('Settings were successfully saved.')}
|
||||
onDismiss={onDismiss}
|
||||
/>
|
||||
</Portal>;
|
||||
}
|
||||
|
||||
FailAlert.propTypes = {
|
||||
onDismiss: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export function FailAlert({onDismiss}) {
|
||||
return <Portal containerId={ALERT_CONTAINER_ID}>
|
||||
<Alert
|
||||
type='danger'
|
||||
message={_('Settings update was failed.')}
|
||||
onDismiss={onDismiss}
|
||||
/>
|
||||
</Portal>
|
||||
}
|
100
src/form/hooks.js
Normal file
100
src/form/hooks.js
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
|
||||
*
|
||||
* This is free software, licensed under the GNU General Public License v3.
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import {useCallback, useEffect, useReducer} from 'react';
|
||||
import update from 'immutability-helper';
|
||||
|
||||
import {useAPIGet} from 'api/hooks';
|
||||
import useWSForisModule from 'webSockets/hooks';
|
||||
|
||||
|
||||
export function useForm(validator, prepData) {
|
||||
const [state, dispatch] = useReducer(formReducer, {
|
||||
data: null,
|
||||
initialData: null,
|
||||
errors: {},
|
||||
});
|
||||
|
||||
const onFormReload = useCallback(data => {
|
||||
dispatch({
|
||||
type: FORM_ACTIONS.resetData,
|
||||
data: data,
|
||||
prepData: prepData,
|
||||
validator: validator,
|
||||
});
|
||||
}, [prepData, validator]);
|
||||
|
||||
const onFormChangeHandler = useCallback(updateRule =>
|
||||
event => {
|
||||
dispatch({
|
||||
type: FORM_ACTIONS.updateValue,
|
||||
value: getChangedValue(event.target),
|
||||
updateRule: updateRule,
|
||||
validator: validator,
|
||||
})
|
||||
}, [validator]);
|
||||
|
||||
return [
|
||||
state,
|
||||
onFormChangeHandler,
|
||||
onFormReload,
|
||||
]
|
||||
}
|
||||
|
||||
const FORM_ACTIONS = {
|
||||
updateValue: 1,
|
||||
resetData: 2,
|
||||
};
|
||||
|
||||
function formReducer(state, action) {
|
||||
switch (action.type) {
|
||||
case FORM_ACTIONS.updateValue: {
|
||||
const newData = update(state.data, action.updateRule(action.value));
|
||||
const errors = action.validator(newData);
|
||||
return {
|
||||
...state,
|
||||
data: newData,
|
||||
errors: errors
|
||||
};
|
||||
}
|
||||
case FORM_ACTIONS.resetData: {
|
||||
if (!action.data)
|
||||
return {...state, initialData: state.data};
|
||||
const prepData = action.prepData ? action.prepData(action.data) : action.data;
|
||||
return {
|
||||
data: prepData,
|
||||
initialData: prepData,
|
||||
errors: action.data ? action.validator(prepData) : undefined,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getChangedValue(target) {
|
||||
let value = target.value;
|
||||
if (target.type === 'checkbox') {
|
||||
value = target.checked;
|
||||
} else if (target.type === 'number') {
|
||||
const parsedValue = parseInt(value);
|
||||
value = isNaN(parsedValue) ? value : parsedValue;
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
export function useForisModule(ws, config) {
|
||||
const [APIGetState, get] = useAPIGet(config.endpoint);
|
||||
const [WSData] = useWSForisModule(ws, config.wsModule, config.wsAction);
|
||||
|
||||
useEffect(() => {
|
||||
get();
|
||||
}, [WSData, get]);
|
||||
|
||||
return [APIGetState];
|
||||
}
|
5
src/form/index.js
Normal file
5
src/form/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
import * as ForisForm from "./components/ForisForm";
|
||||
import * as SubmitButton from "./components/SubmitButton";
|
||||
import * as hooks from "./hooks";
|
||||
|
||||
export {ForisForm, SubmitButton, hooks}
|
Reference in New Issue
Block a user