Skip to content

Commit fa1d52a

Browse files
authored
Merge pull request #84 from laruiss/feat/dsfr-button-group
feat: ✨ Implémente DsfrButtonGroup
2 parents 21e7912 + 6ceee48 commit fa1d52a

File tree

8 files changed

+726
-31
lines changed

8 files changed

+726
-31
lines changed

src/assets/variables-dsfr.css

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,15 @@
3232
--error: #e10600;
3333
--rm300: #f7bfc3;
3434
--rm500: #e1000f;
35-
--scroll-shadow: rgba(30,30,30,0.16);
36-
--overlay: hsla(0,0%,61.2%,0.32);
35+
--scroll-shadow: rgba(30, 30, 30, 0.16);
36+
--overlay: rgba(156, 156, 156, 0.32);
3737

3838
--color-hover: rgba(0, 0, 221, 0.5);
3939
--color-active: rgba(41, 41, 255, 0.5);
4040

4141
--block-color-hover: hsla(0,0%,87.8%,0.5);
4242
--block-color-active: hsla(0,0%,76.1%,0.5);
4343

44-
4544
--in-content-page-background: rgb(28,27,34);
4645
--in-content-page-color: rgb(251,251,254);
4746
--in-content-deemphasized-text: rgb(191,191,201);
@@ -78,6 +77,23 @@
7877
--dialog-warning-text-color: var(--red-40);
7978
--is-link: '';
8079
scrollbar-color: rgba(249,249,250,.4) rgba(20,20,25,.3);
80+
--ul-type: none;
81+
--ol-type: none;
82+
83+
--link-underline: 0 1px 0 0 currentColor;
84+
--link-blank-font: normal normal normal 1rem/1 dsfr-icons;
85+
--link-blank-content: "\00a0";
86+
--ul-type: "●\00a0\00a0";
87+
--ol-type: decimal;
88+
--ul-start: 1rem;
89+
--ol-start: 1.5rem;
90+
--xl-block: 0.5rem;
91+
--li-bottom: 0.25rem;
92+
--xl-base: 1em;
93+
--ol-content: counters(li-counter, ".") ".\00a0\00a0";
94+
95+
--block-color-hover: rgba(224, 224, 224, 0.5);
96+
--block-color-active: rgba(194, 194, 194, 0.5);
8197
}
8298

