From 74893748014e9e3006b3099203e038997c2df3a1 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 25 Feb 2019 15:22:29 -0700 Subject: [PATCH] Add refreshConfig to embeddable container state (#31611) (#31959) * Add refreshConfig to embeddable container state * TS fun --- .../kibana/public/dashboard/README.md | 38 +++++++++---------- .../kibana/public/dashboard/actions/view.ts | 10 ++++- .../kibana/public/dashboard/dashboard_app.js | 6 ++- .../dashboard/dashboard_state_manager.js | 8 ++++ .../public/dashboard/panel/dashboard_panel.js | 1 + .../kibana/public/dashboard/reducers/view.ts | 10 ++++- .../public/dashboard/selectors/dashboard.ts | 13 ++++++- .../public/dashboard/selectors/types.ts | 3 +- src/legacy/ui/public/embeddable/index.ts | 2 +- src/legacy/ui/public/embeddable/types.ts | 7 ++++ 10 files changed, 73 insertions(+), 25 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/README.md b/src/legacy/core_plugins/kibana/public/dashboard/README.md index 70735bba7b83b..01aef6db7dfa0 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/README.md +++ b/src/legacy/core_plugins/kibana/public/dashboard/README.md @@ -17,8 +17,8 @@ A high level walk through of types of dashboard state and how dashboard and separation because it allows us to force some consistency in our UX. For example, we may not want a visualization to all of a sudden go from supporting drilldown links to not supporting it, as it would mean disappearing panel context menu items. - -**Embeddable state** - Data the embeddable gives the dashboard throughout it's lifecycle as + +**Embeddable state** - Data the embeddable gives the dashboard throughout it's lifecycle as things update and the user interacts with it. This is communicated to the dashboard via the function `onEmbeddableStateChanged` that is passed in to the `EmbeddableFactory.create` call. @@ -26,10 +26,10 @@ A high level walk through of types of dashboard state and how dashboard and as things update and the user interacts with Kibana. This is communicated to the embeddable via the function `onContainerStateChanged` which is returned from the `EmbeddableFactory.create` call -**Container metadata** - State that only needs to be given to the embeddable once, +**Container metadata** - State that only needs to be given to the embeddable once, and does not change thereafter. This will contain data given to dashboard when a new embeddable is added to a dashboard. Currently, this is really only the saved object id. - + **Dashboard storage data** - Data persisted in elasticsearch. Should not be coupled to the redux tree. **Dashboard redux tree** - State stored in the dashboard redux tree. @@ -38,9 +38,9 @@ A high level walk through of types of dashboard state and how dashboard and I'm not sure if *any* data belongs here but we talked about it, so keeping it in here. We thought initially it could be supportsDrillDowns but for type visualizations, for example, this depends on the visualization "subtype" (e.g. input controls vs line chart). - - - + + + ### Dashboard/Embeddable communication psuedocode ```js dashboard_panel.js: @@ -59,11 +59,11 @@ class EmbeddableViewport extends Component { } } } - + componentWillUnmount() { this.embeddable.destroy(); } - + // We let react/redux tell us when to tell the embeddable that some container // state changed. componentDidUpdate(prevProps) { @@ -71,7 +71,7 @@ class EmbeddableViewport extends Component { this.embeddable.onContainerStateChanged(this.props.containerState); } } - + render() { return (
@@ -106,7 +106,7 @@ State communicated to the embeddable. embeddableCustomization: Object, hidePanelTitles: boolean, title: string, - + // TODO: filters: FilterObject, timeRange: TimeRangeObject, @@ -124,21 +124,21 @@ State communicated to the embeddable. ### Embeddable Metadata ``` - { + { // Index patterns used by this embeddable. This information is currently // used by the filter on a dashboard for which fields to show in the // dropdown. Otherwise we'd have to show all fields over all indexes and // if no embeddables use those index patterns, there really is no point // to filtering on them. indexPatterns: Array., - + // Dashboard navigates to this url when the user clicks 'Edit visualization' // in the panel context menu. editUrl: string, - + // Title to be shown in the panel. Can be overridden at the panel level. title: string, - + // TODO: // If this is true, then dashboard will show a "configure drill down // links" menu option in the context menu for the panel. @@ -156,12 +156,12 @@ Embeddable state is the data that the embeddable gives dashboard when something // If a filter action was initiated by a user action (e.g. clicking on a bar chart) // This is how dashboard will know and update itself to match. stagedFilters: FilterObject, - - - // TODO: More possible options to go in here: + + + // TODO: More possible options to go in here: error: Error, isLoading: boolean, - renderComplete: boolean, + renderComplete: boolean, appliedtimeRange: TimeRangeObject, stagedTimeRange: TimeRangeObject, // This information will need to be exposed so other plugins (e.g. ML) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/actions/view.ts b/src/legacy/core_plugins/kibana/public/dashboard/actions/view.ts index 2b608b2db6d12..1b84df22f8df1 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/actions/view.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/actions/view.ts @@ -18,7 +18,7 @@ */ import { createAction } from 'redux-actions'; -import { Filters, Query, TimeRange } from 'ui/embeddable'; +import { Filters, Query, RefreshConfig, TimeRange } from 'ui/embeddable'; import { KibanaAction } from '../../selectors/types'; import { DashboardViewMode } from '../dashboard_view_mode'; import { PanelId } from '../selectors'; @@ -32,6 +32,7 @@ export enum ViewActionTypeKeys { UPDATE_USE_MARGINS = 'UPDATE_USE_MARGINS', UPDATE_HIDE_PANEL_TITLES = 'UPDATE_HIDE_PANEL_TITLES', UPDATE_TIME_RANGE = 'UPDATE_TIME_RANGE', + UPDATE_REFRESH_CONFIG = 'UPDATE_REFRESH_CONFIG', UPDATE_FILTERS = 'UPDATE_FILTERS', UPDATE_QUERY = 'UPDATE_QUERY', CLOSE_CONTEXT_MENU = 'CLOSE_CONTEXT_MENU', @@ -64,6 +65,9 @@ export interface UpdateHidePanelTitlesAction export interface UpdateTimeRangeAction extends KibanaAction {} +export interface UpdateRefreshConfigAction + extends KibanaAction {} + export interface UpdateFiltersAction extends KibanaAction {} @@ -79,6 +83,7 @@ export type ViewActions = | UpdateUseMarginsAction | UpdateHidePanelTitlesAction | UpdateTimeRangeAction + | UpdateRefreshConfigAction | UpdateFiltersAction | UpdateQueryAction; @@ -97,5 +102,8 @@ export const updateHidePanelTitles = createAction( ViewActionTypeKeys.UPDATE_HIDE_PANEL_TITLES ); export const updateTimeRange = createAction(ViewActionTypeKeys.UPDATE_TIME_RANGE); +export const updateRefreshConfig = createAction( + ViewActionTypeKeys.UPDATE_REFRESH_CONFIG +); export const updateFilters = createAction(ViewActionTypeKeys.UPDATE_FILTERS); export const updateQuery = createAction(ViewActionTypeKeys.UPDATE_QUERY); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.js b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.js index a2f997205a398..880455cbd82d7 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.js @@ -183,6 +183,7 @@ app.directive('dashboardApp', function ($injector) { courier.fetch(); }; dashboardStateManager.handleTimeChange(timefilter.getTime()); + dashboardStateManager.handleRefreshConfigChange(timefilter.getRefreshInterval()); $scope.expandedPanel = null; $scope.dashboardViewMode = dashboardStateManager.getViewMode(); @@ -291,7 +292,10 @@ app.directive('dashboardApp', function ($injector) { // directly passed down time filter. Then we can get rid of this reliance on scope broadcasts. $scope.refresh(); }); - $scope.$listenAndDigestAsync(timefilter, 'refreshIntervalUpdate', updateState); + $scope.$listenAndDigestAsync(timefilter, 'refreshIntervalUpdate', () => { + dashboardStateManager.handleRefreshConfigChange(timefilter.getRefreshInterval()); + updateState(); + }); $scope.$listenAndDigestAsync(timefilter, 'timeUpdate', updateState); function updateViewMode(newMode) { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.js b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.js index d2d4c5b92e729..db9f73ca9e908 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.js @@ -34,6 +34,7 @@ import { updateDescription, updateHidePanelTitles, updateTimeRange, + updateRefreshConfig, clearStagedFilters, updateFilters, updateQuery, @@ -151,6 +152,13 @@ export class DashboardStateManager { })); } + handleRefreshConfigChange({ pause, value }) { + store.dispatch(updateRefreshConfig({ + isPaused: pause, + interval: value, + })); + } + /** * Changes made to app state outside of direct calls to this class will need to be propagated to the store. * @private diff --git a/src/legacy/core_plugins/kibana/public/dashboard/panel/dashboard_panel.js b/src/legacy/core_plugins/kibana/public/dashboard/panel/dashboard_panel.js index 500079cf22a89..a36c4ba36995d 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/panel/dashboard_panel.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/panel/dashboard_panel.js @@ -177,6 +177,7 @@ DashboardPanelUi.propTypes = { destroy: PropTypes.func.isRequired, containerState: PropTypes.shape({ timeRange: PropTypes.object, + refreshConfig: PropTypes.object, filters: PropTypes.array, query: PropTypes.object, embeddableCustomization: PropTypes.object, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/reducers/view.ts b/src/legacy/core_plugins/kibana/public/dashboard/reducers/view.ts index 94607dc997e08..3792203bcb231 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/reducers/view.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/reducers/view.ts @@ -21,7 +21,7 @@ import { cloneDeep } from 'lodash'; import { Reducer } from 'redux'; import { ViewActions, ViewActionTypeKeys } from '../actions'; -import { Filters, Query, TimeRange } from 'ui/embeddable'; +import { Filters, Query, RefreshConfig, TimeRange } from 'ui/embeddable'; import { QueryLanguageType } from 'ui/embeddable/types'; import { DashboardViewMode } from '../dashboard_view_mode'; import { PanelId, ViewState } from '../selectors'; @@ -61,6 +61,11 @@ const updateTimeRange = (view: ViewState, timeRange: TimeRange) => ({ timeRange, }); +const updateRefreshConfig = (view: ViewState, refreshConfig: RefreshConfig) => ({ + ...view, + refreshConfig, +}); + const updateFilters = (view: ViewState, filters: Filters) => ({ ...view, filters: cloneDeep(filters), @@ -88,6 +93,7 @@ export const viewReducer: Reducer = ( isFullScreenMode: false, query: { language: QueryLanguageType.LUCENE, query: '' }, timeRange: { to: 'now', from: 'now-15m' }, + refreshConfig: { isPaused: true, interval: 0 }, useMargins: true, viewMode: DashboardViewMode.VIEW, }, @@ -106,6 +112,8 @@ export const viewReducer: Reducer = ( return updateHidePanelTitles(view, action.payload); case ViewActionTypeKeys.UPDATE_TIME_RANGE: return updateTimeRange(view, action.payload); + case ViewActionTypeKeys.UPDATE_REFRESH_CONFIG: + return updateRefreshConfig(view, action.payload); case ViewActionTypeKeys.UPDATE_USE_MARGINS: return updateUseMargins(view, action.payload); case ViewActionTypeKeys.UPDATE_VIEW_MODE: diff --git a/src/legacy/core_plugins/kibana/public/dashboard/selectors/dashboard.ts b/src/legacy/core_plugins/kibana/public/dashboard/selectors/dashboard.ts index afdd1657e686b..af04f0e565d3e 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/selectors/dashboard.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/selectors/dashboard.ts @@ -18,7 +18,14 @@ */ import _ from 'lodash'; -import { ContainerState, EmbeddableMetadata, Filters, Query, TimeRange } from 'ui/embeddable'; +import { + ContainerState, + EmbeddableMetadata, + Filters, + Query, + RefreshConfig, + TimeRange, +} from 'ui/embeddable'; import { EmbeddableCustomization } from 'ui/embeddable/types'; import { DashboardViewMode } from '../dashboard_view_mode'; import { @@ -108,6 +115,9 @@ export const getMaximizedPanelId = (dashboard: DashboardState): PanelId | undefi export const getTimeRange = (dashboard: DashboardState): TimeRange => dashboard.view.timeRange; +export const getRefreshConfig = (dashboard: DashboardState): RefreshConfig => + dashboard.view.refreshConfig; + export const getFilters = (dashboard: DashboardState): Filters => dashboard.view.filters; export const getQuery = (dashboard: DashboardState): Query => dashboard.view.query; @@ -132,6 +142,7 @@ export const getContainerState = (dashboard: DashboardState, panelId: PanelId): from: time.from, to: time.to, }, + refreshConfig: getRefreshConfig(dashboard), viewMode: getViewMode(dashboard), }; }; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/selectors/types.ts b/src/legacy/core_plugins/kibana/public/dashboard/selectors/types.ts index 806e9f2ca4881..abfdd9ec5bddb 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/selectors/types.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/selectors/types.ts @@ -17,7 +17,7 @@ * under the License. */ -import { EmbeddableMetadata, Filters, Query, TimeRange } from 'ui/embeddable'; +import { EmbeddableMetadata, Filters, Query, RefreshConfig, TimeRange } from 'ui/embeddable'; import { DashboardViewMode } from '../dashboard_view_mode'; export interface ViewState { @@ -26,6 +26,7 @@ export interface ViewState { readonly maximizedPanelId?: string; readonly visibleContextMenuPanelId?: string; readonly timeRange: TimeRange; + readonly refreshConfig: RefreshConfig; readonly hidePanelTitles: boolean; readonly useMargins: boolean; readonly query: Query; diff --git a/src/legacy/ui/public/embeddable/index.ts b/src/legacy/ui/public/embeddable/index.ts index 3899f05c684b6..9b6f010f893da 100644 --- a/src/legacy/ui/public/embeddable/index.ts +++ b/src/legacy/ui/public/embeddable/index.ts @@ -21,4 +21,4 @@ export { EmbeddableFactory, OnEmbeddableStateChanged } from './embeddable_factor export * from './embeddable'; export * from './context_menu_actions'; export { EmbeddableFactoriesRegistryProvider } from './embeddable_factories_registry'; -export { ContainerState, EmbeddableState, Query, Filters, TimeRange } from './types'; +export { ContainerState, EmbeddableState, Query, Filters, TimeRange, RefreshConfig } from './types'; diff --git a/src/legacy/ui/public/embeddable/types.ts b/src/legacy/ui/public/embeddable/types.ts index 683e3471df6f6..c29e75c2211cb 100644 --- a/src/legacy/ui/public/embeddable/types.ts +++ b/src/legacy/ui/public/embeddable/types.ts @@ -22,6 +22,11 @@ export interface TimeRange { from: string; } +export interface RefreshConfig { + isPaused: boolean; + interval: number; +} + export interface FilterMeta { disabled: boolean; } @@ -55,6 +60,8 @@ export interface ContainerState { filters: Filters; + refreshConfig: RefreshConfig; + query: Query; // The shape will be up to the embeddable type.