Skip to content
Merged
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
148 changes: 138 additions & 10 deletions assets/blocks/sign-in-with-google/Edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,159 @@
/**
* WordPress dependencies
*/
import { useBlockProps } from '@wordpress-core/block-editor';
import { useBlockProps, InspectorControls } from '@wordpress-core/block-editor';
import {
PanelBody,
SelectControl,
TextControl,
} from '@wordpress-core/components';
import { Fragment } from '@wordpress-core/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import {
SIGN_IN_WITH_GOOGLE_SHAPES,
SIGN_IN_WITH_GOOGLE_TEXTS,
SIGN_IN_WITH_GOOGLE_THEMES,
} from '@/js/modules/sign-in-with-google/datastore/constants';
import SignInWithGoogleIcon from './icon.svg';

const DEFAULT_OPTION = {
label: __( 'Default (use site settings)', 'google-site-kit' ),
value: '',
};

/**
* Sign in with Google Block Edit component.
*
* @since 1.147.0
* @since n.e.x.t Added attributes, setAttributes, and className to props.
*
* @param {Object} props Component props.
* @param {Object} props.attributes Block attributes.
* @param {Function} props.setAttributes Block attribute setter.
* @param {string} props.className Block class name.
* @return {Element} Element to render.
*/
export default function Edit() {
const blockProps = useBlockProps();
export default function Edit( { attributes, setAttributes, className } ) {
const { shape, text, theme, buttonClassName } = attributes;
const blockProps = useBlockProps( { className } );

function createSelectOptions( options ) {
return [ DEFAULT_OPTION, ...options ];
}

function handleSelectChange( attribute, value ) {
setAttributes( { [ attribute ]: value || undefined } );
}

function handleClassChange( value ) {
const sanitizedValue = value.trim();
setAttributes( {
buttonClassName: sanitizedValue ? sanitizedValue : undefined,
} );
}

function createSelectChangeHandler( attribute ) {
return function onChange( value ) {
handleSelectChange( attribute, value );
};
}

const dataAttributes = {
...( shape ? { 'data-googlesitekit-siwg-shape': shape } : {} ),
...( text ? { 'data-googlesitekit-siwg-text': text } : {} ),
...( theme ? { 'data-googlesitekit-siwg-theme': theme } : {} ),
};

const combinedClassName = [
'googlesitekit-blocks-sign-in-with-google',
buttonClassName || '',
]
.filter( Boolean )
.join( ' ' );

return (
<div { ...blockProps }>
<div
className="googlesitekit-blocks-sign-in-with-google"
style={ { maxWidth: '180px', minWidth: '120px' } }
>
<SignInWithGoogleIcon />
<Fragment>
<InspectorControls>
<PanelBody
title={ __( 'Button settings', 'google-site-kit' ) }
initialOpen
>
<SelectControl
label={ __( 'Button shape', 'google-site-kit' ) }
value={ shape ?? '' }
onChange={ createSelectChangeHandler( 'shape' ) }
options={ createSelectOptions(
SIGN_IN_WITH_GOOGLE_SHAPES
) }
// Opt-in to new WordPress components styles introduced in Gutenberg 6.7+.
// Safe for pre-6.7 WordPress: these props are ignored in older versions.
// __next40pxDefaultSize - use new 40px height (replaces deprecated 36px)
// __nextHasNoMarginBottom - remove legacy bottom margin
// Ref: https://github.com/WordPress/gutenberg/pull/61132
__next40pxDefaultSize
__nextHasNoMarginBottom
Comment on lines +113 to +114
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

SelectControl and TextControl are getting depricated php notices unless these props are included

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's add some documentation for that in the actual component then, preferably with links to the props or the code you found this in, since without any context these look strange/unintuitive.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I extracted it from the deprication notice that was raised in CI e2e. Added as short inline comment

/>
<SelectControl
label={ __( 'Button text', 'google-site-kit' ) }
value={ text ?? '' }
onChange={ createSelectChangeHandler( 'text' ) }
options={ createSelectOptions(
SIGN_IN_WITH_GOOGLE_TEXTS
) }
// Opt-in to new WordPress components styles introduced in Gutenberg 6.7+.
// Safe for pre-6.7 WordPress: these props are ignored in older versions.
// __next40pxDefaultSize - use new 40px height (replaces deprecated 36px)
// __nextHasNoMarginBottom - remove legacy bottom margin
// Ref: https://github.com/WordPress/gutenberg/pull/61132
__next40pxDefaultSize
__nextHasNoMarginBottom
/>
<SelectControl
label={ __( 'Button theme', 'google-site-kit' ) }
value={ theme ?? '' }
onChange={ createSelectChangeHandler( 'theme' ) }
options={ createSelectOptions(
SIGN_IN_WITH_GOOGLE_THEMES
) }
// Opt-in to new WordPress components styles introduced in Gutenberg 6.7+.
// Safe for pre-6.7 WordPress: these props are ignored in older versions.
// __next40pxDefaultSize - use new 40px height (replaces deprecated 36px)
// __nextHasNoMarginBottom - remove legacy bottom margin
// Ref: https://github.com/WordPress/gutenberg/pull/61132
__next40pxDefaultSize
__nextHasNoMarginBottom
/>
<TextControl
label={ __( 'HTML class', 'google-site-kit' ) }
help={ __(
'Add optional classes to customize the button in the editor and on the frontend.',
'google-site-kit'
) }
value={ buttonClassName || '' }
onChange={ handleClassChange }
// Opt-in to new WordPress components styles introduced in Gutenberg 6.7+.
// Safe for pre-6.7 WordPress: these props are ignored in older versions.
// __next40pxDefaultSize - use new 40px height (replaces deprecated 36px)
// __nextHasNoMarginBottom - remove legacy bottom margin
// Ref: https://github.com/WordPress/gutenberg/pull/61132
__next40pxDefaultSize
__nextHasNoMarginBottom
/>
</PanelBody>
</InspectorControls>
<div { ...blockProps }>
<div
className={ combinedClassName }
style={ { maxWidth: '180px', minWidth: '120px' } }
{ ...dataAttributes }
>
<SignInWithGoogleIcon />
</div>
</div>
</div>
</Fragment>
);
}
25 changes: 24 additions & 1 deletion assets/blocks/sign-in-with-google/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,28 @@
"category": "widgets",
"icon": "google",
"description": "Allow users to sign in to your site using their Google Account.",
"textdomain": "google-site-kit"
"textdomain": "google-site-kit",
"attributes": {
"shape": {
"type": "string",
"enum": [ "", "rectangular", "pill" ]
},
"text": {
"type": "string",
"enum": [
"",
"continue_with",
"signin",
"signin_with",
"signup_with"
]
},
"theme": {
"type": "string",
"enum": [ "", "outline", "filled_blue", "filled_black" ]
},
"buttonClassName": {
"type": "string"
}
}
}
32 changes: 30 additions & 2 deletions includes/Modules/Sign_In_With_Google/Sign_In_With_Google_Block.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,43 @@ function () {
* Render callback for the Sign in with Google block.
*
* @since 1.147.0
* @since n.e.x.t Added the `$attributes` parameter.
*
* @param array $attributes Block attributes.
* @return string Rendered block.
*/
public function render_callback() {
public function render_callback( $attributes = array() ) {
// If the user is already signed in, do not render a Sign in
// with Google button.
if ( is_user_logged_in() ) {
return '';
}

$attributes = is_array( $attributes ) ? $attributes : array();
$button_args = array();

$allowed_attributes = array(
'text' => wp_list_pluck( Settings::TEXTS, 'value' ),
'theme' => wp_list_pluck( Settings::THEMES, 'value' ),
'shape' => wp_list_pluck( Settings::SHAPES, 'value' ),
);

foreach ( array( 'text', 'theme', 'shape' ) as $key ) {
if ( ! empty( $attributes[ $key ] ) && in_array( $attributes[ $key ], $allowed_attributes[ $key ], true ) ) {
$button_args[ $key ] = $attributes[ $key ];
}
}

if ( ! empty( $attributes['buttonClassName'] ) && is_string( $attributes['buttonClassName'] ) ) {
$classes = array_filter(
preg_split( '/\s+/', trim( $attributes['buttonClassName'] ) )
);

if ( ! empty( $classes ) ) {
$button_args['class'] = $classes;
}
}

ob_start();
/**
* Display the Sign in with Google button.
Expand All @@ -94,7 +122,7 @@ public function render_callback() {
*
* @param array $args Optional arguments to customize button attributes.
*/
do_action( 'googlesitekit_render_sign_in_with_google_button' );
do_action( 'googlesitekit_render_sign_in_with_google_button', $button_args );
return ob_get_clean();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php
/**
* Class Google\Site_Kit\Tests\Modules\Sign_In_With_Google\Sign_In_With_Google_BlockTest
*
* @package Google\Site_Kit\Tests\Modules\Sign_In_With_Google
* @copyright 2025 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/

namespace Google\Site_Kit\Tests\Modules\Sign_In_With_Google;

use Google\Site_Kit\Context;
use Google\Site_Kit\Modules\Sign_In_With_Google;
use Google\Site_Kit\Modules\Sign_In_With_Google\Sign_In_With_Google_Block;
use Google\Site_Kit\Tests\TestCase;

class Sign_In_With_Google_BlockTest extends TestCase {

/**
* Block instance.
*
* @var Sign_In_With_Google_Block
*/
private $block;

/**
* Module instance.
*
* @var Sign_In_With_Google
*/
private $module;

public function set_up() {
parent::set_up();

$context = new Context( GOOGLESITEKIT_PLUGIN_MAIN_FILE );

$this->block = new Sign_In_With_Google_Block( $context );
$this->module = new Sign_In_With_Google( $context );

add_action(
'googlesitekit_render_sign_in_with_google_button',
array( $this->module, 'render_sign_in_with_google_button' ),
10,
1
);
}

public function tear_down() {
remove_action(
'googlesitekit_render_sign_in_with_google_button',
array( $this->module, 'render_sign_in_with_google_button' ),
10
);

parent::tear_down();
}

public function test_render_callback_with_overrides() {
wp_set_current_user( 0 );

$output = $this->block->render_callback(
array(
'shape' => 'pill',
'text' => 'signin_with',
'theme' => 'filled_black',
'buttonClassName' => 'custom-class another-class invalid@class',
)
);

$this->assertStringContainsString( 'data-googlesitekit-siwg-shape="pill"', $output, 'Expected shape data attribute to be present.' );
$this->assertStringContainsString( 'data-googlesitekit-siwg-text="signin_with"', $output, 'Expected text data attribute to be present.' );
$this->assertStringContainsString( 'data-googlesitekit-siwg-theme="filled_black"', $output, 'Expected theme data attribute to be present.' );
$this->assertStringContainsString( 'class="googlesitekit-sign-in-with-google__frontend-output-button custom-class another-class invalidclass"', $output, 'Expected sanitized classes to be present.' );
}

public function test_render_callback_with_defaults_outputs_no_overrides() {
wp_set_current_user( 0 );

$output = $this->block->render_callback(
array(
'shape' => '',
'text' => '',
'theme' => '',
)
);

$this->assertStringNotContainsString( 'data-googlesitekit-siwg-shape', $output, 'Default selection should not add shape data attribute.' );
$this->assertStringNotContainsString( 'data-googlesitekit-siwg-text', $output, 'Default selection should not add text data attribute.' );
$this->assertStringNotContainsString( 'data-googlesitekit-siwg-theme', $output, 'Default selection should not add theme data attribute.' );
$this->assertStringContainsString( 'class="googlesitekit-sign-in-with-google__frontend-output-button"', $output, 'Default output should include the base class only.' );
}
}
Loading