From f8807b0f6f1e7d86524ad406fe50b980f4d2039e Mon Sep 17 00:00:00 2001 From: gjulivan Date: Tue, 9 Dec 2025 13:25:14 +0100 Subject: [PATCH 1/2] fix: treenode showing infinite load spinner and change hasChildren to expression --- .../src/TreeNode.editorPreview.tsx | 6 +-- .../tree-node-web/src/TreeNode.tsx | 4 +- .../tree-node-web/src/TreeNode.xml | 3 +- .../tree-node-web/src/components/TreeNode.tsx | 38 ++++++++++--------- .../src/components/hooks/lazyLoading.ts | 2 +- .../tree-node-web/typings/TreeNodeProps.d.ts | 4 +- 6 files changed, 30 insertions(+), 27 deletions(-) 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; From 96a0f2595492e3fa2770812da47f1fd6d8711e3b Mon Sep 17 00:00:00 2001 From: gjulivan Date: Tue, 9 Dec 2025 13:28:24 +0100 Subject: [PATCH 2/2] chore: add changelog --- packages/pluggableWidgets/tree-node-web/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) 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