Cards not staying horizontally aligned after running for loop - css

I am trying to align the cards horizontally and it worked fine before, but after I pulled the data from the API and used for-loop to show the data in the browser the cards stacked vertically.
Here is the javascript code
import React, { useEffect, useState } from "react";
import "./Home.css";
import axios from "axios";
function Home() {
const [ApiData, setApiData] = useState([]);
useEffect(() => {
async function getData() {
const data = await axios.get("http://127.0.0.1:8000/?format=json");
console.log(data.data);
setApiData(data.data.Product);
return data;
}
getData();
}, []);
return (
<>
{ApiData.map((obj, index) => {
let x;
for (x in obj) {
return (
<div className="container">
<div className="card">
{/* <img className="store-img" src={obj.image} alt="" /> */}
<span>{obj.name}</span>
</div>
</div>
);
}
})}
</>
);
}
export default Home;
Here is the CSS
.container {
display: flex;
flex-direction: row;
flex: 1;
}
.card {
display: flex;
flex-direction: row;
margin: 10px 50px 10px 50px;
justify-content: center;
align-items: baseline;
flex: 1;
height: 16em;
width: 12em;
max-height: 18em;
max-width: 16em;
border-radius: 4px;
box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
}
.store-img {
height: 100%;
width: 100%;
}
The JSON File which I am using for this code

You are creating a new div with class container in every loop iteration. Put loop inside container div.
import React, { useEffect, useState } from "react";
import "./Home.css";
import axios from "axios";
function Home() {
const [ApiData, setApiData] = useState([]);
useEffect(() => {
async function getData() {
const data = await axios.get("http://127.0.0.1:8000/?format=json");
console.log(data.data);
setApiData(data.data.Product);
return data;
}
getData();
}, []);
return (
<div className="container">
{ApiData.map((obj, index) => {
let x;
for (x in obj) {
return (
<div className="card">
{/* <img className="store-img" src={obj.image} alt="" /> */}
<span>{obj.name}</span>
</div>
);
}
})}
</div>
);
}
export default Home;

Wrap your divs with class d-flex if you are using bootstrap or create a class with property display: flex. Try below code :
import React, { useEffect, useState } from "react";
import "./Home.css";
import axios from "axios";
function Home() {
const [ApiData, setApiData] = useState([]);
useEffect(() => {
async function getData() {
const data = await axios.get("http://127.0.0.1:8000/?format=json");
console.log(data.data);
setApiData(data.data.Product);
return data;
}
getData();
}, []);
return (
<div className="d-flex">
{ApiData.map((obj, index) => {
let x;
for (x in obj) {
return (
<div className="container" key={index}>
<div className="card">
{/* <img className="store-img" src={obj.image} alt="" /> */}
<span>{obj.name}</span>
</div>
</div>
);
}
})}
</div>
);
}
export default Home;

Related

react javascript css grid masonry responsive issue

The responsive part of my code doesn't work well, as the browser width shrink, the height of image-list doesn't adjust properly. Why?
here is my imageList.js
import React from "react";
import "./imageList.css";
import ImageCard from "./imageCard";
const imageList = ({ images }) => {
const renderList = images.map((image) => {
return <ImageCard key={image.id} image={image} />;
});
return <div className="image-list">{renderList}</div>;
};
export default imageList;
here is my imageList.css
.image-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
grid-gap: 0px 10px;
grid-auto-rows: minmax(1px, auto);
}
.image-list img {
width: 250px;
grid-row-end: span 2;
}
here is my imageCard.js
import React, { useRef, useState, useEffect } from "react";
const ImageCard = ({ image }) => {
const imageRef = useRef(null);
const [spans, setSpans] = useState(0);
useEffect(() => {
imageRef.current.addEventListener("load", handleSpansChange);
}, [spans]);
const handleSpansChange = () => {
const height = imageRef.current.clientHeight;
const spans = Math.ceil(height) + 10;
setSpans(spans);
console.log(spans);
};
return (
<div style={{ gridRowEnd: `span ${spans}` }}>
<img src={image.urls.regular} alt={image.description} ref={imageRef} />
</div>
);
};
export default ImageCard;
Notes that ImageList is a component render in app.js file

How to make container expand up instead of down when component added?

I have a React component that asks user to upload profile photo. In the same view, the user can see a greyed out 'next' button. When the photo is uploaded and displayed, the user sees a horizontal scrolling bar to adjust photo size that comes between the photo and next button. My goal is to keep the next button in view at all times but also keep all elements of the component in the same order. Right now, a user has to scroll down to see the 'next' once the image is loaded. I've been experimenting with flex and position and even a ternary in the JSX to add paddingBottom when !isLoading. Can someone give a good direction so that the user doesn't have to scroll down to see the next' button?
Here is my component:
import React, { useContext, useState, useEffect } from 'react';
import styled from 'styled-components';
import Typography from 'components/Typography';
// import { useMediaQuery } from 'beautiful-react-hooks';
import { medium } from 'constants/mediaQueries';
import AppButton from 'components/AppButton';
import useFetcher from 'hooks/useFetcher';
import { getMeApi } from 'utils/apiRoutes';
import Loader from 'components/Loader';
import routePaths from 'containers/Router/routePaths';
import { useHistory, useLocation } from 'react-router-dom';
import { UserContext } from 'containers/context/UserContext';
import checkmarkImage from 'assets/images/checkmark.svg';
import { useDispatch } from 'react-redux';
import BackArrowGrid from 'components/Auth/BackArrowGrid';
import UploadImageInput from 'components/UploadImageInput';
import { setPlainLogo, setInitialLogo } from '../../store/header/actions';
// const MOBILE = 250;
// const DESKTOP = 356;
const Container = styled.div`
width: 100%;
position: relative;
align-items: center;
justify-content: flex-end;
display: flex;
flex-direction: column;
border: pink solid 2px;
`;
const ButtonContainer = styled.div`
margin-top: 32px;
display: flex;
align-items: center;
flex-direction: row;
div {
margin-bottom: 16px;
}
img {
margin-left: 8px;
}
`;
const Title = styled(Typography)`
font-size: 40px;
text-transform: uppercase;
margin-bottom: 64px;
#media (min-width: ${medium}) {
margin-bottom: 64px;
}
`;
const ButtonContent = styled.span`
display: flex;
align-items: center;
`;
const Form = styled.div`
width: 100%;
display: flex;
flex-direction: column;
align-self: flex-end;
border: yellow solid 2px;
.empty {
display: none;
}
#media (min-width: ${medium}) {
flex-direction: column;
align-items: center;
min-height: auto;
.empty {
display: block;
width: 118px;
}
}
`;
const UploadPicture = () => {
const { fetcher, error, isLoading } = useFetcher();
const { setUserFromLogin } = useContext(UserContext);
const history = useHistory();
const location = useLocation();
// const isDesktop = useMediaQuery(`(min-width: ${medium})`);
const dispatch = useDispatch();
const [image, setImage] = useState({});
const uploadImage = async () => {
const result = await fetcher({
url: getMeApi,
method: 'POST',
body: {
photo: image.croppedImage.source,
},
});
if (result.errors) {
return;
}
setUserFromLogin(result.user);
if (location.profile) {
history.push(routePaths.Profile);
} else {
history.push(routePaths.Welcome);
}
};
useEffect(() => {
dispatch(setPlainLogo());
return () => dispatch(setInitialLogo());
}, []);
const onPictureChange = newImage => {
setImage(prevImage => ({
...prevImage,
...newImage,
}));
};
return (
<>
<BackArrowGrid>
<Container>
<Title h1>Profile photo</Title>
<Form>
<div className="empty"> </div>
<UploadImageInput
onChange={onPictureChange}
image={image}
placeholder={
"Upload a profile photo. This is how you'll appear to other Color TV users."
}
height={356}
width={356}
horizontalPadding={60}
aspect={1}
round
/>
<ButtonContainer>
{error && (
<Typography error>
Error while uploading the image, please try again.
</Typography>
)}
<AppButton
primary={!!image.croppedImage}
disabled={isLoading || !image.croppedImage}
onClick={uploadImage}
noBorder
>
<ButtonContent>
{isLoading ? (
<Loader className="center" size={30} />
) : (
'Next'
)}
{!isLoading && !!image.croppedImage && (
<img src={checkmarkImage} alt="checkmark_icon" />
)}
</ButtonContent>
</AppButton>
</ButtonContainer>
</Form>
</Container>
</BackArrowGrid>
</>
);
};
export default UploadPicture;
Since this component is part of a larger code base, I'm not sure how to run a snippet of it but here are 2 photos - before and after. Once the photo is uploaded, the next button drops out of view because the slider component is added in. I want to next button to stay in view no matter how the browser size.

