React CSSTransition For a single image with a changing [src] value? - css

I am working on a carousel that provides an alternating src value to an image. This does work, however I cannot get React CSSTransition to work, since technically it is the same element just with a changing src value that is bound to a state variable.
Here is the code:
<CSSTransition
classNames="carousel"
in={true}
appear={true}
timeout={1000}
>
<img
src={this.state.imgLink}
key={this.state.imgLink}
/>
</CSSTransition>
Any advice is greatly appreciated!
Cheers,
Gabe

If you're in a state managed component you could use a switch variable in your state to tell CSSTransition when the src attribute is changing (by putting it on true) and then putting it back to false when the first transition is completed (with a onEntered callback):
<CSSTransition
classNames="carousel"
in={this.state.changing}
onEntered={()=> this.setState({switch:false})}
appear={true}
timeout={1000}
>
<img
src={this.state.imgLink}
key={this.state.imgLink}
/>
</CSSTransition>
That would work nice with a fading effect.
But since your classNames is called carousel, I think you want to be able to move images around (like one exiting while another one entering). You won't be able to do that with only one <img> tag but you will by having one per image and unmounting them on exit (unmountOnExit prop) and managing them with an index variable on the state:
{pictures.map((p, i) => (
<CSSTransition
key={p.id}
in={i === this.state.index}
classNames="carousel"
timeout={1000}
>
<img
src={p.url}
className={i === this.state.index ? "fade-enter-done" : ""}
/>
</CSSTransition>
))}

This may not be the official way but I found that if you wrap CssTransition with TransitionGroup and move the key from img to CssTransition it will work as you want it to. The enter and exit animation will run at the same time, perfect for images to slide in and out at the same time or crossfade, etc.
<TransitionGroup component={null}>
<CSSTransition classNames="carousel" timeout={1000} key={this.state.imgLink}>
<img src={this.state.imgLink} />
</CSSTransition>
</TransitionGroup>
If you want one animation to start only after the other one has finished, wrap it with SwitchTransition.
Transition modes. out-in: Current element transitions out first, then when complete, the new element transitions in. in-out: New element transitions in first, then when complete, the current element transitions out.
type: 'out-in'|'in-out'
default: 'out-in'
<SwitchTransition mode={"out-in"}>
<CSSTransition classNames="carousel" timeout={1000} key={this.state.imgLink}>
<img src={this.state.imgLink} />
</CSSTransition>
</SwitchTransition>;

Related

How to make smooth fade-in transition with tailwind HeadlessUI

I have a simple HeadlessUI Tab component like the one below.
import { Tab } from '#headlessui/react'
function MyTabs() {
return (
<Tab.Group>
<Tab.List>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</Tab.List>
<Tab.Panels>
<Tab.Panel>Image content 1</Tab.Panel>
<Tab.Panel>Image content 2</Tab.Panel>
<Tab.Panel>Image content 3</Tab.Panel>
</Tab.Panels>
</Tab.Group>
)
}
I would like to smoothly change the view (in this case, each Tab.Panel content) when I click the tab menu.
When I looked into the official example, there was no description of how to handle the transition like fade-in.
I know there is a tailwind fade-in & delay & transition animation CSS tag, but I am unsure where to add that tag so the headlessUI Tabs work smoothly.
Any code example is appreciated!
Use transitions provided from headlessui:
import { Transition } from '#headlessui/react'
Example:
import { Transition } from '#headlessui/react'
import { useState } from 'react'
function MyComponent() {
const [isShowing, setIsShowing] = useState(false)
return (
<>
<button onClick={() => setIsShowing((isShowing) => !isShowing)}>
Toggle
</button>
<Transition
show={isShowing}
enter="transition-opacity duration-75"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity duration-150"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
I will fade in and out
</Transition>
</>
)
}
Animating transitions
By default, a Transition will enter and leave instantly, which is probably not what you're looking for if you're using this component.
To animate your enter/leave transitions, add classes that provide the styling for each phase of the transitions using these props:
enter: Applied the entire time an element is entering. Usually you define your duration and what properties you want to transition here, for example transition-opacity duration-75.
enterFrom: The starting point to enter from, for example opacity-0 if something should fade in.
enterTo: The ending point to enter to, for example opacity-100 after fading in.
leave: Applied the entire time an element is leaving. Usually you define your duration and what properties you want to transition here, for example transition-opacity duration-75.
leaveFrom: The starting point to leave from, for example opacity-100 if something should fade out.
leaveTo: The ending point to leave to, for example opacity-0 after fading out.
Reference : Create top-down slide animation using `Transition` from `#headlessui/react` using Tailwind CSS

Link react-router-dom causes CSS to be lost

