Skip to content

Commit 96ff1ae

Browse files
committed
Improve responsive design
1 parent 480ca24 commit 96ff1ae

File tree

6 files changed

+316
-108
lines changed

6 files changed

+316
-108
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// CircleCI doesn't like import { motion } from "framer-motion" here, so we use require
2+
const { motion } = require('framer-motion');
3+
4+
/**
5+
* Fade content left to right. Needs to be used with FadeLeftToRightItem
6+
* @function FadeLeftToRight
7+
* @param {ReactNode} children - Children content to render
8+
* @param {string} cssClass - CSS classes to apply to component
9+
* @param {number} delay - Time to wait before starting animation
10+
* @param {number} staggerDelay - Time to wait before starting animation for children items
11+
* @param {boolean} animateNotReverse - Start animation backwards
12+
* @returns {JSX.Element} - Rendered component
13+
*/
14+
15+
const FadeLeftToRight = ({
16+
children,
17+
cssClass,
18+
delay,
19+
staggerDelay,
20+
animateNotReverse,
21+
}) => {
22+
const FadeLeftToRightVariants = {
23+
visible: {
24+
opacity: 1,
25+
transition: {
26+
when: 'beforeChildren',
27+
staggerChildren: staggerDelay ? staggerDelay : 0.5,
28+
delay,
29+
ease: 'easeInOut',
30+
staggerDirection: 1,
31+
},
32+
},
33+
hidden: {
34+
opacity: 0,
35+
transition: {
36+
when: 'afterChildren',
37+
staggerChildren: staggerDelay ? staggerDelay : 0.5,
38+
staggerDirection: -1,
39+
},
40+
},
41+
};
42+
return (
43+
<motion.div
44+
initial="hidden"
45+
animate={animateNotReverse ? 'visible' : 'hidden'}
46+
variants={FadeLeftToRightVariants}
47+
className={cssClass}
48+
data-testid="fadelefttoright"
49+
>
50+
{children}
51+
</motion.div>
52+
);
53+
};
54+
55+
export default FadeLeftToRight;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// CircleCI doesn't like import { motion } from "framer-motion" here, so we use require
2+
const { motion } = require('framer-motion');
3+
4+
/**
5+
* Fade content left to right. Needs to be used with FadeLeftToRight as parent container
6+
* @function FadeLeftToRightItem
7+
* @param {ReactNode} children - Children content to render
8+
* @param {string} cssClass - CSS classes to apply to component
9+
* @returns {JSX.Element} - Rendered component
10+
*/
11+
12+
const FadeLeftToRightItem = ({ children, cssClass }) => {
13+
const FadeLeftToRightItemVariants = {
14+
visible: { opacity: 1, x: 0 },
15+
hidden: { opacity: 0, x: -20 },
16+
};
17+
return (
18+
<motion.span
19+
variants={FadeLeftToRightItemVariants}
20+
className={cssClass}
21+
data-testid="fadelefttorightitem"
22+
>
23+
{children}
24+
</motion.span>
25+
);
26+
};
27+
28+
export default FadeLeftToRightItem;

components/Header/Hamburger.component.jsx

Lines changed: 95 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,114 @@
1-
import { useState } from 'react';
2-
import { useSpring, animated } from 'react-spring';
1+
import { useState, useEffect, useCallback } from 'react';
32
import Link from 'next/link';
43

4+
import FadeLeftToRight from 'components/Animations/FadeLeftToRight.component';
5+
import FadeLeftToRightItem from 'components/Animations/FadeLeftToRightItem.component';
6+
57
import LINKS from '../../utils/constants/LINKS';
68

