From 32ab5f54ac5931eb96f286281ef5da28943bbbdc Mon Sep 17 00:00:00 2001 From: Brandon Farmer Date: Wed, 13 Mar 2019 17:13:30 -0700 Subject: [PATCH] VEO uses react router Co-authored-by: Iris Scholten --- ui/package-lock.json | 41 ++---- ui/src/dashboards/actions/views.ts | 2 +- .../dashboards/components/DashboardPage.tsx | 69 ++-------- ui/src/dashboards/components/VEO.tsx | 120 ++++++++++++++---- ui/src/dashboards/selectors/index.ts | 5 +- ui/src/index.tsx | 41 +++--- ui/src/timeMachine/components/TimeMachine.tsx | 1 - 7 files changed, 142 insertions(+), 137 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 13dcab9516..a9bb6bf4f5 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -6106,8 +6106,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -6131,15 +6130,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6156,22 +6153,19 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -6302,8 +6296,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -6317,7 +6310,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6334,7 +6326,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6343,15 +6334,13 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -6372,7 +6361,6 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -6461,8 +6449,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -6476,7 +6463,6 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6572,8 +6558,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -6615,7 +6600,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6637,7 +6621,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6686,15 +6669,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", - "dev": true, - "optional": true + "dev": true } } }, diff --git a/ui/src/dashboards/actions/views.ts b/ui/src/dashboards/actions/views.ts index fc14660cf0..514420a63b 100644 --- a/ui/src/dashboards/actions/views.ts +++ b/ui/src/dashboards/actions/views.ts @@ -63,7 +63,7 @@ export const updateView = (dashboardID: string, view: View) => async ( ): Promise => { const viewID = view.cellID - dispatch(setView(viewID, null, RemoteDataState.Loading)) + dispatch(setView(viewID, view, RemoteDataState.Loading)) try { const newView = await updateViewAJAX(dashboardID, viewID, view) diff --git a/ui/src/dashboards/components/DashboardPage.tsx b/ui/src/dashboards/components/DashboardPage.tsx index 029e8187f2..0dfcbdbd02 100644 --- a/ui/src/dashboards/components/DashboardPage.tsx +++ b/ui/src/dashboards/components/DashboardPage.tsx @@ -10,8 +10,6 @@ import {ErrorHandling} from 'src/shared/decorators/errors' import DashboardHeader from 'src/dashboards/components/DashboardHeader' import DashboardComponent from 'src/dashboards/components/Dashboard' import ManualRefresh from 'src/shared/components/ManualRefresh' -import VEO from 'src/dashboards/components/VEO' -import {Overlay} from 'src/clockface' import {HoverTimeProvider} from 'src/dashboards/utils/hoverTime' import NoteEditorContainer from 'src/dashboards/components/NoteEditorContainer' @@ -25,25 +23,13 @@ import {setActiveTimeMachine} from 'src/timeMachine/actions' // Utils import {getDeep} from 'src/utils/wrappers' import {GlobalAutoRefresher} from 'src/utils/AutoRefresher' -import {createView} from 'src/shared/utils/view' -import {cellAddFailed} from 'src/shared/copy/notifications' // Constants import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'src/shared/constants' import {DEFAULT_TIME_RANGE} from 'src/shared/constants/timeRanges' -import {VEO_TIME_MACHINE_ID} from 'src/timeMachine/constants' // Types -import { - Links, - Dashboard, - Cell, - View, - ViewType, - TimeRange, - AppState, -} from 'src/types/v2' -import {NewView, XYView, QueryViewProperties} from 'src/types/v2/dashboards' +import {Links, Dashboard, Cell, View, TimeRange, AppState} from 'src/types/v2' import {RemoteDataState} from 'src/types' import {WithRouterProps} from 'react-router' import {ManualRefreshProps} from 'src/shared/components/ManualRefresh' @@ -162,8 +148,8 @@ class DashboardPage extends Component { inPresentationMode, handleChooseAutoRefresh, handleClickPresentationButton, + children, } = this.props - const {isShowingVEO} = this.state return ( @@ -200,9 +186,7 @@ class DashboardPage extends Component { onAddCell={this.handleAddCell} /> )} - - - + {children} @@ -244,50 +228,19 @@ class DashboardPage extends Component { } private handleAddCell = async (): Promise => { - const newView = createView(ViewType.XY) - - this.showVEO(newView) - } - - private handleHideVEO = (): void => { - this.setState({isShowingVEO: false}) - } - - private handleSaveVEO = async (view: View): Promise => { - this.setState({isShowingVEO: false}) - - const {dashboard, onCreateCellWithView, onUpdateView, notify} = this.props - - try { - if (view.id) { - onUpdateView(dashboard, view) - } else { - await onCreateCellWithView(dashboard, view) - } - } catch (error) { - console.error(error) - notify(cellAddFailed()) - } + this.showVEO() } private handleEditView = (cellID: string): void => { - const entry = this.props.views[cellID] - - if (!entry || !entry.view) { - throw new Error(`Can't edit non-existent view with ID "${cellID}"`) - } - - this.showVEO(entry.view as View) + this.showVEO(cellID) } - private showVEO = ( - view: View | NewView - ): void => { - const {onSetActiveTimeMachine} = this.props - - onSetActiveTimeMachine(VEO_TIME_MACHINE_ID, {view}) - - this.setState({isShowingVEO: true}) + private showVEO = (id?: string): void => { + if (id) { + this.props.router.push(`${this.props.location.pathname}/cells/${id}/edit`) + } else { + this.props.router.push(`${this.props.location.pathname}/cells/new`) + } } private handleCloneCell = async (cell: Cell): Promise => { diff --git a/ui/src/dashboards/components/VEO.tsx b/ui/src/dashboards/components/VEO.tsx index 2a56dc20d3..29b1caed1b 100644 --- a/ui/src/dashboards/components/VEO.tsx +++ b/ui/src/dashboards/components/VEO.tsx @@ -1,6 +1,10 @@ // Libraries import React, {PureComponent} from 'react' +import {withRouter, WithRouterProps} from 'react-router' import {connect} from 'react-redux' +import _ from 'lodash' + +import {Overlay} from 'src/clockface' // Components import VEOHeader from 'src/dashboards/components/VEOHeader' @@ -8,58 +12,94 @@ import TimeMachine from 'src/timeMachine/components/TimeMachine' // Actions import {setName} from 'src/timeMachine/actions' +import * as viewActions from 'src/dashboards/actions/views' +import * as dashboardActions from 'src/dashboards/actions' +import {notify} from 'src/shared/actions/notifications' // Utils import {getActiveTimeMachine} from 'src/timeMachine/selectors' +import {createView} from 'src/shared/utils/view' // Types -import {AppState, DashboardQuery, View, NewView} from 'src/types/v2' +import {AppState, DashboardQuery, ViewType} from 'src/types/v2' +import {QueryView} from 'src/types/v2/dashboards' +import {Dashboard} from 'src/types' +import {XYView} from 'src/types/v2/dashboards' +import {setActiveTimeMachine} from 'src/timeMachine/actions' + +// Constants +import {VEO_TIME_MACHINE_ID} from 'src/timeMachine/constants' + +// Nofication Messages +import {cellAddFailed} from 'src/shared/copy/notifications' // Styles import './VEO.scss' interface StateProps { - draftView: NewView + draftView: QueryView + existingView?: QueryView draftQueries: DashboardQuery[] + dashboard: Dashboard } interface DispatchProps { onSetName: typeof setName + onCreateCellWithView: typeof dashboardActions.createCellWithView + onUpdateView: typeof viewActions.updateView + notify: typeof notify + setActiveTimeMachine: typeof setActiveTimeMachine } -interface OwnProps { - onHide: () => void - onSave: (v: NewView | View) => Promise +interface OwnProps extends WithRouterProps { + params: { + dashboardID: string + cellID?: string + } } type Props = OwnProps & StateProps & DispatchProps class VEO extends PureComponent { public render() { - const {draftView, onSetName, onHide} = this.props + const {draftView, onSetName} = this.props return ( -
- -
- + +
+ +
+ +
-
+ ) } - private handleSave = (): void => { - const {draftView, draftQueries, onSave} = this.props + public async componentDidMount() { + const {existingView} = this.props + const view = existingView || createView(ViewType.XY) - // Ensure that the latest queries are saved with the view, even if they - // haven't been submitted yet - const view = { + this.props.setActiveTimeMachine(VEO_TIME_MACHINE_ID, {view}) + } + + private handleSave = async (): Promise => { + const { + draftView, + draftQueries, + dashboard, + onCreateCellWithView, + onUpdateView, + notify, + } = this.props + + const view: QueryView & {id?: string} = { ...draftView, properties: { ...draftView.properties, @@ -67,21 +107,49 @@ class VEO extends PureComponent { }, } - onSave(view) + try { + if (view.id) { + await onUpdateView(dashboard.id, view) + } else { + await onCreateCellWithView(dashboard, view) + } + this.close() + } catch (error) { + console.error(error) + notify(cellAddFailed()) + } + } + + private close = (): void => { + this.props.router.goBack() } } -const mstp = (state: AppState): StateProps => { +const mstp = (state: AppState, {params}): StateProps => { + const {cellID, dashboardID} = params + const { + views: {views}, + dashboards, + } = state const {view, draftQueries} = getActiveTimeMachine(state) + const dashboard = dashboards.find(d => d.id === dashboardID) + let existingView = null + if (cellID) { + existingView = _.get(views, [cellID, 'view']) + } - return {draftView: view, draftQueries} + return {draftView: view, existingView, draftQueries, dashboard} } const mdtp: DispatchProps = { onSetName: setName, + onCreateCellWithView: dashboardActions.createCellWithView, + onUpdateView: viewActions.updateView, + setActiveTimeMachine, + notify, } export default connect( mstp, mdtp -)(VEO) +)(withRouter(VEO)) diff --git a/ui/src/dashboards/selectors/index.ts b/ui/src/dashboards/selectors/index.ts index c12517e5c9..c714e662b3 100644 --- a/ui/src/dashboards/selectors/index.ts +++ b/ui/src/dashboards/selectors/index.ts @@ -2,8 +2,9 @@ import {get} from 'lodash' import {AppState, View} from 'src/types/v2' -export const getView = (state: AppState, id: string): View => - get(state, `views.views.${id}.view`) +export const getView = (state: AppState, id: string): View => { + return get(state, `views.views.${id}.view`) +} export const getViewsForDashboard = ( state: AppState, diff --git a/ui/src/index.tsx b/ui/src/index.tsx index 4b6df8c0c4..2d69a3d60b 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -46,6 +46,7 @@ import OrgScrapersIndex from 'src/organizations/containers/OrgScrapersIndex' import OrgTasksIndex from 'src/organizations/containers/OrgTasksIndex' import TaskExportOverlay from 'src/organizations/components/TaskExportOverlay' import TaskImportOverlay from 'src/organizations/components/TaskImportOverlay' +import VEO from 'src/dashboards/components/VEO' import OnboardingWizardPage from 'src/onboarding/containers/OnboardingWizardPage' @@ -141,21 +142,20 @@ class Root extends PureComponent { path="scrapers" component={OrgScrapersIndex} /> - - - + + + + + + + - - @@ -172,7 +172,14 @@ class Root extends PureComponent { path="data-explorer" component={DataExplorerPage} /> - + + + + + + + + - = ({activeTab}) => { className="time-machine--bottom" data-testid="time-machine--bottom" > - {/* TODO: Is this div needed? */}