Tabbed dates for api content - javascript

in React i am attempting to add my api content to a series of tabs, so you click on a tab, and it will show any listings in the api matching that date.
my attempt, code of component is below
import context from "./apiContext";
import styled from "styled-components";
const Tab = styled.button`
font-size: 20px;
padding: 10px 60px;
cursor: pointer;
opacity: 0.6;
background: white;
border: 0;
outline: 0;
${({ active }) =>
active &&
`
border-bottom: 2px solid black;
opacity: 1;
`}
`;
const ButtonGroup = styled.div`
display: flex;
`;
const tabs = ["18-08-20", "19-08-20", "20-08-20"];
function Movies() {
const { films } = useContext(context);
console.log(films);
const [active, setActive] = useState(tabs[0]);
return (
<>
<div className="movies">
<div className="title">
<h1>
Movies: <span />
All Releases
</h1>
<div className="tab-menu">
<ButtonGroup>
{tabs.map((tab) => (
<Tab
key={tab.label}
active={active === tab}
onClick={() => setActive(tab)}
>
{tab.label}
</Tab>
))}
</ButtonGroup>
</div>
</div>
<div className="content">
{films
.filter((item) => item.PreShowStartTime === Date.parse({ active }))
.map((item, index) => (
<div class="card" key={index}>
<div class="title">
<span class="title">{item.Title}</span>{" "}
<span>
{new Date(item.PreShowStartTime).toLocaleDateString("en", {
day: "2-digit",
month: "short"
})}
</span>
</div>
</div>
))}
</div>
</div>
</>
);
}
export default Movies;
To view with live API:
https://codesandbox.io/s/strange-hoover-8hik2
On line 24, is my test array of dates, which also should show as the label of the tab.

You are going to have to format the dates returned by the API according to what you have defined in the tabs constant in your code (or vice versa). Currently your API returns example 2020-08-18T14:00:00 but your tabs has format 18-08-20.
So for that, on your filter condition, you can use something like:
new Date(item.PreShowStartTime).toJSON().slice(2, 10) === active
where active is the active date selected on the tab
https://codesandbox.io/s/reverent-yonath-pv8i0?file=/src/Movies.js:1293-1357
As a side note, you were also accessing tab.label to print your tab labels. tabs does not contain a property named label...

Related

Make list items assume height of largest list item [duplicate]

This question already has answers here:
Equal height rows in CSS Grid Layout
(2 answers)
Closed 4 days ago.
I have a group of list items that are essentially containers with some text content. On mobile screen dimensions, I want the height of the containers to be set automatically according to the text content. However, I want all of the containers to assume the same height. So in this case, each container height would equal the height of the tallest container. The codesandbox link is here.
As you can see in the screenshot below, on a 414 px screen, the third container in the list is taller than the other two. I want all the containers to assume the height of the third one so that they can be the same.
How can I accomplish this? Here is the relevant lines of code:
<div className="App">
<h1>Lender Benefits</h1>
<ul className="list">
{lenderBenefits.map((benefit) => (
<li className="benefit_container">{benefit}</li>
))}
</ul>
</div>
.list {
padding-left: 0;
width: 100%;
}
.benefit_container {
/* height: max-content; */
border-radius: 24px;
margin-bottom: 12px;
padding: 2px 8px;
font-size: 16px;
font-weight: 600;
background-color: #f1e8dc;
display: flex;
align-items: center;
}
The CSS Grid approach (preferred):
const styleForEqualHeightRows = {
display: "grid",
gridAutoRows: "1fr",
gap: "1rem" // <= Optional
}
export default function App() {
return (
<div className="App">
<h1>Lender Benefits</h1>
<ul
className="list"
style={styleForEqualHeightRows}
>
{lenderBenefits.map((benefit) => (
<li
key={benefit}
className="benefit_container"
>
{benefit}
</li>
))}
</ul>
</div>
);
}
Here's one (hacky) approach using JavaScript:
import { useLayoutEffect, useRef, useState } from "react";
import "./styles.css";
const lenderBenefits = [
"No repayment, just reduced margins through credit",
"A cash advance without predatory interest",
"Works with your community to bring them in on your mission"
];
export default function App() {
const list = useRef();
const [style, setStyle] = useState({});
useLayoutEffect(() => {
async function handleResize() {
await setStyle({}); // Break from batch updates...
const lis = list.current.querySelectorAll("li");
const heights = [...lis].map((li) => li.offsetHeight);
const maxHeight = Math.max(...heights);
setStyle({ height: maxHeight + "px" });
}
handleResize();
window.addEventListener("resize", handleResize);
return function cleanUp() {
window.removeEventListener("resize", handleResize);
};
}, []);
return (
<div className="App">
<h1>Lender Benefits</h1>
<ul ref={list} className="list">
{lenderBenefits.map((benefit) => (
<li key={benefit} style={style} className="benefit_container">
{benefit}
</li>
))}
</ul>
</div>
);
}

