@comicrelief/component-library 6.10.0 → 7.0.1

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 (56) hide show
  1. package/cypress/integration/components/Organisms/Donate.spec.js +13 -13
  2. package/cypress/integration/components/Organisms/EmailSignUp.spec.js +47 -132
  3. package/dist/components/Organisms/Donate/Donate.md +9 -9
  4. package/dist/components/Organisms/Donate/Form/Form.js +2 -1
  5. package/dist/components/Organisms/Donate/GivingSelector/GivingSelector.js +17 -71
  6. package/dist/components/Organisms/Donate/GivingSelector/GivingSelector.style.js +71 -0
  7. package/dist/components/Organisms/Donate/__snapshots__/Donate.test.js.snap +11 -25
  8. package/dist/components/Organisms/EmailSignUp/EmailSignUp.md +8 -123
  9. package/dist/components/Organisms/EmailSignUp/EmailSignUp.style.js +46 -29
  10. package/dist/components/Organisms/EmailSignUp/EmailSignUp.test.js +24 -69
  11. package/dist/components/Organisms/EmailSignUp/EmailSignUpForm.js +92 -0
  12. package/dist/components/Organisms/EmailSignUp/_Confetti.js +116 -0
  13. package/dist/components/Organisms/EmailSignUp/_EmailSignUp.js +107 -0
  14. package/dist/components/Organisms/EmailSignUp/_EmailSignUpConfig.js +51 -0
  15. package/dist/components/Organisms/EmailSignUp/_TextInput.js +51 -0
  16. package/dist/components/Organisms/EmailSignUp/__snapshots__/EmailSignUp.test.js.snap +249 -406
  17. package/dist/components/Organisms/Header/Header.md +1 -13
  18. package/dist/components/Organisms/Membership/Membership.test.js +1 -1
  19. package/dist/index.js +14 -10
  20. package/package.json +2 -1
  21. package/src/components/Organisms/Donate/Donate.md +9 -9
  22. package/src/components/Organisms/Donate/Form/Form.js +1 -0
  23. package/src/components/Organisms/Donate/GivingSelector/GivingSelector.js +15 -85
  24. package/src/components/Organisms/Donate/GivingSelector/GivingSelector.style.js +78 -0
  25. package/src/components/Organisms/Donate/__snapshots__/Donate.test.js.snap +11 -25
  26. package/src/components/Organisms/EmailSignUp/EmailSignUp.md +8 -123
  27. package/src/components/Organisms/EmailSignUp/EmailSignUp.style.js +33 -13
  28. package/src/components/Organisms/EmailSignUp/EmailSignUp.test.js +35 -69
  29. package/src/components/Organisms/EmailSignUp/EmailSignUpForm.js +60 -0
  30. package/src/components/Organisms/EmailSignUp/_Confetti.js +106 -0
  31. package/src/components/Organisms/EmailSignUp/_EmailSignUp.js +138 -0
  32. package/src/components/Organisms/EmailSignUp/_EmailSignUpConfig.js +54 -0
  33. package/src/components/Organisms/EmailSignUp/_TextInput.js +45 -0
  34. package/src/components/Organisms/EmailSignUp/__snapshots__/EmailSignUp.test.js.snap +249 -406
  35. package/src/components/Organisms/Header/Header.md +1 -13
  36. package/src/components/Organisms/Membership/Membership.test.js +33 -33
  37. package/src/index.js +10 -4
  38. package/cypress/integration/components/Molecules/HeaderEsuWithIcon.spec.js +0 -69
  39. package/dist/components/Molecules/HeaderEsuWithIcon/HeaderEsuWithIcon.js +0 -136
  40. package/dist/components/Molecules/HeaderEsuWithIcon/HeaderEsuWithIcon.md +0 -47
  41. package/dist/components/Molecules/HeaderEsuWithIcon/HeaderEsuWithIcon.style.js +0 -52
  42. package/dist/components/Molecules/HeaderEsuWithIcon/HeaderEsuWithIcon.test.js +0 -99
  43. package/dist/components/Molecules/HeaderEsuWithIcon/__snapshots__/HeaderEsuWithIcon.test.js.snap +0 -1211
  44. package/dist/components/Molecules/HeaderEsuWithIcon/assets/HeaderIcons.js +0 -25
  45. package/dist/components/Molecules/HeaderEsuWithIcon/assets/icon--close.svg +0 -5
  46. package/dist/components/Molecules/HeaderEsuWithIcon/assets/icon--email.svg +0 -5
  47. package/dist/components/Organisms/EmailSignUp/EmailSignUp.js +0 -182
  48. package/src/components/Molecules/HeaderEsuWithIcon/HeaderEsuWithIcon.js +0 -135
  49. package/src/components/Molecules/HeaderEsuWithIcon/HeaderEsuWithIcon.md +0 -47
  50. package/src/components/Molecules/HeaderEsuWithIcon/HeaderEsuWithIcon.style.js +0 -60
  51. package/src/components/Molecules/HeaderEsuWithIcon/HeaderEsuWithIcon.test.js +0 -103
  52. package/src/components/Molecules/HeaderEsuWithIcon/__snapshots__/HeaderEsuWithIcon.test.js.snap +0 -1211
  53. package/src/components/Molecules/HeaderEsuWithIcon/assets/HeaderIcons.js +0 -15
  54. package/src/components/Molecules/HeaderEsuWithIcon/assets/icon--close.svg +0 -5
  55. package/src/components/Molecules/HeaderEsuWithIcon/assets/icon--email.svg +0 -5
  56. package/src/components/Organisms/EmailSignUp/EmailSignUp.js +0 -197