I have a react component looks like this:
<MenuSection backgroundurl="/images/home-slide-4-1920x800.jpg" fluid>
<MenuContainer>
<MenuRow>
{categoryItems.map(categoryItem => (
<MenuItemCard key={categoryItem.Id}>
<Link to={categoryItem.CategoryUrl}>
<MenuItemCardImg src={categoryItem.ImageUrl} />
<MenuItemCardBody>
<MenuItemTitle>{categoryItem.CategoryName}</MenuItemTitle>
</MenuItemCardBody>
</Link>
</MenuItemCard >
))}
</MenuRow>
</MenuContainer>
</MenuSection>
When I click the Link, the route is correct and it takes me to the correct page. However all the CSS in that following page is not applied. If I reload the page that 'Link' took me to the CSS is applied correctly.
When I change the 'Link' and replace it with an < a > tag, when I click the link everything works the next page has the css applied correctly with out any problems.
<MenuSection backgroundurl="/images/home-slide-4-1920x800.jpg" fluid>
<MenuContainer>
<MenuRow>
{categoryItems.map(categoryItem => (
<MenuItemCard key={categoryItem.Id}>
<a href={categoryItem.CategoryUrl}>
<MenuItemCardImg src={categoryItem.ImageUrl} />
<MenuItemCardBody>
<MenuItemTitle>{categoryItem.CategoryName}</MenuItemTitle>
</MenuItemCardBody>
</a>
</MenuItemCard >
))}
</MenuRow>
</MenuContainer>
</MenuSection>
I can't figure out why this is happening, this is the first React project I have done, so maybe I am missing something really obvious?
UPDATE:
When I use import {Nav} from "react-bootstrap"; (Nav.Link) - from react bootstrap, when I click the link the page navigates correctly and the CSS is applied correctly.
<MenuSection backgroundurl="/images/home-slide-4-1920x800.jpg" fluid>
<MenuContainer>
<MenuRow>
{categoryItems.map(categoryItem => (
<MenuItemCard key={categoryItem.Id}>
<Nav.Link href={categoryItem.CategoryUrl}>
<MenuItemCardImg src={categoryItem.ImageUrl} />
<MenuItemCardBody>
<MenuItemTitle>{categoryItem.CategoryName}</MenuItemTitle>
</MenuItemCardBody>
</Nav.Link>
</MenuItemCard >
))}
</MenuRow>
</MenuContainer>
So it just seems when I use Link from react-router-dom I get this strange behaviour.
For now I have found a solution by using Nav.Link, but I would really like to know why when I use Link the navigation works but the CSS is not applied untill I reload the page.

Tooltip on absolute element in React JS

I am using this library https://tvkhoa.github.io/testlib/ to make some nice tooltip in my React App.
I use that code in my own component which look like this
const Tooltip = ({className, children, text, placement = 'bottom', isDisabled = false}) => {
return (
<ReactTippyTooltip className={className} html={<span>{text}</span>}
theme="dark" position={placement} arrow={true} arrowSize="regular"
animation="shift" hideDelay={300}>
{children}
</ReactTippyTooltip>
);
};
On basic elements such as simple images or buttons, everything works fine. But in some cases, I need to have a tooltip on an absolute element. For example on an InfoButton which overlaps an image.
<div className="relative"> <!-- container -->
<img />
<Tooltip> <!-- tooltip container -->
<InfoButton className="absolute top-0 left-0">
</Tooltip>
</div>
But I noticed that the tooltip appears bellow the image, instead of bellow the InfoButton.
If I have a look with the devtool, I can see that my InfoButton is inside a div that represents ReactTippyTooltip, and that div is 0*0 and is positionned bellow the image in the DOM. This is because only its content (InfoButton) is absolute and positionned inside the image. It can be summarized by the following image
I put a className to ReactTippyTooltip as a workaround and I made it absolute as well. Like this my tooltip is at the right place, but I feel like there is something wrong with what I am my doing.
Has anyone encountered some issues with tooltip on absolute elements ?
Can I do some better code with the library I am using ?
Does it exist some react tooltip library that use reference of the element to display the tooltip ?

ARIA accessibility issues with <Select>

