Skip to content

Commit

Permalink
feat: adds the switch
Browse files Browse the repository at this point in the history
  • Loading branch information
gokh4nozturk authored and Gökhan Öztürk committed Aug 23, 2022
1 parent 9532615 commit 2d94f4f
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 0 deletions.
133 changes: 133 additions & 0 deletions src/components/Switch/Switch.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';

import Switch from './Switch.vue';

<Meta
title="Components/Switch"
component={Switch}
argTypes={{
id: {
control: {
type: 'text',
},
},
value: {
control: {
type: 'boolean',
},
},
size: {
control: {
type: 'select',
options: ['small', 'medium', 'large'],
},
},
disabled: {
control: {
type: 'boolean',
},
},
onChange: {
control: {
type: 'action',
},
},
}}
/>

export const Default = (args) => {
return {
components: { Switch },
setup() {
return {
args,
};
},
template: '<Switch v-bind="args" />',
};
};

# All the sizes

<hr />

<Canvas>
<Story
name="Small"
argTypes={{ ...Default.argTypes }}
args={{
value: false,
size: 'small',
}}
>
{Default.bind({})}
</Story>
<Story
name="Medium"
argTypes={{ ...Default.argTypes }}
args={{
value: false,
size: 'medium',
}}
>
{Default.bind({})}
</Story>
<Story
name="Large"
argTypes={{ ...Default.argTypes }}
args={{
value: false,
size: 'large',
}}
>
{Default.bind({})}
</Story>
</Canvas>

# Reference

<hr />

<ArgsTable props={Default.argTypes} />

# Notes

<hr />
<p>
This component is a wrapper around the <code>input</code> element.
</p>
<p>
The <code>value</code> prop is passed to the <code>input</code> element.
</p>
<p>
The <code>disabled</code> prop is passed to the <code>input</code> element.
</p>
<p>
The <code>size</code> prop is passed to the <code>input</code> element.
</p>

# Installation

<hr />

<p>Install the component with:</p>

```js
import { Switch } from '@/components/Switch';
```

# Examples

<hr />

<h6>Basic using</h6>

```html
<Switch :value="false" />
```

<h6>Advance using</h6>

```html
<Switch :value="false" size="large" :disabled="false" />
```
39 changes: 39 additions & 0 deletions src/components/Switch/Switch.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script setup lang="ts">
import { computed, reactive } from 'vue';
import './switch.css';
export interface Props {
id?: string;
value: boolean;
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
}
const props = defineProps<Props>();
const emit = defineEmits(['change']);
const state = reactive({
value: props.value || false,
});
const classes = computed(() => {
return {
switch: true,
'switch--disabled': props.disabled,
[`switch--${props.size || 'medium'}`]: props.size,
};
});
const onChange = () => {
emit('change', { value: state.value });
};
</script>
<template>
<fieldset>
<label :for="props.id || 'switch'" :class="classes">
<input
:id="props.id || 'switch'"
type="checkbox"
:disabled="props.disabled"
v-model="state.value"
@change="onChange"
/>
<span class="slider round" />
</label>
</fieldset>
</template>
56 changes: 56 additions & 0 deletions src/components/Switch/switch.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
@tailwind components;

fieldset {
@apply font-sans border-none m-0 p-0;
}
.switch {
@apply relative inline-block w-16 h-9;
}
.switch--small {
@apply w-12 h-6;
}
.switch--large {
@apply w-20 h-11;
}
.switch input {
@apply opacity-0 w-0 h-0 -z-10;
}
.slider {
@apply absolute top-0 left-0 right-0 bottom-0 bg-gray-300 transition duration-200 ease-in-out;
}
.switch--small > .slider::before {
@apply w-4 h-4;
}
.switch--large > .slider::before {
@apply w-9 h-9;
}
.slider:before {
content: '';
@apply absolute h-7 w-7 left-1 bottom-1 bg-white transition duration-200 ease-in-out;
}
input:checked + .slider {
@apply bg-teal-400;
}
input:focus + .slider {
box-shadow: 0 0 1px;
@apply shadow-teal-400;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
.switch--large > input:checked + .slider:before {
-webkit-transform: translateX(34px);
-ms-transform: translateX(34px);
transform: translateX(34px);
}
.slider.round {
@apply rounded-full;
}
.slider.round:before {
@apply rounded-full;
}
.switch--disabled {
@apply opacity-50 cursor-not-allowed;
}
23 changes: 23 additions & 0 deletions src/components/Switch/switch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { describe, it, expect } from 'vitest';

import { mount } from '@vue/test-utils';
import Switch from './Switch.vue';

describe('Switch', () => {
it('renders properly', () => {
const wrapper = mount(Switch, {
props: {
id: 'switch-id',
disabled: false,
size: 'small',
},
});
expect(wrapper.exists()).toBe(true);
expect(wrapper.find('label').exists()).toBe(true);
expect(wrapper.find('label').element.getAttribute('for')).toBe('switch-id');
expect(wrapper.find('input').exists()).toBe(true);
expect(wrapper.find('input').element.getAttribute('id')).toBe('switch-id');
expect(wrapper.find('input').element.getAttribute('type')).toBe('checkbox');
expect(wrapper.find('input').element.getAttribute('disabled')).toBe(null);
});
});

0 comments on commit 2d94f4f

Please # to comment.