1
0
Эх сурвалжийг харах

reset file selector input after upload (#2280)

* reset file selector input after upload
disable number input arrows
resolves #2103

* lint

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide 6 сар өмнө
parent
commit
f67b93a791

+ 30 - 12
frontend/taipy-gui/src/components/Taipy/FileSelector.tsx

@@ -22,20 +22,19 @@ import React, {
     useRef,
     useState,
 } from "react";
+import UploadFile from "@mui/icons-material/UploadFile";
 import Button from "@mui/material/Button";
 import LinearProgress from "@mui/material/LinearProgress";
 import Tooltip from "@mui/material/Tooltip";
-import UploadFile from "@mui/icons-material/UploadFile";
+import { SxProps } from "@mui/material";
+import { nanoid } from "nanoid";
 
 import { TaipyContext } from "../../context/taipyContext";
 import { createNotificationAction, createSendActionNameAction } from "../../context/taipyReducers";
 import { useClassNames, useDynamicProperty, useModule } from "../../utils/hooks";
-import { expandSx, getCssSize, noDisplayStyle, TaipyActiveProps } from "./utils";
 import { uploadFile } from "../../workers/fileupload";
-import { SxProps } from "@mui/material";
 import { getComponentClassName } from "./TaipyStyle";
-
-
+import { expandSx, getCssSize, noDisplayStyle, TaipyActiveProps } from "./utils";
 
 interface FileSelectorProps extends TaipyActiveProps {
     onAction?: string;
@@ -75,22 +74,27 @@ const FileSelector = (props: FileSelectorProps) => {
         notify = true,
         withBorder = true,
     } = props;
-    const directoryProps = ["d", "dir", "directory", "folder"].includes(selectionType?.toLowerCase()) ?
-                           {webkitdirectory: "", directory: "", mozdirectory: "", nwdirectory: ""} :
-                           undefined;
     const [dropLabel, setDropLabel] = useState("");
     const [dropSx, setDropSx] = useState<SxProps | undefined>(defaultSx);
     const [upload, setUpload] = useState(false);
     const [progress, setProgress] = useState(0);
     const { state, dispatch } = useContext(TaipyContext);
     const butRef = useRef<HTMLElement>(null);
-    const inputId = useMemo(() => (id || `tp-${Date.now()}-${Math.random()}`) + "-upload-file", [id]);
+    const inputId = useMemo(() => (id || `tp-${nanoid()}`) + "-upload-file", [id]);
     const module = useModule();
 
     const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
     const active = useDynamicProperty(props.active, props.defaultActive, true);
     const hover = useDynamicProperty(props.hoverText, props.defaultHoverText, undefined);
 
+    const directoryProps = useMemo(
+        () =>
+            selectionType.toLowerCase().startsWith("d") || "folder" == selectionType.toLowerCase()
+                ? { webkitdirectory: "", directory: "", mozdirectory: "", nwdirectory: "" }
+                : undefined,
+        [selectionType]
+    );
+
     useEffect(
         () =>
             setDropSx((sx: SxProps | undefined) =>
@@ -123,20 +127,34 @@ const FileSelector = (props: FileSelectorProps) => {
                         onAction && dispatch(createSendActionNameAction(id, module, onAction));
                         notify &&
                             dispatch(
-                                createNotificationAction({ atype: "success", message: value, system: false, duration: 3000 })
+                                createNotificationAction({
+                                    atype: "success",
+                                    message: value,
+                                    system: false,
+                                    duration: 3000,
+                                })
                             );
+                        const fileInput = document.getElementById(inputId) as HTMLInputElement;
+                        fileInput && (fileInput.value = "");
                     },
                     (reason) => {
                         setUpload(false);
                         notify &&
                             dispatch(
-                                createNotificationAction({ atype: "error", message: reason, system: false, duration: 3000 })
+                                createNotificationAction({
+                                    atype: "error",
+                                    message: reason,
+                                    system: false,
+                                    duration: 3000,
+                                })
                             );
+                        const fileInput = document.getElementById(inputId) as HTMLInputElement;
+                        fileInput && (fileInput.value = "");
                     }
                 );
             }
         },
-        [state.id, id, onAction, props.onUploadAction, props.uploadData, notify, updateVarName, dispatch, module]
+        [state.id, id, onAction, props.onUploadAction, props.uploadData, notify, updateVarName, dispatch, module, inputId]
     );
 
     const handleChange = useCallback(

+ 16 - 14
frontend/taipy-gui/src/components/Taipy/Input.spec.tsx

@@ -173,6 +173,19 @@ describe("Input Component", () => {
         const visibilityButton = getByLabelText("toggle password visibility");
         expect(visibilityButton).toBeInTheDocument();
     });
+    it("should prevent default action when mouse down event occurs on password visibility button", async () => {
+        const { getByLabelText } = render(<Input value={"Test Input"} type="password" />);
+        const visibilityButton = getByLabelText("toggle password visibility");
+        const keyDown = createEvent.mouseDown(visibilityButton);
+        fireEvent(visibilityButton, keyDown);
+        expect(keyDown.defaultPrevented).toBe(true);
+    });
+    it("parses actionKeys correctly", () => {
+        const { rerender } = render(<Input type="text" value="test" actionKeys="Enter;Escape;F1" />);
+        rerender(<Input type="text" value="test" actionKeys="Enter;F1;F2" />);
+        rerender(<Input type="text" value="test" actionKeys="F1;F2;F3" />);
+        rerender(<Input type="text" value="test" actionKeys="F2;F3;F4" />);
+    });
 });
 
 describe("Number Component", () => {
@@ -195,9 +208,11 @@ describe("Number Component", () => {
         getByDisplayValue("1");
     });
     it("is disabled", async () => {
-        const { getByDisplayValue } = render(<Input value={"33"} type="number" active={false} />);
+        const { getByDisplayValue, getByLabelText } = render(<Input value={"33"} type="number" active={false} />);
         const elt = getByDisplayValue("33");
         expect(elt).toBeDisabled();
+        const upSpinner = getByLabelText("Increment value");
+        expect(upSpinner).toBeDisabled();
     });
     it("is enabled by default", async () => {
         const { getByDisplayValue } = render(<Input value={"33"} type="number" />);
@@ -309,12 +324,6 @@ describe("Number Component", () => {
         await user.keyboard("[ArrowDown]");
         expect(elt.value).toBe("0");
     });
-    it("parses actionKeys correctly", () => {
-        const { rerender } = render(<Input type="text" value="test" actionKeys="Enter;Escape;F1" />);
-        rerender(<Input type="text" value="test" actionKeys="Enter;F1;F2" />);
-        rerender(<Input type="text" value="test" actionKeys="F1;F2;F3" />);
-        rerender(<Input type="text" value="test" actionKeys="F2;F3;F4" />);
-    });
     it("it should not decrement below the min value", () => {
         const { getByLabelText } = render(<Input id={"Test Input"} type="number" value="0" min={0} />);
         const downSpinner = getByLabelText("Decrement value");
@@ -333,11 +342,4 @@ describe("Number Component", () => {
             expect(inputElement.value).toBe("20");
         });
     });
-    it("should prevent default action when mouse down event occurs on password visibility button", async () => {
-        const { getByLabelText } = render(<Input value={"Test Input"} type="password" />);
-        const visibilityButton = getByLabelText("toggle password visibility");
-        const keyDown = createEvent.mouseDown(visibilityButton);
-        fireEvent(visibilityButton, keyDown);
-        expect(keyDown.defaultPrevented).toBe(true);
-    });
 });

+ 20 - 17
frontend/taipy-gui/src/components/Taipy/Input.tsx

@@ -277,6 +277,7 @@ const Input = (props: TaipyInputProps) => {
                                       aria-label="Increment value"
                                       size="small"
                                       onMouseDown={handleUpStepperMouseDown}
+                                      disabled={!active}
                                   >
                                       <ArrowDropUpIcon fontSize="inherit" />
                                   </IconButton>
@@ -284,6 +285,7 @@ const Input = (props: TaipyInputProps) => {
                                       aria-label="Decrement value"
                                       size="small"
                                       onMouseDown={handleDownStepperMouseDown}
+                                      disabled={!active}
                                   >
                                       <ArrowDropDownIcon fontSize="inherit" />
                                   </IconButton>
@@ -309,6 +311,7 @@ const Input = (props: TaipyInputProps) => {
                   }
                 : undefined,
         [
+            active,
             type,
             step,
             min,
@@ -330,23 +333,23 @@ const Input = (props: TaipyInputProps) => {
     return (
         <Tooltip title={hover || ""}>
             <>
-            <TextField
-                sx={textSx}
-                margin="dense"
-                hiddenLabel
-                value={value ?? ""}
-                className={`${className} ${getComponentClassName(props.children)}`}
-                type={showPassword && type == "password" ? "text" : type}
-                id={id}
-                slotProps={inputProps}
-                label={props.label}
-                onChange={handleInput}
-                disabled={!active}
-                onKeyDown={handleAction}
-                multiline={multiline}
-                minRows={linesShown}
-            />
-            {props.children}
+                <TextField
+                    sx={textSx}
+                    margin="dense"
+                    hiddenLabel
+                    value={value ?? ""}
+                    className={`${className} ${getComponentClassName(props.children)}`}
+                    type={showPassword && type == "password" ? "text" : type}
+                    id={id}
+                    slotProps={inputProps}
+                    label={props.label}
+                    onChange={handleInput}
+                    disabled={!active}
+                    onKeyDown={handleAction}
+                    multiline={multiline}
+                    minRows={linesShown}
+                />
+                {props.children}
             </>
         </Tooltip>
     );