Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ module.exports = {
'@vue-storefront/eslint-config-jest',
],
rules: {
"@typescript-eslint/no-floating-promises": "off"
"@typescript-eslint/no-floating-promises": "off",
"jest/expect-expect": [
"error",
{
"assertFunctionNames": ["expect", "getByRole", "getByTestId"],
}
],
"no-plusplus": "off",
}
}

24 changes: 5 additions & 19 deletions packages/theme/components/AppHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,7 @@
<HeaderLogo />
</template>
<template #navigation>
<HeaderNavigationItem
v-for="(category, index) in categoryTree"
:key="index"
v-e2e="'app-header-url_women'"
class="nav-item"
:label="category.name"
:link="localePath(getCatLink(category))"
/>
<HeaderNavigation :category-tree="categoryTree" />
</template>
<template #aside>
<div class="sf-header__switchers">
Expand Down Expand Up @@ -122,7 +115,7 @@ import {
onMounted,
useFetch,
} from '@nuxtjs/composition-api';
import HeaderNavigationItem from '~/components/Header/Navigation/HeaderNavigationItem.vue';
import HeaderNavigation from '~/components/Header/Navigation/HeaderNavigation.vue';
import {
useCart,
useCategory,
Expand All @@ -132,6 +125,7 @@ import {
useUser,
} from '~/composables';

import type { CategoryTree } from '~/modules/GraphQL/types';
import CurrencySelector from '~/components/CurrencySelector.vue';
import HeaderLogo from '~/components/HeaderLogo.vue';
import SvgImage from '~/components/General/SvgImage.vue';
Expand All @@ -140,7 +134,7 @@ import { useCustomerStore } from '~/stores/customer';

export default defineComponent({
components: {
HeaderNavigationItem,
HeaderNavigation,
SfHeader,
SfOverlay,
CurrencySelector,
Expand Down Expand Up @@ -171,7 +165,7 @@ export default defineComponent({

const wishlistHasProducts = computed(() => wishlistItemsQty.value > 0);
const accountIcon = computed(() => (isAuthenticated.value ? 'profile_fill' : 'profile'));
const categoryTree = ref([]);
const categoryTree = ref<CategoryTree[]>([]);

const handleAccountClick = async () => {
if (isAuthenticated.value) {
Expand Down Expand Up @@ -231,14 +225,6 @@ export default defineComponent({
z-index: 2;
}

.nav-item {
--header-navigation-item-margin: 0 var(--spacer-sm);

.sf-header-navigation-item__item--mobile {
display: none;
}
}

.cart-badge {
position: absolute;
bottom: 40%;
Expand Down
134 changes: 134 additions & 0 deletions packages/theme/components/Header/Navigation/HeaderNavigation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<template>
<div class="header-navigation">
<div class="sf-header-navigation-item__item sf-header-navigation-item__item--desktop">
<HeaderNavigationItem
v-for="(category, index) in categoryTree"
:key="index"
ref="lvl0CatRefs"
:data-testid="category.uid"
:label="category.name"
:link="localePath(getCatLink(category))"
tabindex="1"
aria-haspopup="true"
class="nav-item"
:data-index="index"
:has-children="hasChildren(category)"
@mouseenter.native.prevent="onMouseEnter(category)"
@keydown.down.native.prevent="setCurrentCategory(category)"
@keydown.up.native.prevent="setCurrentCategory(null)"
@keyup.tab.native.prevent="setFocus($event)"
@keydown.right.native.prevent="navRight()"
@keydown.left.native.prevent="navLeft()"
/>
</div>
<HeaderNavigationSubcategories
v-if="hasChildren(currentCategory)"
:current-category="currentCategory"
:has-focus="hasFocus"
@hideSubcategories="focusRootElementAndHideSubcategories"
/>
</div>
</template>
<script lang="ts">
import {
defineComponent, PropType, ref,
} from '@nuxtjs/composition-api';
import HeaderNavigationItem from './HeaderNavigationItem.vue';

import { CategoryTree } from '~/modules/GraphQL/types';
import { useUiHelpers } from '~/composables';

export default defineComponent({
name: 'HeaderNavigation',
components: {
HeaderNavigationSubcategories: () => import('~/components/Header/Navigation/HeaderNavigationSubcategories.vue'),
HeaderNavigationItem,
},
props: {
categoryTree: {
type: Array as PropType<CategoryTree[]>,
default: () => [],
},
},
setup() {
const { getCatLink } = useUiHelpers();

const currentCategory = ref<CategoryTree>(null);
const lvl0CatRefs = ref([]);
const hasFocus = ref(false);
let lvl0CatFocusIdx = 0;
let focusedElement = null;

const setCurrentCategory = (category: CategoryTree | null) => {
currentCategory.value = category;
};

const hasChildren = (category: CategoryTree) => Boolean(category?.children);

const setFocus = (event) => {
focusedElement = event.target;
lvl0CatFocusIdx = Number(event.target.dataset.index);
hasFocus.value = true;
};

const focusRootElementAndHideSubcategories = () => {
setCurrentCategory(null);
if (focusedElement !== null) focusedElement.focus();
};

const navRight = () => {
lvl0CatFocusIdx++;
if (lvl0CatRefs.value[lvl0CatFocusIdx]) {
lvl0CatRefs.value[lvl0CatFocusIdx].$el.focus();
focusedElement = lvl0CatRefs.value[lvl0CatFocusIdx].$el;
} else {
lvl0CatFocusIdx--;
}
};

const navLeft = () => {
lvl0CatFocusIdx--;
if (lvl0CatRefs.value[lvl0CatFocusIdx]) {
lvl0CatRefs.value[lvl0CatFocusIdx].$el.focus();
focusedElement = lvl0CatRefs.value[lvl0CatFocusIdx].$el;
} else {
lvl0CatFocusIdx++;
}
};

const onMouseEnter = (category: CategoryTree) => {
currentCategory.value = category;
focusedElement = null;
hasFocus.value = false;
};

return {
getCatLink,
setCurrentCategory,
currentCategory,
hasChildren,
setFocus,
focusRootElementAndHideSubcategories,
lvl0CatRefs,
navRight,
navLeft,
hasFocus,
onMouseEnter,
};
},
});
</script>
<style lang="scss" scoped>
.header-navigation {
&__main {
display: flex;
}
}
.nav-item {
--header-navigation-item-margin: 0 var(--spacer-sm);

.sf-header-navigation-item__item--mobile {
display: none;
}
}
</style>
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
<template>
<div class="sf-header-navigation-item">
<div class="sf-header-navigation-item__item sf-header-navigation-item__item--desktop">
<slot name="desktop-navigation-item">
<SfLink
class="sf-header-navigation-item__link"
:link="link"
>
{{
label
}}
</SfLink>
</slot>
<slot />
</div>
</div>
<SfLink
class="sf-header-navigation-item__link"
:link="link"
>
{{
label
}}
<SfIcon
v-if="hasChildren"
icon="chevron_down"
size="xxs"
color="green-primary"
viewBox="0 0 24 24"
:coverage="1"
/>
</SfLink>
</template>
<script>
import { SfLink } from '@storefront-ui/vue';
<script lang="ts">
import { SfLink, SfIcon } from '@storefront-ui/vue';

export default {
name: 'HeaderNavigationItem',
components: {
SfLink,
SfIcon,
},
props: {
label: {
Expand All @@ -32,6 +34,19 @@ export default {
type: [String, Object],
default: '',
},
hasChildren: {
type: Boolean,
default: false,
},
},
};
</script>
<style lang="scss">
.sf-header-navigation-item__link {
display: flex;
.sf-icon {
display: inline-flex;
margin: 0 var(--spacer-xs);
}
}
</style>
Loading