diff --git a/.github/workflows/e2e-tests-wp-4-7.yml b/.github/workflows/e2e-tests-wp-4-7.yml index 6dc0b8afbcf..4e3972e64d9 100644 --- a/.github/workflows/e2e-tests-wp-4-7.yml +++ b/.github/workflows/e2e-tests-wp-4-7.yml @@ -54,6 +54,12 @@ jobs: - name: Build run: npm run build:test + # E2E tests use a dedicated container, this is just for Composer. + - uses: shivammathur/setup-php@v2 + with: + php-version: '7.x' + tools: composer + - name: Get Composer Cache Directory id: composer-cache run: echo "::set-output name=dir::$(composer config cache-files-dir)" @@ -65,13 +71,17 @@ jobs: restore-keys: ${{ runner.os }}-composer- - name: Composer Install - uses: php-actions/composer@v4 - with: - dev: no - php_version: 7.3 + run: composer install --no-interaction --no-progress --no-dev - name: Start environment run: npm run env:start - name: Run the tests run: npm run test:e2e + + - name: Upload artifacts + uses: actions/upload-artifact@v2 + if: failure() + with: + name: e2e-screenshots + path: tests/e2e/screenshots diff --git a/.github/workflows/e2e-tests-wp-4-9-gutenberg.yml b/.github/workflows/e2e-tests-wp-4-9-gutenberg.yml index 2a11df20b7a..64ba37b7918 100644 --- a/.github/workflows/e2e-tests-wp-4-9-gutenberg.yml +++ b/.github/workflows/e2e-tests-wp-4-9-gutenberg.yml @@ -54,6 +54,12 @@ jobs: - name: Build run: npm run build:test + # E2E tests use a dedicated container, this is just for Composer. + - uses: shivammathur/setup-php@v2 + with: + php-version: '7.x' + tools: composer + - name: Get Composer Cache Directory id: composer-cache run: echo "::set-output name=dir::$(composer config cache-files-dir)" @@ -65,13 +71,17 @@ jobs: restore-keys: ${{ runner.os }}-composer- - name: Composer Install - uses: php-actions/composer@v4 - with: - dev: no - php_version: 7.3 + run: composer install --no-interaction --no-progress --no-dev - name: Start environment run: npm run env:start - name: Run the tests run: npm run test:e2e + + - name: Upload artifacts + uses: actions/upload-artifact@v2 + if: failure() + with: + name: e2e-screenshots + path: tests/e2e/screenshots diff --git a/.github/workflows/e2e-tests-wp-latest.yml b/.github/workflows/e2e-tests-wp-latest.yml index b30e699ae05..7d1b71bb6bb 100644 --- a/.github/workflows/e2e-tests-wp-latest.yml +++ b/.github/workflows/e2e-tests-wp-latest.yml @@ -53,6 +53,12 @@ jobs: - name: Build run: npm run build:test + # E2E tests use a dedicated container, this is just for Composer. + - uses: shivammathur/setup-php@v2 + with: + php-version: '7.x' + tools: composer + - name: Get Composer Cache Directory id: composer-cache run: echo "::set-output name=dir::$(composer config cache-files-dir)" @@ -64,13 +70,17 @@ jobs: restore-keys: ${{ runner.os }}-composer- - name: Composer Install - uses: php-actions/composer@v4 - with: - dev: no - php_version: 7.3 + run: composer install --no-interaction --no-progress --no-dev - name: Start environment run: npm run env:start - name: Run the tests run: npm run test:e2e + + - name: Upload artifacts + uses: actions/upload-artifact@v2 + if: failure() + with: + name: e2e-screenshots + path: tests/e2e/screenshots diff --git a/.github/workflows/e2e-tests-wp-nightly.yml b/.github/workflows/e2e-tests-wp-nightly.yml index 4d6db66be35..46192d4d666 100644 --- a/.github/workflows/e2e-tests-wp-nightly.yml +++ b/.github/workflows/e2e-tests-wp-nightly.yml @@ -53,6 +53,12 @@ jobs: - name: Build run: npm run build:test + # E2E tests use a dedicated container, this is just for Composer. + - uses: shivammathur/setup-php@v2 + with: + php-version: '7.x' + tools: composer + - name: Get Composer Cache Directory id: composer-cache run: echo "::set-output name=dir::$(composer config cache-files-dir)" @@ -64,13 +70,17 @@ jobs: restore-keys: ${{ runner.os }}-composer- - name: Composer Install - uses: php-actions/composer@v4 - with: - dev: no - php_version: 7.3 + run: composer install --no-interaction --no-progress --no-dev - name: Start environment run: npm run env:start - name: Run the tests run: npm run test:e2e + + - name: Upload artifacts + uses: actions/upload-artifact@v2 + if: failure() + with: + name: e2e-screenshots + path: tests/e2e/screenshots diff --git a/.github/workflows/php-tests-wp-latest-multisite-php-7-4.yml b/.github/workflows/php-tests-wp-latest-multisite-php-7-4.yml index 5552e336cd4..65825c121dd 100644 --- a/.github/workflows/php-tests-wp-latest-multisite-php-7-4.yml +++ b/.github/workflows/php-tests-wp-latest-multisite-php-7-4.yml @@ -60,4 +60,4 @@ jobs: - name: Set up PHP test data run: tests/bin/install-wp-tests.sh ${MYSQL_DATABASE} ${MYSQL_USER} ${MYSQL_PASSWORD} ${DB_HOST}:${DB_PORT} ${WP_VERSION} - name: Run Unit Tests - run: composer test + run: composer test:multisite diff --git a/.github/workflows/php-tests-wp-latest-php-8-0.yml b/.github/workflows/php-tests-wp-latest-php-8-0.yml new file mode 100644 index 00000000000..8dc466c1bfa --- /dev/null +++ b/.github/workflows/php-tests-wp-latest-php-8-0.yml @@ -0,0 +1,64 @@ +name: PHP Tests + +on: + push: + branches: + - develop + - master + pull_request: + types: + - opened + - reopened + - synchronize + +jobs: + php-tests-wp-latest-php-8-0: + name: PHP (PHP 8.0, WordPress Latest) + runs-on: ubuntu-latest + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + env: + DB_HOST: 127.0.0.1 + DB_PORT: 3306 + MYSQL_USER: wordpress + MYSQL_PASSWORD: wordpress + MYSQL_DATABASE: wordpress_test + MYSQL_ROOT_PASSWORD: root + WP_VERSION: latest + steps: + - uses: actions/checkout@v2 + - name: Grant database access + env: + MYSQL_TCP_PORT: ${{ env.DB_PORT }} + run: | + mysql -u root -h ${DB_HOST} -e "CREATE USER ${MYSQL_USER} IDENTIFIED BY '${MYSQL_PASSWORD}';" + mysql -u root -h ${DB_HOST} -e "GRANT ALL PRIVILEGES ON *.* TO ${MYSQL_USER};" + - uses: shivammathur/setup-php@v2 + with: + extensions: mysqli + tools: composer + php-version: '8.0' + - name: Get Composer Cache Directory + id: composer-cache + run: | + echo "::set-output name=dir::$(composer config cache-files-dir)" + - uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + - name: Composer Install + run: composer install --no-interaction --no-progress + - name: Update PHPUnit + run: composer update phpunit/phpunit --with-dependencies --ignore-platform-reqs --no-scripts + - name: Set up PHP test data + run: tests/bin/install-wp-tests.sh ${MYSQL_DATABASE} ${MYSQL_USER} ${MYSQL_PASSWORD} ${DB_HOST}:${DB_PORT} ${WP_VERSION} + - name: Run Unit Tests + run: composer test diff --git a/.gitignore b/.gitignore index 5a2169fd191..f8599388132 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules bower_components vendor +!/assets/sass/vendor /php-scoper/* !/php-scoper/composer.* !/php-scoper/README.md @@ -24,7 +25,8 @@ tmtags *.un~ Session.vim *.swp -.vscode +.vscode/* +!.vscode/settings.json # Mac OSX .DS_Store @@ -40,5 +42,6 @@ Desktop.ini tests/backstop/html_report/ tests/backstop/tests/ -# Jest coverage +# Jest coverage/ +tests/e2e/screenshots diff --git a/.storybook/config.js b/.storybook/config.js index 4115156011a..1908662b590 100644 --- a/.storybook/config.js +++ b/.storybook/config.js @@ -51,8 +51,8 @@ const resetGlobals = () => { isFirstAdmin: true, isOwner: true, splashURL: 'http://example.com/wp-admin/admin.php?page=googlesitekit-splash', - proxySetupURL: 'https://sitekit.withgoogle.com/site-management/setup/?scope=openid%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fsiteverification%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fwebmasters%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fanalytics%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fanalytics.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fanalytics.manage.users%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fanalytics.edit&supports=credentials_retrieval%20short_verification_token%20file_verification&nonce=&site_id=storybooksiteid.apps.sitekit.withgoogle.com', - proxyPermissionsURL: 'https://sitekit.withgoogle.com/site-management/permissions/?token=storybooktoken&site_id=storybooksiteid.apps.sitekit.withgoogle.com', + proxySetupURL: 'http://example.com/wp-admin/index.php?action=googlesitekit_proxy_setup&nonce=abc123', + proxyPermissionsURL: 'http://example.com/wp-admin/index.php?action=googlesitekit_proxy_permissions&nonce=abc123', trackingEnabled: false, trackingID: 'UA-000000000-1', }; @@ -62,6 +62,26 @@ const resetGlobals = () => { currentEntityTitle: null, currentEntityID: null, }; + global._googlesitekitUserData = { + user: { + id: 1, + name: 'Wapuu WordPress', + email: 'wapuu.wordpress@gmail.com', + picture: 'https://wapu.us/wp-content/uploads/2017/11/WapuuFinal-100x138.png', + }, + connectURL: 'http://example.com/wp-admin/admin.php?page=googlesitekit-splash&googlesitekit_connect=1&nonce=abc123', + verified: true, + userInputState: 'completed', + permissions: { + googlesitekit_authenticate: true, + googlesitekit_setup: true, + googlesitekit_view_posts_insights: true, + googlesitekit_view_dashboard: true, + googlesitekit_view_module_details: true, + googlesitekit_manage_options: true, + googlesitekit_publish_posts: true, + }, + }; }; resetGlobals(); diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html index ced9d73cd02..f3651cd93f6 100644 --- a/.storybook/preview-head.html +++ b/.storybook/preview-head.html @@ -26,16 +26,8 @@ s.parentNode.insertBefore( wf, s ); } )(); - const allImagesLoaded = new Promise( ( resolve ) => { - // Wait for the document to completely finish loading before checking for images. - window.addEventListener( 'load', () => { - imagesLoaded( '.googlesitekit-plugin-preview', { background: true }, resolve ); - } ); - } ); - Promise.all( [ webFontsLoaded, - allImagesLoaded, ] ).then( () => { // Signal Backstop that the environment is ready to go. document.body.classList.add( 'backstopjs-ready' ); diff --git a/.storybook/storybook-data.js b/.storybook/storybook-data.js index 4226112ea12..ef9d86a5c42 100644 --- a/.storybook/storybook-data.js +++ b/.storybook/storybook-data.js @@ -42,20 +42,6 @@ module.exports = [ }, }, }, - { - id: 'dashboard--all-traffic', - kind: 'Dashboard', - name: 'All Traffic', - story: 'All Traffic', - parameters: { - fileName: './stories/dashboard.stories.js', - options: { - hierarchyRootSeparator: '|', - hierarchySeparator: {}, - readySelector: '.googlesitekit-line-chart > div[style="position: relative;"]', - }, - }, - }, { id: 'dashboard--post-searcher', kind: 'Dashboard', @@ -79,7 +65,7 @@ module.exports = [ options: { hierarchyRootSeparator: '|', hierarchySeparator: {}, - readySelector: '.googlesitekit-line-chart > div[style="position: relative;"]', + readySelector: '.googlesitekit-chart-v2 .googlesitekit-chart-v2__inner', }, }, }, @@ -93,7 +79,7 @@ module.exports = [ options: { hierarchyRootSeparator: '|', hierarchySeparator: {}, - readySelector: '.googlesitekit-line-chart > div[style="position: relative;"]', + readySelector: '.googlesitekit-chart-v2 .googlesitekit-chart-v2__inner', }, }, }, diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..dbae49118cd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,23 @@ +{ + "search.exclude": { + ".storybook/data": true, + "dist/": true, + "node_modules/": true, + "third-party/": true, + "vendor/": true + }, + "files.exclude": { + "dist/": true, + "node_modules/": true, + "third-party/": true, + "vendor/": true + }, + "jest.autoEnable": false, + "jest.pathToConfig": "./tests/js/jest.config.js", + "editor.codeActionsOnSave": { + "source.fixAll": true, + "source.fixAll.eslint": true + }, + "phpcs.enable": true, + "phpcs.executablePath": "vendor/bin/phpcs" +} diff --git a/assets/images/logo-g_white_small.png b/assets/images/logo-g_white_small.png deleted file mode 100644 index f18ad671be3..00000000000 Binary files a/assets/images/logo-g_white_small.png and /dev/null differ diff --git a/assets/images/rocket.png b/assets/images/rocket.png deleted file mode 100644 index 6ba577ce43d..00000000000 Binary files a/assets/images/rocket.png and /dev/null differ diff --git a/assets/images/sun-small.png b/assets/images/sun-small.png deleted file mode 100644 index 944f066a9f9..00000000000 Binary files a/assets/images/sun-small.png and /dev/null differ diff --git a/assets/images/thumbs-up.png b/assets/images/thumbs-up.png deleted file mode 100644 index d1cb89540a7..00000000000 Binary files a/assets/images/thumbs-up.png and /dev/null differ diff --git a/assets/js/components/Button.js b/assets/js/components/Button.js index f4fd40c7bac..033c79a455f 100644 --- a/assets/js/components/Button.js +++ b/assets/js/components/Button.js @@ -73,7 +73,7 @@ const Button = forwardRef( ( { { ...extraProps } > { icon } - { children } + { children && { children } } { trailingIcon } ); @@ -83,7 +83,7 @@ Button.displayName = 'Button'; Button.propTypes = { onClick: PropTypes.func, - children: PropTypes.string.isRequired, + children: PropTypes.string, href: PropTypes.string, text: PropTypes.bool, className: PropTypes.string, diff --git a/assets/js/components/Dialog.js b/assets/js/components/Dialog.js index 07c608102b3..2e3105b07c1 100644 --- a/assets/js/components/Dialog.js +++ b/assets/js/components/Dialog.js @@ -36,6 +36,7 @@ import { __ } from '@wordpress/i18n'; import Button from './Button'; import Link from './Link'; import { MDCDialog } from '../material-components'; +import Spinner from './Spinner.js'; const Dialog = ( { dialogActive, @@ -47,6 +48,7 @@ const Dialog = ( { confirmButton, dependentModules, danger, + inProgress = false, } ) => { const dialogRef = useCallback( ( el ) => { if ( el !== null ) { @@ -54,7 +56,7 @@ const Dialog = ( { } }, [] ); - // eslint-disable-next-line sitekit/camelcase-acronyms + // eslint-disable-next-line sitekit/acronym-case const instanceID = useInstanceId( Dialog ); const labelledByID = `googlesitekit-dialog-label-${ instanceID }`; const describedByID = `googlesitekit-dialog-description-${ instanceID }`; @@ -109,13 +111,18 @@ const Dialog = ( { + { __( 'Cancel', 'google-site-kit' ) } diff --git a/assets/js/components/GoogleChart.js b/assets/js/components/GoogleChart.js index b1c9607380d..54ecaaf1124 100644 --- a/assets/js/components/GoogleChart.js +++ b/assets/js/components/GoogleChart.js @@ -151,11 +151,15 @@ export default function GoogleChart( props ) { chart: googleChart, onSelect: selectListener, onReady: readyListener, + onMouseOver: mouseOverListener, + onMouseOut: mouseOutListener, } = GoogleChart.charts.get( chartID ); if ( googleChart ) { global.google.visualization.events.removeListener( selectListener ); global.google.visualization.events.removeListener( readyListener ); + global.google.visualization.events.removeListener( mouseOverListener ); + global.google.visualization.events.removeListener( mouseOutListener ); googleChart.clearChart(); } diff --git a/assets/js/components/GoogleChartV2.js b/assets/js/components/GoogleChartV2.js new file mode 100644 index 00000000000..0cf1ee4cf3b --- /dev/null +++ b/assets/js/components/GoogleChartV2.js @@ -0,0 +1,182 @@ +/** + * GoogleChartV2 component. + * + * Site Kit by Google, Copyright 2021 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'; +import { Chart } from 'react-google-charts'; + +/** + * WordPress dependencies + */ +import { useEffect, useLayoutEffect, useRef } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import PreviewBlock from './PreviewBlock'; + +export default function GoogleChartV2( props ) { + const { + chartEvents, + chartType, + children, + className, + getChartWrapper, + height, + loaded, + loadingHeight, + loadingWidth, + onMouseOver, + onMouseOut, + onReady, + onSelect, + width, + ...otherProps + } = props; + + const loadingHeightToUse = loadingHeight || height; + const loadingWidthToUse = loadingWidth || width; + const loadingShape = chartType === 'PieChart' ? 'circular' : 'square'; + + const loader = ( +
+ +
+ ); + + const chartWrapperRef = useRef(); + const googleRef = useRef(); + + useEffect( () => { + // Remove all event listeners after the component has unmounted. + return () => { + // eslint-disable-next-line no-unused-expressions + if ( googleRef.current && chartWrapperRef.current ) { + const { events } = googleRef.current.visualization; + events.removeAllListeners( chartWrapperRef.current.getChart() ); + events.removeAllListeners( chartWrapperRef.current ); + } + }; + }, [] ); + + // These event listeners are added manually to the current chart because + // `react-google-charts` doesn't support `mouseOver` or `mouseOut` events + // in its `chartEvents` prop. + // See: https://github.com/google/site-kit-wp/pull/2805#discussion_r579172660 + useLayoutEffect( () => { + if ( onMouseOver ) { + // eslint-disable-next-line no-unused-expressions + googleRef.current?.visualization.events.addListener( chartWrapperRef.current.getChart(), 'onmouseover', ( event ) => { + onMouseOver( event, { + chartWrapper: chartWrapperRef.current, + google: googleRef.current, + } ); + } ); + } + + if ( onMouseOut ) { + // eslint-disable-next-line no-unused-expressions + googleRef.current?.visualization.events.addListener( chartWrapperRef.current.getChart(), 'onmouseout', ( event ) => { + onMouseOut( event, { + chartWrapper: chartWrapperRef.current, + google: googleRef.current, + } ); + } ); + } + }, [ onMouseOver, onMouseOut ] ); + + if ( ! loaded ) { + return ( +
+ { loader } +
+ ); + } + + const combinedChartEvents = [ ...chartEvents || [] ]; + + if ( onReady ) { + combinedChartEvents.push( { + eventName: 'ready', + callback: onReady, + } ); + } + + if ( onSelect ) { + combinedChartEvents.push( { + eventName: 'select', + callback: onSelect, + } ); + } + + return ( +
+ { + // Remove all the event listeners on the old chart before we draw + // a new one. Only run this if the old chart and the new chart aren't + // the same reference though, otherwise we'll remove existing `onReady` + // events and other event listeners, which will cause bugs. + if ( chartWrapper !== chartWrapperRef.current ) { + // eslint-disable-next-line no-unused-expressions + googleRef.current?.visualization.events.removeAllListeners( chartWrapperRef.current?.getChart() ); + // eslint-disable-next-line no-unused-expressions + googleRef.current?.visualization.events.removeAllListeners( chartWrapperRef.current ); + } + + chartWrapperRef.current = chartWrapper; + googleRef.current = google; + + if ( getChartWrapper ) { + getChartWrapper( chartWrapper, google ); + } + } } + width={ width } + { ...otherProps } + /> + { children } +
+ ); +} + +GoogleChartV2.defaultProps = { + ...Chart.defaultProps, + loaded: true, +}; diff --git a/assets/js/modules/analytics/components/adminbar/index.js b/assets/js/components/Null.js similarity index 74% rename from assets/js/modules/analytics/components/adminbar/index.js rename to assets/js/components/Null.js index f3fda6cc0b9..b56f104b84b 100644 --- a/assets/js/modules/analytics/components/adminbar/index.js +++ b/assets/js/components/Null.js @@ -1,5 +1,5 @@ /** - * Analytics adminbar components. + * Null component. * * Site Kit by Google, Copyright 2021 Google LLC * @@ -16,5 +16,7 @@ * limitations under the License. */ -export { default as AnalyticsAdminbarWidget } from './AnalyticsAdminbarWidget'; -export { default as AnalyticsAdminbarWidgetOverview } from './AnalyticsAdminbarWidgetOverview'; +export default function Null() { + return null; +} + diff --git a/assets/js/components/OptIn.js b/assets/js/components/OptIn.js index 216fe2d572c..d4fe49e632e 100644 --- a/assets/js/components/OptIn.js +++ b/assets/js/components/OptIn.js @@ -21,119 +21,77 @@ */ import PropTypes from 'prop-types'; import classnames from 'classnames'; + /** * WordPress dependencies */ -import apiFetch from '@wordpress/api-fetch'; -import { Component } from '@wordpress/element'; -import { __, sprintf } from '@wordpress/i18n'; +import { useCallback, createInterpolateElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { getMetaKeyForUserOption, sanitizeHTML } from '../util'; +import Data from 'googlesitekit-data'; +import { CORE_USER } from '../googlesitekit/datastore/user/constants'; +import { toggleTracking, trackEvent } from '../util/tracking'; import Checkbox from './Checkbox'; -import { - isTrackingEnabled, - toggleTracking, - trackEvent, -} from '../util/tracking'; - -class OptIn extends Component { - constructor( props ) { - super( props ); - - this.state = { - optIn: isTrackingEnabled(), - error: false, - }; - - this.handleOptIn = this.handleOptIn.bind( this ); - } - - async handleOptIn( e ) { - const checked = !! e.target.checked; - const trackingUserOptInKey = getMetaKeyForUserOption( 'googlesitekit_tracking_optin' ); +import Link from './Link'; +const { useSelect, useDispatch } = Data; - toggleTracking( checked ); +export default function OptIn( { id, name, className, optinAction } ) { + const enabled = useSelect( ( select ) => select( CORE_USER ).isTrackingEnabled() ); + const saving = useSelect( ( select ) => select( CORE_USER ).isSavingTrackingEnabled() ); + const error = useSelect( ( select ) => select( CORE_USER ).getErrorForAction( 'setTrackingEnabled', [ ! enabled ] ) ); - if ( checked ) { - await trackEvent( 'tracking_plugin', this.props.optinAction ); + const { setTrackingEnabled } = useDispatch( CORE_USER ); + const handleOptIn = useCallback( async ( e ) => { + const { + response, + error: responseError, + } = await setTrackingEnabled( !! e.target.checked ); + + if ( ! responseError ) { + toggleTracking( response.enabled ); + if ( response.enabled ) { + trackEvent( 'tracking_plugin', optinAction ); + } } + }, [ enabled, optinAction ] ); - try { - await apiFetch( { - path: '/wp/v2/users/me', - method: 'POST', - data: { - meta: { - [ trackingUserOptInKey ]: checked, - }, - }, - } ); - this.setState( { - optIn: checked, - error: false, - } ); - } catch ( err ) { - this.setState( { - optIn: ! checked, - error: { - errorCode: err.code, - errorMsg: err.message, - }, - } ); - } + if ( enabled === undefined ) { + return null; } - render() { - const { - optIn, - error, - } = this.state; - - const { - id, - name, - className, - } = this.props; - - const labelHTML = sprintf( - /* translators: %s: privacy policy URL */ - __( 'Help us improve the Site Kit plugin by allowing tracking of anonymous usage stats. All data are treated in accordance with Google Privacy Policy', 'google-site-kit' ), - 'https://policies.google.com/privacy' - ); - - return ( -
- - - - { error && + return ( +
+ + { createInterpolateElement( + __( 'Help us improve the Site Kit plugin by allowing tracking of anonymous usage stats. All data are treated in accordance with Google Privacy Policy', 'google-site-kit' ), + { + a: , + } + ) } + + + { error?.message && (
- { error.errorMsg } + { error?.message }
- } -
- ); - } + ) } +
+ ); } OptIn.propTypes = { @@ -147,5 +105,3 @@ OptIn.defaultProps = { id: 'googlesitekit-opt-in', name: 'optIn', }; - -export default OptIn; diff --git a/assets/js/modules/search-console/components/wp-dashboard/index.js b/assets/js/components/PageFooter.js similarity index 71% rename from assets/js/modules/search-console/components/wp-dashboard/index.js rename to assets/js/components/PageFooter.js index b4d64000333..d50d4f8a95a 100644 --- a/assets/js/modules/search-console/components/wp-dashboard/index.js +++ b/assets/js/components/PageFooter.js @@ -1,5 +1,5 @@ /** - * Search Console wp-dashboard components. + * PageFooter component. * * Site Kit by Google, Copyright 2021 Google LLC * @@ -16,5 +16,15 @@ * limitations under the License. */ -export { default as WPSearchConsoleDashboardWidget } from './WPSearchConsoleDashboardWidget'; -export { default as WPSearchConsoleDashboardWidgetOverview } from './WPSearchConsoleDashboardWidgetOverview'; +/** + * Internal dependencies + */ +import HelpLink from './HelpLink'; + +export default function PageFooter() { + return ( +
+ +
+ ); +} diff --git a/assets/js/components/PageHeader.js b/assets/js/components/PageHeader.js index 53507c99246..a47d219e7bb 100644 --- a/assets/js/components/PageHeader.js +++ b/assets/js/components/PageHeader.js @@ -21,7 +21,7 @@ import classnames from 'classnames'; import PropTypes from 'prop-types'; -function PageHeader( props ) { +export default function PageHeader( props ) { const { title, icon, className, status, statusText, fullWidth, children } = props; const widthClasses = fullWidth @@ -97,5 +97,3 @@ PageHeader.defaultProps = { statusText: '', fullWidth: false, }; - -export default PageHeader; diff --git a/assets/js/components/PostSearcher.js b/assets/js/components/PostSearcher.js index 0083ee76aa2..4250c2e72c9 100644 --- a/assets/js/components/PostSearcher.js +++ b/assets/js/components/PostSearcher.js @@ -42,7 +42,7 @@ const { useSelect, useDispatch } = Data; function PostSearcher() { const [ canSubmit, setCanSubmit ] = useState( false ); const [ match, setMatch ] = useState( {} ); - const analyticsModuleActive = useSelect( ( select ) => select( CORE_MODULES ).isModuleActive( 'analytics' ) ); + const analyticsModuleConnected = useSelect( ( select ) => select( CORE_MODULES ).isModuleConnected( 'analytics' ) ); const detailsURL = useSelect( ( select ) => { return select( CORE_SITE ).getAdminURL( 'googlesitekit-dashboard', { @@ -56,9 +56,9 @@ function PostSearcher() { return (
-