React-tooltip rendering two times - javascript

I have this component form react-tooltip where I pass some props and it creates the tooltip. I want the place of the tooltip to be "top" on default, but when I pass the props to be in a different place, to change it.
class Tooltip extends PureComponent {
render() {
const { text, element, type, place, className } = this.props;
return (
<div data-tip={text} className="m0 p0">
{element}
<ReactTooltip
type={type}
place={place}
effect="solid"
className={className}
html
/>
</div>
);
}
}
Tooltip.defaultProps = {
type: 'info',
place: 'top',
className: 'tooltip-top',
};
Tooltip.propTypes = {
text: PropTypes.string.isRequired,
element: PropTypes.element.isRequired,
type: PropTypes.string,
place: PropTypes.sring,
className: PropTypes.sring,
};
export default Tooltip;
Then I have this other component where I pass some props to the Tooltip component and I just want this only component to be placed on the bottom.
<Tooltip
type="warning"
place="bottom"
className="tooltip-bottom"
text={
'Ingrese los datos de la información financiera histórica de su compañía en la plantilla'
}
element={
<div className={`center mt3 ${styles.optionButton}`}>
<NavLink
className="btn btn-primary"
to={`${path}/manual-upload`}
>Continuar</NavLink>
</div>
}
/>
The problem is that is rendering in the bottom but also on the top. How can I make this to only appear on the bottom (in this component and the rest of the tooltips on the top). Thanks ;)

If you use <ReactTooltip /> inside a loop then you will have to set a data-for and id for each one.
const Tooltip = ({ children, data }) => {
const [randomID, setRandomID] = useState(String(Math.random()))
return (
<>
<div data-tip={data} data-for={randomID}>{children}</div>
<ReactTooltip id={randomID} effect='solid' />
</>
)
}
export default Tooltip

I ran into this issue today as well, I was able to get it resolved, this might not be relevant to you anymore, but for people looking:
I had this issue with data-for and id as well, the solution for me
was to set a more unique identifier/a combination of a word and a
variable that I was getting from the parent component (i.e.
id=`tooltip-${parent.id}`).
Heres the same
issue.

Related

How to .Map over different props that are passed into a component?

I'm new to React but hopefully someone can help!
So I've just created a component that takes in a value (via prop) and then .maps over that value creating an Image slider. The props are all an array of objects that contain different values such as :
const Songs = [
{
artist: 'Artist Name',
song: 'Song Name',
lenght: '2:36',
poster: 'images/....jpg'
},
{
artist: 'Artist Name',
song: 'Song Name',
lenght: '2:36',
poster: 'images/....jpg'
},
]
I have been making the same component over and over again because I don't know how to make the 'prop'.map value dynamic. Essentially I don't know how to change the value before the .map each different prop.
Here's an example. I want to make 'Songs'.map dynamic so the new props can replace that so they can also be mapped. Maybe there's another way. Hopefully some can help.
import React from 'react';
import { FaCaretDown } from 'react-icons/fa';
function ImageSlider({Songs, KidsMovies, Movies, TvShows}) {
return (
<>
{Songs.map((image, index) => (
<div className="movie-card">
<img src={'https://image.tmdb.org/t/p/w500' + image.poster_path}
className='movie-img' />
<h5 className='movie-card-desc'>{image.original_title}</h5>
<p className='movie-card-overview'>{movie.overview}</p>
</div>
))}
</>
);
}
export default ImageSlider;
Given your example,
I feel like all you need is render ImageSlides for each array
function ImageSlider({ items }) {
return (
<>
{items.map((item, idx) => (
<div ... key={idx}> // be careful to not forget to put a key when you map components
...
</div>
))}
</>
);
}
When rendering your component
function OtherComponent({ songs, kidsMovies, movies, tvShows }) {
return (
<div>
<ImageSlider items={songs} />
<ImageSlider items={kidsMovies} />
<ImageSlider items={movies} />
<ImageSlider items={tvShows} />
</div>
);
}

