avatarJennifer Fu

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

14163

Abstract

ame">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>></span> <span class="hljs-tag"><<span class="hljs-name">header</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App-header"</span>></span> <span class="hljs-tag"><<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{logo}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App-logo"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"logo"</span> /></span> <span class="hljs-tag"><<span class="hljs-name">p</span>></span> Edit <span class="hljs-tag"><<span class="hljs-name">code</span>></span>src/App.js<span class="hljs-tag"></<span class="hljs-name">code</span>></span> and save to reload. <span class="hljs-tag"></<span class="hljs-name">p</span>></span> <span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App-link"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://reactjs.org"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span> ></span> Learn React <span class="hljs-tag"></<span class="hljs-name">a</span>></span> <span class="hljs-tag"></<span class="hljs-name">header</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">FloatButton</span> <span class="hljs-attr">icon</span>=<span class="hljs-string">{</span><<span class="hljs-attr">LikeOutlined</span> /></span>} description="Survey" onClick={() => setOpen(true)} style={{ width: 80, height: 80, bottom: 24, right: 24 }} /> <span class="hljs-tag"><<span class="hljs-name">Drawer</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Survey"</span> <span class="hljs-attr">placement</span>=<span class="hljs-string">"right"</span> <span class="hljs-attr">onClose</span>=<span class="hljs-string">{()</span> =></span> setOpen(false)} open={open} size="large" > <span class="hljs-tag"><<span class="hljs-name">iframe</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Survey"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform?embedded=true"</span> <span class="hljs-attr">frameborder</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">marginheight</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">marginwidth</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">width:</span> '<span class="hljs-attr">100</span>%', <span class="hljs-attr">height:</span> '<span class="hljs-attr">calc</span>(<span class="hljs-attr">100</span>% <span class="hljs-attr">-</span> <span class="hljs-attr">3px</span>)' }} ></span> Loading… <span class="hljs-tag"></<span class="hljs-name">iframe</span>></span> <span class="hljs-tag"></<span class="hljs-name">Drawer</span>></span> <span class="hljs-tag"></></span></span> ); }

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">App</span>;</pre></div><h1 id="1368">Build Customized Google Forms</h1><p id="524f">Embedding Google Forms as an iframe works. However, we may want to have a customized UI with styling and branding. Google Forms enables us to do it by two steps:</p><ul><li>Get pre-filled link.</li><li>Build a customized UI.</li></ul><h2 id="09b0">Get Pre-Filled Link</h2><p id="e59e">Go back to the form builder page. There is a list of available commands, upon clicking the more menu (also called the kebab menu, the three-dot menu, and the vertical three-dot menu).</p><p id="73f3">Select <code>Get pre-filled link</code>:</p><figure id="dacc"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*iRZ-W3N7l9ptgQhq4Kt9FQ.png"><figcaption>Image by author</figcaption></figure><p id="26af">It shows a survey UI to be pre-filled. It says that <code>Email</code> cannot be pre-filled, but do fill in answers for other questions.</p><figure id="5441"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*Fu0jMI-_8K6nh-XO3SGSlg.png"><figcaption>Image by author</figcaption></figure><p id="ef66">Click the button <code>Get link</code>, and the following is the saved link from the clip board:</p><div id="1225"><pre><span class="hljs-symbol">https:</span>/<span class="hljs-regexp">/docs.google.com/forms</span><span class="hljs-regexp">/d/e</span><span class="hljs-regexp">/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform</span>?usp=pp_url&entry.<span class="hljs-number">2057655542</span>=<span class="hljs-title class_">React</span>+<span class="hljs-number">18</span>&entry.<span class="hljs-number">1743450499</span>=<span class="hljs-number">8</span>+months</pre></div><p id="be2f">The link shows internal IDs of the fields:</p><ul><li><code>entry.2057655542</code>: It is the ID for React version’s choice.</li><li><code>entry.1743450499</code>: It is the ID for the duration of use.</li></ul><p id="da6a">There is another field missing, <code>emailAddress</code>, as it cannot be pre-filled. We append an email address to the saved link:</p><div id="034b"><pre><span class="hljs-symbol">https:</span>/<span class="hljs-regexp">/docs.google.com/forms</span><span class="hljs-regexp">/d/e</span><span class="hljs-regexp">/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform</span>?usp=pp_url&entry.<span class="hljs-number">2057655542</span>=<span class="hljs-title class_">React</span>+<span class="hljs-number">18</span>&entry.<span class="hljs-number">1743450499</span>=<span class="hljs-number">8</span>+months&emailAddress=test<span class="hljs-variable">@yahoo</span>.com</pre></div><p id="7aed">Copy and paste the link to the browser URL, and we can confirm that all fields are populated correctly.</p><figure id="776c"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*bK3ldDgdAU30h4vKunsz_Q.png"><figcaption>Image by author</figcaption></figure><p id="87e7">After replacing <code>viewform</code> with <code>formResponse</code>, the link can be used to send responses to Google Forms.</p><h2 id="e82b">Build a Customized UI</h2><p id="2f6a">We would like to create a custom UI using Ant Design System.</p><figure id="cb7f"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*d0d2i4lwGJ3XiFc08tgVvg.png"><figcaption>Image by author</figcaption></figure><p id="ae30"><b>The Form Component</b></p><p id="8600"><a href="https://betterprogramming.pub/how-to-use-ant-forms-in-your-web-app-3cc1a7468ff7"><code>F</code>orm</a> allows a user to enter data that is sent to a server for processing. Ant Design System provides <code>Button</code>, <code>Form</code>, <code>Input</code>, <code>Radio</code>, and many components that are wrapped by <code>Form.Item</code>.</p><p id="ce14"><b>The Email Input</b></p><p id="6314">The custom UI requires an email, which is a string input. The <code>Form.Item</code> is named <code>email</code>.</p><div id="3834"><pre><span class="hljs-tag"><<span class="hljs-name">Form.Item</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"Email"</span> <span class="hljs-attr">rules</span>=<span class="hljs-string">{[{</span> <span class="hljs-attr">required:</span> <span class="hljs-attr">true</span>, <span class="hljs-attr">message:</span> '<span class="hljs-attr">Email</span> <span class="hljs-attr">address</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>' }]} ></span> <span class="hljs-tag"><<span class="hljs-name">Input</span> /></span> <span class="hljs-tag"></<span class="hljs-name">Form.Item</span>></span></pre></div><p id="a433"><b>The React Version Radio Group</b></p><p id="7e83">The custom UI requires to select the React version, which is a group of <code>Radio</code> buttons. The <code>Form.Item</code> is named <code>version</code>.</p><div id="0dc4"><pre><<span class="hljs-title class_">Form</span>.<span class="hljs-property">Item</span> name=<span class="hljs-string">"version"</span> label=<span class="hljs-string">"1. Which version of React are you using?"</span> rules={[{ <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'React version is required'</span> }]} > <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Radio.Group</span>></span> <span class="hljs-tag"><<span class="hljs-name">Space</span> <span class="hljs-attr">direction</span>=<span class="hljs-string">"vertical"</span>></span> <span class="hljs-tag"><<span class="hljs-name">Radio</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"React 16"</span>></span>React 16<span class="hljs-tag"></<span class="hljs-name">Radio</span>></span> <span class="hljs-tag"><<span class="hljs-name">Radio</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"React 17"</span>></span>React 17<span class="hljs-tag"></<span class="hljs-name">Radio</span>></span> <span class="hljs-tag"><<span class="hljs-name">Radio</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"React 18"</span>></span>React 18<span class="hljs-tag"></<span class="hljs-name">Radio</span>></span> <span class="hljs-tag"></<span class="hljs-name">Space</span>></span> <span class="hljs-tag"></<span class="hljs-name">Radio.Group</span>></span></span> </<span class="hljs-title class_">Form</span>.<span class="hljs-property">Item</span>></pre></div><p id="bab4"><b>The Duration of Use Input</b></p><p id="35eb">The custom UI can optionally have the duration of use, which is a string input. The <code>Form.Item</code> is named <code>duration</code>.</p><div id="b886"><pre><<span class="hljs-title class_">Form</span>.<span class="hljs-property">Item</span> name=<span class="hljs-string">"duration"</span> label=<span class="hljs-string">"2. How long have you been using React version from Question 1?"</span> > <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Input</span> /></span></span> </<span class="hljs-title class_">Form</span>.<span class="hljs-property">Item</span>></pre></div><p id="84de"><b>The Submit Button</b></p><p id="42e1">The form has a submit button. Since no data is used, it works with or without being wrapped by <code>Form.Item</code>.</p><div id="620f"><pre><<span class="hljs-title class_">Button</span> type=<span class="hljs-string">"primary"</span> htmlType=<span class="hljs-string">"submit"</span>> <span class="hljs-title class_">Submit</span> </<span class="hljs-title class_">Button</span>></pre></div><p id="8d23">When the button is clicked, the form is validated:</p><figure id="240d"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*-F5f8DPlLM69xE49TUewkQ.png"><figcaption>Image by author</figcaption></figure><p id="ef46">After all required fields are validated, <code>Submit</code> works.</p><figure id="c02f"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*ubdF1-zucoKsw2ymSJPQpg.png"><figcaption>Image by author</figcaption></figure><p id="2b34">The form’s <code>onFinish</code> method is invoked with an object of the populated data. The above input generates the following object:</p><div id="100e"><pre>{ <span class="hljs-attr">email</span>: <span class="hljs-string">'[email protected]'</span>, <span class="hljs-attr">version</span>: <span class="hljs-string">'React 18'</span>, <span class="hljs-attr">duration</span>: <span class="hljs-string">'6 months'</span>, }</pre></div><p id="f4cf">This object is used to populate <code>formResponse</code>'s query parameters, with internal IDs.</p><div id="1c7c"><pre><span class="hljs-keyword">const</span> onFinish = useCallback( <span class="hljs-keyword">async</span> ({ email, version, duration }) => { <span class="hljs-keyword">try</span> { <span class="hljs-keyword">await</span> fetch( <span class="hljs-string">'https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/formResponse?'</span> + <span class="hljs-keyword">new</span> URLSearchParams({ <span class="hljs-string">'entry.2057655542'</span>: version, <span class="hljs-string">'entry.1743450499'</span>: duration ?? <span class="hljs-string">''</span>, emailAddress: email, }), { mode: <span class="hljs-string">'no-cors'</span>, } ); } <span class="hljs-keyword">catch</span> (e) { console.log(e.message) } }, [<span class="hljs-meta">api</span>] );</pre></div><p id="edda">We have to set <code>mode</code> to <code>'no-cors'</code>, as this app located <code>localhost:3000</code> fetching to <code>https://docs.google.com</code> causes the violation of Cross-Origin Resource Sharing (CORS). <code>'no-cors'</code> works, but we should put this kind of service into the backend, such as a node.js server.</p><p id="10e2"><b>The Complete Demo</b></p><p id="c394">For a complete demo, we also use Ant Design System’s notification to send out a success (<code>api.success</c

