@wandelbots/wandelbots-js-react-components 2.31.0 → 2.32.0-pr.feature-robot-precondition-list.372.8c2beb0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wandelbots/wandelbots-js-react-components",
3
- "version": "2.31.0",
3
+ "version": "2.32.0-pr.feature-robot-precondition-list.372.8c2beb0",
4
4
  "description": "React UI toolkit for building applications on top of the Wandelbots platform",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -59,8 +59,8 @@
59
59
  "@rollup/plugin-node-resolve": "^16.0.0",
60
60
  "@rollup/plugin-terser": "^0.4.4",
61
61
  "@rollup/plugin-typescript": "^12.1.2",
62
- "@storybook/addon-docs": "^9.0.8",
63
- "@storybook/react-vite": "^9.0.8",
62
+ "@storybook/addon-docs": "^9.1.2",
63
+ "@storybook/react-vite": "^9.1.2",
64
64
  "@storybook/test-runner": "^0.23.0",
65
65
  "@svgr/rollup": "^8.1.0",
66
66
  "@testing-library/jest-dom": "^6.6.3",
@@ -72,7 +72,7 @@
72
72
  "@vitejs/plugin-react": "^4.3.4",
73
73
  "@wandelbots/nova-js": "^2.1.0",
74
74
  "add": "^2.0.6",
75
- "eslint-plugin-storybook": "^9.0.8",
75
+ "eslint-plugin-storybook": "^9.1.2",
76
76
  "glob": "^11.0.1",
77
77
  "http-server": "^14.1.1",
78
78
  "husky": "^9.1.7",
@@ -95,7 +95,7 @@
95
95
  "rollup-plugin-peer-deps-external": "^2.2.4",
96
96
  "rollup-plugin-postcss": "^4.0.2",
97
97
  "semantic-release": "^24.2.3",
98
- "storybook": "^9.0.8",
98
+ "storybook": "^9.1.2",
99
99
  "storybook-preset-inline-svg": "^1.0.1",
100
100
  "three": "^0.174.0",
101
101
  "three-stdlib": "^2.35.14",
