summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-06-01 19:34:43 +0200
committerArmand Philippot <git@armandphilippot.com>2022-06-01 22:32:16 +0200
commit6be20422494e3806fba3d1c5ad5c3e98bd6e67e5 (patch)
tree7c679e54ba4bbadaf0a59bbde780f5742e3b875d /src
parent8320b1d39ea6402c32e907dbb35082efc6af9f5a (diff)
chore: replace the Ackee select by a toggle component
Diffstat (limited to 'src')
-rw-r--r--src/components/atoms/forms/fieldset.fixture.tsx2
-rw-r--r--src/components/atoms/forms/fieldset.module.scss28
-rw-r--r--src/components/atoms/forms/fieldset.test.tsx11
-rw-r--r--src/components/atoms/forms/fieldset.tsx59
-rw-r--r--src/components/molecules/forms/ackee-select.stories.tsx86
-rw-r--r--src/components/molecules/forms/ackee-select.test.tsx26
-rw-r--r--src/components/molecules/forms/ackee-select.tsx103
-rw-r--r--src/components/molecules/forms/ackee-toggle.fixture.tsx (renamed from src/components/molecules/forms/ackee-select.fixture.tsx)0
-rw-r--r--src/components/molecules/forms/ackee-toggle.module.scss (renamed from src/components/molecules/forms/ackee-select.module.scss)5
-rw-r--r--src/components/molecules/forms/ackee-toggle.stories.tsx112
-rw-r--r--src/components/molecules/forms/ackee-toggle.test.tsx15
-rw-r--r--src/components/molecules/forms/ackee-toggle.tsx143
-rw-r--r--src/components/molecules/forms/fieldset.fixture.tsx6
-rw-r--r--src/components/molecules/forms/fieldset.module.scss (renamed from src/components/molecules/forms/select-with-tooltip.module.scss)47
-rw-r--r--src/components/molecules/forms/fieldset.stories.tsx (renamed from src/components/atoms/forms/fieldset.stories.tsx)51
-rw-r--r--src/components/molecules/forms/fieldset.test.tsx22
-rw-r--r--src/components/molecules/forms/fieldset.tsx118
-rw-r--r--src/components/molecules/forms/motion-toggle.stories.tsx26
-rw-r--r--src/components/molecules/forms/motion-toggle.tsx2
-rw-r--r--src/components/molecules/forms/prism-theme-toggle.stories.tsx26
-rw-r--r--src/components/molecules/forms/prism-theme-toggle.tsx2
-rw-r--r--src/components/molecules/forms/radio-group.stories.tsx52
-rw-r--r--src/components/molecules/forms/radio-group.tsx6
-rw-r--r--src/components/molecules/forms/select-with-tooltip.stories.tsx223
-rw-r--r--src/components/molecules/forms/select-with-tooltip.test.tsx32
-rw-r--r--src/components/molecules/forms/select-with-tooltip.tsx78
-rw-r--r--src/components/molecules/forms/theme-toggle.stories.tsx26
-rw-r--r--src/components/molecules/forms/theme-toggle.tsx2
-rw-r--r--src/components/molecules/modals/tooltip.fixture.tsx4
-rw-r--r--src/components/molecules/modals/tooltip.stories.tsx22
-rw-r--r--src/components/molecules/modals/tooltip.test.tsx6
-rw-r--r--src/components/molecules/modals/tooltip.tsx11
-rw-r--r--src/components/organisms/forms/settings-form.module.scss54
-rw-r--r--src/components/organisms/forms/settings-form.stories.tsx2
-rw-r--r--src/components/organisms/forms/settings-form.test.tsx11
-rw-r--r--src/components/organisms/forms/settings-form.tsx23
-rw-r--r--src/components/organisms/modals/settings-modal.module.scss12
-rw-r--r--src/components/organisms/modals/settings-modal.tsx7
38 files changed, 716 insertions, 745 deletions
diff --git a/src/components/atoms/forms/fieldset.fixture.tsx b/src/components/atoms/forms/fieldset.fixture.tsx
deleted file mode 100644
index b533dfb..0000000
--- a/src/components/atoms/forms/fieldset.fixture.tsx
+++ /dev/null
@@ -1,2 +0,0 @@
-export const body = 'doloribus magni aut';
-export const legend = 'maiores autem est';
diff --git a/src/components/atoms/forms/fieldset.module.scss b/src/components/atoms/forms/fieldset.module.scss
deleted file mode 100644
index d1b5956..0000000
--- a/src/components/atoms/forms/fieldset.module.scss
+++ /dev/null
@@ -1,28 +0,0 @@
-.wrapper {
- max-width: 100%;
- padding: 0;
- border: none;
-
- &--inline {
- display: flex;
- flex-flow: row wrap;
- align-items: center;
-
- .legend {
- float: left;
- margin-right: var(--spacing-2xs);
- }
- }
-
- &--stacked {
- .legend {
- padding: 0 var(--spacing-xs) 0 0;
- }
- }
-}
-
-.legend {
- color: var(--color-primary-darker);
- font-size: var(--font-size-md);
- font-weight: 600;
-}
diff --git a/src/components/atoms/forms/fieldset.test.tsx b/src/components/atoms/forms/fieldset.test.tsx
deleted file mode 100644
index 1d1d246..0000000
--- a/src/components/atoms/forms/fieldset.test.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { render, screen } from '@test-utils';
-import Fieldset from './fieldset';
-import { body, legend } from './fieldset.fixture';
-
-describe('Fieldset', () => {
- it('renders a legend and a body', () => {
- render(<Fieldset legend={legend}>{body}</Fieldset>);
- expect(screen.findByRole('group', { name: legend })).toBeTruthy();
- expect(screen.findByText(body)).toBeTruthy();
- });
-});
diff --git a/src/components/atoms/forms/fieldset.tsx b/src/components/atoms/forms/fieldset.tsx
deleted file mode 100644
index 99d31e7..0000000
--- a/src/components/atoms/forms/fieldset.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { FC, ReactNode } from 'react';
-import styles from './fieldset.module.scss';
-
-export type FieldsetProps = {
- /**
- * The fieldset body.
- */
- children: ReactNode | ReactNode[];
- /**
- * Set additional classnames to the fieldset wrapper.
- */
- className?: string;
- /**
- * The fieldset legend.
- */
- legend: string;
- /**
- * Set additional classnames to the legend.
- */
- legendClassName?: string;
- /**
- * The legend position. Default: stacked.
- */
- legendPosition?: 'inline' | 'stacked';
- /**
- * An accessible role. Default: group.
- */
- role?: 'group' | 'radiogroup' | 'presentation' | 'none';
-};
-
-/**
- * Fieldset component
- *
- * Render a fieldset with a legend.
- */
-const Fieldset: FC<FieldsetProps> = ({
- children,
- className = '',
- legend,
- legendClassName = '',
- legendPosition = 'stacked',
- ...props
-}) => {
- const wrapperModifier = `wrapper--${legendPosition}`;
-
- return (
- <fieldset
- className={`${styles.wrapper} ${styles[wrapperModifier]} ${className}`}
- {...props}
- >
- <legend className={`${styles.legend} ${legendClassName}`}>
- {legend}
- </legend>
- {children}
- </fieldset>
- );
-};
-
-export default Fieldset;
diff --git a/src/components/molecules/forms/ackee-select.stories.tsx b/src/components/molecules/forms/ackee-select.stories.tsx
deleted file mode 100644
index f8d04f6..0000000
--- a/src/components/molecules/forms/ackee-select.stories.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import { ComponentMeta, ComponentStory } from '@storybook/react';
-import AckeeSelect from './ackee-select';
-import { storageKey } from './ackee-select.fixture';
-
-/**
- * AckeeSelect - Storybook Meta
- */
-export default {
- title: 'Molecules/Forms/Select',
- component: AckeeSelect,
- argTypes: {
- className: {
- control: {
- type: 'text',
- },
- description: 'Set additional classnames to the select wrapper.',
- table: {
- category: 'Styles',
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- initialValue: {
- control: {
- type: 'select',
- },
- description: 'Initial selected option.',
- options: ['full', 'partial'],
- type: {
- name: 'string',
- required: true,
- },
- },
- labelClassName: {
- control: {
- type: 'text',
- },
- description: 'Set additional classnames to the label wrapper.',
- table: {
- category: 'Styles',
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- storageKey: {
- control: {
- type: 'text',
- },
- description: 'Set Ackee settings local storage key.',
- type: {
- name: 'string',
- required: true,
- },
- },
- tooltipClassName: {
- control: {
- type: 'text',
- },
- description: 'Set additional classnames to the tooltip wrapper.',
- table: {
- category: 'Styles',
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- },
-} as ComponentMeta<typeof AckeeSelect>;
-
-const Template: ComponentStory<typeof AckeeSelect> = (args) => (
- <AckeeSelect {...args} />
-);
-
-/**
- * Select Stories - Ackee select
- */
-export const Ackee = Template.bind({});
-Ackee.args = {
- initialValue: 'full',
- storageKey,
-};
diff --git a/src/components/molecules/forms/ackee-select.test.tsx b/src/components/molecules/forms/ackee-select.test.tsx
deleted file mode 100644
index d255b00..0000000
--- a/src/components/molecules/forms/ackee-select.test.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import user from '@testing-library/user-event';
-import { act, render, screen } from '@test-utils';
-import AckeeSelect from './ackee-select';
-import { storageKey } from './ackee-select.fixture';
-
-describe('Select', () => {
- it('should correctly set default option', () => {
- render(<AckeeSelect storageKey={storageKey} initialValue="full" />);
- expect(screen.getByRole('combobox')).toHaveValue('full');
- expect(screen.queryByRole('combobox')).not.toHaveValue('partial');
- });
-
- it('should correctly change value when user choose another option', async () => {
- render(<AckeeSelect storageKey={storageKey} initialValue="full" />);
-
- await act(async () => {
- await user.selectOptions(
- screen.getByRole('combobox'),
- screen.getByRole('option', { name: 'Partial' })
- );
- });
-
- expect(screen.getByRole('combobox')).toHaveValue('partial');
- expect(screen.queryByRole('combobox')).not.toHaveValue('full');
- });
-});
diff --git a/src/components/molecules/forms/ackee-select.tsx b/src/components/molecules/forms/ackee-select.tsx
deleted file mode 100644
index f00ca74..0000000
--- a/src/components/molecules/forms/ackee-select.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import { type SelectOptions } from '@components/atoms/forms/select';
-import useLocalStorage from '@utils/hooks/use-local-storage';
-import useUpdateAckeeOptions, {
- type AckeeOptions,
-} from '@utils/hooks/use-update-ackee-options';
-import { Dispatch, FC, SetStateAction } from 'react';
-import { useIntl } from 'react-intl';
-import SelectWithTooltip, {
- type SelectWithTooltipProps,
-} from './select-with-tooltip';
-
-export type AckeeSelectProps = Pick<
- SelectWithTooltipProps,
- 'className' | 'labelClassName' | 'tooltipClassName'
-> & {
- /**
- * A default value for Ackee settings.
- */
- initialValue: AckeeOptions;
- /**
- * The local storage key to save preference.
- */
- storageKey: string;
-};
-
-/**
- * AckeeSelect component
- *
- * Render a select to set Ackee settings.
- */
-const AckeeSelect: FC<AckeeSelectProps> = ({
- initialValue,
- storageKey,
- ...props
-}) => {
- const intl = useIntl();
- const { value, setValue } = useLocalStorage<AckeeOptions>(
- storageKey,
- initialValue
- );
- useUpdateAckeeOptions(value);
-
- const ackeeLabel = intl.formatMessage({
- defaultMessage: 'Tracking:',
- description: 'AckeeSelect: select label',
- id: '2pmylc',
- });
- const tooltipTitle = intl.formatMessage({
- defaultMessage: 'Ackee tracking (analytics)',
- description: 'AckeeSelect: tooltip title',
- id: 'F1EQX3',
- });
- const tooltipContent = [
- intl.formatMessage({
- defaultMessage: 'Partial includes only page url, views and duration.',
- description: 'AckeeSelect: tooltip message',
- id: 'skb4W5',
- }),
- intl.formatMessage({
- defaultMessage:
- 'Full includes all information from partial as well as information about referrer, operating system, device, browser, screen size and language.',
- description: 'AckeeSelect: tooltip message',
- id: 'Ogccx6',
- }),
- ];
- const options: SelectOptions[] = [
- {
- id: 'partial',
- name: intl.formatMessage({
- defaultMessage: 'Partial',
- description: 'AckeeSelect: partial option name',
- id: 'e/8Kyj',
- }),
- value: 'partial',
- },
- {
- id: 'full',
- name: intl.formatMessage({
- defaultMessage: 'Full',
- description: 'AckeeSelect: full option name',
- id: 'PzRpPw',
- }),
- value: 'full',
- },
- ];
-
- return (
- <SelectWithTooltip
- id="ackee-settings"
- name="ackee-settings"
- label={ackeeLabel}
- labelSize="medium"
- options={options}
- title={tooltipTitle}
- content={tooltipContent}
- value={value}
- setValue={setValue as Dispatch<SetStateAction<string>>}
- {...props}
- />
- );
-};
-
-export default AckeeSelect;
diff --git a/src/components/molecules/forms/ackee-select.fixture.tsx b/src/components/molecules/forms/ackee-toggle.fixture.tsx
index 04602f2..04602f2 100644
--- a/src/components/molecules/forms/ackee-select.fixture.tsx
+++ b/src/components/molecules/forms/ackee-toggle.fixture.tsx
diff --git a/src/components/molecules/forms/ackee-select.module.scss b/src/components/molecules/forms/ackee-toggle.module.scss
index 87cd9ee..f238bda 100644
--- a/src/components/molecules/forms/ackee-select.module.scss
+++ b/src/components/molecules/forms/ackee-toggle.module.scss
@@ -4,8 +4,3 @@
align-items: center;
position: relative;
}
-
-.tooltip {
- position: absolute;
- bottom: -100%;
-}
diff --git a/src/components/molecules/forms/ackee-toggle.stories.tsx b/src/components/molecules/forms/ackee-toggle.stories.tsx
new file mode 100644
index 0000000..bbc6fb4
--- /dev/null
+++ b/src/components/molecules/forms/ackee-toggle.stories.tsx
@@ -0,0 +1,112 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import AckeeToggleComponent from './ackee-toggle';
+import { storageKey } from './ackee-toggle.fixture';
+
+/**
+ * AckeeToggle - Storybook Meta
+ */
+export default {
+ title: 'Molecules/Forms/Toggle',
+ component: AckeeToggleComponent,
+ argTypes: {
+ bodyClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the fieldset body wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ className: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the toggle wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ defaultValue: {
+ control: {
+ type: 'select',
+ },
+ description: 'Set the default value.',
+ options: ['full', 'partial'],
+ type: {
+ name: 'string',
+ required: true,
+ },
+ },
+ groupClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the radio group wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ legendClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the legend.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ storageKey: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set local storage key.',
+ type: {
+ name: 'string',
+ required: true,
+ },
+ },
+ tooltipClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the tooltip wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ },
+} as ComponentMeta<typeof AckeeToggleComponent>;
+
+const Template: ComponentStory<typeof AckeeToggleComponent> = (args) => (
+ <AckeeToggleComponent {...args} />
+);
+
+/**
+ * Toggle Stories - Ackee
+ */
+export const Ackee = Template.bind({});
+Ackee.args = {
+ defaultValue: 'full',
+ storageKey,
+};
diff --git a/src/components/molecules/forms/ackee-toggle.test.tsx b/src/components/molecules/forms/ackee-toggle.test.tsx
new file mode 100644
index 0000000..8a57ce7
--- /dev/null
+++ b/src/components/molecules/forms/ackee-toggle.test.tsx
@@ -0,0 +1,15 @@
+import { render, screen } from '@test-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(<AckeeToggle storageKey={storageKey} defaultValue="full" />);
+ expect(
+ screen.getByRole('radiogroup', {
+ name: /Tracking:/i,
+ })
+ ).toBeInTheDocument();
+ });
+});
diff --git a/src/components/molecules/forms/ackee-toggle.tsx b/src/components/molecules/forms/ackee-toggle.tsx
new file mode 100644
index 0000000..a666731
--- /dev/null
+++ b/src/components/molecules/forms/ackee-toggle.tsx
@@ -0,0 +1,143 @@
+import useLocalStorage from '@utils/hooks/use-local-storage';
+import useUpdateAckeeOptions, {
+ type AckeeOptions,
+} from '@utils/hooks/use-update-ackee-options';
+import { FC } from 'react';
+import { useIntl } from 'react-intl';
+import RadioGroup, {
+ type RadioGroupCallback,
+ type RadioGroupCallbackProps,
+ type RadioGroupOption,
+ type RadioGroupProps,
+} from './radio-group';
+import Tooltip, { type TooltipProps } from '../modals/tooltip';
+
+export type AckeeToggleProps = Pick<
+ RadioGroupProps,
+ 'bodyClassName' | 'groupClassName' | 'legendClassName'
+> & {
+ /**
+ * 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;
+ /**
+ * Set additional classnames to the tooltip wrapper.
+ */
+ tooltipClassName?: TooltipProps['className'];
+};
+
+/**
+ * AckeeToggle component
+ *
+ * Render a Toggle component to set reduce motion.
+ */
+const AckeeToggle: FC<AckeeToggleProps> = ({
+ defaultValue,
+ storageKey,
+ tooltipClassName,
+ ...props
+}) => {
+ const intl = useIntl();
+ const { value, setValue } = useLocalStorage<AckeeOptions>(
+ storageKey,
+ defaultValue
+ );
+ useUpdateAckeeOptions(value);
+
+ const ackeeLabel = intl.formatMessage({
+ defaultMessage: 'Tracking:',
+ description: 'AckeeToggle: select label',
+ id: '0gVlI3',
+ });
+ const tooltipTitle = intl.formatMessage({
+ defaultMessage: 'Ackee tracking (analytics)',
+ description: 'AckeeToggle: tooltip title',
+ id: 'nGss/j',
+ });
+ const tooltipContent = [
+ intl.formatMessage({
+ defaultMessage: 'Partial includes only page url, views and duration.',
+ description: 'AckeeToggle: tooltip message',
+ id: 'ZB/Aw2',
+ }),
+ 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 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 options: RadioGroupOption[] = [
+ {
+ id: 'ackee-full',
+ label: fullLabel,
+ name: 'ackee',
+ value: 'full',
+ },
+ {
+ id: 'ackee-partial',
+ label: partialLabel,
+ name: 'ackee',
+ value: 'partial',
+ },
+ ];
+
+ /**
+ * Handle change events.
+ *
+ * @param {RadioGroupCallbackProps} props - An object with choices.
+ */
+ const handleChange: RadioGroupCallback = ({
+ choices,
+ updateChoice,
+ }: RadioGroupCallbackProps) => {
+ let newChoice: AckeeOptions = choices.new as AckeeOptions;
+
+ if (choices.new === choices.prev) {
+ newChoice = choices.new === 'full' ? 'partial' : 'full';
+ updateChoice(newChoice);
+ }
+
+ setValue(newChoice);
+ };
+
+ return (
+ <RadioGroup
+ initialChoice={value}
+ kind="toggle"
+ legend={ackeeLabel}
+ onChange={handleChange}
+ options={options}
+ Tooltip={
+ <Tooltip
+ title={tooltipTitle}
+ content={tooltipContent}
+ icon="?"
+ className={tooltipClassName}
+ />
+ }
+ {...props}
+ />
+ );
+};
+
+export default AckeeToggle;
diff --git a/src/components/molecules/forms/fieldset.fixture.tsx b/src/components/molecules/forms/fieldset.fixture.tsx
new file mode 100644
index 0000000..b94f340
--- /dev/null
+++ b/src/components/molecules/forms/fieldset.fixture.tsx
@@ -0,0 +1,6 @@
+import { TooltipProps } from '../modals/tooltip';
+import { Help } from '../modals/tooltip.stories';
+
+export const body = 'doloribus magni aut';
+export const legend = 'maiores autem est';
+export const Tooltip = <Help {...(Help.args as TooltipProps)} />;
diff --git a/src/components/molecules/forms/select-with-tooltip.module.scss b/src/components/molecules/forms/fieldset.module.scss
index bfadece..3102bf7 100644
--- a/src/components/molecules/forms/select-with-tooltip.module.scss
+++ b/src/components/molecules/forms/fieldset.module.scss
@@ -1,23 +1,16 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.wrapper {
- display: flex;
- flex-flow: row wrap;
- align-items: center;
- position: relative;
-}
-
-.select {
- width: auto;
-
- @include mix.pointer("fine") {
- padding: fun.convert-px(3) var(--spacing-xs);
+.legend {
+ float: left;
+ color: var(--color-primary-darker);
+ font-size: var(--font-size-md);
+ font-weight: 600;
+
+ &#{&}--has-tooltip {
+ padding: 0 var(--spacing-xs) 0 0;
}
}
.btn {
- margin-left: var(--spacing-xs);
+ margin: 0 var(--spacing-2xs) var(--spacing-2xs) 0;
&--activated {
background: var(--color-primary);
@@ -29,12 +22,12 @@
}
.tooltip {
- position: absolute;
top: calc(100% + var(--spacing-xs));
transform-origin: top;
transition: all 0.75s ease-in-out 0s;
&--hidden {
+ flex: 0 0 0;
opacity: 0;
visibility: hidden;
transform: scale(0);
@@ -46,3 +39,23 @@
transform: scale(1);
}
}
+
+.wrapper {
+ display: flex;
+ flex-flow: row wrap;
+ align-items: center;
+ max-width: 100%;
+ padding: 0;
+ position: relative;
+ border: none;
+
+ &--stacked {
+ .body {
+ flex: 1 0 100%;
+ }
+ }
+
+ .tooltip {
+ position: absolute;
+ }
+}
diff --git a/src/components/atoms/forms/fieldset.stories.tsx b/src/components/molecules/forms/fieldset.stories.tsx
index 0353c13..0778094 100644
--- a/src/components/atoms/forms/fieldset.stories.tsx
+++ b/src/components/molecules/forms/fieldset.stories.tsx
@@ -1,6 +1,8 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';
+import { TooltipProps } from '../modals/tooltip';
+import { Help } from '../modals/tooltip.stories';
import FieldsetComponent from './fieldset';
-import { body, legend } from './fieldset.fixture';
+import { body, legend, Tooltip } from './fieldset.fixture';
/**
* Fieldset - Storybook Meta
@@ -13,6 +15,19 @@ export default {
role: 'group',
},
argTypes: {
+ bodyClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the body wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
children: {
control: {
type: null,
@@ -89,6 +104,19 @@ export default {
required: false,
},
},
+ Tooltip: {
+ control: {
+ type: null,
+ },
+ description: 'Add an optional tooltip.',
+ table: {
+ category: 'Options',
+ },
+ type: {
+ name: 'function',
+ required: false,
+ },
+ },
},
} as ComponentMeta<typeof FieldsetComponent>;
@@ -114,3 +142,24 @@ InlinedLegend.args = {
legend: legend,
legendPosition: 'inline',
};
+
+/**
+ * Fieldset Stories - Stacked legend with tooltip
+ */
+export const StackedLegendWithTooltip = Template.bind({});
+StackedLegendWithTooltip.args = {
+ children: body,
+ legend: legend,
+ Tooltip,
+};
+
+/**
+ * Fieldset Stories - Inlined legend with tooltip
+ */
+export const InlinedLegendWithTooltip = Template.bind({});
+InlinedLegendWithTooltip.args = {
+ children: body,
+ legend: legend,
+ legendPosition: 'inline',
+ Tooltip,
+};
diff --git a/src/components/molecules/forms/fieldset.test.tsx b/src/components/molecules/forms/fieldset.test.tsx
new file mode 100644
index 0000000..de89e31
--- /dev/null
+++ b/src/components/molecules/forms/fieldset.test.tsx
@@ -0,0 +1,22 @@
+import { render, screen } from '@test-utils';
+import Fieldset from './fieldset';
+import { body, legend, Tooltip } from './fieldset.fixture';
+
+describe('Fieldset', () => {
+ // Cannot use toBeInTheDocument because of body is not an HTMLElement.
+
+ it('renders a legend and a body', () => {
+ render(<Fieldset legend={legend}>{body}</Fieldset>);
+ expect(screen.findByRole('group', { name: legend })).toBeTruthy();
+ expect(screen.findByText(body)).toBeTruthy();
+ });
+
+ it('renders a button to open a tooltip', () => {
+ render(
+ <Fieldset legend={legend} Tooltip={Tooltip}>
+ {body}
+ </Fieldset>
+ );
+ expect(screen.findByRole('button', { name: /Help/i })).toBeTruthy();
+ });
+});
diff --git a/src/components/molecules/forms/fieldset.tsx b/src/components/molecules/forms/fieldset.tsx
new file mode 100644
index 0000000..9f46247
--- /dev/null
+++ b/src/components/molecules/forms/fieldset.tsx
@@ -0,0 +1,118 @@
+import useClickOutside from '@utils/hooks/use-click-outside';
+import {
+ cloneElement,
+ FC,
+ ReactComponentElement,
+ ReactNode,
+ useRef,
+ useState,
+} from 'react';
+import HelpButton from '../buttons/help-button';
+import Tooltip from '../modals/tooltip';
+import styles from './fieldset.module.scss';
+
+export type FieldsetProps = {
+ /**
+ * Set additional classnames to the body wrapper.
+ */
+ bodyClassName?: string;
+ /**
+ * The fieldset body.
+ */
+ children: ReactNode | ReactNode[];
+ /**
+ * Set additional classnames to the fieldset wrapper.
+ */
+ className?: string;
+ /**
+ * The fieldset legend.
+ */
+ legend: string;
+ /**
+ * Set additional classnames to the legend.
+ */
+ legendClassName?: string;
+ /**
+ * The legend position. Default: stacked.
+ */
+ legendPosition?: 'inline' | 'stacked';
+ /**
+ * An accessible role. Default: group.
+ */
+ role?: 'group' | 'radiogroup' | 'presentation' | 'none';
+ /**
+ * An optional tooltip component.
+ */
+ Tooltip?: ReactComponentElement<typeof Tooltip>;
+};
+
+/**
+ * Fieldset component
+ *
+ * Render a fieldset with a legend.
+ */
+const Fieldset: FC<FieldsetProps> = ({
+ bodyClassName = '',
+ children,
+ className = '',
+ legend,
+ legendClassName = '',
+ legendPosition = 'stacked',
+ Tooltip: TooltipComponent,
+ ...props
+}) => {
+ const [isTooltipOpened, setIsTooltipOpened] = useState<boolean>(false);
+ const buttonRef = useRef<HTMLButtonElement>(null);
+ const tooltipRef = useRef<HTMLDivElement>(null);
+ const wrapperModifier = `wrapper--${legendPosition}`;
+ const buttonModifier = isTooltipOpened ? styles['btn--activated'] : '';
+ const legendModifier =
+ TooltipComponent === undefined ? '' : 'legend--has-tooltip';
+ const tooltipModifier = isTooltipOpened
+ ? 'tooltip--visible'
+ : 'tooltip--hidden';
+
+ /**
+ * Close the tooltip if the event target is outside.
+ *
+ * @param {EventTarget} target - The event target.
+ */
+ const closeTooltip = (target: EventTarget) => {
+ if (buttonRef.current && !buttonRef.current.contains(target as Node))
+ setIsTooltipOpened(false);
+ };
+
+ useClickOutside(
+ tooltipRef,
+ (target) => isTooltipOpened && closeTooltip(target)
+ );
+
+ return (
+ <fieldset
+ className={`${styles.wrapper} ${styles[wrapperModifier]} ${className}`}
+ {...props}
+ >
+ <legend
+ className={`${styles.legend} ${styles[legendModifier]} ${legendClassName}`}
+ >
+ {legend}
+ </legend>
+ {TooltipComponent && (
+ <>
+ <HelpButton
+ className={`${styles.btn} ${buttonModifier}`}
+ onClick={() => setIsTooltipOpened(!isTooltipOpened)}
+ ref={buttonRef}
+ />
+ {cloneElement(TooltipComponent, {
+ cloneClassName: `${styles.tooltip} ${styles[tooltipModifier]}`,
+ ref: tooltipRef,
+ })}
+ </>
+ )}
+ <div className={`${styles.body} ${bodyClassName}`}>{children}</div>
+ </fieldset>
+ );
+};
+
+export default Fieldset;
diff --git a/src/components/molecules/forms/motion-toggle.stories.tsx b/src/components/molecules/forms/motion-toggle.stories.tsx
index 5c524a8..541ca8e 100644
--- a/src/components/molecules/forms/motion-toggle.stories.tsx
+++ b/src/components/molecules/forms/motion-toggle.stories.tsx
@@ -9,6 +9,19 @@ export default {
title: 'Molecules/Forms/Toggle',
component: MotionToggleComponent,
argTypes: {
+ bodyClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the fieldset body wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
defaultValue: {
control: {
type: 'select',
@@ -20,6 +33,19 @@ export default {
required: true,
},
},
+ groupClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the radio group wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
legendClassName: {
control: {
type: 'text',
diff --git a/src/components/molecules/forms/motion-toggle.tsx b/src/components/molecules/forms/motion-toggle.tsx
index 6925248..ec2d950 100644
--- a/src/components/molecules/forms/motion-toggle.tsx
+++ b/src/components/molecules/forms/motion-toggle.tsx
@@ -13,7 +13,7 @@ export type MotionToggleValue = 'on' | 'off';
export type MotionToggleProps = Pick<
RadioGroupProps,
- 'groupClassName' | 'legendClassName'
+ 'bodyClassName' | 'groupClassName' | 'legendClassName'
> & {
/**
* True if motion should be reduced by default.
diff --git a/src/components/molecules/forms/prism-theme-toggle.stories.tsx b/src/components/molecules/forms/prism-theme-toggle.stories.tsx
index 3f57fa5..86f9773 100644
--- a/src/components/molecules/forms/prism-theme-toggle.stories.tsx
+++ b/src/components/molecules/forms/prism-theme-toggle.stories.tsx
@@ -8,6 +8,32 @@ export default {
title: 'Molecules/Forms/Toggle',
component: PrismThemeToggle,
argTypes: {
+ bodyClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the fieldset body wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ groupClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the radio group wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
legendClassName: {
control: {
type: 'text',
diff --git a/src/components/molecules/forms/prism-theme-toggle.tsx b/src/components/molecules/forms/prism-theme-toggle.tsx
index 66be056..7bf5b7c 100644
--- a/src/components/molecules/forms/prism-theme-toggle.tsx
+++ b/src/components/molecules/forms/prism-theme-toggle.tsx
@@ -12,7 +12,7 @@ import RadioGroup, {
export type PrismThemeToggleProps = Pick<
RadioGroupProps,
- 'groupClassName' | 'legendClassName'
+ 'bodyClassName' | 'groupClassName' | 'legendClassName'
>;
/**
diff --git a/src/components/molecules/forms/radio-group.stories.tsx b/src/components/molecules/forms/radio-group.stories.tsx
index 3c01af5..ad1bd6d 100644
--- a/src/components/molecules/forms/radio-group.stories.tsx
+++ b/src/components/molecules/forms/radio-group.stories.tsx
@@ -13,6 +13,19 @@ export default {
labelSize: 'small',
},
argTypes: {
+ bodyClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the fieldset body wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
className: {
control: {
type: 'text',
@@ -26,6 +39,19 @@ export default {
required: false,
},
},
+ groupClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the radio group wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
initialChoice: {
control: {
type: 'text',
@@ -131,6 +157,19 @@ export default {
required: false,
},
},
+ onClick: {
+ control: {
+ type: null,
+ },
+ description: 'A callback function to handle click on a choice.',
+ table: {
+ category: 'Events',
+ },
+ type: {
+ name: 'function',
+ required: false,
+ },
+ },
optionClassName: {
control: {
type: 'text',
@@ -152,6 +191,19 @@ export default {
value: {},
},
},
+ Tooltip: {
+ control: {
+ type: null,
+ },
+ description: 'Add an optional tooltip.',
+ table: {
+ category: 'Options',
+ },
+ type: {
+ name: 'function',
+ required: false,
+ },
+ },
},
} as ComponentMeta<typeof RadioGroup>;
diff --git a/src/components/molecules/forms/radio-group.tsx b/src/components/molecules/forms/radio-group.tsx
index 45f585e..64bdaa0 100644
--- a/src/components/molecules/forms/radio-group.tsx
+++ b/src/components/molecules/forms/radio-group.tsx
@@ -1,4 +1,6 @@
-import Fieldset, { type FieldsetProps } from '@components/atoms/forms/fieldset';
+import Fieldset, {
+ type FieldsetProps,
+} from '@components/molecules/forms/fieldset';
import useStateChange from '@utils/hooks/use-state-change';
import { ChangeEvent, FC, MouseEvent, SetStateAction, useState } from 'react';
import LabelledBooleanField, {
@@ -23,7 +25,7 @@ export type RadioGroupOption = Pick<
export type RadioGroupProps = Pick<
FieldsetProps,
- 'className' | 'legend' | 'legendClassName'
+ 'bodyClassName' | 'className' | 'legend' | 'legendClassName' | 'Tooltip'
> &
Pick<LabelledBooleanFieldProps, 'labelPosition' | 'labelSize'> & {
/**
diff --git a/src/components/molecules/forms/select-with-tooltip.stories.tsx b/src/components/molecules/forms/select-with-tooltip.stories.tsx
deleted file mode 100644
index d6206a9..0000000
--- a/src/components/molecules/forms/select-with-tooltip.stories.tsx
+++ /dev/null
@@ -1,223 +0,0 @@
-import { ComponentMeta, ComponentStory } from '@storybook/react';
-import { useState } from 'react';
-import SelectWithTooltip from './select-with-tooltip';
-
-/**
- * SelectWithTooltip - Storybook Meta
- */
-export default {
- title: 'Molecules/Forms/Select',
- component: SelectWithTooltip,
- argTypes: {
- className: {
- control: {
- type: 'text',
- },
- description: 'Set additional classnames to the select wrapper.',
- table: {
- category: 'Styles',
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- content: {
- control: {
- type: 'text',
- },
- description: 'The tooltip body.',
- type: {
- name: 'string',
- required: true,
- },
- },
- disabled: {
- control: {
- type: 'boolean',
- },
- description: 'Field state: either enabled or disabled.',
- table: {
- category: 'Options',
- defaultValue: { summary: false },
- },
- type: {
- name: 'boolean',
- required: false,
- },
- },
- id: {
- control: {
- type: 'text',
- },
- description: 'Field id.',
- type: {
- name: 'string',
- required: true,
- },
- },
- label: {
- control: {
- type: 'text',
- },
- description: 'The select label.',
- type: {
- name: 'string',
- required: true,
- },
- },
- labelClassName: {
- control: {
- type: 'text',
- },
- description: 'Set additional classnames to the label.',
- table: {
- category: 'Styles',
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- labelSize: {
- control: {
- type: 'select',
- },
- description: 'The label size.',
- options: ['medium', 'small'],
- table: {
- category: 'Options',
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- name: {
- control: {
- type: 'text',
- },
- description: 'Field name.',
- type: {
- name: 'string',
- required: true,
- },
- },
- options: {
- control: {
- type: null,
- },
- description: 'Select options.',
- type: {
- name: 'array',
- required: true,
- value: {
- name: 'string',
- },
- },
- },
- required: {
- control: {
- type: 'boolean',
- },
- description: 'Determine if the field is required.',
- table: {
- category: 'Options',
- defaultValue: { summary: false },
- },
- type: {
- name: 'boolean',
- required: false,
- },
- },
- selectClassName: {
- control: {
- type: 'text',
- },
- description: 'Set additional classnames to the select field.',
- table: {
- category: 'Styles',
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- setValue: {
- control: {
- type: null,
- },
- description: 'Callback function to set field value.',
- table: {
- category: 'Events',
- },
- type: {
- name: 'function',
- required: true,
- },
- },
- title: {
- control: {
- type: 'text',
- },
- description: 'The tooltip title',
- type: {
- name: 'string',
- required: true,
- },
- },
- tooltipClassName: {
- control: {
- type: 'text',
- },
- description: 'Set additional classnames to the tooltip.',
- table: {
- category: 'Styles',
- },
- type: {
- name: 'string',
- required: false,
- },
- },
- value: {
- control: {
- type: 'text',
- },
- description: 'Field value.',
- type: {
- name: 'string',
- required: true,
- },
- },
- },
-} as ComponentMeta<typeof SelectWithTooltip>;
-
-const selectOptions = [
- { id: 'option1', name: 'Option 1', value: 'option1' },
- { id: 'option2', name: 'Option 2', value: 'option2' },
- { id: 'option3', name: 'Option 3', value: 'option3' },
-];
-
-const Template: ComponentStory<typeof SelectWithTooltip> = ({
- value: _value,
- setValue: _setValue,
- ...args
-}) => {
- const [selected, setSelected] = useState<string>('option1');
- return (
- <SelectWithTooltip value={selected} setValue={setSelected} {...args} />
- );
-};
-
-/**
- * Select Stories - With tooltip
- */
-export const WithTooltip = Template.bind({});
-WithTooltip.args = {
- content: 'Illo voluptatibus quia minima placeat sit nostrum excepturi.',
- title: 'Possimus quidem dolor',
- id: 'storybook-select',
- label: 'Officiis:',
- name: 'storybook-select',
- options: selectOptions,
-};
diff --git a/src/components/molecules/forms/select-with-tooltip.test.tsx b/src/components/molecules/forms/select-with-tooltip.test.tsx
deleted file mode 100644
index 7a423f5..0000000
--- a/src/components/molecules/forms/select-with-tooltip.test.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { render, screen } from '@test-utils';
-import SelectWithTooltip from './select-with-tooltip';
-
-const selectOptions = [
- { id: 'option1', name: 'Option 1', value: 'option1' },
- { id: 'option2', name: 'Option 2', value: 'option2' },
- { id: 'option3', name: 'Option 3', value: 'option3' },
-];
-const selectLabel = 'Jest select';
-const selectValue = selectOptions[0].value;
-const tooltipTitle = 'Jest tooltip';
-const tooltipContent = 'Nesciunt voluptatibus voluptatem omnis at quia libero.';
-
-describe('SelectWithTooltip', () => {
- it('renders a select', () => {
- render(
- <SelectWithTooltip
- id="jest-select"
- name="jest-select"
- label={selectLabel}
- options={selectOptions}
- value={selectValue}
- setValue={() => null}
- title={tooltipTitle}
- content={tooltipContent}
- />
- );
- expect(screen.getByRole('combobox', { name: selectLabel })).toHaveValue(
- selectValue
- );
- });
-});
diff --git a/src/components/molecules/forms/select-with-tooltip.tsx b/src/components/molecules/forms/select-with-tooltip.tsx
deleted file mode 100644
index 46075c2..0000000
--- a/src/components/molecules/forms/select-with-tooltip.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import useClickOutside from '@utils/hooks/use-click-outside';
-import { FC, useRef, useState } from 'react';
-import HelpButton from '../buttons/help-button';
-import Tooltip, { type TooltipProps } from '../modals/tooltip';
-import LabelledSelect, { type LabelledSelectProps } from './labelled-select';
-import styles from './select-with-tooltip.module.scss';
-
-export type SelectWithTooltipProps = Omit<
- LabelledSelectProps,
- 'labelPosition'
-> &
- Pick<TooltipProps, 'title' | 'content'> & {
- /**
- * Set additional classnames to the select wrapper.
- */
- className?: string;
- /**
- * Set additional classnames to the tooltip wrapper.
- */
- tooltipClassName?: TooltipProps['className'];
- };
-
-/**
- * SelectWithTooltip component
- *
- * Render a select with a button to display a tooltip about options.
- */
-const SelectWithTooltip: FC<SelectWithTooltipProps> = ({
- className = '',
- content,
- id,
- title,
- tooltipClassName = '',
- ...props
-}) => {
- const [isTooltipOpened, setIsTooltipOpened] = useState<boolean>(false);
- const buttonRef = useRef<HTMLButtonElement>(null);
- const tooltipRef = useRef<HTMLDivElement>(null);
- const buttonModifier = isTooltipOpened ? styles['btn--activated'] : '';
- const tooltipModifier = isTooltipOpened
- ? styles['tooltip--visible']
- : styles['tooltip--hidden'];
-
- const closeTooltip = (target: EventTarget) => {
- if (buttonRef.current && !buttonRef.current.contains(target as Node))
- setIsTooltipOpened(false);
- };
-
- useClickOutside(
- tooltipRef,
- (target) => isTooltipOpened && closeTooltip(target)
- );
-
- return (
- <div className={`${styles.wrapper} ${className}`}>
- <LabelledSelect
- labelPosition="left"
- id={id}
- labelClassName={styles.label}
- {...props}
- />
- <HelpButton
- className={`${styles.btn} ${buttonModifier}`}
- onClick={() => setIsTooltipOpened(!isTooltipOpened)}
- ref={buttonRef}
- />
- <Tooltip
- title={title}
- content={content}
- icon="?"
- className={`${styles.tooltip} ${tooltipModifier} ${tooltipClassName}`}
- ref={tooltipRef}
- />
- </div>
- );
-};
-
-export default SelectWithTooltip;
diff --git a/src/components/molecules/forms/theme-toggle.stories.tsx b/src/components/molecules/forms/theme-toggle.stories.tsx
index cd59d7e..ff1034d 100644
--- a/src/components/molecules/forms/theme-toggle.stories.tsx
+++ b/src/components/molecules/forms/theme-toggle.stories.tsx
@@ -8,6 +8,32 @@ export default {
title: 'Molecules/Forms/Toggle',
component: ThemeToggle,
argTypes: {
+ bodyClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the fieldset body wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ groupClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the radio group wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
legendClassName: {
control: {
type: 'text',
diff --git a/src/components/molecules/forms/theme-toggle.tsx b/src/components/molecules/forms/theme-toggle.tsx
index 30bc55c..b796b27 100644
--- a/src/components/molecules/forms/theme-toggle.tsx
+++ b/src/components/molecules/forms/theme-toggle.tsx
@@ -12,7 +12,7 @@ import RadioGroup, {
export type ThemeToggleProps = Pick<
RadioGroupProps,
- 'groupClassName' | 'legendClassName'
+ 'bodyClassName' | 'groupClassName' | 'legendClassName'
>;
/**
diff --git a/src/components/molecules/modals/tooltip.fixture.tsx b/src/components/molecules/modals/tooltip.fixture.tsx
new file mode 100644
index 0000000..5489f08
--- /dev/null
+++ b/src/components/molecules/modals/tooltip.fixture.tsx
@@ -0,0 +1,4 @@
+export const title = 'Illum eum at';
+export const content =
+ 'Non accusantium ad. Est et impedit iste animi voluptas cum accusamus accusantium. Repellat ut sint pariatur cumque cupiditate. Animi occaecati odio ut debitis ipsam similique. Repudiandae aut earum occaecati consequatur laborum ut nobis iusto. Adipisci laboriosam id.';
+export const icon = '?';
diff --git a/src/components/molecules/modals/tooltip.stories.tsx b/src/components/molecules/modals/tooltip.stories.tsx
index 06a4855..a3dfa9f 100644
--- a/src/components/molecules/modals/tooltip.stories.tsx
+++ b/src/components/molecules/modals/tooltip.stories.tsx
@@ -1,5 +1,6 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';
import Tooltip from './tooltip';
+import { content, icon, title } from './tooltip.fixture';
/**
* Tooltip - Storybook Meta
@@ -21,6 +22,20 @@ export default {
required: false,
},
},
+ cloneClassName: {
+ control: {
+ type: 'text',
+ },
+ description:
+ 'Set additional classnames to the tooltip when using cloneElement.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
content: {
control: {
type: 'text',
@@ -63,8 +78,7 @@ const Template: ComponentStory<typeof Tooltip> = (args) => (
*/
export const Help = Template.bind({});
Help.args = {
- content:
- 'Minima tempora fuga omnis ratione doloribus ut. Totam ea vitae consequatur. Fuga hic ipsum. In non debitis ex assumenda ut dicta. Sit ut maxime eligendi est.',
- icon: '?',
- title: 'Laborum enim vero',
+ content,
+ icon,
+ title,
};
diff --git a/src/components/molecules/modals/tooltip.test.tsx b/src/components/molecules/modals/tooltip.test.tsx
index 24f20d8..d00b4b1 100644
--- a/src/components/molecules/modals/tooltip.test.tsx
+++ b/src/components/molecules/modals/tooltip.test.tsx
@@ -1,10 +1,6 @@
import { render, screen } from '@test-utils';
import Tooltip from './tooltip';
-
-const title = 'Illum eum at';
-const content =
- 'Non accusantium ad. Est et impedit iste animi voluptas cum accusamus accusantium. Repellat ut sint pariatur cumque cupiditate. Animi occaecati odio ut debitis ipsam similique. Repudiandae aut earum occaecati consequatur laborum ut nobis iusto. Adipisci laboriosam id.';
-const icon = '?';
+import { content, icon, title } from './tooltip.fixture';
describe('Tooltip', () => {
it('renders a title', () => {
diff --git a/src/components/molecules/modals/tooltip.tsx b/src/components/molecules/modals/tooltip.tsx
index efb3009..9801393 100644
--- a/src/components/molecules/modals/tooltip.tsx
+++ b/src/components/molecules/modals/tooltip.tsx
@@ -8,6 +8,10 @@ export type TooltipProps = {
*/
className?: string;
/**
+ * Set more additional classnames to the tooltip wrapper. Required when using React.cloneElement.
+ */
+ cloneClassName?: string;
+ /**
* The tooltip body.
*/
content: string | string[];
@@ -27,7 +31,7 @@ export type TooltipProps = {
* Render a tooltip modal.
*/
const Tooltip: ForwardRefRenderFunction<HTMLDivElement, TooltipProps> = (
- { className = '', content, icon, title },
+ { cloneClassName = '', className = '', content, icon, title },
ref
) => {
/**
@@ -43,7 +47,10 @@ const Tooltip: ForwardRefRenderFunction<HTMLDivElement, TooltipProps> = (
};
return (
- <div className={`${styles.wrapper} ${className}`} ref={ref}>
+ <div
+ className={`${styles.wrapper} ${cloneClassName} ${className}`}
+ ref={ref}
+ >
<div className={styles.title}>
<span className={styles.icon}>{icon}</span>
{title}
diff --git a/src/components/organisms/forms/settings-form.module.scss b/src/components/organisms/forms/settings-form.module.scss
index 6174304..647f1b5 100644
--- a/src/components/organisms/forms/settings-form.module.scss
+++ b/src/components/organisms/forms/settings-form.module.scss
@@ -8,7 +8,8 @@
@include mix.media("screen") {
@include mix.dimensions(null, "2xs", "height") {
- gap: var(--spacing-md);
+ column-gap: var(--spacing-lg);
+ row-gap: var(--spacing-xs);
font-size: var(--font-size-sm);
}
}
@@ -16,28 +17,31 @@
.label {
@include mix.media("screen") {
@include mix.dimensions(null, "2xs", "height") {
+ margin: 0 auto;
+ float: none;
font-size: var(--font-size-sm);
-
- &:not(.label--select) {
- float: none;
- margin: 0 auto;
- }
}
- }
- &.label--select {
- @include mix.media("screen") {
- @include mix.dimensions("2xs", null, "height") {
- margin: 0 auto 0 0;
- }
+ @include mix.dimensions(null, "2xs") {
+ //font-size: var(--font-size-sm);
}
}
}
.tooltip {
+ top: unset;
+ bottom: calc(100% + var(--spacing-2xs));
+ font-size: var(--font-size-sm);
+ transform-origin: bottom center;
+
@include mix.media("screen") {
- @include mix.dimensions(null, "2xs") {
- font-size: var(--font-size-sm);
+ @include mix.dimensions(null, "2xs", "height") {
+ width: 250%;
+ transform-origin: bottom left;
+ }
+
+ @include mix.dimensions("sm") {
+ font-size: var(--font-size-md);
}
}
}
@@ -50,28 +54,28 @@
@include mix.dimensions(null, "2xs", "height") {
display: flex;
flex-flow: column wrap;
- width: fit-content;
-
- &:last-of-type {
- flex: 0 0 100%;
- margin: 0;
- }
+ max-width: fit-content;
+ margin: 0;
}
}
}
-.setting {
- &--select {
- flex: 0 0 100%;
+.group {
+ margin-left: auto;
+
+ @include mix.media("screen") {
+ @include mix.dimensions(null, "2xs", "height") {
+ margin: auto;
+ }
}
}
-.group {
+.fieldset__body {
margin-left: auto;
@include mix.media("screen") {
@include mix.dimensions(null, "2xs", "height") {
- margin: auto;
+ margin: 0 auto;
}
}
}
diff --git a/src/components/organisms/forms/settings-form.stories.tsx b/src/components/organisms/forms/settings-form.stories.tsx
index ceb08c7..de9f769 100644
--- a/src/components/organisms/forms/settings-form.stories.tsx
+++ b/src/components/organisms/forms/settings-form.stories.tsx
@@ -1,4 +1,4 @@
-import { storageKey as ackeeStorageKey } from '@components/molecules/forms/ackee-select.fixture';
+import { storageKey as ackeeStorageKey } from '@components/molecules/forms/ackee-toggle.fixture';
import { storageKey as motionStorageKey } from '@components/molecules/forms/motion-toggle.fixture';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import SettingsForm from './settings-form';
diff --git a/src/components/organisms/forms/settings-form.test.tsx b/src/components/organisms/forms/settings-form.test.tsx
index 90a2751..584261d 100644
--- a/src/components/organisms/forms/settings-form.test.tsx
+++ b/src/components/organisms/forms/settings-form.test.tsx
@@ -1,9 +1,8 @@
+import { storageKey as ackeeStorageKey } from '@components/molecules/forms/ackee-toggle.fixture';
+import { storageKey as motionStorageKey } from '@components/molecules/forms/motion-toggle.fixture';
import { render, screen } from '@test-utils';
import SettingsForm from './settings-form';
-const ackeeStorageKey = 'ackee-tracking';
-const motionStorageKey = 'reduce-motion';
-
describe('SettingsForm', () => {
it('renders a form', () => {
render(
@@ -17,7 +16,7 @@ describe('SettingsForm', () => {
).toBeInTheDocument();
});
- it('renders a theme toggle setting', () => {
+ it('renders a theme setting', () => {
render(
<SettingsForm
ackeeStorageKey={ackeeStorageKey}
@@ -29,7 +28,7 @@ describe('SettingsForm', () => {
).toBeInTheDocument();
});
- it('renders a code blocks toggle setting', () => {
+ it('renders a code blocks setting', () => {
render(
<SettingsForm
ackeeStorageKey={ackeeStorageKey}
@@ -61,7 +60,7 @@ describe('SettingsForm', () => {
/>
);
expect(
- screen.getByRole('combobox', { name: /^Tracking:/i })
+ screen.getByRole('radiogroup', { name: /^Tracking:/i })
).toBeInTheDocument();
});
});
diff --git a/src/components/organisms/forms/settings-form.tsx b/src/components/organisms/forms/settings-form.tsx
index 9dc0e90..5d915a8 100644
--- a/src/components/organisms/forms/settings-form.tsx
+++ b/src/components/organisms/forms/settings-form.tsx
@@ -1,7 +1,7 @@
import Form from '@components/atoms/forms/form';
-import AckeeSelect, {
- type AckeeSelectProps,
-} from '@components/molecules/forms/ackee-select';
+import AckeeToggle, {
+ type AckeeToggleProps,
+} from '@components/molecules/forms/ackee-toggle';
import MotionToggle, {
type MotionToggleProps,
} from '@components/molecules/forms/motion-toggle';
@@ -11,11 +11,11 @@ import { FC } from 'react';
import { useIntl } from 'react-intl';
import styles from './settings-form.module.scss';
-export type SettingsFormProps = Pick<AckeeSelectProps, 'tooltipClassName'> & {
+export type SettingsFormProps = Pick<AckeeToggleProps, 'tooltipClassName'> & {
/**
* The local storage key for Ackee settings.
*/
- ackeeStorageKey: AckeeSelectProps['storageKey'];
+ ackeeStorageKey: AckeeToggleProps['storageKey'];
/**
* The local storage key for Reduce motion settings.
*/
@@ -42,24 +42,29 @@ const SettingsForm: FC<SettingsFormProps> = ({
onSubmit={() => null}
>
<ThemeToggle
+ bodyClassName={styles.fieldset__body}
groupClassName={styles.group}
legendClassName={styles.label}
/>
<PrismThemeToggle
+ bodyClassName={styles.fieldset__body}
groupClassName={styles.group}
legendClassName={styles.label}
/>
<MotionToggle
defaultValue="on"
+ bodyClassName={styles.fieldset__body}
groupClassName={styles.group}
legendClassName={styles.label}
storageKey={motionStorageKey}
/>
- <AckeeSelect
- initialValue="full"
- labelClassName={`${styles.label} ${styles['label--select']}`}
- tooltipClassName={`${styles.tooltip} ${tooltipClassName}`}
+ <AckeeToggle
+ defaultValue="full"
+ bodyClassName={styles.fieldset__body}
+ groupClassName={`${styles.group} ${styles['group--ackee']}`}
+ legendClassName={`${styles.label} ${styles['label--ackee']}`}
storageKey={ackeeStorageKey}
+ tooltipClassName={`${styles.tooltip} ${tooltipClassName}`}
/>
</Form>
);
diff --git a/src/components/organisms/modals/settings-modal.module.scss b/src/components/organisms/modals/settings-modal.module.scss
index a6a2077..70b786d 100644
--- a/src/components/organisms/modals/settings-modal.module.scss
+++ b/src/components/organisms/modals/settings-modal.module.scss
@@ -1,11 +1 @@
-@use "@styles/abstracts/mixins" as mix;
-
-.label {
- margin-right: auto;
-
- @include mix.media("screen") {
- @include mix.dimensions(null, "2xs", "height") {
- font-size: var(--font-size-sm);
- }
- }
-}
+// TODO
diff --git a/src/components/organisms/modals/settings-modal.tsx b/src/components/organisms/modals/settings-modal.tsx
index 5d14836..34f5619 100644
--- a/src/components/organisms/modals/settings-modal.tsx
+++ b/src/components/organisms/modals/settings-modal.tsx
@@ -37,12 +37,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
});
return (
- <Modal
- title={title}
- icon="cogs"
- className={`${styles.wrapper} ${className}`}
- headingClassName={styles.heading}
- >
+ <Modal title={title} icon="cogs" className={className}>
<DynamicSettingsForm {...props} />
</Modal>
);