Bläddra i källkod

Created a new visual element - progress bar (#1303)

* created progress bar element
* updated ProgressBar.tsx and factory.py
* removed unnecessary functions
* updated the value property in progress bar
* bug: fixed type error in val
* removed WithValue functions
* removed unwanted params
* renamed ProgressBar to Progress
* init: progress element tests
* updated progress element tests
* all progress tests passed
* improved quality and style
* added python tests and fixed typo in factory.py
* removed type Helpers
* added progress in viselements.json
* moved progress to sorted part
* added default_value label to progress
* improved pytests
* copied viselements.json from the develop branch
* added property to show or hide the progress component
* changed default value of render to true
* added progress to viselements.json and fixed errors in test_progress.py
* added render property to viselements.json
* changed bool to dynamic(bool) in render
* minor changes in pytests
* changed default value of linear to False in viselements.json
* Added default_value entry in element doc + reorder elements
---------

Co-authored-by: Fabien Lelaquais <86590727+FabienLelaquais@users.noreply.github.com>
Yaten Dhingra 11 månader sedan
förälder
incheckning
8a8fb36d15

+ 46 - 0
frontend/taipy-gui/src/components/Taipy/Progress.spec.tsx

@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021-2024 Avaiga Private Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+import React from "react";
+
+import { render } from "@testing-library/react";
+import "@testing-library/jest-dom";
+
+import Progress from "./Progress";
+
+describe("Progress component", () => {
+    it("renders circular progress without value (indeterminate)", () => {
+        const { getByRole } = render(<Progress />);
+        const elt = getByRole("progressbar");
+        expect(elt).toHaveClass("MuiCircularProgress-root");
+    });
+    it("renders circular progress with value (determinate)", () => {
+        const { getByRole, getByText } = render(<Progress showValue value={50} />);
+        const elt = getByRole("progressbar");
+        const valueText = getByText("50%");
+        expect(elt).toHaveClass("MuiCircularProgress-root");
+        expect(valueText).toBeInTheDocument();
+    });
+    it("renders linear progress without value (inderminate)", () => {
+        const { getByRole } = render(<Progress linear />);
+        const elt = getByRole("progressbar");
+        expect(elt).toHaveClass("MuiLinearProgress-root");
+    });
+    it("renders linear progress with value (determinate)", () => {
+        const { getByRole, getByText } = render(<Progress linear showValue value={50} />);
+        const elt = getByRole("progressbar");
+        const valueText = getByText("50%");
+        expect(elt).toHaveClass("MuiLinearProgress-root");
+        expect(valueText).toBeInTheDocument();
+    });
+});

+ 80 - 0
frontend/taipy-gui/src/components/Taipy/Progress.tsx

@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021-2024 Avaiga Private Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+import React from "react";
+
+import Box from "@mui/material/Box";
+import CircularProgress from "@mui/material/CircularProgress";
+import LinearProgress from "@mui/material/LinearProgress";
+import Typography from "@mui/material/Typography";
+
+import { useDynamicProperty } from "../../utils/hooks";
+
+interface ProgressBarProps {
+    linear?: boolean; //by default - false
+    showValue?: boolean; //by default - false
+    value?: number; //progress value
+    defaultValue?: number; //default progress value
+    render?: boolean;
+    defaultRender?: boolean;
+}
+
+const Progress = (props: ProgressBarProps) => {
+    const { linear, showValue } = props;
+
+    const value = useDynamicProperty(props.value, props.defaultValue, undefined);
+    const render = useDynamicProperty(props.render, props.defaultRender, true);
+
+    if (!render) {
+        return null;
+    }
+
+    return showValue && value !== undefined ? (
+        linear ? (
+            <Box sx={{ display: "flex", alignItems: "center" }}>
+                <Box sx={{ width: "100%", mr: 1 }}>
+                    <LinearProgress variant="determinate" value={value} />
+                </Box>
+                <Box sx={{ minWidth: 35 }}>
+                    <Typography variant="body2" color="text.secondary">{`${Math.round(value)}%`}</Typography>
+                </Box>
+            </Box>
+        ) : (
+            <Box sx={{ position: "relative", display: "inline-flex" }}>
+                <CircularProgress variant="determinate" value={value} />
+                <Box
+                    sx={{
+                        top: 0,
+                        left: 0,
+                        bottom: 0,
+                        right: 0,
+                        position: "absolute",
+                        display: "flex",
+                        alignItems: "center",
+                        justifyContent: "center",
+                    }}
+                >
+                    <Typography variant="caption" component="div" color="text.secondary">
+                        {`${Math.round(value)}%`}
+                    </Typography>
+                </Box>
+            </Box>
+        )
+    ) : linear ? (
+        <LinearProgress variant={value === undefined ? "indeterminate" : "determinate"} value={value} />
+    ) : (
+        <CircularProgress variant={value === undefined ? "indeterminate" : "determinate"} value={value} />
+    );
+};
+
+export default Progress;

+ 2 - 0
frontend/taipy-gui/src/components/Taipy/index.ts

@@ -34,6 +34,7 @@ import NavBar from "./NavBar";
 import PageContent from "../pages/PageContent";
 import Pane from "./Pane";
 import Part from "./Part";
+import Progress from "./Progress";
 import Selector from "./Selector";
 import Slider from "./Slider";
 import StatusList from "./StatusList";
@@ -74,6 +75,7 @@ export const getRegisteredComponents = () => {
             Table: Table,
             Toggle: Toggle,
             TreeView: TreeView,
+            Progress: Progress,
         }).forEach(([name, comp]) => (registeredComponents[name] = comp  as ComponentType));
         if (window.taipyConfig?.extensions) {
             Object.entries(window.taipyConfig.extensions).forEach(([libName, elts]) => {

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

@@ -50,6 +50,7 @@ class _Factory:
         "number": "value",
         "pane": "open",
         "part": "class_name",
+        "progress": "value",
         "selector": "value",
         "slider": "value",
         "status": "value",
@@ -600,6 +601,21 @@ class _Factory:
             ]
         )
         ._set_propagate(),
+        "progress": lambda gui, control_type, attrs: _Builder(
+            gui=gui,
+            control_type=control_type,
+            element_name="Progress",
+            attributes=attrs,
+        )
+        .set_value_and_default(var_type=PropertyType.dynamic_number)
+        .set_attributes(
+            [
+                ("linear", PropertyType.boolean, False),
+                ("show_value", PropertyType.boolean, False),
+                ("render", PropertyType.dynamic_boolean, True)
+            ]
+        )
+        ._set_propagate(),
     }
 
     # TODO: process \" in property value

