Skip to content

Commit 9b61c13

Browse files
committed
Add reCaptcha to the feedback module
1 parent 63994b4 commit 9b61c13

File tree

9 files changed

+144
-60
lines changed

9 files changed

+144
-60
lines changed

wp-content/mu-plugins/config/integrations.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
path: config/integrations/body/google-tag-manager.html
6969

7070
- handle: google-recaptcha
71-
path: https://www.google.com/recaptcha/api.js?onload=screenerCallback&render=explicit
71+
path: https://www.google.com/recaptcha/api.js?onload=reCaptchaCallback&render=explicit
7272
in_footer: false
7373
attrs:
7474
async: true

wp-content/mu-plugins/feedback.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ function feedbackHandler() {
2020
$client = get_airtable_client();
2121
$feedback_fields = get_values_from_submission($_POST);
2222
$airtable_record = create_record($feedback_fields, $client);
23+
24+
wp_send_json([
25+
'success' => true,
26+
'error' => 200,
27+
'message' => __('Thank you for your feedback.'),
28+
'retry' => false
29+
]);
2330
} catch (Exception $e) {
2431
$message = $e->getMessage();
2532

@@ -62,7 +69,7 @@ function create_record($args, $client) {
6269
$client_response = (array) $new_record;
6370

6471
foreach ($client_response as $key => $value) {
65-
if (array_key_exists('error', $value)) {
72+
if (isset($value->error)) {
6673
failure(400, "{$value->error->message}");
6774
}
6875
}

wp-content/themes/access/assets/js/screener.9da3645f.js renamed to wp-content/themes/access/assets/js/screener.8721628f.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wp-content/themes/access/assets/js/single-programs.32dc53a6.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wp-content/themes/access/assets/js/single-programs.548d0fd9.js

Lines changed: 0 additions & 2 deletions
This file was deleted.

wp-content/themes/access/single-programs.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
enqueue_inline('google-optimize');
2626
enqueue_inline('google-analytics');
2727
enqueue_inline('google-tag-manager');
28+
enqueue_inline('google-recaptcha');
2829

2930
// Main
3031
// TODO: Evaluate coverage of individual polyfills and load per browser

wp-content/themes/access/src/js/modules/feedback.js

Lines changed: 88 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,105 @@ import Spinner from '@nycopportunity/pttrn-scripts/src/spinner/spinner';
77

88
(() => {
99
'use strict';
10+
1011
/**
11-
* Pass the DOM element to the form.
12+
* Instantiate Form and Modal modules
1213
*/
13-
const form = document.getElementById('feedback-form');
14-
const Form = new Forms(form);
14+
15+
const Form = new Forms(document.getElementById('feedback-form'));
1516

1617
new Modal();
1718

1819
Form.selectors.ERROR_MESSAGE_PARENT = '.c-question__container';
1920

20-
/**
21-
* This function automatically watches inputs within the form and displays
22-
* error messages on the blur event for each input.
23-
*/
24-
Form.watch();
21+
Form.watch(); // Automatically watch for input errors on blur
22+
23+
window.reCaptchaCallback = () => { };
2524

2625
/**
27-
* The submit function for the form.
26+
* The form submission handler
27+
*
28+
* @param {Object} event Form submission event
2829
*/
29-
Form.submit = (event) => {
30+
Form.submit = (event) => {
3031
event.preventDefault();
3132

33+
recaptcha();
34+
};
35+
36+
/**
37+
* Add loading spinner to the DOM
38+
*/
39+
const loading = () => {
3240
let container = document.getElementById('modal-body');
3341
let spinner = new Spinner();
3442
let loading = document.createElement('div');
3543

44+
Form.FORM.classList.add('hidden');
45+
Form.FORM.setAttribute('aria-hidden', 'true');
46+
3647
loading.classList.add('flex', 'justify-center', 'items-center', 'text-yellow-access');
37-
loading.id = 'spinner-container';
48+
loading.id = 'feedback-spinner';
3849
loading.appendChild(spinner);
39-
form.classList.add('hidden');
40-
form.setAttribute('aria-hidden', 'true');
50+
4151
container.appendChild(loading);
52+
};
53+
54+
/**
55+
* Add reCaptcha
56+
*/
57+
const recaptcha = () => {
58+
let questions = Form.FORM.querySelector('[data-js*="questions"]');
59+
let questionRecaptcha = Form.FORM.querySelector('[data-js*="question-recaptcha"]');
60+
61+
questions.classList.add('hidden');
62+
questions.setAttribute('aria-hidden', 'true');
63+
64+
questionRecaptcha.classList.remove('hidden');
65+
questionRecaptcha.setAttribute('aria-hidden', 'false');
66+
67+
window.grecaptcha.render(Form.FORM.querySelector('[data-js="recaptcha"]'), {
68+
'sitekey': '6Lf0tTgUAAAAACnS4fRKqbLll_oFxFzeaVfbQxyX',
69+
'callback': () => {
70+
loading();
71+
submit();
72+
},
73+
'error-callback': () => {
74+
failure();
75+
}
76+
});
77+
};
4278

79+
/**
80+
* Hide the Spinner and show the success message
81+
*/
82+
const success = () => {
83+
let alert = document.querySelector('[data-js="feedback-alert"]');
84+
let spinnerEl = document.getElementById('feedback-spinner');
85+
86+
spinnerEl.classList.add('hidden');
87+
88+
alert.classList.remove('hidden');
89+
alert.setAttribute('aria-hidden', 'false');
90+
};
91+
92+
/**
93+
* Hide the spinner and show the failure message
94+
*/
95+
const failure = () => {
96+
let alert = document.querySelector('[data-js="feedback-alert-error"]');
97+
let spinnerEl = document.getElementById('feedback-spinner');
98+
99+
spinnerEl.classList.add('hidden');
100+
101+
alert.classList.remove('hidden');
102+
alert.setAttribute('aria-hidden', 'false');
103+
};
104+
105+
/**
106+
* Use fetch to submit the request
107+
*/
108+
const submit = () => {
43109
// To send the data with the application/x-www-form-urlencoded header
44110
// we need to use URLSearchParams(); instead of FormData(); which uses
45111
// multipart/form-data
@@ -58,21 +124,19 @@ import Spinner from '@nycopportunity/pttrn-scripts/src/spinner/spinner';
58124
fetch(Form.FORM.getAttribute('action'), {
59125
method: Form.FORM.getAttribute('method'),
60126
body: formData
61-
}).then(response => {
62-
let alert = document.querySelector('[data-alert-name="feedback"]');
63-
let spinnerEl = document.getElementById('spinner-container');
64-
65-
spinnerEl.classList.add('hidden');
66-
alert.classList.remove('hidden');
67-
alert.setAttribute('aria-hidden', 'false');
127+
})
128+
.then(response => response.json())
129+
.then(response => {
130+
if (response.success) {
131+
success();
132+
} else {
133+
failure();
134+
}
68135

69136
if (process.env.NODE_ENV === 'development')
70137
console.dir(response); // eslint-disable-line no-console
71138
}).catch(error => {
72-
let errorAlert = document.querySelector('[data-alert-name="feedback-error"]');
73-
74-
errorAlert.classList.remove('hidden');
75-
errorAlert.setAttribute('aria-hidden', 'false');
139+
failure();
76140

77141
if (process.env.NODE_ENV === 'development')
78142
console.error('There has been a problem with your fetch operation:', error); // eslint-disable-line no-console

wp-content/themes/access/src/js/modules/screener.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ class Screener {
215215
this._initRecaptcha();
216216
viewCount = 0;
217217
} else {
218-
window.screenerCallback = () => {};
218+
window.reCaptchaCallback = () => {};
219219
}
220220

221221
// `2/1440` sets the cookie to expire after two minutes.
@@ -318,7 +318,7 @@ class Screener {
318318
* @return {this} Screener
319319
*/
320320
_initRecaptcha() {
321-
window.screenerCallback = () => {
321+
window.reCaptchaCallback = () => {
322322
window.grecaptcha.render(document.getElementById('screener-recaptcha'), {
323323
'sitekey': Utility.CONFIG.GRECAPTCHA_SITE_KEY,
324324
'callback': () => {

wp-content/themes/access/views/components/feedback.twig

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div aria-describedby="aria-db-modal-body" aria-hidden="true" aria-labelledby="aria-lb-modal-header" aria-modal="true" class="o-modal hidden" id="aria-c-modal" role="dialog" style="background-color: rgba(0,0,0,0.25)">
22
<div class="o-modal__layout">
3-
<div id="modal-body" class="o-modal__body color-light-background animated fadeInUp p-4">
3+
<div id="modal-body" class="o-modal__body color-light-background animated fadeInUp p-4" style="max-width: 100%; width: 376px">
44
<div class="flex justify-end">
55
<button aria-controls="aria-c-modal" aria-expanded="false" data-dialog="close" data-js="dialog" class="btn btn-text text-small flex items-center" tabindex="-1">
66
<svg class="icon-ui icon-3 m-0 text-blue-dark" tabindex="-1">
@@ -13,7 +13,7 @@
1313

1414
<div>
1515
<div class="pt-2">
16-
<article class="c-alert-box color-success-status animated hidden:fadeInUp active print:hidden hidden" data-alert-name="feedback" data-js="alert-banner" id="aria-c-alert-banner" role="dialog" aria-hidden="true">
16+
<article class="c-alert-box color-success-status animated hidden:fadeInUp active print:hidden hidden" data-js="feedback-alert" id="aria-c-alert-banner" role="dialog" aria-hidden="true">
1717
<div class="c-alert-box__icon">
1818
<svg class="c-alert-box__svg icon icon-4 icon-success" aria-hidden="true" tabindex="-1">
1919
<use xlink:href="#icon-success"></use>
@@ -27,48 +27,60 @@
2727
</div>
2828
</article>
2929

30+
<article class="text-red hidden" data-js="feedback-alert-error" aria-hidden="true">
31+
<p class="m-0">{{ __('Something went wrong. Please try again later.', 'accessnyctheme') }}</p>
32+
</article>
33+
3034
<form id="feedback-form" action="{{ action }}" method="post" data-js="feedback-form">
3135
<input name="action" type="hidden" value="feedback">
3236
<input name="program" type="hidden" value="{{ this.program_name}}">
3337
{{ fn('wp_nonce_field', 'feedback', 'feedback-nonce', true, false) }}
3438

35-
<fieldset class="c-question" tabindex="-1">
36-
<legend class="c-question__label" tabindex="-1">{{ __('Was this page helpful?', 'accessnyctheme') }}</legend>
39+
<div data-js="questions">
40+
<fieldset class="c-question" tabindex="-1">
41+
<legend class="c-question__label" tabindex="-1">{{ __('Was this page helpful?', 'accessnyctheme') }}</legend>
3742

38-
<div class="c-question__container">
39-
<div>
40-
<label class="toggle" tabindex="-1">
41-
<input data-type="boolean" name="helpful" type="radio" value="Yes" tabindex="-1" />
42-
<span class="toggle__label">{{ __('Yes', 'accessnyctheme') }}</span>
43-
</label>
43+
<div class="c-question__container">
44+
<div>
45+
<label class="toggle" tabindex="-1">
46+
<input data-type="boolean" name="helpful" type="radio" value="Yes" tabindex="-1" />
47+
<span class="toggle__label">{{ __('Yes', 'accessnyctheme') }}</span>
48+
</label>
4449

45-
<label class="toggle" tabindex="-1">
46-
<input checked="true" data-type="boolean" name="helpful" type="radio" value="No" tabindex="-1" />
47-
<span class="toggle__label">{{ __('No', 'accessnyctheme') }}</span>
48-
</label>
50+
<label class="toggle" tabindex="-1">
51+
<input checked="true" data-type="boolean" name="helpful" type="radio" value="No" tabindex="-1" />
52+
<span class="toggle__label">{{ __('No', 'accessnyctheme') }}</span>
53+
</label>
54+
</div>
4955
</div>
56+
</fieldset>
57+
58+
<fieldset class="c-question" tabindex="-1">
59+
<label class="c-question__label" for="description" tabindex="-1">{{ __('Why or why not?', 'accessnyctheme') }}</label>
60+
61+
<div class="c-question__container">
62+
<div>
63+
<input name="description" required="true" type="text" class="w-full" maxlength="5000" tabindex="-1"/>
64+
</div>
65+
</div>
66+
</fieldset>
67+
68+
<div class="flex justify-between">
69+
<button class="btn-small screen-tablet:btn" type="reset" aria-controls="aria-c-modal" aria-expanded="false" data-js="dialog" tabindex="-1">{{ __('Cancel', 'accessnyctheme') }}</button>
70+
71+
<button class="btn-small screen-tablet:btn btn-primary" type="submit" value="Submit" tabindex="-1">{{ __('Submit', 'accessnyctheme') }}</button>
5072
</div>
51-
</fieldset>
73+
</div>
5274

53-
<fieldset class="c-question" tabindex="-1">
54-
<label class="c-question__label" for="description" tabindex="-1">{{ __('Why or why not?', 'accessnyctheme') }}</label>
75+
<fieldset class="c-question my-3 hidden" tabindex="-1" data-js="question-recaptcha" aria-hidden="true">
76+
<label class="c-question__label" for="description" tabindex="-1">{{ __('Please check "I\'m not a robot"', 'accessnyctheme') }}</label>
5577

5678
<div class="c-question__container">
5779
<div>
58-
<input name="description" required="true" type="text" maxlength="5000" tabindex="-1"/>
80+
<div data-js="recaptcha" class="flex justify-center"></div>
5981
</div>
6082
</div>
6183
</fieldset>
62-
63-
<div class="flex justify-between">
64-
<button class="btn-small screen-tablet:btn" type="reset" aria-controls="aria-c-modal" aria-expanded="false" data-js="dialog" tabindex="-1">{{ __('Cancel', 'accessnyctheme') }}</button>
65-
66-
<button class="btn-small screen-tablet:btn btn-primary" type="submit" value="Submit" tabindex="-1">{{ __('Submit', 'accessnyctheme') }}</button>
67-
</div>
68-
69-
<article class="hidden" data-alert-name="feedback-error" aria-hidden="true">
70-
<p>{{ __('Something went wrong. Please try again later.', 'accessnyctheme') }}</p>
71-
</article>
7284
</form>
7385
</div>
7486
</div>

0 commit comments

Comments
 (0)