diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts index ea064f58f97..0c842b7217c 100644 --- a/packages/grafana-e2e-selectors/src/selectors/pages.ts +++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts @@ -146,9 +146,6 @@ export const Pages = { table: 'Explore Table', scrollBar: () => '.scrollbar-view', }, - Toolbar: { - navBar: () => '.explore-toolbar', - }, }, SoloPanel: { url: (page: string) => `/d-solo/${page}`, diff --git a/packages/grafana-e2e/src/flows/addPanel.ts b/packages/grafana-e2e/src/flows/addPanel.ts index 9e1907f09bb..f24029c400b 100644 --- a/packages/grafana-e2e/src/flows/addPanel.ts +++ b/packages/grafana-e2e/src/flows/addPanel.ts @@ -9,6 +9,5 @@ export const addPanel = (config?: Partial) => panelTitle: `e2e-${uuidv4()}`, ...config, isEdit: false, - isExplore: false, }) ); diff --git a/packages/grafana-e2e/src/flows/configurePanel.ts b/packages/grafana-e2e/src/flows/configurePanel.ts index c4b75ff55ec..182bdaab287 100644 --- a/packages/grafana-e2e/src/flows/configurePanel.ts +++ b/packages/grafana-e2e/src/flows/configurePanel.ts @@ -2,7 +2,7 @@ import { e2e } from '../index'; import { getScenarioContext } from '../support/scenarioContext'; import { selectOption } from './selectOption'; import { setDashboardTimeRange } from './setDashboardTimeRange'; -import { setTimeRange, TimeRangeConfig } from './setTimeRange'; +import { TimeRangeConfig } from './setTimeRange'; interface AddPanelOverrides { dataSourceName: string; @@ -33,12 +33,10 @@ interface ConfigurePanelOptional { panelTitle?: string; timeRange?: TimeRangeConfig; visualizationName?: string; - matchExploreTable?: boolean; } interface ConfigurePanelRequired { isEdit: boolean; - isExplore: boolean; } export type PartialConfigurePanelConfig = Partial & @@ -74,8 +72,6 @@ export const configurePanel = (config: PartialAddPanelConfig | PartialEditPanelC dashboardUid, dataSourceName, isEdit, - isExplore, - matchExploreTable, matchScreenshot, panelTitle, queriesForm, @@ -85,32 +81,20 @@ export const configurePanel = (config: PartialAddPanelConfig | PartialEditPanelC visualizationName, } = fullConfig; - if (isEdit && isExplore) { - throw new TypeError('Invalid configuration'); + if (visitDashboardAtStart) { + e2e.flows.openDashboard({ uid: dashboardUid }); } - if (isExplore) { - e2e.pages.Explore.visit(); + if (isEdit) { + e2e.components.Panels.Panel.title(panelTitle).click(); + e2e.components.Panels.Panel.headerItems('Edit').click(); } else { - if (visitDashboardAtStart) { - e2e.flows.openDashboard({ uid: dashboardUid }); - } - - if (isEdit) { - e2e.components.Panels.Panel.title(panelTitle).click(); - e2e.components.Panels.Panel.headerItems('Edit').click(); - } else { - e2e.components.PageToolbar.item('Add panel').click(); - e2e.pages.AddDashboard.addNewPanel().click(); - } + e2e.components.PageToolbar.item('Add panel').click(); + e2e.pages.AddDashboard.addNewPanel().click(); } if (timeRange) { - if (isExplore) { - e2e.pages.Explore.Toolbar.navBar().within(() => setTimeRange(timeRange)); - } else { - setDashboardTimeRange(timeRange); - } + setDashboardTimeRange(timeRange); } // @todo alias '/**/*.js*' as '@pluginModule' when possible: https://github.com/cypress-io/cypress/issues/1296 @@ -127,25 +111,23 @@ export const configurePanel = (config: PartialAddPanelConfig | PartialEditPanelC // @todo instead wait for '@pluginModule' if not already loaded e2e().wait(2000); - if (!isExplore) { - // `panelTitle` is needed to edit the panel, and unlikely to have its value changed at that point - const changeTitle = panelTitle && !isEdit; + // `panelTitle` is needed to edit the panel, and unlikely to have its value changed at that point + const changeTitle = panelTitle && !isEdit; - if (changeTitle || visualizationName) { - if (changeTitle && panelTitle) { - e2e.components.PanelEditor.OptionsPane.fieldLabel('Panel options Title').type(`{selectall}${panelTitle}`); - } - - if (visualizationName) { - e2e.components.PluginVisualization.item(visualizationName).scrollIntoView().click(); - - // @todo wait for '@pluginModule' if not a core visualization and not already loaded - e2e().wait(2000); - } - } else { - // Consistently closed - closeOptions(); + if (changeTitle || visualizationName) { + if (changeTitle && panelTitle) { + e2e.components.PanelEditor.OptionsPane.fieldLabel('Panel options Title').type(`{selectall}${panelTitle}`); } + + if (visualizationName) { + e2e.components.PluginVisualization.item(visualizationName).scrollIntoView().click(); + + // @todo wait for '@pluginModule' if not a core visualization and not already loaded + e2e().wait(2000); + } + } else { + // Consistently closed + closeOptions(); } if (queriesForm) { @@ -164,11 +146,6 @@ export const configurePanel = (config: PartialAddPanelConfig | PartialEditPanelC //e2e.components.QueryEditorRow.actionButton('Disable/enable query').click(); //e2e().wait('@chartData'); - if (!isExplore) { - e2e().get('button[title="Apply changes and go back to dashboard"]').click(); - e2e().url().should('include', `/d/${dashboardUid}`); - } - // Avoid annotations flakiness e2e.components.RefreshPicker.runButton().should('be.visible').click(); @@ -180,11 +157,7 @@ export const configurePanel = (config: PartialAddPanelConfig | PartialEditPanelC if (matchScreenshot) { let visualization; - if (isExplore) { - visualization = matchExploreTable ? e2e.pages.Explore.General.table() : e2e.pages.Explore.General.graph(); - } else { - visualization = e2e.components.Panels.Panel.containerByTitle(panelTitle).find('.panel-content'); - } + visualization = e2e.components.Panels.Panel.containerByTitle(panelTitle).find('.panel-content'); visualization.scrollIntoView().screenshot(screenshotName); e2e().compareScreenshots(screenshotName); diff --git a/packages/grafana-e2e/src/flows/editPanel.ts b/packages/grafana-e2e/src/flows/editPanel.ts index c003d4729a5..c213465902a 100644 --- a/packages/grafana-e2e/src/flows/editPanel.ts +++ b/packages/grafana-e2e/src/flows/editPanel.ts @@ -4,5 +4,4 @@ export const editPanel = (config: Partial) => configurePanel({ ...config, isEdit: true, - isExplore: false, }); diff --git a/packages/grafana-e2e/src/flows/explore.ts b/packages/grafana-e2e/src/flows/explore.ts deleted file mode 100644 index cc9dd1047c2..00000000000 --- a/packages/grafana-e2e/src/flows/explore.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { configurePanel, PartialConfigurePanelConfig } from './configurePanel'; -import { getScenarioContext } from '../support/scenarioContext'; - -export const explore = (config: Partial) => - getScenarioContext().then(({ lastAddedDataSource }: any) => - configurePanel({ - dataSourceName: lastAddedDataSource, - screenshotName: 'explore-graph', - ...config, - isEdit: false, - isExplore: true, - timeRange: { - from: '2020-01-01 00:00:00', - to: '2020-01-01 06:00:00', - zone: 'Coordinated Universal Time', - ...config.timeRange, - }, - }) - ); diff --git a/packages/grafana-e2e/src/flows/index.ts b/packages/grafana-e2e/src/flows/index.ts index 83ba4e5a8b4..1fc8212c628 100644 --- a/packages/grafana-e2e/src/flows/index.ts +++ b/packages/grafana-e2e/src/flows/index.ts @@ -5,7 +5,6 @@ export * from './assertSuccessNotification'; export * from './deleteDashboard'; export * from './deleteDataSource'; export * from './editPanel'; -export * from './explore'; export * from './login'; export * from './openDashboard'; export * from './openPanelMenuItem'; diff --git a/packages/grafana-runtime/src/components/DataSourcePicker.tsx b/packages/grafana-runtime/src/components/DataSourcePicker.tsx index 8cba8f45f70..fc2663d5249 100644 --- a/packages/grafana-runtime/src/components/DataSourcePicker.tsx +++ b/packages/grafana-runtime/src/components/DataSourcePicker.tsx @@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; // Components -import { ActionMeta, HorizontalGroup, PluginSignatureBadge, Select, stylesFactory } from '@grafana/ui'; +import { ActionMeta, HorizontalGroup, PluginSignatureBadge, Select } from '@grafana/ui'; import { DataSourceInstanceSettings, DataSourceRef, @@ -12,7 +12,6 @@ import { } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { getDataSourceSrv } from '../services/dataSourceSrv'; -import { css, cx } from '@emotion/css'; /** * Component props description for the {@link DataSourcePicker} @@ -152,7 +151,6 @@ export class DataSourcePicker extends PureComponent ({ - select: cx( - css({ - minWidth: 200, - }), - 'ds-picker', - 'select-container' - ), -})); diff --git a/packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx b/packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx index e27106b301f..b13d341c15a 100644 --- a/packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx +++ b/packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx @@ -12,7 +12,7 @@ import { getFocusStyles } from '../../themes/mixins'; export interface Props { pageIcon?: IconName; - title: string; + title?: string; parent?: string; onGoBack?: () => void; titleHref?: string; @@ -80,7 +80,8 @@ export const PageToolbar: FC = React.memo( )} )} - {titleHref && ( + + {title && titleHref && (

= React.memo(

)} - {!titleHref &&

{title}

} + {title && !titleHref &&

{title}

} {leftItems?.map((child, index) => (
diff --git a/public/app/features/explore/ExploreToolbar.tsx b/public/app/features/explore/ExploreToolbar.tsx index bd2ab781dba..efb9431f161 100644 --- a/public/app/features/explore/ExploreToolbar.tsx +++ b/public/app/features/explore/ExploreToolbar.tsx @@ -1,10 +1,7 @@ import React, { PureComponent } from 'react'; import { connect, ConnectedProps } from 'react-redux'; -import classNames from 'classnames'; -import { css } from '@emotion/css'; - import { ExploreId, ExploreItemState } from 'app/types/explore'; -import { Icon, IconButton, SetInterval, ToolbarButton, ToolbarButtonRow, Tooltip } from '@grafana/ui'; +import { PageToolbar, SetInterval, ToolbarButton, ToolbarButtonRow } from '@grafana/ui'; import { DataSourceInstanceSettings, RawTimeRange } from '@grafana/data'; import { DataSourcePicker } from '@grafana/runtime'; import { StoreState } from 'app/types/store'; @@ -18,9 +15,10 @@ import { ExploreTimeControls } from './ExploreTimeControls'; import { LiveTailButton } from './LiveTailButton'; import { RunButton } from './RunButton'; import { LiveTailControls } from './useLiveTailControls'; -import { cancelQueries, clearQueries, runQueries } from './state/query'; +import { cancelQueries, runQueries } from './state/query'; import ReturnToDashboardButton from './ReturnToDashboardButton'; import { isSplit } from './state/selectors'; +import { DashNavButton } from '../dashboard/components/DashNav/DashNavButton'; interface OwnProps { exploreId: ExploreId; @@ -34,10 +32,6 @@ class UnConnectedExploreToolbar extends PureComponent { this.props.changeDatasource(this.props.exploreId, dsSettings.uid, { importQueries: true }); }; - onClearAll = () => { - this.props.clearAll(this.props.exploreId); - }; - onRunQuery = (loading = false) => { const { runQueries, cancelQueries, exploreId } = this.props; if (loading) { @@ -83,131 +77,88 @@ class UnConnectedExploreToolbar extends PureComponent { const showSmallTimePicker = splitted || containerWidth < 1210; return ( -
-
-
-
- {exploreId === 'left' && ( - - - Explore - - )} -
- {splitted && ( - closeSplit(exploreId)} - name="times" - /> - )} -
-
-
-
- {!datasourceMissing ? ( -
-
- -
-
- ) : null} - - + createAndCopyShortLink(window.location.href)} + aria-label="Copy shortened link" + /> + ), + !datasourceMissing && ( + + ), + ].filter(Boolean)} + > + + - {exploreId === 'left' && !splitted ? ( - split()} - icon="columns" - disabled={isLive} - > - Split - - ) : null} + {!splitted ? ( + split()} icon="columns" disabled={isLive}> + Split + + ) : ( + closeSplit(exploreId)} icon="times"> + Close + + )} - - createAndCopyShortLink(window.location.href)} - aria-label="Copy shortened link to the executed query" - /> - + {!isLive && ( + + )} - {!isLive && ( - + + {refreshInterval && } + + {hasLiveOption && ( + + {(controls) => ( + )} - - {!isLive && ( - - Clear all - - )} - - - - {refreshInterval && } - - {hasLiveOption && ( - - {(controls) => ( - - )} - - )} - -
-
-
+ + )} + + ); } } @@ -248,7 +199,6 @@ const mapStateToProps = (state: StoreState, { exploreId }: OwnProps) => { const mapDispatchToProps = { changeDatasource, changeRefreshInterval, - clearAll: clearQueries, cancelQueries, runQueries, closeSplit: splitClose, diff --git a/public/app/features/explore/state/query.ts b/public/app/features/explore/state/query.ts index 04a284ef92b..8467a0be6c5 100644 --- a/public/app/features/explore/state/query.ts +++ b/public/app/features/explore/state/query.ts @@ -37,7 +37,7 @@ import { localStorageFullAction, richHistoryLimitExceededAction, richHistoryUpda import { AnyAction, createAction, PayloadAction } from '@reduxjs/toolkit'; import { updateTime } from './time'; import { historyUpdatedAction } from './history'; -import { createCacheKey, createEmptyQueryResponse, getResultsFromCache } from './utils'; +import { createCacheKey, getResultsFromCache } from './utils'; import deepEqual from 'fast-deep-equal'; // @@ -64,18 +64,13 @@ export interface ChangeQueriesPayload { } export const changeQueriesAction = createAction('explore/changeQueries'); -/** - * Clear all queries and results. - */ -export interface ClearQueriesPayload { - exploreId: ExploreId; -} -export const clearQueriesAction = createAction('explore/clearQueries'); - /** * Cancel running queries. */ -export const cancelQueriesAction = createAction('explore/cancelQueries'); +export interface CancelQueriesPayload { + exploreId: ExploreId; +} +export const cancelQueriesAction = createAction('explore/cancelQueries'); export interface QueriesImportedPayload { exploreId: ExploreId; @@ -222,17 +217,6 @@ export function addQueryRow(exploreId: ExploreId, index: number): ThunkResult { - return (dispatch) => { - dispatch(scanStopAction({ exploreId })); - dispatch(clearQueriesAction({ exploreId })); - dispatch(stateSave()); - }; -} - /** * Cancel running queries */ @@ -627,21 +611,6 @@ export const queryReducer = (state: ExploreItemState, action: AnyAction): Explor }; } - if (clearQueriesAction.match(action)) { - const queries = ensureQueries(); - stopQueryState(state.querySubscription); - return { - ...state, - queries: queries.slice(), - graphResult: null, - tableResult: null, - logsResult: null, - queryKeys: getQueryKeys(queries, state.datasourceInstance), - queryResponse: createEmptyQueryResponse(), - loading: false, - }; - } - if (cancelQueriesAction.match(action)) { stopQueryState(state.querySubscription); diff --git a/public/sass/pages/_explore.scss b/public/sass/pages/_explore.scss index ae1bc264206..ff6f7d6062d 100644 --- a/public/sass/pages/_explore.scss +++ b/public/sass/pages/_explore.scss @@ -1,152 +1,16 @@ -.icon-margin-right { - margin-right: 0.25em; - - @media only screen and (max-width: 1320px) { - margin-right: 0; - } -} - -.icon-brand-gradient { - color: linear-gradient(180deg, #f05a28 30%, #fbca0a 100%); -} - -.icon-margin-left { - margin-left: 0.25em; - - @media only screen and (max-width: 1320px) { - margin-left: 0; - } -} - .explore-active-button { box-shadow: $btn-active-box-shadow; border: 1px solid $orange-dark !important; color: $orange-dark !important; } -.explore-ds-picker { - min-width: 200px; - max-width: 300px; -} - -.explore-ds-picker--small { - min-width: 60px; - max-width: 60px; - - .ds-picker { - min-width: 60px; - max-width: 60px; - } -} - -.explore-toolbar { - background: inherit; - display: flex; - flex-flow: row wrap; - justify-content: flex-start; - height: auto; - padding: 0 $dashboard-padding; - border-bottom: 1px solid #0000; - transition-duration: 0.35s; - transition-timing-function: ease-in-out; - transition-property: box-shadow, border-bottom; -} - -.explore-toolbar-item { - position: relative; - align-self: center; - - &:first-child { - padding-left: 34px; - - @include media-breakpoint-up(md) { - padding-left: 0; - } - } -} - -.explore-toolbar.splitted { - .explore-toolbar-item { - flex: 1 1 100%; - } - - .explore-toolbar-content-item:first-child { - padding-left: 0; - margin-right: auto; - } -} - -.explore-toolbar-item:last-child { - flex: auto; -} - -.explore-toolbar-header { - display: flex; - flex: 1 1 0; - flex-flow: row nowrap; - font-size: 18px; - min-height: $navbarHeight; - line-height: $navbarHeight; - justify-content: space-between; - align-items: center; -} - -.explore-toolbar-header-close { - margin-left: auto; - color: $text-color-weak; -} - -.explore-toolbar-content { - display: flex; - flex-flow: row wrap; - align-items: center; - justify-content: flex-end; -} - -.explore-toolbar-content-item { - display: flex; - padding: 0px 2px; - position: relative; - - &:first-child { - padding-left: $dashboard-padding; - margin-right: auto; - } -} - -@media only screen and (max-width: 1070px) { - .explore-toolbar-content { - justify-content: flex-start; - } - - .explore-toolbar.splitted { - .explore-toolbar-content-item { - padding: 2px 0; - } - } -} - -@media only screen and (max-width: 810px) { - .explore-toolbar { - .explore-toolbar-content-item { - &:first-child { - padding-left: 2px; - margin: 0 12px 0 16px; - } - } - } -} - +// TODO: check if this is used .explore { display: flex; flex: 1 1 auto; flex-direction: column; } -.explore.explore-live { - flex-direction: column-reverse; -} - .explore + .explore { border-left: 1px dotted $table-border; } @@ -156,10 +20,7 @@ flex: 1 1 auto; flex-direction: column; padding: $dashboard-padding; -} - -.explore-container.explore-live { - flex-direction: column-reverse; + padding-top: 0; } .explore-wrapper { @@ -171,10 +32,12 @@ } } +// TODO: this is used in Loki & Prometheus, move it .explore-input-margin { margin-right: 4px; } +// TODO: move to public/app/features/explore/Time.tsx .navbar .elapsed-time { position: absolute; left: 0; @@ -188,78 +51,12 @@ flex-wrap: wrap; } -.query-row { - display: flex; - position: relative; - align-items: flex-start; - @include media-breakpoint-down(sm) { - flex-wrap: wrap; - } - - & + & { - margin-top: $space-sm; - } -} - -.query-row-tools { - white-space: nowrap; -} - -.query-row-status { - position: relative; - top: 0; - right: 35px; - z-index: 1015; - display: flex; - flex-direction: column; - justify-content: center; - height: $input-height; - width: 0; -} - -.query-row-field { - margin-right: 3px; - flex-grow: 1; -} - +// TODO: move to Loki and Prometheus .query-row-break { flex-basis: 100%; } -.query-transactions { - display: table; -} - -.query-transaction { - display: table-row; - color: $text-color-weak; - line-height: 1.44; -} - -.query-transaction--loading { - animation: query-loading-color-change 1s alternate 100; -} - -@keyframes query-loading-color-change { - from { - color: $text-color-faint; - } - - to { - color: $blue; - } -} - -.query-transaction__type, -.query-transaction__duration { - display: table-cell; - font-size: $font-size-xs; - text-align: right; - padding-right: 0.25em; -} - -// Prometheus-specifics, to be extracted to datasource soon - +// TODO: Prometheus-specifics, to be extracted to datasource soon .explore { .prom-query-field-info { margin: 0.25em 0.5em 0.5em;