gatsby-core-theme 44.0.29 → 44.0.30

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/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## [44.0.30](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.0.29...v44.0.30) (2025-05-05)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * add checkers ([95f8ac7](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/95f8ac74e53cdbab0e420edaf29d3867a0d6a9cc))
7
+ * add more tests ([2abb622](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/2abb62253a36a451ba8b62b3152739e5e6bde7d9))
8
+ * add more tests ([2f148e5](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/2f148e56ed676ddfc96447f1f9679190b56de8ca))
9
+ * enable recommended casino market ([d6a669f](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/d6a669f0ff9f75d7b9d5073f4f3d0cca168de865))
10
+ * update search page as well ([a49faf1](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/a49faf139e2e33f6479ffe6e3d8c01117c25509f))
11
+ * update tests ([bab0968](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/bab09682b9ef87241d9eef4081fb5f7220b13331))
12
+
13
+
14
+ * Merge branch 'add-checkers' into 'master' ([4666161](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/4666161d06ea6cf742e9917456085122dd10f4ff))
15
+ * Merge branch 'tm-5434-enable-recommended-casino-market' into 'master' ([a481ab4](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/a481ab4cac0abec7cddda11b921ace2f426a9dd8))
16
+
1
17
  ## [44.0.29](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.0.28...v44.0.29) (2025-05-02)
2
18
 
3
19
 
