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

364 add the integration card for google site kit #21946

Open
wants to merge 16 commits into
base: trunk
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions inc/options/class-wpseo-option-wpseo.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ class WPSEO_Option_Wpseo extends WPSEO_Option {
'new_post_types' => [],
'new_taxonomies' => [],
'show_new_content_type_notification' => false,
'google_site_kit_connected' => false,
];

/**
Expand Down
1 change: 1 addition & 0 deletions packages/js/images/site-kit-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/js/src/integrations-page/algolia-integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const AlgoliaIntegration = ( {
className="yst-flex yst-items-center yst-mt-3 yst-no-underline yst-font-medium"
target="_blank"
>
Learn more
{ __( "Learn more", "wordpress-seo" ) }
<span className="yst-sr-only">
{
/* translators: Hidden accessibility text. */
Expand Down
139 changes: 139 additions & 0 deletions packages/js/src/integrations-page/google-site-kit-integration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { __, sprintf } from "@wordpress/i18n";
import { CheckIcon, XIcon } from "@heroicons/react/solid";
import { Fragment, createInterpolateElement, useMemo, useCallback } from "@wordpress/element";
import { SimpleIntegration } from "./simple-integration";
import { ReactComponent as SiteKitLogo } from "../../images/site-kit-logo.svg";
import { get } from "lodash";
import { useToggleState } from "@yoast/ui-library";
import { SiteKitConsentModal } from "../shared-admin/components";

const integration = {
name: "Site Kit by Google",
Copy link
Member

Choose a reason for hiding this comment

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

Not translated?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

None of the cards names are translated, so I guess we should translate all the rest (or some)?

claim: createInterpolateElement(
sprintf(
/* translators: 1: bold open tag; 2: Site Kit by Google; 3: bold close tag. */
__( "Get valuable insights with %1$s%2$s%3$s", "wordpress-seo" ),
"<strong>",
"Site Kit by Google",
"</strong>"
), {
strong: <strong />,
}
),
learnMoreLink: "https://yoa.st/integrations-google-site-kit",
logoLink: "https://yoa.st/integrations-google-site-kit",
slug: "google-site-kit",
description: __( "View traffic and search rankings on your dashboard by connecting your Google account.", "wordpress-seo" ),
isPremium: false,
isNew: false,
isMultisiteAvailable: false,
logo: SiteKitLogo,
};

const buttonLabels = {
Copy link
Member

Choose a reason for hiding this comment

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

Why only labels and not also the rest of the button props?

install: sprintf(
/* translators: 1: Site Kit by Google */
__( "Install %1$s", "wordpress-seo" ),
"Site Kit by Google"
Copy link
Member

Choose a reason for hiding this comment

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

),
activate: sprintf(
/* translators: 1: Site Kit by Google */
__( "Activate %1$s", "wordpress-seo" ),
"Site Kit by Google"
),
setup: sprintf(
/* translators: 1: Site Kit by Google */
__( "Set up %1$s", "wordpress-seo" ),
"Site Kit by Google"
),
connect: sprintf(
/* translators: 1: Site Kit by Google */
__( "Connect %1$s", "wordpress-seo" ),
"Site Kit by Google"
),
disconnect: __( "Disconnect", "wordpress-seo" ),
};

