How to create a horizontal timeline in React - reactjs

There are two questions already on Stackoverflow:
Create Horizontal Timeline With
React How to create responsive horizontal timeline
None of them have any accepted answer. And also my question is specifically related to react-horizontal-timeline.
I'm creating my personal portfolio and I wish to show my education/college journey.
The author has given the code:
const VALUES = [ /* The date strings go here */ ];
export default class App extends React.Component {
state = { value: 0, previous: 0 };
render() {
return (
<div>
{/* Bounding box for the Timeline */}
<div style={{ width: '60%', height: '100px', margin: '0 auto' }}>
<HorizontalTimeline
index={this.state.value}
indexClick={(index) => {
this.setState({ value: index, previous: this.state.value });
}}
values={ VALUES } />
</div>
<div className='text-center'>
{/* any arbitrary component can go here */}
{this.state.value}
</div>
</div>
);
}
}
Since I'm coming from Angular and MVC frameworks, I didn't understand what this HorizontalTimeline is doing there. Is there anything I need to import? I'm asking this because the code is giving this error:
Line 13:22: 'HorizontalTimeline' is not defined react/jsx-no-undef
Looks like the compiler is not able to recognize HorizontalTimeline.
And also I would like to have it as a separate component for example <MyTimeline> or so. Why would I clutter my App.js. Hope I was able to explain. Please pitch in.

You need to import the component.
Unfortunately the vendor's documentation doesn't include the import statement. Further unfortunately still, the vendor's demo imports it directly from their source code. Which is fine if you're using their source code, but useless if you're installing their npm package.
Unless the IDE can find the import for you (VS Code should be able to, but anything could be preventing that) then best guesses would be:
import HorizontalTimeline from 'react-horizontal-timeline';
or:
import { HorizontalTimeline } from 'react-horizontal-timeline';

Check out this timeline component if you are interested. I think it's better for your need and also has much more clear documentation and a better horizontal mode.
react-chrono
Here is an example of a responsive timeline using the react-chrono component. I have also added some code that will automatically change to the vertical mode which is better for mobile view.
import React from "react";
import { Chrono } from "react-chrono";
class EducationTimeline extends React.Component {
constructor(props) {
super(props);
this.state = { matches: window.matchMedia("(min-width: 768px)").matches };
}
componentDidMount() {
const handler = (e) => this.setState({ matches: e.matches });
window.matchMedia("(min-width: 768px)").addEventListener("change", handler);
}
render() {
const items = [
{
title: "example",
cardTitle: "example",
cardSubtitle: "example",
cardDetailedText: "example",
}
];
return (
<div style={{ width: "500px", height: "400px" }}>
<Chrono
items={items}
mode={this.state.matches ? "HORIZONTAL" : "VERTICAL"}
slideShow={false}
itemWidth={"250"}
hideControls={true}
cardHeight={100}
borderLessCards={true}
theme={{
primary: "#01bf71",
secondary: "#010606",
cardBgColor: "#f7f8fa",
cardForeColor: "#010606",
titleColor: "#fff",
}}
></Chrono>
</div>
);
}
}
export default EducationTimeline;

Related

Make onClick on <li> go to another view

i'm new on react and i'm doing a project, actually it's a to do list, and i need to make a router that when i click on my item on they send me to details of this item. Here's my actual code. That's my app.js
import React, { Component } from 'react';
import List from './List';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
term: '',
items: []
};
}
onChange = (event) => {
this.setState({ term: event.target.value });
}
onSubmit = (event) => {
event.preventDefault();
this.setState({
term: '',
items: [...this.state.items, this.state.term]
});
}
render() {
return (
<div style={{display: "flex", flexDirection: 'column', alignItems: "center", margin: 5}}>
<form onSubmit={this.onSubmit}>
<input style={{borderRadius: 3, borderColor: "black"}}
value={this.state.term} onChange={this.onChange} />
<button style={{borderRadius: 3, borderColor: "black"}}>
Adicionar</button>
</form>
<List items={this.state.items}/>
</div>
);
}
}
And here my List.js
import React from 'react';
const List = ({ items }) => (
<ul style={{display: "block", listStyleType: "none", backgroundColor: "red"}}>
{
items && items.map((item, index) => <li key={index}>{item}</li>)
}
</ul>
);
export default List;
So i now i will have to use some library to make the route, but first i need to know how i make my itens clickable and when i click they return something that can i redirect to a detail view. Make sense?
And there's a library that you guys recommend to do this job?
Thank you
As i think, you need to use custom links in react As:
1) npm i react-router-dom
2) import {Link} from 'react-router-dom';
3) And finally use <Link to="paste_your_url_here" />
you can also place <Link to='' /> inside of ul, li.
You could add a onClick on each li that redirects to the url you want (implementation may vary based off the lib you choose to handle rooting), but that's not really a good idea since in HTML li are not supposed to be clickable.
What I would do is have a link (again, see with the routing lib - generally it provides a <Link /> component) inside each <li>. This provides better semantic to your code.
Hope this makes sense.
As for a good library... React-router or reach-router are pretty nice!