+ 31 - 0
taipy/gui/viselements.json

@@ -1196,6 +1196,37 @@
                 ]
             }
         ],
+        [
+            "progress",
+            {
+                "properties": [
+                    {
+                        "name": "value",
+                        "type": "dynamic(int)",
+                        "doc": "If set, then the value represents the progress percentage that is shown.TODO - if unset?",
+                        "default_property": "true"
+                    },
+                    {
+                        "name": "linear",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If set to True, the control displays a linear progress indicator instead of a circular one."
+                    },
+                    {
+                        "name": "show_value",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If set to True, the progress value is shown."
+                    },
+                    {
+                        "name": "render",
+                        "type": "dynamic(bool)",
+                        "doc": "If False, this progress indicator is hidden from the page.",
+                        "default_property": "true"
+                    }
+                ]
+            }
+        ],
         [
             "indicator",
             {

+ 20 - 0
tests/gui/builder/control/test_progress.py

@@ -0,0 +1,20 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+import taipy.gui.builder as tgb
+from taipy.gui import Gui
+
+
+def test_progress_builder(gui: Gui, helpers):
+    with tgb.Page(frame=None) as page:
+        tgb.progress(linear="true", show_value="true", value={50})  # type: ignore[attr-defined]
+    expected_list = ["<Progress", 'linear={true}', 'showValue={true}', 'value="{50}"']
+    helpers.test_control_builder(gui, page, expected_list)

+ 60 - 0
tests/gui/control/test_progress.py

@@ -0,0 +1,60 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+from taipy.gui import Gui
+
+
+def test_circular_progress_indeterminate_md(gui: Gui, helpers):
+    md_string = "<|progress|>"
+    expected_list = ["<Progress"]
+    helpers.test_control_md(gui, md_string, expected_list)
+
+
+def test_circular_progress_determinate_md(gui: Gui, helpers):
+    md_string = "<|progress|value=50|show_value|>"
+    expected_list = ["<Progress", 'value="50"']
+    helpers.test_control_md(gui, md_string, expected_list)
+
+
+def test_linear_progress_indeterminate_md(gui: Gui, helpers):
+    md_string = "<|progress|linear|>"
+    expected_list = ["<Progress", "linear={true}"]
+    helpers.test_control_md(gui, md_string, expected_list)
+
+
+def test_linear_progress_determinate_md(gui: Gui, helpers):
+    md_string = "<|progress|value=50|show_value|linear|>"
+    expected_list = ["<Progress", 'value="50"', "linear={true}"]
+    helpers.test_control_md(gui, md_string, expected_list)
+
+
+def test_circular_progress_indeterminate_html(gui: Gui, helpers):
+    html_string = "<taipy:progress/>"
+    expected_list = ["<Progress"]
+    helpers.test_control_html(gui, html_string, expected_list)
+
+
+def test_circular_progress_determinate_html(gui: Gui, helpers):
+    html_string = '<taipy:progress show_value value="50"/>'
+    expected_list = ["<Progress", 'value="50"']
+    helpers.test_control_html(gui, html_string, expected_list)
+
+
+def test_linear_progress_indeterminate_html(gui: Gui, helpers):
+    html_string = "<taipy:progress linear/>"
+    expected_list = ["<Progress", 'linear={true}']
+    helpers.test_control_html(gui, html_string, expected_list)
+
+
+def test_linear_progress_determinate_html(gui: Gui, helpers):
+    html_string = '<taipy:progress linear show_value value="50"/>'
+    expected_list = ["<Progress", "linear={true}", 'value="50"']
+    helpers.test_control_html(gui, html_string, expected_list)