KendoReact Grid with custom cell and context menu

1 Answer 15 Views
ContextMenu Grid
Paula
Top achievements
Rank 1
Paula asked on 22 Apr 2024, 11:01 AM

Hi!

I'm brand new to this framework and learning a lot so apologies if this is a silly question.

I have created a grid - it has a text column and a chip column that represents the status of the record. I implemented a context menu for the grid rows but it is not triggered on the chip column...just on the text column. How can I fix this?

Here is my code:


 <Grid
        className="custom-grid"
        data={people.slice(page.skip, page.take + page.skip)}
        skip={page.skip}
        take={page.take}
        total={people.length}
        scrollable="none"
        pageable={{
          buttonCount: 3,
          responsive: false,
          pageSizeValue: pageSizeValue,
        }}
        onPageChange={pageChange}
        onContextMenu={handleContextMenu}
      >
        <GridColumn field="PersonName" title="Full Name" />
        <GridColumn
          field="Status"
          title="Status"
          cell={(props) => (
            <ChipCell
              {...props}
              text={props.dataItem['Status']}
              className={props.dataItem['Color']}
            />
          )}
          width="12.5rem"
        />
      </Grid>
      <ContextMenu
        className="custom-context-menu"
        show={show}
        offset={offset.current}
        // onSelect={handleOnSelect}
        onClose={handleCloseMenu}
      >
        <MenuItem text="Option 1" />
        <MenuItem text="Option 2" />
      </ContextMenu>
    </div>

1 Answer, 1 is accepted

Sort by
0
Konstantin Dikov
Telerik team
answered on 24 Apr 2024, 07:39 AM

Hello Paula,

Thank you for reaching out to us.

The props of the custom cell will have onContextMenu handler which you can assign to the custom TD element:

const ChipCell = (props) => {
  return (
    <td onContextMenu={props.onContextMenu}>{props.dataItem.ProductName}</td>
  );
};

I should mention that defining custom cells inline is not recommended, because each change in the state will re-mount the entire Grid. With that in mind, please set the custom cell only by the name of the custom component and define it as a separate component as shown in the following example:

Finally, I would recommend using the "cells.data" of the column to configure custom cells, because it provides props.tdProps with all styles and events that can be added directly to the TD element:

Hope this helps.

 

Regards,
Konstantin Dikov
Progress Telerik

Stay tuned by visiting our public roadmap and feedback portal pages! Or perhaps, if you are new to our Kendo family, check out our getting started resources!

Paula
Top achievements
Rank 1
commented on 25 Apr 2024, 10:25 AM

Thank you for this information! I'm still a bit confused.

I had defined the chip cell outside of the grid like this (did I define it twice? I'm unable to get your code sample to compile):

  const ChipCell = (props: ChipProps) => {
       
        return (
            <td>
                <Chip
                    className={`chip ${props.className}`}
                    text={props.text}
                    value="chip"
                    disabled={false}
                    removable={false}
                />
            </td>
        );
    };


I added a console.log for the props and I see the context menu as you show in your example, however when I try to add it to the td:

const ChipCell = (props: ChipProps) => {
       
        return (
            <td onContextMenu={props.OnContextMenu}>
                <Chip
                    className={`chip ${props.className}`}
                    text={props.text}
                    value="chip"
                    disabled={false}
                    removable={false}
                />
            </td>
        );
    };

It is not an available selection and does not compile.

Thanks again for helping :)

Konstantin Dikov
Telerik team
commented on 29 Apr 2024, 08:02 AM

Hi Paula,

Once you change the definition of the cell property of the column to the following, the type of the props will be GridCellProps:

 <GridColumn
          field="Status"
          title="Status"
          cell={ChipCell}
          width="12.5rem"
        />

