i have a Data Grid table, there's a colomn that holds actions (edit and delete):
(Below is my whole code)
import React, { useEffect } from 'react'
import { DataGrid } from '#mui/x-data-grid'
import { useNavigate } from 'react-router-dom'
import EditIcon from '#mui/icons-material/Edit'
import DeleteForeverIcon from '#mui/icons-material/DeleteForever'
import { Button } from '#mui/material'
const rows = [
{
id: 1,
lastName: 'Snow',
firstName: 'Jon',
age: 35,
edit: EditIcon,
delete: `${(<Button variant="text">Text</Button>)}`,
},
]
const Colors = () => {
const columns = [
{ field: 'id', headerName: 'ID', width: 70 },
{ field: 'firstName', headerName: 'First name', width: 130 },
{ field: 'lastName', headerName: 'Last name', width: 130 },
{
field: 'age',
headerName: 'Age',
type: 'number',
width: 90,
},
{ field: 'edit', headerName: 'Edit', width: 150 },
{ field: 'delete', headerName: 'Delete', width: 150 },
]
return (
<div style={{ height: 560, width: '100%' }}>
<DataGrid
sx={{ backgroundColor: 'white' }}
rows={rows}
columns={columns}
pageSize={8}
rowsPerPageOptions={[8]}
checkboxSelection
/>
</div>
)
}
export default Colors
Now my problem is, i would like to use a material icon inside edit column.
I tried by implementing icon directly like this:
const rows = [
{
id: 1,
lastName: 'Snow',
firstName: 'Jon',
age: 35,
edit: EditIcon, //<--
},
]
Or calling a button by this way:
const rows = [
{
id: 1,
lastName: 'Snow',
firstName: 'Jon',
age: 35,
delete: `${(<Button variant="text">Text</Button>)}`, //<--
},
]
Result: i'm getting a render [object object] instead of icon or button.
How to properly render a button or icon inside Data Grid column?
I think you can try doing this. MUI exposes an actions column type that you can pass custom icons and implementations to. If you scroll down a bit on this link you can find a lot of column options.
Actions column
import { GridActionsCellItem, GridRowParams } from "#mui/x-data-grid-pro";
import EditIcon from "#mui/icons-material/Edit";
{
field: "actions",
type: "actions",
align: "left",
headerName: t("actions"),
getActions: (params: GridRowParams) => [
<GridActionsCellItem
key={0}
icon={<EditIcon titleAccess={t("edit")} color="primary" />}
label={t("edit")}
onClick={() => handleEditPressed(params)}
/>,
],
},
I fixed my problem by using renderCell, with this i can render html inside grid data the way i want (i can put button for exemple):
{
field: 'edit',
headerName: 'Edit',
width: 70,
renderCell: (params) => {
return <EditIcon /> //<-- Mui icons should be put this way here.
},
},
Goal:
Display all row in the in table at the same time.
Problem:
It display only 3 row at the same time in the table.
I would like to display all row at the same time without any limitation.
It doesn't work to use "height: '100%'"
Any idea?
Codesandbox:
https://codesandbox.io/s/mkd4dw?file=/demo.tsx
Thank you!
demo.tsx
import * as React from 'react';
import Box from '#mui/material/Box';
import Rating from '#mui/material/Rating';
import {
DataGrid,
GridRenderCellParams,
GridColDef,
useGridApiContext,
} from '#mui/x-data-grid';
function renderRating(params: GridRenderCellParams<number>) {
return <Rating readOnly value={params.value} />;
}
function RatingEditInputCell(props: GridRenderCellParams<number>) {
const { id, value, field } = props;
const apiRef = useGridApiContext();
const handleChange = (event: React.SyntheticEvent, newValue: number | null) => {
apiRef.current.setEditCellValue({ id, field, value: newValue });
};
const handleRef = (element: HTMLSpanElement) => {
if (element) {
const input = element.querySelector<HTMLInputElement>(
`input[value="${value}"]`,
);
input?.focus();
}
};
return (
<Box sx={{ display: 'flex', alignItems: 'center', pr: 2 }}>
<Rating
ref={handleRef}
name="rating"
precision={1}
value={value}
onChange={handleChange}
/>
</Box>
);
}
const renderRatingEditInputCell: GridColDef['renderCell'] = (params) => {
return <RatingEditInputCell {...params} />;
};
export default function CustomEditComponent() {
return (
<div style={{ height: 250, width: '100%' }}>
<DataGrid
rows={rows}
columns={columns}
experimentalFeatures={{ newEditingApi: true }}
/>
</div>
);
}
const columns = [
{
field: 'places',
headerName: 'Places',
width: 120,
},
{
field: 'rating',
headerName: 'Rating',
renderCell: renderRating,
renderEditCell: renderRatingEditInputCell,
editable: true,
width: 180,
type: 'number',
},
];
const rows = [
{ id: 1, places: 'Barcelona', rating: 5 },
{ id: 2, places: 'Rio de Janeiro', rating: 4 },
{ id: 3, places: 'London', rating: 3 },
{ id: 4, places: 'New York', rating: 2 },
];
You need to make use of autoHeight prop supported by the <DataGrid /> component, update your DataGrid component usage to this:
<DataGrid
autoHeight
rows={rows}
columns={columns}
experimentalFeatures={{ newEditingApi: true }}
/>
Reference: https://mui.com/x/react-data-grid/layout/#auto-height
Since I pushed my code on production, I cant call anymore getValue inside renderCell
Environnement is the same as dev by the way
Here is the columns def:
const columns = [
{
field: 'edit',
headerName: 'Edit',
sortable: false,
width: 78,
renderCell: (params) => (
<div>
<EditProduct defaultCats={props.defaultCats} prodId={params.getValue('id')} productIdGL={params.getValue('productId')} />
</div>
),
align: "left",
},
{
field: 'active',
headerName: 'Off / On',
width: 104,
renderCell: (params) => (
<div><CustomSwitch
checked={Boolean(params.value)}
onChange={() => myswitch(params.getValue('id'), Boolean(params.value))}
name="switch-"
inputProps={{ 'aria-label': 'primary checkbox' }}
></CustomSwitch>
</div>
),
align: "left",
},
{ field: 'category_name', headerName: 'Catégorie', width: 120 },
{ field: 'item_name', headerName: 'Nom du produit', width: 220 },
{
field: 'price',
headerName: 'Prix de vente',
type: 'number',
width: 140,
},
{
field: 'profit',
headerName: 'Profit par vente',
type: 'number',
width: 160,
alt: 'Test'
},
{ field: 'type', headerName: 'Type', width: 90 },
{ field: 'id', headerName: '#', width: 80 },
];
Error:
Unhandled Runtime Error
TypeError: Cannot read property 'undefined' of undefined
Any help appreciate since this bug occur only on production
mui-core: 4.11.4
mui-datagrid: 4.0.0-alpha.26
params.getValue(params.id, 'id') || ''
Instead of
params.getValue('id')
Here is my scenario,
I am making a onFetchEvents Redux Action call and getting all events and passing it as follows. rowData={this.props.events.data}
Everything works fine. Now I need to apply a filter, and this requires another call to the server.
I set enableServerSideFilter: true, so that when a filter applied, datagrid does not make a call on its own.
I monitor filter changes and call handleFilterChanged. I get the const filterModel = this.gridApi.getFilterModel(); and send it to rest endpoint.
Everything works fine but the datagrid does not remember the filter state.
I tried this.gridApi.setFilterModel(filterModel); but it gets me into infinite loop.
To make the long story short, how can I use filters with Redux so that I have full control also where should I use this.gridApi.setFilterModel()
Let me know if you need more clarification.
Thanks
/* eslint-disable */
import React, { Component, Fragment } from 'react';
import { connect } from 'react-redux';
import { AgGridReact } from 'ag-grid-react';
import { fetchEvents } from '#mc/duck/actions/AppActions';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css';
// import './customTheme.scss';
// col resizing pipe is not visibles
class DatagridPage extends Component {
constructor(props) {
super(props);
this.state = {
params: {
filterModel: {},
page: 1,
limit: 10,
},
gridOptions: {
enableServerSideFilter: true,
enableSorting: true,
enableColResize: true,
suppressMenuHide: true,
pagination: true,
animateRows: true,
onFilterChanged: this.handleFilterChanged,
onSortChanged: () => console.log('onSortChanged'),
columnDefs: [
{
headerName: 'id',
field: 'id',
headerClass: 'test',
checkboxSelection: true,
filter: 'agTextColumnFilter',
},
{
headerName: 'name',
field: 'name',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'risk score',
field: 'risk_score',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'urgency',
field: 'urgency',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{ headerName: 'type', field: 'type', headerClass: 'test', filter: 'agTextColumnFilter' },
{
headerName: 'artifacts',
field: 'artifacts',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'created',
field: 'created',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'updated',
field: 'updated',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{ headerName: 'due', field: 'due', headerClass: 'test', filter: 'agTextColumnFilter' },
{
headerName: 'owner',
field: 'owner',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'status',
field: 'status',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'description',
field: 'description',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
],
},
};
}
componentDidMount() {
console.log('componentDidMount');
const { params } = this.state;
this.props.onFetchEvents(params);
}
onGridReady = params => {
this.gridApi = params.api;
this.gridColumnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
console.log('onGridReady', this.gridApi.getFilterModel());
};
onPageSizeChanged(newPageSize) {
var value = document.getElementById('page-size').value;
const paramsCopy = { ...this.state.params };
paramsCopy.limit = Number(value);
paramsCopy.page = 1;
this.setState({ params: paramsCopy }, () => this.props.onFetchEvents(paramsCopy));
this.gridApi.paginationSetPageSize(Number(value));
}
handleFilterChanged = () => {
console.log('handleFilterChanged');
const filterModel = this.gridApi.getFilterModel();
const paramsCopy = { ...this.state.params, filterModel };
console.group('handleFilterChanged');
console.log('filterModel', filterModel);
console.log('state filterModel', this.state.params.filterModel);
if (!areEqualShallow(filterModel, this.state.params.filterModel)) {
this.gridApi.setFilterModel(filterModel);
this.setState({ params: paramsCopy }, () => this.props.onFetchEvents(paramsCopy));
}
console.groupEnd();
function areEqualShallow(a, b) {
for (var key in a) {
if (!(key in b) || a[key] !== b[key]) {
return false;
}
}
for (var key in b) {
if (!(key in a) || a[key] !== b[key]) {
return false;
}
}
return true;
}
// this.gridApi.setFilterModel(filterModel);
};
render() {
return (
<Fragment>
<div>
Page Size:
<select onChange={this.onPageSizeChanged.bind(this)} id="page-size">
<option value="10">10</option>
<option value="100">100</option>
<option value="500">500</option>
<option value="1000">1000</option>
</select>
</div>
<div
className="ag-theme-balham-dark"
style={{
height: '500px',
width: '100%',
}}
>
<AgGridReact
rowSelection="multiple"
gridOptions={this.state.gridOptions}
columnDefs={this.state.columnDefs}
rowData={this.props.events.data}
defaultColDef={this.state.defaultColDef}
onGridReady={this.onGridReady}
rowHeight={49}
/>
</div>
</Fragment>
);
}
}
const mapStateToProps = state => ({
events: state.app.events,
});
const mapDispatchToProps = {
onFetchEvents: params => fetchEvents(params),
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(DatagridPage);
In your column definition try setting the newRowsAction to "keep", e.g.
{
headerName: 'name',
field: 'name',
headerClass: 'test',
filter: 'agTextColumnFilter',
filterParams: {
newRowsAction: 'keep'
}
}
Or set it for all via defaultColDef, e.g.
gridOptions: {
defaultColDef: {
filterParams: {
newRowsAction: 'keep'
}
}
}
Hope someone can help, I've search examples of this but can't get them to work on my particular setup. I have a stacked column chart in Highcharts representing 5 sets of data for each day of the week.
I then select a day to see an hourly breakdown of the day, I have the data coming through but I can't get the 5 values I have for each hour to stack on top of each other. (in the same way as the first chart) - ideally this second chart would be an area chart)
Here is my code:
$(function () {
Highcharts.setOptions({
lang: {
drillUpText: 'Reset'
}
});
// Create the chart
$('#chart2').highcharts({
chart: {
type: 'column',
height: 300
},
credits: {
enabled: false
},
title: {
text: null
},
subtitle: {
text: 'Select a day to expand to hourly data'
},
legend: {
enabled: false
},
tooltip: {
formatter: function() {
var point = this.point,
s = 'Day: <b>' + point.name + '</b><br/>Utilisation: <b>' + this.y + '% </b><br/>';
if (point.drilldown) {
s = 'Day: <b>' + point.name + '</b><br/>Utilisation: <b>' + this.y + '% </b><br/>Select to view hours';
} else {
s = 'Time: <b>' + point.name + '</b><br/>Utilisation: <b>' + this.y + '% </b><br/>Reset to return';
}
return s;
}
},
xAxis: {
type: 'category',
//categories: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
//categories: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24']
},
yAxis: {
title: false, // {text: 'Utilisation'}, Y axis title - taken text out
tickPositions: [0, 50, 70, 100], // Y axis labels
labels: {
format: '{value}%' // Y axis labels with % suffix
},
min: 0, // Following lines setting the grids to off adding min max
max: 100,
minorGridLineWidth: 0,
gridLineWidth: 0,
alternateGridColor: null,
plotBands: [{ // Below Avg.
from: 0,
to: 50,
color: 'rgba(255,108,96, 0.5)',
label: {
// text: 'Below Average',
style: {
color: 'rgba(153,194,98, 0.8)'
}
}
}, { // Average
from: 50,
to: 70,
color: 'rgba(248,211,71, 0.5)',
label: {
// text: 'Average',
style: {
color: 'rgba(153,194,98, 0.8)'
}
}
}, { // Above Avg.
from: 70,
to: 100,
color: 'rgba(153,194,98, 0.5)',
label: {
text: 'Above Average',
style: {
color: 'rgba(153,194,98, 0.8)'
}
}
}]
},
plotOptions: {
series: {
stacking: 'normal'
}
},
series: [{
type: 'column',
name: 'Status 5',
color: '#86C9F2',
borderWidth: 0,
shadow: true,
data: [<?php echo $data7a5?>]
}, {
type: 'column',
name: 'Status 4',
color: '#6FB2DB',
borderWidth: 0,
shadow: true,
data: [<?php echo $data7a4?>]
}, {
type: 'column',
name: 'Status 3',
color: '#589BC4',
borderWidth: 0,
shadow: true,
data: [<?php echo $data7a3?>]
}, {
type: 'column',
name: 'Status 2',
color: '#4184AD',
borderWidth: 0,
shadow: true,
data: [<?php echo $data7a2?>]
}, {
type: 'column',
name: 'At the Desk',
color: '#2B6E97', //rgb(43, 110, 151)
borderWidth: 0,
shadow: true,
data: [<?php echo $data7a?>]
}],
drilldown: {
drillUpButton: {
//relativeTo: 'spacingBox',
position: {
y: 10,
x: -10
},
theme: {
fill: 'white',
stroke: 'silver',
r: 2,
states: {
hover: {
fill: '#41739D',
style: {
color: 'white'
}
}
}
}
},
series: [{
type: 'column',
id: 'D2',
data: [['8', 13.77],['8', 2.74],['8', 1.27],['8', 2.64],['8', 2.28],['9', 29.30],['9', 6.44],['9', 3.79],['9', 5.11],['9', 5.32],['10', 36.41],['10', 9.01],['10', 5.47],['10', 7.11],['10', 7.06],['11', 34.12],['11', 7.50],['11', 4.48],['11', 10.02],['11', 8.28],['12', 26.82],['12', 5.03],['12', 5.79],['12', 15.00],['12', 10.27],['13', 30.08],['13', 5.40],['13', 5.34],['13', 11.73],['13', 9.57],['14', 33.90],['14', 7.75],['14', 4.78],['14', 6.41],['14', 9.33],['15', 33.27],['15', 7.73],['15', 4.95],['15', 8.11],['15', 7.09],['16', 31.29],['16', 8.53],['16', 4.51],['16', 6.44],['16', 5.81],['17', 17.36],['17', 3.87],['17', 2.06],['17', 4.47],['17', 3.42],['18', 4.79],['18', .38],['18', .79],['18', 1.44],['18', 2.45]]
}, {
type: 'area',
id: 'D3',
data: [<?php echo $data7b2?>]
}, {
type: 'area',
id: 'D4',
data: [<?php echo $data7b3?>]
}, {
type: 'area',
id: 'D5',
data: [<?php echo $data7b4?>]
}, {
type: 'area',
id: 'D6',
data: [<?php echo $data7b5?>]
}]
}
});
});
I've shown the first drilldown data so you can see the structure. Any help would be appreciated.
Thanks
Rob
You can change the type of drill-down data from ['time', data] to [time, data]:
data: [
['8', 13.77],
['8', 2.74],
['8', 1.27],
['8', 2.64],
['8', 2.28],
['9', 29.30],
['9', 6.44],
['9', 3.79],
['9', 5.11],
['9', 5.32],
...
]
to:
data: [
[8, 13.77],
[8, 2.74],
[8, 1.27],
[8, 2.64],
[8, 2.28],
[9, 29.30],
[9, 6.44],
[9, 3.79],
[9, 5.11],
[9, 5.32],
...
]
And I also changed the tooltip.formatter to show the correct tooltip for drill-downs. Here's the DEMO