diff --git a/packages/pluggableWidgets/tree-node-web/CHANGELOG.md b/packages/pluggableWidgets/tree-node-web/CHANGELOG.md
index e0035def4f..ff4eef976f 100644
--- a/packages/pluggableWidgets/tree-node-web/CHANGELOG.md
+++ b/packages/pluggableWidgets/tree-node-web/CHANGELOG.md
@@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [Unreleased]
+### Changed
+
+- We changed `hasChildren` configuration from boolean to expression.
+
+### Fixed
+
+- We fixed an issue where Tree Nodes showing loading spinner when children can't be found while `hasChildren` property configured to `true`.
+
## [3.6.0] - 2025-10-01
### Changed
diff --git a/packages/pluggableWidgets/tree-node-web/src/TreeNode.editorPreview.tsx b/packages/pluggableWidgets/tree-node-web/src/TreeNode.editorPreview.tsx
index 9acba12343..50de82ddbd 100644
--- a/packages/pluggableWidgets/tree-node-web/src/TreeNode.editorPreview.tsx
+++ b/packages/pluggableWidgets/tree-node-web/src/TreeNode.editorPreview.tsx
@@ -1,5 +1,5 @@
-import { parseStyle } from "@mendix/widget-plugin-platform/preview/parse-style";
import { mapPreviewIconToWebIcon } from "@mendix/widget-plugin-platform/preview/map-icon";
+import { parseStyle } from "@mendix/widget-plugin-platform/preview/parse-style";
import { GUID } from "mendix";
import { ReactElement } from "react";
import { TreeNodePreviewProps } from "../typings/TreeNodeProps";
@@ -32,10 +32,10 @@ export function preview(props: TreeNodePreviewProps): ReactElement | null {
- )
+ ),
+ isUserDefinedLeafNode: !props.hasChildren
}
]}
- isUserDefinedLeafNode={!props.hasChildren}
startExpanded
showCustomIcon={Boolean(props.expandedIcon) || Boolean(props.collapsedIcon)}
iconPlacement={props.showIcon}
diff --git a/packages/pluggableWidgets/tree-node-web/src/TreeNode.tsx b/packages/pluggableWidgets/tree-node-web/src/TreeNode.tsx
index a7c633fc6c..3763788f7a 100644
--- a/packages/pluggableWidgets/tree-node-web/src/TreeNode.tsx
+++ b/packages/pluggableWidgets/tree-node-web/src/TreeNode.tsx
@@ -8,7 +8,8 @@ function mapDataSourceItemToTreeNodeItem(item: ObjectItem, props: TreeNodeContai
id: item.id,
headerContent:
props.headerType === "text" ? props.headerCaption?.get(item).value : props.headerContent?.get(item),
- bodyContent: props.children?.get(item)
+ bodyContent: props.children?.get(item),
+ isUserDefinedLeafNode: props.hasChildren?.get(item).value === false
};
}
@@ -36,7 +37,6 @@ export function TreeNode(props: TreeNodeContainerProps): ReactElement {
class={props.class}
style={props.style}
items={treeNodeItems}
- isUserDefinedLeafNode={!props.hasChildren}
startExpanded={props.startExpanded}
showCustomIcon={Boolean(props.expandedIcon) || Boolean(props.collapsedIcon)}
iconPlacement={props.showIcon}
diff --git a/packages/pluggableWidgets/tree-node-web/src/TreeNode.xml b/packages/pluggableWidgets/tree-node-web/src/TreeNode.xml
index baeb7d19df..73552bc94d 100644
--- a/packages/pluggableWidgets/tree-node-web/src/TreeNode.xml
+++ b/packages/pluggableWidgets/tree-node-web/src/TreeNode.xml
@@ -40,9 +40,10 @@
Header caption
-
+
Has children
Indicate whether the node has children or is an end node. When set to yes, a composable region becomes available to define the child nodes.
+
Start expanded
diff --git a/packages/pluggableWidgets/tree-node-web/src/components/TreeNode.tsx b/packages/pluggableWidgets/tree-node-web/src/components/TreeNode.tsx
index f8e0f2c3df..e935717efb 100644
--- a/packages/pluggableWidgets/tree-node-web/src/components/TreeNode.tsx
+++ b/packages/pluggableWidgets/tree-node-web/src/components/TreeNode.tsx
@@ -13,13 +13,13 @@ import { TreeNodeBranchContext, useInformParentContextOfChildNodes } from "./Tre
export interface TreeNodeItem extends ObjectItem {
headerContent: ReactNode;
bodyContent: ReactNode;
+ isUserDefinedLeafNode: boolean;
}
export interface TreeNodeProps extends Pick {
class: string;
style?: CSSProperties;
items: TreeNodeItem[] | null;
- isUserDefinedLeafNode: TreeNodeBranchProps["isUserDefinedLeafNode"];
startExpanded: TreeNodeBranchProps["startExpanded"];
showCustomIcon: boolean;
iconPlacement: TreeNodeBranchProps["iconPlacement"];
@@ -34,7 +34,6 @@ export function TreeNode({
class: className,
items,
style,
- isUserDefinedLeafNode,
showCustomIcon,
startExpanded,
iconPlacement,
@@ -79,22 +78,25 @@ export function TreeNode({
data-focusindex={tabIndex || 0}
role={level === 0 ? "tree" : "group"}
>
- {items.map(({ id, headerContent, bodyContent }) => (
-
- {bodyContent}
-
- ))}
+ {items.map(item => {
+ const { id, headerContent, bodyContent, isUserDefinedLeafNode } = item;
+ return (
+
+ {bodyContent}
+
+ );
+ })}
);
}
diff --git a/packages/pluggableWidgets/tree-node-web/src/components/hooks/lazyLoading.ts b/packages/pluggableWidgets/tree-node-web/src/components/hooks/lazyLoading.ts
index 3dd46cb5a5..90d6a6ccaa 100644
--- a/packages/pluggableWidgets/tree-node-web/src/components/hooks/lazyLoading.ts
+++ b/packages/pluggableWidgets/tree-node-web/src/components/hooks/lazyLoading.ts
@@ -10,7 +10,7 @@ export const useTreeNodeLazyLoading = (
hasNestedTreeNode: () => boolean;
} => {
const hasNestedTreeNode = useCallback(
- () => treeNodeBranchBody.current?.lastElementChild?.className.includes("widget-tree-node") ?? true,
+ () => treeNodeBranchBody.current?.lastElementChild?.className.includes("widget-tree-node") ?? false,
[]
);
diff --git a/packages/pluggableWidgets/tree-node-web/typings/TreeNodeProps.d.ts b/packages/pluggableWidgets/tree-node-web/typings/TreeNodeProps.d.ts
index 004c12f44a..0d47f596a2 100644
--- a/packages/pluggableWidgets/tree-node-web/typings/TreeNodeProps.d.ts
+++ b/packages/pluggableWidgets/tree-node-web/typings/TreeNodeProps.d.ts
@@ -23,7 +23,7 @@ export interface TreeNodeContainerProps {
openNodeOn: OpenNodeOnEnum;
headerContent?: ListWidgetValue;
headerCaption?: ListExpressionValue;
- hasChildren: boolean;
+ hasChildren: ListExpressionValue;
startExpanded: boolean;
children?: ListWidgetValue;
animate: boolean;
@@ -50,7 +50,7 @@ export interface TreeNodePreviewProps {
openNodeOn: OpenNodeOnEnum;
headerContent: { widgetCount: number; renderer: ComponentType<{ children: ReactNode; caption?: string }> };
headerCaption: string;
- hasChildren: boolean;
+ hasChildren: string;
startExpanded: boolean;
children: { widgetCount: number; renderer: ComponentType<{ children: ReactNode; caption?: string }> };
animate: boolean;