AsyncTypeahead results are not being displayed on screen

I have been trying to integrate the AsyncTypeahead functionality from the react-bootstrap-typeahead library. I am trying to use this with the TheMovieDB search api. I can see from the developer console output that I am making the api requests successfully, and the results are being passed back into to the AsyncTypeahead component, but it is saying "No matches found." on my screen.
I have copied most of the code from their github for the AsyncTypeahead implementation, and can even see the reponse results are being passed down into the options the Typeahead component. So I am unsure what I am missing to get them to be displayed on the screen like it is in their examples.
My search implementation component
import React from 'react';
import {AsyncTypeahead} from 'react-bootstrap-typeahead';
import axios from 'axios';
import SearchResultMenuItem from './SearchResultMenuItem';
//Needs to be a class based component, as we have to handle state
class SearchBar extends React.Component {
state = {
term: '',
options: [],
isLoading: false
};
onFormSubimit = (event) => { //Arrow function makes sure the value of 'this' is always the instance of the search bar
event.preventDefault(); //Stops browser from submitting form automatically and refreshing the pagee
this.props.onSubmit(this.state.term);
}
onHandleSearch = async (term) => {
this.setState({isLoading: true});
const response = await axios.get('https://api.themoviedb.org/3/search/movie?api_key=c055530078ac3b21b64e0bf8a0b3b9e1&language=en-US&page=1&include_adult=false', {
params: { query: term }
});
//Extract details from the search
const searchResults = response.data.results.map((i) => ({
title: i.original_title,
id: i.id,
}));
this.setState({
isLoading: false,
options: searchResults
})
}
render() {
return (
<div className="ui segment">
<form onSubmit={this.onFormSubimit} className="ui form">
<div className="field">
<label>Image Search</label>
<AsyncTypeahead
{...this.state}
labelKey="original_title"
isLoading={this.state.isLoading}
onSearch={this.onHandleSearch}
placeholder="Enter a Movie Title..."
renderMenuItemChildren={(option, props) => (
<SearchResultMenuItem key={option.id} item={option} />
)}
/>
</div>
</form>
</div>
);
}
}
export default SearchBar;
My SearchResultMenuItem component
import PropTypes from 'prop-types';
import React from 'react';
const SearchResultMenuItem = ({item}) => (
<div>
<img
style={{
height: '24px',
marginRight: '10px',
width: '24px',
}}
/>
<span>{item.original_title}</span>
</div>
);
SearchResultMenuItem.propTypes = {
item: PropTypes.shape({
original_title: PropTypes.string.isRequired,
}).isRequired,
};
export default SearchResultMenuItem;
Expected Results
I expect a list of movie names to be shown as options to choose, that come from the response from the query of TheMovieDB's api.
Actual Results
The UI simple displays a No matches found message on the screen.
I get a warning message on the console saying Warning: [react-bootstrap-typeahead] The prop id is required to make Menu accessible for users of assistive technologies such as screen readers. I have tried googling this, but have not found any solutions.
See below for screenshots from the developer console
You can see results from the search api in the options array above
Update UI After Fix
Please use labelKey="title" in AsyncTypeahead since your options object has title.Also, in SearchResultMenuItem change <span>{item.title}</span>, also change the props as well title: PropTypes.string.isRequired. This should resolve the issue. Also, to remove the id warning just provide a unique id name to AsyncTypeahead component like id="some_unique_id" (it can be any random id)
Check if the array exists before setting the value, something like this
onInputChange={(v) => {
if (v.length) {
console.log(v, "KDDDDDDDK");
setValue(v[0]);
}
}}

Custom Read More in React.js

As you can see this image, "+Las mor" is a "see more" button, which when clicked expands the whole paragraph written above.
I need React code for this to be functional. Any help will be appreciated.
I am also attaching the code upon which this functionality is to be applied.
<section id="section-2">
<h4>Om mig</h4>
<p className="para">
{about}
</p>
</section>
<p style={{color:'#d39176'}}>
<img src={plus1} />
Läs mer
</p>
You probably want a button that toggles the state of expanded text onClick. Upon hitting the button you would set the state to the opposite of what it was. Here's a working example I wrote with React and Reactstrap. I just tested it locally. Here's a video demo of what you will see: https://screencast.com/t/in5clDiyEcUs
import React, { Component } from 'react'
import { Container, Button } from 'reactstrap'
class App extends Component {
constructor(props) {
super(props)
this.state = {
expanded: false //begin with box closed
}
}
//function that takes in expanded and makes it the opposite of what it currently is
showButton = () => {
this.setState({ expanded: !this.state.expanded })
}
render() {
const { expanded } = this.state
return (
<Container style={ { justifyContent: 'center', alignItems: 'center' } }>
<div>Always visable text.</div>
<Button onClick={ this.showButton }>Expand</Button>
{
expanded && //show if expanded is true
<div>Extended Text Here</div>
}
</Container>
)
}
}
export default App