package/gatsby-node.mjs CHANGED
@@ -381,7 +381,7 @@ export const createPages = async (
381
381
  const contextData = clean({
382
382
  page,
383
383
  allMarkets: allMarketsData,
384
- marketSections: removeUnwantedSections(marketSection, page.type),
384
+ marketSections: removeUnwantedSections(marketSection, page.type, ribbonsData),
385
385
  prefilledModules,
386
386
  siteGeneralData,
387
387
  autogenerated,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gatsby-core-theme",
3
- "version": "44.0.29",
3
+ "version": "44.0.30",
4
4
  "description": "Gatsby Theme NPM Package",
5
5
  "author": "",
6
6
  "license": "ISC",
@@ -83,9 +83,8 @@ const Main = ({
83
83
  )}
84
84
  {pathPrefixes && (
85
85
  <Search
86
- page={page}
87
86
  serverData={serverData}
88
- allMarkets={pageContext.allMarkets}
87
+ pageContext={pageContext}
89
88
  />
90
89
  )}
91
90
  {Newsletter && (
@@ -12,18 +12,35 @@ import { sortDateOn, sortIntOn, sortStringOn } from '~helpers/search';
12
12
  import { getOrderedKeys } from '../../../helpers/getPrioritySortedKeys';
13
13
  import CustomSelect from '~atoms/custom-select';
14
14
  import Tabs from '~hooks/tabs';
15
+ import { mainSettings } from "../../../constants/site-settings/main";
16
+ import VariableComponent from "../../organisms/search/variableComponent";
15
17
 
16
18
  const Search = ({
17
- page,
18
19
  serverData,
19
20
  pageSearchOptions,
20
21
  showNumberOfResults = true,
21
- allMarkets,
22
+ pageContext,
22
23
  }) => {
24
+ const { page, allMarkets } = pageContext;
23
25
  const { data, searchParam } = serverData;
24
26
  const [sortQuery] = useState(pageSearchOptions.sort[0]);
25
27
  const [searchData, setSearchData] = useState(data);
26
28
  const activeMarket = allMarkets && allMarkets[page.market];
29
+ const { template } = page;
30
+
31
+ const recommendedCasinos =
32
+ pageContext?.marketSections?.recommended_casinos?.modules[0]?.items[0]
33
+ ?.items;
34
+ const topSearches =
35
+ pageContext?.marketSections?.top_searches?.modules[0]?.items;
36
+
37
+ const showRecommendedCasinos =
38
+ mainSettings[template]?.recommendedCasinos ||
39
+ mainSettings?.default?.recommendedCasinos;
40
+
41
+ const showTopSearches =
42
+ mainSettings[template]?.topSearches || mainSettings?.default?.topSearches;
43
+
27
44
  if (activeMarket) activeMarket.market = page?.market;
28
45
 
29
46
  const marketPrefix = activeMarket?.path_prefix || '/';
@@ -145,6 +162,16 @@ const Search = ({
145
162
  'no_search_data',
146
163
  'No data, please type keyword in textbox above'
147
164
  )}
165
+ {(showRecommendedCasinos || showTopSearches) && (
166
+ <VariableComponent
167
+ recommendedCasinos={recommendedCasinos}
168
+ topSearches={topSearches}
169
+ showRecommendedCasinos={showRecommendedCasinos}
170
+ showTopSearches={showTopSearches}
171
+ showAutoComplete={false}
172
+ autocompleteEnabled={false}
173
+ />
174
+ )}
148
175
  </div>
149
176
  )}
150
177
  </div>
@@ -156,7 +183,6 @@ Search.propTypes = {
156
183
  data: PropTypes.arrayOf(),
157
184
  searchParam: PropTypes.string,
158
185
  }),
159
- allMarkets: PropTypes.shape({}),
160
186
  formSearchOptions: PropTypes.shape({
161
187
  autoComplete: PropTypes.bool,
162
188
  sort: PropTypes.bool,
@@ -193,16 +219,40 @@ Search.propTypes = {
193
219
  }),
194
220
  }),
195
221
  styles: PropTypes.shape({}),
196
- page: PropTypes.shape({
197
- translations: PropTypes.shape({}),
198
- market: PropTypes.string,
199
- }),
222
+ showNumberOfResults: PropTypes.bool,
200
223
  pageContext: PropTypes.shape({
201
224
  page: PropTypes.shape({
225
+ translations: PropTypes.shape({}),
202
226
  market: PropTypes.string,
227
+ template: PropTypes.string,
228
+ }),
229
+ allMarkets: PropTypes.shape({}),
230
+ marketSections: PropTypes.shape({
231
+ recommended_casinos: PropTypes.shape({
232
+ modules: PropTypes.arrayOf(
233
+ PropTypes.shape({
234
+ items: PropTypes.arrayOf(
235
+ PropTypes.shape({
236
+ items: PropTypes.arrayOf(PropTypes.shape({})),
237
+ })
238
+ ),
239
+ })
240
+ ),
241
+ }),
242
+ top_searches: PropTypes.shape({
243
+ modules: PropTypes.arrayOf(
244
+ PropTypes.shape({
245
+ items: PropTypes.arrayOf(
246
+ PropTypes.shape({
247
+ title: PropTypes.string,
248
+ id: PropTypes.string,
249
+ })
250
+ ),
251
+ })
252
+ ),
253
+ }),
203
254
  }),
204
255
  }),
205
- showNumberOfResults: PropTypes.bool,
206
256
  };
207
257
 
208
258
  export default Search;
@@ -20,6 +20,10 @@ describe('Search Component', () => {
20
20
  serverData={{ data: groupBy(data, 'type'), searchParam: 'casino' }}
21
21
  inputPlaceholder="Your Search"
22
22
  formSearchOptions={formSearchOptions}
23
+ pageContext={{
24
+ page: {},
25
+ allMarkets: [],
26
+ }}
23
27
  pageSearchOptions={{
24
28
  useArchive: true,
25
29
  archiveOptions: {
@@ -35,6 +35,7 @@
35
35
 
36
36
  &.list {
37
37
  grid-template-columns: repeat(5, 1fr);
38
+ position: relative;
38
39
 
39
40
  @include max(tablet) {
40
41
  grid-template-columns: auto;
@@ -13,7 +13,7 @@ import { getComponent } from '../../../helpers/autocomplete';
13
13
  import Loading from '../../atoms/search/autocomplete/loading';
14
14
  import NoResults from '../../atoms/search/autocomplete/no-results';
15
15
  import { mainSettings } from '../../../constants/site-settings/main';
16
-
16
+ import VariableComponent from './variableComponent';
17
17
  import styles from './search.module.scss';
18
18
 
19
19
  const SearchForm = ({
@@ -29,6 +29,9 @@ const SearchForm = ({
29
29
  autoCompleteIcon = <IoIosArrowForward title="Search Icon" />,
30
30
  }) => {
31
31
  const { market, template } = pageContext.page;
32
+ const recommendedCasinos = pageContext?.marketSections?.recommended_casinos?.modules[0]?.items[0]?.items
33
+ const topSearches = pageContext?.marketSections?.top_searches?.modules[0]?.items
34
+
32
35
  const [localSearch, setLocalSearch] = useState(false);
33
36
 
34
37
  const loadedCounts = {
@@ -46,6 +49,13 @@ const SearchForm = ({
46
49
  const [filteredData, setFilteredData] = useState(null);
47
50
  const [showAutoComplete, setShowAutoComplete] = useState(false);
48
51
  const autocompleteEnabled = mainSettings[template]?.autocomplete || mainSettings?.default?.autocomplete;
52
+ const showRecommendedCasinos =
53
+ mainSettings[template]?.recommendedCasinos ||
54
+ mainSettings?.default?.recommendedCasinos;
55
+
56
+ const showTopSearches =
57
+ mainSettings[template]?.topSearches ||
58
+ mainSettings?.default?.topSearches;
49
59
 
50
60
  const [searchBoxQuery,] = useState();
51
61
  const searchInputRef = useRef();
@@ -220,6 +230,16 @@ const SearchForm = ({
220
230
  )}
221
231
  </div>
222
232
  )}
233
+ {(showRecommendedCasinos || showTopSearches) && (
234
+ <VariableComponent
235
+ recommendedCasinos={recommendedCasinos}
236
+ topSearches={topSearches}
237
+ showRecommendedCasinos={showRecommendedCasinos}
238
+ showTopSearches={showTopSearches}
239
+ showAutoComplete={showAutoComplete}
240
+ autocompleteEnabled={autocompleteEnabled}
241
+ />
242
+ )}
223
243
  </div>
224
244
  </div>
225
245
  )}
@@ -236,8 +256,33 @@ SearchForm.propTypes = {
236
256
  pageContext: PropTypes.shape({
237
257
  page: PropTypes.shape({
238
258
  market: PropTypes.string,
239
- template: PropTypes.string
240
- })
259
+ template: PropTypes.string,
260
+ }),
261
+ marketSections: PropTypes.shape({
262
+ recommended_casinos: PropTypes.shape({
263
+ modules: PropTypes.arrayOf(
264
+ PropTypes.shape({
265
+ items: PropTypes.arrayOf(
266
+ PropTypes.shape({
267
+ items: PropTypes.arrayOf(PropTypes.shape({})),
268
+ })
269
+ ),
270
+ })
271
+ ),
272
+ }),
273
+ top_searches: PropTypes.shape({
274
+ modules: PropTypes.arrayOf(
275
+ PropTypes.shape({
276
+ items: PropTypes.arrayOf(
277
+ PropTypes.shape({
278
+ title: PropTypes.string,
279
+ id: PropTypes.string,
280
+ })
281
+ ),
282
+ })
283
+ ),
284
+ }),
285
+ }),
241
286
  }),
242
287
  marketPrefix: PropTypes.string,
243
288
  isDisabled: PropTypes.bool,
@@ -0,0 +1,57 @@
1
+ /* eslint-disable react-hooks/rules-of-hooks */
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import useTranslate from "~hooks/useTranslate/useTranslate";
5
+ import Link from '~hooks/link';
6
+ import Row from "../../../molecules/toplist/default-row";
7
+ import styles from './searchVariableComponent.module.scss'
8
+
9
+ const VariableComponent = ({
10
+ recommendedCasinos,
11
+ topSearches,
12
+ showRecommendedCasinos,
13
+ showTopSearches,
14
+ showAutoComplete,
15
+ autocompleteEnabled,
16
+ }) => (
17
+ <div className={styles.searchVariableComponent}>
18
+ {showTopSearches && (
19
+ <div className={styles.topSearches}>
20
+ <p>Top Searches:</p>
21
+ {topSearches?.map((item) => (
22
+ <Link key={item.id} to={item.path}>
23
+ {item.title}
24
+ </Link>
25
+ ))}
26
+ </div>
27
+ )}
28
+ {showRecommendedCasinos &&
29
+ (!autocompleteEnabled || (autocompleteEnabled && !showAutoComplete)) && (
30
+ <>
31
+ <p className={styles.recommendedCasinosHeader}>
32
+ {useTranslate("recommendedCasinosHeader", "Recommended Casinos")}
33
+ </p>
34
+ {recommendedCasinos?.map((item) => (
35
+ <Row key={item.id} item={item} />
36
+ ))}
37
+ </>
38
+ )}
39
+ </div>
40
+ );
41
+
42
+ VariableComponent.propTypes = {
43
+ // eslint-disable-next-line react/forbid-prop-types
44
+ recommendedCasinos: PropTypes.array.isRequired,
45
+ showRecommendedCasinos: PropTypes.bool,
46
+ showTopSearches: PropTypes.bool,
47
+ showAutoComplete: PropTypes.bool,
48
+ autocompleteEnabled: PropTypes.bool,
49
+ topSearches: PropTypes.arrayOf(
50
+ PropTypes.shape({
51
+ id: PropTypes.number,
52
+ title: PropTypes.string,
53
+ })
54
+ ).isRequired,
55
+ };
56
+
57
+ export default VariableComponent;
@@ -0,0 +1,16 @@
1
+
2
+
3
+ .searchVariableComponent {
4
+ background: #fff;
5
+ padding: 2.4rem;
6
+
7
+ @include flex-direction(column);
8
+
9
+ gap: 1.6rem;
10
+ }
11
+
12
+ .topSearches {
13
+ display: flex;
14
+ gap: 1rem;
15
+ color: #000;
16
+ }
@@ -0,0 +1,114 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import React from "react";
3
+ import VariableComponent from "./index";
4
+ import "@testing-library/jest-dom";
5
+
6
+ jest.mock("~hooks/useTranslate/useTranslate", () =>
7
+ jest.fn().mockReturnValue("Recommended Casinos")
8
+ );
9
+
10
+ describe("VariableComponent", () => {
11
+ const recommendedCasinos = [
12
+ { id: 1, title: "Casino 1", path: "/casino-1" },
13
+ { id: 2, title: "Casino 2", path: "/casino-2" },
14
+ ];
15
+
16
+ const topSearches = [
17
+ { id: 1, title: "Search 1", path: "/search-1" },
18
+ { id: 2, title: "Search 2", path: "/search-2" },
19
+ ];
20
+
21
+ it("should render Top Searches when showTopSearches is true", () => {
22
+ render(
23
+ <VariableComponent
24
+ recommendedCasinos={recommendedCasinos}
25
+ topSearches={topSearches}
26
+ showRecommendedCasinos={false}
27
+ showTopSearches
28
+ showAutoComplete={false}
29
+ />
30
+ );
31
+
32
+ expect(screen.getByText("Top Searches:")).toBeInTheDocument();
33
+
34
+ topSearches.forEach((item) => {
35
+ expect(screen.getByText(item.title)).toBeInTheDocument();
36
+ });
37
+ });
38
+
39
+ it("should not render Recommended Casinos when showAutoComplete is true", () => {
40
+ render(
41
+ <VariableComponent
42
+ recommendedCasinos={recommendedCasinos}
43
+ topSearches={topSearches}
44
+ showRecommendedCasinos
45
+ showTopSearches={false}
46
+ showAutoComplete
47
+ autocompleteEnabled
48
+ />
49
+ );
50
+
51
+ expect(screen.queryByText("Recommended Casinos")).toBeNull();
52
+ });
53
+
54
+ it("should render the correct links for Top Searches", () => {
55
+ render(
56
+ <VariableComponent
57
+ recommendedCasinos={recommendedCasinos}
58
+ topSearches={topSearches}
59
+ showRecommendedCasinos={false}
60
+ showTopSearches
61
+ showAutoComplete={false}
62
+ />
63
+ );
64
+
65
+ topSearches.forEach((item) => {
66
+ const link = screen.getByText(item.title);
67
+ expect(link).toHaveAttribute("href", item.path);
68
+ });
69
+ });
70
+
71
+ it("should render the Recommended Casinos header with translation", () => {
72
+ render(
73
+ <VariableComponent
74
+ recommendedCasinos={recommendedCasinos}
75
+ topSearches={topSearches}
76
+ showRecommendedCasinos
77
+ showTopSearches={false}
78
+ showAutoComplete={false}
79
+ />
80
+ );
81
+
82
+ expect(screen.getAllByText("Recommended Casinos")).toHaveLength(3);
83
+ });
84
+ it("should render nothing when both showTopSearches and showRecommendedCasinos are false", () => {
85
+ render(
86
+ <VariableComponent
87
+ recommendedCasinos={recommendedCasinos}
88
+ topSearches={topSearches}
89
+ showRecommendedCasinos={false}
90
+ showTopSearches={false}
91
+ showAutoComplete={false}
92
+ />
93
+ );
94
+
95
+ expect(screen.queryByText("Top Searches:")).toBeNull();
96
+ expect(screen.queryByText("Recommended Casinos")).toBeNull();
97
+ });
98
+ it("should not render Recommended Casinos when showRecommendedCasinos is false regardless of other flags", () => {
99
+ render(
100
+ <VariableComponent
101
+ recommendedCasinos={recommendedCasinos}
102
+ topSearches={topSearches}
103
+ showRecommendedCasinos={false}
104
+ showTopSearches={false}
105
+ showAutoComplete={false}
106
+ autocompleteEnabled={false}
107
+ />
108
+ );
109
+
110
+ expect(screen.queryByText("Recommended Casinos")).toBeNull();
111
+ });
112
+
113
+
114
+ });
@@ -5,13 +5,15 @@ import Search from '~molecules/search';
5
5
  import Card from '~atoms/cards/default-card';
6
6
  import useTranslate from '~hooks/useTranslate/useTranslate';
7
7
 
8
- const SearchContent = ({ page, serverData, allMarkets }) => {
8
+ const SearchContent = ({ serverData, pageContext }) => {
9
+ const { page, allMarkets } = pageContext;
9
10
  const searchURLPageName = page.path;
10
11
  return (
11
12
  <Search
12
13
  serverData={serverData}
13
14
  searchURLPageName={searchURLPageName}
14
15
  page={page}
16
+ pageContext={pageContext}
15
17
  pageSearchOptions={{
16
18
  useArchive: true,
17
19
  archiveOptions: {
@@ -80,13 +82,15 @@ const SearchContent = ({ page, serverData, allMarkets }) => {
80
82
 
81
83
  SearchContent.propTypes = {
82
84
  serverData: PropTypes.shape({}),
83
- allMarkets: PropTypes.shape({}),
84
- page: PropTypes.shape({
85
- template: PropTypes.string,
86
- path: PropTypes.string,
87
- updated_at: PropTypes.string,
88
- created_at: PropTypes.string,
89
- translations: PropTypes.shape({}),
85
+ pageContext: PropTypes.shape({
86
+ allMarkets: PropTypes.shape({}),
87
+ page: PropTypes.shape({
88
+ template: PropTypes.string,
89
+ path: PropTypes.string,
90
+ updated_at: PropTypes.string,
91
+ created_at: PropTypes.string,
92
+ translations: PropTypes.shape({}),
93
+ }),
90
94
  }),
91
95
  };
92
96
 
@@ -8,6 +8,8 @@ export const mainSettings = {
8
8
  },
9
9
  default: {
10
10
  operator_banner: false,
11
- autocomplete: false
12
- }
13
- }
11
+ autocomplete: false,
12
+ recommendedCasinos: true,
13
+ topSearches: true,
14
+ },
15
+ };
@@ -138,14 +138,31 @@ export function clean(object) {
138
138
  return object;
139
139
  }
140
140
 
141
- export function removeUnwantedSections(obj, pageType) {
141
+ export function removeUnwantedSections(obj, pageType, ribbonsData) {
142
142
 
143
143
  const marketSection = {
144
144
  games: ["post_main_games"],
145
- operator: ['post_main_operators', "pre_main_operators", "recommended_casinos"],
146
- article: ['post_main_articles'],
147
- default: ['footer', 'links', 'navigation', 'popup', 'rg_navigation', 'sidebar', 'footer_navigation', 'top_menu', 'sidebar_navigation']
148
- }
145
+ operator: [
146
+ "post_main_operators",
147
+ "pre_main_operators",
148
+ "recommended_casinos",
149
+ "top_searches",
150
+ ],
151
+ article: ["post_main_articles"],
152
+ default: [
153
+ "footer",
154
+ "links",
155
+ "navigation",
156
+ "popup",
157
+ "rg_navigation",
158
+ "sidebar",
159
+ "footer_navigation",
160
+ "top_menu",
161
+ "sidebar_navigation",
162
+ "recommended_casinos",
163
+ "top_searches",
164
+ ],
165
+ };
149
166
 
150
167
  // Get sections to keep: merge default sections and specific sections for the key
151
168
  const sectionsToKeep = new Set([
@@ -157,7 +174,31 @@ export function removeUnwantedSections(obj, pageType) {
157
174
  const filteredObject = Object.keys(obj).reduce((acc, key) => {
158
175
  if (sectionsToKeep.has(key)) {
159
176
  acc[key] = obj[key];
177
+
178
+ if (
179
+ key === "recommended_casinos" &&
180
+ Array.isArray(acc[key]?.modules?.[0]?.items?.[0]?.items)
181
+ ) {
182
+ acc[key].modules[0].items[0].items = acc[
183
+ key
184
+ ].modules[0].items[0].items.slice(0, 3);
185
+ const { items } = acc[key].modules[0].items[0];
186
+
187
+ // Connect ribbons ID to label
188
+ items.forEach((item) => {
189
+ if (item.ribbons?.length) {
190
+ item.ribbons = item.ribbons.map((ribbon) => {
191
+ if (typeof ribbon === "number") {
192
+ const label = ribbonsData[ribbon]?.label;
193
+ return label ?? null;
194
+ }
195
+ return ribbon;
196
+ });
197
+ }
198
+ });
199
+ }
160
200
  }
201
+
161
202
  return acc;
162
203
  }, {});
163
204
 
@@ -18,6 +18,23 @@ describe("Sanitize Operator Data", () => {
18
18
  expect(sanitizedOperator.ribbons).toHaveLength(1);
19
19
  expect(sanitizedOperator.ribbons[0]).toBe('testRibbon');
20
20
  });
21
+ test("Handles missing optional fields gracefully", () => {
22
+ const operatorData = {
23
+ name: "Test Casino",
24
+ ribbons: [null, "customLabel", 2],
25
+ operator_url: "/test-url",
26
+ };
27
+ const operatorPage = [{}];
28
+ const relations = { ribbons: { 2: { label: "fromRelations" } } };
29
+
30
+ const result = sanitizeOperatorData(operatorData, operatorPage, relations);
31
+
32
+ expect(result.name).toBe("Test Casino");
33
+ expect(result.url).toBe("/test-url");
34
+ expect(result.ribbons).toEqual(["customLabel", "fromRelations"]);
35
+ expect(result.author_name).toBeUndefined();
36
+ });
37
+
21
38
 
22
39
  // test("Sanitize Data with Transformed Pages Object", () => {
23
40
  // const operatorsData = {