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

Add hidden form field #1728

Merged
merged 14 commits into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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 blocks.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@
"form-file": {
"block": "blocks/blocks/form/file/block.json"
},
"form-hidden-field": {
"block": "blocks/blocks/form/hidden-field/block.json"
},
"google-map": {
"block": "blocks/blocks/google-map/block.json",
"assets": {
Expand Down
2 changes: 2 additions & 0 deletions plugins/otter-pro/inc/class-main.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public function register_blocks( $blocks ) {
'product-upsells',
'review-comparison',
'form-file',
'form-hidden-field',
);

$blocks = array_merge( $blocks, $pro_blocks );
Expand Down Expand Up @@ -135,6 +136,7 @@ public function register_dynamic_blocks( $dynamic_blocks ) {
'product-upsells' => '\ThemeIsle\OtterPro\Render\WooCommerce\Product_Upsells_Block',
'review-comparison' => '\ThemeIsle\OtterPro\Render\Review_Comparison_Block',
'form-file' => '\ThemeIsle\OtterPro\Render\Form_File_Block',
'form-hidden-field' => '\ThemeIsle\OtterPro\Render\Form_Hidden_Block',
);

$dynamic_blocks = array_merge( $dynamic_blocks, $blocks );
Expand Down
12 changes: 12 additions & 0 deletions plugins/otter-pro/inc/plugins/class-form-emails-storing.php
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,18 @@ class="otter_form_record_meta__value"
<?php
}
break;
case 'hidden':
?>
<input
style="width: 100%; max-width: 350px;"
name="<?php echo esc_attr( 'otter_meta_' . $id ); ?>"
id="<?php echo intval( $id ); ?>"
type="text"
class="otter_form_record_meta__value"
value="<?php echo esc_html( $field['value'] ); ?>"
/>
<?php
break;
default:
?>
<input
Expand Down
54 changes: 54 additions & 0 deletions plugins/otter-pro/inc/render/class-form-hidden-block.pnp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php
/**
* Form_Hidden_Block
*
* @package ThemeIsle\OtterPro\Render
*/

namespace ThemeIsle\OtterPro\Render;

use ThemeIsle\OtterPro\Plugins\License;

/**
* Form_Hidden_Block
*/
class Form_Hidden_Block {

/**
* Block render function for server-side.
*
* This method will pe passed to the render_callback parameter and it will output
* the server side output of the block.
*
* @param array $attributes Block attrs.
* @return mixed|string
*/
public function render( $attributes ) {

if ( ! License::has_active_license() ) {
return '';
}

$class_names = 'wp-block-themeisle-blocks-form-hidden-field ' . ( isset( $attributes['className'] ) ? $attributes['className'] : '' );
$id = isset( $attributes['id'] ) ? $attributes['id'] : '';
$label = isset( $attributes['label'] ) ? $attributes['label'] : __( 'Hidden Field', 'otter-blocks' );
$param_name = isset( $attributes['paramName'] ) ? $attributes['paramName'] : '';


$output = '<div style="display: none;" class="' . $class_names . '" id="' . $id . '">';

$output .= '<label class="otter-form-input-label" for="field-' . $id . '">' . $label . '</label>';

$output .= '<input type="hidden" class="otter-form-input" name="field-'
. $id . '" '
. ( ! empty( $param_name ) ? 'data-param-name="' . $param_name . '"' : '' )
. ' />';

$output .= '</div>';


return $output;
}


}
1 change: 0 additions & 1 deletion src/blocks/blocks/form/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ import Inspector from './inspector.js';
import Placeholder from './placeholder.js';
import { useResponsiveAttributes } from '../../helpers/utility-hooks';
import { renderBoxOrNumWithUnit, _cssBlock, _px, findInnerBlocks } from '../../helpers/helper-functions';
import { Notice } from '@wordpress/components';

const { attributes: defaultAttributes } = metadata;

Expand Down
8 changes: 8 additions & 0 deletions src/blocks/blocks/form/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,11 @@
border-top: 1px solid #ededed;
}
}

