mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[controls] add filters, query, and timeRange props to ControlGroupRenderer and create search example (#147581)
Part of https://github.com/elastic/kibana/issues/145428 PR makes the following changes: 1) updates ControlGroupRenderer component with declarative properties for filters, query, and timeRange. 2) creates a search example showing how to use controls to narrow results 3) Updates redux example to use web logs sample data set 4) Updates existing uses of ControlGroupRenderer to use new props. <img width="600" alt="Screen Shot 2022-12-14 at 4 29 58 PM" src="https://user-images.githubusercontent.com/373691/207719012-28771203-27c3-45c0-a8ac-2bf96c10f641.png"> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
486b2e0068
commit
b5d3a63516
10 changed files with 299 additions and 114 deletions
|
@ -7,5 +7,12 @@
|
|||
"version": "1.0.0",
|
||||
"kibanaVersion": "kibana",
|
||||
"ui": true,
|
||||
"requiredPlugins": ["data", "developerExamples", "presentationUtil", "controls"]
|
||||
"requiredPlugins": [
|
||||
"controls",
|
||||
"data",
|
||||
"developerExamples",
|
||||
"embeddable",
|
||||
"navigation",
|
||||
"presentationUtil"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -8,34 +8,36 @@
|
|||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import { AppMountParameters } from '@kbn/core/public';
|
||||
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
|
||||
import { ControlsExampleStartDeps } from './plugin';
|
||||
import { BasicReduxExample } from './basic_redux_example';
|
||||
import { SearchExample } from './search_example';
|
||||
|
||||
const ControlsExamples = ({ dataViewId }: { dataViewId?: string }) => {
|
||||
const examples = dataViewId ? (
|
||||
<>
|
||||
<BasicReduxExample dataViewId={dataViewId} />
|
||||
</>
|
||||
) : (
|
||||
<div>{'Please install e-commerce sample data to run controls examples.'}</div>
|
||||
);
|
||||
return (
|
||||
export const renderApp = async (
|
||||
{ data, navigation }: ControlsExampleStartDeps,
|
||||
{ element }: AppMountParameters
|
||||
) => {
|
||||
const dataViews = await data.dataViews.find('kibana_sample_data_logs');
|
||||
const examples =
|
||||
dataViews.length > 0 ? (
|
||||
<>
|
||||
<SearchExample dataView={dataViews[0]} navigation={navigation} data={data} />
|
||||
<EuiSpacer size="xl" />
|
||||
<BasicReduxExample dataViewId={dataViews[0].id!} />
|
||||
</>
|
||||
) : (
|
||||
<div>{'Install web logs sample data to run controls examples.'}</div>
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
<KibanaPageTemplate>
|
||||
<KibanaPageTemplate.Header pageTitle="Controls as a Building Block" />
|
||||
<KibanaPageTemplate.Section>{examples}</KibanaPageTemplate.Section>
|
||||
</KibanaPageTemplate>
|
||||
</KibanaPageTemplate>,
|
||||
element
|
||||
);
|
||||
};
|
||||
|
||||
export const renderApp = async (
|
||||
{ data }: ControlsExampleStartDeps,
|
||||
{ element }: AppMountParameters
|
||||
) => {
|
||||
const dataViews = await data.dataViews.find('kibana_sample_data_ecommerce');
|
||||
const dataViewId = dataViews.length > 0 ? dataViews[0].id : undefined;
|
||||
ReactDOM.render(<ControlsExamples dataViewId={dataViewId} />, element);
|
||||
return () => ReactDOM.unmountComponentAtNode(element);
|
||||
};
|
||||
|
|
|
@ -15,15 +15,8 @@ import {
|
|||
ControlStyle,
|
||||
} from '@kbn/controls-plugin/public';
|
||||
import { withSuspense } from '@kbn/presentation-util-plugin/public';
|
||||
import {
|
||||
EuiButtonGroup,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { EuiButtonGroup, EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
|
||||
const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer);
|
||||
|
||||
|
@ -44,51 +37,36 @@ export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => {
|
|||
const controlStyle = select((state) => state.explicitInput.controlStyle);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText>
|
||||
<p>Choose a style for your control group:</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiButtonGroup
|
||||
legend="Text style"
|
||||
options={[
|
||||
{
|
||||
id: `oneLine`,
|
||||
label: 'One line',
|
||||
value: 'oneLine' as ControlStyle,
|
||||
},
|
||||
{
|
||||
id: `twoLine`,
|
||||
label: 'Two lines',
|
||||
value: 'twoLine' as ControlStyle,
|
||||
},
|
||||
]}
|
||||
idSelected={controlStyle}
|
||||
onChange={(id, value) => {
|
||||
dispatch(setControlStyle(value));
|
||||
}}
|
||||
type="single"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
<EuiButtonGroup
|
||||
legend="Text style"
|
||||
options={[
|
||||
{
|
||||
id: `oneLine`,
|
||||
label: 'One line',
|
||||
value: 'oneLine' as ControlStyle,
|
||||
},
|
||||
{
|
||||
id: `twoLine`,
|
||||
label: 'Two lines',
|
||||
value: 'twoLine' as ControlStyle,
|
||||
},
|
||||
]}
|
||||
idSelected={controlStyle}
|
||||
onChange={(id, value) => {
|
||||
dispatch(setControlStyle(value));
|
||||
}}
|
||||
type="single"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiTitle>
|
||||
<h2>Basic Redux Example</h2>
|
||||
<h2>Redux example</h2>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
<p>
|
||||
This example uses the redux context from the control group container in order to
|
||||
dynamically change the style of the control group.
|
||||
</p>
|
||||
<p>Use the redux context from the control group to set layout style.</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiPanel hasBorder={true}>
|
||||
|
@ -105,17 +83,22 @@ export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => {
|
|||
getInitialInput={async (initialInput, builder) => {
|
||||
await builder.addDataControlFromField(initialInput, {
|
||||
dataViewId,
|
||||
fieldName: 'customer_first_name.keyword',
|
||||
width: 'small',
|
||||
title: 'Destintion country',
|
||||
fieldName: 'geo.dest',
|
||||
width: 'medium',
|
||||
grow: false,
|
||||
});
|
||||
await builder.addDataControlFromField(initialInput, {
|
||||
dataViewId,
|
||||
fieldName: 'customer_last_name.keyword',
|
||||
fieldName: 'bytes',
|
||||
width: 'medium',
|
||||
grow: false,
|
||||
title: 'Last Name',
|
||||
grow: true,
|
||||
title: 'Bytes',
|
||||
});
|
||||
return initialInput;
|
||||
return {
|
||||
...initialInput,
|
||||
viewMode: ViewMode.VIEW,
|
||||
};
|
||||
}}
|
||||
/>
|
||||
</EuiPanel>
|
||||
|
|
9
examples/controls_example/public/constants.ts
Normal file
9
examples/controls_example/public/constants.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const PLUGIN_ID = 'controlsExamples';
|
|
@ -13,9 +13,11 @@ import {
|
|||
CoreStart,
|
||||
Plugin,
|
||||
} from '@kbn/core/public';
|
||||
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import type { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';
|
||||
import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public';
|
||||
import img from './control_group_image.png';
|
||||
import { PLUGIN_ID } from './constants';
|
||||
|
||||
interface SetupDeps {
|
||||
developerExamples: DeveloperExamplesSetup;
|
||||
|
@ -23,6 +25,7 @@ interface SetupDeps {
|
|||
|
||||
export interface ControlsExampleStartDeps {
|
||||
data: DataPublicPluginStart;
|
||||
navigation: NavigationPublicPluginStart;
|
||||
}
|
||||
|
||||
export class ControlsExamplePlugin
|
||||
|
@ -30,7 +33,7 @@ export class ControlsExamplePlugin
|
|||
{
|
||||
public setup(core: CoreSetup<ControlsExampleStartDeps>, { developerExamples }: SetupDeps) {
|
||||
core.application.register({
|
||||
id: 'controlsExamples',
|
||||
id: PLUGIN_ID,
|
||||
title: 'Controls examples',
|
||||
navLinkStatus: AppNavLinkStatus.hidden,
|
||||
async mount(params: AppMountParameters) {
|
||||
|
|
168
examples/controls_example/public/search_example.tsx
Normal file
168
examples/controls_example/public/search_example.tsx
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import uuid from 'uuid/v4';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public';
|
||||
import type { Filter, Query, TimeRange } from '@kbn/es-query';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import {
|
||||
EuiCallOut,
|
||||
EuiLoadingSpinner,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { LazyControlGroupRenderer, ControlGroupContainer } from '@kbn/controls-plugin/public';
|
||||
import { withSuspense } from '@kbn/presentation-util-plugin/public';
|
||||
import { PLUGIN_ID } from './constants';
|
||||
|
||||
const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer);
|
||||
|
||||
interface Props {
|
||||
data: DataPublicPluginStart;
|
||||
dataView: DataView;
|
||||
navigation: NavigationPublicPluginStart;
|
||||
}
|
||||
|
||||
export const SearchExample = ({ data, dataView, navigation }: Props) => {
|
||||
const [controlFilters, setControlFilters] = useState<Filter[]>([]);
|
||||
const [controlGroup, setControlGroup] = useState<ControlGroupContainer>();
|
||||
const [hits, setHits] = useState(0);
|
||||
const [filters, setFilters] = useState<Filter[]>([]);
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const [query, setQuery] = useState<Query | undefined>({
|
||||
language: 'kuery',
|
||||
query: '',
|
||||
});
|
||||
const [timeRange, setTimeRange] = useState<TimeRange>({ from: 'now-7d', to: 'now' });
|
||||
|
||||
useEffect(() => {
|
||||
if (!controlGroup) {
|
||||
return;
|
||||
}
|
||||
const subscription = controlGroup.onFiltersPublished$.subscribe((newFilters) => {
|
||||
setControlFilters([...newFilters]);
|
||||
});
|
||||
return () => {
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, [controlGroup]);
|
||||
|
||||
useEffect(() => {
|
||||
const abortController = new AbortController();
|
||||
const search = async () => {
|
||||
setIsSearching(true);
|
||||
const searchSource = await data.search.searchSource.create();
|
||||
searchSource.setField('index', dataView);
|
||||
searchSource.setField('size', 0);
|
||||
searchSource.setField('filter', [
|
||||
...filters,
|
||||
...controlFilters,
|
||||
data.query.timefilter.timefilter.createFilter(dataView, timeRange),
|
||||
] as Filter[]);
|
||||
searchSource.setField('query', query);
|
||||
const { rawResponse: resp } = await lastValueFrom(
|
||||
searchSource.fetch$({
|
||||
abortSignal: abortController.signal,
|
||||
sessionId: uuid(),
|
||||
legacyHitsTotal: false,
|
||||
})
|
||||
);
|
||||
const total = resp.hits?.total as undefined | { relation: string; value: number };
|
||||
if (total !== undefined) {
|
||||
setHits(total.value);
|
||||
}
|
||||
setIsSearching(false);
|
||||
};
|
||||
|
||||
search().catch((error) => {
|
||||
setIsSearching(false);
|
||||
if (error.name === 'AbortError') {
|
||||
// ignore abort errors
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
abortController.abort();
|
||||
};
|
||||
}, [controlFilters, data, dataView, filters, query, timeRange]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiTitle>
|
||||
<h2>Search example</h2>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
<p>
|
||||
Pass filters, query, and time range to narrow controls. Combine search bar filters with
|
||||
controls filters to narrow results.
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiPanel hasBorder={true}>
|
||||
<navigation.ui.TopNavMenu
|
||||
appName={PLUGIN_ID}
|
||||
dateRangeFrom={timeRange.from}
|
||||
dateRangeTo={timeRange.to}
|
||||
filters={filters}
|
||||
indexPatterns={[dataView]}
|
||||
onFiltersUpdated={(newFilters) => {
|
||||
// filterManager.setFilters populates filter.meta so filter pill has pretty title
|
||||
data.query.filterManager.setFilters(newFilters);
|
||||
setFilters(newFilters);
|
||||
}}
|
||||
onQuerySubmit={({ dateRange, query: newQuery }) => {
|
||||
setQuery(newQuery);
|
||||
setTimeRange(dateRange);
|
||||
}}
|
||||
query={query}
|
||||
showSearchBar={true}
|
||||
/>
|
||||
<ControlGroupRenderer
|
||||
filters={filters}
|
||||
getInitialInput={async (initialInput, builder) => {
|
||||
await builder.addDataControlFromField(initialInput, {
|
||||
dataViewId: dataView.id!,
|
||||
title: 'Destintion country',
|
||||
fieldName: 'geo.dest',
|
||||
width: 'medium',
|
||||
grow: false,
|
||||
});
|
||||
await builder.addDataControlFromField(initialInput, {
|
||||
dataViewId: dataView.id!,
|
||||
fieldName: 'bytes',
|
||||
width: 'medium',
|
||||
grow: true,
|
||||
title: 'Bytes',
|
||||
});
|
||||
return {
|
||||
...initialInput,
|
||||
viewMode: ViewMode.VIEW,
|
||||
};
|
||||
}}
|
||||
onLoadComplete={async (newControlGroup) => {
|
||||
setControlGroup(newControlGroup);
|
||||
}}
|
||||
query={query}
|
||||
timeRange={timeRange}
|
||||
/>
|
||||
<EuiCallOut title="Search results">
|
||||
{isSearching ? <EuiLoadingSpinner size="l" /> : <p>Hits: {hits}</p>}
|
||||
</EuiCallOut>
|
||||
</EuiPanel>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -17,6 +17,7 @@
|
|||
{ "path": "../developer_examples/tsconfig.json" },
|
||||
{ "path": "../../src/plugins/data/tsconfig.json" },
|
||||
{ "path": "../../src/plugins/controls/tsconfig.json" },
|
||||
{ "path": "../../src/plugins/navigation/tsconfig.json" },
|
||||
{ "path": "../../src/plugins/presentation_util/tsconfig.json" }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -7,11 +7,14 @@
|
|||
*/
|
||||
|
||||
import uuid from 'uuid';
|
||||
import { isEqual } from 'lodash';
|
||||
import useLifecycles from 'react-use/lib/useLifecycles';
|
||||
import React, { useMemo, useRef, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { IEmbeddable } from '@kbn/embeddable-plugin/public';
|
||||
import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public';
|
||||
import type { Filter, TimeRange, Query } from '@kbn/es-query';
|
||||
import { compareFilters } from '@kbn/es-query';
|
||||
|
||||
import { pluginServices } from '../services';
|
||||
import { getDefaultControlGroupInput } from '../../common';
|
||||
|
@ -26,16 +29,22 @@ import { controlGroupReducers } from './state/control_group_reducers';
|
|||
import { controlGroupInputBuilder } from './control_group_input_builder';
|
||||
|
||||
export interface ControlGroupRendererProps {
|
||||
onLoadComplete?: (controlGroup: ControlGroupContainer) => void;
|
||||
filters?: Filter[];
|
||||
getInitialInput: (
|
||||
initialInput: Partial<ControlGroupInput>,
|
||||
builder: typeof controlGroupInputBuilder
|
||||
) => Promise<Partial<ControlGroupInput>>;
|
||||
onLoadComplete?: (controlGroup: ControlGroupContainer) => void;
|
||||
timeRange?: TimeRange;
|
||||
query?: Query;
|
||||
}
|
||||
|
||||
export const ControlGroupRenderer = ({
|
||||
onLoadComplete,
|
||||
getInitialInput,
|
||||
filters,
|
||||
timeRange,
|
||||
query,
|
||||
}: ControlGroupRendererProps) => {
|
||||
const controlGroupRef = useRef(null);
|
||||
const [controlGroup, setControlGroup] = useState<ControlGroupContainer>();
|
||||
|
@ -74,6 +83,24 @@ export const ControlGroupRenderer = ({
|
|||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!controlGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
(timeRange && !isEqual(controlGroup.getInput().timeRange, timeRange)) ||
|
||||
!compareFilters(controlGroup.getInput().filters ?? [], filters ?? []) ||
|
||||
!isEqual(controlGroup.getInput().query, query)
|
||||
) {
|
||||
controlGroup.updateInput({
|
||||
timeRange,
|
||||
query,
|
||||
filters,
|
||||
});
|
||||
}
|
||||
}, [query, filters, controlGroup, timeRange]);
|
||||
|
||||
return <div ref={controlGroupRef} />;
|
||||
};
|
||||
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { ControlGroupContainer, CONTROL_GROUP_TYPE } from '@kbn/controls-plugin/public';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import { Filter, TimeRange, compareFilters } from '@kbn/es-query';
|
||||
import { isEqual } from 'lodash';
|
||||
import { Filter, TimeRange } from '@kbn/es-query';
|
||||
import { LazyControlsRenderer } from './lazy_controls_renderer';
|
||||
import { useControlPanels } from '../hooks/use_control_panels_url_state';
|
||||
|
||||
|
@ -44,21 +43,26 @@ export const ControlsContent: React.FC<Props> = ({
|
|||
if (!controlGroup) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!isEqual(controlGroup.getInput().timeRange, timeRange) ||
|
||||
!compareFilters(controlGroup.getInput().filters ?? [], filters) ||
|
||||
!isEqual(controlGroup.getInput().query, query)
|
||||
) {
|
||||
controlGroup.updateInput({
|
||||
timeRange,
|
||||
query,
|
||||
filters,
|
||||
const filtersSubscription = controlGroup.onFiltersPublished$.subscribe((newFilters) => {
|
||||
setPanelFilters([...newFilters]);
|
||||
});
|
||||
const inputSubscription = controlGroup
|
||||
.getInput$()
|
||||
.subscribe(({ panels, filters: currentFilters }) => {
|
||||
setControlPanels(panels);
|
||||
if (currentFilters?.length === 0) {
|
||||
setPanelFilters([]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [query, filters, controlGroup, timeRange]);
|
||||
return () => {
|
||||
filtersSubscription.unsubscribe();
|
||||
inputSubscription.unsubscribe();
|
||||
};
|
||||
}, [controlGroup, setControlPanels, setPanelFilters]);
|
||||
|
||||
return (
|
||||
<LazyControlsRenderer
|
||||
filters={filters}
|
||||
getInitialInput={async () => ({
|
||||
id: dataViewId,
|
||||
type: CONTROL_GROUP_TYPE,
|
||||
|
@ -74,16 +78,9 @@ export const ControlsContent: React.FC<Props> = ({
|
|||
})}
|
||||
onLoadComplete={(newControlGroup) => {
|
||||
setControlGroup(newControlGroup);
|
||||
newControlGroup.onFiltersPublished$.subscribe((newFilters) => {
|
||||
setPanelFilters([...newFilters]);
|
||||
});
|
||||
newControlGroup.getInput$().subscribe(({ panels, filters: currentFilters }) => {
|
||||
setControlPanels(panels);
|
||||
if (currentFilters?.length === 0) {
|
||||
setPanelFilters([]);
|
||||
}
|
||||
});
|
||||
}}
|
||||
query={query}
|
||||
timeRange={timeRange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -31,7 +31,6 @@ export interface Props {
|
|||
|
||||
export class Timeslider extends Component<Props, {}> {
|
||||
private _isMounted: boolean = false;
|
||||
private _controlGroup?: ControlGroupContainer | undefined;
|
||||
private readonly _subscriptions = new Subscription();
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -39,17 +38,6 @@ export class Timeslider extends Component<Props, {}> {
|
|||
this._subscriptions.unsubscribe();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (
|
||||
this._controlGroup &&
|
||||
!_.isEqual(this._controlGroup.getInput().timeRange, this.props.timeRange)
|
||||
) {
|
||||
this._controlGroup.updateInput({
|
||||
timeRange: this.props.timeRange,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
}
|
||||
|
@ -71,9 +59,8 @@ export class Timeslider extends Component<Props, {}> {
|
|||
return;
|
||||
}
|
||||
|
||||
this._controlGroup = controlGroup;
|
||||
this._subscriptions.add(
|
||||
this._controlGroup
|
||||
controlGroup
|
||||
.getOutput$()
|
||||
.pipe(
|
||||
distinctUntilChanged(({ timeslice: timesliceA }, { timeslice: timesliceB }) =>
|
||||
|
@ -84,7 +71,7 @@ export class Timeslider extends Component<Props, {}> {
|
|||
// use waitForTimesliceToLoad$ observable to wait until next frame loaded
|
||||
// .pipe(first()) waits until the first value is emitted from an observable and then automatically unsubscribes
|
||||
this.props.waitForTimesliceToLoad$.pipe(first()).subscribe(() => {
|
||||
this._controlGroup!.anyControlOutputConsumerLoading$.next(false);
|
||||
controlGroup.anyControlOutputConsumerLoading$.next(false);
|
||||
});
|
||||
|
||||
this.props.setTimeslice(
|
||||
|
@ -105,6 +92,7 @@ export class Timeslider extends Component<Props, {}> {
|
|||
<ControlGroupRenderer
|
||||
onLoadComplete={this._onLoadComplete}
|
||||
getInitialInput={this._getInitialInput}
|
||||
timeRange={this.props.timeRange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue