Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions projects/packages/forms/changelog/add-forms-modal
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added

Forms: Add modal support under feature flag
15 changes: 15 additions & 0 deletions projects/packages/forms/src/blocks/contact-form/attributes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/**
* External dependencies
*/
import { hasFeatureFlag } from '@automattic/jetpack-shared-extension-utils';
import { __ } from '@wordpress/i18n';

const isModalFeatureFlagEnabled = hasFeatureFlag( 'jetpack-forms-modal-feature' );

export default {
subject: {
type: 'string',
Expand Down Expand Up @@ -85,4 +88,16 @@ export default {
type: 'array',
default: [],
},
...( isModalFeatureFlagEnabled
? {
modalEnabled: {
type: 'boolean',
default: false,
},
modalTrigger: {
type: 'string',
default: 'immediate',
},
}
: {} ),
};
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public static function register_block() {
* @return array
*/
public static function register_feature( $features ) {
// Features under feature flag.
$features['jetpack-forms-modal-feature'] = apply_filters( 'jetpack_contact_form_render_in_modal', false );
// Features that are only available to users with a paid plan.
$features['multistep-form'] = Current_Plan::supports( 'multistep-form' );

Expand Down Expand Up @@ -652,6 +654,22 @@ public static function set_file_field_extension_available() {
*/
private static $form_step_count = 1;

/**
* Static storage for modal form HTML.
* Array of form data indexed by unique form ID.
* Each entry contains 'html' and 'trigger'.
*
* @var array
*/
private static $modal_forms = array();

/**
* Flag to track if modal CSS has been enqueued.
*
* @var bool
*/
private static $modal_css_enqueued = false;

/**
* Hook into pre_render_block to count form steps before inner blocks render.
*
Expand Down Expand Up @@ -702,6 +720,23 @@ public static function get_form_step_count() {
return self::$form_step_count;
}

/**
* Check if form should render in modal mode.
*
* @param array $atts Block attributes.
* @return bool
*/
private static function should_render_in_modal( $atts ) {
if ( apply_filters( 'jetpack_contact_form_render_in_modal', false ) ) {
// Check if modal is enabled via block attribute.
if ( $atts['modalEnabled'] === true ) {
return true;
}
}

return false;
}

/**
* Render the gutenblock form.
*
Expand All @@ -727,7 +762,38 @@ public static function gutenblock_render_form( $atts, $content ) {

self::load_view_scripts();

return Contact_Form::parse( $atts, do_blocks( $content ) );
$form_html = Contact_Form::parse( $atts, do_blocks( $content ) );

if ( self::should_render_in_modal( $atts ) ) {
$form_id = uniqid( 'jetpack-contact-form-modal-', true );
$trigger = $atts['modalTrigger'] ?? 'immediate';

self::$modal_forms[ $form_id ] = array(
'html' => $form_html,
'trigger' => $trigger,
);

// Enqueue modal CSS only once.
if ( ! self::$modal_css_enqueued ) {
wp_enqueue_style(
'jetpack-contact-form-modal',
plugins_url( 'modal.css', __FILE__ ),
array(),
\JETPACK__VERSION
);
self::$modal_css_enqueued = true;
}

// Hook into wp_footer to output modal (only once).
if ( ! has_action( 'wp_footer', array( __CLASS__, 'add_modal_to_footer' ) ) ) {
add_action( 'wp_footer', array( __CLASS__, 'add_modal_to_footer' ) );
}

// Return empty string - form will be rendered in footer.
return '';
}

return $form_html;
}

/**
Expand Down Expand Up @@ -808,6 +874,30 @@ public static function preload_endpoints( $paths ) {
return $paths;
}

/**
* Adds modal HTML to footer.
*
* @return void
*/
public static function add_modal_to_footer() {
if ( empty( self::$modal_forms ) ) {
return;
}

foreach ( self::$modal_forms as $form_id => $form_data ) {
$form_html = $form_data['html'];
$trigger = $form_data['trigger'] ?? 'immediate';

?>
<div class="jetpack-contact-form-modal" data-form-id="<?php echo esc_attr( $form_id ); ?>" data-trigger="<?php echo esc_attr( $trigger ); ?>">
<div class="jetpack-contact-form-modal__modal-content">
<?php echo $form_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
</div>
<?php
}
}

/**
* Loads scripts
*/
Expand Down
45 changes: 44 additions & 1 deletion projects/packages/forms/src/blocks/contact-form/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* External dependencies
*/
import { ThemeProvider } from '@automattic/jetpack-components';
import { useModuleStatus } from '@automattic/jetpack-shared-extension-utils';
import { hasFeatureFlag, useModuleStatus } from '@automattic/jetpack-shared-extension-utils';
import {
URLInput,
InspectorAdvancedControls,
Expand All @@ -18,6 +18,7 @@ import { createBlock } from '@wordpress/blocks';
import {
ExternalLink,
PanelBody,
SelectControl,
TextareaControl,
TextControl,
ToggleControl,
Expand Down Expand Up @@ -133,6 +134,8 @@ type JetpackContactFormAttributes = {
disableGoBack: boolean;
disableSummary: boolean;
notificationRecipients: string[];
modalEnabled: boolean;
modalTrigger: 'immediate' | 'leave';
};
type JetpackContactFormEditProps = {
name: string;
Expand All @@ -142,6 +145,8 @@ type JetpackContactFormEditProps = {
className: string;
};

const isModalFeatureFlagEnabled = hasFeatureFlag( 'jetpack-forms-modal-feature' );

function JetpackContactFormEdit( {
name,
attributes,
Expand All @@ -166,6 +171,8 @@ function JetpackContactFormEdit( {
disableGoBack,
disableSummary,
notificationRecipients,
modalEnabled,
modalTrigger,
} = attributes;
const showFormIntegrations = useConfigValue( 'isIntegrationsEnabled' );
const instanceId = useInstanceId( JetpackContactFormEdit );
Expand Down Expand Up @@ -923,6 +930,42 @@ function JetpackContactFormEdit( {
setAttributes={ setAttributes }
/>
</PanelBody>
{ isModalFeatureFlagEnabled && (
<PanelBody
title={ __( 'Modal settings', 'jetpack-forms' ) }
className="jetpack-contact-form__panel jetpack-contact-form__modal-panel"
initialOpen={ false }
>
<ToggleControl
label={ __( 'Display form in modal', 'jetpack-forms' ) }
checked={ modalEnabled || false }
onChange={ ( value: boolean ) => setAttributes( { modalEnabled: value } ) }
__nextHasNoMarginBottom={ true }
__next40pxDefaultSize={ true }
/>
{ modalEnabled && (
<SelectControl
label={ __( 'Modal trigger', 'jetpack-forms' ) }
value={ modalTrigger || 'immediate' }
options={ [
{
label: __( 'When form is loaded', 'jetpack-forms' ),
value: 'immediate',
},
{
label: __( 'When cursor leaves the page', 'jetpack-forms' ),
value: 'leave',
},
] }
onChange={ ( value: 'immediate' | 'leave' ) =>
setAttributes( { modalTrigger: value } )
}
__nextHasNoMarginBottom={ true }
__next40pxDefaultSize={ true }
/>
) }
</PanelBody>
) }
</InspectorControls>
<InspectorAdvancedControls>
<TextControl
Expand Down
50 changes: 50 additions & 0 deletions projects/packages/forms/src/blocks/contact-form/modal.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
body.jetpack-contact-form-modal-open {
overflow: hidden;
}

.jetpack-contact-form-modal {
visibility: hidden;
position: fixed;
z-index: 50000; /* Same as WP.com Action bar */
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: transparent;
transition: all 0.4s;
}

.jetpack-contact-form-modal.open {
background-color: rgba(0, 0, 0, 0.3);
visibility: visible;
}

.jetpack-contact-form-modal__modal-content {
position: relative;
visibility: hidden;
overflow: hidden;
top: 100%;
background-color: #fefefe;
margin: 15% auto;
width: 100%;
max-width: 600px;
border-radius: 10px;
box-sizing: border-box;
transition: all 0.4s;
text-wrap: balance;
padding: 32px;
}

.jetpack-contact-form-modal.open .jetpack-contact-form-modal__modal-content {
top: 0;
visibility: visible;
}

@media screen and (max-width: 640px) {

.jetpack-contact-form-modal__modal-content {
width: 94%;
}
}

Loading
Loading