@@ -1,129 +1,14 @@
1
- # Email Sign Up
1
+ # Email SignUp Form
2
2
 
3
- ```js
4
- import RichText from '../../Atoms/RichText/RichText';
5
-
6
- const title = 'Stay in the know!';
7
- const topCopy = (
8
- <RichText
9
- markup={`<p>Get regular email updates and info on what we're up to!</p>`}
10
- />
11
- );
12
- const privacyCopy = (
13
- <RichText
14
- markup={`<p>Our <a class="link link--white inline" href="/privacy-notice">Privacy Policy</a> describes how we handle and protect your information.<br><br>If you are under 18, please make sure you have your parents’ permission before providing us with any personal details.</p>`}
15
- />
16
- );
17
- const successCopy = (
18
- <RichText
19
- markup={`<p>Thanks! Your first email will be with you shortly</p>`}
20
- />
21
- );
22
-
23
- const [success, setSuccess] = React.useState(false);
24
- const [error, setError] = React.useState('');
25
-
26
- const sendEmail = email => {
27
- setTimeout(() => setSuccess(!success), 2000);
28
- console.log(email);
29
- };
30
-
31
- const validate = ({ email }) => {
32
- let isValid = false;
33
- if (email.includes('@')) {
34
- isValid = true;
35
- setError('');
36
- } else {
37
- setError('invalid email!');
38
- }
39
- return isValid;
40
- };
41
-
42
- <EmailSignUp
43
- title={title}
44
- topCopy={topCopy}
45
- successCopy={successCopy}
46
- isSuccess={success}
47
- privacyCopy={privacyCopy}
48
- errorMsg={error}
49
- subscribe={sendEmail}
50
- validate={validate}
51
- />;
52
- ```
53
-
54
- # Email Sign Up Schools
55
3
 
56
4
  ```js
57
- import RichText from '../../Atoms/RichText/RichText';
58
-
59
- const title = 'Stay in the know!';
60
- const topCopy = (
61
- <RichText
62
- markup={`<p>Get regular email updates and info on what we're up to!</p>`}
63
- />
64
- );
65
- const privacyCopy = (
66
- <RichText
67
- markup={`<p>Our <a class="link link--white inline" href="/privacy-notice">Privacy Policy</a> describes how we handle and protect your information.<br><br>If you are under 18, please make sure you have your parents’ permission before providing us with any personal details.</p>`}
68
- />
69
- );
70
- const successCopy = (
71
- <RichText
72
- markup={`<p>Thanks! Your first email will be with you shortly</p>`}
73
- />
74
- );
75
- const selectItems = [
76
- { value: '', displayValue: '-- Select age group --' },
77
- { value: 'Option one', displayValue: 'The first option' },
78
- {
79
- value: 'Option two',
80
- displayValue: 'The second option'
81
- },
82
- { value: 'Option three', displayValue: 'The third option' },
83
- { value: 'Option four', displayValue: 'The fourth option' }
84
- ];
85
-
86
- const [successSchools, setSuccessSchools] = React.useState(false);
87
- const [error, setError] = React.useState('');
88
-
89
- sendEmail = emailAndAge => {
90
- setTimeout(
91
- () => setSuccessSchools(!successSchools),
92
- 2000
93
- );
94
- console.log(emailAndAge);
95
- };
5
+ import EmailSignUpForm from './EmailSignUpForm';
6
+ import Text from '../../Atoms/Text/Text';
96
7
 
