diff --git a/.playground/pages/composant/date/date.vue b/.playground/pages/composant/date/date.vue index 320a5b2..ec73c15 100644 --- a/.playground/pages/composant/date/date.vue +++ b/.playground/pages/composant/date/date.vue @@ -50,6 +50,25 @@ /> + +
+ Tous les champs de formulaire dans leur état readonly, vides puis remplis.
+ Règles : bordure noire même vide, label et icône gris quand vide → noir quand rempli,
+ pas de focus bleu ni de grossissement.
+
Détails avancés
+ +
+ 7 onglets avec :max-visible-tabs="5" — flèches gauche/droite pour faire défiler
+ (1 par 1). L'onglet actif reste sélectionné même hors fenêtre.
+
Contenu Informations
+Contenu Adresses
+Contenu Contacts
+Contenu Comptabilité
+Contenu Documents
+Contenu Historique
+Contenu Paramètres
+
+ 3 onglets avec :max-visible-tabs="5" — le fenêtrage ne s'active pas
+ (onglets ≤ max), donc pas de flèches, affichage normal centré.
+
Contenu Général
+Contenu Adresses
+Contenu Contacts
+Panel 1
'}, + ) + + await wrapper.find('[data-test="tab-next"]').trigger('click') + await wrapper.find('[data-test="tab-next"]').trigger('click') + + // Tab 1 is no longer in the window + const labels = wrapper.findAll('[role="tab"]').map(b => b.text()) + expect(labels.some(l => l.includes('Tab 1'))).toBe(false) + + // but its panel is still rendered and visible + const panels = wrapper.findAll('[role="tabpanel"]') + expect(panels).toHaveLength(7) + expect(wrapper.text()).toContain('Panel 1') + }) + + it('keeps exactly one rendered tab with tabindex=0 when the active tab scrolls out of the window', async () => { + const wrapper = mountComponent({tabs: sevenTabs, maxVisibleTabs: 5}) + + // active tab is the first one (t1) by default; scroll it out of the window + await wrapper.find('[data-test="tab-next"]').trigger('click') + await wrapper.find('[data-test="tab-next"]').trigger('click') + + // t1 is no longer rendered + const labels = wrapper.findAll('[role="tab"]').map(b => b.text()) + expect(labels.some(l => l.includes('Tab 1'))).toBe(false) + + const focusable = wrapper.findAll('[role="tab"]').filter(b => b.attributes('tabindex') === '0') + expect(focusable).toHaveLength(1) + // falls back to the first visible tab (Tab 3) + expect(focusable[0].text()).toContain('Tab 3') + }) + + it('arrows expose aria-labels', () => { + const wrapper = mountComponent({tabs: sevenTabs, maxVisibleTabs: 5}) + expect(wrapper.find('[data-test="tab-prev"]').attributes('aria-label')).toBe('Onglets précédents') + expect(wrapper.find('[data-test="tab-next"]').attributes('aria-label')).toBe('Onglets suivants') + }) +}) diff --git a/app/components/malio/tab/TabList.vue b/app/components/malio/tab/TabList.vue index efa4f3d..fe0ab04 100644 --- a/app/components/malio/tab/TabList.vue +++ b/app/components/malio/tab/TabList.vue @@ -1,11 +1,81 @@