@@ -0,0 +1,148 @@
1
+ import { Chip, useTheme } from "@mui/material"
2
+ import type {
3
+ RobotControllerStateOperationModeEnum,
4
+ RobotControllerStateSafetyStateEnum,
5
+ } from "@wandelbots/nova-js/v1"
6
+ import { observer } from "mobx-react-lite"
7
+ import { useTranslation } from "react-i18next"
8
+ import { externalizeComponent } from "../externalizeComponent"
9
+ import type { ProgramState } from "./ProgramControl"
10
+
11
+ export interface ProgramStateIndicatorProps {
12
+ /** The current state of the program */
13
+ programState: ProgramState
14
+ /** The current safety state of the robot controller */
15
+ safetyState: RobotControllerStateSafetyStateEnum
16
+ /** The current operation mode of the robot controller */
17
+ operationMode: RobotControllerStateOperationModeEnum
18
+ /** Additional CSS class name */
19
+ className?: string
20
+ }
21
+
22
+ /**
23
+ * A state indicator component that displays the current program execution state
24
+ * combined with robot controller safety and operation mode states.
25
+ *
26
+ * Features:
27
+ * - Combines program state with safety and operation mode states
28
+ * - Color-coded based on state severity (success, warning, error)
29
+ * - Rendered as Material-UI filled chip
30
+ * - Localization support via react-i18next
31
+ */
32
+ export const ProgramStateIndicator = externalizeComponent(
33
+ observer(
34
+ ({
35
+ programState,
36
+ safetyState,
37
+ operationMode,
38
+ className,
39
+ }: ProgramStateIndicatorProps) => {
40
+ const theme = useTheme()
41
+ const { t } = useTranslation()
42
+
43
+ const getStateInfo = () => {
44
+ // First check for emergency stop or critical safety states
45
+ if (
46
+ safetyState === "SAFETY_STATE_DEVICE_EMERGENCY_STOP" ||
47
+ safetyState === "SAFETY_STATE_ROBOT_EMERGENCY_STOP" ||
48
+ safetyState === "SAFETY_STATE_STOP_0" ||
49
+ safetyState === "SAFETY_STATE_STOP_1" ||
50
+ safetyState === "SAFETY_STATE_STOP_2" ||
51
+ safetyState === "SAFETY_STATE_PROTECTIVE_STOP" ||
52
+ safetyState === "SAFETY_STATE_STOP" ||
53
+ safetyState === "SAFETY_STATE_REDUCED" ||
54
+ safetyState === "SAFETY_STATE_MASTERING" ||
55
+ safetyState === "SAFETY_STATE_CONFIRM_SAFETY" ||
56
+ safetyState === "SAFETY_STATE_OPERATOR_SAFETY" ||
57
+ safetyState === "SAFETY_STATE_RECOVERY" ||
58
+ safetyState === "SAFETY_STATE_VIOLATION"
59
+ ) {
60
+ return {
61
+ label: t("ProgramStateIndicator.EStop.lb"),
62
+ color: theme.palette.error.main,
63
+ }
64
+ }
65
+
66
+ // Check for error states
67
+ if (
68
+ safetyState === "SAFETY_STATE_UNKNOWN" ||
69
+ safetyState === "SAFETY_STATE_FAULT"
70
+ ) {
71
+ return {
72
+ label: t("ProgramStateIndicator.Error.lb"),
73
+ color: theme.palette.error.main,
74
+ }
75
+ }
76
+
77
+ // For normal safety states, check program state
78
+ if (safetyState === "SAFETY_STATE_NORMAL") {
79
+ switch (programState) {
80
+ case "running":
81
+ return {
82
+ label: t("ProgramStateIndicator.Running.lb"),
83
+ color: theme.palette.success.main,
84
+ }
85
+ case "paused":
86
+ return {
87
+ label: t("ProgramStateIndicator.Paused.lb"),
88
+ color: theme.palette.grey[600],
89
+ }
90
+ case "stopping":
91
+ return {
92
+ label: t("ProgramStateIndicator.Stopped.lb"),
93
+ color: theme.palette.error.main,
94
+ }
95
+ case "idle":
96
+ default:
97
+ return {
98
+ label: t("ProgramStateIndicator.Ready.lb"),
99
+ color: theme.palette.success.main,
100
+ }
101
+ }
102
+ }
103
+
104
+ // Default fallback
105
+ return {
106
+ label: t("ProgramStateIndicator.Idle.lb"),
107
+ color: theme.palette.grey[600],
108
+ }
109
+ }
110
+
111
+ const { label, color } = getStateInfo()
112
+
113
+ // Add operation mode suffix if not automatic
114
+ const getOperationModeText = () => {
115
+ switch (operationMode) {
116
+ case "OPERATION_MODE_AUTO":
117
+ return t("ProgramStateIndicator.Auto.lb")
118
+ case "OPERATION_MODE_MANUAL":
119
+ return t("ProgramStateIndicator.Manual.lb")
120
+ case "OPERATION_MODE_MANUAL_T1":
121
+ return t("ProgramStateIndicator.ManualT1.lb")
122
+ case "OPERATION_MODE_MANUAL_T2":
123
+ return t("ProgramStateIndicator.ManualT2.lb")
124
+ default:
125
+ return t("ProgramStateIndicator.Auto.lb") // Default to Auto for unknown modes
126
+ }
127
+ }
128
+
129
+ const fullLabel = `${label} / ${getOperationModeText()}`
130
+
131
+ return (
132
+ <Chip
133
+ className={className}
134
+ label={fullLabel}
135
+ variant="filled"
136
+ sx={{
137
+ backgroundColor: color,
138
+ color: theme.palette.getContrastText(color),
139
+ fontWeight: 500,
140
+ "& .MuiChip-label": {
141
+ paddingX: 2,
142
+ },
143
+ }}
144
+ />
145
+ )
146
+ },
147
+ ),
148
+ )
@@ -0,0 +1,153 @@
1
+ import ErrorIcon from "@mui/icons-material/Error"
2
+ import { Box, Divider, Typography, useTheme } from "@mui/material"
3
+ import { observer } from "mobx-react-lite"
4
+ import type { ComponentType } from "react"
5
+ import { externalizeComponent } from "../externalizeComponent"
6
+ import { RobotIcon } from "../icons"
7
+ import {
8
+ RobotSetupReadinessIndicator,
9
+ RobotSetupReadinessState,
10
+ } from "./RobotSetupReadinessIndicator"
11
+
12
+ export interface RobotListItemProps {
13
+ /** The name of the robot */
14
+ robotName: string
15
+ /** The type/model of the robot (will try to derive from nova if not provided) */
16
+ robotType?: string
17
+ /** The current setup readiness state of the robot */
18
+ setupState: RobotSetupReadinessState
19
+ /**
20
+ * Component to render for the precondition indicator.
21
+ * Defaults to RobotSetupReadinessIndicator.
22
+ * Pass null or undefined to hide the indicator.
23
+ */
24
+ PreconditionComponent?: ComponentType<{
25
+ setupState: RobotSetupReadinessState
26
+ }> | null
27
+ /** Additional CSS class name */
28
+ className?: string
29
+ }
30
+
31
+ /**
32
+ * A list item component that displays robot information and setup readiness state.
33
+ *
34
+ * Features:
35
+ * - Shows robot name, type, and customizable precondition component
36
+ * - Color-coded icon based on readiness state (robot icon for ready, warning for issues)
37
+ * - Styled with consistent border, background, and spacing
38
+ * - Responsive layout with proper spacing and alignment
39
+ * - Flexible precondition component that can be customized or hidden
40
+ */
41
+ export const RobotListItem = externalizeComponent(
42
+ observer(
43
+ ({
44
+ robotName,
45
+ robotType,
46
+ setupState,
47
+ PreconditionComponent = RobotSetupReadinessIndicator,
48
+ className,
49
+ }: RobotListItemProps) => {
50
+ const theme = useTheme()
51
+
52
+ // Use provided robot type or default
53
+ const displayRobotType = robotType || "Robot"
54
+
55
+ const isReady = setupState === RobotSetupReadinessState.READY
56
+
57
+ return (
58
+ <Box
59
+ className={className}
60
+ sx={{
61
+ border:
62
+ "1px solid var(--secondary-_states-outlinedBorder, #FFFFFF1F)",
63
+ background: "var(--background-paper-elevation-8, #292B3E)",
64
+ width: 732,
65
+ height: 80,
66
+ minHeight: "80px",
67
+ borderRadius: "8px",
68
+ padding: "20px",
69
+ display: "flex",
70
+ alignItems: "center",
71
+ justifyContent: "space-between",
72
+ opacity: 1,
73
+ }}
74
+ >
75
+ <Box
76
+ sx={{
77
+ display: "flex",
78
+ alignItems: "center",
79
+ gap: 2,
80
+ flex: 1,
81
+ }}
82
+ >
83
+ {/* Robot/Warning Icon */}
84
+ {isReady ? (
85
+ <Box
86
+ sx={{
87
+ fontSize: 24,
88
+ display: "flex",
89
+ alignItems: "center",
90
+ justifyContent: "center",
91
+ width: 24,
92
+ height: 24,
93
+ "& svg": {
94
+ fill: "var(--primary-main, #8E56FC) !important",
95
+ },
96
+ "& svg path": {
97
+ fill: "var(--primary-main, #8E56FC) !important",
98
+ },
99
+ }}
100
+ >
101
+ <RobotIcon />
102
+ </Box>
103
+ ) : (
104
+ <ErrorIcon
105
+ sx={{
106
+ color: theme.palette.error.main,
107
+ fontSize: 24,
108
+ }}
109
+ />
110
+ )}
111
+
112
+ {/* Robot Name */}
113
+ <Typography
114
+ variant="body1"
115
+ sx={{
116
+ fontWeight: 500,
117
+ color: theme.palette.text.primary,
118
+ }}
119
+ >
120
+ {robotName}
121
+ </Typography>
122
+
123
+ {/* Divider */}
124
+ <Divider
125
+ orientation="vertical"
126
+ flexItem
127
+ sx={{
128
+ backgroundColor: theme.palette.text.secondary,
129
+ opacity: 0.3,
130
+ width: "1px",
131
+ }}
132
+ />
133
+
134
+ {/* Robot Type */}
135
+ <Typography
136
+ variant="body2"
137
+ sx={{
138
+ color: theme.palette.text.secondary,
139
+ }}
140
+ >
141
+ {displayRobotType}
142
+ </Typography>
143
+ </Box>
144
+
145
+ {/* Setup Readiness Indicator */}
146
+ {PreconditionComponent && (
147
+ <PreconditionComponent setupState={setupState} />
148
+ )}
149
+ </Box>
150
+ )
151
+ },
152
+ ),
153
+ )
@@ -0,0 +1,60 @@
1
+ import { render, screen } from "@testing-library/react"
2
+ import { describe, expect, it } from "vitest"
3
+ import {
4
+ RobotSetupReadinessIndicator,
5
+ RobotSetupReadinessState,
6
+ } from "./RobotSetupReadinessIndicator"
7
+
8
+ describe("RobotSetupReadinessIndicator", () => {
9
+ it("renders ready state correctly", () => {
10
+ render(
11
+ <RobotSetupReadinessIndicator
12
+ setupState={RobotSetupReadinessState.READY}
13
+ />,
14
+ )
15
+
16
+ expect(screen.getByText("Ready")).toBeInTheDocument()
17
+ })
18
+
19
+ it("renders robot disconnected state correctly", () => {
20
+ render(
21
+ <RobotSetupReadinessIndicator
22
+ setupState={RobotSetupReadinessState.ROBOT_DISCONNECTED}
23
+ />,
24
+ )
25
+
26
+ expect(screen.getByText("Robot disconnected")).toBeInTheDocument()
27
+ })
28
+
29
+ it("renders precondition not fulfilled state correctly", () => {
30
+ render(
31
+ <RobotSetupReadinessIndicator
32
+ setupState={RobotSetupReadinessState.PRECONDITION_NOT_FULFILLED}
33
+ />,
34
+ )
35
+
36
+ expect(screen.getByText("Precondition not fulfilled")).toBeInTheDocument()
37
+ })
38
+
39
+ it("applies correct CSS class when provided", () => {
40
+ const { container } = render(
41
+ <RobotSetupReadinessIndicator
42
+ setupState={RobotSetupReadinessState.READY}
43
+ className="test-class"
44
+ />,
45
+ )
46
+
47
+ expect(container.firstChild).toHaveClass("test-class")
48
+ })
49
+
50
+ it("renders indicator circle", () => {
51
+ render(
52
+ <RobotSetupReadinessIndicator
53
+ setupState={RobotSetupReadinessState.READY}
54
+ />,
55
+ )
56
+
57
+ // Check if the component is rendered - it's a chip, not a button
58
+ expect(screen.getByText("Ready")).toBeInTheDocument()
59
+ })
60
+ })
@@ -0,0 +1,122 @@
1
+ import { Box, Chip, Typography, useTheme } from "@mui/material"
2
+ import { observer } from "mobx-react-lite"
3
+ import { useTranslation } from "react-i18next"
4
+ import { externalizeComponent } from "../externalizeComponent"
5
+
6
+ /**
7
+ * Enum representing the robot setup readiness state
8
+ */
9
+ export enum RobotSetupReadinessState {
10
+ /** Preconditions are not fulfilled for robot operation */
11
+ PRECONDITION_NOT_FULFILLED = "PRECONDITION_NOT_FULFILLED",
12
+ /** Robot is disconnected from the system */
13
+ ROBOT_DISCONNECTED = "ROBOT_DISCONNECTED",
14
+ /** Robot is ready for operation */
15
+ READY = "READY",
16
+ }
17
+
18
+ export interface RobotSetupReadinessIndicatorProps {
19
+ /** The current setup readiness state of the robot */
20
+ setupState: RobotSetupReadinessState
21
+ /** Additional CSS class name */
22
+ className?: string
23
+ }
24
+
25
+ /**
26
+ * A state indicator component that displays the current robot setup readiness state.
27
+ *
28
+ * Features:
29
+ * - Shows three states: Precondition not fulfilled, Robot disconnected, Ready
30
+ * - Color-coded based on state (ready: tertiary/main, others: error/main)
31
+ * - Rendered as Material-UI filled chip with paper-elevation-11 background
32
+ * - Includes colored circle indicator (8px width)
33
+ * - Localization support via react-i18next
34
+ */
35
+ export const RobotSetupReadinessIndicator = externalizeComponent(
36
+ observer(({ setupState, className }: RobotSetupReadinessIndicatorProps) => {
37
+ const theme = useTheme()
38
+ const { t } = useTranslation()
39
+
40
+ const getStateInfo = () => {
41
+ switch (setupState) {
42
+ case RobotSetupReadinessState.READY:
43
+ return {
44
+ label: t("RobotSetupReadinessIndicator.Ready.lb"),
45
+ indicatorColor:
46
+ theme.palette.tertiary?.main || theme.palette.primary.main,
47
+ backgroundColor:
48
+ theme.palette.backgroundPaperElevation?.[11] ||
49
+ theme.palette.background.paper,
50
+ textColor: "var(--secondary-contrast, #FFFFFFDE)",
51
+ }
52
+ case RobotSetupReadinessState.ROBOT_DISCONNECTED:
53
+ return {
54
+ label: t("RobotSetupReadinessIndicator.RobotDisconnected.lb"),
55
+ indicatorColor: theme.palette.error.main,
56
+ backgroundColor:
57
+ theme.palette.backgroundPaperElevation?.[11] ||
58
+ theme.palette.background.paper,
59
+ textColor: "var(--secondary-contrast, #FFFFFFDE)",
60
+ }
61
+ case RobotSetupReadinessState.PRECONDITION_NOT_FULFILLED:
62
+ default:
63
+ return {
64
+ label: t(
65
+ "RobotSetupReadinessIndicator.PreconditionNotFulfilled.lb",
66
+ ),
67
+ indicatorColor: theme.palette.error.main,
68
+ backgroundColor:
69
+ theme.palette.backgroundPaperElevation?.[11] ||
70
+ theme.palette.background.paper,
71
+ textColor: "var(--secondary-contrast, #FFFFFFDE)",
72
+ }
73
+ }
74
+ }
75
+
76
+ const { label, indicatorColor, backgroundColor, textColor } = getStateInfo()
77
+
78
+ return (
79
+ <Chip
80
+ className={className}
81
+ label={
82
+ <Box
83
+ sx={{
84
+ display: "flex",
85
+ alignItems: "center",
86
+ gap: 1,
87
+ }}
88
+ >
89
+ <Box
90
+ sx={{
91
+ width: 8,
92
+ height: 8,
93
+ borderRadius: "50%",
94
+ backgroundColor: indicatorColor,
95
+ flexShrink: 0,
96
+ }}
97
+ />
98
+ <Typography
99
+ variant="body2"
100
+ sx={{
101
+ color: textColor,
102
+ fontSize: "0.75rem", // Smaller than body2
103
+ lineHeight: 1.2,
104
+ }}
105
+ >
106
+ {label}
107
+ </Typography>
108
+ </Box>
109
+ }
110
+ variant="filled"
111
+ sx={{
112
+ backgroundColor,
113
+ color: theme.palette.getContrastText(backgroundColor),
114
+ fontWeight: 500,
115
+ "& .MuiChip-label": {
116
+ paddingX: 1.5,
117
+ },
118
+ }}
119
+ />
120
+ )
121
+ }),
122
+ )
@@ -50,5 +50,19 @@
50
50
  "ProgramControl.Start.bt": "Start",
