Skip to content

Commit

Permalink
Merge pull request #406 from PrestaShopCorp/404-tab-navigation-create…
Browse files Browse the repository at this point in the history
…-an-event-to-detect-the-active-tab

[tab navigation] create an event to detect the active tab
  • Loading branch information
mattgoud authored Jan 15, 2025
2 parents ceb44fb + 6742051 commit b01c49f
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div
role="tablist"
:aria-label="ariaLabel"
:aria-label="props.ariaLabel"
class="puik-tab-navigation__group-titles"
>
<slot />
Expand All @@ -15,7 +15,7 @@ defineOptions({
name: 'PuikTabNavigationGroupTitles'
});
defineProps<TabNavigationGroupTitlesProps>();
const props = defineProps<TabNavigationGroupTitlesProps>();
</script>

<style lang="scss">
Expand Down
3 changes: 3 additions & 0 deletions packages/components/tab-navigation/src/tab-navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export interface TabNavigationProps {
defaultPosition?: number
}

export type TabNavigationEmits = {
'change-active-tab': [position: number]
};
export type TabNavigationInstance = InstanceType<typeof TabNavigation>;

export type currentTabKeyContext = {
Expand Down
6 changes: 5 additions & 1 deletion packages/components/tab-navigation/src/tab-navigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<script setup lang="ts">
import { nextTick, provide, ref } from 'vue';
import { currentTabKey, type TabNavigationProps } from './tab-navigation';
import { currentTabKey, type TabNavigationProps, TabNavigationEmits } from './tab-navigation';
defineOptions({
name: 'PuikTabNavigation'
Expand All @@ -19,13 +19,15 @@ defineOptions({
const props = withDefaults(defineProps<TabNavigationProps>(), {
defaultPosition: 1
});
const emit = defineEmits<TabNavigationEmits>();
const name = ref<string>(props.name);
const numberOfTabs = ref<number>(1);
const currentPosition = ref<number>(props.defaultPosition);
const keyEventDirection = ref<string | null>();
const handleTabClick = (index: number) => {
currentPosition.value = index;
emit('change-active-tab', currentPosition.value);
};
provide(currentTabKey, {
Expand All @@ -50,6 +52,7 @@ const selectNextTab = () => {
'.puik-tab-navigation__title--selected'
) as HTMLElement;
tabSelected.focus();
emit('change-active-tab', currentPosition.value);
});
};
Expand All @@ -67,6 +70,7 @@ const selectPreviousTab = () => {
'.puik-tab-navigation__title--selected'
) as HTMLElement;
tabSelected.focus();
emit('change-active-tab', currentPosition.value);
});
};
Expand Down
143 changes: 116 additions & 27 deletions packages/components/tab-navigation/stories/tab-navigation.stories.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { ref } from 'vue';
import { PuikTabNavigation, PuikTabNavigationGroupTitles, PuikTabNavigationGroupPanels, PuikTabNavigationTitle, PuikTabNavigationPanel, PuikIcon } from '@prestashopcorp/puik-components';
import { PuikTabNavigation, PuikTabNavigationGroupTitles, PuikTabNavigationGroupPanels, PuikTabNavigationTitle, PuikTabNavigationPanel, PuikIcon, PuikBadge } from '@prestashopcorp/puik-components';
import type { Meta, StoryFn, Args, StoryObj } from '@storybook/vue3';

const meta: Meta<
| typeof PuikTabNavigation
| typeof PuikTabNavigationGroupTitles
| typeof PuikTabNavigationGroupPanels
| typeof PuikTabNavigationTitle
| typeof PuikTabNavigationPanel
> = {
export default {
title: 'Components/TabNavigation',
component: PuikTabNavigation,
argTypes: {
Expand Down Expand Up @@ -70,18 +64,29 @@ const meta: Meta<
summary: 'false'
}
}
},
'change-active-tab': {
description: 'Event triggered when the current tab is changed',
table: {
defaultValue: {
summary: '@change-active-tab'
},
type: {
summary: 'Event (see details)',
detail: `
'change-active-tab': [position: number]
`
},
category: 'Events'
}
}
},
args: {
name: 'name-example',
defaultPosition: 1,
ariaLabel: 'aria-label-example'
}
};

export default meta;
type Story = StoryObj<
| typeof PuikTabNavigation
| typeof PuikTabNavigationGroupTitles
| typeof PuikTabNavigationGroupPanels
| typeof PuikTabNavigationTitle
| typeof PuikTabNavigationPanel
>;
} as Meta;

