gatsby-core-theme 25.0.14 → 26.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 +19 -0
- package/package.json +1 -1
- package/src/components/atoms/contact-form/contact-form.module.scss +88 -0
- package/src/components/atoms/contact-form/contact-form.stories.js +77 -0
- package/src/components/atoms/contact-form/contact-form.test.js +40 -0
- package/src/components/atoms/contact-form/index.js +55 -0
- package/src/components/atoms/operator-cta-button/operator-cta-button.module.scss +0 -28
- package/src/components/molecules/module/index.js +2 -0
- package/src/components/organisms/form/form.module.scss +162 -89
- package/src/components/organisms/form/form.stories.js +59 -47
- package/src/components/organisms/form/form.test.js +34 -5
- package/src/components/organisms/form/index.js +196 -67
- package/src/constants/forms.js +100 -0
- package/src/styles/utils/_mixins.scss +28 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
# [26.0.0](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/compare/v25.0.14...v26.0.0) (2023-08-16)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* fix storybook form json ([da6d479](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/da6d479186108e875c53e0160bb99a1d9d06fdd1))
|
|
7
|
+
* moved contact form story to template block ([ddd4f02](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/ddd4f02c9720dcca58d57a595547bc02af0990f6))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Code Refactoring
|
|
11
|
+
|
|
12
|
+
* dynamic form with validations, storybook and test. constant form json ([d727a64](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/d727a64af6466c6a92422437783232a1fa203d63))
|
|
13
|
+
* remove unwanted code and add responsive css ([d091869](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/d091869597e07c438c2938693a2b3aa9c7a81682))
|
|
14
|
+
* temp changes ([241386b](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/241386bb27caaa433fce62b29a5355384a1b770f))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
* Merge branch 'tm-3556-form' into 'master' ([325d44e](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/325d44e55bd835c97490965214d7e2eb60b89877))
|
|
18
|
+
* Merge branch 'master' into tm-3556-form ([8b16477](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/commit/8b164778193dd10cb663bf0069f1029e68d241ac))
|
|
19
|
+
|
|
1
20
|
## [25.0.14](https://git.ilcd.rocks/team-floyd/themes/gatsby-themes/compare/v25.0.13...v25.0.14) (2023-08-10)
|
|
2
21
|
|
|
3
22
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
.contactForm {
|
|
2
|
+
@include flex-align(center, center);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.contactBox {
|
|
6
|
+
width: 100%;
|
|
7
|
+
max-width: var(--main-container-max);
|
|
8
|
+
margin: 0 auto;
|
|
9
|
+
gap: 2.4rem;
|
|
10
|
+
@include flex-direction(column);
|
|
11
|
+
|
|
12
|
+
@include min(tablet) {
|
|
13
|
+
@include flex-direction(row);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@include min(laptop) {
|
|
17
|
+
margin: 0;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.formWrapper {
|
|
22
|
+
flex-basis: 100%;
|
|
23
|
+
padding: 0;
|
|
24
|
+
gap: 2.4rem;
|
|
25
|
+
@include flex-direction(column);
|
|
26
|
+
|
|
27
|
+
p {
|
|
28
|
+
font-weight: normal;
|
|
29
|
+
font-size: 1.6rem;
|
|
30
|
+
line-height: 2.7rem;
|
|
31
|
+
color: #515156;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
h1 {
|
|
35
|
+
margin: 0;
|
|
36
|
+
font-weight: bold;
|
|
37
|
+
font-size: 2.4rem;
|
|
38
|
+
line-height: 3.2rem;
|
|
39
|
+
color: #1b1b1c;
|
|
40
|
+
text-transform: capitalize;
|
|
41
|
+
|
|
42
|
+
@include min(tablet) {
|
|
43
|
+
font-size: 3.2rem;
|
|
44
|
+
line-height: 4rem;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.socialSection {
|
|
50
|
+
@include flex-direction(column);
|
|
51
|
+
gap: 2rem;
|
|
52
|
+
flex-grow: 1;
|
|
53
|
+
flex-basis: 40rem;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.emailContainer {
|
|
57
|
+
@include flex-align(flex-start, center);
|
|
58
|
+
@include flex-direction(column);
|
|
59
|
+
gap: 1.6rem;
|
|
60
|
+
|
|
61
|
+
h2 {
|
|
62
|
+
margin: 0;
|
|
63
|
+
font-weight: bold;
|
|
64
|
+
font-size: 3.2rem;
|
|
65
|
+
line-height: 4.6rem;
|
|
66
|
+
color: var(--color-33);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
p {
|
|
70
|
+
@include flex-align(center, center);
|
|
71
|
+
gap: 1rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
span {
|
|
75
|
+
display: inline-block;
|
|
76
|
+
width: 4rem;
|
|
77
|
+
height: 4rem;
|
|
78
|
+
line-height: 4.7rem;
|
|
79
|
+
border-radius: 50%;
|
|
80
|
+
background: var(--color-52);
|
|
81
|
+
text-align: center;
|
|
82
|
+
font-size: 2rem;
|
|
83
|
+
|
|
84
|
+
svg {
|
|
85
|
+
color: #000;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
import { siteSchema } from 'gatsby-core-theme/tests/factories/modules/site-schema.factory';
|
|
11
|
+
import ContactForm from '.';
|
|
12
|
+
|
|
13
|
+
const page = {};
|
|
14
|
+
siteSchema.support_email = 'contact@irishluck.ie';
|
|
15
|
+
page.siteSchema = { ...siteSchema };
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
title: 'Theme/Modules/Template Blocks/Contact Form',
|
|
19
|
+
component: ContactForm,
|
|
20
|
+
argTypes: {
|
|
21
|
+
page: {
|
|
22
|
+
name: 'page',
|
|
23
|
+
type: { name: 'object', required: true },
|
|
24
|
+
defaultValue: '',
|
|
25
|
+
description: 'page object',
|
|
26
|
+
siteSchema: {
|
|
27
|
+
type: { summary: 'object' },
|
|
28
|
+
defaultValue: { summary: '' },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
submitUrl: {
|
|
32
|
+
name: 'submitUrl',
|
|
33
|
+
type: { name: 'string', required: false },
|
|
34
|
+
defaultValue: null,
|
|
35
|
+
description: 'submit URL',
|
|
36
|
+
},
|
|
37
|
+
successMessage: {
|
|
38
|
+
name: 'successMessage',
|
|
39
|
+
type: { name: 'string', required: false },
|
|
40
|
+
defaultValue: 'Message sent successfully.',
|
|
41
|
+
description: 'sent message text',
|
|
42
|
+
},
|
|
43
|
+
failMessage: {
|
|
44
|
+
name: 'failMessage',
|
|
45
|
+
type: { name: 'string', required: false },
|
|
46
|
+
defaultValue: 'Message not sent.',
|
|
47
|
+
description: 'fail message text',
|
|
48
|
+
},
|
|
49
|
+
validationMessage: {
|
|
50
|
+
name: 'validationMessage',
|
|
51
|
+
type: { name: 'string', required: false },
|
|
52
|
+
defaultValue: 'Fill all the required fields.',
|
|
53
|
+
description: 'sent message info',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
parameters: {
|
|
57
|
+
docs: {
|
|
58
|
+
description: {
|
|
59
|
+
component: 'A component that displays authors cards',
|
|
60
|
+
},
|
|
61
|
+
page: () => (
|
|
62
|
+
<>
|
|
63
|
+
<Title />
|
|
64
|
+
<Description />
|
|
65
|
+
<Primary />
|
|
66
|
+
<ArgsTable story={PRIMARY_STORY} />
|
|
67
|
+
</>
|
|
68
|
+
),
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const Template = (args) => <ContactForm {...args} />;
|
|
74
|
+
export const Default = Template.bind({});
|
|
75
|
+
Default.args = {
|
|
76
|
+
page,
|
|
77
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render, cleanup, waitFor } from '@testing-library/react';
|
|
4
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
5
|
+
import { siteSchema } from 'gatsby-core-theme/tests/factories/modules/site-schema.factory';
|
|
6
|
+
import ContactForm from '.';
|
|
7
|
+
|
|
8
|
+
const page = {};
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
page.siteSchema = { ...siteSchema };
|
|
12
|
+
});
|
|
13
|
+
describe('Contact Us form Component', () => {
|
|
14
|
+
test('render contact us form and follow us titles', async () => {
|
|
15
|
+
const { container } = render(<ContactForm page={page} submitUrl="submit" />);
|
|
16
|
+
|
|
17
|
+
await waitFor(() => {
|
|
18
|
+
expect(container).toBeTruthy();
|
|
19
|
+
expect(container.querySelector('H1').innerHTML).toBe('Contact Us');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
test('render contact us form ', async () => {
|
|
23
|
+
const { container } = render(<ContactForm page={page} submitUrl="submit" />);
|
|
24
|
+
|
|
25
|
+
await waitFor(() => {
|
|
26
|
+
expect(container.querySelector('.contactForm')).toBeTruthy();
|
|
27
|
+
expect(container.querySelector('.contactForm').querySelectorAll('input')).toHaveLength(3);
|
|
28
|
+
expect(
|
|
29
|
+
container.querySelector('.contactForm').querySelectorAll('input[type="text"]')
|
|
30
|
+
).toHaveLength(1);
|
|
31
|
+
expect(
|
|
32
|
+
container.querySelector('.contactForm').querySelectorAll('input[type="email"]')
|
|
33
|
+
).toHaveLength(1);
|
|
34
|
+
expect(container.querySelector('.contactForm').querySelectorAll('textarea')).toHaveLength(1);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
cleanup();
|
|
40
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { Context } from 'gatsby-core-theme/src/context/MainProvider';
|
|
4
|
+
import Form from 'gatsby-core-theme/src/components/organisms/form';
|
|
5
|
+
import { translate } from 'gatsby-core-theme/src/helpers/getters';
|
|
6
|
+
import { contactUsForm } from '../../../constants/forms';
|
|
7
|
+
import styles from './contact-form.module.scss';
|
|
8
|
+
|
|
9
|
+
const ContactForm = ({
|
|
10
|
+
submitUrl,
|
|
11
|
+
successMessage = 'Message sent successfully.',
|
|
12
|
+
failMessage = 'Message failed to send.',
|
|
13
|
+
validationMessage = 'Fill all the required fields.',
|
|
14
|
+
}) => {
|
|
15
|
+
const { translations } = useContext(Context) || {};
|
|
16
|
+
|
|
17
|
+
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>
|
|
31
|
+
<Form
|
|
32
|
+
formOptions={contactUsForm}
|
|
33
|
+
type="contact"
|
|
34
|
+
hasButton
|
|
35
|
+
buttonLabel={translate(translations, 'send_msg', 'Send Message')}
|
|
36
|
+
submitUrl={submitUrl}
|
|
37
|
+
successMessage={successMessage}
|
|
38
|
+
failMessage={failMessage}
|
|
39
|
+
validationMessage={validationMessage}
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default ContactForm;
|
|
48
|
+
|
|
49
|
+
ContactForm.propTypes = {
|
|
50
|
+
page: PropTypes.shape({}).isRequired,
|
|
51
|
+
submitUrl: PropTypes.string,
|
|
52
|
+
successMessage: PropTypes.string,
|
|
53
|
+
failMessage: PropTypes.string,
|
|
54
|
+
validationMessage: PropTypes.string,
|
|
55
|
+
};
|
|
@@ -1,31 +1,3 @@
|
|
|
1
|
-
@mixin buttonsColor($color1, $color2, $color3, $textColor: 'white') {
|
|
2
|
-
display: inline-flex;
|
|
3
|
-
align-items: center;
|
|
4
|
-
justify-content: center;
|
|
5
|
-
text-align: center;
|
|
6
|
-
background-color: $color1;
|
|
7
|
-
color: $textColor;
|
|
8
|
-
padding: 0.9rem 3rem;
|
|
9
|
-
font-weight: 700;
|
|
10
|
-
font-size: 1.8rem;
|
|
11
|
-
border-radius: var(--border-radius);
|
|
12
|
-
|
|
13
|
-
>svg {
|
|
14
|
-
flex: none;
|
|
15
|
-
margin-left: .8rem;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
&:hover {
|
|
19
|
-
background-color: $color2;
|
|
20
|
-
color: $textColor;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
&:active {
|
|
24
|
-
background-color: $color3;
|
|
25
|
-
color: $textColor;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
1
|
.primary {
|
|
30
2
|
@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));
|
|
31
3
|
white-space: nowrap;
|
|
@@ -25,6 +25,8 @@ const Modules = ({ module, page, pageContext }) => {
|
|
|
25
25
|
case 'cards':
|
|
26
26
|
case 'cards_v2':
|
|
27
27
|
return loadable(() => import('~organisms/cards'));
|
|
28
|
+
case 'contact_form':
|
|
29
|
+
return loadable(() => import('~atoms/contact-form'));
|
|
28
30
|
case 'pros_and_cons':
|
|
29
31
|
return loadable(() => import('~molecules/pros-cons'));
|
|
30
32
|
case 'faq':
|
|
@@ -1,150 +1,223 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
padding-top: 1.6rem;
|
|
4
|
-
form {
|
|
5
|
-
width: 100%;
|
|
6
|
-
@include flex-direction(column);
|
|
7
|
-
@include min(tablet) {
|
|
8
|
-
display: block;
|
|
9
|
-
}
|
|
1
|
+
:global {
|
|
2
|
+
.contact_form {
|
|
10
3
|
> div {
|
|
11
|
-
margin
|
|
4
|
+
margin: 3.2rem auto;
|
|
5
|
+
|
|
12
6
|
@include min(tablet) {
|
|
13
|
-
margin
|
|
7
|
+
margin: 4rem auto;
|
|
14
8
|
}
|
|
15
9
|
}
|
|
16
10
|
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.formComponent {
|
|
14
|
+
padding: 2.4rem;
|
|
15
|
+
width: 100%;
|
|
16
|
+
box-shadow: 0px 4px 6px -2px rgba(27, 27, 28, 0.02), 0px 12px 16px -4px rgba(27, 27, 28, 0.05);
|
|
17
|
+
border-radius: 16px;
|
|
18
|
+
background: #f4f4f4;
|
|
19
|
+
@include flex-direction(column);
|
|
20
|
+
|
|
17
21
|
@include min(tablet) {
|
|
18
|
-
.
|
|
19
|
-
width: 100%;
|
|
20
|
-
display: inline-block;
|
|
21
|
-
background-color: var(--color-48);
|
|
22
|
-
&.name {
|
|
23
|
-
width: calc(50% - 0.8rem);
|
|
24
|
-
margin-right: 1.6rem;
|
|
25
|
-
}
|
|
26
|
-
&.email {
|
|
27
|
-
width: calc(50% - 0.8rem);
|
|
28
|
-
}
|
|
29
|
-
&.message {
|
|
30
|
-
width: 100%;
|
|
31
|
-
margin-bottom: 1.6rem;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
22
|
+
padding: 2.4rem;
|
|
34
23
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
24
|
+
|
|
25
|
+
h2 {
|
|
26
|
+
font-size: 2rem;
|
|
27
|
+
line-height: 2.8rem;
|
|
28
|
+
font-weight: 700;
|
|
29
|
+
text-transform: capitalize;
|
|
30
|
+
margin: 0;
|
|
31
|
+
|
|
32
|
+
@include min(tablet) {
|
|
33
|
+
font-size: 2.8rem;
|
|
34
|
+
line-height: 3.6rem;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
form {
|
|
39
|
+
width: 100%;
|
|
49
40
|
}
|
|
50
41
|
|
|
51
42
|
input,
|
|
52
|
-
textarea
|
|
53
|
-
|
|
54
|
-
|
|
43
|
+
textarea,
|
|
44
|
+
select {
|
|
45
|
+
background: var(--color-4);
|
|
55
46
|
font-size: 1.4rem;
|
|
56
47
|
line-height: 2.1rem;
|
|
57
48
|
padding: 1.6rem;
|
|
58
49
|
width: 100%;
|
|
59
50
|
max-width: 100%;
|
|
60
|
-
border: none;
|
|
61
51
|
outline: none;
|
|
52
|
+
border-radius: 0.8rem;
|
|
53
|
+
border: 1.5px solid #F4F4F4;
|
|
54
|
+
|
|
55
|
+
|
|
62
56
|
&:invalid,
|
|
63
57
|
&.invalid {
|
|
64
58
|
border: 1px solid var(--color-39);
|
|
65
59
|
color: var(--color-39);
|
|
66
60
|
}
|
|
67
61
|
}
|
|
62
|
+
|
|
68
63
|
input {
|
|
69
64
|
height: 4.4rem;
|
|
70
65
|
}
|
|
66
|
+
|
|
67
|
+
input[type="file"] {
|
|
68
|
+
height: auto;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
input[type="color"],
|
|
72
|
+
input[type="range"] {
|
|
73
|
+
padding: 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
input[type="checkbox"],
|
|
77
|
+
input[type="radio"] {
|
|
78
|
+
width: auto;
|
|
79
|
+
height: auto;
|
|
80
|
+
}
|
|
81
|
+
|
|
71
82
|
textarea {
|
|
72
83
|
height: 12rem;
|
|
73
84
|
display: block;
|
|
74
85
|
resize: vertical;
|
|
75
86
|
}
|
|
87
|
+
|
|
76
88
|
label {
|
|
77
|
-
|
|
89
|
+
font-size: 1.6rem;
|
|
90
|
+
font-weight: 700;
|
|
91
|
+
line-height: 2.7rem;
|
|
92
|
+
color: #262629;
|
|
93
|
+
|
|
94
|
+
span {
|
|
95
|
+
color: red;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
&:invalid,
|
|
99
|
+
&.invalid {
|
|
100
|
+
color: var(--color-39);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
button {
|
|
105
|
+
@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));
|
|
106
|
+
line-height: 2.7rem;
|
|
107
|
+
text-transform: capitalize;
|
|
108
|
+
gap: 0.4rem;
|
|
109
|
+
padding: 1.6rem 2.4rem;
|
|
110
|
+
border-radius: 100px;
|
|
78
111
|
}
|
|
79
112
|
}
|
|
80
113
|
|
|
81
|
-
.
|
|
82
|
-
display: inline-block;
|
|
114
|
+
.formGroup {
|
|
83
115
|
width: 100%;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
116
|
+
@include flex-direction(column);
|
|
117
|
+
gap: 0.4rem;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.radioGroup,
|
|
121
|
+
.checkboxGroup,
|
|
122
|
+
.textareaGroup {
|
|
123
|
+
@include flex-direction(column);
|
|
124
|
+
|
|
125
|
+
> label {
|
|
126
|
+
@include flex-align(center, flex-start);
|
|
127
|
+
gap: 0.8rem;
|
|
128
|
+
font-weight: normal;
|
|
94
129
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.textareaGroup {
|
|
133
|
+
@include flex-align(flex-end, flex-start);
|
|
134
|
+
|
|
135
|
+
> span {
|
|
136
|
+
font-size: 1.4rem;
|
|
137
|
+
font-weight: 400;
|
|
138
|
+
line-height: 2.2rem;
|
|
100
139
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.rangeValues {
|
|
143
|
+
@include flex-align(center, space-between);
|
|
144
|
+
|
|
145
|
+
.invalid {
|
|
146
|
+
color: var(--color-39);
|
|
105
147
|
}
|
|
106
148
|
}
|
|
149
|
+
|
|
150
|
+
|
|
107
151
|
.formAlerts {
|
|
108
152
|
display: inline-block;
|
|
153
|
+
grid-column: span 2 / span 2;
|
|
109
154
|
order: -1;
|
|
155
|
+
|
|
110
156
|
@include min(tablet) {
|
|
111
157
|
width: calc(100% - 18rem);
|
|
112
158
|
order: unset;
|
|
113
159
|
}
|
|
160
|
+
|
|
114
161
|
@include min(desktop) {
|
|
115
162
|
display: inline-block;
|
|
116
163
|
}
|
|
164
|
+
|
|
117
165
|
div {
|
|
118
166
|
border-radius: 0.4rem;
|
|
119
|
-
border: 1px
|
|
167
|
+
border-width: 1px;
|
|
168
|
+
border-style: solid;
|
|
120
169
|
font-size: 1.4rem;
|
|
121
170
|
font-weight: 400;
|
|
122
171
|
width: 100%;
|
|
123
172
|
height: 4.4rem;
|
|
124
173
|
padding: 1rem 1.6rem;
|
|
125
|
-
&.alertSuccess {
|
|
126
|
-
border-color: var(--color-44);
|
|
127
|
-
color: var(--color-44) ;
|
|
128
|
-
}
|
|
129
|
-
&.alertDanger {
|
|
130
|
-
border-color: var(--color-11);
|
|
131
|
-
color: var(--color-11);
|
|
132
|
-
}
|
|
133
|
-
&.alertWarning {
|
|
134
|
-
border-color: var(--color-39);
|
|
135
|
-
color: var(--color-39);
|
|
136
|
-
}
|
|
137
174
|
}
|
|
138
175
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
.alertSuccess {
|
|
179
|
+
border-color: var(--color-44);
|
|
180
|
+
color: var(--color-44) ;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.alertDanger {
|
|
184
|
+
border-color: var(--color-11);
|
|
185
|
+
color: var(--color-11);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.alertWarning {
|
|
189
|
+
border-color: var(--color-39);
|
|
190
|
+
color: var(--color-39);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.twoCol {
|
|
194
|
+
@include flex-direction(column);
|
|
195
|
+
gap: 2.4rem;
|
|
196
|
+
|
|
197
|
+
@include min(tablet) {
|
|
198
|
+
display: grid;
|
|
199
|
+
grid-template-columns: repeat(2, 1fr);
|
|
200
|
+
column-gap: 4.8rem;
|
|
201
|
+
row-gap: 1.6rem;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
button {
|
|
142
205
|
width: 100%;
|
|
143
|
-
&.name {
|
|
144
|
-
width: 100%;
|
|
145
|
-
}
|
|
146
|
-
&.email {
|
|
147
|
-
width: 100%;
|
|
148
|
-
}
|
|
149
206
|
}
|
|
150
207
|
}
|
|
208
|
+
|
|
209
|
+
.singleCol {
|
|
210
|
+
@include flex-direction(column);
|
|
211
|
+
gap: 2.4rem;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.fullWidth {
|
|
215
|
+
grid-column: span 2 / span 2;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.formButton {
|
|
219
|
+
grid-column: span 2 / span 2;
|
|
220
|
+
button {
|
|
221
|
+
width: auto;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
3
|
import {
|
|
3
4
|
Title,
|
|
4
5
|
Description,
|
|
@@ -6,6 +7,7 @@ import {
|
|
|
6
7
|
PRIMARY_STORY,
|
|
7
8
|
ArgsTable,
|
|
8
9
|
} from '@storybook/addon-docs/blocks';
|
|
10
|
+
import { contactUsForm } from '../../../constants/forms';
|
|
9
11
|
|
|
10
12
|
import Form from '.';
|
|
11
13
|
|
|
@@ -13,47 +15,13 @@ export default {
|
|
|
13
15
|
title: 'Theme/Organisms/Form',
|
|
14
16
|
component: Form,
|
|
15
17
|
argTypes: {
|
|
16
|
-
|
|
17
|
-
name: '
|
|
18
|
-
type: { name: '
|
|
19
|
-
defaultValue: 'contact',
|
|
20
|
-
description: 'Type of the form.',
|
|
21
|
-
table: {
|
|
22
|
-
type: { summary: 'string' },
|
|
23
|
-
defaultValue: { summary: 'contact' },
|
|
24
|
-
},
|
|
25
|
-
control: {
|
|
26
|
-
type: 'inline-radio',
|
|
27
|
-
options: ['contact', 'newsletter'],
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
hasButton: {
|
|
31
|
-
name: 'hasButton',
|
|
32
|
-
type: { name: 'boolean', required: false },
|
|
33
|
-
defaultValue: true,
|
|
34
|
-
description: 'From has or has not a submit button.',
|
|
35
|
-
table: {
|
|
36
|
-
type: { summary: 'boolean' },
|
|
37
|
-
defaultValue: { summary: true },
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
buttonLabel: {
|
|
41
|
-
name: 'buttonLabel',
|
|
42
|
-
type: { name: 'string', required: false },
|
|
43
|
-
defaultValue: 'Submit',
|
|
44
|
-
description: `The submit button's text.`,
|
|
45
|
-
table: {
|
|
46
|
-
type: { summary: 'string' },
|
|
47
|
-
defaultValue: { summary: 'Submit' },
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
submitUrl: {
|
|
51
|
-
name: 'submitUrl',
|
|
52
|
-
type: { name: 'string', required: true },
|
|
18
|
+
formOptions: {
|
|
19
|
+
name: 'formOptions',
|
|
20
|
+
type: { name: 'object', required: true },
|
|
53
21
|
defaultValue: '',
|
|
54
|
-
description:
|
|
55
|
-
|
|
56
|
-
type: { summary: '
|
|
22
|
+
description: 'Form Options object from contstants',
|
|
23
|
+
siteSchema: {
|
|
24
|
+
type: { summary: 'object' },
|
|
57
25
|
defaultValue: { summary: '' },
|
|
58
26
|
},
|
|
59
27
|
},
|
|
@@ -87,6 +55,56 @@ export default {
|
|
|
87
55
|
defaultValue: { summary: 'Fill all the required fields' },
|
|
88
56
|
},
|
|
89
57
|
},
|
|
58
|
+
submitUrl: {
|
|
59
|
+
name: 'submitUrl',
|
|
60
|
+
type: { name: 'string', required: true },
|
|
61
|
+
defaultValue: '',
|
|
62
|
+
description: `The form data will send to this URL on submit.`,
|
|
63
|
+
table: {
|
|
64
|
+
type: { summary: 'string' },
|
|
65
|
+
defaultValue: { summary: '' },
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
hasButton: {
|
|
69
|
+
name: 'hasButton',
|
|
70
|
+
type: { name: 'boolean', required: false },
|
|
71
|
+
defaultValue: true,
|
|
72
|
+
description: 'From has or has not a submit button.',
|
|
73
|
+
table: {
|
|
74
|
+
type: { summary: 'boolean' },
|
|
75
|
+
defaultValue: { summary: true },
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
buttonLabel: {
|
|
79
|
+
name: 'buttonLabel',
|
|
80
|
+
type: { name: 'string', required: false },
|
|
81
|
+
defaultValue: 'Submit',
|
|
82
|
+
description: `The submit button's text.`,
|
|
83
|
+
table: {
|
|
84
|
+
type: { summary: 'string' },
|
|
85
|
+
defaultValue: { summary: 'Submit' },
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
disabled: {
|
|
89
|
+
name: 'disabled',
|
|
90
|
+
type: { name: 'boolean', required: false },
|
|
91
|
+
defaultValue: true,
|
|
92
|
+
description: 'Form disabled',
|
|
93
|
+
table: {
|
|
94
|
+
type: { summary: 'boolean' },
|
|
95
|
+
defaultValue: { summary: true },
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
showLabels: {
|
|
99
|
+
name: 'showLabels',
|
|
100
|
+
type: { name: 'boolean', required: false },
|
|
101
|
+
defaultValue: true,
|
|
102
|
+
description: 'Show field lables or hide',
|
|
103
|
+
table: {
|
|
104
|
+
type: { summary: 'boolean' },
|
|
105
|
+
defaultValue: { summary: true },
|
|
106
|
+
},
|
|
107
|
+
},
|
|
90
108
|
},
|
|
91
109
|
parameters: {
|
|
92
110
|
docs: {
|
|
@@ -109,11 +127,5 @@ const Template = (args) => <Form {...args} />;
|
|
|
109
127
|
|
|
110
128
|
export const Default = Template.bind({});
|
|
111
129
|
Default.args = {
|
|
112
|
-
|
|
113
|
-
successMessage: 'Form has sent successfully',
|
|
114
|
-
failMessage: 'Form has not sent, Got some error',
|
|
115
|
-
validationMessage: 'Fill all the required fields.',
|
|
116
|
-
submitUrl: 'https://your-end-point.com',
|
|
117
|
-
hasButton: true,
|
|
118
|
-
buttonLabel: 'Submit',
|
|
130
|
+
formOptions: contactUsForm,
|
|
119
131
|
};
|
|
@@ -3,13 +3,19 @@ import ReactDOM from 'react-dom';
|
|
|
3
3
|
import { render, cleanup, fireEvent, screen, waitFor } from '@testing-library/react';
|
|
4
4
|
import '@testing-library/jest-dom/extend-expect';
|
|
5
5
|
import { act } from 'react-dom/test-utils';
|
|
6
|
+
import { contactUsForm, newsLetterForm } from '../../../constants/forms';
|
|
6
7
|
|
|
7
8
|
import Form from '.';
|
|
8
9
|
|
|
9
10
|
describe('Form Component', () => {
|
|
10
11
|
test('render contact form ', () => {
|
|
11
12
|
const { container } = render(
|
|
12
|
-
<Form
|
|
13
|
+
<Form
|
|
14
|
+
formOptions={contactUsForm}
|
|
15
|
+
hasButton
|
|
16
|
+
type="contact"
|
|
17
|
+
submitUrl="https://submit-form.com"
|
|
18
|
+
/>
|
|
13
19
|
);
|
|
14
20
|
expect(container.querySelector('input[type="text"]')).toBeTruthy();
|
|
15
21
|
expect(container.querySelector('input[type="email"]')).toBeTruthy();
|
|
@@ -18,7 +24,12 @@ describe('Form Component', () => {
|
|
|
18
24
|
});
|
|
19
25
|
test('render newsletter form ', () => {
|
|
20
26
|
const { container } = render(
|
|
21
|
-
<Form
|
|
27
|
+
<Form
|
|
28
|
+
formOptions={newsLetterForm}
|
|
29
|
+
hasButton
|
|
30
|
+
type="newsletter"
|
|
31
|
+
submitUrl="https://submit-form.com"
|
|
32
|
+
/>
|
|
22
33
|
);
|
|
23
34
|
expect(container.querySelector('input[type="text"]')).toBeTruthy();
|
|
24
35
|
expect(container.querySelector('input[type="email"]')).toBeTruthy();
|
|
@@ -27,7 +38,14 @@ describe('Form Component', () => {
|
|
|
27
38
|
});
|
|
28
39
|
|
|
29
40
|
test('on change', async () => {
|
|
30
|
-
render(
|
|
41
|
+
render(
|
|
42
|
+
<Form
|
|
43
|
+
formOptions={contactUsForm}
|
|
44
|
+
hasButton
|
|
45
|
+
type="contact"
|
|
46
|
+
submitUrl="https://submit-form.com"
|
|
47
|
+
/>
|
|
48
|
+
);
|
|
31
49
|
const input = document.querySelector('input[type="text"]');
|
|
32
50
|
const messageInput = document.querySelector('textarea');
|
|
33
51
|
|
|
@@ -43,7 +61,12 @@ describe('Form Component', () => {
|
|
|
43
61
|
const container = document.createElement('div');
|
|
44
62
|
act(() => {
|
|
45
63
|
ReactDOM.render(
|
|
46
|
-
<Form
|
|
64
|
+
<Form
|
|
65
|
+
formOptions={contactUsForm}
|
|
66
|
+
hasButton
|
|
67
|
+
type="contact"
|
|
68
|
+
submitUrl="https://submit-form.com"
|
|
69
|
+
/>,
|
|
47
70
|
container
|
|
48
71
|
);
|
|
49
72
|
});
|
|
@@ -58,7 +81,13 @@ describe('Form Component', () => {
|
|
|
58
81
|
|
|
59
82
|
test('handle submit with filled fields', async () => {
|
|
60
83
|
const { container } = render(
|
|
61
|
-
<Form
|
|
84
|
+
<Form
|
|
85
|
+
formOptions={newsLetterForm}
|
|
86
|
+
hasButton
|
|
87
|
+
disabled={false}
|
|
88
|
+
type="newsletter"
|
|
89
|
+
submitUrl="https://submit-form.com"
|
|
90
|
+
/>
|
|
62
91
|
);
|
|
63
92
|
const nameInput = document.querySelector('input[type="text"]');
|
|
64
93
|
const emailInput = document.querySelector('input[type="email"]');
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import React, { useState, useRef } from 'react';
|
|
1
|
+
import React, { useState, useRef, useContext } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import ReCAPTCHA from 'react-google-recaptcha';
|
|
4
|
+
import { FaArrowRight } from '@react-icons/all-files/fa/FaArrowRight';
|
|
5
|
+
import { translate } from 'gatsby-core-theme/src/helpers/getters';
|
|
6
|
+
import { Context } from 'gatsby-core-theme/src/context/MainProvider';
|
|
4
7
|
import Button from '~atoms/button';
|
|
5
8
|
import styles from './form.module.scss';
|
|
6
9
|
|
|
7
10
|
const FormComponent = ({
|
|
8
|
-
|
|
11
|
+
formOptions = {},
|
|
9
12
|
successMessage = 'Form has sent successfully',
|
|
10
13
|
failMessage = 'Form has not sent, Got some error',
|
|
11
14
|
validationMessage = 'Fill all the required fields.',
|
|
@@ -13,8 +16,9 @@ const FormComponent = ({
|
|
|
13
16
|
hasButton = true,
|
|
14
17
|
buttonLabel = 'Submit',
|
|
15
18
|
disabled = true,
|
|
16
|
-
|
|
19
|
+
showLabels = true,
|
|
17
20
|
}) => {
|
|
21
|
+
const { translations } = useContext(Context) || {};
|
|
18
22
|
const recaptchaRef = useRef();
|
|
19
23
|
const [state, setState] = useState({
|
|
20
24
|
loading: false,
|
|
@@ -26,10 +30,14 @@ const FormComponent = ({
|
|
|
26
30
|
email: '',
|
|
27
31
|
message: '',
|
|
28
32
|
});
|
|
33
|
+
const values = formOptions.fields.map((field) => ({
|
|
34
|
+
[field.id]: field.value ? field.value : '',
|
|
35
|
+
}));
|
|
36
|
+
const [elements, setElements] = useState(Object.assign({}, ...values));
|
|
29
37
|
|
|
30
|
-
const handleChange = (
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
const handleChange = (name, value) => {
|
|
39
|
+
setState({ ...state, [name]: value });
|
|
40
|
+
setElements({ ...elements, [name]: value });
|
|
33
41
|
};
|
|
34
42
|
|
|
35
43
|
async function postData(url = '', data = {}) {
|
|
@@ -43,21 +51,19 @@ const FormComponent = ({
|
|
|
43
51
|
body: JSON.stringify(Object.fromEntries(data)),
|
|
44
52
|
});
|
|
45
53
|
}
|
|
54
|
+
|
|
46
55
|
const handleSubmit = (e) => {
|
|
47
56
|
e.preventDefault();
|
|
48
57
|
|
|
49
|
-
|
|
58
|
+
// check if all required fields are empty
|
|
59
|
+
const inValid = Object.values(elements).some((value) => value === '');
|
|
50
60
|
|
|
51
|
-
if (
|
|
52
|
-
name === '' ||
|
|
53
|
-
email === '' ||
|
|
54
|
-
(type === 'contact' && message === '') ||
|
|
55
|
-
isDisabled === true
|
|
56
|
-
) {
|
|
61
|
+
if (inValid) {
|
|
57
62
|
setState({ ...state, isValid: false, success: false });
|
|
58
63
|
return;
|
|
59
64
|
}
|
|
60
65
|
|
|
66
|
+
// form is valid
|
|
61
67
|
const form = e.target;
|
|
62
68
|
setState({ ...state, loading: true, success: false, failed: false, isDisabled: true });
|
|
63
69
|
postData(submitUrl, new FormData(form))
|
|
@@ -81,7 +87,7 @@ const FormComponent = ({
|
|
|
81
87
|
});
|
|
82
88
|
}
|
|
83
89
|
})
|
|
84
|
-
.catch(() => {
|
|
90
|
+
.catch((err) => {
|
|
85
91
|
// on error
|
|
86
92
|
setState({
|
|
87
93
|
...state,
|
|
@@ -98,56 +104,176 @@ const FormComponent = ({
|
|
|
98
104
|
value && setState({ ...state, isDisabled: false });
|
|
99
105
|
}
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
107
|
+
const getField = (field) => {
|
|
108
|
+
const { type, id, translationKey, label, ...props } = field;
|
|
109
|
+
|
|
110
|
+
switch (type) {
|
|
111
|
+
case 'textarea':
|
|
112
|
+
return (
|
|
113
|
+
<div className={styles.textareaGroup}>
|
|
114
|
+
<textarea
|
|
115
|
+
name={id}
|
|
116
|
+
id={id}
|
|
117
|
+
{...props}
|
|
118
|
+
value={elements[id]}
|
|
119
|
+
onChange={(e) => handleChange(e.target.name, e.target.value)}
|
|
120
|
+
className={`${!state.isValid && elements[id] === '' && styles.invalid}`}
|
|
121
|
+
/>
|
|
122
|
+
{field.maxLength && <span>{`${elements[id].length}/${field.maxLength}`}</span>}
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
case 'checkbox':
|
|
126
|
+
return (
|
|
127
|
+
<div className={styles.checkboxGroup}>
|
|
128
|
+
{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
|
+
>
|
|
134
|
+
<input
|
|
135
|
+
id={option.id}
|
|
136
|
+
name={option.id}
|
|
137
|
+
type={type}
|
|
138
|
+
checked={elements[option.id] === 'true'}
|
|
139
|
+
onChange={(e) => handleChange(e.target.name, e.target.checked.toString())}
|
|
140
|
+
/>
|
|
141
|
+
{translate(translations, option.translationKey, option.label)}
|
|
142
|
+
</label>
|
|
143
|
+
))}
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
case 'radio':
|
|
147
|
+
return (
|
|
148
|
+
<div className={styles.radioGroup}>
|
|
149
|
+
{field.options.map((option) => (
|
|
150
|
+
<label
|
|
151
|
+
key={option.id}
|
|
152
|
+
htmlFor={option.id}
|
|
153
|
+
className={`${!state.isValid && elements[id] === '' && styles.invalid}`}
|
|
154
|
+
>
|
|
155
|
+
<input
|
|
156
|
+
id={option.id}
|
|
157
|
+
name={id}
|
|
158
|
+
type={type}
|
|
159
|
+
checked={elements[id] === option.label}
|
|
160
|
+
onChange={(e) => handleChange(e.target.name, option.label)}
|
|
161
|
+
/>{' '}
|
|
162
|
+
{translate(translations, option.translationKey, option.label)}
|
|
163
|
+
</label>
|
|
164
|
+
))}
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
case 'select':
|
|
168
|
+
return (
|
|
169
|
+
<select
|
|
170
|
+
id={id}
|
|
171
|
+
aria-label={id}
|
|
172
|
+
name={id}
|
|
173
|
+
defaultValue={elements[id]}
|
|
174
|
+
onChange={(e) => handleChange(e.target.name, e.target.value)}
|
|
175
|
+
className={`${!state.isValid && elements[id] === '' && styles.invalid}`}
|
|
176
|
+
>
|
|
177
|
+
{field.showDefault && (
|
|
178
|
+
<option value="">
|
|
179
|
+
{translate(translations, field.defaultTranslationKey, field.defaultLabel)}
|
|
180
|
+
</option>
|
|
181
|
+
)}
|
|
182
|
+
{field.options.map((option) => (
|
|
183
|
+
<option key={option.id} value={option.label}>
|
|
184
|
+
{translate(translations, option.translationKey, option.label)}
|
|
185
|
+
</option>
|
|
186
|
+
))}
|
|
187
|
+
</select>
|
|
188
|
+
);
|
|
189
|
+
case 'range':
|
|
190
|
+
return (
|
|
191
|
+
<div className={styles.radioGroup}>
|
|
192
|
+
<input
|
|
193
|
+
name={id}
|
|
194
|
+
id={id}
|
|
195
|
+
type={type}
|
|
196
|
+
aria-describedby={id}
|
|
197
|
+
{...props}
|
|
198
|
+
value={elements[id]}
|
|
199
|
+
onChange={(e) => handleChange(e.target.name, e.target.value)}
|
|
200
|
+
/>
|
|
201
|
+
<div className={styles.rangeValues}>
|
|
202
|
+
<span>{field.min}</span>
|
|
203
|
+
<span className={`${!state.isValid && elements[id] === '' && styles.invalid}`}>
|
|
204
|
+
{elements[id] || 'Selected Value'}
|
|
205
|
+
</span>
|
|
206
|
+
<span>{field.max}</span>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
default:
|
|
211
|
+
return (
|
|
120
212
|
<input
|
|
121
|
-
name=
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
213
|
+
name={id}
|
|
214
|
+
id={id}
|
|
215
|
+
type={type}
|
|
216
|
+
aria-describedby={id}
|
|
217
|
+
{...props}
|
|
218
|
+
value={elements[id]}
|
|
219
|
+
onChange={(e) => handleChange(e.target.name, e.target.value)}
|
|
220
|
+
className={`${!state.isValid && elements[id] === '' && styles.invalid}`}
|
|
128
221
|
/>
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<div className={`${styles.formComponent || ''} `}>
|
|
228
|
+
{formOptions.title && (
|
|
229
|
+
<h2>
|
|
230
|
+
{translate(translations, formOptions.title?.translationKey, formOptions.title?.label)}
|
|
231
|
+
</h2>
|
|
232
|
+
)}
|
|
233
|
+
{formOptions.description && (
|
|
234
|
+
<p>
|
|
235
|
+
{translate(
|
|
236
|
+
translations,
|
|
237
|
+
formOptions.description?.translationKey,
|
|
238
|
+
formOptions.description?.label
|
|
239
|
+
)}
|
|
240
|
+
</p>
|
|
241
|
+
)}
|
|
242
|
+
<form
|
|
243
|
+
onSubmit={(e) => handleSubmit(e)}
|
|
244
|
+
action={submitUrl}
|
|
245
|
+
className={formOptions.twoCol ? styles.twoCol || '' : styles.singleCol || ''}
|
|
246
|
+
target="_self"
|
|
247
|
+
>
|
|
248
|
+
{formOptions.fields &&
|
|
249
|
+
formOptions.fields.map((field) => {
|
|
250
|
+
const { id, translationKey, label } = field;
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<div
|
|
254
|
+
key={id}
|
|
255
|
+
className={`${styles.formGroup || ''} ${styles[id] ? styles[id] : ''} ${
|
|
256
|
+
(!field.twoCol && styles.fullWidth) || ''
|
|
257
|
+
}`}
|
|
258
|
+
>
|
|
259
|
+
{showLabels && label && (
|
|
260
|
+
<label htmlFor={id}>{translate(translations, translationKey, label)}</label>
|
|
261
|
+
)}
|
|
262
|
+
{elements && getField(field)}
|
|
263
|
+
</div>
|
|
264
|
+
);
|
|
265
|
+
})}
|
|
266
|
+
|
|
267
|
+
{formOptions.hasReCAPTCHA && (
|
|
268
|
+
<div className={styles.recaptcha || ''}>
|
|
269
|
+
<ReCAPTCHA
|
|
270
|
+
ref={recaptchaRef}
|
|
271
|
+
sitekey={`${process.env.RECAPTCHA_SITE_KEY}`}
|
|
272
|
+
// eslint-disable-next-line react/jsx-no-bind
|
|
273
|
+
onChange={recaptchaOnChange}
|
|
139
274
|
/>
|
|
140
275
|
</div>
|
|
141
276
|
)}
|
|
142
|
-
{/* if has button */}
|
|
143
|
-
<div className={styles.recaptcha || ''}>
|
|
144
|
-
<ReCAPTCHA
|
|
145
|
-
ref={recaptchaRef}
|
|
146
|
-
sitekey={`${process.env.RECAPTCHA_SITE_KEY}`}
|
|
147
|
-
// eslint-disable-next-line react/jsx-no-bind
|
|
148
|
-
onChange={recaptchaOnChange}
|
|
149
|
-
/>
|
|
150
|
-
</div>
|
|
151
277
|
{hasButton && (
|
|
152
278
|
<div className={styles.formButton || ''}>
|
|
153
279
|
<Button
|
|
@@ -155,22 +281,25 @@ const FormComponent = ({
|
|
|
155
281
|
btnText={state.loading ? 'sending...' : buttonLabel}
|
|
156
282
|
disabled={state.loading}
|
|
157
283
|
gtmClass="form-gtm btn-cta"
|
|
158
|
-
|
|
284
|
+
noStyle
|
|
285
|
+
icon={<FaArrowRight fontSize={20} />}
|
|
159
286
|
/>
|
|
160
287
|
</div>
|
|
161
288
|
)}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
289
|
+
{(state.success || state.failed || state.isValid) && (
|
|
290
|
+
<div className={styles.formAlerts || ''}>
|
|
291
|
+
{state.success && <div className={styles.alertSuccess || ''}>{successMessage}</div>}
|
|
292
|
+
{state.failed && <div className={styles.alertDanger || ''}>{failMessage}</div>}
|
|
293
|
+
{!state.isValid && <div className={styles.alertWarning || ''}>{validationMessage}</div>}
|
|
294
|
+
</div>
|
|
295
|
+
)}
|
|
167
296
|
</form>
|
|
168
297
|
</div>
|
|
169
298
|
);
|
|
170
299
|
};
|
|
171
300
|
|
|
172
301
|
FormComponent.propTypes = {
|
|
173
|
-
|
|
302
|
+
formOptions: PropTypes.shape({}),
|
|
174
303
|
successMessage: PropTypes.string,
|
|
175
304
|
failMessage: PropTypes.string,
|
|
176
305
|
validationMessage: PropTypes.string,
|
|
@@ -178,6 +307,6 @@ FormComponent.propTypes = {
|
|
|
178
307
|
hasButton: PropTypes.bool,
|
|
179
308
|
buttonLabel: PropTypes.string,
|
|
180
309
|
disabled: PropTypes.bool,
|
|
181
|
-
|
|
310
|
+
showLabels: PropTypes.bool,
|
|
182
311
|
};
|
|
183
312
|
export default FormComponent;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
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,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
label: 'Email Address',
|
|
19
|
+
id: 'email',
|
|
20
|
+
type: 'email',
|
|
21
|
+
placeholder: 'Email@placeholder.com',
|
|
22
|
+
translationKey: 'email_translation',
|
|
23
|
+
twoCol: true,
|
|
24
|
+
},
|
|
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',
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
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,
|
|
73
|
+
},
|
|
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,
|
|
82
|
+
},
|
|
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
|
+
],
|
|
100
|
+
};
|
|
@@ -143,4 +143,32 @@
|
|
|
143
143
|
-webkit-mask-composite: destination-out;
|
|
144
144
|
mask-composite: exclude;
|
|
145
145
|
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@mixin buttonsColor($color1, $color2, $color3, $textColor: 'white') {
|
|
149
|
+
display: inline-flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
justify-content: center;
|
|
152
|
+
text-align: center;
|
|
153
|
+
background-color: $color1;
|
|
154
|
+
color: $textColor;
|
|
155
|
+
padding: 0.9rem 3rem;
|
|
156
|
+
font-weight: 700;
|
|
157
|
+
font-size: 1.8rem;
|
|
158
|
+
border-radius: var(--border-radius);
|
|
159
|
+
|
|
160
|
+
>svg {
|
|
161
|
+
flex: none;
|
|
162
|
+
margin-left: .8rem;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
&:hover {
|
|
166
|
+
background-color: $color2;
|
|
167
|
+
color: $textColor;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
&:active {
|
|
171
|
+
background-color: $color3;
|
|
172
|
+
color: $textColor;
|
|
173
|
+
}
|
|
146
174
|
}
|