97
- const validate = ({ email, age }) => {
98
- let isValid = false;
99
- if (email.includes('@')) {
100
- isValid = true;
101
- setError('');
102
- } else {
103
- setError('invalid email!');
104
- }
105
- if (isValid === true && typeof age !== 'undefined') {
106
- if (age) {
107
- setError('');
108
- } else {
109
- isValid = false;
110
- setError('invalid age!');
111
- }
112
- }
113
- return isValid;
114
- };
8
+ <>
9
+ <Text tag="p">This EmailSignUpForm component exists purely to show the EmailSignUp component functioning within the Component Library; applications are to provide their own react-hook-form form and validation, based on these.</Text>
10
+
11
+ <EmailSignUpForm />
12
+ </>
115
13
 
116
- <EmailSignUp
117
- title={title}
118
- topCopy={topCopy}
119
- successCopy={successCopy}
120
- schoolsCopy="Now please select your teaching group so you get the right updates."
121
- isSuccess={successSchools}
122
- selectItems={selectItems}
123
- isSchools
124
- privacyCopy={privacyCopy}
125
- errorMsg={error}
126
- subscribe={sendEmail}
127
- validate={validate}
128
- />;
129
14
  ```
@@ -1,7 +1,7 @@
1
1
  import styled from 'styled-components';
2
2
 
3
3
  import spacing from '../../../theme/shared/spacing';
4
- import Input from '../../Atoms/Input/Input';
4
+ import TextInput from './_TextInput';
5
5
  import Text from '../../Atoms/Text/Text';
6
6
 
7
7
  const ESUWrapper = styled.div`
@@ -9,7 +9,8 @@ const ESUWrapper = styled.div`
9
9
  flex-direction: column;
10
10
  font-size: ${({ theme }) => theme.fontSize('s')};
11
11
  color: ${({ theme }) => theme.color('white')};
12
- background-color: ${({ theme, backgroundColor }) => theme.color(backgroundColor)};
12
+ background-color: ${({ theme, backgroundColour }) => theme.color(backgroundColour)};
13
+ padding: ${spacing('m')};
13
14
  `;
14
15
 
15
16
  const TopCopyWrapper = styled.div`
@@ -19,14 +20,8 @@ const TopCopyWrapper = styled.div`
19
20
 
20
21
  const ButtonWrapper = styled.div`
21
22
  margin-top: ${spacing('md')};
22
- input {
23
- text-align: center;
24
- width: 100%;
25
- font-size: ${({ theme }) => theme.fontSize('s')};
26
- @media ${({ theme }) => theme.breakpoint('small')} {
27
- font-size: ${({ theme }) => theme.fontSize('m')};
28
- max-width: 180px;
29
- }
23
+ button {
24
+ background-color: ${({ theme, buttonColour }) => theme.color(buttonColour)};
30
25
  }
31
26
  `;
32
27
 
@@ -34,6 +29,7 @@ const PrivacyCopyWrapper = styled.div`
34
29
  display: flex;
35
30
  flex-direction: column;
36
31
  margin-top: ${spacing('md')};
32
+
37
33
  p {
38
34
  font-size: ${({ theme }) => theme.fontSize('s')};
39
35
  line-height: ${({ theme }) => theme.fontSize('xl')};
@@ -44,14 +40,37 @@ const PrivacyCopyWrapper = styled.div`
44
40
  }
45
41
  `;
46
42
 
47
- const Form = styled.form`
43
+ const FormInner = styled.div`
48
44
  display: flex;
49
45
  flex-direction: column;
50
46
  margin: ${spacing('md')} 0;
51
47
  `;
