UNPKG

6.6 kBJavaScriptView Raw
1import React, { useState, useEffect } from 'react';
2import PropTypes from 'prop-types';
3import {
4 Button,
5 ModalBody,
6 ModalHeader,
7 ModalFooter,
8 FormGroup,
9} from 'reactstrap';
10import { avLogMessagesApi, avRegionsApi } from '@availity/api-axios';
11import { Form, Field } from '@availity/form';
12import { SelectField } from '@availity/select';
13import * as yup from 'yup';
14import SmileField from './SmileField';
15
16yup.addMethod(yup.string, 'isRequired', function format(isRequired, msg) {
17 return this.test({
18 name: 'dateRange',
19 exclusive: true,
20 message: msg || 'This field is required.',
21 test(value) {
22 if (isRequired) {
23 return value !== undefined;
24 }
25 return true;
26 },
27 });
28});
29
30const FeedbackForm = ({
31 name,
32 onClose,
33 faceOptions,
34 aboutOptions,
35 onFeedbackSent,
36 prompt,
37 additionalComments,
38 staticFields,
39 analytics,
40 modalHeaderProps,
41 ...formProps
42}) => {
43 const [active, setActive] = useState(null);
44 const [sent, setSent] = useState(null);
45
46 const sendFeedback = async ({ smileField, ...values }) => {
47 const response = await avRegionsApi.getCurrentRegion();
48
49 await analytics.info({
50 surveyId: `${name.replace(/\s/g, '_')}_Smile_Survey`,
51 smileLocation: `${name}`,
52 smile: `icon-${smileField.icon}`,
53 url: window.location.href,
54 region: response.data.regions[0] && response.data.regions[0].id,
55 userAgent: window.navigator.userAgent,
56 submitTime: new Date(),
57 ...values, // Spread the form values onto the logger
58 ...staticFields, // Spread the static key value pairs onto the logger
59 });
60
61 setSent(values);
62 };
63
64 // Close the Modal once sent after 2 seconds
65 useEffect(() => {
66 if (sent) {
67 setTimeout(() => {
68 if (onClose) {
69 onClose(); // Mostly for Screen Reader use but a nice to have for all
70 }
71 if (onFeedbackSent) {
72 Object.keys(sent).forEach(
73 key => sent[key] === undefined && delete sent[key]
74 );
75
76 onFeedbackSent({
77 active: active.icon,
78 ...sent,
79 });
80 }
81 }, 2000);
82 }
83 // eslint-disable-next-line react-hooks/exhaustive-deps
84 }, [sent]);
85
86 return sent ? (
87 <ModalHeader
88 aria-live="assertive"
89 tabIndex="0"
90 className="d-flex justify-content-center"
91 {...modalHeaderProps}
92 >
93 Thank you for your feedback.
94 </ModalHeader>
95 ) : (
96 <>
97 <ModalHeader
98 aria-live="assertive"
99 id="feedback-form-header"
100 {...modalHeaderProps}
101 >
102 {prompt || `Tell us what you think about ${name}`}
103 </ModalHeader>
104 <Form
105 aria-label="Feedback Form"
106 aria-describedby="feedback-form-header"
107 role="form"
108 data-testid="feedback-form"
109 initialValues={{
110 'face-options': undefined,
111 additionalFeedback: undefined,
112 feedback: undefined,
113 feedbackApp: undefined,
114 smileField: undefined,
115 }}
116 validationSchema={yup.object().shape({
117 feedback: yup
118 .string()
119 .max(200, 'Additional Feedback cannot exceed 200 characters.')
120 .required('This field is required.'),
121 additionalFeedback: yup
122 .string()
123 .max(200, 'Additional Feedback cannot exceed 200 characters.'),
124 smileField: yup
125 .object()
126 .shape({
127 icon: yup.string().required(),
128 description: yup.string(),
129 placeholder: yup.string(),
130 })
131 .required('This field is required.'),
132 feedbackApp: yup
133 .string()
134 .isRequired(aboutOptions.length > 0, 'This field is required.'),
135 })}
136 {...formProps}
137 onSubmit={values => sendFeedback(values)}
138 >
139 <ModalBody>
140 <FormGroup
141 size="lg"
142 id="face-options"
143 data-testid="face-options"
144 className="d-flex flex-row justify-content-between"
145 >
146 <SmileField
147 options={faceOptions}
148 name="smileField"
149 onChange={option => setActive(option)}
150 />
151 </FormGroup>
152 {active ? (
153 <>
154 {aboutOptions.length > 0 && (
155 <SelectField
156 name="feedbackApp"
157 id="about-options"
158 data-testid="about-options"
159 placeholder="This is about..."
160 options={aboutOptions}
161 />
162 )}
163 <Field
164 type="textarea"
165 name="feedback"
166 placeholder={
167 (active && active.placeholder) ||
168 'Feedback? Requests? Defects?'
169 }
170 style={{ resize: 'none' }}
171 rows="2"
172 />
173 {additionalComments && (
174 <Field
175 type="textarea"
176 name="additionalFeedback"
177 placeholder="Additional Comments... (Optional)"
178 style={{ resize: 'none' }}
179 rows="2"
180 />
181 )}
182 </>
183 ) : null}
184 </ModalBody>
185
186 <ModalFooter>
187 {onClose ? (
188 <Button
189 onClick={onClose}
190 color="secondary"
191 onKeyDown={({ keyCode }) => keyCode === 13 && onClose()}
192 >
193 Close
194 </Button>
195 ) : null}
196 <Button type="submit" color="primary" disabled={!active}>
197 Send Feedback
198 </Button>
199 </ModalFooter>
200 </Form>
201 </>
202 );
203};
204
205FeedbackForm.propTypes = {
206 name: PropTypes.string.isRequired,
207 onFeedbackSent: PropTypes.func,
208 faceOptions: PropTypes.arrayOf(
209 PropTypes.shape({
210 icon: PropTypes.string,
211 description: PropTypes.string,
212 placeholder: PropTypes.string,
213 })
214 ),
215 aboutOptions: PropTypes.arrayOf(
216 PropTypes.shape({
217 name: PropTypes.string,
218 value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
219 })
220 ),
221 onClose: PropTypes.func,
222 prompt: PropTypes.string,
223 additionalComments: PropTypes.bool,
224 staticFields: PropTypes.object,
225 modalHeaderProps: PropTypes.shape({ ...ModalHeader.propTypes }),
226 analytics: PropTypes.shape({
227 info: PropTypes.func.isRequired,
228 }),
229};
230
231FeedbackForm.defaultProps = {
232 aboutOptions: [],
233 additionalComments: false,
234 modalHeaderProps: {},
235 analytics: avLogMessagesApi,
236};
237
238export default FeedbackForm;