diff --git a/.storybook/stories/TheHeader.stories.js b/.storybook/stories/TheHeader.stories.js
index 49facb5a41..f5ae40b156 100644
--- a/.storybook/stories/TheHeader.stories.js
+++ b/.storybook/stories/TheHeader.stories.js
@@ -23,6 +23,7 @@ const loggedIn = {
"https://www-dev-kiva-org.freetls.fastly.net/img/s140/726677.jpg",
},
},
+ team: null,
isBorrower: false,
mostRecentBorrowedLoan: null,
trustee: null,
@@ -66,6 +67,84 @@ const loggedInLargeCart = {
}
};
+const loggedInWithOneTeam = {
+ my: {
+ ...loggedIn.my,
+ teams: {
+ totalCount: 1,
+ values: [
+ {
+ id: 1,
+ team: {
+ id: 1,
+ name: 'Team 1',
+ teamPublicId: 'team1',
+ }
+ },
+ ],
+ },
+ },
+};
+
+const loggedInWithMultipleTeams = {
+ my: {
+ ...loggedIn.my,
+ teams: {
+ totalCount: 6,
+ values: [
+ {
+ id: 1,
+ team: {
+ id: 1,
+ name: '(A+) Atheists, Agnostics, Skeptics, Freethinkers, Secular Humanists and the Non-Religious',
+ teamPublicId: 'aplus',
+ }
+ },
+ {
+ id: 2,
+ team: {
+ id: 2,
+ name: 'Team 2',
+ teamPublicId: 'team2',
+ }
+ },
+ {
+ id: 3,
+ team: {
+ id: 3,
+ name: 'Team 3',
+ teamPublicId: 'team3',
+ }
+ },
+ {
+ id: 4,
+ team: {
+ id: 4,
+ name: 'Team 4',
+ teamPublicId: 'team4',
+ }
+ },
+ {
+ id: 5,
+ team: {
+ id: 5,
+ name: 'Team 5',
+ teamPublicId: 'team5',
+ }
+ },
+ {
+ id: 6,
+ team: {
+ id: 6,
+ name: 'Team 6',
+ teamPublicId: 'team6',
+ }
+ },
+ ],
+ },
+ },
+};
+
const provideMockedApollo = (mockedResult) => {
return {
readQuery() {
@@ -223,4 +302,32 @@ Minimal.args = {
minimal: true,
};
+export const LoggedInWithOneTeam = (args, { argTypes }) => ({
+ props: Object.keys(argTypes),
+ components: {
+ TheHeader,
+ },
+ mixins: [cookieStoreStoryMixin(), kvAuth0StoryMixin],
+ provide: {
+ apollo: provideMockedApollo(loggedInWithOneTeam),
+ },
+ template: `
+
+ `,
+});
+
+export const LoggedInWithMultipleTeams = (args, { argTypes }) => ({
+ props: Object.keys(argTypes),
+ components: {
+ TheHeader,
+ },
+ mixins: [cookieStoreStoryMixin(), kvAuth0StoryMixin],
+ provide: {
+ apollo: provideMockedApollo(loggedInWithMultipleTeams),
+ },
+ template: `
+
+ `,
+});
+
// TODO: trustee
diff --git a/src/components/WwwFrame/Header/TeamsMenu.vue b/src/components/WwwFrame/Header/TeamsMenu.vue
new file mode 100644
index 0000000000..24978160e2
--- /dev/null
+++ b/src/components/WwwFrame/Header/TeamsMenu.vue
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
diff --git a/src/components/WwwFrame/TheHeader.vue b/src/components/WwwFrame/TheHeader.vue
index 8619340538..07f0f2b79f 100644
--- a/src/components/WwwFrame/TheHeader.vue
+++ b/src/components/WwwFrame/TheHeader.vue
@@ -217,6 +217,13 @@
Borrow
+
+
+
import('@/components/WwwFrame/LendMenu/TheLendMenu'),
+ TeamsMenu,
},
inject: ['apollo', 'cookieStore', 'kvAuth0'],
data() {
@@ -633,6 +642,7 @@ export default {
hasEverLoggedIn: false,
isMobile: false,
basketTotal: 0,
+ teams: null,
};
},
props: {
@@ -749,6 +759,7 @@ export default {
this.basketTotal = data.shop?.basket?.items?.values?.reduce((sum, item) => {
return sum + +(item?.price ?? 0);
}, 0) ?? 0;
+ this.teams = data?.my?.teams ?? {};
},
errorHandlers: {
'shop.invalidBasketId': ({ cookieStore, route }) => {
diff --git a/src/graphql/query/wwwHeader.graphql b/src/graphql/query/wwwHeader.graphql
index 9bd9aee72a..1f348e25a5 100644
--- a/src/graphql/query/wwwHeader.graphql
+++ b/src/graphql/query/wwwHeader.graphql
@@ -54,5 +54,16 @@ query wwwHeader($basketId: String) {
trustee {
id
}
+ teams (limit: 5) {
+ totalCount
+ values {
+ id
+ team {
+ id
+ name
+ teamPublicId
+ }
+ }
+ }
}
}
diff --git a/test/unit/specs/components/WwwFrame/TeamsMenu.spec.js b/test/unit/specs/components/WwwFrame/TeamsMenu.spec.js
new file mode 100644
index 0000000000..9a787d6ca3
--- /dev/null
+++ b/test/unit/specs/components/WwwFrame/TeamsMenu.spec.js
@@ -0,0 +1,170 @@
+import { render } from '@testing-library/vue';
+import TeamsMenu from '@/components/WwwFrame/Header/TeamsMenu';
+import Vue from 'vue';
+import CookieStore from '@/util/cookieStore';
+import { MockKvAuth0 } from '@/util/KvAuth0';
+
+const user = {
+ my: {
+ teams: {
+ totalCount: 0,
+ values: [],
+ },
+ },
+};
+
+const userWithOneTeam = {
+ my: {
+ teams: {
+ totalCount: 1,
+ values: [
+ {
+ id: 1,
+ team: {
+ id: 1,
+ name: 'Team 1',
+ teamPublicId: 'team1',
+ },
+ },
+ ],
+ },
+ },
+};
+
+const userWithMultipleTeams = {
+ my: {
+ teams: {
+ totalCount: 6,
+ values: [
+ {
+ id: 1,
+ team: {
+ id: 1,
+ name: 'Team 1',
+ teamPublicId: 'team1',
+ },
+ },
+ {
+ id: 2,
+ team: {
+ id: 2,
+ name: 'Team 2',
+ teamPublicId: 'team2',
+ },
+ },
+ {
+ id: 3,
+ team: {
+ id: 3,
+ name: 'Team 3',
+ teamPublicId: 'team3',
+ },
+ },
+ {
+ id: 4,
+ team: {
+ id: 4,
+ name: 'Team 4',
+ teamPublicId: 'team4',
+ },
+ },
+ {
+ id: 5,
+ team: {
+ id: 5,
+ name: 'Team 5',
+ teamPublicId: 'team5',
+ },
+ },
+ {
+ id: 6,
+ team: {
+ id: 6,
+ name: 'Team 6',
+ teamPublicId: 'team6',
+ },
+ },
+ ],
+ },
+ },
+};
+
+function renderTeamsMenu(props) {
+ Vue.directive('kv-track-event', () => ({}));
+
+ return render(TeamsMenu, {
+ props,
+ provide: {
+ apollo: {
+ readFragment: () => {},
+ query: () => Promise.resolve({}),
+ readQuery: () => {},
+ },
+ cookieStore: new CookieStore(),
+ kvAuth0: MockKvAuth0,
+ },
+ stubs: ['router-link']
+ });
+}
+
+describe('TeamsMenu', () => {
+ it('should show only Teams link', async () => {
+ const props = {
+ teams: user.my.teams,
+ };
+ const { queryByTestId } = renderTeamsMenu(props);
+
+ const anchor = queryByTestId('header-teams');
+ expect(anchor.getAttribute('to')).toBe('/teams');
+ });
+
+ it('should show Teams Dropdown with one team option', async () => {
+ const props = {
+ teams: userWithOneTeam.my.teams,
+ };
+ const { queryByText } = renderTeamsMenu(props);
+
+ const activity = queryByText("My Team's activity");
+ const impact = queryByText("My Team's impact");
+ const join = queryByText('Join another team');
+
+ const { teamPublicId } = props.teams.values[0].team;
+
+ expect(activity.getAttribute('href')).toBe(`/team/${teamPublicId}`);
+ expect(impact.getAttribute('href')).toBe(`/team/${teamPublicId}/impact`);
+ expect(join.getAttribute('href')).toBe('/teams');
+ });
+
+ it('should show 3 teams', async () => {
+ const limitedTeams = userWithMultipleTeams.my.teams.values.slice(0, 3);
+ const props = {
+ teams: {
+ totalCount: limitedTeams.length,
+ values: limitedTeams,
+ },
+ };
+ const { queryByText } = renderTeamsMenu(props);
+
+ props.teams.values.forEach(t => {
+ const { teamPublicId } = t.team;
+ expect(queryByText(t.team.name).getAttribute('href')).toBe(`/team/${teamPublicId}`);
+ });
+ });
+
+ it('should only show 5 teams and a link to view all my teams', async () => {
+ const props = {
+ teams: userWithMultipleTeams.my.teams,
+ };
+ const { queryByText } = renderTeamsMenu(props);
+
+ const limitedTeams = props.teams.values.slice(0, 5);
+
+ limitedTeams.forEach(t => {
+ const { teamPublicId } = t.team;
+ expect(queryByText(t.team.name).getAttribute('href')).toBe(`/team/${teamPublicId}`);
+ });
+
+ const allMyTeams = queryByText('View all my teams');
+ expect(allMyTeams.getAttribute('href')).toBe('/teams/my-teams');
+ });
+});