How to generate a component in React Form onClick

I am struggling with some React functionality. My goal is to create a form where a day template can be added (for context - like a training club can make up a template of trainings for the day and then schedule them regularly). For that I wanted to add a button which onClick will create a smaller block with 2 form fields - time and training info. And I need user to add several of those, as much as they want.
The thing is, while I understand a bit how react works, it seems to me I am just banging my head against the wall with this, as one thing is to render a component, but another to generate a bunch of same, completely new ones and connected to the form somehow, so I can send the data when clicking submit button.
Here is repository with this component:
https://github.com/badgerwannabe/spacefitness-test-client
Here is path to this component
spacefitness-test-client/src/components/template-components/addTemplateForm.js
Here below how it looks rendered
UPDATE 1 here is the full component here:
import React, {useState} from "react";
import {useMutation } from "#apollo/client";
import {useForm} from '../../utils/hooks'
import { Button, Form } from "semantic-ui-react";
import {FETCH_TEMPLATES_QUERY, FETCH_TRAININGS_QUERY,ADD_TEMPLATES_MUTATION} from '../../utils/graphql'
//hook for form functioning
function AddTemplateForm (props){
const {values, onChange, onSubmit} = useForm(createDayCallback,{
date:'', dayTrainings:[{
time:'testing time', training:"60e9e7580a6b113b2486113a"
},{
time:'testing2 time2', training:"61ec6a6d0f94870016f419bd"
}
]
});
//apollo hook to send data through GraphQL
const [createDay, {error}] = useMutation(ADD_TEMPLATES_MUTATION, {
errorPolicy: 'all',
variables:values,
update(proxy, result){
const data = proxy.readQuery({
query:FETCH_TEMPLATES_QUERY,
});
proxy.writeQuery({query:FETCH_TEMPLATES_QUERY,
data:{
getDays: [result.data.createDay, ...data.getDays]
}})
props.history.push('/templates')
},},
{});
function createDayCallback(){
createDay();
}
//little component I want to dynamically add each time people press a button
function addDayTraining(){
const addDayTraining = (
<>
<Form.Field>
<Form.Input
placeholder="time"
name="time"
onChange={()=>{
console.log("time")
}}
values={values.time}
error={error ? true : false}
/>
<Form.Input
placeholder="training"
name="training"
onChange={()=>{
console.log("training")
}}
values={values.training}
error={error ? true : false}
/>
</Form.Field>
</>
)
return addDayTraining
}
//Form component itself
const AddTemplateForm = (
<>
<Form onSubmit={onSubmit}>
<h2>Add a template :</h2>
<Form.Field>
<Form.Input
placeholder="date"
name="date"
onChange={onChange}
values={values.date}
error={error ? true : false}
/>
</Form.Field>
<Form.Field>
<Button type="button" onClick={
addDayTraining
}>Add training</Button>
</Form.Field>
<Button type ="submit" color="teal">Submit</Button>
</Form>
{error && (
<div className="ui error message" style={{marginBottom:20}}>
<li>{error.graphQLErrors[0].message}</li>
</div>
)}
</>
)
return AddTemplateForm
}
export default AddTemplateForm;
Can you just set up a function on the submit button which pushes an object with {time: new Date(), trainingInfo: ""} and push that object into an existing array of training objects? (obviously starting empty)
You could then map those objects into a component and when the component is updated (i.e. when the user adds a time and training details text) use a callback function to update the values in the array at the index of that object.
export default function yourIndexPage({yourprops}) {
const [trainingObjects, setTrainingObjects] = useState([]);
function addTraining(){
const newTrainingObject = {
time: new Date(), //assuming you want it to default to todays date
trainingInfo: "your placeholder text"
};
setTrainingObjects([...trainingObjects, newTrainingObject]);
}
//I am assuming your training object will be a list item here so wrapped in <ul>
return(
<div>
<div className='your list of training things'> (might need to set style as flex and add some padding etc..)
{trainingObjects.length === 0 ? <div/> : trainingObjects.map((trainingObject, index) => (
<YourTrainingObjectComponent trainingObject={trainingObject} trainingItemIndex={index} key={index}/>
))}
</div>
<Button onClick={() => {addTraining}} />
</div>
)
}

