* Use a store to render CES tracks

* Use the CES store to trigger CES survey when users change date range or single product filters.

* Support onClick event for the Compare button

* Trigger CES survey when a user clicks the Compare button on products, variations, categories, coupon, and taxes pages

* Set default text for onSubmitLabel in js and remove its requirement

* Add addCesSurveyTrackForAnalytics action so that it can be shared in other components in analytics pages -- remove duplicates

* Call addCesSurveyTrack from addCesSurveyTrackForAnalytics to avoid duplicate

* Remove 'tracks' from the method name
This commit is contained in:
Moon 2020-11-24 15:18:50 -08:00 committed by GitHub
parent ea86886d88
commit eb7a779e1f
16 changed files with 306 additions and 43 deletions

View File

@ -2,9 +2,10 @@
* External dependencies * External dependencies
*/ */
import { Component } from '@wordpress/element'; import { Component } from '@wordpress/element';
import { compose } from '@wordpress/compose';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { omitBy, isUndefined, snakeCase } from 'lodash'; import { omitBy, isUndefined, snakeCase } from 'lodash';
import { withSelect } from '@wordpress/data'; import { withSelect, withDispatch } from '@wordpress/data';
import { ReportFilters as Filters } from '@woocommerce/components'; import { ReportFilters as Filters } from '@woocommerce/components';
import { LOCALE } from '@woocommerce/wc-admin-settings'; import { LOCALE } from '@woocommerce/wc-admin-settings';
import { SETTINGS_STORE_NAME } from '@woocommerce/data'; import { SETTINGS_STORE_NAME } from '@woocommerce/data';
@ -19,36 +20,52 @@ import { recordEvent } from '@woocommerce/tracks';
* Internal dependencies * Internal dependencies
*/ */
import { CurrencyContext } from '../../../lib/currency-context'; import { CurrencyContext } from '../../../lib/currency-context';
import { STORE_KEY as CES_STORE_KEY } from '../../../customer-effort-score-tracks/data/constants';
class ReportFilters extends Component { class ReportFilters extends Component {
constructor() { constructor() {
super(); super();
this.trackDateSelect = this.trackDateSelect.bind( this ); this.onDateSelect = this.onDateSelect.bind( this );
this.trackFilterSelect = this.trackFilterSelect.bind( this ); this.onFilterSelect = this.onFilterSelect.bind( this );
this.trackAdvancedFilterAction = this.trackAdvancedFilterAction.bind( this.onAdvancedFilterAction = this.onAdvancedFilterAction.bind( this );
this
);
} }
trackDateSelect( data ) { onDateSelect( data ) {
const { report } = this.props; const { report, addCesSurveyForAnalytics } = this.props;
addCesSurveyForAnalytics();
recordEvent( 'datepicker_update', { recordEvent( 'datepicker_update', {
report, report,
...omitBy( data, isUndefined ), ...omitBy( data, isUndefined ),
} ); } );
} }
trackFilterSelect( data ) { onFilterSelect( data ) {
const { report } = this.props; const { report, addCesSurveyForAnalytics } = this.props;
// This event gets triggered in the following cases.
// 1. Select "Single Product" and choose a product.
// 2. Select "Comparison" or any other filter types.
// The comparsion and other filter types require a user to click
// a button to execute a query, so this is not a good place to
// trigger a CES survey for those.
const triggerCesFor = [
'single_product',
'single_category',
'single_coupon',
'single_variation',
];
const filterName = data.filter || data[ 'filter-variations' ];
if ( triggerCesFor.includes( filterName ) ) {
addCesSurveyForAnalytics();
}
recordEvent( 'analytics_filter', { recordEvent( 'analytics_filter', {
report, report,
filter: data.filter || 'all', filter: data.filter || 'all',
} ); } );
} }
trackAdvancedFilterAction( action, data ) { onAdvancedFilterAction( action, data ) {
const { report } = this.props; const { report, addCesSurveyForAnalytics } = this.props;
switch ( action ) { switch ( action ) {
case 'add': case 'add':
recordEvent( 'analytics_filters_add', { recordEvent( 'analytics_filters_add', {
@ -70,6 +87,7 @@ class ReportFilters extends Component {
}, },
{} {}
); );
addCesSurveyForAnalytics();
recordEvent( 'analytics_filters_filter', { recordEvent( 'analytics_filters_filter', {
report, report,
...snakeCaseData, ...snakeCaseData,
@ -123,9 +141,9 @@ class ReportFilters extends Component {
filters={ filters } filters={ filters }
advancedFilters={ advancedFilters } advancedFilters={ advancedFilters }
showDatePicker={ showDatePicker } showDatePicker={ showDatePicker }
onDateSelect={ this.trackDateSelect } onDateSelect={ this.onDateSelect }
onFilterSelect={ this.trackFilterSelect } onFilterSelect={ this.onFilterSelect }
onAdvancedFilterAction={ this.trackAdvancedFilterAction } onAdvancedFilterAction={ this.onAdvancedFilterAction }
dateQuery={ dateQuery } dateQuery={ dateQuery }
isoDateFormat={ isoDateFormat } isoDateFormat={ isoDateFormat }
/> />
@ -135,12 +153,18 @@ class ReportFilters extends Component {
ReportFilters.contextType = CurrencyContext; ReportFilters.contextType = CurrencyContext;
export default withSelect( ( select ) => { export default compose(
const { woocommerce_default_date_range: defaultDateRange } = select( withSelect( ( select ) => {
SETTINGS_STORE_NAME const { woocommerce_default_date_range: defaultDateRange } = select(
).getSetting( 'wc_admin', 'wcAdminSettings' ); SETTINGS_STORE_NAME
return { defaultDateRange }; ).getSetting( 'wc_admin', 'wcAdminSettings' );
} )( ReportFilters ); return { defaultDateRange };
} ),
withDispatch( ( dispatch ) => {
const { addCesSurveyForAnalytics } = dispatch( CES_STORE_KEY );
return { addCesSurveyForAnalytics };
} )
)( ReportFilters );
ReportFilters.propTypes = { ReportFilters.propTypes = {
/** /**

View File

@ -4,6 +4,7 @@
import { Component, Fragment } from '@wordpress/element'; import { Component, Fragment } from '@wordpress/element';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { withDispatch } from '@wordpress/data';
/** /**
* Internal dependencies * Internal dependencies
@ -15,8 +16,9 @@ import ReportChart from '../../components/report-chart';
import ReportSummary from '../../components/report-summary'; import ReportSummary from '../../components/report-summary';
import ProductsReportTable from '../products/table'; import ProductsReportTable from '../products/table';
import ReportFilters from '../../components/report-filters'; import ReportFilters from '../../components/report-filters';
import { STORE_KEY as CES_STORE_KEY } from '../../../customer-effort-score-tracks/data/constants';
export default class CategoriesReport extends Component { class CategoriesReport extends Component {
getChartMeta() { getChartMeta() {
const { query } = this.props; const { query } = this.props;
const isCompareView = const isCompareView =
@ -42,7 +44,12 @@ export default class CategoriesReport extends Component {
} }
render() { render() {
const { isRequesting, query, path } = this.props; const {
isRequesting,
query,
path,
addCesSurveyForAnalytics,
} = this.props;
const { mode, itemsLabel, isSingleCategoryView } = this.getChartMeta(); const { mode, itemsLabel, isSingleCategoryView } = this.getChartMeta();
const chartQuery = { const chartQuery = {
@ -55,6 +62,10 @@ export default class CategoriesReport extends Component {
: 'category'; : 'category';
} }
filters[ 0 ].filters.find(
( item ) => item.value === 'compare-categories'
).settings.onClick = addCesSurveyForAnalytics;
return ( return (
<Fragment> <Fragment>
<ReportFilters <ReportFilters
@ -122,3 +133,8 @@ CategoriesReport.propTypes = {
query: PropTypes.object.isRequired, query: PropTypes.object.isRequired,
path: PropTypes.string.isRequired, path: PropTypes.string.isRequired,
}; };
export default withDispatch( ( dispatch ) => {
const { addCesSurveyForAnalytics } = dispatch( CES_STORE_KEY );
return { addCesSurveyForAnalytics };
} )( CategoriesReport );

View File

@ -4,6 +4,7 @@
import { Component, Fragment } from '@wordpress/element'; import { Component, Fragment } from '@wordpress/element';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { withDispatch } from '@wordpress/data';
/** /**
* Internal dependencies * Internal dependencies
@ -14,8 +15,9 @@ import getSelectedChart from '../../../lib/get-selected-chart';
import ReportChart from '../../components/report-chart'; import ReportChart from '../../components/report-chart';
import ReportSummary from '../../components/report-summary'; import ReportSummary from '../../components/report-summary';
import ReportFilters from '../../components/report-filters'; import ReportFilters from '../../components/report-filters';
import { STORE_KEY as CES_STORE_KEY } from '../../../customer-effort-score-tracks/data/constants';
export default class CouponsReport extends Component { class CouponsReport extends Component {
getChartMeta() { getChartMeta() {
const { query } = this.props; const { query } = this.props;
const isCompareView = const isCompareView =
@ -33,9 +35,18 @@ export default class CouponsReport extends Component {
} }
render() { render() {
const { isRequesting, query, path } = this.props; const {
isRequesting,
query,
path,
addCesSurveyForAnalytics,
} = this.props;
const { mode, itemsLabel } = this.getChartMeta(); const { mode, itemsLabel } = this.getChartMeta();
filters[ 0 ].filters.find(
( item ) => item.value === 'compare-coupons'
).settings.onClick = addCesSurveyForAnalytics;
const chartQuery = { const chartQuery = {
...query, ...query,
}; };
@ -88,3 +99,8 @@ export default class CouponsReport extends Component {
CouponsReport.propTypes = { CouponsReport.propTypes = {
query: PropTypes.object.isRequired, query: PropTypes.object.isRequired,
}; };
export default withDispatch( ( dispatch ) => {
const { addCesSurveyForAnalytics } = dispatch( CES_STORE_KEY );
return { addCesSurveyForAnalytics };
} )( CouponsReport );

View File

@ -6,7 +6,7 @@ import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ITEMS_STORE_NAME } from '@woocommerce/data'; import { ITEMS_STORE_NAME } from '@woocommerce/data';
import { withSelect } from '@wordpress/data'; import { withSelect, withDispatch } from '@wordpress/data';
/** /**
* Internal dependencies * Internal dependencies
@ -19,6 +19,7 @@ import ReportError from '../../components/report-error';
import ReportSummary from '../../components/report-summary'; import ReportSummary from '../../components/report-summary';
import VariationsReportTable from '../variations/table'; import VariationsReportTable from '../variations/table';
import ReportFilters from '../../components/report-filters'; import ReportFilters from '../../components/report-filters';
import { STORE_KEY as CES_STORE_KEY } from '../../../customer-effort-score-tracks/data/constants';
class ProductsReport extends Component { class ProductsReport extends Component {
getChartMeta() { getChartMeta() {
@ -60,6 +61,7 @@ class ProductsReport extends Component {
isError, isError,
isRequesting, isRequesting,
isSingleProductVariable, isSingleProductVariable,
addCesSurveyForAnalytics,
} = this.props; } = this.props;
if ( isError ) { if ( isError ) {
@ -75,6 +77,10 @@ class ProductsReport extends Component {
compareObject === 'products' ? 'product' : 'variation'; compareObject === 'products' ? 'product' : 'variation';
} }
filters[ 0 ].filters.find(
( item ) => item.value === 'compare-products'
).settings.onClick = addCesSurveyForAnalytics;
return ( return (
<Fragment> <Fragment>
<ReportFilters <ReportFilters
@ -177,8 +183,8 @@ export default compose(
'is-variable': isVariable, 'is-variable': isVariable,
}, },
isSingleProductView, isSingleProductView,
isSingleProductVariable: isVariable,
isRequesting: isProductsRequesting, isRequesting: isProductsRequesting,
isSingleProductVariable: isVariable,
isError: isProductsError, isError: isProductsError,
}; };
} }
@ -187,5 +193,9 @@ export default compose(
query, query,
isSingleProductView, isSingleProductView,
}; };
} ),
withDispatch( ( dispatch ) => {
const { addCesSurveyForAnalytics } = dispatch( CES_STORE_KEY );
return { addCesSurveyForAnalytics };
} ) } )
)( ProductsReport ); )( ProductsReport );

View File

@ -4,6 +4,7 @@
import { Component, Fragment } from '@wordpress/element'; import { Component, Fragment } from '@wordpress/element';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { withDispatch } from '@wordpress/data';
/** /**
* Internal dependencies * Internal dependencies
@ -14,8 +15,9 @@ import ReportChart from '../../components/report-chart';
import ReportSummary from '../../components/report-summary'; import ReportSummary from '../../components/report-summary';
import TaxesReportTable from './table'; import TaxesReportTable from './table';
import ReportFilters from '../../components/report-filters'; import ReportFilters from '../../components/report-filters';
import { STORE_KEY as CES_STORE_KEY } from '../../../customer-effort-score-tracks/data/constants';
export default class TaxesReport extends Component { class TaxesReport extends Component {
getChartMeta() { getChartMeta() {
const { query } = this.props; const { query } = this.props;
const isCompareTaxView = query.filter === 'compare-taxes'; const isCompareTaxView = query.filter === 'compare-taxes';
@ -29,9 +31,18 @@ export default class TaxesReport extends Component {
} }
render() { render() {
const { isRequesting, query, path } = this.props; const {
isRequesting,
query,
path,
addCesSurveyForAnalytics,
} = this.props;
const { mode, itemsLabel } = this.getChartMeta(); const { mode, itemsLabel } = this.getChartMeta();
filters[ 0 ].filters.find(
( item ) => item.value === 'compare-taxes'
).settings.onClick = addCesSurveyForAnalytics;
const chartQuery = { const chartQuery = {
...query, ...query,
}; };
@ -82,3 +93,8 @@ export default class TaxesReport extends Component {
TaxesReport.propTypes = { TaxesReport.propTypes = {
query: PropTypes.object.isRequired, query: PropTypes.object.isRequired,
}; };
export default withDispatch( ( dispatch ) => {
const { addCesSurveyForAnalytics } = dispatch( CES_STORE_KEY );
return { addCesSurveyForAnalytics };
} )( TaxesReport );

View File

@ -4,6 +4,7 @@
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { Fragment } from '@wordpress/element'; import { Fragment } from '@wordpress/element';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { withDispatch } from '@wordpress/data';
/** /**
* Internal dependencies * Internal dependencies
@ -15,6 +16,7 @@ import ReportError from '../../components/report-error';
import ReportSummary from '../../components/report-summary'; import ReportSummary from '../../components/report-summary';
import VariationsReportTable from './table'; import VariationsReportTable from './table';
import ReportFilters from '../../components/report-filters'; import ReportFilters from '../../components/report-filters';
import { STORE_KEY as CES_STORE_KEY } from '../../../customer-effort-score-tracks/data/constants';
const getChartMeta = ( { query } ) => { const getChartMeta = ( { query } ) => {
const isCompareView = const isCompareView =
@ -31,7 +33,13 @@ const getChartMeta = ( { query } ) => {
const VariationsReport = ( props ) => { const VariationsReport = ( props ) => {
const { itemsLabel, mode } = getChartMeta( props ); const { itemsLabel, mode } = getChartMeta( props );
const { path, query, isError, isRequesting } = props; const {
path,
query,
isError,
isRequesting,
addCesSurveyForAnalytics,
} = props;
if ( isError ) { if ( isError ) {
return <ReportError isError />; return <ReportError isError />;
@ -45,6 +53,10 @@ const VariationsReport = ( props ) => {
chartQuery.segmentby = 'variation'; chartQuery.segmentby = 'variation';
} }
filters[ 0 ].filters.find(
( item ) => item.value === 'compare-variations'
).settings.onClick = addCesSurveyForAnalytics;
return ( return (
<Fragment> <Fragment>
<ReportFilters <ReportFilters
@ -91,4 +103,7 @@ VariationsReport.propTypes = {
query: PropTypes.object.isRequired, query: PropTypes.object.isRequired,
}; };
export default VariationsReport; export default withDispatch( ( dispatch ) => {
const { addCesSurveyForAnalytics } = dispatch( CES_STORE_KEY );
return { addCesSurveyForAnalytics };
} )( VariationsReport );

View File

@ -10,6 +10,8 @@ import PropTypes from 'prop-types';
* Internal dependencies * Internal dependencies
*/ */
import CustomerEffortScoreTracks from './customer-effort-score-tracks'; import CustomerEffortScoreTracks from './customer-effort-score-tracks';
import { STORE_KEY, QUEUE_OPTION_NAME } from './data/constants';
import './data';
/** /**
* Maps the queue of CES tracks surveys to CustomerEffortScoreTracks * Maps the queue of CES tracks surveys to CustomerEffortScoreTracks
@ -73,11 +75,9 @@ CustomerEffortScoreTracksContainer.propTypes = {
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { getOption, isResolving } = select( OPTIONS_STORE_NAME ); const { getCesSurveyQueue, isResolving } = select( STORE_KEY );
const queue = getOption( 'woocommerce_ces_tracks_queue' ) || []; const queue = getCesSurveyQueue();
const resolving = isResolving( 'getOption', [ const resolving = isResolving( 'getOption', [ QUEUE_OPTION_NAME ] );
'woocommerce_ces_tracks_queue',
] );
return { queue, resolving }; return { queue, resolving };
} ), } ),

View File

@ -35,7 +35,7 @@ function CustomerEffortScoreTracks( {
action, action,
trackProps, trackProps,
label, label,
onSubmitLabel, onSubmitLabel = __( 'Thank you for your feedback!', 'woocommerce-admin' ),
cesShownForActions, cesShownForActions,
allowTracking, allowTracking,
resolving, resolving,
@ -144,7 +144,7 @@ CustomerEffortScoreTracks.propTypes = {
/** /**
* The label for the snackbar that appears upon survey submission. * The label for the snackbar that appears upon survey submission.
*/ */
onSubmitLabel: PropTypes.string.isRequired, onSubmitLabel: PropTypes.string,
/** /**
* The array of actions that the CES modal has been shown for. * The array of actions that the CES modal has been shown for.
*/ */

View File

@ -0,0 +1,6 @@
const TYPES = {
SET_CES_SURVEY_QUEUE: 'SET_CES_SURVEY_QUEUE',
ADD_CES_SURVEY: 'ADD_CES_SURVEY',
};
export default TYPES;

View File

@ -0,0 +1,62 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import TYPES from './action-types';
/**
* Initialize the state
*
* @param {Object} queue initial queue
*/
export function setCesSurveyQueue( queue ) {
return {
type: TYPES.SET_CES_SURVEY_QUEUE,
queue,
};
}
/**
* Add a new CES track to the state.
*
* @param {string} action action name for the survey
* @param {string} label label for the snackback
* @param {string} pageNow value of window.pagenow
* @param {string} adminPage value of window.adminpage
* @param {string} onsubmit_label label for the snackback onsubmit
*/
export function addCesSurvey(
action,
label,
pageNow = window.pagenow,
adminPage = window.adminpage,
onsubmit_label = undefined
) {
return {
type: TYPES.ADD_CES_SURVEY,
action,
label,
pageNow,
adminPage,
onsubmit_label,
};
}
/**
* Add a new CES survey track for the pages in Analytics menu
*/
export function addCesSurveyForAnalytics() {
return addCesSurvey(
'analytics_filtered',
__(
'How easy was it to filter your store analytics?',
'woocommerce-admin'
),
'woocommerce_page_wc-admin',
'woocommerce_page_wc-admin'
);
}

View File

@ -0,0 +1,3 @@
export const STORE_KEY = 'wc/customer-effort-score';
export const API_NAMESPACE = '/wc-admin';
export const QUEUE_OPTION_NAME = 'woocommerce_ces_tracks_queue';

View File

@ -0,0 +1,22 @@
/**
* External dependencies
*/
import { registerStore } from '@wordpress/data';
import { controls } from '@wordpress/data-controls';
/**
* Internal dependencies
*/
import * as actions from './actions';
import * as resolvers from './resolvers';
import * as selectors from './selectors';
import reducer from './reducer';
import { STORE_KEY } from './constants';
export default registerStore( STORE_KEY, {
actions,
selectors,
resolvers,
controls,
reducer,
} );

View File

@ -0,0 +1,42 @@
/**
* Internal dependencies
*/
import TYPES from './action-types';
const DEFAULT_STATE = {
queue: [],
};
const reducer = ( state = DEFAULT_STATE, action ) => {
switch ( action.type ) {
case TYPES.SET_CES_SURVEY_QUEUE:
return {
...state,
queue: action.queue,
};
case TYPES.ADD_CES_SURVEY:
// Prevent duplicate
const hasDuplicate = state.queue.filter(
( track ) => track.action === action.action
);
if ( hasDuplicate.length ) {
return state;
}
const newTrack = {
action: action.action,
label: action.label,
pagenow: action.pageNow,
adminpage: action.adminPage,
onSubmitLabel: action.onSubmitLabel,
};
return {
...state,
queue: [ ...state.queue, newTrack ],
};
default:
return state;
}
};
export default reducer;

View File

@ -0,0 +1,22 @@
/**
* External dependencies
*/
import { apiFetch } from '@wordpress/data-controls';
/**
* Internal dependencies
*/
import { setCesSurveyQueue } from './actions';
import { API_NAMESPACE, QUEUE_OPTION_NAME } from './constants';
export function* getCesSurveyQueue() {
const response = yield apiFetch( {
path: `${ API_NAMESPACE }/options?options=${ QUEUE_OPTION_NAME }`,
} );
if ( response ) {
yield setCesSurveyQueue( response[ QUEUE_OPTION_NAME ] || [] );
} else {
throw new Error();
}
}

View File

@ -0,0 +1,3 @@
export function getCesSurveyQueue( state ) {
return state.queue;
}

View File

@ -4,7 +4,7 @@
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { Component } from '@wordpress/element'; import { Component } from '@wordpress/element';
import { Button } from '@wordpress/components'; import { Button } from '@wordpress/components';
import { isEqual } from 'lodash'; import { isEqual, isFunction } from 'lodash';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { getIdsFromQuery, updateQueryString } from '@woocommerce/navigation'; import { getIdsFromQuery, updateQueryString } from '@woocommerce/navigation';
@ -27,11 +27,10 @@ export class CompareFilter extends Component {
this.state = { this.state = {
selected: [], selected: [],
}; };
this.clearQuery = this.clearQuery.bind( this ); this.clearQuery = this.clearQuery.bind( this );
this.updateQuery = this.updateQuery.bind( this ); this.updateQuery = this.updateQuery.bind( this );
this.updateLabels = this.updateLabels.bind( this ); this.updateLabels = this.updateLabels.bind( this );
this.onButtonClicked = this.onButtonClicked.bind( this );
if ( query[ param ] ) { if ( query[ param ] ) {
getLabels( query[ param ], query ).then( this.updateLabels ); getLabels( query[ param ], query ).then( this.updateLabels );
} }
@ -79,6 +78,13 @@ export class CompareFilter extends Component {
updateQueryString( { [ param ]: idList.join( ',' ) }, path, query ); updateQueryString( { [ param ]: idList.join( ',' ) }, path, query );
} }
onButtonClicked( e ) {
this.updateQuery( e );
if ( isFunction( this.props.onClick ) ) {
this.props.onClick( e );
}
}
render() { render() {
const { labels, type } = this.props; const { labels, type } = this.props;
const { selected } = this.state; const { selected } = this.state;
@ -101,7 +107,7 @@ export class CompareFilter extends Component {
<CompareButton <CompareButton
count={ selected.length } count={ selected.length }
helpText={ labels.helpText } helpText={ labels.helpText }
onClick={ this.updateQuery } onClick={ this.onButtonClicked }
> >
{ labels.update } { labels.update }
</CompareButton> </CompareButton>