React tutorial - css not loading

I'm working through a tutorial on React/Spring Boot located here. All was going well, including the initial display of groups on React.
However, once the React piece was refactored to separate the group list into a separate module and a nav bar was added, I get a display without any css rendering.
Here is the code:
App.js
import React, { Component } from 'react';
import './App.css';
import Home from './Home';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import GroupList from './GroupList';
class App extends Component {
render() {
return (
<Router>
<Switch>
<Route path='/' exact={true} component={Home}/>
<Route path='/groups' exact={true} component={GroupList}/>
</Switch>
</Router>
)
}
}
export default App;
AppNavbar.js
import React, { Component } from 'react';
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
import { Link } from 'react-router-dom';
export default class AppNavbar extends Component {
constructor(props) {
super(props);
this.state = {isOpen: false};
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState({
isOpen: !this.state.isOpen
});
}
render() {
return <Navbar color="dark" dark expand="md">
<NavbarBrand tag={Link} to="/">Home</NavbarBrand>
<NavbarToggler onClick={this.toggle}/>
<Collapse isOpen={this.state.isOpen} navbar>
<Nav className="ml-auto" navbar>
<NavItem>
<NavLink
href="https://twitter.com/oktadev">#oktadev</NavLink>
</NavItem>
<NavItem>
<NavLink href="https://github.com/oktadeveloper/okta-spring-boot-react-crud-example">GitHub</NavLink>
</NavItem>
</Nav>
</Collapse>
</Navbar>;
}
}
Home.js
import './App.css';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
import { Button, Container } from 'reactstrap';
class Home extends Component {
render() {
return (
<div>
<AppNavbar/>
<Container fluid>
<Button color="link"><Link to="/groups">Manage JUG Tour</Link></Button>
</Container>
</div>
);
}
}
export default Home;
GroupList.js
import React, { Component } from 'react';
import { Button, ButtonGroup, Container, Table } from 'reactstrap';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
class GroupList extends Component {
constructor(props) {
super(props);
this.state = {groups: [], isLoading: true};
this.remove = this.remove.bind(this);
}
componentDidMount() {
this.setState({isLoading: true});
fetch('api/groups')
.then(response => response.json())
.then(data => this.setState({groups: data, isLoading: false}));
}
async remove(id) {
await fetch(`/api/group/${id}`, {
method: 'DELETE',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}).then(() => {
let updatedGroups = [...this.state.groups].filter(i => i.id !== id);
this.setState({groups: updatedGroups});
});
}
render() {
const {groups, isLoading} = this.state;
if (isLoading) {
return <p>Loading...</p>;
}
const groupList = groups.map(group => {
const address = `${group.address || ''} ${group.city || ''} ${group.stateOrProvince || ''}`;
return <tr key={group.id}>
<td style={{whiteSpace: 'nowrap'}}>{group.name}</td>
<td>{address}</td>
<td>{group.events.map(event => {
return <div key={event.id}>{new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: '2-digit'
}).format(new Date(event.date))}: {event.title}</div>
})}</td>
<td>
<ButtonGroup>
<Button size="sm" color="primary" tag={Link} to={"/groups/" + group.id}>Edit</Button>
<Button size="sm" color="danger" onClick={() => this.remove(group.id)}>Delete</Button>
</ButtonGroup>
</td>
</tr>
});
return (
<div>
<AppNavbar/>
<Container fluid>
<div className="float-right">
<Button color="success" tag={Link} to="/groups/new">Add Group</Button>
</div>
<h3>My JUG Tour</h3>
<Table className="mt-4">
<thead>
<tr>
<th width="20%">Name</th>
<th width="20%">Location</th>
<th>Events</th>
<th width="10%">Actions</th>
</tr>
</thead>
<tbody>
{groupList}
</tbody>
</Table>
</Container>
</div>
);
}
}
export default GroupList;
App.css
.App {
text-align: center;
}
.container, .container-fluid {
margin-top: 20px;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
#media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
#keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
Any help much appreciated.
If I understand correctly, you are not using the classes you define in the App.css file.
To use the styles add the className property to the element where you need the styling.
For example, in your home.js:
<Button className="App-link" color="link"><Link to="/groups">Manage JUG Tour</Link></Button>
As in the example App.js you linked:
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<div className="App-intro">
<h2>JUG List</h2>
{groups.map(group =>
<div key={group.id}>
{group.name}
</div>
)}
</div>
</header>
</div>
);
It was my fault, I just needed to add this into index.js, which was right in the instructions:
import 'bootstrap/dist/css/bootstrap.min.css';