.o-hidden-field-mark {
padding: 5px 10px;
background-color: var(--label-color, #2B2784);
color: white;
border-radius: 5px;
margin-right: 5px;
}
26 changes: 26 additions & 0 deletions src/blocks/blocks/form/hidden-field/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "themeisle-blocks/form-hidden-field",
"title": "Hidden Field",
"category": "themeisle-blocks",
"description": "A field used for adding extra metadata to the Form via URL params.",
"keywords": [ "metadata", "hidden", "field" ],
"textdomain": "otter-blocks",
"ancestor": [ "themeisle-blocks/form" ],
"attributes": {
"id": {
"type": "string"
},
"label": {
"type": "string",
"default": "Hidden Field"
},
"paramName": {
"type": "string"
}
},
"supports": {
"align": [ "wide", "full" ]
}
}
41 changes: 41 additions & 0 deletions src/blocks/blocks/form/hidden-field/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

import { registerBlockType } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import metadata from './block.json';
import { formFieldIcon as icon } from '../../../helpers/icons.js';
import Inspector from '../file/inspector';

const { name } = metadata;

if ( ! window.themeisleGutenberg.isAncestorTypeAvailable ) {
metadata.parent = [ 'themeisle-blocks/form' ];
}

if ( ! Boolean( window.themeisleGutenberg.hasPro ) ) {

registerBlockType( name, {
...metadata,
title: __( 'Nonce Field', 'otter-blocks' ),
description: __( 'Protect the form from CSRF.', 'otter-blocks' ),
icon,
keywords: [
'protection',
'csrf',
'field'
],
edit: ( props ) => {
return (
<Inspector { ...props } />
);
},
save: () => null
});

}
81 changes: 81 additions & 0 deletions src/blocks/blocks/form/hidden-field/inspector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

import {
InspectorControls
} from '@wordpress/block-editor';

import {
Button, ExternalLink,
PanelBody,
TextControl
} from '@wordpress/components';

import { useContext } from '@wordpress/element';

/**
* Internal dependencies
*/

import { FormContext } from '../edit';
import { Notice } from '../../../components';
import { setUtm } from '../../../helpers/helper-functions';


/**
*
* @param {import('./types').FormHiddenFieldInspectorPros} props
* @returns {JSX.Element}
*/
const Inspector = ({
attributes,
setAttributes
}) => {

const {
selectForm
} = useContext( FormContext );

return (
<InspectorControls>
<PanelBody
title={ __( 'Field Settings', 'otter-blocks' ) }
>
<Button
isSecondary
variant="secondary"
onClick={ () => selectForm?.() }
>
{ __( 'Back to the Form', 'otter-blocks' ) }
Soare-Robert-Daniel marked this conversation as resolved.
Show resolved Hide resolved
</Button>

<TextControl
label={ __( 'Label', 'otter-blocks' ) }
value={ attributes.label }
onChange={ undefined }
help={ __( 'The label will be used as the field name.', 'otter-blocks' ) }
disabled={true}
/>

<TextControl
label={ __( 'Query Param', 'otter-blocks' ) }
value={ attributes.paramName }
onChange={ undefined }
help={ __( 'The query parameter name that is used in URL. If the param is present, its value will be extracted and send with the Form.', 'otter-blocks' ) }
placeholder={ __( 'e.g. utm_source', 'otter-blocks' ) }
disabled={true}
/>

<Notice
notice={ <ExternalLink href={ setUtm( window.themeisleGutenberg.upgradeLink, 'formfileblock' )}>{ __( 'Get more options with Otter Pro. ', 'otter-blocks' ) }</ExternalLink> }
variant="upsell"
/> )
</PanelBody>

</InspectorControls>
);
};

export default Inspector;
12 changes: 12 additions & 0 deletions src/blocks/blocks/form/hidden-field/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { BlockProps, InspectorProps } from '../../../helpers/blocks';


type Attributes = {
id: string
formId: string
label: string
paramName: string
}

export type FormHiddenFieldProps = BlockProps<Attributes>
export type FormHiddenFieldInspectorPros = InspectorProps<Attributes>
1 change: 1 addition & 0 deletions src/blocks/blocks/form/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import './nonce/index.js';
import './textarea/index.js';
import './multiple-choice/index.js';
import './file/index.js';
import './hidden-field/index.js';

