gatsby-core-theme 30.0.103 → 30.0.105

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 (26) hide show
  1. package/.storybook/preview.js +1 -0
  2. package/CHANGELOG.md +42 -0
  3. package/package.json +1 -1
  4. package/src/components/atoms/author-box/description/index.js +0 -14
  5. package/src/components/atoms/author-box/details/index.js +4 -2
  6. package/src/components/atoms/author-box/template-one/author-box.test.js +0 -1
  7. package/src/components/atoms/author-box/template-three/author-box.test.js +0 -1
  8. package/src/components/atoms/author-box/template-two/author-box.test.js +0 -1
  9. package/src/components/atoms/breadcrumbs/breadcrumbs.test.js +1 -7
  10. package/src/components/atoms/breadcrumbs/index.js +4 -8
  11. package/src/components/molecules/content/content.module.scss +8 -0
  12. package/src/components/molecules/content/index.js +7 -5
  13. package/src/components/molecules/floating-area/index.js +7 -0
  14. package/src/components/organisms/cookie-consent/cookie-consent.test.js +2 -0
  15. package/src/components/organisms/cookie-consent/index.js +8 -2
  16. package/src/components/organisms/footer-navigation/footer-navigation.module.scss +96 -0
  17. package/src/components/organisms/footer-navigation/footer-navigation.stories.js +73 -0
  18. package/src/components/organisms/footer-navigation/footer-navigation.test.js +32 -0
  19. package/src/components/organisms/footer-navigation/index.js +45 -0
  20. package/src/components/pages/body/body.module.scss +1 -1
  21. package/src/helpers/getters.mjs +27 -11
  22. package/src/helpers/getters.test.js +50 -0
  23. package/src/helpers/processor/index.mjs +1 -1
  24. package/src/helpers/schema.js +38 -1
  25. package/src/helpers/schema.test.js +76 -49
  26. package/tests/factories/modules/footer-navigation.factory.js +72 -0