Generating a grid-container style when mapping through an array

Summary
I have the following chart image (react-vis):
My React code (below) is creating this with a map through an array. How should I modify the code so the charts go across the page and are contained in some kind of fluid wrapper like this:
What have I looked at/tried?
I have basic understanding of HTML and CSS but would not know how to approach this kind of task and modify the code.
Would I need to use something like this and integrate with the code above?
<div class="grid-container">
<div className="grid-item">1</div>
<div className="grid-item">2</div>
<div className="grid-item">3</div>
<div className="grid-item">4</div>
</div>
I would like to understand an effective way to do this please using CSS, bootstrap or whatever would be considered best practice.
Code:
MyComp.js
import React, { useState, useEffect } from "react"
import Example from "./plotBar.js"
function getJson() {
return fetch("http://secstat.info/testthechartdata3.json")
.then(response => response.json())
.catch(error => {
console.error(error)
})
}
const MyComp = () => {
const [list, setList] = useState([])
useEffect(() => {
getJson().then(list => setList(list))
}, [])
return (
<div>
{list.map((data, index) => (
<Example
key={index}
data={data.map(({ id, count }) => ({
x: id,
y: count,
}))}
/>
))}
</div>
)
}
export default MyComp
plotBar.js
import React from "react"
import {
XYPlot,
XAxis,
YAxis,
VerticalGridLines,
HorizontalGridLines,
VerticalBarSeries,
} from "react-vis"
export default function Example({ data }) {
return (
<XYPlot margin={{ bottom: 70 }} xType="ordinal" width={300} height={300}>
<VerticalGridLines />
<HorizontalGridLines />
<XAxis tickLabelAngle={-45} />
<YAxis />
<VerticalBarSeries data={data} />
</XYPlot>
)
}
The data looks like this:
URL for JSON
http://secstat.info/testthechartdata3.json
You should read about flex and flex-flow, after that it just applying minor styling, this is CSS-in-JS example:
const Item = styled(Example)``;
const Container = styled.div`
display: flex;
flex-flow: row wrap;
background: lightgray;
padding: 0.5rem;
${Item} {
margin: 0.5rem;
padding: 1rem;
background: white;
}
`;
const Item = styled(Example)``;
const Container = styled.div`
display: flex;
flex-flow: row wrap;
background: lightgray;
padding: 0.5rem;
${Item} {
margin: 0.5rem;
padding: 1rem;
background: white;
}
`;
export default function Example({ data, className }) {
return (
<XYPlot className={className} xType="ordinal" width={200} height={200}>
<VerticalGridLines />
<HorizontalGridLines />
<XAxis tickLabelAngle={-45} />
<YAxis />
<VerticalBarSeries data={data} />
</XYPlot>
);
}
const list = // fetch on mount
const MyComp = () => {
return (
<Container>
{list.map((data, index) => (
<Item
key={index}
data={data.map(({ id, count }) => ({
x: id,
y: count,
}))}
/>
))}
</Container>
);
};

Having trouble getting rid of the blue highlight

