Storybook fetch http addons knobs - javascript

The result I would like to obtain in the first image, an input field where a url is put from which the data must be taken.
In the second image you see the problem I am getting when I try to get the data from http.
The module works if I don't use it within the storybook.
So I don't understand where the problem is.
Can anyone help me out?
import React, { useState, useEffect } from 'react';
import { storiesOf } from '#storybook/react-native';
import { withKnobs, object, text } from '#storybook/addon-knobs';
import { Chip, Text } from 'react-native-paper';
import Datatable from './DatatableFetch';
const columns = [
{
name: 'Name',
selector: 'name',
sortable: true,
},
{
name: 'Trips',
selector: 'trips',
sortable: true,
cell: ({ trips }) => <Chip mode="outlined">{trips}</Chip>,
right: true,
},
];
const App = ({url}) => {
const [state, setState] = useState({
data3: [],
page: 1,
perPage: 5,
totalRows: 1,
});
const { data3, page, totalRows, perPage } = state;
const child = (obj, str) => {
const props = str.split('.');
let child = obj;
props.forEach((prop) => (child = child[prop]));
return child;
};
const load = (sort, sortField = '', sortAsc = false) =>
fetch(
url +
(page - 1) +
'&size=' +
perPage
)
.then((response) => response.json())
.then(({ data, totalPassengers }) => {
var copyData = Object.assign([], data);
var result = sort
? copyData
: copyData.sort((a, b) => {
let [x, z] = sortAsc ? [a, b] : [b, a];
return child(x, sortField) > child(z, sortField) ? 1 : -1;
});
setState((prev) => ({
...prev,
data3: result,
totalRows: totalPassengers,
}));
})
.catch((error) => {
console.error(error);
});
useEffect(() => {
load(false);
}, [page]);
return (
<Datatable
columns={object('columns', columns, 'Columns')}
data={object('data', data3, 'Data')}
//data={data3}
striped
stripedColors={['#2196f3', '#ccc']}
viewLoader
defaultSortField={'name'}
defaultSortAsc={true}
paginationServer
paginationTotalRows={totalRows}
onChangePage={(_inPage) =>
setState((prev) => ({
...prev,
page: _inPage,
}))
}
onSort={(selector, sortDirection) =>
load(true, selector, sortDirection === 'asc')
}
paginationComponentOptions={(currentPage, totalPage) =>
`${currentPage} di ${totalPage}`
}
style={{
backgroundColor: "#ccc",
borderRadius: 4,
}}
/>
);
};
storiesOf('DatatableFetch', module)
.addDecorator(withKnobs)
.add('example', () => {
return <App url={text("Url", "https://api.instantwebtools.net/v1/passenger?page=", "Url")} />;
});
DatatableFetch.js
import React, { useState, useEffect, useCallback } from 'react';
import { View, Animated } from 'react-native';
import { DataTable, ProgressBar, Colors, Text } from 'react-native-paper';
function Datatable({
columns,
data = [],
noTableHead = false,
striped = false,
stripedColors = ['#2196f3','#8bc34a'],
viewLoader = false,
defaultSortField,
defaultSortAsc,
page: _page = 1,
perPage: _perPage = 5,
pagination = false,
paginationComponentOptions,
noDataComponent = <Text>There are no records to display.</Text>,
style,
paginationServer = false,
paginationTotalRows = 0,
onChangePage,
onSort
}) {
const [state, setState] = useState({
datatable: data,
page: _page - 1,
perPage: _perPage,
numberOfPages: Math.ceil( (paginationServer ? paginationTotalRows : data.length) / _perPage),
sortField: defaultSortField,
sortAsc: defaultSortAsc,
progressBar: false,
loading: false,
});
const {
datatable,
page,
perPage,
numberOfPages,
sortField,
sortAsc,
progressBar,
loading,
} = state;
/*useEffect(() => {
setState((prev) => ({
...prev,
datatable: data,
numberOfPages: Math.ceil( (paginationServer ? paginationTotalRows : data.length) / _perPage)
}));
}, [data]);*/
const dataPagination = !paginationServer && pagination
? datatable.slice(perPage * page, perPage * (page + 1))
: datatable;
const getValue = (object, { selector, cell }) => {
if (cell) return cell(object);
return selector
.replace(/\[/g, '.')
.replace(/\]/g, '')
.split('.')
.reduce((o, k) => (o || {})[k], object);
};
useEffect(() => {
console.log('a')
setState((prev) => ({
...prev,
progressBar: true,
}));
sort(sortField, sortAsc);
}, [data]);
const child = (obj, str) => {
const props = str.split('.');
let child = obj;
props.forEach((prop) => (child = child[prop]));
return child;
};
const sort = async (sortField, sortAsc) => {
var copyData = Object.assign([], data);
var result = paginationServer ? copyData : copyData.sort((a, b) => {
let [x, z] = sortAsc ? [a, b] : [b, a];
return child(x, sortField) > child(z, sortField) ? 1 : -1;
});
setTimeout(() => {
setState((prev) => ({
...prev,
datatable: result,
numberOfPages: Math.ceil( (paginationServer ? paginationTotalRows : result.length) / _perPage),
progressBar: false,
loading: true,
}));
}, 2000);
};
return (
<DataTable style={style}>
{!noTableHead && (
<DataTable.Header>
{columns.map((item, i) => {
const sortDirection =
datatable.length > 0 &&
!progressBar &&
sortField === item.selector
? !sortAsc
? 'ascending'
: 'descending'
: null;
return (
!item.omit && (
<DataTable.Title
key={i}
onPress={() => {
if (item.sortable) {
const sortAscC =
sortField === item.selector ? !sortAsc : true;
setState((prev) => ({
...prev,
sortField: item.selector,
sortAsc: sortAscC,
progressBar: true,
}));
if(paginationServer) onSort(item.selector, sortAscC ? 'asc' : 'desc')
else sort(item.selector, sortAscC);
}
}}
sortDirection={sortDirection}
numeric={item.right}
style={item.styleHeader}>
{item.name} {false && sortDirection}
</DataTable.Title>
)
);
})}
</DataTable.Header>
)}
{viewLoader && progressBar && (
<ProgressBar indeterminate color={Colors.blue900} />
)}
{!progressBar && datatable.length === 0 && (
<View
style={{
margin: 10,
justifyContent: 'center',
alignItems: 'center',
}}>
{noDataComponent}
</View>
)}
{loading &&
dataPagination.map((item, i) => {
return (
<DataTable.Row
key={i}
style={{
opacity: progressBar ? 0.2 : 1,
backgroundColor: striped
? i % 2
? stripedColors[0]
: stripedColors[1]
: null,
}}>
{columns.map((headerItem, j) => {
return (
!headerItem.omit && (
<DataTable.Cell
key={j}
numeric={headerItem.right}
style={headerItem.styleRow}>
{getValue(item, headerItem)}
</DataTable.Cell>
)
);
})}
</DataTable.Row>
);
})}
{(pagination || paginationServer) && (
<DataTable.Pagination
page={page}
numberOfPages={numberOfPages}
onPageChange={(page) => {
onChangePage && onChangePage(page+1)
if(true){
setState((prev) => ({
...prev,
progressBar: true,
}));
setTimeout(() => {
setState((prev) => ({
...prev,
page: page,
progressBar: false,
}));
}, 2000);
}
}}
label={paginationComponentOptions ? paginationComponentOptions(page + 1, numberOfPages) : `${page + 1}-${numberOfPages} of ${datatable.length}`}
/>
)}
</DataTable>
);
}
export default Datatable;

