MUI Datatables delete rows - reactjs

I'm using a custom toolbar with MUI Datatables and can access the currently selected rows but when I delete these rows, I want to clear the selected rows. On the current behavior, if I select and delete the first two rows (0,1), these rows are removed from the MUI Datatable but the selection change to the rows (2,3).
const options = {
filterType: 'checkbox',
customToolbarSelect: selectedRows => (
<MyCustomToolbarSelect
selectedRows={selectedRows}
onRowsDelete={deleteSelected}
/>
),
}

Turns out you can set a state variable to hold the selected rows and change the value on the onRowsDelete:
const [selectedRows, setSelectedRows] = useState([]);
const options = {
rowsSelected: selectedRows,
onRowSelectionChange: (rowsSelectedData, allRows, rowsSelected) => {
setSelectedRows(rowsSelected);
},
customToolbarSelect: () => (
<MyCustomToolbarSelect
selectedRows={selectedRows}
onRowsDelete={() => {
deleteSelected();
setSelectedRows([]);
}
/>
),
You can access an example using class components here: https://codesandbox.io/s/muidatatables-custom-toolbar-hv5n9?file=/index.js

Related

enableRowSelection in useTable

I'm not too familiar with useReactTable, however I need to make use of useTable to conditionally select certain rows based on some conditions.
Below is the useReactTable equivalent of what I want to achieve:
const table = useReactTable({
data,
columns,
state: {
rowSelection
},
enableRowSelection: (row) => row.original.age > 18, //This is what I want to achieve with useTable
onRowSelectionChange: setRowSelection,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
debugTable: true
});
The checkbox at any particular row should only be selectable if the users age is more than 18.
How can that be achieved with useTable in React?
Thanks
Update:
I was able to get the rows disabled based on some conditions, but there is another bigger problem, which is that when I use the Select All checkbox at the top of the table, I see even the disabled checkboxes still getting checked, I don't want that. Below is a screenshot of the behavior I don't want
A disabled checkbox should never get checked even when Select All is used
You can add disabled property to checkboxes and control if they are checked not or not based on a condition. I used official row-selection-example to test.
function Table({ columns, data }) {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
selectedFlatRows,
state: { selectedRowIds },
} = useTable(
{
columns,
data,
},
useRowSelect,
hooks => {
hooks.visibleColumns.push(columns => [
// Let's make a column for selection
{
id: 'selection',
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// Disabled and not checked if age < 13
Cell: ({ row }) => (
<div>
{row.original.age >= 13 ? <IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
: <IndeterminateCheckbox {...row.getToggleRowSelectedProps()} disabled={true} checked={row.original.age >= 13 ? true : false} />}
</div>
),
},
...columns,
])
}
)

Deselect checkboxes based on dropdown select value in ProComponents React

Resources for ProComponents that are being used in this demo.
ProForm same properties as ProModal
ProFormCheckBox.Group
ProFormSelect
I am trying to deselect all checkboxes every time I select a new item.
So for example if I select PCA from the dropdown and checked boxed 2.
Example 1
Then switched over to LSCA from the dropdown I want to deselect all checkboxes.
Example 2
Instead, what happens check box 2 is still selected.
Example 3
I have it set where each dropdown item has a different list of checkboxes set into it.
They are four different arrays. The more interesting parts are the three different useState. One controls the state of which dropdown item is selected. Another controls the state of which array of checkboxes should be displayed. The last controls the state of which checkboxes should be marked. Notes of interest in the code are
// Controls the state of the dropdown menu to be selected
const [selected, setSelected] = useState('');
// Controls the state of which array of checkboxes should be displayed
const [checkBoxes, setCheckBoxes] = useState([]);
// Controls the state of which checkboxes are checkmarked
const [markedCheckBoxes, setMarkedCheckBoxes] = useState([]);
Next code of interest is the function changeSelectOptionHandler which is run onChange of the ProFormSelect. This should run setMarkedCheckBoxes to set the state into an empty array so no boxes are selected.
/** Function that will set different values to state variable
* based on which dropdown is selected
*/
const changeSelectOptionHandler = (event) => {
// This should set the state of the setMarkedCheckBoxes to be empty
setMarkedCheckBoxes([]);
// Sets the state of which array of checkboxes should be displayed based on event
checkBoxOptions(event);
// Sets the state of which dropdown is selected based on the event
setSelected(event);
};
According to the docs I should set the value as what checkBoxes should be marked in ProFormCheckbox.Group
<ProFormCheckbox.Group
name="rows"
label="Select Rows"
options={checkBoxes}
onChange={(e) => {
console.log('state changes');
setMarkedCheckBoxes(e);
}}
// This is where I set which checkboxes should be marked with value
// initialValue={markedCheckBoxes}
value={markedCheckBoxes}
/>
I was able to use the React Dev Tools and confirm values are updated for markedCheckBoxes based on when a new dropdown item is selected which should be an empty array. I also tested that when I cancel or submit the modalForm that markedCheckBoxes is an empty array and is correctly displayed with setting the value on the ProFormCheckbox.Group. So I am stumped on how to correctly display what is on the value in ProFormCheckbox.Group after updating the select menu. Below is the full code snippet of said RowModal component.
import { PlusOutlined } from '#ant-design/icons';
import { Button, message } from 'antd';
import { useState } from 'react';
import ProForm, { ModalForm, ProFormSelect, ProFormCheckbox } from '#ant-design/pro-form';
import { updateRule } from '#/services/ant-design-pro/api';
const RowModal = ({ orderId, actionRef }) => {
/** Different arrays for different dropdowns */
const eca = ['1', '2', '3', '4', '5'];
const pca = ['1', '2'];
const lsca = ['1', '2', '3', '4', '5', '6'];
const mobility = ['1', '2', '3', '4'];
// Controls the state of the dropdown menu to be selected
const [selected, setSelected] = useState('');
// Controls the state of which array of checkboxes should be displayed
const [checkBoxes, setCheckBoxes] = useState([]);
// Controls the state of which checkboxes are checkmarked
const [markedCheckBoxes, setMarkedCheckBoxes] = useState([]);
/** Function that will set different values to state variable
* based on which dropdown is selected
*/
const changeSelectOptionHandler = (event) => {
// This should set the state of the setMarkedCheckBoxes to be empty
setMarkedCheckBoxes([]);
// Sets the state of which array of checkboxes should be displayed based on event
checkBoxOptions(event);
// Sets the state of which dropdown is selected based on the event
setSelected(event);
};
/** This will be used to create set of checkboxes that user will see based on what they select in dropdown*/
const checkBoxOptions = (event) => {
/** Setting Type variable according to dropdown */
if (event === 'ECA') setCheckBoxes(eca);
else if (event === 'PCA') setCheckBoxes(pca);
else if (event === 'LSCA') setCheckBoxes(lsca);
else if (event === 'Mobility') setCheckBoxes(mobility);
else setCheckBoxes([]);
};
return (
<ModalForm
title="Assign to Area and Row"
trigger={
<Button type="primary">
<PlusOutlined />
Assign
</Button>
}
autoFocusFirstInput
modalProps={{
destroyOnClose: true,
onCancel: () => {
setSelected('');
setCheckBoxes([]);
setMarkedCheckBoxes([]);
},
}}
onFinish={async (values) => {
const newValues = { ...values, order: orderId };
const req = await updateRule('http://127.0.0.1:3000/api/v1/floorPlans', {
data: newValues,
});
message.success('Success');
setSelected('');
setCheckBoxes([]);
setMarkedCheckBoxes([]);
actionRef.current?.reloadAndRest?.();
return true;
}}
// initialValues={{ rows: ['A'] }}
>
<ProForm.Group>
<ProFormSelect
request={async () => [
{
value: 'PCA',
label: 'PCA',
},
{
value: 'ECA',
label: 'ECA',
},
{
value: 'LSCA',
label: 'LSCA',
},
{
value: 'Mobility',
label: 'Mobility',
},
]}
// On change of dropdown, changeSelectOptionHandler will be called
onChange={changeSelectOptionHandler}
width="xs"
name="area"
label="Select Area"
value={selected}
/>
</ProForm.Group>
<ProFormCheckbox.Group
name="rows"
label="Select Rows"
options={checkBoxes}
onChange={(e) => {
console.log('state changes');
setMarkedCheckBoxes(e);
}}
// This is where I set which checkboxes should be marked with value
// initialValue={markedCheckBoxes}
value={markedCheckBoxes}
/>
</ModalForm>
);
};
export default RowModal;
Thanks in advance!
ProForm is a repackaging of antd Form
So you can use Form API for your purpose
import { useForm } from 'antd/lib/form/Form'
//...
const [form] = useForm();
// and then pass FormInstance in your component
<ModalForm
form={form}
//...
/>
// then in your handlers where you want to modify values use
form.setFieldsValue({
"fieldName": value
})

Persist selection between pagination using fluentui/React in DetailsList

How to Persist selection between pagination in DataList in fluentui/React
<DetailsList
items={items}
compact={true}
columns={columns}
selectionMode={SelectionMode.multiple}
getKey={getKey}
setKey="none"
layoutMode={DetailsListLayoutMode.justified}
isHeaderVisible={true}
onItemInvoked={onItemInvoked}
/>
I have manage the selection between some other re-render events, I think the solution should be compatible.
The basic idea is to store the selected items by myself, and when the selection changes or grid re-render due to data change, set the selection manually.
define the state to cache selected item, and store selected item in change event:
const [selectedItems, setSelectedItems] = useState<Array<IObjectWithKey> | undefined>(undefined)
const [selection] = useState(new Selection({
onSelectionChanged: () => {
setSelectedItems(selection.getSelection());
}
}));
manually set the selected item after any re-render trigger, remember to switch off event to avoid infinite loop:
useEffect(() => {
var preSelected = getSelectedChildren(selectedItems);
if (preSelected.length > 0) {
selection.setChangeEvents(false);
itemList.forEach(item => {
selection.setKeySelected(item.key.toString(),
preSelected.findIndex(s => s.key === item.key) >= 0, true);
});
selection.setChangeEvents(true);
}
reloadState(getSelectedChildren(selectedItems));
}, [itemList, selectedItems]);
bind selection to detaillist by pass selection={selection}

AG grid react framwork component cell render doesn't updae props

I am building a react functional component with an AgGridReact:
const DataGrid = (props) =>
{
const [gridRowData, setGridRowData] = useState([]);
const [columnDefinition, setColumnDefinition] = useState([]);
useEffect(async () =>
{
if(props.name && props.name !== "")
{
... get grid row data and column definition here according to props.name - working fine
}
},[props.name]);
let frameworkComponents = {
customLoadingOverlay: LoadingOverlayTemplate,
customNoRowsOverlay: UxDataGridCustomNoRows,
editButton: params => <ViewAndDeleteSetting {...params}
openAddConfigurationsWindow={openAddConfigurationsWindow}
onDeleteSetting={onDeleteSetting} />
};
.
.
.
const onDeleteSetting = async () =>
{
console.log("ON DELETE AND NAME IS: " + props.name ); //PRINTS AN EMPTY STRING
...
}
return (
<AgGridReact
columnDefs={columnDefinition}
rowData={gridRowData}
frameworkComponents={frameworkComponents}/>
);
As you can see in the comment in onDeleteSetting, the name is empty when this callback is invoked. The rendering of the cell itself is fine and ViewAndDeleteSetting is indeed rendered, just it seems like it is being rendered only once and not every time the name changes. How can I cause the inner ViewAndDeleteSetting component to be re rendered with the most recent frameworkComponents?
The problem was with how I tried passing props to ViewAndDeleteSetting. If you want to pass prop to a cell rendered component, you shouldn't be doing it in frameworkComponents, but rather you need to do it in the column definition like this:
useEffect(() =>
{
let columns = [{headerName: '', cellRenderer: 'editButton', width: 90, editable: false,
cellRendererParams: {
openAddConfigurationsWindow: openAddConfigurationsWindow,
onDeleteSetting: onDeleteSetting
}},
.. other columns
]
setColumnDefinition(columns);
},[props.name]);
The columns with the cellRendererParams do gets recreated in the useEffect when the name changes, and then the component can access this params regularly via its props

Antd: Is it possible to move the table row expander inside one of the cells?

I have an antd table where the data inside one of the columns can get pretty large. I am showing this data in full when the row is expanded but because the cell with a lot of data is on the right side of the screen and the expander icon is on the left side of the screen it is not very intuitive. What I would like to do is move the expander icon inside the actual cell so that the user knows they can click the + to see the rest of the data.
Thanks in advance.
Yes, you can and you have to dig a little deeper their docucmentation...
According to rc-table docs you can use expandIconColumnIndex for the index column you want to add the +, also you have to add expandIconAsCell={false} to make it render as part of the cell.
See Demo
This is how you can make any column expendable.
First add expandedRowKeys in your component state
state = {
expandedRowKeys: [],
};
Then you need to add these two functions onExpand and updateExpandedRowKeys
<Table
id="table-container"
rowKey={record => record.rowKey}
className={styles['quote-summary-table']}
pagination={false}
onExpand={this.onExpand}
expandedRowKeys={this.state.expandedRowKeys}
columns={columns({
updateExpandedRowKeys: this.updateExpandedRowKeys,
})
}
dataSource={this.data}
oldTable={false}
/>
This is how you need to define the function so
that in expandedRowKeys we will always have
updates values of expanded rowKeys
onExpand = (expanded, record) => {
this.updateExpandedRowKeys({ record });
};
updateExpandedRowKeys = ({ record }) => {
const rowKey = record.rowKey;
const isExpanded = this.state.expandedRowKeys.find(key => key === rowKey);
let expandedRowKeys = [];
if (isExpanded) {
expandedRowKeys = expandedRowKeys.reduce((acc, key) => {
if (key !== rowKey) acc.push(key);
return acc;
}, []);
} else {
expandedRowKeys.push(rowKey);
}
this.setState({
expandedRowKeys,
});
}
And finally, you need to call the function updateExpandedRowKeys
for whichever column you want to have the expand-collapse functionality available.
Even it can be implemented for multiple columns.
export const columns = ({
updateExpandedRowKeys,
}) => {
let columnArr = [
{
title: 'Product',
key: 'productDes',
dataIndex: 'productDes',
className: 'productDes',
render: (text, record) => (
<span onClick={rowKey => updateExpandedRowKeys({ record })}>
{text}
</span>
),
}, {
title: 'Product Cat',
key: 'productCat',
dataIndex: 'productCat',
className: 'product-Cat',
}]
}

Resources