I've been working on a section with expandable/collapsible sections. When I click on a section to expand or collapse it, a blue focus area shows up but it is placed on a weird angle. I don't know what is causing it and would like a solution to either get rid of it or place it back at the normal horizontal angle. Does anybody have any suggestions as to how to fix this?
I am using a Macbook and Chrome browser.
The entire grey block that this component appears in is placed at an angle as you can see from the top of the image attached below but in the reverse direction from the highlighted focus area.
My css:
#import '../../theme/variables.css';
.rotatedSection {
padding-bottom: 2rem;
}
.container {
max-width: 64rem;
margin: 0 auto;
display: flex;
padding: 2rem 0;
#media screen and (max-width: 68rem) {
margin: 0 3rem;
}
}
.accordianContainer {
flex: 1;
margin-right: 2rem;
min-width: 500px;
#media screen and (max-width: $tablet-lg-max-width) {
margin-right: 0;
}
#media screen and (max-width: 900px) {
min-width: 0;
}
}
.imageContainer {
flex: 1;
margin-left: 2rem;
max-height: 300px;
display: flex;
justify-content: center;
img {
flex: 1;
}
#media screen and (max-width: $tablet-lg-max-width) {
margin-left: 0;
}
}
.heading {
composes: h2 from 'theme/text';
margin-left: auto;
margin-right: auto;
}
My react code:
import React, {Component, PropTypes} from 'react';
import RotatedSection from 'components/RotatedSection';
import AccordionItem from './AccordionItem';
import css from './styles.css';
class AccordionSectionWithImage extends Component {
constructor (props) {
super(props);
this.state = {
activeIndex: null,
};
this.onOpen = this.onOpen.bind(this);
this.onClose = this.onClose.bind(this);
this.setActive = this.setActive.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
}
onOpen = (index) => {
this.setActive(index);
};
onClose = (callback = () => null) => {
this.setActive(null);
callback();
};
setActive = (activeIndex) => this.setState({activeIndex});
handleClickOutside = () => this.props.collapseOnBlur && this.onClose();
render () {
const {
entry: {
items,
heading,
image,
},
showIndex,
classNames,
meta = {},
} = this.props;
const {routeParams, toggleHamburger} = meta;
const {activeIndex} = this.state;
return (
<RotatedSection color='whisper' className={css.rotatedSection}>
<div className={css.container}>
<div className={css.accordianContainer}>
<h2 className={css.heading}>{heading}</h2>
{items && items.map((item, index) => (
<AccordionItem
key={index}
showIndex={showIndex}
entry={item}
meta={{
position: index,
isOpen: (index === activeIndex),
onOpen: () => this.onOpen(index),
onClose: () => this.onClose(),
onChildClick: () => this.onClose(toggleHamburger),
routeParams,
}}
classNames={classNames}
/>
))}
</div>
<div className={css.imageContainer}>
<img src={image && image.fields && image.fields.file.url} alt='Educational assessment' />
</div>
</div>
</RotatedSection>
);
}
}
AccordionSectionWithImage.propTypes = {
meta: PropTypes.object,
entry: PropTypes.object,
collapseOnBlur: PropTypes.bool,
showIndex: PropTypes.bool,
classNames: PropTypes.object,
};
export default AccordionSectionWithImage;
React component for individual section:
function AccordionItem (props) {
const {
meta: {
isOpen,
onOpen,
onClose,
},
entry: {
heading,
text,
},
} = props;
const handleClick = () => (isOpen ? onClose() : onOpen());
return (
<div className={css.itemContainer}>
<div className={css.innerContainer}>
<h3 className={css.heading} onClick={handleClick}>
<span className={css.titleText}>{heading}</span>
<i className={`zmdi zmdi-plus ${css.titleToggle}`} />
</h3>
{isOpen && (
<div className={css.contents}>
{text}
</div>
)}
</div>
</div>
);
}
For anybody else experiencing a similar problem:
Problem only appeared on mobile phones and the device mode of chrome inspector. It was due to the tap-highlight property.
Setting -webkit-tap-highlight-color to rgba(0,0,0,0) hid the problem but it's a non standard css property so the solution may not work for all devices/browsers/users.

Resources