Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/switch button #49

Merged
merged 3 commits into from
Dec 18, 2024
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
1 change: 1 addition & 0 deletions src/components/switch/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './switch';
53 changes: 53 additions & 0 deletions src/components/switch/switch.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Switch } from './switch';

const meta: Meta<typeof Switch> = {
title: 'Components/Switch',
component: Switch,
tags: ['autodocs'],
argTypes: {
checked: {
description: 'Controls the checked state of the switch',
control: 'boolean',
value: false
},
disabled: {
description: 'Determines if the switch is disabled',
control: 'boolean',
value: false
},
highlight: {
description: 'Applies a highlight style to the switch',
control: 'boolean',
value: false
},
onChange: {
description: 'Callback function triggered when the switch state changes',
control: false
}
}
};

export default meta;
type Story = StoryObj<typeof Switch>;

export const Default: Story = {
args: {}
};

export const Checked: Story = {
args: {
checked: true
}
};

export const Highlight: Story = {
args: { highlight: true, checked: true }
};

export const Disabled: Story = {
args: {
checked: false,
disabled: true
}
};
42 changes: 42 additions & 0 deletions src/components/switch/switch.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import { Switch } from './switch';

describe('Switch Component', () => {
it('should render correctly', () => {
render(<Switch />);
expect(screen.getByRole('checkbox')).toBeInTheDocument();
});

it('should call onChange when clicked', () => {
const handleChange = jest.fn();
render(<Switch onChange={handleChange} />);

fireEvent.click(screen.getByRole('checkbox'));
expect(handleChange).toHaveBeenCalledTimes(1);
});

it('should display correct checked state', () => {
render(<Switch checked />);
expect(screen.getByRole('checkbox')).toBeChecked();
});

it('should be disabled when disabled prop is true', () => {
render(<Switch disabled />);
expect(screen.getByRole('checkbox')).toBeDisabled();
});

it('should not call onChange when clicked if disabled', () => {
const handleChange = jest.fn();
render(<Switch onChange={handleChange} disabled />);

fireEvent.click(screen.getByRole('checkbox'));
expect(handleChange).not.toHaveBeenCalled();
});

it('should have purple background when highlight prop is true', () => {
render(<Switch highlight />);
const switchElement = screen.getByRole('checkbox');
expect(switchElement).toHaveStyle({ backgroundColor: 'var(--purple-500)' });
});
});
41 changes: 41 additions & 0 deletions src/components/switch/switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import { cn } from '@/lib/utils';

interface SwitchProps {
checked?: boolean;
disabled?: boolean;
highlight?: boolean;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
}

function Switch({ checked, disabled, highlight, onChange }: SwitchProps) {
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (!disabled && onChange) {
onChange(event);
}
};

return (
<label className="inline-flex items-center cursor-pointer">
<input type="checkbox" className="sr-only peer" checked={checked} disabled={disabled} onChange={handleChange} />
<div
className={cn(
'relative w-9 h-5 bg-switch-bg hover:bg-switch-bg-hover transition-all shadow-switch rounded-full peer',
'peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full',
"after:content-[''] after:bg-switch-thumb after:absolute after:top-[2px] after:start-[2px]",
'after:rounded-full after:h-4 after:w-4 after:transition-all',
'after:shadow-[0px_0px_4px_0px_#00000052,0px_1px_1px_0px_#00000014]',
'peer-focus:after:ring-8 peer-focus:after:ring-[#0000000A]',
highlight
? 'peer-checked:bg-switch-highlight peer-checked:hover:bg-switch-highlight-hover peer-checked:peer-focus:after:ring-switch-highlight-ring'
: 'peer-checked:bg-switch-checked peer-checked:hover:bg-switch-checked-hover peer-checked:peer-focus:after:ring-switch-checked-ring',
'peer-disabled:bg-gray-300/0 peer-disabled:cursor-not-allowed',
'peer-disabled:shadow-[inset_0_0_0_1px_rgb(228,228,231)]',
'peer-disabled:after:bg-switch-thumb-disabled peer-disabled:after:shadow-none'
)}
/>
</label>
);
}

export { Switch };
14 changes: 14 additions & 0 deletions src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
--bottom_sheet-2: 0px 0px 1px 1px rgba(0, 0, 0, 0.04), 0px 4px 16px -2px rgba(0, 0, 0, 0.08);
--modals-shadow: 0px 0px 1px 1px rgba(0, 0, 0, 0.04), 0px -6px 24px -3px rgba(0, 0, 0, 0.08);
--dropdown-shadow: 0px 0px 10px 3px rgba(0, 0, 0, 0.04), 0px 10px 40px -5px rgba(0, 0, 0, 0.08);
--switch-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.161) inset;

--button-solid-shadow: 0px 0px 0px 3px #9061f940;

Expand Down Expand Up @@ -70,6 +71,19 @@
--palette-gradiente-active_menu: linear-gradient(270deg, #7e3af200 0%, #7e3af20b 100%);

--icons-default: #71717a;

--switch-bg-default: #e4e4e740;
--switch-bg-hover: #e4e4e7;
--switch-thumb-bg: #fafafa;
--switch-thumb-disabled: #e5e5e5;

--switch-checked-bg: #31c48d;
--switch-checked-hover: #0e9f6e;
--switch-checked-ring: #31c48d1f;

--switch-highlight-bg: #ac94fa;
--switch-highlight-hover: #9061f9;
--switch-highlight-ring: #ac94fa1f;
}

.classic-purple-dark {
Expand Down
23 changes: 22 additions & 1 deletion tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,26 @@ const config = {
700: '#bf125d',
800: '#99154b',
900: '#751a3d'
},
switch: {
bg: {
DEFAULT: 'var(--switch-bg-default)',
hover: 'var(--switch-bg-hover)'
},
thumb: {
DEFAULT: 'var(--switch-thumb-bg)',
disabled: 'var(--switch-thumb-disabled)'
},
checked: {
DEFAULT: 'var(--switch-checked-bg)',
hover: 'var(--switch-checked-hover)',
ring: 'var(--switch-checked-ring)'
},
highlight: {
DEFAULT: 'var(--switch-highlight-bg)',
hover: 'var(--switch-highlight-hover)',
ring: 'var(--switch-highlight-ring)'
}
}
},
extend: {
Expand All @@ -140,7 +160,8 @@ const config = {
modal: 'var(--modals-shadow)',
custom: '0px 0px 0px 1px rgba(0,0,0,0.15)',
solid: 'var(--button-solid-shadow)',
dropdown: 'var(--dropdown-shadow)'
dropdown: 'var(--dropdown-shadow)',
switch: 'var(--switch-shadow)'
},
backgroundImage: {
'active-menu': 'var(--palette-gradiente-active_menu)'
Expand Down