Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example Webform usage #86

Open
wants to merge 48 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
03a00a2
GET
harumijang Jul 26, 2022
65892b5
added submit method with POST but client not working
harumijang Jul 26, 2022
2427a9e
working submit and removed markdown
harumijang Jul 27, 2022
756c24c
added start of dynamic form element rendering
harumijang Jul 27, 2022
19b5993
support multiple forms on one node
harumijang Jul 28, 2022
62d5a25
dynamically pass in webform_id
harumijang Jul 28, 2022
1c512f4
dynamically build body request
harumijang Jul 28, 2022
d508023
started checkboxes/radio for POST
harumijang Jul 28, 2022
45b1c00
Merge branch 'main' into webforms
harumijang Jul 29, 2022
8188daf
Merge branch 'main' into webforms
harumijang Aug 1, 2022
42ef37e
added POST support for checkboxes, radio buttons, single checkbox
harumijang Aug 1, 2022
83399c0
Merge branch 'main' into webforms
harumijang Aug 1, 2022
74f4cf6
Moved webform related functions to lib directory
harumijang Aug 1, 2022
4203766
custom API GET works, moved POST into api/webform
harumijang Aug 2, 2022
b5d23e0
added super basic styling
harumijang Aug 2, 2022
db7fda1
Move webform rendering to /webform, pass in additionalContent to all …
harumijang Aug 2, 2022
c9c684e
Created Webform component and created types.ts file
harumijang Aug 3, 2022
f26db83
Use new types
harumijang Aug 3, 2022
46e2521
Started on custom components
harumijang Aug 3, 2022
be9f34a
Added example of working example of custom component
harumijang Aug 4, 2022
3486357
Merge branch 'main' into webforms
harumijang Aug 4, 2022
809164e
Merge branch 'main' into webforms
harumijang Aug 8, 2022
cabaadc
Added component wrapper for radio buttons and print validation msg
harumijang Aug 8, 2022
44134a4
Render validation error msg for one webform element
harumijang Aug 8, 2022
c9e0cf3
Added more wrapper components for webform elements
harumijang Aug 9, 2022
45bcb59
added wrapper for custom component and checkbox
harumijang Aug 9, 2022
07f85c5
prettier
harumijang Aug 9, 2022
1673ff2
add webform debug fallback component and some css for my sanity
harumijang Aug 10, 2022
87c025e
Added error and success form message
harumijang Aug 10, 2022
17451e4
Merge branch 'main' into webforms
harumijang Aug 10, 2022
8b11f33
Moved webforms to different pkg and added README
harumijang Aug 11, 2022
bda6dfb
add back line
harumijang Aug 11, 2022
1f1ac35
Updated readme
harumijang Aug 12, 2022
8e8cf49
Removed client from next webform
harumijang Aug 12, 2022
f031328
Use backend value for name and add comment about type in custom compo…
harumijang Aug 12, 2022
4191cb7
readme
harumijang Aug 12, 2022
26e6c44
removed webform
harumijang Aug 17, 2022
69d9b0a
Use webform from exports
lauriii Aug 17, 2022
db03401
Add second arg to webform method and add condition to node-article
harumijang Aug 17, 2022
cfe7e4b
uncomment custom component
harumijang Aug 18, 2022
e045044
Pass in client info
harumijang Aug 19, 2022
77ddec6
added submit api to starter and package
harumijang Aug 22, 2022
6ed31cd
Pass url into submit instead of client
harumijang Aug 23, 2022
6dde63c
added get api
harumijang Aug 25, 2022
374d84d
Merge branch 'main' into webforms
harumijang Sep 19, 2022
47664dc
Merge branch 'main' into webforms
harumijang Sep 23, 2022
752c3e8
Remove client as arg from getWebformFields
harumijang Aug 29, 2022
7bddac9
Change custom webform component
harumijang Sep 23, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/nextjs-drupal-webform/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
47 changes: 47 additions & 0 deletions packages/nextjs-drupal-webform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Next.js Drupal Webform

