Skip to content

Commit

Permalink
Add: Add useValueChange hook for form components
Browse files Browse the repository at this point in the history
All form components should be able to call the onChange handler with the
value, name pair. Therefore introduce a generic hook to abstract the
event handling.
  • Loading branch information
bjoernricks committed Jun 6, 2024
1 parent cf751df commit 0d0e5f2
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 0 deletions.
43 changes: 43 additions & 0 deletions src/web/components/form/__tests__/useValueChange.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* SPDX-FileCopyrightText: 2024 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import {render, screen, fireEvent} from 'web/utils/testing';

import useValueChange from '../useValueChange';

const TestComponent = ({value, onChange, name, disabled}) => {
const handleChange = useValueChange({onChange, name, disabled});

return (
<input value={value} name={name} type="text" onChange={handleChange} />
);
};

describe('onValueChange Tests', () => {
test('should call onChange when value changes', () => {
const onChange = jest.fn();

render(<TestComponent value="test" onChange={onChange} name="test" />);

const input = screen.getByRole('textbox');

fireEvent.change(input, {target: {value: 'new value'}});

expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith('new value', 'test');
});

test('should not call onChange when disabled', () => {
const onChange = jest.fn();

render(<TestComponent value="test" onChange={onChange} name="test" disabled={true}/>);

const input = screen.getByRole('textbox');

fireEvent.change(input, {target: {value: 'new value'}});

expect(onChange).not.toHaveBeenCalled();
});
});
52 changes: 52 additions & 0 deletions src/web/components/form/useValueChange.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* SPDX-FileCopyrightText: 2024 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import {useCallback} from 'react';

import {isDefined} from 'gmp/utils/identity';

const eventTargetValue = event => event.target.value;
const noOpConvert = value => value;
/**
* A hook that handles the change of a value of an input field.
*
* It gets the event target value and optionally converts it.
* value and name are passed to the onChange function.
*
* @param {Object} param0 An object with the following properties:
* - disabled: A boolean that indicates if the value can be changed.
* - name: A string that represents the name of the value.
* - onChange: A function that is called when the value changes.
* - convert: A function that converts the value optionally.
* - valueFunc: A function that gets the value from the event. Defaults to event.target.value.
* @returns A function as a callback that handles the change of a value in an input field.
*/
const useValueChange = ({
disabled,
name,
onChange,
convert = noOpConvert,
valueFunc = eventTargetValue,
}) => {
const notifyChange = useCallback(
value => {
if (isDefined(onChange) && !disabled) {
onChange(value, name);
}
},
[disabled, name, onChange],
);

const handleChange = useCallback(
event => {
notifyChange(convert(valueFunc(event)));
},
[notifyChange, convert, valueFunc],
);

return handleChange;
};

export default useValueChange;

0 comments on commit 0d0e5f2

Please sign in to comment.