@treely/strapi-slices 7.4.2 → 7.5.1

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.
Files changed (56) hide show
  1. package/dist/components/PreviewAlert/PreviewAlert.stories.d.ts +2 -2
  2. package/dist/integrations/strapi/getAvailableLocalesFromStrapi.d.ts +2 -0
  3. package/dist/slices/BlogCards/BlogCards.stories.d.ts +4 -4
  4. package/dist/slices/CarouselMarqueeBanner/CarouselMarqueeBanner.stories.d.ts +3 -3
  5. package/dist/slices/Comparison/Comparison.stories.d.ts +5 -5
  6. package/dist/slices/Cta/Cta.stories.d.ts +8 -8
  7. package/dist/slices/CtaOnly/CtaOnly.stories.d.ts +2 -2
  8. package/dist/slices/CustomerStories/CustomerStories.stories.d.ts +5 -5
  9. package/dist/slices/Events/Events.stories.d.ts +3 -3
  10. package/dist/slices/Facts/Facts.stories.d.ts +6 -6
  11. package/dist/slices/FullWidthHighlightQuote/FullWidthHighlightQuote.stories.d.ts +3 -3
  12. package/dist/slices/FullWidthImage/FullWidthImage.stories.d.ts +4 -4
  13. package/dist/slices/FullWidthImageSlider/FullWidthImageSlider.stories.d.ts +2 -2
  14. package/dist/slices/Glossary/Glossary.stories.d.ts +2 -2
  15. package/dist/slices/Hero/Hero.stories.d.ts +8 -8
  16. package/dist/slices/IconGrid/IconGrid.stories.d.ts +4 -4
  17. package/dist/slices/ImageGrid/ImageGrid.stories.d.ts +4 -4
  18. package/dist/slices/ImageTextSequence/ImageTextSequence.stories.d.ts +6 -6
  19. package/dist/slices/LeftTextRightCard/LeftTextRightCard.stories.d.ts +7 -7
  20. package/dist/slices/LinkCardsGrid/LinkCardsGrid.stories.d.ts +4 -4
  21. package/dist/slices/LogoGridWithText/LogoGridWithText.stories.d.ts +5 -5
  22. package/dist/slices/MapHero/MapHero.stories.d.ts +7 -7
  23. package/dist/slices/ProjectFacts/ProjectFacts.stories.d.ts +7 -7
  24. package/dist/slices/ProjectsGrid/ProjectsGrid.stories.d.ts +4 -4
  25. package/dist/slices/ProjectsMap/ProjectsMap.stories.d.ts +5 -5
  26. package/dist/slices/QAndA/QAndA.stories.d.ts +6 -6
  27. package/dist/slices/QuoteCards/QuoteCards.stories.d.ts +7 -7
  28. package/dist/slices/RichTextSection/RichTextSection.stories.d.ts +2 -2
  29. package/dist/slices/ShopCheckout/ShopCheckout.stories.d.ts +7 -7
  30. package/dist/slices/SideBySideImages/SideBySideImages.stories.d.ts +2 -2
  31. package/dist/slices/SmallHero/SmallHero.stories.d.ts +10 -10
  32. package/dist/slices/Steps/Steps.stories.d.ts +6 -6
  33. package/dist/slices/TextCardGrid/TextCardGrid.stories.d.ts +7 -7
  34. package/dist/slices/TextCarousel/TextCarousel.stories.d.ts +7 -7
  35. package/dist/slices/TextWithCard/TextWithCard.stories.d.ts +9 -9
  36. package/dist/slices/TextWithTextCards/TextWithTextCards.stories.d.ts +6 -6
  37. package/dist/slices/Timeline/Timeline.stories.d.ts +8 -8
  38. package/dist/slices/Video/Video.stories.d.ts +2 -2
  39. package/dist/strapi-slices.cjs.development.js +166 -130
  40. package/dist/strapi-slices.cjs.development.js.map +1 -1
  41. package/dist/strapi-slices.cjs.production.min.js +1 -1
  42. package/dist/strapi-slices.cjs.production.min.js.map +1 -1
  43. package/dist/strapi-slices.esm.js +166 -130
  44. package/dist/strapi-slices.esm.js.map +1 -1
  45. package/package.json +17 -12
  46. package/src/integrations/strapi/getAllSlugsFromStrapi.test.ts +30 -5
  47. package/src/integrations/strapi/getAllSlugsFromStrapi.ts +39 -26
  48. package/src/integrations/strapi/getAvailableLocalesFromStrapi.test.ts +22 -0
  49. package/src/integrations/strapi/getAvailableLocalesFromStrapi.ts +8 -0
  50. package/src/integrations/strapi/getPortfolioProjects.ts +1 -1
  51. package/src/integrations/strapi/getStrapiCollectionType.test.ts +41 -10
  52. package/src/integrations/strapi/getStrapiCollectionType.ts +30 -17
  53. package/src/integrations/strapi/getStrapiSingleType.ts +1 -1
  54. package/src/integrations/strapi/strapiClient.ts +1 -0
  55. package/src/slices/Events/Events.tsx +8 -3
  56. package/src/stories/{Introduction.stories.mdx → Introduction.mdx} +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treely/strapi-slices",
