Skip to content

Commit 02e49ac

Browse files
authored
Merge pull request #467 from ambroisemaupate/fix/tabs
fix(DsfrTabs): Fixes #425 - Calc tabs-height CSS property
2 parents 514c6d9 + 6c0d13b commit 02e49ac

File tree

2 files changed

+150
-4
lines changed

2 files changed

+150
-4
lines changed

src/components/DsfrTabs/DsfrTabs.stories.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import DsfrTabContent from './DsfrTabContent.vue'
44
import { addIcons } from 'oh-vue-icons'
55

66
import { RiCheckboxCircleLine } from 'oh-vue-icons/icons/ri/index.js'
7+
import DsfrAccordionsGroup from '../DsfrAccordion/DsfrAccordionsGroup.vue'
8+
import DsfrAccordion from '../DsfrAccordion/DsfrAccordion.vue'
79

810
addIcons(RiCheckboxCircleLine)
911

@@ -160,3 +162,97 @@ OngletsComplexes.args = {
160162
selectedTabIndex: 1,
161163
initialSelectedIndex: 1,
162164
}
165+
166+
export const OngletsAvecAccordeon = (args) => ({
167+
components: { DsfrTabs, DsfrTabContent, DsfrAccordionsGroup, DsfrAccordion },
168+
data () {
169+
return {
170+
...args,
171+
asc: true,
172+
}
173+
},
174+
175+
template: `
176+
<DsfrTabs
177+
:tab-list-name="tabListName"
178+
:tab-titles="tabTitles"
179+
:initial-selected-index="initialSelectedIndex"
180+
@select-tab="selectTab"
181+
>
182+
<DsfrTabContent
183+
panel-id="tab-content-0"
184+
tab-id="tab-0"
185+
:selected="selectedTabIndex === 0"
186+
:asc="asc"
187+
>
188+
<DsfrAccordionsGroup>
189+
<li>
190+
<DsfrAccordion
191+
id="accordion-1"
192+
:title="title1"
193+
:expanded-id="expandedId"
194+
@expand="expandedId = $event"
195+
>
196+
Contenu de l’accordéon 1
197+
</DsfrAccordion>
198+
</li>
199+
<li>
200+
<DsfrAccordion
201+
id="accordion-2"
202+
:title="title2"
203+
:expanded-id="expandedId"
204+
@expand="id => expandedId = id"
205+
>
206+
Contenu de l’accordéon 2
207+
</DsfrAccordion>
208+
</li>
209+
<li>
210+
<DsfrAccordion
211+
id="accordion-3"
212+
:title="title3"
213+
:expanded-id="expandedId"
214+
@expand="id => expandedId = id"
215+
>
216+
Contenu de l’accordéon 3
217+
</DsfrAccordion>
218+
</li>
219+
</DsfrAccordionsGroup>
220+
</DsfrTabContent>
221+
222+
<DsfrTabContent
223+
panel-id="tab-content-1"
224+
tab-id="tab-1"
225+
:selected="selectedTabIndex === 1"
226+
:asc="asc"
227+
>
228+
<div>Contenu 2 avec d'autres composants</div>
229+
</DsfrTabContent>
230+
</DsfrTabs>
231+
`,
232+
233+
methods: {
234+
selectTab (idx) {
235+
this.onSelectTab(idx)
236+
this.asc = this.selectedTabIndex < idx
237+
this.selectedTabIndex = idx
238+
},
239+
},
240+
241+
mounted () {
242+
document.body.parentElement.setAttribute('data-fr-theme', this.dark ? 'dark' : 'light')
243+
},
244+
})
245+
OngletsAvecAccordeon.args = {
246+
dark: false,
247+
tabListName,
248+
tabTitles: [
249+
{ title: 'Onglet avec accordéon', icon: 'ri-checkbox-circle-line', tabId: 'tab-0', panelId: 'tab-content-0' },
250+
{ title: 'Titre 2', icon: 'ri-checkbox-circle-line', tabId: 'tab-1', panelId: 'tab-content-1' },
251+
],
252+
selectedTabIndex: 0,
253+
initialSelectedIndex: 0,
254+
title1: 'Un titre d’accordéon 1',
255+
title2: 'Un titre d’accordéon 2',
256+
title3: 'Un titre d’accordéon 3',
257+
expandedId: '',
258+
}

src/components/DsfrTabs/DsfrTabs.vue

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,36 @@ export default defineComponent({
4444
selectedIndex: this.initialSelectedIndex || 0,
4545
generatedIds: {},
4646
asc: true,
47+
resizeObserver: null,
4748
}
4849
},
4950
51+
mounted () {
52+
/*
53+
* Need to use a resize-observer as tab-content height can
54+
* change according to its inner components.
55+
*/
56+
if (window.ResizeObserver) {
57+
this.resizeObserver = new window.ResizeObserver(() => {
58+
this.renderTabs()
59+
})
60+
}
61+
62+
this.$el.querySelectorAll('.fr-tabs__panel').forEach((element) => {
63+
if (element) {
64+
this.resizeObserver?.observe(element)
65+
}
66+
})
67+
},
68+
69+
unmounted () {
70+
this.$el.querySelectorAll('.fr-tabs__panel').forEach((element) => {
71+
if (element) {
72+
this.resizeObserver?.unobserve(element)
73+
}
74+
})
75+
},
76+
5077
methods: {
5178
isSelected (idx) {
5279
return this.selectedIndex === idx
@@ -66,17 +93,39 @@ export default defineComponent({
6693
},
6794
async selectPrevious () {
6895
const newIndex = this.selectedIndex === 0 ? this.tabTitles.length - 1 : this.selectedIndex - 1
69-
this.selectIndex(newIndex)
96+
await this.selectIndex(newIndex)
7097
},
7198
async selectNext () {
7299
const newIndex = this.selectedIndex === this.tabTitles.length - 1 ? 0 : this.selectedIndex + 1
73-
this.selectIndex(newIndex)
100+
await this.selectIndex(newIndex)
74101
},
75102
async selectFirst () {
76-
this.selectIndex(0)
103+
await this.selectIndex(0)
77104
},
78105
async selectLast () {
79-
this.selectIndex(this.tabTitles.length - 1)
106+
await this.selectIndex(this.tabTitles.length - 1)
107+
},
108+
/*
109+
* Need to reimplement tab-height calc
110+
* @see https://github.com/GouvernementFR/dsfr/blob/main/src/component/tab/script/tab/tabs-group.js#L117
111+
*/
112+
renderTabs () {
113+
if (this.selectedIndex < 0) {
114+
return
115+
}
116+
const tablist = this.$refs.tablist
117+
if (!tablist || !tablist.offsetHeight) {
118+
return
119+
}
120+
const tablistHeight = tablist.offsetHeight
121+
// Need to manually select tabs-content in case of manual slot filling
122+
const selectedTab = this.$el.querySelectorAll('.fr-tabs__panel')[this.selectedIndex]
123+
if (!selectedTab || !selectedTab.offsetHeight) {
124+
return
125+
}
126+
const selectedTabHeight = selectedTab.offsetHeight
127+
128+
this.$el.style.setProperty('--tabs-height', (tablistHeight + selectedTabHeight) + 'px')
80129
},
81130
},
82131
})
@@ -85,6 +134,7 @@ export default defineComponent({
85134
<template>
86135
<div class="fr-tabs">
87136
<ul
137+
ref="tablist"
88138
class="fr-tabs__list"
89139
role="tablist"
90140
:aria-label="tabListName"

0 commit comments

Comments
 (0)