1
0
mirror of https://gitlab.nic.cz/turris/reforis/foris-js.git synced 2024-07-05 20:53:12 +00:00

Resolve "Increment/decrement value in NumberInput when +/- button is kept pushed."

This commit is contained in:
Maciej Lenartowicz 2019-10-07 09:18:25 +00:00
parent 0af56ec84c
commit afa8c160a3
5 changed files with 53 additions and 21 deletions

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "foris", "name": "foris",
"version": "0.1.0-beta.7", "version": "0.1.0-beta.8",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "foris", "name": "foris",
"version": "0.1.0-beta.7", "version": "0.1.0-beta.8",
"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

@ -6,8 +6,9 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useConditionalTimeout } from "utils/hooks";
import { Input } from "./Input"; import { Input } from "./Input";
import "./NumberInput.css"; import "./NumberInput.css";
@ -33,15 +34,25 @@ NumberInput.defaultProps = {
value: 0, value: 0,
}; };
export function NumberInput({ onChange, inlineText, ...props }) { export function NumberInput({
onChange, inlineText, value, ...props
}) {
function updateValue(initialValue, difference) {
onChange({ target: { value: initialValue + difference } });
}
const enableIncrease = useConditionalTimeout({ callback: updateValue }, value, 1);
const enableDecrease = useConditionalTimeout({ callback: updateValue }, value, -1);
return ( return (
<Input type="number" onChange={onChange} {...props}> <Input type="number" onChange={onChange} value={value} {...props}>
<div className="input-group-append"> <div className="input-group-append">
{inlineText && <p className="input-group-text">{inlineText}</p>} {inlineText && <p className="input-group-text">{inlineText}</p>}
<button <button
type="button" type="button"
className="btn btn-outline-secondary" className="btn btn-outline-secondary"
onClick={() => onChange({ target: { value: props.value + 1 } })} onMouseDown={() => enableIncrease(true)}
onMouseUp={() => enableIncrease(false)}
aria-label="Increase" aria-label="Increase"
> >
<i className="fas fa-plus" /> <i className="fas fa-plus" />
@ -49,7 +60,8 @@ export function NumberInput({ onChange, inlineText, ...props }) {
<button <button
type="button" type="button"
className="btn btn-outline-secondary" className="btn btn-outline-secondary"
onClick={() => onChange({ target: { value: props.value - 1 } })} onMouseDown={() => enableDecrease(true)}
onMouseUp={() => enableDecrease(false)}
aria-label="Decrease" aria-label="Decrease"
> >
<i className="fas fa-minus" /> <i className="fas fa-minus" />

View File

@ -7,39 +7,39 @@
import React from "react"; import React from "react";
import { render, fireEvent } from "customTestRender"; import { render, fireEvent, getByLabelText, wait } from "customTestRender";
import { NumberInput } from "../NumberInput"; import { NumberInput } from "../NumberInput";
describe("<NumberInput/>", () => { describe("<NumberInput/>", () => {
const onChangeMock = jest.fn(); const onChangeMock = jest.fn();
let container; let componentContainer;
let getByLabelText;
beforeEach(() => { beforeEach(() => {
({ container, getByLabelText } = render( const { container } = render(
<NumberInput <NumberInput
label="Test label" label="Test label"
helpText="Some help text" helpText="Some help text"
value={1} value={1}
onChange={onChangeMock} onChange={onChangeMock}
/> />
)); );
componentContainer = container;
}); });
it("Render number input", () => { it("Render number input", () => {
expect(container.firstChild).toMatchSnapshot(); expect(componentContainer.firstChild).toMatchSnapshot();
}); });
it("Increase number with button", () => { it("Increase number with button", async () => {
const increaseButton = getByLabelText("Increase"); const increaseButton = getByLabelText(componentContainer, "Increase");
fireEvent.click(increaseButton); fireEvent.mouseDown(increaseButton);
expect(onChangeMock).toHaveBeenCalledWith({"target": {"value": 2}}); await wait(() => expect(onChangeMock).toHaveBeenCalledWith({"target": {"value": 2}}));
}); });
it("Decrease number with button", () => { it("Decrease number with button", async () => {
const decreaseButton = getByLabelText("Decrease"); const decreaseButton = getByLabelText(componentContainer, "Decrease");
fireEvent.click(decreaseButton); fireEvent.mouseDown(decreaseButton);
expect(onChangeMock).toHaveBeenCalledWith({"target": {"value": 0}}); await wait(() => expect(onChangeMock).toHaveBeenCalledWith({"target": {"value": 0}}));
}); });
}); });

20
src/utils/hooks.js Normal file
View File

@ -0,0 +1,20 @@
/*
* 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 { useState, useEffect } from "react";
/** Execute callback when condition is set to true. */
export function useConditionalTimeout({ callback, timeout = 125 }, ...callbackArgs) {
const [condition, setCondition] = useState(false);
useEffect(() => {
if (condition) {
const interval = setTimeout(() => callback(...callbackArgs), timeout);
return () => setTimeout(interval);
}
}, [condition, callback, timeout, callbackArgs]);
return setCondition;
}