Merge branch 'button-group' into 'main'
Button group See merge request cellule-financiere-pmo/design-system/visua-vue!2
This commit is contained in:
commit
9a52d13e5e
|
@ -1,8 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
import VButtonView from '../template/VButtonView.vue'
|
||||
// import VButtonView from '../template/VButtonView.vue'
|
||||
import VButtonGroupView from '../template/VButtonGroupView.vue';
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<VButtonView/>
|
||||
<!-- <VButtonView/> -->
|
||||
<VButtonGroupView/>
|
||||
</template>
|
||||
|
|
103
src/components/button/VButtonGroup.vue
Normal file
103
src/components/button/VButtonGroup.vue
Normal file
|
@ -0,0 +1,103 @@
|
|||
<script setup lang="ts">
|
||||
import VButton from './VButton.vue';
|
||||
import type IVButtonGroup from './IVButton.type.ts';
|
||||
import { computed, ref, onMounted } from 'vue';
|
||||
|
||||
const props = withDefaults(defineProps<IVButtonGroup>(), {
|
||||
buttons: () => [],
|
||||
inlineLayoutWhen: 'never',
|
||||
size: 'md',
|
||||
align: undefined,
|
||||
iconRight: false,
|
||||
})
|
||||
|
||||
// Reference to <ul> element containing buttons
|
||||
const buttonsEl = ref<HTMLUListElement | null>(null)
|
||||
|
||||
// Determines whether the layout should be in line
|
||||
const inlineAlways = computed(() => ['always', '', true].includes(props.inlineLayoutWhen))
|
||||
const inlineSm = computed(() => ['sm', 'small'].includes(props.inlineLayoutWhen as string))
|
||||
const inlineMd = computed(() => ['md', 'medium'].includes(props.inlineLayoutWhen as string))
|
||||
const inlineLg = computed(() => ['lg', 'large'].includes(props.inlineLayoutWhen as string))
|
||||
|
||||
// Horizontal alignment of button group
|
||||
const center = computed(() => props.align === 'center')
|
||||
const right = computed(() => props.align === 'right')
|
||||
|
||||
// Uniform width for all buttons
|
||||
const equisizedWidth = ref('auto')
|
||||
const groupStyle = computed(() => `--equisized-width: ${equisizedWidth.value};`)
|
||||
|
||||
// Function for calculating maximum button width and applying it to all buttons
|
||||
const computeEquisizedWidth = async () => {
|
||||
let maxWidth = 0
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
buttonsEl.value?.querySelectorAll('.p-button').forEach((btn: Element) => {
|
||||
const button = btn as HTMLButtonElement
|
||||
const width = button.offsetWidth
|
||||
const buttonStyle = window.getComputedStyle(button)
|
||||
const marginLeft = +buttonStyle.marginLeft.replace('px', '')
|
||||
const marginRight = +buttonStyle.marginRight.replace('px', '')
|
||||
button.style.width = 'var(--equisized-width)'
|
||||
const newWidth = width + marginLeft + marginRight
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
})
|
||||
equisizedWidth.value = `${maxWidth}px`
|
||||
}
|
||||
|
||||
// Calculation of uniform installation width if necessary
|
||||
onMounted(async () => {
|
||||
if (!buttonsEl.value || !props.equisized) {
|
||||
return
|
||||
}
|
||||
await computeEquisizedWidth()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul
|
||||
ref="buttonsEl"
|
||||
role="list"
|
||||
:style="groupStyle"
|
||||
class="btns-group"
|
||||
:class="{
|
||||
'btns-group--equisized': equisized,
|
||||
'btns-group--inline': inlineAlways || inlineSm || inlineMd || inlineLg,
|
||||
'btns-group--center': center,
|
||||
'btns-group--right': right,
|
||||
'btns-group--inline-reverse': reverse,
|
||||
}"
|
||||
>
|
||||
<li v-for="({onClick, title, ...button}, i) in buttons" :key="i" role="listitem">
|
||||
<VButton
|
||||
v-bind="button"
|
||||
@click="onClick"
|
||||
:size="props.size"
|
||||
:icon-right="props.iconRight"
|
||||
:title="title"
|
||||
/>
|
||||
</li>
|
||||
<slot/>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.btns-group{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
list-style: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.btns-group--equisized{width: var(--equisized-width, auto);}
|
||||
.btns-group--inline{flex-direction: row;}
|
||||
.btns-group--center{justify-content: center;}
|
||||
.btns-group--right{justify-content: flex-end;}
|
||||
.btns-group--inline-reverse{flex-direction: row-reverse;}
|
||||
|
||||
</style>
|
|
@ -93,6 +93,21 @@ describe('VButton', () => {
|
|||
expect(onClick).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('Clicked button', async () => {
|
||||
const onClick = vi.fn()
|
||||
const wrapper = mount(VButton, {
|
||||
props: {
|
||||
title: 'button',
|
||||
label: 'label',
|
||||
onClick,
|
||||
}
|
||||
})
|
||||
const button = wrapper.find('button')
|
||||
await button.trigger('click')
|
||||
// check that the onClck function has been called
|
||||
expect(onClick).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('small button', () => {
|
||||
const wrapper = mount(VButton, {
|
||||
props: {
|
||||
|
|
46
test/VButtonGroup.spec.ts
Normal file
46
test/VButtonGroup.spec.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { mount } from "@vue/test-utils";
|
||||
import VButtonGroup from '../src/components/button/VButtonGroup.vue';
|
||||
import {test, expect, describe, vi} from 'vitest';
|
||||
|
||||
const buttons = [
|
||||
{title: 'button 1', label: 'label 1', onClick: vi.fn()},
|
||||
{title: 'button 2', label: 'label 2', onClick: vi.fn()}
|
||||
]
|
||||
|
||||
describe('VButtonGroup', () => {
|
||||
test('Displays all buttons passed as props', () => {
|
||||
const wrapper = mount(VButtonGroup, {
|
||||
props: {
|
||||
buttons,
|
||||
title: 'button group',
|
||||
}
|
||||
})
|
||||
const buttonElements = wrapper.findAll('button')
|
||||
expect(buttonElements).toHaveLength(2)
|
||||
expect(buttonElements[0].text()).toContain('label 1')
|
||||
expect(buttonElements[1].text()).toContain('label 2')
|
||||
})
|
||||
|
||||
test('applies the inline class when inlineLayoutWhen is “always”', () => {
|
||||
const wrapper = mount(VButtonGroup, {
|
||||
props: {
|
||||
buttons,
|
||||
title: 'button group',
|
||||
inlineLayoutWhen: 'always',
|
||||
}
|
||||
})
|
||||
expect(wrapper.classes()).toContain('btns-group--inline')
|
||||
})
|
||||
|
||||
test('clicked button', async () => {
|
||||
const onClick = vi.fn()
|
||||
const wrapper = mount(VButtonGroup, {
|
||||
props: {
|
||||
buttons: [{title: 'button 1', label: 'label 1', onClick}],
|
||||
title: 'button group',
|
||||
}
|
||||
})
|
||||
await wrapper.find('button').trigger('click')
|
||||
expect(onClick).toHaveBeenCalled()
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user