Skip to content

Commit

Permalink
[Text based] Enables save Lens chart to dashboard from Discover (#159190
Browse files Browse the repository at this point in the history
)

## Summary

Adds a save to dashboard functionality in the Lens charts created in
Discover by text based languages.

<img width="1738" alt="image"
src="https://github.com/elastic/kibana/assets/17003240/c4b5f459-1124-4800-954f-298332601eaf">


<img width="832" alt="image"
src="https://github.com/elastic/kibana/assets/17003240/ae742d0a-5911-4622-8387-60db87daffcc">

We allow only saving by value panels and not by reference because we are
going to remove this functionality in the next minor (create Lens text
based languages SOs).

### Checklist

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
  • Loading branch information
stratoula authored Jun 9, 2023
1 parent af3f13e commit 5c7753f
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 11 deletions.
5 changes: 5 additions & 0 deletions src/plugins/unified_histogram/public/__mocks__/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,9 @@ export const unifiedHistogramServicesMock = {
clear: jest.fn(),
},
expressions: expressionsPluginMock.createStartContract(),
capabilities: {
dashboard: {
showWriteControls: true,
},
},
} as unknown as UnifiedHistogramServices;
35 changes: 34 additions & 1 deletion src/plugins/unified_histogram/public/chart/chart.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import React, { ReactElement } from 'react';
import { act } from 'react-dom/test-utils';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import type { Capabilities } from '@kbn/core/public';
import type { DataView } from '@kbn/data-views-plugin/public';
import type { Suggestion } from '@kbn/lens-plugin/public';
import type { UnifiedHistogramFetchStatus } from '../types';
Expand Down Expand Up @@ -41,6 +42,7 @@ async function mountComponent({
currentSuggestion,
allSuggestions,
isPlainRecord,
hasDashboardPermissions,
}: {
noChart?: boolean;
noHits?: boolean;
Expand All @@ -51,11 +53,21 @@ async function mountComponent({
currentSuggestion?: Suggestion;
allSuggestions?: Suggestion[];
isPlainRecord?: boolean;
hasDashboardPermissions?: boolean;
} = {}) {
(searchSourceInstanceMock.fetch$ as jest.Mock).mockImplementation(
jest.fn().mockReturnValue(of({ rawResponse: { hits: { total: noHits ? 0 : 2 } } }))
);

const services = {
...unifiedHistogramServicesMock,
capabilities: {
dashboard: {
showWriteControls: hasDashboardPermissions ?? true,
},
} as unknown as Capabilities,
};

const props = {
dataView,
query: {
Expand All @@ -64,7 +76,7 @@ async function mountComponent({
},
filters: [],
timeRange: { from: '2020-05-14T11:05:13.590', to: '2020-05-14T11:20:13.590' },
services: unifiedHistogramServicesMock,
services,
hits: noHits
? undefined
: {
Expand Down Expand Up @@ -221,6 +233,27 @@ describe('Chart', () => {
expect(component.find(SuggestionSelector).exists()).toBeTruthy();
});

it('should render the save button when chart is visible and suggestions exist', async () => {
const component = await mountComponent({
currentSuggestion: currentSuggestionMock,
allSuggestions: allSuggestionsMock,
});
expect(
component.find('[data-test-subj="unifiedHistogramSaveVisualization"]').exists()
).toBeTruthy();
});

it('should not render the save button when the dashboard save by value permissions are false', async () => {
const component = await mountComponent({
currentSuggestion: currentSuggestionMock,
allSuggestions: allSuggestionsMock,
hasDashboardPermissions: false,
});
expect(
component.find('[data-test-subj="unifiedHistogramSaveVisualization"]').exists()
).toBeFalsy();
});

it('should not render the Lens SuggestionsSelector when chart is hidden', async () => {
const component = await mountComponent({
chartHidden: true,
Expand Down
35 changes: 34 additions & 1 deletion src/plugins/unified_histogram/public/chart/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import { ReactElement, useMemo } from 'react';
import { ReactElement, useMemo, useState } from 'react';
import React, { memo } from 'react';
import {
EuiButtonIcon,
Expand Down Expand Up @@ -111,6 +111,7 @@ export function Chart({
onFilter,
onBrushEnd,
}: ChartProps) {
const [isSaveModalVisible, setIsSaveModalVisible] = useState(false);
const {
showChartOptionsPopover,
chartRef,
Expand Down Expand Up @@ -221,6 +222,9 @@ export function Chart({
lensAttributes: lensAttributesContext.attributes,
isPlainRecord,
});
const LensSaveModalComponent = services.lens.SaveModalComponent;
const canSaveVisualization =
chartVisible && currentSuggestion && services.capabilities.dashboard?.showWriteControls;

return (
<EuiFlexGroup
Expand Down Expand Up @@ -271,6 +275,27 @@ export function Chart({
/>
</EuiFlexItem>
)}
{canSaveVisualization && (
<>
<EuiFlexItem grow={false} css={chartToolButtonCss}>
<EuiToolTip
content={i18n.translate('unifiedHistogram.saveVisualizationButton', {
defaultMessage: 'Save visualization',
})}
>
<EuiButtonIcon
size="xs"
iconType="save"
onClick={() => setIsSaveModalVisible(true)}
data-test-subj="unifiedHistogramSaveVisualization"
aria-label={i18n.translate('unifiedHistogram.saveVisualizationButton', {
defaultMessage: 'Save visualization',
})}
/>
</EuiToolTip>
</EuiFlexItem>
</>
)}
{onEditVisualization && (
<EuiFlexItem grow={false} css={chartToolButtonCss}>
<EuiToolTip
Expand Down Expand Up @@ -354,6 +379,14 @@ export function Chart({
{appendHistogram}
</EuiFlexItem>
)}
{canSaveVisualization && isSaveModalVisible && lensAttributesContext.attributes && (
<LensSaveModalComponent
initialInput={lensAttributesContext.attributes as unknown as LensEmbeddableInput}
onSave={() => {}}
onClose={() => setIsSaveModalVisible(false)}
isSaveable={false}
/>
)}
</EuiFlexGroup>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -739,4 +739,18 @@ describe('getLensAttributes', () => {
}
`);
});

it('should return suggestion title if no title is given', () => {
expect(
getLensAttributes({
title: undefined,
filters,
query,
dataView,
timeInterval,
breakdownField: undefined,
suggestion: currentSuggestionMock,
}).attributes.title
).toBe(currentSuggestionMock.title);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ export const getLensAttributes = ({
const attributes = {
title:
title ??
suggestion?.title ??
i18n.translate('unifiedHistogram.lensTitle', {
defaultMessage: 'Edit visualization',
}),
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/unified_histogram/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import type { Theme } from '@kbn/charts-plugin/public/plugin';
import type { IUiSettingsClient } from '@kbn/core/public';
import type { IUiSettingsClient, Capabilities } from '@kbn/core/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { LensPublicStart } from '@kbn/lens-plugin/public';
Expand Down Expand Up @@ -42,6 +42,7 @@ export interface UnifiedHistogramServices {
lens: LensPublicStart;
storage: Storage;
expressions: ExpressionsStart;
capabilities: Capabilities;
}

/**
Expand Down
16 changes: 16 additions & 0 deletions x-pack/test/functional/apps/discover/visualize_field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,5 +184,21 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
return dimensions.length === 2 && (await dimensions[1].getVisibleText()) === 'average';
});
});

it('should save correctly chart to dashboard', async () => {
await PageObjects.discover.selectTextBaseLang('SQL');
await PageObjects.header.waitUntilLoadingHasFinished();
await monacoEditor.setCodeEditorValue(
'SELECT extension, AVG("bytes") as average FROM "logstash*" GROUP BY extension'
);
await testSubjects.click('querySubmitButton');
await PageObjects.header.waitUntilLoadingHasFinished();
await testSubjects.click('TextBasedLangEditor-expand');
await testSubjects.click('unifiedHistogramSaveVisualization');
await PageObjects.header.waitUntilLoadingHasFinished();

await PageObjects.lens.saveModal('TextBasedChart', false, false, false, 'new');
await testSubjects.existOrFail('embeddablePanelHeading-TextBasedChart');
});
});
}
33 changes: 25 additions & 8 deletions x-pack/test/functional/page_objects/lens_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -709,21 +709,14 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
await testSubjects.missingOrFail(`lns-fieldOption-${field}`);
}
},

/**
* Save the current Lens visualization.
*/
async save(
async saveModal(
title: string,
saveAsNew?: boolean,
redirectToOrigin?: boolean,
saveToLibrary?: boolean,
addToDashboard?: 'new' | 'existing' | null,
dashboardId?: string
) {
await PageObjects.header.waitUntilLoadingHasFinished();
await testSubjects.click('lnsApp_saveButton');

await PageObjects.timeToVisualize.setSaveModalValues(title, {
saveAsNew,
redirectToOrigin,
Expand All @@ -741,6 +734,30 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
);
},

/**
* Save the current Lens visualization.
*/
async save(
title: string,
saveAsNew?: boolean,
redirectToOrigin?: boolean,
saveToLibrary?: boolean,
addToDashboard?: 'new' | 'existing' | null,
dashboardId?: string
) {
await PageObjects.header.waitUntilLoadingHasFinished();
await testSubjects.click('lnsApp_saveButton');

await this.saveModal(
title,
saveAsNew,
redirectToOrigin,
saveToLibrary,
addToDashboard,
dashboardId
);
},

async saveAndReturn() {
await testSubjects.click('lnsApp_saveAndReturnButton');
},
Expand Down

0 comments on commit 5c7753f

Please sign in to comment.