@@ -25,6 +25,7 @@ export const parameters = {
25
25
  'Theme',
26
26
  [
27
27
  'Layout',
28
+ ['Navigation', 'Footer', 'Footer Navigation'],
28
29
  'Modules',
29
30
  [
30
31
  'Content',
package/CHANGELOG.md CHANGED
@@ -1,3 +1,45 @@
1
+ ## [30.0.105](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/compare/v30.0.104...v30.0.105) (2024-04-03)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * added required keys to the WebPage schema ([0372ef1](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/0372ef1f55935ece04f7d2b785a926ce4f355ec6))
7
+ * breadcrumbs ([6f7f7fc](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/6f7f7fc47111c6fb6c304ac4b305adf060786798))
8
+ * fixed tests for the schema ([54d0c14](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/54d0c14f6abede9a8461d25b12e9d0332e2c6e62))
9
+ * increased test coverage ([51cb07f](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/51cb07fd0d97b01a58f9afe86ff7fdeda6a6c5fa))
10
+ * remove continue reading link from author box description ([eae0f9a](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/eae0f9a2516dc8e8bc708baba8ecafafedca0f5c))
11
+ * update breadcrumbs translations ([0289c66](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/0289c66b97801e32b885e8a3e110ec3bc43ce4c8))
12
+ * update tests ([f15a058](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/f15a0582057c413f839211e0c0107cc65bd4b448))
13
+
14
+
15
+ * Merge branch 'tm-4187-webpage-schema' into 'master' ([04ff989](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/04ff989b0fb0b43e8e1c6f7fb77be6f6cbcf38b7))
16
+ * Merge remote-tracking branch 'origin' into tm-4187-webpage-schema ([f080656](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/f080656adc8e0c9ce6bb1dcd94f6c650f329d6c7))
17
+ * Merge branch 'tm-4223-breadcrumbs' into 'master' ([a53736d](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/a53736d18d9f8dddf543f34a65893db6febc6c06))
18
+ * Merge branch 'tm-4178-author-box-changes' into 'master' ([ca1e276](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/ca1e276bb2da842e138527a0da64adffe111e923))
19
+
20
+ ## [30.0.104](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/compare/v30.0.103...v30.0.104) (2024-04-02)
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * add show more text in the content just hide with css ([05d1605](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/05d1605c328b6a069e1695d7c01c8e021706d4a9))
26
+ * added footer nav to core theme and added to floating area ([3262cfe](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/3262cfe6c685d182449d50cc150f87bac2ec6fd6))
27
+ * added templates ([2e74696](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/2e746960cedf7eed957155c4f170748101f5358d))
28
+ * fix conflicts ([416e930](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/416e930c40b957bc2fc29f446e7130dfe2915c29))
29
+ * fixed storybook not showing ([67cd31c](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/67cd31ca5d78c169cbc162f9e1c2e139625f7d46))
30
+ * storybook and test for footer navigation ([565158d](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/565158d5b541781600b3f2f05e0548f4156537c4))
31
+ * update theme ([0db0e66](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/0db0e667e364290fa21b0e43718cd930c306e4a9))
32
+
33
+
34
+ ### Code Refactoring
35
+
36
+ * changes to cookie decline with session storage ([d26d9ed](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/d26d9ed8310ea6d934005e7fc6373c2ffc516022))
37
+
38
+
39
+ * Merge branch 'tm-4015-cookie1' into 'master' ([ecdafd6](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/ecdafd678eec7201e58ce7ba446b7bbc87f95e65))
40
+ * Merge branch 'tm-4197-content' into 'master' ([33f3cc8](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/33f3cc8e02d6fd78a01189bbe7cf93a1655afd14))
41
+ * Merge branch 'tm-4054-footer-nav' into 'master' ([6b4b785](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/6b4b785722d88ad93f8844802a63458e011eaca1))
42
+
1
43
  ## [30.0.103](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/compare/v30.0.102...v30.0.103) (2024-03-19)
2
44
 
3
45
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gatsby-core-theme",
3
- "version": "30.0.103",
3
+ "version": "30.0.105",
4
4
  "description": "Gatsby Theme NPM Package",
5
5
  "author": "",
6
6
  "license": "ISC",
@@ -1,14 +1,11 @@
1
1
  import React, { useContext, useRef, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { FaArrowRight } from '@react-icons/all-files/fa/FaArrowRight';
4
3
  import { translate } from '~helpers/getters';
5
4
  import styles from './author-description.module.scss';
6
- import Link from '~hooks/link';
7
5
  import { Context } from '~context/MainProvider';
8
6
 
9
7
  export default function AuthorDescription({
10
8
  author,
11
- contReadIcon = <FaArrowRight title="Right-pointing Arrow Icon" />,
12
9
  template = '',
13
10
  readMore = false,
14
11
  maximumLength = 30,
@@ -45,16 +42,6 @@ export default function AuthorDescription({
45
42
  : author?.biography,
46
43
  }}
47
44
  />
48
- {author?.profile_page_path && (
49
- <Link
50
- to={author?.profile_page_path}
51
- title={author?.name}
52
- className={`${styles.readMore || ''} author-gtm`}
53
- >
54
- {translate(translations, 'cont_read', 'continue reading')}
55
- {contReadIcon}
56
- </Link>
57
- )}
58
45
  </div>
59
46
  );
60
47
  }
@@ -66,7 +53,6 @@ AuthorDescription.propTypes = {
66
53
  expertise: PropTypes.arrayOf(PropTypes.string),
67
54
  profile_page_path: PropTypes.string,
68
55
  }).isRequired,
69
- contReadIcon: PropTypes.node,
70
56
  template: PropTypes.string,
71
57
  readMore: PropTypes.bool,
72
58
  maximumLength: PropTypes.number,
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import { imagePrettyUrl, getAltText, translate } from '~helpers/getters';
4
4
  import styles from './author-details.module.scss';
5
5
  import LazyImage from '~hooks/lazy-image';
6
+ import Link from '~hooks/link';
6
7
  import socialIcons from '../icons/socialIcons';
7
8
  import FactCheckIcon from '../../../../images/icons/fact-check';
8
9
  import { Context } from '~context/MainProvider';
@@ -29,10 +30,10 @@ export default function AuthorDetails({
29
30
  />
30
31
  )}
31
32
  <div className={styles.information || ''}>
32
- <p className={styles.name || ''}>
33
+ <Link to={author?.profile_page_path} className={`${styles.name || ''} author-gtm`}>
33
34
  <span>{author?.name}</span>
34
35
  <FactCheckIcon width={verifiedIconWidth} height={verifiedIconHeight} color="#457BF9" />
35
- </p>
36
+ </Link>
36
37
  <div className={styles.ribbonAndExpert}>
37
38
  {author?.author_title && <p className={styles.title || ''}>{author?.author_title}</p>}
38
39
  {author?.ribbon_label && ribbon && (
@@ -87,6 +88,7 @@ AuthorDetails.propTypes = {
87
88
  ribbon_label: PropTypes.string,
88
89
  experience: PropTypes.string,
89
90
  pages_count: PropTypes.string,
91
+ profile_page_path: PropTypes.string,
90
92
  }),
91
93
  width: PropTypes.number,
92
94
  height: PropTypes.number,
@@ -14,7 +14,6 @@ describe('author box component', () => {
14
14
  expect(getByText('Casino Specialist')).toBeTruthy();
15
15
  expect(container.querySelectorAll('.contactLinks a')).toBeTruthy();
16
16
  expect(container.querySelectorAll('.socialIcons a')).toBeTruthy();
17
- expect(getByText('continue reading')).toBeTruthy();
18
17
  });
19
18
 
20
19
  test('render with read more ', async () => {
@@ -14,7 +14,6 @@ describe('author box component', () => {
14
14
  expect(getByText('Casino Specialist')).toBeTruthy();
15
15
  expect(container.querySelectorAll('.contactLinks a')).toBeTruthy();
16
16
  expect(container.querySelectorAll('.socialIcons a')).toBeTruthy();
17
- expect(getByText('continue reading')).toBeTruthy();
18
17
  });
19
18
 
20
19
  test('render with read more ', async () => {
@@ -14,7 +14,6 @@ describe('author box component', () => {
14
14
  expect(getByText('Casino Specialist')).toBeTruthy();
15
15
  expect(container.querySelectorAll('.contactLinks a')).toBeTruthy();
16
16
  expect(container.querySelectorAll('.socialIcons a')).toBeTruthy();
17
- expect(getByText('continue reading')).toBeTruthy();
18
17
  });
19
18
 
20
19
  test('render with read more ', async () => {
@@ -54,13 +54,7 @@ describe('Show Breadcrumbs in a page', () => {
54
54
  expect(getByText('Home').getAttribute('href')).toEqual('/');
55
55
  expect(getByText('Alternative title')).toBeTruthy();
56
56
  });
57
- test('Translation for Home available', () => {
58
- const page = {
59
- template: 'operator_review',
60
- };
61
- const { getByText } = renderComponent(page);
62
- expect(getByText('Sample Casino')).toBeTruthy();
63
- });
57
+
64
58
  test('Page category available', () => {
65
59
  const page = {
66
60
  categories: [
@@ -5,7 +5,7 @@ import Link from '~hooks/link';
5
5
 
6
6
  import styles from './breadcrumbs.module.scss';
7
7
  import keygen from '~helpers/keygen';
8
- import { translate } from '~helpers/getters';
8
+ import { getHomeBreadcrumbs } from '~helpers/getters';
9
9
  import { Context } from '~context/MainProvider';
10
10
 
11
11
  function Breadcrumbs({ page, separator = <span> / </span>, markets }) {
@@ -19,13 +19,8 @@ function Breadcrumbs({ page, separator = <span> / </span>, markets }) {
19
19
  return <></>;
20
20
  }
21
21
  const isPPC = page?.template === 'ppc';
22
- const home = translate(
23
- translations,
24
- page.categories?.length
25
- ? `home_${page.template}_${page.categories[0].short_name}`
26
- : `home_${page.template}`,
27
- translate(translations, 'Home', 'Home')
28
- );
22
+
23
+ const home = getHomeBreadcrumbs(page, translations);
29
24
 
30
25
  return (
31
26
  <ol className={styles.breadcrumbs || ''}>
@@ -74,6 +69,7 @@ Breadcrumbs.propTypes = {
74
69
  vanity_label: PropTypes.string,
75
70
  translations: PropTypes.shape({}),
76
71
  template: PropTypes.string,
72
+ type: PropTypes.string,
77
73
  categories: PropTypes.arrayOf({
78
74
  short_name: PropTypes.string,
79
75
  }),
@@ -150,6 +150,14 @@
150
150
  padding: 6px;
151
151
  }
152
152
  }
153
+ .hide{
154
+ display: none;
155
+ }
156
+
157
+
158
+ .showMore{
159
+ display: block;
160
+ }
153
161
 
154
162
  .showMoreTwoButtonOpen {
155
163
  border-radius: 8px 8px 0px 0px;
@@ -40,13 +40,15 @@ const Content = ({
40
40
  }
41
41
  };
42
42
 
43
- const mainContent = (content, customClassName = '') => (
43
+ const mainContent = (content, customClassName = '', showMoreText = false) => (
44
44
  <div
45
45
  className={` ${styles.content || ''} ${lists.ul || ''} ${blockquote.blockquote || ''}
46
46
  ${showMore && !isContentCollapsible ? styles.showMore : ''}
47
47
  ${switchStyle(module.style) || ''} ${
48
48
  isHomepageFirstModule ? styles.homepageFirstModuleContent || '' : ''
49
- } ${isModuleIntroduction ? styles.moduleIntroMargin || '' : ''} ${customClassName}`}
49
+ } ${isModuleIntroduction ? styles.moduleIntroMargin || '' : ''} ${customClassName}
50
+ ${showMoreText && !showMore ? styles.hide : ''}
51
+ `}
50
52
  >
51
53
  {parse(content, {
52
54
  replace: (node) => replaceMedia(node, page, translations),
@@ -70,11 +72,11 @@ const Content = ({
70
72
  {icon && isContentCollapsible && icon}
71
73
  </button>
72
74
 
73
- {showMore &&
74
- module.show_more_content &&
75
+ {module.show_more_content &&
75
76
  mainContent(
76
77
  module.show_more_content,
77
- isContentCollapsible ? styles.contentCollapsibleBox : ''
78
+ isContentCollapsible ? styles.contentCollapsibleBox : '',
79
+ true
78
80
  )}
79
81
  </>
80
82
  );
@@ -19,12 +19,18 @@ export default function FloatingArea({
19
19
  const showScroll = isSticky(offsetTop);
20
20
  const [closedBanner, setClosedBanner] = useState(false);
21
21
  const pageType = pageContext.page.relation_type;
22
+ const footerNavigationData = pageContext.marketSections?.footer_navigation || null;
22
23
 
23
24
  const OperatorBanner =
24
25
  pageType === 'operator' && !hideBanner && !hideOperatorBanner
25
26
  ? loadable(() => import(`gatsby-core-theme/src/components/molecules/bonus/template-two`))
26
27
  : null;
27
28
 
29
+ const FooterNavigation =
30
+ footerNavigationData && footerNavigationData?.modules?.length > 0 && pageType !== 'operator'
31
+ ? loadable(() => import(`gatsby-core-theme/src/components/organisms/footer-navigation`))
32
+ : null;
33
+
28
34
  return (
29
35
  <div className={`${customStyles.floatingArea} ${showScroll ? customStyles.show : ''}`}>
30
36
  <ScrollToTop />
@@ -39,6 +45,7 @@ export default function FloatingArea({
39
45
  />
40
46
  )}
41
47
  <CookieConsent />
48
+ {FooterNavigation && <FooterNavigation section={footerNavigationData} />}
42
49
  </div>
43
50
  );
44
51
  }
@@ -57,4 +57,6 @@ describe('cookie consent component', () => {
57
57
  });
58
58
  afterEach(() => {
59
59
  cleanup();
60
+ document.cookie = `CookieConsent=; expires=Thu, 01 Jan 1970 00:00:00; path=/`;
61
+ sessionStorage.removeItem('CookieConsentDecline');
60
62
  });
@@ -22,13 +22,19 @@ const CookieConsent = ({
22
22
  const [ck, setCk] = useState(
23
23
  (typeof window !== `undefined` && getCookie(cookieName) === 'true') || false
24
24
  );
25
+ const [ckReject, setCkReject] = useState(
26
+ (typeof window !== `undefined` && sessionStorage.getItem('CookieConsentDecline') === 'true') ||
27
+ false
28
+ );
25
29
  const { showModal } = useContext(ModalContext);
26
30
  const { translations } = useContext(Context) || {};
27
31
 
28
32
  // when user declines
29
33
  const handleDecline = () => {
30
34
  if (declineURL !== '') navigate(`/${declineURL}`);
31
- setCk(true);
35
+ setCkReject(true);
36
+ setCookie(cookieName, false, 365, '/');
37
+ sessionStorage.setItem('CookieConsentDecline', true);
32
38
  };
33
39
  // when user accepts
34
40
  const handleAccept = () => {
@@ -38,7 +44,7 @@ const CookieConsent = ({
38
44
 
39
45
  return (
40
46
  <>
41
- {!showModal && !ck && (
47
+ {!showModal && !ck && !ckReject && (
42
48
  <div className={styles.cookieConsent || ''}>
43
49
  <div className={styles.content || ''}>{children}</div>
44
50
  <div className={styles.buttonsContainer || ''}>
@@ -0,0 +1,96 @@
1
+ .footerLinks {
2
+ width: 100%;
3
+ height: 6rem;
4
+ padding: 0;
5
+ background-color: white;
6
+ border-radius: 0;
7
+ z-index: 1001;
8
+ display: none;
9
+ &.isStoryBook {
10
+ display: block;
11
+ }
12
+ border-top: 2px solid #6e33e5;
13
+ @media only screen and (max-width: 767px) {
14
+ display: flex;
15
+ }
16
+
17
+ > ul {
18
+ display: flex;
19
+ width: 100%;
20
+ margin-top: -20px;
21
+ padding: 0.5rem 0;
22
+ > li {
23
+ width: 100%;
24
+ @include flex-direction(column-reverse);
25
+ @include flex-align(center, center);
26
+
27
+ span {
28
+ font-style: normal;
29
+ font-weight: 700;
30
+ font-size: 1.2rem;
31
+ line-height: 2rem;
32
+ text-align: center;
33
+ color: #6e33e5;
34
+ position: relative;
35
+ }
36
+
37
+ img {
38
+ width: 4.2rem;
39
+ padding: 0.7rem;
40
+ background-color: #6e33e5;
41
+ border-radius: 2rem;
42
+ }
43
+
44
+ a {
45
+ display: flex;
46
+ flex-flow: column;
47
+
48
+ flex-direction: column-reverse;
49
+ justify-content: center;
50
+ align-items: center;
51
+ color: #6e33e5;
52
+ font-weight: 700;
53
+
54
+ span {
55
+ font-style: normal;
56
+ font-weight: 700;
57
+ font-size: 1.2rem;
58
+ line-height: 2rem;
59
+ text-align: center;
60
+ color: #6e33e5;
61
+ position: relative;
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ .template_three {
69
+ height: 7.2rem;
70
+ > ul {
71
+ margin-top: 0;
72
+ }
73
+ }
74
+
75
+ .template_two {
76
+ height: 7.2rem;
77
+ background-color: #262629;
78
+ border-top: 2px solid #262629;
79
+ > ul {
80
+ margin-top: 0;
81
+
82
+ > li {
83
+ color: white;
84
+
85
+ a {
86
+ > span {
87
+ color: white;
88
+ }
89
+ }
90
+
91
+ img {
92
+ background-color: transparent;
93
+ }
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,73 @@
1
+ import React from 'react';
2
+ // eslint-disable-next-line import/no-extraneous-dependencies
3
+ import {
4
+ Title,
5
+ Description,
6
+ Primary,
7
+ PRIMARY_STORY,
8
+ ArgsTable,
9
+ } from '@storybook/addon-docs/blocks';
10
+
11
+ import { getModules } from '~tests/factories/modules/footer-navigation.factory';
12
+ import FooterNavigation from '.';
13
+
14
+ export default {
15
+ title: 'Theme/Layout/Footer Navigation',
16
+ component: FooterNavigation,
17
+ argTypes: {
18
+ section: {
19
+ name: 'section',
20
+ type: { name: 'object', required: true },
21
+ defaultValue: '',
22
+ description: 'Section object containing a menu module.',
23
+ table: {
24
+ type: { summary: 'object' },
25
+ defaultValue: { summary: '' },
26
+ },
27
+ },
28
+ },
29
+ parameters: {
30
+ docs: {
31
+ description: {
32
+ component:
33
+ 'A component that displays a list of links for use on Mobile only as component fixed to the bottom of the screen.',
34
+ },
35
+ page: () => (
36
+ <>
37
+ <Title />
38
+ <Description />
39
+ <Primary />
40
+ <ArgsTable story={PRIMARY_STORY} />
41
+ </>
42
+ ),
43
+ },
44
+ },
45
+ };
46
+
47
+ const Template = (args) => <FooterNavigation {...args} />;
48
+ export const TemplateOne = Template.bind({});
49
+ TemplateOne.args = {
50
+ section: {
51
+ style: 'template_one',
52
+ modules: getModules(),
53
+ },
54
+ isStorybook: true,
55
+ };
56
+
57
+ export const TemplateTwo = Template.bind({});
58
+ TemplateTwo.args = {
59
+ section: {
60
+ style: 'template_two',
61
+ modules: getModules(),
62
+ },
63
+ isStorybook: true,
64
+ };
65
+
66
+ export const TemplateThree = Template.bind({});
67
+ TemplateThree.args = {
68
+ section: {
69
+ style: 'template_three',
70
+ modules: getModules(),
71
+ },
72
+ isStorybook: true,
73
+ };
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import { render, cleanup } from '@testing-library/react';
3
+ import '@testing-library/jest-dom/extend-expect';
4
+
5
+ import { getModules } from '~tests/factories/modules/footer-navigation.factory';
6
+ import FooterNavigation from '.';
7
+
8
+ describe('Footer Nav component', () => {
9
+ test('render Nav', () => {
10
+ const { container, getByText } = render(
11
+ <FooterNavigation
12
+ isStorybook={true}
13
+ section={{
14
+ style: 'template_one',
15
+ modules: getModules(),
16
+ }}
17
+ />
18
+ );
19
+ expect(container).toBeTruthy();
20
+
21
+ // Default cards
22
+ expect(getByText('Home')).toBeTruthy();
23
+ expect(getByText('Bonus')).toBeTruthy();
24
+ expect(getByText('Casinos')).toBeTruthy();
25
+ expect(getByText('Slots')).toBeTruthy();
26
+ expect(container.querySelectorAll('a')).toHaveLength(4);
27
+ expect(container.querySelectorAll('img')).toHaveLength(4);
28
+ });
29
+ });
30
+ afterEach(() => {
31
+ cleanup();
32
+ });
@@ -0,0 +1,45 @@
1
+ /* eslint-disable react-hooks/exhaustive-deps */
2
+ import React from 'react';
3
+ // eslint-disable-next-line import/no-extraneous-dependencies
4
+ import PropTypes from 'prop-types';
5
+
6
+ import LinkList from 'gatsby-core-theme/src/components/molecules/link-list';
7
+ import { getFirstModuleByName } from 'gatsby-core-theme/src/helpers/getters';
8
+ import styles from './footer-navigation.module.scss';
9
+
10
+ const FooterNavigation = ({ section, isStorybook = false }) => {
11
+ const menuArray = getFirstModuleByName(section, 'menu');
12
+ return (
13
+ menuArray?.children?.length > 0 && (
14
+ <div
15
+ className={`${styles?.footerLinks || ''}
16
+ ${isStorybook ? styles?.isStoryBook : ''}
17
+ ${styles?.showFooterNav || ''}
18
+ ${styles[section.style]}
19
+ `}
20
+ >
21
+ <LinkList
22
+ showListTitle={false}
23
+ singleList
24
+ lists={menuArray}
25
+ width={48}
26
+ height={48}
27
+ gtmClass="mobile-menu-gtm"
28
+ />
29
+ </div>
30
+ )
31
+ );
32
+ };
33
+
34
+ FooterNavigation.propTypes = {
35
+ section: PropTypes.shape({
36
+ modules: PropTypes.arrayOf(
37
+ PropTypes.shape({
38
+ name: PropTypes.string,
39
+ })
40
+ ),
41
+ }).isRequired,
42
+ isStorybook: PropTypes.bool,
43
+ };
44
+
45
+ export default FooterNavigation;
@@ -3,7 +3,7 @@
3
3
  width: 100%;
4
4
  position: fixed;
5
5
  transition: all 1s;
6
- bottom: -30rem;
6
+ bottom: -40rem;
7
7
  z-index: map-get($z-index, scroll-to-top);
8
8
  }
9
9
 
@@ -127,8 +127,9 @@ export function imagePrettyUrl(filename = 'image.png', width = null, height = nu
127
127
  const cdnURL =
128
128
  (hasFilters
129
129
  ? process.env.IMAGE_CDN_URL
130
- : process.env.IMAGE_CDN_URL.replace('/filters:format(webp)', '')) ||
131
- process.env.STORYBOOK_IMAGE_CDN_URL;
130
+ : process.env.IMAGE_CDN_URL
131
+ ? process.env.IMAGE_CDN_URL.replace('/filters:format(webp)', '')
132
+ : '') || process.env.STORYBOOK_IMAGE_CDN_URL;
132
133
 
133
134
  if (width && height && hasFilters) {
134
135
  const urlPath = '/fit-in';
@@ -171,9 +172,7 @@ export function getPageImage(page) {
171
172
 
172
173
  export function getParameterCaseInsensitive(object, key) {
173
174
  const asLowercase = key.toLowerCase();
174
- return object[Object.keys(object)
175
- .find(k => k.toLowerCase() === asLowercase)
176
- ];
175
+ return object[Object.keys(object).find((k) => k.toLowerCase() === asLowercase)];
177
176
  }
178
177
 
179
178
  export function prettyTracker(
@@ -185,16 +184,20 @@ export function prettyTracker(
185
184
  if (pageTemplateName && operator && operator.links && operator.links[pageTemplateName]) {
186
185
  return generateTrackerLink(operator, pageTemplateName, provider);
187
186
  }
188
-
187
+
189
188
  if (!operator || !operator.short_name || (operator.status && operator.status !== 'active')) {
190
189
  return null;
191
190
  }
192
191
 
193
192
  // if tracker is not main, check if the tracker exists, if not use main
194
- if (trackerType !== 'main' && operator.links && getParameterCaseInsensitive(operator.links, trackerType) === undefined) {
193
+ if (
194
+ trackerType !== 'main' &&
195
+ operator.links &&
196
+ getParameterCaseInsensitive(operator.links, trackerType) === undefined
197
+ ) {
195
198
  trackerType = 'main';
196
199
  }
197
-
200
+
198
201
  return generateTrackerLink(operator, trackerType, provider);
199
202
  }
200
203
 
@@ -356,6 +359,19 @@ export function translate(translations, key, defaultValue = '') {
356
359
  return defaultValue;
357
360
  }
358
361
 
362
+ export function getHomeBreadcrumbs(page, translations) {
363
+ const home = translate(
364
+ translations,
365
+ (page.categories?.length > 0 &&
366
+ translations[`home_${page.template}_${page.categories[0].short_name}`] !== undefined)
367
+ ? `home_${page.template}_${page.categories[0].short_name}`
368
+ : `home_${page.type}`,
369
+ translate(translations, 'Home', 'Home')
370
+ );
371
+
372
+ return home;
373
+ }
374
+
359
375
  // This part of the code, help us to add operator related to the page as the first item in the comparison table automatically
360
376
  // example: if we are on the operator review page(Rizk) that should be first in the comparison table
361
377
  // First, we check if we have that item in the array, if that item is in the array, we check its position
@@ -439,6 +455,6 @@ export function getBonusData(item, tracker, splitBy = '+') {
439
455
  return {
440
456
  isInoperative,
441
457
  mainLine,
442
- secondLine
443
- }
444
- }
458
+ secondLine,
459
+ };
460
+ }
@@ -266,6 +266,56 @@ describe('Getters Helper', () => {
266
266
  expect(Getters.translate(object, 'foo', 'bar')).toEqual('bar');
267
267
  });
268
268
 
269
+ test('getHomeBreadcrumbs() with categories', () => {
270
+ const page = {
271
+ categories: [{ short_name: 'category1' }],
272
+ template: 'template1',
273
+ type: 'type1',
274
+ };
275
+ const translations = {
276
+ home_template1_category1: 'Template 1 Category 1 Home',
277
+ Home: 'Home',
278
+ };
279
+ expect(Getters.getHomeBreadcrumbs(page, translations)).toEqual('Template 1 Category 1 Home');
280
+ });
281
+
282
+ test('getHomeBreadcrumbs() with categories but with no category translations', () => {
283
+ const page = {
284
+ categories: [{ short_name: 'category2' }, { short_name: 'category1' }],
285
+ template: 'template1',
286
+ type: 'type1',
287
+ };
288
+ const translations = {
289
+ home_type1_category1: 'Template 1 Category 1 Home',
290
+ home_type1: 'Template 1',
291
+ Home: 'Home',
292
+ };
293
+ expect(Getters.getHomeBreadcrumbs(page, translations)).toEqual('Template 1');
294
+ });
295
+
296
+ test('getHomeBreadcrumbs() without categories', () => {
297
+ const page = {
298
+ categories: [],
299
+ type: 'type1',
300
+ };
301
+ const translations = {
302
+ home_type1: 'Type 1 Home',
303
+ Home: 'Home',
304
+ };
305
+ expect(Getters.getHomeBreadcrumbs(page, translations)).toEqual('Type 1 Home');
306
+ });
307
+
308
+ test('getHomeBreadcrumbs() with missing translation', () => {
309
+ const page = {
310
+ categories: [],
311
+ type: 'type2',
312
+ };
313
+ const translations = {
314
+ Home: 'Home',
315
+ };
316
+ expect(Getters.getHomeBreadcrumbs(page, translations)).toEqual('Home');
317
+ });
318
+
269
319
  const module = {
270
320
  items: [
271
321
  {
@@ -318,7 +318,7 @@ export default {
318
318
  }
319
319
 
320
320
  // add reviewer object to page
321
- if (page.reviewer_id !== null && Object.values(data.authors).length) {
321
+ if (page.reviewer_id !== null && data.authors[page.reviewer_id]) {
322
322
  transformedPages[market][pageType][index].reviewer = data.authors[page.reviewer_id];
323
323
  }
324
324
 
@@ -185,7 +185,26 @@ export function webPageSchema(page, pageImage) {
185
185
  url: `${process.env.GATSBY_SITE_URL}`,
186
186
  inLanguage: getLanguage(page?.language),
187
187
  },
188
- author: {
188
+ reviewedBy: page?.reviewer_id
189
+ ? {
190
+ '@type': 'Person',
191
+ name: page?.reviewer?.name,
192
+ jobTitle: page?.reviewer?.author_title,
193
+ image: page?.reviewer?.image_object?.url,
194
+ email: page?.reviewer?.email_address,
195
+ nationality: {
196
+ '@type': 'Country',
197
+ name: page?.reviewer?.country?.name,
198
+ },
199
+
200
+ knowsAbout: page?.reviewer?.knows_abouts?.map((item) => ({
201
+ '@type': item?.type,
202
+ name: item?.name,
203
+ sameAs: item?.links.map((link) => link?.link).filter((link) => link),
204
+ })),
205
+ }
206
+ : '',
207
+ publisher: {
189
208
  '@type': 'Organization',
190
209
  name: page?.siteSchema?.site_name || '',
191
210
  alternateName: page?.siteSchema?.alias_site_name || '',
@@ -207,6 +226,23 @@ export function webPageSchema(page, pageImage) {
207
226
  .filter((socialLink) => socialLink),
208
227
  ...(page?.knowsAbout ? { knowsAbout: page?.knowsAbout } : {}),
209
228
  },
229
+ author: {
230
+ '@type': 'Person',
231
+ name: page?.author?.name,
232
+ jobTitle: page?.author?.author_title,
233
+ image: page?.author?.image_object?.url,
234
+ email: page?.author?.email_address,
235
+ nationality: {
236
+ '@type': 'Country',
237
+ name: page?.author?.country?.name,
238
+ },
239
+
240
+ knowsAbout: page?.author?.knows_abouts?.map((item) => ({
241
+ '@type': item?.type,
242
+ name: item?.name,
243
+ sameAs: item?.links.map((link) => link?.link).filter((link) => link),
244
+ })),
245
+ },
210
246
  };
211
247
  if (speakAbleModules.length > 0) {
212
248
  schema['@speakAbleModules'] = speakAbleModules.map((module) => JSON.parse(module));
@@ -285,6 +321,7 @@ export function templateSchemas(page, pageImage) {
285
321
  '@type': 'Person',
286
322
  name: page.author?.name?.substring(0, 100),
287
323
  url: getUrl(page.author?.profile_page_path || '/'),
324
+ ...(page?.author?.knowsAbout ? { knowsAbout: page?.author?.knowsAbout } : {}),
288
325
  },
289
326
  publisher: {
290
327
  '@type': 'Organization',
@@ -118,18 +118,75 @@ describe('Schema Helper', () => {
118
118
  created_at: '01/01/01',
119
119
  updated_at: '02/02/02',
120
120
  seo_keywords: ['keyword_a', 'keyword_b'],
121
- authors: [
122
- {
123
- profile_page_path: 'author_a/author_page',
124
- email_address: 'email_a@email.com',
125
- name: 'Author_A',
121
+ author: {
122
+ profile_page_path: 'author_a/author_page',
123
+ email_address: 'email_a@email.com',
124
+ name: 'Author_A',
125
+ image_object: {
126
+ url: 'author_image.jpg',
126
127
  },
127
- {
128
- profile_page_path: 'author_b/author_page',
129
- email_address: 'email_b@email.com',
130
- name: 'Author_B',
128
+ knows_abouts: [
129
+ {
130
+ '@type': 'Thing',
131
+ name: 'Ireland',
132
+ links: [
133
+ {
134
+ link: 'www.test.com',
135
+ },
136
+ {
137
+ link: 'www.test2.com',
138
+ },
139
+ ],
140
+ },
141
+ {
142
+ '@type': 'Thing',
143
+ name: 'South Africa',
144
+ links: [
145
+ {
146
+ link: 'www.test.com',
147
+ },
148
+ {
149
+ link: 'www.test2.com',
150
+ },
151
+ ],
152
+ },
153
+ ],
154
+ },
155
+ reviewer_id: 112,
156
+ reviewer: {
157
+ profile_page_path: 'reviewer_a/author_page',
158
+ email_address: 'email_a@email.com',
159
+ name: 'Reviewer_A',
160
+ image_object: {
161
+ url: 'reviewer_image.jpg',
131
162
  },
132
- ],
163
+ knows_abouts: [
164
+ {
165
+ '@type': 'Thing',
166
+ name: 'Ireland',
167
+ links: [
168
+ {
169
+ link: 'www.test.com',
170
+ },
171
+ {
172
+ link: 'www.test2.com',
173
+ },
174
+ ],
175
+ },
176
+ {
177
+ '@type': 'Thing',
178
+ name: 'South Africa',
179
+ links: [
180
+ {
181
+ link: 'www.test.com',
182
+ },
183
+ {
184
+ link: 'www.test2.com',
185
+ },
186
+ ],
187
+ },
188
+ ],
189
+ },
133
190
  siteSchema: {
134
191
  site_name: 'Site Name',
135
192
  alias_site_name: 'Alias Site Name',
@@ -151,56 +208,26 @@ describe('Schema Helper', () => {
151
208
  expect(json.inLanguage).toEqual('site_lang');
152
209
  expect(json.datePublished).toEqual('01/01/01');
153
210
  expect(json.dateModified).toEqual('02/02/02');
154
-
155
211
  expect(Object.prototype.toString.call(json.isPartOf)).toEqual('[object Object]');
156
212
  expect(json.isPartOf['@type']).toEqual('WebSite');
157
213
  expect(json.isPartOf['@id']).toEqual(`${process.env.GATSBY_SITE_URL}#website`);
158
214
  expect(json.isPartOf.url).toEqual(`${process.env.GATSBY_SITE_URL}`);
159
215
  expect(json.isPartOf.inLanguage).toEqual('site_lang');
160
-
161
216
  expect(Object.prototype.toString.call(json.primaryImageOfPage)).toEqual('[object Object]');
162
217
  expect(json.primaryImageOfPage['@type']).toEqual('ImageObject');
163
218
  expect(json.primaryImageOfPage['@id']).toEqual(`${getUrl('web_page')}#primaryimage`);
164
219
  expect(json.primaryImageOfPage.url).toEqual(pageImage);
165
220
  expect(json.primaryImageOfPage.inLanguage).toEqual('site_lang');
166
-
167
221
  expect(Object.prototype.toString.call(json.author)).toEqual('[object Object]');
168
- expect(json.author['@type']).toEqual('Organization');
169
- expect(json.author.name).toEqual('Site Name');
170
- expect(json.author.alternateName).toEqual('Alias Site Name');
171
- expect(json.author.foundingDate).toEqual('01/02/03');
172
- expect(json.author.publishingPrinciples).toEqual(undefined);
173
-
174
- expect(Object.prototype.toString.call(json.author.logo)).toEqual('[object Object]');
175
- expect(json.author.logo['@type']).toEqual('ImageObject');
176
- expect(json.author.logo.url).toEqual(pageImage);
177
-
178
- expect(Object.prototype.toString.call(json.author.contactPoint)).toEqual('[object Array]');
179
- expect(json.author.contactPoint).toHaveLength(2);
180
- expect(Object.prototype.toString.call(json.author.contactPoint[0])).toEqual('[object Object]');
181
- expect(Object.prototype.toString.call(json.author.contactPoint[1])).toEqual('[object Object]');
182
- expect(json.author.contactPoint[0]['@type']).toEqual('ContactPoint');
183
- expect(json.author.contactPoint[1]['@type']).toEqual('ContactPoint');
184
- expect(json.author.contactPoint[0].contactType).toEqual('Author');
185
- expect(json.author.contactPoint[1].contactType).toEqual('Author');
186
- expect(json.author.contactPoint[0].url).toEqual(`${getUrl('/')}author_a/author_page`);
187
- expect(json.author.contactPoint[1].url).toEqual(`${getUrl('/')}author_b/author_page`);
188
- expect(json.author.contactPoint[0].name).toEqual('Author_A');
189
- expect(json.author.contactPoint[1].name).toEqual('Author_B');
190
- expect(json.author.contactPoint[0].email).toEqual('email_a@email.com');
191
- expect(json.author.contactPoint[1].email).toEqual('email_b@email.com');
192
-
193
- expect(Object.prototype.toString.call(json.author.sameAs)).toEqual('[object Array]');
194
- expect(json.author.sameAs).toHaveLength(3);
195
- expect(json.author.sameAs[0]).toEqual('author_linkedin');
196
- expect(json.author.sameAs[1]).toEqual('author_spotify');
197
- expect(json.author.sameAs[2]).toEqual('author_wikipedia');
198
- expect(json.author.knowsAbout).toEqual(undefined);
199
-
200
- expect(Object.prototype.toString.call(json.keywords)).toEqual('[object Array]');
201
- expect(json.keywords).toHaveLength(2);
202
- expect(json.keywords[0]).toEqual('keyword_a');
203
- expect(json.keywords[1]).toEqual('keyword_b');
222
+ expect(json.author['@type']).toEqual('Person');
223
+ expect(json.author.image).toEqual('author_image.jpg');
224
+ expect(Object.prototype.toString.call(json.author.knowsAbout)).toEqual('[object Array]');
225
+ expect(json.author.knowsAbout).toHaveLength(2);
226
+ expect(Object.prototype.toString.call(json.reviewedBy)).toEqual('[object Object]');
227
+ expect(json.reviewedBy['@type']).toEqual('Person');
228
+ expect(json.reviewedBy.image).toEqual('reviewer_image.jpg');
229
+ expect(Object.prototype.toString.call(json.reviewedBy.knowsAbout)).toEqual('[object Array]');
230
+ expect(json.reviewedBy.knowsAbout).toHaveLength(2);
204
231
  });
205
232
 
206
233
  test('organizationSchema()', () => {
@@ -0,0 +1,72 @@
1
+ export function getModules() {
2
+ return [
3
+ {
4
+ menu_id: 353,
5
+ menu_short_code: 'footer_navigation',
6
+ module_value_id: 167244,
7
+ name: 'menu',
8
+ title: 'Footer Navigation',
9
+ short_code: 'footer_navigation',
10
+ language: 'en',
11
+ market: 'za_en',
12
+ children: [
13
+ {
14
+ id: 5503,
15
+ page_id: null,
16
+ title: 'Home',
17
+ nofollow: false,
18
+ value: '/za_en/visit/footerNav/zar_casino',
19
+ image: '1711362545/home.svg',
20
+ image_object: {
21
+ width: '80',
22
+ height: '80',
23
+ url: 'https://assets-srv.s3.eu-west-1.amazonaws.com/1711362545/home.svg',
24
+ filename: 'zar-casinopng.webp',
25
+ },
26
+ },
27
+ {
28
+ id: 5501,
29
+ page_id: null,
30
+ title: 'Bonus',
31
+ nofollow: false,
32
+ value: '/za_en/visit/footerNav/springbok_casino',
33
+ image: '1711362636/bonus.svg',
34
+ image_object: {
35
+ width: '80',
36
+ height: '80',
37
+ url: 'https://assets-srv.s3.eu-west-1.amazonaws.com/1711362636/bonus.svg',
38
+ filename: 'springbok-2png.webp',
39
+ },
40
+ },
41
+ {
42
+ id: 5613,
43
+ page_id: null,
44
+ title: 'Casinos',
45
+ nofollow: false,
46
+ value: '/za_en/visit/footerNav/rich_casino',
47
+ // image: 'rich-casinopng.webp',
48
+ logo: {
49
+ width: '80',
50
+ height: '80',
51
+ url: 'https://assets-srv.s3.eu-west-1.amazonaws.com/1711362583/sports-%26-casino-casino-chip.svg',
52
+ filename: 'rich-casinopng.webp',
53
+ },
54
+ },
55
+ {
56
+ id: 5614,
57
+ page_id: null,
58
+ title: 'Slots',
59
+ nofollow: false,
60
+ value: '/za_en/visit/footerNav/punt_casino',
61
+ // image: 'rich-casinopng.webp',
62
+ logo: {
63
+ width: '80',
64
+ height: '80',
65
+ url: 'https://assets-srv.s3.eu-west-1.amazonaws.com/1711362652/sports-%26-casino-slots.svg',
66
+ filename: 'punt-casinopng-1.webp',
67
+ },
68
+ },
69
+ ],
70
+ },
71
+ ];
72
+ }