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

Create the Ads module setup flow. #8348

Merged
merged 7 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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: 3 additions & 1 deletion assets/js/components/notifications/BannerNotifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export default function BannerNotifications() {
} );

const [ notification ] = useQueryArg( 'notification' );
const [ slug ] = useQueryArg( 'slug' );

if ( viewOnly ) {
return <ZeroDataStateNotifications />;
Expand All @@ -105,7 +106,8 @@ export default function BannerNotifications() {
{ adSenseModuleActive && <AdSenseAlerts /> }
<ModuleRecoveryAlert />
<ActivationBanner />
{ 'authentication_success' === notification && (
{ /* The Ads module uses the new, subtle notification rather than the old SetupSuccessBannerNotification */ }
{ 'authentication_success' === notification && slug !== 'ads' && (
<SetupSuccessBannerNotification />
) }
{ 'ad_blocking_recovery_setup_success' === notification && (
Expand Down
Copy link
Collaborator

@10upsimon 10upsimon Mar 19, 2024

Choose a reason for hiding this comment

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

@benbowler I was thinking about this for a while, and it may seem like a nit pick comment, but if this banner is only related to ads (and it is, given the textual nature of it) I feel we may want to be a bit more explicit in terms of naming convention here. The file name and its location do not really elude to the fact that it is an ads only banner.

I think it may be worth renaming this to AdsSetupSuccessSubtleNotification or similar, what do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

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

As per Slack comms, the thought from @benbowler here was that in future other modules may use this style, and conditional output will simply live within this same component.

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* SetupSuccessSubtleNotification component.
*
* Site Kit by Google, Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import CheckFill from '../../../svg/icons/check-fill.svg';
import { Button } from 'googlesitekit-components';
import { Grid, Cell, Row } from '../../material-components';
import useQueryArg from '../../hooks/useQueryArg';

export default function SetupSuccessSubtleNotification() {
const [ notification, setNotification ] = useQueryArg( 'notification' );
const [ slug, setSlug ] = useQueryArg( 'slug' );

const onDismiss = () => {
setNotification( undefined );
setSlug( undefined );
};

// The Ads module setup flow is the only module setup flow that uses this new style subtle
// notification, all others use the BannerNotification still.
if ( 'authentication_success' !== notification || slug !== 'ads' ) {
return null;
}

return (
<Grid>
<Row>
<Cell
alignMiddle
size={ 12 }
className="googlesitekit-subtle-notification"
>
<div className="googlesitekit-subtle-notification__icon">
<CheckFill width={ 24 } height={ 24 } />
</div>
<div className="googlesitekit-subtle-notification__content">
<p>
{ __(
'Success! Your Ads conversion ID was added to your site',
'google-site-kit'
) }
</p>
<p className="googlesitekit-subtle-notification__secondary_description">
{ __(
'You can now track conversions for your Ads campaigns.',
'google-site-kit'
) }
</p>
</div>
<Button tertiary onClick={ onDismiss }>
{ __( 'Got it', 'google-site-kit' ) }
</Button>
</Cell>
</Row>
</Grid>
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

The same applies to the stories file as I outlined above IRO a more ads explicit naming convention here.

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* SetupSuccessSubtleNotification Component Stories.
*
* Site Kit by Google, Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* External dependencies
*/
import { withQuery } from '@storybook/addon-queryparams';

/**
* Internal dependencies
*/
import SetupSuccessSubtleNotification from './SetupSuccessSubtleNotification';
import { WithTestRegistry } from '../../../../tests/js/utils';

function Template( { ...args } ) {
return <SetupSuccessSubtleNotification { ...args } />;
}

export const Ads = Template.bind( {} );
Ads.storyName = 'Setup Success - Ads';
Ads.parameters = {
query: {
notification: 'authentication_success',
slug: 'ads',
},
};
Ads.scenario = {
label: 'Global/SetupSuccessSubtleNotification/Ads',
};

export default {
title: 'Components/SetupSuccessSubtleNotification',
component: SetupSuccessSubtleNotification,
decorators: [
withQuery,
( Story, { parameters } ) => {
return (
<WithTestRegistry features={ parameters.features || [] }>
<Story />
</WithTestRegistry>
);
},
],
};
2 changes: 2 additions & 0 deletions assets/js/components/notifications/SubtleNotifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { Fragment } from '@wordpress/element';
* Internal dependencies
*/
import GA4AdSenseLinkedNotification from './GA4AdSenseLinkedNotification';
import SetupSuccessSubtleNotification from './SetupSuccessSubtleNotification';

export default function SubtleNotifications() {
// Each notification component rendered here has its own logic to determine
Expand All @@ -37,6 +38,7 @@ export default function SubtleNotifications() {
return (
<Fragment>
<GA4AdSenseLinkedNotification />
<SetupSuccessSubtleNotification />
</Fragment>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* Ads Module Ads Conversion ID component.
*
* Site Kit by Google, Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import Data from 'googlesitekit-data';
import { TextField } from 'googlesitekit-components';
import { MODULES_ADS } from '../../datastore/constants';
import VisuallyHidden from '../../../../components/VisuallyHidden';
import { isValidAdsConversionID } from '../../utils/validation';
import WarningIcon from '../../../../../svg/icons/warning-v2.svg';
const { useSelect, useDispatch } = Data;

export default function AdsConversionIDTextField() {
const adsConversionID = useSelect( ( select ) =>
select( MODULES_ADS ).getAdsConversionID()
);

const { setAdsConversionID } = useDispatch( MODULES_ADS );
const onChange = useCallback(
( { currentTarget } ) => {
let newValue = currentTarget.value.trim().toUpperCase();
// Automatically add the AW- prefix if not provided.
if ( 'AW-'.length < newValue.length && ! /^AW-/.test( newValue ) ) {
newValue = `AW-${ newValue }`;
}

if ( newValue !== adsConversionID ) {
setAdsConversionID( newValue );
}
},
[ adsConversionID, setAdsConversionID ]
);

const isValidValue = Boolean( isValidAdsConversionID( adsConversionID ) );

return (
<div className="googlesitekit-settings-module__fields-group">
<h4 className="googlesitekit-settings-module__fields-group-title">
{ __( 'Conversion ID', 'google-site-kit' ) }
</h4>
<TextField
label={ __( 'Ads Conversion ID', 'google-site-kit' ) }
className={ classnames( {
'mdc-text-field--error': ! isValidValue,
} ) }
helperText={
! isValidValue &&
__(
'Tracking for your Ads campaigns won’t work until you insert a valid ID',
'google-site-kit'
)
}
trailingIcon={
! isValidValue && (
<span className="googlesitekit-text-field-icon--error">
<VisuallyHidden>
{ __( 'Error', 'google-site-kit' ) }
</VisuallyHidden>
<WarningIcon width={ 14 } height={ 12 } />
</span>
)
}
outlined
value={ adsConversionID }
onChange={ onChange }
/>
</div>
);
}
19 changes: 19 additions & 0 deletions assets/js/modules/ads/components/common/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Ads Common components.
*
* Site Kit by Google, Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export { default as AdsConversionIDTextField } from './AdsConversionIDTextField';
92 changes: 92 additions & 0 deletions assets/js/modules/ads/components/setup/SetupForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* Ads Setup form.
*
* Site Kit by Google, Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* External dependencies
*/
import PropTypes from 'prop-types';

/**
* WordPress dependencies
*/
import { useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import Data from 'googlesitekit-data';
import { SpinnerButton } from 'googlesitekit-components';
import { MODULES_ADS } from '../../datastore/constants';
import { CORE_LOCATION } from '../../../../googlesitekit/datastore/location/constants';
import StoreErrorNotices from '../../../../components/StoreErrorNotices';
import AdsConversionIDTextField from '../common/AdsConversionIDTextField';
const { useSelect, useDispatch } = Data;

export default function SetupForm( { finishSetup } ) {
const canSubmitChanges = useSelect( ( select ) =>
select( MODULES_ADS ).canSubmitChanges()
);
const isSaving = useSelect(
( select ) =>
select( MODULES_ADS ).isDoingSubmitChanges() ||
select( CORE_LOCATION ).isNavigating()
);

const { submitChanges } = useDispatch( MODULES_ADS );

const submitForm = useCallback(
async ( event ) => {
event.preventDefault();

const { error } = await submitChanges();

if ( ! error ) {
finishSetup();
}
},
[ finishSetup, submitChanges ]
);

return (
<form className="googlesitekit-ads-setup__form" onSubmit={ submitForm }>
<StoreErrorNotices moduleSlug="ads" storeName={ MODULES_ADS } />

<div className="googlesitekit-setup-module__inputs">
<AdsConversionIDTextField />
</div>

<div className="googlesitekit-setup-module__action">
<SpinnerButton
disabled={ ! canSubmitChanges || isSaving }
isSaving={ isSaving }
>
{ __( 'Complete set up', 'google-site-kit' ) }
tofumatt marked this conversation as resolved.
Show resolved Hide resolved
</SpinnerButton>
</div>
</form>
);
}

SetupForm.propTypes = {
finishSetup: PropTypes.func,
};

SetupForm.defaultProps = {
finishSetup: () => {},
};
Loading
Loading