gatsby-core-theme 27.0.1 → 28.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,33 @@
1
+ # [28.0.0](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/compare/v27.0.1...v28.0.0) (2023-08-22)
2
+
3
+
4
+ ### Code Refactoring
5
+
6
+ * add ([1fb65d2](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/1fb65d24108a9e258a33bec5aba9e3b55476b4e5))
7
+ * add button label ([6fffc12](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/6fffc12888e0f53e0bd93b3d9f5d1767bd4106a1))
8
+ * add multiple markets, validations, tests, recaptcha validation ([b5b5c36](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/b5b5c367bdaeacfe67d76ba071b5599b249d0763))
9
+ * add prop to show/hide button icon ([528d5f3](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/528d5f33ef13c271c7e419d798408fd7de173486))
10
+ * add recaptcha to form fields ([f2a77b9](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/f2a77b9d999f313cbeb50fc17272be5debc1e735))
11
+ * change button style so on site level it adapts the primary button styles ([9f565ce](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/9f565cebf9fae352e1784cf7db91163c2f0faf04))
12
+ * change to test ([1132888](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/113288861a9087325d2a2549ed6fcda4995673c7))
13
+ * changes to qa feedback and enable to add links inside field labels ([578b21b](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/578b21b7884229b1918ecde872ac84081a670c54))
14
+ * correction to label display condition ([305c026](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/305c026fa53a64d8fc451695adc2b5ebdc28cba3))
15
+ * correction to validation ([2d2ebbc](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/2d2ebbc6586827251786afef376ba10be8fdd4d6))
16
+ * form link styles ([0c1dbd9](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/0c1dbd97ef2ff9f7b94708b33af113162647cbe5))
17
+ * remove test recpatcha ([65401aa](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/65401aad2e185c1355aca5cf54b9d66a90432af6))
18
+ * remove unused vars and correction to form storybook ([01545ef](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/01545ef5685c9559221766356c4b821ca844e4f8))
19
+ * remove unwanted state ([9704234](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/97042348141c2f6afd3e1b030a7f537539ffc9cb))
20
+
21
+
22
+ * Merge branch 'tm-3556-contact-us-page' into 'master' ([c79e7d8](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/c79e7d846b6bc84f006fcc54f0e1f6b2bc524010))
23
+ * Merge branch 'master' into tm-3556-contact-us-page ([3984e56](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/3984e56d59ac1939eae7d2828ee9337bca43311d))
24
+ * Merge branch 'master' into tm-3556-contact-us-page ([960071a](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/960071a5f887163d1d293c610334f75a07566261))
25
+
26
+
27
+ ### Tests
28
+
29
+ * test for components ([5784586](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/5784586cbc8e7675956b70f75ae8a5135568208d))
30
+
1
31
  ## [27.0.1](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/compare/v27.0.0...v27.0.1) (2023-08-21)
2
32
 
3
33
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gatsby-core-theme",
3
- "version": "27.0.1",
3
+ "version": "28.0.0",
4
4
  "description": "Gatsby Theme NPM Package",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -7,12 +7,22 @@ import {
7
7
  PRIMARY_STORY,
8
8
  ArgsTable,
9
9
  } from '@storybook/addon-docs/blocks';
10
- import { siteSchema } from 'gatsby-core-theme/tests/factories/modules/site-schema.factory';
10
+ import getPageList from '~tests/factories/pages/page.factory';
11
11
  import ContactForm from '.';
12
12
 
13
- const page = {};
14
- siteSchema.support_email = 'contact@irishluck.ie';
15
- page.siteSchema = { ...siteSchema };
13
+ const relationType = 'default';
14
+ const market = 'ie_en';
15
+ const samplePages = {
16
+ [market]: {
17
+ [relationType]: getPageList({
18
+ template: relationType,
19
+ count: 3,
20
+ relation: null,
21
+ relation_id: 5,
22
+ market,
23
+ }),
24
+ },
25
+ };
16
26
 
17
27
  export default {
18
28
  title: 'Theme/Modules/Template Blocks/Contact Form',
@@ -52,6 +62,36 @@ export default {
52
62
  defaultValue: 'Fill all the required fields.',
53
63
  description: 'sent message info',
54
64
  },
65
+ buttonLabel: {
66
+ name: 'buttonLabel',
67
+ type: { name: 'string', required: false },
68
+ defaultValue: 'Send Message',
69
+ description: 'Send button text',
70
+ },
71
+ showButtonIcon: {
72
+ name: 'showButtonIcon',
73
+ type: { name: 'boolean', required: false },
74
+ defaultValue: true,
75
+ description: 'Show/hide button icon',
76
+ },
77
+ showLabels: {
78
+ name: 'showLabels',
79
+ type: { name: 'boolean', required: false },
80
+ defaultValue: true,
81
+ description: 'Show/hide form field lables',
82
+ },
83
+ showHeading: {
84
+ name: 'showHeading',
85
+ type: { name: 'boolean', required: false },
86
+ defaultValue: true,
87
+ description: 'Show/hide intro title of page',
88
+ },
89
+ showDescription: {
90
+ name: 'showDescription',
91
+ type: { name: 'boolean', required: false },
92
+ defaultValue: true,
93
+ description: 'Show/hide intro description of page',
94
+ },
55
95
  },
