[UnifiedDocViewer] Move Discover doc viewer into plugin/package (#162847)

## Summary

Replaces https://github.com/elastic/kibana/pull/154012.

Moves the Discover doc viewer component into a new plugin/package,
`@kbn/unified-doc-viewer` and `@kbn/unified-doc-viewer-plugin`.

### 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)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [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/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

### Risk Matrix

Before closing this PR, invite QA, stakeholders, and other developers to
identify risks that should be tested prior to the change/feature
release.

When forming the risk matrix, consider some of the following examples
and how they may potentially impact the change:

| Risk | Probability | Severity | Mitigation/Notes |

|---------------------------|-------------|----------|-------------------------|
| Multiple Spaces—unexpected behavior in non-default Kibana Space.
| Low | High | Integration tests will verify that all features are still
supported in non-default Kibana Space and when user switches between
spaces. |
| Multiple nodes—Elasticsearch polling might have race conditions
when multiple Kibana nodes are polling for the same tasks. | High | Low
| Tasks are idempotent, so executing them multiple times will not result
in logical error, but will degrade performance. To test for this case we
add plenty of unit tests around this logic and document manual testing
procedure. |
| Code should gracefully handle cases when feature X or plugin Y are
disabled. | Medium | High | Unit tests will verify that any feature flag
or plugin combination still results in our service operational. |
| [See more potential risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) |

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Shahzad <shahzad31comp@gmail.com>
This commit is contained in:
Lukas Olson 2023-08-31 11:46:59 -07:00 committed by GitHub
parent 59231988c7
commit 09cd69d386
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
137 changed files with 1325 additions and 760 deletions

3
.github/CODEOWNERS vendored
View file

@ -750,6 +750,9 @@ test/plugin_functional/plugins/ui_settings_plugin @elastic/kibana-core
packages/kbn-ui-shared-deps-npm @elastic/kibana-operations
packages/kbn-ui-shared-deps-src @elastic/kibana-operations
packages/kbn-ui-theme @elastic/kibana-operations
packages/kbn-unified-doc-viewer @elastic/kibana-data-discovery
examples/unified_doc_viewer @elastic/kibana-core
src/plugins/unified_doc_viewer @elastic/kibana-data-discovery
packages/kbn-unified-field-list @elastic/kibana-data-discovery
examples/unified_field_list_examples @elastic/kibana-data-discovery
src/plugins/unified_histogram @elastic/kibana-data-discovery

View file

@ -122,6 +122,7 @@
"visTypeXy": "src/plugins/vis_types/xy",
"visualizations": "src/plugins/visualizations",
"visualizationUiComponents": "packages/kbn-visualization-ui-components",
"unifiedDocViewer": ["src/plugins/unified_doc_viewer", "packages/kbn-unified-doc-viewer"],
"unifiedSearch": "src/plugins/unified_search",
"unifiedFieldList": "packages/kbn-unified-field-list",
"unifiedHistogram": "src/plugins/unified_histogram"

View file

@ -336,6 +336,10 @@ In general this plugin provides:
|Registers commercially licensed generic actions like per panel time range and contains some code that supports drilldown work.
|{kib-repo}blob/{branch}/src/plugins/unified_doc_viewer/README.md[unifiedDocViewer]
|This plugin contains services reliant on the plugin lifecycle for the unified doc viewer component (see @kbn/unified-doc-viewer).
|{kib-repo}blob/{branch}/src/plugins/unified_histogram/README.md[unifiedHistogram]
|Unified Histogram is a UX Building Block including a layout with a resizable histogram and a main display.
It manages its own state and data fetching, and can easily be dropped into pages with minimal setup.

View file

@ -0,0 +1,3 @@
## Unified Doc Viewer
An example plugin showing usage of the unified doc viewer plugin (plugins/unified_doc_viewer) and package (@kbn/unified-doc-viewer).

View file

@ -0,0 +1,16 @@
{
"type": "plugin",
"id": "@kbn/unified-doc-viewer-examples",
"owner": "@elastic/kibana-core",
"description": "Examples showing usage of the unified doc viewer.",
"plugin": {
"id": "unifiedDocViewerExamples",
"server": false,
"browser": true,
"requiredPlugins": [
"data",
"developerExamples",
"unifiedDocViewer"
]
}
}

View file

@ -0,0 +1,72 @@
/*
* 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 ReactDOM from 'react-dom';
import type { AppMountParameters, CoreStart } from '@kbn/core/public';
import { buildDataTableRecord } from '@kbn/discover-utils';
import type { DataTableRecord } from '@kbn/discover-utils/types';
import type { DataView } from '@kbn/data-views-plugin/common';
import { UnifiedDocViewer } from '@kbn/unified-doc-viewer-plugin/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { StartDeps } from './plugin';
export const renderApp = (
core: CoreStart,
{ data }: StartDeps,
{ element }: AppMountParameters
) => {
ReactDOM.render(<UnifiedDocViewerExamplesApp data={data} />, element);
return () => {
ReactDOM.unmountComponentAtNode(element);
};
};
function UnifiedDocViewerExamplesApp({ data }: { data: DataPublicPluginStart }) {
const [dataView, setDataView] = useState<DataView | null>();
const [hit, setHit] = useState<DataTableRecord | null>();
useEffect(() => {
data.dataViews.getDefault().then((defaultDataView) => setDataView(defaultDataView));
}, [data]);
useEffect(() => {
const setDefaultHit = async () => {
if (!dataView?.id) return;
const response = await data.search
.search({
params: {
index: dataView?.getIndexPattern(),
body: {
fields: ['*'],
_source: false,
},
},
})
.toPromise();
const docs = response?.rawResponse?.hits?.hits ?? [];
if (docs.length > 0) {
const record = buildDataTableRecord(docs[0], dataView);
setHit(record);
}
};
setDefaultHit();
}, [data, dataView]);
return (
<>
{dataView?.id && hit ? (
<UnifiedDocViewer hit={hit} dataView={dataView} />
) : (
'Loading... (make sure you have a default data view and at least one matching document)'
)}
</>
);
}

View file

@ -0,0 +1,12 @@
/*
* 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 { UnifiedDocViewerExamplesPlugin } from './plugin';
export function plugin() {
return new UnifiedDocViewerExamplesPlugin();
}

View file

@ -0,0 +1,49 @@
/*
* 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 { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
export interface SetupDeps {
developerExamples: DeveloperExamplesSetup;
}
export interface StartDeps {
data: DataPublicPluginStart;
}
export class UnifiedDocViewerExamplesPlugin implements Plugin<void, void, SetupDeps, StartDeps> {
public setup(core: CoreSetup<StartDeps>, deps: SetupDeps) {
// Register an application into the side navigation menu
core.application.register({
id: 'unifiedDocViewer',
title: 'Unified Doc Viewer Examples',
async mount(params: AppMountParameters) {
// Load application bundle
const { renderApp } = await import('./application');
// Get start services as specified in kibana.json
const [coreStart, depsStart] = await core.getStartServices();
// Render the application
return renderApp(coreStart, depsStart, params);
},
});
// This section is only needed to get this example plugin to show up in our Developer Examples.
deps.developerExamples.register({
appId: 'unifiedDocViewer',
title: 'Unified Doc Viewer Examples',
description: 'Examples showcasing the unified doc viewer.',
});
}
public start(core: CoreStart) {
return {};
}
public stop() {}
}

View file

@ -0,0 +1,24 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types"
},
"include": [
"index.ts",
"common/**/*.ts",
"public/**/*.ts",
"public/**/*.tsx",
"../../typings/**/*"
],
"exclude": [
"target/**/*",
],
"kbn_references": [
"@kbn/core",
"@kbn/data-plugin",
"@kbn/data-views-plugin",
"@kbn/developer-examples-plugin",
"@kbn/discover-utils",
"@kbn/unified-doc-viewer-plugin",
]
}

View file

@ -742,6 +742,9 @@
"@kbn/ui-shared-deps-npm": "link:packages/kbn-ui-shared-deps-npm",
"@kbn/ui-shared-deps-src": "link:packages/kbn-ui-shared-deps-src",
"@kbn/ui-theme": "link:packages/kbn-ui-theme",
"@kbn/unified-doc-viewer": "link:packages/kbn-unified-doc-viewer",
"@kbn/unified-doc-viewer-examples": "link:examples/unified_doc_viewer",
"@kbn/unified-doc-viewer-plugin": "link:src/plugins/unified_doc_viewer",
"@kbn/unified-field-list": "link:packages/kbn-unified-field-list",
"@kbn/unified-field-list-examples-plugin": "link:examples/unified_field_list_examples",
"@kbn/unified-histogram-plugin": "link:src/plugins/unified_histogram",

View file

