diff --git a/blocks/api/parser.js b/blocks/api/parser.js index 5a7b871719fb5e..a02a75e7b80a9e 100644 --- a/blocks/api/parser.js +++ b/blocks/api/parser.js @@ -4,6 +4,11 @@ import { parse as hpqParse } from 'hpq'; import { pickBy } from 'lodash'; +/** + * WordPress dependencies + */ +import { bumpStat } from '@wordpress/utils'; + /** * Internal dependencies */ @@ -75,6 +80,7 @@ export function createBlockWithFallback( name, rawContent, attributes ) { // Convert 'core/text' blocks in existing content to the new // 'core/paragraph'. if ( name === 'core/text' ) { + bumpStat( 'block_auto_convert', 'core-text-to-paragraph' ); name = 'core/paragraph'; } diff --git a/components/button/index.js b/components/button/index.js index dda515022caba1..39ff5967237cbc 100644 --- a/components/button/index.js +++ b/components/button/index.js @@ -36,7 +36,7 @@ class Button extends Component { isToggled, className, disabled, - ...additionalProps, // eslint-disable-line comma-dangle + ...additionalProps } = this.props; const classes = classnames( 'components-button', className, { button: ( isPrimary || isSecondary || isLarge ), diff --git a/editor/enable-tracking-prompt/index.js b/editor/enable-tracking-prompt/index.js index 9a4077fd13a208..c7dfb271974a6c 100644 --- a/editor/enable-tracking-prompt/index.js +++ b/editor/enable-tracking-prompt/index.js @@ -10,12 +10,12 @@ import clickOutside from 'react-click-outside'; import { Component } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { Button, Popover } from '@wordpress/components'; +import { bumpStat } from '@wordpress/utils'; /** * Internal dependencies */ import './style.scss'; -import { bumpStat } from '../utils/tracking'; import { removeNotice } from '../actions'; export const TRACKING_PROMPT_NOTICE_ID = 'notice:enable-tracking-prompt'; diff --git a/editor/enable-tracking-prompt/test/index.js b/editor/enable-tracking-prompt/test/index.js index 686fa0ec0dade2..e83ce61960de81 100644 --- a/editor/enable-tracking-prompt/test/index.js +++ b/editor/enable-tracking-prompt/test/index.js @@ -2,6 +2,7 @@ * External dependencies */ import { mount } from 'enzyme'; +import clickOutside from 'react-click-outside'; /** * Internal dependencies @@ -12,35 +13,37 @@ import { } from '../'; describe( 'EnableTrackingPrompt', () => { - const tracking = require( '../../utils/tracking' ); // no default export const originalSetUserSetting = window.setUserSetting; - const originalBumpStat = tracking.bumpStat; + const originalDocumentAddEventListener = document.addEventListener; + let eventMap = {}; let removeNotice; beforeEach( () => { window.setUserSetting = jest.fn(); - tracking.bumpStat = jest.fn(); + document.addEventListener = jest.fn( ( event, cb ) => { + eventMap[ event ] = cb; + } ); removeNotice = jest.fn(); } ); afterEach( () => { window.setUserSetting = originalSetUserSetting; - tracking.bumpStat = originalBumpStat; + document.addEventListener = originalDocumentAddEventListener; + eventMap = {}; } ); - it( 'should render a prompt with Yes and No buttons', () => { + it( 'should render a prompt with Yes, No, and More info buttons', () => { const prompt = mount( ); - const buttons = prompt.find( '.button' ); - expect( buttons.length ).toBe( 2 ); + const buttons = prompt.find( 'Button' ); + expect( buttons.length ).toBe( 3 ); expect( buttons.at( 0 ).text() ).toBe( 'Yes' ); expect( buttons.at( 1 ).text() ).toBe( 'No' ); + expect( buttons.at( 2 ).text() ).toBe( 'More info' ); expect( window.setUserSetting ) .not.toHaveBeenCalled(); - expect( tracking.bumpStat ) - .not.toHaveBeenCalled(); expect( removeNotice ) .not.toHaveBeenCalled(); } ); @@ -49,14 +52,12 @@ describe( 'EnableTrackingPrompt', () => { const prompt = mount( ); - const buttonYes = prompt.find( '.button' ) + const buttonYes = prompt.find( 'Button' ) .filterWhere( node => node.text() === 'Yes' ); buttonYes.simulate( 'click' ); expect( window.setUserSetting ) .toHaveBeenCalledWith( 'gutenberg_tracking', 'on' ); - expect( tracking.bumpStat ) - .toHaveBeenCalledWith( 'tracking', 'opt-in' ); expect( removeNotice ) .toHaveBeenCalledWith( TRACKING_PROMPT_NOTICE_ID ); } ); @@ -65,15 +66,54 @@ describe( 'EnableTrackingPrompt', () => { const prompt = mount( ); - const buttonNo = prompt.find( '.button' ) + const buttonNo = prompt.find( 'Button' ) .filterWhere( node => node.text() === 'No' ); buttonNo.simulate( 'click' ); expect( window.setUserSetting ) .toHaveBeenCalledWith( 'gutenberg_tracking', 'off' ); - expect( tracking.bumpStat ) - .not.toHaveBeenCalled(); expect( removeNotice ) .toHaveBeenCalledWith( TRACKING_PROMPT_NOTICE_ID ); } ); + + it( 'should show and hide a popover when clicking More info', () => { + const EnableTrackingPromptWrapped = clickOutside( EnableTrackingPrompt ); + const prompt = mount( + + ); + + expect( prompt.find( 'Popover' ).length ).toBe( 0 ); + + const buttonMoreInfo = prompt.find( 'Button' ) + .filterWhere( node => node.text() === 'More info' ); + + // Click the "More info" button to show the info popover + buttonMoreInfo.simulate( 'click' ); + expect( prompt.find( 'Popover' ).length ).toBe( 1 ); + + // Click the "More info" button to hide the info popover + buttonMoreInfo.simulate( 'click' ); + expect( prompt.find( 'Popover' ).length ).toBe( 0 ); + + // Click the "More info" button to show the info popover + buttonMoreInfo.simulate( 'click' ); + expect( prompt.find( 'Popover' ).length ).toBe( 1 ); + + // Click inside the prompt to hide the info popover + prompt.simulate( 'click' ); + expect( prompt.find( 'Popover' ).length ).toBe( 0 ); + + // Click the "More info" button to show the info popover + buttonMoreInfo.simulate( 'click' ); + expect( prompt.find( 'Popover' ).length ).toBe( 1 ); + + // Click outside the prompt to hide the info popover + eventMap.click( { target: document.body } ); + expect( prompt.find( 'Popover' ).length ).toBe( 0 ); + + expect( window.setUserSetting ) + .not.toHaveBeenCalled(); + expect( removeNotice ) + .not.toHaveBeenCalled(); + } ); } ); diff --git a/editor/inserter/index.js b/editor/inserter/index.js index aee767446526d2..84902061adf9f9 100644 --- a/editor/inserter/index.js +++ b/editor/inserter/index.js @@ -11,6 +11,7 @@ import { __ } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; import { IconButton } from '@wordpress/components'; import { createBlock } from '@wordpress/blocks'; +import { bumpStat } from '@wordpress/utils'; /** * Internal dependencies @@ -18,7 +19,6 @@ import { createBlock } from '@wordpress/blocks'; import InserterMenu from './menu'; import { getBlockInsertionPoint, getEditorMode } from '../selectors'; import { insertBlock, hideInsertionPoint } from '../actions'; -import { bumpStat } from '../utils/tracking'; class Inserter extends Component { constructor() { diff --git a/editor/modes/visual-editor/block-list.js b/editor/modes/visual-editor/block-list.js index 38f053917677c8..3c5dfbb520738b 100644 --- a/editor/modes/visual-editor/block-list.js +++ b/editor/modes/visual-editor/block-list.js @@ -12,7 +12,7 @@ import { __ } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; import { serialize, getDefaultBlock, createBlock } from '@wordpress/blocks'; import { IconButton } from '@wordpress/components'; -import { keycodes } from '@wordpress/utils'; +import { keycodes, bumpStat } from '@wordpress/utils'; /** * Internal dependencies @@ -29,7 +29,6 @@ import { getMultiSelectedBlockUids, } from '../../selectors'; import { insertBlock, multiSelect } from '../../actions'; -import { bumpStat } from '../../utils/tracking'; const INSERTION_POINT_PLACEHOLDER = '[[insertion-point]]'; const { ENTER } = keycodes; diff --git a/test/setup-globals.js b/test/setup-globals.js index 931f6e146d7985..d5a8ed265c7899 100644 --- a/test/setup-globals.js +++ b/test/setup-globals.js @@ -51,3 +51,14 @@ global.wp = global.wp || {}; global.wp.a11y = { speak: () => {}, }; + +global.window.getUserSetting = settingName => { + switch ( settingName ) { + case 'gutenberg_tracking': + return 'on'; + default: + throw new Error( + 'Unrecognized user setting requested: ' + settingName + ); + } +}; diff --git a/utils/index.js b/utils/index.js index e24a5b1782f919..01b2b0a7b30bb3 100644 --- a/utils/index.js +++ b/utils/index.js @@ -3,3 +3,5 @@ import * as nodetypes from './nodetypes'; export { keycodes }; export { nodetypes }; + +export * from './tracking'; diff --git a/editor/utils/test/tracking.js b/utils/test/tracking.js similarity index 80% rename from editor/utils/test/tracking.js rename to utils/test/tracking.js index 8c0b11f9f3a0cb..7fd360fe1b377a 100644 --- a/editor/utils/test/tracking.js +++ b/utils/test/tracking.js @@ -1,5 +1,10 @@ /* eslint-disable no-console */ +/** + * External dependencies + */ +import url from 'url'; + /** * Internal dependencies */ @@ -11,7 +16,6 @@ describe( 'bumpStat', () => { beforeEach( () => { console.error = jest.fn(); - window.getUserSetting = () => 'off'; } ); afterEach( () => { @@ -62,22 +66,21 @@ describe( 'bumpStat', () => { } ); it( 'should do nothing if the user has not opted in', () => { + window.getUserSetting = () => 'off'; expect( bumpStat( 'valid_group', 'valid-name' ) ).toBeUndefined(); expect( console.error ).not.toHaveBeenCalled(); } ); it( 'should bump valid stats', () => { - window.getUserSetting = () => 'on'; - const url = bumpStat( 'valid_group', 'valid-name' ); - // There are a couple of pieces of the URL where we don't care about - // testing the specific value. Replace them with placeholders. - const urlMatch = url - .replace( /^[a-z]+:/, 'PROTOCOL:' ) - .replace( /t=[0-9.]+$/, 't=NUMBER' ); - expect( urlMatch ).toBe( - 'PROTOCOL://pixel.wp.com/g.gif?v=wpcom-no-pv' - + '&x_gutenberg_valid_group=valid-name' - + '&t=NUMBER' + // Testing the URL protocol and the randomized `?t=` cache-buster is + // difficult, so only test the pieces we're actually interested in. + const statUrlPieces = url.parse( + bumpStat( 'valid_group', 'valid-name' ), + true ); + expect( statUrlPieces.hostname ).toBe( 'pixel.wp.com' ); + expect( statUrlPieces.pathname ).toBe( '/g.gif' ); + expect( statUrlPieces.query.v ).toBe( 'wpcom-no-pv' ); + expect( statUrlPieces.query.x_gutenberg_valid_group ).toBe( 'valid-name' ); } ); } ); diff --git a/editor/utils/tracking.js b/utils/tracking.js similarity index 100% rename from editor/utils/tracking.js rename to utils/tracking.js diff --git a/webpack.config.js b/webpack.config.js index d8ccb63b9a458e..72711d026f1b3a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -37,13 +37,13 @@ const extractConfig = { }; const entryPointNames = [ - 'element', - 'i18n', - 'components', - 'utils', 'blocks', + 'components', 'date', 'editor', + 'element', + 'i18n', + 'utils', ]; const externals = {