From a6ff5eee45215effb3344cb5d631a27a7c0369aa Mon Sep 17 00:00:00 2001
From: Armand Philippot
Date: Fri, 22 Sep 2023 19:34:01 +0200
Subject: refactor(components): rewrite form components
---
.../forms/ackee-toggle/ackee-toggle.fixture.tsx | 1 +
.../forms/ackee-toggle/ackee-toggle.stories.tsx | 47 ++++
.../forms/ackee-toggle/ackee-toggle.test.tsx | 15 ++
.../organisms/forms/ackee-toggle/ackee-toggle.tsx | 139 ++++++++++++
.../organisms/forms/ackee-toggle/index.ts | 1 +
.../organisms/forms/comment-form.module.scss | 8 -
.../organisms/forms/comment-form.stories.tsx | 123 ----------
.../organisms/forms/comment-form.test.tsx | 23 --
src/components/organisms/forms/comment-form.tsx | 193 ----------------
.../forms/comment-form/comment-form.module.scss | 18 ++
.../forms/comment-form/comment-form.stories.tsx | 123 ++++++++++
.../forms/comment-form/comment-form.test.tsx | 23 ++
.../organisms/forms/comment-form/comment-form.tsx | 251 +++++++++++++++++++++
.../organisms/forms/comment-form/index.ts | 1 +
.../organisms/forms/contact-form.module.scss | 8 -
.../organisms/forms/contact-form.stories.tsx | 65 ------
.../organisms/forms/contact-form.test.tsx | 48 ----
src/components/organisms/forms/contact-form.tsx | 154 -------------
.../forms/contact-form/contact-form.module.scss | 15 ++
.../forms/contact-form/contact-form.stories.tsx | 65 ++++++
.../forms/contact-form/contact-form.test.tsx | 48 ++++
.../organisms/forms/contact-form/contact-form.tsx | 210 +++++++++++++++++
.../organisms/forms/contact-form/index.ts | 1 +
src/components/organisms/forms/index.ts | 4 +
.../organisms/forms/motion-toggle/index.ts | 1 +
.../forms/motion-toggle/motion-toggle.fixture.tsx | 1 +
.../forms/motion-toggle/motion-toggle.stories.tsx | 47 ++++
.../forms/motion-toggle/motion-toggle.test.tsx | 15 ++
.../forms/motion-toggle/motion-toggle.tsx | 89 ++++++++
.../organisms/forms/prism-theme-toggle/index.ts | 1 +
.../prism-theme-toggle.stories.tsx | 20 ++
.../prism-theme-toggle/prism-theme-toggle.test.tsx | 13 ++
.../prism-theme-toggle/prism-theme-toggle.tsx | 85 +++++++
.../organisms/forms/search-form.module.scss | 58 -----
.../organisms/forms/search-form.stories.tsx | 65 ------
.../organisms/forms/search-form.test.tsx | 16 --
src/components/organisms/forms/search-form.tsx | 72 ------
.../organisms/forms/search-form/index.ts | 1 +
.../forms/search-form/search-form.module.scss | 67 ++++++
.../forms/search-form/search-form.stories.tsx | 65 ++++++
.../forms/search-form/search-form.test.tsx | 16 ++
.../organisms/forms/search-form/search-form.tsx | 98 ++++++++
.../organisms/forms/theme-toggle/index.ts | 1 +
.../forms/theme-toggle/theme-toggle.stories.tsx | 20 ++
.../forms/theme-toggle/theme-toggle.test.tsx | 13 ++
.../organisms/forms/theme-toggle/theme-toggle.tsx | 76 +++++++
src/components/organisms/layout/no-results.tsx | 2 +-
src/components/organisms/modals/search-modal.tsx | 13 +-
.../organisms/modals/settings-modal.module.scss | 26 +--
.../organisms/modals/settings-modal.stories.tsx | 4 +-
.../organisms/modals/settings-modal.test.tsx | 4 +-
src/components/organisms/modals/settings-modal.tsx | 60 ++---
.../organisms/toolbar/main-nav.stories.tsx | 2 +-
src/components/organisms/toolbar/main-nav.tsx | 6 +-
.../organisms/toolbar/search.stories.tsx | 2 +-
src/components/organisms/toolbar/search.tsx | 6 +-
.../organisms/toolbar/settings.stories.tsx | 2 +-
src/components/organisms/toolbar/settings.tsx | 19 +-
src/components/organisms/toolbar/toolbar.tsx | 1 -
59 files changed, 1654 insertions(+), 917 deletions(-)
create mode 100644 src/components/organisms/forms/ackee-toggle/ackee-toggle.fixture.tsx
create mode 100644 src/components/organisms/forms/ackee-toggle/ackee-toggle.stories.tsx
create mode 100644 src/components/organisms/forms/ackee-toggle/ackee-toggle.test.tsx
create mode 100644 src/components/organisms/forms/ackee-toggle/ackee-toggle.tsx
create mode 100644 src/components/organisms/forms/ackee-toggle/index.ts
delete mode 100644 src/components/organisms/forms/comment-form.module.scss
delete mode 100644 src/components/organisms/forms/comment-form.stories.tsx
delete mode 100644 src/components/organisms/forms/comment-form.test.tsx
delete mode 100644 src/components/organisms/forms/comment-form.tsx
create mode 100644 src/components/organisms/forms/comment-form/comment-form.module.scss
create mode 100644 src/components/organisms/forms/comment-form/comment-form.stories.tsx
create mode 100644 src/components/organisms/forms/comment-form/comment-form.test.tsx
create mode 100644 src/components/organisms/forms/comment-form/comment-form.tsx
create mode 100644 src/components/organisms/forms/comment-form/index.ts
delete mode 100644 src/components/organisms/forms/contact-form.module.scss
delete mode 100644 src/components/organisms/forms/contact-form.stories.tsx
delete mode 100644 src/components/organisms/forms/contact-form.test.tsx
delete mode 100644 src/components/organisms/forms/contact-form.tsx
create mode 100644 src/components/organisms/forms/contact-form/contact-form.module.scss
create mode 100644 src/components/organisms/forms/contact-form/contact-form.stories.tsx
create mode 100644 src/components/organisms/forms/contact-form/contact-form.test.tsx
create mode 100644 src/components/organisms/forms/contact-form/contact-form.tsx
create mode 100644 src/components/organisms/forms/contact-form/index.ts
create mode 100644 src/components/organisms/forms/motion-toggle/index.ts
create mode 100644 src/components/organisms/forms/motion-toggle/motion-toggle.fixture.tsx
create mode 100644 src/components/organisms/forms/motion-toggle/motion-toggle.stories.tsx
create mode 100644 src/components/organisms/forms/motion-toggle/motion-toggle.test.tsx
create mode 100644 src/components/organisms/forms/motion-toggle/motion-toggle.tsx
create mode 100644 src/components/organisms/forms/prism-theme-toggle/index.ts
create mode 100644 src/components/organisms/forms/prism-theme-toggle/prism-theme-toggle.stories.tsx
create mode 100644 src/components/organisms/forms/prism-theme-toggle/prism-theme-toggle.test.tsx
create mode 100644 src/components/organisms/forms/prism-theme-toggle/prism-theme-toggle.tsx
delete mode 100644 src/components/organisms/forms/search-form.module.scss
delete mode 100644 src/components/organisms/forms/search-form.stories.tsx
delete mode 100644 src/components/organisms/forms/search-form.test.tsx
delete mode 100644 src/components/organisms/forms/search-form.tsx
create mode 100644 src/components/organisms/forms/search-form/index.ts
create mode 100644 src/components/organisms/forms/search-form/search-form.module.scss
create mode 100644 src/components/organisms/forms/search-form/search-form.stories.tsx
create mode 100644 src/components/organisms/forms/search-form/search-form.test.tsx
create mode 100644 src/components/organisms/forms/search-form/search-form.tsx
create mode 100644 src/components/organisms/forms/theme-toggle/index.ts
create mode 100644 src/components/organisms/forms/theme-toggle/theme-toggle.stories.tsx
create mode 100644 src/components/organisms/forms/theme-toggle/theme-toggle.test.tsx
create mode 100644 src/components/organisms/forms/theme-toggle/theme-toggle.tsx
(limited to 'src/components/organisms')
diff --git a/src/components/organisms/forms/ackee-toggle/ackee-toggle.fixture.tsx b/src/components/organisms/forms/ackee-toggle/ackee-toggle.fixture.tsx
new file mode 100644
index 0000000..04602f2
--- /dev/null
+++ b/src/components/organisms/forms/ackee-toggle/ackee-toggle.fixture.tsx
@@ -0,0 +1 @@
+export const storageKey = 'ackee';
diff --git a/src/components/organisms/forms/ackee-toggle/ackee-toggle.stories.tsx b/src/components/organisms/forms/ackee-toggle/ackee-toggle.stories.tsx
new file mode 100644
index 0000000..b5f8ef8
--- /dev/null
+++ b/src/components/organisms/forms/ackee-toggle/ackee-toggle.stories.tsx
@@ -0,0 +1,47 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import { AckeeToggle } from './ackee-toggle';
+import { storageKey } from './ackee-toggle.fixture';
+
+/**
+ * AckeeToggle - Storybook Meta
+ */
+export default {
+ title: 'Organisms/Forms/Toggle',
+ component: AckeeToggle,
+ argTypes: {
+ defaultValue: {
+ control: {
+ type: 'select',
+ },
+ description: 'Set the default value.',
+ options: ['full', 'partial'],
+ type: {
+ name: 'string',
+ required: true,
+ },
+ },
+ storageKey: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set local storage key.',
+ type: {
+ name: 'string',
+ required: true,
+ },
+ },
+ },
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => (
+
+);
+
+/**
+ * Toggle Stories - Ackee
+ */
+export const Ackee = Template.bind({});
+Ackee.args = {
+ defaultValue: 'full',
+ storageKey,
+};
diff --git a/src/components/organisms/forms/ackee-toggle/ackee-toggle.test.tsx b/src/components/organisms/forms/ackee-toggle/ackee-toggle.test.tsx
new file mode 100644
index 0000000..7784d5f
--- /dev/null
+++ b/src/components/organisms/forms/ackee-toggle/ackee-toggle.test.tsx
@@ -0,0 +1,15 @@
+import { render, screen } from '../../../../../tests/utils';
+import { AckeeToggle } from './ackee-toggle';
+import { storageKey } from './ackee-toggle.fixture';
+
+describe('AckeeToggle', () => {
+ // toHaveValue received undefined. Maybe because of localStorage hook...
+ it('renders a toggle component', () => {
+ render();
+ expect(
+ screen.getByRole('radiogroup', {
+ name: /Tracking:/i,
+ })
+ ).toBeInTheDocument();
+ });
+});
diff --git a/src/components/organisms/forms/ackee-toggle/ackee-toggle.tsx b/src/components/organisms/forms/ackee-toggle/ackee-toggle.tsx
new file mode 100644
index 0000000..681d384
--- /dev/null
+++ b/src/components/organisms/forms/ackee-toggle/ackee-toggle.tsx
@@ -0,0 +1,139 @@
+import { ChangeEvent, FC, useState } from 'react';
+import { useIntl } from 'react-intl';
+import {
+ type AckeeOptions,
+ useLocalStorage,
+ useUpdateAckeeOptions,
+} from '../../../../utils/hooks';
+import { Legend, List } from '../../../atoms';
+import {
+ Switch,
+ SwitchOption,
+ SwitchProps,
+ Tooltip,
+ TooltipProps,
+} from '../../../molecules';
+
+export type AckeeToggleProps = Omit<
+ SwitchProps,
+ 'isInline' | 'items' | 'name' | 'onSwitch' | 'value'
+> &
+ Pick & {
+ /**
+ * Set additional classnames to the toggle wrapper.
+ */
+ className?: string;
+ /**
+ * True if motion should be reduced by default.
+ */
+ defaultValue: AckeeOptions;
+ /**
+ * The local storage key to save preference.
+ */
+ storageKey: string;
+ };
+
+/**
+ * AckeeToggle component
+ *
+ * Render a Toggle component to set reduce motion.
+ */
+export const AckeeToggle: FC = ({
+ defaultValue,
+ direction,
+ storageKey,
+ ...props
+}) => {
+ const intl = useIntl();
+ const { value, setValue } = useLocalStorage(
+ storageKey,
+ defaultValue
+ );
+ const [isTooltipOpened, setIsTooltipOpened] = useState(false);
+
+ useUpdateAckeeOptions(value);
+
+ const ackeeLabel = intl.formatMessage({
+ defaultMessage: 'Tracking:',
+ description: 'AckeeToggle: select label',
+ id: '0gVlI3',
+ });
+ const partialLabel = intl.formatMessage({
+ defaultMessage: 'Partial',
+ description: 'AckeeToggle: partial option name',
+ id: 'tIZYpD',
+ });
+ const fullLabel = intl.formatMessage({
+ defaultMessage: 'Full',
+ description: 'AckeeToggle: full option name',
+ id: '5eD6y2',
+ });
+ const tooltipTitle = intl.formatMessage({
+ defaultMessage: 'Ackee tracking (analytics)',
+ description: 'AckeeToggle: tooltip title',
+ id: 'nGss/j',
+ });
+ const tooltipPartial = intl.formatMessage({
+ defaultMessage: 'Partial includes only page url, views and duration.',
+ description: 'AckeeToggle: tooltip message',
+ id: 'ZB/Aw2',
+ });
+ const tooltipFull = intl.formatMessage({
+ defaultMessage:
+ 'Full includes all information from partial as well as information about referrer, operating system, device, browser, screen size and language.',
+ description: 'AckeeToggle: tooltip message',
+ id: '7zDlQo',
+ });
+
+ const options: [SwitchOption, SwitchOption] = [
+ {
+ id: 'ackee-full',
+ label: fullLabel,
+ value: 'full',
+ },
+ {
+ id: 'ackee-partial',
+ label: partialLabel,
+ value: 'partial',
+ },
+ ];
+
+ const updateSetting = (e: ChangeEvent) => {
+ setValue(e.target.value === 'full' ? 'full' : 'partial');
+ };
+
+ const closeTooltip = () => {
+ setIsTooltipOpened(false);
+ };
+ const toggleTooltip = () => {
+ setIsTooltipOpened((prev) => !prev);
+ };
+
+ return (
+ {ackeeLabel}}
+ name="ackee"
+ onSwitch={updateSetting}
+ tooltip={
+
+
+
+ }
+ value={value}
+ />
+ );
+};
diff --git a/src/components/organisms/forms/ackee-toggle/index.ts b/src/components/organisms/forms/ackee-toggle/index.ts
new file mode 100644
index 0000000..7f6313c
--- /dev/null
+++ b/src/components/organisms/forms/ackee-toggle/index.ts
@@ -0,0 +1 @@
+export * from './ackee-toggle';
diff --git a/src/components/organisms/forms/comment-form.module.scss b/src/components/organisms/forms/comment-form.module.scss
deleted file mode 100644
index f3f2646..0000000
--- a/src/components/organisms/forms/comment-form.module.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-.field {
- width: 100%;
-}
-
-.button {
- display: block;
- margin: auto;
-}
diff --git a/src/components/organisms/forms/comment-form.stories.tsx b/src/components/organisms/forms/comment-form.stories.tsx
deleted file mode 100644
index a6069e6..0000000
--- a/src/components/organisms/forms/comment-form.stories.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-import { ComponentMeta, ComponentStory } from '@storybook/react';
-import { CommentForm } from './comment-form';
-
-const saveComment = async () => {
- /** Do nothing. */
-};
-
-/**
- * CommentForm - Storybook Meta
- */
-export default {
- title: 'Organisms/Forms',
- component: CommentForm,
- args: {
- saveComment,
- titleAlignment: 'left',
- titleLevel: 2,
- },
- argTypes: {
- className: {
- control: {
- type: 'text',
- },
- description: 'Set additional classnames to the form wrapper.',
- table: {
- category: 'Styles',
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- Notice: {
- control: {
- type: null,
- },
- description: 'A component to display a success or error message.',
- table: {
- category: 'Options',
- },
- type: {
- name: 'function',
- required: false,
- },
- },
- parentId: {
- control: {
- type: null,
- },
- description: 'The parent id if it is a reply.',
- type: {
- name: 'number',
- required: false,
- },
- },
- saveComment: {
- control: {
- type: null,
- },
- description: 'A callback function to process the comment form data.',
- table: {
- category: 'Events',
- },
- type: {
- name: 'function',
- required: true,
- },
- },
- title: {
- control: {
- type: 'text',
- },
- description: 'The form title.',
- table: {
- category: 'Options',
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- titleAlignment: {
- control: {
- type: 'select',
- },
- description: 'The heading alignment.',
- options: ['center', 'left'],
- table: {
- category: 'Options',
- defaultValue: { summary: 'left' },
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- titleLevel: {
- control: {
- type: 'number',
- min: 1,
- max: 6,
- },
- description: 'The title level (hn).',
- table: {
- category: 'Options',
- defaultValue: { summary: 2 },
- },
- type: {
- name: 'number',
- required: false,
- },
- },
- },
-} as ComponentMeta;
-
-const Template: ComponentStory = (args) => (
-
-);
-
-/**
- * Forms Stories - Comment
- */
-export const Comment = Template.bind({});
diff --git a/src/components/organisms/forms/comment-form.test.tsx b/src/components/organisms/forms/comment-form.test.tsx
deleted file mode 100644
index f11c449..0000000
--- a/src/components/organisms/forms/comment-form.test.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { render, screen } from '../../../../tests/utils';
-import { CommentForm } from './comment-form';
-
-const saveComment = async () => {
- /** Do nothing. */
-};
-const title = 'Cum voluptas voluptatibus';
-
-describe('CommentForm', () => {
- it('renders a form', () => {
- render();
- expect(screen.getByRole('form')).toBeInTheDocument();
- });
-
- it('renders an optional title', () => {
- render(
-
- );
- expect(
- screen.getByRole('heading', { level: 2, name: title })
- ).toBeInTheDocument();
- });
-});
diff --git a/src/components/organisms/forms/comment-form.tsx b/src/components/organisms/forms/comment-form.tsx
deleted file mode 100644
index e4140dd..0000000
--- a/src/components/organisms/forms/comment-form.tsx
+++ /dev/null
@@ -1,193 +0,0 @@
-import { FC, ReactNode, useState } from 'react';
-import { useIntl } from 'react-intl';
-import {
- Button,
- Form,
- type FormProps,
- Heading,
- type HeadingLevel,
- type HeadingProps,
- Spinner,
-} from '../../atoms';
-import { LabelledField } from '../../molecules';
-import styles from './comment-form.module.scss';
-
-export type CommentFormData = {
- comment: string;
- email: string;
- name: string;
- parentId?: number;
- website?: string;
-};
-
-export type CommentFormProps = Pick & {
- /**
- * Pass a component to print a success/error message.
- */
- Notice?: ReactNode;
- /**
- * The comment parent id.
- */
- parentId?: number;
- /**
- * A callback function to save comment. It takes a function as parameter to
- * reset the form.
- */
- saveComment: (data: CommentFormData, reset: () => void) => Promise;
- /**
- * The form title.
- */
- title?: string;
- /**
- * The form title alignment. Default: left.
- */
- titleAlignment?: HeadingProps['alignment'];
- /**
- * The title level. Default: 2.
- */
- titleLevel?: HeadingLevel;
-};
-
-export const CommentForm: FC = ({
- Notice,
- parentId,
- saveComment,
- title,
- titleAlignment,
- titleLevel = 2,
- ...props
-}) => {
- const intl = useIntl();
- const [name, setName] = useState('');
- const [email, setEmail] = useState('');
- const [website, setWebsite] = useState('');
- const [comment, setComment] = useState('');
- const [isSubmitting, setIsSubmitting] = useState(false);
-
- /**
- * Reset all the form fields.
- */
- const resetForm = () => {
- setName('');
- setEmail('');
- setWebsite('');
- setComment('');
- setIsSubmitting(false);
- };
-
- const nameLabel = intl.formatMessage({
- defaultMessage: 'Name:',
- description: 'CommentForm: name label',
- id: 'ZIrTee',
- });
-
- const emailLabel = intl.formatMessage({
- defaultMessage: 'Email:',
- description: 'CommentForm: email label',
- id: 'Bh7z5v',
- });
-
- const websiteLabel = intl.formatMessage({
- defaultMessage: 'Website:',
- description: 'CommentForm: website label',
- id: 'u41qSk',
- });
-
- const commentLabel = intl.formatMessage({
- defaultMessage: 'Comment:',
- description: 'CommentForm: comment label',
- id: 'A8hGaK',
- });
-
- const formTitle = intl.formatMessage({
- defaultMessage: 'Comment form',
- description: 'CommentForm: aria label',
- id: 'dz2kDV',
- });
-
- const formAriaLabel = title ? undefined : formTitle;
- const formId = 'comment-form-title';
- const formLabelledBy = title ? formId : undefined;
-
- /**
- * Handle form submit.
- */
- const submitHandler = () => {
- setIsSubmitting(true);
- saveComment({ comment, email, name, parentId, website }, resetForm).then(
- () => setIsSubmitting(false)
- );
- };
-
- return (
-
- );
-};
diff --git a/src/components/organisms/forms/comment-form/comment-form.module.scss b/src/components/organisms/forms/comment-form/comment-form.module.scss
new file mode 100644
index 0000000..fbf8c96
--- /dev/null
+++ b/src/components/organisms/forms/comment-form/comment-form.module.scss
@@ -0,0 +1,18 @@
+.form {
+ display: flex;
+ flex-flow: column wrap;
+ gap: var(--spacing-xs);
+
+ > * {
+ max-width: 45ch;
+ }
+}
+
+.field {
+ width: 100%;
+}
+
+.button {
+ display: block;
+ margin: var(--spacing-sm) auto 0;
+}
diff --git a/src/components/organisms/forms/comment-form/comment-form.stories.tsx b/src/components/organisms/forms/comment-form/comment-form.stories.tsx
new file mode 100644
index 0000000..a6069e6
--- /dev/null
+++ b/src/components/organisms/forms/comment-form/comment-form.stories.tsx
@@ -0,0 +1,123 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import { CommentForm } from './comment-form';
+
+const saveComment = async () => {
+ /** Do nothing. */
+};
+
+/**
+ * CommentForm - Storybook Meta
+ */
+export default {
+ title: 'Organisms/Forms',
+ component: CommentForm,
+ args: {
+ saveComment,
+ titleAlignment: 'left',
+ titleLevel: 2,
+ },
+ argTypes: {
+ className: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the form wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ Notice: {
+ control: {
+ type: null,
+ },
+ description: 'A component to display a success or error message.',
+ table: {
+ category: 'Options',
+ },
+ type: {
+ name: 'function',
+ required: false,
+ },
+ },
+ parentId: {
+ control: {
+ type: null,
+ },
+ description: 'The parent id if it is a reply.',
+ type: {
+ name: 'number',
+ required: false,
+ },
+ },
+ saveComment: {
+ control: {
+ type: null,
+ },
+ description: 'A callback function to process the comment form data.',
+ table: {
+ category: 'Events',
+ },
+ type: {
+ name: 'function',
+ required: true,
+ },
+ },
+ title: {
+ control: {
+ type: 'text',
+ },
+ description: 'The form title.',
+ table: {
+ category: 'Options',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ titleAlignment: {
+ control: {
+ type: 'select',
+ },
+ description: 'The heading alignment.',
+ options: ['center', 'left'],
+ table: {
+ category: 'Options',
+ defaultValue: { summary: 'left' },
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ titleLevel: {
+ control: {
+ type: 'number',
+ min: 1,
+ max: 6,
+ },
+ description: 'The title level (hn).',
+ table: {
+ category: 'Options',
+ defaultValue: { summary: 2 },
+ },
+ type: {
+ name: 'number',
+ required: false,
+ },
+ },
+ },
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => (
+
+);
+
+/**
+ * Forms Stories - Comment
+ */
+export const Comment = Template.bind({});
diff --git a/src/components/organisms/forms/comment-form/comment-form.test.tsx b/src/components/organisms/forms/comment-form/comment-form.test.tsx
new file mode 100644
index 0000000..8aa38af
--- /dev/null
+++ b/src/components/organisms/forms/comment-form/comment-form.test.tsx
@@ -0,0 +1,23 @@
+import { render, screen } from '../../../../../tests/utils';
+import { CommentForm } from './comment-form';
+
+const saveComment = async () => {
+ /** Do nothing. */
+};
+const title = 'Cum voluptas voluptatibus';
+
+describe('CommentForm', () => {
+ it('renders a form', () => {
+ render();
+ expect(screen.getByRole('form')).toBeInTheDocument();
+ });
+
+ it('renders an optional title', () => {
+ render(
+
+ );
+ expect(
+ screen.getByRole('heading', { level: 2, name: title })
+ ).toBeInTheDocument();
+ });
+});
diff --git a/src/components/organisms/forms/comment-form/comment-form.tsx b/src/components/organisms/forms/comment-form/comment-form.tsx
new file mode 100644
index 0000000..be5d58f
--- /dev/null
+++ b/src/components/organisms/forms/comment-form/comment-form.tsx
@@ -0,0 +1,251 @@
+import { ChangeEvent, FC, FormEvent, ReactNode, useState } from 'react';
+import { useIntl } from 'react-intl';
+import {
+ Button,
+ Form,
+ type FormProps,
+ Heading,
+ type HeadingLevel,
+ type HeadingProps,
+ Spinner,
+ Input,
+ TextArea,
+ Label,
+} from '../../../atoms';
+import { LabelledField } from '../../../molecules';
+import styles from './comment-form.module.scss';
+
+export type CommentFormData = {
+ author: string;
+ comment: string;
+ email: string;
+ parentId?: number;
+ website?: string;
+};
+
+export type CommentFormProps = Pick & {
+ /**
+ * Pass a component to print a success/error message.
+ */
+ Notice?: ReactNode;
+ /**
+ * The comment parent id.
+ */
+ parentId?: number;
+ /**
+ * A callback function to save comment. It takes a function as parameter to
+ * reset the form.
+ */
+ saveComment: (data: CommentFormData, reset: () => void) => Promise;
+ /**
+ * The form title.
+ */
+ title?: string;
+ /**
+ * The form title alignment. Default: left.
+ */
+ titleAlignment?: HeadingProps['alignment'];
+ /**
+ * The title level. Default: 2.
+ */
+ titleLevel?: HeadingLevel;
+};
+
+export const CommentForm: FC = ({
+ className = '',
+ Notice,
+ parentId,
+ saveComment,
+ title,
+ titleAlignment,
+ titleLevel = 2,
+ ...props
+}) => {
+ const formClass = `${styles.form} ${className}`;
+ const intl = useIntl();
+ const emptyForm: CommentFormData = {
+ author: '',
+ comment: '',
+ email: '',
+ parentId,
+ website: '',
+ };
+ const [data, setData] = useState(emptyForm);
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ /**
+ * Reset all the form fields.
+ */
+ const resetForm = () => {
+ setData(emptyForm);
+ setIsSubmitting(false);
+ };
+
+ const nameLabel = intl.formatMessage({
+ defaultMessage: 'Name:',
+ description: 'CommentForm: name label',
+ id: 'ZIrTee',
+ });
+
+ const emailLabel = intl.formatMessage({
+ defaultMessage: 'Email:',
+ description: 'CommentForm: email label',
+ id: 'Bh7z5v',
+ });
+
+ const websiteLabel = intl.formatMessage({
+ defaultMessage: 'Website:',
+ description: 'CommentForm: website label',
+ id: 'u41qSk',
+ });
+
+ const commentLabel = intl.formatMessage({
+ defaultMessage: 'Comment:',
+ description: 'CommentForm: comment label',
+ id: 'A8hGaK',
+ });
+
+ const formTitle = intl.formatMessage({
+ defaultMessage: 'Comment form',
+ description: 'CommentForm: aria label',
+ id: 'dz2kDV',
+ });
+
+ const formAriaLabel = title ? undefined : formTitle;
+ const formId = 'comment-form-title';
+ const formLabelledBy = title ? formId : undefined;
+
+ const updateForm = (
+ e: ChangeEvent
+ ) => {
+ switch (e.target.name) {
+ case 'author':
+ setData((prevData) => {
+ return { ...prevData, author: e.target.value };
+ });
+ break;
+ case 'comment':
+ setData((prevData) => {
+ return { ...prevData, comment: e.target.value };
+ });
+ break;
+ case 'email':
+ setData((prevData) => {
+ return { ...prevData, email: e.target.value };
+ });
+ break;
+ case 'website':
+ setData((prevData) => {
+ return { ...prevData, website: e.target.value };
+ });
+ break;
+ default:
+ break;
+ }
+ };
+
+ const submitHandler = (e: FormEvent) => {
+ e.preventDefault();
+ setIsSubmitting(true);
+ saveComment(data, resetForm).then(() => setIsSubmitting(false));
+ };
+
+ return (
+
+ );
+};
diff --git a/src/components/organisms/forms/comment-form/index.ts b/src/components/organisms/forms/comment-form/index.ts
new file mode 100644
index 0000000..9e22bd9
--- /dev/null
+++ b/src/components/organisms/forms/comment-form/index.ts
@@ -0,0 +1 @@
+export * from './comment-form';
diff --git a/src/components/organisms/forms/contact-form.module.scss b/src/components/organisms/forms/contact-form.module.scss
deleted file mode 100644
index f3f2646..0000000
--- a/src/components/organisms/forms/contact-form.module.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-.field {
- width: 100%;
-}
-
-.button {
- display: block;
- margin: auto;
-}
diff --git a/src/components/organisms/forms/contact-form.stories.tsx b/src/components/organisms/forms/contact-form.stories.tsx
deleted file mode 100644
index 4df3db0..0000000
--- a/src/components/organisms/forms/contact-form.stories.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { ComponentMeta, ComponentStory } from '@storybook/react';
-import { ContactForm } from './contact-form';
-
-/**
- * ContactForm - Storybook Meta
- */
-export default {
- title: 'Organisms/Forms',
- component: ContactForm,
- argTypes: {
- className: {
- control: {
- type: 'text',
- },
- description: 'Set additional classnames to the form wrapper.',
- table: {
- category: 'Styles',
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- Notice: {
- control: {
- type: null,
- },
- description: 'A component to display a success or error message.',
- table: {
- category: 'Options',
- },
- type: {
- name: 'function',
- required: false,
- },
- },
- sendMail: {
- control: {
- type: null,
- },
- description: 'A callback function to process the contact form data.',
- table: {
- category: 'Events',
- },
- type: {
- name: 'function',
- required: true,
- },
- },
- },
-} as ComponentMeta;
-
-const Template: ComponentStory = (args) => (
-
-);
-
-/**
- * Forms Stories - Contact
- */
-export const Contact = Template.bind({});
-Contact.args = {
- sendMail: async (_data, reset: () => void) => {
- reset();
- },
-};
diff --git a/src/components/organisms/forms/contact-form.test.tsx b/src/components/organisms/forms/contact-form.test.tsx
deleted file mode 100644
index 8e27cd0..0000000
--- a/src/components/organisms/forms/contact-form.test.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { render, screen } from '../../../../tests/utils';
-import { ContactForm } from './contact-form';
-
-const props = {
- sendMail: async () => {
- /** Do nothing. */
- },
-};
-
-describe('ContactForm', () => {
- it('renders a contact form', () => {
- render();
- expect(
- screen.getByRole('form', { name: 'Contact form' })
- ).toBeInTheDocument();
- });
-
- it('renders a name field', () => {
- render();
- expect(screen.getByRole('textbox', { name: /^Name:/ })).toBeInTheDocument();
- });
-
- it('renders an email field', () => {
- render();
- expect(
- screen.getByRole('textbox', { name: /^Email:/ })
- ).toBeInTheDocument();
- });
-
- it('renders an object field', () => {
- render();
- expect(
- screen.getByRole('textbox', { name: /^Object:/ })
- ).toBeInTheDocument();
- });
-
- it('renders a message field', () => {
- render();
- expect(
- screen.getByRole('textbox', { name: /^Message:/ })
- ).toBeInTheDocument();
- });
-
- it('renders a submit button', () => {
- render();
- expect(screen.getByRole('button', { name: /^Send/ })).toBeInTheDocument();
- });
-});
diff --git a/src/components/organisms/forms/contact-form.tsx b/src/components/organisms/forms/contact-form.tsx
deleted file mode 100644
index ca84c25..0000000
--- a/src/components/organisms/forms/contact-form.tsx
+++ /dev/null
@@ -1,154 +0,0 @@
-import { FC, ReactNode, useState } from 'react';
-import { useIntl } from 'react-intl';
-import { Button, Form, Spinner } from '../../atoms';
-import { LabelledField } from '../../molecules';
-import styles from './contact-form.module.scss';
-
-export type ContactFormData = {
- email: string;
- message: string;
- name: string;
- subject: string;
-};
-
-export type ContactFormProps = {
- /**
- * Set additional classnames to the form wrapper.
- */
- className?: string;
- /**
- * Pass a component to print a success/error message.
- */
- Notice?: ReactNode;
- /**
- * A callback function to send mail.
- */
- sendMail: (data: ContactFormData, reset: () => void) => Promise;
-};
-
-/**
- * ContactForm component
- *
- * Render a contact form.
- */
-export const ContactForm: FC = ({
- className = '',
- Notice,
- sendMail,
-}) => {
- const intl = useIntl();
- const [name, setName] = useState('');
- const [email, setEmail] = useState('');
- const [object, setObject] = useState('');
- const [message, setMessage] = useState('');
- const [isSubmitting, setIsSubmitting] = useState(false);
-
- /**
- * Reset all the form fields.
- */
- const resetForm = () => {
- setName('');
- setEmail('');
- setObject('');
- setMessage('');
- setIsSubmitting(false);
- };
-
- const formName = intl.formatMessage({
- defaultMessage: 'Contact form',
- description: 'ContactForm: form accessible name',
- id: 'HFdzae',
- });
-
- const nameLabel = intl.formatMessage({
- defaultMessage: 'Name:',
- description: 'ContactForm: name label',
- id: '1dCuCx',
- });
-
- const emailLabel = intl.formatMessage({
- defaultMessage: 'Email:',
- description: 'ContactForm: email label',
- id: 'w4B5PA',
- });
-
- const objectLabel = intl.formatMessage({
- defaultMessage: 'Object:',
- description: 'ContactForm: object label',
- id: 's8/tyz',
- });
-
- const messageLabel = intl.formatMessage({
- defaultMessage: 'Message:',
- description: 'ContactForm: message label',
- id: 'yN5P+m',
- });
-
- const submitHandler = async () => {
- setIsSubmitting(true);
- sendMail({ email, message, name, subject: object }, resetForm).then(() =>
- setIsSubmitting(false)
- );
- };
-
- return (
-
- );
-};
diff --git a/src/components/organisms/forms/contact-form/contact-form.module.scss b/src/components/organisms/forms/contact-form/contact-form.module.scss
new file mode 100644
index 0000000..c106fb1
--- /dev/null
+++ b/src/components/organisms/forms/contact-form/contact-form.module.scss
@@ -0,0 +1,15 @@
+.form {
+ display: flex;
+ flex-flow: column wrap;
+ gap: var(--spacing-xs);
+ max-width: 45ch;
+}
+
+.field {
+ width: 100%;
+}
+
+.button {
+ display: block;
+ margin: var(--spacing-sm) auto 0;
+}
diff --git a/src/components/organisms/forms/contact-form/contact-form.stories.tsx b/src/components/organisms/forms/contact-form/contact-form.stories.tsx
new file mode 100644
index 0000000..4df3db0
--- /dev/null
+++ b/src/components/organisms/forms/contact-form/contact-form.stories.tsx
@@ -0,0 +1,65 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import { ContactForm } from './contact-form';
+
+/**
+ * ContactForm - Storybook Meta
+ */
+export default {
+ title: 'Organisms/Forms',
+ component: ContactForm,
+ argTypes: {
+ className: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the form wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ Notice: {
+ control: {
+ type: null,
+ },
+ description: 'A component to display a success or error message.',
+ table: {
+ category: 'Options',
+ },
+ type: {
+ name: 'function',
+ required: false,
+ },
+ },
+ sendMail: {
+ control: {
+ type: null,
+ },
+ description: 'A callback function to process the contact form data.',
+ table: {
+ category: 'Events',
+ },
+ type: {
+ name: 'function',
+ required: true,
+ },
+ },
+ },
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => (
+
+);
+
+/**
+ * Forms Stories - Contact
+ */
+export const Contact = Template.bind({});
+Contact.args = {
+ sendMail: async (_data, reset: () => void) => {
+ reset();
+ },
+};
diff --git a/src/components/organisms/forms/contact-form/contact-form.test.tsx b/src/components/organisms/forms/contact-form/contact-form.test.tsx
new file mode 100644
index 0000000..59d69fa
--- /dev/null
+++ b/src/components/organisms/forms/contact-form/contact-form.test.tsx
@@ -0,0 +1,48 @@
+import { render, screen } from '../../../../../tests/utils';
+import { ContactForm } from './contact-form';
+
+const props = {
+ sendMail: async () => {
+ /** Do nothing. */
+ },
+};
+
+describe('ContactForm', () => {
+ it('renders a contact form', () => {
+ render();
+ expect(
+ screen.getByRole('form', { name: 'Contact form' })
+ ).toBeInTheDocument();
+ });
+
+ it('renders a name field', () => {
+ render();
+ expect(screen.getByRole('textbox', { name: /^Name:/ })).toBeInTheDocument();
+ });
+
+ it('renders an email field', () => {
+ render();
+ expect(
+ screen.getByRole('textbox', { name: /^Email:/ })
+ ).toBeInTheDocument();
+ });
+
+ it('renders an object field', () => {
+ render();
+ expect(
+ screen.getByRole('textbox', { name: /^Object:/ })
+ ).toBeInTheDocument();
+ });
+
+ it('renders a message field', () => {
+ render();
+ expect(
+ screen.getByRole('textbox', { name: /^Message:/ })
+ ).toBeInTheDocument();
+ });
+
+ it('renders a submit button', () => {
+ render();
+ expect(screen.getByRole('button', { name: /^Send/ })).toBeInTheDocument();
+ });
+});
diff --git a/src/components/organisms/forms/contact-form/contact-form.tsx b/src/components/organisms/forms/contact-form/contact-form.tsx
new file mode 100644
index 0000000..6208b94
--- /dev/null
+++ b/src/components/organisms/forms/contact-form/contact-form.tsx
@@ -0,0 +1,210 @@
+import { ChangeEvent, FC, FormEvent, ReactNode, useState } from 'react';
+import { useIntl } from 'react-intl';
+import { Button, Form, Input, Label, Spinner, TextArea } from '../../../atoms';
+import { LabelledField } from '../../../molecules';
+import styles from './contact-form.module.scss';
+
+export type ContactFormData = {
+ email: string;
+ message: string;
+ name: string;
+ object: string;
+};
+
+export type ContactFormProps = {
+ /**
+ * Set additional classnames to the form wrapper.
+ */
+ className?: string;
+ /**
+ * Pass a component to print a success/error message.
+ */
+ Notice?: ReactNode;
+ /**
+ * A callback function to send mail.
+ */
+ sendMail: (data: ContactFormData, reset: () => void) => Promise;
+};
+
+/**
+ * ContactForm component
+ *
+ * Render a contact form.
+ */
+export const ContactForm: FC = ({
+ className = '',
+ Notice,
+ sendMail,
+}) => {
+ const formClass = `${styles.form} ${className}`;
+ const intl = useIntl();
+ const emptyForm: ContactFormData = {
+ email: '',
+ message: '',
+ name: '',
+ object: '',
+ };
+ const [data, setData] = useState(emptyForm);
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ /**
+ * Reset all the form fields.
+ */
+ const resetForm = () => {
+ setData(emptyForm);
+ setIsSubmitting(false);
+ };
+
+ const updateForm = (
+ e: ChangeEvent
+ ) => {
+ switch (e.target.name) {
+ case 'email':
+ setData((prevData) => {
+ return { ...prevData, email: e.target.value };
+ });
+ break;
+ case 'message':
+ setData((prevData) => {
+ return { ...prevData, message: e.target.value };
+ });
+ break;
+ case 'name':
+ setData((prevData) => {
+ return { ...prevData, name: e.target.value };
+ });
+ break;
+ case 'object':
+ setData((prevData) => {
+ return { ...prevData, object: e.target.value };
+ });
+ break;
+ default:
+ break;
+ }
+ };
+
+ const formName = intl.formatMessage({
+ defaultMessage: 'Contact form',
+ description: 'ContactForm: form accessible name',
+ id: 'HFdzae',
+ });
+
+ const nameLabel = intl.formatMessage({
+ defaultMessage: 'Name:',
+ description: 'ContactForm: name label',
+ id: '1dCuCx',
+ });
+
+ const emailLabel = intl.formatMessage({
+ defaultMessage: 'Email:',
+ description: 'ContactForm: email label',
+ id: 'w4B5PA',
+ });
+
+ const objectLabel = intl.formatMessage({
+ defaultMessage: 'Object:',
+ description: 'ContactForm: object label',
+ id: 's8/tyz',
+ });
+
+ const messageLabel = intl.formatMessage({
+ defaultMessage: 'Message:',
+ description: 'ContactForm: message label',
+ id: 'yN5P+m',
+ });
+
+ const submitHandler = async (e: FormEvent) => {
+ e.preventDefault();
+ setIsSubmitting(true);
+ sendMail(data, resetForm).then(() => setIsSubmitting(false));
+ };
+
+ return (
+
+ );
+};
diff --git a/src/components/organisms/forms/contact-form/index.ts b/src/components/organisms/forms/contact-form/index.ts
new file mode 100644
index 0000000..c72af3d
--- /dev/null
+++ b/src/components/organisms/forms/contact-form/index.ts
@@ -0,0 +1 @@
+export * from './contact-form';
diff --git a/src/components/organisms/forms/index.ts b/src/components/organisms/forms/index.ts
index 10eaf20..e507895 100644
--- a/src/components/organisms/forms/index.ts
+++ b/src/components/organisms/forms/index.ts
@@ -1,3 +1,7 @@
+export * from './ackee-toggle';
export * from './comment-form';
export * from './contact-form';
+export * from './motion-toggle';
+export * from './prism-theme-toggle';
export * from './search-form';
+export * from './theme-toggle';
diff --git a/src/components/organisms/forms/motion-toggle/index.ts b/src/components/organisms/forms/motion-toggle/index.ts
new file mode 100644
index 0000000..0e35578
--- /dev/null
+++ b/src/components/organisms/forms/motion-toggle/index.ts
@@ -0,0 +1 @@
+export * from './motion-toggle';
diff --git a/src/components/organisms/forms/motion-toggle/motion-toggle.fixture.tsx b/src/components/organisms/forms/motion-toggle/motion-toggle.fixture.tsx
new file mode 100644
index 0000000..f13658a
--- /dev/null
+++ b/src/components/organisms/forms/motion-toggle/motion-toggle.fixture.tsx
@@ -0,0 +1 @@
+export const storageKey = 'reduced-motion';
diff --git a/src/components/organisms/forms/motion-toggle/motion-toggle.stories.tsx b/src/components/organisms/forms/motion-toggle/motion-toggle.stories.tsx
new file mode 100644
index 0000000..7e541db
--- /dev/null
+++ b/src/components/organisms/forms/motion-toggle/motion-toggle.stories.tsx
@@ -0,0 +1,47 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import { MotionToggle } from './motion-toggle';
+import { storageKey } from './motion-toggle.fixture';
+
+/**
+ * MotionToggle - Storybook Meta
+ */
+export default {
+ title: 'Organisms/Forms/Toggle',
+ component: MotionToggle,
+ argTypes: {
+ defaultValue: {
+ control: {
+ type: 'select',
+ },
+ description: 'Set the default value.',
+ options: ['on', 'off'],
+ type: {
+ name: 'string',
+ required: true,
+ },
+ },
+ storageKey: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set local storage key.',
+ type: {
+ name: 'string',
+ required: true,
+ },
+ },
+ },
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => (
+
+);
+
+/**
+ * Toggle Stories - Motion
+ */
+export const Motion = Template.bind({});
+Motion.args = {
+ defaultValue: 'on',
+ storageKey,
+};
diff --git a/src/components/organisms/forms/motion-toggle/motion-toggle.test.tsx b/src/components/organisms/forms/motion-toggle/motion-toggle.test.tsx
new file mode 100644
index 0000000..614c038
--- /dev/null
+++ b/src/components/organisms/forms/motion-toggle/motion-toggle.test.tsx
@@ -0,0 +1,15 @@
+import { render, screen } from '../../../../../tests/utils';
+import { MotionToggle } from './motion-toggle';
+import { storageKey } from './motion-toggle.fixture';
+
+describe('MotionToggle', () => {
+ // toHaveValue received undefined. Maybe because of localStorage hook...
+ it('renders a toggle component', () => {
+ render();
+ expect(
+ screen.getByRole('radiogroup', {
+ name: /Animations:/i,
+ })
+ ).toBeInTheDocument();
+ });
+});
diff --git a/src/components/organisms/forms/motion-toggle/motion-toggle.tsx b/src/components/organisms/forms/motion-toggle/motion-toggle.tsx
new file mode 100644
index 0000000..a8ca7ce
--- /dev/null
+++ b/src/components/organisms/forms/motion-toggle/motion-toggle.tsx
@@ -0,0 +1,89 @@
+import { ChangeEvent, FC } from 'react';
+import { useIntl } from 'react-intl';
+import { useAttributes, useLocalStorage } from '../../../../utils/hooks';
+import { Legend } from '../../../atoms';
+import { Switch, SwitchOption, SwitchProps } from '../../../molecules';
+
+export type MotionToggleValue = 'on' | 'off';
+
+export type MotionToggleProps = Omit<
+ SwitchProps,
+ 'isInline' | 'items' | 'name' | 'onSwitch' | 'value'
+> & {
+ /**
+ * True if motion should be reduced by default.
+ */
+ defaultValue: 'on' | 'off';
+ /**
+ * The local storage key to save preference.
+ */
+ storageKey: string;
+};
+
+/**
+ * MotionToggle component
+ *
+ * Render a Toggle component to set reduce motion.
+ */
+export const MotionToggle: FC = ({
+ defaultValue,
+ storageKey,
+ ...props
+}) => {
+ const intl = useIntl();
+ const { value: isReduced, setValue: setIsReduced } = useLocalStorage(
+ storageKey,
+ defaultValue !== 'on'
+ );
+ useAttributes({
+ element:
+ typeof window !== 'undefined' ? document.documentElement : undefined,
+ attribute: 'reduced-motion',
+ value: `${isReduced}`,
+ });
+
+ const reduceMotionLabel = intl.formatMessage({
+ defaultMessage: 'Animations:',
+ description: 'MotionToggle: reduce motion label',
+ id: '/q5csZ',
+ });
+ const onLabel = intl.formatMessage({
+ defaultMessage: 'On',
+ description: 'MotionToggle: activate reduce motion label',
+ id: 'va65iw',
+ });
+ const offLabel = intl.formatMessage({
+ defaultMessage: 'Off',
+ description: 'MotionToggle: deactivate reduce motion label',
+ id: 'pWKyyR',
+ });
+
+ const options: [SwitchOption, SwitchOption] = [
+ {
+ id: 'reduced-motion-on',
+ label: onLabel,
+ value: 'on',
+ },
+ {
+ id: 'reduced-motion-off',
+ label: offLabel,
+ value: 'off',
+ },
+ ];
+
+ const updateSetting = (e: ChangeEvent) => {
+ setIsReduced((prev) => !prev);
+ };
+
+ return (
+ {reduceMotionLabel}}
+ name="reduced-motion"
+ onSwitch={updateSetting}
+ value={isReduced ? 'off' : 'on'}
+ />
+ );
+};
diff --git a/src/components/organisms/forms/prism-theme-toggle/index.ts b/src/components/organisms/forms/prism-theme-toggle/index.ts
new file mode 100644
index 0000000..f4e490f
--- /dev/null
+++ b/src/components/organisms/forms/prism-theme-toggle/index.ts
@@ -0,0 +1 @@
+export * from './prism-theme-toggle';
diff --git a/src/components/organisms/forms/prism-theme-toggle/prism-theme-toggle.stories.tsx b/src/components/organisms/forms/prism-theme-toggle/prism-theme-toggle.stories.tsx
new file mode 100644
index 0000000..3c8eaba
--- /dev/null
+++ b/src/components/organisms/forms/prism-theme-toggle/prism-theme-toggle.stories.tsx
@@ -0,0 +1,20 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import { PrismThemeToggle } from './prism-theme-toggle';
+
+/**
+ * PrismThemeToggle - Storybook Meta
+ */
+export default {
+ title: 'Organisms/Forms/Toggle',
+ component: PrismThemeToggle,
+ argTypes: {},
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => (
+
+);
+
+/**
+ * Toggle Stories - Prism theme
+ */
+export const PrismTheme = Template.bind({});
diff --git a/src/components/organisms/forms/prism-theme-toggle/prism-theme-toggle.test.tsx b/src/components/organisms/forms/prism-theme-toggle/prism-theme-toggle.test.tsx
new file mode 100644
index 0000000..f29418e
--- /dev/null
+++ b/src/components/organisms/forms/prism-theme-toggle/prism-theme-toggle.test.tsx
@@ -0,0 +1,13 @@
+import { render, screen } from '../../../../../tests/utils';
+import { PrismThemeToggle } from './prism-theme-toggle';
+
+describe('PrismThemeToggle', () => {
+ it('renders a toggle component', () => {
+ render();
+ expect(
+ screen.getByRole('radiogroup', {
+ name: /Code blocks:/i,
+ })
+ ).toBeInTheDocument();
+ });
+});
diff --git a/src/components/organisms/forms/prism-theme-toggle/prism-theme-toggle.tsx b/src/components/organisms/forms/prism-theme-toggle/prism-theme-toggle.tsx
new file mode 100644
index 0000000..0e1649b
--- /dev/null
+++ b/src/components/organisms/forms/prism-theme-toggle/prism-theme-toggle.tsx
@@ -0,0 +1,85 @@
+import { ChangeEvent, FC } from 'react';
+import { useIntl } from 'react-intl';
+import { type PrismTheme, usePrismTheme } from '../../../../utils/providers';
+import { Legend, Moon, Sun } from '../../../atoms';
+import { Switch, SwitchOption, SwitchProps } from '../../../molecules';
+
+export type PrismThemeToggleProps = Omit<
+ SwitchProps,
+ 'isInline' | 'items' | 'name' | 'onSwitch' | 'value'
+>;
+
+/**
+ * PrismThemeToggle component
+ *
+ * Render a Toggle component to set code blocks theme.
+ */
+export const PrismThemeToggle: FC = (props) => {
+ const intl = useIntl();
+ const { theme, setTheme, resolvedTheme } = usePrismTheme();
+
+ /**
+ * Check if the resolved or chosen theme is dark theme.
+ *
+ * @returns {boolean} True if it is dark theme.
+ */
+ const isDarkTheme = (prismTheme?: PrismTheme): boolean => {
+ if (prismTheme === 'system') return resolvedTheme === 'dark';
+ return prismTheme === 'dark';
+ };
+
+ const updateTheme = (e: ChangeEvent) => {
+ setTheme(e.target.value === 'light' ? 'light' : 'dark');
+ };
+
+ const themeLabel = intl.formatMessage({
+ defaultMessage: 'Code blocks:',
+ description: 'PrismThemeToggle: theme label',
+ id: 'ftXN+0',
+ });
+ const lightThemeLabel = intl.formatMessage({
+ defaultMessage: 'Light theme',
+ description: 'PrismThemeToggle: light theme label',
+ id: 'tsWh8x',
+ });
+ const darkThemeLabel = intl.formatMessage({
+ defaultMessage: 'Dark theme',
+ description: 'PrismThemeToggle: dark theme label',
+ id: 'og/zWL',
+ });
+
+ const options: [SwitchOption, SwitchOption] = [
+ {
+ id: 'code-blocks-light',
+ label: (
+ <>
+ {lightThemeLabel}
+
+ >
+ ),
+ value: 'light',
+ },
+ {
+ id: 'code-blocks-dark',
+ label: (
+ <>
+ {darkThemeLabel}
+
+ >
+ ),
+ value: 'dark',
+ },
+ ];
+
+ return (
+ {themeLabel}}
+ name="code-blocks"
+ onSwitch={updateTheme}
+ value={isDarkTheme(theme) ? 'dark' : 'light'}
+ />
+ );
+};
diff --git a/src/components/organisms/forms/search-form.module.scss b/src/components/organisms/forms/search-form.module.scss
deleted file mode 100644
index 773a79f..0000000
--- a/src/components/organisms/forms/search-form.module.scss
+++ /dev/null
@@ -1,58 +0,0 @@
-@use "../../../styles/abstracts/functions" as fun;
-@use "../../../styles/abstracts/mixins" as mix;
-
-.wrapper {
- display: flex;
- align-items: center;
- position: relative;
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- max-width: 35ch;
- }
- }
-}
-
-.btn {
- position: absolute;
- right: 0;
-
- &__icon {
- transform: scale(0.85);
- transition: all 0.3s ease-in-out 0s;
- }
-
- &:focus {
- outline: var(--color-primary-light) solid fun.convert-px(3);
- }
-
- &:active {
- outline: none;
- }
-
- &:hover &,
- &:focus & {
- &__icon {
- transform: scale(0.85) rotate(20deg) translateY(#{fun.convert-px(3)});
- }
- }
-
- &:active & {
- &__icon {
- transform: scale(0.7);
- }
- }
-}
-
-.field {
- width: 100%;
- padding-right: var(--spacing-lg);
-
- &:hover ~ .btn {
- transform: translate(fun.convert-px(-3), fun.convert-px(-3));
- }
-
- &:focus ~ .btn {
- transform: translate(fun.convert-px(3), fun.convert-px(3));
- }
-}
diff --git a/src/components/organisms/forms/search-form.stories.tsx b/src/components/organisms/forms/search-form.stories.tsx
deleted file mode 100644
index 4a0a15c..0000000
--- a/src/components/organisms/forms/search-form.stories.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { ComponentMeta, ComponentStory } from '@storybook/react';
-import { SearchForm } from './search-form';
-
-/**
- * SearchForm - Storybook Meta
- */
-export default {
- title: 'Organisms/Forms',
- component: SearchForm,
- args: {
- hideLabel: false,
- searchPage: '#',
- },
- argTypes: {
- className: {
- control: {
- type: 'text',
- },
- description: 'Set additional classnames to the form wrapper.',
- table: {
- category: 'Styles',
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- hideLabel: {
- control: {
- type: 'boolean',
- },
- description: 'Determine if the input label should be visually hidden.',
- table: {
- category: 'Options',
- defaultValue: { summary: false },
- },
- type: {
- name: 'boolean',
- required: false,
- },
- },
- searchPage: {
- control: {
- type: 'text',
- },
- description: 'The search results page url.',
- type: {
- name: 'string',
- required: true,
- },
- },
- },
-} as ComponentMeta;
-
-const Template: ComponentStory = (args) => (
-
-);
-
-/**
- * Forms Stories - Search
- */
-export const Search = Template.bind({});
-Search.args = {
- hideLabel: true,
-};
diff --git a/src/components/organisms/forms/search-form.test.tsx b/src/components/organisms/forms/search-form.test.tsx
deleted file mode 100644
index bc9b7a0..0000000
--- a/src/components/organisms/forms/search-form.test.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { render, screen } from '../../../../tests/utils';
-import { SearchForm } from './search-form';
-
-describe('SearchForm', () => {
- it('renders a search input', () => {
- render();
- expect(
- screen.getByRole('searchbox', { name: 'Search for:' })
- ).toBeInTheDocument();
- });
-
- it('renders a submit button', () => {
- render();
- expect(screen.getByRole('button', { name: 'Search' })).toBeInTheDocument();
- });
-});
diff --git a/src/components/organisms/forms/search-form.tsx b/src/components/organisms/forms/search-form.tsx
deleted file mode 100644
index f80d295..0000000
--- a/src/components/organisms/forms/search-form.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { useRouter } from 'next/router';
-import { forwardRef, ForwardRefRenderFunction, useId, useState } from 'react';
-import { useIntl } from 'react-intl';
-import { Button, Form, MagnifyingGlass } from '../../atoms';
-import { LabelledField, type LabelledFieldProps } from '../../molecules';
-import styles from './search-form.module.scss';
-
-export type SearchFormProps = Pick & {
- /**
- * The search page url.
- */
- searchPage: string;
-};
-
-const SearchFormWithRef: ForwardRefRenderFunction<
- HTMLInputElement,
- SearchFormProps
-> = ({ hideLabel, searchPage }, ref) => {
- const intl = useIntl();
- const fieldLabel = intl.formatMessage({
- defaultMessage: 'Search for:',
- description: 'SearchForm: field accessible label',
- id: 'X8oujO',
- });
- const buttonLabel = intl.formatMessage({
- defaultMessage: 'Search',
- description: 'SearchForm: button accessible name',
- id: 'WMqQrv',
- });
-
- const router = useRouter();
- const [value, setValue] = useState('');
-
- const submitHandler = () => {
- router.push({ pathname: searchPage, query: { s: value } });
- setValue('');
- };
-
- const id = useId();
-
- return (
-
- );
-};
-
-/**
- * SearchForm component
- *
- * Render a search form.
- */
-export const SearchForm = forwardRef(SearchFormWithRef);
diff --git a/src/components/organisms/forms/search-form/index.ts b/src/components/organisms/forms/search-form/index.ts
new file mode 100644
index 0000000..e7d3f3d
--- /dev/null
+++ b/src/components/organisms/forms/search-form/index.ts
@@ -0,0 +1 @@
+export * from './search-form';
diff --git a/src/components/organisms/forms/search-form/search-form.module.scss b/src/components/organisms/forms/search-form/search-form.module.scss
new file mode 100644
index 0000000..e485380
--- /dev/null
+++ b/src/components/organisms/forms/search-form/search-form.module.scss
@@ -0,0 +1,67 @@
+@use "../../../../styles/abstracts/functions" as fun;
+@use "../../../../styles/abstracts/mixins" as mix;
+
+.wrapper {
+ display: flex;
+ align-items: center;
+ position: relative;
+
+ @include mix.media("screen") {
+ @include mix.dimensions("sm") {
+ max-width: 35ch;
+ }
+ }
+}
+
+.btn {
+ align-self: stretch;
+ background: var(--color-bg-tertiary);
+ border: fun.convert-px(2) solid var(--color-border);
+ border-left: none;
+ box-shadow: fun.convert-px(3) fun.convert-px(3) 0 0 var(--color-shadow);
+ transition: all 0.25s linear 0s;
+
+ &__icon {
+ transform: scale(0.85);
+ transition: all 0.3s ease-in-out 0s;
+ }
+
+ &:focus {
+ outline: var(--color-primary-light) solid fun.convert-px(3);
+ }
+
+ &:active {
+ outline: none;
+ }
+
+ &:hover &,
+ &:focus & {
+ &__icon {
+ transform: scale(0.85) rotate(20deg) translateY(#{fun.convert-px(3)});
+ }
+ }
+
+ &:active & {
+ &__icon {
+ transform: scale(0.7);
+ }
+ }
+}
+
+.field {
+ &:focus-within ~ .btn {
+ background: var(--color-bg);
+ border-color: var(--color-primary);
+ box-shadow: none;
+ transform: translate(fun.convert-px(3), fun.convert-px(3));
+ transition:
+ all 0.2s ease-in-out 0s,
+ transform 0.3s ease-out 0s;
+ }
+
+ &:hover:not(:focus-within) ~ .btn {
+ box-shadow: fun.convert-px(5) fun.convert-px(5) 0 fun.convert-px(1)
+ var(--color-shadow);
+ transform: translate(fun.convert-px(-3), fun.convert-px(-3));
+ }
+}
diff --git a/src/components/organisms/forms/search-form/search-form.stories.tsx b/src/components/organisms/forms/search-form/search-form.stories.tsx
new file mode 100644
index 0000000..c5fbeb9
--- /dev/null
+++ b/src/components/organisms/forms/search-form/search-form.stories.tsx
@@ -0,0 +1,65 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import { SearchForm } from './search-form';
+
+/**
+ * SearchForm - Storybook Meta
+ */
+export default {
+ title: 'Organisms/Forms',
+ component: SearchForm,
+ args: {
+ isLabelHidden: false,
+ searchPage: '#',
+ },
+ argTypes: {
+ className: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the form wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ isLabelHidden: {
+ control: {
+ type: 'boolean',
+ },
+ description: 'Determine if the input label should be visually hidden.',
+ table: {
+ category: 'Options',
+ defaultValue: { summary: false },
+ },
+ type: {
+ name: 'boolean',
+ required: false,
+ },
+ },
+ searchPage: {
+ control: {
+ type: 'text',
+ },
+ description: 'The search results page url.',
+ type: {
+ name: 'string',
+ required: true,
+ },
+ },
+ },
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => (
+
+);
+
+/**
+ * Forms Stories - Search
+ */
+export const Search = Template.bind({});
+Search.args = {
+ isLabelHidden: true,
+};
diff --git a/src/components/organisms/forms/search-form/search-form.test.tsx b/src/components/organisms/forms/search-form/search-form.test.tsx
new file mode 100644
index 0000000..b53b9cf
--- /dev/null
+++ b/src/components/organisms/forms/search-form/search-form.test.tsx
@@ -0,0 +1,16 @@
+import { render, screen } from '../../../../../tests/utils';
+import { SearchForm } from './search-form';
+
+describe('SearchForm', () => {
+ it('renders a search input', () => {
+ render();
+ expect(
+ screen.getByRole('searchbox', { name: 'Search for:' })
+ ).toBeInTheDocument();
+ });
+
+ it('renders a submit button', () => {
+ render();
+ expect(screen.getByRole('button', { name: 'Search' })).toBeInTheDocument();
+ });
+});
diff --git a/src/components/organisms/forms/search-form/search-form.tsx b/src/components/organisms/forms/search-form/search-form.tsx
new file mode 100644
index 0000000..826e6c8
--- /dev/null
+++ b/src/components/organisms/forms/search-form/search-form.tsx
@@ -0,0 +1,98 @@
+import { useRouter } from 'next/router';
+import {
+ ChangeEvent,
+ FormEvent,
+ forwardRef,
+ ForwardRefRenderFunction,
+ useId,
+ useState,
+} from 'react';
+import { useIntl } from 'react-intl';
+import { Button, Form, Input, Label, MagnifyingGlass } from '../../../atoms';
+import { LabelledField } from '../../../molecules';
+import styles from './search-form.module.scss';
+
+export type SearchFormProps = {
+ /**
+ * Should the label be visually hidden?
+ *
+ * @default false
+ */
+ isLabelHidden?: boolean;
+ /**
+ * The search page url.
+ */
+ searchPage: string;
+};
+
+const SearchFormWithRef: ForwardRefRenderFunction<
+ HTMLInputElement,
+ SearchFormProps
+> = ({ isLabelHidden = false, searchPage }, ref) => {
+ const intl = useIntl();
+ const fieldLabel = intl.formatMessage({
+ defaultMessage: 'Search for:',
+ description: 'SearchForm: field accessible label',
+ id: 'X8oujO',
+ });
+ const buttonLabel = intl.formatMessage({
+ defaultMessage: 'Search',
+ description: 'SearchForm: button accessible name',
+ id: 'WMqQrv',
+ });
+
+ const router = useRouter();
+ const [value, setValue] = useState('');
+
+ const submitHandler = (e: FormEvent) => {
+ e.preventDefault();
+ router.push({ pathname: searchPage, query: { s: value } });
+ setValue('');
+ };
+
+ const updateForm = (e: ChangeEvent) => {
+ setValue(e.target.value);
+ };
+
+ const id = useId();
+
+ return (
+
+ );
+};
+
+/**
+ * SearchForm component
+ *
+ * Render a search form.
+ */
+export const SearchForm = forwardRef(SearchFormWithRef);
diff --git a/src/components/organisms/forms/theme-toggle/index.ts b/src/components/organisms/forms/theme-toggle/index.ts
new file mode 100644
index 0000000..0dbf668
--- /dev/null
+++ b/src/components/organisms/forms/theme-toggle/index.ts
@@ -0,0 +1 @@
+export * from './theme-toggle';
diff --git a/src/components/organisms/forms/theme-toggle/theme-toggle.stories.tsx b/src/components/organisms/forms/theme-toggle/theme-toggle.stories.tsx
new file mode 100644
index 0000000..ac228b4
--- /dev/null
+++ b/src/components/organisms/forms/theme-toggle/theme-toggle.stories.tsx
@@ -0,0 +1,20 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import { ThemeToggle } from './theme-toggle';
+
+/**
+ * ThemeToggle - Storybook Meta
+ */
+export default {
+ title: 'Organisms/Forms/Toggle',
+ component: ThemeToggle,
+ argTypes: {},
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => (
+
+);
+
+/**
+ * Toggle Stories - Theme
+ */
+export const Theme = Template.bind({});
diff --git a/src/components/organisms/forms/theme-toggle/theme-toggle.test.tsx b/src/components/organisms/forms/theme-toggle/theme-toggle.test.tsx
new file mode 100644
index 0000000..9f37a26
--- /dev/null
+++ b/src/components/organisms/forms/theme-toggle/theme-toggle.test.tsx
@@ -0,0 +1,13 @@
+import { render, screen } from '../../../../../tests/utils';
+import { ThemeToggle } from './theme-toggle';
+
+describe('ThemeToggle', () => {
+ it('renders a toggle component', () => {
+ render();
+ expect(
+ screen.getByRole('radiogroup', {
+ name: /Theme:/i,
+ })
+ ).toBeInTheDocument();
+ });
+});
diff --git a/src/components/organisms/forms/theme-toggle/theme-toggle.tsx b/src/components/organisms/forms/theme-toggle/theme-toggle.tsx
new file mode 100644
index 0000000..da303d3
--- /dev/null
+++ b/src/components/organisms/forms/theme-toggle/theme-toggle.tsx
@@ -0,0 +1,76 @@
+import { useTheme } from 'next-themes';
+import { ChangeEvent, FC } from 'react';
+import { useIntl } from 'react-intl';
+import { Legend, Moon, Sun } from '../../../atoms';
+import { Switch, SwitchOption, SwitchProps } from '../../../molecules';
+
+export type ThemeToggleProps = Omit<
+ SwitchProps,
+ 'isInline' | 'items' | 'name' | 'onSwitch' | 'value'
+>;
+
+/**
+ * ThemeToggle component
+ *
+ * Render a Toggle component to set theme.
+ */
+export const ThemeToggle: FC = (props) => {
+ const intl = useIntl();
+ const { resolvedTheme, setTheme } = useTheme();
+ const isDarkTheme = resolvedTheme === 'dark';
+
+ const updateTheme = (e: ChangeEvent) => {
+ setTheme(e.target.value === 'light' ? 'light' : 'dark');
+ };
+
+ const themeLabel = intl.formatMessage({
+ defaultMessage: 'Theme:',
+ description: 'ThemeToggle: theme label',
+ id: 'suXOBu',
+ });
+ const lightThemeLabel = intl.formatMessage({
+ defaultMessage: 'Light theme',
+ description: 'ThemeToggle: light theme label',
+ id: 'Ygea7s',
+ });
+ const darkThemeLabel = intl.formatMessage({
+ defaultMessage: 'Dark theme',
+ description: 'ThemeToggle: dark theme label',
+ id: '2QwvtS',
+ });
+
+ const options: [SwitchOption, SwitchOption] = [
+ {
+ id: 'theme-light',
+ label: (
+ <>
+ {lightThemeLabel}
+
+ >
+ ),
+ value: 'light',
+ },
+ {
+ id: 'theme-dark',
+ label: (
+ <>
+ {darkThemeLabel}
+
+ >
+ ),
+ value: 'dark',
+ },
+ ];
+
+ return (
+ {themeLabel}}
+ name="theme"
+ onSwitch={updateTheme}
+ value={isDarkTheme ? 'dark' : 'light'}
+ />
+ );
+};
diff --git a/src/components/organisms/layout/no-results.tsx b/src/components/organisms/layout/no-results.tsx
index 1e7afe1..b2acf12 100644
--- a/src/components/organisms/layout/no-results.tsx
+++ b/src/components/organisms/layout/no-results.tsx
@@ -28,7 +28,7 @@ export const NoResults: FC = ({ searchPage }) => {
id: 'DVBwfu',
})}
-
+
>
);
};
diff --git a/src/components/organisms/modals/search-modal.tsx b/src/components/organisms/modals/search-modal.tsx
index 7ba770f..7d772df 100644
--- a/src/components/organisms/modals/search-modal.tsx
+++ b/src/components/organisms/modals/search-modal.tsx
@@ -1,6 +1,6 @@
import { forwardRef, ForwardRefRenderFunction } from 'react';
import { useIntl } from 'react-intl';
-import { Modal, type ModalProps } from '../../molecules';
+import { Heading, Modal, type ModalProps } from '../../atoms';
import { SearchForm, type SearchFormProps } from '../forms';
import styles from './search-modal.module.scss';
@@ -23,8 +23,15 @@ const SearchModalWithRef: ForwardRefRenderFunction<
});
return (
-
-
+
+ {modalTitle}
+
+ }
+ >
+
);
};
diff --git a/src/components/organisms/modals/settings-modal.module.scss b/src/components/organisms/modals/settings-modal.module.scss
index e9b3b85..95626ab 100644
--- a/src/components/organisms/modals/settings-modal.module.scss
+++ b/src/components/organisms/modals/settings-modal.module.scss
@@ -20,27 +20,17 @@
column-gap: var(--spacing-lg);
}
-.items {
+.item {
+ width: 100%;
margin: 0 0 var(--spacing-2xs);
- max-width: unset;
-}
-.fieldset__body {
- margin-left: auto;
+ > *:last-child {
+ margin-left: auto;
+ }
}
-.tooltip {
- font-size: var(--font-size-sm);
- z-index: 2;
+.icon {
+ --icon-size: #{fun.convert-px(30)};
- @media screen and (max-height: #{var.get-breakpoint("2xs")}) {
- width: calc(100vw - var(--spacing-md));
- padding: var(--spacing-md) var(--spacing-2xs) var(--spacing-2xs)
- var(--spacing-2xs);
- right: 0;
- }
-
- @media screen and (min-width: #{var.get-breakpoint("sm")}) {
- width: 100%;
- }
+ margin-right: var(--spacing-2xs);
}
diff --git a/src/components/organisms/modals/settings-modal.stories.tsx b/src/components/organisms/modals/settings-modal.stories.tsx
index 093922d..7af0d60 100644
--- a/src/components/organisms/modals/settings-modal.stories.tsx
+++ b/src/components/organisms/modals/settings-modal.stories.tsx
@@ -1,6 +1,6 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';
-import { storageKey as ackeeStorageKey } from '../../molecules/forms/ackee-toggle.fixture';
-import { storageKey as motionStorageKey } from '../../molecules/forms/motion-toggle.fixture';
+import { storageKey as ackeeStorageKey } from '../../organisms/forms/ackee-toggle/ackee-toggle.fixture';
+import { storageKey as motionStorageKey } from '../../organisms/forms/motion-toggle/motion-toggle.fixture';
import { SettingsModal } from './settings-modal';
/**
diff --git a/src/components/organisms/modals/settings-modal.test.tsx b/src/components/organisms/modals/settings-modal.test.tsx
index ec14719..3cd64f6 100644
--- a/src/components/organisms/modals/settings-modal.test.tsx
+++ b/src/components/organisms/modals/settings-modal.test.tsx
@@ -1,6 +1,6 @@
import { render, screen } from '../../../../tests/utils';
-import { storageKey as ackeeStorageKey } from '../../molecules/forms/ackee-toggle.fixture';
-import { storageKey as motionStorageKey } from '../../molecules/forms/motion-toggle.fixture';
+import { storageKey as ackeeStorageKey } from '../../organisms/forms/ackee-toggle/ackee-toggle.fixture';
+import { storageKey as motionStorageKey } from '../../organisms/forms/motion-toggle/motion-toggle.fixture';
import { SettingsModal } from './settings-modal';
describe('SettingsModal', () => {
diff --git a/src/components/organisms/modals/settings-modal.tsx b/src/components/organisms/modals/settings-modal.tsx
index d4a3a49..bb3d886 100644
--- a/src/components/organisms/modals/settings-modal.tsx
+++ b/src/components/organisms/modals/settings-modal.tsx
@@ -1,29 +1,26 @@
import { FC } from 'react';
import { useIntl } from 'react-intl';
-import { Form } from '../../atoms';
+import { Cog, Form, Heading, Modal, type ModalProps } from '../../atoms';
import {
AckeeToggle,
type AckeeToggleProps,
- Modal,
- type ModalProps,
MotionToggle,
type MotionToggleProps,
PrismThemeToggle,
ThemeToggle,
-} from '../../molecules';
+} from '../../organisms';
import styles from './settings-modal.module.scss';
-export type SettingsModalProps = Pick &
- Pick & {
- /**
- * The local storage key for Ackee settings.
- */
- ackeeStorageKey: AckeeToggleProps['storageKey'];
- /**
- * The local storage key for Reduce motion settings.
- */
- motionStorageKey: MotionToggleProps['storageKey'];
- };
+export type SettingsModalProps = Pick & {
+ /**
+ * The local storage key for Ackee settings.
+ */
+ ackeeStorageKey: AckeeToggleProps['storageKey'];
+ /**
+ * The local storage key for Reduce motion settings.
+ */
+ motionStorageKey: MotionToggleProps['storageKey'];
+};
/**
* SettingsModal component
@@ -34,7 +31,6 @@ export const SettingsModal: FC = ({
className = '',
ackeeStorageKey,
motionStorageKey,
- tooltipClassName,
}) => {
const intl = useIntl();
const title = intl.formatMessage({
@@ -51,40 +47,30 @@ export const SettingsModal: FC = ({
return (
+
+ {title}
+
+ }
>
diff --git a/src/components/organisms/toolbar/main-nav.stories.tsx b/src/components/organisms/toolbar/main-nav.stories.tsx
index 1ef10b5..57485d3 100644
--- a/src/components/organisms/toolbar/main-nav.stories.tsx
+++ b/src/components/organisms/toolbar/main-nav.stories.tsx
@@ -57,7 +57,7 @@ export default {
} as ComponentMeta;
const Template: ComponentStory = ({
- isActive,
+ isActive = false,
setIsActive: _setIsActive,
...args
}) => {
diff --git a/src/components/organisms/toolbar/main-nav.tsx b/src/components/organisms/toolbar/main-nav.tsx
index cf49bd4..4182b4c 100644
--- a/src/components/organisms/toolbar/main-nav.tsx
+++ b/src/components/organisms/toolbar/main-nav.tsx
@@ -18,7 +18,7 @@ export type MainNavProps = {
/**
* The button state.
*/
- isActive: BooleanFieldProps['checked'];
+ isActive: BooleanFieldProps['isChecked'];
/**
* The main nav items.
*/
@@ -30,7 +30,7 @@ export type MainNavProps = {
};
const MainNavWithRef: ForwardRefRenderFunction = (
- { className = '', isActive, items, setIsActive },
+ { className = '', isActive = false, items, setIsActive },
ref
) => {
const intl = useIntl();
@@ -49,9 +49,9 @@ const MainNavWithRef: ForwardRefRenderFunction = (
return (
;
const Template: ComponentStory = ({
- isActive,
+ isActive = false,
setIsActive: _setIsActive,
...args
}) => {
diff --git a/src/components/organisms/toolbar/search.tsx b/src/components/organisms/toolbar/search.tsx
index 1b2643c..b20f0d5 100644
--- a/src/components/organisms/toolbar/search.tsx
+++ b/src/components/organisms/toolbar/search.tsx
@@ -19,7 +19,7 @@ export type SearchProps = {
/**
* The button state.
*/
- isActive: BooleanFieldProps['checked'];
+ isActive: BooleanFieldProps['isChecked'];
/**
* A callback function to execute search.
*/
@@ -31,7 +31,7 @@ export type SearchProps = {
};
const SearchWithRef: ForwardRefRenderFunction = (
- { className = '', isActive, searchPage, setIsActive },
+ { className = '', isActive = false, searchPage, setIsActive },
ref
) => {
const intl = useIntl();
@@ -57,9 +57,9 @@ const SearchWithRef: ForwardRefRenderFunction = (
return (
;
const Template: ComponentStory = ({
- isActive,
+ isActive = false,
setIsActive: _setIsActive,
...args
}) => {
diff --git a/src/components/organisms/toolbar/settings.tsx b/src/components/organisms/toolbar/settings.tsx
index 8a4d4a9..3f328a5 100644
--- a/src/components/organisms/toolbar/settings.tsx
+++ b/src/components/organisms/toolbar/settings.tsx
@@ -3,14 +3,13 @@ import { useIntl } from 'react-intl';
import { BooleanField, type BooleanFieldProps, Cog } from '../../atoms';
import { FlippingLabel } from '../../molecules';
import { SettingsModal, type SettingsModalProps } from '../modals';
-import settingsStyles from './settings.module.scss';
-import sharedStyles from './toolbar-items.module.scss';
+import styles from './toolbar-items.module.scss';
export type SettingsProps = SettingsModalProps & {
/**
* The button state.
*/
- isActive: BooleanFieldProps['checked'];
+ isActive: BooleanFieldProps['isChecked'];
/**
* A callback function to handle button state.
*/
@@ -24,10 +23,9 @@ const SettingsWithRef: ForwardRefRenderFunction<
{
ackeeStorageKey,
className = '',
- isActive,
+ isActive = false,
motionStorageKey,
setIsActive,
- tooltipClassName = '',
},
ref
) => {
@@ -45,11 +43,11 @@ const SettingsWithRef: ForwardRefRenderFunction<
});
return (
-
+
@@ -65,9 +63,8 @@ const SettingsWithRef: ForwardRefRenderFunction<
);
diff --git a/src/components/organisms/toolbar/toolbar.tsx b/src/components/organisms/toolbar/toolbar.tsx
index 218b4fb..94c9d95 100644
--- a/src/components/organisms/toolbar/toolbar.tsx
+++ b/src/components/organisms/toolbar/toolbar.tsx
@@ -68,7 +68,6 @@ export const Toolbar: FC
= ({
motionStorageKey={motionStorageKey}
ref={settingsRef}
setIsActive={() => setIsSettingsOpened(!isSettingsOpened)}
- tooltipClassName={styles.tooltip}
/>
);
--
cgit v1.2.3