How to prevent rows adjustment after they are expanded? - javascript

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:

Related

How to add condition in React JS dynamic table?

I have a react js dynamic table as given below: now I want to add condition in this dynamic table. but condition only working with values all the header still displaying.
I need to display email and mobile fields conditional.
My Code:
const App = (props) => {
const [condition, setCondition] = useState(true);
var colums = [{
Header: "Name",
accessor: "name",
},
{
width: 150,
Header: "Email",
accessor: "email",
Cell: (props) => {
return ( <
>
{
condition ? < div > {
props.row.values.email
} < /div> : ""}</ >
);
},
},
{
width: 150,
Header: "Phone",
accessor: "phone",
Cell: (props) => {
return ( <
>
{
condition ? < div > {
props.row.values.phone
} < /div> : ""}</ >
);
},
},
]
return ( <>
<BasicTable
columns ={colums}
tableType = "default">
</BasicTable>
export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Thanks for your efforts!
You would have to set your condition at the array level, something like this should work:
Edit: Avoid having empty objects.
var colums = [
{
Header: "Name",
accessor: "name",
},
(condition && {
width: 150,
Header: "Email",
accessor: "email",
Cell: (props) => {
return (
<>{<div>{props.row.values.email}</div>}</>
);
},
}),
(condition && {
width: 150,
Header: "Phone",
accessor: "phone",
Cell: (props) => {
return (
<>{<div>{props.row.values.phone}</div>}</>
);
}})
].filter(item => item)
keep columns array 'full' and then just filter items depending on condition. In separate function or just in callback.

How to increment a cell in AG Grid React tabel?

Pretty new with AG Grid library and stuck on updating the cells.
In reality, I need to do the task with dates, but just to keep it simple, I'll explain my problem with simple numbers.
I have
Start, which is 1.
Stop, which is already defined
End
Duration, which is already defined
My goal
Start = Duration + End
End = Start + Stop
Here is what I have so far
Desired result
Sandbox Link and code below
import React from "react";
import { AgGridReact } from "ag-grid-react";
import "./styles.css";
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";
function App() {
const start = 1;
const [gridApi, setGridApi] = React.useState(null);
const [gridColumnApi, setGridColumnApi] = React.useState(null);
const columnDefs = [
{
headerName: "Name",
field: "name"
//valueGetter: (params) => console.log(params)
},
{
headerName: "start",
field: "start",
valueGetter: (params) => {
if (params.node.rowIndex !== 0) {
return params.getValue("end") + params.data.duration;
} else {
return start;
}
}
},
{ headerName: "stop", field: "stop" },
{
headerName: "end",
colId: "end",
valueGetter: (params) => {
return params.data.start + params.data.stop;
}
},
{
headerName: "duration",
field: "duration",
colId: "duration"
}
];
const rowData = React.useMemo(
() => [
{
name: "John",
stop: 10
},
{
name: "David",
stop: 15
},
{
name: "Dan",
stop: 20
}
],
[]
);
const durationArray = [5, 8, 6];
const rowDataWithStart = React.useMemo(() => {
if (durationArray) {
return (
rowData &&
rowData.map((row, i) => ({
...row,
start: start,
duration: durationArray[i]
}))
);
}
}, [start, rowData, durationArray]);
const defaultColDef = {
flex: 1,
editable: true
};
const onGridReady = (params) => {
setGridApi(params.api);
setGridColumnApi(params.columnApi);
};
return (
<div className="App">
<h1 align="center">React-App</h1>
<div className="ag-theme-alpine">
<AgGridReact
columnDefs={columnDefs}
rowData={rowDataWithStart}
defaultColDef={defaultColDef}
domLayout={"autoHeight"}
onGridReady={onGridReady}
></AgGridReact>
</div>
</div>
);
}
export default App;
Any help will be appreciated
you can use valueGetter for this. I have modified the valueGetter in you code
const columnDefs = [
{
headerName: "Name",
field: "name"
//valueGetter: (params) => console.log(params)
},
{
field: "start",
valueGetter: ({ data }) => {
const end = data.stop + data.start
return data.duration + end;
}
},
{ field: "stop" },
{
field: "end",
valueGetter: ({ data }) => {
return data.start + data.stop
}
},
{
headerName: "duration",
field: "duration"
}
];

When a prop is changing const is backing to its initial values

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

Put Link into json using hook (React)

i have a question, i want to add this command line:
Cell: ({ row }) => <a href={row.original.lastName}>{row.original.lastName}</a>
into 'lastName',using hook in reactjs, for example:
Before:
const [columns, setColumns] = useState(
[
{
id:'firstName',
Header: 'First Name',
accessor: 'firstName',
},
{
Header: 'Last Name',
accessor: 'lastName',
},
]
);
setColumns(???)
Result:
[
{
id:'firstName',
Header: 'First Name',
accessor: 'firstName',
},
{
Header: 'Last Name',
accessor: 'lastName',
Cell: ({ row }) => <a href={row.original.lastName}>{row.original.lastName}</a>
},
]
Best regards
Giuseppe
Please find a small working solution as below. Please feel free to replace the button click with your side effect which can be an API call or any action side effect. Just for simplicity, I have used a button.
import React, { useState } from "react";
import ReactDOM from "react-dom";
const Row = ({ row }) => (
<a href={row.original.lastName}>{row.original.lastName}</a>
);
function App() {
const [columns, setColumns] = useState([
{
id: "firstName",
Header: "First Name",
accessor: "firstName"
},
{
Header: "Last Name",
accessor: "lastName"
}
]);
const createRowsForLastName = () => {
const replaceColumnForLastName = columns.map(column => {
if (column.accessor === "lastName") {
return {
...column,
Cell: <Row row={{ original: { lastName: "some name" } }} />
};
}
return { ...column };
});
setColumns(replaceColumnForLastName);
};
console.log(columns);
return (
<div className="App">
<button onClick={createRowsForLastName}>Create Row!</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

select input field always return undefined

when ever i try to select an option in input field it must set the state value to the selected option but it return undefined
I am using Semantic ui react Form to take input but when ever i select the option and submit it gives me undefined
import React from 'react'
import { Form, Input, TextArea, Button, Select, Container } from
'semantic-ui-react'
const RankOptions = [
{ key: 'lg', text: 'Lt-Gen', value: 'Lt-Gen' },
{ key: 'mg', text: 'Mj-Gen', value: 'Mj-Gen' },
{ key: 'b', text: 'Brig', value: 'Brig' },
{ key: 'col', text: 'Col', value: 'Col' },
{ key: 'lc', text: 'Lt-Col', value: 'Lt-Col' },
{ key: 'm', text: 'Major', value: 'Mj' },
{ key: 'capt', text: 'Capt', value: 'Capt' },
{ key: 'lt', text: 'Lt', value: 'Lt' },
{ key: '2lt', text: '2-Lt', value: 'Lt-2' },
]
export default class Employee extends React.Component{
state={}
handleSubmit = () => {
console.log(this.state)
}
handlerankChange = (e) => {
const value = e.target.value
this.setState({
rank : value
})
}
render() {
return (
<Container>
<Form size='huge'>
<Form.Group widths='equal'>
<Form.Field
name = 'rank'
control = {Select}
label = 'Rank'
options = {RankOptions}
placeholder = 'Rank'
value = {this.state.value}
onChange = {this.handlerankChange}
/>
<Button primary onClick=
{this.handleSubmit}>Submit</Button>
</Form>
</Container>
)
}
}
the state must be any option from ranks
A working code will help you out:
import React from "react";
import { render } from "react-dom";
import Hello from "./Hello";
import {
Button,
Form,
Grid,
Header,
Image,
Message,
Segment,
Label,
Dropdown
} from "semantic-ui-react";
import Select from "react-select";
import "./index.css";
const styles = {
fontFamily: "sans-serif",
textAlign: "center"
};
class App extends React.Component {
state = {
selectedOption: ""
};
handleChange = selectedOption => {
this.setState({ selectedOption });
};
render() {
const { selectedOption } = this.state;
const value = selectedOption && selectedOption.value;
return (
<div className="login-form">
<Grid
textAlign="center"
style={{ height: "100%" }}
verticalAlign="middle"
>
<Grid.Column style={{ maxWidth: 450 }} textAlign="left">
<Form size="large">
<Segment>
<div>
<Select
name="form-field-name"
value={value}
onChange={this.handleChange}
options={[
{ value: "one", label: "One" },
{ value: "two", label: "Two" }
]}
/>
</div>
</Segment>
</Form>
</Grid.Column>
</Grid>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Source of the code on the codesandbox
Set initial value for rank in state as
state = {
rank:''
}
and change
<Form.Field
name = 'rank'
control = {Select}
label = 'Rank'
options = {RankOptions}
placeholder = 'Rank'
value = {this.state.rank}
onChange = {this.handlerankChange}
/>
you need not access the value by e.target.value, the callback provides object with key 'value';
eg:
import React from 'react'
import { Form, Input, TextArea, Button, Select, Container } from
'semantic-ui-react'
const RankOptions = [
{ key: 'lg', text: 'Lt-Gen', value: 'Lt-Gen' },
{ key: 'mg', text: 'Mj-Gen', value: 'Mj-Gen' },
{ key: 'b', text: 'Brig', value: 'Brig' },
{ key: 'col', text: 'Col', value: 'Col' },
{ key: 'lc', text: 'Lt-Col', value: 'Lt-Col' },
{ key: 'm', text: 'Major', value: 'Mj' },
{ key: 'capt', text: 'Capt', value: 'Capt' },
{ key: 'lt', text: 'Lt', value: 'Lt' },
{ key: '2lt', text: '2-Lt', value: 'Lt-2' },
]
export default class Employee extends React.Component{
state={}
handleSubmit = () => {
console.log(this.state)
}
handlerankChange = ({ value }) => {
this.setState({
rank : value
})
}
render() {
return (
<Container>
<Form size='huge'>
<Form.Group widths='equal'>
<Form.Field
name = 'rank'
control = {Select}
label = 'Rank'
options = {RankOptions}
placeholder = 'Rank'
value = {this.state.rank} // this should be rank
onChange = {this.handlerankChange}
/>
</Form.Group>.
<Button primary onClick=
{this.handleSubmit}>Submit</Button>
</Form>
</Container>
)
}
}
Hope this helps!!

Categories