Skip to content

Commit

Permalink
Radio component
Browse files Browse the repository at this point in the history
  • Loading branch information
mdl95r committed Feb 25, 2024
1 parent 488dca4 commit 9b4b486
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 0 deletions.
94 changes: 94 additions & 0 deletions src/components/Radio/Radio.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
.ui-radio {
position: relative;
display: inline-flex;
align-items: center;
gap: var(--radio-gap-text, 8px);
cursor: pointer;
user-select: none;

--radio-size-width: 20px;
--radio-size-height: 20px;

&:focus {
&:after {
opacity: 1;
}
}

// .ui-radio__radio
&__radio {
opacity: 0;
position: absolute;
top: 0;
left: 0;
margin: 0;
z-index: 1;

&:disabled {

~.ui-radio__marker {
cursor: not-allowed;
background: var(--radio-disabled-marker-bg, #DCDCE4);

&:before {
border-color: var(--radio-disabled-marker-border, #C0C0CF);
}
}
}

&:checked~.ui-radio__marker {
&:before {
opacity: 1;
}
}

&:focus~.ui-radio__marker {
&:after {
opacity: 1;
}
}
}

// .ui-radio__marker
&__marker {
position: relative;
cursor: pointer;
width: var(--radio-size-width);
height: var(--radio-size-height);
z-index: 1;
flex-shrink: 0;
border-radius: var(--radio-border-radius, 4px);
border: 1px solid var(--radio-border-color, #C0C0CF);
border-radius: 100%;

&:after {
content: "";
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
z-index: -1;
border-width: 2px;
border-style: solid;
border-color: var(--radio-focus-bg, #4945FF);
border-radius: 100%;
opacity: 0;
transition: .2s ease-in;
}

&:before {
position: absolute;
content: "";
top: calc(50% - 6px);
left: calc(50% - 6px);
width: 12px;
height: 12px;
border-radius: 100%;
background: var(--checkbox-bg-color-checked-flag, #4945FF);
opacity: 0;
z-index: 1;
transition: .2s ease-in;
}
}
}
69 changes: 69 additions & 0 deletions src/components/Radio/Radio.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<template>
<label class="ui-radio" data-test-id="radio">
<input
type="radio"
:checked="isChecked"
:disabled="disabled"
:name="name"
:value="value"
class="ui-radio__radio"
@change="switchHandler"
>

<span class="ui-radio__marker"></span>

<div v-if="$slots.default" class="ui-radio__text">
<slot />
</div>
</label>
</template>

<script setup>
import { computed } from 'vue';
const props = defineProps({
modelValue: {
type: String,
default: null,
},
/**
* Делает radio неактивным
*/
disabled: {
type: Boolean,
default: false,
},
/**
* Добавить атрибут name
*/
name: {
type: String,
default: null,
},
/**
* Значение value
*/
value: {
type: [String, Number],
default: null,
}
})
const isChecked = computed(() => {
return props.modelValue === props.value;
});
const emit = defineEmits(['update:modelValue']);
const switchHandler = ($event) => {
emit('update:modelValue', $event.target.value);
}
</script>

<style lang="scss" scoped>
@use './Radio.scss';
</style>
1 change: 1 addition & 0 deletions src/components/Radio/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Radio.vue';
81 changes: 81 additions & 0 deletions stories/Radio.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import UiRadio from "../src/components/Radio";
import './assets/main.scss';

export default {
title: 'Elements/Radio',
component: UiRadio,
tags: ['autodocs'],
};

export const Base = {
render: (args) => ({
components: { UiRadio },
setup() {
return { args };
},
data() {
return {
checked: false,
};
},
template: `
<UiRadio v-bind="args" v-model="checked" />
`,
}),
};

export const WithText = {
render: (args) => ({
components: { UiRadio },
setup() {
return { args };
},
data() {
return {
checked: false,
};
},
template: `
<UiRadio v-bind="args" v-model="checked">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Velit molestias ex natus eligendi nulla tempora id illo a enim, sapiente officia corporis incidunt asperiores modi quibusdam ut et repellat neque! Lorem ipsum dolor sit, amet consectetur adipisicing elit. Velit molestias ex natus eligendi nulla tempora id illo a enim, sapiente officia corporis incidunt asperiores modi quibusdam ut et repellat neque!</UiRadio>
`,
}),
};

export const Disabled = {
args: {
disabled: true,
},

render: (args) => ({
components: { UiRadio },
setup() {
return { args };
},
data() {
return {
checked: false,
};
},
template: `
<UiRadio v-bind="args" v-model="checked" />
`,
}),
};

export const TwoRadio = {
render: (args) => ({
components: { UiRadio },
setup() {
return { args };
},
data() {
return {
gender: null,
};
},
template: `
<UiRadio v-bind="args" name="gender" v-model="gender" value="male" style="display: flex; margin-bottom: 10px;">male</UiRadio>
<UiRadio v-bind="args" name="gender" v-model="gender" value="female" style="display: flex;">female</UiRadio>
`,
}),
};
26 changes: 26 additions & 0 deletions tests/Radio.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@


import { mount } from '@vue/test-utils';
import Radio from '../src/components/Radio';

describe('Radio', () => {
it('Radio render', () => {
const wrapper = mount(Radio);

expect(wrapper.exists()).toBe(true);
})

it('Radio emitting update:modelValue', async () => {
const wrapper = mount(Radio, {
attachTo: document.body,
props: {
modelValue: false,
'update:modelValue': (e) => wrapper.setProps({ modelValue: e })
}
})

await wrapper.find('[data-test-id="radio"]').trigger('click');

expect(wrapper.emitted()).toHaveProperty('update:modelValue')
})
});

0 comments on commit 9b4b486

Please sign in to comment.