bryntum / support

An issues-only repository for the Bryntum project management component suite which includes powerful Grid, Scheduler, Calendar, Kanban Task Board and Gantt chart components all built in pure JS / CSS / TypeScript
https://www.bryntum.com
54 stars 6 forks source link

[REACT] `RowExpander.renderer` support for React JSX component #7713

Open SergeyMaltsev opened 1 year ago

SergeyMaltsev commented 1 year ago

Forum post

Using Grid and attempting to use RowExpanderfeature to render a React component.

Steps to reproduce:

Relevant configuration:

export function Testcomp() {
    return (<h2>Testing 123!</h2>);
}
        rowExpanderFeature: {
            columnPosition: 'first',
            column: {
                width: 60,
                align: 'center',
                readOnly: true,
            },
            renderer: () => <Testcomp />,
        },

Unhandled Runtime Error

Error: Could not load stylesheet TypeError: Cannot add property cls, object is not extensible
Call Stack
eval
..\..\node_modules\@bryntum\grid-thin\lib\feature\RowExpander.js (538:0)

Wrokaround

rowExpanderFeature : {
    columnPosition : 'first',
    column         : {
        width    : 60,
        align    : 'center',
        readOnly : true
    },
    widget : {
        type : 'widget',
        html : <TestComp />
    }
},

image

rubiconsid commented 11 months ago

After investigation, discovered that we currently support if the content is string

    renderExpander(record, row, recordState) {
        const
            me                                           = this,
            { client : grid, widget, shouldSpanRegions } = me,
            cellHeight                                   = row.cells[0]?.offsetHeight,
            { expandedBodyElements = {} }                = recordState,
            renderPromises                               = [],
            // Will be called sync or async depending on the implementation of the renderer function.
            continueRendering                            = (content, expanderElement, region) => {
                if (content != null) {
                    if (typeof content === 'string') {
                        // In case there is nodes already there
                        const currentChildren = [...expanderElement.childNodes].map(n => expanderElement.removeChild(n));
                        expanderElement.innerHTML = content;
                        currentChildren.reverse().forEach(n => expanderElement.insertBefore(n, expanderElement.firstChild));
                    }
                    else if (me.isWidgetConfig(content)) {
                        createWidget(content, expanderElement, region);
                    }
                    // Everything else will be treated as a dom config for now
                    else {
                        content = DomHelper.createElement(content);
                        expanderElement.appendChild(content);
                    }
                }
            },
...

so in order to render out dom element in our current code, we need to wrap it in string in one line like below ( tested in nested elements) :

 in AppConfig.tsx
rowExpanderFeature: {
        columnPosition: 'first',
        column: {
            width: 60,
            align: 'center',
            readOnly: true,
        },
        renderer: () => ('<div><h2>Test</h2><p>hello world! this is working</p></div>'),
    },

image

and if user wants to render areact component : we need to convert JSX element to a JSX string by using ReactDomServer.renderToString shown as below:

in Textcomp.tsx

export function Testcomp() {
    return (
        <div>
            <h2>Test</h2>
            <p>hello world! this is working with REACT JSX Component</p>
        </div>
    )
   ;
}

in AppConfig.tsx

import { BryntumGridProps } from '@bryntum/grid-react';
import { Testcomp } from './Testcomp';
import ReactDomServer from 'react-dom/server';

export const gridConfig : Partial<BryntumGridProps> = {
  ......
    rowExpanderFeature: {
        columnPosition: 'first',
        column: {
            width: 60,
            align: 'center',
            readOnly: true,
        },
        renderer: () => ReactDomServer.renderToString(<Testcomp />),
    },
};

image