aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/molecules/forms/flipping-label
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-10-07 18:44:14 +0200
committerArmand Philippot <git@armandphilippot.com>2023-11-11 18:14:41 +0100
commitd75b9a1e150ab211c1052fb49bede9bd16320aca (patch)
treee5bb221d2b8dc83151697fe646e9194f921b5807 /src/components/molecules/forms/flipping-label
parent12a03a9a72f7895d571dbaeeb245d92aa277a610 (diff)
feat(components): add a generic Flip component
The flipping animation is used at several places so it makes sense to use a single component to handle the animation. It will avoid styles duplication.
Diffstat (limited to 'src/components/molecules/forms/flipping-label')
-rw-r--r--src/components/molecules/forms/flipping-label/flipping-label.module.scss58
-rw-r--r--src/components/molecules/forms/flipping-label/flipping-label.stories.tsx12
-rw-r--r--src/components/molecules/forms/flipping-label/flipping-label.test.tsx15
-rw-r--r--src/components/molecules/forms/flipping-label/flipping-label.tsx45
4 files changed, 54 insertions, 76 deletions
diff --git a/src/components/molecules/forms/flipping-label/flipping-label.module.scss b/src/components/molecules/forms/flipping-label/flipping-label.module.scss
index 4e7947f..169bde3 100644
--- a/src/components/molecules/forms/flipping-label/flipping-label.module.scss
+++ b/src/components/molecules/forms/flipping-label/flipping-label.module.scss
@@ -1,61 +1,17 @@
@use "../../../../styles/abstracts/functions" as fun;
-.label {
- display: block;
- width: var(--btn-size, #{fun.convert-px(60)});
- height: var(--btn-size, #{fun.convert-px(60)});
+.wrapper {
+ --size: var(--btn-size, #{fun.convert-px(60)});
+ --flipper-speed: 0.5s;
+
+ width: var(--size);
+ height: var(--size);
}
+.wrapper,
.front,
.back {
display: flex;
place-content: center;
- width: 100%;
- height: 100%;
- position: absolute;
- top: 0;
- right: 0;
- backface-visibility: hidden;
- transition: all 0.6s ease-in 0s;
-}
-
-.front {
- z-index: 20;
-}
-
-.back {
- z-index: 10;
-}
-
-.wrapper {
- display: flex;
- place-content: center;
place-items: center;
- width: 100%;
- height: 100%;
- position: relative;
- transition: all 0.5s ease-in-out 0s;
- transform-style: preserve-3d;
-
- &--active {
- transform: rotateY(180deg);
-
- .front {
- transform: scale(0.2);
- }
-
- .back {
- transform: scale(1) rotateY(180deg);
- }
- }
-
- &--inactive {
- .front {
- transform: scale(1);
- }
-
- .back {
- transform: scale(0.2) rotateY(180deg);
- }
- }
}
diff --git a/src/components/molecules/forms/flipping-label/flipping-label.stories.tsx b/src/components/molecules/forms/flipping-label/flipping-label.stories.tsx
index bf5724e..c3c4f9a 100644
--- a/src/components/molecules/forms/flipping-label/flipping-label.stories.tsx
+++ b/src/components/molecules/forms/flipping-label/flipping-label.stories.tsx
@@ -1,6 +1,6 @@
import type { ComponentMeta, ComponentStory } from '@storybook/react';
import { useCallback, useState } from 'react';
-import { Icon } from '../../../atoms';
+import { Button, Icon } from '../../../atoms';
import { FlippingLabel } from './flipping-label';
export default {
@@ -78,20 +78,22 @@ const Template: ComponentStory<typeof FlippingLabel> = ({
const updateState = useCallback(() => setActive((prev) => !prev), []);
return (
- <button onClick={updateState} type="button">
+ <Button kind="neutral" onClick={updateState} shape="initial" type="button">
<FlippingLabel {...args} isActive={active} />
- </button>
+ </Button>
);
};
export const Active = Template.bind({});
Active.args = {
- children: <Icon shape="magnifying-glass" />,
+ icon: <Icon shape="magnifying-glass" />,
isActive: true,
+ label: 'Close the search',
};
export const Inactive = Template.bind({});
Inactive.args = {
- children: <Icon shape="magnifying-glass" />,
+ icon: <Icon shape="magnifying-glass" />,
isActive: false,
+ label: 'Open the search',
};
diff --git a/src/components/molecules/forms/flipping-label/flipping-label.test.tsx b/src/components/molecules/forms/flipping-label/flipping-label.test.tsx
index 71ea2ba..d59c5f3 100644
--- a/src/components/molecules/forms/flipping-label/flipping-label.test.tsx
+++ b/src/components/molecules/forms/flipping-label/flipping-label.test.tsx
@@ -1,15 +1,18 @@
import { describe, expect, it } from '@jest/globals';
-import { render, screen } from '../../../../../tests/utils';
+import { render, screen as rtlScreen } from '@testing-library/react';
+import { Icon } from '../../../atoms';
import { FlippingLabel } from './flipping-label';
describe('FlippingLabel', () => {
it('renders a label', () => {
- const ariaLabel = 'vero quo inventore';
+ const label = 'vero quo inventore';
render(
- <FlippingLabel aria-label={ariaLabel} isActive={false}>
- <>Test</>
- </FlippingLabel>
+ <FlippingLabel
+ icon={<Icon shape="arrow" />}
+ isActive={false}
+ label={label}
+ />
);
- expect(screen.getByLabelText(ariaLabel)).toBeInTheDocument();
+ expect(rtlScreen.getByText(label)).toBeInTheDocument();
});
});
diff --git a/src/components/molecules/forms/flipping-label/flipping-label.tsx b/src/components/molecules/forms/flipping-label/flipping-label.tsx
index e9d6a10..586301f 100644
--- a/src/components/molecules/forms/flipping-label/flipping-label.tsx
+++ b/src/components/molecules/forms/flipping-label/flipping-label.tsx
@@ -1,37 +1,54 @@
-import type { FC } from 'react';
-import { Icon, Label, type LabelProps } from '../../../atoms';
+import type { FC, ReactNode } from 'react';
+import {
+ Icon,
+ Label,
+ VisuallyHidden,
+ type LabelProps,
+ Flip,
+ FlipSide,
+} from '../../../atoms';
import styles from './flipping-label.module.scss';
-export type FlippingLabelProps = Pick<
+export type FlippingLabelProps = Omit<
LabelProps,
- 'aria-label' | 'className' | 'htmlFor'
+ 'children' | 'isHidden' | 'isRequired'
> & {
/**
* The front icon.
*/
- children: JSX.Element;
+ icon: ReactNode;
/**
* Which side of the label should be displayed? True for the close icon.
*/
isActive: boolean;
+ /**
+ * An accessible name for the label.
+ */
+ label: string;
};
export const FlippingLabel: FC<FlippingLabelProps> = ({
- children,
className = '',
+ icon,
isActive,
+ label,
...props
}) => {
- const wrapperModifier = isActive ? 'wrapper--active' : 'wrapper--inactive';
+ const wrapperClass = `${styles.wrapper} ${className}`;
return (
- <Label {...props} className={`${styles.label} ${className}`}>
- <span className={`${styles.wrapper} ${styles[wrapperModifier]}`}>
- <span className={styles.front}>{children}</span>
- <span className={styles.back}>
- <Icon aria-hidden={true} shape="cross" />
- </span>
- </span>
+ <Label {...props} className={wrapperClass}>
+ <VisuallyHidden>{label}</VisuallyHidden>
+ <Flip
+ aria-hidden
+ // eslint-disable-next-line react/jsx-no-literals -- Shape allowed
+ showBack={isActive}
+ >
+ <FlipSide className={styles.front}>{icon}</FlipSide>
+ <FlipSide className={styles.back} isBack>
+ <Icon aria-hidden shape="cross" />
+ </FlipSide>
+ </Flip>
</Label>
);
};