/**
* The Site Kit integration component.
*
* @returns {WPElement} The Site Kit integration component.
*/
export const GoogleSiteKitIntegration = () => {
const { isActive, afterSetup, isInstalled, isConnected } = get( window, "wpseoIntegrationsData.googleSiteKit", { isActive: false, afterSetup: false, isInstalled: false, isConnected: false } );
Copy link
Member

Choose a reason for hiding this comment

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

I think you should not read the window state inside this component.
Instead, take an example of the Woo integration and use props to connect it from the recommended plugins in this case.

That would've made your tests also more straight forward to use normal props for component rendering.

const [ isModalOpen, toggleModal ] = useToggleState( false );

const getButtonConfig = useCallback( () => {
Copy link
Member

Choose a reason for hiding this comment

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

Instead of config. These are the props, right?

E.g. getButtonProps

const button = {
className: "yst-mt-6 yst-w-full",
as: "a",
Copy link
Member

Choose a reason for hiding this comment

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

Weird that the as prop default is now swapped 😅

id: "google-site-kit-button",
};

if ( ! isInstalled ) {
Copy link
Member

Choose a reason for hiding this comment

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

This giant if/else is a bit bewildering.

Later on you combine states as well. I wonder if this can be done in an easier way.
Really, you have just a button in the content and a status in the footer.

button.children = buttonLabels.install;
button.href = "/wp-admin/plugin-install.php?s=google%2520site%2520kit&tab=search&type=term";
Copy link
Member

Choose a reason for hiding this comment

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

This and below, the admin URLs should be retrieved from PHP to ensure compatibility for changes. See the integration' use of self_admin_url

} else if ( ! isActive ) {
button.children = buttonLabels.activate;
button.href = "/wp-admin/plugins.php";
} else if ( ! afterSetup ) {
button.children = buttonLabels.setup;
button.href = "/wp-admin/admin.php?page=googlesitekit-splash";
} else if ( ! isConnected ) {
button.children = buttonLabels.connect;
button.onClick = toggleModal;
button.as = "button";
} else if ( isConnected ) {
button.children = buttonLabels.disconnect;
button.as = "button";
button.variant = "secondary";
}

return button;
}, [ isInstalled, isActive, afterSetup, isConnected ] );

const notConnected = useMemo( () => ! isConnected || ! afterSetup, [ isConnected, afterSetup ] );
const pluginNotDetected = useMemo( () => ! isActive || ! isInstalled, [ isActive, isInstalled ] );
const successfullyConnected = useMemo( () => isActive && afterSetup && isConnected, [ isActive, afterSetup, isConnected ] );
Copy link
Member

Choose a reason for hiding this comment

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

These useMemo usages seem a bit overkill for simple boolean logic


return (
<>
<SimpleIntegration
integration={ integration }
isActive={ isInstalled && isActive }
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
isActive={ isInstalled && isActive }
isActive={ isInstalled && isActive }

button={ getButtonConfig( isInstalled, isActive, afterSetup, isConnected ) }
>

{ successfullyConnected && <Fragment>
<span className="yst-text-slate-700 yst-font-medium">{ __( "Successfully connected", "wordpress-seo" ) }</span>
<CheckIcon
className="yst-h-5 yst-w-5 yst-text-green-400 yst-flex-shrink-0"
/>
</Fragment> }

{ notConnected && isActive && <Fragment>
<span className="yst-text-slate-700 yst-font-medium">
{
__( "Not connected", "wordpress-seo" )
}
</span>
<XIcon
className="yst-h-5 yst-w-5 yst-text-red-500 yst-flex-shrink-0"
/>
</Fragment> }

{ pluginNotDetected && <Fragment>
<span className="yst-text-slate-700 yst-font-medium">
{
__( "Plugin not detected", "wordpress-seo" )
}
</span>
<XIcon
className="yst-h-5 yst-w-5 yst-text-red-500 yst-flex-shrink-0"
/>
</Fragment> }
</SimpleIntegration>
<SiteKitConsentModal isOpen={ isModalOpen } onClose={ toggleModal } />
</>
);
};
2 changes: 1 addition & 1 deletion packages/js/src/integrations-page/other-integrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const mastodonIntegration = {
"Yoast SEO Premium"
),
isPremium: true,
isNew: true,
isNew: false,
isMultisiteAvailable: true,
logo: MastodonLogo,
upsellLink: "https://yoa.st/get-mastodon-integration",
Expand Down
13 changes: 12 additions & 1 deletion packages/js/src/integrations-page/recommended-integrations.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { createInterpolateElement } from "@wordpress/element";
import { __, sprintf } from "@wordpress/i18n";
import { get } from "lodash";
import { ReactComponent as SemrushLogo } from "../../images/semrush-logo.svg";
import { ReactComponent as WincherLogo } from "../../images/wincher-logo.svg";
import { getInitialState, getIsMultisiteAvailable, getIsNetworkControlEnabled, updateIntegrationState } from "./helper";

import { ToggleableIntegration } from "./toggleable-integration";
import { GoogleSiteKitIntegration } from "./google-site-kit-integration";

const integrations = [
{
Expand Down Expand Up @@ -61,7 +63,9 @@ const integrations = [
},
];

export const RecommendedIntegrations = [
const isGoogleSiteKitFeatureEnabled = get( window, "wpseoIntegrationsData.googleSiteKit.featureEnabled", false );

const RecommendedIntegrations = [
integrations.map( ( integration, index ) => {
return (
<ToggleableIntegration
Expand All @@ -76,3 +80,10 @@ export const RecommendedIntegrations = [
);
} ),
];


if ( isGoogleSiteKitFeatureEnabled ) {
RecommendedIntegrations.push( <GoogleSiteKitIntegration key={ integrations.length } /> );
}

export { RecommendedIntegrations };
6 changes: 3 additions & 3 deletions packages/js/src/integrations-page/schema-api-integrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const integrations = [
"Yoast SEO"
),
isPremium: false,
isNew: true,
isNew: false,
isMultisiteAvailable: true,
logo: SSPLogo,
},
Expand All @@ -85,7 +85,7 @@ const integrations = [
"Yoast SEO"
),
isPremium: true,
isNew: true,
isNew: false,
isMultisiteAvailable: true,
logo: EDDLogo,
},
Expand All @@ -111,7 +111,7 @@ const integrations = [
"Yoast SEO"
),
isPremium: false,
isNew: true,
isNew: false,
isMultisiteAvailable: true,
logo: RecipeMakerLogo,
},
Expand Down
11 changes: 8 additions & 3 deletions packages/js/src/integrations-page/simple-integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import { getIsFreeIntegrationOrPremiumAvailable } from "./helper";
* @param {object} integration The integration.
* @param {boolean} isActive The integration state.
* @param {wp.Element} children The child components.
* @param {wp.Element} button The button component.
Copy link
Member

