Skip to content

Commit

Permalink
fix(motion-icons): re-design LandingNav hamburger button motion (#769)
Browse files Browse the repository at this point in the history
issue #683
issue #701
  • Loading branch information
sabertazimi committed May 2, 2022
1 parent 8ba7af4 commit d908726
Show file tree
Hide file tree
Showing 14 changed files with 197 additions and 138 deletions.
2 changes: 1 addition & 1 deletion components/Icons/Comment.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { motion } from '@components/utils';
import IconFactory from './IconFactory';

const Svg = () => (
const Svg = (): JSX.Element => (
<motion.svg
viewBox="0 0 1024 1024"
p-id="1097"
Expand Down
20 changes: 0 additions & 20 deletions components/Icons/Fork.tsx

This file was deleted.

66 changes: 52 additions & 14 deletions components/Icons/Hamburger.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,58 @@
import type { MotionProps } from '@components/utils';
import { motion } from '@components/utils';
import type { MouseEvent } from 'react';
import { useCallback, useState } from 'react';
import IconFactory from './IconFactory';

const Svg = () => (
<motion.svg
viewBox="0 0 1024 1024"
p-id="7522"
width="2em"
height="2em"
fill="currentColor"
>
<motion.path
d="M904 160H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8zM904 784H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8zM904 472H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"
p-id="7523"
/>
</motion.svg>
);
const firstVariants: MotionProps['variants'] = {
checked: { y: '30%', rotate: 45 },
unchecked: { y: 0, rotate: 0 },
};

const secondVariants: MotionProps['variants'] = {
checked: { scaleX: 0 },
unchecked: { scaleX: 1 },
};

const thirdVariants: MotionProps['variants'] = {
checked: { y: '-30%', rotate: -45 },
unchecked: { y: 0, rotate: 0 },
};

const Svg = (): JSX.Element => {
const [isChecked, setIsChecked] = useState(false);
const handleClick = useCallback(
(event: MouseEvent) => setIsChecked(isChecked => !isChecked),
[]
);

return (
<motion.svg
viewBox="0 0 1024 1024"
p-id="7522"
width="2em"
height="2em"
fill="currentColor"
initial={false}
animate={isChecked ? 'checked' : 'unchecked'}
onClick={handleClick}
data-testid="hamburger-icon"
>
<motion.path
d="M904 160H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"
variants={firstVariants}
/>
<motion.path
d="M904 472H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"
variants={secondVariants}
/>
<motion.path
d="M904 784H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"
variants={thirdVariants}
/>
</motion.svg>
);
};

const Hamburger = IconFactory(Svg, 'Hamburger');
export default Hamburger;
20 changes: 16 additions & 4 deletions components/Icons/IconFactory.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import Icon from '@ant-design/icons';
import type { ComponentType, SVGProps } from 'react';
import type { SVGProps } from 'react';

interface Props {
onClick?: () => void;
className?: string;
}

type IconType = (props: Props) => JSX.Element;

const IconFactory = (
svg: ComponentType<SVGProps<SVGSVGElement>>,
svg: (props: SVGProps<SVGSVGElement>) => JSX.Element,
ariaLabel: string
): IconType => {
const IconComponent = ({ className }: Props): JSX.Element => (
<Icon component={svg} className={className} aria-label={ariaLabel} />
const IconComponent = ({
onClick,
className,
...props
}: Props): JSX.Element => (
<Icon
{...props}
component={svg}
aria-label={ariaLabel}
onClick={onClick}
className={className}
/>
);

return IconComponent;
};

Expand Down
3 changes: 1 addition & 2 deletions components/Icons/Icons.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { render } from '@testing-library/react';
import { axe } from 'jest-axe';
import Comment from './Comment';
import Fork from './Fork';
import Hamburger from './Hamburger';

describe('Icons', () => {
const Icons = [Comment, Fork, Hamburger];
const Icons = [Comment, Hamburger];

test.each(Icons)('should render %# icon correctly (snapshot)', Icon => {
const { container } = render(<Icon />);
Expand Down
34 changes: 13 additions & 21 deletions components/Icons/__snapshots__/Icons.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -30,39 +30,31 @@ exports[`Icons should render 0 icon correctly (snapshot) 1`] = `
exports[`Icons should render 1 icon correctly (snapshot) 1`] = `
<div>
<span
aria-label="Fork"
aria-label="Hamburger"
class="anticon"
role="img"
>
<svg
data-testid="hamburger-icon"
fill="currentColor"
height="2em"
viewBox="0 0 1024 1024"
width="2em"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2 0.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9c-4.4 5.2-0.7 13.1 6.1 13.1h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
d="M904 160H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"
style="transform: none; transform-origin: 0px 0px;"
transform-origin="0px 0px"
/>
<path
d="M904 472H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"
style="transform: none; transform-origin: 0px 0px;"
transform-origin="0px 0px"
/>
</svg>
</span>
</div>
`;

exports[`Icons should render 2 icon correctly (snapshot) 1`] = `
<div>
<span
aria-label="Hamburger"
class="anticon"
role="img"
>
<svg
fill="currentColor"
height="2em"
viewBox="0 0 1024 1024"
width="2em"
>
<path
d="M904 160H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8zM904 784H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8zM904 472H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"
d="M904 784H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"
style="transform: none; transform-origin: 0px 0px;"
transform-origin="0px 0px"
/>
</svg>
</span>
Expand Down
1 change: 0 additions & 1 deletion components/Icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,5 @@ export {
UserOutlined as User,
} from '@ant-design/icons';
export { default as Comment } from './Comment';
export { default as Fork } from './Fork';
export { default as Hamburger } from './Hamburger';
export { default as SocialIcon } from './SocialIcon';
5 changes: 1 addition & 4 deletions components/LandingNav/LandingNav.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
.button {
@apply fixed top-12 left-12 z-100;
@apply bg-transparent border-none;
@apply cursor-pointer;
}

.icon {
@apply text-lg font-extrabold md:text-4xl text-light;
@apply cursor-pointer;
}
14 changes: 12 additions & 2 deletions components/LandingNav/LandingNav.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('LandingNav', () => {
expect(nav).toBeInTheDocument();
expect(icon).toBeInTheDocument();

const navButton = screen.getByRole('button');
const navButton = screen.getByTestId('hamburger-button');

expect(navButton).toBeInTheDocument();
expect(navButton).toContainElement(icon);
Expand All @@ -51,7 +51,7 @@ describe('LandingNav', () => {
);
expect(screen.getByRole('banner')).toHaveStyle('opacity: 0');

fireEvent.click(screen.getByRole('button'));
fireEvent.click(screen.getByTestId('hamburger-icon'));
act(() => {
jest.advanceTimersByTime(1000);
});
Expand All @@ -60,5 +60,15 @@ describe('LandingNav', () => {
'transform: translateX(0%) translateZ(0);'
);
expect(screen.getByRole('banner')).toHaveStyle('opacity: 0.8');

fireEvent.click(screen.getByTestId('hamburger-button'));
act(() => {
jest.advanceTimersByTime(1000);
});

expect(screen.getByRole('navigation')).toHaveStyle(
'transform: translateX(-100%) translateZ(0);'
);
expect(screen.getByRole('banner')).toHaveStyle('opacity: 0');
});
});
16 changes: 7 additions & 9 deletions components/LandingNav/LandingNav.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Fork, Hamburger } from '@components/Icons';
import { Switch, Toggle } from '@components/Motion';
import { Hamburger } from '@components/Icons';
import { Switch } from '@components/Motion';
import type { MotionProps } from '@components/utils';
import { routes as defaultRoutes } from '@config';
import type { Route } from '@types';
Expand Down Expand Up @@ -49,6 +49,11 @@ const LandingNav = ({ routes = defaultRoutes }: Props): JSX.Element => {

return (
<>
<Hamburger
onClick={handleToggle}
className={styles.button}
data-testid="hamburger-button"
/>
<Switch
className={styles.nav}
role="navigation"
Expand All @@ -62,13 +67,6 @@ const LandingNav = ({ routes = defaultRoutes }: Props): JSX.Element => {
</LandingNavLink>
))}
</Switch>
<Toggle
className={styles.button}
isToggled={expanded}
onToggle={handleToggle}
iconClose={<Hamburger className={styles.icon} />}
iconOpen={<Fork className={styles.icon} />}
/>
<Switch
className={styles.banner}
role="banner"
Expand Down
59 changes: 31 additions & 28 deletions components/LandingNav/__snapshots__/LandingNav.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@

exports[`LandingNav should render routes correctly (snapshot) 1`] = `
<div>
<span
aria-label="Hamburger"
class="anticon button"
data-testid="hamburger-button"
role="img"
tabindex="-1"
>
<svg
data-testid="hamburger-icon"
fill="currentColor"
height="2em"
viewBox="0 0 1024 1024"
width="2em"
>
<path
d="M904 160H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"
style="transform: none; transform-origin: 0px 0px;"
transform-origin="0px 0px"
/>
<path
d="M904 472H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"
style="transform: none; transform-origin: 0px 0px;"
transform-origin="0px 0px"
/>
<path
d="M904 784H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"
style="transform: none; transform-origin: 0px 0px;"
transform-origin="0px 0px"
/>
</svg>
</span>
<div
class="nav"
role="navigation"
Expand Down Expand Up @@ -68,34 +99,6 @@ exports[`LandingNav should render routes correctly (snapshot) 1`] = `
</span>
</div>
</div>
<div
class="wrapper button"
role="button"
tabindex="0"
>
<span
aria-current="true"
class="span"
style="opacity: 1; transform: none;"
>
<span
aria-label="Hamburger"
class="anticon icon"
role="img"
>
<svg
fill="currentColor"
height="2em"
viewBox="0 0 1024 1024"
width="2em"
>
<path
d="M904 160H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8zM904 784H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8zM904 472H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"
/>
</svg>
</span>
</span>
</div>
<div
class="banner"
role="banner"
Expand Down
Loading

1 comment on commit d908726

@vercel
Copy link

@vercel vercel bot commented on d908726 May 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

blog – ./

blog-sabertaz.vercel.app
blog.tazimi.dev
blog-git-main-sabertaz.vercel.app

Please sign in to comment.