const Template: StoryFn = (args: Args) => ({
components: {
Expand Down Expand Up @@ -120,12 +125,7 @@ const Template: StoryFn = (args: Args) => ({
`
});

export const Default: Story = {
args: {
name: 'name-example',
defaultPosition: 1,
ariaLabel: 'aria-label-example'
},
export const Default: StoryObj = {
render: Template,
parameters: {
docs: {
Expand Down Expand Up @@ -157,10 +157,99 @@ export const Default: Story = {
</template>
</puik-tab-navigation-group-panels>
</puik-tab-navigation>
`,
language: 'html'
}
}
}
};

<!--HTML/CSS Snippet-->
<!--NB: the id attribute of the component corresponds to the prop name (allows you to identify each instance of the navigation component in the event that there are several on the same page)-->
<div id="name-example" class="puik-tab-navigation"></div>
export const ChangeActiveTabEvent: StoryObj = {
render: () => ({
components: {
PuikIcon,
PuikTabNavigation,
PuikTabNavigationGroupTitles,
PuikTabNavigationGroupPanels,
PuikTabNavigationTitle,
PuikTabNavigationPanel,
PuikBadge
},
setup() {
const args = {
name: 'active-tab-event-example',
defaultPosition: 1,
ariaLabel: 'active-tab-event-example'
};
const tabs = ref([
{ name: 'title-1', content: 'content 1' },
{ name: 'title-2', content: 'content 2' },
{ name: 'title-3', content: 'content 3' }
]);
const activeTab = ref(args.defaultPosition);
return { tabs, args, activeTab };
},
template: `
<div class="flex flex-col space-y-4">
<div>
<puik-badge variant="info">
Current tab position at {{ activeTab }}
</puik-badge>
</div>
<puik-tab-navigation
:name="args.name"
:default-position="args.defaultPosition"
@change-active-tab="(position) => activeTab = position"
>
<puik-tab-navigation-group-titles :aria-label="args.ariaLabel">
<template v-for="(tab, index) in tabs" :key="index">
<puik-tab-navigation-title :position="index + 1" :disabled="tab?.disabled">
{{ tab?.name }}
</puik-tab-navigation-title>
</template>
</puik-tab-navigation-group-titles>
<puik-tab-navigation-group-panels>
<template v-for="(tab, index) in tabs" :key="index">
<puik-tab-navigation-panel :position="index + 1">
{{ tab?.content }}
</puik-tab-navigation-panel>
</template>
</puik-tab-navigation-group-panels>
</puik-tab-navigation>
</div>
`
}),
parameters: {
docs: {
source: {
code: `
<!--VueJS Snippet-->
// const activeTab = ref(args.defaultPosition);
<puik-badge variant="info">
Current tab position at {{ activeTab }}
</puik-badge>
<puik-tab-navigation
:name="args.name"
:default-position="args.defaultPosition"
@change-active-tab="(position) => activeTab = position"
>
<puik-tab-navigation-group-titles :aria-label="args.ariaLabel">
<template v-for="(tab, index) in tabs" :key="index">
<puik-tab-navigation-title :position="index + 1" :disabled="tab?.disabled">
{{ tab?.name }}
</puik-tab-navigation-title>
</template>
</puik-tab-navigation-group-titles>
<puik-tab-navigation-group-panels>
<template v-for="(tab, index) in tabs" :key="index">
<puik-tab-navigation-panel :position="index + 1">
{{ tab?.content }}
</puik-tab-navigation-panel>
</template>
</puik-tab-navigation-group-panels>
</puik-tab-navigation>
`,
language: 'html'
}
Expand Down
71 changes: 69 additions & 2 deletions packages/components/tab-navigation/test/tab-navigation.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils';
import { describe, it, expect } from 'vitest';
import { describe, it, expect, vi } from 'vitest';
import {
PuikTabNavigation,
PuikTabNavigationGroupTitles,
Expand Down Expand Up @@ -56,18 +56,85 @@ const template = `
</puik-tab-navigation-group-panels>
</puik-tab-navigation>
`;

describe('TabNavigation tests', () => {
it('should be a vue instance', () => {
factory(template);
expect(getTabNavigationComponent).toBeTruthy();
expect(getTabNavigationComponent()).toBeTruthy();
});

it('As defaultPosition prop value is 2, tab-2 should be selected', () => {
factory(template);
const tab2 = getTabNavigationTitleHtml()[1];
expect(tab2.classes()).toContain('puik-tab-navigation__title--selected');
});

it('As name prop value is "name-example", id attribute of puik-tab-navigation should be "name-example"', () => {
factory(template);
expect(getTabNavigationHtml().attributes().id).toBe('name-example');
});

it('should not select disabled tab', async () => {
factory(template);
const tab3 = getTabNavigationTitleHtml()[2];
await tab3.trigger('click');
expect(tab3.classes()).not.toContain('puik-tab-navigation__title--selected');
});

it('should change active tab when a tab is clicked', async () => {
factory(template);
const tab1 = getTabNavigationTitleHtml()[0];
await tab1.trigger('click');
expect(tab1.classes()).toContain('puik-tab-navigation__title--selected');
});

it('should emit change-active-tab event when active tab changes', async () => {
const handleChangeActiveTab = vi.fn();
const wrapper = mount({
components: {
PuikTabNavigation,
PuikTabNavigationGroupTitles,
PuikTabNavigationGroupPanels,
PuikTabNavigationTitle,
PuikTabNavigationPanel
},
template: `
<puik-tab-navigation name="name-example" :default-position="2" @change-active-tab="handleChangeActiveTab">
<puik-tab-navigation-group-titles aria-label="aria-label-example">
<puik-tab-navigation-title :position="1">
title 1
</puik-tab-navigation-title>
<puik-tab-navigation-title :position="2">
title 2
</puik-tab-navigation-title>
<puik-tab-navigation-title :position="3" disabled>
title 3
</puik-tab-navigation-title>
</puik-tab-navigation-group-titles>
<puik-tab-navigation-group-panels>
<puik-tab-navigation-panel :position="1">
Content 1
</puik-tab-navigation-panel>
<puik-tab-navigation-panel :position="2">
Content 2
</puik-tab-navigation-panel>
<puik-tab-navigation-panel :position="3">
Content 3
</puik-tab-navigation-panel>
</puik-tab-navigation-group-panels>
</puik-tab-navigation>
`,
methods: {
handleChangeActiveTab
}
});

const tab1 = wrapper.findAll('.puik-tab-navigation__title')[0];
await tab1.trigger('click');

await wrapper.vm.$nextTick(); // Assurez-vous que le DOM est mis à jour

expect(handleChangeActiveTab).toHaveBeenCalled();
expect(handleChangeActiveTab).toHaveBeenCalledWith(1);
});
});

0 comments on commit b01c49f

Please sign in to comment.