Choose a reason for hiding this comment

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

These are actually just button props, of type object. Not an element like this.
You made it a nullable object in the propTypes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right! I was first thinking to pass the element and then decided to pass the props...

*
* @returns {WPElement} A card representing an integration.
*/
export const SimpleIntegration = ( { integration, isActive, children } ) => {
export const SimpleIntegration = ( { integration, isActive, children, button } ) => {
const IntegrationLogo = integration.logo;

return (
Expand Down Expand Up @@ -59,7 +60,7 @@ export const SimpleIntegration = ( { integration, isActive, children } ) => {
className="yst-flex yst-items-center yst-mt-3 yst-no-underline yst-font-medium"
target="_blank"
>
Learn more
{ __( "Learn more", "wordpress-seo" ) }
<span className="yst-sr-only">
{
/* translators: Hidden accessibility text. */
Expand All @@ -68,11 +69,13 @@ export const SimpleIntegration = ( { integration, isActive, children } ) => {
</span>
<ArrowSmRightIcon className="yst-h-4 yst-w-4 yst-ms-1 yst-icon-rtl" />
</Link> }

{ button && <Button { ...button } /> }
Comment on lines +72 to +73
Copy link
Member

Choose a reason for hiding this comment

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

Getting less simple by the commit 🙈

</div>
</Card.Content>
<Card.Footer>
{ ! getIsFreeIntegrationOrPremiumAvailable( integration ) && <Button
id={ `${ integration.name }-upsell-button` }
id={ `${ integration.slug }-upsell-button` }
Copy link
Member

Choose a reason for hiding this comment

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

Might this have consequences for automated tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, got an approval from Natalia.

type="button"
as="a"
href={ integration.upsellLink }
Expand Down Expand Up @@ -121,9 +124,11 @@ SimpleIntegration.propTypes = {
PropTypes.node,
PropTypes.arrayOf( PropTypes.node ),
] ),
button: PropTypes.object,
};

SimpleIntegration.defaultProps = {
isActive: true,
children: [],
button: null,
};
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const ToggleableIntegration = ( {
className="yst-flex yst-items-center yst-mt-3 yst-no-underline yst-font-medium"
target="_blank"
>
Learn more
{ __( "Learn more", "wordpress-seo" ) }
<span className="yst-sr-only">
{
/* translators: Hidden accessibility text. */
Expand Down
1 change: 1 addition & 0 deletions packages/js/src/shared-admin/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export { SidebarRecommendations } from "./sidebar-recommendations";
export { UnsavedChangesModal } from "./unsaved-changes-modal";
export { VideoFlow } from "./video-flow";
export { ReactComponent as YoastLogo } from "./yoast-logo.svg";
export { SiteKitConsentModal } from "./site-kit-consent-modal";
32 changes: 32 additions & 0 deletions packages/js/src/shared-admin/components/site-kit-consent-modal.js
Copy link
Member

Choose a reason for hiding this comment

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

You expect this to be reused in the dashboard?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Modal } from "@yoast/ui-library";
import { __ } from "@wordpress/i18n";
import { PropTypes } from "prop-types";

/**
* The Site Kit consent modal component.
*
* @param {boolean} isOpen Whether the modal is open.
* @param {Function} onClose Callback to close the modal.
*
* @returns {WPElement} The Site Kit consent modal component.
Copy link
Member

Choose a reason for hiding this comment

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

We normally type this as JSX.Element.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, my mistake!

*/
export const SiteKitConsentModal = ( { isOpen, onClose } ) => {
return (
<Modal
isOpen={ isOpen }
onClose={ onClose }
>
<Modal.Panel>
<Modal.Title>{ __( "Connect Site Kit by Google", "wordpress-seo" ) }</Modal.Title>
<Modal.Description>
<p>{ __( "Connect your Google account to view traffic and search rankings on your dashboard.", "wordpress-seo" ) }</p>
Copy link
Member

Choose a reason for hiding this comment

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

The Modal.Description is a p by default. This extra paragraph inside triggers a console error.

</Modal.Description>
</Modal.Panel>
</Modal>
);
};

SiteKitConsentModal.propTypes = {
isOpen: PropTypes.bool,
onClose: PropTypes.func,
};
Loading
Loading