I am currently using Material ui v1.4.3 autocomplete. Material UI stated that this autocomplete is integrated with react-select.
I have followed the code here which is working like a charm but in order to handle fetching larger data in the future, I would like to implement the search function to call the database whenever the input changes so that I am able to narrow down the data that is being fetched from the database.
Has anyone had experience on this? Because the static method from this code is blocking me to call any reducer function that is passed from my parent component.
What would be an appropriate way that allows me to catch the input from the user so that I am able to call my function.
function NoOptionsMessage(props) {
return (
<Typography
color="textSecondary"
className={props.selectProps.classes.noOptionsMessage}
{...props.innerProps}
>
{props.children}
</Typography>
);
}
function inputComponent({ inputRef, ...props }) {
return <div ref={inputRef} {...props} />;
}
function Control(props) {
////console.dir(props.selectProps.inputValue); i am able to get the user input here
// so i was thinking i can call my reducer somewhere here but no luck
// my function is passed from my parent component so i wish to call this.props.theFunction here
return (
<TextField
fullWidth
InputProps={{
inputComponent,
inputProps: {
className: props.selectProps.classes.input,
ref: props.innerRef,
children: props.children,
...props.innerProps,
},
}}
onChange={(e) => IntegrationReactSelect.testing(e)}
/>
);
}
function Option(props) {
return (
<MenuItem
buttonRef={props.innerRef}
selected={props.isFocused}
component="div"
style={{
fontWeight: props.isSelected ? 500 : 400,
}}
{...props.innerProps}
>
{props.children}
</MenuItem>
);
}
function Placeholder(props) {
return (
<Typography
color="textSecondary"
className={props.selectProps.classes.placeholder}
{...props.innerProps}
>
{props.children}
</Typography>
);
}
function SingleValue(props) {
return (
<Typography className={props.selectProps.classes.singleValue} {...props.innerProps}>
{props.children}
</Typography>
);
}
function ValueContainer(props) {
return <div className={props.selectProps.classes.valueContainer}>{props.children}</div>;
}
function MultiValue(props) {
return (
<Chip
tabIndex={-1}
label={props.children}
className={classNames(props.selectProps.classes.chip, {
[props.selectProps.classes.chipFocused]: props.isFocused,
})}
onDelete={event => {
props.removeProps.onClick();
props.removeProps.onMouseDown(event);
}}
/>
);
}
const components = {
Option,
Control,
NoOptionsMessage,
Placeholder,
SingleValue,
MultiValue,
ValueContainer
};
class IntegrationReactSelect extends React.Component {
state = {
single: null,
multi: null,
};
handleChange = name => value => {
this.setState({
[name]: value,
});
this.props.getSelectMultipleValue(value);
};
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
<Select
classes={classes}
options={this.props.theUserFromParentComponent}
components={components}
value={this.state.multi}
onChange={this.handleChange('multi')}
placeholder={this.props.reactSelectName}
isMulti
/>
</div>
);
}
}
Related
I want to perform using the test on the component I have in my react app. when I simply tried to render my component using the render from react testing it is throwing me the error.
Can anyone please suggest to me the solution to this? please see the code below:
Code for testing the authorgroup componenet;
import { render, screen } from "#testing-library/react";
import AuthorsGroup from "./Authorsgroup";
it("should render author component", () => {
render(<AuthorsGroup />);
const headerEl = screen.getByRole("heading", { name: /happyusertapesh/i });
expect(headerEl).toBeInTheDocument();
});
Authorcomponent
const Authorsgroup = ({
posts,
groupDropdownValue,
setShowForm,
setPostId,
showForm
}) => {
const authorGroup = posts.reduce((group, authorgroup) => {
(group[authorgroup.author.replace(/ +/g, "")] =
group[authorgroup.author.replace(/ +/g, "")] || []).push(authorgroup);
return group;
}, {});
const [authorGroupValues, setAuthorGroupValues] = useState(authorGroup);
const authorGroupEntries = Object.entries(authorGroupValues);
useEffect(() => {
setAuthorGroupValues(authorGroup);
}, [groupDropdownValue, showForm]);
return (
<>
<Container>
<Container style={{ marginTop: "3rem" }}>
{authorGroupEntries.map(([author, posts]) => {
return (
<div key={author}>
<Accordion>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography variant="h6" style={{ color: "#EB1283" }}>
{author}
</Typography>
</AccordionSummary>
{posts.map(({ id, text, author, location }) => {
return (
<div key={id}>
<AccordionDetails>
<Typography variant="h4">
{id} {author}
</Typography>
<Typography>
{text} {location}
</Typography>
<Button
variant="outlined"
onClick={() => {
setShowForm(!showForm);
setPostId(id);
}}
startIcon={<EditIcon />}
>
Edit
</Button>
</AccordionDetails>
</div>
);
})}
</Accordion>
</div>
);
})}
</Container>
</Container>
</>
);
};
export default Authorsgroup;
Thanks for your help.
Error
You aren't passing any props into the test component. This means that the value posts is being set to undefined.
Simply pass a mock value for posts as a prop in the render method
render(<AuthorsGroup posts={/* posts mock value here */} />)
You will also need to do this for each of the other props you have defined.
Goal: I should display the specific contents of a specific button after one of three buttons was clicked. Then, after the specific button is clicked, all three buttons should be hidden and replaced with the contents of the clicked specific button.
Issue: I tried passing props and using if-else statement in terms of conditional rendering but I am having trouble figuring out how to properly state a condition for the functionality to work since the remaining if else statements are ignored. Only the Beef button is working but the rest of the buttons are not.
Source code:
import * as React from "react";
import { Stack } from '#mui/material';
import FoodTraysItemButton from "./FoodTraysItemButton";
import PastaNoodlesButtonsFT from "./foodTraysPages/PastaNoodlesButtonsFT";
import DessertsButtonsFT from "./foodTraysPages/DessertsButtonsFT";
import BeefButtonsFT from "./foodTraysPages/BeefButtonsFT";
import { useState } from "react";
function preventDefault(event) {
event.preventDefault();
}
export default function FoodTraysButtons(props) {
const [myBoolBeef, setmyBoolBeef] = useState(true);
const [myBoolDesserts, setmyBoolDesserts] = useState(true);
const [myBoolPastaNoodles, setmyBoolPastaNoodles] = useState(true);
function toggleBoolBeef() {
setmyBoolBeef(!myBoolBeef);
}
function toggleBoolDesserts() {
setmyBoolDesserts(!myBoolDesserts);
}
function toggleBoolPastaNoodles() {
setmyBoolPastaNoodles(!myBoolPastaNoodles);
}
return (
// stuck here: (I plan to use multiple separate if else statements to work the functionality out but it doesn't work)
<React.Fragment>
{(() => {
// only works here
if (myBoolBeef) {
return (<Landing toggleBoolBeef={toggleBoolBeef} />);
} else{
return <BeefFT/>;
}
// these are ignored:
if (myBoolDesserts) {
return (<Landing toggleBoolDesserts={toggleBoolDesserts} />);
} else{
return <DessertsFT/>;
}
if (myBoolPastaNoodles) {
return (<Landing toggleBoolPastaNoodles={toggleBoolPastaNoodles} />);
} else{
return <PastaNoodlesFT/>;
}
})()}
</React.Fragment>
);
}
function Landing(props) {
return (
<div>
<Stack spacing={0} direction="row" sx={{ mb: 4.5 }}>
<FoodTraysItemButton
title="Beef"
onClick={props.toggleBoolBeef}
/>
<FoodTraysItemButton
title="Desserts"
onClick={props.toggleBoolDesserts}
/>
<FoodTraysItemButton title="Pasta/Noodles" onClick={props.toggleBoolPastaNoodles} />
</Stack>
</div>
);
}
function BeefFT() {
return (
<div>
<BeefButtonsFT />
</div>
);
}
function DessertsFT() {
return (
<div>
<DessertsButtonsFT />
</div>
);
}
function PastaNoodlesFT() {
return (
<div>
<PastaNoodlesButtonsFT />
</div>
);
}
Full source codes in Codesandbox: https://codesandbox.io/s/show-hide-buttons-ralph-ecv9g2?file=/src/FoodTraysButtons.jsx:773-815
How it should look like:
Beef button:
Desserts button:
Pasta Noodles button:
In what way should I implement this in order to achieve its functionality?
Your responses would be highly appreciated as I am exploring MUI and React at the moment. It would be a really big help for my project. Thank you very much!!!
Update FoodTraysButtons to hold a single state, selection that is then used to conditionally render the Landing component or any of BeefFT, DessertsFT, or PastaNoodlesFT component.
export default function FoodTraysButtons(props) {
const [selection, setSelection] = useState();
const selectHandler = (selection) => setSelection(selection);
return (
<React.Fragment>
{!selection && <Landing onSelect={selectHandler} />}
{selection === "beef" && <BeefFT />}
{selection === "dessets" && <DessertsFT />}
{selection === "pastaNoodles" && <PastaNoodlesFT />}
</React.Fragment>
);
}
Update the Landing component to take a single onSelect prop callback.
function Landing({ onSelect }) {
const selectHandler = (selection) => () => onSelect(selection);
return (
<div>
<Stack spacing={0} direction="row" sx={{ mb: 4.5 }}>
<FoodTraysItemButton title="Beef" onClick={selectHandler("beef")} />
<FoodTraysItemButton
title="Desserts"
onClick={selectHandler("desserts")}
/>
<FoodTraysItemButton
title="Pasta/Noodles"
onClick={selectHandler("pastaNoodles")}
/>
</Stack>
</div>
);
}
You need a switch case block instead of multiple boolean value state. Consider this way of structuring your code:
const menuState = {
NONE: "none",
BEEF: "beef",
DESSERTS: "desserts",
PASTA: "pasta"
};
export default function FoodTraysButtons(props) {
const [selectedMenu, setSelectedMenu] = useState(menuState.NONE);
const renderMenu = () => {
switch (selectedMenu) {
case menuState.BEEF:
return <BeefFT />;
case menuState.DESSERTS:
return <DessertsFT />;
case menuState.PASTA:
return <PastaNoodlesFT />;
case menuState.NONE:
default:
return null;
}
};
return (
<React.Fragment>
{selectedMenu === menuState.NONE && (
<Landing setSelectedMenu={setSelectedMenu} />
)}
{renderMenu()}
</React.Fragment>
);
}
function Landing(props) {
return (
<div>
<Stack spacing={0} direction="row" sx={{ mb: 4.5 }}>
<FoodTraysItemButton
title="Beef"
onClick={() => props.setSelectedMenu(menuState.BEEF)}
/>
<FoodTraysItemButton
title="Desserts"
onClick={() => props.setSelectedMenu(menuState.DESSERTS)}
/>
<FoodTraysItemButton
title="Pasta/Noodles"
onClick={() => props.setSelectedMenu(menuState.PASTA)}
/>
</Stack>
</div>
);
}
Working Demo:
NOTE: If you want to always show the button menu then remove the selectedMenu === menuState.NONE wrapper condition.
I am trying to create a timer window and I have created a seperate component for that. I am trying to pass the time in from the main component into the child component. The issue is the state for the timer input box is not changing. I have tried using useEffect but I have a feeling I may be implementing it wrongly.
Update
Found out that the event.target.value is returning an undefined which is why the state is not changing. Any ideas as to why it is returning an undefined?
Code
// Parent Component
const [targetTimeS, setTargetTimeS] = React.useState(60);
function handleMinuteSelectionChange(event) {
const minuteSelection = event.target.value;
if (isNaN(minuteSelection)) {
window.alert("Please select a number");
} else {
const minuteSelectionS = parseInt(minuteSelection);
if (minuteSelectionS > 59 || minuteSelectionS < 0) {
window.alert("Please choose a minutes value between 0 and 59");
} else {
console.log(`Minute selection changed: ${minuteSelectionS}`);
setTempTargetTimeS(minuteSelectionS * 60);
}
}
}
function timerWindow() {
return (
<TimerWindow
handleTargetTimeCancelButtonClicked={
handleTargetTimeCancelButtonClicked
}
handleMinuteSelectionChange={(e)=> handleMinuteSelectionChange(e)}
handleTargetTimeDoneButtonClicked={handleTargetTimeDoneButtonClicked}
value={targetTimeS / 60}
openModal={showTargetTimeWindow}
closeModal={hideTargetTimeWindow}
handlePresetButtonClicked={handlePresetButtonClicked}
handleHideTimerWindowClicked={hideTargetTimeWindow}
/>
);
}
Child Component
export default function TimerWindow(props: {
handleTargetTimeDoneButtonClicked: (e) => void;
handleTargetTimeCancelButtonClicked: (e) => void;
handleMinuteSelectionChange: (e) => void;
handlePresetButtonClicked?: (e: number) => void;
handleHideTimerWindowClicked?: (e) => void;
closeModal: (e) => void;
openModal: boolean;
value: number;
}): JSX.Element {
const [targetTimeS, setTargetTimeS] = useState(60);
useEffect(() => {
setTargetTimeS(props.value);
console.log(props.value);
}, [targetTimeS]);
const recorderClasses = speechRecorderStyles();
return (
<Modal open={props.openModal} onClose={props.closeModal}>
<div className={recorderClasses.targetTimeWindow}>
<CloseIcon
onClick={props.handleHideTimerWindowClicked}
className={recorderClasses.windowCloseButton}
/>
<Typography className={recorderClasses.selectRecordingTime}>
Select recording time
</Typography>
<Stack spacing={4} direction="column" alignItems="center" height="100%">
<Stack spacing={4} direction="row">
<TimerPresets onClick={props.handlePresetButtonClicked} />
</Stack>
</Stack>
<TextField
id="outlined-basic"
label="Minutes"
variant="outlined"
className={recorderClasses.targetTimeSelection}
value={targetTimeS}
onChange={(e) => props.handleMinuteSelectionChange(e.target.value)}
sx={{
mt: 6,
ml: 1,
}}
type="number"
/>
<Stack spacing={4} direction="row">
<Button
className={recorderClasses.targetTimeCancelButton}
onClick={props.handleTargetTimeCancelButtonClicked}
>
<Typography
className={recorderClasses.targetTimeCancelButtonTypography}
>
Cancel
</Typography>
</Button>
<Button
className={recorderClasses.targetTimeDoneButton}
onClick={props.handleTargetTimeDoneButtonClicked}
>
<Typography
className={recorderClasses.targetTimeDoneButtonTypography}
>
Done
</Typography>
</Button>
</Stack>
</div>
</Modal>
);
}
The problem is that you are doing the equivalent of event.target.value.target.value because you are calling event.target.value twice. That`s why it is returning undefined.
The first call is in your textField in the onChange callback:
onChange={(e) => props.handleMinuteSelectionChange(e.target.value)}
So you are already passing the value of the target component up and not the event. But then you are handling the value as it was the event in your handleMinuteSelectionChange
//Here in reality you have already the value, not the event
function handleMinuteSelectionChange(event) {
const minuteSelection = event.target.value;
//remaining code ...
}
This is either very simple or I am doing it completely wrong. I am a novice so please advise.
I am trying to show different components inside different tabs using Material UI using array map. The tabs are showing fine but the components do not render. Basically if the array label is 'Welcome', the tab name should be 'Welcome' and the Welcome component should show up and so on. Please help!
return (
<Box sx={{ width: '100%' }}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs value={value} onChange={handleChange} aria-label="basic tabs example">
{fetchedCategories.map((category) => (
<Tab key={category.label} label={category.label} />
))}
</Tabs>
</Box>
{fetchedCategories.map((category, index) => {
const Component=myComponents[category.label];
})}
{fetchedCategories.map((category, index) => (
<TabPanel key={category.label} value={value} index={index}>
<Component label={category.label} />
</TabPanel>
))}
</Box>
);
Here is my props & Component function:
interface ComponentProps {
label: string;
value?: number;
}
function Component (props: ComponentProps)
{
const {label, value} = props;
return myComponents[label];
}
const myComponents = {
'Welcome': Welcome,
'Salad/Soup': Welcome
}
Try something like:
function Component({ label, value }: ComponentProps) {
const [Comp, setComponent] = useState(<div />);
React.useEffect(() => {
const LabelComp = myComponents[label];
if (label && LabelComp) {
setComponent(<LabelComp value={value} />); // <-- if you want to pass value to you component
}
}, [value, label]);
return Comp;
}
And you will use it like:
const App = () => {
return <Component label={"myComponentLabel"} value={"some value"} />;
};
I need to make a small change to a textfield. Basically there is a reset button that clears the search. The user can write the search string in the TextField. But the clear function only resets the actual search, but not the TextField which then still contains some kind of search string the user have written.
interface DrawerContainerProps {
dataProfile: DataProfile;
}
interface DrawerContainerComponentProps extends DrawerContainerProps, WithStibStyles<typeof styles>, WithNamespaces {
}
interface DrawerCantainerState {
attributeSearchText: string;
}
class ProfileDrawerContainerComponent extends React.Component<DrawerContainerComponentProps, DrawerCantainerState> {
readonly state: Readonly<DrawerCantainerState> = {
attributeSearchText: ""
};
setAttributeSearchText = debounce((searchText) => this.setState({ attributeSearchText: searchText }), 200);
onSearch = (event: React.ChangeEvent<HTMLInputElement>) => this.setAttributeSearchText(event.target.value);
clearSearch = () => this.setAttributeSearchText("");
render() {
const { classes, dataProfile, t } = this.props;
return (
<Drawer
variant="permanent"
classes={{ paper: classes.drawerPaper }}>
<div className={classes.toolbar} />
<div className={classes.menu}>
<Typography className={classes.drawerTitle}>{t("drawer.objectType", { defaultValue: "Object Type Filter" })}</Typography>
<div className={classes.objectTypes}>
{dataProfile && <ObjectTypeDrawer
objectTypes={dataProfile.profiledObjectTypes}
objectCount={dataProfile.objectCount}
businessConditionFiltering={dataProfile.businessConditionFilteringResult} />}
</div>
<div className={classes.attributeTitleSearch}>
<Typography className={classes.drawerTitle}>{t("drawer.attributes", { defaultValue: "Attributes" })}</Typography>
<TextField
id="attribute-search"
placeholder={t("drawer.search", { defaultValue: "Search" })}
type="search"
className={classes.searchField}
onChange={this.onSearch }
/>
</div>
<div className={classes.attributes}>
<AttributesDrawer attributeFilter={this.state.attributeSearchText} sendFunction={this.clearSearch} />
</div>
</div>
</Drawer>
);
}
}
My knowledge of web programming is very elementary. But I suspect that whenever the clearSearch function is called, it also has to update the value of the TextField. But that is where my knowledge of React and state handling comes short. Hope someone can give a hint.
You need to 'control' the value of your TextField. That is
<TextField
id="attribute-search"
placeholder={t("drawer.search", { defaultValue: "Search" })}
type="search"
className={classes.searchField}
onChange={this.onSearch }
value={this.state.attributeSearchText}
/>