@@ -28,17 +28,7 @@ const generateMoreData = (count) => {
2828 } ) ) ;
2929} ;
3030
31- interface PropTypes {
32- onRowSelect : (
33- e ?: CustomEvent < {
34- allRowsSelected : boolean ;
35- row ?: Record < string , unknown > ;
36- isSelected ?: boolean ;
37- selectedFlatRows : Record < string , unknown > [ ] ;
38- selectedRowIds : Record < string | number , boolean > ;
39- } >
40- ) => void ;
41- }
31+ type PropTypes = AnalyticalTablePropTypes [ 'onRowSelect' ] ;
4232
4333const columns = [
4434 {
@@ -2716,9 +2706,267 @@ describe('AnalyticalTable', () => {
27162706 cy . get ( '[data-empty-row]' ) . should ( 'exist' ) . and ( 'have.length' , 5 ) . and ( 'be.visible' ) ;
27172707 } ) ;
27182708
2709+ it ( 'TreeTable + SubComps + lazy-load' , ( ) => {
2710+ const initialData = [
2711+ {
2712+ displayId : '1337' ,
2713+ name : 'root1' ,
2714+ nodeId : 'root1' ,
2715+ parentId : null
2716+ } ,
2717+ {
2718+ displayId : '1337' ,
2719+ name : 'root2' ,
2720+ nodeId : 'root2' ,
2721+ parentId : null
2722+ }
2723+ ] ;
2724+
2725+ const columns = [
2726+ {
2727+ Header : 'Test' ,
2728+ accessor : 'name'
2729+ } ,
2730+ {
2731+ accessor : 'displayId'
2732+ }
2733+ ] ;
2734+
2735+ /**
2736+ * This example will render a tree table using AnalyticalTable.
2737+ * the children nodes will be lazy loaded from server when expanding the parent node.
2738+ *a "Load more" button is rendered if the parent node's children are not completely loaded.
2739+ */
2740+ const TestComp = ( ) => {
2741+ // flattend data. will be transformed before passed to the tree table
2742+ const [ raw , setRaw ] = useState ( initialData ) ;
2743+ const rowById = useRef ( { } ) ;
2744+ const names = useRef ( mockNames ) ;
2745+
2746+ // simulate getting children from server. randomly generate a child node.
2747+ const fetchChildren = ( nodeId ) => {
2748+ return Promise . resolve ( {
2749+ value : [
2750+ {
2751+ displayId : `1337` ,
2752+ name : `${ nodeId } -${ names . current [ 0 ] } ` ,
2753+ nodeId : `${ nodeId } -${ names . current [ 0 ] } ` ,
2754+ parentId : nodeId
2755+ }
2756+ ]
2757+ } ) ;
2758+ } ;
2759+
2760+ const getChildren = useCallback (
2761+ ( nodeId ) => {
2762+ return fetchChildren ( nodeId ) . then ( ( result ) => {
2763+ names . current . shift ( ) ;
2764+ setRaw ( [ ...raw , ...result . value ] ) ;
2765+ } ) ;
2766+ } ,
2767+ [ raw ]
2768+ ) ;
2769+
2770+ const handleRowExpandChange = useCallback (
2771+ ( event ) => {
2772+ const row = event . detail . row ;
2773+ if ( ! row . isExpanded && row . canExpand && ! row . original . subRows ?. length ) {
2774+ void getChildren ( row . original . nodeId , row . original . subRows ?. length || 0 ) ;
2775+ }
2776+ } ,
2777+ [ getChildren ]
2778+ ) ;
2779+
2780+ // render "Load more" button
2781+ // the "Load more" button will be rendered as the row's subcomponent if the row is the last child of its parent node
2782+ const renderLoadMore = ( row ) => {
2783+ const parentId = row . original . parentId ;
2784+
2785+ // root node
2786+ const parentNode = rowById . current [ parentId ] ;
2787+ if ( ! parentNode ) {
2788+ return null ;
2789+ }
2790+
2791+ // current node is not the last node of the parent's children: do not render the Load more button
2792+ const currentChildrenCount = parentNode . subRows ?. length || 0 ;
2793+ const currentRowIndex = parentNode . subRows ?. findIndex ( ( subRow ) => subRow . nodeId === row . original . nodeId ) ;
2794+ if ( currentRowIndex !== currentChildrenCount - 1 ) {
2795+ return null ;
2796+ }
2797+
2798+ const arrowWidth = 35 ;
2799+
2800+ return (
2801+ < div
2802+ style = { {
2803+ paddingBottom : '0.25rem' ,
2804+ paddingInlineStart : `calc(var(--_ui5wcr-AnalyticalTableTreePaddingLevel${ row . depth } ) + ${ arrowWidth } px)`
2805+ } }
2806+ >
2807+ < Button
2808+ design = "Transparent"
2809+ onClick = { ( ) => {
2810+ getChildren && getChildren ( parentId , currentChildrenCount ) ;
2811+ } }
2812+ >
2813+ Load more for { parentNode . name }
2814+ </ Button >
2815+ </ div >
2816+ ) ;
2817+ } ;
2818+
2819+ const customTableHook = ( hooks ) => {
2820+ hooks . prepareRow . push ( ( row ) => {
2821+ row . canExpand = true ;
2822+ } ) ;
2823+ } ;
2824+
2825+ // transform data to the pattern which is accepted by the tree table
2826+ // NOTES: this algorithm is less likely related to the bug, because in our reality project there is a different algorithm to generate the tree table and the bug still occurs.
2827+ const data = useMemo ( ( ) => {
2828+ raw . forEach ( ( item ) => {
2829+ const newItem = { ...item } ;
2830+ rowById . current [ newItem . nodeId ] = {
2831+ ...( rowById [ newItem . node ] || { } ) ,
2832+ ...newItem
2833+ } ;
2834+ if ( ! newItem . parentId ) {
2835+ rowById . current [ newItem . nodeId ] = {
2836+ ...newItem ,
2837+ ...( rowById . current [ newItem . nodeId ] || { } )
2838+ } ;
2839+ } else {
2840+ if ( ! rowById . current [ newItem . parentId ] ) {
2841+ rowById . current [ newItem . parentId ] = {
2842+ nodeId : newItem . parentId ,
2843+ subRows : [ ]
2844+ } ;
2845+ } else if ( ! rowById . current [ newItem . parentId ] . subRows ) {
2846+ rowById . current [ newItem . parentId ] . subRows = [ ] ;
2847+ }
2848+ rowById . current [ newItem . parentId ] . subRows . push ( rowById . current [ newItem . nodeId ] ) ;
2849+ }
2850+ } ) ;
2851+
2852+ return Object . values ( rowById . current ) . filter ( ( row ) => ! row . parentId ) ;
2853+ } , [ raw ] ) ;
2854+
2855+ return (
2856+ < AnalyticalTable
2857+ columns = { columns }
2858+ data = { data }
2859+ isTreeTable
2860+ onRowExpandChange = { handleRowExpandChange }
2861+ reactTableOptions = { {
2862+ autoResetExpanded : false
2863+ } }
2864+ renderRowSubComponent = { renderLoadMore }
2865+ subComponentsBehavior = { 'IncludeHeight' }
2866+ tableHooks = { [ customTableHook ] }
2867+ minRows = { 1 }
2868+ />
2869+ ) ;
2870+ } ;
2871+
2872+ cy . mount ( < TestComp /> ) ;
2873+
2874+ cy . findByText ( 'root1' ) . siblings ( ) . click ( ) ;
2875+ cy . findByText ( 'Load more for root1' ) . should ( 'have.length' , 1 ) . click ( ) ;
2876+ cy . findByText ( 'Load more for root1' ) . should ( 'have.length' , 1 ) . click ( ) ;
2877+
2878+ cy . findByText ( 'root1-John' ) . siblings ( ) . click ( ) ;
2879+ cy . findByText ( 'Load more for root1-John' ) . should ( 'have.length' , 1 ) . click ( ) ;
2880+
2881+ cy . get ( '[aria-rowindex="6"]' ) . should ( 'have.css' , 'transform' , 'matrix(1, 0, 0, 1, 0, 260)' ) ;
2882+ } ) ;
2883+
27192884 cypressPassThroughTestsFactory ( AnalyticalTable , { data, columns } ) ;
27202885} ) ;
27212886
2887+ const mockNames = [
2888+ 'John' ,
2889+ 'Jane' ,
2890+ 'Bob' ,
2891+ 'Alice' ,
2892+ 'Charlie' ,
2893+ 'David' ,
2894+ 'Eva' ,
2895+ 'Frank' ,
2896+ 'Grace' ,
2897+ 'Henry' ,
2898+ 'Isabel' ,
2899+ 'Jack' ,
2900+ 'Kate' ,
2901+ 'Liam' ,
2902+ 'Mia' ,
2903+ 'Noah' ,
2904+ 'Olivia' ,
2905+ 'Parker' ,
2906+ 'Quinn' ,
2907+ 'Ryan' ,
2908+ 'Sophia' ,
2909+ 'Thomas' ,
2910+ 'Uma' ,
2911+ 'Vincent' ,
2912+ 'Willow' ,
2913+ 'Xavier' ,
2914+ 'Yara' ,
2915+ 'Zane' ,
2916+ 'Ava' ,
2917+ 'Benjamin' ,
2918+ 'Cora' ,
2919+ 'Dylan' ,
2920+ 'Emily' ,
2921+ 'Finn' ,
2922+ 'Gabriella' ,
2923+ 'Hudson' ,
2924+ 'Isla' ,
2925+ 'Julian' ,
2926+ 'Katherine' ,
2927+ 'Leo' ,
2928+ 'Mila' ,
2929+ 'Nathan' ,
2930+ 'Oliver' ,
2931+ 'Penelope' ,
2932+ 'Quentin' ,
2933+ 'Rose' ,
2934+ 'Samuel' ,
2935+ 'Tessa' ,
2936+ 'Ulysses' ,
2937+ 'Victoria' ,
2938+ 'Wesley' ,
2939+ 'Xander' ,
2940+ 'Yasmine' ,
2941+ 'Zachary' ,
2942+ 'Abigail' ,
2943+ 'Brady' ,
2944+ 'Chloe' ,
2945+ 'Daniel' ,
2946+ 'Eleanor' ,
2947+ 'Felix' ,
2948+ 'Giselle' ,
2949+ 'Hayden' ,
2950+ 'Isabella' ,
2951+ 'Jasper' ,
2952+ 'Kylie' ,
2953+ 'Landon' ,
2954+ 'Maddison' ,
2955+ 'Natalie' ,
2956+ 'Oscar' ,
2957+ 'Paige' ,
2958+ 'Quincy' ,
2959+ 'Riley' ,
2960+ 'Savannah' ,
2961+ 'Theodore' ,
2962+ 'Ursula' ,
2963+ 'Violet' ,
2964+ 'Wyatt' ,
2965+ 'Ximena' ,
2966+ 'Yannick' ,
2967+ 'Zara'
2968+ ] ;
2969+
27222970const columnsWithPopIn = [
27232971 {
27242972 Header : 'Name' ,
0 commit comments