Plugin for integrating the [Webform](https://www.drupal.org/project/webform) module with Next.js applications.

## Setup
1. Install the [Webform](https://www.drupal.org/project/webform) and [Webform REST](https://www.drupal.org/project/webform_rest) modules.
2. Enable REST resources: "Webform Submit", "Webform Elements", "Webform Submission" on /admin/config/services/rest.
3. Set Webform permissions on RESTful Web Services `/admin/people/permissions#module-rest` for Anonymous Users.
4. Create your webform.
5. @todo add how to get the field_webform from entity?

## Example
```
<Webform
webformObject={additionalContent.webform}
id={additionalContent.webform.drupal_internal__id}
key={additionalContent.webform.drupal_internal__id}
/>
```


## Using custom components
1. Create your custom component
```
// Example custom component
export const WebformDate = ({ element, error }) => {
return (
<WebformElementWrapper
labelFor={element['#title']}
labelClassName={element['#required']}
settings={null}
error={error}
>
<input type="date" name="date" min="2022-01-01" max="2022-12-31" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the name should be dynamically coming from the backend?

</WebformElementWrapper>
);
};
```
2. Pass in your component(s) to the `customComponents` property.
```
<Webform
webformObject={additionalContent.webform}
id={additionalContent.webform.drupal_internal__id}
key={additionalContent.webform.drupal_internal__id}
customComponents={{ date: WebformDate }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is unclear based on these docs is that what is date? Is it the name of the form element or is it the type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type

/>
```
16 changes: 16 additions & 0 deletions packages/nextjs-drupal-webform/examples/WebformDate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import WebformElementWrapper from '../src/components/WebformElementWrapper';

// Example custom component
export const WebformDate = ({ element, error }) => {
return (
<WebformElementWrapper
labelFor={element['#title']}
labelClassName={element['#required']}
settings={null}
error={error}
>
<input type="date" name="date" min="2022-01-01" max="2022-12-31" />
</WebformElementWrapper>
);
};
5 changes: 5 additions & 0 deletions packages/nextjs-drupal-webform/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
33 changes: 33 additions & 0 deletions packages/nextjs-drupal-webform/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "nextjs-drupal-webform",
"version": "1.0.0",
"private": true,
"license": "MIT",
"scripts": {
"build": "next build",
"dev": "next dev"
},
"dependencies": {
"@tailwindcss/typography": "^0.5.0",
"classnames": "^2.3.1",
"drupal-jsonapi-params": "^2.1.0",
"html-react-parser": "^1.4.8",
"next": "^12.2.0",
"next-acms": "^1.0.0-beta1",
"next-drupal": "^1.1.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"sharp": "^0.30.2"
},
"devDependencies": {
"@babel/core": "^7.18.9",
"@types/node": "^18.0.6",
"@types/react": "^17.0.4",
"autoprefixer": "^10.4.2",
"eslint": "^8.20.0",
"eslint-config-next": "^12.2.3",
"postcss": "^8.4.5",
"tailwindcss": "^3.0.15",
"typescript": "^4.6.0"
}
}
18 changes: 18 additions & 0 deletions packages/nextjs-drupal-webform/pages/api/webform/[id].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { drupal } from '../../../src/drupal';

export default async function handler(
request: NextApiRequest,
response: NextApiResponse,
) {
try {
const url = drupal.buildUrl(
`/webform_rest/${request.query.id}/fields?_format=json`,
);
const result = await fetch(url.toString());
const webform = await result.json();
response.status(200).json(webform);
} catch (e) {
console.log(e.message);
}
}
26 changes: 26 additions & 0 deletions packages/nextjs-drupal-webform/pages/api/webform/submit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { drupal } from '../../../src/drupal';

export default async function handler(
request: NextApiRequest,
response: NextApiResponse,
) {
if (request.method === 'POST') {
const url = drupal.buildUrl('/webform_rest/submit?_format=json');
// Submit to Drupal.
const result = await fetch(url.toString(), {
method: 'POST',
body: JSON.stringify(request.body),
headers: {
'Content-Type': 'application/json',
},
});
if (!result.ok) {
const message = await result.json();
// Send error to client.
return response.status(result.status).json({ message });
}
response.end(JSON.stringify(result));
response.status(200);
}
}
80 changes: 80 additions & 0 deletions packages/nextjs-drupal-webform/src/Webform.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { useState } from 'react';
import { WebformProps } from './types';
import { formToJSON, renderWebformElement, styles } from './utils';

export class WebformError extends Error {
response: string;

constructor(response: string) {
super();

this.response = response;
}
}

/**
* Errors returned by Drupal.
*/
type WebformErrors = {
[name: string]: string;
};

export const Webform = ({
webformObject,
id,
customComponents,
}: WebformProps) => {
const [errors, setErrors] = useState<WebformErrors>({});
const [status, setStatus] = useState<'error' | 'success'>();
console.log('webform object', webformObject);

const submitHandler = async (event) => {
event.preventDefault();
const data = formToJSON(event.target.elements);
// Post process serialized data:
// Some webform elements require specialized data formatting.
for (const element in webformObject.elements) {
if (data[element]) {
switch (webformObject.elements[element]['#type']) {
case 'checkbox':
data[element] = 1;
break;
}
}
}
const body = { ...(data as object), ...{ webform_id: id } };
console.log('request body', body);
const response = await fetch('/api/webform/submit', {
method: 'POST',
body: JSON.stringify(body),
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
setStatus('error');
const message = await response.json();
setErrors(message.message.error);
} else {
setStatus('success');
// Clear webform element errors.
setErrors({});
}
};

return (
<form style={styles.form} onSubmit={(e) => submitHandler(e)}>
{status === 'error' ? (
<div style={styles.formError}>An error occurred. Please try again.</div>
) : null}
{status === 'success' ? (
<div style={styles.formSuccess}>
Your submission has been sent. Thank you.
</div>
) : null}
{Object.values(webformObject.elements).map((el) =>
renderWebformElement(el, customComponents, errors[el['#webform_key']]),
)}
</form>
);
};
26 changes: 26 additions & 0 deletions packages/nextjs-drupal-webform/src/components/WebformCheckbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import WebformElementWrapper from './WebformElementWrapper';
import { styles } from '../utils';
import { WebformCustomComponent } from '../types';

export const WebformCheckbox: WebformCustomComponent = ({ element, error }) => {
return (
<WebformElementWrapper
labelFor={element['#title']}
labelClassName={element['#required']}
settings={null}
error={error}
>
<div>
<input
type={element.type}
id={element['#webform_key']}
name={element['#webform_key']}
style={styles.checkbox}
/>
<label className="form-check-label">{element['#description']}</label>
</div>
</WebformElementWrapper>
);
};

export default WebformCheckbox;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { styles } from '../utils';
import WebformElementWrapper from './WebformElementWrapper';

export const WebformCheckboxGroup = ({ element, error }) => {
return (
<WebformElementWrapper
labelFor={element['#title']}
labelClassName={element['#required']}
settings={null}
error={error}
>
{element['#options'] &&
Object.keys(element['#options']).map((option) => (
<div className="form-check" key={option}>
<input
type={element.type}
name={element['#webform_key']}
id={option}
value={option}
style={styles.checkbox}
/>
<label className="form-check-label">{option}</label>
</div>
))}
</WebformElementWrapper>
);
};

export default WebformCheckboxGroup;
10 changes: 10 additions & 0 deletions packages/nextjs-drupal-webform/src/components/WebformDebug.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const WebformDebug = ({ element, error }) => {
return (
<code>
{error}
<pre>{JSON.stringify(element, null, 2)}</pre>
</code>
);
};

export default WebformDebug;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { styles } from '../utils';

export const WebformElementWrapper = ({
children,
labelFor,
labelClassName,
error,
...props
}) => {
const css = `
.required-field:after {
content: ' *';
color: red;
}
.invalid-feedback {
color: red;
}
`;
return (
<div {...props}>
<style>{css}</style>
<label
style={styles.elementLabel}
htmlFor={labelFor}
className={labelClassName ? 'required-field' : ''}
>
{labelFor}
</label>
{children}
{error && (
<div className="form-text invalid-feedback" {...props}>
{error}
</div>
)}
</div>
);
};

export default WebformElementWrapper;
22 changes: 22 additions & 0 deletions packages/nextjs-drupal-webform/src/components/WebformText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import WebformElementWrapper from './WebformElementWrapper';
import { styles } from '../utils';

export const WebformText = ({ element, error }) => {
return (
<WebformElementWrapper
labelFor={element['#title']}
labelClassName={element['#required']}
settings={null}
error={error}
>
<input
placeholder={element['#title']}
id={element['#webform_key']}
name={element['#webform_key']}
style={styles.textArea}
/>
</WebformElementWrapper>
);
};

export default WebformText;
22 changes: 22 additions & 0 deletions packages/nextjs-drupal-webform/src/components/WebformTextArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import WebformElementWrapper from './WebformElementWrapper';
import { styles } from '../utils';

export const WebformText = ({ element, error }) => {
return (
<WebformElementWrapper
labelFor={element['#title']}
labelClassName={element['#required']}
settings={null}
error={error}
>
<textarea
placeholder={element['#title']}
id={element['#webform_key']}
name={element['#webform_key']}
style={styles.textArea}
/>
</WebformElementWrapper>
);
};

export default WebformText;
Loading