#szhsin/react-menu can't get Menu items inline, always one on top of another

I'm trying to get the Menu (from #szhsin/react-menu module) element buttons to show up to the right of the previous generated item, however I'm a bit lost as to how to get it to do so. Everything results in the element showing below previous.
import React from 'react';
import {
Menu,
MenuItem,
MenuButton,
SubMenu
} from '#szhsin/react-menu';
import '#szhsin/react-menu/dist/index.css'
class TopMenuDropdown extends React.Component {
constructor(props) {
super(props);
}
render () {
return (
<div>
{this.props.TMPMenuTestCategory.map (({name,items},i) =>
{
return <Menu
align={'end'}
key={i}
menuButton={<MenuButton>{name}</MenuButton>}
reposition={'initial'}
>
{items.map((item,j) =>
{
console.log(item,j);
return <MenuItem key={j}>{item}</MenuItem>
}
)}
</Menu>
} )}
</div>
)
}
}
I was looking through the documentation on https://szhsin.github.io/react-menu/docs , however, me trying the following has had no effect:
Assigning the display:'inline' or 'flex' the <Menu> or to a <div><Menu> as I attempted to give each menu it's own div when generated.
Wrapping each generated menu in a <span>
Fiddling with the Menu item's props like 'align' , 'position' , and 'reposition' (though I'm guessing Reposition needs an additional RepositionFlag to work if I understand it correctly)
Here's the snippet of index.JS it is part of
const basicMenuArray = [
{ name: 'ProTIS', items: [ 'Login', 'Exit' ] },
{ name: 'Project', items: [ 'Open', 'Info' ] },
]
class App extends React.Component {
state={
language:'sq'
}
render () {
return (
<div >
<div style={{display:'flex', width:'75%', float:'left' }}>
<span> Temp Text </span>
</div>
<div style={{display:'flex', width:'25%'}}>
<span style={{marginLeft:'auto'}}>
<DataComboBox
dropdownOptions={languages}
value={this.state.language}
valueField='language_code'
textField='language_full_name'
onChange={(value) => alert(JSON.stringify(value))}
/>
</span>
</div>
<div>
<TopMenuDropdown TMPMenuTestCategory={basicMenuArray} />
</div>
</div>
);
}
}
So I ended up realizing something this morning, as I'm learning ReactJS still, and my brain did not process the things properly.
I changed the initial
<div>
to
<div style={{display:'flex'}}>
and added a style={{display:'flex', float:'left'}} to the <Menu> which generates the button.
the final code snippet looks like this for anyone still learning like I am :)
return (
<div style={{display:'flex'}}>
{this.props.TMPMenuTestCategory.map (({name,items},i) =>
{
return <Menu
style={{display:'flex', float:'left'}}
key={i}
menuButton={<MenuButton>{name}</MenuButton>}
>
{items.map((item,j) =>
{
console.log(item,j);
return <MenuItem key={j}>{item}</MenuItem>
}
)}
</Menu>
} )}
</div>
)

How to insert newline in reactstrap tooltip?

