Skip to content

Commit

Permalink
feat(theme): add theme support for Checkbox, Radio and ToggleSwitch (#…
Browse files Browse the repository at this point in the history
…551)

Co-authored-by: Ricardo Lüders <[email protected]>
  • Loading branch information
emilcheva and rluders authored Jan 19, 2023
1 parent a62e84f commit 05c934a
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 19 deletions.
24 changes: 23 additions & 1 deletion src/lib/components/Checkbox/Checkbox.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { Flowbite } from '../Flowbite';
import { Checkbox } from './Checkbox';

describe.concurrent('Components / Checkbox', () => {
Expand All @@ -10,4 +11,25 @@ describe.concurrent('Components / Checkbox', () => {
expect(checkbox).toBeInTheDocument();
});
});

describe('Theme', () => {
it('should use custom `base` classes', () => {
const theme = {
checkbox: {
root: {
base: 'bg-yellow-400 dark:bg-yellow-40',
},
},
};
render(
<Flowbite theme={{ theme }}>
<Checkbox />
</Flowbite>,
);

expect(checkbox()).toHaveClass('bg-yellow-400 dark:bg-yellow-40');
});
});
});

const checkbox = () => screen.getByRole('checkbox');
5 changes: 4 additions & 1 deletion src/lib/components/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { mergeDeep } from '../../helpers/mergeDeep';
import { useTheme } from '../Flowbite/ThemeContext';

export interface FlowbiteCheckboxTheme {
root: FlowbiteCheckboxRootTheme;
}
export interface FlowbiteCheckboxRootTheme {
base: string;
}

Expand All @@ -17,7 +20,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
({ theme: customTheme = {}, className, ...props }, ref) => {
const theme = mergeDeep(useTheme().theme.checkbox, customTheme);

return <input ref={ref} className={classNames(theme.base, className)} type="checkbox" {...props} />;
return <input ref={ref} className={classNames(theme.root.base, className)} type="checkbox" {...props} />;
},
);

Expand Down
24 changes: 23 additions & 1 deletion src/lib/components/Radio/Radio.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { Flowbite } from '../Flowbite';
import { Radio } from './Radio';

describe.concurrent('Components / Radio', () => {
Expand All @@ -10,4 +11,25 @@ describe.concurrent('Components / Radio', () => {
expect(radio).toBeInTheDocument();
});
});

describe('Theme', () => {
it('should use custom `base` classes', () => {
const theme = {
radio: {
root: {
base: 'bg-yellow-400 dark:bg-yellow-40',
},
},
};
render(
<Flowbite theme={{ theme }}>
<Radio />
</Flowbite>,
);

expect(radio()).toHaveClass('bg-yellow-400 dark:bg-yellow-40');
});
});
});

const radio = () => screen.getByRole('radio');
5 changes: 4 additions & 1 deletion src/lib/components/Radio/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { mergeDeep } from '../../helpers/mergeDeep';
import { useTheme } from '../Flowbite/ThemeContext';

export interface FlowbiteRadioTheme {
root: FlowbiteRadioRootTheme;
}
export interface FlowbiteRadioRootTheme {
base: string;
}

Expand All @@ -17,7 +20,7 @@ export const Radio = forwardRef<HTMLInputElement, RadioProps>(
({ theme: customTheme = {}, className, ...props }, ref) => {
const theme = mergeDeep(useTheme().theme.radio, customTheme);

return <input ref={ref} className={classNames(theme.base, className)} type="radio" {...props} />;
return <input ref={ref} className={classNames(theme.root.base, className)} type="radio" {...props} />;
},
);

Expand Down
89 changes: 89 additions & 0 deletions src/lib/components/ToggleSwitch/ToggleSwitch.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { FC, useState } from 'react';
import { describe, expect, it, vi } from 'vitest';
import { Flowbite } from '../Flowbite';
import { TextInput } from '../TextInput';
import { ToggleSwitch } from './ToggleSwitch';

Expand Down Expand Up @@ -127,6 +128,94 @@ describe('Components / Toggle switch', () => {
expect(toggleSwitch()).toHaveAttribute('type', 'submit');
});
});