How to change to Active when its clicked

I have a list and it's already set to Active, I mean the first list is already active, but my question is how to make the other list active also only when it's clicked and as long it's clicked and on the same page it keeps active.
return (
<div className="sidebar">
<div className="sidebarWrapper">
<div className="sidebarMenu">
<h3 className="sidebarTitle">Dashboard</h3>
<ul className="sidebarList">
<Link to="/" className="link">
<li className="sidebarListItem active">
<LineStyleIcon className="sidebarIcon" />
Home
</li>
</Link>
<li className="sidebarListItem">
<TimelineIcon className="sidebarIcon" />
Analytics
</li>
<li className="sidebarListItem">
<TrendingUpIcon className="sidebarIcon" />
Sales
</li>
</ul>
);
CSS:
.sidebarTitle {
font-size: 18px;
font-weight: 700;
color: rgb(187, 186, 186);
}
.sidebarList {
list-style: none;
padding: 5px;
}
.sidebarListItem.active,
.sidebarListItem:hover {
background-color: black;
color: white;
}
Your component will need to keep state for the currently active item. The initial state can be 0, the index of the first element in the list of items. When another item is clicked, we can setActive(index) for the index of the clicked item. Determining an individual item's active property is a derivative of the active state and the item's index, active == index -
function App({ items = [] }) {
const [active, setActive] = React.useState(0)
return items.map((value, index) =>
<ListItem
key={index}
value={value}
active={index == active}
onClick={_ => setActive(index)}
/>
)
}
function ListItem({ value, active, onClick }) {
return <button
type="button"
class={active ? "active" : "inactive"}
onClick={onClick}
children={value}
/>
}
ReactDOM.render(<App items={["🫐", "🍑", "🥝"]}/>, document.body)
button { border-color: dodgerblue; font-size: 4rem; background-color: white; }
.active { border-color: tomato; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
I would store the list items in an array so that I can loop over them later
const listItems = ["Home", "Analytics", "Sales"];
and I would create a state to store the active index
const [activeIndex, setActiveIndex] = React.useState(0);
and finally I would render the list items like this:
<ul className="sidebarList">
{listItems.map((listItem, index) => {
return (
<li
onClick={() => setActiveIndex(index)}
className={`sidebarListItem ${
index === activeIndex ? "active" : ""
}`}
>
{listItem}
</li>
);
})}
</ul>
when the user clicks on the item .. the activeIndex will be set as the item-index for the clicked item ..
and I am adding the className: "active" to the list-item only when the item-index === activeIndex

Focused elements display wrong style

I have element with width 400% and I want to move it to left by using translateX(-(index/4)*100%) when focused index changes.
Changing focused element translateX property with tab keyboard button displays it wrong on middle elements (1,2) even though using same hardcoded styling works as expected. What am I missing here?
const {useState} = React;
const App = () => {
const [curr, setCurr] = useState(0);
const carouselStyles = {
transform: `translateX(${-(curr / 4) * 100}%)`
// uncomment to see that styling works fine with hardcoded values 1,2..
// transform: `translateX(${-(1 / 4) * 100}%)`
};
const handleFocus = (num) => {
if (num !== curr) {
setCurr(num);
}
};
console.log(carouselStyles);
return (
<div>
<div className="carousel" style={carouselStyles}>
<div className="item">
11 very long text
<a href="/111" onFocus={() => handleFocus(0)}>
11111
</a>
</div>
<div className="item">
22 very long text
<a href="/222" onFocus={() => handleFocus(1)}>
22222
</a>
</div>
<div className="item">
33 very long text
<a href="/333" onFocus={() => handleFocus(2)}>
33333
</a>
</div>
<div className="item">
44 very long text
<a href="/444" onFocus={() => handleFocus(3)}>
44444
</a>
</div>
</div>
current: {curr}
</div>
);
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
.carousel {
display: flex;
align-items: center;
width: 400%;
}
.item {
flex: 0 1 100%;
text-align: center;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I needed to prevent the scrolling and in my provided example its enough to add this line into handleFocus function
window.scrollTo(0, 0);
But in my real scenario parent wrapper also had overflow: hidden; which prevented above code from working. So I've used refs
const handleFocus = (num) => {
if (num !== curr) {
setCurr(num);
carouselRef.current.scrollTo(0, 0);
}
};
return (
<div ref={carouselRef}>
<div className="carousel" style={carouselStyles}>
...
</div>
current: {curr}
</div>
);

Prevent focus on Expand More button after content is inserted in React

I need to list out a long name list inside my page while showing all names at first is not desirable.
So I try to add an expand more button on it.
However, using a button will keep the browser focus on that button after it's pressed, left the button position unchanged on the screen while the name was inserted before that button.
On the other hand, using any, not focusable element (eg. div with onclick function) will do the desired behavior but lost the accessibility at all. Making the "button" only clickable but not focusable.
How do I make the button flushed to list bottom like the snippet div block does? Or is there a better choice to expand the existing list?
const myArray = [
'Alex',
'Bob',
'Charlie',
'Dennis',
'Evan',
'Floron',
'Gorgious',
'Harris',
'Ivan',
'Jennis',
'Kurber',
'Lowrance',
]
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const handleExpand = e => {
setIdx(idx + 1)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div>
</div>
}
ReactDOM.render(<ExpandList/>, document.getElementById('root'))
.demo>p {
display: block;
padding: 20px;
color: #666;
background: #3331;
}
.demo>div>div {
display: flex;
padding: 15px;
margin-left: auto;
color: #666;
background: #3331;
}
.pointer {
cursor: pointer;
}
.pointer:hover {
background-color: #6663;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id='root' class='demo'>hello</div>
Removing focus from the button in the click handler is probably the most elegant approach: e.target.blur(). It will work on any HTML element, whether it is focusable or not (as with the div in your case).
const myArray = [
'Alex',
'Bob',
'Charlie',
'Dennis',
'Evan',
'Floron',
'Gorgious',
'Harris',
'Ivan',
'Jennis',
'Kurber',
'Lowrance',
]
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const handleExpand = e => {
e.target.blur()
setIdx(idx + 1)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div>
</div>
}
ReactDOM.render(<ExpandList/>, document.getElementById('root'))
.demo>p {
display: block;
padding: 20px;
color: #666;
background: #3331;
}
.demo>div>div {
display: flex;
padding: 15px;
margin-left: auto;
color: #666;
background: #3331;
}
.pointer {
cursor: pointer;
}
.pointer:hover {
background-color: #6663;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id='root' class='demo'>hello</div>
Inspired by #MiKo, temporally unmount the button after click and set a timeout to add it back seems to do the work. Since browser lose the focus on original expand button, this will keep content flush down without focusing the original button:
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const [showBtn, setShowBtn] = React.useState(true)
const handleExpand = e => {
setShowBtn(false)
setIdx(idx + 1)
setTimeout(() => setShowBtn(true), 10)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
{showBtn?
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div> :
<div></div>
}
</div>
}
But I'm still looking a method that doesn't need to 'unmount' a thing which should be there all time.

Is it possible to pass dynamic props, from one page to another with next.js?

I'm new to Next and have been trying to make a page(index.js) that fetches data(countries) and then displays that data, where each returned element(country) has a button to go to a page(info.js) where that specific countries data will be displayed, was wondering if its possible to pass the props(all country data) to the info.js page? I've tried reading the documentation and watching YT videos but can't seem understand what i'm reading/watching.
index.js:
import Link from 'next/link'
Welcome.getInitialProps = async function (props) {
const res = await fetch('https://restcountries.eu/rest/v2/all')
const data = await res.json()
return {
data: data
}
}
const MyLink = props => {
return (
<p>
<Link href={`/info?name=${props.name}`} >
<a>Learn More</a>
</Link>
</p>
)
}
function Welcome(props) {
return (
<div>
<div className="main-content">
<style jsx>{`
.main-content {
width: 80%;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-gap: 5px;
}
.item {
border: 1px solid black;
text-align: center;
}
.item ul{
padding: 0;
}
.item ul li {
list-style-type: none;
}
`}</style>
{props.data.map(country => (
<div key={country.numericCode} className="item">
<h4>{country.name}</h4>
<p>Region: {country.region}</p>
<p>Population: {country.population}</p>
<MyLink name={country.name} borders={country.borders} currencies={country.currencies}/>
</div>
))}
</div>
</div>
)
}
export default Welcome
info.js:
import { withRouter } from 'next/router'
import Link from 'next/link'
const Info = (props) => {
return (
<div>
<h1>{props.router.query.name}</h1>
<Link href="/">
<a>Home</a>
</Link>
</div>
)
}
export default withRouter(Info)
In MyLink component instead of using Link you can create a normal div (style it like a link) and onClick of that div push it to different page using nextjs router:
//import useRouter
import { useRouter } from 'next/router'
//then call it
const router = useRouter()
const MyLink = props => {
return (
<p onClick={() => {
router.push({
pathname: `/info?name=${props.name}`,
query: { data: //data to pass },
})
}}>
<a>Learn More</a>
</p>
)
}
You can access that data in the location object in the query key
import {useLocation} from ""
const location = useLocation()
const data = location.query

Categories