Skip to content

Commit f31e954

Browse files
authored
Merge pull request #832 from w3bdesign/develop
Improve responsive design
2 parents 75148e1 + ef1e65d commit f31e954

16 files changed

+526
-297
lines changed

components/AlgoliaSearch/MobileSearch.component.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const MobileSearch = () => {
1616
const [search, setSearch] = useState(null);
1717
const [hasFocus, sethasFocus] = useState(false);
1818
return (
19-
<div className="inline mt-4">
19+
<div className="inline mt-4 md:hidden">
2020
<InstantSearch
2121
indexName={process.env.NEXT_PUBLIC_ALGOLIA_INDEX_NAME}
2222
searchClient={searchClient}
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/Cart/Cart.component.jsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { AppContext } from 'utils/context/AppContext';
88
* Currently only displays a sample cart.
99
* Displays amount of items in cart.
1010
*/
11-
const Cart = () => {
11+
const Cart = ({ stickyNav }) => {
1212
const [cart] = useContext(AppContext);
1313

1414
const productsCount =
@@ -18,11 +18,11 @@ const Cart = () => {
1818
<>
1919
<Link href="/handlekurv">
2020
<a
21-
className="inline-block pl-4 mt-4 no-underline"
21+
className="pl-4 mt-4 no-underline inline-block"
2222
aria-label="Handlekurv"
2323
>
2424
<svg
25-
className="fill-current"
25+
className={`${stickyNav ? 'fill-white' : 'fill-current'}`}
2626
xmlns="https://www.w3.org/2000/svg"
2727
width="55"
2828
height="55"
@@ -38,9 +38,12 @@ const Cart = () => {
3838
</svg>
3939
</a>
4040
</Link>
41-
{/*Cart quantity */}
41+
4242
{productsCount && (
43-
<span className="w-6 h-6 pb-2 -mt-5 text-center text-white bg-black rounded-full">
43+
<span
44+
className={`w-6 h-6 pb-2 -mt-5 text-center rounded-full
45+
${stickyNav ? 'text-black bg-white' : 'text-white bg-black'}`}
46+
>
4447
{productsCount}
4548
</span>
4649
)}

components/Footer/Footer.component.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/**
22
* Footer of the application.
33
*/
4-
const Footer = () => (
5-
<footer className="container px-6 mx-auto text-center bg-white border border-gray-300 rounded-lg shadow">
4+
const Footer = () => (
5+
<footer className="w-full sm:absolute sm:bottom-0 px-6 mb-28 sm:mb-0 mx-auto text-center bg-white border border-gray-300 rounded-lg shadow">
66
<div className="p-6">
77
Copyright &copy; {new Date().getFullYear()} Daniel / w3bdesign
88
</div>
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { useState, useEffect, useCallback } from 'react';
2+
import Link from 'next/link';
3+
4+
import FadeLeftToRight from 'components/Animations/FadeLeftToRight.component';
5+
import FadeLeftToRightItem from 'components/Animations/FadeLeftToRightItem.component';
6+
7+
import LINKS from '../../utils/constants/LINKS';
8+
9+
/**
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
15+
*/
16+
17+
const Hamburger = () => {
18+
const [isExpanded, setisExpanded] = useState(false);
19+
const [hidden, setHidden] = useState('invisible');
20+
21+
useEffect(() => {
22+
if (isExpanded) {
23+
setHidden('');
24+
} else {
25+
setTimeout(() => {
26+
setHidden('invisible');
27+
}, 1000);
28+
}
29+
}, [isExpanded]);
30+
31+
const handleMobileMenuClick = useCallback(() => {
32+
/**
33+
* Anti-pattern: setisExpanded(!isExpanded)
34+
* Even if your state updates are batched and multiple updates to the enabled/disabled state are made together
35+
* each update will rely on the correct previous state so that you always end up with the result you expect.
36+
*/
37+
setisExpanded((prevExpanded) => !prevExpanded);
38+
}, [setisExpanded]);
39+
40+
const hamburgerLine =
41+
'h-1 w-10 my-1 rounded-full bg-white transition ease transform duration-300 not-sr-only';
42+
43+
const opacityFull = 'opacity-100 group-hover:opacity-100';
44+
45+
return (
46+
<div className="z-50 md:hidden lg:hidden xl:hidden bg-blue-800">
47+
<button
48+
className="flex flex-col w-16 rounded justify-center items-center group"
49+
data-cy="hamburger"
50+
data-testid="hamburger"
51+
onClick={handleMobileMenuClick}
52+
aria-expanded={isExpanded}
53+
type="button"
54+
>
55+
<span className="sr-only text-white text-2xl">Hamburger</span>
56+
<span
57+
data-testid="hamburgerline"
58+
className={`${hamburgerLine} ${
59+
isExpanded
60+
? 'rotate-45 translate-y-3 opacity-100 group-hover:opacity-100'
61+
: opacityFull
62+
}`}
63+
/>
64+
<span
65+
className={`${hamburgerLine} ${
66+
isExpanded ? 'opacity-0' : opacityFull
67+
}`}
68+
/>
69+
<span
70+
className={`${hamburgerLine} ${
71+
isExpanded
72+
? '-rotate-45 -translate-y-3 opacity-100 group-hover:opacity-100'
73+
: opacityFull
74+
}`}
75+
/>
76+
</button>
77+
<FadeLeftToRight
78+
delay={0.2}
79+
staggerDelay={0.2}
80+
animateNotReverse={isExpanded}
81+
>
82+
<div
83+
id="mobile-menu"
84+
aria-hidden={!isExpanded}
85+
className={`absolute left-0 bottom-24 z-10 w-full text-center text-black bg-white ${hidden}`}
86+
>
87+
<ul>
88+
{LINKS.map(({ id, title, href }) => (
89+
<FadeLeftToRightItem key={id} cssClass="block">
90+
<li
91+
id="mobile-li"
92+
className="w-full p-4 border-t border-gray-400 border-solid rounded"
93+
>
94+
<Link href={href} passHref>
95+
<a
96+
className="inline-block px-4 py-2 no-underline hover:text-black hover:underline"
97+
onClick={() => {
98+
setisExpanded((prevExpanded) => !prevExpanded);
99+
}}
100+
>
101+
{title}
102+
</a>
103+
</Link>
104+
</li>
105+
</FadeLeftToRightItem>
106+
))}
107+
</ul>
108+
</div>
109+
</FadeLeftToRight>
110+
</div>
111+
);
112+
};
113+
114+
export default Hamburger;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import Link from 'next/link';
2+
3+
import Cart from '../Cart/Cart.component';
4+
import Search from '../AlgoliaSearch/AlgoliaSearchBox.component';
5+
import SVGMobileSearchIcon from '../SVG/SVGMobileSearchIcon.component';
6+
7+
import Hamburger from './Hamburger.component';
8+
9+
/**
10+
* Navigation for the application.
11+
* Includes mobile menu.
12+
*/
13+
const Stickynav = () => (
14+
<nav
15+
id="footer"
16+
className="fixed bottom-0 z-50 w-full md:hidden lg:hidden xl:hidden"
17+
>
18+
<div className="container flex flex-wrap items-center justify-between px-6 py-3 mx-auto mt-0 md:min-w-96 bg-blue-800">
19+
<Hamburger />
20+
<div
21+
className="order-3 hidden w-full md:flex md:items-center md:w-auto md:order-1"
22+
id="menu"
23+
>
24+
<ul className="items-center justify-between pt-4 text-base text-gray-700 md:flex md:pt-0">
25+
<li>
26+
<Link href="/produkter">
27+
<a className="inline-block py-2 pr-4 text-xl font-bold no-underline hover:underline">
28+
Produkter
29+
</a>
30+
</Link>
31+
</li>
32+
<li>
33+
<Link href="/kategorier">
34+
<a className="inline-block py-2 pr-4 text-xl font-bold no-underline hover:underline">
35+
Kategorier
36+
</a>
37+
</Link>
38+
</li>
39+
</ul>
40+
</div>
41+
<div className="flex items-center order-2 md:order-3" id="nav-content">
42+
<Search />
43+
<SVGMobileSearchIcon />
44+
<Cart stickyNav />
45+
</div>
46+
</div>
47+
</nav>
48+
);
49+
50+
export default Stickynav;

0 commit comments

Comments
 (0)