@treely/strapi-slices 7.1.4 → 7.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__mocks__/swr/index.d.ts +6 -0
- package/dist/__mocks__/swr/infinite.d.ts +4 -0
- package/dist/components/EventCard/EventCard.d.ts +6 -0
- package/dist/components/EventCard/index.d.ts +2 -0
- package/dist/components/EventCard/messages.de.d.ts +15 -0
- package/dist/components/EventCard/messages.en.d.ts +15 -0
- package/dist/components/StrapiLinkButton/StrapiLinkButton.d.ts +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/models/SWRData.d.ts +5 -0
- package/dist/models/hooks/UseInfiniteDataHookProps.d.ts +11 -0
- package/dist/models/hooks/useEvents.d.ts +9 -0
- package/dist/models/strapi/StrapiEvent.d.ts +48 -0
- package/dist/rootMessages.de.d.ts +21 -0
- package/dist/rootMessages.en.d.ts +21 -0
- package/dist/slices/Events/Events.d.ts +11 -0
- package/dist/slices/Events/Events.stories.d.ts +4 -0
- package/dist/slices/Events/index.d.ts +2 -0
- package/dist/slices/Events/messages.de.d.ts +12 -0
- package/dist/slices/Events/messages.en.d.ts +12 -0
- package/dist/slices/QAndA/QAndA.d.ts +1 -0
- package/dist/strapi-slices.cjs.development.js +785 -44
- package/dist/strapi-slices.cjs.development.js.map +1 -1
- package/dist/strapi-slices.cjs.production.min.js +1 -1
- package/dist/strapi-slices.cjs.production.min.js.map +1 -1
- package/dist/strapi-slices.esm.js +786 -46
- package/dist/strapi-slices.esm.js.map +1 -1
- package/dist/test/strapiMocks/strapiEventMock.d.ts +3 -0
- package/dist/utils/getCountryFlag.d.ts +2 -0
- package/dist/utils/getMessages.d.ts +42 -0
- package/package.json +17 -3
- package/src/components/ContextProvider/ContextProvider.tsx +32 -4
- package/src/components/CustomerQuoteCard/CustomerQuoteCard.tsx +1 -0
- package/src/components/EventCard/EventCard.test.tsx +127 -0
- package/src/components/EventCard/EventCard.tsx +309 -0
- package/src/components/EventCard/index.ts +3 -0
- package/src/components/EventCard/messages.de.ts +16 -0
- package/src/components/EventCard/messages.en.ts +16 -0
- package/src/components/SliceRenderer/SliceRenderer.tsx +5 -0
- package/src/components/StrapiLinkButton/StrapiLinkButton.tsx +1 -0
- package/src/index.tsx +2 -0
- package/src/models/SWRData.ts +6 -0
- package/src/models/hooks/UseInfiniteDataHookProps.tsx +13 -0
- package/src/models/hooks/useEvents.ts +46 -0
- package/src/models/strapi/StrapiEvent.ts +51 -0
- package/src/rootMessages.de.ts +4 -0
- package/src/rootMessages.en.ts +4 -0
- package/src/slices/Events/Events.stories.tsx +36 -0
- package/src/slices/Events/Events.test.tsx +344 -0
- package/src/slices/Events/Events.tsx +440 -0
- package/src/slices/Events/index.ts +3 -0
- package/src/slices/Events/messages.de.ts +14 -0
- package/src/slices/Events/messages.en.ts +13 -0
- package/src/slices/QAndA/QAndA.stories.tsx +5 -0
- package/src/slices/QAndA/QAndA.test.tsx +1 -0
- package/src/slices/QAndA/QAndA.tsx +2 -1
- package/src/test/strapiMocks/strapiEventMock.ts +57 -0
- package/src/utils/getCountryFlag.test.ts +9 -0
- package/src/utils/getCountryFlag.ts +6 -0
- package/src/utils/strapiMediaUrl.ts +1 -2
package/src/index.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import StrapiContactArea from './models/strapi/StrapiContactArea';
|
|
|
12
12
|
import StrapiCustomerStory from './models/strapi/StrapiCustomerStory';
|
|
13
13
|
import StrapiCustomerStoryProps from './models/strapi/StrapiCustomerStoryProps';
|
|
14
14
|
import StrapiDefaultHeader from './models/strapi/StrapiDefaultHeader';
|
|
15
|
+
import StrapiEvent from './models/strapi/StrapiEvent';
|
|
15
16
|
import StrapiGlobal from './models/strapi/StrapiGlobal';
|
|
16
17
|
import StrapiGlossaryItem from './models/strapi/StrapiGlossaryItem';
|
|
17
18
|
import StrapiHeroCard from './models/strapi/StrapiHeroCard';
|
|
@@ -93,6 +94,7 @@ export type {
|
|
|
93
94
|
StrapiCustomerStory,
|
|
94
95
|
StrapiCustomerStoryProps,
|
|
95
96
|
StrapiDefaultHeader,
|
|
97
|
+
StrapiEvent,
|
|
96
98
|
StrapiGlobal,
|
|
97
99
|
StrapiGlossaryItem,
|
|
98
100
|
StrapiHeroCard,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import SWRData from '../SWRData';
|
|
2
|
+
|
|
3
|
+
interface UseInfiniteDataHookProps<T> {
|
|
4
|
+
data: SWRData<T[]>[] | undefined;
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
isLoadingMore: boolean;
|
|
7
|
+
canLoadMore: boolean;
|
|
8
|
+
refetch: () => void;
|
|
9
|
+
loadMore: () => void;
|
|
10
|
+
count: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default UseInfiniteDataHookProps;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import useSWRInfinite, { SWRInfiniteKeyLoader } from 'swr/infinite';
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
|
+
import UseInfiniteDataHookProps from './UseInfiniteDataHookProps';
|
|
4
|
+
import SWRData from '../SWRData';
|
|
5
|
+
import IStrapiResponse from '../../models/strapi/IStrapiResponse';
|
|
6
|
+
import StrapiEvent from '../strapi/StrapiEvent';
|
|
7
|
+
|
|
8
|
+
const useEvents = ({
|
|
9
|
+
getKey,
|
|
10
|
+
batchSize,
|
|
11
|
+
}: {
|
|
12
|
+
getKey: SWRInfiniteKeyLoader;
|
|
13
|
+
batchSize: number;
|
|
14
|
+
}): UseInfiniteDataHookProps<IStrapiResponse<StrapiEvent[]>[]> => {
|
|
15
|
+
const { data, isLoading, setSize, size, mutate } = useSWRInfinite<
|
|
16
|
+
SWRData<IStrapiResponse<StrapiEvent[]>>
|
|
17
|
+
>(getKey, { revalidateFirstPage: false, revalidateAll: false });
|
|
18
|
+
|
|
19
|
+
const count: number | undefined = useMemo(() => {
|
|
20
|
+
return data?.[0]?.body?.meta?.pagination?.total || 0;
|
|
21
|
+
}, [data]);
|
|
22
|
+
|
|
23
|
+
const isLoadingMore = useMemo(
|
|
24
|
+
() => !!(size > 0 && data && typeof data[size - 1] === 'undefined'),
|
|
25
|
+
[size, data]
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const loadMore = useCallback(() => setSize(size + 1), [setSize, size]);
|
|
29
|
+
|
|
30
|
+
const canLoadMore = useMemo(
|
|
31
|
+
() => count !== undefined && size * batchSize < count,
|
|
32
|
+
[count, size, batchSize]
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
data: data as any,
|
|
37
|
+
isLoading,
|
|
38
|
+
isLoadingMore,
|
|
39
|
+
canLoadMore,
|
|
40
|
+
refetch: mutate,
|
|
41
|
+
loadMore,
|
|
42
|
+
count,
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default useEvents;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import Locale from '../Locale';
|
|
2
|
+
import StrapiImage from './StrapiImage';
|
|
3
|
+
import StrapiLink from './StrapiLink';
|
|
4
|
+
import StrapiLocalization from './StrapiLocalization';
|
|
5
|
+
import StrapiTopBanner from './StrapiTopBanner';
|
|
6
|
+
|
|
7
|
+
export enum EventType {
|
|
8
|
+
CONFERENCE = 'Conference',
|
|
9
|
+
MEET_UP = 'Meet Up',
|
|
10
|
+
WEBINAR = 'Webinar',
|
|
11
|
+
FOREST_WALK = 'Forest Walk',
|
|
12
|
+
LUNCH_AND_LEARN = 'Lunch & Learn',
|
|
13
|
+
FESTIVAL = 'Festival',
|
|
14
|
+
ROADSHOW = 'RoadShow',
|
|
15
|
+
PARTNER_EVENT = 'Partner Event',
|
|
16
|
+
FAIR = 'Fair',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface StrapiEvent {
|
|
20
|
+
title: string;
|
|
21
|
+
description: string;
|
|
22
|
+
button: StrapiLink;
|
|
23
|
+
buttonVariant?: 'outline' | 'ghost' | 'link' | 'solid' | 'outlineWhite';
|
|
24
|
+
recommended?: boolean;
|
|
25
|
+
speakers: {
|
|
26
|
+
id: number;
|
|
27
|
+
name: string;
|
|
28
|
+
image: StrapiImage;
|
|
29
|
+
}[];
|
|
30
|
+
image: StrapiImage;
|
|
31
|
+
logo: StrapiImage;
|
|
32
|
+
eventTypes: {
|
|
33
|
+
id: number;
|
|
34
|
+
eventType: EventType;
|
|
35
|
+
}[];
|
|
36
|
+
languages: {
|
|
37
|
+
id: number;
|
|
38
|
+
language: string;
|
|
39
|
+
countryCode: string;
|
|
40
|
+
}[];
|
|
41
|
+
location?: string;
|
|
42
|
+
locale: Locale;
|
|
43
|
+
online?: boolean;
|
|
44
|
+
start: Date;
|
|
45
|
+
end: Date;
|
|
46
|
+
slices: any[];
|
|
47
|
+
localizations: StrapiLocalization[];
|
|
48
|
+
topBanner?: StrapiTopBanner;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default StrapiEvent;
|
package/src/rootMessages.de.ts
CHANGED
|
@@ -3,6 +3,8 @@ import creditsAvailableBadgeMessagesDe from './components/CreditsAvailableBadge/
|
|
|
3
3
|
import ctaMessagesDe from './slices/Cta/messages.de';
|
|
4
4
|
import customerCardMessagesDe from './components/CustomerCard/messages.de';
|
|
5
5
|
import customerQuoteCardMessagesDe from './components/CustomerQuoteCard/messages.de';
|
|
6
|
+
import eventCardMessagesDe from './components/EventCard/messages.de';
|
|
7
|
+
import eventsMessagesDe from './slices/Events/messages.de';
|
|
6
8
|
import glossaryMessagesDe from './slices/Glossary/messages.de';
|
|
7
9
|
import portfolioDocumentsDownloadListMessagesDe from './components/portfolio/DocumentsDownloadList/messages.de';
|
|
8
10
|
import projectFactsMessagesDe from './slices/ProjectFacts/messages.de';
|
|
@@ -21,6 +23,7 @@ const rootMessagesDe = {
|
|
|
21
23
|
// Components
|
|
22
24
|
//
|
|
23
25
|
...creditsAvailableBadgeMessagesDe,
|
|
26
|
+
...eventCardMessagesDe,
|
|
24
27
|
...portfolioDocumentsDownloadListMessagesDe,
|
|
25
28
|
...portfolioProjectInfoMessagesDe,
|
|
26
29
|
...portfolioSmallCheckoutMessagesDe,
|
|
@@ -33,6 +36,7 @@ const rootMessagesDe = {
|
|
|
33
36
|
...ctaMessagesDe,
|
|
34
37
|
...customerCardMessagesDe,
|
|
35
38
|
...customerQuoteCardMessagesDe,
|
|
39
|
+
...eventsMessagesDe,
|
|
36
40
|
...glossaryMessagesDe,
|
|
37
41
|
...projectFactsMessagesDe,
|
|
38
42
|
...projectsMapMessagesDe,
|
package/src/rootMessages.en.ts
CHANGED
|
@@ -3,6 +3,8 @@ import creditsAvailableBadgeMessagesEn from './components/CreditsAvailableBadge/
|
|
|
3
3
|
import ctaMessagesEn from './slices/Cta/messages.en';
|
|
4
4
|
import customerCardMessagesEn from './components/CustomerCard/messages.en';
|
|
5
5
|
import customerQuoteCardMessagesEn from './components/CustomerQuoteCard/messages.en';
|
|
6
|
+
import eventCardMessagesEn from './components/EventCard/messages.en';
|
|
7
|
+
import eventsMessagesEn from './slices/Events/messages.en';
|
|
6
8
|
import glossaryMessagesEn from './slices/Glossary/messages.en';
|
|
7
9
|
import portfolioDocumentsDownloadListMessagesEn from './components/portfolio/DocumentsDownloadList/messages.en';
|
|
8
10
|
import projectFactsMessagesEn from './slices/ProjectFacts/messages.en';
|
|
@@ -21,6 +23,7 @@ const rootMessagesEn = {
|
|
|
21
23
|
// Components
|
|
22
24
|
//
|
|
23
25
|
...creditsAvailableBadgeMessagesEn,
|
|
26
|
+
...eventCardMessagesEn,
|
|
24
27
|
...portfolioDocumentsDownloadListMessagesEn,
|
|
25
28
|
...portfolioProjectInfoMessagesEn,
|
|
26
29
|
...portfolioSmallCheckoutMessagesEn,
|
|
@@ -33,6 +36,7 @@ const rootMessagesEn = {
|
|
|
33
36
|
...ctaMessagesEn,
|
|
34
37
|
...customerCardMessagesEn,
|
|
35
38
|
...customerQuoteCardMessagesEn,
|
|
39
|
+
...eventsMessagesEn,
|
|
36
40
|
...glossaryMessagesEn,
|
|
37
41
|
...projectFactsMessagesEn,
|
|
38
42
|
...projectsMapMessagesEn,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StoryFn, Meta } from '@storybook/react';
|
|
3
|
+
|
|
4
|
+
import Events from '.';
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
title: 'slices/Events',
|
|
8
|
+
component: Events,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'fullscreen',
|
|
11
|
+
},
|
|
12
|
+
} as Meta<typeof Events>;
|
|
13
|
+
|
|
14
|
+
const Template: StoryFn<typeof Events> = (args) => <Events {...args} />;
|
|
15
|
+
|
|
16
|
+
export const Minimal = Template.bind({});
|
|
17
|
+
Minimal.args = {
|
|
18
|
+
slice: {
|
|
19
|
+
upcomingTitle: 'Tree.ly Events',
|
|
20
|
+
upcomingDescription: 'Join us for these amazing events',
|
|
21
|
+
pastTitle: 'Past Events',
|
|
22
|
+
pastDescription: 'Check out our past events',
|
|
23
|
+
filterSearch: false,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const WithFilterSearch = Template.bind({});
|
|
28
|
+
WithFilterSearch.args = {
|
|
29
|
+
slice: {
|
|
30
|
+
upcomingTitle: 'Tree.ly Events',
|
|
31
|
+
upcomingDescription: 'Join us for these amazing events',
|
|
32
|
+
pastTitle: 'Past Events',
|
|
33
|
+
pastDescription: 'Check out our past events',
|
|
34
|
+
filterSearch: true,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { mergeDeep } from '../../utils/mergeDeep';
|
|
2
|
+
import { EventsProps } from './Events';
|
|
3
|
+
import Events from '.';
|
|
4
|
+
import { render, screen, waitFor } from '../../test/testUtils';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { strapiEventMock } from '../../test/strapiMocks/strapiEventMock';
|
|
7
|
+
import useEvents from '../../models/hooks/useEvents';
|
|
8
|
+
import messagesEn from './messages.en';
|
|
9
|
+
import { EventType } from '../../models/strapi/StrapiEvent';
|
|
10
|
+
import userEvent from '@testing-library/user-event';
|
|
11
|
+
import messagesEnEventCard from '../../components/EventCard/messages.en';
|
|
12
|
+
|
|
13
|
+
jest.mock('../../models/hooks/useEvents');
|
|
14
|
+
|
|
15
|
+
const NOW = new Date('2025-01-01T12:00:00.000Z');
|
|
16
|
+
|
|
17
|
+
const pastEventMock = {
|
|
18
|
+
...strapiEventMock,
|
|
19
|
+
attributes: {
|
|
20
|
+
...strapiEventMock.attributes,
|
|
21
|
+
title: 'Past Event',
|
|
22
|
+
start: '2024-12-01T10:00:00.000Z',
|
|
23
|
+
eventTypes: [{ id: 1, eventType: EventType.MEET_UP }],
|
|
24
|
+
languages: [{ id: 1, language: 'English', countryCode: 'GB' }],
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const upcomingEventMock = {
|
|
29
|
+
...strapiEventMock,
|
|
30
|
+
attributes: {
|
|
31
|
+
...strapiEventMock.attributes,
|
|
32
|
+
title: 'Upcoming Event',
|
|
33
|
+
start: '2025-02-01T10:00:00.000Z',
|
|
34
|
+
eventTypes: [{ id: 1, eventType: EventType.CONFERENCE }],
|
|
35
|
+
languages: [{ id: 1, language: 'German', countryCode: 'DE' }],
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const defaultProps: EventsProps = {
|
|
40
|
+
slice: {
|
|
41
|
+
upcomingTitle: 'Upcoming Events',
|
|
42
|
+
upcomingDescription: 'Join us for these amazing events',
|
|
43
|
+
pastTitle: 'Past Events',
|
|
44
|
+
pastDescription: 'Check out our past events',
|
|
45
|
+
filterSearch: true,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
let getKeyCalls: string[] = [];
|
|
50
|
+
|
|
51
|
+
const setup = (props = {}) => {
|
|
52
|
+
const combinedProps = mergeDeep(defaultProps, props);
|
|
53
|
+
render(<Events {...combinedProps} />);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
describe('The Events slice', () => {
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
jest.useFakeTimers().setSystemTime(NOW);
|
|
59
|
+
getKeyCalls = [];
|
|
60
|
+
|
|
61
|
+
(useEvents as jest.Mock).mockImplementation(({ getKey }) => {
|
|
62
|
+
const key = getKey(0, null);
|
|
63
|
+
getKeyCalls.push(key.toString());
|
|
64
|
+
|
|
65
|
+
if (getKey.toString().includes('filters[start][$gte]')) {
|
|
66
|
+
return {
|
|
67
|
+
data: [{ body: { data: [upcomingEventMock] } }],
|
|
68
|
+
isLoading: false,
|
|
69
|
+
isLoadingMore: false,
|
|
70
|
+
canLoadMore: false,
|
|
71
|
+
loadMore: jest.fn(),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (getKey.toString().includes('filters[start][$lt]')) {
|
|
75
|
+
return {
|
|
76
|
+
data: [{ body: { data: [pastEventMock] } }],
|
|
77
|
+
isLoading: false,
|
|
78
|
+
isLoadingMore: false,
|
|
79
|
+
canLoadMore: false,
|
|
80
|
+
loadMore: jest.fn(),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
data: [],
|
|
85
|
+
isLoading: false,
|
|
86
|
+
isLoadingMore: false,
|
|
87
|
+
canLoadMore: false,
|
|
88
|
+
loadMore: jest.fn(),
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
afterEach(() => {
|
|
93
|
+
jest.clearAllMocks();
|
|
94
|
+
jest.useRealTimers();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('displays the title and tagline if it is defined', async () => {
|
|
98
|
+
setup();
|
|
99
|
+
|
|
100
|
+
await waitFor(() => {
|
|
101
|
+
expect(screen.getByText('Upcoming Events')).toBeInTheDocument();
|
|
102
|
+
expect(screen.getByText('Past Events')).toBeInTheDocument();
|
|
103
|
+
expect(
|
|
104
|
+
screen.getByText('Join us for these amazing events')
|
|
105
|
+
).toBeInTheDocument();
|
|
106
|
+
expect(screen.getByText('Check out our past events')).toBeInTheDocument();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('displays upcoming and past events correctly', async () => {
|
|
111
|
+
setup();
|
|
112
|
+
|
|
113
|
+
await waitFor(() => {
|
|
114
|
+
expect(screen.getByText('Past Event')).toBeInTheDocument();
|
|
115
|
+
expect(screen.getByText('Upcoming Event')).toBeInTheDocument();
|
|
116
|
+
expect(screen.queryAllByText('Event Description')).toHaveLength(2);
|
|
117
|
+
expect(
|
|
118
|
+
screen.getByText(
|
|
119
|
+
messagesEnEventCard['sections.eventCard.eventType.meetup']
|
|
120
|
+
)
|
|
121
|
+
).toBeInTheDocument();
|
|
122
|
+
expect(
|
|
123
|
+
screen.getByText(
|
|
124
|
+
messagesEnEventCard['sections.eventCard.eventType.conference']
|
|
125
|
+
)
|
|
126
|
+
).toBeInTheDocument();
|
|
127
|
+
expect(screen.getByText('English')).toBeInTheDocument();
|
|
128
|
+
expect(screen.getByText('German')).toBeInTheDocument();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('does not display the load more button when there are no more events to load', async () => {
|
|
133
|
+
setup();
|
|
134
|
+
|
|
135
|
+
await waitFor(() => {
|
|
136
|
+
expect(
|
|
137
|
+
screen.queryByText(messagesEn['sections.events.loadMore'])
|
|
138
|
+
).not.toBeInTheDocument();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('displays the load more button when there are more events to load', async () => {
|
|
143
|
+
// Use past events for the testing, because the batch for past events is defined as 2 and
|
|
144
|
+
// the "Load button" will be shown when there are more then 2 past events
|
|
145
|
+
(useEvents as jest.Mock).mockImplementation(({ getKey }) => {
|
|
146
|
+
if (getKey.toString().includes('filters[start][$lt]')) {
|
|
147
|
+
return {
|
|
148
|
+
data: [
|
|
149
|
+
{
|
|
150
|
+
body: {
|
|
151
|
+
data: [
|
|
152
|
+
pastEventMock,
|
|
153
|
+
{
|
|
154
|
+
...pastEventMock,
|
|
155
|
+
id: 2,
|
|
156
|
+
attributes: {
|
|
157
|
+
...pastEventMock.attributes,
|
|
158
|
+
title: 'Past Event 2',
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
...pastEventMock,
|
|
163
|
+
id: 3,
|
|
164
|
+
attributes: {
|
|
165
|
+
...pastEventMock.attributes,
|
|
166
|
+
title: 'Past Event 3',
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
isLoading: false,
|
|
174
|
+
isLoadingMore: false,
|
|
175
|
+
canLoadMore: true,
|
|
176
|
+
loadMore: jest.fn(),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
setup();
|
|
180
|
+
|
|
181
|
+
expect(
|
|
182
|
+
screen.getByText(messagesEn['sections.events.loadMore'])
|
|
183
|
+
).toBeInTheDocument();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('display "no events" message if there are no events to display', async () => {
|
|
188
|
+
(useEvents as jest.Mock).mockImplementation(() => {
|
|
189
|
+
return {
|
|
190
|
+
data: [],
|
|
191
|
+
isLoading: false,
|
|
192
|
+
isLoadingMore: false,
|
|
193
|
+
canLoadMore: false,
|
|
194
|
+
loadMore: jest.fn(),
|
|
195
|
+
};
|
|
196
|
+
});
|
|
197
|
+
setup();
|
|
198
|
+
|
|
199
|
+
await waitFor(() => {
|
|
200
|
+
expect(
|
|
201
|
+
screen.getByText(messagesEn['sections.events.noUpcomingEvents'])
|
|
202
|
+
).toBeInTheDocument();
|
|
203
|
+
expect(
|
|
204
|
+
screen.getByText(messagesEn['sections.events.noPastEvents'])
|
|
205
|
+
).toBeInTheDocument();
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe('filters the events', () => {
|
|
210
|
+
it('by event type', async () => {
|
|
211
|
+
setup();
|
|
212
|
+
|
|
213
|
+
const selectButtons = await screen.findAllByRole('combobox');
|
|
214
|
+
expect(selectButtons).toHaveLength(3);
|
|
215
|
+
expect(selectButtons[0]).toHaveTextContent(
|
|
216
|
+
messagesEn['sections.events.eventsFilter.eventType']
|
|
217
|
+
);
|
|
218
|
+
const selectButton = selectButtons[0];
|
|
219
|
+
|
|
220
|
+
userEvent.click(selectButton);
|
|
221
|
+
|
|
222
|
+
await waitFor(async () => {
|
|
223
|
+
const options = await screen.getAllByRole('menuitemradio', {
|
|
224
|
+
hidden: true,
|
|
225
|
+
});
|
|
226
|
+
expect(options).toHaveLength(2);
|
|
227
|
+
expect(options[0]).toHaveTextContent(
|
|
228
|
+
messagesEnEventCard['sections.eventCard.eventType.conference']
|
|
229
|
+
);
|
|
230
|
+
expect(options[1]).toHaveTextContent(
|
|
231
|
+
messagesEnEventCard['sections.eventCard.eventType.meetup']
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
userEvent.click(options[0]);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
await waitFor(() => {
|
|
238
|
+
expect(getKeyCalls.map(decodeURIComponent)).toEqual(
|
|
239
|
+
expect.arrayContaining([
|
|
240
|
+
expect.stringContaining('filters[start][$gte]'),
|
|
241
|
+
expect.stringContaining(
|
|
242
|
+
'filters[$and][0][eventTypes][eventType]=Conference'
|
|
243
|
+
),
|
|
244
|
+
])
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('by language', async () => {
|
|
250
|
+
setup();
|
|
251
|
+
|
|
252
|
+
const selectButtons = await screen.findAllByRole('combobox');
|
|
253
|
+
expect(selectButtons).toHaveLength(3);
|
|
254
|
+
expect(selectButtons[1]).toHaveTextContent(
|
|
255
|
+
messagesEn['sections.events.eventsFilter.language']
|
|
256
|
+
);
|
|
257
|
+
const selectButton = selectButtons[1];
|
|
258
|
+
|
|
259
|
+
userEvent.click(selectButton);
|
|
260
|
+
|
|
261
|
+
await waitFor(async () => {
|
|
262
|
+
const options = await screen.getAllByRole('menuitemradio', {
|
|
263
|
+
hidden: true,
|
|
264
|
+
});
|
|
265
|
+
expect(options).toHaveLength(2);
|
|
266
|
+
expect(options[0]).toHaveTextContent('German');
|
|
267
|
+
expect(options[1]).toHaveTextContent('English');
|
|
268
|
+
|
|
269
|
+
userEvent.click(options[0]);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
await waitFor(() => {
|
|
273
|
+
expect(getKeyCalls.map(decodeURIComponent)).toEqual(
|
|
274
|
+
expect.arrayContaining([
|
|
275
|
+
expect.stringContaining('filters[start][$gte]'),
|
|
276
|
+
expect.stringContaining(
|
|
277
|
+
'filters[$and][0][languages][language]=German'
|
|
278
|
+
),
|
|
279
|
+
])
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
describe('sorts the events', () => {
|
|
286
|
+
it('by newest first', async () => {
|
|
287
|
+
setup();
|
|
288
|
+
|
|
289
|
+
const selectButtons = await screen.findAllByRole('combobox');
|
|
290
|
+
expect(selectButtons).toHaveLength(3);
|
|
291
|
+
// Default value of "Sort by" select is "Newest first"
|
|
292
|
+
expect(selectButtons[2]).toHaveTextContent(
|
|
293
|
+
messagesEn['sections.events.eventsFilter.sortBy.newest']
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
await waitFor(() => {
|
|
297
|
+
expect(getKeyCalls.map(decodeURIComponent)).toEqual(
|
|
298
|
+
expect.arrayContaining([
|
|
299
|
+
expect.stringContaining(
|
|
300
|
+
`filters[start][$gte]=${NOW.toISOString()}`
|
|
301
|
+
),
|
|
302
|
+
])
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('by oldest first', async () => {
|
|
308
|
+
setup();
|
|
309
|
+
|
|
310
|
+
const selectButtons = await screen.findAllByRole('combobox');
|
|
311
|
+
expect(selectButtons).toHaveLength(3);
|
|
312
|
+
// Default value of "Sort by" select is "Newest first"
|
|
313
|
+
expect(selectButtons[2]).toHaveTextContent(
|
|
314
|
+
messagesEn['sections.events.eventsFilter.sortBy.newest']
|
|
315
|
+
);
|
|
316
|
+
const selectButton = selectButtons[2];
|
|
317
|
+
|
|
318
|
+
userEvent.click(selectButton);
|
|
319
|
+
|
|
320
|
+
await waitFor(async () => {
|
|
321
|
+
const options = await screen.getAllByRole('menuitemradio', {
|
|
322
|
+
hidden: true,
|
|
323
|
+
});
|
|
324
|
+
expect(options).toHaveLength(2);
|
|
325
|
+
expect(options[0]).toHaveTextContent(
|
|
326
|
+
messagesEn['sections.events.eventsFilter.sortBy.newest']
|
|
327
|
+
);
|
|
328
|
+
expect(options[1]).toHaveTextContent(
|
|
329
|
+
messagesEn['sections.events.eventsFilter.sortBy.oldest']
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
userEvent.click(options[1]);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
await waitFor(() => {
|
|
336
|
+
expect(getKeyCalls.map(decodeURIComponent)).toEqual(
|
|
337
|
+
expect.arrayContaining([
|
|
338
|
+
expect.stringContaining(`filters[start][$lt]=${NOW.toISOString()}`),
|
|
339
|
+
])
|
|
340
|
+
);
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
});
|