Skip to content

Commit

Permalink
Contact Form: Add a Gutenberg based form builder
Browse files Browse the repository at this point in the history
Context: #9452

@oskosk Requested this unfinished branch to be pushed up to
Automattic/jetpack for use in the Jetpck Beta Tester Plugin.

--

Adds a set of Gutenberg "Form" blocks for use with the contact form
module.

Blocks:

* Form - The main block, consists of an InnerBlock for the <form>
element and a set template consisting of a default set of inner blocks
preconfigured as a contact form

* Text - General <input type=text>/<textarea> field with configurable
  label, rows

* Button - A <button type=submit> with configurable button text

Blocking Release:

* Fix issues identified in original PR #9452

* InnerBlock templating requires WordPress/gutenberg#5452.

* WordPress/gutenberg#5452 needs to override isPrivate settings to
  prevent form blocks being seen in the inserter when out of form
  context.

* WordPress/gutenberg#5452 is whitelist only meaning we cant add
  extra blocks in between form elements - not really a huge issue
  for this but likely an issue for other use cases.

Todo - Integration with grunion-contact-form:

* Captcha support should be relatively easy to achieve with a serverside
  rendered block

* A server side filter / hook could replace the form action

Todo - Handle duplicate field names gracefully

* Currently uses a variation on the label / field name to set
  id / for settings on label / inputs.

* Currently uses same variation for <input name=""> attributes
  • Loading branch information
lsl committed Jul 12, 2018
1 parent efa088b commit b078e73
Show file tree
Hide file tree
Showing 13 changed files with 466 additions and 120 deletions.
7 changes: 4 additions & 3 deletions modules/contact-form.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
* Additional Search Queries: contact, form, grunion, feedback, submission
*/

include dirname( __FILE__ ) . '/contact-form/grunion-contact-form.php';
require_once dirname( __FILE__ ) . '/contact-form/grunion-contact-form.php';

/*
* Filters if the new Contact Form Editor View should be used.
*
Expand All @@ -22,11 +23,11 @@
* Expected to be removed in Jetpack 5.8 or if a security issue merits removing the old code sooner.
*
* @since 5.2.0
*
*
* @param boolean $view Use new Editor View. Default true.
*/
if ( is_admin() && apply_filters( 'tmp_grunion_allow_editor_view', true ) ) {
require_once dirname( __FILE__ ) . '/contact-form/grunion-editor-view.php';
}

include dirname( __FILE__ ) . '/contact-form/gutenberg-blocks.php';
require_once dirname( __FILE__ ) . '/contact-form/jetpack-contact-form-gutenberg.php';
33 changes: 33 additions & 0 deletions modules/contact-form/blocks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Contact Form - Gutenberg Blocks

Contact form Gutenberg Blocks are implemented in JSX and currently depends on using Gutenberg's [5452](https://github.com/WordPress/gutenberg/pull/5452).

Current form is more of a v0.1 library we can expand on to incorporate the original contact form logic.

Development:

* Run `gulp build` initially to create the blocks .js files after that you can run `gulp gutenpack` and `gulp gutenpack:watch` to continue development.
* Set `define('SCRIPT_DEBUG', true)` and `Jetpack_Constants::set_constant('SCRIPT_DEBUG', true)` somewhere to use your local blocks built with gutenpack.

Blocks Available:

* Form - The main block, consists of an InnerBlock for the `<form>` element and a set template consisting of a default set of inner blocks preconfigured as a contact form

* Text - General `<input type=text>`/`<textarea>` field with configurable label, rows

* Button - A `<button type=submit>` with configurable button text

Current state:

