Skip to content

Commit

Permalink
feat(Switch): introduce switch component (#190)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukicenturi authored Feb 5, 2024
1 parent fa38362 commit 8e33e98
Show file tree
Hide file tree
Showing 9 changed files with 641 additions and 0 deletions.
23 changes: 23 additions & 0 deletions example/cypress/e2e/forms/switch.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
describe('forms/Switch', () => {
beforeEach(() => {
cy.visit('/switches');
});

it('checks for switches', () => {
cy.contains('h2[data-cy=switches]', 'Switches');

cy.get('input[type="checkbox"]').first().as('firstSwitch');
cy.get('input[type="checkbox"]').eq(1).as('secondSwitch');
cy.get('input[type="checkbox"][disabled]').first().as('disabledSwitch');

cy.get('@firstSwitch').should('not.be.checked');
cy.get('@firstSwitch').click();
cy.get('@firstSwitch').should('be.checked');

cy.get('@secondSwitch').should('not.be.checked');
cy.get('@secondSwitch').click();
cy.get('@secondSwitch').should('be.checked');

cy.get('@disabledSwitch').should('be.disabled');
});
});
1 change: 1 addition & 0 deletions example/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const navigation = ref([
{ to: { name: 'buttons' }, title: 'Buttons' },
{ to: { name: 'icons' }, title: 'Icons' },
{ to: { name: 'checkboxes' }, title: 'Checkboxes' },
{ to: { name: 'switches' }, title: 'Switches' },
{ to: { name: 'radios' }, title: 'Radio' },
{ to: { name: 'text-fields' }, title: 'Text Fields' },
{ to: { name: 'text-areas' }, title: 'Text Areas' },
Expand Down
6 changes: 6 additions & 0 deletions example/src/router/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable import/max-dependencies */
import { createRouter, createWebHistory } from 'vue-router';
import ButtonView from '@/views/ButtonView.vue';

Expand Down Expand Up @@ -31,6 +32,11 @@ const router = createRouter({
name: 'checkboxes',
component: () => import('@/views/CheckboxView.vue'),
},
{
path: '/switches',
name: 'switches',
component: () => import('@/views/SwitchView.vue'),
},
{
path: '/radios',
name: 'radios',
Expand Down
115 changes: 115 additions & 0 deletions example/src/views/SwitchView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<script lang="ts" setup>
import { RuiSwitch, type SwitchProps } from '@rotki/ui-library';
import { ref } from 'vue';
type SwitchData = SwitchProps & {
value?: boolean;
};
const switches = ref<SwitchData[]>([
{ value: false, color: 'primary' },
{ value: false, color: 'secondary' },
{ value: false, color: 'error' },
{ value: false, color: 'warning' },
{ value: false, color: 'info' },
{ value: false, color: 'success' },
{ value: true, color: 'primary' },
{ value: true, color: 'secondary' },
{ value: true, color: 'error' },
{ value: true, color: 'warning' },
{ value: true, color: 'info' },
{ value: true, color: 'success' },
{ value: false, color: 'primary', size: 'sm' },
{ value: false, color: 'secondary', size: 'sm' },
{ value: false, color: 'error', size: 'sm' },
{ value: false, color: 'warning', size: 'sm' },
{ value: false, color: 'info', size: 'sm' },
{ value: false, color: 'success', size: 'sm' },
{ value: false, color: 'primary', disabled: true },
{ value: false, color: 'secondary', disabled: true },
{ value: false, color: 'error', disabled: true },
{ value: false, color: 'warning', disabled: true },
{ value: false, color: 'info', disabled: true },
{ value: false, color: 'success', disabled: true },
{ value: true, color: 'primary', disabled: true },
{ value: true, color: 'secondary', disabled: true },
{ value: true, color: 'error', disabled: true },
{ value: true, color: 'warning', disabled: true },
{ value: true, color: 'info', disabled: true },
{ value: true, color: 'success', disabled: true },
{ value: false, color: 'primary', hint: 'Switch hint' },
{ value: false, color: 'secondary', hint: 'Switch hint' },
{ value: false, color: 'error', hint: 'Switch hint' },
{ value: false, color: 'warning', hint: 'Switch hint' },
{ value: false, color: 'info', hint: 'Switch hint' },
{ value: false, color: 'success', hint: 'Switch hint' },
{ value: false, color: 'primary', errorMessages: ['Switch error message'] },
{
value: false,
color: 'secondary',
errorMessages: 'Switch error message',
},
{ value: false, color: 'error', errorMessages: ['Switch error message'] },
{ value: false, color: 'warning', errorMessages: 'Switch error message' },
{ value: false, color: 'info', errorMessages: ['Switch error message'] },
{ value: false, color: 'success', errorMessages: 'Switch error message' },
{
value: false,
color: 'primary',
successMessages: ['Switch success message'],
},
{
value: false,
color: 'secondary',
successMessages: 'Switch success message',
},
{
value: false,
color: 'error',
successMessages: ['Switch success message'],
},
{
value: false,
color: 'warning',
successMessages: 'Switch success message',
},
{
value: false,
color: 'info',
successMessages: ['Switch success message'],
},
{
value: false,
color: 'success',
successMessages: 'Switch success message',
},
]);
</script>

<template>
<div>
<h2
class="text-h4 mb-6"
data-cy="switches"
>
Switches
</h2>
<div class="grid gap-3 grid-rows-2 grid-cols-6">
<RuiSwitch
v-for="(sw, i) in switches"
:key="i"
v-model="sw.value"
v-bind="sw"
>
<span class="capitalize"> {{ sw.color }} </span>
</RuiSwitch>
</div>
</div>
</template>
8 changes: 8 additions & 0 deletions src/components/forms/checkbox/Checkbox.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const meta: Meta<PropsAndLabel> = {
label: { control: 'text' },
modelValue: { control: 'boolean' },
size: { control: 'select', options: ['medium', 'sm', 'lg'] },
successMessages: { control: 'array', defaultValue: [] },
},
component: Checkbox,
parameters: {
Expand Down Expand Up @@ -106,6 +107,13 @@ export const WithErrorMessage: Story = {
},
};

export const WithSuccessMessage: Story = {
args: {
label: 'Label',
successMessages: ['With success messages'],
},
};

export const WithHint: Story = {
args: {
hint: 'With hint',
Expand Down
83 changes: 83 additions & 0 deletions src/components/forms/switch/Switch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { describe, expect, it } from 'vitest';
import { type ComponentMountingOptions, mount } from '@vue/test-utils';
import Switch from '@/components/forms/switch/Switch.vue';

function createWrapper(options?: ComponentMountingOptions<typeof Switch>) {
return mount(Switch, { ...options });
}

describe('forms/Switch', () => {
it('renders properly', () => {
const label = 'Switch Label';
const wrapper = createWrapper({
slots: {
default: () => label,
},
});
expect(wrapper.text()).toContain(label);
expect(wrapper.get('label > div > div').classes()).toMatch(/_toggle_/);
});

it('passes disabled props', async () => {
const wrapper = createWrapper();
expect(wrapper.find('input').attributes('disabled')).toBeUndefined();
expect(wrapper.get('label').classes()).not.toMatch(/_disabled_/);
await wrapper.setProps({ disabled: true });
expect(wrapper.find('input').attributes('disabled')).toBeDefined();
expect(wrapper.get('label').classes()).toMatch(/_disabled_/);
await wrapper.setProps({ disabled: false });
expect(wrapper.find('input').attributes('disabled')).toBeUndefined();
expect(wrapper.get('label').classes()).not.toMatch(/_disabled_/);
});

it('passes color props', async () => {
const wrapper = createWrapper({ props: { color: 'primary' } });
expect(wrapper.find('label').classes()).toMatch(/_primary_/);

await wrapper.setProps({ color: 'secondary' });
expect(wrapper.find('label').classes()).toMatch(/_secondary_/);

await wrapper.setProps({ color: 'error' });
expect(wrapper.find('label').classes()).toMatch(/_error_/);

await wrapper.setProps({ color: 'success' });
expect(wrapper.find('label').classes()).toMatch(/_success_/);
});

it('passes size props', async () => {
const wrapper = createWrapper({ props: { size: 'sm' } });
expect(wrapper.find('label').classes()).toMatch(/_sm_/);
});

it('passes hint props', async () => {
const wrapper = createWrapper();
expect(wrapper.find('.details > div').exists()).toBeFalsy();

const hint = 'Switch Hints';
await wrapper.setProps({ hint });
expect(wrapper.find('.details > div').classes()).toMatch(
/text-rui-text-secondary/,
);
expect(wrapper.find('.details > div').text()).toBe(hint);
});

it('passes hint errorMessages', async () => {
const wrapper = createWrapper();
expect(wrapper.find('.details > div').exists()).toBeFalsy();

const errorMessage = 'Switch Error Message';
await wrapper.setProps({ errorMessages: [errorMessage] });
expect(wrapper.find('.details > div').classes()).toMatch(/text-rui-error/);
expect(wrapper.find('.details > div').text()).toBe(errorMessage);
});

it('passes hideDetails', () => {
const wrapper = createWrapper({
props: {
hideDetails: true,
hint: 'This hint should not be rendered',
},
});
expect(wrapper.find('.details > div').exists()).toBeFalsy();
});
});
112 changes: 112 additions & 0 deletions src/components/forms/switch/Switch.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { contextColors } from '@/consts/colors';
import { type Props, default as Switch } from './Switch.vue';
import type { Meta, StoryFn, StoryObj } from '@storybook/vue3';

type PropsAndLabel = Props & { label: string };

const render: StoryFn<PropsAndLabel> = args => ({
components: { Switch },
setup() {
const modelValue = computed({
get() {
return args.modelValue;
},
set(val) {
args.modelValue = val;
},
});

return { args, modelValue };
},
template: `<Switch v-bind="args" v-model="modelValue">
{{ args.label }}
</Switch>`,
});

const meta: Meta<PropsAndLabel> = {
argTypes: {
color: { control: 'select', options: contextColors },
disabled: { control: 'boolean', table: { category: 'State' } },
errorMessages: { control: 'array', defaultValue: [] },
hideDetails: { control: 'boolean' },
hint: { control: 'text' },
label: { control: 'text' },
modelValue: { control: 'boolean' },
size: { control: 'select', options: ['medium', 'sm'] },
successMessages: { control: 'array', defaultValue: [] },
},
component: Switch,
parameters: {
docs: {
controls: { exclude: ['default'] },
},
},
render,
tags: ['autodocs'],
title: 'Components/Forms/Switch',
};

type Story = StoryObj<PropsAndLabel>;

export const Checked: Story = {
args: {
modelValue: true,
},
};

export const Small: Story = {
args: {
label: 'asdfa',
size: 'sm',
},
};

export const Primary: Story = {
args: {
color: 'primary',
},
};

export const WithLabel: Story = {
args: {
label: 'With Label',
},
};

export const Disabled: Story = {
args: {
disabled: true,
label: 'Disabled',
},
};

export const WithErrorMessage: Story = {
args: {
errorMessages: ['With error messages'],
label: 'Label',
},
};

export const WithSuccessMessage: Story = {
args: {
label: 'Label',
successMessages: ['With success messages'],
},
};

export const WithHint: Story = {
args: {
hint: 'With hint',
label: 'Label',
},
};

export const HideDetails: Story = {
args: {
hideDetails: true,
hint: 'Hint (should be invisible)',
label: 'Label',
},
};

export default meta;
Loading

0 comments on commit 8e33e98

Please sign in to comment.