8399
:host, :root {

src/components/DsfrButton/DsfrButton.stories.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ export const SuiteDeBoutons = (args) => ({
157157
</div>
158158
`,
159159
})
160+
160161
SuiteDeBoutons.args = {
161162
dark: false,
162163
label: 'Texte du bouton',

src/components/DsfrButton/DsfrButton.vue

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
:class="{
44
'fr-btn': true,
55
'fr-btn--secondary': secondary,
6-
'flex': true,
6+
'inline-flex': true,
77
'reverse': iconRight,
88
}"
99
:disabled="disabled"
@@ -44,41 +44,18 @@ export default {
4444
}
4545
</script>
4646

47-
<style scoped>
48-
.fr-btn {
49-
border: 0;
50-
padding: 0.5rem 1.5rem;
51-
font-size: 1rem;
52-
background-color: var(--bf500);
53-
color: var(--w-bf500);
54-
55-
&[disabled] {
56-
color: var(--g600-g400);
57-
background-color: var(--g200);
58-
}
59-
}
60-
.fr-btn--secondary {
61-
--color-hover: var(--block-color-hover);
62-
--color-active: var(--block-color-active);
47+
<style src="./button.css" />
6348

64-
background-color: var(--t-plain);
65-
color: var(--bf500);
66-
box-shadow: inset 0 0 0 1px var(--bf500);
67-
68-
&[disabled] {
69-
color: var(--g600-g400);
70-
box-shadow: inset 0 0 0 1px var(--g400);
71-
}
72-
}
49+
<style scoped>
7350
.icon-left {
7451
margin-right: 0.5rem
7552
}
7653
.icon-right {
7754
margin-left: 0.5rem
7855
}
7956
80-
.flex {
81-
display: flex;
57+
.inline-flex {
58+
display: inline-flex;
8259
align-items: center;
8360
}
8461
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { fireEvent } from '@testing-library/dom'
2+
import { render } from '@testing-library/vue'
3+
4+
import DsfrButtonGroup from './DsfrButtonGroup.vue'
5+
6+
const VIcon = { props: ['name'], template: '<i :class="name"></i>' }
7+
8+
describe('DsfrButtonGroup', () => {
9+
it('should mount DsfrButtonGroup with right content', async () => {
10+
// Given
11+
const labelPrimary = 'Button primary'
12+
const labelSecondary = 'Button secondary'
13+
const onClickFirst = jest.fn()
14+
const onClickSecond = jest.fn()
15+
const buttonsProps = [
16+
{
17+
label: labelPrimary,
18+
onclick: onClickFirst,
19+
},
20+
{
21+
label: labelSecondary,
22+
secondary: true,
23+
onclick: onClickSecond,
24+
},
25+
]
26+
27+
// When
28+
const { getByText, getByTestId } = render(DsfrButtonGroup, {
29+
global: {
30+
components: {
31+
VIcon,
32+
},
33+
},
34+
props: {
35+
class: 'extra-class',
36+
buttons: buttonsProps,
37+
},
38+
})
39+
40+
const wrapper = getByTestId('fr-btns')
41+
const firstButtonSpan = getByText(labelPrimary)
42+
const secondButtonSpan = getByText(labelSecondary)
43+
44+
await fireEvent.click(firstButtonSpan)
45+
await fireEvent.click(secondButtonSpan)
46+
47+
// Then
48+
expect(firstButtonSpan.parentNode).not.toHaveClass('fr-btn--secondary')
49+
expect(secondButtonSpan.parentNode).toHaveClass('fr-btn--secondary')
50+
expect(wrapper).toHaveClass('extra-class')
51+
expect(onClickFirst).toHaveBeenCalled()
52+
expect(onClickSecond).toHaveBeenCalled()
53+
})
54+
})
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import OhVueIcon from 'oh-vue-icons/dist/v3/icon.es'
2+
3+
import { RiCheckboxCircleLine } from 'oh-vue-icons/icons'
4+
5+
import DsfrButtonGroup from './DsfrButtonGroup.vue'
6+
7+
OhVueIcon.add(RiCheckboxCircleLine)
8+
9+
export default {
10+
component: DsfrButtonGroup,
11+
title: 'Basic/Groupe de Boutons - ButtonGroup',
12+
argTypes: {
13+
dark: {
14+
control: 'boolean',
15+
description: 'Permet de voir le composant dans les deux **thèmes** : **clair** (`false`, défaut) et **sombre* (`true`).\n\n*N.B. : Ne fait pas partie du composant.*',
16+
},
17+
buttons: {
18+
control: 'object',
19+
description: 'Tableau d’objets, chaque objet contiendra les props à passer à DsfrButton',
20+
},
21+
inline: {
22+
control: 'boolean',
23+
description: 'Indique si le groupe de boutons doit apparaître en empilement horizontal',
24+
},
25+
reverse: {
26+
control: 'boolean',
27+
description: 'Indique si l’ordre des boutons doit être inversé par rapport au DOM.\n\n *N.B. : Ne fonctionne que si `align` est à `right`*',
28+
},
29+
size: {
30+
control: 'radio',
31+
options: ['default', 'small', 'medium', 'large'],
32+
description: 'Indique la taille du groupe de bouton',
33+
},
34+
align: {
35+
control: 'radio',
36+
options: ['default', 'center', 'right'],
37+
description: 'Indique l\'alignement du groupe de boutons',
38+
},
39+
onClick: { action: 'clicked' },
40+
},
41+
}
42+
43+
export const GroupeDeBoutons = (args) => ({
44+
components: {
45+
DsfrButtonGroup,
46+
VIcon: OhVueIcon,
47+
},
48+
data () {
49+
return {
50+
...args,
51+
buttons: args.buttons.map(btn => ({ ...btn, onClick: args.onClick })),
52+
}
53+
},
54+
template: `
55+
<div :data-rf-theme="dark ? 'dark' : ''" style="background-color: var(--w); padding: 1rem;">
56+
<div style="margin: 1rem 0;">
57+
<DsfrButtonGroup
58+
:buttons="buttons"
59+
:size="size"
60+
:align="align"
61+
:inline="inline"
62+
:reverse="reverse"
63+
/>
64+
</div>
65+
66+
</div>
67+
`,
68+
})
69+
GroupeDeBoutons.args = {
70+
dark: false,
71+
align: 'center',
72+
inline: false,
73+
reverse: false,
74+
size: undefined,
75+
buttons: [
76+
{
77+
label: 'Label 1',
78+
icon: 'ri-checkbox-circle-line',
79+
},
80+
{
81+
label: 'Label 2',
82+
secondary: true,
83+
icon: 'ri-checkbox-circle-line',
84+
},
85+
{
86+
label: 'Label 3',
87+
icon: 'ri-checkbox-circle-line',
88+
iconRight: true,
89+
},
90+
{
91+
label: 'Label 4',
92+
secondary: true,
93+
icon: 'ri-checkbox-circle-line',
94+
iconRight: true,
95+
},
96+
],
97+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<script>
2+
import { defineComponent } from 'vue'
3+
import DsfrButton from './DsfrButton.vue'
4+
5+
export default defineComponent({
6+
name: 'DsfrButtonGroup',
7+
components: {
8+
DsfrButton,
9+
},
10+
11+
props: {
12+
buttons: {
13+
type: Array,
14+
default: () => [],
15+
},
16+
inline: Boolean,
17+
size: {
18+
type: String,
19+
validator: (val) => ['sm', 'small', 'lg', 'large', 'md', 'medium', undefined].includes(val),
20+
default: undefined,
21+
},
22+
reverse: Boolean,
23+
align: {
24+
type: String,
25+
validator: (val) => ['right', 'center', undefined].includes(val),
26+
default: undefined,
27+
},
28+
},
29+
30+
computed: {
31+
sm () {
32+
return ['sm', 'small'].includes(this.size)
33+
},
34+
md () {
35+
return ['md', 'medium'].includes(this.size)
36+
},
37+
lg () {
38+
return ['lg', 'large'].includes(this.size)
39+
},
40+
center () {
41+
return this.align === 'center'
42+
},
43+
right () {
44+
return this.align === 'right'
45+
},
46+
},
47+
})
48+
</script>
49+
50+
<template>
51+
<ul
52+
class="fr-btns-group"
53+
:class="{
54+
'fr-btns-group--inline': inline,
55+
'fr-btns-group--inline-sm': sm,
56+
'fr-btns-group--inline-md': md,
57+
'fr-btns-group--inline-lg': lg,
58+
'fr-btns-group--center': center,
59+
'fr-btns-group--right': right,
60+
'fr-btns-group--inline-reverse': reverse,
61+
}"
62+
data-testid="fr-btns"
63+
>
64+
<li
65+
v-for="({ onClick, ...button }, i) in buttons"
66+
:key="i"
67+
>
68+
<DsfrButton
69+
v-bind="button"
70+
@click="onClick"
71+
/>
72+
</li>
73+
</ul>
74+
</template>
75+
76+
<style src="./buttons.css" />

0 commit comments

Comments
 (0)