+ );
+}
diff --git a/assets/js/modules/ads/components/common/index.js b/assets/js/modules/ads/components/common/index.js
new file mode 100644
index 00000000000..57e6317aff9
--- /dev/null
+++ b/assets/js/modules/ads/components/common/index.js
@@ -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';
diff --git a/assets/js/modules/ads/components/setup/SetupForm.js b/assets/js/modules/ads/components/setup/SetupForm.js
new file mode 100644
index 00000000000..59cc460613c
--- /dev/null
+++ b/assets/js/modules/ads/components/setup/SetupForm.js
@@ -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 (
+
+ );
+}
+
+SetupForm.propTypes = {
+ finishSetup: PropTypes.func,
+};
+
+SetupForm.defaultProps = {
+ finishSetup: () => {},
+};
diff --git a/assets/js/modules/ads/components/setup/SetupForm.stories.js b/assets/js/modules/ads/components/setup/SetupForm.stories.js
new file mode 100644
index 00000000000..89753e484de
--- /dev/null
+++ b/assets/js/modules/ads/components/setup/SetupForm.stories.js
@@ -0,0 +1,74 @@
+/**
+ * SetupForm 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.
+ */
+
+/**
+ * Internal dependencies
+ */
+import { MODULES_ADS } from '../../datastore/constants';
+import {
+ provideModuleRegistrations,
+ provideSiteInfo,
+ provideModules,
+} from '../../../../../../tests/js/utils';
+import ModuleSetup from '../../../../components/setup/ModuleSetup';
+import WithRegistrySetup from '../../../../../../tests/js/WithRegistrySetup';
+
+function Template() {
+ return ;
+}
+
+export const Default = Template.bind( null );
+Default.storyName = 'Default';
+Default.scenario = {
+ label: 'Modules/Ads/Setup/SetupForm/Default',
+ delay: 250,
+};
+
+export default {
+ title: 'Modules/Ads/Setup/SetupForm',
+ decorators: [
+ ( Story ) => {
+ const setupRegistry = ( registry ) => {
+ provideModules( registry, [
+ {
+ slug: 'ads',
+ active: true,
+ connected: true,
+ },
+ ] );
+
+ provideSiteInfo( registry );
+ provideModuleRegistrations( registry );
+
+ registry
+ .dispatch( MODULES_ADS )
+ .setSettings( { adsConversionID: 'AW-123456789' } );
+ };
+
+ return (
+
+
+
+ );
+ },
+ ],
+ parameters: {
+ features: [ 'adsModule' ],
+ padding: 0,
+ },
+};
diff --git a/assets/js/modules/ads/components/setup/SetupMain.js b/assets/js/modules/ads/components/setup/SetupMain.js
new file mode 100644
index 00000000000..065976b4c60
--- /dev/null
+++ b/assets/js/modules/ads/components/setup/SetupMain.js
@@ -0,0 +1,69 @@
+/**
+ * Ads Main setup 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 PropTypes from 'prop-types';
+
+/**
+ * WordPress dependencies
+ */
+import { __, _x } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import AdsIcon from '../../../../../svg/graphics/ads.svg';
+import SetupForm from './SetupForm';
+
+export default function SetupMain( { finishSetup } ) {
+ return (
+
+ { __(
+ 'Ad your conversion ID below. Site Kit will place it on your site so you can track the performance of your Google Ads campaigns.',
+ 'google-site-kit'
+ ) }
+
+ { __(
+ 'You can always change this later in Site Kit Settings.',
+ 'google-site-kit'
+ ) }
+
+
+
+
+ );
+}
+
+SetupMain.propTypes = {
+ finishSetup: PropTypes.func,
+};
+
+SetupMain.defaultProps = {
+ finishSetup: () => {},
+};
diff --git a/assets/js/modules/ads/components/setup/index.js b/assets/js/modules/ads/components/setup/index.js
new file mode 100644
index 00000000000..d1bf25e1bc9
--- /dev/null
+++ b/assets/js/modules/ads/components/setup/index.js
@@ -0,0 +1,20 @@
+/**
+ * Ads Setup 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 SetupForm } from './SetupForm';
+export { default as SetupMain } from './SetupMain';
diff --git a/assets/js/modules/ads/datastore/base.js b/assets/js/modules/ads/datastore/base.js
index 324b115f221..0cb0b85b9d8 100644
--- a/assets/js/modules/ads/datastore/base.js
+++ b/assets/js/modules/ads/datastore/base.js
@@ -26,8 +26,8 @@ import { submitChanges, validateCanSubmitChanges } from './settings';
const baseModuleStore = Modules.createModuleStore( 'ads', {
ownedSettingsSlugs: [ 'adsConversionID' ],
storeName: MODULES_ADS,
- settingSlugs: [ 'adsConversionID' ],
- requiresSetup: false,
+ settingSlugs: [ 'adsConversionID', 'ownerID' ],
+ requiresSetup: true,
submitChanges,
validateCanSubmitChanges,
} );
diff --git a/assets/js/modules/ads/index.js b/assets/js/modules/ads/index.js
index 7013e87d396..05e4d8bf37b 100644
--- a/assets/js/modules/ads/index.js
+++ b/assets/js/modules/ads/index.js
@@ -16,12 +16,18 @@
* limitations under the License.
*/
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+
/**
* Internal dependencies
*/
import AdsIcon from '../../../svg/graphics/ads.svg';
import { isFeatureEnabled } from '../../features';
import { SettingsEdit, SettingsView } from './components/settings';
+import SetupMain from './components/setup/SetupMain';
import { MODULES_ADS } from './datastore/constants';
export { registerStore } from './datastore';
@@ -32,7 +38,18 @@ export const registerModule = ( modules ) => {
storeName: MODULES_ADS,
SettingsEditComponent: SettingsEdit,
SettingsViewComponent: SettingsView,
+ SetupComponent: SetupMain,
Icon: AdsIcon,
+ features: [
+ __(
+ 'Tagging necessary for your ads campaigns to work',
+ 'google-site-kit'
+ ),
+ __(
+ 'Conversion tracking for your ads campaigns',
+ 'google-site-kit'
+ ),
+ ],
} );
}
};
diff --git a/assets/js/modules/ads/utils/validation.js b/assets/js/modules/ads/utils/validation.js
new file mode 100644
index 00000000000..63cdeab44ed
--- /dev/null
+++ b/assets/js/modules/ads/utils/validation.js
@@ -0,0 +1,31 @@
+/**
+ * Validation utilities.
+ *
+ * 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.
+ */
+
+/**
+ * Checks if the given ads conversion ID is valid.
+ *
+ * @since 1.32.0
+ * @since 1.121.0 Migrated from analytics to analytics-4.
+ * @since n.e.x.t Migrated from analytics-4 to ads.
+ *
+ * @param {*} value Conversion ID to test.
+ * @return {boolean} Whether or not the given ID is valid.
+ */
+export function isValidAdsConversionID( value ) {
+ return typeof value === 'string' && /^AW-[0-9]+$/.test( value );
+}
diff --git a/assets/js/modules/ads/utils/validation.test.js b/assets/js/modules/ads/utils/validation.test.js
new file mode 100644
index 00000000000..9705f870976
--- /dev/null
+++ b/assets/js/modules/ads/utils/validation.test.js
@@ -0,0 +1,48 @@
+/**
+ * Validation function tests.
+ *
+ * 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.
+ */
+
+/**
+ * Internal dependencies
+ */
+import { isValidAdsConversionID } from './validation';
+
+describe( 'modules/ads validations', () => {
+ describe( 'isValidAdsConversionID', () => {
+ it( 'should return TRUE when a valid AdsConversionID is passed', () => {
+ expect( isValidAdsConversionID( 'AW-123456789' ) ).toBe( true );
+ } );
+
+ it.each( [
+ [ 'false', false ],
+ [ 'an integer', 12345 ],
+ [ 'an empty string', '' ],
+ [ 'a string not starting with AW', 'AB-123456789' ],
+ [
+ 'a string starts with AW but ends without numbers',
+ 'AW-ABCDEFGHI',
+ ],
+ ] )(
+ 'should return FALSE when %s is passed',
+ ( _, adsConversionID ) => {
+ expect( isValidAdsConversionID( adsConversionID ) ).toBe(
+ false
+ );
+ }
+ );
+ } );
+} );
diff --git a/assets/js/modules/analytics-4/components/common/AdsConversionIDTextField.js b/assets/js/modules/analytics-4/components/common/AdsConversionIDTextField.js
index 9e190a6d3ee..77f47fca33b 100644
--- a/assets/js/modules/analytics-4/components/common/AdsConversionIDTextField.js
+++ b/assets/js/modules/analytics-4/components/common/AdsConversionIDTextField.js
@@ -34,8 +34,8 @@ import Data from 'googlesitekit-data';
import { TextField } from 'googlesitekit-components';
import { MODULES_ANALYTICS_4 } from '../../datastore/constants';
import VisuallyHidden from '../../../../components/VisuallyHidden';
-import { isValidAdsConversionID } from '../../utils/validation';
import WarningIcon from '../../../../../svg/icons/warning-v2.svg';
+import { isValidAdsConversionID } from '../../../ads/utils/validation';
const { useSelect, useDispatch } = Data;
export default function AdsConversionIDTextField() {
diff --git a/assets/js/modules/analytics-4/utils/validation.js b/assets/js/modules/analytics-4/utils/validation.js
index 06791d56430..7554248cfff 100644
--- a/assets/js/modules/analytics-4/utils/validation.js
+++ b/assets/js/modules/analytics-4/utils/validation.js
@@ -171,18 +171,6 @@ export function isValidGoogleTagContainerID( googleTagContainerID ) {
return isValidNumericID( googleTagContainerID );
}
-/** Checks if the given ads conversion ID is valid.
- *
- * @since 1.32.0
- * @since 1.121.0 Migrated from analytics to analytics-4.
- *
- * @param {*} value Conversion ID to test.
- * @return {boolean} Whether or not the given ID is valid.
- */
-export function isValidAdsConversionID( value ) {
- return typeof value === 'string' && /^AW-[0-9]+$/.test( value );
-}
-
/**
* Checks whether the passed audience object is valid.
*
diff --git a/assets/js/modules/analytics-4/utils/validation.test.js b/assets/js/modules/analytics-4/utils/validation.test.js
index 4597f0d3680..dcff41a8ea2 100644
--- a/assets/js/modules/analytics-4/utils/validation.test.js
+++ b/assets/js/modules/analytics-4/utils/validation.test.js
@@ -21,7 +21,6 @@
*/
import { WEBDATASTREAM_CREATE } from '../datastore/constants';
import {
- isValidAdsConversionID,
isValidGoogleTagAccountID,
isValidGoogleTagContainerID,
isValidGoogleTagID,
@@ -116,28 +115,4 @@ describe( 'modules/analytics-4 validations', () => {
expect( isValidGoogleTagContainerID( 'X' ) ).toBe( false );
} );
} );
-
- describe( 'isValidAdsConversionID', () => {
- it( 'should return TRUE when a valid AdsConversionID is passed', () => {
- expect( isValidAdsConversionID( 'AW-123456789' ) ).toBe( true );
- } );
-
- it.each( [
- [ 'false', false ],
- [ 'an integer', 12345 ],
- [ 'an empty string', '' ],
- [ 'a string not starting with AW', 'AB-123456789' ],
- [
- 'a string starts with AW but ends without numbers',
- 'AW-ABCDEFGHI',
- ],
- ] )(
- 'should return FALSE when %s is passed',
- ( _, adsConversionID ) => {
- expect( isValidAdsConversionID( adsConversionID ) ).toBe(
- false
- );
- }
- );
- } );
} );
diff --git a/assets/sass/admin.scss b/assets/sass/admin.scss
index 1b4b61aef9d..76fa0c050e8 100644
--- a/assets/sass/admin.scss
+++ b/assets/sass/admin.scss
@@ -196,6 +196,7 @@
@import "components/feature-tours/googlesitekit-dashboard-sharing";
// Modules
+@import "components/ads";
@import "components/adsense";
@import "components/analytics-4";
diff --git a/assets/sass/components/ads/_googlesitekit-ads-setup-module.scss b/assets/sass/components/ads/_googlesitekit-ads-setup-module.scss
new file mode 100644
index 00000000000..3ed6b04ab71
--- /dev/null
+++ b/assets/sass/components/ads/_googlesitekit-ads-setup-module.scss
@@ -0,0 +1,40 @@
+/**
+ * Ads Setup styles.
+ *
+ * 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.
+ */
+
+.googlesitekit-plugin {
+
+ .googlesitekit-setup-module--ads {
+
+ .googlesitekit-settings-module__fields-group-title {
+ font-size: $fs-body-md;
+ font-weight: $fw-medium;
+ }
+
+ .googlesitekit-setup-module__description {
+ margin-top: 30px;
+ }
+
+ .googlesitekit-setup-module__inputs > .googlesitekit-settings-module__fields-group {
+ margin: 0;
+ }
+
+ .googlesitekit-setup-module__action {
+ margin-bottom: 32px;
+ }
+ }
+}
diff --git a/assets/sass/components/ads/_index.scss b/assets/sass/components/ads/_index.scss
new file mode 100644
index 00000000000..27700901574
--- /dev/null
+++ b/assets/sass/components/ads/_index.scss
@@ -0,0 +1,17 @@
+/**
+ * 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.
+ */
+
+@import "googlesitekit-ads-setup-module";
diff --git a/assets/sass/components/dashboard/_googlesitekit-subtle-notification.scss b/assets/sass/components/dashboard/_googlesitekit-subtle-notification.scss
index 309faf625e3..c9fe32aa17a 100644
--- a/assets/sass/components/dashboard/_googlesitekit-subtle-notification.scss
+++ b/assets/sass/components/dashboard/_googlesitekit-subtle-notification.scss
@@ -66,4 +66,8 @@
margin: 0;
}
}
+
+ p.googlesitekit-subtle-notification__secondary_description {
+ font-weight: $fw-normal;
+ }
}
diff --git a/tests/backstop/reference/google-site-kit_Global_SetupSuccessSubtleNotification_Ads_0_document_0_small.png b/tests/backstop/reference/google-site-kit_Global_SetupSuccessSubtleNotification_Ads_0_document_0_small.png
new file mode 100644
index 00000000000..a5009ec3b46
Binary files /dev/null and b/tests/backstop/reference/google-site-kit_Global_SetupSuccessSubtleNotification_Ads_0_document_0_small.png differ
diff --git a/tests/backstop/reference/google-site-kit_Global_SetupSuccessSubtleNotification_Ads_0_document_1_medium.png b/tests/backstop/reference/google-site-kit_Global_SetupSuccessSubtleNotification_Ads_0_document_1_medium.png
new file mode 100644
index 00000000000..32b89d8fc65
Binary files /dev/null and b/tests/backstop/reference/google-site-kit_Global_SetupSuccessSubtleNotification_Ads_0_document_1_medium.png differ
diff --git a/tests/backstop/reference/google-site-kit_Global_SetupSuccessSubtleNotification_Ads_0_document_2_large.png b/tests/backstop/reference/google-site-kit_Global_SetupSuccessSubtleNotification_Ads_0_document_2_large.png
new file mode 100644
index 00000000000..4d64cd8c341
Binary files /dev/null and b/tests/backstop/reference/google-site-kit_Global_SetupSuccessSubtleNotification_Ads_0_document_2_large.png differ
diff --git a/tests/backstop/reference/google-site-kit_Modules_Ads_Setup_SetupForm_Default_0_document_0_small.png b/tests/backstop/reference/google-site-kit_Modules_Ads_Setup_SetupForm_Default_0_document_0_small.png
new file mode 100644
index 00000000000..7e52f802915
Binary files /dev/null and b/tests/backstop/reference/google-site-kit_Modules_Ads_Setup_SetupForm_Default_0_document_0_small.png differ
diff --git a/tests/backstop/reference/google-site-kit_Modules_Ads_Setup_SetupForm_Default_0_document_1_medium.png b/tests/backstop/reference/google-site-kit_Modules_Ads_Setup_SetupForm_Default_0_document_1_medium.png
new file mode 100644
index 00000000000..fad63954a5f
Binary files /dev/null and b/tests/backstop/reference/google-site-kit_Modules_Ads_Setup_SetupForm_Default_0_document_1_medium.png differ
diff --git a/tests/backstop/reference/google-site-kit_Modules_Ads_Setup_SetupForm_Default_0_document_2_large.png b/tests/backstop/reference/google-site-kit_Modules_Ads_Setup_SetupForm_Default_0_document_2_large.png
new file mode 100644
index 00000000000..5f1aeb6241e
Binary files /dev/null and b/tests/backstop/reference/google-site-kit_Modules_Ads_Setup_SetupForm_Default_0_document_2_large.png differ