From 3beb059b317d39b6ac6b24598946233237ea9a14 Mon Sep 17 00:00:00 2001 From: Edward Gou Date: Thu, 28 Oct 2021 18:01:03 -0400 Subject: [PATCH] default add widget values for top n saved queries --- .../modals/addDashboardWidgetModal.tsx | 65 ++++++++++++------- .../app/views/eventsV2/savedQuery/utils.tsx | 2 + .../modals/addDashboardWidgetModal.spec.jsx | 22 +++++++ .../views/eventsV2/savedQuery/index.spec.jsx | 8 ++- 4 files changed, 71 insertions(+), 26 deletions(-) diff --git a/static/app/components/modals/addDashboardWidgetModal.tsx b/static/app/components/modals/addDashboardWidgetModal.tsx index d03cc38b67faac..70b3a37c25187a 100644 --- a/static/app/components/modals/addDashboardWidgetModal.tsx +++ b/static/app/components/modals/addDashboardWidgetModal.tsx @@ -27,7 +27,7 @@ import { TagCollection, } from 'app/types'; import trackAdvancedAnalyticsEvent from 'app/utils/analytics/trackAdvancedAnalyticsEvent'; -import {Aggregation} from 'app/utils/discover/fields'; +import {Aggregation, parseFunction} from 'app/utils/discover/fields'; import Measurements from 'app/utils/measurements/measurements'; import withApi from 'app/utils/withApi'; import withGlobalSelection from 'app/utils/withGlobalSelection'; @@ -133,6 +133,7 @@ class AddDashboardWidgetModal extends React.Component { componentDidMount() { const {fromDiscover} = this.props; if (fromDiscover) this.fetchDashboards(); + this.handleDefaultFields(); } handleSubmit = async (event: React.FormEvent) => { @@ -245,33 +246,47 @@ class AddDashboardWidgetModal extends React.Component { } }; - handleFieldChange = (field: string) => (value: string) => { - const {defaultWidgetQuery, defaultTableColumns, fromDiscover, organization} = - this.props; + handleDefaultFields = () => { + const {defaultWidgetQuery, defaultTableColumns} = this.props; this.setState(prevState => { const newState = cloneDeep(prevState); - set(newState, field, value); - - if (field === 'displayType') { - const displayType = value as Widget['displayType']; - const normalized = normalizeQueries(displayType, prevState.queries); - - // If switching to Table visualization, use saved query fields for Y-Axis if user has not made query changes - if (defaultWidgetQuery && defaultTableColumns && !prevState.userHasModified) { - if (displayType === DisplayType.TABLE) { - normalized.forEach(query => { - query.fields = [...defaultTableColumns]; - }); - } else { - normalized.forEach(query => { - query.fields = [...defaultWidgetQuery.fields]; - }); - } + const displayType = prevState.displayType as Widget['displayType']; + const normalized = normalizeQueries(displayType, prevState.queries); + + // If switching to Table visualization, use saved query fields for Y-Axis if user has not made query changes + if (defaultWidgetQuery && defaultTableColumns && !prevState.userHasModified) { + if (displayType === DisplayType.TABLE) { + normalized.forEach(query => { + query.fields = [...defaultTableColumns]; + }); + } else if (displayType === DisplayType.TOP_N) { + // Function columns not valid group bys for TOP_N display + const topNFields = [ + ...defaultTableColumns.filter(column => !parseFunction(column)), + ...defaultWidgetQuery.fields, + ]; + normalized.forEach(query => { + query.fields = [...topNFields]; + query.orderby = defaultWidgetQuery.orderby; + }); + } else { + normalized.forEach(query => { + query.fields = [...defaultWidgetQuery.fields]; + }); } - - set(newState, 'queries', normalized); } + set(newState, 'queries', normalized); + return {...newState, errors: undefined}; + }); + }; + + handleFieldChange = (field: string) => (value: string) => { + const {fromDiscover, organization} = this.props; + this.setState(prevState => { + const newState = cloneDeep(prevState); + set(newState, field, value); + trackAdvancedAnalyticsEvent('dashboards_views.add_widget_modal.change', { from: fromDiscover ? 'discoverv2' : 'dashboards', field, @@ -281,6 +296,10 @@ class AddDashboardWidgetModal extends React.Component { return {...newState, errors: undefined}; }); + + if (field === 'displayType') { + this.handleDefaultFields(); + } }; handleQueryChange = (widgetQuery: WidgetQuery, index: number) => { diff --git a/static/app/views/eventsV2/savedQuery/utils.tsx b/static/app/views/eventsV2/savedQuery/utils.tsx index d91e226a11a1f6..1ba3f9a0bea70c 100644 --- a/static/app/views/eventsV2/savedQuery/utils.tsx +++ b/static/app/views/eventsV2/savedQuery/utils.tsx @@ -248,6 +248,8 @@ export function displayModeToDisplayType(displayMode: DisplayModes): DisplayType return DisplayType.BAR; case DisplayModes.WORLDMAP: return DisplayType.WORLD_MAP; + case DisplayModes.TOP5: + return DisplayType.TOP_N; default: return DisplayType.LINE; } diff --git a/tests/js/spec/components/modals/addDashboardWidgetModal.spec.jsx b/tests/js/spec/components/modals/addDashboardWidgetModal.spec.jsx index 4a26d453b24c4c..7a6c2766cd2b95 100644 --- a/tests/js/spec/components/modals/addDashboardWidgetModal.spec.jsx +++ b/tests/js/spec/components/modals/addDashboardWidgetModal.spec.jsx @@ -943,4 +943,26 @@ describe('Modals -> AddDashboardWidgetModal', function () { expect(wrapper.find('SelectPicker').at(1).props().value.value).toEqual('bar'); wrapper.unmount(); }); + + it('correctly defaults fields and orderby when in Top N display', async function () { + const wrapper = mountModal({ + initialData, + onAddWidget: () => undefined, + onUpdateWidget: () => undefined, + fromDiscover: true, + displayType: types.DisplayType.TOP_N, + defaultWidgetQuery: {fields: ['count_unique(user)'], orderby: '-count_unique_user'}, + defaultTableColumns: ['title', 'count()', 'count_unique(user)'], + }); + + expect(wrapper.find('SelectPicker').at(1).props().value.value).toEqual('top_n'); + expect(wrapper.find('WidgetQueriesForm').props().queries[0].fields).toEqual([ + 'title', + 'count_unique(user)', + ]); + expect(wrapper.find('WidgetQueriesForm').props().queries[0].orderby).toEqual( + '-count_unique_user' + ); + wrapper.unmount(); + }); }); diff --git a/tests/js/spec/views/eventsV2/savedQuery/index.spec.jsx b/tests/js/spec/views/eventsV2/savedQuery/index.spec.jsx index 8272c281abfb21..881cd9e3ae82b7 100644 --- a/tests/js/spec/views/eventsV2/savedQuery/index.spec.jsx +++ b/tests/js/spec/views/eventsV2/savedQuery/index.spec.jsx @@ -2,6 +2,7 @@ import {mountWithTheme} from 'sentry-test/enzyme'; import {mountGlobalModal} from 'sentry-test/modal'; import EventView from 'app/utils/discover/eventView'; +import {DisplayModes} from 'app/utils/discover/types'; import DiscoverBanner from 'app/views/eventsV2/banner'; import {ALL_VIEWS} from 'app/views/eventsV2/data'; import SavedQueryButtonGroup from 'app/views/eventsV2/savedQuery'; @@ -49,6 +50,7 @@ describe('EventsV2 > SaveQueryButtonGroup', function () { const errorsQuery = { ...ALL_VIEWS.find(view => view.name === 'Errors by Title'), yAxis: 'count()', + display: DisplayModes.DEFAULT, }; const errorsView = EventView.fromSavedQuery(errorsQuery); @@ -443,9 +445,9 @@ describe('EventsV2 > SaveQueryButtonGroup', function () { await tick(); await tick(); const modal = await mountGlobalModal(); - expect(modal.find('AddDashboardWidgetModal').find('h4').children().html()).toEqual( - 'Add Widget to Dashboard' - ); + expect( + modal.find('AddDashboardWidgetModal').find('h4').children().at(0).html() + ).toEqual('Add Widget to Dashboard'); }); it('populates dashboard widget modal with saved query data if created from discover', async () => {