diff --git a/README.md b/README.md
index 87c83b9..e329c20 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@ This repo provides a basic setup for developing component libraries in Vite with
- [x] LcInput
- [x] LcModal
- [x] LcPagination
+- [x] LcRadioGroup & LcRadio
- [x] LcTable
- [x] LcTooltip
diff --git a/src/components/LcForm.vue b/src/components/LcForm.vue
index 191a7f2..9ec60ac 100644
--- a/src/components/LcForm.vue
+++ b/src/components/LcForm.vue
@@ -33,6 +33,12 @@
v-model="field.model"
v-bind="field.attr"
/>
+
@@ -86,6 +92,7 @@ import LcMultiselect from './LcMultiselect/LcMultiselect.vue'
import LcCheckbox from './LcCheckbox.vue'
import LcInput from './LcInput.vue'
import LcTextarea from './LcTextarea'
+import { LcRadioGroup } from './LcRadio'
configure({
generateMessage: localize({
@@ -108,6 +115,7 @@ export default defineComponent({
LcInput,
LcMultiselect,
LcTextarea,
+ LcRadioGroup,
vForm,
},
props: {
diff --git a/src/components/LcRadio/LcRadio.d.ts b/src/components/LcRadio/LcRadio.d.ts
new file mode 100644
index 0000000..dd6e676
--- /dev/null
+++ b/src/components/LcRadio/LcRadio.d.ts
@@ -0,0 +1,3 @@
+import { DefineComponent } from 'vue'
+const component: DefineComponent<{}, {}, any>
+export default component
diff --git a/src/components/LcRadio/LcRadio.stories.ts b/src/components/LcRadio/LcRadio.stories.ts
new file mode 100644
index 0000000..d8314dd
--- /dev/null
+++ b/src/components/LcRadio/LcRadio.stories.ts
@@ -0,0 +1,34 @@
+import { action } from '@storybook/addon-actions'
+
+import LcRadio from './LcRadio.vue'
+
+export default {
+ title: 'Example/LcRadio',
+ component: LcRadio,
+}
+
+const Template = (args: any) => ({
+ components: { LcRadio },
+ setup() {
+ return { args }
+ },
+ template: '',
+ methods: {
+ onChange: action('onChange'),
+ },
+})
+
+export const Base = Template.bind({}) as any
+Base.args = {
+ label: 'Monsieur',
+ modelValue: '',
+ name: 'civility',
+ value: 'mr',
+ vertical: false,
+}
+
+export const Disabled = Template.bind({}) as any
+Disabled.args = {
+ ...Base.args,
+ disabled: true,
+}
diff --git a/src/components/LcRadio/LcRadio.vue b/src/components/LcRadio/LcRadio.vue
new file mode 100644
index 0000000..8aeeb93
--- /dev/null
+++ b/src/components/LcRadio/LcRadio.vue
@@ -0,0 +1,107 @@
+
+
+
+
+
+
diff --git a/src/components/LcRadio/LcRadioGroup.stories.ts b/src/components/LcRadio/LcRadioGroup.stories.ts
new file mode 100644
index 0000000..0f8b963
--- /dev/null
+++ b/src/components/LcRadio/LcRadioGroup.stories.ts
@@ -0,0 +1,79 @@
+import { action } from '@storybook/addon-actions'
+
+import LcRadio from './LcRadio.vue'
+import LcRadioGroup from './LcRadioGroup.vue'
+
+export default {
+ title: 'Example/LcRadioGroup',
+ component: LcRadioGroup,
+ subcomponents: { LcRadio },
+}
+
+const Template = (args: any) => ({
+ components: { LcRadioGroup, LcRadio },
+ setup() {
+ return { args }
+ },
+ template: '',
+ methods: {
+ onChange: action('onChange'),
+ },
+})
+
+export const Base = Template.bind({}) as any
+Base.args = {
+ modelValue: '',
+ name: 'civility',
+ options: [
+ {
+ label: 'Monsieur',
+ value: 'mr',
+ },
+ {
+ label: 'Madame',
+ value: 'ms',
+ },
+ {
+ label: 'Non binaire',
+ value: 'unknown',
+ },
+ ],
+}
+
+export const WithLabel = Template.bind({}) as any
+WithLabel.args = {
+ ...Base.args,
+ label: 'Choisir votre civilité :',
+}
+
+export const Vertical = Template.bind({}) as any
+Vertical.args = {
+ ...Base.args,
+ vertical: true,
+}
+
+export const VerticalWithLabel = Template.bind({}) as any
+VerticalWithLabel.args = {
+ ...WithLabel.args,
+ vertical: true,
+}
+
+export const Disabled = Template.bind({}) as any
+Disabled.args = {
+ ...WithLabel.args,
+ options: [
+ {
+ label: 'Monsieur',
+ value: 'mr',
+ disabled: true,
+ },
+ {
+ label: 'Madame',
+ value: 'ms',
+ },
+ {
+ label: 'Non binaire',
+ value: 'unknown',
+ },
+ ],
+}
diff --git a/src/components/LcRadio/LcRadioGroup.vue b/src/components/LcRadio/LcRadioGroup.vue
new file mode 100644
index 0000000..46eb8e3
--- /dev/null
+++ b/src/components/LcRadio/LcRadioGroup.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/LcRadio/__tests__/LcRadio.spec.ts b/src/components/LcRadio/__tests__/LcRadio.spec.ts
new file mode 100644
index 0000000..60a2217
--- /dev/null
+++ b/src/components/LcRadio/__tests__/LcRadio.spec.ts
@@ -0,0 +1,58 @@
+import { mount } from '@vue/test-utils'
+import { LcRadio } from '../index'
+
+let wrapper: any
+
+beforeEach(() => {
+ wrapper = mount(LcRadio, {
+ props: {
+ name: 'civility',
+ value: 'mr',
+ vertical: false,
+ },
+ })
+})
+
+afterEach(() => {
+ wrapper?.unmount()
+})
+
+describe('LcRadio', () => {
+ it('is a Vue instance', () => {
+ expect(wrapper.vm).toBeTruthy()
+ })
+
+ describe('Input behaviour', () => {
+ it('should emit the event', () => {
+ const radioButton = wrapper.find('.lc-radio')
+ radioButton.trigger('change')
+
+ expect(wrapper.emitted()).toHaveProperty('update:modelValue')
+ })
+
+ it('should emit the right value', () => {
+ const radioButton = wrapper.find('.lc-radio')
+ radioButton.trigger('change')
+
+ const changeEvent = wrapper.emitted('update:modelValue')
+
+ expect(changeEvent[0]).toEqual(['mr'])
+ })
+
+ it('should disabled the radio button', async() => {
+ await wrapper.setProps({ disabled: true })
+ const radioButton = wrapper.find('.lc-radio')
+
+ expect(radioButton.attributes()).toHaveProperty('disabled')
+ })
+ })
+
+ describe('Input style', () => {
+ it('should set style for vertical layout', async() => {
+ await wrapper.setProps({ vertical: true })
+ const label = wrapper.find('.lc-radio-label')
+
+ expect(label.classes()).toContain('lc-radio-label--vertical')
+ })
+ })
+})
diff --git a/src/components/LcRadio/__tests__/LcRadioGroup.spec.ts b/src/components/LcRadio/__tests__/LcRadioGroup.spec.ts
new file mode 100644
index 0000000..aaa27a5
--- /dev/null
+++ b/src/components/LcRadio/__tests__/LcRadioGroup.spec.ts
@@ -0,0 +1,74 @@
+import { mount } from '@vue/test-utils'
+import { LcRadioGroup } from '../index'
+
+let wrapper: any
+
+beforeEach(() => {
+ wrapper = mount(LcRadioGroup, {
+ props: {
+ options: [
+ { label: 'Monsieur', value: 'mr' },
+ { label: 'Madame', value: 'ms' },
+ { label: 'Non spécifié', value: 'unspecified' },
+ ],
+ name: 'civility',
+ modelValue: '',
+ },
+ })
+})
+
+afterEach(() => {
+ wrapper?.unmount()
+})
+
+describe('LcRadioGroup', () => {
+ it('is a Vue instance', () => {
+ expect(wrapper.vm).toBeTruthy()
+ })
+
+ describe('Input behaviour', () => {
+ it('should render good number of radio-buttons', () => {
+ const radiosButtons = wrapper.findAll('.lc-radio')
+
+ expect(radiosButtons).toHaveLength(3)
+ })
+
+ it('should set good name attribute', () => {
+ const radiosButtons = wrapper.findAll('.lc-radio')
+
+ expect(radiosButtons[1].attributes('value')).toEqual('ms')
+ })
+
+ it('should emit the event', () => {
+ const radioButton = wrapper.find('.lc-radio')
+ radioButton.trigger('change')
+
+ expect(wrapper.emitted()).toHaveProperty('update:modelValue')
+ })
+
+ it('should emit the right value', () => {
+ const radioButton = wrapper.find('.lc-radio')
+ radioButton.trigger('change')
+
+ const changeEvent = wrapper.emitted('update:modelValue')
+
+ expect(changeEvent[0]).toEqual(['mr'])
+ })
+ })
+
+ describe('Input layout', () => {
+ it('it should render the right label', async() => {
+ await wrapper.setProps({ label: 'Your civility :' })
+
+ const label = wrapper.find('.lc-radiogroup-label')
+ expect(label.text()).toEqual('Your civility :')
+ })
+
+ it('it should render vertical layout', async() => {
+ await wrapper.setProps({ vertical: true })
+
+ const wrapperBtn = wrapper.find('.lc-radiogroup-layout')
+ expect(wrapperBtn.classes()).toContain('lc-radiogroup-layout--vertical')
+ })
+ })
+})
diff --git a/src/components/LcRadio/index.ts b/src/components/LcRadio/index.ts
new file mode 100644
index 0000000..2a78255
--- /dev/null
+++ b/src/components/LcRadio/index.ts
@@ -0,0 +1,2 @@
+export { default as LcRadio } from './LcRadio.vue'
+export { default as LcRadioGroup } from './LcRadioGroup.vue'
diff --git a/src/library.ts b/src/library.ts
index 618093f..501fe4d 100644
--- a/src/library.ts
+++ b/src/library.ts
@@ -6,6 +6,7 @@ import LcInput from './components/LcInput.vue'
import LcModal from './components/LcModal.vue'
import LcMultiselect from './components/LcMultiselect'
import LcPagination from './components/LcPagination.vue'
+import { LcRadio, LcRadioGroup } from './components/LcRadio'
import LcTable from './components/LcTable.vue'
import LcTextarea from './components/LcTextarea'
import LcTooltip from './components/LcTooltip.vue'
@@ -19,6 +20,8 @@ export {
LcModal,
LcMultiselect,
LcPagination,
+ LcRadio,
+ LcRadioGroup,
LcTable,
LcTextarea,
LcTooltip,
diff --git a/src/stories/LcForm.stories.ts b/src/stories/LcForm.stories.ts
index 2e0a8e4..ff2b2ce 100644
--- a/src/stories/LcForm.stories.ts
+++ b/src/stories/LcForm.stories.ts
@@ -72,11 +72,27 @@ Base.args = {
model: false,
inputType: 'checkbox',
attr: {
+ class: 'mb-4',
label: 'Je souhaite recevoir occasionnellement une sélection d’expériences et de maisons.',
name: 'newsletter',
rules: 'required',
},
},
+ {
+ model: '',
+ inputType: 'radio',
+ attr: {
+ label: 'Votre civilité :',
+ name: 'civility',
+ rules: 'required',
+ vertical: false,
+ },
+ options: [
+ { label: 'Monsieur', value: 'mr' },
+ { label: 'Madame', value: 'ms' },
+ { label: 'Non spécifié', value: 'unspecified' },
+ ],
+ },
{
model: null,
inputType: 'select',