diff --git a/public/index.html b/public/index.html index 7419aad3eeb3e2eb2f8664c18d220827581aab39..0032925cef3d0d2cfcfc7f828085a328898242f0 100644 --- a/public/index.html +++ b/public/index.html @@ -3,9 +3,9 @@ <head> <meta charset="utf-8" /> <link rel="icon" href="/favicon.ico" /> + <link rel="stylesheet" href="/style-copied-from-website.css" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> - <meta name="description" content="Web site created using create-snowpack-app" /> - <title>Snowpack App</title> + <title>Helpful contact form | Development version</title> </head> <body> <div style="border: 2pt solid red; font-size: 200%; padding: 5pt;">Warning: this is a development version of @@ -14,28 +14,6 @@ for actual support.</div> <div lang="en" id="helpful-contact-form" style="width: 40rem; margin:10rem auto 0 auto"></div> <noscript>You need to enable JavaScript to run this app.</noscript> - <script type="module" src="/dist/index.js"></script> - <script> - if ('%MODE%' === 'development') { - // we wrap this in a function to prevent the variables from leaking out - (function () { - console.log('[dev] injecting greenhost stylesheet..') - var greenhostCss = document.createElement('link'); - greenhostCss.setAttribute('rel', 'stylesheet'); - greenhostCss.setAttribute('href', '/style-copied-from-website.css'); - document.head.appendChild(greenhostCss); - }()) - } - </script> - <!-- - This HTML file is a template. - If you open it directly in the browser, you will see an empty page. - - You can add webfonts, meta tags, or analytics to this file. - The build step will place the bundled scripts into the <body> tag. - - To begin the development, run `npm start` or `yarn start`. - To create a production bundle, use `npm run build` or `yarn build`. - --> + <script type="module" src="/bundle.js"></script> </body> </html> diff --git a/src/App.tsx b/src/App.tsx index 696c6c4220b5b97631dc53c429b1a6677f74c976..ca926afdcc82eb1c651158df413083cd30d90615 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -105,14 +105,7 @@ export const App: React.FC = () => { <h1> <Markdown>{t('contact.title')}</Markdown> </h1> - <ContactForm {...{ problem, problemGroup }} /> - <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> - <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> - <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> - <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> - <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> - <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> - + <ContactForm problem={problem} problemGroup={problemGroup} formAction={config.formAction} /> </Suspense> ); } diff --git a/src/components/contact-form/ContactForm.test.tsx b/src/components/contact-form/ContactForm.test.tsx index 0a3379e3a1bc1f63cd257b135708382baa8a0c9b..fc41797c163279ab5ee5b1bc8f1dca40ca456544 100644 --- a/src/components/contact-form/ContactForm.test.tsx +++ b/src/components/contact-form/ContactForm.test.tsx @@ -41,7 +41,7 @@ describe('<ContactForm problemGroup="%s"}>', () => { 'renders the contact form with problemGroup: %s', (problemGroup) => { const { getByRole, queryByRole, getByText } = render( - <ContactForm {...{ problemGroup, problem }} />, + <ContactForm formAction='' problem={problem} problemGroup={problemGroup} />, ); const contactDetailFieldset = getByRole('group', { name: /Contact details/i, @@ -73,7 +73,7 @@ describe('<ContactForm problemGroup="%s"}>', () => { ])('confirmation modal closes on %s %s', (action, value) => { const problemGroup = 'technical'; const { getByRole } = render( - <ContactForm {...{ problemGroup, problem }} />, + <ContactForm formAction='' problem={problem} problemGroup={problemGroup} />, ); const regularSubmit = getByRole('button', { name: /submit request/i }); @@ -94,21 +94,10 @@ describe('<ContactForm problemGroup="%s"}>', () => { expect(modal).not.toBeInTheDocument(); }); - it('can be submitted by a form submit event', ()=>{ - const problemGroup = 'technical'; - const { getByRole } = render( - <ContactForm {...{ problemGroup, problem }} />, - ); - const form = getByRole("form", {name:"Contact form"}); - fireEvent.submit(form); - const modal = getByRole('dialog', { name: /thank you/i }); - expect(modal).toBeInTheDocument(); - }); - it('has an option to make the request urgent', () => { const problemGroup = 'technical'; const { getByText, getByRole } = render( - <ContactForm {...{ problemGroup, problem }} />, + <ContactForm formAction='' problem={problem} problemGroup={problemGroup} />, ); const urgencyCheckbox = getByRole('checkbox', { name: /urgent request/i, diff --git a/src/components/contact-form/ContactForm.tsx b/src/components/contact-form/ContactForm.tsx index c1697891b58bf3f78faa2a6e8dc17ad0292192f5..b0b94d5c6dcd4f05f0a2cfad37b623f9882c0228 100644 --- a/src/components/contact-form/ContactForm.tsx +++ b/src/components/contact-form/ContactForm.tsx @@ -1,7 +1,6 @@ -import React, { ChangeEvent, FormEvent, useState } from 'react'; +import React, { ChangeEvent, MouseEventHandler, useState } from 'react'; import { useTranslation } from 'react-i18next'; import UrgentRequest from '../urgent-request/UrgentRequest'; -import { CSSTransition, SwitchTransition } from 'react-transition-group'; import styles from './ContactForm.module.scss'; import panelTransition from '../../styles/transitions/panel.module.scss'; import Fieldset from '../fieldset/Fieldset'; @@ -9,10 +8,12 @@ import Input from '../input/Input'; import Modal from '../modal/Modal'; export interface ContactFormProps { + formAction: string; problemGroup: string; problem: string; } export const ContactForm: React.FC<ContactFormProps> = ({ + formAction, problemGroup, problem, }: ContactFormProps) => { @@ -31,54 +32,47 @@ export const ContactForm: React.FC<ContactFormProps> = ({ setUrgentIntent(event.target.checked); }; - const submit = (event: FormEvent<HTMLFormElement>) => { - event.preventDefault(); - sendRequest(); + const submit: MouseEventHandler<HTMLButtonElement> = (event) => { + if (process.env.NODE_ENV !== 'production') { + event.preventDefault(); + console.log('Not submitting if not in production mode.'); + const formData = { ...state, ...{ urgent: urgentIntent ? 'yes' : 'no' } }; + setModalContent('form contents: ' + JSON.stringify(formData)); + } }; - const sendRequest = (urgent = false) => { - const formData = { ...state, ...{ urgent: urgent ? 'yes' : 'no' } }; - - Object.fromEntries( - Object.entries(formData).map(([name, value]) => [ - `SupportForm[${name}]`, - value, - ]), - ); - setModalContent(JSON.stringify(formData)); - }; - - // For use in the CSSTransition, see - // https://github.com/reactjs/react-transition-group/issues/668#issuecomment-695162879 - const submitRef = React.useRef<any>(null); - return ( - <form onSubmit={submit} name="HelpfulContactForm" className={`${styles.ContactForm} cosmosForm`} aria-label={t("Contact form")}> + <form + action={formAction} + name="HelpfulContactForm" + className={`${styles.ContactForm} cosmosForm`} + aria-label={t("Contact form")} + > <Fieldset legend={t('contact.details')}> <Input label={t('contact.name')} id="name" - name="name" + name="HelpfulContactForm[name]" required onChange={changeHandler} /> <Input label={t('contact.phone')} id="phone" - name="phone" + name="HelpfulContactForm[phone]" onChange={changeHandler} /> <Input label={t('contact.identification')} id="identification" - name="identification" + name="HelpfulContactForm[identification]" onChange={changeHandler} required /> <Input label={t('contact.email')} id="email" - name="email" + name="HelpfulContactForm[email]" onChange={changeHandler} required /> @@ -87,7 +81,7 @@ export const ContactForm: React.FC<ContactFormProps> = ({ <Input label={t('Message')} id="message" - name="message" + name="HelpfulContactForm[message]" onChange={changeHandler} type="textarea" required @@ -105,36 +99,25 @@ export const ContactForm: React.FC<ContactFormProps> = ({ </div> )} <div className="field"> - <SwitchTransition> - <CSSTransition - key={urgentIntent ? 'urgent' : 'normal'} - in={urgentIntent} - classNames={{ ...panelTransition }} - nodeRef={submitRef} - unmountOnExit={true} - mountOnEnter={true} - addEndListener={(done: () => void) => { - // use the css transitionend event to mark the finish of a transition - submitRef.current?.addEventListener('transitionend', done, false); - }} - > - <div ref={submitRef}> - {urgentIntent ? ( - <UrgentRequest sendUrgent={() => sendRequest(true)} /> - ) : ( - <button - className="good" - onClick={(event) => { - event.preventDefault(); - sendRequest(); - }} - > - {t('submit')} - </button> - )} - </div> - </CSSTransition> - </SwitchTransition> + <div> + <input + name="HelpfulContactForm[urgency]" + value={urgentIntent ? 'urgent' : 'normal'} + type="hidden" + /> + {urgentIntent && ( + <UrgentRequest submit={submit}/> + )} + {!urgentIntent && ( + <button + className="good" + type="submit" + onClick={submit} + > + {t('submit')} + </button> + )} + </div> </div> {modalContent && ( <Modal diff --git a/src/components/urgent-request/UrgentRequest.test.tsx b/src/components/urgent-request/UrgentRequest.test.tsx index 456f4aae43fe4fa140cacd6f2c151602a278e370..4dc8f3c07542f25c258a0f51c8aa610e8b059bf9 100644 --- a/src/components/urgent-request/UrgentRequest.test.tsx +++ b/src/components/urgent-request/UrgentRequest.test.tsx @@ -3,13 +3,13 @@ import { render } from '@testing-library/react'; import UrgentRequest from './UrgentRequest'; import userEvent from '@testing-library/user-event'; -const mockSendUrgent = jest.fn(); +const mockSubmit: React.MouseEventHandler<HTMLButtonElement> = jest.fn().mockImplementation((e) => e.preventDefault()); describe('<UrgentRequest>', () => { it('renders the option to make a contact request urgent', () => { const { getByRole } = render( <UrgentRequest - sendUrgent={mockSendUrgent} + submit={mockSubmit} />, ); const urgentFieldset = getByRole('group'); @@ -38,6 +38,6 @@ describe('<UrgentRequest>', () => { // Can submit now.. userEvent.click(urgentSubmit); // Triggers a message sent up the hierarchy.. - expect(mockSendUrgent).toBeCalled(); + expect(mockSubmit).toBeCalled(); }); }); diff --git a/src/components/urgent-request/UrgentRequest.tsx b/src/components/urgent-request/UrgentRequest.tsx index aca85881014483056bb1259e6f53b351ea8fc8a6..247b3789990ef19aaa58166a20abd8bf46476e3b 100644 --- a/src/components/urgent-request/UrgentRequest.tsx +++ b/src/components/urgent-request/UrgentRequest.tsx @@ -1,14 +1,14 @@ import Markdown from 'markdown-to-jsx'; -import React, { ChangeEvent, useEffect, useState } from 'react'; +import React, { ChangeEvent, MouseEventHandler, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import styles from './UrgentRequest.module.scss'; export interface UrgentRequestProps { - sendUrgent: CallableFunction; + submit: MouseEventHandler<HTMLButtonElement>; } export const UrgentRequest: React.FC<UrgentRequestProps> = ({ - sendUrgent, + submit, }: UrgentRequestProps) => { const [urgent, setUrgent] = useState<boolean>(false); const [urgentText, setUrgentText] = useState<string>(''); @@ -36,10 +36,8 @@ export const UrgentRequest: React.FC<UrgentRequestProps> = ({ <button disabled={!urgent} className="bad" - onClick={(event) => { - event?.preventDefault(); - sendUrgent(); - }} + type="submit" + onClick={submit} > {t('urgency.submit')} </button> diff --git a/src/config.yml b/src/config.yml index f4a2403625aaf0c236c82f3d241ee4216b22ecc4..079e5d426fb8f127d99f6aec669f6ddfbd084f50 100644 --- a/src/config.yml +++ b/src/config.yml @@ -1,5 +1,7 @@ # This determines where to mount the "app" in the DOM. mountingElementId: 'helpful-contact-form' +formAction: 'https://service.greenhost.net/site/helpfulContact' +#formAction: 'http://cosmos2.local/site/helpfulContact' urgentRequestFee: 420 localisations: en: diff --git a/webpack.config.js b/webpack.config.js index 9ec38eef9a6f3c764cb8195b1a026da1a9361a53..0d503d25c37be9d2e422f4e193d0f955584f1ffb 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,6 +2,15 @@ const CopyPlugin = require("copy-webpack-plugin"); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const path = require('path'); +staticFiles = [ + { from: "public/style-copied-from-website.css" }, +]; +if (process.env.NODE_ENV === 'development') { + staticFiles.append( + { from: "public/index.html" } + ); +} + module.exports = { entry: './src/index.tsx', plugins: [ @@ -11,14 +20,15 @@ module.exports = { ignoreOrder: false, // Enable to remove warnings about conflicting order }), new CopyPlugin({ - patterns: [ - { from: "public/style-copied-from-website.css" }, - ], + patterns: staticFiles, options: { concurrency: 100, }, }), ], + devServer: { + contentBase: path.join(__dirname, 'public'), + }, module: { rules: [ {