diff --git a/examples/example-webform/components/WebformButton.tsx b/examples/example-webform/components/WebformButton.tsx new file mode 100644 index 000000000..cb1050bd0 --- /dev/null +++ b/examples/example-webform/components/WebformButton.tsx @@ -0,0 +1,17 @@ +import { components } from "nextjs-drupal-webform" +import classNames from "classnames" + +const buttonDecorator = (DecoratedComponent) => { + return function WebformCustomTable(props) { + const fieldProps = props.fieldProps ?? {} + const additionalClasses = [] + if (props.element["#button_type"] === "primary") { + additionalClasses.push("bg-black hover:bg-black text-white") + } + fieldProps.className = classNames(fieldProps.className, additionalClasses) + + return + } +} + +export default buttonDecorator(components.button) diff --git a/examples/example-webform/components/withCustomStyles.tsx b/examples/example-webform/components/withCustomStyles.tsx new file mode 100644 index 000000000..d417e5e69 --- /dev/null +++ b/examples/example-webform/components/withCustomStyles.tsx @@ -0,0 +1,28 @@ +const withCustomStyles = ( + EnhancedComponent, + fieldProps = {}, + labelProps = {}, + wrapperProps = {} +) => { + return function WebformElementWithCustomStyles(props) { + return ( + + ) + } +} + +export default withCustomStyles diff --git a/examples/example-webform/cypress/integration/webform.spec.tsx b/examples/example-webform/cypress/integration/webform.spec.tsx index 78b838bf3..47f6026ca 100644 --- a/examples/example-webform/cypress/integration/webform.spec.tsx +++ b/examples/example-webform/cypress/integration/webform.spec.tsx @@ -30,7 +30,7 @@ context("server side webform", () => { cy.get("[name=message]").type("This is my message.") cy.get("[data-cy=btn-submit]").click() - cy.contains("Your message has been sent. Thank you.") + cy.contains("New submission added to Contact") }) }) diff --git a/examples/example-webform/lib/drupal.ts b/examples/example-webform/lib/drupal.ts new file mode 100644 index 000000000..a1e60835e --- /dev/null +++ b/examples/example-webform/lib/drupal.ts @@ -0,0 +1,14 @@ +import { DrupalClient } from "next-drupal" + +export const drupal = new DrupalClient( + process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, + { + previewSecret: process.env.DRUPAL_PREVIEW_SECRET, + auth: { + clientId: process.env.DRUPAL_CLIENT_ID, + clientSecret: process.env.DRUPAL_CLIENT_SECRET, + scope: + "administrator developer site_builder content_administrator content_author content_editor user_administrator headless", + }, + } +) diff --git a/examples/example-webform/package.json b/examples/example-webform/package.json index 6522a24b1..33cb5281a 100644 --- a/examples/example-webform/package.json +++ b/examples/example-webform/package.json @@ -22,7 +22,8 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "react-hook-form": "^7.25.3", - "yup": "^0.32.11" + "yup": "^0.32.11", + "nextjs-drupal-webform": "^1.0.0-beta1" }, "devDependencies": { "@babel/core": "^7.12.9", diff --git a/examples/example-webform/pages/api/webform.ts b/examples/example-webform/pages/api/webform.ts new file mode 100644 index 000000000..60889f251 --- /dev/null +++ b/examples/example-webform/pages/api/webform.ts @@ -0,0 +1,10 @@ +import { NextApiRequest, NextApiResponse } from "next" +import { drupal } from "../../lib/drupal" +import { WebformDefaultApiRoute } from "nextjs-drupal-webform" + +export default async function handler( + request: NextApiRequest, + response: NextApiResponse +) { + return WebformDefaultApiRoute(request, response, drupal) +} diff --git a/examples/example-webform/pages/index.tsx b/examples/example-webform/pages/index.tsx index 661473675..743c4be72 100644 --- a/examples/example-webform/pages/index.tsx +++ b/examples/example-webform/pages/index.tsx @@ -1,5 +1,6 @@ import Head from "next/head" import Link from "next/link" +import * as React from "react" export default function IndexPage() { return ( @@ -33,19 +34,15 @@ export default function IndexPage() { See Example

-

Server Side (API Route)

+

Server Side

- We submit the form values to a custom API route first. The API route - then submits the form to Drupal using the{" "} - - Webform REST + This example uses the{" "} + + Next.js Webform {" "} + library to render and submit forms built using the Drupal Webform module.

-

- This is useful if we need to hide client IDs and secrets or our - Drupal implementation. -

See Example diff --git a/examples/example-webform/pages/server-side.tsx b/examples/example-webform/pages/server-side.tsx index 28afe26f2..0e3185731 100644 --- a/examples/example-webform/pages/server-side.tsx +++ b/examples/example-webform/pages/server-side.tsx @@ -2,39 +2,57 @@ import * as React from "react" import { GetStaticPropsResult } from "next" import Head from "next/head" import Link from "next/link" -import { getResourceCollection, DrupalTaxonomyTerm } from "next-drupal" -import { useForm } from "react-hook-form" -import * as yup from "yup" -import { yupResolver } from "@hookform/resolvers/yup" - -import { contactFormSchema } from "../validations/contact" - -type FormData = yup.TypeOf +import withCustomStyles from "../components/withCustomStyles" +import { + resolveWebformContent, + Webform, + components, + WebformProps, +} from "nextjs-drupal-webform" +import { drupal } from "basic-starter/lib/drupal" +import classNames from "classnames" +import WebformButton from "../components/WebformButton" interface WebformPageProps { - teams: DrupalTaxonomyTerm[] + webform: WebformProps } -export default function WebformPage({ teams }: WebformPageProps) { - const [status, setStatus] = React.useState<"error" | "success">() - const { register, handleSubmit, formState, reset } = useForm({ - resolver: yupResolver(contactFormSchema), - }) - - // This makes a POST to a custom API route. - // The Drupal base URL and the webform_id are NOT exposed. - async function onSubmit(data: FormData) { - const response = await fetch(`/api/contact`, { - method: "POST", - body: JSON.stringify(data), - }) - - if (response.ok) { - reset() - return setStatus("success") - } - - return setStatus("error") +export default function WebformPage({ webform }: WebformPageProps) { + const labelProps = { + className: classNames(["block", "text-sm", "font-medium", "text-gray-700"]), + } + const fieldProps = { + className: classNames([ + "relative", + "block", + "w-full px-3", + "py-2 mt-1 text-gray-900", + "placeholder-gray-500", + "border border-gray-300", + "rounded-md appearance-none", + "focus:outline-none", + "focus:ring-black", + "focus:border-black", + "focus:z-10", + "sm:text-sm", + ]), + } + const wrapperProps = { + className: classNames(["space-y-3"]), + } + const buttonProps = { + className: classNames([ + "px-4", + "py-2", + "text-sm font-medium", + "text-white", + "bg-black", + "border border-transparent", + "rounded-md", + "shadow-sm", + "hover:bg-black", + ]), + "data-cy": "btn-submit", } return ( @@ -47,123 +65,52 @@ export default function WebformPage({ teams }: WebformPageProps) {

Next.js for Drupal

Webform Example - Server Side

- We submit the form values to a custom API route first. The API route - then submits the form to Drupal using the{" "} - - Webform REST + This example uses the{" "} + + Next.js Webform {" "} + library to render and submit forms built using the Drupal Webform module.

-

- This is useful if we need to hide client IDs and secrets or our - Drupal implementation. -

- {status === "error" ? ( -
- An error occured. Please try again. -
- ) : null} - {status === "success" ? ( -
- Your message has been sent. Thank you. -
- ) : null} - {Object.values(formState.errors)?.length ? ( -
- {Object.values(formState.errors).map((error, index) => ( -

{error.message}

- ))} -
- ) : null} -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- -
+

@@ -179,10 +126,9 @@ export default function WebformPage({ teams }: WebformPageProps) { export async function getStaticProps(): Promise< GetStaticPropsResult > { - // Load terms terms for the contact form. return { props: { - teams: await getResourceCollection("taxonomy_term--team"), + webform: await resolveWebformContent("contact", drupal), }, } } diff --git a/yarn.lock b/yarn.lock index 4f2ddedc7..8f4d8c326 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4555,6 +4555,11 @@ classnames@^2.3.1: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== +classnames@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" + integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -9686,6 +9691,13 @@ next@^12.2.3: "@next/swc-win32-ia32-msvc" "12.3.4" "@next/swc-win32-x64-msvc" "12.3.4" +nextjs-drupal-webform@^1.0.0-beta1: + version "1.0.0-beta1" + resolved "https://registry.yarnpkg.com/nextjs-drupal-webform/-/nextjs-drupal-webform-1.0.0-beta1.tgz#fbb7989856fa0f559b599c3859c0922f4c5f7bba" + integrity sha512-qdV0LVZvBS9+VEI3IwGp43HkY2z6WuMxRWEuoVC9thqRxTWvspoCGCNYqpdm37IqN9uUgafQyzpQa6eI08ejSA== + dependencies: + classnames "^2.3.2" + node-abi@^3.3.0: version "3.51.0" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.51.0.tgz#970bf595ef5a26a271307f8a4befa02823d4e87d"