52
48
 
53
- const InputField = styled(Input)`
49
+ const NameWrapper = styled.div`
50
+ display: flex;
51
+ flex-direction: column;
52
+ gap: 0;
53
+
54
+ @media ${({ theme }) => theme.breakpoint('medium')} {
55
+ justify-content: start;
56
+ flex-direction: ${({ columnLayout }) => (columnLayout ? 'column' : 'row')};
57
+ gap: ${({ columnLayout }) => (columnLayout ? 0 : spacing('md'))};
58
+ )};
59
+
60
+ }
61
+ `;
62
+
63
+ const InputField = styled(TextInput)`
54
64
  width: 100%;
65
+ margin-bottom: ${spacing('md')};
66
+
67
+ & > span:first-child {
68
+ color: ${({ theme }) => theme.color('white')};
69
+ }
70
+
71
+ @media ${({ theme }) => theme.breakpoint('medium')} {
72
+ max-width: 290px;
73
+ }
55
74
  `;
56
75
 
57
76
  const Title = styled(Text)`
@@ -63,7 +82,8 @@ export {
63
82
  TopCopyWrapper,
64
83
  PrivacyCopyWrapper,
65
84
  ButtonWrapper,
66
- Form,
85
+ FormInner,
67
86
  InputField,
87
+ NameWrapper,
68
88
  Title
69
89
  };
@@ -1,76 +1,42 @@
1
- import React from 'react';
2
- import 'jest-styled-components';
3
- import TestRenderer from 'react-test-renderer';
4
- import renderWithTheme from '../../../hoc/shallowWithTheme';
5
- import EmailSignUp from './EmailSignUp';
6
- import RichText from '../../Atoms/RichText/RichText';
1
+ import React from "react";
2
+ import "jest-styled-components";
3
+ import renderWithTheme from "../../../hoc/shallowWithTheme";
4
+ import { EmailSignUp, validationSchema } from "./_EmailSignUp";
5
+ import RichText from "../../Atoms/RichText/RichText";
6
+ import { useForm, FormProvider } from "react-hook-form";
7
+ import { yupResolver } from "@hookform/resolvers/yup";
7
8
 
