I'm trying to use react-tabulator but I'm having troubles rendering a function inside my table columns.
The function is returning JSX but it is using other class functions, which fails to execute.
basically, the column "actions" has 2 icons, edit and delete, each of them is an icon component with onClick property which calls a function inside my class. The function fails to execute because "this" is undefined, so the function couldn't be found.
here is my code:
import React, {Component} from 'react';
import {reactFormatter, ReactTabulator} from 'react-tabulator'
import IconButton from "#material-ui/core/IconButton";
import {IconsDiv} from "./styles";
import {FontAwesomeIcon} from "#fortawesome/react-fontawesome";
import {faPencilAlt, faTrash} from "#fortawesome/free-solid-svg-icons";
class MyTable extends Component {
constructor(props) {
super(props);
this.state = {
teamsTableData: this.createFormattedData([1,2]),
rowID: 0,
};
this.options = {
movableColumns: true,
pagination: "local",
paginationSize: 15,
};
this.ref = null;
this.handleEdit = this.handleEdit.bind(this);
this.handleDelete = this.handleDelete.bind(this);
}
createFormattedData(data) {
return {
rows: data.map(ele => {
return {
id: ele.teamId,
name: "test1",
count: "test1",
};
}
),
columns: [
{
title: "Name", field: "name",
headerFilter: "input",
},
{
title: "Count", field: "count",
headerFilter: "input",
headerFilterFunc: "=",
},
{
title: "Actions", field: "actions",
formatter: reactFormatter(<this.createActions/>),
}
],
}
}
createActions(table) {
const rowData = table.cell._cell.row.data;
return <IconsDiv>
<IconButton onClick={() => this.handleEdit(rowData)}>
<FontAwesomeIcon icon={faPencilAlt} className={'Icon'}/>
</IconButton>
{" "}
<IconButton onClick={() => this.handleDelete(rowData)}>
<FontAwesomeIcon icon={faTrash} className='Icon'/>
</IconButton>
</IconsDiv>;
}
handleEdit(rowData) {
this.setState({rowID: rowData.id});
}
handleDelete(rowData) {
this.setState({deleteRow: rowData.id});
}
render() {
return (
<>
<ReactTabulator
ref={ref => (this.ref = ref)}
data={this.state.teamsTableData.rows}
columns={this.state.teamsTableData.columns}
tooltips={true}
options={this.options}
layout={"fitColumns"}
/>
</>
)
}
}
Does anyone know what is wrong? why the column is rendered properly, but the action fails? why does "this.handleEdit" or "this.handleDelete" couldn't be found? ( I guess "this" is undefined)
You can assign this to another variable so you can pass the scope into the function:
createActions(table) {
var self = this; //assign this to the self variable and pass into the icons
const rowData = table.cell._cell.row.data;
return <IconsDiv>
<IconButton onClick={() => self.handleEdit(rowData)}>
<FontAwesomeIcon icon={faPencilAlt} className={'Icon'}/>
</IconButton>
{" "}
<IconButton onClick={() => self.handleDelete(rowData)}>
<FontAwesomeIcon icon={faTrash} className='Icon'/>
</IconButton>
</IconsDiv>;
}
Have you tried using an arrow function instead?
createActions = (table) => {
const rowData = table.cell._cell.row.data;
return <IconsDiv>
<IconButton onClick={() => this.handleEdit(rowData)}>
<FontAwesomeIcon icon={faPencilAlt} className={'Icon'}/>
</IconButton>
{" "}
<IconButton onClick={() => this.handleDelete(rowData)}>
<FontAwesomeIcon icon={faTrash} className='Icon'/>
</IconButton>
</IconsDiv>;
}
Related
I have the below component as the main layout. When I click on the menu item, I want them to navigate to the home, holiday calendar, and event pages. What can I do with this horizontal menu? The below code shows the main layout wrapped around the above pages. I am using AntD and react-router. this main layout is wrapped around all other routers.
import { Layout, Menu, Button, Avatar, Row, Col, Space, Dropdown } from "antd";
import React, { useState } from "react";
import { Outlet } from "react-router-dom";
const navigation = [
{ label: "Home", key: 1 },
{ label: "Holiday Calendar", key: 2 },
{ label: "Event", key: 3 },
];
const MainLayout = () => {
const [open, setOpen] = useState(false);
const showDrawer = () => {
setOpen(true);
};
const onClose = () => {
setOpen(false);
window.dispatchEvent(new Event("loadHolidayCalander"));
};
const handleLogOut = () => {
localStorage.removeItem("access-token");
};
const menu = (
<Menu
items={[
{
key: "1",
label: <Button onClick={handleLogOut}>Log out</Button>,
},
]}
></Menu>
);
return (
<Layout style={{backgroundColor:"white"}}>
<Row style={{ backgroundColor:"#404140"}}>
<Col
style={{padding:5, margin:0, height:48}}
flex="300px"
>
<a href="/holiday-calander">
<img src="/logo.png" alt="logo" style={{ height: 38 }} />
</a>
</Col>
<Col>
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={["2"]}
items={navigation}
/>
</Col>
<Col
flex="auto"
style={{
padding:5
}}
>
<div style={{ position: "absolute", right: "5px" }}>
<Space size={20}>
<Dropdown overlay={menu} placement="topRight" arrow>
<Avatar style={{ width: 38, height:38 }} />
</Dropdown>
</Space>
</div>
</Col>
</Row>
<Layout
style={{
padding: 0,
backgroundColor: "white",
marginLeft:28,
marginRight:28,
}}
>
<Outlet />
</Layout>
</Layout>
);
};
export default MainLayout;
You can add an onClick handler to the Menu component, which will be passed an object with the key property you can search the navigation array for the matching element.
Add a link target to the navigation array elements.
import { ..., useNavigate, ... } from 'react-router-dom';
...
const navigation = [
{ label: "Home", key: 1, target: "/" },
{ label: "Holiday Calendar", key: 2, "/holidaycalendar" },
{ label: "Event", key: 3, "/event" },
];
...
const navigate = useNavigate();
const handleMenuClick = ({ key }) => {
const { target } = navigation.find(item => item.key === key) || {};
if (target) {
navigate(target);
}
};
...
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={["2"]}
items={navigation}
onClick={handleMenuClick}
/>
An improvement could be to make the key property the link target.
import { ..., useNavigate, ... } from 'react-router-dom';
...
const navigation = [
{ label: "Home", key: "/" },
{ label: "Holiday Calendar", key: "/holidaycalendar" },
{ label: "Event", key: "/event" },
];
...
const navigate = useNavigate();
const handleMenuClick = ({ key }) => {
if (key) {
navigate(key);
}
};
...
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={["/holidaycalendar"]}
items={navigation}
onClick={handleMenuClick}
/>
These are my codes for the snackbar and it wasn't working whenever I'll click the button. I wanted the snackbar to appear once I'll click the button "confirm". Almost all of the examples I have seen are in a functional component, so how can I make the Snackbar work as expected in a class component?
class name extends Component {
constructor() {
super();
this.state = { orders: [], open: false };
}
handleOpen = () => this.setState({ open: true });
handleClose = () => this.setState({ open: false });
columns = [
{
name: "Confirm",
options: {
customBodyRender: (value, tableMeta) => {
return (
<FormControlLabel
value={value}
control={
<Button>
confirm
</Button>
}
onClick={(e) => {
try {
//firestore codes
);
} catch (err) {
console.log(err);
}
this.handleOpen();
}}
/>
);
},
},
},
];
//code for options
//data fetching codes
render() {
const { open } = this.state;
return this.state.orders ? (
<div>
//muidatatable codes
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
open={open}
onClose={this.handleClose}
autoHideDuration={2000}
// other Snackbar props
>
Order Confirmed
</Snackbar>
</div>
) : (
<p>Loading...</p>
);
}
}
Ignoring a few syntax errors, you should check if there are any orders by using the length and not just by mere existence of the array as you have initialized an empty array this.state.orders will always result in true. Instead use this.state.orders.length > 0 ? to check if there are any orders or not.
Snackbar's child(ren) should be wrapped in components and not just strings directly, for using string directly you can use message prop of Snackbar.
Also, it's a standard to write class's name starting with an upper-case letter.
Here's a working code: Material UI Snackbar using classes
import React, { Component } from "react";
import { FormControlLabel, Button, Snackbar } from "#material-ui/core";
import MuiAlert from "#material-ui/lab/Alert";
export default class Name extends Component {
constructor() {
super();
this.state = { orders: [], open: false };
}
handleOpen = () => this.setState({ open: true });
handleClose = () => this.setState({ open: false });
handleClick = () => this.setState({ orders: [1], open: true });
columns = [
{
name: "Confirm",
options: {
customBodyRender: (value, tableMeta) => {
return (
<FormControlLabel
value={value}
control={<Button>confirm</Button>}
onClick={(e) => {
try {
//firestore codes
} catch (err) {
console.log(err);
}
this.handleOpen();
}}
/>
);
}
}
}
];
//code for options
//data fetching codes
render() {
const { open } = this.state;
return (
<>
<Button variant="outlined" onClick={this.handleClick}>
Open snackbar
</Button>
{this.state.orders.length > 0 ? (
<div>
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left"
}}
open={open}
onClose={this.handleClose}
autoHideDuration={2000}
// other Snackbar props
>
{/* <span
style={{
background: "#000",
color: "#fff",
padding: "20px 5px",
width: "100%",
borderRadius: "5px"
}}
>
Order Confirmed
</span> */}
<MuiAlert
onClose={this.handleClose}
severity="success"
elevation={6}
variant="filled"
>
Success Message
</MuiAlert>
</Snackbar>
</div>
) : (
<p>loading...</p>
)}
</>
);
}
}
The following changes are made to make it work:
Removed Order Confirmed and used message prop of Snackbar
Passed values to orders array in constructor
Passed true in open variable.
Below is the working code for snack bar.
import React, { Component } from "react";
import Snackbar from "#material-ui/core/Snackbar";
class SnackBarSof extends Component {
constructor() {
super();
this.state = { orders: [1, 2], open: true };
}
handleOpen = () => this.setState({ open: true });
handleClose = () => this.setState({ open: false });
render() {
console.log(this.state.orders);
console.log(this.state);
const { open } = this.state;
return this.state.orders ? (
<div>
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
open={open}
onClose={this.handleClose}
message="order confirmed"
autoHideDuration={2000}
></Snackbar>
</div>
) : (
<p>Loading...</p>
);
}
}
export default SnackBarSof;
I'm newbie at React.
My project simply allows more than one person to listen to music simultaneously. I managed to make an api connection over Spotify, but I keep the information of the room on the firebase. Therefore, I need to get the room's founder, that is, roomAdminMail information, from firebase. When I try to retrieve room information like I did on Homepage, I get an error like this.
import React, {Component} from "react";
import {
Grid,
Typography,
Card,
IconButton,
LinearProgress,
} from "#material-ui/core";
import Spotify from 'spotify-web-api-js';
import PlayArrowIcon from "#material-ui/icons/PlayArrow";
import PauseIcon from "#material-ui/icons/Pause";
import SkipNextIcon from "#material-ui/icons/SkipNext";
import SkipPreviousIcon from '#material-ui/icons/SkipPrevious';
import firebase from 'firebase';
import {GridItem} from '#chakra-ui/react';
const spotifyWebApi = new Spotify();
//const Player = props =>
class Player extends Component {
constructor(){
super();
const params = this.getHashParams();
this.state = {
logeedIn : params.access_token ? true : false,
currentStatus: false,
roomAdminMail: "",
roomName: "",
roomInfo: "",
nowPlaying: {
artist_name : 'Not Checked',
song_name: 'Not Checked',
image: ''
}
}
if(params.access_token){
spotifyWebApi.setAccessToken(params.access_token)
}
}
getHashParams() {
var hashParams = {};
var e, r = /([^&;=]+)=?([^&;]*)/g,
q = window.location.hash.substring(1);
while ( e = r.exec(q)) {
hashParams[e[1]] = decodeURIComponent(e[2]);
}
return hashParams;
}
getNowPlaying(){
spotifyWebApi.getMyCurrentPlayingTrack()
.then((response) => {
this.setState({
nowPlaying: {
artist_name: response.item.artists[0].name,
song_name: response.item.name,
image: response.item.album.images[0].url
}
})
})
}
getRoomCollection(){
const db = firebase.firestore();
db.collection("rooms").onSnapshot(function(querySnapshot) {
this.setState(querySnapshot.docs.map((doc) => ({
roomAdminMail: doc.data().roomAdminMail,
roomName: doc.data().roomName,
roomInfo: doc.data().roomInfo
})));
})
}
componentDidMount(){
this.getNowPlaying();
this.getRoomCollection();
}
render() {
return (
<GridItem
colStart={[1, null, null, 2, null, null]}
colSpan={[3, null, null, 1, null, null]}
p={6}
>
<a href='http://localhost:8888'>
<button>Login With Spotify</button>
</a>
<Typography component="h5" variant="h5">
Room Admin: {this.state.roomAdminMail}
</Typography>
<Card item align="center">
<Grid container alignItems="center">
<Grid item align="center" xs={12} className="now-playing__img">
<img src={this.state.nowPlaying.image} />
</Grid>
<Grid item align="center" xs={8}>
<Typography item align="center" component="h5" variant="h5">
{this.state.nowPlaying.song_name}
</Typography>
<Typography item align="center" color="textSecondary" variant="subtitle1">
{this.state.nowPlaying.artist_name}
</Typography>
<div>
<IconButton
onClick={() => { spotifyWebApi.skipToPrevious();
{this.getNowPlaying()}
}}
>
<SkipPreviousIcon />
</IconButton>
<IconButton
onClick={() => { spotifyWebApi.play();
{this.getNowPlaying()}
}}
>
<PlayArrowIcon />
</IconButton>
<IconButton
onClick={() => { spotifyWebApi.pause();
{this.getNowPlaying()}
}}
>
<PauseIcon />
</IconButton>
<IconButton onClick={() => spotifyWebApi.skipToNext()}>
{this.getNowPlaying()}
<SkipNextIcon />
</IconButton>
</div>
</Grid>
</Grid>
</Card>
</GridItem>
);
}
}
export default Player;
You need to bind this context to the function or use an arrow function.
Binding this:
constructor(){
this.getRoomCollection = this.getRoomCollection.bind(this);
this.getNowPlaying = this.getNowPlaying.bind(this);
}
Using arrow function:
Just make the function an arrow function
getRoomCollection = () => {
const db = firebase.firestore();
db.collection("rooms").onSnapshot((querySnapshot) => {
this.setState(querySnapshot.docs.map((doc) => ({
roomAdminMail: doc.data().roomAdminMail,
roomName: doc.data().roomName,
roomInfo: doc.data().roomInfo
})));
})
}
Currently I have a simple material-table like this:
<MaterialTable
options={myOptions}
title="MyTitle"
columns={state.columns}
data={state.data}
tableRef={tableRef} // Not working
editable={{
onRowAdd: ...,
onRowDelete: ...,
onRowUpdate: ...
}}
/>;
where I'm trying to a create new add button (not edit the current one): each Row in the Bar Column should have a custom add button. I've looked through the MaterialTable source code but I couldn't reproduce the code that is used for the default add button which is:
calculatedProps.actions.push({
icon: calculatedProps.icons.Add,
tooltip: localization.addTooltip,
position: "toolbar",
disabled: !!this.dataManager.lastEditingRow,
onClick: () => {
this.dataManager.changeRowEditing();
this.setState({
...this.dataManager.getRenderState(),
showAddRow: !this.state.showAddRow,
});
},
});
in particular I can't get to access the dataManager variable.
That is how the current table looks like, and I need to add the add button where there is the red sign.
I think this is what you are looking for:
The Actions column represents the default actions set. I added an specific button using custom column rendering (docs):
//..previous columns definition
{
title: "Custom Add",
field: "internal_action",
editable: false,
render: (rowData) =>
rowData && (
<IconButton
color="secondary"
onClick={() => addActionRef.current.click()}
>
<AddIcon />
</IconButton>
)
}
*Using rowData as conditional, prevents from rendering while filling the addition row.
Then I triggered the add action as shown here:
const MyComponent() {
const addActionRef = React.useRef();
return (
<>
<button onClick={() => addActionRef.current.click()}>
Add new item
</button>
<MaterialTable
//...
components={{
Action: props => {
//If isn't the add action
if (typeof props.action === typeof Function || props.action.tooltip !== 'Add') {
return <MTableAction {...props} />
} else {
return <div ref={addActionRef} onClick={props.action.onClick}/>;
}}
}}
editable={{
onRowAdd: (newData, oldData) => Promise.resolve(); //your callback here
}}
/>
</>
);
}
I extended the original snippet in order to complete the addition cycle. If you need to handle different types of actions, I think Editable section from the oficial docs would be handy.
Hope this works for you! Full code and sandbox here:
import React, { Fragment, useState } from "react";
import MaterialTable, { MTableAction } from "material-table";
import AddIcon from "#material-ui/icons/AddAlarm";
import IconButton from "#material-ui/core/IconButton";
export default function CustomEditComponent(props) {
const tableRef = React.createRef();
const addActionRef = React.useRef();
const tableColumns = [
{ title: "Client", field: "client" },
{ title: "Name", field: "name" },
{ title: "Year", field: "year" },
{
title: "Custom Add",
field: "internal_action",
editable: false,
render: (rowData) =>
rowData && (
<IconButton
color="secondary"
onClick={() => addActionRef.current.click()}
>
<AddIcon />
</IconButton>
)
}
];
const [tableData, setTableData] = useState([
{
client: "client1",
name: "Mary",
year: "2019"
},
{
client: "client2",
name: "Yang",
year: "2018"
},
{
client: "client3",
name: "Kal",
year: "2019"
}
]);
return (
<Fragment>
<MaterialTable
tableRef={tableRef}
columns={tableColumns}
data={tableData}
title="Custom Add Mode"
options={{
search: false
}}
components={{
Action: (props) => {
//If isn't the add action
if (
typeof props.action === typeof Function ||
props.action.tooltip !== "Add"
) {
return <MTableAction {...props} />;
} else {
return <div ref={addActionRef} onClick={props.action.onClick} />;
}
}
}}
actions={[
{
icon: "save",
tooltip: "Save User",
onClick: (event, rowData) => alert("You saved " + rowData.name)
}
]}
editable={{
onRowAdd: (newData) =>
Promise.resolve(setTableData([...tableData, newData]))
}}
/>
</Fragment>
);
Anyone know how to change the fontSize of the TableHeaderRow in a DevExtreme React Grid?
Here's an example of code from the website (https://devexpress.github.io/devextreme-reactive/react/grid/demos/featured/data-editing/) that I have been working with
import * as React from 'react';
import {
SortingState, EditingState, PagingState,
IntegratedPaging, IntegratedSorting,
} from '#devexpress/dx-react-grid';
import {
Grid,
Table, TableHeaderRow, TableEditRow, TableEditColumn,
PagingPanel, DragDropProvider, TableColumnReordering,
} from '#devexpress/dx-react-grid-material-ui';
import Paper from '#material-ui/core/Paper';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import Button from '#material-ui/core/Button';
import IconButton from '#material-ui/core/IconButton';
import Input from '#material-ui/core/Input';
import Select from '#material-ui/core/Select';
import MenuItem from '#material-ui/core/MenuItem';
import TableCell from '#material-ui/core/TableCell';
import DeleteIcon from '#material-ui/icons/Delete';
import EditIcon from '#material-ui/icons/Edit';
import SaveIcon from '#material-ui/icons/Save';
import CancelIcon from '#material-ui/icons/Cancel';
import { withStyles } from '#material-ui/core/styles';
import { ProgressBarCell } from '../../../theme-sources/material-ui/components/progress-bar-cell';
import { HighlightedCell } from '../../../theme-sources/material-ui/components/highlighted-cell';
import { CurrencyTypeProvider } from '../../../theme-sources/material-ui/components/currency-type-provider';
import { PercentTypeProvider } from '../../../theme-sources/material-ui/components/percent-type-provider';
import {
generateRows,
globalSalesValues,
} from '../../../demo-data/generator';
const styles = theme => ({
lookupEditCell: {
paddingTop: theme.spacing.unit * 0.875,
paddingRight: theme.spacing.unit,
paddingLeft: theme.spacing.unit,
},
dialog: {
width: 'calc(100% - 16px)',
},
inputRoot: {
width: '100%',
},
});
const AddButton = ({ onExecute }) => (
<div style={{ textAlign: 'center' }}>
<Button
color="primary"
onClick={onExecute}
title="Create new row"
>
New
</Button>
</div>
);
const EditButton = ({ onExecute }) => (
<IconButton onClick={onExecute} title="Edit row">
<EditIcon />
</IconButton>
);
const DeleteButton = ({ onExecute }) => (
<IconButton onClick={onExecute} title="Delete row">
<DeleteIcon />
</IconButton>
);
const CommitButton = ({ onExecute }) => (
<IconButton onClick={onExecute} title="Save changes">
<SaveIcon />
</IconButton>
);
const CancelButton = ({ onExecute }) => (
<IconButton color="secondary" onClick={onExecute} title="Cancel changes">
<CancelIcon />
</IconButton>
);
const commandComponents = {
add: AddButton,
edit: EditButton,
delete: DeleteButton,
commit: CommitButton,
cancel: CancelButton,
};
const Command = ({ id, onExecute }) => {
const CommandButton = commandComponents[id];
return (
<CommandButton
onExecute={onExecute}
/>
);
};
const availableValues = {
product: globalSalesValues.product,
region: globalSalesValues.region,
customer: globalSalesValues.customer,
};
const LookupEditCellBase = ({
availableColumnValues, value, onValueChange, classes,
}) => (
<TableCell
className={classes.lookupEditCell}
>
<Select
value={value}
onChange={event => onValueChange(event.target.value)}
input={(
<Input
classes={{ root: classes.inputRoot }}
/>
)}
>
{availableColumnValues.map(item => (
<MenuItem key={item} value={item}>
{item}
</MenuItem>
))}
</Select>
</TableCell>
);
export const LookupEditCell = withStyles(styles, { name: 'ControlledModeDemo' })(LookupEditCellBase);
const Cell = (props) => {
const { column } = props;
if (column.name === 'discount') {
return <ProgressBarCell {...props} />;
}
if (column.name === 'amount') {
return <HighlightedCell {...props} />;
}
return <Table.Cell {...props} />;
};
const EditCell = (props) => {
const { column } = props;
const availableColumnValues = availableValues[column.name];
if (availableColumnValues) {
return <LookupEditCell {...props} availableColumnValues={availableColumnValues} />;
}
return <TableEditRow.Cell {...props} />;
};
const getRowId = row => row.id;
class DemoBase extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
columns: [
{ name: 'product', title: 'Product' },
{ name: 'region', title: 'Region' },
{ name: 'amount', title: 'Sale Amount' },
{ name: 'discount', title: 'Discount' },
{ name: 'saleDate', title: 'Sale Date' },
{ name: 'customer', title: 'Customer' },
],
tableColumnExtensions: [
{ columnName: 'amount', align: 'right' },
],
rows: generateRows({
columnValues: { id: ({ index }) => index, ...globalSalesValues },
length: 12,
}),
sorting: [],
editingRowIds: [],
addedRows: [],
rowChanges: {},
currentPage: 0,
deletingRows: [],
pageSize: 0,
pageSizes: [5, 10, 0],
columnOrder: ['product', 'region', 'amount', 'discount', 'saleDate', 'customer'],
currencyColumns: ['amount'],
percentColumns: ['discount'],
};
const getStateDeletingRows = () => {
const { deletingRows } = this.state;
return deletingRows;
};
const getStateRows = () => {
const { rows } = this.state;
return rows;
};
this.changeSorting = sorting => this.setState({ sorting });
this.changeEditingRowIds = editingRowIds => this.setState({ editingRowIds });
this.changeAddedRows = addedRows => this.setState({
addedRows: addedRows.map(row => (Object.keys(row).length ? row : {
amount: 0,
discount: 0,
saleDate: new Date().toISOString().split('T')[0],
product: availableValues.product[0],
region: availableValues.region[0],
customer: availableValues.customer[0],
})),
});
this.changeRowChanges = rowChanges => this.setState({ rowChanges });
this.changeCurrentPage = currentPage => this.setState({ currentPage });
this.changePageSize = pageSize => this.setState({ pageSize });
this.commitChanges = ({ added, changed, deleted }) => {
let { rows } = this.state;
if (added) {
const startingAddedId = rows.length > 0 ? rows[rows.length - 1].id + 1 : 0;
rows = [
...rows,
...added.map((row, index) => ({
id: startingAddedId + index,
...row,
})),
];
}
if (changed) {
rows = rows.map(row => (changed[row.id] ? { ...row, ...changed[row.id] } : row));
}
this.setState({ rows, deletingRows: deleted || getStateDeletingRows() });
};
this.cancelDelete = () => this.setState({ deletingRows: [] });
this.deleteRows = () => {
const rows = getStateRows().slice();
getStateDeletingRows().forEach((rowId) => {
const index = rows.findIndex(row => row.id === rowId);
if (index > -1) {
rows.splice(index, 1);
}
});
this.setState({ rows, deletingRows: [] });
};
this.changeColumnOrder = (order) => {
this.setState({ columnOrder: order });
};
}
render() {
const {
classes,
} = this.props;
const {
rows,
columns,
tableColumnExtensions,
sorting,
editingRowIds,
addedRows,
rowChanges,
currentPage,
deletingRows,
pageSize,
pageSizes,
columnOrder,
currencyColumns,
percentColumns,
} = this.state;
return (
<Paper>
<Grid
rows={rows}
columns={columns}
getRowId={getRowId}
>
<SortingState
sorting={sorting}
onSortingChange={this.changeSorting}
/>
<PagingState
currentPage={currentPage}
onCurrentPageChange={this.changeCurrentPage}
pageSize={pageSize}
onPageSizeChange={this.changePageSize}
/>
<IntegratedSorting />
<IntegratedPaging />
<CurrencyTypeProvider for={currencyColumns} />
<PercentTypeProvider for={percentColumns} />
<EditingState
editingRowIds={editingRowIds}
onEditingRowIdsChange={this.changeEditingRowIds}
rowChanges={rowChanges}
onRowChangesChange={this.changeRowChanges}
addedRows={addedRows}
onAddedRowsChange={this.changeAddedRows}
onCommitChanges={this.commitChanges}
/>
<DragDropProvider />
<Table
columnExtensions={tableColumnExtensions}
cellComponent={Cell}
/>
<TableColumnReordering
order={columnOrder}
onOrderChange={this.changeColumnOrder}
/>
<TableHeaderRow showSortingControls />
<TableEditRow
cellComponent={EditCell}
/>
<TableEditColumn
width={120}
showAddCommand={!addedRows.length}
showEditCommand
showDeleteCommand
commandComponent={Command}
/>
<PagingPanel
pageSizes={pageSizes}
/>
</Grid>
<Dialog
open={!!deletingRows.length}
onClose={this.cancelDelete}
classes={{ paper: classes.dialog }}
>
<DialogTitle>
Delete Row
</DialogTitle>
<DialogContent>
<DialogContentText>
Are you sure to delete the following row?
</DialogContentText>
<Paper>
<Grid
rows={rows.filter(row => deletingRows.indexOf(row.id) > -1)}
columns={columns}
>
<CurrencyTypeProvider for={currencyColumns} />
<PercentTypeProvider for={percentColumns} />
<Table
columnExtensions={tableColumnExtensions}
cellComponent={Cell}
/>
<TableHeaderRow />
</Grid>
</Paper>
</DialogContent>
<DialogActions>
<Button onClick={this.cancelDelete} color="primary">
Cancel
</Button>
<Button onClick={this.deleteRows} color="secondary">
Delete
</Button>
</DialogActions>
</Dialog>
</Paper>
);
}
}
export default withStyles(styles, { name: 'ControlledModeDemo' })(DemoBase);
The font size of the text labelling the columns (e.g. product, region, amount) is fixed, and I see no parameters that can change it. Any ideas?
I think there are a few ways around this, the way I have used is having a fully controlled component.
Looks a little like this
<TableHeaderRow cellComponent={this.ExampleHeaderCell} />
Where ExampleHeaderCell is a component that would look something like this
ExampleHeaderCell = (props: any) => (<TableHeaderRow.Cell
className={exampleClass}
{...props}
key={column.name}
getMessage={() => column.title}
/>)
From there you can pass it a class as shown with exampleClass
You can take this further and have it customised for a particular column.
ExampleHeaderCells = (props: any) => {
const exampleClass = css({ backgroundColor: "blue" })
const { column } = props
if (column.name === "name") {
return (
<TableHeaderRow.Cell
className={exampleClass}
{...props}
key={column.name}
getMessage={() => column.title}
/>
)
}
return <TableHeaderRow.Cell {...props} key={column.name} getMessage={() => column.title} />
}
The example above is returning a specific cell with the exampleClass if the column name is equal to "name". Otherwise it just returns the regular TableHeaderRow.Cell