My project has a reusable tooltip component that utilizes reactstrap tooltip to show the tooltip. The usage is shown below
import { UncontrolledTooltip } from 'reactstrap';
const Tooltip = ({
target,
title,
trigger,
position,
delay,
size,
boundariesElement,
}) => {
return (
<div className={TooltipStyles.tooltip}>
{title.length && target && (
<UncontrolledTooltip
cssModule={TooltipStyles}
placement={position}
target={target}
trigger={trigger}
delay={delay}
defaultOpen={false}
popperClassName={TooltipStyles.tooltipFade}
innerClassName={classNames({
[TooltipStyles.textSizeSmall]: size === 'small',
[TooltipStyles.textSizeDefault]: size === 'default',
[TooltipStyles.textSizeLarge]: size === 'large',
})}
boundariesElement={boundariesElement}
>
{title}
</UncontrolledTooltip>
)}
</div>
);
};
I want to create a multi-line tooltip to show a list of items. Basically, I want the title prop to render a multi-line string. Can someone please guide me on how to do this?
I tried sending HTML in the title prop but it doesn't work.
Send title as html, add <br /> tags.
Below code show tooltip in 2 lines
<p>Somewhere in here is a <span style={{textDecoration: 'underline', color:'blue'}} href='#' id='UncontrolledTooltipExample'>tooltip</span>.</p>
<UncontrolledTooltip placement='right' target='UncontrolledTooltipExample'>
Hello <br />world!
</UncontrolledTooltip>
</div>
I tried sending HTML in the title prop but it doesn't work. :(
You need to send it as JSX not as html string
title='Hello<br />World' will not work
title={<>Hello<br />World</>} should work
Use below code in your example and see it working
import React from 'react';
import Tooltip from '#bit/reactstrap.reactstrap.tooltip';
class Example extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
tooltipOpen: false
};
}
toggle() {
this.setState({
tooltipOpen: !this.state.tooltipOpen
});
}
render() {
return (
<div>
<link
rel='stylesheet'
href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css'
/>
<p>Somewhere in here is a <span style={{textDecoration: 'underline', color:'blue'}} href='#' id='TooltipExample'>tooltip</span>.</p>
<Tooltip placement='right' isOpen={this.state.tooltipOpen} target='TooltipExample' toggle={this.toggle}>
{this.props.title}
</Tooltip>
</div>
);
}
}
export default <Example title={<>Line one<br />Line 2<br />Line3</>} />

React pass props from multiple arrays in state

