diff --git a/doc/gui/examples/controls/status_icons.py b/doc/gui/examples/controls/status_icons.py
new file mode 100644
index 000000000..af7d62edd
--- /dev/null
+++ b/doc/gui/examples/controls/status_icons.py
@@ -0,0 +1,16 @@
+from taipy.gui import Gui
+
+status = [
+ ("warning", "Task is launched."),
+ ("warning", "Tasks is waiting."),
+ ("error", "Task timeout."),
+ ("success", "Task Succeeded"),
+ ("info", "Process was cancelled.")
+]
+
+page = """
+<|{status}|status|use_icon|>
+"""
+
+if __name__ == "__main__":
+ Gui(page).run(title="Status - With icons")
diff --git a/doc/gui/examples/controls/status_mixed_icons.py b/doc/gui/examples/controls/status_mixed_icons.py
new file mode 100644
index 000000000..94182bfe3
--- /dev/null
+++ b/doc/gui/examples/controls/status_mixed_icons.py
@@ -0,0 +1,20 @@
+from taipy.gui import Gui
+
+status = [
+ ("warning", "Task is launched."),
+ ("warning", "Tasks is waiting."),
+ ("error", "Task timeout."),
+ ("success", "Task Succeeded"),
+ ("info", "Process was cancelled.")
+]
+
+# Info: svg icon (pants.svg)
+# success: no icon
+# warning: default icon
+# error: inline svg icon (red disc)
+page = """
+<|{status}|status|don't use_icon|use_icon[info]=https://www.svgrepo.com/show/530594/pants.svg|use_icon[success]|use_icon[error]=|>
+""" # noqa: E501
+
+if __name__ == "__main__":
+ Gui(page).run(title="Status - With mixed icons")
diff --git a/doc/gui/examples/controls/status_simple.py b/doc/gui/examples/controls/status_simple.py
new file mode 100644
index 000000000..79cab030b
--- /dev/null
+++ b/doc/gui/examples/controls/status_simple.py
@@ -0,0 +1,16 @@
+from taipy.gui import Gui
+
+status = [
+ ("warning", "Task is launched."),
+ ("warning", "Tasks is waiting."),
+ ("error", "Task timeout."),
+ ("success", "Task Succeeded"),
+ ("info", "Process was cancelled.")
+]
+
+page = """
+<|{status}|status|>
+"""
+
+if __name__ == "__main__":
+ Gui(page).run(title="Status - Simple")
diff --git a/doc/gui/examples/controls/status_svg_icons.py b/doc/gui/examples/controls/status_svg_icons.py
new file mode 100644
index 000000000..3df3ab375
--- /dev/null
+++ b/doc/gui/examples/controls/status_svg_icons.py
@@ -0,0 +1,20 @@
+from taipy.gui import Gui
+
+status = [
+ ("warning", "Task is launched."),
+ ("warning", "Tasks is waiting."),
+ ("error", "Task timeout."),
+ ("success", "Task Succeeded"),
+ ("info", "Process was cancelled.")
+]
+
+# Info: svg icon (pants.svg)
+# success: svg icon (hotel.svg)
+# warning: svg icon (diving-goggles.svg)
+# error: svg icon (hat.svg)
+page = """
+<|{status}|status|use_icon[info]=https://www.svgrepo.com/show/530594/pants.svg|use_icon[success]=https://www.svgrepo.com/show/530595/hotel.svg|use_icon[warning]=https://www.svgrepo.com/show/530596/diving-goggles.svg|use_icon[error]=https://www.svgrepo.com/show/530597/hat.svg|>
+"""
+
+if __name__ == "__main__":
+ Gui(page).run(title="Status - With SVG icons")
diff --git a/doc/gui/extension/example_library/example_library.py b/doc/gui/extension/example_library/example_library.py
index c66c7f870..acba0aeca 100644
--- a/doc/gui/extension/example_library/example_library.py
+++ b/doc/gui/extension/example_library/example_library.py
@@ -42,6 +42,16 @@ def __init__(self) -> None:
# element, exported as GameTable in front-end/src/index.ts
# react_component="GameTable",
),
+ "visual_label_list": Element(
+ "lov",
+ {
+ "lov": ElementProperty(PropertyType.lov),
+ "sort": ElementProperty(PropertyType.string),
+ },
+ # The name of the React component (VisualLabelList) that implements this custom
+ # element, exported as LabeledItemList in front-end/src/index.ts
+ react_component="VisualLabelList",
+ )
}
# The implementation of the rendering for the "fraction" static element
diff --git a/doc/gui/extension/example_library/front-end/src/VisualLabelList.tsx b/doc/gui/extension/example_library/front-end/src/VisualLabelList.tsx
new file mode 100644
index 000000000..793a6499a
--- /dev/null
+++ b/doc/gui/extension/example_library/front-end/src/VisualLabelList.tsx
@@ -0,0 +1,50 @@
+import React, { useMemo } from "react";
+import { LoV, useLovListMemo } from "taipy-gui";
+
+interface VisualLabelListProps {
+ lov?: LoV;
+ defaultLov?: string;
+ sort?: "asc" | "desc";
+}
+
+const styles = {
+ listItem: {
+ display: "flex",
+ alignItems: "center",
+ },
+ image: {
+ marginRight: "8px",
+ width: "1em",
+ height: "1em",
+ },
+};
+
+const VisualLabelList: React.FC = ({ lov, defaultLov = "", sort }) => {
+ const lovList = useLovListMemo(lov, defaultLov);
+
+ const sortedLovList = useMemo(() => {
+ if (sort) {
+ return lovList.slice().sort((a, b) => {
+ return sort === "asc" ? a.id.localeCompare(b.id) : b.id.localeCompare(a.id);
+ });
+ }
+ return lovList;
+ }, [lovList, sort]);
+
+ return (
+
+
+ {sortedLovList.map((item, index) => (
+ -
+ {typeof item.item === "string" ? null : (
+
+ )}
+ {item.id}
+
+ ))}
+
+
+ );
+};
+
+export default VisualLabelList;
diff --git a/doc/gui/extension/example_library/front-end/src/index.ts b/doc/gui/extension/example_library/front-end/src/index.ts
index c7b47323e..c2de08c12 100644
--- a/doc/gui/extension/example_library/front-end/src/index.ts
+++ b/doc/gui/extension/example_library/front-end/src/index.ts
@@ -8,5 +8,6 @@
// the name used in the element declaration in the element library.
import ColoredLabel from "./ColoredLabel";
import GameTable from "./GameTable";
+import VisualLabelList from "./VisualLabelList";
-export { ColoredLabel as ExampleLabel, GameTable };
+export { ColoredLabel as ExampleLabel, GameTable, VisualLabelList };
diff --git a/doc/gui/extension/images/cpp.png b/doc/gui/extension/images/cpp.png
new file mode 100644
index 000000000..a02e2eaa6
Binary files /dev/null and b/doc/gui/extension/images/cpp.png differ
diff --git a/doc/gui/extension/images/java.png b/doc/gui/extension/images/java.png
new file mode 100644
index 000000000..78fa9c5d7
Binary files /dev/null and b/doc/gui/extension/images/java.png differ
diff --git a/doc/gui/extension/images/javascript.png b/doc/gui/extension/images/javascript.png
new file mode 100644
index 000000000..9f148627c
Binary files /dev/null and b/doc/gui/extension/images/javascript.png differ
diff --git a/doc/gui/extension/images/python.png b/doc/gui/extension/images/python.png
new file mode 100644
index 000000000..a29196d44
Binary files /dev/null and b/doc/gui/extension/images/python.png differ
diff --git a/doc/gui/extension/images/typescript.png b/doc/gui/extension/images/typescript.png
new file mode 100644
index 000000000..ec4a95e24
Binary files /dev/null and b/doc/gui/extension/images/typescript.png differ
diff --git a/doc/gui/extension/visual_label_list.py b/doc/gui/extension/visual_label_list.py
new file mode 100644
index 000000000..73cc5efa5
--- /dev/null
+++ b/doc/gui/extension/visual_label_list.py
@@ -0,0 +1,28 @@
+# 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 example_library import ExampleLibrary
+
+from taipy.gui import Gui, Icon
+
+languages = [
+ ["Python", Icon("images/python.png", "Python logo")],
+ ["JavaScript", Icon("images/javascript.png", "JavaScript logo")],
+ ["TypeScript", Icon("images/typescript.png", "TypeScript logo")],
+ ["Java", Icon("images/java.png", "Java logo")],
+ ["C++", Icon("images/cpp.png", "C++ logo")],
+]
+
+page = """
+<|{languages}|example.visual_label_list|sort=asc|>
+"""
+
+if __name__ == "__main__":
+ Gui(page, libraries=[ExampleLibrary()]).run(title="List of item")
diff --git a/frontend/taipy-gui/src/components/Taipy/Status.spec.tsx b/frontend/taipy-gui/src/components/Taipy/Status.spec.tsx
index 072472a0c..d584fa6fa 100644
--- a/frontend/taipy-gui/src/components/Taipy/Status.spec.tsx
+++ b/frontend/taipy-gui/src/components/Taipy/Status.spec.tsx
@@ -42,14 +42,14 @@ describe("Status Component", () => {
await userEvent.click(elt);
expect(myClose).toHaveBeenCalled();
})
- it("displays the icon", async () => {
- const {getByTestId} = render(} onClose={jest.fn()} />);
+ it("displays the open icon", async () => {
+ const {getByTestId} = render(} onClose={jest.fn()} />);
getByTestId("PlusOneOutlinedIcon");
})
// Test case for Inline SVG content
it("renders an Avatar with inline SVG", () => {
const inlineSvg = "";
- const { getByTestId } = render();
+ const { getByTestId } = render();
const avatar = getByTestId("Avatar");
// Inline SVG should be rendered as inner HTML inside the Avatar
const svgElement = avatar.querySelector("svg");
@@ -57,44 +57,44 @@ describe("Status Component", () => {
});
// Test case for Text content (default behavior)
- it("renders Avatar with initial when content is text", () => {
- const { getByTestId } = render();
+ it("renders Avatar with initial when icon is text", () => {
+ const { getByTestId } = render();
const avatar = getByTestId("Avatar");
expect(avatar).toHaveTextContent("S");
});
// Test case for empty content
- it("renders Avatar with initial when no content is provided", () => {
- const { getByTestId } = render();
+ it("renders Avatar with initial when no icon is provided", () => {
+ const { getByTestId } = render();
const avatar = getByTestId("Avatar");
expect(avatar).toHaveTextContent("S");
});
// Test case for an invalid content type (like a non-SVG string)
- it("renders Avatar with initial if content is invalid", () => {
- const { getByTestId } = render();
+ it("renders Avatar with initial if icon is invalid", () => {
+ const { getByTestId } = render();
const avatar = getByTestId("Avatar");
expect(avatar).toHaveTextContent("S");
});
- it("renders an avatar with initial when withIcons is false", () => {
+ it("renders an avatar with initial when icon is false", () => {
const statusWithoutIcons: StatusType = { status: "warning", message: "Warning detected" };
-
- const { getByTestId } = render();
-
+
+ const { getByTestId } = render();
+
// Check if the avatar has the initial of the status (W)
const avatar = getByTestId("Avatar");
expect(avatar).toHaveTextContent("W");
});
- it("renders the correct icon when withIcons is true", () => {
+ it("renders the correct icon when icon is true", () => {
const statusWithIcons: StatusType = { status: "success", message: "Operation successful" };
-
- const { getByTestId } = render();
-
+
+ const { getByTestId } = render();
+
// Check if the Avatar element contains the icon (CheckCircleIcon for success status)
const avatar = getByTestId("Avatar");
-
+
// Check if the avatar contains the appropriate icon, in this case CheckCircleIcon
// Since CheckCircleIcon is rendered as part of the Avatar, we can check for its presence by looking for SVGs or icon classes
const icon = avatar.querySelector("svg");
diff --git a/frontend/taipy-gui/src/components/Taipy/Status.tsx b/frontend/taipy-gui/src/components/Taipy/Status.tsx
index b9985c46f..2f15fd7a2 100644
--- a/frontend/taipy-gui/src/components/Taipy/Status.tsx
+++ b/frontend/taipy-gui/src/components/Taipy/Status.tsx
@@ -11,7 +11,7 @@
* specific language governing permissions and limitations under the License.
*/
-import React, { MouseEvent, ReactNode, useEffect, useMemo, useRef} from "react";
+import React, { MouseEvent, ReactNode, useEffect, useMemo, useRef } from "react";
import Chip from "@mui/material/Chip";
import Avatar from "@mui/material/Avatar";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
@@ -31,9 +31,8 @@ export interface StatusType {
interface StatusProps extends TaipyBaseProps {
value: StatusType;
onClose?: (evt: MouseEvent) => void;
- icon?: ReactNode;
- withIcons?: boolean;
- content?: string;
+ openedIcon?: ReactNode;
+ icon ?: boolean | string;
}
const status2Color = (status: string): "error" | "info" | "success" | "warning" => {
@@ -51,111 +50,92 @@ const status2Color = (status: string): "error" | "info" | "success" | "warning"
};
// Function to get the appropriate icon based on the status
-const GetStatusIcon = (status: string, withIcons?: boolean): ReactNode => {
+const getStatusIcon = (status: string, icon?: boolean): ReactNode => {
// Use useMemo to memoize the iconProps as well
const color = status2Color(status);
// Memoize the iconProps
const iconProps = {
- sx: { fontSize: 20, color: `${color}.main` }}
-
- if (withIcons) {
- switch (color) {
- case "success":
- return ;
- case "warning":
- return ;
- case "error":
- return ;
- default:
- return ;
- }
- } else {
- return getInitials(status);
+ sx: { fontSize: 20, color: `${color}.main` },
+ };
+
+ if (icon) {
+ switch (color) {
+ case "success":
+ return ;
+ case "warning":
+ return ;
+ case "error":
+ return ;
+ default:
+ return ;
}
-
+ } else {
+ return getInitials(status);
+ }
};
-
const chipSx = { alignSelf: "flex-start" };
const defaultAvatarStyle = {
- width: '100%',
- height: '100%',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- };
+ width: "100%",
+ height: "100%",
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center",
+};
const defaultAvatarSx = {
- bgcolor: 'transparent'
+ bgcolor: "transparent",
};
const baseStyles = {
- fontSize: '1rem',
- textShadow: '1px 1px 4px black, -1px -1px 4px black',
+ fontSize: "1rem",
+ textShadow: "1px 1px 4px black, -1px -1px 4px black",
};
-const isSvgUrl = (content?: string) => {
- return content?.substring(content?.length - 4).toLowerCase() === ".svg"; // Check if it ends with ".svg"
-};
+const isSvgUrl = (icon: boolean | string) =>
+ typeof icon === "string" && icon.substring(icon?.length - 4).toLowerCase() === ".svg"; // Check if it ends with ".svg"
-const isInlineSvg = (content?: string) => {
-return content?.substring(0, 4).toLowerCase() === "