51
51
  "ProgramControl.Resume.bt": "Weiter",
52
52
  "ProgramControl.Pause.bt": "Pause",
53
- "ProgramControl.Stop.bt": "Stopp"
53
+ "ProgramControl.Stop.bt": "Stopp",
54
+ "ProgramStateIndicator.Running.lb": "In Betrieb",
55
+ "ProgramStateIndicator.Error.lb": "Fehler",
56
+ "ProgramStateIndicator.EStop.lb": "Not-Aus",
57
+ "ProgramStateIndicator.Idle.lb": "Leerlauf",
58
+ "ProgramStateIndicator.Paused.lb": "Pausiert",
59
+ "ProgramStateIndicator.Ready.lb": "Bereit",
60
+ "ProgramStateIndicator.Stopped.lb": "Gestoppt",
61
+ "ProgramStateIndicator.Auto.lb": "Auto",
62
+ "ProgramStateIndicator.Manual.lb": "Manuell",
63
+ "ProgramStateIndicator.ManualT1.lb": "Manuell T1",
64
+ "ProgramStateIndicator.ManualT2.lb": "Manuell T2",
65
+ "RobotSetupReadinessIndicator.Ready.lb": "Bereit",
66
+ "RobotSetupReadinessIndicator.RobotDisconnected.lb": "Roboter getrennt",
67
+ "RobotSetupReadinessIndicator.PreconditionNotFulfilled.lb": "Voraussetzung nicht erfüllt"
54
68
  }
