Contenu Qualimat
+Contenu Adresses
+Contenu Contacts
+Contenu Comptabilité
+Contenu onglet 1
+Contenu onglet 2
+Contenu onglet 3
+Contenu Qualimat
+Contenu Adresses
+Contenu Contacts
+Contenu Comptabilité
+Informations générales
+Détails avancés
+Home content
', + settings: 'Settings content
', + profile: 'Profile content
', + }) + const panels = wrapper.findAll('[role="tabpanel"]') + expect(panels[0].attributes('style')).toBeUndefined() + expect(panels[1].attributes('style')).toContain('display: none') + expect(panels[2].attributes('style')).toContain('display: none') + }) + + it('switches tab on click in uncontrolled mode', async () => { + const wrapper = mountComponent({tabs}) + const buttons = wrapper.findAll('[role="tab"]') + + await buttons[1].trigger('click') + + expect(buttons[1].attributes('aria-selected')).toBe('true') + expect(buttons[0].attributes('aria-selected')).toBe('false') + }) + + it('emits update:modelValue on click in controlled mode', async () => { + const wrapper = mountComponent({tabs, modelValue: 'home'}) + const buttons = wrapper.findAll('[role="tab"]') + + await buttons[2].trigger('click') + + expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['profile']) + }) + + it('respects modelValue for active tab in controlled mode', () => { + const wrapper = mountComponent({tabs, modelValue: 'settings'}) + const buttons = wrapper.findAll('[role="tab"]') + expect(buttons[0].attributes('aria-selected')).toBe('false') + expect(buttons[1].attributes('aria-selected')).toBe('true') + expect(buttons[2].attributes('aria-selected')).toBe('false') + }) + + it('sets correct aria-controls and aria-labelledby', () => { + const wrapper = mountComponent({tabs, id: 'test'}) + const buttons = wrapper.findAll('[role="tab"]') + const panels = wrapper.findAll('[role="tabpanel"]') + + expect(buttons[0].attributes('aria-controls')).toBe('test-panel-home') + expect(buttons[1].attributes('aria-controls')).toBe('test-panel-settings') + expect(panels[0].attributes('aria-labelledby')).toBe('test-tab-home') + expect(panels[1].attributes('aria-labelledby')).toBe('test-tab-settings') + }) + + it('has role="tablist" on the tab container', () => { + const wrapper = mountComponent({tabs}) + expect(wrapper.find('[role="tablist"]').exists()).toBe(true) + }) + + it('active tab has tabindex 0, others have -1', () => { + const wrapper = mountComponent({tabs}) + const buttons = wrapper.findAll('[role="tab"]') + expect(buttons[0].attributes('tabindex')).toBe('0') + expect(buttons[1].attributes('tabindex')).toBe('-1') + expect(buttons[2].attributes('tabindex')).toBe('-1') + }) + + it('renders icon props correctly via findComponent', () => { + const wrapper = mount(TabListForTest, { + props: {tabs}, + }) + const icons = wrapper.findAllComponents(IconifyIcon) + expect(icons).toHaveLength(2) + expect(icons[0].props('icon')).toBe('mdi:home') + expect(icons[1].props('icon')).toBe('mdi:account') + }) +}) diff --git a/app/components/malio/tab/TabList.vue b/app/components/malio/tab/TabList.vue new file mode 100644 index 0000000..6293880 --- /dev/null +++ b/app/components/malio/tab/TabList.vue @@ -0,0 +1,87 @@ + +Contenu onglet Qualimat
+Contenu onglet Adresses
+Contenu onglet Contacts
+Contenu onglet Comptabilité
+Contenu onglet 1
+Contenu onglet 2
+Contenu Qualimat
+Contenu Adresses (actif par défaut)
+Contenu Contacts
+Contenu Comptabilité
+Contenu Qualimat
', adresses: 'Contenu Adresses
' }, + ) + const panels = wrapper.findAll('[role="tabpanel"]') + const qualimatPanel = panels.find(p => p.attributes('aria-labelledby')?.includes('qualimat')) + const adressesPanel = panels.find(p => p.attributes('aria-labelledby')?.includes('adresses')) + expect(qualimatPanel?.isVisible()).toBe(true) + expect(adressesPanel?.isVisible()).toBe(false) + }) + + it('switches tab on click in uncontrolled mode', async () => { + const wrapper = mountComponent( + { tabs }, + { qualimat: 'Contenu Q
', adresses: 'Contenu A
' }, + ) + const tabButtons = wrapper.findAll('[role="tab"]') + await tabButtons[1].trigger('click') + + const panels = wrapper.findAll('[role="tabpanel"]') + const qualimatPanel = panels.find(p => p.attributes('aria-labelledby')?.includes('qualimat')) + const adressesPanel = panels.find(p => p.attributes('aria-labelledby')?.includes('adresses')) + expect(qualimatPanel?.isVisible()).toBe(false) + expect(adressesPanel?.isVisible()).toBe(true) + }) + + it('emits update:modelValue on click in controlled mode', async () => { + const wrapper = mountComponent({ tabs, modelValue: 'qualimat' }) + const tabButtons = wrapper.findAll('[role="tab"]') + await tabButtons[1].trigger('click') + + expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['adresses']) + }) + + it('respects modelValue for active tab in controlled mode', () => { + const wrapper = mountComponent({ tabs, modelValue: 'adresses' }) + const tabButtons = wrapper.findAll('[role="tab"]') + expect(tabButtons[1].attributes('aria-selected')).toBe('true') + expect(tabButtons[0].attributes('aria-selected')).toBe('false') + }) + + it('sets correct aria-controls and aria-labelledby', () => { + const wrapper = mountComponent({ tabs, id: 'test' }) + const firstTab = wrapper.findAll('[role="tab"]')[0] + const firstPanel = wrapper.findAll('[role="tabpanel"]')[0] + expect(firstTab.attributes('aria-controls')).toBe('test-panel-qualimat') + expect(firstPanel.attributes('aria-labelledby')).toBe('test-tab-qualimat') + expect(firstPanel.attributes('id')).toBe('test-panel-qualimat') + }) + + it('has role="tablist" on the tab container', () => { + const wrapper = mountComponent({ tabs }) + expect(wrapper.find('[role="tablist"]').exists()).toBe(true) + }) + + it('active tab has tabindex 0, others have -1', () => { + const wrapper = mountComponent({ tabs, modelValue: 'adresses' }) + const tabButtons = wrapper.findAll('[role="tab"]') + expect(tabButtons[0].attributes('tabindex')).toBe('-1') + expect(tabButtons[1].attributes('tabindex')).toBe('0') + expect(tabButtons[2].attributes('tabindex')).toBe('-1') + }) + + it('renders icon props correctly via findComponent', () => { + const wrapper = mount(TabListForTest, { + props: { tabs }, + }) + const icons = wrapper.findAllComponents(IconifyIcon) + expect(icons[0].props('icon')).toBe('mdi:certificate-outline') + }) +}) +``` + +- [ ] **Step 2: Lancer les tests** + +Run: `npm run test` +Expected: tous les tests passent + +- [ ] **Step 3: Lancer le lint** + +Run: `npm run lint` +Expected: pas d'erreur + +--- + +### Task 3: Créer la page playground + +**Files:** +- Create: `.playground/pages/composant/tab/tabList.vue` + +- [ ] **Step 1: Créer la page** + +```vue + +Contenu Qualimat
+Contenu Adresses
+Contenu Contacts
+Contenu Comptabilité
+Contenu onglet 1
+Contenu onglet 2
+Contenu onglet 3
+Contenu Qualimat
+Contenu Adresses
+Contenu Contacts
+Contenu Comptabilité
+Informations générales
+Détails avancés
+Contenu onglet Qualimat
+Contenu onglet Adresses
+Contenu onglet Contacts
+Contenu onglet Comptabilité
+Contenu onglet 1
+Contenu onglet 2
+Contenu Qualimat
+Contenu Adresses (actif par défaut)
+Contenu Contacts
+Contenu Comptabilité
+