Browse Source

#1735 pane close button (#1755)

* Show a open button when the pane is closed (via show_button property)
resolves #1735

* Show a open button when the pane is closed (via show_button property)
resolves #1735

* Fab's comment

Co-authored-by: Fabien Lelaquais <86590727+FabienLelaquais@users.noreply.github.com>

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Co-authored-by: Fabien Lelaquais <86590727+FabienLelaquais@users.noreply.github.com>
Fred Lefévère-Laoide 8 months ago
parent
commit
35c4ebe46e

+ 18 - 2
frontend/taipy-gui/src/components/Taipy/Pane.spec.tsx

@@ -140,7 +140,7 @@ describe("Pane Component", () => {
     it("dispatch a well formed message on close", async () => {
         const dispatch = jest.fn();
         const state: TaipyState = INITIAL_STATE;
-        const { getByText } = render(
+        render(
             <TaipyContext.Provider value={{ state, dispatch }}>
                 <HelmetProvider>
                     <Pane id="testId" page="page" open={true} onClose="testCloseAction" />
@@ -152,7 +152,7 @@ describe("Pane Component", () => {
         elt && await userEvent.click(elt);
         expect(dispatch).toHaveBeenLastCalledWith({
             name: "testId",
-            payload: { action: "testCloseAction", args: [] },
+            payload: { action: "testCloseAction", args: [false] },
             type: "SEND_ACTION_ACTION",
         });
     });
@@ -177,4 +177,20 @@ describe("Pane Component", () => {
             type: "SEND_UPDATE_ACTION",
         });
     });
+    it("shows a button when closed with property", async () => {
+        const dispatch = jest.fn();
+        const state: TaipyState = INITIAL_STATE;
+        const { getByRole } = render(
+            <TaipyContext.Provider value={{ state, dispatch }}>
+                <HelmetProvider>
+                    <Pane page="page" open={false} showButton={true} persistent={true} onClose="testCloseAction" />
+                </HelmetProvider>
+            </TaipyContext.Provider>
+        );
+        const elt = document.querySelector(".MuiBackdrop-root");
+        expect(elt).toBeNull();
+        const but = getByRole("button");
+        expect(but).not.toBeDisabled();
+    });
+
 });

+ 40 - 7
frontend/taipy-gui/src/components/Taipy/Pane.tsx

@@ -17,13 +17,16 @@ import Divider from "@mui/material/Divider";
 import Drawer from "@mui/material/Drawer";
 import IconButton from "@mui/material/IconButton";
 import Tooltip from "@mui/material/Tooltip";
+
 import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
 import ChevronRightIcon from "@mui/icons-material/ChevronRight";
+import ExpandLess from "@mui/icons-material/ExpandLess";
+import ExpandMore from "@mui/icons-material/ExpandMore";
 
 import { createSendActionNameAction, createSendUpdateAction } from "../../context/taipyReducers";
 import { useClassNames, useDispatch, useDynamicProperty, useModule } from "../../utils/hooks";
 import TaipyRendered from "../pages/TaipyRendered";
-import { TaipyActiveProps, TaipyChangeProps } from "./utils";
+import { getSuffixedClassNames, TaipyActiveProps, TaipyChangeProps } from "./utils";
 
 type AnchorType = "left" | "bottom" | "right" | "top" | undefined;
 
@@ -38,6 +41,7 @@ interface PaneProps extends TaipyActiveProps, TaipyChangeProps {
     partial?: boolean;
     height?: string | number;
     width?: string | number;
+    showButton?: boolean;
 }
 
 const getHeaderSx = (anchor: AnchorType) => {
@@ -61,6 +65,14 @@ const getDrawerSx = (horizontal: boolean, width: string | number, height: string
     },
 });
 
+const buttonDrawerSx = {
+    "& .MuiDrawer-paper": {
+        width: "fit-content",
+        height: "fit-content",
+        background: "transparent",
+    },
+};
+
 const Pane = (props: PaneProps) => {
     const {
         id,
@@ -74,6 +86,7 @@ const Pane = (props: PaneProps) => {
         width = "30vw",
         updateVarName,
         propagate = true,
+        showButton = false,
     } = props;
     const [open, setOpen] = useState(defaultOpen === "true" || defaultOpen === true);
     const dispatch = useDispatch();
@@ -91,12 +104,26 @@ const Pane = (props: PaneProps) => {
 
     const handleClose = useCallback(() => {
         if (active) {
+            setOpen(false);
             if (onClose) {
-                dispatch(createSendActionNameAction(id, module, onClose));
+                Promise.resolve().then(() => dispatch(createSendActionNameAction(id, module, onClose, false)));
             } else if (updateVarName) {
-                dispatch(createSendUpdateAction(updateVarName, false, module, props.onChange, propagate));
-            } else {
-                setOpen(false);
+                Promise.resolve().then(() =>
+                    dispatch(createSendUpdateAction(updateVarName, false, module, props.onChange, propagate))
+                );
+            }
+        }
+    }, [active, dispatch, id, onClose, updateVarName, propagate, props.onChange, module]);
+
+    const handleOpen = useCallback(() => {
+        if (active) {
+            setOpen(true);
+            if (onClose) {
+                Promise.resolve().then(() => dispatch(createSendActionNameAction(id, module, onClose, true)));
+            } else if (updateVarName) {
+                Promise.resolve().then(() =>
+                    dispatch(createSendUpdateAction(updateVarName, true, module, props.onChange, propagate))
+                );
             }
         }
     }, [active, dispatch, id, onClose, updateVarName, propagate, props.onChange, module]);
@@ -107,7 +134,7 @@ const Pane = (props: PaneProps) => {
         }
     }, [props.open]);
 
-    return !persistent || (persistent && open) ? (
+    return open ? (
         <Drawer
             sx={drawerSx}
             variant={persistent ? "permanent" : undefined}
@@ -120,7 +147,7 @@ const Pane = (props: PaneProps) => {
                 <>
                     <Box sx={headerSx}>
                         <IconButton onClick={handleClose} disabled={!active}>
-                            {anchor === "left" ? <ChevronLeftIcon /> : <ChevronRightIcon />}
+                            {anchor === "left" ? <ChevronLeftIcon /> : anchor === "right" ? <ChevronRightIcon />: anchor === "top" ? <ExpandLess/>: <ExpandMore/>}
                         </IconButton>
                     </Box>
                     <Divider />
@@ -133,6 +160,12 @@ const Pane = (props: PaneProps) => {
                 </>
             </Tooltip>
         </Drawer>
+    ) : showButton ? (
+        <Drawer variant="permanent" sx={buttonDrawerSx} anchor={anchor} open={true} className={getSuffixedClassNames(className, "-button")}>
+            <IconButton onClick={handleOpen} disabled={!active}>
+                {anchor === "left" ? <ChevronRightIcon /> : anchor === "right" ? <ChevronLeftIcon /> : anchor === "top" ? <ExpandMore/> : <ExpandLess/>}
+            </IconButton>
+        </Drawer>
     ) : null;
 };
 

+ 1 - 0
taipy/gui/_renderers/factory.py

@@ -427,6 +427,7 @@ class _Factory:
                 ("height", PropertyType.string_or_number, "30vh"),
                 ("hover_text", PropertyType.dynamic_string),
                 ("on_change", PropertyType.function),
+                ("show_button", PropertyType.boolean, False),
             ]
         )
         ._set_propagate(),

+ 6 - 0
taipy/gui/viselements.json

@@ -1883,6 +1883,12 @@
                         "type": "bool",
                         "default_value": "False",
                         "doc": "If False, the pane covers the page where it appeared and disappears if the user clicks in the page.<br/>If True, the pane appears next to the page. Note that the parent section of the pane must have the <i>flex</i> display mode set. See below for an example using the <code>persistent</code> property."
+                    },
+                    {
+                        "name": "show_button",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True and when the pane is closed, a button allowing the pane to be opened is shown."
                     }
                 ]
             }