@@ -51,5 +51,19 @@
51
51
  "ProgramControl.Start.bt": "Start",
52
52
  "ProgramControl.Resume.bt": "Resume",
53
53
  "ProgramControl.Pause.bt": "Pause",
54
- "ProgramControl.Stop.bt": "Stop"
54
+ "ProgramControl.Stop.bt": "Stop",
55
+ "ProgramStateIndicator.Running.lb": "Running",
56
+ "ProgramStateIndicator.Error.lb": "Error",
57
+ "ProgramStateIndicator.EStop.lb": "E-Stop",
58
+ "ProgramStateIndicator.Idle.lb": "Idle",
59
+ "ProgramStateIndicator.Paused.lb": "Paused",
60
+ "ProgramStateIndicator.Ready.lb": "Ready",
61
+ "ProgramStateIndicator.Stopped.lb": "Stopped",
62
+ "ProgramStateIndicator.Auto.lb": "Auto",
63
+ "ProgramStateIndicator.Manual.lb": "Manual",
64
+ "ProgramStateIndicator.ManualT1.lb": "Manual T1",
65
+ "ProgramStateIndicator.ManualT2.lb": "Manual T2",
66
+ "RobotSetupReadinessIndicator.Ready.lb": "Ready",
67
+ "RobotSetupReadinessIndicator.RobotDisconnected.lb": "Robot disconnected",
68
+ "RobotSetupReadinessIndicator.PreconditionNotFulfilled.lb": "Precondition not fulfilled"
55
69
  }
package/src/index.ts CHANGED
@@ -11,10 +11,13 @@ export * from "./components/jogging/PoseJointValues"
11
11
  export * from "./components/LoadingCover"
12
12
  export * from "./components/modal/NoMotionGroupModal"
13
13
  export * from "./components/ProgramControl"
14
+ export * from "./components/ProgramStateIndicator"
15
+ export * from "./components/RobotListItem"
14
16
  export * from "./components/robots/AxisConfig"
15
17
  export * from "./components/robots/Robot"
16
18
  export { defaultGetModel } from "./components/robots/robotModelLogic"
17
19
  export * from "./components/robots/SupportedRobot"
20
+ export * from "./components/RobotSetupReadinessIndicator"
18
21
  export * from "./components/safetyBar/SafetyBar"
19
22
  export * from "./components/SelectableFab"
20
23
  export * from "./components/utils/hooks"