Skip to content

Commit

Permalink
[Lens] Provide formula helper to simplify integration of Lens instanc…
Browse files Browse the repository at this point in the history
…es (#122371)

* [Lens] Provide formula helper to simplify integration of Lens instances

Closes: #103055

* remove generateFormulaColumns from start contract

* upsertFormulaColumn

* add upsertFormulaColumn to start contract

* add integration with embedded_lens_examples

* upsert -> insertOrReplace

* add support of overriding operations

* add docs

* fix TS issues

* fix some comments

* fix PR comments

* fix PR comments

* fix CI

* Update x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula_helper.ts

Co-authored-by: Marco Liberati <dej611@users.noreply.github.com>

* Update x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula_helper.ts

Co-authored-by: Marco Liberati <dej611@users.noreply.github.com>

* remove useEffect

* move baseLayer part into getLensAttributes

* introduce stateHelperApi

* Map -> WeakMap

* remove [params.operations] from params

Co-authored-by: Marco Liberati <dej611@users.noreply.github.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
3 people committed Jan 17, 2022
1 parent 8c0fbdf commit 2c52ac2
Show file tree
Hide file tree
Showing 20 changed files with 728 additions and 396 deletions.
297 changes: 143 additions & 154 deletions x-pack/examples/embedded_lens_example/public/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,49 +17,51 @@ import {
EuiPageHeader,
EuiPageHeaderSection,
EuiTitle,
EuiCallOut,
} from '@elastic/eui';
import { IndexPattern } from 'src/plugins/data/public';
import { CoreStart } from 'kibana/public';
import { ViewMode } from '../../../../src/plugins/embeddable/public';
import {

import type { DataView } from 'src/plugins/data_views/public';
import type { CoreStart } from 'kibana/public';
import type { StartDependencies } from './plugin';
import type {
TypedLensByValueInput,
PersistedIndexPatternLayer,
XYState,
LensEmbeddableInput,
FormulaPublicApi,
DateHistogramIndexPatternColumn,
} from '../../../plugins/lens/public';
import { StartDependencies } from './plugin';

import { ViewMode } from '../../../../src/plugins/embeddable/public';

// Generate a Lens state based on some app-specific input parameters.
// `TypedLensByValueInput` can be used for type-safety - it uses the same interfaces as Lens-internal code.
function getLensAttributes(
defaultIndexPattern: IndexPattern,
color: string
color: string,
dataView: DataView,
formula: FormulaPublicApi
): TypedLensByValueInput['attributes'] {
const dataLayer: PersistedIndexPatternLayer = {
columnOrder: ['col1', 'col2'],
const baseLayer: PersistedIndexPatternLayer = {
columnOrder: ['col1'],
columns: {
col2: {
dataType: 'number',
isBucketed: false,
label: 'Count of records',
operationType: 'count',
scale: 'ratio',
sourceField: 'Records',
},
col1: {
dataType: 'date',
isBucketed: true,
label: '@timestamp',
operationType: 'date_histogram',
params: { interval: 'auto' },
scale: 'interval',
sourceField: defaultIndexPattern.timeFieldName!,
sourceField: dataView.timeFieldName!,
} as DateHistogramIndexPatternColumn,
},
};

const dataLayer = formula.insertOrReplaceFormulaColumn(
'col2',
{ formula: 'count()' },
baseLayer,
dataView
);

const xyConfig: XYState = {
axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true },
fittingFunction: 'None',
Expand All @@ -85,12 +87,12 @@ function getLensAttributes(
title: 'Prefilled from example app',
references: [
{
id: defaultIndexPattern.id!,
id: dataView.id!,
name: 'indexpattern-datasource-current-indexpattern',
type: 'index-pattern',
},
{
id: defaultIndexPattern.id!,
id: dataView.id!,
name: 'indexpattern-datasource-layer-layer1',
type: 'index-pattern',
},
Expand All @@ -99,7 +101,7 @@ function getLensAttributes(
datasourceStates: {
indexpattern: {
layers: {
layer1: dataLayer,
layer1: dataLayer!,
},
},
},
Expand All @@ -113,19 +115,22 @@ function getLensAttributes(
export const App = (props: {
core: CoreStart;
plugins: StartDependencies;
defaultIndexPattern: IndexPattern | null;
defaultDataView: DataView;
formula: FormulaPublicApi;
}) => {
const [color, setColor] = useState('green');
const [isLoading, setIsLoading] = useState(false);
const [isSaveModalVisible, setIsSaveModalVisible] = useState(false);
const LensComponent = props.plugins.lens.EmbeddableComponent;
const LensSaveModalComponent = props.plugins.lens.SaveModalComponent;

const [time, setTime] = useState({
from: 'now-5d',
to: 'now',
});

const LensComponent = props.plugins.lens.EmbeddableComponent;
const LensSaveModalComponent = props.plugins.lens.SaveModalComponent;

const attributes = getLensAttributes(color, props.defaultDataView, props.formula);

return (
<EuiPage>
<EuiPageBody style={{ maxWidth: 1200, margin: '0 auto' }}>
Expand All @@ -147,138 +152,122 @@ export const App = (props: {
the series which causes Lens to re-render. The Edit button will take the current
configuration and navigate to a prefilled editor.
</p>
{props.defaultIndexPattern && props.defaultIndexPattern.isTimeBased() ? (
<>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="lns-example-change-color"
isLoading={isLoading}
onClick={() => {
// eslint-disable-next-line no-bitwise
const newColor = '#' + ((Math.random() * 0xffffff) << 0).toString(16);
setColor(newColor);
}}
>
Change color
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
aria-label="Open lens in new tab"
isDisabled={!props.plugins.lens.canUseEditor()}
onClick={() => {
props.plugins.lens.navigateToPrefilledEditor(
{
id: '',
timeRange: time,
attributes: getLensAttributes(props.defaultIndexPattern!, color),
},
{
openInNewTab: true,
}
);
// eslint-disable-next-line no-bitwise
const newColor = '#' + ((Math.random() * 0xffffff) << 0).toString(16);
setColor(newColor);
}}
>
Edit in Lens (new tab)
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
aria-label="Open lens in same tab"
data-test-subj="lns-example-open-editor"
isDisabled={!props.plugins.lens.canUseEditor()}
onClick={() => {
props.plugins.lens.navigateToPrefilledEditor(
{
id: '',
timeRange: time,
attributes: getLensAttributes(props.defaultIndexPattern!, color),
},
{
openInNewTab: false,
}
);
}}
>
Edit in Lens (same tab)
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
aria-label="Save visualization into library or embed directly into any dashboard"
data-test-subj="lns-example-save"
isDisabled={!getLensAttributes(props.defaultIndexPattern, color)}
onClick={() => {
setIsSaveModalVisible(true);
}}
>
Save Visualization
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
aria-label="Change time range"
data-test-subj="lns-example-change-time-range"
isDisabled={!getLensAttributes(props.defaultIndexPattern, color)}
onClick={() => {
setTime({
from: '2015-09-18T06:31:44.000Z',
to: '2015-09-23T18:31:44.000Z',
});
}}
>
Change time range
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
<LensComponent
id=""
withActions
style={{ height: 500 }}
timeRange={time}
attributes={getLensAttributes(props.defaultIndexPattern, color)}
onLoad={(val) => {
setIsLoading(val);

<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="lns-example-change-color"
isLoading={isLoading}
onClick={() => {
// eslint-disable-next-line no-bitwise
const newColor = '#' + ((Math.random() * 0xffffff) << 0).toString(16);
setColor(newColor);
}}
onBrushEnd={({ range }) => {
setTime({
from: new Date(range[0]).toISOString(),
to: new Date(range[1]).toISOString(),
});
>
Change color
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
aria-label="Open lens in new tab"
isDisabled={!props.plugins.lens.canUseEditor()}
onClick={() => {
props.plugins.lens.navigateToPrefilledEditor(
{
id: '',
timeRange: time,
attributes,
},
{
openInNewTab: true,
}
);
// eslint-disable-next-line no-bitwise
const newColor = '#' + ((Math.random() * 0xffffff) << 0).toString(16);
setColor(newColor);
}}
>
Edit in Lens (new tab)
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
aria-label="Open lens in same tab"
data-test-subj="lns-example-open-editor"
isDisabled={!props.plugins.lens.canUseEditor()}
onClick={() => {
props.plugins.lens.navigateToPrefilledEditor(
{
id: '',
timeRange: time,
attributes,
},
{
openInNewTab: false,
}
);
}}
onFilter={(_data) => {
// call back event for on filter event
>
Edit in Lens (same tab)
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
aria-label="Save visualization into library or embed directly into any dashboard"
data-test-subj="lns-example-save"
isDisabled={!attributes}
onClick={() => {
setIsSaveModalVisible(true);
}}
onTableRowClick={(_data) => {
// call back event for on table row click event
>
Save Visualization
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
aria-label="Change time range"
data-test-subj="lns-example-change-time-range"
isDisabled={!attributes}
onClick={() => {
setTime({
from: '2015-09-18T06:31:44.000Z',
to: '2015-09-23T18:31:44.000Z',
});
}}
viewMode={ViewMode.VIEW}
/>
{isSaveModalVisible && (
<LensSaveModalComponent
initialInput={
getLensAttributes(
props.defaultIndexPattern,
color
) as unknown as LensEmbeddableInput
}
onSave={() => {}}
onClose={() => setIsSaveModalVisible(false)}
/>
)}
</>
) : (
<EuiCallOut
title="Please define a default index pattern to use this demo"
color="danger"
iconType="alert"
>
<p>This demo only works if your default index pattern is set and time based</p>
</EuiCallOut>
>
Change time range
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
<LensComponent
id=""
withActions
style={{ height: 500 }}
timeRange={time}
attributes={attributes}
onLoad={(val) => {
setIsLoading(val);
}}
onBrushEnd={({ range }) => {
setTime({
from: new Date(range[0]).toISOString(),
to: new Date(range[1]).toISOString(),
});
}}
onFilter={(_data) => {
// call back event for on filter event
}}
onTableRowClick={(_data) => {
// call back event for on table row click event
}}
viewMode={ViewMode.VIEW}
/>
{isSaveModalVisible && (
<LensSaveModalComponent
initialInput={attributes as unknown as LensEmbeddableInput}
onSave={() => {}}
onClose={() => setIsSaveModalVisible(false)}
/>
)}
</EuiPageContentBody>
</EuiPageContent>
Expand Down
Loading

0 comments on commit 2c52ac2

Please sign in to comment.