Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/common/components/Form/Toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ export interface ToggleProps<T extends FieldValues> extends BaseComponentProps {
/**
* The `Toggle` component renders a button form control which may be used for
* binary (true/false) inputs.
* @param {ToggleProps} props - Component properties.
* @returns JSX
*/
const Toggle = <T extends FieldValues>({
className,
Expand Down
5 changes: 5 additions & 0 deletions src/common/components/Router/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const SearchInputComponents = lazy(
const TabsComponents = lazy(() => import('pages/Components/components/TabsComponents'));
const TextComponents = lazy(() => import('pages/Components/components/TextComponents'));
const TextareaComponents = lazy(() => import('pages/Components/components/TextareaComponents'));
const ToggleComponents = lazy(() => import('pages/Components/components/ToggleComponents'));

// Tasks Page Family
const TasksPage = lazy(() => import('pages/Tasks/TasksPage'));
Expand Down Expand Up @@ -176,6 +177,10 @@ export const routes: RouteObject[] = [
path: 'textarea',
element: withSuspense(<TextareaComponents />),
},
{
path: 'toggle',
element: withSuspense(<ToggleComponents />),
},
],
},
],
Expand Down
3 changes: 3 additions & 0 deletions src/pages/Components/ComponentsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ const ComponentsPage = (): JSX.Element => {
<MenuNavLink to="textarea" styleActive>
Textarea
</MenuNavLink>
<MenuNavLink to="toggle" styleActive>
Toggle
</MenuNavLink>
</Columns.Column>
<Columns.Column data-testid="page-components-content">
<Outlet />
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Components/components/HelpTextComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const HelpTextComponents = ({
Content
</Heading>
<div className="mb-4 opacity-85">
You may pass complex content as the "chilren" to the HelpText component.
You may pass complex content as the "children" to the HelpText component.
</div>
<div className="my-8">
<div className="mb-2 flex flex-col place-content-center rounded-sm border border-neutral-500/10 p-4 dark:bg-neutral-700/25">
Expand Down
6 changes: 3 additions & 3 deletions src/pages/Components/components/InputComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ const InputComponents = ({
},
{
name: 'label',
description: 'Optional. The input label text.',
description: 'Optional. The label text.',
},
{
name: 'name',
description: 'The input name.',
description: 'The form control name.',
},
{
name: 'supportingText',
description: 'Optional. The input supporting or help text.',
description: 'Optional. The supporting or help text.',
},
{
name: 'testId',
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Components/components/TextareaComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const TextareaComponents = ({
},
{
name: 'name',
description: 'The textarea name.',
description: 'The form control name.',
},
{
name: 'supportingText',
Expand Down
252 changes: 252 additions & 0 deletions src/pages/Components/components/ToggleComponents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
import { createColumnHelper } from '@tanstack/react-table';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { boolean, object } from 'yup';
import noop from 'lodash/noop';

import { BaseComponentProps } from 'common/utils/types';
import { ComponentProperty } from '../model/components';
import Table from 'common/components/Table/Table';
import CodeSnippet from 'common/components/Text/CodeSnippet';
import Heading from 'common/components/Text/Heading';
import Toggle from 'common/components/Form/Toggle';
import Button from 'common/components/Button/Button';

/**
* The `ToggleComponents` component renders a set of examples illustrating
* the use of the `Toggle` component.
*/
const ToggleComponents = ({
className,
testId = 'components-toggle',
}: BaseComponentProps): JSX.Element => {
const data: ComponentProperty[] = [
{
name: 'className',
description: 'Optional. Additional CSS class names.',
},
{
name: 'control',
description: 'The React Hook Form control object.',
},
{
name: 'disabled',
description: 'Optional. Indicates if the control is disabled. Defaults to false.',
},
{
name: 'label',
description: 'Optional. The label text.',
},
{
name: 'name',
description: 'The form control name.',
},
{
name: 'required',
description: 'Optional. Indicates if the control is required. Defaults to false.',
},
{
name: 'supportingText',
description: 'Optional. The supporting or help text.',
},
{
name: 'testId',
description: 'Optional. Identifier for testing.',
},
];
const columnHelper = createColumnHelper<ComponentProperty>();
const columns = [
columnHelper.accessor('name', {
cell: (info) => (
<span className="font-mono text-sky-700 dark:text-sky-500">{info.getValue()}</span>
),
header: () => 'Name',
}),
columnHelper.accessor('description', {
cell: (info) => info.renderValue(),
header: () => 'Description',
}),
];

const { control, handleSubmit, reset } = useForm({
defaultValues: {
isNotificationsEnabled: true,
},
mode: 'all',
resolver: yupResolver(
object({
isNotificationsEnabled: boolean(),
}),
),
});

const onSubmit = noop;

return (
<section className={className} data-testid={testId}>
<Heading level={2} className="mb-4">
Toggle Component
</Heading>

<div className="my-8">
<div className="mb-4">
The <span className="font-mono font-bold">Toggle</span> component renders a button form
control which may be used for binary inputs, i.e. true / false.
</div>

<div className="my-8">
<Heading level={3} className="mb-2">
Properties
</Heading>
<Table<ComponentProperty, string> data={data} columns={columns} />
</div>

<Heading level={3} className="mb-2">
Examples
</Heading>

<Heading level={4} className="my-2">
Basic
</Heading>
<div className="mb-4 opacity-85">
This is the most basic use of the Toggle component. It has no label or supporting text. It
is integrated with React Hook Form through the "control" and "reset" values obtained from
the "useForm" hook (see the React Hook Form documentation for more information).
</div>
<div className="my-8">
<div className="mb-2 flex flex-col place-content-center rounded-sm border border-neutral-500/10 p-4 dark:bg-neutral-700/25">
{/* Example */}
<form onSubmit={handleSubmit(onSubmit)}>
<Toggle control={control} name="isNotificationsEnabled" className="mb-4" />
<Button
onClick={() => reset()}
size="sm"
variant="outline"
className="ml-auto"
testId="reset-1"
>
Reset
</Button>
</form>
</div>
<CodeSnippet
className="my-2"
code={`<form onSubmit={handleSubmit(onSubmit)}>
<Toggle control={control} name="isNotificationsEnabled" className="mb-4" />
<Button
onClick={() => reset()}
size="sm"
variant="outline"
className="ml-auto"
>
Reset
</Button>
</form>`}
/>
</div>

<Heading level={4} className="my-2">
Labels
</Heading>
<div className="mb-4 opacity-85">
Use the "label" property to associate a HTML label with the toggle. When the toggle is
required, the label is styled to indicate.
</div>
<div className="my-8">
<div className="mb-2 flex flex-col place-content-center rounded-sm border border-neutral-500/10 p-4 dark:bg-neutral-700/25">
{/* Example */}
<form onSubmit={handleSubmit(onSubmit)}>
<Toggle
control={control}
name="isNotificationsEnabled"
label="Enable Notifications"
className="mb-4"
/>
<Button
onClick={() => reset()}
size="sm"
variant="outline"
className="ml-auto"
testId="reset-2"
>
Reset
</Button>
</form>
</div>
<CodeSnippet
className="my-2"
code={`<form onSubmit={handleSubmit(onSubmit)}>
<Toggle
control={control}
name="isNotificationsEnabled"
label="Enable Notifications"
className="mb-4"
/>
<Button
onClick={() => reset()}
size="sm"
variant="outline"
className="ml-auto"
>
Reset
</Button>
</form>`}
/>
</div>

<Heading level={4} className="my-2">
Supporting Text
</Heading>
<div className="mb-4 opacity-85">
Use the "supportingText" property to add helpful information below the toggle containing
instructions, validation requirements, or other tips for entering information.
</div>
<div className="my-8">
<div className="mb-2 flex flex-col place-content-center rounded-sm border border-neutral-500/10 p-4 dark:bg-neutral-700/25">
{/* Example */}
<form onSubmit={handleSubmit(onSubmit)}>
<Toggle
control={control}
name="isNotificationsEnabled"
label="Enable Notifications"
supportingText="Enable to receive push notifications from this app."
className="mb-4"
/>
<Button
onClick={() => reset()}
size="sm"
variant="outline"
className="ml-auto"
testId="reset-3"
>
Reset
</Button>
</form>
</div>
<CodeSnippet
className="my-2"
code={`<form onSubmit={handleSubmit(onSubmit)}>
<Toggle
control={control}
name="isNotificationsEnabled"
label="Enable Notifications"
supportingText="Enable to receive push notifications from this app."
className="mb-4"
/>
<Button
onClick={() => reset()}
size="sm"
variant="outline"
className="ml-auto"
>
Reset
</Button>
</form>`}
/>
</div>
</div>
</section>
);
};

export default ToggleComponents;
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expect, it } from 'vitest';
import userEvent from '@testing-library/user-event';

import { render, screen } from 'test/test-utils';

import ToggleComponents from '../ToggleComponents';

describe('ToggleComponents', () => {
it('should render successfully', async () => {
// ARRANGE
render(<ToggleComponents />);
await screen.findByTestId('components-toggle');

// ASSERT
expect(screen.getByTestId('components-toggle')).toBeDefined();
});

it('should click buttons', async () => {
// ARRANGE
const user = userEvent.setup();
render(<ToggleComponents />);
await screen.findByTestId('components-toggle');

// ACT
await user.click(screen.getByTestId('reset-1'));
await user.click(screen.getByTestId('reset-2'));
await user.click(screen.getByTestId('reset-3'));

// ASSERT
expect(screen.getByTestId('components-toggle')).toBeDefined();
});
});