Skip to content

Commit

Permalink
reset file selector input after upload (#2280)
Browse files Browse the repository at this point in the history
* reset file selector input after upload
disable number input arrows
resolves #2103

* lint

---------

Co-authored-by: Fred Lefévère-Laoide <[email protected]>
  • Loading branch information
FredLL-Avaiga and Fred Lefévère-Laoide authored Nov 26, 2024
1 parent 8f0142b commit f67b93a
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 43 deletions.
42 changes: 30 additions & 12 deletions frontend/taipy-gui/src/components/Taipy/FileSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) =>
Expand Down Expand Up @@ -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(
Expand Down
30 changes: 16 additions & 14 deletions frontend/taipy-gui/src/components/Taipy/Input.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand All @@ -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" />);
Expand Down Expand Up @@ -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");
Expand All @@ -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);
});
});
37 changes: 20 additions & 17 deletions frontend/taipy-gui/src/components/Taipy/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,13 +277,15 @@ const Input = (props: TaipyInputProps) => {
aria-label="Increment value"
size="small"
onMouseDown={handleUpStepperMouseDown}
disabled={!active}
>
<ArrowDropUpIcon fontSize="inherit" />
</IconButton>
<IconButton
aria-label="Decrement value"
size="small"
onMouseDown={handleDownStepperMouseDown}
disabled={!active}
>
<ArrowDropDownIcon fontSize="inherit" />
</IconButton>
Expand All @@ -309,6 +311,7 @@ const Input = (props: TaipyInputProps) => {
}
: undefined,
[
active,
type,
step,
min,
Expand All @@ -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>
);
Expand Down

0 comments on commit f67b93a

Please sign in to comment.