3
- "version": "7.4.2",
3
+ "version": "7.5.1",
4
4
  "license": "MIT",
5
5
  "author": "Tree.ly FlexCo",
6
6
  "description": "@treely/strapi-slices is a open source library maintained by Tree.ly.",
@@ -99,22 +99,27 @@
99
99
  }
100
100
  ],
101
101
  "devDependencies": {
102
+ "@chromatic-com/storybook": "^3.2.5",
102
103
  "@size-limit/preset-small-lib": "^11.0.0",
103
- "@storybook/addon-essentials": "^7.5.3",
104
- "@storybook/addon-interactions": "^7.5.3",
105
- "@storybook/addon-links": "^7.5.3",
106
- "@storybook/addon-onboarding": "^1.0.8",
107
- "@storybook/addons": "^7.5.3",
108
- "@storybook/blocks": "^7.5.3",
109
- "@storybook/nextjs": "^7.5.3",
110
- "@storybook/react": "^7.5.3",
111
- "@storybook/testing-library": "^0.2.2",
104
+ "@storybook/addon-essentials": "^8.6.4",
105
+ "@storybook/addon-interactions": "^8.6.4",
106
+ "@storybook/addon-links": "^8.6.4",
107
+ "@storybook/addon-onboarding": "^8.6.4",
108
+ "@storybook/blocks": "^8.6.4",
109
+ "@storybook/manager-api": "^8.6.4",
110
+ "@storybook/nextjs": "^8.6.4",
111
+ "@storybook/preview-api": "^8.6.4",
112
+ "@storybook/react": "^8.6.4",
113
+ "@storybook/test": "^8.6.4",
114
+ "@storybook/theming": "^8.6.4",
115
+ "@storybook/types": "^8.6.4",
112
116
  "@testing-library/jest-dom": "^6.1.4",
113
117
  "@testing-library/react": "^14.1.2",
114
118
  "@testing-library/user-event": "^14.5.1",
115
119
  "@tsconfig/recommended": "^1.0.3",
116
120
  "@tsconfig/vite-react": "^2.0.1",
117
121
  "@types/mapbox-gl": "^2.7.19",
122
+ "@types/qs": "^6.9.18",
118
123
  "@types/react": "^18.2.38",
119
124
  "@types/react-dom": "^18.2.17",
120
125
  "@typescript-eslint/eslint-plugin": "^7.9.0",
@@ -133,7 +138,7 @@
133
138
  "react-dom": "^18.2.0",
134
139
  "semantic-release": "^22.0.8",
135
140
  "size-limit": "^11.0.0",
136
- "storybook": "^7.5.3",
141
+ "storybook": "^8.6.4",
137
142
  "typescript": "^5.3.2"
138
143
  },
