I am using React popper for displaying additional information for input fields in my form. The problem is that, when i am displaying Tooltip for more than 1 element, it displays the same tooltip. How can i display different tooltips for each field.
Here is the code i am using inside my Component
https://codesandbox.io/s/hungry-gould-modgk?fontsize=14&hidenavigation=1&theme=dark
// Popper Tooltip Props;
const {
getArrowProps,
getTooltipProps,
setTooltipRef,
setTriggerRef,
visible,
} = usePopperTooltip({
trigger: 'hover',
placement: 'right',
closeOnOutsideClick: false,
visible: controlledVisible,
onVisibleChange: setControlledVisible
})
return (
<TextBox
label="Title"
className="title-field"
name="title"
type="text"
isRequired={true}
/>
<div className="field-info" ref={setTriggerRef}>
<Icon size="medium">
<FontAwesomeIcon icon={faInfoCircle} size="lg" />
</Icon>
</div>
{visible && (
<div
ref={setTooltipRef}
{...getTooltipProps({ className: 'tooltip-container' })}
>
Tooltip element
<div {...getArrowProps({ className: 'tooltip-arrow' })} />
</div>
)}
<TextBox
label="Price"
className="price-field"
name="price"
type="text"
isRequired={true}
/>
<div className="field-info" ref={setTriggerRef}>
<Icon size="medium">
<FontAwesomeIcon icon={faInfoCircle} size="lg" />
</Icon>
</div>
{visible && (
<div
ref={setTooltipRef}
{...getTooltipProps({ className: 'tooltip-container' })}
>
Tooltip element
<div {...getArrowProps({ className: 'tooltip-arrow' })} />
</div>
)}
)
[1]: https://i.stack.imgur.com/oHuBA.png
Each tooltip needs its own visible state variable. Can you create your own tooltip component like so:
const MyTooltip = ({tooltipText}) => {
const [isVisible, setIsVisible] = useState(false)
const {
getArrowProps,
getTooltipProps,
setTooltipRef,
setTriggerRef,
visible,
} = usePopperTooltip({
trigger: 'hover',
placement: 'right',
closeOnOutsideClick: false,
visible: isVisible,
onVisibleChange: setIsVisible
})
return (
<>
<div className="field-info" ref={setTriggerRef}>
<Icon size="medium">
<FontAwesomeIcon icon={faInfoCircle} size="lg" />
</Icon>
</div>
{visible && (
<div
ref={setTooltipRef}
{...getTooltipProps({ className: 'tooltip-container' })}
>
{tooltipText}
<div {...getArrowProps({ className: 'tooltip-arrow' })} />
</div>
)}
</>
)
}
Then you can use the component like this:
<TextBox
label="Title"
className="title-field"
name="title"
type="text"
isRequired={true}
/>
<MyTooltip tooltipText="Tooltip Element 1" />
<TextBox
label="Price"
className="price-field"
name="price"
type="text"
isRequired={true}
/>
<MyTooltip tooltipText="Tooltip Element 2" />
Related
I would love to have a search bar in "Select" component from the library native-base in react-native.
I have tried adding a "TextInput" component inside the "Select" component. In UI it aligns perfectly, but when I click on the "TextInput" it gets selected and the list drops down.
Following is the code I tried:
<Select
w={w}
h={h}
variant="outline"
selectedValue={selectedValue}
minWidth="200"
// borderColor={primaryColor}
accessibilityLabel={accessibilityLabel?accessibilityLabel: "Choose Service" }
placeholder={placeholder?placeholder: "Choose Service"}
_selectedItem={{
bg:"coolGray.200",
// endIcon: <CheckIcon size="5" />
}}
mt={1}
onValueChange={onValueChange}
>
<Input
placeholder="Search"
variant="filled"
width="100%"
h={heightPercentageToDP("6%")}
borderRadius="10"
py="1"
px="2"
borderWidth="0"
/>
{
data?.map(item => {
return(
<Select.Item
label={item.label}
value={item.value}
/>
)
})
}
</Select>
Select box of native base has prop _actionSheetBody, it contains IFlatListProps so you can use ListHeaderComponent in there. So can use this way.
You use a state to save search value:
const [searchValue, setSearchValue] = React.useState<string>('');
Edit select box
`
<Select w={w} h={h} variant="outline" selectedValue={selectedValue}
minWidth="200"
accessibilityLabel={accessibilityLabel?accessibilityLabel: "Choose Service" }
placeholder={placeholder?placeholder: "Choose Service"}
_selectedItem={{
bg:"coolGray.200",
// endIcon: <CheckIcon size="5" />
}}
mt={1}
onValueChange={onValueChange}
_actionSheetBody={{
ListHeaderComponent: <FormControl px={3} mb={3}>
<Input
px={15}
py={2}
fontSize={16}
value={searchValue}
placeholder=""
_focus={{ bg: colors.white['50'], borderColor: 'darkBlue.600' }}
type='text'
onChangeText={(value: string) => {
setSearchValue(value);
}}
/>
</FormControl>
}}
>
<Input
placeholder="Search"
variant="filled"
width="100%"
h={heightPercentageToDP("6%")}
borderRadius="10"
py="1"
px="2"
borderWidth="0"
/>
{
(data && data.length)?data.filter((item)=>{
// you filter with searchValue
return true;
}).map(item => {
return(
<Select.Item
label={item.label}
value={item.value}
/>
)
})
}
</Select>
From the parent component. There are other forms, hence, there is a conditional form where it will used the child component's form.
A user may click the button to show the form from the child component.
const [isTrue, setIsTrue] = useState(false);
<ButtonForm onClick={() => setIsTrue(true)}>
Click for the Form
</ButtonForm>
{isTrue == true ? (
<Form
data={entryData}
items={items}
names={names}
/>
) : (
></>
)}
This is the child component. The problem here is that, everytime I'll submit it, it will reload the page:
const Form = ({ entryData, items, names }) => {
const handleSubmit = (e) => {
e.preventDefault();
try {
console.log(" saved");
} catch (err) {
console.log(err);
}
};
return (
<>
<form onSubmit={handleSubmit}>
//some codes here
<Grid item>
<TextField
type="text"
label="Item Number"
variant="outlined"
fullWidth
required
/>
</Grid>
//some codes here
<Grid item>
<Button type="submit" fullWidth>
Submit
</Button>
</Grid>
</form>
<br /> <br />
</>
);
};
export default Form;
This is the sample parent component: most where just forms
<div className="App">
<Card className={classes.root}>
<CardContent>
{users &&
users.map((user) => (
<li style={{ listStyle: "none" }}>
<Button onClick={() => setIsTrue(true)}>
Click for the Form
</Button>
{//if user condition is true here == true ? (
<div>
//form here with the textfields
</div>
) : (
<div>
//form here with the textfields
</div>
)}
</li>
))}
</>
{isTrue == true ? (
<Form
data={entryData}
items={items}
names={names}
/>
) : (
></>
)}
</CardContent>
</Card>
</div>
// on click trigger submit in first and second forms
<form>
<form>
<button type="submit" />
</form>
<button type="submit" />
</form>;
//solution
<form onSubmit={(e) => e.preventDefault()}>
<form onSubmit={(e) => e.preventDefault()}>
<button type="submit" /> // on click trigger submit in first and second form
</form>
<button type="submit" />
</form>;
I am building header using material UI Tab Component but I see below error:
index.js:2178 Material-UI: The value provided to the Tabs component is invalid.
None of the Tabs' children match with [object Object].
You can provide one of the following values: 0, 1.
I tried console.log the newValue to see what value it is getting and I can see 0 and 1 while navigating through tabs.
Note : Removed Some Code for better visibility
Here is my component:
const Header = (props) => {
const { classes } = props;
const [value, setValue] = useState(0);
const [modalIsOpen, setIsOpen] = React.useState(false);
const openModal = () => {
setIsOpen(true);
};
const closeModal = () => {
setIsOpen(false);
};
const handleTabChange = (event, newValue) => {
console.log(newValue);
setValue({ newValue });
};
return (
<div>
<div className="topnav">
<img src={logo} className="logo" alt="Movies App Logo" />
<div className="topnav-right">
<Button variant="contained" color="default" onClick={openModal}>
Login
</Button>
</div>
<div className="topnav-right">
<Button variant="contained" color="default">
Logout
</Button>
</div>
</div>
<Modal
ariaHideApp={false}
isOpen={modalIsOpen}
onRequestClose={closeModal}
contentLabel="Login"
aria-labelledby="Modal"
aria-describedby="Modal for Login and Registration"
style={customStyles}
>
<Paper className={classes.Paper}>
<CardContent>
<Tabs
className="tabs"
value={value}
onChange={handleTabChange}
centered
>
<Tab label="Login" />
<Tab label="Register" />
</Tabs>
{value === 0 && (
<div>
<FormControl required>
<InputLabel htmlFor="username" className={classes.inputLable}>
Username
</InputLabel>
<Input
className={classes.Input}
id="username"
type="text"
username={username}
onChange={usernameChangeHandler}
/>
<FormHelperText>
<span className="red">required</span>
</FormHelperText>
</FormControl>
<br />
<br />
<FormControl required>
<InputLabel
htmlFor="loginPassword"
className={classes.inputLable}
>
Password
</InputLabel>
<Input
className={classes.Input}
id="loginPassword"
type="password"
password={password}
onChange={passwordChangeHandler}
/>
<FormHelperText>
<span className="red">required</span>
</FormHelperText>
</FormControl>
<br />
<br />
{loggedIn === true && (
<FormControl>
<span className="success-text">Login Successful!</span>
</FormControl>
)}
<br />
<br />
<Button
variant="contained"
color="primary"
onClick={loginHandler}
>
LOGIN
</Button>
</div>
)}
{value === 1 && (
<div>
<h1>something</h2>
</div>
)}
</CardContent>
</Paper>
</Modal>
</div>
);
};
export default withStyles(styles)(Header);
For some reason you enclosed the value in an object (curly braces syntax).
Replace setValue({ newValue }) with setValue(newValue).
I am building a form that lets users fill out names of an indefinite number of animals. At the end of the questions is clickable text that reveals the option to fill in another animal's name. How do I let this process occur an indefinite number of times, as right now I can click it once to reveal the extra field, but then cannot click it again.
Because each field needs to be individually checked with the backend database, each extra field cannot be overwritten when they appear and are filled out.
Below is my code snippet and a screenshot of the page after the extra field has been revealed.
Thanks in advance
const CustomModel = (props) => {
const { classes, ...rest } = props;
const [showNextAnimal, setShowNextAnimal] = React.useState(false);
const onClick = () => setShowNextAnimal(true);
const AnotherAnimal = () => (
<div>
<h4>What other animal are you looking for?</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Enter name of animal"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'description' }}
/>
</div>
<br />
</div>
);
return (
<main className={classes.content}>
<div className={classes.toolbar} />
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<Paper className={classes.paper}>
<h2>Create a new model</h2>
<Divider />
<h4>Name?</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Feel free to be creative!"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'name' }}
/>
</div>
<br />
<h4>Description</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Enter your description"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'description' }}
/>
</div>
<br />
<h4>What animal are you looking for?</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Enter name of animal"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'description' }}
/>
</div>
<br />
{showNextAnimal ? <AnotherAnimal /> : null}
<div>
<h4 onClick={onClick}>+ Add another animal</h4>
<br />
</div>
<Button
color="primary"
variant="contained"
type="submit"
className={classes.continueButton}
// onClick={(updateFormValues, handleClick)}
>
Review
</Button>
<br />
</Paper>
</GridItem>
</GridContainer>
<StickyFooter />
</main>
);
};
export default withStyles(styles)(CustomModel);
You should use a count variable and then, on click of Add new animal, increment the count variable. In the body, display <AnotherAnimal /> for count number of times.
const CustomModel = (props) => {
const { classes, ...rest } = props;
const [countOfOtherAnimals, setCountOfOtherAnimals] = React.useState(0);
const onClick = () => setCountOfOtherAnimals(countOfOtherAnimals + 1);
const AnotherAnimal = () => (
<div>
<h4>What other animal are you looking for?</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Enter name of animal"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'description' }}
/>
</div>
<br />
</div>
);
return (
<main className={classes.content}>
<div className={classes.toolbar} />
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<Paper className={classes.paper}>
<h2>Create a new model</h2>
<Divider />
<h4>Name?</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Feel free to be creative!"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'name' }}
/>
</div>
<br />
<h4>Description</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Enter your description"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'description' }}
/>
</div>
<br />
<h4>What animal are you looking for?</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Enter name of animal"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'description' }}
/>
</div>
<br />
{
[...Array(countOfOtherAnimals)].map((e, i) => (
<AnotherAnimal key={i} />
))
}
<div>
<h4 onClick={onClick}>+ Add another animal</h4>
<br />
</div>
<Button
color="primary"
variant="contained"
type="submit"
className={classes.continueButton}
// onClick={(updateFormValues, handleClick)}
>
Review
</Button>
<br />
</Paper>
</GridItem>
</GridContainer>
<StickyFooter />
</main>
);
};
export default withStyles(styles)(CustomModel);
Using .map, I mapped all cards and each card has an edit button, so when I click the edit button it opens a Modal (antd) that has a form with input values. Values are bound with API, and when I open any card model it shows perfect values in input.
Problem is, when I click any other card's edit button, it shows again the same values (previous popup values)
code:
{surveys.length > 0 && surveys.map((survey, i) => (
<Col className="gutter-row" xs={24} sm={12} md={12} lg={6} xl={6} key={survey.publicID}>
<Card
cover={<Link to='/survey'><img alt="example" src={ survey.imagePath || "https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"} /></Link>}
actions={[
<img className="icons-svgs" src="/svg/Share.svg" onClick={() => this.onHandleShareSurvey(survey)} />,
<img className="icons-svgs" src="/svg/Edit.svg" onClick={() => this.onHandleEditSurvey(survey)} />,
<img className="icons-svgs" src="/svg/Duplicate.svg" onClick={()=>this.onHandleDuplicateSurvey(survey)} />,
<img className="icons-svgs" src="/svg/Delete.svg" onClick={() => this.onHandleDeleteSurvey(survey)}/>
]} >
<Row type="flex" align="middle">
<Col span={18}>
<Link to='/survey'>
<Meta
title={survey.Name}
description={survey.Description}
/>
</Link>
</Col>
<Col type="flex" span={6} align="middle">
<span onClick={()=> this.setUploadDataSurvey(true)}>
<Icon type="upload" className="upload-icon"/>
</span>
</Col>
</Row>
</Card>
</Col>
))}
And popup component is:
<Modal
visible={visible}
onCancel={onCancel}
// onOk={() => this.setEditSurvey(false)}
title="Edit survey"
className= "surveys-popup"
style={{ top: 20 }}
destroyOnClose={true}
footer={false}
>
<Form onSubmit={this.handleEdit}>
<p className="form-title">What is your product?</p>
<FormItem key={10}>
{getFieldDecorator('Name', {
rules: [{ required: false, message: 'Title Can\'t leave blank' }],
})(
<div className="form-input">
<Input ref="Name" defaultValue={survey.Name}/>
</div>
)}
</FormItem>
<p className="form-title">Please describe your product</p>
<div className="form-input">
<FormItem key={11}>
{getFieldDecorator('Description', {
rules: [{ required: false, message: 'Description Can\'t leave blank' }],
})(
<div className="form-input">
<TextArea autosize={{ minRows: 2, maxRows: 6 }} ref="Description" defaultValue={survey.Description}/>
</div>
)}
</FormItem>
</div>
<div span={24} className="upload-images-container">
<div className="clearfix">
<Upload
action="//jsonplaceholder.typicode.com/posts/"
listType="picture-card"
fileList={fileList}
onChange={this.handleChange}
>
{fileList.length >= 3 ? null : uploadButton}
</Upload>
</div>
</div>
<p className="form-title">Define your probability model <Icon type="info-circle" className="small-icon" /></p>
<div className="model-table">
<div className="table-column">
<div span={24} className="model-table-title">Too cheap %</div>
<div span={24} className="model-table-value">
<FormItem key={12}>
{getFieldDecorator('tooCheap', {
rules: [{ required: false, message: '' }],
})(
<div className="form-input">
<Input type="number" defaultValue={survey.tooCheap}/>
</div>
)}
</FormItem>
</div>
</div>
<div className="table-column">
<div span={24} className="model-table-title">Very cheap %</div>
<div span={24} className="model-table-value">
<FormItem key={13}>
{getFieldDecorator('veryCheap', {
rules: [{ required: false, message: '' }],
})(
<div className="form-input">
<Input type="number" defaultValue={survey.veryCheap}/>
</div>
)}
</FormItem>
</div>
</div>
<div className="table-column">
<div span={24} className="model-table-title">Cheap %</div>
<div span={24} className="model-table-value">
<FormItem key={14}>
{getFieldDecorator('cheap', {
rules: [{ required: false, message: '' }],
})(
<div className="form-input">
<Input type="number" defaultValue={survey.cheap}/>
</div>
)}
</FormItem>
</div>
</div>
<div className="table-column">
<div span={24} className="model-table-title">Correct price %</div>
<div span={24} className="model-table-value">
<FormItem key={15}>
{getFieldDecorator('correctPrice', {
rules: [{ required: false, message: '' }],
})(
<div className="form-input">
<Input type="number" defaultValue={survey.correctPrice}/>
</div>
)}
</FormItem>
</div>
</div>
<div className="table-column">
<div span={24} className="model-table-title">Expensive %</div>
<div span={24} className="model-table-value">
<FormItem key={16}>
{getFieldDecorator('expensive', {
rules: [{ required: false, message: '' }],
})(
<div className="form-input">
<Input type="number" defaultValue={survey.expensive}/>
</div>
)}
</FormItem>
</div>
</div>
</div>
<Row>
<Col xs={24} sm={12} md={12} lg={12} xl={12}>
<div className="form-title"><p>Choose a password <Icon type="info-circle" className="small-icon" /> (optional)</p>
<div>
<FormItem key={17}>
{getFieldDecorator('password', {
rules: [{ required: false, message: '' }],
})(
<div className="form-input">
<Input />
</div>
)}
</FormItem>
<FormItem key={17} className="hidden">
{getFieldDecorator('publicID', {
rules: [{ required: false, message: '' }],
})(
<div className="form-input">
<Input value={survey.publicID}/>
</div>
)}
</FormItem>
{/* <input value={survey.publicID} ref="publicID"/> */}
</div>
</div>
</Col>
<Col xs={24} sm={12} md={8} lg={8} xl={8} offset={4}>
<div className="form-title">
<p>(optional)</p>
<div>
<Upload key={10}>
<Button><Icon type="upload" /> UPLOAD DATA</Button>
</Upload>
</div>
</div>
</Col>
</Row>
<Row className="footer-cta">
<Button key="back" onClick={onCancel}>Cancel</Button>
<Button key="submit" type="primary" visible={visible} htmlType="submit" onClick={onCancel}>
Save
</Button>
</Row>
</Form>
</Modal>
I had a similar issue, whereby I needed the content of the Modal to re-render when re-opening - i.e. a modal showing content based on a button being clicked, but after closing the modal, it would show the same data when re-opened.
Adding destroyOnClose={true} to my Modal properties solved this issue for me. Now, closing a Modal would destroy it, and each time the modal is opened, it would re-fetch dynamic content based on the button used to open it.
i.e.
<Modal
destroyOnClose={true}
visible={this.state.modalVisible}
title="Example Modal"
onOk={this.changeTimeModalHandleOk}
onCancel={this.changeTimeModalHandleCancel}
>
...Dynamic Modal Content Here...
</Modal>
I got problem when trying to initialize value for a form by setState. the form didn't get value from the State. Found out this solution
<Modal destroyOnClose>
<Form preserve={false} >
</Modal>
I am using antd 4.2 and I had been facing the same issue, when I clicked on update button to show the edit form in a model but the model was showing same same values for different records, destroyOnClose={true} fixed my issue.
Try conditionally render Modal component. It will be mount and unmount Modal depending on state of isModalVisible.
const ParentComponent = () => {
const [isModalVisible, setModalVisible] = useState(false);
return (
<>
{isModalVisible ? <Modal /> : null}
</>
)
Another solution is to re-create the Modal whenever it becomes visible. This way it will not cache any previous form values, and will be initialized with whatever the initial field values given.
{isVisible && <Modal visible/>