Background
We are using #axe-core/react to work on ARIA accessibility in react application. By default, on page reload no issues are reported, but on Select click we are facing various issues with accessibility, according to axe-core/react.
Problems
serious: Page must have means to bypass repeated blocks
https://dequeuniversity.com/rules/axe/4.0/bypass?application=axeAPI
moderate: Document must have one main landmark
https://dequeuniversity.com/rules/axe/4.0/landmark-one-main?application=axeAPI
moderate: All page content must be contained by landmarks
https://dequeuniversity.com/rules/axe/4.0/region?application=axeAPI
In addition to problems, there are 2 more:
moderate: Page must contain a level-one heading https://dequeuniversity.com/rules/axe/4.0/page-has-heading-one?application=axeAPI
serious: Elements must have sufficient color contrast https://dequeuniversity.com/rules/axe/4.0/color-contrast?application=axeAPI
Code
import React from "react";
import ReactDOM from "react-dom";
import axe from "#axe-core/react";
import { BrowserRouter, Route, Link } from "react-router-dom";
import { MenuItem, Select, InputLabel } from "#material-ui/core";
const BypassRepeatedBlocks = () => (
<div aria-hidden="true">
<a href="#select-content" style={{ display: "none" }}>
bypass repeated blocks
</a>
</div>
);
const NavigationSample = () => (
// we must have <nav> here in order to put content inside landmark. Otherwise we would get issue.
<nav>
<ul>
<li>
<Link to="/home">Home</Link>
</li>
</ul>
</nav>
);
const HomePage = () => <div></div>;
const App = () => (
// BrowserRouter, Route and <Link> (NavigationSample) are the cause of bypass repeated blocks issue
<BrowserRouter>
{/**but with BypassRepeatedBlocks portion of code, it can be solved */}
<BypassRepeatedBlocks />
<NavigationSample />
<main>
<Route path="/home" component={HomePage} />
{/**without <h1> there is heading issue on page loading */}
<h1>H1</h1>
<InputLabel id="my-input" style={{ color: "black" }}>
Sort
</InputLabel>
<Select
id="select-content"
labelId="my-input"
value="item1"
style={{ width: "200px" }}
>
<MenuItem key="item1" value="item1">
item1
</MenuItem>
<MenuItem key="item2" value="item2">
item2
</MenuItem>
</Select>
</main>
</BrowserRouter>
);
axe(React, ReactDOM, 1000);
ReactDOM.render(<App />, document.getElementById("root"));
Observations
As with shown code, when drop-down menu is shown I can navigate through menuitems (options) with key down and key up from keyboard, and that is what is important, from accessibility point of view, right?
After clicking on Select and inspecting HTML elements of web page, I can see two things: first, that my main content gets hidden <div id="root" aria-hidden="true">, and second that new <div> is generated with role='presentation' which contains this drop-down content. And it is outside of <main> which sort of explains the issue All page content must be contained by landmarks, as well as Document must have one main landmark and Page must contain a level-one heading
In addition to previous observation, I tried to add MenuProps={{ disablePortal: true }} to Select element, and issue All page content must be contained by landmarks was removed but i don't know if and what negative impacts this can have
from what i read on internet on this topic, I find this case (select, drop-down) to be very specific and advance.
Questions
Is there a proper way to handle mentioned issues for Select element? How?
Is this one of the situations to be considered as a trade-off (don't have to / can't get rid of all issues)?
If 2. is true, is then that bug to axe-core/react tool?
Let me know if I can provide more details, or update some content.
Thanks.
EDIT
Github repo for reproduction: https://github.com/StefanZivkovic/aria-accessibility-problems.
When you git clone it from cmd, navigate to cd aria-accessibility-problems, execute npm i, thennpm start and wait for server to start. If you use VS code, from cmd do code . to open the files and check the code if you want.
By the way, i found how to overcome Page must have means to bypass repeated blocks, at least on this simple example. That particular issue was caused by react-router-dom, and its elements, Link, BrowserRouter and Route. Might be useful for some.

Materialize navbar links conditional rendering

I am using React Materialize and React Router Dom. I'm trying to show navlinks to only authenticated users, so I'm using conditional rendering. But if I do it like below, navlinks are rendered vertical, not horizontal as usual. Is there a solution for that? Thanks
<Navbar>
{isAuthenticated && (
<>
<NavLink to="/locations" href="/locations">
Locations
</NavLink>
<NavLink to="/categories" href="/categories">
Categories
</NavLink>
</>
)}
</Navbar>
Looks like the Navbar component is rendering all of its children inside li elements. When you wrap them in a fragment, the component considers this as its only child element and puts all of the NavLink elements in a single li.
I can think of two simple approaches to handle this:
If there are only a few links, you can do conditional rendering for them:
<Navbar>
{isAuthenticated && (<NavLink to="/locations" href="/locations">Locations</NavLink>)}
{isAuthenticated && (<NavLink to="/categories" href="/categories">Categories</NavLink>)}
</Navbar>
However, this solution is not reliable, especially if you have a lot of links.
Store the NavLink elements in some array and conditionally add the auth-dependant items:
// In the component:
const links = [/* some public links */];
const privateLinks = [
// Don't forget keys. The numbers here are only for example.
<NavLink key={1} to="/locations" href="/locations">Locations</NavLink>,
<NavLink key={2} to="/categories" href="/categories">Categories</NavLink>
];
if(isAuthenticated){
links.push(...privateLinks);
}
// In the template:
<Navbar>{links}</Navbar>
The logic with the links arrays is pretty trivial (with setting private links as the last items) only to keep the demo more simple.

Resources