describe('Theme', () => {
it('should use `base` classes', () => {
const theme = {
toggleSwitch: {
root: {
base: 'text-blue-100',
},
},
};
render(
<Flowbite theme={{ theme }}>
<ToggleSwitch checked={false} label="Enable" onChange={console.log} type="submit" />
</Flowbite>,
);

expect(toggleSwitch()).toHaveClass('text-blue-100');
});

it('should use `active` classes', () => {
const theme = {
toggleSwitch: {
root: {
active: {
off: 'text-blue-200',
on: 'text-blue-300',
},
},
},
};
render(
<Flowbite theme={{ theme }}>
<ToggleSwitch checked={false} label="Enable" onChange={console.log} type="submit" />
<ToggleSwitch disabled checked={false} label="Enable" onChange={console.log} type="submit" />
</Flowbite>,
);
const activeToggleSwitch = toggleSwitches()[0];
const disabledToggleSwitch = toggleSwitches()[1];

expect(activeToggleSwitch).toHaveClass('text-blue-300');
expect(disabledToggleSwitch).toHaveClass('text-blue-200');
});

it('should use `label` classes', () => {
const theme = {
toggleSwitch: {
root: {
label: 'test-label',
},
},
};
render(
<Flowbite theme={{ theme }}>
<ToggleSwitch checked={false} label="Enable" onChange={console.log} type="submit" />
</Flowbite>,
);

expect(label()).toHaveClass('test-label');
});

it('should use `toggle` classes', () => {
const theme = {
toggleSwitch: {
toggle: {
base: 'h-6 w-11',
checked: {
color: {
blue: 'bg-pink-700',
},
},
},
},
};
render(
<Flowbite theme={{ theme }}>
<ToggleSwitch checked label="Enable" onChange={console.log} type="submit" />
</Flowbite>,
);

expect(toggle()).toHaveClass('h-6 w-11 bg-pink-700');
});
});
});

const toggleSwitch = () => screen.getByRole('switch');

const toggleSwitches = () => screen.getAllByRole('switch');

const label = () => screen.getByTestId('flowbite-toggleswitch-label');

const toggle = () => screen.getByTestId('flowbite-toggleswitch-toggle');
23 changes: 15 additions & 8 deletions src/lib/components/ToggleSwitch/ToggleSwitch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,23 @@ import { FlowbiteBoolean, FlowbiteColors } from '../Flowbite/FlowbiteTheme';
import { useTheme } from '../Flowbite/ThemeContext';

export interface FlowbiteToggleSwitchTheme {
root: FlowbiteToggleSwitchRootTheme;
toggle: FlowbiteToggleSwitchToggleTheme;
}

export interface FlowbiteToggleSwitchRootTheme {
base: string;
active: FlowbiteBoolean;
toggle: {
base: string;
checked: FlowbiteBoolean & {
color: FlowbiteColors;
};
};
label: string;
}

export interface FlowbiteToggleSwitchToggleTheme {
base: string;
checked: FlowbiteBoolean & {
color: FlowbiteColors;
};
}

export type ToggleSwitchProps = Omit<ComponentProps<'button'>, 'onChange'> & {
checked: boolean;
label: string;
Expand Down Expand Up @@ -64,10 +70,11 @@ export const ToggleSwitch: FC<ToggleSwitchProps> = ({
role="switch"
tabIndex={0}
type="button"
className={classNames(theme.base, theme.active[disabled ? 'off' : 'on'], className)}
className={classNames(theme.root.base, theme.root.active[disabled ? 'off' : 'on'], className)}
{...props}
>
<div
data-testid="flowbite-toggleswitch-toggle"
className={classNames(
theme.toggle.base,
theme.toggle.checked[checked ? 'on' : 'off'],
Expand All @@ -77,7 +84,7 @@ export const ToggleSwitch: FC<ToggleSwitchProps> = ({
<span
data-testid="flowbite-toggleswitch-label"
id={`${id}-flowbite-toggleswitch-label`}
className={theme.label}
className={theme.root.label}
>
{label}
</span>
Expand Down
20 changes: 13 additions & 7 deletions src/lib/theme/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,9 @@ const theme: FlowbiteTheme = {
},
},
checkbox: {
base: 'h-4 w-4 rounded border border-gray-300 bg-gray-100 focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-blue-600',
root: {
base: 'h-4 w-4 rounded border border-gray-300 bg-gray-100 focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-blue-600',
},
},
fileInput: {
base: 'flex',
Expand Down Expand Up @@ -419,7 +421,9 @@ const theme: FlowbiteTheme = {
disabled: 'opacity-50',
},
radio: {
base: 'h-4 w-4 border border-gray-300 focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:focus:bg-blue-600 dark:focus:ring-blue-600',
root: {
base: 'h-4 w-4 border border-gray-300 focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:focus:bg-blue-600 dark:focus:ring-blue-600',
},
},
rangeSlider: {
base: 'flex',
Expand Down Expand Up @@ -545,10 +549,13 @@ const theme: FlowbiteTheme = {
},
},
toggleSwitch: {
base: 'group relative flex items-center rounded-lg focus:outline-none',
active: {
on: 'cursor-pointer',
off: 'cursor-not-allowed opacity-50',
root: {
base: 'group relative flex items-center rounded-lg focus:outline-none',
active: {
on: 'cursor-pointer',
off: 'cursor-not-allowed opacity-50',
},
label: 'ml-3 text-sm font-medium text-gray-900 dark:text-gray-300',
},
toggle: {
base: 'toggle-bg h-6 w-11 rounded-full border group-focus:ring-4 group-focus:ring-blue-500/25',
Expand Down Expand Up @@ -576,7 +583,6 @@ const theme: FlowbiteTheme = {
},
},
},
label: 'ml-3 text-sm font-medium text-gray-900 dark:text-gray-300',
},
helperText: {
base: 'mt-2 text-sm',
Expand Down

0 comments on commit 05c934a

Please sign in to comment.