Related

Adding sortInfo and/or filterValue to DataGrid breaks sorting/filtering functionality

I am trying to add some 'save preferences' functionality to a DataGrid using https://reactdatagrid.io/docs/miscellaneous but when I add sortInfo makes columns unsortable, the same happens for filterValue (trying to save filtering strings/data). Code here:
import React, { useCallback, useState } from 'react';
import DataGrid from '#inovua/reactdatagrid-enterprise';
import { columnFilters, eeOverviewColumns, filterTypes } from "./overview-columns";
import '#inovua/reactdatagrid-enterprise/index.css';
import { TypeRowProps, TypeRowSelection } from '#inovua/reactdatagrid-community/types';
import { TypeOnSelectionChangeArg } from "#inovua/reactdatagrid-community/types/TypeDataGridProps"
import { Button, FormControl, MenuItem, Select, SelectChangeEvent, TextField } from '#mui/material';
import { TypeColumn, TypeFilterValue, TypeSortInfo } from '#inovua/reactdatagrid-enterprise/types';
interface StoreLayout {
columns: TypeColumn[];
sortInfo: TypeSortInfo;
columnOrder : string[];
filterValue: TypeFilterValue;
}
let STORE: StoreLayout = {
columns: eeOverviewColumns,
sortInfo: [],
columnOrder: eeOverviewColumns.map(ee => ee.name) as string[],
filterValue: columnFilters,
}
let VIEWS= [
{
id: 0,
name: "Default view",
state: STORE
}
]
const EEOverview = (props: any) => {
const dataSource = props.eeData
// Checkbox selection
const [selected, setSelected] = useState<TypeRowSelection>();
const onSelectionChange = useCallback(
(config: TypeOnSelectionChangeArg) => {
setSelected(config.selected)
},
[],
);
const goToEe = useCallback((rowProps: TypeRowProps) => {
window.location.href = `${window.location.href}/${rowProps.data.key}`;
}, [])
const initialState = Object.assign({}, STORE, {});
const [state, setState] = useState(initialState);
const [viewName, setViewName] = useState('')
const sendViewName = (viewName: string) => { setViewName(viewName) }
const saveState = () => {
if (!viewName || viewName.length === 0 ) {
alert("View name not provided")
return
}
STORE = {
columnOrder: state.columnOrder,
columns: state.columns,
sortInfo: state.sortInfo,
filterValue: state.filterValue
}
setState(Object.assign({}, state, {}))
if(VIEWS.map(view => view.name).some(name => name === viewName)) {
const view = VIEWS.find(view => view.name === viewName)!
view.state = state
} else {
VIEWS.push({
id: VIEWS.length,
name: viewName,
state: state
})
}
}
const onSortInfoChange = (sortInfo: TypeSortInfo) => {
setState(Object.assign({}, state, { sortInfo }));
}
const onColumnOrderChange = (columnOrder: string[]) => {
setState(Object.assign({}, state, { columnOrder }));
}
const onFilterValueChange = (filterValue: TypeFilterValue) => {
setState(Object.assign({}, state, { filterValue }));
}
const onBatchColumnResize = (batchColumnInfo: any, { reservedViewportWidth }: any) => {
const colsMap = batchColumnInfo.reduce((acc: any, colInfo: any) => {
const { column, width, flex } = colInfo
acc[column.name] = { width, flex }
return acc
}, {})
const columns = state.columns.map((c: any) => {
return Object.assign({}, c, colsMap[c.name])
})
setState(Object.assign({}, state, {
columns,
reservedViewportWidth
}))
}
return (
<div>
<ViewSelector state = {state} onChange = {setState} ></ViewSelector>
<ViewText onChange = {sendViewName} ></ViewText>
<Button sx={{ mx: 2, my: 2, minWidth: 80 }} variant="contained"
onClick = {saveState}
>
Save view
</Button>
<DataGrid
idProperty="key"
theme="default-light"
className="data-grid"
defaultFilterValue={columnFilters}
filterTypes={filterTypes}
filterValue={state.filterValue} //<- here
onRowClick={goToEe}
columns={state.columns}
sortInfo={state.sortInfo} //<- and here
columnOrder={state.columnOrder}
pagination="local"
dataSource={dataSource}
onSelectionChange={onSelectionChange}
sortable={true}
checkboxColumn
selected={selected}
enableSelection={true}
onSortInfoChange={onSortInfoChange}
onBatchColumnResize={onBatchColumnResize}
onColumnOrderChange={onColumnOrderChange}
onFilterValueChange={onFilterValueChange}
/>
</div>
);
}
export default EEOverview;
const ViewText = ({onChange}: {onChange: any}) => {
const onViewNameChange = (viewName: string) => {
onChange(viewName)
}
return (
<TextField sx={{ mx: 2, my: 2 }}
id="search"
variant="outlined"
size="small"
label="Name current view"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => { onViewNameChange(e.target.value) }}
/>
)
}
const ViewSelector = ({state, onChange}: {state:any, onChange: any}) => {
const [selectedView, setSelectedView] = useState(0)
const handleViewChange = (event: SelectChangeEvent<number>) => {
const selectedView = VIEWS.find(view => view.id===event.target.value)!
setSelectedView(selectedView.id)
const selectedState = selectedView.state
onChange(Object.assign({},selectedState, {}))
}
return (
<FormControl sx={{ m: 2, minWidth: 140 }} >
<Select labelId="view" variant="standard" size="medium" value={selectedView}
renderValue={(selected: any) => { return <div>{VIEWS[selected].name}</div>; }}
onChange={handleViewChange}>
{VIEWS.map((val, key) => (
<MenuItem value={key}>{val.name}</MenuItem>
))}
</Select>
</FormControl>
)
}
If I remove sortInfo/filterValue from passing to DataGrid, it behaves correctly, but it won't be saved to preferences.
Tried to move dataSource from props to state but it has the same behaviour

MUI Tree select - Set Value programatically

Hi Im using this package, All working fine but I can't select a value by default programatically which is user selected already using same component .
https://codesandbox.io/s/github/mikepricedev/mui-tree-select
Developer saying use this doc https://mikepricedev.github.io/mui-tree-select/interfaces/TreeSelectProps.html#value we can set, But I cant understand. Pls help me fix it.
Here is a working example of how to use a default value. I have changed this source to have an initial value programatically. value: "2:0"
import React, { useCallback, useState } from "react";
import TreeSelect, { BranchNode, defaultInput } from "mui-tree-select";
const generateOptions = (parentBranch, randomAsync = true) => {
const depth = parentBranch
? Number.parseInt(parentBranch.valueOf().label.split(":")[0]) + 1
: 0;
const options = [];
for (let i = 0, len = Math.ceil(Math.random() * 10); i < len; i++) {
const option = `${depth}:${i}`;
options.push(new BranchNode({ label: option }, parentBranch), option);
}
return randomAsync && Math.random() > 0.5
? new Promise((resolve) => {
setTimeout(() => {
resolve(options);
}, Math.ceil(Math.random() * 1000));
})
: options;
};
const getOptionLabel = (option) =>
option instanceof BranchNode ? option.valueOf().label : option.toString();
const defaultBranch = BranchNode.createBranchNode([
{ label: "0:5" },
{ label: "1:2" }
]);
const Sample = () => {
const [state, setState] = useState({
single: {
value: "2:0",
options: generateOptions(defaultBranch, false),
loading: false,
branch: defaultBranch
},
multiple: {
value: [],
options: generateOptions(null, false),
loading: false,
branch: null
}
});
return (
<div style={{ width: 350, padding: 16 }}>
<TreeSelect
branch={state.single.branch}
onBranchChange={(_, branch) => {
const options = generateOptions(branch);
if (options instanceof Promise) {
setState((state) => ({
...state,
single: {
...state.single,
branch,
loading: true
}
}));
options.then((options) => {
setState((state) => ({
...state,
single: {
...state.single,
options,
loading: false
}
}));
});
} else {
setState((state) => ({
...state,
single: {
...state.single,
branch,
options,
loading: false
}
}));
}
}}
options={state.single.options}
loading={state.single.loading}
getOptionLabel={getOptionLabel}
renderInput={useCallback(
(params) =>
defaultInput({
...params,
variant: "outlined",
label: "Single"
}),
[]
)}
value={state.single.value}
onChange={useCallback(
(_, value) => {
setState((state) => ({
...state,
single: {
...state.single,
value
}
}));
},
[setState]
)}
/>
<div style={{ height: "16px" }} />
<TreeSelect
onBranchChange={(_, branchOption) => {
const options = generateOptions(branchOption);
if (options instanceof Promise) {
setState((state) => ({
...state,
multiple: {
...state.multiple,
loading: true
}
}));
options.then((options) => {
setState((state) => ({
...state,
multiple: {
...state.multiple,
options,
loading: false
}
}));
});
} else {
setState((state) => ({
...state,
multiple: {
...state.multiple,
options,
loading: false
}
}));
}
}}
options={state.multiple.options}
loading={state.multiple.loading}
getOptionLabel={getOptionLabel}
freeSolo
multiple
renderInput={useCallback(
(params) =>
defaultInput({
...params,
variant: "outlined",
label: "Multiple"
}),
[]
)}
/>
</div>
);
};
export default Sample;
Hi I have got solution, That you have to pass particular object with
const [value, setValue] = useState(null);
useEffect(() => {
if (defaultIndustries !== null) {
setValue(() => new Node(defaultIndustries));
}
}, [defaultIndustries]);
And
<TreeSelect
getChildren={(node) =>
syncOrAsync(
node
? node.getChildren()
: industryMultiTree.map((country) => new Node(country)),
runAsync
)
}
getOptionDisabled={(option) => {
var _a;
return (
option.isBranch() &&
!((_a = option.getChildren()) === null || _a === void 0
? void 0
: _a.length)
);
}}
getParent={(node) => syncOrAsync(node.getParent(), runAsync)}
isBranch={(node) => syncOrAsync(node.isBranch(), runAsync)}
isOptionEqualToValue={(option, value) => {
return option instanceof FreeSoloNode ? false : option.isEqual(value);
}}
value={value}
renderInput={(params) => (
<TextField
{...params}
label={label}
error={error}
helperText={helperText}
/>
)}
sx={{ mb: 3 }}
onChange={(_, value) => {
onChangeSelect(value);
}}
/>

Adding drag`n`drop(react-dnd) to Material-UI component [TreeView]

I have a question. I created a TreeView and tried to bind the drag'n'drop, everything works, the TreeItem can be moved. BUT. If you expand any TreeItem and try to drag it, then all its child TreeItems will move with it.
How to make only one TreeItem drag'n'drop, without its child TreeItems????
My guess is I need to access the inner component of the item tree. I also don't know how to do this.
My Code:
export const PathTreeItem = (props: PathTreeItemProps) => {
const [obj, setObj] = useState<TreeItemType[] | undefined>(undefined)
const [isCurrentRequest, setIsCurrentRequest] = useState(false)
const [{ isDragging }, drag] = useDrag({
item: { type: props.obj.description || 'asd' },
canDrag: true,
collect: (monitor: DragSourceMonitor) => ({
isDragging: monitor.isDragging(),
}),
})
const treeItemStyle = useMemo(
() => ({
opacity: isDragging ? 0.4 : 1,
}),
[isDragging]
)
useEffect(() => {
if (isCurrentRequest && props.obj.parameter_type === 'ABC') {
APIs.get(props.obj.name)
.then(res => {
setObj(res.data)
})
.catch(err => {=
console.error('Error ', err)
})
}
}, [isCurrentRequest])
const handleLabelCLick = useCallback(event => {
console.log(event)
setIsCurrentRequest(!isCurrentRequest)
}, [])
return (
<TreeItem
ref={drag}
style={treeItemStyle}
nodeId={props.index}
label={props.obj.description}
onLabelClick={handleLabelCLick}
>
{props.obj.parameter_type === 'ABC' ? (
obj ? (
obj.map((value, index) => (
<PathTreeItem
key={props.keyIndex * 100 + index}
keyIndex={index}
index={`${props.index}.${index}`}
obj={value}
/>
))
) : (
<div></div>
)
) : null}
</TreeItem>
)
}
I have solved that problem by not dragging the TreeItem itself, but a custom component attached to it as its label attribute. Unfortunately, the solution currently only works in Firefox, not in Chrome or Safari:
const CustomItem = () => {
return (
// custom item components (Box, Typography, etc.)
);
}
const DraggableCustomItem = () => {
const [{ isDragging }, drag] = useDrag({
collect: (monitor: DragSourceMonitor) => ({
isDragging: monitor.isDragging()
}),
type: 'CustomItem'
})
return (
<div ref={drag} style={{ opacity: isDragging ? 0.5 : 1}}>
<CustomItem/>
</div>
)
}
const TreeViewDraggableCustomItem = () => {
return (
<TreeView>
<TreeItem key = { '1' }
nodeId = { '1' }
label = { <DraggableCustomItem/> }>
</TreeView>
);
}
See also related SO question, example sandbox and github comment.

Filter treeNodes in Ant Design Tree

I'm using a Searchable Tree component in Ant Design. I would like to filter treeNodes on search. So that if a search value is found, a treeNode is shown. If not, it is hidden (i. e. filtered).
There is a filterTreeNode prop in API. Is it possible to filter treeNodes (hide unrelevant treeNodes from search) with this prop? If not, how can achieve the desired result?
Here is my code for filterTreeNode function:
const filterTreeNode = (node) => {
const title = node.title.props.children[2];
const result = node.key.indexOf(searchValue) !== -1 ? true : false
console.log(searchValue);
console.log(result);
return result;
};
I can see that the result (true or false) is logged in the console, but with the Tree itself nothing happens.
Here is a link to codesandbox and the full code for the component:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Tree, Input } from "antd";
import gData from "./gData.js";
const { Search } = Input;
const dataList = [];
const generateList = (data) => {
for (let i = 0; i < data.length; i++) {
const node = data[i];
const { key } = node;
dataList.push({ key, title: key });
if (node.children) {
generateList(node.children);
}
}
};
generateList(gData);
const getParentKey = (key, tree) => {
let parentKey;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some((item) => item.key === key)) {
parentKey = node.key;
} else if (getParentKey(key, node.children)) {
parentKey = getParentKey(key, node.children);
}
}
}
return parentKey;
};
const SearchTree = () => {
const [expandedKeys, setExpandedKeys] = useState([]);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const [searchValue, setSearchValue] = useState("");
const onExpand = (expandedKeys) => {
setExpandedKeys(expandedKeys);
setAutoExpandParent(false);
};
const onChange = (e) => {
const { value } = e.target;
const expandedKeys = dataList
.map((item) => {
if (item.title.indexOf(value) > -1) {
return getParentKey(item.key, gData);
}
return null;
})
.filter((item, i, self) => item && self.indexOf(item) === i);
if (value) {
setExpandedKeys(expandedKeys);
setSearchValue(value);
setAutoExpandParent(true);
} else {
setExpandedKeys([]);
setSearchValue("");
setAutoExpandParent(false);
}
};
const filterTreeNode = (node) => {
const title = node.title.props.children[2];
const result = title.indexOf(searchValue) !== -1 ? true : false;
console.log(searchValue);
console.log(result);
return result;
};
const loop = (data) =>
data.map((item) => {
const index = item.title.indexOf(searchValue);
const beforeStr = item.title.substr(0, index);
const afterStr = item.title.substr(index + searchValue.length);
const title =
index > -1 ? (
<span>
{beforeStr}
<span className="site-tree-search-value">{searchValue}</span>
{afterStr}
</span>
) : (
<span>{item.title}</span>
);
if (item.children) {
return { title, key: item.key, children: loop(item.children) };
}
return {
title,
key: item.key
};
});
return (
<div>
<Search
style={{ marginBottom: 8 }}
placeholder="Search"
onChange={onChange}
/>
<Tree
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
treeData={loop(gData)}
filterTreeNode={filterTreeNode}
/>
</div>
);
};
ReactDOM.render(<SearchTree />, document.getElementById("container"));
As mentioned in the API document, filterTreeNode will highlight tree node, and will not hide it.
filterTreeNode
Defines a function to filter (highlight) treeNodes. When the function returns true, the corresponding treeNode will be highlighted
If you want to hide tree node, you will have to manually filter it first before before passing it to Tree in loop function, something like:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Tree, Input } from "antd";
import gData from "./gData.js";
const { Search } = Input;
const dataList = [];
const generateList = (data) => {
for (let i = 0; i < data.length; i++) {
const node = data[i];
const { key } = node;
dataList.push({ key, title: key });
if (node.children) {
generateList(node.children);
}
}
};
generateList(gData);
const getParentKey = (key, tree) => {
let parentKey;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some((item) => item.key === key)) {
parentKey = node.key;
} else if (getParentKey(key, node.children)) {
parentKey = getParentKey(key, node.children);
}
}
}
return parentKey;
};
const SearchTree = () => {
const [expandedKeys, setExpandedKeys] = useState([]);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const [searchValue, setSearchValue] = useState("");
const [treeData, setTreeData] = useState(gData);
const onExpand = (expandedKeys) => {
setExpandedKeys(expandedKeys);
setAutoExpandParent(false);
};
const onChange = (e) => {
const value = e.target.value?.toLowerCase();
const expandedKeys = dataList
.map((item) => {
if (item.title.indexOf(value) > -1) {
return getParentKey(item.key, gData);
}
return null;
})
.filter((item, i, self) => item && self.indexOf(item) === i);
if (value) {
const hasSearchTerm = (n) => n.toLowerCase().indexOf(value) !== -1;
const filterData = (arr) =>
arr?.filter(
(n) => hasSearchTerm(n.title) || filterData(n.children)?.length > 0
);
const filteredData = filterData(gData).map((n) => {
return {
...n,
children: filterData(n.children)
};
});
setTreeData(filteredData);
setExpandedKeys(expandedKeys);
setSearchValue(value);
setAutoExpandParent(true);
} else {
setTreeData(gData);
setExpandedKeys([]);
setSearchValue("");
setAutoExpandParent(false);
}
};
const filterTreeNode = (node) => {
const title = node.title.props.children[2];
const result = title.indexOf(searchValue) !== -1 ? true : false;
console.log(searchValue);
console.log(result);
return result;
};
const loop = (data) =>
data.map((item) => {
const index = item.title.indexOf(searchValue);
const beforeStr = item.title.substr(0, index);
const afterStr = item.title.substr(index + searchValue.length);
const title =
index > -1 ? (
<span>
{beforeStr}
<span className="site-tree-search-value">{searchValue}</span>
{afterStr}
</span>
) : (
<span>{item.title}</span>
);
if (item.children) {
return { title, key: item.key, children: loop(item.children) };
}
return {
title,
key: item.key
};
});
return (
<div>
<Search
style={{ marginBottom: 8 }}
placeholder="Search"
onChange={onChange}
/>
<Tree
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
treeData={loop(treeData)}
filterTreeNode={filterTreeNode}
/>
</div>
);
};
ReactDOM.render(<SearchTree />, document.getElementById("container"));
Couple weeks ago, i had to meet same like this client's enhancement. So how i did it was, we had Search over DirectoryTree and inside tree we rendered the nodes with a function
<DirectoryTree
onExpand={handleOperatorExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onSelect={handleOperatorSelect}
>
{renderTreeNodes(mtoCharts)}
</DirectoryTree>
In search onChange event we saved values to a state, like searchValue. Then searchValue was checked in renderTreeNodes function with regex:
let dynamicRegex = `.*(${searchValue.toUpperCase()}){1}.*`;
if
item.name.match(dynamicRegex)
then we display the node otherwise we dont. SIMPLE !
UPDATE : codes
<Search onChange={handleOperatorChange} />
const handleOperatorChange = e => {
const { value } = e.target;
let temp = value.replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()"'+#<>?\\\[\]]/g, '');
setSearchValue(temp);
setAutoExpandParent(true);};
and finally renderTreeNodes :
const renderTreeNodes = data =>
data &&
data instanceof Array &&
data.map(item => {
const title = (
<Highlighter
highlightStyle={{ color: '#f50' }}
searchWords={[searchValue]}
autoEscape={true}
textToHighlight={item.name}
/>
);
if (searchValue) {
let dynamicRegex = `.*(${searchValue.toUpperCase()}){1}.*`;
if (!isEmpty(item.children)) {
// let doesChildrenHaveMatchingName = false;
// item.children.forEach(itemChild => {
// if (itemChild.name.match(dynamicRegex)) {
// doesChildrenHaveMatchingName = true;
// }
// });
// if (doesChildrenHaveMatchingName) {
return (
<TreeNode
className={!item.active ? 'dim-category' : ''}
key={item.code}
title={title}
id={item.id}
code={item.code}
level={item.level}
parentId={item.parentId}
parentReference={item.path}
icon={({ expanded }) => (
<Icon type={expanded ? 'folder-open' : 'folder'} style={{ color: '#32327f' }} />
)}
>
{renderTreeNodes(item.children)}
</TreeNode>
);
// }
}
if (item.name.match(dynamicRegex) || item.path.match(dynamicRegex)) {
return (
<TreeNode
className={item.active ? 'false' : 'dim-category'}
key={item.code}
title={!item.leaf ? title : getMtoTitle(item, title)}
{...(item.leaf ? { leaf: item.leaf } : {})}
id={item.id}
code={item.code}
level={item.level}
parentId={item.parentId}
parentReference={item.path}
icon={({ expanded }) => {
return item.leaf ? (
<Icon type="money-collect" style={{ color: '#47bfa5' }} />
) : (
<Icon type={expanded ? 'folder-open' : 'folder'} style={{ color: '#32327f' }} />
);
}}
/>
);
}
} else {
if (!isEmpty(item.children)) {
return (
<TreeNode
className={!item.active ? 'dim-category' : ''}
key={item.code}
title={title}
id={item.id}
code={item.code}
level={item.level}
parentId={item.parentId}
parentReference={item.path}
icon={({ expanded }) => (
<Icon type={expanded ? 'folder-open' : 'folder'} style={{ color: '#32327f' }} />
)}
>
{renderTreeNodes(item.children)}
</TreeNode>
);
}
return (
<TreeNode
className={item.active ? 'false' : 'dim-category'}
key={item.code}
title={!item.leaf ? title : getMtoTitle(item, title)}
{...(item.leaf ? { leaf: item.leaf } : {})}
id={item.id}
code={item.code}
level={item.level}
parentId={item.parentId}
parentReference={item.path}
icon={({ expanded }) => {
return item.leaf ? (
<Icon type="money-collect" style={{ color: '#47bfa5' }} />
) : (
<Icon type={expanded ? 'folder-open' : 'folder'} style={{ color: '#32327f' }} />
);
}}
/>
);
}
});

CheckMap State not updating when unticking checkbox in React

This is my first time to develop a react application. Please bear with me.
I intended to display the key (value in the table) on a confirmation dialog. It works as intended but when I tick then untick the checkbox, it seems that the key is still in the map.
I displayed checkedMap by retrieving its keys and add it in an array. But when I try to untick and then invoke the dialog, checkedMap still has the unticked key, in the debug view.
Thank you for your help.
import React, { useState, useEffect, useRef } from "react";
//import makeData from "../makeData";
import { useTableState } from "react-table";
import Table from "../TransactionPanelTable";
// Simulate a server
const getServerData = async ({ filters, sortBy, pageSize, pageIndex }) => {
await new Promise(resolve => setTimeout(resolve, 500));
// Ideally, you would pass this info to the server, but we'll do it here for convenience
const filtersArr = Object.entries(filters);
// Get our base data
let rows = [];
rows.push({
transaction_seq: 1555,
record_count: 300,
user_id: "test1",
updated_at: "09-MAY-19 10.01.45.371373000 PM",
duration: 5.7
});
rows.push({
transaction_seq: 2666,
rec_count: 1234,
user_id: "test2",
updated_at: "",
duration: 1.23
});
// Apply Filters
if (filtersArr.length) {
rows = rows.filter(row =>
filtersArr.every(([key, value]) => row[key].includes(value))
);
}
// Apply Sorting
if (sortBy.length) {
const [{ id, desc }] = sortBy;
rows = [...rows].sort(
(a, b) => (a[id] > b[id] ? 1 : a[id] === b[id] ? 0 : -1) * (desc ? -1 : 1)
);
}
// Get page counts
const pageCount = Math.ceil(rows.length / pageSize);
const rowStart = pageSize * pageIndex;
const rowEnd = rowStart + pageSize;
// Get the current page
rows = rows.slice(rowStart, rowEnd);
return {
rows,
pageCount
};
};
export default function({ infinite }) {
**const [checkedMap, setCheckedMap] = useState(new Map());**
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const currentRequestRef = useRef();
**let newMap = new Map();**
const fetchData = async () => {
setLoading(true);
// We can use a ref to disregard any outdated requests
const id = Date.now();
currentRequestRef.current = id;
// Call our server for the data
const { rows, pageCount } = await getServerData({
filters,
sortBy,
pageSize,
pageIndex
});
// If this is an outdated request, disregard the results
if (currentRequestRef.current !== id) {
return;
}
// Set the data and pageCount
setData(rows);
setState(old => ({
...old,
pageCount
}));
**rows.forEach(row => newMap.set(row, false));**
//setCheckedMap(newMap);
setLoading(false);
};
**const handleCheckedChange = transaction_seq => {
let modifiedMap = checkedMap;
modifiedMap.set(transaction_seq, !checkedMap.get(transaction_seq));
setCheckedMap(modifiedMap);
};**
const columns = [
{
Header: "Transaction(s)",
className: "left",
columns: [
{
id: "checkbox",
accessor: "checkbox",
Cell: ({ row }) => {
return (
<input
type="checkbox"
className="checkbox"
checked={checkedMap.get(row.original.transaction_seq)}
onChange={() =>
handleCheckedChange(row.original.transaction_seq)
}
/>
);
},
sortable: false,
width: 45
},
{
Header: "Transaction Sequence",
accessor: "transaction_seq",
id: "transaction_seq",
minWidth: 200,
maxWidth: 300
},
{
Header: "Record count",
accessor: "record_count",
width: 300
},
{
Header: "User Id",
accessor: "user_id",
width: 300
},
{
Header: "Updated At",
accessor: "updated_at",
width: 400
},
{
Header: "Duration",
accessor: "duration",
width: 400
}
]
}
];
// Make a new controllable table state instance
const state = useTableState({ pageCount: 0 });
const [{ sortBy, filters, pageIndex, pageSize }, setState] = state;
// When sorting, filters, pageSize, or pageIndex change, fetch new data
useEffect(() => {
fetchData();
}, [sortBy, filters, pageIndex, pageSize]);
return (
<React.Fragment>
<Table
{...{
data,
**checkedMap,**
columns,
infinite,
state, // Pass the state to the table
loading,
manualSorting: true, // Manual sorting
manualFilters: true, // Manual filters
manualPagination: true, // Manual pagination
disableMultiSort: true, // Disable multi-sort
disableGrouping: true, // Disable grouping
debug: true
}}
/>
</React.Fragment>
);
}
Here is the table.js
import styled, { css } from "styled-components";
import React, { useRef, useState, useEffect, useLayoutEffect } from "react";
import { FixedSizeList as List } from "react-window";
import {
useTable,
useColumns,
useRows,
useFilters,
useSortBy,
useExpanded,
usePagination,
useFlexLayout
} from "react-table";
export default function MyTable({ loading, infinite, checkedMap, ...props }) {
const instance = useTable(
{
...props
},
useColumns,
useRows,
useFilters,
useSortBy,
useExpanded,
usePagination,
useFlexLayout
);
const {
getTableProps,
headerGroups,
rows,
getRowProps,
pageOptions,
page,
state: [{ pageIndex, pageSize, sortBy, filters }],
gotoPage,
prepareRow,
previousPage,
nextPage,
setPageSize,
canPreviousPage,
canNextPage
} = instance;
const { listRef, rowHeight, height, overscan } = useInfiniteScroll({
enabled: infinite,
sortBy,
filters,
pageIndex,
pageSize
});
let tableBody;
const renderRow = (row, index, style = {}) => {
if (!row) {
return (
<Row {...{ style, even: index % 2 }}>
<Cell>Loading more...</Cell>
</Row>
);
}
prepareRow(row);
return (
<Row {...row.getRowProps({ style, even: index % 2 })}>
{row.cells.map(cell => {
const isPivot = row.groupByID === cell.column.id;
const showAggregate = row.subRows && !isPivot;
return (
<Cell {...cell.getCellProps()}>
{showAggregate ? (
cell.column.aggregate ? (
cell.render("Aggregated")
) : null
) : (
<span>
{isPivot ? (
<span
style={{
cursor: "pointer",
paddingLeft: `${row.depth * 2}rem`,
paddingRight: "1rem",
whiteSpace: "nowrap"
}}
onClick={() => row.toggleExpanded()}
/>
) : null}
{cell.render("Cell")}
{isPivot ? <span> ({row.subRows.length})</span> : null}
</span>
)}
</Cell>
);
})}
</Row>
);
};
if (infinite) {
tableBody = (
<List
ref={listRef}
height={height}
itemCount={rows.length + 1}
itemSize={rowHeight}
overscanCount={overscan}
scrollToAlignment="start"
{...getRowProps()}
>
{({ index, style }) => {
const row = rows[index];
return renderRow(row, index, style);
}}
</List>
);
} else {
tableBody =
page && page.length ? page.map((row, i) => renderRow(row, i)) : null;
}
let pagination;
pagination = pageOptions.length ? (
<Pagination {...getRowProps()}>
<Cell>
<Button onClick={() => previousPage()} disabled={!canPreviousPage}>
Previous
</Button>{" "}
<Button onClick={() => nextPage()} disabled={!canNextPage}>
Next
</Button>{" "}
<span>
Page{" "}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{" "}
</span>
<span>
| Go to page:{" "}
<Input
type="number"
defaultValue={pageIndex + 1}
onChange={e => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
gotoPage(page);
}}
style={{ width: "100px" }}
/>
</span>{" "}
<Select
value={pageSize}
onChange={e => {
setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>{" "}
<Button onClick={() => reprocessConfirmation()}>Reprocess</Button>
</Cell>
</Pagination>
) : null;
function reprocessConfirmation() {
let confirmation = window.confirm(
"Do you want to reprocess transaction sequence " +
Array.from(checkedMap.keys())
);
if (confirmation === true) console.log(Array.from(checkedMap.keys()));
else console.log("CANCEL");
}
return (
<React.Fragment>
<Table {...getTableProps()}>
{headerGroups.map(headerGroup => (
<HeaderRow {...headerGroup.getRowProps()}>
{headerGroup.headers.map(column => (
<Header
{...column.getHeaderProps()}
sorted={column.sorted}
sortedDesc={column.sortedDesc}
sortedIndex={column.sortedIndex}
>
<div>
<span {...column.getSortByToggleProps()}>
{column.render("Header")}
</span>{" "}
</div>
{column.canFilter ? <div>{column.render("Filter")}</div> : null}
</Header>
))}
</HeaderRow>
))}
{tableBody}
<Row {...getRowProps()}>
{loading ? (
<Cell>
<strong>Loading...</strong>
</Cell>
) : (
<Cell>{rows.length} Total Records</Cell>
)}
</Row>
{pagination}
</Table>
</React.Fragment>
);
}

Categories