I am trying to pass props from two arrays that are contained in the state of the react component. One array is generated by the user and the other is already built in. This is a bit over my head as I'm still new to React and am unsure how to correctly pass props.
There are no errors here in the component, it's just that I don't know how to do it.
I will explain below what I'm looking for, any help would be greatly appreciated
Main Component
In this.state below you will see questions (which works perfectly) then hints. Questions is mapped over correctly however when I try to add in hints to map along with it, it returns all of the hints at once instead of in order and one by one. I've tried just adding (questions, hints) but it doesn't return it correctly.
export default class AutoFocusText extends Component {
constructor() {
super();
this.state = {
active: 0,
questions: [
'1. Enter A Proper Noun',
'2. Enter A Location',
'3. Enter A Proper Noun that Describes Evil',
'4. Describe Something Menacing',
'5. Describe a fortified area',
"6. A Woman's Name",
'7. Describe a large area of mass'
],
hints: [
"Hint: Use words like Rebel, Hell's Angels",
'Hint: Use a word such as Base, Bunker, Foxhole, Bedroom',
'Hint: Use words like Empire, Ottoman, Mongols',
'Hint: Freeze Ray, Leftover Fruitcake',
'Hint: Castle, Bunker, Planet',
'Hint: Astrid, Diana, Mononoke, Peach',
'Hint: Use words such as Galaxy, Planet, Wal Mart'
],
answers: []
};
I'd like to take the user's inputs of answers and have it passed along as props into another component such as properName1={this.state.value1} and so on, I know that's mapping an array of answers, I'm just unsure how to do this.
Below is the rest of the main component.
this.submitHandler = this.submitHandler.bind(this);
this.renderQuestion = this.renderQuestion.bind(this);
this.onChange = this.onChange.bind(this);
}
renderQuestion() {
const { questions, hints, active, value } = this.state;
if (active >= questions.length)
return <Crawler style={{ width: '500px', position: 'absolute' }} />;
return questions.filter((quest, index) => index === active).map(quest => ( // get next question // map over selected question, the key prop allows react to
<FormElement
key={active}
text={quest}
hint={hints}
value={value}
onChange={this.onChange}
/>
));
}
onChange(e) {
this.setState({ value: e.target.value });
}
submitHandler(e) {
e.preventDefault();
const answers = [...this.state.answers, this.state.value]; //push new value to answsers array without mutation
const value = ''; // clear input
const active = this.state.active + 1; // index pointer
this.setState({ answers, value, active });
}
render() {
return (
<MainContainer>
<DivStyle>
{/* Form Wrapper */}
<form onSubmit={this.submitHandler}>
{this.renderQuestion()}
<SubmitButton type="submit">Submit</SubmitButton>
</form>
<ul>
{this.state.answers.map((ans, index) => {
return (
<li key={index}>
{ans}
</li>
);
})}
</ul>
</DivStyle>
</MainContainer>
);
}
}
Child Component 1
This is the dumb component where the questions (and where I want the hints as well) generated
class FormElement extends Component {
constructor() {
super();
}
componentDidMount() {
//focus text input upon mounting component
this.textInput.focus();
}
render() {
const { text, hint, value, onChange } = this.props;
return (
<div>
<InputQuestion>
{text}
{hint}
</InputQuestion>
<input
className="inputstyling"
ref={el => {
this.textInput = el;
}}
onChange={onChange}
type="text"
value={value}
/>
</div>
);
}
}
export default FormElement;
At the moment, "hint" brings in all the hints at once, instead of one at a time and in order.
Child Component 2
Finally the props needed to pass go here. The array is throwing me as I've never passed props via an array
class Crawler extends Component {
constructor() {
super();
this.state = {
properName1: 'Rebel',
noun1: 'frog',
properName2: 'Empire',
properName3: 'DEATH STAR',
noun2: 'station',
noun3: 'planet',
personsName1: 'Leia',
noun4: 'starship',
pluralnoun1: 'people',
noun5: 'galaxy'
};
}
render() {
return (
<ContainLeft style={{ padding: 0 }}>
<CrawlHolder>
<div class="fade" />
<section className="star-wars">
<div className="crawl">
<div className="title">
<p>Episode IV</p>
<h1>A New Hope</h1>
</div>
<p>
It is a period of civil war.
{' '}
{this.props.properName1}
{' '}
spaceships, striking from a hidden
{' '}
{this.props.noun1}
, have won their first victory against the evil Galactic
{' '}
{this.props.properName2}
.
</p>
<p>
During the battle,
{' '}
{this.props.properName1}
{' '}
spies managed to steal secret plans to the
{' '}
{this.props.properName2}
's ultimate weapon, the
{' '}
{this.props.properName3}
, an armored
{' '}
{this.props.noun2}
{' '}
with enough power to destroy an entire planet.
</p>
<p>
Pursued by the Empire’s sinister agents, Princess
{' '}
{this.props.personsName1}
{' '}
races home aboard her starship, custodian of the stolen plans that can save her people and restore freedom to the
{' '}
{this.props.noun3}
…
</p>
</div>
</section>
</CrawlHolder>
</ContainLeft>
);
}
}
export default Crawler;
Thank you for your help
the function in map has an optional second param, the index of the current element so you could do:
return questions.filter((quest, index) => index === active).map((quest,i) => (
<FormElement
key={active}
text={quest}
hint={hints[i]}
value={value}
onChange={this.onChange}
/>
));
EDIT:
I see now you are rendering only one question at a time so a map isn't needed I think; since you have the index of the current question (active) I think you could just
return (
<FormElement
text={questions[active]}
hint={hints[active]}
value={value}
onChange={this.onChange}
/>
)

Categories

Resources