Skip to content

Commit

Permalink
[Discover] Provide direct link from sample data UI to Discover (#130108)
Browse files Browse the repository at this point in the history
* [Discover] Allow to view sample data in Discover

* [Discover] Update deps format

* [Discover] Define order of items in the context menu

* [Discover] Update for tests

* [Discover] Add upgrade tests

* [Discover] Add a test for ordering appLinks

* [Discover] Use existing helpers

* [Discover] Add 7 days time range to Discover link

* [Discover] Rename the helper

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
jughosta and kibanamachine authored May 5, 2022
1 parent 0650bd3 commit 58bc0f7
Show file tree
Hide file tree
Showing 17 changed files with 298 additions and 41 deletions.
1 change: 1 addition & 0 deletions src/plugins/discover/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Side Public License, v 1.
*/

export const APP_ICON = 'discoverApp';
export const DEFAULT_COLUMNS_SETTING = 'defaultColumns';
export const SAMPLE_SIZE_SETTING = 'discover:sampleSize';
export const SORT_DEFAULT_ORDER_SETTING = 'discover:sort:defaultOrder';
Expand Down
9 changes: 9 additions & 0 deletions src/plugins/discover/common/services/saved_searches/index.ts
Original file line number Diff line number Diff line change
@@ -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 { getSavedSearchUrl, getSavedSearchFullPathUrl } from './saved_searches_url';
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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 { getSavedSearchUrl, getSavedSearchFullPathUrl } from './saved_searches_url';

describe('saved_searches_url', () => {
describe('getSavedSearchUrl', () => {
test('should return valid saved search url', () => {
expect(getSavedSearchUrl()).toBe('#/');
expect(getSavedSearchUrl('id')).toBe('#/view/id');
});
});

describe('getSavedSearchFullPathUrl', () => {
test('should return valid full path url', () => {
expect(getSavedSearchFullPathUrl()).toBe('/app/discover#/');
expect(getSavedSearchFullPathUrl('id')).toBe('/app/discover#/view/id');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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 getSavedSearchUrl = (id?: string) => (id ? `#/view/${encodeURIComponent(id)}` : '#/');

export const getSavedSearchFullPathUrl = (id?: string) => `/app/discover${getSavedSearchUrl(id)}`;
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
*/

import {
getSavedSearchUrl,
getSavedSearchFullPathUrl,
fromSavedSearchAttributes,
toSavedSearchAttributes,
throwErrorOnSavedSearchUrlConflict,
Expand All @@ -19,20 +17,6 @@ import { createSearchSourceMock } from '@kbn/data-plugin/public/mocks';
import type { SavedSearchAttributes, SavedSearch } from './types';

describe('saved_searches_utils', () => {
describe('getSavedSearchUrl', () => {
test('should return valid saved search url', () => {
expect(getSavedSearchUrl()).toBe('#/');
expect(getSavedSearchUrl('id')).toBe('#/view/id');
});
});

describe('getSavedSearchFullPathUrl', () => {
test('should return valid full path url', () => {
expect(getSavedSearchFullPathUrl()).toBe('/app/discover#/');
expect(getSavedSearchFullPathUrl('id')).toBe('/app/discover#/view/id');
});
});

describe('fromSavedSearchAttributes', () => {
test('should convert attributes into SavedSearch', () => {
const attributes: SavedSearchAttributes = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
import { i18n } from '@kbn/i18n';
import type { SavedSearchAttributes, SavedSearch } from './types';

export const getSavedSearchUrl = (id?: string) => (id ? `#/view/${encodeURIComponent(id)}` : '#/');

export const getSavedSearchFullPathUrl = (id?: string) => `/app/discover${getSavedSearchUrl(id)}`;
export {
getSavedSearchUrl,
getSavedSearchFullPathUrl,
} from '../../../common/services/saved_searches';

export const getSavedSearchUrlConflictMessage = async (savedSearch: SavedSearch) =>
i18n.translate('discover.savedSearchEmbeddable.legacyURLConflict.errorMessage', {
Expand Down
7 changes: 7 additions & 0 deletions src/plugins/discover/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@

import { CoreSetup, CoreStart, Plugin } from '@kbn/core/server';
import type { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server';
import type { HomeServerPluginSetup } from '@kbn/home-plugin/server';
import { getUiSettings } from './ui_settings';
import { capabilitiesProvider } from './capabilities_provider';
import { getSavedSearchObjectType } from './saved_objects';
import { registerSampleData } from './sample_data';

export class DiscoverServerPlugin implements Plugin<object, object> {
public setup(
core: CoreSetup,
plugins: {
data: DataPluginSetup;
home?: HomeServerPluginSetup;
}
) {
const getSearchSourceMigrations = plugins.data.search.searchSource.getAllMigrations.bind(
Expand All @@ -26,6 +29,10 @@ export class DiscoverServerPlugin implements Plugin<object, object> {
core.uiSettings.register(getUiSettings(core.docLinks));
core.savedObjects.registerType(getSavedSearchObjectType(getSearchSourceMigrations));

if (plugins.home) {
registerSampleData(plugins.home.sampleData);
}

return {};
}

Expand Down
9 changes: 9 additions & 0 deletions src/plugins/discover/server/sample_data/index.ts
Original file line number Diff line number Diff line change
@@ -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 { registerSampleData } from './register_sample_data';
44 changes: 44 additions & 0 deletions src/plugins/discover/server/sample_data/register_sample_data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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 { i18n } from '@kbn/i18n';
import type { SampleDataRegistrySetup } from '@kbn/home-plugin/server';
import { APP_ICON } from '../../common';
import { getSavedSearchFullPathUrl } from '../../common/services/saved_searches';

function getDiscoverPathForSampleDataset(objId: string) {
// TODO: remove the time range from the URL query when saved search objects start supporting time range configuration
// https://github.com/elastic/kibana/issues/9761
return `${getSavedSearchFullPathUrl(objId)}?_g=(time:(from:now-7d,to:now))`;
}

export function registerSampleData(sampleDataRegistry: SampleDataRegistrySetup) {
const linkLabel = i18n.translate('discover.sampleData.viewLinkLabel', {
defaultMessage: 'Discover',
});
const { addAppLinksToSampleDataset, getSampleDatasets } = sampleDataRegistry;
const sampleDatasets = getSampleDatasets();

sampleDatasets.forEach((sampleDataset) => {
const sampleSavedSearchObject = sampleDataset.savedObjects.find(
(object) => object.type === 'search'
);

if (sampleSavedSearchObject) {
addAppLinksToSampleDataset(sampleDataset.id, [
{
sampleObject: sampleSavedSearchObject,
getPath: getDiscoverPathForSampleDataset,
label: linkLabel,
icon: APP_ICON,
order: -1,
},
]);
}
});
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ SampleDataSetCard.propTypes = {
path: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
icon: PropTypes.string.isRequired,
order: PropTypes.number,
})
).isRequired,
status: PropTypes.oneOf([INSTALLED_STATUS, UNINSTALLED_STATUS, 'unknown']).isRequired,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import React from 'react';
import PropTypes from 'prop-types';
import { sortBy } from 'lodash';
import { EuiButton, EuiContextMenu, EuiIcon, EuiPopover } from '@elastic/eui';

import { i18n } from '@kbn/i18n';
Expand Down Expand Up @@ -47,7 +48,6 @@ export class SampleDataViewDataButton extends React.Component {
}
);
const dashboardPath = `/app/dashboards#/view/${this.props.overviewDashboard}`;
const prefixedDashboardPath = this.addBasePath(dashboardPath);

if (this.props.appLinks.length === 0) {
return (
Expand All @@ -61,12 +61,24 @@ export class SampleDataViewDataButton extends React.Component {
);
}

const additionalItems = this.props.appLinks.map(({ path, label, icon }) => {
const dashboardAppLink = {
path: dashboardPath,
label: i18n.translate('home.sampleDataSetCard.dashboardLinkLabel', {
defaultMessage: 'Dashboard',
}),
icon: 'dashboardApp',
order: 0,
'data-test-subj': `viewSampleDataSet${this.props.id}-dashboard`,
};

const sortedItems = sortBy([dashboardAppLink, ...this.props.appLinks], 'order');
const items = sortedItems.map(({ path, label, icon, ...rest }) => {
return {
name: label,
icon: <EuiIcon type={icon} size="m" />,
href: this.addBasePath(path),
onClick: createAppNavigationHandler(path),
...(rest['data-test-subj'] ? { 'data-test-subj': rest['data-test-subj'] } : {}),
};
});

Expand All @@ -75,18 +87,7 @@ export class SampleDataViewDataButton extends React.Component {
const panels = [
{
id: 0,
items: [
{
name: i18n.translate('home.sampleDataSetCard.dashboardLinkLabel', {
defaultMessage: 'Dashboard',
}),
icon: <EuiIcon type="dashboardApp" size="m" />,
href: prefixedDashboardPath,
onClick: createAppNavigationHandler(dashboardPath),
'data-test-subj': `viewSampleDataSet${this.props.id}-dashboard`,
},
...additionalItems,
],
items,
},
];
const popoverButton = (
Expand Down Expand Up @@ -124,6 +125,7 @@ SampleDataViewDataButton.propTypes = {
path: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
icon: PropTypes.string.isRequired,
order: PropTypes.number,
})
).isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,41 @@ test('should render popover when appLinks is not empty', () => {
);
expect(component).toMatchSnapshot(); // eslint-disable-line
});

test('should render popover with ordered appLinks', () => {
const appLinks = [
{
path: 'app/myAppPath',
label: 'myAppLabel[-1]',
icon: 'logoKibana',
order: -1, // to position it above Dashboard link
},
{
path: 'app/myAppPath',
label: 'myAppLabel',
icon: 'logoKibana',
},
{
path: 'app/myAppPath',
label: 'myAppLabel[5]',
icon: 'logoKibana',
order: 5,
},
{
path: 'app/myAppPath',
label: 'myAppLabel[3]',
icon: 'logoKibana',
order: 3,
},
];

const component = shallow(
<SampleDataViewDataButton
id="ecommerce"
name="Sample eCommerce orders"
overviewDashboard="722b74f0-b882-11e8-a6d9-e546fe2bba5f"
appLinks={appLinks}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
});
Loading

0 comments on commit 58bc0f7

Please sign in to comment.