56
96
  parameters: {
57
97
  docs: {
@@ -73,5 +113,5 @@ export default {
73
113
  const Template = (args) => <ContactForm {...args} />;
74
114
  export const Default = Template.bind({});
75
115
  Default.args = {
76
- page,
116
+ page: samplePages[market][relationType][0],
77
117
  };
@@ -2,38 +2,80 @@
2
2
  import React from 'react';
3
3
  import { render, cleanup, waitFor } from '@testing-library/react';
4
4
  import '@testing-library/jest-dom/extend-expect';
5
- import { siteSchema } from 'gatsby-core-theme/tests/factories/modules/site-schema.factory';
5
+ import getPageList from '~tests/factories/pages/page.factory';
6
6
  import ContactForm from '.';
7
7
 
8
- const page = {};
8
+ let samplePages;
9
+ const relationType = 'default';
10
+ const market = 'ie_en';
9
11
 
10
12
  beforeEach(() => {
11
- page.siteSchema = { ...siteSchema };
13
+ samplePages = {
14
+ [market]: {
15
+ [relationType]: getPageList({
16
+ template: relationType,
17
+ count: 3,
18
+ relation: null,
19
+ relation_id: 5,
20
+ market,
21
+ }),
22
+ },
23
+ };
12
24
  });
13
25
  describe('Contact Us form Component', () => {
14
26
  test('render contact us form and follow us titles', async () => {
15
- const { container } = render(<ContactForm page={page} submitUrl="submit" />);
27
+ const { container } = render(
28
+ <ContactForm page={samplePages[market][relationType][0]} submitUrl="submit" />
29
+ );
16
30
 
17
31
  await waitFor(() => {
18
32
  expect(container).toBeTruthy();
19
33
  expect(container.querySelector('H1').innerHTML).toBe('Contact Us');
20
34
  });
21
35
  });
36
+ test('render form without main heading and description ', () => {
37
+ const { container } = render(
38
+ <ContactForm
39
+ page={samplePages[market][relationType][0]}
40
+ submitUrl="submit"
41
+ showHeading={false}
42
+ showDescription={false}
43
+ />
44
+ );
45
+
46
+ expect(container.querySelector('h1')).toBeFalsy();
47
+ });
22
48
  test('render contact us form ', async () => {
23
- const { container } = render(<ContactForm page={page} submitUrl="submit" />);
49
+ const { container } = render(
50
+ <ContactForm page={samplePages[market][relationType][0]} submitUrl="submit" />
51
+ );
24
52
 
25
53
  await waitFor(() => {
26
54
  expect(container.querySelector('.contactForm')).toBeTruthy();
27
- expect(container.querySelector('.contactForm').querySelectorAll('input')).toHaveLength(3);
55
+ expect(container.querySelector('.contactForm').querySelectorAll('input')).toHaveLength(4);
28
56
  expect(
29
57
  container.querySelector('.contactForm').querySelectorAll('input[type="text"]')
30
- ).toHaveLength(1);
58
+ ).toHaveLength(2);
31
59
  expect(
32
60
  container.querySelector('.contactForm').querySelectorAll('input[type="email"]')
33
61
  ).toHaveLength(1);
34
62
  expect(container.querySelector('.contactForm').querySelectorAll('textarea')).toHaveLength(1);
35
63
  });
36
64
  });
65
+ test('render form without labels ', () => {
66
+ const { container } = render(
67
+ <ContactForm
68
+ page={samplePages[market][relationType][0]}
69
+ submitUrl="submit"
70
+ showLabels={false}
71
+ />
72
+ );
73
+
74
+ expect(container.querySelectorAll('input[type="text"]').length).toEqual(2);
75
+ expect(container.querySelector('textarea')).toBeTruthy();
76
+ expect(container.querySelector('.formGroup').querySelector('label')).toBeFalsy();
77
+ expect(container.querySelectorAll('label').length).toEqual(1);
78
+ });
37
79
  });
38
80
  afterEach(() => {
39
81
  cleanup();
@@ -7,32 +7,42 @@ import { contactUsForm } from '../../../constants/forms';
7
7
  import styles from './contact-form.module.scss';
8
8
 
9
9
  const ContactForm = ({
10
- submitUrl,
10
+ page,
11
+ submitUrl = '',
11
12
  successMessage = 'Message sent successfully.',
12
13
  failMessage = 'Message failed to send.',
13
14
  validationMessage = 'Fill all the required fields.',
15
+ buttonLabel = 'Send Message',
16
+ showButtonIcon = true,
17
+ showLabels = true,
18
+ showHeading = true,
19
+ showDescription = true,
14
20
  }) => {
15
21
  const { translations } = useContext(Context) || {};
22
+ const activeMarket = page?.market;
16
23
 
17
24
  return (
18
- <div className={styles.contactForm}>
19
- <div className={styles.contactBox}>
20
- <div className={styles.formWrapper}>
21
- <h1>{translate(translations, 'contact_us', 'Contact Us')}</h1>
22
- <p>
23
- {translate(
24
- translations,
25
- 'contact_us_questions',
26
- `If you have questions about our reviews, games or content, or just want to leave some
27
- feedback, the Irish Luck team would love to hear from you. You can contact us any time
28
- using the details below and we'll endeavour to get back to you within 48 hours.`
29
- )}
30
- </p>
25
+ <div className={styles.contactForm || ''}>
26
+ <div className={styles.contactBox || ''}>
27
+ <div className={styles.formWrapper || ''}>
28
+ {showHeading && <h1>{translate(translations, 'contact_us', 'Contact Us')}</h1>}
29
+ {showDescription && (
30
+ <p>
31
+ {translate(
32
+ translations,
33
+ 'contact_us_questions',
34
+ `If you have questions about our reviews, games or content, or just want to leave some
35
+ feedback, the Irish Luck team would love to hear from you. You can contact us any time
36
+ using the details below and we'll endeavour to get back to you within 48 hours.`
37
+ )}
38
+ </p>
39
+ )}
31
40
  <Form
32
- formOptions={contactUsForm}
33
- type="contact"
41
+ formOptions={contactUsForm[activeMarket]}
42
+ showLabels={showLabels}
34
43
  hasButton
35
- buttonLabel={translate(translations, 'send_msg', 'Send Message')}
44
+ showButtonIcon={showButtonIcon}
45
+ buttonLabel={buttonLabel}
36
46
  submitUrl={submitUrl}
37
47
  successMessage={successMessage}
38
48
  failMessage={failMessage}
@@ -47,9 +57,16 @@ const ContactForm = ({
47
57
  export default ContactForm;
48
58
 
49
59
  ContactForm.propTypes = {
50
- page: PropTypes.shape({}).isRequired,
60
+ page: PropTypes.shape({
61
+ market: PropTypes.string,
62
+ }).isRequired,
51
63
  submitUrl: PropTypes.string,
52
64
  successMessage: PropTypes.string,
53
65
  failMessage: PropTypes.string,
54
66
  validationMessage: PropTypes.string,
67
+ buttonLabel: PropTypes.string,
68
+ showButtonIcon: PropTypes.bool,
69
+ showLabels: PropTypes.bool,
70
+ showHeading: PropTypes.bool,
71
+ showDescription: PropTypes.bool,
55
72
  };
@@ -24,15 +24,16 @@
24
24
  }
25
25
 
26
26
  h2 {
27
- font-size: 2rem;
28
- line-height: 2.8rem;
27
+ font-size: 2.4rem;
28
+ line-height: 3.2rem;
29
29
  font-weight: 700;
30
30
  text-transform: capitalize;
31
31
  margin: 0;
32
+ color: #1b1b1c;
32
33
 
33
34
  @include min(tablet) {
34
- font-size: 2.8rem;
35
- line-height: 3.6rem;
35
+ font-size: 3.2rem;
36
+ line-height: 4rem;
36
37
  }
37
38
  }
38
39
 
@@ -84,6 +85,9 @@
84
85
  height: 12rem;
85
86
  display: block;
86
87
  resize: vertical;
88
+ resize: none;
89
+ height: 20.8rem;
90
+ padding: 1.2rem;
87
91
  }
88
92
 
89
93
  label {
@@ -103,12 +107,26 @@
103
107
  }
104
108
 
105
109
  button {
106
- @include buttonsColor(var(--primary-button-color, #6E33E5), var(--primary-button-color-hover, #776ABA), var(--primary-button-color-active, #998FCB), var(--primary-button-color-text, #FFFFFF));
110
+ align-items: center;
111
+ background-color: var(--primary-button-color,#6e33e5) !important;
112
+ border-radius: 100px !important;
113
+ color: var(--primary-button-color-text,#fff) !important;
114
+ display: inline-flex !important;
115
+ font-size: 1.8rem;
116
+ font-weight: 700;
117
+ gap: 0.4rem;
118
+ justify-content: center;
107
119
  line-height: 2.7rem;
120
+ padding: 1.6rem 2.4rem !important;
121
+ text-align: center;
108
122
  text-transform: capitalize;
109
- gap: 0.4rem;
110
- padding: 1.6rem 2.4rem;
111
- border-radius: 100px;
123
+
124
+ &:hover,
125
+ &:focus {
126
+ background: var(--primary-button-color,#6e33e5) !important;
127
+ border-radius: 100px !important;
128
+ color: var(--primary-button-color-text,#fff) !important;
129
+ }
112
130
  }
113
131
  }
114
132
 
@@ -123,11 +141,22 @@
123
141
  .textareaGroup {
124
142
  @include flex-direction(column);
125
143
 
126
- > label {
144
+ > div {
127
145
  @include flex-align(center, flex-start);
128
146
  gap: 0.8rem;
129
147
  font-weight: normal;
130
148
  }
149
+
150
+ label {
151
+ font-weight: 400;
152
+ color: #3c3c40;
153
+
154
+ > a {
155
+ color: #165af8;
156
+ text-decoration: underline;
157
+ font-weight: 500;
158
+ }
159
+ }
131
160
  }
132
161
 
133
162
  .textareaGroup {
@@ -191,6 +220,16 @@
191
220
  color: var(--color-39);
192
221
  }
193
222
 
223
+ .alertWarningMessage {
224
+ @include flex-align(center, flex-start);
225
+ gap: 0.5rem;
226
+ background: var(--color-39);
227
+ border-radius: 0.4rem;
228
+ color: var(--color-4);
229
+ margin-top: 0.8rem;
230
+ padding: 8px;
231
+ }
232
+
194
233
  .twoCol {
195
234
  @include flex-direction(column);
196
235
  gap: 2.4rem;
@@ -221,4 +260,30 @@
221
260
  button {
222
261
  width: auto;
223
262
  }
263
+ }
264
+
265
+ .recaptcha {
266
+ overflow: hidden;
267
+
268
+ &.invalid {
269
+ border: 1px solid var(--color-39);
270
+ color: var(--color-39);
271
+ }
272
+
273
+ // override overflowing styles
274
+ > div > div > div {
275
+ width: 100% !important;
276
+
277
+ iframe {
278
+ width: 100%;
279
+ }
280
+
281
+ @include min(tablet) {
282
+ width: 304px !important;
283
+
284
+ iframe {
285
+ width: 304px;
286
+ }
287
+ }
288
+ }
224
289
  }
@@ -127,5 +127,5 @@ const Template = (args) => <Form {...args} />;
127
127
 
128
128
  export const Default = Template.bind({});
129
129
  Default.args = {
130
- formOptions: contactUsForm,
130
+ formOptions: contactUsForm.ie_en,
131
131
  };
@@ -11,7 +11,7 @@ describe('Form Component', () => {
11
11
  test('render contact form ', () => {
12
12
  const { container } = render(
13
13
  <Form
14
- formOptions={contactUsForm}
14
+ formOptions={contactUsForm.ie_en}
15
15
  hasButton
16
16
  type="contact"
17
17
  submitUrl="https://submit-form.com"
@@ -20,12 +20,14 @@ describe('Form Component', () => {
20
20
  expect(container.querySelector('input[type="text"]')).toBeTruthy();
21
21
  expect(container.querySelector('input[type="email"]')).toBeTruthy();
22
22
  expect(container.querySelector('textarea')).toBeTruthy();
23
+ expect(container.querySelector('label')).toBeTruthy();
24
+ expect(container.querySelectorAll('label').length).toEqual(5);
23
25
  expect(container.querySelector('button')).toBeTruthy();
24
26
  });
25
27
  test('render newsletter form ', () => {
26
28
  const { container } = render(
27
29
  <Form
28
- formOptions={newsLetterForm}
30
+ formOptions={newsLetterForm.ie_en}
29
31
  hasButton
30
32
  type="newsletter"
31
33
  submitUrl="https://submit-form.com"
@@ -36,11 +38,25 @@ describe('Form Component', () => {
36
38
  expect(container.querySelector('textarea')).toBeFalsy();
37
39
  expect(container.querySelector('button')).toBeTruthy();
38
40
  });
39
-
41
+ test('render form without labels ', () => {
42
+ const { container } = render(
43
+ <Form
44
+ formOptions={contactUsForm.ie_en}
45
+ hasButton
46
+ type="contact"
47
+ submitUrl="https://submit-form.com"
48
+ showLabels={false}
49
+ />
50
+ );
51
+ expect(container.querySelectorAll('input[type="text"]').length).toEqual(2);
52
+ expect(container.querySelector('textarea')).toBeTruthy();
53
+ expect(container.querySelector('.formGroup').querySelector('label')).toBeFalsy();
54
+ expect(container.querySelectorAll('label').length).toEqual(1);
55
+ });
40
56
  test('on change', async () => {
41
57
  render(
42
58
  <Form
43
- formOptions={contactUsForm}
59
+ formOptions={contactUsForm.ie_en}
44
60
  hasButton
45
61
  type="contact"
46
62
  submitUrl="https://submit-form.com"
@@ -62,7 +78,7 @@ describe('Form Component', () => {
62
78
  act(() => {
63
79
  ReactDOM.render(
64
80
  <Form
65
- formOptions={contactUsForm}
81
+ formOptions={contactUsForm.ie_en}
66
82
  hasButton
67
83
  type="contact"
68
84
  submitUrl="https://submit-form.com"
@@ -82,7 +98,7 @@ describe('Form Component', () => {
82
98
  test('handle submit with filled fields', async () => {
83
99
  const { container } = render(
84
100
  <Form
85
- formOptions={newsLetterForm}
101
+ formOptions={newsLetterForm.ie_en}
86
102
  hasButton
87
103
  disabled={false}
88
104
  type="newsletter"
@@ -14,6 +14,7 @@ const FormComponent = ({
14
14
  validationMessage = 'Fill all the required fields.',
15
15
  submitUrl = '',
16
16
  hasButton = true,
17
+ showButtonIcon = true,
17
18
  buttonLabel = 'Submit',
18
19
  disabled = true,
19
20
  showLabels = true,
@@ -26,13 +27,12 @@ const FormComponent = ({
26
27
  failed: false,
27
28
  isValid: true,
28
29
  isDisabled: disabled,
29
- name: '',
30
- email: '',
31
- message: '',
32
30
  });
33
31
  const values = formOptions.fields.map((field) => ({
34
32
  [field.id]: field.value ? field.value : '',
35
33
  }));
34
+ // add recaptcha if its added in form options
35
+ if (formOptions.hasReCAPTCHA) values.push({ gRecaptchaResponse: '' });
36
36
  const [elements, setElements] = useState(Object.assign({}, ...values));
37
37
 
38
38
  const handleChange = (name, value) => {
@@ -96,41 +96,41 @@ const FormComponent = ({
96
96
  isValid: true,
97
97
  });
98
98
  });
99
- recaptchaRef.current.reset();
99
+ recaptchaRef?.current?.reset();
100
100
  };
101
101
 
102
102
  function recaptchaOnChange(value) {
103
103
  // eslint-disable-next-line no-unused-expressions
104
- value && setState({ ...state, isDisabled: false });
104
+ if (value) {
105
+ setState({ ...state, isDisabled: false });
106
+ setElements({ ...elements, gRecaptchaResponse: value });
107
+ }
105
108
  }
106
109
 
107
110
  const getField = (field) => {
108
- const { type, id, translationKey, label, ...props } = field;
111
+ const { type, id, translationKey, label, placeholder, ...props } = field;
109
112
 
110
113
  switch (type) {
111
114
  case 'textarea':
112
115
  return (
113
- <div className={styles.textareaGroup}>
116
+ <div className={styles.textareaGroup || ''}>
114
117
  <textarea
115
118
  name={id}
116
119
  id={id}
120
+ placeholder={translate(translations, placeholder?.translationKey, placeholder?.label)}
117
121
  {...props}
118
122
  value={elements[id]}
119
123
  onChange={(e) => handleChange(e.target.name, e.target.value)}
120
- className={`${!state.isValid && elements[id] === '' && styles.invalid}`}
124
+ className={`${(!state.isValid && elements[id] === '' && styles.invalid) || ''}`}
121
125
  />
122
126
  {field.maxLength && <span>{`${elements[id].length}/${field.maxLength}`}</span>}
123
127
  </div>
124
128
  );
125
129
  case 'checkbox':
126
130
  return (
127
- <div className={styles.checkboxGroup}>
131
+ <div className={styles.checkboxGroup || ''}>
128
132
  {field.options.map((option) => (
129
- <label
130
- key={option.id}
131
- htmlFor={option.id}
132
- className={`${!state.isValid && !elements[option.id].length && styles.invalid}`}
133
- >
133
+ <div key={option.id}>
134
134
  <input
135
135
  id={option.id}
136
136
  name={option.id}
@@ -138,29 +138,38 @@ const FormComponent = ({
138
138
  checked={elements[option.id] === 'true'}
139
139
  onChange={(e) => handleChange(e.target.name, e.target.checked.toString())}
140
140
  />
141
- {translate(translations, option.translationKey, option.label)}
142
- </label>
141
+ <label
142
+ htmlFor={option.id}
143
+ className={`${
144
+ (!state.isValid && !elements[option.id].length && styles.invalid) || ''
145
+ }`}
146
+ >
147
+ {translate(translations, option.translationKey, option.label)}
148
+ </label>
149
+ </div>
143
150
  ))}
144
151
  </div>
145
152
  );
146
153
  case 'radio':
147
154
  return (
148
- <div className={styles.radioGroup}>
155
+ <div className={styles.radioGroup || ''}>
149
156
  {field.options.map((option) => (
150
- <label
151
- key={option.id}
152
- htmlFor={option.id}
153
- className={`${!state.isValid && elements[id] === '' && styles.invalid}`}
154
- >
157
+ <div key={option.id}>
155
158
  <input
156
159
  id={option.id}
157
160
  name={id}
158
161
  type={type}
159
162
  checked={elements[id] === option.label}
160
163
  onChange={(e) => handleChange(e.target.name, option.label)}
161
- />{' '}
162
- {translate(translations, option.translationKey, option.label)}
163
- </label>
164
+ />
165
+ <label
166
+ key={option.id}
167
+ htmlFor={option.id}
168
+ className={`${(!state.isValid && elements[id] === '' && styles.invalid) || ''}`}
169
+ >
170
+ {translate(translations, option.translationKey, option.label)}
171
+ </label>
172
+ </div>
164
173
  ))}
165
174
  </div>
166
175
  );
@@ -172,7 +181,7 @@ const FormComponent = ({
172
181
  name={id}
173
182
  defaultValue={elements[id]}
174
183
  onChange={(e) => handleChange(e.target.name, e.target.value)}
175
- className={`${!state.isValid && elements[id] === '' && styles.invalid}`}
184
+ className={`${(!state.isValid && elements[id] === '' && styles.invalid) || ''}`}
176
185
  >
177
186
  {field.showDefault && (
178
187
  <option value="">
@@ -188,7 +197,7 @@ const FormComponent = ({
188
197
  );
189
198
  case 'range':
190
199
  return (
191
- <div className={styles.radioGroup}>
200
+ <div className={styles.radioGroup || ''}>
192
201
  <input
193
202
  name={id}
194
203
  id={id}
@@ -198,9 +207,11 @@ const FormComponent = ({
198
207
  value={elements[id]}
199
208
  onChange={(e) => handleChange(e.target.name, e.target.value)}
200
209
  />
201
- <div className={styles.rangeValues}>
210
+ <div className={styles.rangeValues || ''}>
202
211
  <span>{field.min}</span>
203
- <span className={`${!state.isValid && elements[id] === '' && styles.invalid}`}>
212
+ <span
213
+ className={`${(!state.isValid && elements[id] === '' && styles.invalid) || ''}`}
214
+ >
204
215
  {elements[id] || 'Selected Value'}
205
216
  </span>
206
217
  <span>{field.max}</span>
@@ -214,10 +225,11 @@ const FormComponent = ({
214
225
  id={id}
215
226
  type={type}
216
227
  aria-describedby={id}
228
+ placeholder={translate(translations, placeholder?.translationKey, placeholder?.label)}
217
229
  {...props}
218
230
  value={elements[id]}
219
231
  onChange={(e) => handleChange(e.target.name, e.target.value)}
220
- className={`${!state.isValid && elements[id] === '' && styles.invalid}`}
232
+ className={`${(!state.isValid && elements[id] === '' && styles.invalid) || ''}`}
221
233
  />
222
234
  );
223
235
  }
@@ -247,7 +259,7 @@ const FormComponent = ({
247
259
  >
248
260
  {formOptions.fields &&
249
261
  formOptions.fields.map((field) => {
250
- const { id, translationKey, label } = field;
262
+ const { id, translationKey, label, validations } = field;
251
263
 
252
264
  return (
253
265
  <div
@@ -260,18 +272,40 @@ const FormComponent = ({
260
272
  <label htmlFor={id}>{translate(translations, translationKey, label)}</label>
261
273
  )}
262
274
  {elements && getField(field)}
275
+ {validations && !state.isValid && elements[id] === '' && (
276
+ <span className={styles.alertWarningMessage || ''}>
277
+ {validations?.icon}
278
+ {translate(translations, validations?.translationKey, validations?.label)}
279
+ </span>
280
+ )}
263
281
  </div>
264
282
  );
265
283
  })}
266
284
 
267
285
  {formOptions.hasReCAPTCHA && (
268
- <div className={styles.recaptcha || ''}>
286
+ <div
287
+ className={`${styles.recaptcha || ''} ${
288
+ (!state.isValid && elements.gRecaptchaResponse === '' && styles.invalid) || ''
289
+ }`}
290
+ >
269
291
  <ReCAPTCHA
270
292
  ref={recaptchaRef}
271
293
  sitekey={`${process.env.RECAPTCHA_SITE_KEY}`}
272
294
  // eslint-disable-next-line react/jsx-no-bind
273
295
  onChange={recaptchaOnChange}
274
296
  />
297
+ {!state.isValid &&
298
+ elements.gRecaptchaResponse === '' &&
299
+ formOptions?.reCaptcha?.validations && (
300
+ <span className={styles.alertWarningMessage || ''}>
301
+ {formOptions?.reCaptcha?.validations?.icon}
302
+ {translate(
303
+ translations,
304
+ formOptions?.reCaptcha?.validations?.translationKey,
305
+ formOptions?.reCaptcha?.validations.label
306
+ )}
307
+ </span>
308
+ )}
275
309
  </div>
276
310
  )}
277
311
  {hasButton && (
@@ -281,12 +315,12 @@ const FormComponent = ({
281
315
  btnText={state.loading ? 'sending...' : buttonLabel}
282
316
  disabled={state.loading}
283
317
  gtmClass="form-gtm btn-cta"
284
- noStyle
285
- icon={<FaArrowRight fontSize={20} />}
318
+ primaryColor
319
+ icon={showButtonIcon ? <FaArrowRight fontSize={20} /> : null}
286
320
  />
287
321
  </div>
288
322
  )}
289
- {(state.success || state.failed || state.isValid) && (
323
+ {(state.success || state.failed || !state.isValid) && (
290
324
  <div className={styles.formAlerts || ''}>
291
325
  {state.success && <div className={styles.alertSuccess || ''}>{successMessage}</div>}
292
326
  {state.failed && <div className={styles.alertDanger || ''}>{failMessage}</div>}
@@ -308,5 +342,6 @@ FormComponent.propTypes = {
308
342
  buttonLabel: PropTypes.string,
309
343
  disabled: PropTypes.bool,
310
344
  showLabels: PropTypes.bool,
345
+ showButtonIcon: PropTypes.bool,
311
346
  };
312
347
  export default FormComponent;
@@ -1,100 +1,166 @@
1
+ import React from 'react';
2
+ // import { IoMdCloseCircleOutline } from '@react-icons/all-files/io/IoMdCloseCircleOutline';
3
+ import { replaceWith } from '~helpers/strings';
4
+
1
5
  export const contactUsForm = {
2
- title: {
3
- label: 'Send us a message',
4
- translationKey: 'send_us_a_msg',
5
- },
6
- twoCol: true,
7
- hasReCAPTCHA: true,
8
- fields: [
9
- {
10
- label: 'Name',
11
- id: 'name',
12
- type: 'text',
13
- placeholder: 'Text placeholder',
14
- translationKey: 'text_translation',
15
- twoCol: true,
6
+ ie_en: {
7
+ title: {
8
+ label: 'Send us a message',
9
+ translationKey: 'send_us_a_msg',
16
10
  },
17
- {
18
- label: 'Email Address',
19
- id: 'email',
20
- type: 'email',
21
- placeholder: 'Email@placeholder.com',
22
- translationKey: 'email_translation',
23
- twoCol: true,
11
+ twoCol: false,
12
+ hasReCAPTCHA: true,
13
+ reCaptcha: {
14
+ // validations: {
15
+ // label: 'Please confirm',
16
+ // translationKey: 'valid_recaptcha',
17
+ // icon: <IoMdCloseCircleOutline />,
18
+ // }
24
19
  },
25
- {
26
- label: 'Textarea',
27
- id: 'comment',
28
- type: 'textarea',
29
- placeholder: 'Textarea placeholder',
30
- translationKey: 'textarea_translation',
31
- maxLength: '10',
32
- twoCol: false,
33
- },
34
- {
35
- id: 'tnc',
36
- type: 'checkbox',
37
- required: true,
38
- translationKey: 'checkbox_translation',
39
- twoCol: false,
40
- options: [
41
- {
42
- id: 'tnc',
43
- label:
44
- '+18 Lorem ipsum dolor sit amet consectetur. Egestas nibh ullamcorper venenatis vulputate. Sed elit diam at id feugiat orci ornare Privacy Policy',
45
- translationKey: 'option_one_translation',
20
+ fields: [
21
+ {
22
+ label: 'Field Label',
23
+ translationKey: 'text_translation',
24
+ id: 'name',
25
+ type: 'text',
26
+ placeholder: {
27
+ label: 'Write something',
28
+ translationKey: 'field_placeholder',
46
29
  },
47
- ],
48
- },
49
- ],
30
+ twoCol: true,
31
+ // validations: {
32
+ // label: 'Please enter a name',
33
+ // translationKey: 'valid_name',
34
+ // icon: <IoMdCloseCircleOutline />,
35
+ // }
36
+ },
37
+ {
38
+ label: 'Email Address',
39
+ id: 'email',
40
+ type: 'email',
41
+ placeholder: {
42
+ label: 'Email@placeholder.com',
43
+ translationKey: 'field_placeholder',
44
+ },
45
+ translationKey: 'email_translation',
46
+ twoCol: true,
47
+ // validations: {
48
+ // label: 'Please enter a valid email address',
49
+ // translationKey: 'valid_email',
50
+ // icon: <IoMdCloseCircleOutline />,
51
+ // }
52
+ },
53
+ {
54
+ label: 'Field Label',
55
+ id: 'subject',
56
+ type: 'text',
57
+ placeholder: {
58
+ label: 'Write something',
59
+ translationKey: 'field_placeholder',
60
+ },
61
+ translationKey: 'text_translation',
62
+ twoCol: true,
63
+ // validations: {
64
+ // label: 'Please enter a subject',
65
+ // translationKey: 'valid_subject',
66
+ // icon: <IoMdCloseCircleOutline />,
67
+ // }
68
+ },
69
+ {
70
+ label: 'Field Label',
71
+ id: 'comment',
72
+ type: 'textarea',
73
+ placeholder: {
74
+ label: 'Write something',
75
+ translationKey: 'field_placeholder',
76
+ },
77
+ translationKey: 'textarea_translation',
78
+ maxLength: '1000',
79
+ twoCol: false,
80
+ // validations: {
81
+ // label: 'Please enter a message',
82
+ // translationKey: 'valid_message',
83
+ // icon: <IoMdCloseCircleOutline />,
84
+ // }
85
+ },
86
+ {
87
+ id: 'tnc',
88
+ type: 'checkbox',
89
+ required: true,
90
+ translationKey: 'checkbox_translation',
91
+ twoCol: false,
92
+ options: [
93
+ {
94
+ id: 'tnc',
95
+ label: replaceWith(
96
+ '+18 Lorem ipsum dolor sit amet consectetur. Egestas nibh ullamcorper venenatis vulputate. Sed elit diam at id feugiat orci ornare [keyword]',
97
+ '[keyword]',
98
+ <a href="/privacy-policy">Privacy Policy</a>
99
+ ),
100
+ translationKey: 'option_one_translation',
101
+ },
102
+ ],
103
+ // validations: {
104
+ // label: 'Please select at least one',
105
+ // translationKey: 'valid_check',
106
+ // icon: <IoMdCloseCircleOutline />,
107
+ // }
108
+ },
109
+ ],
110
+ },
50
111
  };
51
112
 
52
113
  export const newsLetterForm = {
53
- title: {
54
- label: 'Sign up To our newsletters ',
55
- translationKey: 'sign_up_newsletter',
56
- },
57
- description: {
58
- label:
59
- "If you have questions about our reviews, games or content, or just want to leave some feedback, the Irish Luck team would love to hear from you. You can contact us any time using the details below and we'll endeavour to get back to you within 48 hours.",
60
- translationKey: 'contact_us_questions',
61
- },
62
- twoCol: true,
63
- hasReCAPTCHA: false,
64
- fields: [
65
- {
66
- label: 'Name',
67
- id: 'name',
68
- type: 'text',
69
- placeholder: 'Text placeholder',
70
- translationKey: 'text_translation',
71
- twoCol: true,
72
- // required: true,
114
+ ie_en: {
115
+ title: {
116
+ label: 'Sign up To our newsletters ',
117
+ translationKey: 'sign_up_newsletter',
73
118
  },
74
- {
75
- label: 'Email Address',
76
- id: 'email',
77
- type: 'email',
78
- placeholder: 'Email@placeholder.com',
79
- translationKey: 'email_translation',
80
- twoCol: true,
81
- // required: true,
119
+ description: {
120
+ label:
121
+ "If you have questions about our reviews, games or content, or just want to leave some feedback, the Irish Luck team would love to hear from you. You can contact us any time using the details below and we'll endeavour to get back to you within 48 hours.",
122
+ translationKey: 'contact_us_questions',
82
123
  },
83
- {
84
- label: 'Terms',
85
- id: 'tnc',
86
- type: 'checkbox',
87
- required: true,
88
- translationKey: 'checkbox_translation',
89
- twoCol: false,
90
- options: [
91
- {
92
- id: 'tnc',
93
- label:
94
- '+18 Lorem ipsum dolor sit amet consectetur. Egestas nibh ullamcorper venenatis vulputate. Sed elit diam at id feugiat orci ornare Privacy Policy',
95
- translationKey: 'option_one_translation',
96
- },
97
- ],
98
- },
99
- ],
124
+ twoCol: true,
125
+ hasReCAPTCHA: false,
126
+ fields: [
127
+ {
128
+ label: 'Name',
129
+ id: 'name',
130
+ type: 'text',
131
+ placeholder: 'Text placeholder',
132
+ translationKey: 'text_translation',
133
+ twoCol: true,
134
+ // required: true,
135
+ },
136
+ {
137
+ label: 'Email Address',
138
+ id: 'email',
139
+ type: 'email',
140
+ placeholder: 'Email@placeholder.com',
141
+ translationKey: 'email_translation',
142
+ twoCol: true,
143
+ // required: true,
144
+ },
145
+ {
146
+ label: 'Terms',
147
+ id: 'tnc',
148
+ type: 'checkbox',
149
+ required: true,
150
+ translationKey: 'checkbox_translation',
151
+ twoCol: false,
152
+ options: [
153
+ {
154
+ id: 'tnc',
155
+ label: replaceWith(
156
+ '+18 Lorem ipsum dolor sit amet consectetur. Egestas nibh ullamcorper venenatis vulputate. Sed elit diam at id feugiat orci ornare [keyword]',
157
+ '[keyword]',
158
+ <a href="/privacy-policy">Privacy Policy</a>
159
+ ),
160
+ translationKey: 'option_one_translation',
161
+ },
162
+ ],
163
+ },
164
+ ],
165
+ },
100
166
  };
@@ -1,3 +1,4 @@
1
+ import React from 'react';
1
2
  import { imagePrettyUrl } from './getters';
2
3
 
3
4
  export function capitalize(string) {
@@ -70,3 +71,10 @@ export function parseContentImageUrl(src) {
70
71
  }
71
72
 
72
73
  export const removeSymbols = (string) => string.replace(/[!?@£#$%^&*():;"'|/.,~`]/g, '');
74
+
75
+ export function replaceWith(string, replaceThis, replacement) {
76
+ return string
77
+ .split(replaceThis)
78
+ .map((item) => <>{item}</>)
79
+ .reduce((acc, x) => (acc === null ? [x] : [acc, replacement, x]), null);
80
+ }
@@ -1,3 +1,6 @@
1
+ import React from 'react';
2
+ import { render, cleanup, fireEvent, screen, waitFor, shallow } from '@testing-library/react';
3
+ import { translate } from 'gatsby-core-theme/src/helpers/getters';
1
4
  import * as Strings from './strings';
2
5
 
3
6
  describe('Strings Helper', () => {
@@ -11,6 +11,7 @@ export const siteSchema = {
11
11
  phone_number: null,
12
12
  site_name: null,
13
13
  spotify: 'https://www.spotify.com/',
14
+ support_email: 'sales@irishluck.ie',
14
15
  twitter: 'https://www.twitter.com/',
15
16
  wikipedia: 'https://www.wikipedia.com/',
16
17
  youtube: 'https://www.youtube.com/',