const { name } = metadata;

Expand Down
14 changes: 12 additions & 2 deletions src/blocks/frontend/form/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const getFormFieldInputs = ( form ) => {
*
* @type {Array.<HTMLDivElement>}
*/
return [ ...form?.querySelectorAll( ':scope > .otter-form__container .wp-block-themeisle-blocks-form-input, :scope > .otter-form__container .wp-block-themeisle-blocks-form-textarea, :scope > .otter-form__container .wp-block-themeisle-blocks-form-multiple-choice, :scope > .otter-form__container .wp-block-themeisle-blocks-form-file' ) ].filter( input => {
return [ ...form?.querySelectorAll( ':scope > .otter-form__container .wp-block-themeisle-blocks-form-input, :scope > .otter-form__container .wp-block-themeisle-blocks-form-textarea, :scope > .otter-form__container .wp-block-themeisle-blocks-form-multiple-choice, :scope > .otter-form__container .wp-block-themeisle-blocks-form-file, :scope > .otter-form__container > .wp-block-themeisle-blocks-form-hidden-field ' ) ].filter( input => {
return ! innerForms?.some( innerForm => innerForm?.contains( input ) );
});
};
Expand Down Expand Up @@ -57,7 +57,7 @@ const extractFormFields = async( form ) => {
let fieldType = undefined;
const { id } = input;

const valueElem = input.querySelector( '.otter-form-input:not([type="checkbox"], [type="radio"], [type="file"]), .otter-form-textarea-input' );
const valueElem = input.querySelector( '.otter-form-input:not([type="checkbox"], [type="radio"], [type="file"], [type="hidden"]), .otter-form-textarea-input' );
if ( null !== valueElem ) {
value = valueElem?.value;
fieldType = valueElem?.type;
Expand All @@ -67,6 +67,8 @@ const extractFormFields = async( form ) => {
/** @type{HTMLInputElement} */
const fileInput = input.querySelector( 'input[type="file"]' );

const hiddenInput = input.querySelector( 'input[type="hidden"]' );

if ( fileInput ) {
const files = fileInput?.files;

Expand All @@ -89,6 +91,14 @@ const extractFormFields = async( form ) => {
} else if ( select ) {
value = [ ...select.selectedOptions ].map( o => o?.label )?.filter( l => Boolean( l ) ).join( ', ' );
fieldType = 'multiple-choice';
} else if ( hiddenInput ) {
const paramName = hiddenInput?.dataset?.paramName;

if ( paramName ) {
const urlParams = new URLSearchParams( window.location.search );
value = urlParams.get( paramName );
fieldType = 'hidden';
}
} else {
const labels = input.querySelectorAll( '.o-form-multiple-choice-field > label' );
const valuesElem = input.querySelectorAll( '.o-form-multiple-choice-field > input' );
Expand Down
38 changes: 38 additions & 0 deletions src/blocks/test/e2e/blocks/form.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,42 @@ test.describe( 'Form Block', () => {

// TODO: load a file and check if it is uploaded
});

test( 'insert a hidden field and check if it renders in frontend', async({ page, editor }) => {

await page.waitForTimeout( 1000 );
await editor.insertBlock({ name: 'themeisle-blocks/form', innerBlocks: [
{
name: 'themeisle-blocks/form-hidden-field',
attributes: {
label: 'Hidden Field Test',
paramName: 'test'
}
}
] });

const blocks = await editor.getBlocks();

const formBlock = blocks.find( ( block ) => 'themeisle-blocks/form' === block.name );
expect( formBlock ).toBeTruthy();

const fileHiddenBlock = formBlock.innerBlocks.find( ( block ) => 'themeisle-blocks/form-hidden-field' === block.name );

expect( fileHiddenBlock ).toBeTruthy();

const { attributes } = fileHiddenBlock;

expect( attributes.id ).toBeTruthy();

const postId = await editor.publishPost();

await page.goto( `/?p=${postId}&test=123` );

const hiddenInput = await page.locator( `#${attributes.id} input[type="hidden"]` );

expect( hiddenInput ).toBeTruthy();

await expect( hiddenInput ).toHaveAttribute( 'data-param-name', 'test' );

});
});
Loading