mirror of
				https://gitlab.nic.cz/turris/reforis/foris-js.git
				synced 2025-11-03 23:00:31 +01:00 
			
		
		
		
	Merge branch '4-increment-decrement' into 'dev'
Resolve "Increment/decrement value in NumberInput when +/- button is kept pushed." Closes #4 See merge request turris/reforis/foris-js!14
This commit is contained in:
		
							
								
								
									
										2
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -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": {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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": {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
									
								
							
							
						
						
									
										20
									
								
								src/utils/hooks.js
									
									
									
									
									
										Normal 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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user