79
/**
8-
* Shows the mobile menu.
10+
* Hamburger component used in mobile menu. Animates to a X when clicked
11+
* @function Hamburger
12+
* @param {MouseEventHandler<HTMLButtonElement>} onClick - onClick handler to respond to clicks
13+
* @param {boolean} isExpanded - Should the hamburger animate to a X?
14+
* @returns {JSX.Element} - Rendered component
915
*/
16+
1017
const Hamburger = () => {
1118
const [isExpanded, setisExpanded] = useState(false);
12-
const hamburgerSlideDownAnimation = useSpring({
13-
to: [
14-
{
15-
opacity: isExpanded ? 1 : 0,
16-
marginTop: isExpanded ? '170px' : '-180px',
17-
},
18-
],
19-
from: {
20-
opacity: isExpanded ? 1 : 0,
21-
marginTop: isExpanded ? '170px' : '-180px',
22-
},
23-
});
24-
const showHamburgerHideXAnimation = useSpring({
25-
to: [
26-
{
27-
opacity: isExpanded ? 0 : 1,
28-
display: isExpanded ? 'none' : 'inline',
29-
},
30-
],
31-
from: {
32-
opacity: isExpanded ? 0 : 1,
33-
},
34-
});
35-
const showXHideHamburgerAnimation = useSpring({
36-
to: [
37-
{
38-
opacity: isExpanded ? 1 : 0,
39-
display: isExpanded ? 'inline' : 'none',
40-
},
41-
],
42-
from: {
43-
opacity: isExpanded ? 0 : 1,
44-
display: isExpanded ? 'none' : 'inline',
45-
},
46-
});
19+
const [hidden, setHidden] = useState('invisible');
20+
21+
useEffect(() => {
22+
if (isExpanded) {
23+
//document.addEventListener("mousedown", handleClickOutside);
24+
setHidden('');
25+
} else {
26+
setTimeout(() => {
27+
setHidden('invisible');
28+
}, 1000);
29+
30+
// document.removeEventListener("mousedown", handleClickOutside);
31+
}
32+
/*return () => {
33+
document.removeEventListener("mousedown", handleClickOutside);
34+
};*/
35+
}, [isExpanded]);
36+
37+
const handleMobileMenuClick = useCallback(() => {
38+
/**
39+
* Anti-pattern: setisExpanded(!isExpanded)
40+
* Even if your state updates are batched and multiple updates to the enabled/disabled state are made together
41+
* each update will rely on the correct previous state so that you always end up with the result you expect.
42+
*/
43+
setisExpanded((prevExpanded) => !prevExpanded);
44+
}, [setisExpanded]);
45+
46+
const hamburgerLine =
47+
'h-1 w-10 my-1 rounded-full bg-white transition ease transform duration-300 not-sr-only';
48+
49+
const opacityFull = 'opacity-100 group-hover:opacity-100';
50+
4751
return (
48-
<>
49-
<label
50-
htmlFor="menu-toggle"
51-
aria-label="Meny"
52-
className="block cursor-pointer md:hidden"
52+
<div className="z-50 md:hidden lg:hidden xl:hidden bg-gray-800">
53+
<button
54+
className="flex flex-col w-16 rounded justify-center items-center group"
55+
data-cy="hamburger"
56+
data-testid="hamburger"
57+
onClick={handleMobileMenuClick}
58+
aria-expanded={isExpanded}
59+
type="button"
5360
>
54-
<animated.svg
55-
id="hamburgersvg"
56-
style={showHamburgerHideXAnimation}
57-
onClick={() => {
58-
setisExpanded((prevExpanded) => !prevExpanded);
59-
}}
60-
className="text-gray-900 fill-current"
61-
xmlns="https://www.w3.org/2000/svg"
62-
width="25"
63-
height="25"
64-
viewBox="0 0 25 25"
65-
>
66-
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" />
67-
</animated.svg>
68-
<animated.svg
69-
id="xsvg"
70-
onClick={() => {
71-
setisExpanded((prevExpanded) => !prevExpanded);
72-
}}
73-
style={showXHideHamburgerAnimation}
74-
xmlns="https://www.w3.org/2000/svg"
75-
width="25"
76-
height="25"
77-
viewBox="0 0 25 25"
78-
>
79-
<line x1="18" y1="6" x2="6" y2="18" />
80-
<line x1="6" y1="6" x2="18" y2="18" />
81-
</animated.svg>
82-
</label>
83-
{isExpanded && (
84-
<animated.div
85-
style={hamburgerSlideDownAnimation}
61+
<span className="sr-only text-white text-2xl">Hamburger</span>
62+
<span
63+
data-testid="hamburgerline"
64+
className={`${hamburgerLine} ${
65+
isExpanded
66+
? 'rotate-45 translate-y-3 opacity-100 group-hover:opacity-100'
67+
: opacityFull
68+
}`}
69+
/>
70+
<span
71+
className={`${hamburgerLine} ${
72+
isExpanded ? 'opacity-0' : opacityFull
73+
}`}
74+
/>
75+
<span
76+
className={`${hamburgerLine} ${
77+
isExpanded
78+
? '-rotate-45 -translate-y-3 opacity-100 group-hover:opacity-100'
79+
: opacityFull
80+
}`}
81+
/>
82+
</button>
83+
<FadeLeftToRight
84+
delay={0.2}
85+
staggerDelay={0.2}
86+
animateNotReverse={isExpanded}
87+
>
88+
<div
8689
id="mobile-menu"
87-
className="absolute right-0 z-10 w-full text-center text-black bg-white "
90+
aria-hidden={!isExpanded}
91+
className={`absolute right-0 z-10 w-full text-center text-black bg-white ${hidden}`}
8892
>
8993
<ul>
9094
{LINKS.map(({ id, title, href }) => (
91-
<li
92-
key={id}
93-
id="mobile-li"
94-
className="w-full p-4 border-t border-gray-400 border-solid rounded"
95-
>
96-
<Link href={href}>
97-
<a className="inline-block px-4 py-2 no-underline hover:text-black hover:underline">
98-
{title}
99-
</a>
100-
</Link>
101-
</li>
95+
<FadeLeftToRightItem key={id} cssClass="block">
96+
<li
97+
id="mobile-li"
98+
className="w-full p-4 border-t border-gray-400 border-solid rounded"
99+
>
100+
<Link href={href}>
101+
<a className="inline-block px-4 py-2 no-underline hover:text-black hover:underline">
102+
{title}
103+
</a>
104+
</Link>
105+
</li>
106+
</FadeLeftToRightItem>
102107
))}
103108
</ul>
104-
</animated.div>
105-
)}
106-
</>
109+
</div>
110+
</FadeLeftToRight>
111+
</div>
107112
);
108113
};
109114

components/Header/Navbar.component.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Link from 'next/link';
33
import Cart from '../Cart/Cart.component';
44
import Search from '../AlgoliaSearch/AlgoliaSearchBox.component';
55
import SVGMobileSearchIcon from '../SVG/SVGMobileSearchIcon.component';
6+
67
import Hamburger from './Hamburger.component';
78

89
/**
@@ -14,6 +15,7 @@ const Navbar = () => (
1415
<nav id="header" className="fixed top-0 z-50 w-full py-1 bg-white ">
1516
<div className="container flex flex-wrap items-center justify-between px-6 py-3 mx-auto mt-0 md:min-w-96">
1617
<Hamburger />
18+
1719
<div
1820
className="order-3 hidden w-full md:flex md:items-center md:w-auto md:order-1"
1921
id="menu"

0 commit comments

Comments
 (0)