WithStyles not working in Material UI for React

I have an app using Material UI Beta where I try to style a simple component as follows:
import { MuiThemeProvider } from 'material-ui/styles';
const styles = theme => ({
container: {
display: 'flex',
flexWrap: 'wrap',
},
textField: {
marginLeft: 200,
marginRight: theme.spacing.unit,
width: 200,
},
menu: {
width: 200,
},
});
export const CreateJob = (props) => {
const { classes } = props;
let confirmDelete = () => {
const r = window.confirm("Confirm deletion of job");
return r === true;
};
return (
<MuiThemeProvider>
<div>
<form onSubmit={props.isEditting ? props.handleEdit : props.handleSubmit} noValidate autoComplete="off">
<h2>Update job details</h2>
<TextField
error={props.jobIdError !== ''}
helperText={props.jobIdError || "Example: ES10"}
autoFocus
margin="dense"
id="jobId"
label="Job ID"
name="jobid"
fullWidth
onChange={props.handleInputChange('jobId')}
value={props.jobId} />
</form>
</div>
</MultiThemeProvider>
I then use this in my parent component as follows:
<CreateJob open={this.state.open} />
However, this yields the following error:
TypeError: Cannot read property 'classes' of undefined
this.state is not defined in your code. In the example, state is defined as
state = {
name: 'Cat in the Hat',
age: '',
multiline: 'Controlled',
currency: 'EUR',
};
Sorry I'm kinda late with an answer, but I just found this question while searching for another solution.
I'm going to assume you also imported withStyles.
Firstly, you don't need to export both the simple component and the enhanced one:
export const CreateJob = props => {...} // lose the 'export'
export default withStyles(styles)(CreateJob); // only export here
Secondly, a real problem: <MuiThemeProvider> should be placed around your highest component(usually the <App> component that you render in your entry point file), so you can customize the default theme to your liking for the whole app; see their example here. I'm not sure, but this might even solve your problem, since that should have thrown another error like in this issue.
I just hope this helps someone, but I cannot be sure about what your exact problem is without the complete component file.

React props.children width

Is there anyway of getting the width of React component children. I have wrapper component called for lack of name Container and I add children of div type from Component1 to it. See below example.
I'm wondering if there is a way to get the width of each div child in Container when it mounts.
UPDATED NOTE:
The reason I'm trying to get the containers children widths is so I can dynamical set the containers width based on the total number of children. By setting the containers width to the number of children's width then I can allow for some horizontal scrolling effects I want to do.
Component 1
export default class Component1 extends Component{
render(){
return(
<Container>
<div className="large-box"/>
<div className="large-box-dark"/>
</Container>
)
}
}
Now my Container component.
export default class Container extends Component{
componentDidMount(){
this.props.children.forEach(( el ) => {
// get each child's width
console.log("el =", el);
});
}
render() {
return (
<div className="scroller" ref={(scroller) => { this.scroller = scroller }}>
{this.props.children}
</div>
)
}
}
You can use refs. Although you should avoid touching the DOM until and unless there is no other way. But here we go.
Give your child components a ref which is escape hatch provided by React to access the DOM(with a warning to use other methods before coming to this).
Child Component
class ChildComp extends Component {
getWidth = () => {
//Access the node here and get the width
return this.childNode.offsetWidth(); //or clientWidth();
}
render() {
return (
<div ref={(r) => {this.childNode = r}}>
</div>
);
}
}
Parent Component:
class ParentComp extends Component {
componentDidMount() {
//Access the child component function from here
this.childComponent.getWidth();
}
render() {
return (
<ChildComp
ref={(r) => {this.childComponent = r}} />
);
}
}
But do remember use the above method when there is no way of getting the thing done declaratively.
I would say that technically it doesn't seem possible. The JSX
<div className="large-box"/>
does not refer to DOM element, (which has a width once it's been rendered) but to a React element, which is an in-memory object describing how to make an DOM element. Since the React element isn't rendered or even connected to the actual DOM in the browser, it can't know the width.
Remember that React can be rendered on the server -- there's no way the server can know what the browser on different computer is going to display.
I'd also echo what Pedro Nascimento noted -- this solution is probably best solved some other way, but without context, it's difficult to help.
then try to get the ref of "DivColorOpacy"! and put whatever you want on parent and cutom your behavior. That's silly but that do the job.
with this css
.DivColorOpacy{
height: max-content;
width: max-content;
position: relative;
}
import { Component } from "react";
import * as React from "react";
interface DivColorOpacyProps {
backgroundColor: string,
opacity: number,
}
export class DivColorOpacy extends Component<DivColorOpacyProps, any>{
componentDidMount(){
}
render() {
const { backgroundColor, opacity } = this.props;
return <div className="DivColorOpacy">
{this.props.children}
<div style={{
position: "absolute",
zIndex: -1,
backgroundColor,
opacity,
width: "100%",
height: "100%",
top:0
}} />
</div>
}
}

Resources