Options

ode>) message or an error (<code>api.error</code>) message. For a successful submission, the form data is cleared (<code>form.resetFields()</code>), and the drawer is closed (<code>setOpen(false)</code>). Since Ant Design System divides the design area into 24 sections, the form takes three quarters of the width with <code>span</code> set to 18.</p><p id="ba57">Here is <code>src/CustomGoogleForms.js</code>:</p><div id="3760"><pre><span class="hljs-keyword">import</span> { useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>; <span class="hljs-keyword">import</span> { <span class="hljs-title class_">Button</span>, <span class="hljs-title class_">Form</span>, <span class="hljs-title class_">Input</span>, <span class="hljs-title class_">Radio</span>, <span class="hljs-title class_">Space</span>, notification } <span class="hljs-keyword">from</span> <span class="hljs-string">'antd'</span>;

<span class="hljs-keyword">const</span> <span class="hljs-title function_">CustomGoogleForms</span> = (<span class="hljs-params">{ setOpen }</span>) => { <span class="hljs-keyword">const</span> [form] = <span class="hljs-title class_">Form</span>.<span class="hljs-title function_">useForm</span>(); <span class="hljs-keyword">const</span> [api, contextHolder] = notification.<span class="hljs-title function_">useNotification</span>();

<span class="hljs-keyword">const</span> onFinish = <span class="hljs-title function_">useCallback</span>( <span class="hljs-keyword">async</span> ({ email, version, duration }) => { <span class="hljs-keyword">try</span> { <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>( <span class="hljs-string">'https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/formResponse?'</span> + <span class="hljs-keyword">new</span> <span class="hljs-title class_">URLSearchParams</span>({ <span class="hljs-string">'entry.2057655542'</span>: version, <span class="hljs-string">'entry.1743450499'</span>: duration ?? <span class="hljs-string">''</span>, <span class="hljs-attr">emailAddress</span>: email, }), { <span class="hljs-attr">mode</span>: <span class="hljs-string">'no-cors'</span>, } ); api.<span class="hljs-title function_">success</span>({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Submitted successfully'</span>, }); form.<span class="hljs-title function_">resetFields</span>(); <span class="hljs-title function_">setOpen</span>(<span class="hljs-literal">false</span>); } <span class="hljs-keyword">catch</span> (e) { api.<span class="hljs-title function_">error</span>({ <span class="hljs-attr">message</span>: e.<span class="hljs-property">message</span>, }); } }, [api, form] );

<span class="hljs-keyword">return</span> ( <span class="language-xml"><span class="hljs-tag"><></span> {contextHolder} <span class="hljs-tag"><<span class="hljs-name">Form</span> <span class="hljs-attr">form</span>=<span class="hljs-string">{form}</span> <span class="hljs-attr">layout</span>=<span class="hljs-string">"vertical"</span> <span class="hljs-attr">wrapperCol</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">span:</span> <span class="hljs-attr">18</span> }} <span class="hljs-attr">onFinish</span>=<span class="hljs-string">{onFinish}</span> ></span> <span class="hljs-tag"><<span class="hljs-name">Form.Item</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"Email"</span> <span class="hljs-attr">rules</span>=<span class="hljs-string">{[{</span> <span class="hljs-attr">required:</span> <span class="hljs-attr">true</span>, <span class="hljs-attr">message:</span> '<span class="hljs-attr">Email</span> <span class="hljs-attr">address</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>' }]} ></span> <span class="hljs-tag"><<span class="hljs-name">Input</span> /></span> <span class="hljs-tag"></<span class="hljs-name">Form.Item</span>></span> <span class="hljs-tag"><<span class="hljs-name">Form.Item</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"version"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"1. Which version of React are you using?"</span> <span class="hljs-attr">rules</span>=<span class="hljs-string">{[{</span> <span class="hljs-attr">required:</span> <span class="hljs-attr">true</span>, <span class="hljs-attr">message:</span> '<span class="hljs-attr">React</span> <span class="hljs-attr">version</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>' }]} ></span> <span class="hljs-tag"><<span class="hljs-name">Radio.Group</span>></span> <span class="hljs-tag"><<span class="hljs-name">Space</span> <span class="hljs-attr">direction</span>=<span class="hljs-string">"vertical"</span>></span> <span class="hljs-tag"><<span class="hljs-name">Radio</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"React 16"</span>></span>React 16<span class="hljs-tag"></<span class="hljs-name">Radio</span>></span> <span class="hljs-tag"><<span class="hljs-name">Radio</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"React 17"</span>></span>React 17<span class="hljs-tag"></<span class="hljs-name">Radio</span>></span> <span class="hljs-tag"><<span class="hljs-name">Radio</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"React 18"</span>></span>React 18<span class="hljs-tag"></<span class="hljs-name">Radio</span>></span> <span class="hljs-tag"></<span class="hljs-name">Space</span>></span> <span class="hljs-tag"></<span class="hljs-name">Radio.Group</span>></span> <span class="hljs-tag"></<span class="hljs-name">Form.Item</span>></span> <span class="hljs-tag"><<span class="hljs-name">Form.Item</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"duration"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"2. How long have you been using React version from Question 1?"</span> ></span> <span class="hljs-tag"><<span class="hljs-name">Input</span> /></span> <span class="hljs-tag"></<span class="hljs-name">Form.Item</span>></span> <span class="hljs-tag"><<span class="hljs-name">Button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"primary"</span> <span class="hljs-attr">htmlType</span>=<span class="hljs-string">"submit"</span>></span> Submit <span class="hljs-tag"></<span class="hljs-name">Button</span>></span> <span class="hljs-tag"></<span class="hljs-name">Form</span>></span> <span class="hljs-tag"></></span></span> ); };

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">CustomGoogleForms</span>;</pre></div><p id="bd02">The created component, <code>CustomGoogleForms</code>, is used in <code>src/App.js</code>:</p><div id="9790"><pre><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>; <span class="hljs-keyword">import</span> logo <span class="hljs-keyword">from</span> <span class="hljs-string">'./logo.svg'</span>; <span class="hljs-keyword">import</span> <span class="hljs-string">'./App.css'</span>; <span class="hljs-keyword">import</span> { <span class="hljs-title class_">Drawer</span>, <span class="hljs-title class_">FloatButton</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'antd'</span>; <span class="hljs-keyword">import</span> { <span class="hljs-title class_">LikeOutlined</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'@ant-design/icons'</span>; <span class="hljs-keyword">import</span> <span class="hljs-title class_">CustomGoogleForms</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'./CustomGoogleForms'</span>;

<span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) { <span class="hljs-keyword">const</span> [open, setOpen] = <span class="hljs-title function_">useState</span>(<span class="hljs-literal">false</span>); <span class="hljs-keyword">return</span> ( <span class="language-xml"><span class="hljs-tag"><></span> <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>></span> <span class="hljs-tag"><<span class="hljs-name">header</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App-header"</span>></span> <span class="hljs-tag"><<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{logo}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App-logo"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"logo"</span> /></span> <span class="hljs-tag"><<span class="hljs-name">p</span>></span> Edit <span class="hljs-tag"><<span class="hljs-name">code</span>></span>src/App.js<span class="hljs-tag"></<span class="hljs-name">code</span>></span> and save to reload. <span class="hljs-tag"></<span class="hljs-name">p</span>></span> <span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App-link"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://reactjs.org"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span> ></span> Learn React <span class="hljs-tag"></<span class="hljs-name">a</span>></span> <span class="hljs-tag"></<span class="hljs-name">header</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">FloatButton</span> <span class="hljs-attr">icon</span>=<span class="hljs-string">{</span><<span class="hljs-attr">LikeOutlined</span> /></span>} description="Survey" onClick={() => setOpen(true)} style={{ width: 80, height: 80, bottom: 24, right: 24 }} /> <span class="hljs-tag"><<span class="hljs-name">Drawer</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Survey"</span> <span class="hljs-attr">placement</span>=<span class="hljs-string">"right"</span> <span class="hljs-attr">onClose</span>=<span class="hljs-string">{()</span> =></span> setOpen(false)} open={open} size="large" > <span class="hljs-tag"><<span class="hljs-name">CustomGoogleForms</span> <span class="hljs-attr">setOpen</span>=<span class="hljs-string">{setOpen}</span> /></span> <span class="hljs-tag"></<span class="hljs-name">Drawer</span>></span> <span class="hljs-tag"></></span></span> ); }</pre></div><p id="514a">After a successful submission, the drawer is closed and the success message is displayed.</p><figure id="ea40"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*2cBSxAg7SD712Ru0OpoTIg.png"><figcaption>Image by author</figcaption></figure><p id="f713">How to test the failed case?</p><p id="fd44">Simply change <code>mode</code> to <code>'cors'</code>:</p><div id="7ec6"><pre><span class="hljs-attr">mode</span>: <span class="hljs-string">'cors'</span></pre></div><p id="4bfe">Upon clicking <code>Submit</code>, the fetch command failed, and the error message is displayed.</p><figure id="f858"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*TiisYXvPQcQQld1RYnL6ug.png"><figcaption>Image by author</figcaption></figure><p id="6d1f"><b>The Statistical Data</b></p><p id="ceb0">After more forms are submitted, the statistical data is updated:</p><figure id="3edd"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*eKoWyGZ3TnkXpA-YLL8hgA.png"><figcaption></figcaption></figure><h1 id="024f">Conclusion</h1><p id="ac3f">Google Forms is a powerful tool to collect feedback from users. We have shown two examples:</p><ul><li>Embed Google Forms as an iframe in React App.</li><li>Invoke Google Forms from a customized UI.</li></ul><p id="31d9">Which way do you prefer?</p><p id="2b59">Thanks for reading.</p><div id="e52f"><pre>Want to Connect?

If you are interested,<span class="hljs-built_in"> check </span>out my directory of web development articles.</pre></div><p id="2307"><i>More content at <a href="https://plainenglish.io/"><b>PlainEnglish.io</b></a>.</i></p><p id="4c8b"><i>Sign up for our <a href="http://newsletter.plainenglish.io/"><b>free weekly newsletter</b></a>. Follow us on <a href="https://twitter.com/inPlainEngHQ"><b>Twitter</b></a></i>, <a href="https://www.linkedin.com/company/inplainenglish/"><b><i>LinkedIn</i></b></a><i>, <a href="https://www.youtube.com/channel/UCtipWUghju290NWcn8jhyAw"><b>YouTube</b></a>, and <a href="https://discord.gg/GtDtUAvyhW"><b>Discord</b></a><b>.</b></i></p><p id="f22b"><b><i>Interested in scaling your software startup</i></b><i>? Check out <a href="https://circuit.ooo?utm=publication-post-cta"><b>Circuit</b></a>.</i></p></article></body>

Embedding Google Forms in React Apps

A comprehensive guide starting from embedding Google Forms as an iframe to any customized UI with styling and branding

Photo by Sergey Zolkin on Unsplash

Have you ever built a website? Have you ever thought about contacting your users to get feedback?

If your answer is yes to both of questions, there is a powerful tool to collect feedback from users, which is called Google Forms.

Google Forms is a survey administration software. It is part of the free, web-based Google Docs Editors suite, including Google Docs, Google Sheets, Google Slides, Google Drawings, Google Sites, and Google Keep. Google Forms can manage event registrations, create a quick opinion poll, and perform much more tasks.

Let’s take a look at how to use Google Forms in React apps, starting from embedding it as an iframe to any customized UI with styling and branding.

Create Google Forms

Go to https://docs.google.com/forms, it shows a list of form templates and recently used forms:

Image by author

Click the colorful + sign to start a new form, which has three tabs, Questions, Responses, and Settings.

Here is the first tab, Questions.

Image by author

It builds a form with title, description, and various form components. Clicking on the select, showing Multiple choice, we see various form components, including Short answer, Paragraph, Multiple choice, Checkboxes, Dropdown, File upload, Linear scale, Multiple choice grid, Checkbox grid, Date, and Time.

Image by author

Using this UI, we have built a survey for React usage:

Image by author

Click on the button, Send, and it shows the choices to send the form via Email, Link, or Embed HTML. If the choice is Email, an email list needs to be provided:

Image by author

If the choice is Link, the link can be copied, long or short version:

Image by author

If the choice is Embed HTML, an iframe tag can be copied:

Image by author

Here is the copied iframe tag with default width and height set to 640px and 685px.

<iframe src="https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform?embedded=true" width="640" height="685" frameborder="0" marginheight="0" marginwidth="0">Loading…</iframe>

Go back to the choice of Email, and click the button, Send.

We receive the email immediately. On the email content, there is a button, FILL OUT IN GOOGLE FORMS.

Image by author

It opens a new tab/window. Fill in fields and click the button, Submit.

Image by author

Go back to the form builder, and choose the second tab, Responses. It shows one response received and one response pending, along with statistical data:

Image by author

Click on the green button at the top of form, Link to Sheet, and it allows us to save responses to a spreadsheet.

Image by author

Google Forms is a web application. It works out of the box, and we are going to use it in a React app.

Set Up Ant Design System in Create React App

We use Create React App as a base to embed Google Forms. The following command creates a React project:

% yarn create react-app react-google-form
% cd react-google-form

Ant Design System is an open source code for enterprise-level UI design languages and React UI library. We install antd to take advantage of its rich component library to build the app.

% yarn add antd

It becomes part of dependencies in package.json:

"dependencies": {
  "antd": "^5.3.2"
}

The working environment is ready.

Build Google Forms App

Ant Design System has many UI components, such as button, icon, typography, modal, select, table, and tree components. We are going to embed Google Forms using FloatButton and Drawer, along with an icon, LikeOutlined.

The FloatButton Component

FloatButton is used for global functionality on the site. It can be seen wherever you browse. We place the button at the lower right corner.

Image by author

Here is the code:

<FloatButton
  icon={<LikeOutlined />}
  description="Survey"
  onClick={() => setOpen(true)}
  style={{ width: 80, height: 80, bottom: 24, right: 24 }}
/>

Optionally, FloatButton can have icon and description. Upon clicking, we make it to open the drawer (setOpen(true)). The button size has been configured to be 80px x 80px, and the distance to bottom and right are both 24px.

The Drawer Component

Drawer is a panel which typically overlaid on top of a page and slides in from the side. Here is the drawer after clicking the survey button:

Image by author

Here is the code:

<Drawer
  title="Survey"
  placement="right"
  onClose={() => setOpen(false)}
  open={open}
  size="large"
>
  {children}
</Drawer>

Drawer has title, and placement, which can be 'left' | 'right' | 'top' | 'bottom'. The drawer is open if the open state is true. When clicking the X button or outside of the drawer, we close the open drawer. Drawer supports two sizes, "default" or "large". For our survey, it is set to "large".

Copy the iframe tag, and paste it as the child of Drawer, adjusting some props to fill up the drawer area.

<Drawer
  title="Survey"
  placement="right"
  onClose={() => setOpen(false)}
  open={open}
  size="large"
>
  <iframe
    title="Survey"
    src="https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform?embedded=true"
    frameborder="0"
    marginheight="0"
    marginwidth="0"
    style={{ width: '100%', height: 'calc(100% - 3px)' }}
  >
    Loading…
  </iframe>
</Drawer>

Here is the Google Forms displayed on the drawer:

Image by author

The Complete Code

Here is the complete code of the modified src/App.js:

import { useState } from 'react';
import logo from './logo.svg';
import './App.css';
import { Drawer, FloatButton } from 'antd';
import { LikeOutlined } from '@ant-design/icons';

function App() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
      <FloatButton
        icon={<LikeOutlined />}
        description="Survey"
        onClick={() => setOpen(true)}
        style={{ width: 80, height: 80, bottom: 24, right: 24 }}
      />
      <Drawer
        title="Survey"
        placement="right"
        onClose={() => setOpen(false)}
        open={open}
        size="large"
      >
        <iframe
          title="Survey"
          src="https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform?embedded=true"
          frameborder="0"
          marginheight="0"
          marginwidth="0"
          style={{ width: '100%', height: 'calc(100% - 3px)' }}
        >
          Loading…
        </iframe>
      </Drawer>
    </>
  );
}

export default App;

Build Customized Google Forms

Embedding Google Forms as an iframe works. However, we may want to have a customized UI with styling and branding. Google Forms enables us to do it by two steps:

  • Get pre-filled link.
  • Build a customized UI.

Get Pre-Filled Link

Go back to the form builder page. There is a list of available commands, upon clicking the more menu (also called the kebab menu, the three-dot menu, and the vertical three-dot menu).

Select Get pre-filled link:

Image by author

It shows a survey UI to be pre-filled. It says that Email cannot be pre-filled, but do fill in answers for other questions.

Image by author

Click the button Get link, and the following is the saved link from the clip board:

https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform?usp=pp_url&entry.2057655542=React+18&entry.1743450499=8+months

The link shows internal IDs of the fields:

  • entry.2057655542: It is the ID for React version’s choice.
  • entry.1743450499: It is the ID for the duration of use.

There is another field missing, emailAddress, as it cannot be pre-filled. We append an email address to the saved link:

https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform?usp=pp_url&entry.2057655542=React+18&entry.1743450499=8+months&emailAddress=test@yahoo.com

Copy and paste the link to the browser URL, and we can confirm that all fields are populated correctly.

Image by author

After replacing viewform with formResponse, the link can be used to send responses to Google Forms.

Build a Customized UI

We would like to create a custom UI using Ant Design System.

Image by author

The Form Component

Form allows a user to enter data that is sent to a server for processing. Ant Design System provides Button, Form, Input, Radio, and many components that are wrapped by Form.Item.

The Email Input

The custom UI requires an email, which is a string input. The Form.Item is named email.

<Form.Item
  name="email"
  label="Email"
  rules={[{ required: true, message: 'Email address is required' }]}
>
  <Input />
</Form.Item>

The React Version Radio Group

The custom UI requires to select the React version, which is a group of Radio buttons. The Form.Item is named version.

<Form.Item
  name="version"
  label="1. Which version of React are you using?"
  rules={[{ required: true, message: 'React version is required' }]}
>
  <Radio.Group>
    <Space direction="vertical">
      <Radio value="React 16">React 16</Radio>
      <Radio value="React 17">React 17</Radio>
      <Radio value="React 18">React 18</Radio>
    </Space>
  </Radio.Group>
</Form.Item>

The Duration of Use Input

The custom UI can optionally have the duration of use, which is a string input. The Form.Item is named duration.

<Form.Item
  name="duration"
  label="2. How long have you been using React version from Question 1?"
>
  <Input />
</Form.Item>

The Submit Button

The form has a submit button. Since no data is used, it works with or without being wrapped by Form.Item.

<Button type="primary" htmlType="submit">
  Submit
</Button>

When the button is clicked, the form is validated:

Image by author

After all required fields are validated, Submit works.

Image by author

The form’s onFinish method is invoked with an object of the populated data. The above input generates the following object:

{
  email: '[email protected]',
  version: 'React 18', 
  duration: '6 months',
}

This object is used to populate formResponse's query parameters, with internal IDs.

const onFinish = useCallback(
  async ({ email, version, duration }) => {
    try {
      await fetch(
        'https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/formResponse?' +
          new URLSearchParams({
            'entry.2057655542': version,
            'entry.1743450499': duration ?? '',
            emailAddress: email,
          }),
        {
          mode: 'no-cors',
        }
      );
    } catch (e) {
      console.log(e.message)
    }
  },
  [api]
);

We have to set mode to 'no-cors', as this app located localhost:3000 fetching to https://docs.google.com causes the violation of Cross-Origin Resource Sharing (CORS). 'no-cors' works, but we should put this kind of service into the backend, such as a node.js server.

The Complete Demo

For a complete demo, we also use Ant Design System’s notification to send out a success (api.success) message or an error (api.error) message. For a successful submission, the form data is cleared (form.resetFields()), and the drawer is closed (setOpen(false)). Since Ant Design System divides the design area into 24 sections, the form takes three quarters of the width with span set to 18.

Here is src/CustomGoogleForms.js:

import { useCallback } from 'react';
import { Button, Form, Input, Radio, Space, notification } from 'antd';

const CustomGoogleForms = ({ setOpen }) => {
  const [form] = Form.useForm();
  const [api, contextHolder] = notification.useNotification();

  const onFinish = useCallback(
    async ({ email, version, duration }) => {
      try {
        await fetch(
          'https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/formResponse?' +
            new URLSearchParams({
              'entry.2057655542': version,
              'entry.1743450499': duration ?? '',
              emailAddress: email,
            }),
          {
            mode: 'no-cors',
          }
        );
        api.success({
          message: 'Submitted successfully',
        });
        form.resetFields();
        setOpen(false);
      } catch (e) {
        api.error({
          message: e.message,
        });
      }
    },
    [api, form]
  );

  return (
    <>
      {contextHolder}
      <Form
        form={form}
        layout="vertical"
        wrapperCol={{ span: 18 }}
        onFinish={onFinish}
      >
        <Form.Item
          name="email"
          label="Email"
          rules={[{ required: true, message: 'Email address is required' }]}
        >
          <Input />
        </Form.Item>
        <Form.Item
          name="version"
          label="1. Which version of React are you using?"
          rules={[{ required: true, message: 'React version is required' }]}
        >
          <Radio.Group>
            <Space direction="vertical">
              <Radio value="React 16">React 16</Radio>
              <Radio value="React 17">React 17</Radio>
              <Radio value="React 18">React 18</Radio>
            </Space>
          </Radio.Group>
        </Form.Item>
        <Form.Item
          name="duration"
          label="2. How long have you been using React version from Question 1?"
        >
          <Input />
        </Form.Item>
        <Button type="primary" htmlType="submit">
          Submit
        </Button>
      </Form>
    </>
  );
};

export default CustomGoogleForms;

The created component, CustomGoogleForms, is used in src/App.js:

import { useState } from 'react';
import logo from './logo.svg';
import './App.css';
import { Drawer, FloatButton } from 'antd';
import { LikeOutlined } from '@ant-design/icons';
import CustomGoogleForms from './CustomGoogleForms';

function App() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
      <FloatButton
        icon={<LikeOutlined />}
        description="Survey"
        onClick={() => setOpen(true)}
        style={{ width: 80, height: 80, bottom: 24, right: 24 }}
      />
      <Drawer
        title="Survey"
        placement="right"
        onClose={() => setOpen(false)}
        open={open}
        size="large"
      >
        <CustomGoogleForms setOpen={setOpen} />
      </Drawer>
    </>
  );
}

After a successful submission, the drawer is closed and the success message is displayed.

Image by author

How to test the failed case?

Simply change mode to 'cors':

mode: 'cors'

Upon clicking Submit, the fetch command failed, and the error message is displayed.

Image by author

The Statistical Data

After more forms are submitted, the statistical data is updated:

Conclusion

Google Forms is a powerful tool to collect feedback from users. We have shown two examples:

  • Embed Google Forms as an iframe in React App.
  • Invoke Google Forms from a customized UI.

Which way do you prefer?

Thanks for reading.

Want to Connect?

If you are interested, check out my directory of web development articles.

More content at PlainEnglish.io.

Sign up for our free weekly newsletter. Follow us on Twitter, LinkedIn, YouTube, and Discord.

Interested in scaling your software startup? Check out Circuit.

Google Forms
Ant Design
React
Web Development
Programming
Recommended from ReadMedium