I am using the #material-ui-searchbar to filter data. I'm using the same container for multiple different pages to display the info. When ever I switch between pages (and thus the data) the whole container reloads to display the new data and new {subType}... however, despite me changing the value of {searched} using setSearched("")(verified that it does in fact change from the debugger), the old search value from the previous page/state remains in the search bar even though the search is inactive. Why won't the search bar value update with the change of state? Doesn't make intuitive sense. Here is the code for my container:
import SearchBar from 'material-ui-search-bar';
const facilityGridConfig = {
hospitals: {
columns: [
{ name: 'name', title: 'Name' },
{ name: 'address1', title: 'Address' },
{ name: 'city', title: 'City' },
{ name: 'state', title: 'State' },
{ name: 'zipcode', title: 'Zip' },
{ name: 'phoneNumber', title: 'Phone' },
{ name: 'userTotal', title: 'Therapists', link: '/users/therapists' },
{ name: 'endUserTotal', title: 'Patients', link: '/users/patients' },
]
},
clinics: {
columns: [
{ name: 'name', title: 'Name' },
{ name: 'address1', title: 'Address' },
{ name: 'city', title: 'City' },
{ name: 'state', title: 'State' },
{ name: 'zipcode', title: 'Zip' },
{ name: 'phoneNumber', title: 'Phone' },
{ name: 'userTotal', title: 'Therapists', link: '/users/therapists' },
{ name: 'endUserTotal', title: 'Patients', link: '/users/patients' },
]
},
staff: {
columns: [
{ name: 'firstName', title: 'First Name' },
{ name: 'lastName', title: 'Last Name' },
{ name: 'facility', title: 'Facility' },
{ name: 'logins', title: 'Logins', width: 90 },
{ name: 'enrollmentStatus', title: 'Status', width: 90 },
{ name: 'email', title: 'Email' },
{ name: 'phoneNumber', title: 'Phone' }
]
},
therapists: {
columns: [
{ name: 'firstName', title: 'First Name' },
{ name: 'lastName', title: 'Last Name' },
{ name: 'facility', title: 'Facility' },
{ name: 'logins', title: 'Logins', width: 90 },
{ name: 'enrollmentStatus', title: 'Status', width: 90 },
{ name: 'email', title: 'Email' },
{ name: 'phoneNumber', title: 'Phone' },
{ name: 'endUserTotal', title: 'Patients' },
]
},
patients: {
columns: [
{ name: 'firstName', title: 'First Name' },
{ name: 'lastName', title: 'Last Name' },
{ name: 'facility', title: 'Facility' },
{ name: 'email', title: 'Email' },
{ name: "sessionsInPastWeek", title: "Sessions Last 7 Days"},
]
}
};
const facilityGridConfigSmall = {
hospitals: {
columns: [
{ name: 'name', title: 'Name' },
{ name: 'userTotal', title: 'Therapists', link: '/users/therapists' },
{ name: 'endUserTotal', title: 'Patients', link: '/users/patients' },
]
},
clinics: {
columns: [
{ name: 'name', title: 'Name' },
{ name: 'userTotal', title: 'Therapists', link: '/users/therapists' },
{ name: 'endUserTotal', title: 'Patients', link: '/users/patients' },
]
},
staff: {
columns: [
{ name: 'firstName', title: 'First Name' },
{ name: 'lastName', title: 'Last Name' },
{ name: 'logins', title: 'Logins' }
]
},
therapists: {
columns: [
{ name: 'firstName', title: 'First Name' },
{ name: 'lastName', title: 'Last Name' },
{ name: 'logins', title: 'Logins' },
{ name: 'endUserTotal', title: 'Patients' },
]
},
patients: {
columns: [
{ name: 'firstName', title: 'First Name' },
{ name: 'lastName', title: 'Last Name' },
{ name: "sessionsInPastWeek", title: "Sessions Last 7 Days"},
]
}
};
const useStyles = makeStyles((theme: Theme) => ({
root: {
position: 'relative',
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
height: '100%',
},
container: {
position: 'relative',
height: '100%',
},
breadcrumb: {
fontFamily: "Open Sans",
height: '28px',
},
content: {
position: 'relative',
height: 'calc(100% - 28px)',
},
gridHeaderEdit: {
textAlign: 'center!important' as any,
},
menuIcon: {
marginRight: theme.spacing(1)
},
}));
const FacilitiesGridContainer = (props: any) => {
const {
type,
subType,
uid,
} = props;
const mountedRef = useRef(true);
const classes = useStyles();
const dispatch = useDispatch();
const targetType = uid ? (
subType === Navigations.facilities.hospitals ? Navigations.facilities.clinics :
subType === Navigations.facilities.clinics ? Navigations.users.therapists : Navigations.users.patients
) : subType
const userProfile = useSelector(state => _get(state, ['user', 'profile']) || {}, shallowEqual);
const activeHospital = useSelector(state => _get(state, ['facilities', 'activeHospital']) || null, shallowEqual);
const activeClinic = useSelector(state => _get(state, ['facilities', 'activeClinic']) || null, shallowEqual);
const data = useSelector(state => _get(state, ['facilities', targetType]) || [], shallowEqual);
const loading = useSelector(state => _get(state, ['facilities', 'loading']) || false, shallowEqual);
const [fdata, setfData] = useState(data.data);
const [ openDeleteModal, setopenDeleteModal ] = useState(false);
const [ openInviteModal, setOpenInviteModal ] = useState(false);
const [ facilitiesInEdit, setFacilitiesInEdit ] = useState([] as any);
const [ sort, setSort ] = useState([] as any); //[{ field: 'name', dir: 'asc' }]
const [ visibleAddButton, setVisibleAddButton ] = useState(true);
const [ singleSubType, setSingleSubType ] = useState('');
const [ windowTitle, setWindowTitle ] = useState('');
const [ modalUserName, setModalUserName ] = useState('');
const [ visibleQRModal, setVisibleQRModal ] = useState(true);
const [ openQRModal, setOpenQRModal ] = useState(false);
const [ qrString, setQRString ] = useState('');
const [ qrFullName, setQRfullName ] = useState('');
const gridConfig = _get(facilityGridConfig, [targetType]) || {};
const gridConfigSmall = _get(facilityGridConfigSmall, [targetType]) || {};
const [searched, setSearched] = useState("");
const requestSearch = (searchedVal: string) => {
const filteredData = data.data.filter((row:any) => {
if (subType === "therapists" || subType === "patients" || subType === "staff"){
return (row['firstName'].toLowerCase().includes(searchedVal.toLowerCase()) || row['lastName'].toLowerCase().includes(searchedVal.toLowerCase()));
}
else {
return (row['name'].toLowerCase().includes(searchedVal.toLowerCase()));
}
});
setfData(filteredData);
};
const cancelSearch = () => {
setSearched("");
requestSearch(searched);
};
useEffect(() => {
setSearched("");
return () => {
mountedRef.current = false;
}
}, []);
useEffect(() => {setfData(data.data)}, [data.data] )
useEffect(() => {
setSearched("");
if (subType === Navigations.facilities.hospitals) {
if (uid) {
setSingleSubType('clinic');
} else {
setSingleSubType('hospital');
}
}
else if (subType === Navigations.facilities.clinics) {
if (uid) {
setSingleSubType('therapist');
} else {
setSingleSubType('clinic');
}
}
else if (subType === Navigations.users.therapists) {
if (uid) {
setSingleSubType('patient');
} else {
setSingleSubType('therapist');
}
}
else if (subType === Navigations.users.staff) {
setSingleSubType('staff');
}
else
setSingleSubType('patient');
return () => {
}
}, [subType, uid]);
useEffect(() => {
console.log('call to api here...')
dispatch({
type: FacilityActionType.GetFacilities,
payload: {
type: type,
subType: subType,
targetType: targetType,
facilityUid: targetType === 'therapists' ? (uid || null) : (_get(activeClinic, 'uid') || null),
parentFacilityUid: uid ? null : (_get(activeHospital, 'uid') || null),
parentUid: uid || null,
skip: 0,
take: 0
}
});
if (!userProfile)
setVisibleAddButton(false);
if (subType === 'hospitals') {
if ([UserRoles.SYSTEMADMIN].indexOf(userProfile.role) >= 0 && !!uid) {
setVisibleAddButton(true);
} else if ([UserRoles.SUPERUSER].indexOf(userProfile.role) < 0) {
setVisibleAddButton(false);
} else {
setVisibleAddButton(true);
}
}
else if (subType === 'clinics') {
if ([UserRoles.FACILITYADMIN].indexOf(userProfile.role) >= 0 && !!uid) {
setVisibleAddButton(true);
} else if ([UserRoles.SUPERUSER, UserRoles.SYSTEMADMIN, UserRoles.FACILITYADMIN].indexOf(userProfile.role) < 0) {
setVisibleAddButton(false);
} else {
setVisibleAddButton(true);
}
} else if (subType === 'therapists') {
if ([UserRoles.SUPERUSER, UserRoles.SYSTEMADMIN, UserRoles.FACILITYADMIN].indexOf(userProfile.role) < 0) {
setVisibleAddButton(false);
} else {
setVisibleAddButton(true);
}
} else if (subType === 'patients') {
if ([UserRoles.SUPERUSER, UserRoles.SYSTEMADMIN, UserRoles.FACILITYADMIN, UserRoles.USER].indexOf(userProfile.role) < 0) {
setVisibleAddButton(false);
} else {
setVisibleAddButton(true);
}
}
setWindowTitle("Augment Therapy: "+subType.charAt(0).toUpperCase() + subType.slice(1));
setSearched("");
return () => {
};
}, [userProfile, subType, uid, dispatch, type, targetType, activeClinic, activeHospital])
useEffect(() => {
setSearched("");
if (data) {
if (subType === Navigations.users.therapists) {
for (let rec of data.data) {
if (!rec['enrollmentStatus']) {
rec.enrollmentStatus = '--'
}
}
}
}
}, [subType,data]);
const setActiveFacility = (dataItem: any) => {
const payload: any = getActiveFacilityPayload(subType, uid);
if (dataItem.facilityType === FacilityType.Hospital) {
payload.activeHospital = dataItem;
} else if (dataItem.facilityType === FacilityType.Clinic) {
payload.activeClinic = dataItem;
} else if (subType === 'clinics' && !!uid) {
payload.activeTherapist = dataItem;
} else if (subType === 'staff' && !uid) {
payload.activeStaff = dataItem;
} else if (subType === 'therapists' && !uid) {
payload.activeTherapist = dataItem;
} else {
payload.activePatient = dataItem;
}
dispatch({type: FacilityActionType.SetActiveFacility, payload: payload});
}
const handleRowClick = (e: any) => {
setActiveFacility(e.dataItem);
if (e.dataItem) {
if (e.dataItem.facilityType === FacilityType.Hospital) {
navigate(`${Navigations.facilities.root}/${Navigations.facilities.hospitals}/${e.dataItem.uid}`, false);
} else if (e.dataItem.facilityType === FacilityType.Clinic) {
navigate(`${Navigations.facilities.root}/${Navigations.facilities.clinics}/${e.dataItem.uid}`, false);
} else if (subType === 'clinics') {
if (!uid) {
navigate(`${Navigations.facilities.root}/${Navigations.facilities.clinics}/${_get(e.dataItem, 'uid')}`, false);
} else { // it's therapist
navigate(`${Navigations.users.root}/${Navigations.users.therapists}/${_get(e.dataItem, 'uid')}`, false);
}
} else if (subType === 'therapists') {
if (!uid) {
navigate(`${Navigations.users.root}/${Navigations.users.therapists}/${_get(e.dataItem, 'uid')}`, false);
} else { // it's therapist
navigate(`${Navigations.users.root}/${Navigations.users.patients}/${_get(e.dataItem, 'uid')}`, false);
}
} else if (subType === 'patients') {
navigate(`${Navigations.users.root}/${Navigations.users.patients}/${_get(e.dataItem, 'uid')}`, false);
}
}
}
const handleLink = (dataItem: any, fieldName: string) => {
setActiveFacility(dataItem);
if (fieldName === 'userTotal') {
navigate(`${Navigations.users.root}/${Navigations.users.therapists}`, false);
} else if (fieldName === 'endUserTotal') {
navigate(`${Navigations.users.root}/${Navigations.users.patients}`, false);
}
}
const handleEdit = (dataItem: any) => {
let newType = Navigations.facilities.root;
if (targetType === Navigations.users.staff || targetType === Navigations.users.therapists || targetType === Navigations.users.patients) {
newType = Navigations.users.root;
}
if (dataItem) {
setActiveFacility(dataItem);
navigate(`${newType}/${targetType}/edit`, false, {uid: dataItem.uid});
} else {
navigate(`${newType}/${targetType}/new`);
}
}
const handleDelete = (dataItem: any) => {
setFacilitiesInEdit([...facilitiesInEdit, dataItem]);
setModalName(dataItem);
setopenDeleteModal(true);
}
const handleDeleteOk = () => {
dispatch({
type: FacilityActionType.DeleteFacilities,
payload: {
type: type,
subType: subType,
targetType: targetType,
body: facilitiesInEdit
}});
setopenDeleteModal(false);
}
const handleDeleteCancel = () => {
setFacilitiesInEdit([]);
setopenDeleteModal(false);
};
const handleInvite = (dataItem: any) => {
//setFacilitiesInEdit([...facilitiesInEdit, dataItem]);
setFacilitiesInEdit([dataItem]);
setModalName(dataItem);
setOpenInviteModal(true);
};
const handleInviteOk = () => {
dispatch({
type: FacilityActionType.PostUserSendInvite,
payload: {
body: facilitiesInEdit,
},
});
setOpenInviteModal(false);
};
const handleInviteCancel = () => {
setFacilitiesInEdit([]);
setOpenInviteModal(false);
};
const setModalName = (dataItem: any) => {
if (dataItem.firstName) {
setModalUserName(dataItem.firstName+' '+dataItem.lastName);
} else {
setModalUserName(dataItem.name);
}
return () => {
}
}
const closeQR = () => {
setOpenQRModal( false );
};
const handleQR = (dataItem: any) => {
setFacilitiesInEdit([...facilitiesInEdit, dataItem]);
setQRString(dataItem.qr);
if (dataItem.lastName) {
setQRfullName( dataItem.firstName + ' ' + dataItem.lastName.charAt(0) + '.' );
} else {
setQRfullName( dataItem.firstName );
}
setOpenQRModal(true);
};
return (
<Page
className={classes.root}
title={windowTitle}
>
<Container
maxWidth={false}
className={classes.container}
>
<Grid
container
direction='row'
style={{height: '100%'}}
>
<Grid
item
xs={12}
className={classes.breadcrumb}
>
<FacilitiesBreadCrumb
type={type}
subType={subType}
/>
</Grid>
<Grid
item
xs={12}
className={classes.content}
>
{
loading && (
<CircularIndeterminate/>
)
}
<Grid item xs={12} >
<StatsCards
type={subType}
uid={uid}
/>
</Grid>
<Hidden mdDown>
<DataGrid
style={{ height: '100%', overflow: 'auto', cursor:'pointer' }}
data={orderBy(fdata, sort)}
sortable={{
mode: 'multiple',
}}
sort={sort}
onSortChange={(e) => {
setSort(e.sort);
}}
onRowClick={(e: any) => handleRowClick(e)}
>
{
(visibleAddButton) && (
<GridToolbar>
<Grid container
style={{justifyContent: 'center', display: 'flex', alignItems: 'center', padding: 10}}>
<Grid item>
<SearchBar
value={searched}
onChange={(searchVal) => requestSearch(searchVal)}
onCancelSearch={() => cancelSearch()}
style={{
display: 'flex',
margin: '0 auto',
maxWidth: 800,
minWidth: 800,
paddingRight: 10
}}>
</SearchBar>
</Grid>
<Grid item>
<button
title="Add new"
className="k-button k-primary"
onClick={() => handleEdit(null)}
style={{
height: 50,
paddingLeft: 10
}}
>
Add New {singleSubType}
</button>
</Grid>
</Grid>
</GridToolbar>
)
}
{
(!(targetType==='hospitals' || targetType==='clinics')) && (
<GridColumn
title="Badge"
width="100"
headerClassName={classes.gridHeaderEdit}
cell={CellWithQR({userProfile: userProfile, type:"badge", handleAction: handleQR})}
/>
)
}
{
((targetType==='hospitals' || targetType==='clinics')) && (
<GridColumn
title=""
width="1"
headerClassName={classes.gridHeaderEdit}
cell={CellWithEdit({userProfile: userProfile, type:"empty", handleAction: handleInvite})}
/>
)
}
{
gridConfig.columns.map((c: any, index: number) => {
return (
<GridColumn key={`grid-${index}`}
field={c.name}
title={c.title}
cell={c.link ? CellWithLink({handleLink}) : undefined}
width= {c.width ? c.width : undefined}
/>
)
})
}
<GridColumn
title="Actions"
width="100"
headerClassName={classes.gridHeaderEdit}
cell={CellWithComboMenu({userProfile: userProfile, type:"edit", handleActionInvite: handleInvite, handleActionEdit: handleEdit, handleActionDelete: handleDelete})}
/>
</DataGrid>
</Hidden>
<Hidden lgUp>
<DataGrid
style={{ height: '100%', overflow: 'auto', cursor:'pointer' }}
data={orderBy(fdata, sort)}
sortable={{
mode: 'multiple',
}}
sort={sort}
onSortChange={(e) => {
setSort(e.sort);
}}
onRowClick={(e: any) => handleRowClick(e)}
>
{
(visibleAddButton) && (
<GridToolbar>
<Grid container
style={{justifyContent: 'center', display: 'flex', alignItems: 'center', padding: 10}}>
<Grid item>
<SearchBar
value={searched}
onChange={(searchVal) => requestSearch(searchVal)}
onCancelSearch={() => cancelSearch()}
style={{
display: 'flex',
margin: '0 auto',
maxWidth: 800,
paddingRight: 10
}}>
</SearchBar>
</Grid>
<Grid item>
<button
title="Add new"
className="k-button k-primary"
onClick={() => handleEdit(null)}
style={{
height: 50,
paddingLeft: 10
}}
>
Add New {singleSubType}
</button>
</Grid>
</Grid>
</GridToolbar>
)
}
{
(!(targetType==='hospitals' || targetType==='clinics')) && (
<GridColumn
title="Badge"
width="20"
headerClassName={classes.gridHeaderEdit}
cell={CellWithQR({userProfile: userProfile, type:"badge", handleAction: handleQR})}
/>
)
}
{
((targetType==='hospitals' || targetType==='clinics')) && (
<GridColumn
title=""
width="10"
headerClassName={classes.gridHeaderEdit}
cell={CellWithEdit({userProfile: userProfile, type:"empty", handleAction: handleInvite})}
/>
)
}
{
gridConfigSmall.columns.map((c: any, index: number) => {
return (
<GridColumn key={`grid-${index}`}
field={c.name}
title={c.title}
cell={c.link ? CellWithLink({handleLink}) : undefined}
minResizableWidth = {c.name.toLowerCase().includes("name")? 20 : 10}
/>
)
})
}
<GridColumn
title="Actions"
width="100"
headerClassName={classes.gridHeaderEdit}
cell={CellWithComboMenu({userProfile: userProfile, type:"edit", handleActionInvite: handleInvite, handleActionEdit: handleEdit, handleActionDelete: handleDelete})}
/>
</DataGrid>
</Hidden>
<ConfirmModal
title="Confirm Deletion"
content={`Are you sure you want to delete the ${singleSubType} ( ${modalUserName} ) ?`}
open={openDeleteModal}
handleClose={handleDeleteCancel}
handleOk={handleDeleteOk}
handleCancel={handleDeleteCancel}
/>
<ConfirmModal
title="Confirm Invite"
content={`Send ${modalUserName} an invitation to connect?`}
open={openInviteModal}
handleClose={handleInviteCancel}
handleOk={handleInviteOk}
handleCancel={handleInviteCancel}
/>
{
(openQRModal) && (
<UserQRCode
id="qrCode"
qrCodeString={qrString}
size={256}
userfullname={qrFullName}
open={true}
handleClose={closeQR}
/>
)
}
</Grid>
</Grid>
</Container>
</Page>
);
}
export default FacilitiesGridContainer;```
Well, I'm new to reactjs, but I suggest to call setSearched in a useEffect hook if your filteredData get changed. So it'll be something like this :
useEffect(() => {
setSearched("");
}, [filteredData])
..despite me changing the value of {searched} using setSearched("")..
this because you call setSearched("") on compoenent mount using (useEffect) but you need this to be called every time your filteredData get updated
Related
I'm implementing a MUI datagrid with inline editing. Whenever a cell loses focus, the code sets the mode to 'view' (as it should) but then immediately the processRowUpdate callback is called (to send data to the endpoint) it sets the cell mode back to 'edit' meaning that the cell remains in edit mode.
Does anyone know why this is happening?
Maybe something to do with this processRowUpdate error logged to console?:
TypeError: Cannot read properties of undefined (reading 'id')
at getRowId (productTable.js:213:1)
at getRowIdFromRowModel (gridRowsUtils.js:17:1)
at useGridRows.js:106:1
at Array.forEach (<anonymous>)
at Object.updateRows (useGridRows.js:105:1)
at apiRef.current.<computed> [as updateRows] (useGridApiMethod.js:14:1)
at useGridCellEditing.new.js:334:1
Code:
export default function FullFeaturedCrudGrid(props) {
const [rows, setRows] = React.useState([]);
const [cellModesModel, setCellModesModel] = React.useState({})
const [selectedCellParams, setSelectedCellParams] = React.useState(null);
const { tableName } = props
const [snackbar, setSnackbar] = React.useState(null);
const handleCloseSnackbar = () => setSnackbar(null);
React.useEffect(() => {
console.log('useEffect called')
axios.get(`http://localhost:8000/mvhr/all`)
.then((response) => {
setRows(response.data);
})
}, [])
React.useEffect(() => {
console.log('cellModesModel',cellModesModel)
});
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 handleDeleteClick = (id) => () => {
axios.delete(`http://localhost:8000/delete_mvhr/${id}`
).then(() => {
setRows(rows.filter((row) => row.id !== id));
setSnackbar({ children: tableName + ' successfully deleted', severity: 'success' });
})
};
const handleCancel = () => {
if (!selectedCellParams) {
return;
}
const { id, field } = selectedCellParams;
setCellModesModel({
...cellModesModel,
[id]: {
...cellModesModel[id],
[field]: { mode: GridCellModes.View },
},
});
};
const processRowUpdate = React.useCallback(
(newRow) => {
axios.put(`http://localhost:8000/mvhr/`, newRow)
.then((response) => {
const updatedRow = { ...newRow, isNew: false };
setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row)));
setSnackbar({ children: tableName + ' successfully saved', severity: 'success' });
return updatedRow
})
});
const handleProcessRowUpdateError = React.useCallback((error) => {
setSnackbar({ children: error.message, severity: 'error' });
}, []);
const columns = [
{ field: 'description', headerName: 'description', width: 180, editable: true },
{ field: 'elec_efficiency', headerName: 'elec_efficiency', type: 'number', editable: true },
{ field: 'heat_recovery_eff', headerName: 'heat_recovery_eff', type: 'number', editable: true },
{ field: 'range_low', headerName: 'range_low', type: 'number', editable: true },
{ field: 'range_high', headerName: 'range_high', type: 'number', editable: true },
{ field: 'superseded', headerName: 'superseded', type: 'boolean', editable: true },
{
field: 'actions',
type: 'actions',
headerName: 'Actions',
width: 100,
cellClassName: 'actions',
getActions: ({ id }) => {
return [
<GridActionsCellItem
icon={<DeleteIcon />}
label="Delete"
onClick={handleDeleteClick(id)}
color="inherit"
/>,
];
},
},
];
return (
<Box
sx={{
height: '100vh',
width: '100%',
'& .actions': {
color: 'text.secondary',
},
'& .textPrimary': {
color: 'text.primary',
},
}}
>
<StripedDataGrid
rows={rows}
columns={columns}
processRowUpdate={processRowUpdate}
onProcessRowUpdateError={handleProcessRowUpdateError}
onCellEditStop={handleCancel}
cellModesModel={cellModesModel}
onCellModesModelChange={(model) => setCellModesModel(model)}
components={{
Toolbar: AddToolbar,
}}
componentsProps={{
toolbar: { setRows, setSnackbar, tableName },
cell: {
onFocus: handleCellFocus,
},
}}
experimentalFeatures={{ newEditingApi: true }}
getRowClassName={(params) =>
params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd'
}
/>
{!!snackbar && (
<Snackbar
open
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
onClose={handleCloseSnackbar}
autoHideDuration={6000}
>
<Alert {...snackbar} onClose={handleCloseSnackbar} />
</Snackbar>
)}
</Box>
);
}
Solved it. I refactored the code to return the updatedRow synchronously rather than as a callback to the axios request:
const processRowUpdate = React.useCallback(
(newRow) => {
const updatedRow = { ...newRow, isNew: false };
setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row)));
setSnackbar({ children: tableName + ' successfully saved', severity: 'success' });
axios.put(`http://localhost:8000/mvhr/`, newRow)
return updatedRow
});
I'm new to frontend development with React/Redux/Typescript. I'm trying to make the components work. I want to use DataGrid from #mui/material/styles. https://mui.com/x/react-data-grid/.
I have tried searching online but I cannot figure out why I have this error.
My code:
import {
FilteringState,
IntegratedFiltering,
IntegratedPaging,
IntegratedSorting,
PagingState,
Row,
RowDetailState,
SearchState,
SelectionState,
SortingState,
} from "#devexpress/dx-react-grid";
import {
ColumnChooser,
Grid,
PagingPanel,
SearchPanel,
Table,
TableColumnResizing,
TableColumnVisibility,
TableFilterRow,
TableHeaderRow,
Toolbar,
} from "#devexpress/dx-react-grid-material-ui";
import { Card } from "#mui/material";
import { JiraDefect } from "api/classes/JiraDefect";
import { ReactText, useMemo, useState } from "react";
export default function RSADefectsTable(props: {
defects: JiraDefect[];
epicNames: { [key: string]: string };
fixVersions: string[];
}) {
const { defects } = props;
const [selection, setSelection] = useState<ReactText[]>([]);
const [search, setSearch] = useState<string>();
const [defect, setDefect] = useState<string>();
const [open, setOpen] = useState(false);
useMemo(() => {
if (defect) setOpen(true);
}, [defect]);
const [defaultColumnWidths] = useState([
{ columnName: "key", width: 125 },
{ columnName: "created", width: 125 },
{ columnName: "name", width: 500 },
{ columnName: "priority", width: 100 },
{ columnName: "severity", width: 100 },
{ columnName: "status", width: 200 },
{ columnName: "fixVersion", width: 125 },
{ columnName: "components", width: 125 },
{ columnName: "epicLink", width: 175 },
]);
const columns = [
{ name: "key", title: "Key" },
{ name: "created", title: "Created" },
{ name: "name", title: "Headline" },
{ name: "priority", title: "Priority" },
{ name: "severity", title: "Severity" },
{ name: "fixVersion", title: "Fix Version" },
{ name: "status", title: "Status" },
{ name: "components", title: "Component" },
{ name: "epicLink", title: "Epic Name" },
];
const cellValue = (row: JiraDefect, column: string) => {
if (column === "status") return (row[column as keyof JiraDefect] as string).replace(/_/g, " ");
if (column === "priority") return (row[column as keyof JiraDefect] as string).replace(/..- /g, "");
if (column === "created") return row.created.toISOString().slice(0, 10);
if (column === "epicLink" && row.epicLink) return props.epicNames[row.epicLink];
return row[column as keyof JiraDefect];
};
const TableRow = ({ row, ...restProps }: { row: Row }) => (
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
// #ts-ignore
<Table.Row
{...restProps}
onClick={() => {
setDefect(row["key"]);
window.open("https://jira.tooling.intactfc.cloud/browse/" + row["key"]);
}}
style={{ cursor: "pointer" }}
/>
);
const filteredDefects = defects.filter((val) => props.fixVersions.includes(val.fixVersion[0]));
return (
<>
<Card style={{ borderRadius: 10 }}>
<Grid columns={columns} rows={filteredDefects} getCellValue={cellValue}>
<PagingState defaultCurrentPage={0} pageSize={20} />
<SortingState defaultSorting={[{ columnName: "key", direction: "desc" }]} columnSortingEnabled />
<IntegratedSorting />
<SearchState onValueChange={setSearch} value={search} />
<FilteringState />
<RowDetailState />
<SelectionState selection={selection} onSelectionChange={setSelection} />
<IntegratedFiltering />
<IntegratedPaging />
<Toolbar />
<SearchPanel />
<Table rowComponent={TableRow} />
<TableColumnResizing defaultColumnWidths={defaultColumnWidths} />
<TableHeaderRow showSortingControls />
<TableColumnVisibility />
<ColumnChooser />
<TableFilterRow />
<PagingPanel />
</Grid>
</Card>
</>
);
}
Error:
[![enter image description here][1]][1]
Any help will be appreciated.
We have One parents componet Template in That i m using one detailsCellRendering componet for cellRedering in ag-grid..in the detailsCellRendering componet I am updating TextValue and after clicking on handleDoneValue event (done button ) the Template page should also refresh.But I am not able to do ...i need help like how can i update Template page while updating in DetailsCellRending Component.
class Template extends Component {
constructor(props) {
super(props);
this.state = {
columnDefs: [
{
headerName: "Channel Field",
field: "field",
filter: "agTextColumnFilter",
filterParams: {
buttons: ["reset"],
debounceMs: 1000,
suppressAndOrCondition: true, // it will remove AND/OR conditions
filterOptions: [
"contains",
"notContains",
"equals",
"notEqual",
"startsWith",
"endsWith"
]
},
cellRendererParams: {
prop1: "this is prop1"
},
cellRenderer: "agGroupCellRenderer",
},
{
headerName: "Mapping from Your Data",
field: "mapping_field",
filter: "agNumberColumnFilter",
},
{
headerName: skuValue,
field: "value",
},
{
headerName: "Status",
field: "status",
filter: 'agSetColumnFilter',
filterParams: {
values: function (params) {
// simulating async delay
setTimeout(function () {
params.success(["True", "False"]);
}, 500);
}
},
// cellRenderer: "ColourRender",
cellStyle: params => {
if (params.value === "Mapped") {
//mark mapped cells as green
return { color: 'green' };
} else {
return { color: 'red' };
}
}
},
{
headerName: "Required",
field: "required",
filter: "agSetColumnFilter",
cellRenderer: "BooleanRender",
filterParams: {
values: function (params) {
// simulating async delay
setTimeout(function () {
params.success(["True", "False"]);
}, 500);
}
}
},
],
columnDefsforSku: [
{
headerName: "Channel Field",
field: "field",
filter: "agTextColumnFilter",
filterParams: {
buttons: ["reset"],
debounceMs: 1000,
suppressAndOrCondition: true, // it will remove AND/OR conditions
filterOptions: [
"contains",
"notContains",
"equals",
"notEqual",
"startsWith",
"endsWith",
// "In List",
// "Not In List"
]
},
cellRenderer: "agGroupCellRenderer",
},
{
headerName: "Mapping from Your Data",
field: "mapping_field",
filter: "agNumberColumnFilter",
},
{
headerName: "Status",
field: "status",
filter: 'agSetColumnFilter',
filterParams: {
values: function (params) {
// simulating async delay
setTimeout(function () {
params.success(["True", "False"]);
}, 500);
}
},
cellStyle: params => {
if (params.value === "Mapped") {
//mark mapped cells as green
return { color: 'green' };
} else {
return { color: 'red' };
}
}
},
{
headerName: "Required",
field: "required",
filter: "agSetColumnFilter",
cellRenderer: "BooleanRender",
filterParams: {
values: function (params) {
// simulating async delay
setTimeout(function () {
params.success(["True", "False"]);
}, 500);
}
}
},
],
components: {
DetailCellRenderer: {DetailCellRenderer},
},
defaultColDef: {
flex: 1,
resizable: true,
// tooltipComponent: "customTooltip",
floatingFilter: true,
minWidth: 170,
sortable: true,
},
rowModelType: "serverSide",
frameworkComponents: {
customNoRowsOverlay: CustomNoRowsOverlay,
loadingRender: loadingRender,
myDetailCellRenderer: DetailCellRenderer,
customTooltip: CustomTooltip,
BooleanRender: BooleanRender,
// ColourRender: ColourRender,
},
context: { componentParent: this },
pagination: true,
serverSideStoreType: "full",
rowSelection: "multiple",
loadingCellRenderer: "loadingRender",
loadingCellRendererParams: { loadingMessage: "One moment please..." },
loadingOverlayComponent: "loadingRender",
totalProductsCount: 0,
catalogfilterCount: 0,
noRowsOverlayComponent: "customNoRowsOverlay",
noRowsOverlayComponentParams: {
noRowsMessageFunc: function () {
return "Please Select Product Preview"
}
},
getDetailRowData: function (params) {
// params.successCallback([]);
params.successCallback(params.data.callRecords);
},
sideBar: 'filters',
tooltipShowDelay: 0,
paginationPageSize: 100,
cacheBlockSize: 30,
filterStatus: false,
sweetAlert: "",
allSKu: [],
sku: localStorage.getItem("skuPreviewProduct"),
isProductPrevewSelected: false,
skuInputValue: "",
loading: false,
product_id: localStorage.getItem("products_ids") ? localStorage.getItem("products_ids") : null,
// loadingForDetails: localStorage.getItem("loadings")
};
<Grid item xs={10}>
<div
id="myGrid"
style={{
height: "90%",
width: "90%",
}}
className="ag-theme-alpine template"
>
<AgGridReact
columnDefs={this.state.product_id ? columnDefs : columnDefsforSku}
defaultColDef={defaultColDef}
onGridReady={this.onGridReady}
rowModelType={rowModelType}
masterDetail={true}
enableCellChangeFlash={true}
masterDetail={true}
detailCellRenderer={'myDetailCellRenderer'}
detailRowHeight={350}
height={400}
width={600}
animateRows={true}
frameworkComponents={frameworkComponents}
embedFullWidthRows={true}
loadingOverlayComponent={loadingOverlayComponent}
loadingCellRenderer={loadingCellRenderer} // default loading renderer
loadingCellRendererParams={loadingCellRendererParams} // renderer Params to load msg
noRowsOverlayComponent={noRowsOverlayComponent} // default no rows overlay component
noRowsOverlayComponentParams={noRowsOverlayComponentParams} // to show default no rows message
paginationPageSize={this.state.paginationPageSize} // pagination page size
onColumnVisible={this.onColumnVisible.bind(this)}
getDetailRowData={getDetailRowData}
cacheBlockSize={cacheBlockSize}
detailCellRendererParams={detailCellRendererParams }
/>
</div>
</Grid>
</Grid>
</div>
)
1. **List item**
}
// DetailsCellRendering page
import React, { useEffect, useState } from 'react';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import { makeStyles } from '#material-ui/core/styles';
import TemplateTab from "../views/FunctionalComponents/TemplateTab"
import { TEMPLATE_MARKETPLACE_FEILD_MAPPING, TEMPLATE_API } from "../configurations/configApi";
import { apiEdit, fetchUrl } from "../apiActions/action";
import LoadingOverlay from "../components/overlays/LoadingOverlay";
import Templates from 'views/Tables/Templates';
// import Template from 'views/Tables/Templates';
const useStyles = makeStyles(() => ({
root: {
flexGrow: 1,
textAlign: "center",
marginBottom: 20,
}
}));
const DetailCellRenderer = ({ data, params, api, node}) => {
const classes = useStyles();
const [allData, setAllData] = useState();
const [loading, setLoading] = useState(false);
const marketplaceId = localStorage.getItem("marketplaceId") ? localStorage.getItem("marketplaceId") : "" ;
const skuValue = localStorage.getItem("skuPreviewProduct") ? localStorage.getItem("skuPreviewProduct") : "";
const fetchAllTemplate = () => {
setLoading(true);
var soniServe = `${TEMPLATE_API}${marketplaceId}/?sku=${skuValue}&filter=${"[]"}`;
fetchUrl(soniServe, ({ status, response }) => {
if (status) {
let refreshValue = response.data.response[0].mappings.filter(item => data.id === item.id);
localStorage.setItem("cellRenderId", refreshValue && refreshValue[0]&& refreshValue[0].id )
setAllData(refreshValue && refreshValue[0])
setLoading(false)
} else {
if (response.response &&
response.response.status &&
((response.response.status === 401))
) {
localStorage.removeItem("token");
localStorage.removeItem("email");
window.location = "/auth/login-page";
}
}
});
}
useEffect(() => {
setAllData(data)
}, data)
// update text value
const handleDoneValue = (textValue) => {
const productId = allData && allData.id;
let value = textValue
// updating the existing labels
const urlForSave = `${TEMPLATE_MARKETPLACE_FEILD_MAPPING}${productId}/`;
const methodOfSave = "put";
const data = { "mappings": { "text_value": value } }
apiEdit(urlForSave, data, methodOfSave, ({ status, response }) => {
if (status) {
// window.location = `/configuration/details/${marketplaceId}`
fetchAllTemplate();
// inputMethod === "saveAs" && this.fetchProductDisplay("saveAs");
} else {
if (response.response &&
response.response.status &&
((response.response.status === 401))
) {
localStorage.removeItem("token");
localStorage.removeItem("email");
window.location = "/auth/login-page";
} else if (
(response &&
response.status &&
(response.status === 404 ||
response.status === 500)) ||
!response
) {
window.location = "/error";
} else {
// to show some popups
window.location = "/error";
}
}
});
}
return (
<div>
<div className={classes.root}>
<Grid container style={{ paddingTop: "10px" }}>
<Grid item xs={3}>
{allData && allData.field ? allData.field : null}
</Grid>
<Grid item xs={3}>
{ allData && allData.mapping_field ? allData.mapping_field : null}
</Grid>
<Grid item xs={3}>
{urlCheck && urlCheck[0] === "https" || urlCheck && urlCheck[0] === "http"? {allData && allData.value} : allData && allData.value ? allData.value : null}
{/* { data && data.value ? data.value : null} */}
</Grid>
<Grid item xs={3} >
{allData && allData.status}
</Grid>
</Grid>
</div>
<div className={classes.root}>
<Grid container spacing={1}>
<Grid item xs={4} justify="flex-end" style={{ display: "flex" }}>
Description
</Grid>
<Grid item xs={8} >
<Paper className="templateBox">
<span style={{ marginBottom: "20px" }}>What value should we send for each product's "{data.field}"? </span>
<TemplateTab allCellData={data} handleDoneValue={handleDoneValue}
/>
</Paper>
</Grid>
</Grid>
</div>
<LoadingOverlay showOverlay={loading} />
</div>
);
};
export default DetailCellRenderer;
Based on ANTD's Table example: https://ant.design/components/table/#components-table-demo-edit-cell, I would like to replicate this, with the addition of having the ability to add/delete new columns. The sample from the link above only illustrates how to add new rows.
Here's the code from the sample:
import { Table, Input, Button, Popconfirm, Form } from 'antd';
const FormItem = Form.Item;
const EditableContext = React.createContext();
const EditableRow = ({ form, index, ...props }) => (
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
);
const EditableFormRow = Form.create()(EditableRow);
class EditableCell extends React.Component {
state = {
editing: false,
}
componentDidMount() {
if (this.props.editable) {
document.addEventListener('click', this.handleClickOutside, true);
}
}
componentWillUnmount() {
if (this.props.editable) {
document.removeEventListener('click', this.handleClickOutside, true);
}
}
toggleEdit = () => {
const editing = !this.state.editing;
this.setState({ editing }, () => {
if (editing) {
this.input.focus();
}
});
}
handleClickOutside = (e) => {
const { editing } = this.state;
if (editing && this.cell !== e.target && !this.cell.contains(e.target)) {
this.save();
}
}
save = () => {
const { record, handleSave } = this.props;
this.form.validateFields((error, values) => {
if (error) {
return;
}
this.toggleEdit();
handleSave({ ...record, ...values });
});
}
render() {
const { editing } = this.state;
const {
editable,
dataIndex,
title,
record,
index,
handleSave,
...restProps
} = this.props;
return (
<td ref={node => (this.cell = node)} {...restProps}>
{editable ? (
<EditableContext.Consumer>
{(form) => {
this.form = form;
return (
editing ? (
<FormItem style={{ margin: 0 }}>
{form.getFieldDecorator(dataIndex, {
rules: [{
required: true,
message: `${title} is required.`,
}],
initialValue: record[dataIndex],
})(
<Input
ref={node => (this.input = node)}
onPressEnter={this.save}
/>
)}
</FormItem>
) : (
<div
className="editable-cell-value-wrap"
style={{ paddingRight: 24 }}
onClick={this.toggleEdit}
>
{restProps.children}
</div>
)
);
}}
</EditableContext.Consumer>
) : restProps.children}
</td>
);
}
}
class EditableTable extends React.Component {
constructor(props) {
super(props);
this.columns = [{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
}, {
title: 'age',
dataIndex: 'age',
}, {
title: 'address',
dataIndex: 'address',
}, {
title: 'operation',
dataIndex: 'operation',
render: (text, record) => (
this.state.dataSource.length >= 1
? (
<Popconfirm title="Sure to delete?" onConfirm={() => this.handleDelete(record.key)}>
Delete
</Popconfirm>
) : null
),
}];
this.state = {
dataSource: [{
key: '0',
name: 'Edward King 0',
age: '32',
address: 'London, Park Lane no. 0',
}, {
key: '1',
name: 'Edward King 1',
age: '32',
address: 'London, Park Lane no. 1',
}],
count: 2,
};
}
handleDelete = (key) => {
const dataSource = [...this.state.dataSource];
this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
}
handleAdd = () => {
const { count, dataSource } = this.state;
const newData = {
key: count,
name: `Edward King ${count}`,
age: 32,
address: `London, Park Lane no. ${count}`,
};
this.setState({
dataSource: [...dataSource, newData],
count: count + 1,
});
}
handleSave = (row) => {
const newData = [...this.state.dataSource];
const index = newData.findIndex(item => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
this.setState({ dataSource: newData });
}
render() {
const { dataSource } = this.state;
const components = {
body: {
row: EditableFormRow,
cell: EditableCell,
},
};
const columns = this.columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave: this.handleSave,
}),
};
});
return (
<div>
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
Add a row
</Button>
<Table
components={components}
rowClassName={() => 'editable-row'}
bordered
dataSource={dataSource}
columns={columns}
/>
</div>
);
}
}
ReactDOM.render(<EditableTable />, mountNode);
You can make your columns array a part of the state and update it through setState.
Here is a working codepen: https://codepen.io/gges5110/pen/GLPjYr?editors=0010
// State
this.state = {
dataSource: [{
key: '0',
name: 'Edward King 0',
age: '32',
address: 'London, Park Lane no. 0',
}, {
key: '1',
name: 'Edward King 1',
age: '32',
address: 'London, Park Lane no. 1',
}],
columns: [{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
}, {
title: 'age',
dataIndex: 'age',
}]
};
// Event to add new column
handleAddColumn = () => {
const { columns } = this.state;
const newColumn = {
title: 'age',
dataIndex: 'age',
};
this.setState({
columns: [...columns, newColumn]
});
}
// Render method
render() {
const { dataSource, columns } = this.state;
return (
<Table
dataSource={dataSource}
columns={columns}
/>
);
}
Looking forward for any hint how to style new material ui v1 autocomplete or how to pass props to it.
Here's a codesandbox code (working example):
https://codesandbox.io/s/xrzq940854
In my particular case - I would like to style the label (which goes up after entering some value into input) and that horizontal line (underline under the input value).
Thank u for any help. (dropping code also in the snippet)
P.S. I got also a question how to pass props to the styles function. If anyone knows, please let me know :)
import React from 'react';
import PropTypes from 'prop-types';
import Autosuggest from 'react-autosuggest';
import match from 'autosuggest-highlight/match';
import parse from 'autosuggest-highlight/parse';
import TextField from 'material-ui/TextField';
import Paper from 'material-ui/Paper';
import { MenuItem } from 'material-ui/Menu';
import { withStyles } from 'material-ui/styles';
const suggestions = [
{ label: 'Afghanistan' },
{ label: 'Aland Islands' },
{ label: 'Albania' },
{ label: 'Algeria' },
{ label: 'American Samoa' },
{ label: 'Andorra' },
{ label: 'Angola' },
{ label: 'Anguilla' },
{ label: 'Antarctica' },
{ label: 'Antigua and Barbuda' },
{ label: 'Argentina' },
{ label: 'Armenia' },
{ label: 'Aruba' },
{ label: 'Australia' },
{ label: 'Austria' },
{ label: 'Azerbaijan' },
{ label: 'Bahamas' },
{ label: 'Bahrain' },
{ label: 'Bangladesh' },
{ label: 'Barbados' },
{ label: 'Belarus' },
{ label: 'Belgium' },
{ label: 'Belize' },
{ label: 'Benin' },
{ label: 'Bermuda' },
{ label: 'Bhutan' },
{ label: 'Bolivia, Plurinational State of' },
{ label: 'Bonaire, Sint Eustatius and Saba' },
{ label: 'Bosnia and Herzegovina' },
{ label: 'Botswana' },
{ label: 'Bouvet Island' },
{ label: 'Brazil' },
{ label: 'British Indian Ocean Territory' },
{ label: 'Brunei Darussalam' },
];
function renderInput(inputProps) {
const { classes, autoFocus, value, ref, ...other } = inputProps;
return (
<TextField
autoFocus={autoFocus}
className={classes.textField}
value={value}
inputRef={ref}
label="Country"
InputProps={{
classes: {
input: classes.input,
},
...other,
}}
/>
);
}
function renderSuggestion(suggestion, { query, isHighlighted }) {
const matches = match(suggestion.label, query);
const parts = parse(suggestion.label, matches);
return (
<MenuItem selected={isHighlighted} component="div">
<div>
{parts.map((part, index) => {
return part.highlight ? (
<span key={String(index)} style={{ fontWeight: 300 }}>
{part.text}
</span>
) : (
<strong key={String(index)} style={{ fontWeight: 500 }}>
{part.text}
</strong>
);
})}
</div>
</MenuItem>
);
}
function renderSuggestionsContainer(options) {
const { containerProps, children } = options;
return (
<Paper {...containerProps} square>
{children}
</Paper>
);
}
function getSuggestionValue(suggestion) {
return suggestion.label;
}
function getSuggestions(value) {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
let count = 0;
return inputLength === 0
? []
: suggestions.filter(suggestion => {
const keep =
count < 5 && suggestion.label.toLowerCase().slice(0, inputLength) === inputValue;
if (keep) {
count += 1;
}
return keep;
});
}
const styles = theme => ({
container: {
flexGrow: 1,
position: 'relative',
height: 200,
},
suggestionsContainerOpen: {
position: 'absolute',
marginTop: theme.spacing.unit,
marginBottom: theme.spacing.unit * 3,
left: 0,
right: 0,
},
suggestion: {
display: 'block',
},
suggestionsList: {
margin: 0,
padding: 0,
listStyleType: 'none',
},
textField: {
width: '100%',
},
label: {
color: 'yellow',
}
});
class IntegrationAutosuggest extends React.Component {
state = {
value: '',
suggestions: [],
};
handleSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value),
});
};
handleSuggestionsClearRequested = () => {
this.setState({
suggestions: [],
});
};
handleChange = (event, { newValue }) => {
this.setState({
value: newValue,
});
};
render() {
const { classes } = this.props;
return (
<Autosuggest
theme={{
container: classes.container,
suggestionsContainerOpen: classes.suggestionsContainerOpen,
suggestionsList: classes.suggestionsList,
suggestion: classes.suggestion,
}}
renderInputComponent={renderInput}
suggestions={this.state.suggestions}
onSuggestionsFetchRequested={this.handleSuggestionsFetchRequested}
onSuggestionsClearRequested={this.handleSuggestionsClearRequested}
renderSuggestionsContainer={renderSuggestionsContainer}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={{
autoFocus: true,
classes,
placeholder: 'Search a country (start with a)',
value: this.state.value,
onChange: this.handleChange,
}}
/>
);
}
}
IntegrationAutosuggest.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(IntegrationAutosuggest);
Material-UI v1 uses React-autosuggest module.
Check the below link
https://github.com/moroshko/react-autosuggest/blob/master/src/Autosuggest.js