const ChipCell = (props: GridCellProps) => {

Note that if you switch to using the column cells.data instead, the props will be GrdiCustomCellProps:

If the issues persist, please share a stackblitz example demonstrating the issue.

Paula
Top achievements
Rank 1
commented on 29 Apr 2024, 09:14 PM

Hi! 

I tried to implement your solution but the types are not matching up & I'm stuck on how to solve this.

I defined the chip component like this:

    const ChipCell = (props: GridCellProps) => {
        return (
            <td onContextMenu={props.onContextMenu}>
                <Chip
                    className={`chip ${props.className}`}
                    text={props.dataItem.text}
                    value="chip"
                    disabled={false}
                    removable={false}
                />
            </td>
        );
    };

 

For this line:

 <td onContextMenu={props.onContextMenu}>

 

I'm getting the following compile error:

Type '((event: MouseEvent<HTMLElement, MouseEvent>, dataItem: any, field?: string | undefined) => void) | undefined' is not assignable to type 'MouseEventHandler<HTMLTableDataCellElement> | undefined'.
Type '(event: MouseEvent<HTMLElement, MouseEvent>, dataItem: any, field?: string | undefined) => void' is not assignable to type 'MouseEventHandler<HTMLTableDataCellElement>'.
Target signature provides too few arguments. Expected 2 or more, but got 1.
ts(2322)
index.d.ts(2438, 9): The expected type comes from property 'onContextMenu' which is declared here on type 'DetailedHTMLProps<TdHTMLAttributes<HTMLTableDataCellElement>, HTMLTableDataCellElement>'

 

I think it is because I changed ChipProps to GridCellProps - something isn't translating.

Konstantin Dikov
Telerik team
commented on 01 May 2024, 01:04 PM

Hi Paula,

You can handle the onContextMenu event of the TD and manually call the props.onContextMenu with the correct type. You can use the default cell source code as reference:

export const GridCell = (props: GridCellProps) => {
    let defaultRendering: React.ReactElement<HTMLTableCellElement> | null = null;
    const intl = useInternationalization();
    const navigationAttributes = useTableKeyboardNavigation(props.id);

    const onContextMenu = React.useCallback((event: React.MouseEvent<HTMLElement>) => {
        if (props.onContextMenu) {
            props.onContextMenu.call(undefined, event, props.dataItem, props.field);
        }
    }, [props.onContextMenu, props.dataItem, props.field]);
    let tdProps: GridTdAttributes | null = null;
    let content = null;
    if (props.rowType === 'groupFooter') {
        tdProps = {
            onContextMenu: onContextMenu,
            className: props.className
        };
        defaultRendering = <td {...tdProps} /> as React.ReactElement<HTMLTableCellElement>;
    } else if (props.rowType !== 'groupHeader') {
        if(props.field !== undefined) {
            const data = getNestedValue(props.field, props.dataItem);

            if (data !== undefined && data !== null) {
                content = props.format ?
                    intl.format(props.format, data) :
                    data.toString();
            }
        }

        const className: string = classNames(
            'k-table-td',
            props.className,
            { 'k-selected': props.isSelected }
        );

        tdProps = {
            onContextMenu: onContextMenu,
            colSpan: props.colSpan,
            style: props.style,
            className: className,
            role: 'gridcell',
            'aria-colindex': props.ariaColumnIndex,
            'aria-selected': props.isSelected,
            [GRID_COL_INDEX_ATTRIBUTE]: props.columnIndex,
            ...navigationAttributes
        };

        defaultRendering = (
          <td {...tdProps}>
            {content}
          </td>
        ) as React.ReactElement<HTMLTableCellElement>;
    }


    const rowTypeSetting = props.rowType || 'data';
    const customCells = props.cells;
    if(customCells && customCells[rowTypeSetting]) {
        const CustomCell = customCells[rowTypeSetting] as any;
        return (<CustomCell {...props} tdProps={tdProps}>
          {content}
        </CustomCell>);
    }

    return props.render ?
        props.render.call(undefined, defaultRendering, props) :
        defaultRendering;
};

Tags
ContextMenu Grid
Asked by
Paula
Top achievements
Rank 1
Answers by
Konstantin Dikov
Telerik team
Share this question
or