gatsby-core-theme 44.0.16 → 44.0.18

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 (33) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +1 -1
  3. package/src/components/atoms/search/autocomplete/article/article.module.scss +27 -0
  4. package/src/components/atoms/search/autocomplete/article/index.js +25 -0
  5. package/src/components/atoms/search/autocomplete/default/default.module.scss +27 -0
  6. package/src/components/atoms/search/autocomplete/default/index.js +25 -0
  7. package/src/components/atoms/search/autocomplete/game/game.module.scss +24 -0
  8. package/src/components/atoms/search/autocomplete/game/index.js +42 -0
  9. package/src/components/atoms/search/autocomplete/loading.js +34 -0
  10. package/src/components/atoms/search/autocomplete/loading.module.scss +47 -0
  11. package/src/components/atoms/search/autocomplete/no-results.js +9 -0
  12. package/src/components/atoms/search/autocomplete/no-results.module.scss +3 -0
  13. package/src/components/atoms/search/autocomplete/operator/index.js +41 -0
  14. package/src/components/atoms/search/autocomplete/operator/operator.module.scss +23 -0
  15. package/src/components/organisms/cards/cards.test.js +1 -0
  16. package/src/components/organisms/navigation/navigation.test.js +8 -7
  17. package/src/components/organisms/search/index.js +67 -2
  18. package/src/components/organisms/search/search.module.scss +14 -0
  19. package/src/constants/site-settings/main.js +1 -0
  20. package/src/helpers/autocomplete.js +16 -0
  21. package/src/helpers/autocomplete.test.js +75 -0
  22. package/src/helpers/getters.mjs +22 -18
  23. package/src/helpers/processor/index.mjs +15 -7
  24. package/src/helpers/processor/modules.mjs +13 -0
  25. package/static/images/default-article.jpg +0 -0
  26. package/static/images/default-slot.jpg +0 -0
  27. package/storybook/deploy.php +1 -1
  28. package/src/components/atoms/search/autocomplete/article.js +0 -20
  29. package/src/components/atoms/search/autocomplete/article.module.scss +0 -9
  30. package/src/components/atoms/search/autocomplete/game.js +0 -18
  31. package/src/components/atoms/search/autocomplete/game.module.scss +0 -9
  32. package/src/components/atoms/search/autocomplete/operator.js +0 -38
  33. package/src/components/atoms/search/autocomplete/operator.module.scss +0 -12
package/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## [44.0.18](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.0.17...v44.0.18) (2025-04-15)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * added autocomplete implementation ([b32c30f](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/b32c30f99a56f26e5a1558e5afd34207c12edfb9))
7
+ * added settings ([3e41337](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/3e41337b18ca26a6bac09cd218b64382f37e6563))
8
+ * conflicts ([9cfd59c](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/9cfd59c5b2cdfd582fc6a3569d0183d662aae8e7))
9
+ * tests ([f7141d4](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/f7141d475210efec322f785da9211291ae4f9466))
10
+ * update server ip ([90d2608](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/90d260864d808cb4c06732b1d1c25fad16b33d86))
11
+
12
+
13
+ * Merge branch 'tm-4548-autocomplete' into 'master' ([779e0ae](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/779e0ae6ca6d46d22dcefe1cc2ee50d429e615c6))
14
+
15
+ ## [44.0.17](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.0.16...v44.0.17) (2025-04-14)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * add alphabetical sorting ([a923c31](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/a923c3132448a4502a24eea457825fdeb955cb2e))
21
+
1
22
  ## [44.0.16](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.0.15...v44.0.16) (2025-04-14)
2
23
 
3
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gatsby-core-theme",
3
- "version": "44.0.16",
3
+ "version": "44.0.18",
4
4
  "description": "Gatsby Theme NPM Package",
5
5
  "author": "",
6
6
  "license": "ISC",
@@ -0,0 +1,27 @@
1
+ .row {
2
+ @include flex-align(center, start);
3
+
4
+ gap: .8rem;
5
+ width: 100%;
6
+ background-color: var(--color-7);
7
+ border-radius: .8rem;
8
+ padding: 1.2rem;
9
+
10
+ > a{
11
+ margin-left: auto;
12
+
13
+ > svg{
14
+ margin: 0 !important;
15
+ }
16
+ }
17
+ }
18
+
19
+ .operatorImage{
20
+ border-radius: .8rem;
21
+ }
22
+
23
+ .operatorTitle{
24
+ margin: 0 !important;
25
+ font-size: 1.6rem;
26
+ font-weight: 400;
27
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import Button from 'gatsby-core-theme/src/components/atoms/button/button';
4
+ import { FaArrowRight } from '@react-icons/all-files/fa/FaArrowRight';
5
+ import styles from './article.module.scss';
6
+
7
+ const Article = ({ item = {} }) => (
8
+ <li className={styles.row || ''}>
9
+ <h3 className={styles.operatorTitle}>{item.title}</h3>
10
+ <Button
11
+ btnText=''
12
+ to={item?.path}
13
+ gtmClass="autocomplete-article-item-gtm btn-cta"
14
+ icon={<FaArrowRight title="Right-pointing Arrow Icon" />}
15
+ />
16
+ </li>
17
+ );
18
+
19
+ Article.propTypes = {
20
+ item: PropTypes.shape({
21
+ title: PropTypes.string,
22
+ }).isRequired,
23
+ };
24
+
25
+ export default Article;
@@ -0,0 +1,27 @@
1
+ .row {
2
+ @include flex-align(center, start);
3
+
4
+ gap: .8rem;
5
+ width: 100%;
6
+ background-color: var(--color-7);
7
+ border-radius: .8rem;
8
+ padding: 1.2rem;
9
+
10
+ > a{
11
+ margin-left: auto;
12
+
13
+ > svg{
14
+ margin: 0 !important;
15
+ }
16
+ }
17
+ }
18
+
19
+ .operatorImage{
20
+ border-radius: .8rem;
21
+ }
22
+
23
+ .operatorTitle{
24
+ margin: 0 !important;
25
+ font-size: 1.6rem;
26
+ font-weight: 400;
27
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import Button from 'gatsby-core-theme/src/components/atoms/button/button';
4
+ import { FaArrowRight } from '@react-icons/all-files/fa/FaArrowRight';
5
+ import styles from './default.module.scss';
6
+
7
+ const Default = ({ item = {} }) => (
8
+ <li className={styles.row || ''}>
9
+ <h3 className={styles.operatorTitle}>{item.title}</h3>
10
+ <Button
11
+ btnText=''
12
+ to={item?.path}
13
+ gtmClass="autocomplete-article-item-gtm btn-cta"
14
+ icon={<FaArrowRight title="Right-pointing Arrow Icon" />}
15
+ />
16
+ </li>
17
+ );
18
+
19
+ Default.propTypes = {
20
+ item: PropTypes.shape({
21
+ title: PropTypes.string,
22
+ }).isRequired,
23
+ };
24
+
25
+ export default Default;
@@ -0,0 +1,24 @@
1
+ .row {
2
+ @include flex-align(center, start);
3
+
4
+ gap: .8rem;
5
+ width: 100%;
6
+ background-color: var(--color-7);
7
+ border-radius: .8rem;
8
+ padding: 1.2rem;
9
+
10
+ > a{
11
+ margin-left: auto;
12
+ }
13
+ }
14
+
15
+ .gameImage{
16
+ border-radius: .8rem;
17
+ height: revert-layer !important;
18
+ }
19
+
20
+ .gameTitle{
21
+ margin: 0 !important;
22
+ font-size: 1.6rem;
23
+ font-weight: 400;
24
+ }
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { FaArrowRight } from '@react-icons/all-files/fa/FaArrowRight';
4
+ import Button from 'gatsby-core-theme/src/components/atoms/button/button';
5
+ import LazyImage from '~hooks/lazy-image';
6
+ import { imagePrettyUrl } from '~helpers/getters';
7
+ import styles from './game.module.scss';
8
+
9
+ const Game = ({ item = {}, width = 56, height = 56 }) => {
10
+ const { logo } = item;
11
+
12
+ return (
13
+ <li className={styles.row || ''}>
14
+ <LazyImage
15
+ className={styles.gameImage}
16
+ width={width}
17
+ height={height}
18
+ src={logo ? imagePrettyUrl(logo, width, height) : '/images/default-slot.jpg'}
19
+ alt={item.title}
20
+ />
21
+ <h3 className={styles.gameTitle}>{item.title}</h3>
22
+ <Button
23
+ to={item?.path}
24
+ gtmClass="autocomplete-game-item-gtm btn-cta"
25
+ icon={<FaArrowRight title="Right-pointing Arrow Icon" />}
26
+ />
27
+ </li>
28
+ );
29
+ };
30
+
31
+ Game.propTypes = {
32
+ item: PropTypes.shape({
33
+ title: PropTypes.string,
34
+ relation: PropTypes.shape({
35
+ logo: PropTypes.shape({}),
36
+ }),
37
+ }).isRequired,
38
+ width: PropTypes.number,
39
+ height: PropTypes.number,
40
+ };
41
+
42
+ export default Game;
@@ -0,0 +1,34 @@
1
+ /* eslint-disable react/prop-types */
2
+ import React from 'react';
3
+ import styles from './loading.module.scss';
4
+
5
+ const Loading = ({ single = false }) => single ?
6
+ (
7
+ <div className={styles.post}>
8
+ <div className={styles.avatar} />
9
+ <div className={styles.line} />
10
+ <div className={styles.line} />
11
+ </div>
12
+ ) : (
13
+ <div className={styles.container}>
14
+ <div className={styles.post}>
15
+ <div className={styles.avatar} />
16
+ <div className={styles.line} />
17
+ <div className={styles.line} />
18
+ </div>
19
+
20
+ <div className={styles.post}>
21
+ <div className={styles.avatar} />
22
+ <div className={styles.line} />
23
+ <div className={styles.line} />
24
+ </div>
25
+
26
+ <div className={styles.post}>
27
+ <div className={styles.avatar} />
28
+ <div className={styles.line} />
29
+ <div className={styles.line} />
30
+ </div>
31
+ </div>
32
+ );
33
+
34
+ export default Loading;
@@ -0,0 +1,47 @@
1
+ .post {
2
+ width: 220px;
3
+ height: 80px;
4
+
5
+ .avatar {
6
+ float: left;
7
+ width: 52px;
8
+ height: 52px;
9
+ background-color: #ccc;
10
+ border-radius: 25%;
11
+ margin: 8px;
12
+ animation: loading 1.5s infinite ease-in-out;
13
+ }
14
+
15
+ .line {
16
+ float: left;
17
+ width: 140px;
18
+ height: 16px;
19
+ margin-top: 12px;
20
+ background-color: #ccc;
21
+ border-radius: 7px;
22
+ animation: loading 1.5s infinite ease-in-out;
23
+ }
24
+
25
+ .avatar + .line {
26
+ margin-top: 11px;
27
+ width: 100px;
28
+ }
29
+
30
+ .line ~ .line {
31
+ background-color: #ddd;
32
+ }
33
+
34
+ @keyframes loading {
35
+ 0% {
36
+ background-color: #ccc;
37
+ }
38
+
39
+ 50% {
40
+ background-color: #e0e0e0;
41
+ }
42
+
43
+ 100% {
44
+ background-color: #ccc;
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,9 @@
1
+ /* eslint-disable react/prop-types */
2
+ import React from 'react';
3
+ import styles from './no-results.module.scss';
4
+
5
+ const NoResults = ({ text }) => (
6
+ <p className={styles.noResults}>{text}</p>
7
+ );
8
+
9
+ export default NoResults;
@@ -0,0 +1,3 @@
1
+ .noResults{
2
+ padding: 1.2rem;
3
+ }
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { FaArrowRight } from '@react-icons/all-files/fa/FaArrowRight';
4
+ import Button from 'gatsby-core-theme/src/components/atoms/button/button';
5
+ import LazyImage from '~hooks/lazy-image';
6
+ import { imagePrettyUrl } from '~helpers/getters';
7
+ import styles from './operator.module.scss';
8
+
9
+ const Operator = ({ item = {}, width = 56, height = 56 }) => {
10
+ const { logo } = item;
11
+ return (
12
+ <li className={styles.row || ''}>
13
+ <LazyImage
14
+ className={styles.operatorImage}
15
+ width={width}
16
+ height={height}
17
+ src={imagePrettyUrl(logo, width, height)}
18
+ alt={item.title}
19
+ />
20
+ <h3 className={styles.operatorTitle}>{item.title}</h3>
21
+ <Button
22
+ to={item?.path}
23
+ gtmClass="autocomplete-operator-item-gtm btn-cta"
24
+ icon={<FaArrowRight title="Right-pointing Arrow Icon" />}
25
+ />
26
+ </li>
27
+ );
28
+ };
29
+
30
+ Operator.propTypes = {
31
+ item: PropTypes.shape({
32
+ title: PropTypes.string,
33
+ relation: PropTypes.shape({
34
+ logo: PropTypes.shape({}),
35
+ }),
36
+ }).isRequired,
37
+ width: PropTypes.number,
38
+ height: PropTypes.number,
39
+ };
40
+
41
+ export default Operator;
@@ -0,0 +1,23 @@
1
+ .row {
2
+ @include flex-align(center, start);
3
+
4
+ gap: .8rem;
5
+ width: 100%;
6
+ background-color: var(--color-7);
7
+ border-radius: .8rem;
8
+ padding: 1.2rem;
9
+
10
+ > a{
11
+ margin-left: auto;
12
+ }
13
+ }
14
+
15
+ .operatorImage{
16
+ border-radius: .8rem;
17
+ }
18
+
19
+ .operatorTitle{
20
+ margin: 0 !important;
21
+ font-size: 1.6rem;
22
+ font-weight: 400;
23
+ }
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-restricted-syntax */
1
2
  import React from 'react';
2
3
  import { render, cleanup } from '@testing-library/react';
3
4
  import '@testing-library/jest-dom/extend-expect';
@@ -3,36 +3,37 @@ import { render, cleanup, waitFor } from '@testing-library/react';
3
3
  import '@testing-library/jest-dom/extend-expect';
4
4
 
5
5
  import { getNavigationMenus } from '~tests/factories/sections/navigation.factory';
6
+ import getPageData from '../../../../tests/factories/pages/default.factory';
6
7
  import Navigation from '.';
7
8
 
8
9
  const menuSection = getNavigationMenus(1, 2, true);
9
-
10
+ const pageContext = {page: getPageData()}
10
11
  describe('Navigation Component', () => {
11
12
  test('render navigation', async () => {
12
- const { container } = render(<Navigation section={menuSection} />);
13
+ const { container } = render(<Navigation pageContext={pageContext} section={menuSection} />);
13
14
 
14
15
  await waitFor(() => {
15
- expect(container.querySelectorAll('.nav')).toBeTruthy();
16
+ expect(container.querySelectorAll('.nav')).toHaveLength(1);
16
17
  expect(container.querySelectorAll('.logo')).toHaveLength(1);
17
18
  expect(container.querySelectorAll('.menuContainer')).toHaveLength(1);
18
19
  expect(container.querySelectorAll('.search')).toHaveLength(1);
19
20
  });
20
21
  });
21
22
  test('render navigation when template is ppc', async () => {
22
- const { container } = render(<Navigation section={menuSection} template="ppc" />);
23
+ const { container } = render(<Navigation pageContext={pageContext} section={menuSection} template="ppc" />);
23
24
 
24
25
  await waitFor(() => {
25
- expect(container.querySelectorAll('.nav')).toBeTruthy();
26
+ expect(container.querySelectorAll('.nav')).toHaveLength(1);
26
27
  expect(container.querySelectorAll('.logo')).toHaveLength(1);
27
28
  expect(container.querySelectorAll('.menuContainer')).toHaveLength(0);
28
29
  expect(container.querySelectorAll('.search')).toHaveLength(0);
29
30
  });
30
31
  });
31
32
  test('render navigation without search', async () => {
32
- const { container } = render(<Navigation section={menuSection} hasSearch={false} />);
33
+ const { container } = render(<Navigation pageContext={pageContext} section={menuSection} hasSearch={false} />);
33
34
 
34
35
  await waitFor(() => {
35
- expect(container.querySelectorAll('.nav')).toBeTruthy();
36
+ expect(container.querySelectorAll('.nav')).toHaveLength(1);
36
37
  expect(container.querySelectorAll('.menuContainer')).toHaveLength(1);
37
38
  expect(container.querySelectorAll('.search')).toHaveLength(0);
38
39
  });
@@ -1,21 +1,39 @@
1
+ /* eslint-disable no-unused-expressions */
2
+ /* eslint-disable no-console */
1
3
  /* eslint-disable react-hooks/rules-of-hooks */
2
- import React, { useRef, useState, useEffect } from 'react';
4
+ import React, { useRef, useState, useEffect, Suspense } from 'react';
3
5
  import PropTypes from 'prop-types';
4
6
  import { BiSearchAlt } from '@react-icons/all-files/bi/BiSearchAlt';
7
+ import loadSource from '../../../helpers/search-source';
5
8
  import useTranslate from '~hooks/useTranslate/useTranslate';
6
9
  import { toggleScroll } from '~helpers/scroll';
10
+ import keygen from '~helpers/keygen';
11
+ import { getComponent } from '../../../helpers/autocomplete';
12
+ import Loading from '../../atoms/search/autocomplete/loading';
13
+ import NoResults from '../../atoms/search/autocomplete/no-results';
14
+ import { mainSettings } from '../../../constants/site-settings/main';
7
15
 
8
16
  import styles from './search.module.scss';
9
17
 
10
18
  const SearchForm = ({
19
+ pageContext,
11
20
  className,
12
21
  searchIcon,
13
22
  iconWidth = 24,
14
23
  iconHeight = 24,
15
24
  marketPrefix = '/',
25
+ startSearchAt = 3,
16
26
  isDisabled,
17
27
  }) => {
28
+ const { market, template } = pageContext.page;
18
29
  const [localSearch, setLocalSearch] = useState(false);
30
+
31
+ const [autoCompleteLoading, setAutoCompleteLoading] = useState(false);
32
+ const autoCompleteData = useRef(null);
33
+ const [filteredData, setFilteredData] = useState(null);
34
+ const [showAutoComplete, setShowAutoComplete] = useState(false);
35
+ const autocompleteEnabled = mainSettings[template]?.autocomplete || mainSettings?.default?.autocomplete;
36
+
19
37
  const [searchBoxQuery,] = useState();
20
38
  const searchInputRef = useRef();
21
39
  const searchFormRef = useRef();
@@ -45,6 +63,28 @@ const SearchForm = ({
45
63
  }
46
64
  };
47
65
 
66
+ const autoCompleteHandler = async (event) => {
67
+ const query = event.target.value.trim();
68
+ setShowAutoComplete(prevState => (query.length >= startSearchAt) !== prevState ? query.length >= startSearchAt : prevState);
69
+
70
+ setAutoCompleteLoading(true);
71
+
72
+ try {
73
+ autoCompleteData.current = autoCompleteData.current || await loadSource(market)
74
+
75
+ setFilteredData(autoCompleteData.current.filter(item =>
76
+ item.title.toLowerCase().includes(query.toLowerCase())
77
+ ));
78
+
79
+ } catch (error) {
80
+ console.error("Error loading autocomplete data:", error);
81
+ } finally {
82
+ setAutoCompleteLoading(false);
83
+ }
84
+ }
85
+
86
+
87
+
48
88
  useEffect(() => {
49
89
  const handleClickOutside = (event) => {
50
90
  if (localSearch && searchFormRef.current && !searchFormRef.current.contains(event.target)) {
@@ -100,11 +140,30 @@ const SearchForm = ({
100
140
  maxLength="60"
101
141
  ref={searchInputRef}
102
142
  onKeyUp={(event) => {
143
+ autoCompleteHandler(event);
103
144
  if (event.key === 'Enter') {
104
145
  formSubmitBoxHandler(event);
105
146
  }
106
147
  }}
107
148
  />
149
+ {showAutoComplete && autocompleteEnabled && (
150
+ <div className={styles.autoCompleteContainer}>
151
+ {autoCompleteLoading ? (
152
+ <div className={styles.autoCompleteInner}>
153
+ <Loading />
154
+ </div>
155
+ ) : (
156
+ <div className={styles.autoCompleteInner}>
157
+ {filteredData.length ? <ul>
158
+ {filteredData.map((value) => {
159
+ const PageTypeCard = getComponent(value.pageType);
160
+ return <Suspense fallback={<Loading single />} key={keygen()}><PageTypeCard item={value} /></Suspense>
161
+ })}
162
+ </ul> : <NoResults text={useTranslate('autocomplete_no_results', 'No Results Found')} />}
163
+ </div>
164
+ )}
165
+ </div>
166
+ )}
108
167
  </div>
109
168
  </div>
110
169
  )}
@@ -117,9 +176,15 @@ SearchForm.propTypes = {
117
176
  searchIcon: PropTypes.string,
118
177
  iconWidth: PropTypes.number,
119
178
  iconHeight: PropTypes.number,
120
- pageContext: PropTypes.shape({}),
179
+ pageContext: PropTypes.shape({
180
+ page: PropTypes.shape({
181
+ market: PropTypes.string,
182
+ template: PropTypes.string
183
+ })
184
+ }),
121
185
  marketPrefix: PropTypes.string,
122
186
  isDisabled: PropTypes.bool,
187
+ startSearchAt: PropTypes.number
123
188
  };
124
189
 
125
190
  export default SearchForm;
@@ -67,3 +67,17 @@
67
67
  cursor: not-allowed;
68
68
  }
69
69
  }
70
+
71
+ .autoCompleteContainer{
72
+ background-color: #fff;
73
+ margin-top: .8rem;
74
+ border-radius: .8rem;
75
+ }
76
+
77
+ .autoCompleteInner > ul{
78
+ @include flex-direction(column);
79
+
80
+ gap: .8rem;
81
+ padding: 1.2rem;
82
+
83
+ }
@@ -8,5 +8,6 @@ export const mainSettings = {
8
8
  },
9
9
  default: {
10
10
  operator_banner: false,
11
+ autocomplete: false
11
12
  }
12
13
  }
@@ -0,0 +1,16 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+ // eslint-disable-next-line import/no-extraneous-dependencies
3
+ import { lazy } from 'react';
4
+
5
+ export function getComponent(type) {
6
+ switch (type) {
7
+ case 'operator':
8
+ return lazy(() => import(`../components/atoms/search/autocomplete/operator`));
9
+ case 'article':
10
+ return lazy(() => import(`../components/atoms/search/autocomplete/article`));
11
+ case 'game':
12
+ return lazy(() => import(`../components/atoms/search/autocomplete/game`));
13
+ default:
14
+ return lazy(() => import(`../components/atoms/search/autocomplete/default`));
15
+ }
16
+ }
@@ -0,0 +1,75 @@
1
+ /* eslint-disable no-unused-expressions */
2
+ import React, { Suspense } from "react";
3
+ import { render, screen } from "@testing-library/react";
4
+ import "@testing-library/jest-dom/extend-expect";
5
+ import { getComponent } from "./autocomplete";
6
+
7
+ describe("Autocomplete Get Component Helper", () => {
8
+ test("Autocomplete Get Component Operator", async () => {
9
+ const Component = getComponent('operator');
10
+ render(
11
+ <Suspense fallback={<></>}>
12
+ <Component item={{
13
+ title: 'Test Operator',
14
+ path: '/test'
15
+ }} />
16
+ </Suspense>
17
+ );
18
+ const link = await screen.findByRole('link'); // gets <a>
19
+ expect(link).toHaveAttribute('href', '/test');
20
+
21
+ const title = await screen.findByText('Test Operator');
22
+ expect(title).toBeInTheDocument();
23
+ });
24
+
25
+ test("Autocomplete Get Component Article", async () => {
26
+ const Component = getComponent('article');
27
+ render(
28
+ <Suspense fallback={<></>}>
29
+ <Component item={{
30
+ title: 'Test Article',
31
+ path: '/test'
32
+ }} />
33
+ </Suspense>
34
+ );
35
+ const link = await screen.findByRole('link'); // gets <a>
36
+ expect(link).toHaveAttribute('href', '/test');
37
+
38
+ const title = await screen.findByText('Test Article');
39
+ expect(title).toBeInTheDocument();
40
+ });
41
+
42
+ test("Autocomplete Get Component Game", async () => {
43
+ const Component = getComponent('game');
44
+ render(
45
+ <Suspense fallback={<></>}>
46
+ <Component item={{
47
+ title: 'Test Game',
48
+ path: '/test'
49
+ }} />
50
+ </Suspense>
51
+ );
52
+ const link = await screen.findByRole('link'); // gets <a>
53
+ expect(link).toHaveAttribute('href', '/test');
54
+
55
+ const title = await screen.findByText('Test Game');
56
+ expect(title).toBeInTheDocument();
57
+ });
58
+
59
+ test("Autocomplete Get Component Default", async () => {
60
+ const Component = getComponent('payment_methods');
61
+ render(
62
+ <Suspense fallback={<></>}>
63
+ <Component item={{
64
+ title: 'Test Default',
65
+ path: '/test'
66
+ }} />
67
+ </Suspense>
68
+ );
69
+ const link = await screen.findByRole('link'); // gets <a>
70
+ expect(link).toHaveAttribute('href', '/test');
71
+
72
+ const title = await screen.findByText('Test Default');
73
+ expect(title).toBeInTheDocument();
74
+ });
75
+ });
@@ -142,28 +142,32 @@ export function isBrowserWebpCompatable(feature, callback) {
142
142
  }
143
143
 
144
144
  export function imagePrettyUrl(
145
- filename = "image.png",
145
+ filename,
146
146
  width = null,
147
147
  height = null
148
148
  ) {
149
- const extension = getImageExtension(filename);
150
-
151
- const hasFilters = extension !== "svg" && extension !== "gif";
152
- const cdnURL =
153
- (hasFilters
154
- ? process.env.IMAGE_CDN_URL
155
- : process.env.IMAGE_CDN_URL
156
- ? process.env.IMAGE_CDN_URL.replace("/filters:format(webp)", "")
157
- : "") || process.env.STORYBOOK_IMAGE_CDN_URL;
158
-
159
- if (width && height && hasFilters) {
160
- const urlPath = "/fit-in";
161
- const urlSize = `/${width}x${height}`;
162
-
163
- return `${cdnURL}${urlPath}${urlSize}/${filename}`;
149
+ if(filename){
150
+ const extension = getImageExtension(filename);
151
+
152
+ const hasFilters = extension !== "svg" && extension !== "gif";
153
+ const cdnURL =
154
+ (hasFilters
155
+ ? process.env.IMAGE_CDN_URL
156
+ : process.env.IMAGE_CDN_URL
157
+ ? process.env.IMAGE_CDN_URL.replace("/filters:format(webp)", "")
158
+ : "") || process.env.STORYBOOK_IMAGE_CDN_URL;
159
+
160
+ if (width && height && hasFilters) {
161
+ const urlPath = "/fit-in";
162
+ const urlSize = `/${width}x${height}`;
163
+
164
+ return `${cdnURL}${urlPath}${urlSize}/${filename}`;
165
+ }
166
+
167
+ return `${cdnURL}/${filename}`;
164
168
  }
165
-
166
- return `${cdnURL}/${filename}`;
169
+ return '/images/placeholder-image.jpg';
170
+
167
171
  }
168
172
 
169
173
  export function getPageImage(page) {
@@ -49,6 +49,7 @@ let relations = null;
49
49
  let menus = null;
50
50
  let prefilledMarketModules = null;
51
51
  let prefilledMarketModulesRaw = null;
52
+
52
53
  const searchData = {};
53
54
 
54
55
  export function processSitemapPages(pages, markets) {
@@ -82,8 +83,7 @@ export function processSitemapPages(pages, markets) {
82
83
 
83
84
  const groupByMarketAndType = (data) =>
84
85
  Object.values(data).reduce((acc, item) => {
85
- const market = item.market;
86
- const type = item.type;
86
+ const {market, type} = item;
87
87
 
88
88
  // Initialize language and type groupings if not already present
89
89
  if (!acc[market]) acc[market] = {};
@@ -364,7 +364,6 @@ export default {
364
364
  prefilledMarketModules = data.prefilled_market_modules || {};
365
365
  prefilledMarketModulesRaw = cloneDeep(data.prefilled_market_modules);
366
366
  searchData[market] = [];
367
-
368
367
  Object.keys(transformedPages[market]).forEach((pageType) => {
369
368
  transformedPages[market][pageType].forEach((page, index) => {
370
369
  const translations = translationsData
@@ -476,10 +475,19 @@ export default {
476
475
 
477
476
  // add search data
478
477
  if (searchTemplatesAcitve.includes(page.template) && searchEnabled) {
479
- searchData[page.market].push(
480
- clonePageForCards(
481
- cloneDeep(transformedPages[market][pageType][index]),
482
- '', data.content)
478
+ const { title, path, status, relation } = transformedPages[market][pageType][index];
479
+
480
+ const minimalPage = {
481
+ title,
482
+ pageType,
483
+ path,
484
+ ...(relation?.short_name && { short_name: relation.short_name }),
485
+ ...(relation?.logo && { logo: relation.logo.filename }),
486
+ ...(relation?.game_id && { game_id: relation.game_id })
487
+ };
488
+
489
+ status === 'active' && searchData[page.market].push(
490
+ cloneDeep(minimalPage)
483
491
  );
484
492
  }
485
493
 
@@ -256,6 +256,19 @@ export function processCardsV2(
256
256
  cardItems[b64].sort((a, b) => (a.created_at > b.created_at ? -1 : 1));
257
257
  module.items = cardItems[b64].slice(0, itemLimit);
258
258
  }
259
+ } else if (sortType === "alphabetical") {
260
+ if (cardItems[b64].length > 0) {
261
+ cardItems[b64].sort((a, b) => {
262
+ if (a.title < b.title) {
263
+ return -1;
264
+ }
265
+ if (a.title > b.title) {
266
+ return 1;
267
+ }
268
+ return 0;
269
+ });
270
+ module.items = module.cards_selector_filters_limit ? cardItems[b64].slice(0, module.cards_selector_filters_limit) : cardItems[b64];
271
+ }
259
272
  }
260
273
  }
261
274
  }
Binary file
@@ -13,7 +13,7 @@ set('default_timeout', 960);
13
13
  set('allow_anonymous_stats', false);
14
14
 
15
15
  $hostNames = [
16
- 'storybook' => '82.196.10.160',
16
+ 'storybook' => '167.99.18.218',
17
17
  ];
18
18
 
19
19
  // Storybook
@@ -1,20 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import { IoMdArrowRoundForward } from '@react-icons/all-files/io/IoMdArrowRoundForward';
4
-
5
- import styles from './article.module.scss';
6
-
7
- const Article = ({ item = {} }) => (
8
- <div className={styles.row || ''}>
9
- <h3>{item.title}</h3>
10
- <IoMdArrowRoundForward title="Right-pointing Arrow Icon" />
11
- </div>
12
- );
13
-
14
- Article.propTypes = {
15
- item: PropTypes.shape({
16
- title: PropTypes.string,
17
- }).isRequired,
18
- };
19
-
20
- export default Article;
@@ -1,9 +0,0 @@
1
- .row {
2
- @include flex-align(center, space-between);
3
- background-color: white;
4
- padding: 0 1rem;
5
- border-top: 1px solid #ccc;
6
- h3 {
7
- font-size: 1.6rem;
8
- }
9
- }
@@ -1,18 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
-
4
- import styles from './game.module.scss';
5
-
6
- const Game = ({ item = {} }) => (
7
- <div className={styles.row || ''}>
8
- <h3>{item.title}</h3>
9
- </div>
10
- );
11
-
12
- Game.propTypes = {
13
- item: PropTypes.shape({
14
- title: PropTypes.string,
15
- }).isRequired,
16
- };
17
-
18
- export default Game;
@@ -1,9 +0,0 @@
1
- .row {
2
- @include flex-align(center, space-between);
3
- background-color: white;
4
- padding: 0 1rem;
5
- border-top: 1px solid #ccc;
6
- h3 {
7
- font-size: 1.6rem;
8
- }
9
- }
@@ -1,38 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
-
4
- import LazyImage from '~hooks/lazy-image';
5
- import { imagePrettyUrl } from '~helpers/getters';
6
- import OperatorCta from '~atoms/button/operator-cta';
7
- import styles from './operator.module.scss';
8
-
9
- const Operator = ({ item = {}, width = 56, height = 56 }) => {
10
- const { relation } = item;
11
- const img = relation?.logo?.filename;
12
-
13
- return (
14
- <div className={styles.row || ''}>
15
- <LazyImage
16
- width={width}
17
- height={height}
18
- src={imagePrettyUrl(img, width, height)}
19
- alt={item.title}
20
- />
21
- <h3>{item.title}</h3>
22
- <OperatorCta moduleName="search_autocomplete" operator={relation} gtmClass="toplist-operator-cta-gtm" />
23
- </div>
24
- );
25
- };
26
-
27
- Operator.propTypes = {
28
- item: PropTypes.shape({
29
- title: PropTypes.string,
30
- relation: PropTypes.shape({
31
- logo: PropTypes.shape({}),
32
- }),
33
- }).isRequired,
34
- width: PropTypes.number,
35
- height: PropTypes.number,
36
- };
37
-
38
- export default Operator;
@@ -1,12 +0,0 @@
1
- .row {
2
- @include flex-align(center, space-between);
3
- background-color: white;
4
- padding: 0 1rem;
5
- border-top: 1px solid #ccc;
6
- img {
7
- max-width: 6.5rem;
8
- }
9
- h3 {
10
- font-size: 1.6rem;
11
- }
12
- }