I have this component which is a filter for a table..
handleSearch function is responsible to update const filters... its work perfectly when dataIndex props is the same, but when it changes, filters value is backing to it's initial value, an empty array.
I can't manage to resolve it, I've already console log everything.
import React, { useState, } from "react";
import { SearchOutlined } from "#ant-design/icons";
import { Select, Button, Space } from "antd";
const TableFilter = (props) => {
const {
filterType,
filterMode,
filterOptions,
FilterSelectOnFocus,
dataIndex,
setSelectedKeys,
selectedKeys,
confirm,
clearFilters,
} = props;
const [filters, setFilters] = useState([]);
const SelectFilter = (
<Select
style={{ width: 188, marginBottom: 8, display: "block" }}
type={filterType}
mode={filterMode}
name={dataIndex}
value={selectedKeys}
optionFilterProp="children"
placeholder={`Search ${dataIndex}`}
onFocus={FilterSelectOnFocus}
showSearch
onChange={(value) => setSelectedKeys(value ? value : [])}
getPopupContainer={(trigger) => trigger}
notFoundContent
>
{filterOptions?.map((type, key) => (
<Select.Option value={type.value} key={key}>
{type.label}
</Select.Option>
))}
</Select>
);
const defaultFilterTypes = [
{
type: "select",
element: SelectFilter,
},
];
const handleFilterType = () => {
const type = defaultFilterTypes.find((types) => types.type === filterType);
return type.element;
};
const handleSearch = () => {
console.log(filters) //is empty when dataIndex value change, when it's is the same it get the update value of the 75 line
confirm();
const newFilterValues = [...filters]
const index = newFilterValues.findIndex(newValue => newValue.searchedColumn === dataIndex)
if(index === -1){
newFilterValues.push({ searchText: selectedKeys, searchedColumn: dataIndex})
}
else{
newFilterValues[index] = {searchText: selectedKeys, searchedColumn: dataIndex}
}
setFilters(newFilterValues)
}
const handleReset = () => {
console.log('reset');
clearFilters();
setFilters({ searchText: "" });
setSelectedKeys([]);
};
return (
<div style={{ padding: 8 }}>
{handleFilterType()}
<Space>
<Button
type="primary"
onClick={() => handleSearch()}
icon={<SearchOutlined />}
size="small"
style={{ width: 90 }}
>
Search
</Button>
<Button
onClick={() => handleReset()}
size="small"
style={{ width: 90 }}
>
Reset
</Button>
</Space>
</div>
);
};
export default TableFilter;
Table Component
import React, { useEffect, useState } from "react";
import { Table } from "antd";
import { getTransactions } from "../../../../api/Transactions";
import { formatCnpjCpf, formatCurrency } from "../../../../utils/masks";
import TableFilter from "../../../../shared-components/ant-design/containers/TableFilters";
import { getPartnersAsOptions } from "../../../../api/Partners";
const Insider = (props) => {
const [data, setData] = useState([]);
const [paginationValues, setPaginationValues] = useState({
current: 1,
pageSize: 50,
total: 0,
position: ["topRight"],
});
const [partners, setPartners] = useState([{value: null, label: 'carregando...'}])
const context = "insider";
function getColumnSearchProps(
dataIndex,
filterType,
filterMode,
filterOptions,
FilterSelectOnFocus
) {
return {
filterDropdown: ({
setSelectedKeys,
selectedKeys,
confirm,
clearFilters,
}) => {
return (
<TableFilter
dataIndex={dataIndex}
filterType={filterType}
filterMode={filterMode}
filterOptions={filterOptions}
FilterSelectOnFocus={FilterSelectOnFocus}
setSelectedKeys={setSelectedKeys}
selectedKeys={selectedKeys}
confirm={confirm}
clearFilters={clearFilters}
/>
);
},
};
}
async function getPartners(){
if(partners.length > 2){
return
}
const response = await getPartnersAsOptions(paginationValues)
setPartners(response.data)
}
const columns = [
{
dataIndex: ["transactionType", "desc"],
title: "Tipo de Transação",
sorter: true,
key: "orderTransactionType",
...getColumnSearchProps("orderTransactionType"),
},
{
dataIndex: "transactionDate",
title: "Data Transação",
key: "orderTransactionDate",
sorter: true,
...getColumnSearchProps("orderTransactionDate"),
},
{
title: "Nome origem",
dataIndex: ["source", "name"],
sorter: true,
key: "orderSourceCustomerName",
},
{
render: (render) => formatCnpjCpf(render.source.document.value),
title: "Documento origem",
key: "sourceCustomer",
...getColumnSearchProps("sourceCustomer", "select", "tags")
},
{
title: "Nome destino",
dataIndex: ["target", "name"],
sorter: true,
key: "orderTargetCustomerName",
},
{
render: (render) => formatCnpjCpf(render.target.document.value),
title: "Documento destino",
},
{
render: (render) => formatCurrency(render.value),
title: "Valor da transação",
key: "orderValue",
sorter: true,
align: "right",
},
{
render: (render) => formatCurrency(render.chargedTariff),
title: "Tarifa",
key: "orderChargedTariff",
sorter: true,
align: "right",
},
{
render: (render) => formatCurrency(render.cost),
title: "Custo",
key: "orderCost",
sorter: true,
align: "right",
},
{
render: (render) => formatCurrency(render.revenue),
title: "Receita",
key: "orderRevenue",
sorter: true,
align: "right",
},
{
title: "Parceiro",
name: "Parceiro",
dataIndex: ["partner", "name"],
key: "orderPartnerName",
sorter: true,
align: "center",
...getColumnSearchProps(
"orderPartnerName",
"select",
"multiple",
partners,
getPartners)
},
{
title: "id da transação",
name: "id da transação",
dataIndex: "id",
},
];
useEffect(function transactions() {
async function fetchTransactions() {
const response = await getTransactions(context, paginationValues);
if (response) {
const { data, pagination } = response;
setData(data);
setPaginationValues(pagination);
}
}
fetchTransactions();
// eslint-disable-next-line
}, []);
return <Table dataSource={data} columns={columns} />;
};
export default Insider;
You could move this piece of code
const [filters, setFilters] = useState([]);
In a higher level
Related
When I updated the cell in the data grid, I was only able to retrieve the ID and field through the prop selectedCellParams here but I could not get its modified value.
I want to get the selected value inside the handleSaveOrEdit() function in order to accomplish the PUT request to update the API. How to use the getValue as given in the MUI Docs.
The entire code is below;
import * as React from 'react';
import PropTypes from 'prop-types';
import { Typography, Grid } from "#mui/material";
import Box from '#mui/material/Box';
import Button from '#mui/material/Button';
import { DataGrid, GridCellModes } from '#mui/x-data-grid';
import { styled } from '#mui/material/styles';
import { useState, useEffect } from "react";
import Avatar from "#mui/material/Avatar";
import axios from 'axios';
const DrawerHeader = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: theme.spacing(0, 1),
...theme.mixins.toolbar,
}));
function EditToolbar(props) {
const { selectedCellParams, cellMode, cellModesModel, setCellModesModel } = props;
const handleSaveOrEdit = () => {
if (!selectedCellParams) {
return;
}
const { id, field } = selectedCellParams;
if (cellMode === 'edit') {
setCellModesModel({
...cellModesModel,
[id]: { ...cellModesModel[id], [field]: { mode: GridCellModes.View } },
});
const getValue = (params) => console.log(params.value);
console.log('value:::', getValue);
} else {
setCellModesModel({
...cellModesModel,
[id]: { ...cellModesModel[id], [field]: { mode: GridCellModes.Edit } },
});
}
};
const handleCancel = () => {
if (!selectedCellParams) {
return;
}
const { id, field } = selectedCellParams;
setCellModesModel({
...cellModesModel,
[id]: {
...cellModesModel[id],
[field]: { mode: GridCellModes.View, ignoreModifications: true },
},
});
};
const handleMouseDown = (event) => {
// Keep the focus in the cell
event.preventDefault();
};
return (
<Box
sx={{
borderBottom: 1,
borderColor: 'divider',
p: 1,
}}
>
<Button
onClick={handleSaveOrEdit}
onMouseDown={handleMouseDown}
disabled={!selectedCellParams}
color="primary"
variant="outlined"
>
{cellMode === 'edit' ? 'Save' : 'Edit'}
</Button>
<Button
onClick={handleCancel}
onMouseDown={handleMouseDown}
disabled={cellMode === 'view'}
color="primary"
variant="outlined"
sx={{ ml: 1 }}
>
Cancel
</Button>
</Box>
);
}
EditToolbar.propTypes = {
cellMode: PropTypes.oneOf(['edit', 'view']).isRequired,
cellModesModel: PropTypes.object.isRequired,
selectedCellParams: PropTypes.shape({
field: PropTypes.string.isRequired,
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
}),
setCellModesModel: PropTypes.func.isRequired,
};
export default function Donees() {
const [selectedCellParams, setSelectedCellParams] = React.useState(null);
const [cellModesModel, setCellModesModel] = React.useState({});
const [tableData, setTableData] = useState([])
const [fname, setFname] = useState("");
const [lname, setLname] = useState("");
const [address, setAddress] = useState("");
const [dob, setDob] = useState("");
const [gender, setGender] = useState("");
const [email, setEmail] = useState("");
const [phone, setPhone] = useState("");
const [grade, setGrade] = useState("");
const [photo, setPhoto] = useState("");
useEffect(() => {
fetch("http://localhost:4000/donees")
.then((data) => data.json())
.then((data) => setTableData(data))
}, [])
useEffect(() => {
axios.get("http://localhost:4000/donees")
.then((response) => {
setFname(response.data.fname);
setLname(response.data.lname);
setAddress(response.data.address);
setDob(response.data.dob);
setGender(response.data.gender);
setEmail(response.data.email);
setPhone(response.data.phone);
setGrade(response.data.grade);
setPhoto(response.data.photo);
})
}, [])
// console.log(tableData);
const handleCellFocus = React.useCallback((event) => {
const row = event.currentTarget.parentElement;
const id = row.dataset.id;
const field = event.currentTarget.dataset.field;
setSelectedCellParams({ id, field });
}, []);
const cellMode = React.useMemo(() => {
if (!selectedCellParams) {
return 'view';
}
const { id, field } = selectedCellParams;
return cellModesModel[id]?.[field]?.mode || 'view';
}, [cellModesModel, selectedCellParams]);
const handleCellKeyDown = React.useCallback(
(params, event) => {
if (cellMode === 'edit') {
// Prevents calling event.preventDefault() if Tab is pressed on a cell in edit mode
event.defaultMuiPrevented = true;
}
},
[cellMode],
);
return (
<Box component="main">
<DrawerHeader />
<Typography sx={{ textAlign: "center", pt: 5 }} variant="h5">Donees Table</Typography>
<Grid container spacing={3} sx={{ pl: 15, pr: 15, pt: 5 }}>
<div style={{ height: 400, width: '100%' }}>
<DataGrid
rows={tableData}
columns={columns}
getRowId={(row) => row._id}
onCellKeyDown={handleCellKeyDown}
cellModesModel={cellModesModel}
components={{
Toolbar: EditToolbar,
}}
componentsProps={{
toolbar: {
cellMode,
selectedCellParams,
setSelectedCellParams,
cellModesModel,
setCellModesModel,
},
cell: {
onFocus: handleCellFocus,
},
}}
experimentalFeatures={{ newEditingApi: true }}
/>
</div>
</Grid>
</Box>
);
}
const columns = [
{ field: 'fname', headerName: 'First Name', width: 140, editable: true },
{ field: 'lname', headerName: 'Last Name', width: 140, editable: true },
{ field: 'address', headerName: 'Address', width: 200, editable: true },
{ field: 'dob', headerName: 'Date of Birth', width: 140, editable: true },
{ field: 'gender', headerName: 'Gender', width: 100, editable: true },
{ field: 'email', headerName: 'Email ID', width: 140, editable: true },
{ field: 'phone', headerName: 'Mobile Number', type: 'number', width: 140, editable: true },
{ field: 'grade', headerName: 'Grade', width: 180, editable: true },
{
field: 'photo', headerName: 'Photo', width: 180, editable: true, renderCell: (params) => {
// console.log(params);
return (
<>
<Avatar src={params.value} />
</>
);
}
},
];
add following to dataGrid
<DataGrid
processRowUpdate={processRowUpdate}
/>
add
const processRowUpdate = (newRow: any) => {
const updatedRow = { ...newRow, isNew: false };
console.log(updatedRow);
//handle send data to api
return updatedRow;
};
i'm new to react and i'm trying to convert this class based component to a functionnal component but i get an error of state, how can i convert it please ?
This is my components :)
sandbox link
Thank you
In this example, it is quite straight forward as there are no component life cycle methods. You can just define all the methods in the function, and return the component.
function Demo() {
const [state, setState] = React.useState({
expandedKeys: [],
autoExpandParent: true,
checkedKeys: [],
allCheckedKeys: [],
selectedKeys: [],
newTreeView: false,
newTreeData: []
});
const onExpand = (expandedKeys) => {
console.log("onExpand", expandedKeys);
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setState({
...state,
expandedKeys,
autoExpandParent: false
});
};
const onCheck = (checkedKeys, e) => {
const allCheckedKeys = [...checkedKeys, ...e.halfCheckedKeys];
console.log("onCheck", allCheckedKeys);
console.log(createNewTreeData(treeData, allCheckedKeys));
setState((prevState) => ({
...prevState,
allCheckedKeys,
checkedKeys
}));
};
const onSelect = (selectedKeys, info) => {
console.log("onSelect", info);
setState({ ...state, selectedKeys });
};
const renderTreeNodes = (data) =>
data.map((item) => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode {...item} />;
});
const createTree = () => {
setState((prevState) => ({
...prevState,
newTreeView: true,
newTreeData: createNewTreeData(treeData, prevState.allCheckedKeys)
}));
};
return (
<>
<Tree
checkable
onExpand={onExpand}
expandedKeys={state.expandedKeys}
autoExpandParent={state.autoExpandParent}
onCheck={onCheck}
checkedKeys={state.checkedKeys}
onSelect={onSelect}
selectedKeys={state.selectedKeys}
>
{renderTreeNodes(treeData)}
</Tree>
<button onClick={createTree}>Validate</button>
{state.newTreeView && <Tree>{renderTreeNodes(state.newTreeData)}</Tree>}
</>
);
}
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Tree } from 'antd';
const treeData = [
{
title: '0-0',
key: '0-0',
children: [
{
title: '0-0-0',
key: '0-0-0',
children: [
{
title: '0-0-0-0',
key: '0-0-0-0',
},
{
title: '0-0-0-1',
key: '0-0-0-1',
},
{
title: '0-0-0-2',
key: '0-0-0-2',
},
],
},
{
title: '0-0-1',
key: '0-0-1',
children: [
{
title: '0-0-1-0',
key: '0-0-1-0',
},
{
title: '0-0-1-1',
key: '0-0-1-1',
},
{
title: '0-0-1-2',
key: '0-0-1-2',
},
],
},
{
title: '0-0-2',
key: '0-0-2',
},
],
},
{
title: '0-1',
key: '0-1',
children: [
{
title: '0-1-0-0',
key: '0-1-0-0',
},
{
title: '0-1-0-1',
key: '0-1-0-1',
},
{
title: '0-1-0-2',
key: '0-1-0-2',
},
],
},
{
title: '0-2',
key: '0-2',
},
];
const Demo = () => {
const [expandedKeys, setExpandedKeys] = useState(['0-0-0', '0-0-1']);
const [checkedKeys, setCheckedKeys] = useState(['0-0-0']);
const [selectedKeys, setSelectedKeys] = useState([]);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const onExpand = (expandedKeysValue) => {
console.log('onExpand', expandedKeysValue); // if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setExpandedKeys(expandedKeysValue);
setAutoExpandParent(false);
};
const onCheck = (checkedKeysValue) => {
console.log('onCheck', checkedKeysValue);
setCheckedKeys(checkedKeysValue);
};
const onSelect = (selectedKeysValue, info) => {
console.log('onSelect', info);
setSelectedKeys(selectedKeysValue);
};
return (
<Tree
checkable
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
onSelect={onSelect}
selectedKeys={selectedKeys}
treeData={treeData}
/>
);
};
ReactDOM.render(<Demo />, document.getElementById('container'));
I have updated the code using ES6 arrow functions resulting in shorter and simpler code than traditional functional components.
import React,{useState} from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Tree } from "antd";
const { TreeNode } = Tree;
const treeData = [
{
title: "0-0",
key: "0-0",
children: [
{
title: "0-0-0",
key: "0-0-0",
children: [
{ title: "0-0-0-0", key: "0-0-0-0" },
{ title: "0-0-0-1", key: "0-0-0-1" },
{ title: "0-0-0-2", key: "0-0-0-2" }
]
},
{
title: "0-0-1",
key: "0-0-1",
children: [
{ title: "0-0-1-0", key: "0-0-1-0" },
{ title: "0-0-1-1", key: "0-0-1-1" },
{ title: "0-0-1-2", key: "0-0-1-2" }
]
},
{
title: "0-0-2",
key: "0-0-2"
}
]
},
{
title: "0-1",
key: "0-1",
children: [
{ title: "0-1-0-0", key: "0-1-0-0" },
{ title: "0-1-0-1", key: "0-1-0-1" },
{ title: "0-1-0-2", key: "0-1-0-2" }
]
},
{
title: "0-2",
key: "0-2"
}
];
const createNewTreeData = (treeData, checkedKeys) => {
return treeData.reduce((acc, treeDataItem) => {
if (checkedKeys.includes(treeDataItem.key)) {
if (treeDataItem.children) {
acc.push({
...treeDataItem,
children: createNewTreeData(treeDataItem.children, checkedKeys)
});
} else {
acc.push(treeDataItem);
}
}
return acc;
}, []);
};
const Demo =()=> {
const [state,setState] = useState({
expandedKeys: [],
autoExpandParent: true,
checkedKeys: [],
allCheckedKeys: [],
selectedKeys: [],
newTreeView: false,
newTreeData: []
});
const onExpand = (expandedKeys) => {
console.log("onExpand", expandedKeys);
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setState({
expandedKeys,
autoExpandParent: false
});
};
const onCheck = (checkedKeys, e) => {
const allCheckedKeys = [...checkedKeys, ...e.halfCheckedKeys];
console.log("onCheck", allCheckedKeys);
console.log(createNewTreeData(treeData, allCheckedKeys));
setState((prevState) => ({
...prevState,
allCheckedKeys,
checkedKeys
}));
};
const onSelect = (selectedKeys, info) => {
console.log("onSelect", info);
setState({ selectedKeys });
};
const renderTreeNodes = (data) =>
data.map((item) => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode {...item} />;
});
const createTree = () => {
setState((prevState) => ({
...prevState,
newTreeView: true,
newTreeData: createNewTreeData(treeData, prevState.allCheckedKeys)
}));
};
return (
<>
<Tree
checkable
onExpand={onExpand}
expandedKeys={state.expandedKeys}
autoExpandParent={state.autoExpandParent}
onCheck={onCheck}
checkedKeys={state.checkedKeys}
onSelect={onSelect}
selectedKeys={state.selectedKeys}
>
{renderTreeNodes(treeData)}
</Tree>
<button onClick={createTree}>Validate</button>
{state.newTreeView && (
<Tree>{renderTreeNodes(state.newTreeData)}</Tree>
)}
</>
);
}
ReactDOM.render(<Demo />, document.getElementById("container"));
I used the code from this article as an example.
It is possible to read the data, but not to save it.
Code:
const { Component, Fragment } = wp.element;
const {
RichText,
InspectorControls,
PanelColorSettings,
AlignmentToolbar,
BlockControls,
} = wp.editor;
const { Button, PanelBody, SelectControl, TextControl } = wp.components;
const { __ } = wp.i18n;
const { registerBlockType } = wp.blocks;
const { withSelect, withDispatch } = wp.data;
class Inspector extends Component {
constructor(props) {
super(...arguments);
}
render() {
const backgroundColors = [
{ color: "#525252", name: "Черный" },
{ color: "#872d2d", name: "Акцентный красный" },
{ color: "#e49312", name: "Акцентный желтый" },
{ color: "#bab3a6", name: "Акцентный кремовый" },
];
const fontSizeOptions = [
{ value: "14px", label: __("14px") },
{ value: "16px", label: __("16px") },
{ value: "18px", label: __("18px") },
{ value: "22px", label: __("22px") },
{ value: "28px", label: __("28px") },
];
const paddingTopOptions = [
{ value: "0px", label: __("0px") },
{ value: "10px", label: __("10px") },
{ value: "25px", label: __("25px") },
{ value: "50px", label: __("50px") },
];
const paddingBottomOptions = [
{ value: "0px", label: __("0px") },
{ value: "10px", label: __("10px") },
{ value: "25px", label: __("25px") },
{ value: "50px", label: __("50px") },
];
const {
setAttributes,
attributes: { text_color, font_size, padding_top, padding_bottom },
} = this.props;
let PluginMetaFields = (props) => {
return (
<>
<TextControl
value={props.text_metafield}
label={__("Text Meta", "textdomain")}
onChange={(value) => props.onMetaFieldChange(value)}
/>
</>
);
};
PluginMetaFields = withSelect((select) => {
return {
text_metafield: select("core/editor").getEditedPostAttribute("meta")[
"_myprefix_text_metafield"
],
};
})(PluginMetaFields);
PluginMetaFields = withDispatch((dispatch) => {
return {
onMetaFieldChange: (value) => {
dispatch("core/editor").editPost({
meta: { _myprefix_text_metafield: value },
});
},
};
})(PluginMetaFields);
return (
<InspectorControls key="inspector">
<PanelBody title={__("Настройки абзаца")}>
<PanelColorSettings
title={__("Цвет шрифта")}
initialOpen={true}
colorSettings={[
{
value: text_color,
colors: backgroundColors,
onChange: (value) => setAttributes({ text_color: value }),
label: __("Цвет шрифта"),
},
]}
/>
<SelectControl
label={__("Размер шрифта")}
options={fontSizeOptions}
value={font_size}
onChange={(value) => this.props.setAttributes({ font_size: value })}
/>
<SelectControl
label={__("Отступ сверху")}
options={paddingTopOptions}
value={padding_top}
onChange={(value) =>
this.props.setAttributes({ padding_top: value })
}
/>
<SelectControl
label={__("Отступ снизу")}
options={paddingBottomOptions}
value={padding_bottom}
onChange={(value) =>
this.props.setAttributes({ padding_bottom: value })
}
/>
<PluginMetaFields />
</PanelBody>
</InspectorControls>
);
}
}
class HeadlineBlock extends Component {
render() {
const {
attributes: {
headline,
text_color,
font_size,
padding_top,
padding_bottom,
alignment,
},
setAttributes,
} = this.props;
const onChangeAlignment = (newAlignment) => {
this.props.setAttributes({
alignment: newAlignment === undefined ? "none" : newAlignment,
});
};
return [
<Inspector {...{ setAttributes, ...this.props }} />,
<div>
{
<BlockControls>
<AlignmentToolbar value={alignment} onChange={onChangeAlignment} />
</BlockControls>
}
<RichText
tagName="p"
placeholder={__("Текст...")}
keepPlaceholderOnFocus
value={headline}
formattingControls={["bold", "italic", "strikethrough", "link"]}
className={"font-" + font_size + " post-desc__p-text"}
style={{
color: text_color,
textAlign: alignment,
}}
onChange={(value) => setAttributes({ headline: value })}
/>
</div>,
];
}
}
registerBlockType("amm-custom-block/test-block", {
title: __("Тест блок"),
icon: "shield",
category: "AMM",
attributes: {
headline: {
type: "string",
},
alignment: {
type: "string",
default: "none",
},
text_color: {
type: "string",
default: "#525252",
},
font_size: {
type: "string",
default: "14px",
},
padding_top: {
type: "string",
default: "50px",
},
padding_bottom: {
type: "string",
default: "0px",
},
},
edit: HeadlineBlock,
save: function (props) {
const {
attributes: {
headline,
text_color,
font_size,
padding_top,
padding_bottom,
alignment,
},
} = props;
return (
<Fragment>
{headline && !!headline.length && (
<RichText.Content
tagName="p"
className={"font-" + font_size + " post-desc__p-text"}
style={{
color: text_color,
paddingTop: padding_top,
paddingBottom: padding_bottom,
textAlign: alignment,
}}
value={headline}
/>
)}
</Fragment>
);
},
});
So far just added a text field to the block and am trying to read and save the data.
With reading everything is OK, but saving the data does not work and there are no errors.
Any idea why this is happening?
sorry english is not a native language
The meta field _myprefix_text_metafield is a protected field as it starts with a "_" (underscore). This is why you can read the value in withSelect() but not save over it withDispatch() without passing auth_callback.
To save to a protected field, the auth_callback is required, eg:
<?php
register_post_meta( 'post', '_myprefix_text_metafield', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => function() {
return current_user_can( 'edit_posts' );
})
);
?>
There is an example in the tutorial you are following:
https://css-tricks.com/managing-wordpress-metadata-in-gutenberg-using-a-sidebar-plugin/#post-291605
Alternatively, if your meta field is not required to be protected/private: re-register your meta field as myprefix_text_metafield (no underscore in the name and no auth_callback) and your current code will work, eg:
<?php
register_post_meta( 'post', 'myprefix_text_metafield', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string'
);
?>
I have one Lock-Unlock button and a delete button, So the problem is when I render data from axios using useEffect hook its working, but if I lock or unlock a user the table is not changing automatically. That means axios is not getting called.
In that case if I put the useState hook in the useEffect, API is getting called multiple times, that is not as expected. In that case can anyone suggest me how can I re render data table as soon as user clicks on lock-unlock button.
FYI, lock unlock functionality is working 100% correctly. What is needed I have to go to other page and again come back to my datatable page in order to see the change.
code snippet:
const useStyles = makeStyles(theme => ({
button: {
margin: theme.spacing(1)
},
input: {
display: "none"
}
}));
/*
Customization of mui
*/
const getMuiTheme = () => createMuiTheme({
overrides: {
MUIDataTableBodyCell: {
root: {
backgroundColor: "#FFFFFE"
}
}
}
});
/* eslint-disable */
const UserDetailsDatatable = () => {
// console.log('POS: ' + localStorageService.getItem("auth_user"));
if(localStorageService.getItem("auth_user") == null){
history.push({
pathname: "/session/signin"
});
}
const [responsive, setResponsive] = useState("vertical");
const [dataRenderHook, setDataRenderHook] = useState([]);
const [tableBodyHeight, setTableBodyHeight] = useState("650px");
const [tableBodyMaxHeight, setTableBodyMaxHeight] = useState("");
const classes = useStyles();
// Redirect to edit
const newMountpage = (rowData) => {
var pickedUpRowData = rowData.rowData;
let path = `/Admin/users/editUser`;
history.push({
pathname: path,
state: {detail: pickedUpRowData}
});
};
/* Deletion */
const deleteRow = (rowDatam) => {
deleteUser(rowDatam);
};
/* User Locker/Unlocker */
const LockerUnlocker = (Iopticore_ID, state) => {
console.log('State: '+ state);
lockerUnlocker(Iopticore_ID, Boolean(state));
}
const ChangeHandler = (event) => {
console.log(event);
};
const columns = [
{
name: "userID",
label: "Iopticore_ID",
options: {
filter: true,
sort: true,
}
},
{
name: "userName",
label: "Corporate ID",
options: {
filter: true,
sort: true,
}
},
{
name: "name",
label: "User Name",
options: {
filter: true,
sort: true,
}
},
{
name: "email",
label: "Email",
options: {
filter: true,
sort: true,
}
},
{
name: "role",
label: "Role",
options: {
filter: true,
sort: true,
}
},
{
name: "external",
label: "IsExternal",
options: {
filter: true,
sort: true,
filterOptions: {
names: ["Yes", "No"],
logic(v, filterVal) {
const show =
(filterVal.indexOf("Yes") >= 0 && ( v === true || v === 'true') ) ||
(filterVal.indexOf("No") >= 0 && ( v === false || v === 'false') );
return !show;
}
},
customBodyRender: (val) => {
return val === true ? "Yes" : "No";
}
}
},
{
name: "locked",
label: "Access",
options: {
filter: true,
sort: true,
empty: true,
filter: true,
sort: true,
filterOptions: {
names: ["Yes", "No"],
logic(v, filterVal) {
const show =
(filterVal.indexOf("Yes") >= 0 && ( v === true || v === 'true') ) ||
(filterVal.indexOf("No") >= 0 && ( v === false || v === 'false') );
return !show;
}
},
customBodyRender: (val, tableMeta) => {
//console.log('v: ' + val + ' ' + JSON.stringify(tableMeta)) ;
return val === true ? (
<Fab
size="small"
variant="extended"
aria-label="Delete"
className={classes.button}
color="default"
onClick={() => LockerUnlocker(tableMeta.rowData[0], tableMeta.rowData[6])}
>
<img src="https://img.icons8.com/plasticine/25/000000/unlock.png"/>
<b>UnLock </b>
</Fab>
) : (
<Fab
size="small"
variant="extended"
aria-label="Delete"
className={classes.button}
color="default"
onClick={() => LockerUnlocker(tableMeta.rowData[0])}
>
<img src="https://img.icons8.com/dusk/25/000000/unlock.png"/>
<b> Lock </b>
</Fab>
);
}
}
},
{
name: "Edit",
options: {
filter: true,
sort: false,
empty: true,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Fab
size="small"
color="primary"
aria-label="Edit"
className={classes.button}
onClick={() => newMountpage(tableMeta)}
>
<Icon>edit_icon</Icon>
</Fab>
);
}
}
},
{
name: "Delete",
options: {
filter: true,
textAlign: 'center',
sort: false,
empty: true,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Fab size="small"
color="secondary"
aria-label="Edit"
className={classes.button}
onClick={() => deleteRow(tableMeta.rowData[0])}
>
<Icon>delete_icon</Icon>
</Fab>
);
}
}
}];
const options = {
selectableRows: 'none',
filter: true,
textAlign: 'center',
filterType: "dropdown",
rowsPerPage: 5,
pagination: true,
responsive,
enableNestedDataAccess: '.',
tableBodyHeight,
tableBodyMaxHeight
};
var recentReceivedToken = localStorage.getItem('jwtAuthtokenManager');
var res= [];
useEffect(() => {
(async () => {
res = await axios.get('<URL>', {
headers: {"Authorization" : `Bearer ${recentReceivedToken}`}
},
// console.log('Hola : ' + JSON.stringify(res))
)
.catch((error)=> {
if(error.response.status != 200){
swal({
title: "Opps! Access Denied",
imageUrl: 'https://notionpress.com/new-rewamp/images/404-error.gif',
text: "You Might Not Have Access To This Page.",
icon: "error",
});
}
})
//console.log('Res : ' + JSON.stringify(res));
setDataRenderHook(res.data);
})();
}, []);
//console.log('Data Promise : ' + renderDatatable());
if(dataRenderHook.length === 0){
//console.log('Length: 0');
return(
<React.Fragment>
<div style={{padding:40}}>
<MuiThemeProvider theme={getMuiTheme()}>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<LinearBuffer />
</div>
</MuiThemeProvider>
</div>
</React.Fragment>
)
}else{
//console.log('Length: 1');
return (
<React.Fragment>
<div style={{padding:40}}>
<MuiThemeProvider theme={getMuiTheme()}>
<MUIDataTable
title={"IoptiCore User List"}
data={dataRenderHook}
columns={columns}
options={options}
/>
</MuiThemeProvider>
</div>
</React.Fragment>
);
}
}
export default UserDetailsDatatable;
import React, { useState, useEffect } from "react";
import MUIDataTable from "mui-datatables";
import { makeStyles,createMuiTheme, MuiThemeProvider } from "#material-ui/core/styles";
import { Icon, Fab } from "#material-ui/core";
import history from "history.js";
import axios from "axios";
import swal from 'sweetalert';
import LinearBuffer from './EditUserHelper/progresscircle';
import lockerUnlocker from './Lock_Unlock/lockerUnlocker';
import deleteUser from './DeleteUser/DeleteUser'
import localStorageService from "../../../services/localStorageService";
import apiDataReturner from "./userAxios";
const useStyles = makeStyles(theme => ({
button: {
margin: theme.spacing(1)
},
input: {
display: "none"
}
}));
/*
Customization of mui
*/
const getMuiTheme = () => createMuiTheme({
overrides: {
MUIDataTableBodyCell: {
root: {
backgroundColor: "#FFFFFE"
}
}
}
});
/* eslint-disable */
const UserDetailsDatatable = () => {
// console.log('POS: ' + localStorageService.getItem("auth_user"));
if(localStorageService.getItem("auth_user") == null){
history.push({
pathname: "/session/signin"
});
}
const [responsive, setResponsive] = useState("vertical");
const [dataRenderHook, setDataRenderHook] = useState([]);
const [tableBodyHeight, setTableBodyHeight] = useState("650px");
const [tableBodyMaxHeight, setTableBodyMaxHeight] = useState("");
const [tracker, setTracker] = useState();
const classes = useStyles();
// Redirect to edit
const newMountpage = (rowData) => {
var pickedUpRowData = rowData.rowData;
let path = `/Admin/users/editUser`;
history.push({
pathname: path,
state: {detail: pickedUpRowData}
});
};
/* Deletion */
const deleteRow = (rowDatam) => {
deleteUser(rowDatam);
var x = apiDataReturner();
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
setTracker(x);
}, 3000);
});
promise.then(values => {
setDataRenderHook(values);
});
};
/* User Locker/Unlocker */
const LockerUnlocker = (Iopticore_ID, state) => {
lockerUnlocker(Iopticore_ID, Boolean(state));
var x = apiDataReturner();
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
setTracker(x);
}, 3000);
});
promise.then(values => {
setDataRenderHook(values);
});
}
const ChangeHandler = (event) => {
console.log(event);
};
const columns = [
{
name: "userID",
label: "Iopticore_ID",
options: {
filter: true,
sort: true,
responsive: 'scrollFullHeightFullWidth',
display: false
}
},
{
name: "userName",
label: "Corporate ID",
options: {
filter: true,
responsive: 'scrollFullHeightFullWidth',
sort: true,
}
},
{
name: "name",
label: "User Name",
options: {
filter: true,
responsive: 'scrollFullHeightFullWidth',
sort: true,
}
},
{
name: "email",
label: "Email",
options: {
filter: true,
responsive: 'scrollFullHeightFullWidth',
sort: true,
}
},
{
name: "role",
label: "Role",
options: {
filter: true,
responsive: 'scrollFullHeightFullWidth',
sort: true,
}
},
{
name: "external",
label: "IsExternal",
options: {
filter: true,
sort: true,
responsive: 'scrollFullHeightFullWidth',
filterOptions: {
names: ["Yes", "No"],
logic(v, filterVal) {
const show =
(filterVal.indexOf("Yes") >= 0 && ( v === true || v === 'true') ) ||
(filterVal.indexOf("No") >= 0 && ( v === false || v === 'false') );
return !show;
}
},
customBodyRender: (val) => {
return val === true ? "Yes" : "No";
}
}
},
{
name: "locked",
label: "Access",
options: {
filter: true,
sort: true,
empty: true,
filter: true,
sort: true,
responsive: 'scrollFullHeightFullWidth',
filterOptions: {
names: ["Yes", "No"],
logic(v, filterVal) {
const show =
(filterVal.indexOf("Yes") >= 0 && ( v === true || v === 'true') ) ||
(filterVal.indexOf("No") >= 0 && ( v === false || v === 'false') );
return !show;
}
},
customBodyRender: (val, tableMeta) => {
//console.log('v: ' + val + ' ' + JSON.stringify(tableMeta)) ;
return val === true ? (
<Fab
size="small"
variant="extended"
aria-label="Delete"
className={classes.button}
color="default"
onClick={() => LockerUnlocker(tableMeta.rowData[0], tableMeta.rowData[6])}
>
<img src="https://img.icons8.com/plasticine/25/000000/unlock.png"/>
<b>UnLock </b>
</Fab>
) : (
<Fab
size="small"
variant="extended"
aria-label="Delete"
className={classes.button}
color="default"
onClick={() => LockerUnlocker(tableMeta.rowData[0])}
>
<img src="https://img.icons8.com/dusk/25/000000/unlock.png"/>
<b> Lock </b>
</Fab>
);
}
}
},
{
name: "Edit",
options: {
filter: true,
sort: false,
empty: true,
responsive: 'scrollFullHeightFullWidth',
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Fab
size="small"
color="primary"
aria-label="Edit"
className={classes.button}
onClick={() => newMountpage(tableMeta)}
>
<Icon>edit_icon</Icon>
</Fab>
);
}
}
},
{
name: "Delete",
options: {
filter: true,
textAlign: 'center',
sort: false,
empty: true,
responsive: 'scrollMaxWidth',
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Fab size="small"
color="secondary"
aria-label="Edit"
className={classes.button}
onClick={() => deleteRow(tableMeta.rowData[0])}
>
<Icon>delete_icon</Icon>
</Fab>
);
}
}
}];
const options = {
selectableRows: 'none',
filter: true,
textAlign: 'center',
filterType: "dropdown",
fixedHeaderOptions:true,
rowsPerPage: 5,
pagination: true,
responsive: 'stacked',
enableNestedDataAccess: '.',
tableBodyHeight,
tableBodyMaxHeight
};
//var recentReceivedToken = localStorage.getItem('jwtAuthtokenManager');
var res= [];
useEffect(() => {
var x = apiDataReturner();
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(x);
}, 2000);
});
promise.then(values => {
setDataRenderHook(values);
});
}, [tracker]);
if(dataRenderHook.length === 0){
return(
<React.Fragment>
<div style={{padding:0}}>
<MuiThemeProvider theme={getMuiTheme()}>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<LinearBuffer />
</div>
</MuiThemeProvider>
</div>
</React.Fragment>
)
}else{
return (
<div>
<React.Fragment>
<div style={{padding:0}}>
<MuiThemeProvider theme={getMuiTheme()}>
<MUIDataTable
title={"IoptiCore User List"}
data={dataRenderHook}
columns={columns}
options={options}
/>
</MuiThemeProvider>
</div>
</React.Fragment>
</div>
);
}
}
export default UserDetailsDatatable;
Sharing the code, it might help someone.
I am working with antd table and antd transfer component and I am facing a small challenge with CSS.
I have created a small example with codesandbox. If I try to expand a row, you will see that other columns try to adjust themselves. Is there a way I could prevent this? I do not want the rows to adjust themselves. The table should feel the same after the expansion as it was before the expansion.
This is code from the sandbox link I shared above that generates the table.
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Transfer, Table, Tag } from "antd";
function difference(listOne, listTwo) {
const set1 = new Set(listOne);
const set2 = new Set(listTwo);
const difference = new Set([...set1].filter(x => !set2.has(x)));
return Array.from(difference);
}
// Customize Table Transfer
const TableTransfer = ({ leftColumns, rightColumns, ...restProps }) => (
<Transfer {...restProps}>
{({
direction,
filteredItems,
onItemSelectAll,
onItemSelect,
selectedKeys: listSelectedKeys,
disabled: listDisabled
}) => {
const columns = direction === "left" ? leftColumns : rightColumns;
const rowSelection = {
getCheckboxProps: item => ({ disabled: listDisabled || item.disabled }),
onSelectAll(selected, selectedRows) {
const treeSelectedKeys = selectedRows
.filter(item => !item.disabled)
.map(({ key }) => key);
const diffKeys = selected
? difference(treeSelectedKeys, listSelectedKeys)
: difference(listSelectedKeys, treeSelectedKeys);
onItemSelectAll(diffKeys, selected);
},
onSelect({ key }, selected) {
onItemSelect(key, selected);
},
selectedRowKeys: listSelectedKeys
};
return (
<Table
rowSelection={rowSelection}
columns={columns}
dataSource={filteredItems}
size="small"
/>
);
}}
</Transfer>
);
const mockTags = ["eg", "gg", "e"];
const mockData = [];
for (let i = 0; i < 20; i++) {
let data = {
key: i.toString(),
title: `eq${i + 1}`,
description: `description of eq${i + 1}`,
disabled: false, //i % 4 === 0,
tag: mockTags[i % 3]
};
if (i % 2 === 0) {
const children = [
{
key: i.toString() + "children",
title: `children-${i + 1}`,
description: `children description-${i + 1}`,
disabled: true,
tag: "tag"
}
];
data["children"] = children;
}
mockData.push(data);
}
const originTargetKeys = mockData
.filter(item => +item.key % 3 > 1)
.map(item => item.key);
const leftTableColumns = [
{
dataIndex: "title",
title: "Name"
},
{
dataIndex: "tag",
title: "Tag",
render: tag => <Tag>{tag}</Tag>
},
{
dataIndex: "description",
title: "Description"
}
];
const rightTableColumns = [
{
dataIndex: "title",
title: "Names"
},
{
dataIndex: "tag",
title: "Tag",
render: tag => <Tag>{tag}</Tag>
},
{
dataIndex: "description",
title: "Description"
}
];
class App extends React.Component {
state = {
targetKeys: originTargetKeys
};
onChange = nextTargetKeys => {
this.setState({ targetKeys: nextTargetKeys });
};
render() {
const { targetKeys, disabled } = this.state;
return (
<div>
<TableTransfer
className="table-transfer"
dataSource={mockData}
titles={[
<div>
<input type="checkbox" checked />
Equipment <input type="checkbox" checked /> Groups
</div>,
<div>
<input type="checkbox" checked />
Equipment <input type="checkbox" checked /> Groups
</div>
]}
targetKeys={targetKeys}
disabled={disabled}
showSearch={true}
onChange={this.onChange}
filterOption={(inputValue, item) =>
item.title.indexOf(inputValue) !== -1 ||
item.tag.indexOf(inputValue) !== -1
}
leftColumns={leftTableColumns}
rightColumns={rightTableColumns}
locale={{
itemUnit: "Equipment",
itemsUnit: "Equipments",
notFoundContent: "The list is empty",
searchPlaceholder: "Search here"
}}
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
You want to constraint your columns width, for example, try this on the leftTableColumns and notice the difference with the right one:
const leftTableColumns = [
{
dataIndex: 'title',
title: 'Name',
width: '45%'
},
{
dataIndex: 'tag',
title: 'Tag',
render: tag => <Tag>{tag}</Tag>,
width: '10%'
},
{
dataIndex: 'description',
title: 'Description',
width: '40%'
}
];
Refer to Table Column API.
Fork of your codebox: