const handleChangeMultiple = (event: React.ChangeEvent<{ value: unknown }>) => {
const { options } = event.target as HTMLSelectElement;
const value: string[] = [];
for (let i = 0, l = options.length; i < l; i += 1) {
if (options[i].selected) {
value.push(options[i].value);
}
}
setPersonName(value);
};
I just started using material UI and they have this great Select component that let you select from a list.
The code above is the sample code they provided that work for a string[], but my project is selecting from an object array.
example: {label: "string", value:"string", b: boolean}
My question is how can I modify this handleChange to work for an object array?
I try changing string[] to the dataType[] I created but I get the error "Argument of type 'string' is not assignable to parameter of type 'dataType'.
const handleChangeMultiple = (event: ChangeEvent<{ value: dataType[] }>) => {
console.log(event.target.value)
}
When I try this, it console log the correct value selected, but when I change console.log to setValue(event.target.value), I get error value.map is not a function.
{value.map((item) => (
option key={item.value} value={item.label}>
{item.label}
</option>
The code above work when console.log.
Select component is using basic type to determine which options are selected (Comparing objects is not so easy). You can use the array index:
import React from 'react';
import { createStyles, makeStyles, Theme } from '#material-ui/core/styles';
import InputLabel from '#material-ui/core/InputLabel';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
maxWidth: 300,
},
chips: {
display: 'flex',
flexWrap: 'wrap',
},
chip: {
margin: 2,
},
noLabel: {
marginTop: theme.spacing(3),
},
}),
);
interface User {
value: string,
label: string,
superUser: boolean
}
const users = [{
value: 'OliverHansen',
label: 'Oliver Hansen',
superUser: true
}, {
value: 'VanHenry',
label: 'Van Henry',
superUser: false
}, {
value: 'AprilTucker',
label: 'April Tucker',
superUser: true
}, {
value: 'RalphHubbard',
label: 'Ralph Hubbard',
superUser: false
}, {
value: 'OmarAlexander',
label: 'Omar Alexander',
superUser: true
}, {
value: 'CarlosAbbott',
label: 'Carlos Abbott',
superUser: false
}];
export default function MultipleSelect() {
const classes = useStyles();
const [selectedUsers, setSelectedUsers] = React.useState<User[]>([]);
const [selectedUserIndexes, setSelectedUserIndexes] = React.useState<number[]>([]);
const handleChangeMultiple = (event: React.ChangeEvent<{ value: unknown }>) => {
const { options } = event.target as HTMLSelectElement;
const selectedUsers: User[] = [];
const selectedUserIndexes: number[] = [];
for (let i = 0, l = options.length; i < l; i += 1) {
if (options[i].selected) {
let selectedUserIndex = parseInt(options[i].value, 10);
selectedUsers.push(users[selectedUserIndex]);
selectedUserIndexes.push(selectedUserIndex);
}
}
console.log(selectedUserIndexes, selectedUsers);
setSelectedUserIndexes(selectedUserIndexes);
setSelectedUsers(selectedUsers);
};
return (
<div>
<FormControl className={classes.formControl}>
<InputLabel shrink htmlFor="select-multiple-native">
Native
</InputLabel>
<Select
multiple
native
value={selectedUserIndexes}
onChange={(e) => handleChangeMultiple(e)}
inputProps={{
id: 'select-multiple-native',
}}
>
{users.map((user, index) => (
<option key={index} value={index}>
{user.label} {}
</option>
))}
</Select>
</FormControl>
</div>
);
}
Related
This is my first introduction to react-flow. I am looking to create a custom node where after creation, the user can enter information in the node and save/display it. From the react-flow documentation on custom nodes, they have a similar example where they created a TextUpdaterNode that console.logs the user input.
Instead of logging it it via console, I am looking for a way to save the information to the node itself and display it on the node. For example, if a user were to enter "24, male" into the input and hit the "enter" key, I want the node to be updated with that information.
What are the ways I can go about doing this?
What you're trying to do needs a little more than that:
You can see alive example here: https://codesandbox.io/s/dank-waterfall-8jfcf4?file=/src/App.js
Basically, you need:
Import useNodesState from 'react-flow-renderer';
Instead of basic definition of nodes, you will need to use: const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
Then, will have to define the onAdd, which looks like:
const onAdd = useCallback(() => {
const newNode = {
id: getNodeId(),
data: { label: `${state.name} (${state.age})` },
position: {
x: 0,
y: 0 + (nodes.length + 1) * 20
}
};
setNodes((nds) => nds.concat(newNode));
}, [nodes, setNodes, state.name, state.age]);
You can include edit, pretty similar like:
const onEdit = () => {
setNodes((nds) =>
nds.map((node) => {
if (node.id === editState.id) {
node.data = {
...node.data,
label: `${node.id} - ${editState.name} (${editState.age})`
};
}
return node;
})
);
};
Finally, draw the flow: <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
The whole code looks like:
import React, { useState, useCallback } from "react";
import ReactFlow, {
ReactFlowProvider,
useNodesState,
useEdgesState
} from "react-flow-renderer";
import "./styles.css";
const getNodeId = () => `randomnode_${+new Date()}`;
const initialNodes = [
{ id: "1", data: { label: "Node 1" }, position: { x: 100, y: 100 } },
{ id: "2", data: { label: "Node 2" }, position: { x: 100, y: 200 } }
];
const initialEdges = [{ id: "e1-2", source: "1", target: "2" }];
const FlowExample = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges] = useEdgesState(initialEdges);
const [state, setState] = useState({ name: "", age: "" });
const onAdd = useCallback(() => {
const newNode = {
id: getNodeId(),
data: { label: `${state.name} (${state.age})` },
position: {
x: 0,
y: 0 + (nodes.length + 1) * 20
}
};
setNodes((nds) => nds.concat(newNode));
}, [nodes, setNodes, state.name, state.age]);
return (
<div>
Name:{" "}
<input
type="text"
onChange={(e) => {
setState((prev) => ({ ...prev, name: e.target.value }));
}}
/>
Age:{" "}
<input
type="text"
onChange={(e) => {
setState((prev) => ({ ...prev, age: e.target.value }));
}}
/>
<button onClick={onAdd}>add node</button>
<div style={{ width: "500px", height: "500px" }}>
<ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
</div>
</div>
);
};
export default () => (
<ReactFlowProvider>
<FlowExample />
</ReactFlowProvider>
);
Also, with edit:
import React, { useState, useCallback } from "react";
import ReactFlow, {
ReactFlowProvider,
useNodesState,
useEdgesState
} from "react-flow-renderer";
import "./styles.css";
const getNodeId = () => `${String(+new Date()).slice(6)}`;
const initialNodes = [
{ id: "1", data: { label: "Node 1" }, position: { x: 100, y: 100 } },
{ id: "2", data: { label: "Node 2" }, position: { x: 100, y: 200 } }
];
const initialEdges = [{ id: "e1-2", source: "1", target: "2" }];
const FlowExample = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges] = useEdgesState(initialEdges);
const [state, setState] = useState({ name: "", age: "" });
const [editState, setEditState] = useState({ id: "", name: "", age: "" });
const onEdit = () => {
setNodes((nds) =>
nds.map((node) => {
if (node.id === editState.id) {
node.data = {
...node.data,
label: `${node.id} - ${editState.name} (${editState.age})`
};
}
return node;
})
);
};
const onAdd = () => {
const id = getNodeId();
const newNode = {
id,
data: { label: `${id} - ${state.name} (${state.age})` },
position: {
x: 0,
y: 0 + (nodes.length + 1) * 20
}
};
setNodes((nds) => nds.concat(newNode));
};
return (
<div>
Name:{" "}
<input
type="text"
onChange={(e) => {
setState((prev) => ({ ...prev, name: e.target.value }));
}}
/>
Age:{" "}
<input
type="text"
onChange={(e) => {
setState((prev) => ({ ...prev, age: e.target.value }));
}}
/>
<button onClick={onAdd}>add node</button>
<br />
Id:{" "}
<input
type="text"
onChange={(e) => {
setEditState((prev) => ({ ...prev, id: e.target.value }));
}}
/>
Name:{" "}
<input
type="text"
onChange={(e) => {
setEditState((prev) => ({ ...prev, name: e.target.value }));
}}
/>
Age:{" "}
<input
type="text"
onChange={(e) => {
setEditState((prev) => ({ ...prev, age: e.target.value }));
}}
/>
<button onClick={onEdit}>Edit node</button>
<div style={{ width: "500px", height: "500px" }}>
<ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
</div>
</div>
);
};
export default () => (
<ReactFlowProvider>
<FlowExample />
</ReactFlowProvider>
);
A more helpful example from documentation would be:
https://reactflow.dev/docs/examples/interaction/save-and-restore/
https://reactflow.dev/docs/examples/nodes/update-node/
But you have to remove all the extra information (Also, you can use it to go deeper!)
I managed to come up a solution to create such a custom node that allowed you to input, save and display information. I have tried to include relevant information and the code block I used below.
Custom Node
import { useCallback } from 'react';
import { Handle, Position} from 'react-flow-renderer';
const handleStyle = { left: 10 };
//Custom node requires props of data to be passed to it.
function CustomNode({ data }) {
let serviceType = "offered";
//This handles pressing enter inside the description
const handleKeyDown = (evt) => {
if (evt.key === "Enter") {
//Check if empty string
if (evt.target.value.length !== 0) {
//This code is because services are either offered or borrowed.
if (serviceType === "offered") {
data.serviceOffered.push(evt.target.value);
} else if (serviceType === "borrowed") {
data.serviceBorrowed.push(evt.target.value);
}
//Clearing input after pressing enter
evt.currentTarget.value = "";
}
}
};
const onChange = useCallback((evt) => {
//Update service type without pressing enter
serviceType = evt.target.value;
});
return (
<div className="text-updater-node">
<Handle type="target" position={Position.Top} />
<div>
<p>Entity</p>
<label htmlFor="text"><p className='nodeTitle'>{data.label}</p></label>
<input id="text" name="text" onKeyDown={handleKeyDown} />
<select name="type" onChange={onChange}>
<option value="offered" >Offered </option>
<option value="borrowed">Borrowed</option>
</select>
<div className="info">
{/* This is where the description information is displayed. It checks if it is empty, if not it loops through and displays it. */}
<h2>Service Borrowed</h2>
<ul>
{data.serviceBorrowed.length? data.serviceBorrowed.map(service => (<li key={service}>{service}</li>)) : <span></span>}
</ul>
<h2>Service Offered</h2>
<ul>
{data.serviceOffered.length? data.serviceOffered.map(service => (<li key={service}>{service}</li>)) : <span></span>}
</ul>
</div>
</div>
<Handle type="source" position={Position.Bottom} id="a" style={handleStyle} />
<Handle type="source" position={Position.Bottom} id="b" />
</div>
);
}
export default CustomNode;
I have a parent reactFlow component with the following code block. The important thing about this is to set the custom node type of react flow and pass in an object containing information about the nodes and edges to be rendered.
import { Fragment, useCallback, useState } from "react";
import ReactFlow, {
addEdge,
applyEdgeChanges,
applyNodeChanges,
} from "react-flow-renderer";
import initialNodes from "../data/nodes"; //This both ended up being empty file
import initialEdges from "../data/edges"; //This both ended up being empty file
import CustomNode from "./customNode";
import "./customNode.css";
//Set nodetype as Custom node, IMPORTANT!
const nodeTypes = { customNode: CustomNode };
function Flow() {
const defaultEdgeOptions = { animated: true };
//Input Elements
const [name, setName] = useState("");
const addNode = () => {
setNodes((e) =>
e.concat({
id: (e.length + 1).toString(),
data: { label: `${name}`, serviceOffered: [], serviceBorrowed: [] },
position: { x: 0, y: 0 },
type: "customNode",
})
);
};
//Nodes and edges containing information of the nodes and edges
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
//Boiler plate code for reactFlow
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[setNodes]
);
const onEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[setEdges]
);
const onConnect = useCallback(
(connection) => setEdges((eds) => addEdge(connection, eds)),
[setEdges]
);
return (
<Fragment>
<Row>
<Col lg={9}>
<ReactFlow
className="Canvas mt-1 border border-secondary rounded"
nodes={nodes} //Node information is passed here
edges={edges} //Edges information is passed here
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
defaultEdgeOptions={defaultEdgeOptions}
style={{ width: "100%", height: "80vh" }}
fitView
nodeTypes={nodeTypes}
/>
</Col>
</Row>
</Fragment>
);
}
export default Flow;
I added more information inside the data property of my node.js. It ended up being initialize as empty but this template should be helpful in understanding how I saved the information for the node. The edge followed the standard format shown on react-flow documentation.
export default [
// {
// id: '1',
// type: 'customNode',
// data: { label: 'Input Node', info: [{id:1, action:"Everything is burning"}, {id:2, action:"I'm fine"}], noOfActions:2 },
// position: { x: 250, y: 25 },
// },
];
I hope this has been useful!
Accepted answer is about modifying properties of components which is not React way. That code may break easily.
There are other ways to bring callback to custom nodes.
Put callback into node's data
This is from React flow documentation: https://reactflow.dev/docs/examples/nodes/custom-node/
setNodes([
...
{
id: '2',
type: 'selectorNode',
data: { onChange: onChange, color: initBgColor },
...
Cons: you need pay extra attention when you modify or create new nodes dynamically
or Define custom types dynamically
In this approach, you keep node data and behavior concerns separate.
I'm using TypeScript in order to show types of data we operate along the way.
First, you extend your custom node properties with your callback:
import {NodeProps} from "react-flow-renderer/dist/esm/types/nodes";
// by default, custom node is provisioned with NodeProps<T>
// we extend it with additional property
export type CustomNodeProps = NodeProps<CustomData> & {
onClick: (id: string) => void
}
function CustomNode(props: CustomNodeProps) {
return <button onClick={() => props.onClick(props.id)}>Do it</button>
}
Then you create new constructor that provides callback and put it into custom nodes mapping using memoization:
function Flow() {
const [graph, dispatchAction] = useReducer(...);
...
// useMemo is neccessary https://reactflow.dev/docs/guides/troubleshooting/#it-looks-like-you-have-created-a-new-nodetypes-or-edgetypes-object-if-this-wasnt-on-purpose-please-define-the-nodetypesedgetypes-outside-of-the-component-or-memoize-them
const nodeTypes = useMemo(() => {
return {
custom: (props: NodeProps<CustomData>) => {
return CustomNode({...props, onClick: (id: string) => {
dispatchAction({
type: 'customNodeButtonClicked',
nodeId: id,
})
}})
}
}
}, [])
return (
<>
<ReactFlow nodeTypes={nodeTypes} ... />
</>
);
}
I suspect it is a syntax issue. When I use the same approach for instead of I do get the expected results. The data is coming from an endpoint through the import of getAssembly which is generated via await fetch and the results are being rendered as JSON before the data is imported.
The commented out code on line how I assume the commanded need to be executed and I get no error, but no data is rendered. The code on line 50 works fine but does not provide the filtering. I intend the use the value in the typeName variable once the filtering is working.
import { useState, useEffect } from "react";
import MenuItem from "#mui/material/MenuItem";
import Select from "#mui/material/Select";
import InputLabel from "#mui/material/InputLabel";
import ListItemText from "#mui/material/ListItemText";
import { DataGrid } from "#mui/x-data-grid";
import { getAssembly } from "./GetAssembly";
const columns = [
{ field: "id", headerName: "ID", width: 300 },
{ field: "status", headerName: "Status", width: 90 },
{ field: "atype", headerName: "AType", width: 80 },
{ field: "name", headerName: "Name", width: 350 },
{ field: "time", headerName: "Start Time", width: 150 },
{ field: "org", headerName: "Organization", width: 350 },
];
export default function SelectAssembly() {
const [typeName, setTypeName] = useState([""]);
// const [assemRows, setAssemRows] = useState([]);
const [state, setState] = useState({
assembly: [],
assembtypename: [],
unsignenList: [],
});
const handleTypeChange = (event) => {
const {
target: { value },
} = event;
setTypeName(value);
};
console.log(typeName);
useEffect(() => {
console.log("useEffect");
getAssembly().then((res) => {
setState((prevState) => ({ ...prevState, assembly: res.assemblies }));
});
}, []);
const typeSelection = [
...new Set(state.assembly.map((item) => item.assemblyType)),
];
//const assemList = state.assembly.filter(assem => assem === "batch").map(assem => {
const assemList = state.assembly.map((assem) => {
return {
id: assem.assemblyId,
status: assem.status,
atype: assem.assemblyType,
name: assem.name,
time: assem.timeStarted,
org: assem.organizationId,
asid: assem.referenceId,
pmap: assem.propertiesMap,
};
});
// const unsignedList = assemList.filter((str) => {
// //str can include or str can equal with === (return str.includes("import");)
// return str === "import";
// });
return (
<div>
<InputLabel sx={{ fontSize: 12 }}>Assembly Type</InputLabel>
<Select
label="Type"
value={typeName}
sx={{ height: 35, fontSize: 10 }}
fullWidth
focused
onChange={handleTypeChange}
>
{typeSelection.map((types) => {
return (
<MenuItem key={types.indexOf(types) > -1} value={types}>
<ListItemText primary={types} />
</MenuItem>
);
})}
</Select>
<br />
<br />
<DataGrid
density="compact"
hideFooterRowCount
rows={assemList}
// rows={unsignedList}
columns={columns}
pageSize={15}
rowsPerPageOptions={[15]}
/>
</div>
);
}
Thanks to Jim Ptak at Southwire for helping me see the problem. In the filter method I did not specify the particular element to filter on. Once the code was modified as follows:
const assemList = state.assembly.filter(assem => assem.assemblyType === "MO import").map(assem => {
//const assemList = state.assembly.map((assem) => {
return {
id: assem.assemblyId,
status: assem.status,
atype: assem.assemblyType,
name: assem.name,
time: assem.timeStarted,
org: assem.organizationId,
asid: assem.referenceId,
pmap: assem.propertiesMap,
};
});
the contents of the datagrid filters perfectly.
Using fetch, I want to dynamically populate the City material-ui dropdwon (Select) when I select a value from the State dropdown, but could not do so. When I do the same without using the fetch, it works fine. I think the problem is with the promise being returned by the fetch call. There is no problem in the fetch call as I can see the list of cities in return. Please suggest how to do it.
import React from 'react';
import { createStyles, makeStyles, Theme } from '#material-ui/core/styles';
import InputLabel from '#material-ui/core/InputLabel';
import FormHelperText from '#material-ui/core/FormHelperText';
import FormControl from '#material-ui/core/FormControl';
import {Select, MenuItem} from '#material-ui/core';
import './App.css';
export function getStates() {
return [
{name: 'California', id: "1"},
{name: 'New York', id: "2"},
]
}
function Home() {
const useStyles = makeStyles((theme: Theme) =>
createStyles({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}),
);
const [State, setState] = React.useState([]);
const [cities, setCities] = React.useState([]);
const selectStyle = makeStyles(theme => ({
root: {
textDecoration: 'none',
color: 'red',
alignItems: 'center',
fontWeight: "bold",
display: "flex",
justifyContent: "space-around",
fontSize: 18,
margin: 0,
'&:hover': {
textDecoration: 'none'
}
},
}));
function getCities() {
var s = '' // JSON body goes here
const fetchData = async () => {
const cities = [];
try {
const res = await fetch('http://127.0.0.1:8080',
{
method : "POST",
headers: {"content-type": "text/plain"},
body: s
}
);
const data = await res.json();
console.log("state response status: " + res.status)
for(var key in data.cities) {
cities.push({id: key, name: data.cities[key]})
}
return cities;
}
catch (err) {
console.log("Fetch Exception: " + err)
}
}
const cities = fetchData();
return cities;
}
const handleStateChange = (event: React.ChangeEvent< { value: unknown} >) => {
setState(event.target.value);
const r = getCities();
setCities([r]);
}
const fixed_states = getStates();
const classes = useStyles()
const selectClass = selectStyle()
return (
<div className="main-select">
<container>
<FormControl required className={classes.formControl}>
<InputLabel id="sel">State</InputLabel>
<Select labelId="state_select_labelid" id="state_select_id" name="state_select_name" onChange={handleStateChange} className={selectClass.root}>
{fixed_states.map(({id, name}, index) => (
< MenuItem key={id} value={name}>
{name}
</MenuItem>
)) }
</Select>
<FormHelperText></FormHelperText>
</FormControl>
<FormControl required className={classes.formControl}>
<InputLabel id="city_input_label_id">City</InputLabel>
<Select labelId="city_select_labelid" id="city_select_id" name="city_select_name">
{cities.map(({id, name}, index) => (
< MenuItem key={id} value={name}>
{name}
</MenuItem>
))}
</Select>
<FormHelperText></FormHelperText>
</FormControl>
</container>
</div>
);
}
export default Home;
You code:
const handleStateChange = (event: React.ChangeEvent< { value: unknown} >) => {
setState(event.target.value);
const r = getCities();
setCities([r]);
}
but getCities return array of cities and then you set array cities in state like array of array.
So just update argument in setCities row to
const handleStateChange = (event: React.ChangeEvent< { value: unknown} >) => {
setState(event.target.value);
const r = getCities();
setCities(r);
}
Can anybody send code on how to implement fluent UI details List in Functional Component(https://developer.microsoft.com/en-us/fluentui#/controls/web/detailslist/basic) and how to fetch data from API to details List
That's a start you will need to "refact" this code by the way this is a really good practice :
import * as React from "react";
import { Announced } from "office-ui-fabric-react/lib/Announced";
import {
TextField,
ITextFieldStyles
} from "office-ui-fabric-react/lib/TextField";
import {
DetailsList,
DetailsListLayoutMode,
Selection,
IColumn
} from "office-ui-fabric-react/lib/DetailsList";
import { MarqueeSelection } from "office-ui-fabric-react/lib/MarqueeSelection";
import { Fabric } from "office-ui-fabric-react/lib/Fabric";
import { mergeStyles } from "office-ui-fabric-react/lib/Styling";
import { Text } from "office-ui-fabric-react/lib/Text";
const exampleChildClass = mergeStyles({
display: "block",
marginBottom: "10px"
});
const textFieldStyles: Partial<ITextFieldStyles> = {
root: { maxWidth: "300px" }
};
export interface IDetailsListBasicExampleItem {
key: number;
name: string;
value: number;
}
export interface IDetailsListBasicExampleState {
items: IDetailsListBasicExampleItem[];
selectionDetails: string;
}
export const DetailsListBasicExampleFunction: React.FunctionComponent<
{} | IDetailsListBasicExampleState
> = () => {
const _allItems: IDetailsListBasicExampleItem[] = [];
const [selection, setSelection] = React.useState<Selection | undefined>();
function _getSelectionDetails(): string {
const selectionCount = selection ? selection.getSelectedCount() : 0;
switch (selectionCount) {
case 0:
return "No items selected";
case 1:
return (
"1 item selected: " +
(selection.getSelection()[0] as IDetailsListBasicExampleItem).name
);
default:
return `${selectionCount} items selected`;
}
}
const [state, setState] = React.useState({
items: _allItems,
selectionDetails: _getSelectionDetails()
});
React.useEffect(() => {
const _selection: Selection = new Selection({
onSelectionChanged: () =>
setState((prev) => {
return { ...prev, selectionDetails: _getSelectionDetails() };
})
});
setSelection(_selection);
for (let i = 0; i < 200; i++) {
_allItems.push({
key: i,
name: "Item " + i,
value: i
});
}
setState((prev) => {
return { ...prev, items: _allItems };
});
}, []);
const _columns: IColumn[] = [
{
key: "column1",
name: "Name",
fieldName: "name",
minWidth: 100,
maxWidth: 200,
isResizable: true
},
{
key: "column2",
name: "Value",
fieldName: "value",
minWidth: 100,
maxWidth: 200,
isResizable: true
}
];
// Populate with items for demos.
const _onFilter = (
ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
text: string
): void => {
console.log(text);
setState((prev) => {
return {
...prev,
items: text
? _allItems.filter((i) => i.name.toLowerCase().indexOf(text) > -1)
: _allItems
};
});
};
const _onItemInvoked = (item: IDetailsListBasicExampleItem): void => {
alert(`Item invoked: ${item.name}`);
};
return selection ? (
<Fabric>
<div className={exampleChildClass}>{state.selectionDetails}</div>
<Text>
Note: While focusing a row, pressing enter or double clicking will
execute onItemInvoked, which in this example will show an alert.
</Text>
<Announced message={state.selectionDetails} />
<TextField
className={exampleChildClass}
label="Filter by name:"
onChange={(e, t) => _onFilter(e, t ?? "")}
styles={textFieldStyles}
/>
<Announced
message={`Number of items after filter applied: ${state.items.length}.`}
/>
<MarqueeSelection selection={selection}>
<DetailsList
items={state.items}
columns={_columns}
setKey="set"
layoutMode={DetailsListLayoutMode.justified}
selection={selection}
selectionPreservedOnEmptyClick={true}
ariaLabelForSelectionColumn="Toggle selection"
ariaLabelForSelectAllCheckbox="Toggle selection for all items"
checkButtonAriaLabel="select row"
onItemInvoked={_onItemInvoked}
/>
</MarqueeSelection>
</Fabric>
) : (
<div>Loading</div>
);
};
UPDATE
To pass this sample of code in JSX this is pretty easy you just need to remove all type thing.
And to fetch data I use axios.
see the code below:
import * as React from "react";
import { Announced } from "office-ui-fabric-react/lib/Announced";
import { TextField } from "office-ui-fabric-react/lib/TextField";
import {
DetailsList,
DetailsListLayoutMode,
Selection
} from "office-ui-fabric-react/lib/DetailsList";
import { MarqueeSelection } from "office-ui-fabric-react/lib/MarqueeSelection";
import { Fabric } from "office-ui-fabric-react/lib/Fabric";
import { mergeStyles } from "office-ui-fabric-react/lib/Styling";
import { Text } from "office-ui-fabric-react/lib/Text";
import axios from "axios";
const exampleChildClass = mergeStyles({
display: "block",
marginBottom: "10px"
});
const textFieldStyles = {
root: { maxWidth: "300px" }
};
export const DetailsListBasicExampleFunction = () => {
const _allItems = [];
const [selection, setSelection] = React.useState();
function _getSelectionDetails() {
const selectionCount = selection ? selection.getSelectedCount() : 0;
switch (selectionCount) {
case 0:
return "No items selected";
case 1:
return "1 item selected: " + selection.getSelection()[0].name;
default:
return `${selectionCount} items selected`;
}
}
const [state, setState] = React.useState({
items: _allItems,
selectionDetails: _getSelectionDetails()
});
React.useEffect(() => {
const _selection = new Selection({
onSelectionChanged: () =>
setState((prev) => {
return { ...prev, selectionDetails: _getSelectionDetails() };
})
});
setSelection(_selection);
//********************** */fetch data from api***************************************
axios
.get("/data.json") //pass your url in param
.then((res) =>
setState((prev) => {
return { ...prev, items: res.data };
})
); //pass data in setState
}, []);
const _columns = [
{
key: "column1",
name: "Name",
fieldName: "name",
minWidth: 100,
maxWidth: 200,
isResizable: true
},
{
key: "column2",
name: "Value",
fieldName: "value",
minWidth: 100,
maxWidth: 200,
isResizable: true
}
];
// Populate with items for demos.
const _onFilter = (ev, text) => {
console.log(text);
setState((prev) => {
return {
...prev,
items: text
? _allItems.filter((i) => i.name.toLowerCase().indexOf(text) > -1)
: _allItems
};
});
};
const _onItemInvoked = (item) => {
alert(`Item invoked: ${item.name}`);
};
return selection ? (
<Fabric>
<div className={exampleChildClass}>{state.selectionDetails}</div>
<Text>
Note: While focusing a row, pressing enter or double clicking will
execute onItemInvoked, which in this example will show an alert.
</Text>
<Announced message={state.selectionDetails} />
<TextField
className={exampleChildClass}
label="Filter by name:"
onChange={(e, t) => _onFilter(e, t ?? "")}
styles={textFieldStyles}
/>
<Announced
message={`Number of items after filter applied: ${state.items.length}.`}
/>
<MarqueeSelection selection={selection}>
<DetailsList
items={state.items}
columns={_columns}
setKey="set"
layoutMode={DetailsListLayoutMode.justified}
selection={selection}
selectionPreservedOnEmptyClick={true}
ariaLabelForSelectionColumn="Toggle selection"
ariaLabelForSelectAllCheckbox="Toggle selection for all items"
checkButtonAriaLabel="select row"
onItemInvoked={_onItemInvoked}
/>
</MarqueeSelection>
</Fabric>
) : (
<div>Loading</div>
);
};
I've decided to refactor a project I've made with vanilla javascript and use typescript, I'm always in doubt about how to pass a function as a type on interface.
I took a look on the typescript documentation but I didn't understood how it works.
For instance, What type bringTransactions should have ? and the return type of the handleChange func ?
import React from 'react';
import moment from 'moment';
import { Select } from 'antd';
interface Props {
bringTransactions: any;
}
const PeriodPicker: React.FC<Props> = ({ bringTransactions }: Props) => {
const periodOptions = [
{ value: 0, label: 'Hoje' },
{ value: 3, label: 'Últimos 3 dias' },
{ value: 7, label: 'Últimos 7 dias' },
{ value: 15, label: 'Últimos 15 dias' },
{ value: 30, label: 'Últimos 30 dias' },
{ value: 5, label: 'Data específica' },
];
async function handleChange(value: number): Promise<void> {
const filter = [];
// setDataPickerStatus(false);
if (value === 5) {
// setDataPickerStatus(true);
} else {
const current = moment().format('YYYY-MM-DD HH:mm:ss');
const subtracted = moment(current).subtract(value, 'days');
const initialDate = moment(subtracted)
.utc()
.hours(0)
.minutes(0)
.seconds(0)
.milliseconds(0);
filter[0] = initialDate.format('YYYY-MM-DD HH:mm:ss');
const finatlDate = moment(current)
.utc()
.subtract(1, 'days')
.hours(23)
.minutes(59)
.seconds(59)
.milliseconds(0);
filter[1] = finatlDate.format('YYYY-MM-DD HH:mm:ss');
if (value === 0) {
const normalized = `&date=${filter[0]}`;
bringTransactions(normalized);
} else {
const normalized = `&period=${JSON.stringify(filter)}`;
bringTransactions(normalized);
}
}
}
return (
<Select
placeholder="Selecione um período"
onChange={handleChange}
style={{ width: 200 }}
>
{periodOptions.map(option => (
<Select.Option value={option.value} key={option.value}>
{option.label}
</Select.Option>
))}
</Select>
);
};
export default PeriodPicker;
It looks like bringTransactions is a function which takes a single string argument and returns nothing. That would be:
interface Props {
bringTransactions(value: string): void;
}
or you can write it with arrow syntax
interface Props {
bringTransactions: (value: string) => void;
}
Read more about functions in the typescript docs.
I usually write function types like this:
interface Props {
bringTransactions: (prop: propType) => ReturnType;
}
And the handleChange function will return void as events callbacks usually return void