8
- const { act } = TestRenderer;
9
-
10
- it('renders correctly', () => {
11
- const top = '<h1> Top Copy</h1> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>';
12
- const success = '<h1> Success Copy</h1> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>';
13
- const privacy = 'check <a href="https://www.comicrelief.com/privacy-notice">Privacy policy</a>';
14
- const tree = renderWithTheme(
15
- <>
16
- <EmailSignUp
17
- title="sign up letter"
18
- topCopy={<RichText markup={top} />}
19
- successCopy={<RichText markup={success} />}
20
- isSuccess={false}
21
- errorMsg=""
22
- buttonColor="teal"
23
- privacyCopy={<RichText markup={privacy} />}
24
- subscribe={() => 'Done'}
25
- validate={() => true}
26
- />
27
- </>
28
- ).toJSON();
9
+ const DummyForm = () => {
10
+ const formMethods = useForm({
11
+ mode: "onBlur",
12
+ resolver: yupResolver(validationSchema),
13
+ });
14
+ const { handleSubmit } = formMethods;
29
15
 
30
- expect(tree).toMatchSnapshot();
31
- });
16
+ const top =
17
+ "<h1> Top Copy</h1> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>";
18
+ const success =
19
+ "<h1> Success Copy</h1> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>";
20
+ const privacy =
21
+ 'check <a href="https://www.comicrelief.com/privacy-notice">Privacy policy</a>';
32
22
 
33
- it('renders ESU School correctly', () => {
34
- const top = '<h1> Top Copy</h1> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>';
35
- const success = '<h1> Success Copy</h1> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>';
36
- const privacy = 'check <a href="https://www.comicrelief.com/privacy-notice">Privacy policy</a>';
37
- const selectItems = [
38
- { value: '', displayValue: '-- Select age group --' },
39
- { value: 'Option one', displayValue: 'The first option' },
40
- {
41
- value: 'Option two',
42
- displayValue: 'The second option'
43
- },
44
- { value: 'Option three', displayValue: 'The third option' },
45
- { value: 'Option four', displayValue: 'The fourth option' }
46
- ];
47
- const mockNext = jest.fn();
48
- const tree = renderWithTheme(
49
- <>
50
- <EmailSignUp
51
- title="sign up letter"
52
- topCopy={<RichText markup={top} />}
53
- successCopy={<RichText markup={success} />}
54
- schoolsCopy="Now please select your teaching group so you get the right updates."
55
- selectItems={selectItems}
56
- isSuccess={false}
57
- isSchools
58
- errorMsg=""
59
- buttonColor="teal"
60
- privacyCopy={<RichText markup={privacy} />}
61
- subscribe={mockNext}
62
- validate={() => true}
63
- />
64
- </>
23
+ return (
24
+ <FormProvider {...formMethods}>
25
+ <form onSubmit={handleSubmit(() => true)} noValidate>
26
+ <EmailSignUp
27
+ title="sign up letter"
28
+ topCopy={<RichText markup={top} />}
29
+ successCopy={<RichText markup={success} />}
30
+ privacyCopy={<RichText markup={privacy} />}
31
+ formContext={formMethods}
32
+ />
33
+ </form>
34
+ </FormProvider>
65
35
  );
66
- const input = tree.root.findAllByType('input')[0];
67
- input.value = 'test@test.com';
36
+ };
68
37
 
69
- act(() => {
70
- /* fire events that update state */
71
- tree.root.findAllByType('input')[1].props.onClick();
72
- });
38
+ it("renders correctly", () => {
39
+ const tree = renderWithTheme(<DummyForm />).toJSON();
73
40
 
74
- const treeJson = tree.toJSON();
75
- expect(treeJson).toMatchSnapshot();
41
+ expect(tree).toMatchSnapshot();
76
42
  });
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+ import { useForm, FormProvider } from 'react-hook-form';
3
+ import { yupResolver } from '@hookform/resolvers/yup';
4
+ import RichText from '../../Atoms/RichText/RichText';
5
+ import {
6
+ EmailSignUp,
7
+ buildEsuValidationSchema,
8
+ ESU_FIELDS
9
+ } from './_EmailSignUp';
10
+
11
+ const EmailSignUpForm = () => {
12
+ const validationSchema = buildEsuValidationSchema({});
13
+ const formMethods = useForm({
14
+ mode: 'onBlur',
15
+ resolver: yupResolver(validationSchema)
16
+ });
17
+ const { handleSubmit, trigger } = formMethods;
18
+
19
+ async function handleSubscribe(data) {
20
+ const valid = await trigger([
21
+ ESU_FIELDS.EMAIL,
22
+ ESU_FIELDS.FIRST_NAME,
23
+ ESU_FIELDS.LAST_NAME
24
+ ]);
25
+ if (valid) {
26
+ console.log(data);
27
+ }
28
+ }
29
+ const title = 'Stay in the know!';
30
+ const topCopy = (
31
+ <RichText
32
+ markup={"<p>Get regular email updates and info on what we're up to!</p>"}
33
+ />
34
+ );
35
+ const privacyCopy = (
36
+ <RichText
37
+ markup={
38
+ '<p>Our <a class="link link--white inline" href="/privacy-notice">Privacy Policy</a> describes how we handle and protect your information.<br><br>If you are under 18, please make sure you have your parents’ permission before providing us with any personal details.</p>'
39
+ }
40
+ />
41
+ );
42
+ const successCopy = (
43
+ <RichText markup="<p>Thanks! Your first email will be with you shortly</p>" />
44
+ );
45
+ return (
46
+ <FormProvider {...formMethods}>
47
+ <form onSubmit={handleSubmit(handleSubscribe)} noValidate>
48
+ <EmailSignUp
49
+ id="default"
50
+ title={title}
51
+ topCopy={topCopy}
52
+ successCopy={successCopy}
53
+ privacyCopy={privacyCopy}
54
+ formContext={formMethods}
55
+ />
56
+ </form>
57
+ </FormProvider>
58
+ );
59
+ };
60
+ export default EmailSignUpForm;
@@ -0,0 +1,106 @@
1
+ import React, {
2
+ useCallback, useEffect, useRef, useState
3
+ } from 'react';
4
+ import ReactCanvasConfetti from 'react-canvas-confetti';
5
+ import PropTypes from 'prop-types';
6
+
7
+ function randomInRange(min, max) {
8
+ return Math.random() * (max - min) + min;
9
+ }
10
+
11
+ const canvasStyles = {
12
+ position: 'fixed',
13
+ pointerEvents: 'none',
14
+ width: '100%',
15
+ height: '100%',
16
+ top: 0,
17
+ left: 0
18
+ };
19
+
20
+ function getAnimationSettings(originXA, originXB) {
21
+ return {
22
+ startVelocity: 30,
23
+ spread: 360,
24
+ ticks: 60,
25
+ zIndex: 0,
26
+ particleCount: 150,
27
+ origin: {
28
+ x: randomInRange(originXA, originXB),
29
+ y: Math.random() - 0.2
30
+ }
31
+ };
32
+ }
33
+ // TODO: Refactor this into an atom
34
+ export default function Confetti({ trigger, duration }) {
35
+ const refAnimationInstance = useRef(null);
36
+ const [intervalId, setIntervalId] = useState();
37
+
38
+ const getInstance = useCallback(instance => {
39
+ refAnimationInstance.current = instance;
40
+ }, []);
41
+
42
+ const nextTickAnimation = useCallback(() => {
43
+ if (refAnimationInstance.current) {
44
+ refAnimationInstance.current(getAnimationSettings(0.1, 0.3));
45
+ refAnimationInstance.current(getAnimationSettings(0.7, 0.9));
46
+ }
47
+ }, []);
48
+
49
+ const startAnimation = useCallback(() => {
50
+ if (!intervalId) {
51
+ setIntervalId(setInterval(nextTickAnimation, 400));
52
+ }
53
+ }, [intervalId, nextTickAnimation]);
54
+
55
+ const pauseAnimation = useCallback(() => {
56
+ clearInterval(intervalId);
57
+ setIntervalId(null);
58
+ }, [intervalId]);
59
+
60
+ const stopAnimation = useCallback(() => {
61
+ clearInterval(intervalId);
62
+ setIntervalId(null);
63
+ if (refAnimationInstance.current) {
64
+ refAnimationInstance.current.reset();
65
+ }
66
+ }, [intervalId]);
67
+
68
+ // eslint-disable-next-line
69
+ useEffect(() => {
70
+ return () => {
71
+ clearInterval(intervalId);
72
+ };
73
+ }, [intervalId]);
74
+
75
+ useEffect(() => {
76
+ let timeOut;
77
+ if (trigger) {
78
+ startAnimation();
79
+ timeOut = setTimeout(() => {
80
+ // This gracefully ends the animation
81
+ pauseAnimation();
82
+ }, duration);
83
+ }
84
+ return () => {
85
+ if (timeOut) {
86
+ // this clears up the animation
87
+ stopAnimation();
88
+ }
89
+ }; // eslint-disable-next-line react-hooks/exhaustive-deps
90
+ }, [trigger, duration]);
91
+
92
+ return (
93
+ <>
94
+ <ReactCanvasConfetti refConfetti={getInstance} style={canvasStyles} />
95
+ </>
96
+ );
97
+ }
98
+
99
+ Confetti.defaultProps = {
100
+ duration: 3000
101
+ };
102
+
103
+ Confetti.propTypes = {
104
+ trigger: PropTypes.bool.isRequired,
105
+ duration: PropTypes.number
106
+ };
@@ -0,0 +1,138 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import {
4
+ ESUWrapper,
5
+ TopCopyWrapper,
6
+ FormInner,
7
+ PrivacyCopyWrapper,
8
+ InputField,
9
+ ButtonWrapper,
10
+ Title,
11
+ NameWrapper
12
+ } from './EmailSignUp.style';
13
+ import ButtonWithStates from '../../Atoms/ButtonWithStates/ButtonWithStates';
14
+
15
+ import Text from '../../Atoms/Text/Text';
16
+ import { buildEsuValidationSchema, ESU_FIELDS } from './_EmailSignUpConfig';
17
+ import ErrorText from '../../Atoms/ErrorText/ErrorText';
18
+ import Confetti from './_Confetti';
19
+
20
+ const EmailSignUp = ({
21
+ title,
22
+ topCopy,
23
+ successCopy,
24
+ privacyCopy,
25
+ backgroundColour,
26
+ buttonColour,
27
+ formContext,
28
+ columnLayout,
29
+ ...rest
30
+ }) => {
31
+ const {
32
+ formState: {
33
+ isValid,
34
+ isSubmitting,
35
+ isSubmitted,
36
+ isSubmitSuccessful,
37
+ errors
38
+ }
39
+ } = formContext;
40
+
41
+ return (
42
+ <ESUWrapper backgroundColour={backgroundColour} {...rest}>
43
+ <Title tag="h2" size="xxl" weight="400" family="Anton" uppercase>
44
+ {title}
45
+ </Title>
46
+ {!isSubmitted ? (
47
+ <TopCopyWrapper>
48
+ <Text>{topCopy}</Text>
49
+ </TopCopyWrapper>
50
+ ) : (
51
+ isSubmitSuccessful && (
52
+ <>
53
+ <Confetti trigger={isSubmitSuccessful} />
54
+ <TopCopyWrapper>
55
+ <Text>{successCopy}</Text>
56
+ </TopCopyWrapper>
57
+ </>
58
+ )
59
+ )}
60
+ {!isSubmitSuccessful && (
61
+ <FormInner>
62
+ <NameWrapper columnLayout={columnLayout}>
63
+ <InputField
64
+ fieldName={ESU_FIELDS.FIRST_NAME}
65
+ id="first-name"
66
+ type="text"
67
+ label="First Name"
68
+ placeholder="Enter your first name"
69
+ formContext={formContext}
70
+ />
71
+ <InputField
72
+ fieldName={ESU_FIELDS.LAST_NAME}
73
+ id="last-name"
74
+ type="text"
75
+ label="Last Name"
76
+ placeholder="Enter your last name"
77
+ formContext={formContext}
78
+ />
79
+ </NameWrapper>
80
+ <InputField
81
+ fieldName={ESU_FIELDS.EMAIL}
82
+ id="email"
83
+ type="email"
84
+ label="Email Address"
85
+ placeholder="example@youremail.com"
86
+ formContext={formContext}
87
+ />
88
+ <ButtonWrapper buttonColour={buttonColour}>
89
+ <ButtonWithStates
90
+ type="submit"
91
+ disabled={!isValid || isSubmitting}
92
+ loading={isSubmitting}
93
+ loadingText="Submitting..."
94
+ data-test="subscribe-button"
95
+ >
96
+ <Text>Subscribe</Text>
97
+ </ButtonWithStates>
98
+ </ButtonWrapper>
99
+ </FormInner>
100
+ )}
101
+ {isSubmitted && !isSubmitSuccessful && (
102
+ <>
103
+ {/*
104
+ Field errors will prevent submission,
105
+ so theoretically this should just be a single error set in the submission callback
106
+ with with RHF's `setError` method, but will neatly display multiple errors.
107
+ */}
108
+ {Object.values(errors).map(error => (
109
+ <ErrorText>{error.message}</ErrorText>
110
+ ))}
111
+ </>
112
+ )}
113
+
114
+ <PrivacyCopyWrapper>
115
+ <Text>{privacyCopy}</Text>
116
+ </PrivacyCopyWrapper>
117
+ </ESUWrapper>
118
+ );
119
+ };
120
+
121
+ EmailSignUp.propTypes = {
122
+ title: PropTypes.string.isRequired,
123
+ topCopy: PropTypes.node.isRequired,
124
+ successCopy: PropTypes.node.isRequired,
125
+ privacyCopy: PropTypes.node.isRequired,
126
+ backgroundColour: PropTypes.string,
127
+ buttonColour: PropTypes.string,
128
+ formContext: PropTypes.shape().isRequired,
129
+ columnLayout: PropTypes.bool
130
+ };
131
+
132
+ EmailSignUp.defaultProps = {
133
+ backgroundColour: 'deep_violet_dark',
134
+ buttonColour: 'red',
135
+ columnLayout: false
136
+ };
137
+
138
+ export { EmailSignUp, buildEsuValidationSchema, ESU_FIELDS };