139
144
  "dependencies": {
@@ -141,7 +146,7 @@
141
146
  "adblock-detect-react": "^1.1.0",
142
147
  "axios": "^1.7.2",
143
148
  "axios-cache-interceptor": "^1.5.3",
144
- "boemly": "^7.5.0",
149
+ "boemly": "^7.6.0",
145
150
  "embla-carousel-auto-scroll": "^8.5.1",
146
151
  "embla-carousel-autoplay": "^8.5.1",
147
152
  "embla-carousel-react": "^8.5.1",
@@ -2,24 +2,49 @@ import MockAxios from 'jest-mock-axios';
2
2
  import getAllSlugsFromStrapi from './getAllSlugsFromStrapi';
3
3
  import StrapiPage from '../../models/strapi/StrapiPage';
4
4
  import { strapiPageMock } from '../../test/strapiMocks/strapiPage';
5
+ import getAvailableLocalesFromStrapi from './getAvailableLocalesFromStrapi';
6
+
7
+ jest.mock('./getAvailableLocalesFromStrapi', () => ({
8
+ __esModule: true,
9
+ default: jest.fn(),
10
+ }));
5
11
 
6
12
  describe('The getAllSlugsFromStrapi function', () => {
7
13
  afterEach(() => {
8
14
  MockAxios.reset();
15
+ jest.clearAllMocks();
9
16
  });
10
17
 
11
18
  it('returns all slugs and creates a fallback for locales that dont have a translation', async () => {
19
+ (getAvailableLocalesFromStrapi as jest.Mock).mockResolvedValue([
20
+ 'en',
21
+ 'de',
22
+ 'hu',
23
+ ]);
24
+
12
25
  const slugsPromise = getAllSlugsFromStrapi<StrapiPage>('/api/pages', [
13
26
  'en',
14
27
  'de',
15
28
  'hu',
16
29
  ]);
17
30
 
18
- // This page is avaliable in 'de' and 'en'
19
- MockAxios.mockResponseFor(
20
- { url: '/api/pages' },
21
- { data: { data: [strapiPageMock] } }
22
- );
31
+ // This page is available in 'de', 'en', and 'hu'
32
+ MockAxios.get
33
+ .mockResolvedValueOnce({ data: { data: [strapiPageMock] } }) // english
34
+ .mockResolvedValueOnce({
35
+ data: {
36
+ data: [
37
+ {
38
+ ...strapiPageMock,
39
+ attributes: {
40
+ ...strapiPageMock.attributes,
41
+ locale: 'de',
42
+ },
43
+ },
44
+ ],
45
+ },
46
+ })
47
+ .mockResolvedValueOnce({ data: { data: [] } });
23
48
 
24
49
  const slugs = await slugsPromise;
25
50
 
@@ -6,6 +6,7 @@ import {
6
6
  import IStrapiResponse from '../../models/strapi/IStrapiResponse';
7
7
  import IStrapiData from '../../models/strapi/IStrapiData';
8
8
  import LocalizedEntity from '../../models/LocalizedEntity';
9
+ import getAvailableLocalesFromStrapi from './getAvailableLocalesFromStrapi';
9
10
 
10
11
  interface Options {
11
12
  filters?: Record<string, any>;
@@ -18,33 +19,45 @@ const getAllSlugsFromStrapi = async <T extends LocalizedEntity<'slug'>>(
18
19
  locales: string[],
19
20
  { filters = {} }: Options = { filters: {} }
20
21
  ): Promise<Slug[]> => {
21
- const params: Record<string, any> = {
22
- locale: 'all',
23
- 'pagination[pageSize]': STRAPI_DEFAULT_PAGE_SIZE,
24
- filters,
25
- };
26
-
27
- const { data } = await strapiClient.get<IStrapiResponse<IStrapiData<T>[]>>(
28
- path,
29
- { params }
30
- );
31
-
32
- const slugs: Slug[] = data.data.map((page) => ({
33
- slug: page.attributes.slug,
34
- locale: page.attributes.locale,
35
- }));
36
-
37
- const fallBackSlugs: Slug[] = locales.flatMap((locale) =>
38
- slugs
22
+ const allLocales = await getAvailableLocalesFromStrapi();
23
+
24
+ const slugPromises = allLocales.map((locale) => {
25
+ const params: Record<string, any> = {
26
+ locale,
27
+ 'pagination[pageSize]': STRAPI_DEFAULT_PAGE_SIZE,
28
+ filters,
29
+ };
30
+
31
+ return strapiClient.get<IStrapiResponse<IStrapiData<T>[]>>(path, {
32
+ params,
33
+ });
34
+ });
35
+
36
+ const slugResults = await Promise.all(slugPromises);
37
+
38
+ let allSlugs = slugResults
39
+ .map((result) =>
40
+ result.data.data.map((page) => ({
41
+ slug: page.attributes.slug,
42
+ locale: page.attributes.locale,
43
+ }))
44
+ )
45
+ .flat();
46
+
47
+ // Identify missing locales for each slug
48
+ const missingLocales = locales.flatMap((locale) => {
49
+ return allSlugs
39
50
  .filter((slug) => slug.locale === STRAPI_FALLBACK_LOCALE)
40
- .map((slug) => ({ ...slug, locale }))
41
- );
42
-
43
- const nonFallbackSlugs = slugs.filter(
44
- (p) => p.locale !== STRAPI_FALLBACK_LOCALE
45
- );
46
-
47
- return [...fallBackSlugs, ...nonFallbackSlugs];
51
+ .filter(
52
+ (fallbackSlug) =>
53
+ !allSlugs.some(
54
+ (slug) => slug.slug === fallbackSlug.slug && slug.locale === locale
55
+ )
56
+ )
57
+ .map((fallbackSlug) => ({ ...fallbackSlug, locale })); // Clone only for missing locales
58
+ });
59
+
60
+ return [...allSlugs, ...missingLocales]; // Merge original and missing slugs
48
61
  };
49
62
 
50
63
  export default getAllSlugsFromStrapi;
@@ -0,0 +1,22 @@
1
+ import getAvailableLocalesFromStrapi from './getAvailableLocalesFromStrapi';
2
+ import strapiClient from './strapiClient';
3
+
4
+ jest.mock('./strapiClient', () => ({
5
+ get: jest.fn(),
6
+ }));
7
+
8
+ describe('The getAvailableLocales function', () => {
9
+ it('should fetch available locales and return them correctly', async () => {
10
+ const mockResponse = {
11
+ data: [{ code: 'en' }, { code: 'de' }, { code: 'hu' }],
12
+ };
13
+
14
+ (strapiClient.get as jest.Mock).mockResolvedValue(mockResponse);
15
+
16
+ const locales = await getAvailableLocalesFromStrapi();
17
+
18
+ expect(locales).toEqual(['en', 'de', 'hu']);
19
+ expect(strapiClient.get).toHaveBeenCalledWith('/i18n/locales');
20
+ expect(strapiClient.get).toHaveBeenCalledTimes(1);
21
+ });
22
+ });
@@ -0,0 +1,8 @@
1
+ import strapiClient from './strapiClient';
2
+
3
+ const getAvailableLocalesFromStrapi = async (): Promise<string[]> => {
4
+ const { data } = await strapiClient.get('/i18n/locales');
5
+ return data.map((locale: { code: string }) => locale.code);
6
+ };
7
+
8
+ export default getAvailableLocalesFromStrapi;
@@ -17,7 +17,7 @@ const getPortfolioProjects = async (
17
17
  ): Promise<PortfolioProject[]> => {
18
18
  const cache = preview ? false : undefined;
19
19
  const params: Record<string, any> = {
20
- populate: 'deep,6',
20
+ pLevel: '6',
21
21
  locale,
22
22
  'pagination[pageSize]': STRAPI_DEFAULT_PAGE_SIZE,
23
23
  };
@@ -2,6 +2,12 @@ import MockAxios from 'jest-mock-axios';
2
2
  import getStrapiCollectionType from './getStrapiCollectionType';
3
3
  import StrapiPage from '../../models/strapi/StrapiPage';
4
4
  import { strapiPageMock } from '../../test/strapiMocks/strapiPage';
5
+ import getAvailableLocalesFromStrapi from './getAvailableLocalesFromStrapi';
6
+
7
+ jest.mock('./getAvailableLocalesFromStrapi', () => ({
8
+ __esModule: true,
9
+ default: jest.fn(),
10
+ }));
5
11
 
6
12
  describe('The getStrapiCollectionType function', () => {
7
13
  const germanStrapiPageMock = {
@@ -15,20 +21,39 @@ describe('The getStrapiCollectionType function', () => {
15
21
 
16
22
  afterEach(() => {
17
23
  MockAxios.reset();
24
+ jest.clearAllMocks();
18
25
  });
19
26
 
20
27
  it('returns the localized versions if available', async () => {
28
+ (getAvailableLocalesFromStrapi as jest.Mock).mockResolvedValue([
29
+ 'en',
30
+ 'de',
31
+ 'hu',
32
+ ]);
33
+
34
+ MockAxios.get
35
+ .mockResolvedValueOnce({ data: { data: [strapiPageMock] } }) // english
36
+ .mockResolvedValueOnce({
37
+ data: {
38
+ data: [
39
+ {
40
+ ...germanStrapiPageMock,
41
+ attributes: {
42
+ ...germanStrapiPageMock.attributes,
43
+ locale: 'de',
44
+ },
45
+ },
46
+ ],
47
+ },
48
+ })
49
+ .mockResolvedValueOnce({ data: { data: [] } });
50
+
21
51
  const pages = getStrapiCollectionType<StrapiPage, 'slug'>(
22
52
  '/api/pages',
23
53
  'slug',
24
54
  { locale: 'de' }
25
55
  );
26
56
 
27
- MockAxios.mockResponseFor(
28
- { url: '/api/pages' },
29
- { data: { data: [strapiPageMock, germanStrapiPageMock] } }
30
- );
31
-
32
57
  const page = await pages;
33
58
 
34
59
  expect(page).toStrictEqual([
@@ -41,17 +66,23 @@ describe('The getStrapiCollectionType function', () => {
41
66
  });
42
67
 
43
68
  it('returns the english versions if no localized version is available', async () => {
69
+ (getAvailableLocalesFromStrapi as jest.Mock).mockResolvedValue([
70
+ 'en',
71
+ 'de',
72
+ 'hu',
73
+ ]);
74
+
75
+ MockAxios.get
76
+ .mockResolvedValueOnce({ data: { data: [strapiPageMock] } }) // english
77
+ .mockResolvedValueOnce({ data: { data: [] } })
78
+ .mockResolvedValueOnce({ data: { data: [] } });
79
+
44
80
  const pages = getStrapiCollectionType<StrapiPage, 'slug'>(
45
81
  '/api/pages',
46
82
  'slug',
47
83
  { locale: 'de' }
48
84
  );
49
85
 
50
- MockAxios.mockResponseFor(
51
- { url: '/api/pages' },
52
- { data: { data: [strapiPageMock] } }
53
- );
54
-
55
86
  const page = await pages;
56
87
 
57
88
  expect(page).toStrictEqual([
@@ -6,6 +6,7 @@ import {
6
6
  import IStrapiData from '../../models/strapi/IStrapiData';
7
7
  import IStrapiResponse from '../../models/strapi/IStrapiResponse';
8
8
  import LocalizedEntity from '../../models/LocalizedEntity';
9
+ import getAvailableLocalesFromStrapi from './getAvailableLocalesFromStrapi';
9
10
 
10
11
  interface Options {
11
12
  locale?: string;
@@ -23,31 +24,43 @@ const getStrapiCollectionType = async <
23
24
  { locale = 'en', preview = false, filters = {} }: Options
24
25
  ): Promise<IStrapiData<T>[]> => {
25
26
  const cache = preview ? false : undefined;
26
- const params: Record<string, any> = {
27
- populate: 'deep,6',
28
- locale: 'all',
29
- 'pagination[pageSize]': STRAPI_DEFAULT_PAGE_SIZE,
30
- filters,
31
- };
32
-
33
- if (preview) {
34
- params.publicationState = 'preview';
27
+ const allLocales = await getAvailableLocalesFromStrapi();
28
+
29
+ if (!allLocales.includes(STRAPI_FALLBACK_LOCALE)) {
30
+ allLocales.push(STRAPI_FALLBACK_LOCALE);
35
31
  }
36
32
 
37
- const { data } = await strapiClient.get<IStrapiResponse<IStrapiData<T>[]>>(
38
- path,
39
- { params, cache }
40
- );
33
+ const promises = allLocales.map((loc) => {
34
+ const params: Record<string, any> = {
35
+ pLevel: '6',
36
+ locale: loc,
37
+ 'pagination[pageSize]': STRAPI_DEFAULT_PAGE_SIZE,
38
+ filters,
39
+ };
40
+
41
+ if (preview) {
42
+ params.publicationState = 'preview';
43
+ }
44
+
45
+ return strapiClient.get<IStrapiResponse<IStrapiData<T>[]>>(path, {
46
+ params,
47
+ cache,
48
+ });
49
+ });
50
+
51
+ const responses = await Promise.all(promises);
52
+
53
+ const results = responses.flatMap((response) => response.data.data);
41
54
 
42
- const localizedResponses = data.data.filter(
55
+ const localizedResponses = results.filter(
43
56
  (d) => d.attributes.locale === locale
44
57
  );
45
58
 
46
- const fallbackResponses = data.data.filter(
59
+ const fallbackResponses = results.filter(
47
60
  (d) => d.attributes.locale === STRAPI_FALLBACK_LOCALE
48
61
  );
49
62
 
50
- const responses = fallbackResponses.map((fallbackResponse) => {
63
+ const result = fallbackResponses.map((fallbackResponse) => {
51
64
  const localizedResponse = localizedResponses.find(
52
65
  (localized) =>
53
66
  localized.attributes[key] === fallbackResponse.attributes[key]
@@ -56,7 +69,7 @@ const getStrapiCollectionType = async <
56
69
  return localizedResponse || fallbackResponse;
57
70
  });
58
71
 
59
- return responses;
72
+ return result;
60
73
  };
61
74
 
62
75
  export default getStrapiCollectionType;
@@ -19,7 +19,7 @@ const getStrapiSingleType = async <T>(
19
19
  ): Promise<IStrapiData<T>> => {
20
20
  const cache = preview ? false : undefined;
21
21
  const params: Record<string, any> = {
22
- populate: 'deep,6',
22
+ pLevel: '6',
23
23
  locale,
24
24
  'pagination[pageSize]': STRAPI_DEFAULT_PAGE_SIZE,
25
25
  filters,
@@ -6,6 +6,7 @@ import { STRAPI_URI } from '../../constants/strapi';
6
6
  const strapiClient = setupCache(
7
7
  axios.create({
8
8
  baseURL: `${STRAPI_URI}/api`,
9
+ headers: { 'Strapi-Response-Format': 'v4' },
9
10
  paramsSerializer: (p) => qs.stringify(p, { encodeValuesOnly: true }),
10
11
  timeout: 60_000,
11
12
  }),
@@ -90,7 +90,7 @@ export const Events: React.FC<EventsProps> = ({ slice }: EventsProps) => {
90
90
  url.searchParams.append('pagination[limit]', batchSize.toString());
91
91
  url.searchParams.append(startFilter, now);
92
92
  url.searchParams.append('locale', locale);
93
- url.searchParams.append('populate', 'deep,6');
93
+ url.searchParams.append('pLevel', '6');
94
94
 
95
95
  if (sort[0] === Sort.OLDEST_FIRST) {
96
96
  url.searchParams.append('sort', 'start:asc');
@@ -165,10 +165,15 @@ export const Events: React.FC<EventsProps> = ({ slice }: EventsProps) => {
165
165
  const fetchAllOptions = useCallback(async () => {
166
166
  const url = new URL(`/treely-events`, STRAPI_URI);
167
167
  url.searchParams.append('locale', locale);
168
- url.searchParams.append('populate', 'deep,6');
168
+ url.searchParams.append('pLevel', '6');
169
169
 
170
170
  const response = await fetch(
171
- `${STRAPI_URI}/api/treely-events${url.search}`
171
+ `${STRAPI_URI}/api/treely-events${url.search}`,
172
+ {
173
+ headers: {
174
+ 'Strapi-Response-Format': 'v4',
175
+ },
176
+ }
172
177
  );
173
178
  const data = await response.json();
174
179
 
@@ -1,4 +1,4 @@
1
- import { Meta } from '@storybook/addon-docs';
1
+ import { Meta } from '@storybook/blocks';
2
2
 
3
3
  <Meta title="Introduction" />
4
4