* InnerBlock templating requires [5452](https://github.com/WordPress/gutenberg/pull/5452)
* Needs a solution to filtering blocks in the inserter so we can prevent showing form blocks in non form context. Talks of this in [5452](https://github.com/WordPress/gutenberg/pull/5452).
* allowedBlocks solution in [5452](https://github.com/WordPress/gutenberg/pull/5452) is whitelist only meaning we cant add extra blocks in between form elements - not really a huge issue for this but likely an issue for other use cases

Todo

* Integration with grunion-contact-form:
* Captcha support should be relatively easy to achieve with a serverside rendered block
* A server side filter / hook could replace the form action
* Handle duplicate field names gracefully:
* Currently uses a variation on the label / field name to set id / for settings on label / inputs.
* Currently uses same variation for `<input name="">` attributes
62 changes: 62 additions & 0 deletions modules/contact-form/blocks/button/block.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const {
registerBlockType,
InspectorControls,
InnerBlocks,
} = wp.blocks;

const { createElement } = wp.element;
const { ToggleControl, TextControl, ButtonGroup, Button, RadioControl } = wp.components;

const { __ } = wp.i18n;

const inputName = (str) => str.toLowerCase().replace(/[^a-z0-9]/, '');

// Define the form block
let FormButton = {
title : __( 'Form - Submit' ),
icon : 'button',
category : 'common',
description: __( 'A submit button' ),
keywords: [
__( 'send' ),
__( 'submit' ),
__( 'contact' ),
],
useOnce: true,
// isPrivate: true,
attributes: {
label: {
type: 'string',
default: 'Submit',
}
},

save: ( { attributes, className } ) => {
return <div className={ className }>
<Button isPrimary={ true } type={ "submit" }>
{ attributes.label }
</Button>
</div>
},
edit: ( { attributes, setAttributes, className, isSelected } ) => {
if ( ! isSelected ) {
return <div className={ className }>
<Button isPrimary={ true } type={ "submit" }>
{ attributes.label }
</Button>
</div>
}

return [
<TextControl
label={ __( 'Button Text' ) }
placeholder={ __( 'Submit' ) }
value={ attributes.label }
onChange={ value => setAttributes( { 'label': value } ) }
/>
];
},
};

// Register the form block under jetpack/form
registerBlockType( 'jetpack/form-button', FormButton );
1 change: 1 addition & 0 deletions modules/contact-form/blocks/button/editor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.wp-block-jetpack-form-button {}
95 changes: 95 additions & 0 deletions modules/contact-form/blocks/form/block.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const {
registerBlockType,
InspectorControls,
InnerBlocks,
} = wp.blocks;

const { createElement } = wp.element;
const { CheckboxControl, TextControl } = wp.components;

const { __ } = wp.i18n;

// Define the form block
let Form = {
title: __( 'Form' ),
icon: 'feedback',
category: 'common',
description: __( 'Contact Form Settings' ),
keywords: [
__( 'form' ),
__( 'contact' ),
],
useOnce: true,
attributes: {
subject: {
type: 'string',
default: 'Feedback',
},

to: {
type: 'string',
default: ''
}
},

save: ( { attributes, className } ) => {
return <div className={ className }>
<form>
<InnerBlocks.Content />
</form>
</div>
},

edit: ( { attributes, setAttributes, className, isSelected } ) => {
return [
// Display the inner blocks
<InnerBlocks
template={[
[ 'jetpack/form-text', {
'label': __( 'Name' ),
'placeholder': __( 'Full Name' ),
'required': true,
} ],
[ 'jetpack/form-text', {
'label': __( 'Email' ),
'placeholder': __( 'example@example.com' ),
'required': true,
} ],
[ 'jetpack/form-textarea', {
'label': __( 'Message' ),
'placeholder': __( 'Enter message' ),
'required': true,
} ],
[ 'jetpack/form-button', { 'label': __( 'Send' ) } ]
]}
allowedBlocks={ [
'jetpack/form-text',
'jetpack/form-textarea',
'jetpack/form-button'
] }
/>,

// Display on Focus
!! isSelected &&
<InspectorControls key="inspector">
<TextControl
label={ __( 'Email Subject' ) }
help={ __( 'What would you like the subject line of the email to be?' ) }
placeholder={ __( '[Site Feedback]' ) }
value={ attributes.subject }
onChange={ value => setAttributes( { 'subject': value } ) }
/>
<TextControl
label={ __( 'Email Address' ) }
help={ __( 'Which email address should we send the submissions to?' ) }
value={ attributes.to }
placeholder={ __( 'admin@example.com' ) }
onChange={ value => setAttributes( { 'to': value } ) }
/>
</InspectorControls>
];
},
};

// Register the form block under jetpack/form
registerBlockType( 'jetpack/form', Form );
1 change: 1 addition & 0 deletions modules/contact-form/blocks/form/editor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.wp-block-jetpack-form {}
95 changes: 95 additions & 0 deletions modules/contact-form/blocks/text/block.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const {
registerBlockType,
InspectorControls,
InnerBlocks,
} = wp.blocks;

const { createElement } = wp.element;
const { ToggleControl, TextControl, BaseControl } = wp.components;

const { __ } = wp.i18n;

const inputName = ( str ) => str.toLowerCase().replace(/[^a-z0-9]/, '');

// Define the form block
let FormText = {
title : __( 'Form - Text' ),
icon : 'editor-textcolor',
category : 'common',
description: __( 'Text field' ),
keywords: [
__( 'text' ),
__( 'name' ),
__( 'email' ),
],
// isPrivate: true,
attributes: {
// Set label to display above the input
label: {
type: 'string',
default: 'Custom Text',
},

// Inspector: set placeholder value for the input
placeholder: {
type: 'string',
default: '',
},

// Inspector: determine if input is required or optional
isRequired : {
type : 'boolean',
default : true,
},
},

save: ( { attributes, className } ) => {
// todo: replace with uuid4 + saved as attribute?
// The id needs to be unique for for="" but also needs to be saved for block validation
const id = 'jetpack-form-text-input-' + inputName( attributes.label );

return <BaseControl label={ attributes.label } id={ id }>
<input className={ `${className} components-text-control__input` }
type="text"
id={ id }
name={ inputName( attributes.label ) }
placeholder={ attributes.placeholder }

/>
</BaseControl>
},

edit: ( { attributes, setAttributes, className, isSelected } ) => {
if ( ! isSelected ) {
return FormText.save( { attributes: attributes, className: className } )
}

return [
<TextControl
label={ attributes.label }
value={ attributes.label }
placeholder={ __( 'e.g. Name' ) }
onChange= { value => setAttributes ( { 'label': value } ) }
required={ true }
/>,
<InspectorControls key="inspector">
<TextControl
label={ __( 'Placeholder Text' ) }
help={ __( 'Text shown when field is empty.' ) }
placeholder={ __( 'Placeholder' ) }
value={ attributes.placeholder }
onChange={ value => setAttributes( { 'placeholder': value } ) }
/>
<ToggleControl
label={ __( 'Is required?' ) }
help={ ( checked ) => checked ? __( 'Field must be filled out.' ) : __( 'Field is optional.' ) }
checked={ !! attributes.isRequired }
onChange={ value => setAttributes( { 'isRequired': value } ) }
/>
</InspectorControls>
];
},
};

// Register the form block under jetpack/form
registerBlockType( 'jetpack/form-text', FormText );
1 change: 1 addition & 0 deletions modules/contact-form/blocks/text/editor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.wp-block-jetpack-form-text {}
Loading

0 comments on commit b078e73

Please sign in to comment.