@ -16,6 +16,8 @@ export {
ENABLE_SQL,
FIELDS_LIMIT_SETTING,
HIDE_ANNOUNCEMENTS,
KNOWN_FIELD_TYPE_LIST,
KNOWN_FIELD_TYPES,
MAX_DOC_FIELDS_DISPLAYED,
MODIFY_COLUMNS_ON_SWITCH,
ROW_HEIGHT_OPTION,
@ -34,8 +36,10 @@ export {
formatFieldValue,
formatHit,
getDocId,
getFieldTypeName,
getIgnoredReason,
getShouldShowFieldHandler,
isKnownFieldType,
isNestedFieldParent,
usePager,
} from './src';

View file

@ -8,7 +8,7 @@
import type { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
export type { IgnoredReason, ShouldShowFieldInTableHandler } from './utils';
export type { FieldTypeKnown, IgnoredReason, ShouldShowFieldInTableHandler } from './utils';
export interface EsHitRecord extends Omit<SearchHit, '_source'> {
_source?: Record<string, unknown>;

View file

@ -6,7 +6,12 @@
* Side Public License, v 1.
*/
import { FieldTypeKnown } from '../../types';
import type { DataViewField } from '@kbn/data-views-plugin/common';
export type FieldTypeKnown = Exclude<
DataViewField['timeSeriesMetric'] | DataViewField['type'],
undefined
>;
/**
* Field types for which name and description are defined

View file

@ -14,7 +14,7 @@ import { KNOWN_FIELD_TYPES } from './field_types';
* A user-friendly name of an unknown field type
*/
export const UNKNOWN_FIELD_TYPE_MESSAGE = i18n.translate(
'unifiedFieldList.fieldNameIcons.unknownFieldAriaLabel',
'discover.fieldNameIcons.unknownFieldAriaLabel',
{
defaultMessage: 'Unknown field',
}
@ -32,7 +32,7 @@ export function getFieldTypeName(type?: string) {
if (type === 'source') {
// Note that this type is currently not provided, type for _source is undefined
return i18n.translate('unifiedFieldList.fieldNameIcons.sourceFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.sourceFieldAriaLabel', {
defaultMessage: 'Source field',
});
}
@ -40,107 +40,107 @@ export function getFieldTypeName(type?: string) {
const knownType: KNOWN_FIELD_TYPES = type as KNOWN_FIELD_TYPES;
switch (knownType) {
case KNOWN_FIELD_TYPES.DOCUMENT:
return i18n.translate('unifiedFieldList.fieldNameIcons.recordAriaLabel', {
return i18n.translate('discover.fieldNameIcons.recordAriaLabel', {
defaultMessage: 'Records',
});
case KNOWN_FIELD_TYPES.BINARY:
return i18n.translate('unifiedFieldList.fieldNameIcons.binaryAriaLabel', {
return i18n.translate('discover.fieldNameIcons.binaryAriaLabel', {
defaultMessage: 'Binary',
});
case KNOWN_FIELD_TYPES.BOOLEAN:
return i18n.translate('unifiedFieldList.fieldNameIcons.booleanAriaLabel', {
return i18n.translate('discover.fieldNameIcons.booleanAriaLabel', {
defaultMessage: 'Boolean',
});
case KNOWN_FIELD_TYPES.CONFLICT:
return i18n.translate('unifiedFieldList.fieldNameIcons.conflictFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.conflictFieldAriaLabel', {
defaultMessage: 'Conflict',
});
case KNOWN_FIELD_TYPES.COUNTER:
return i18n.translate('unifiedFieldList.fieldNameIcons.counterFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.counterFieldAriaLabel', {
defaultMessage: 'Counter metric',
});
case KNOWN_FIELD_TYPES.DATE:
return i18n.translate('unifiedFieldList.fieldNameIcons.dateFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.dateFieldAriaLabel', {
defaultMessage: 'Date',
});
case KNOWN_FIELD_TYPES.DATE_RANGE:
return i18n.translate('unifiedFieldList.fieldNameIcons.dateRangeFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.dateRangeFieldAriaLabel', {
defaultMessage: 'Date range',
});
case KNOWN_FIELD_TYPES.DENSE_VECTOR:
return i18n.translate('unifiedFieldList.fieldNameIcons.denseVectorFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.denseVectorFieldAriaLabel', {
defaultMessage: 'Dense vector',
});
case KNOWN_FIELD_TYPES.GAUGE:
return i18n.translate('unifiedFieldList.fieldNameIcons.gaugeFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.gaugeFieldAriaLabel', {
defaultMessage: 'Gauge metric',
});
case KNOWN_FIELD_TYPES.GEO_POINT:
return i18n.translate('unifiedFieldList.fieldNameIcons.geoPointFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.geoPointFieldAriaLabel', {
defaultMessage: 'Geo point',
});
case KNOWN_FIELD_TYPES.GEO_SHAPE:
return i18n.translate('unifiedFieldList.fieldNameIcons.geoShapeFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.geoShapeFieldAriaLabel', {
defaultMessage: 'Geo shape',
});
case KNOWN_FIELD_TYPES.HISTOGRAM:
return i18n.translate('unifiedFieldList.fieldNameIcons.histogramFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.histogramFieldAriaLabel', {
defaultMessage: 'Histogram',
});
case KNOWN_FIELD_TYPES.IP:
return i18n.translate('unifiedFieldList.fieldNameIcons.ipAddressFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.ipAddressFieldAriaLabel', {
defaultMessage: 'IP address',
});
case KNOWN_FIELD_TYPES.IP_RANGE:
return i18n.translate('unifiedFieldList.fieldNameIcons.ipRangeFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.ipRangeFieldAriaLabel', {
defaultMessage: 'IP range',
});
case KNOWN_FIELD_TYPES.FLATTENED:
return i18n.translate('unifiedFieldList.fieldNameIcons.flattenedFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.flattenedFieldAriaLabel', {
defaultMessage: 'Flattened',
});
case KNOWN_FIELD_TYPES.MURMUR3:
return i18n.translate('unifiedFieldList.fieldNameIcons.murmur3FieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.murmur3FieldAriaLabel', {
defaultMessage: 'Murmur3',
});
case KNOWN_FIELD_TYPES.NUMBER:
return i18n.translate('unifiedFieldList.fieldNameIcons.numberFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.numberFieldAriaLabel', {
defaultMessage: 'Number',
});
case KNOWN_FIELD_TYPES.RANK_FEATURE:
return i18n.translate('unifiedFieldList.fieldNameIcons.rankFeatureFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.rankFeatureFieldAriaLabel', {
defaultMessage: 'Rank feature',
});
case KNOWN_FIELD_TYPES.RANK_FEATURES:
return i18n.translate('unifiedFieldList.fieldNameIcons.rankFeaturesFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.rankFeaturesFieldAriaLabel', {
defaultMessage: 'Rank features',
});
case KNOWN_FIELD_TYPES.POINT:
return i18n.translate('unifiedFieldList.fieldNameIcons.pointFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.pointFieldAriaLabel', {
defaultMessage: 'Point',
});
case KNOWN_FIELD_TYPES.SHAPE:
return i18n.translate('unifiedFieldList.fieldNameIcons.shapeFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.shapeFieldAriaLabel', {
defaultMessage: 'Shape',
});
case KNOWN_FIELD_TYPES.STRING:
return i18n.translate('unifiedFieldList.fieldNameIcons.stringFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.stringFieldAriaLabel', {
defaultMessage: 'String',
});
case KNOWN_FIELD_TYPES.TEXT:
return i18n.translate('unifiedFieldList.fieldNameIcons.textFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.textFieldAriaLabel', {
defaultMessage: 'Text',
});
case KNOWN_FIELD_TYPES.KEYWORD:
return i18n.translate('unifiedFieldList.fieldNameIcons.keywordFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.keywordFieldAriaLabel', {
defaultMessage: 'Keyword',
});
case KNOWN_FIELD_TYPES.NESTED:
return i18n.translate('unifiedFieldList.fieldNameIcons.nestedFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.nestedFieldAriaLabel', {
defaultMessage: 'Nested',
});
case KNOWN_FIELD_TYPES.VERSION:
return i18n.translate('unifiedFieldList.fieldNameIcons.versionFieldAriaLabel', {
return i18n.translate('discover.fieldNameIcons.versionFieldAriaLabel', {
defaultMessage: 'Version',
});
default:

View file

@ -7,9 +7,11 @@
*/
export * from './build_data_record';
export * from './field_types';
export * from './format_hit';
export * from './format_value';
export * from './get_doc_id';
export * from './get_field_type_name';
export * from './get_ignored_reason';
export * from './get_should_show_field_handler';
export * from './nested_fields';

View file

@ -9,6 +9,7 @@
export type {
DataTableRecord,
EsHitRecord,
FieldTypeKnown,
IgnoredReason,
ShouldShowFieldInTableHandler,
} from './src/types';

View file

@ -142,6 +142,7 @@ pageLoadAssetSize:
triggersActionsUi: 135613
uiActions: 35121
uiActionsEnhanced: 38494
unifiedDocViewer: 25099
unifiedHistogram: 19928
unifiedSearch: 71059
upgradeAssistant: 81241

View file

@ -0,0 +1,15 @@
# @kbn/unified-doc-viewer
This package contains components and services for the unified doc viewer component.
Discover (Classic view → Expanded document)
![image](https://github.com/elastic/kibana/assets/1178348/a0a360bf-2697-4427-a32e-c728f06f5a7e)
Discover (Document explorer → Toggle dialog with details)
![image](https://github.com/elastic/kibana/assets/1178348/c9c11587-c53f-4bcd-8d48-aaceb64981ea)
Discover (View single document)
![image](https://github.com/elastic/kibana/assets/1178348/4a8ea694-d4f5-4c9c-9259-1212f0d50a18)

View file

@ -6,10 +6,4 @@
* Side Public License, v 1.
*/
export enum ElasticRequestState {
Loading,
NotFound,
Found,
Error,
NotFoundDataView,
}
export { DocViewer, DocViewsRegistry, ElasticRequestState, FieldName } from './src';

View file

@ -0,0 +1,13 @@
/*
* 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.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../..',
roots: ['<rootDir>/packages/kbn-unified-doc-viewer'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/unified-doc-viewer",
"owner": "@elastic/kibana-data-discovery"
}

View file

@ -0,0 +1,7 @@
{
"name": "@kbn/unified-doc-viewer",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0",
"sideEffects": false
}

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render <DocViewer/> with 3 different tabs 1`] = `
exports[`<DocViewer /> Render <DocViewer/> with 3 different tabs 1`] = `
<div
className="kbnDocViewer"
data-test-subj="kbnDocViewer"

View file

@ -0,0 +1,60 @@
/*
* 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 from 'react';
import { mount, shallow } from 'enzyme';
import { DocViewer } from './doc_viewer';
import { findTestSubject } from '@elastic/eui/lib/test';
import type { DocViewRenderProps } from '../../types';
import { buildDataTableRecord } from '@kbn/discover-utils';
import { DocViewsRegistry } from '../..';
describe('<DocViewer />', () => {
test('Render <DocViewer/> with 3 different tabs', () => {
const registry = new DocViewsRegistry();
registry.addDocView({ order: 10, title: 'Render function', render: jest.fn() });
registry.addDocView({ order: 20, title: 'React component', component: () => <div>test</div> });
// @ts-expect-error This should be invalid and will throw an error when rendering
registry.addDocView({ order: 30, title: 'Invalid doc view' });
const renderProps = { hit: {} } as DocViewRenderProps;
const wrapper = shallow(
<DocViewer docViews={registry.getDocViewsSorted(renderProps.hit)} {...renderProps} />
);
expect(wrapper).toMatchSnapshot();
});
test('Render <DocViewer/> with 1 tab displaying error message', () => {
function SomeComponent() {
// this is just a placeholder
return null;
}
const registry = new DocViewsRegistry();
registry.addDocView({
order: 10,
title: 'React component',
component: SomeComponent,
});
const renderProps = {
hit: buildDataTableRecord({ _index: 't', _id: '1' }),
} as DocViewRenderProps;
const errorMsg = 'Catch me if you can!';
const wrapper = mount(
<DocViewer docViews={registry.getDocViewsSorted(renderProps.hit)} {...renderProps} />
);
const error = new Error(errorMsg);
wrapper.find(SomeComponent).simulateError(error);
const errorMsgComponent = findTestSubject(wrapper, 'docViewerError');
expect(errorMsgComponent.text()).toMatch(new RegExp(`${errorMsg}`));
});
});

View file

@ -8,9 +8,12 @@
import React from 'react';
import { EuiTabbedContent } from '@elastic/eui';
import { getDocViewsRegistry } from '../../../../kibana_services';
import { DocViewerTab } from './doc_viewer_tab';
import { DocView, DocViewRenderProps } from '../../doc_views_types';
import type { DocView, DocViewRenderProps } from '../../types';
export interface DocViewerProps extends DocViewRenderProps {
docViews: DocView[];
}
/**
* Rendering tabs with different views of 1 Elasticsearch hit in Discover.
@ -18,26 +21,23 @@ import { DocView, DocViewRenderProps } from '../../doc_views_types';
* A view can contain a React `component`, or any JS framework by using
* a `render` function.
*/
export function DocViewer(renderProps: DocViewRenderProps) {
const docViewsRegistry = getDocViewsRegistry();
const tabs = docViewsRegistry
.getDocViewsSorted(renderProps.hit)
.map(({ title, render, component }: DocView, idx: number) => {
return {
id: `kbn_doc_viewer_tab_${idx}`,
name: title,
content: (
<DocViewerTab
id={idx}
title={title}
component={component}
renderProps={renderProps}
render={render}
/>
),
['data-test-subj']: `docViewerTab-${idx}`,
};
});
export function DocViewer({ docViews, ...renderProps }: DocViewerProps) {
const tabs = docViews.map(({ title, render, component }: DocView, idx: number) => {
return {
id: `kbn_doc_viewer_tab_${idx}`,
name: title,
content: (
<DocViewerTab
id={idx}
title={title}
component={component}
renderProps={renderProps}
render={render}
/>
),
['data-test-subj']: `docViewerTab-${idx}`,
};
});
if (!tabs.length) {
// There there's a minimum of 2 tabs active in Discover.

View file

@ -0,0 +1,24 @@
/*
* 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 from 'react';
import { mount } from 'enzyme';
import { DocViewerError } from './doc_viewer_error';
test('DocViewerError should wrap error in boundary', () => {
const props = {
error: new Error('my error'),
};
expect(() => {
const wrapper = mount(<DocViewerError {...props} />);
const html = wrapper.html();
expect(html).toContain('euiErrorBoundary');
expect(html).toContain('my error');
}).not.toThrowError();
});

View file

@ -9,7 +9,7 @@
import React from 'react';
import { mount } from 'enzyme';
import { DocViewRenderTab } from './doc_viewer_render_tab';
import { DocViewRenderProps } from '../../doc_views_types';
import type { DocViewRenderProps } from '../../types';
test('Mounting and unmounting DocViewerRenderTab', () => {
const unmountFn = jest.fn();

View file

@ -7,7 +7,7 @@
*/
import React, { useRef, useEffect } from 'react';
import { DocViewRenderFn, DocViewRenderProps } from '../../doc_views_types';
import type { DocViewRenderFn, DocViewRenderProps } from '../../types';
interface Props {
render: DocViewRenderFn;

View file

@ -9,8 +9,8 @@
import React from 'react';
import { isEqual } from 'lodash';
import { DocViewRenderTab } from './doc_viewer_render_tab';
import { DocViewerError } from './doc_viewer_render_error';
import { DocViewRenderFn, DocViewRenderProps } from '../../doc_views_types';
import { DocViewerError } from './doc_viewer_error';
import type { DocViewRenderFn, DocViewRenderProps } from '../../types';
interface Props {
id: number;

View file

@ -12,8 +12,9 @@ import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiHighlight } from '@
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { FieldIcon, FieldIconProps } from '@kbn/react-field';
import { type DataViewField, getFieldSubtypeMulti } from '@kbn/data-views-plugin/public';
import { getFieldTypeName } from '@kbn/unified-field-list/src/utils/field_types/get_field_type_name';
import type { DataViewField } from '@kbn/data-views-plugin/public';
import { getDataViewFieldSubtypeMulti } from '@kbn/es-query';
import { getFieldTypeName } from '@kbn/discover-utils';
interface Props {
fieldName: string;
@ -36,7 +37,7 @@ export function FieldName({
const displayName =
fieldMapping && fieldMapping.displayName ? fieldMapping.displayName : fieldName;
const tooltip = displayName !== fieldName ? `${fieldName} (${displayName})` : fieldName;
const subTypeMulti = fieldMapping && getFieldSubtypeMulti(fieldMapping.spec);
const subTypeMulti = fieldMapping && getDataViewFieldSubtypeMulti(fieldMapping.spec);
const isMultiField = !!subTypeMulti?.multi;
return (
@ -62,7 +63,7 @@ export function FieldName({
position="top"
delay="long"
content={i18n.translate(
'discover.fieldChooser.discoverField.multiFieldTooltipContent',
'unifiedDocViewer.fieldChooser.discoverField.multiFieldTooltipContent',
{
defaultMessage: 'Multi-fields can have multiple values per field',
}
@ -75,7 +76,7 @@ export function FieldName({
data-test-subj={`tableDocViewRow-${fieldName}-multifieldBadge`}
>
<FormattedMessage
id="discover.fieldChooser.discoverField.multiField"
id="unifiedDocViewer.fieldChooser.discoverField.multiField"
defaultMessage="multi-field"
/>
</EuiBadge>

View file

@ -6,4 +6,4 @@
* Side Public License, v 1.
*/
export { DeferredSpinner } from './common/deferred_spinner';
export * from './field_name';

View file

@ -0,0 +1,10 @@
/*
* 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 * from './doc_viewer';
export * from './field_name';

View file

@ -0,0 +1,10 @@
/*
* 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 * from './components';
export * from './services';

View file

@ -7,7 +7,15 @@
*/
import type { DataTableRecord } from '@kbn/discover-utils/types';
import { DocView, DocViewInput, DocViewInputFn } from './doc_views_types';
import { DocView, DocViewInput, DocViewInputFn } from './types';
export enum ElasticRequestState {
Loading,
NotFound,
Found,
Error,
NotFoundDataView,
}
export class DocViewsRegistry {
private docViews: DocView[] = [];
@ -17,10 +25,10 @@ export class DocViewsRegistry {
*/
addDocView(docViewRaw: DocViewInput | DocViewInputFn) {
const docView = typeof docViewRaw === 'function' ? docViewRaw() : docViewRaw;
if (typeof docView.shouldShow !== 'function') {
docView.shouldShow = () => true;
}
this.docViews.push(docView as DocView);
this.docViews.push({
...docView,
shouldShow: docView.shouldShow ?? (() => true),
});
}
/**
* Returns a sorted array of doc_views for rendering tabs

View 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 { DocViewsRegistry, ElasticRequestState } from './doc_views_registry';

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { DataView, DataViewField } from '@kbn/data-views-plugin/public';
import type { DataView, DataViewField } from '@kbn/data-views-plugin/public';
import type { AggregateQuery, Query } from '@kbn/es-query';
import type { DataTableRecord, IgnoredReason } from '@kbn/discover-utils/types';

View file

@ -0,0 +1,15 @@
/*
* 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 type {
DocView,
DocViewFilterFn,
DocViewRenderFn,
DocViewRenderProps,
FieldRecordLegacy,
} from './services/types';

View file

@ -0,0 +1,27 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": [
"@kbn/discover-utils",
"@kbn/data-views-plugin",
"@kbn/es-query",
"@kbn/data-plugin",
"@kbn/i18n-react",
"@kbn/i18n",
"@kbn/react-field",
]
}

View file

@ -0,0 +1,10 @@
/*
* 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 type { ElasticRequestState } from '.';
export type { DocViewFilterFn, DocViewRenderProps, FieldRecordLegacy } from './src/types';

View file

@ -48,7 +48,6 @@ export type {
AddFieldFilterHandler,
FieldListGroups,
FieldsGroupDetails,
FieldTypeKnown,
FieldListItem,
GetCustomFieldType,
RenderFieldItemParams,
@ -86,13 +85,7 @@ export {
type QuerySubscriberParams,
} from './src/hooks/use_query_subscriber';
export {
getFieldTypeName,
getFieldTypeDescription,
KNOWN_FIELD_TYPES,
getFieldType,
getFieldIconType,
} from './src/utils/field_types';
export { getFieldTypeDescription, getFieldType, getFieldIconType } from './src/utils/field_types';
export {
UnifiedFieldListSidebarContainer,

View file

@ -8,7 +8,7 @@
import React from 'react';
import { FieldIcon as KbnFieldIcon, FieldIconProps as KbnFieldIconProps } from '@kbn/react-field';
import { getFieldTypeName } from '../../utils/field_types';
import { getFieldTypeName } from '@kbn/discover-utils';
export type FieldIconProps = KbnFieldIconProps;

View file

@ -32,15 +32,11 @@ import type { CoreStart } from '@kbn/core-lifecycle-browser';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { type DataViewField } from '@kbn/data-views-plugin/common';
import type { FieldTypeKnown } from '@kbn/discover-utils/types';
import { getFieldTypeName, isKnownFieldType, KNOWN_FIELD_TYPE_LIST } from '@kbn/discover-utils';
import { FieldIcon } from '../field_icon';
import {
getFieldIconType,
getFieldTypeName,
getFieldTypeDescription,
isKnownFieldType,
KNOWN_FIELD_TYPE_LIST,
} from '../../utils/field_types';
import type { FieldListItem, FieldTypeKnown, GetCustomFieldType } from '../../types';
import { getFieldIconType, getFieldTypeDescription } from '../../utils/field_types';
import type { FieldListItem, GetCustomFieldType } from '../../types';
const EQUAL_HEIGHT_OFFSET = 2; // to avoid changes in the header's height after "Clear all" button appears
const popoverTitleStyle = css`

View file

@ -10,8 +10,9 @@ import { useMemo, useState } from 'react';
import { htmlIdGenerator } from '@elastic/eui';
import { type DataViewField } from '@kbn/data-views-plugin/common';
import type { CoreStart } from '@kbn/core-lifecycle-browser';
import type { FieldTypeKnown } from '@kbn/discover-utils/types';
import { type FieldListFiltersProps } from '../components/field_list_filters';
import { type FieldListItem, type FieldTypeKnown, GetCustomFieldType } from '../types';
import { type FieldListItem, GetCustomFieldType } from '../types';
import { getFieldIconType } from '../utils/field_types';
import { fieldNameWildcardMatcher } from '../utils/field_name_wildcard_matcher';

View file

@ -8,6 +8,7 @@
import type { DataViewField } from '@kbn/data-views-plugin/common';
import type { EuiButtonIconProps, EuiButtonProps } from '@elastic/eui';
import type { FieldTypeKnown } from '@kbn/discover-utils/types';
export interface BucketedAggregation<KeyType = string> {
buckets: Array<{
@ -89,11 +90,6 @@ export type FieldListGroups<T extends FieldListItem> = {
[key in FieldsGroupNames]?: FieldsGroup<T>;
};
export type FieldTypeKnown = Exclude<
DataViewField['timeSeriesMetric'] | DataViewField['type'],
undefined
>;
export type GetCustomFieldType<T extends FieldListItem> = (field: T) => FieldTypeKnown;
export interface RenderFieldItemParams<T extends FieldListItem> {

View file

@ -7,9 +7,9 @@
*/
import { type DataViewField } from '@kbn/data-views-plugin/common';
import { isKnownFieldType } from '@kbn/discover-utils';
import type { FieldListItem, GetCustomFieldType } from '../../types';
import { getFieldType } from './get_field_type';
import { isKnownFieldType } from './field_types';
/**
* Returns an icon type for a field

View file

@ -7,7 +7,8 @@
*/
import { type DataViewField } from '@kbn/data-views-plugin/common';
import type { FieldListItem, FieldTypeKnown } from '../../types';
import type { FieldTypeKnown } from '@kbn/discover-utils/types';
import type { FieldListItem } from '../../types';
/**
* Returns a field type. Time series metric type will override the original field type.

View file

@ -7,7 +7,7 @@
*/
import { getFieldTypeDescription, UNKNOWN_FIELD_TYPE_DESC } from './get_field_type_description';
import { KNOWN_FIELD_TYPES } from './field_types';
import { KNOWN_FIELD_TYPES } from '@kbn/discover-utils';
describe('UnifiedFieldList getFieldTypeDescription()', () => {
describe('known field types should be recognized', () => {

View file

@ -8,7 +8,7 @@
import { i18n } from '@kbn/i18n';
import { KBN_FIELD_TYPES } from '@kbn/field-types';
import { KNOWN_FIELD_TYPES } from './field_types';
import { KNOWN_FIELD_TYPES } from '@kbn/discover-utils';
/**
* A user-friendly description of an unknown field type

View file

@ -6,8 +6,6 @@
* Side Public License, v 1.
*/
export { KNOWN_FIELD_TYPES, KNOWN_FIELD_TYPE_LIST, isKnownFieldType } from './field_types';
export { getFieldTypeName } from './get_field_type_name';
export { getFieldTypeDescription } from './get_field_type_description';
export { getFieldType } from './get_field_type';
export { getFieldIconType } from './get_field_icon_type';

View file

@ -1,26 +0,0 @@
/*
* 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 enum KNOWN_FIELD_TYPES {
BOOLEAN = 'boolean',
CONFLICT = 'conflict',
DATE = 'date',
DATE_RANGE = 'date_range',
GEO_POINT = 'geo_point',
GEO_SHAPE = 'geo_shape',
HISTOGRAM = 'histogram',
IP = 'ip',
IP_RANGE = 'ip_range',
KEYWORD = 'keyword',
MURMUR3 = 'murmur3',
NUMBER = 'number',
NESTED = 'nested',
STRING = 'string',
TEXT = 'text',
VERSION = 'version',
}

View file

@ -24,6 +24,7 @@
"dataViewFieldEditor",
"dataViewEditor",
"expressions",
"unifiedDocViewer",
"unifiedSearch",
"unifiedHistogram",
"contentManagement"

View file

@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n';
import { reportPerformanceMetricEvent } from '@kbn/ebt-tools';
import { removeInterceptedWarningDuplicates } from '@kbn/search-response-warnings';
import { DOC_TABLE_LEGACY, SEARCH_FIELDS_FROM_SOURCE } from '@kbn/discover-utils';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { ContextErrorMessage } from './components/context_error_message';
import { LoadingStatus } from './services/context_query_state';
import { AppState, GlobalState, isEqualFilters } from './services/context_state';
@ -28,7 +29,6 @@ import { useContextAppFetch } from './hooks/use_context_app_fetch';
import { popularizeField } from '../../utils/popularize_field';
import { ContextAppContent } from './context_app_content';
import { SurrDocType } from './services/context';
import { DocViewFilterFn } from '../../services/doc_views/doc_views_types';
import { useDiscoverServices } from '../../hooks/use_discover_services';
import { setBreadcrumbs } from '../../utils/breadcrumbs';

View file

@ -19,17 +19,16 @@ import {
SearchResponseWarnings,
} from '@kbn/search-response-warnings';
import { CONTEXT_STEP_SETTING, DOC_HIDE_TIME_COLUMN_SETTING } from '@kbn/discover-utils';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { LoadingStatus } from './services/context_query_state';
import { ActionBar } from './components/action_bar/action_bar';
import { DataLoadingState, DiscoverGrid } from '../../components/discover_grid/discover_grid';
import { DocViewFilterFn } from '../../services/doc_views/doc_views_types';
import { AppState } from './services/context_state';
import { SurrDocType } from './services/context';
import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE } from './services/constants';
import { DocTableContext } from '../../components/doc_table/doc_table_context';
import { useDiscoverServices } from '../../hooks/use_discover_services';
import { DiscoverGridFlyout } from '../../components/discover_grid/discover_grid_flyout';
import { DocViewer } from '../../services/doc_views/components/doc_viewer';
export interface ContextAppContentProps {
columns: string[];
@ -157,7 +156,6 @@ export function ContextAppContent({
sort={sort}
useNewFieldsApi={useNewFieldsApi}
dataTestSubj="contextDocTable"
DocViewer={DocViewer}
/>
)}
{!isLegacy && (

View file

@ -16,29 +16,11 @@ import { Doc, DocProps } from './doc';
import { SEARCH_FIELDS_FROM_SOURCE as mockSearchFieldsFromSource } from '@kbn/discover-utils';
import { dataViewMock } from '@kbn/discover-utils/src/__mocks__';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { setUnifiedDocViewerServices } from '@kbn/unified-doc-viewer-plugin/public/plugin';
import { mockUnifiedDocViewerServices } from '@kbn/unified-doc-viewer-plugin/public/__mocks__';
const mockSearchApi = jest.fn();
jest.mock('../../../kibana_services', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let registry: any[] = [];
return {
getDocViewsRegistry: () => ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
addDocView(view: any) {
registry.push(view);
},
getDocViewsSorted() {
return registry;
},
resetRegistry: () => {
registry = [];
},
}),
};
});
beforeEach(() => {
jest.clearAllMocks();
});
@ -86,6 +68,7 @@ async function mountDoc(update = false) {
locator: { getUrl: jest.fn(() => Promise.resolve('mock-url')) },
chrome: { setBreadcrumbs: jest.fn() },
};
setUnifiedDocViewerServices(mockUnifiedDocViewerServices);
await act(async () => {
comp = mountWithIntl(
<KibanaContextProvider services={services}>

View file

@ -9,40 +9,18 @@
import React, { useEffect } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiCallOut, EuiLink, EuiLoadingSpinner, EuiPage, EuiPageBody } from '@elastic/eui';
import type { DataView } from '@kbn/data-views-plugin/public';
import { i18n } from '@kbn/i18n';
import type { DataTableRecord } from '@kbn/discover-utils/types';
import { ElasticRequestState } from '@kbn/unified-doc-viewer';
import { UnifiedDocViewer, useEsDocSearch } from '@kbn/unified-doc-viewer-plugin/public';
import type { EsDocSearchProps } from '@kbn/unified-doc-viewer-plugin/public/types';
import { setBreadcrumbs } from '../../../utils/breadcrumbs';
import { DocViewer } from '../../../services/doc_views/components/doc_viewer';
import { ElasticRequestState } from '../types';
import { useEsDocSearch } from '../../../hooks/use_es_doc_search';
import { useDiscoverServices } from '../../../hooks/use_discover_services';
export interface DocProps {
/**
* Id of the doc in ES
*/
id: string;
/**
* Index in ES to query
*/
index: string;
/**
* DataView entity
*/
dataView: DataView;
/**
* If set, will always request source, regardless of the global `fieldsFromSource` setting
*/
requestSource?: boolean;
export interface DocProps extends EsDocSearchProps {
/**
* Discover main view url
*/
referrer?: string;
/**
* Records fetched from text based query
*/
textBasedHits?: DataTableRecord[];
}
export function Doc(props: DocProps) {
@ -141,7 +119,7 @@ export function Doc(props: DocProps) {
{reqState === ElasticRequestState.Found && hit !== null && dataView && (
<div data-test-subj="doc-hit">
<DocViewer hit={hit} dataView={dataView} />
<UnifiedDocViewer hit={hit} dataView={dataView} />
</div>
)}
</EuiPageBody>

View file

@ -28,10 +28,10 @@ import {
SAMPLE_SIZE_SETTING,
SEARCH_FIELDS_FROM_SOURCE,
} from '@kbn/discover-utils';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { useInternalStateSelector } from '../../services/discover_internal_state_container';
import { useAppStateSelector } from '../../services/discover_app_state_container';
import { useDiscoverServices } from '../../../../hooks/use_discover_services';
import { DocViewFilterFn } from '../../../../services/doc_views/doc_views_types';
import { DataLoadingState, DiscoverGrid } from '../../../../components/discover_grid/discover_grid';
import { FetchStatus } from '../../../types';
import { useColumns } from '../../../../hooks/use_data_grid_columns';
@ -44,7 +44,6 @@ import { DocumentExplorerUpdateCallout } from '../document_explorer_callout/docu
import { DiscoverTourProvider } from '../../../../components/discover_tour';
import { getRawRecordType } from '../../utils/get_raw_record_type';
import { DiscoverGridFlyout } from '../../../../components/discover_grid/discover_grid_flyout';
import { DocViewer } from '../../../../services/doc_views/components/doc_viewer';
import { useSavedSearchInitial } from '../../services/discover_state_provider';
import { useFetchMoreRecords } from './use_fetch_more_records';
@ -236,7 +235,6 @@ function DiscoverDocumentsComponent({
onSort={!isTextBasedQuery ? onSort : undefined}
useNewFieldsApi={useNewFieldsApi}
dataTestSubj="discoverDocTable"
DocViewer={DocViewer}
/>
</>
)}

View file

@ -24,6 +24,7 @@ import { generateFilters } from '@kbn/data-plugin/public';
import { useDragDropContext } from '@kbn/dom-drag-drop';
import { DataViewField, DataViewType } from '@kbn/data-views-plugin/public';
import { SEARCH_FIELDS_FROM_SOURCE, SHOW_FIELD_STATISTICS } from '@kbn/discover-utils';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { useSavedSearchInitial } from '../../services/discover_state_provider';
import { DiscoverStateContainer } from '../../services/discover_state';
import { VIEW_MODE } from '../../../../../common/constants';
@ -36,7 +37,6 @@ import { LoadingSpinner } from '../loading_spinner/loading_spinner';
import { DiscoverSidebarResponsive } from '../sidebar';
import { popularizeField } from '../../../../utils/popularize_field';
import { DiscoverTopNav } from '../top_nav/discover_topnav';
import { DocViewFilterFn } from '../../../../services/doc_views/doc_views_types';
import { getResultState } from '../../utils/get_result_state';
import { DiscoverUninitialized } from '../uninitialized/uninitialized';
import { DataMainMsg, RecordRawType } from '../../services/discover_data_state_container';

View file

@ -12,10 +12,10 @@ import React, { useCallback } from 'react';
import { DataView } from '@kbn/data-views-plugin/common';
import { METRIC_TYPE } from '@kbn/analytics';
import { i18n } from '@kbn/i18n';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { VIEW_MODE } from '../../../../../common/constants';
import { useDiscoverServices } from '../../../../hooks/use_discover_services';
import { DocumentViewModeToggle } from '../../../../components/view_mode_toggle';
import { DocViewFilterFn } from '../../../../services/doc_views/doc_views_types';
import { DiscoverStateContainer } from '../../services/discover_state';
import { FieldStatisticsTab } from '../field_stats_table';
import { DiscoverDocuments } from './discover_documents';

View file

@ -1,32 +0,0 @@
/*
* 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, useRef, useState } from 'react';
/**
* A component that shows it children with a 300ms delay. This is good for wrapping
* loading spinners for tasks that might potentially be very fast (e.g. loading async chunks).
* That way we don't show a quick flash of the spinner before the actual content and will only
* show the spinner once loading takes a bit longer (more than 300ms).
*/
export const DeferredSpinner: React.FC = ({ children }) => {
const timeoutRef = useRef<number>();
const [showContent, setShowContent] = useState(false);
useEffect(() => {
timeoutRef.current = window.setTimeout(() => {
setShowContent(true);
}, 300);
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);
return showContent ? <React.Fragment>{children}</React.Fragment> : null;
};

View file

@ -42,7 +42,7 @@ import {
MAX_DOC_FIELDS_DISPLAYED,
SHOW_MULTIFIELDS,
} from '@kbn/discover-utils';
import { DocViewFilterFn } from '../../services/doc_views/doc_views_types';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { getSchemaDetectors } from './discover_grid_schema';
import { DiscoverGridFlyout } from './discover_grid_flyout';
import { DiscoverGridContext } from './discover_grid_context';

View file

@ -10,7 +10,7 @@ import React, { useContext } from 'react';
import { EuiDataGridColumnCellActionProps } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DataViewField } from '@kbn/data-views-plugin/public';
import { DocViewFilterFn } from '../../services/doc_views/doc_views_types';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { DiscoverGridContext, GridContext } from './discover_grid_context';
import { useDiscoverServices } from '../../hooks/use_discover_services';
import { copyValueToClipboard } from '../../utils/copy_value_to_clipboard';

View file

@ -17,7 +17,7 @@ import {
} from '@elastic/eui';
import type { DataView } from '@kbn/data-views-plugin/public';
import { ToastsStart, IUiSettingsClient } from '@kbn/core/public';
import { DocViewFilterFn } from '../../services/doc_views/doc_views_types';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { ExpandButton } from './discover_grid_expand_button';
import { DiscoverGridSettings } from './types';
import type { ValueToStringConverter } from '../../types';

View file

@ -9,7 +9,7 @@
import React from 'react';
import type { DataView } from '@kbn/data-views-plugin/public';
import type { DataTableRecord } from '@kbn/discover-utils/types';
import type { DocViewFilterFn } from '../../services/doc_views/doc_views_types';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import type { ValueToStringConverter } from '../../types';
export interface GridContext {

View file

@ -14,8 +14,6 @@ import { DiscoverGridFlyout, DiscoverGridFlyoutProps } from './discover_grid_fly
import { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_manager/filter_manager.mock';
import { dataViewMock, esHitsMock } from '@kbn/discover-utils/src/__mocks__';
import { DiscoverServices } from '../../build_services';
import { DocViewsRegistry } from '../../services/doc_views/doc_views_registry';
import { setDocViewsRegistry } from '../../kibana_services';
import { dataViewWithTimefieldMock } from '../../__mocks__/data_view_with_timefield';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
@ -23,6 +21,8 @@ import type { DataTableRecord, EsHitRecord } from '@kbn/discover-utils/types';
import { buildDataTableRecord } from '@kbn/discover-utils';
import { act } from 'react-dom/test-utils';
import { ReactWrapper } from 'enzyme';
import { setUnifiedDocViewerServices } from '@kbn/unified-doc-viewer-plugin/public/plugin';
import { mockUnifiedDocViewerServices } from '@kbn/unified-doc-viewer-plugin/public/__mocks__';
const waitNextTick = () => new Promise((resolve) => setTimeout(resolve, 0));
@ -34,8 +34,6 @@ const waitNextUpdate = async (component: ReactWrapper) => {
};
describe('Discover flyout', function () {
setDocViewsRegistry(new DocViewsRegistry());
const mountComponent = async ({
dataView,
hits,
@ -60,6 +58,7 @@ describe('Discover flyout', function () {
contextLocator: { getRedirectUrl: jest.fn(() => 'mock-context-redirect-url') },
singleDocLocator: { getRedirectUrl: jest.fn(() => 'mock-doc-redirect-url') },
} as unknown as DiscoverServices;
setUnifiedDocViewerServices(mockUnifiedDocViewerServices);
const hit = buildDataTableRecord(
hitIndex ? esHitsMock[hitIndex] : (esHitsMock[0] as EsHitRecord),

View file

@ -27,8 +27,8 @@ import {
} from '@elastic/eui';
import type { Filter, Query, AggregateQuery } from '@kbn/es-query';
import type { DataTableRecord } from '@kbn/discover-utils/types';
import { DocViewer } from '../../services/doc_views/components/doc_viewer/doc_viewer';
import { DocViewFilterFn } from '../../services/doc_views/doc_views_types';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { UnifiedDocViewer } from '@kbn/unified-doc-viewer-plugin/public';
import { useNavigationProps } from '../../hooks/use_navigation_props';
import { useDiscoverServices } from '../../hooks/use_discover_services';
import { isTextBasedQuery } from '../../application/main/utils/is_text_based_query';
@ -225,7 +225,7 @@ export function DiscoverGridFlyout({
</EuiFlexGroup>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<DocViewer
<UnifiedDocViewer
hit={actualHit}
columns={columns}
dataView={dataView}

View file

@ -285,7 +285,7 @@ describe('Discover grid cell rendering', function () {
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<JsonCodeEditor
<ForwardRef
height={200}
json={
Object {
@ -517,7 +517,7 @@ describe('Discover grid cell rendering', function () {
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<JsonCodeEditor
<ForwardRef
height={200}
json={
Object {
@ -681,7 +681,7 @@ describe('Discover grid cell rendering', function () {
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<JsonCodeEditor
<ForwardRef
height={200}
json={
Object {

View file

@ -27,8 +27,8 @@ import type {
ShouldShowFieldInTableHandler,
} from '@kbn/discover-utils/types';
import { MAX_DOC_FIELDS_DISPLAYED, formatFieldValue, formatHit } from '@kbn/discover-utils';
import { JsonCodeEditor } from '@kbn/unified-doc-viewer-plugin/public';
import { DiscoverGridContext } from './discover_grid_context';
import { JsonCodeEditor } from '../json_code_editor/json_code_editor';
import { defaultMonacoEditorWidth } from './constants';
import { useDiscoverServices } from '../../hooks/use_discover_services';

View file

@ -9,13 +9,10 @@
import React from 'react';
import { mountWithIntl, findTestSubject } from '@kbn/test-jest-helpers';
import { TableRow, TableRowProps } from './table_row';
import { setDocViewsRegistry } from '../../../kibana_services';
import { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_manager/filter_manager.mock';
import { dataViewWithTimefieldMock } from '../../../__mocks__/data_view_with_timefield';
import { DocViewsRegistry } from '../../../services/doc_views/doc_views_registry';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { discoverServiceMock } from '../../../__mocks__/services';
import { DocViewer } from '../../../services/doc_views/components/doc_viewer';
import { DOC_HIDE_TIME_COLUMN_SETTING, MAX_DOC_FIELDS_DISPLAYED } from '@kbn/discover-utils';
import { buildDataTableRecord } from '@kbn/discover-utils';
@ -81,13 +78,8 @@ describe('Doc table row component', () => {
useNewFieldsApi: true,
filterManager: mockFilterManager,
addBasePath: (path: string) => path,
DocViewer,
} as unknown as TableRowProps;
beforeEach(() => {
setDocViewsRegistry(new DocViewsRegistry());
});
it('should render __document__ column', () => {
const component = mountComponent({ ...defaultProps, columns: [] });
const docTableField = findTestSubject(component, 'docTableField');

View file

@ -19,10 +19,10 @@ import type {
} from '@kbn/discover-utils/types';
import { formatFieldValue } from '@kbn/discover-utils';
import { DOC_HIDE_TIME_COLUMN_SETTING, MAX_DOC_FIELDS_DISPLAYED } from '@kbn/discover-utils';
import { DocViewRenderProps } from '../../../services/doc_views/doc_views_types';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { UnifiedDocViewer } from '@kbn/unified-doc-viewer-plugin/public';
import { TableCell } from './table_row/table_cell';
import { formatRow, formatTopLevelObject } from '../utils/row_formatter';
import { DocViewFilterFn } from '../../../services/doc_views/doc_views_types';
import { TableRowDetails } from './table_row_details';
import { useDiscoverServices } from '../../../hooks/use_discover_services';
@ -43,7 +43,6 @@ export interface TableRowProps {
shouldShowFieldHandler: ShouldShowFieldInTableHandler;
onAddColumn?: (column: string) => void;
onRemoveColumn?: (column: string) => void;
DocViewer: React.ComponentType<DocViewRenderProps>;
}
export const TableRow = ({
@ -59,7 +58,6 @@ export const TableRow = ({
shouldShowFieldHandler,
onAddColumn,
onRemoveColumn,
DocViewer,
}: TableRowProps) => {
const { uiSettings, fieldFormats } = useDiscoverServices();
const [maxEntries, hideTimeColumn] = useMemo(
@ -223,7 +221,7 @@ export const TableRow = ({
savedSearchId={savedSearchId}
isPlainRecord={isPlainRecord}
>
<DocViewer
<UnifiedDocViewer
columns={columns}
filter={filter}
hit={row}

View file

@ -9,7 +9,6 @@
import React from 'react';
import { I18nProvider } from '@kbn/i18n-react';
import { DocTableEmbeddable, DocTableEmbeddableProps } from './doc_table_embeddable';
import { DocViewer } from '../../services/doc_views/components/doc_viewer';
export function DiscoverDocTableEmbeddable(renderProps: DocTableEmbeddableProps) {
return (
@ -35,7 +34,6 @@ export function DiscoverDocTableEmbeddable(renderProps: DocTableEmbeddableProps)
isPlainRecord={renderProps.isPlainRecord}
interceptedWarnings={renderProps.interceptedWarnings}
dataTestSubj="embeddedSavedSearchDocTable"
DocViewer={DocViewer}
/>
</I18nProvider>
);

View file

@ -15,7 +15,6 @@ import { discoverServiceMock } from '../../__mocks__/services';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { buildDataTableRecord } from '@kbn/discover-utils';
import type { EsHitRecord } from '@kbn/discover-utils/types';
import { DocViewer } from '../../services/doc_views/components/doc_viewer';
describe('Doc table component', () => {
const mountComponent = (customProps?: Partial<DocTableWrapperProps>) => {
@ -48,7 +47,6 @@ describe('Doc table component', () => {
render: () => {
return <div data-test-subj="docTable">mock</div>;
},
DocViewer,
...(customProps || {}),
};

View file

@ -14,9 +14,9 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { Filter } from '@kbn/es-query';
import type { DataTableRecord } from '@kbn/discover-utils/types';
import { SHOW_MULTIFIELDS, getShouldShowFieldHandler } from '@kbn/discover-utils';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { TableHeader } from './components/table_header/table_header';
import { TableRow } from './components/table_row';
import { DocViewFilterFn, DocViewRenderProps } from '../../services/doc_views/doc_views_types';
import { useDiscoverServices } from '../../hooks/use_discover_services';
export interface DocTableProps {
@ -89,10 +89,6 @@ export interface DocTableProps {
* Remove column callback
*/
onRemoveColumn?: (column: string) => void;
/**
* Doc viewer component
*/
DocViewer: React.ComponentType<DocViewRenderProps>;
}
export interface DocTableRenderProps {
@ -133,7 +129,6 @@ export const DocTableWrapper = forwardRef(
sharedItemTitle,
dataTestSubj,
isLoading,
DocViewer,
}: DocTableWrapperProps,
ref
) => {
@ -191,7 +186,6 @@ export const DocTableWrapper = forwardRef(
shouldShowFieldHandler={shouldShowFieldHandler}
onAddColumn={onAddColumn}
onRemoveColumn={onRemoveColumn}
DocViewer={DocViewer}
isPlainRecord={isPlainRecord}
rows={rows}
/>
@ -207,7 +201,6 @@ export const DocTableWrapper = forwardRef(
shouldShowFieldHandler,
onAddColumn,
onRemoveColumn,
DocViewer,
isPlainRecord,
rows,
]

View file

@ -12,7 +12,6 @@ import type { ScopedHistory, AppMountParameters } from '@kbn/core/public';
import type { UiActionsStart } from '@kbn/ui-actions-plugin/public';
import { createGetterSetter } from '@kbn/kibana-utils-plugin/public';
import { HistoryLocationState } from './build_services';
import { DocViewsRegistry } from './services/doc_views/doc_views_registry';
let uiActions: UiActionsStart;
export interface UrlTracker {
@ -29,9 +28,6 @@ export const [getHeaderActionMenuMounter, setHeaderActionMenuMounter] =
export const [getUrlTracker, setUrlTracker] = createGetterSetter<UrlTracker>('urlTracker');
export const [getDocViewsRegistry, setDocViewsRegistry] =
createGetterSetter<DocViewsRegistry>('DocViewsRegistry');
/**
* Makes sure discover and context are using one instance of history.
*/

View file

@ -15,9 +15,6 @@ export type Start = jest.Mocked<DiscoverStart>;
const createSetupContract = (): Setup => {
const setupContract: Setup = {
docViews: {
addDocView: jest.fn(),
},
locator: sharePluginMock.createLocator(),
};
return setupContract;

View file

@ -6,7 +6,6 @@
* Side Public License, v 1.
*/
import { i18n } from '@kbn/i18n';
import React, { ComponentType } from 'react';
import { BehaviorSubject, combineLatest, map } from 'rxjs';
import {
@ -26,7 +25,6 @@ import { SharePluginStart, SharePluginSetup } from '@kbn/share-plugin/public';
import { UrlForwardingSetup, UrlForwardingStart } from '@kbn/url-forwarding-plugin/public';
import { HomePublicPluginSetup } from '@kbn/home-plugin/public';
import { Start as InspectorPublicPluginStart } from '@kbn/inspector-plugin/public';
import { EuiSkeletonText } from '@elastic/eui';
import { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public';
import { SavedObjectsStart } from '@kbn/saved-objects-plugin/public';
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public';
@ -42,16 +40,14 @@ import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-taggin
import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public';
import type { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public';
import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
import type { UnifiedDocViewerStart } from '@kbn/unified-doc-viewer-plugin/public';
import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public';
import type { LensPublicStart } from '@kbn/lens-plugin/public';
import { TRUNCATE_MAX_HEIGHT } from '@kbn/discover-utils';
import type { ServerlessPluginStart } from '@kbn/serverless/public';
import { DOC_TABLE_LEGACY, TRUNCATE_MAX_HEIGHT } from '@kbn/discover-utils';
import { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public';
import { PLUGIN_ID } from '../common';
import { DocViewInput, DocViewInputFn } from './services/doc_views/doc_views_types';
import { DocViewsRegistry } from './services/doc_views/doc_views_registry';
import {
setDocViewsRegistry,
setHeaderActionMenuMounter,
setScopedHistory,
setUiActions,
@ -61,10 +57,8 @@ import {
import { registerFeature } from './register_feature';
import { buildServices } from './build_services';
import { SearchEmbeddableFactory } from './embeddable';
import { DeferredSpinner } from './components';
import { ViewSavedSearchAction } from './embeddable/view_saved_search_action';
import { injectTruncateStyles } from './utils/truncate_styles';
import { useDiscoverServices } from './hooks/use_discover_services';
import { initializeKbnUrlTracking } from './utils/initialize_kbn_url_tracking';
import {
DiscoverContextAppLocator,
@ -86,24 +80,10 @@ import {
type DiscoverContainerProps,
} from './components/discover_container';
const DocViewerLegacyTable = React.lazy(
() => import('./services/doc_views/components/doc_viewer_table/legacy')
);
const DocViewerTable = React.lazy(() => import('./services/doc_views/components/doc_viewer_table'));
const SourceViewer = React.lazy(() => import('./services/doc_views/components/doc_viewer_source'));
/**
* @public
*/
export interface DiscoverSetup {
docViews: {
/**
* Add new doc view shown along with table view and json view in the details of each document in Discover.
* @param docViewRaw
*/
addDocView(docViewRaw: DocViewInput | DocViewInputFn): void;
};
/**
* `share` plugin URL locator for Discover app. Use it to generate links into
* Discover application, for example, navigate:
@ -211,6 +191,7 @@ export interface DiscoverStartPlugins {
savedObjectsManagement: SavedObjectsManagementPluginStart;
savedSearch: SavedSearchPublicPluginStart;
unifiedSearch: UnifiedSearchPublicPluginStart;
unifiedDocViewer: UnifiedDocViewerStart;
lens: LensPublicStart;
contentManagement: ContentManagementPublicStart;
serverless?: ServerlessPluginStart;
@ -227,7 +208,6 @@ export class DiscoverPlugin
constructor(private readonly initializerContext: PluginInitializerContext) {}
private appStateUpdater = new BehaviorSubject<AppUpdater>(() => ({}));
private docViewsRegistry: DocViewsRegistry | null = null;
private stopUrlTracking: (() => void) | undefined = undefined;
private profileRegistry = createProfileRegistry();
private locator?: DiscoverAppLocator;
@ -253,59 +233,6 @@ export class DiscoverPlugin
);
}
this.docViewsRegistry = new DocViewsRegistry();
setDocViewsRegistry(this.docViewsRegistry);
this.docViewsRegistry.addDocView({
title: i18n.translate('discover.docViews.table.tableTitle', {
defaultMessage: 'Table',
}),
order: 10,
component: (props) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const services = useDiscoverServices();
const DocView = services.uiSettings.get(DOC_TABLE_LEGACY)
? DocViewerLegacyTable
: DocViewerTable;
return (
<React.Suspense
fallback={
<DeferredSpinner>
<EuiSkeletonText />
</DeferredSpinner>
}
>
<DocView {...props} />
</React.Suspense>
);
},
});
this.docViewsRegistry.addDocView({
title: i18n.translate('discover.docViews.json.jsonTitle', {
defaultMessage: 'JSON',
}),
order: 20,
component: ({ hit, dataView, query, textBasedHits }) => {
return (
<React.Suspense
fallback={
<DeferredSpinner>
<EuiSkeletonText />
</DeferredSpinner>
}
>
<SourceViewer
index={hit.raw._index}
id={hit.raw._id ?? hit.id}
dataView={dataView}
textBasedHits={textBasedHits}
hasLineNumbers
/>
</React.Suspense>
);
},
});
const {
setTrackedUrl,
restorePreviousUrl,
@ -418,9 +345,6 @@ export class DiscoverPlugin
this.registerEmbeddable(core, plugins);
return {
docViews: {
addDocView: this.docViewsRegistry.addDocView.bind(this.docViewsRegistry),
},
locator: this.locator,
};
}

View file

@ -1,89 +0,0 @@
/*
* 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 from 'react';
import { mount, shallow } from 'enzyme';
import { DocViewer } from './doc_viewer';
import { findTestSubject } from '@elastic/eui/lib/test';
import { getDocViewsRegistry } from '../../../../kibana_services';
import { DocViewRenderProps } from '../../doc_views_types';
import { buildDataTableRecord } from '@kbn/discover-utils';
jest.mock('../../../../kibana_services', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let registry: any[] = [];
return {
getDocViewsRegistry: () => ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
addDocView(view: any) {
registry.push(view);
},
getDocViewsSorted() {
return registry;
},
resetRegistry: () => {
registry = [];
},
}),
};
});
jest.mock('../../../../hooks/use_discover_services', () => {
return {
useDiscoverServices: {
uiSettings: {
get: jest.fn(),
},
},
};
});
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(getDocViewsRegistry() as any).resetRegistry();
jest.clearAllMocks();
});
test('Render <DocViewer/> with 3 different tabs', () => {
const registry = getDocViewsRegistry();
registry.addDocView({ order: 10, title: 'Render function', render: jest.fn() });
registry.addDocView({ order: 20, title: 'React component', component: () => <div>test</div> });
// @ts-expect-error This should be invalid and will throw an error when rendering
registry.addDocView({ order: 30, title: 'Invalid doc view' });
const renderProps = { hit: {} } as DocViewRenderProps;
const wrapper = shallow(<DocViewer {...renderProps} />);
expect(wrapper).toMatchSnapshot();
});
test('Render <DocViewer/> with 1 tab displaying error message', () => {
function SomeComponent() {
// this is just a placeholder
return null;
}
const registry = getDocViewsRegistry();
registry.addDocView({
order: 10,
title: 'React component',
component: SomeComponent,
});
const renderProps = {
hit: buildDataTableRecord({ _index: 't', _id: '1' }),
} as DocViewRenderProps;
const errorMsg = 'Catch me if you can!';
const wrapper = mount(<DocViewer {...renderProps} />);
const error = new Error(errorMsg);
wrapper.find(SomeComponent).simulateError(error);
const errorMsgComponent = findTestSubject(wrapper, 'docViewerError');
expect(errorMsgComponent.text()).toMatch(new RegExp(`${errorMsg}`));
});

View file

@ -51,8 +51,6 @@
"@kbn/shared-ux-page-analytics-no-data",
"@kbn/alerting-plugin",
"@kbn/ui-theme",
"@kbn/react-field",
"@kbn/monaco",
"@kbn/config-schema",
"@kbn/storybook",
"@kbn/shared-ux-router",
@ -68,6 +66,8 @@
"@kbn/field-types",
"@kbn/search-response-warnings",
"@kbn/content-management-plugin",
"@kbn/unified-doc-viewer",
"@kbn/unified-doc-viewer-plugin",
"@kbn/serverless",
"@kbn/react-kibana-mount",
"@kbn/react-kibana-context-render",

View file

@ -0,0 +1,3 @@
# unifiedDocViewer
This plugin contains services reliant on the plugin lifecycle for the unified doc viewer component (see @kbn/unified-doc-viewer).

View file

@ -0,0 +1,18 @@
/*
* 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.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/src/plugins/unified_doc_viewer'],
coverageDirectory: '<rootDir>/target/kibana-coverage/jest/src/plugins/unified_doc_viewer',
coverageReporters: ['text', 'html'],
collectCoverageFrom: [
'<rootDir>/src/plugins/unified_doc_viewer/{common,public,server}/**/*.{ts,tsx}',
],
};

View file

@ -0,0 +1,13 @@
{
"type": "plugin",
"id": "@kbn/unified-doc-viewer-plugin",
"owner": "@elastic/kibana-data-discovery",
"description": "This plugin contains services reliant on the plugin lifecycle for the unified doc viewer component (see @kbn/unified-doc-viewer).",
"plugin": {
"id": "unifiedDocViewer",
"server": false,
"browser": true,
"requiredBundles": ["kibanaUtils", "kibanaReact"],
"requiredPlugins": ["data", "fieldFormats"],
}
}

View 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 * from './services';

View file

@ -0,0 +1,27 @@
/*
* 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 { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks';
import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
import type { UnifiedDocViewerServices, UnifiedDocViewerStart } from '../types';
import { Storage } from '@kbn/kibana-utils-plugin/public';
export const mockUnifiedDocViewer: jest.Mocked<UnifiedDocViewerStart> = {
getDocViews: jest.fn().mockReturnValue([]),
};
export const mockUnifiedDocViewerServices: jest.Mocked<UnifiedDocViewerServices> = {
analytics: analyticsServiceMock.createAnalyticsServiceStart(),
data: dataPluginMock.createStartContract(),
fieldFormats: fieldFormatsMock,
storage: new Storage(localStorage),
uiSettings: uiSettingsServiceMock.createStartContract(),
unifiedDocViewer: mockUnifiedDocViewer,
};

View file

@ -0,0 +1,22 @@
/*
* 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 from 'react';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types';
import { DocViewer } from '@kbn/unified-doc-viewer';
import { getUnifiedDocViewerServices } from '../../plugin';
export function UnifiedDocViewer(props: DocViewRenderProps) {
const services = getUnifiedDocViewerServices();
return (
<KibanaContextProvider services={services}>
<DocViewer docViews={services.unifiedDocViewer.getDocViews(props.hit)} {...props} />
</KibanaContextProvider>
);
}

View file

@ -0,0 +1,13 @@
/*
* 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 { UnifiedDocViewer } from './doc_viewer';
// Required for usage in React.lazy
// eslint-disable-next-line import/no-default-export
export default UnifiedDocViewer;

View file

@ -10,10 +10,10 @@ import React from 'react';
import type { DataView } from '@kbn/data-views-plugin/public';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import { DocViewerSource } from './source';
import * as hooks from '../../../../hooks/use_es_doc_search';
import * as hooks from '../../hooks/use_es_doc_search';
import * as useUiSettingHook from '@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting';
import { EuiButton, EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui';
import { JsonCodeEditorCommon } from '../../../../components/json_code_editor/json_code_editor_common';
import { JsonCodeEditorCommon } from '../json_code_editor';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { buildDataTableRecord } from '@kbn/discover-utils';
import { of } from 'rxjs';
@ -53,6 +53,7 @@ describe('Source Viewer component', () => {
dataView={mockDataView}
width={123}
hasLineNumbers={true}
onRefresh={() => {}}
/>
</KibanaContextProvider>
);
@ -71,6 +72,7 @@ describe('Source Viewer component', () => {
dataView={mockDataView}
width={123}
hasLineNumbers={true}
onRefresh={() => {}}
/>
</KibanaContextProvider>
);
@ -110,6 +112,7 @@ describe('Source Viewer component', () => {
dataView={mockDataView}
width={123}
hasLineNumbers={true}
onRefresh={() => {}}
/>
</KibanaContextProvider>
);

View file

@ -12,14 +12,14 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { monaco } from '@kbn/monaco';
import { EuiButton, EuiEmptyPrompt, EuiLoadingSpinner, EuiSpacer, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DataView } from '@kbn/data-views-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
import type { DataTableRecord } from '@kbn/discover-utils/types';
import { ElasticRequestState } from '@kbn/unified-doc-viewer';
import { DOC_TABLE_LEGACY, SEARCH_FIELDS_FROM_SOURCE } from '@kbn/discover-utils';
import { useDiscoverServices } from '../../../../hooks/use_discover_services';
import { JSONCodeEditorCommonMemoized } from '../../../../components/json_code_editor/json_code_editor_common';
import { useEsDocSearch } from '../../../../hooks/use_es_doc_search';
import { ElasticRequestState } from '../../../../application/doc/types';
import { useEsDocSearch } from '../../hooks';
import { useUnifiedDocViewerServices } from '../../hooks';
import { getHeight } from './get_height';
import { JSONCodeEditorCommonMemoized } from '../json_code_editor';
interface SourceViewerProps {
id: string;
@ -28,6 +28,8 @@ interface SourceViewerProps {
textBasedHits?: DataTableRecord[];
hasLineNumbers: boolean;
width?: number;
requestState?: ElasticRequestState;
onRefresh: () => void;
}
// Ihe number of lines displayed without scrolling used for classic table, which renders the component
@ -43,14 +45,15 @@ export const DocViewerSource = ({
width,
hasLineNumbers,
textBasedHits,
onRefresh,
}: SourceViewerProps) => {
const [editor, setEditor] = useState<monaco.editor.IStandaloneCodeEditor>();
const [editorHeight, setEditorHeight] = useState<number>();
const [jsonValue, setJsonValue] = useState<string>('');
const { uiSettings } = useDiscoverServices();
const { uiSettings } = useUnifiedDocViewerServices();
const useNewFieldsApi = !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE);
const useDocExplorer = !uiSettings.get(DOC_TABLE_LEGACY);
const [reqState, hit, requestData] = useEsDocSearch({
const [requestState, hit] = useEsDocSearch({
id,
index,
dataView,
@ -59,10 +62,10 @@ export const DocViewerSource = ({
});
useEffect(() => {
if (reqState === ElasticRequestState.Found && hit) {
if (requestState === ElasticRequestState.Found && hit) {
setJsonValue(JSON.stringify(hit.raw, undefined, 2));
}
}, [reqState, hit]);
}, [requestState, hit]);
// setting editor height
// - classic view: based on lines height and count to stretch and fit its content
@ -93,26 +96,26 @@ export const DocViewerSource = ({
<div className="sourceViewer__loading">
<EuiLoadingSpinner className="sourceViewer__loadingSpinner" />
<EuiText size="xs" color="subdued">
<FormattedMessage id="discover.loadingJSON" defaultMessage="Loading JSON" />
<FormattedMessage id="unifiedDocViewer.loadingJSON" defaultMessage="Loading JSON" />
</EuiText>
</div>
);
const errorMessageTitle = (
<h2>
{i18n.translate('discover.sourceViewer.errorMessageTitle', {
{i18n.translate('unifiedDocViewer.sourceViewer.errorMessageTitle', {
defaultMessage: 'An Error Occurred',
})}
</h2>
);
const errorMessage = (
<div>
{i18n.translate('discover.sourceViewer.errorMessage', {
{i18n.translate('unifiedDocViewer.sourceViewer.errorMessage', {
defaultMessage: 'Could not fetch data at this time. Refresh the tab to try again.',
})}
<EuiSpacer size="s" />
<EuiButton iconType="refresh" onClick={requestData}>
{i18n.translate('discover.sourceViewer.refresh', {
<EuiButton iconType="refresh" onClick={onRefresh}>
{i18n.translate('unifiedDocViewer.sourceViewer.refresh', {
defaultMessage: 'Refresh',
})}
</EuiButton>
@ -122,11 +125,11 @@ export const DocViewerSource = ({
<EuiEmptyPrompt iconType="warning" title={errorMessageTitle} body={errorMessage} />
);
if (reqState === ElasticRequestState.Error || reqState === ElasticRequestState.NotFound) {
if (requestState === ElasticRequestState.Error || requestState === ElasticRequestState.NotFound) {
return errorState;
}
if (reqState === ElasticRequestState.Loading || jsonValue === '') {
if (requestState === ElasticRequestState.Loading || jsonValue === '') {
return loadingState;
}

Some files were not shown because too many files have changed in this diff Show more