Replace EuiCodeBlock with JsonCodeEditor in DiscoverGrid (#92442) (#94780)

* Replace EuiCodeBlock with JsonCodeEditor in DiscoverGrid

* Add optional "hasLineNumbers" property to JsonCodeEditor and removed line numbers from the popover

* Update json_code_editor snapshot

* Add functional test for cell expanded content popover

* Remove unused code

* Fix geo point case and refactor some code

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Diana Derevyankina 2021-03-17 13:44:23 +03:00 committed by GitHub
parent 4d2c8e6c8f
commit 8781e4c349
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 176 additions and 109 deletions

View file

@ -24,3 +24,5 @@ export const toolbarVisibility = {
},
showStyleSelector: false,
};
export const defaultMonacoEditorWidth = 370;

View file

@ -22,7 +22,7 @@ import {
} from '@elastic/eui';
import { IndexPattern } from '../../../kibana_services';
import { DocViewFilterFn, ElasticSearchHit } from '../../doc_views/doc_views_types';
import { getPopoverContents, getSchemaDetectors } from './discover_grid_schema';
import { getSchemaDetectors } from './discover_grid_schema';
import { DiscoverGridFlyout } from './discover_grid_flyout';
import { DiscoverGridContext } from './discover_grid_context';
import { getRenderCellValueFn } from './get_render_cell_value';
@ -36,6 +36,7 @@ import {
import { defaultPageSize, gridStyle, pageSizeArr, toolbarVisibility } from './constants';
import { DiscoverServices } from '../../../build_services';
import { getDisplayedColumns } from '../../helpers/columns';
import { KibanaContextProvider } from '../../../../../kibana_react/public';
interface SortObj {
id: string;
@ -219,7 +220,6 @@ export const DiscoverGrid = ({
[displayedColumns, indexPattern, showTimeCol, settings, defaultColumns]
);
const schemaDetectors = useMemo(() => getSchemaDetectors(), []);
const popoverContents = useMemo(() => getPopoverContents(), []);
const columnsVisibility = useMemo(
() => ({
visibleColumns: getVisibleColumns(displayedColumns, indexPattern, showTimeCol) as string[],
@ -259,34 +259,35 @@ export const DiscoverGrid = ({
}}
>
<>
<EuiDataGridMemoized
aria-describedby={randomId}
aria-labelledby={ariaLabelledBy}
columns={euiGridColumns}
columnVisibility={columnsVisibility}
data-test-subj="docTable"
gridStyle={gridStyle as EuiDataGridStyle}
leadingControlColumns={lead}
onColumnResize={(col: { columnId: string; width: number }) => {
if (onResize) {
onResize(col);
<KibanaContextProvider services={{ uiSettings: services.uiSettings }}>
<EuiDataGridMemoized
aria-describedby={randomId}
aria-labelledby={ariaLabelledBy}
columns={euiGridColumns}
columnVisibility={columnsVisibility}
data-test-subj="docTable"
gridStyle={gridStyle as EuiDataGridStyle}
leadingControlColumns={lead}
onColumnResize={(col: { columnId: string; width: number }) => {
if (onResize) {
onResize(col);
}
}}
pagination={paginationObj}
renderCellValue={renderCellValue}
rowCount={rowCount}
schemaDetectors={schemaDetectors}
sorting={sorting as EuiDataGridSorting}
toolbarVisibility={
defaultColumns
? {
...toolbarVisibility,
showColumnSelector: false,
}
: toolbarVisibility
}
}}
pagination={paginationObj}
popoverContents={popoverContents}
renderCellValue={renderCellValue}
rowCount={rowCount}
schemaDetectors={schemaDetectors}
sorting={sorting as EuiDataGridSorting}
toolbarVisibility={
defaultColumns
? {
...toolbarVisibility,
showColumnSelector: false,
}
: toolbarVisibility
}
/>
/>
</KibanaContextProvider>
{showDisclaimer && (
<p className="dscDiscoverGrid__footer">

View file

@ -6,8 +6,6 @@
* Side Public License, v 1.
*/
import React from 'react';
import { EuiCodeBlock, EuiDataGridPopoverContents } from '@elastic/eui';
import { kibanaJSON } from './constants';
import { KBN_FIELD_TYPES } from '../../../../../data/common';
@ -43,18 +41,3 @@ export function getSchemaDetectors() {
},
];
}
/**
* Returns custom popover content for certain schemas
*/
export function getPopoverContents(): EuiDataGridPopoverContents {
return {
[kibanaJSON]: ({ children }) => {
return (
<EuiCodeBlock isCopyable language="json" paddingSize="none" transparentBackground={true}>
{children}
</EuiCodeBlock>
);
},
};
}

View file

@ -7,10 +7,25 @@
*/
import React from 'react';
import { shallow } from 'enzyme';
import { ReactWrapper, shallow } from 'enzyme';
import { getRenderCellValueFn } from './get_render_cell_value';
import { indexPatternMock } from '../../../__mocks__/index_pattern';
jest.mock('../../../../../kibana_react/public', () => ({
useUiSetting: () => true,
withKibana: (comp: ReactWrapper) => {
return comp;
},
}));
jest.mock('../../../kibana_services', () => ({
getServices: () => ({
uiSettings: {
get: jest.fn(),
},
}),
}));
const rowsSource = [
{
_id: '1',
@ -139,20 +154,25 @@ describe('Discover grid cell rendering', function () {
setCellProps={jest.fn()}
/>
);
expect(component.html()).toMatchInlineSnapshot(`
"<span>{
&quot;_id&quot;: &quot;1&quot;,
&quot;_index&quot;: &quot;test&quot;,
&quot;_type&quot;: &quot;test&quot;,
&quot;_score&quot;: 1,
&quot;_source&quot;: {
&quot;bytes&quot;: 100,
&quot;extension&quot;: &quot;.gz&quot;
},
&quot;highlight&quot;: {
&quot;extension&quot;: &quot;@kibana-highlighted-field.gz@/kibana-highlighted-field&quot;
expect(component).toMatchInlineSnapshot(`
<JsonCodeEditor
json={
Object {
"_id": "1",
"_index": "test",
"_score": 1,
"_source": Object {
"bytes": 100,
"extension": ".gz",
},
"_type": "test",
"highlight": Object {
"extension": "@kibana-highlighted-field.gz@/kibana-highlighted-field",
},
}
}
}</span>"
width={370}
/>
`);
});
@ -226,24 +246,30 @@ describe('Discover grid cell rendering', function () {
setCellProps={jest.fn()}
/>
);
expect(component.html()).toMatchInlineSnapshot(`
"<span>{
&quot;_id&quot;: &quot;1&quot;,
&quot;_index&quot;: &quot;test&quot;,
&quot;_type&quot;: &quot;test&quot;,
&quot;_score&quot;: 1,
&quot;fields&quot;: {
&quot;bytes&quot;: [
100
],
&quot;extension&quot;: [
&quot;.gz&quot;
]
},
&quot;highlight&quot;: {
&quot;extension&quot;: &quot;@kibana-highlighted-field.gz@/kibana-highlighted-field&quot;
expect(component).toMatchInlineSnapshot(`
<JsonCodeEditor
json={
Object {
"_id": "1",
"_index": "test",
"_score": 1,
"_source": undefined,
"_type": "test",
"fields": Object {
"bytes": Array [
100,
],
"extension": Array [
".gz",
],
},
"highlight": Object {
"extension": "@kibana-highlighted-field.gz@/kibana-highlighted-field",
},
}
}
}</span>"
width={370}
/>
`);
});

View file

@ -19,6 +19,8 @@ import {
import { IndexPattern } from '../../../kibana_services';
import { ElasticSearchHit } from '../../doc_views/doc_views_types';
import { DiscoverGridContext } from './discover_grid_context';
import { JsonCodeEditor } from '../json_code_editor/json_code_editor';
import { defaultMonacoEditorWidth } from './constants';
export const getRenderCellValueFn = (
indexPattern: IndexPattern,
@ -26,7 +28,7 @@ export const getRenderCellValueFn = (
rowsFlattened: Array<Record<string, unknown>>,
useNewFieldsApi: boolean
) => ({ rowIndex, columnId, isDetails, setCellProps }: EuiDataGridCellValueElementProps) => {
const row = rows ? (rows[rowIndex] as Record<string, unknown>) : undefined;
const row = rows ? rows[rowIndex] : undefined;
const rowFlattened = rowsFlattened
? (rowsFlattened[rowIndex] as Record<string, unknown>)
: undefined;
@ -106,10 +108,18 @@ export const getRenderCellValueFn = (
);
}
if (typeof rowFlattened[columnId] === 'object' && isDetails) {
return (
<JsonCodeEditor
json={rowFlattened[columnId] as Record<string, any>}
width={defaultMonacoEditorWidth}
/>
);
}
if (field && field.type === '_source') {
if (isDetails) {
// nicely formatted JSON for the expanded view
return <span>{JSON.stringify(row, null, 2)}</span>;
return <JsonCodeEditor json={row} width={defaultMonacoEditorWidth} />;
}
const formatted = indexPattern.formatHit(row);

View file

@ -6,9 +6,7 @@ exports[`returns the \`JsonCodeEditor\` component 1`] = `
direction="column"
gutterSize="s"
>
<EuiFlexItem
grow={true}
>
<EuiFlexItem>
<EuiSpacer
size="s"
/>
@ -31,9 +29,7 @@ exports[`returns the \`JsonCodeEditor\` component 1`] = `
</EuiCopy>
</div>
</EuiFlexItem>
<EuiFlexItem
grow={true}
>
<EuiFlexItem>
<CodeEditor
aria-label="Read only JSON view of an elasticsearch document"
editorDidMount={[Function]}
@ -43,6 +39,7 @@ exports[`returns the \`JsonCodeEditor\` component 1`] = `
Object {
"automaticLayout": true,
"fontSize": 12,
"lineNumbers": "off",
"minimap": Object {
"enabled": false,
},

View file

@ -18,5 +18,5 @@ it('returns the `JsonCodeEditor` component', () => {
_score: 1,
_source: { test: 123 },
};
expect(shallow(<JsonCodeEditor hit={value} />)).toMatchSnapshot();
expect(shallow(<JsonCodeEditor json={value} />)).toMatchSnapshot();
});

View file

@ -13,7 +13,6 @@ import { i18n } from '@kbn/i18n';
import { monaco, XJsonLang } from '@kbn/monaco';
import { EuiButtonEmpty, EuiCopy, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { CodeEditor } from '../../../../../kibana_react/public';
import { DocViewRenderProps } from '../../../application/doc_views/doc_views_types';
const codeEditorAriaLabel = i18n.translate('discover.json.codeEditorAriaLabel', {
defaultMessage: 'Read only JSON view of an elasticsearch document',
@ -22,8 +21,14 @@ const copyToClipboardLabel = i18n.translate('discover.json.copyToClipboardLabel'
defaultMessage: 'Copy to clipboard',
});
export const JsonCodeEditor = ({ hit }: DocViewRenderProps) => {
const jsonValue = JSON.stringify(hit, null, 2);
interface JsonCodeEditorProps {
json: Record<string, any>;
width?: string | number;
hasLineNumbers?: boolean;
}
export const JsonCodeEditor = ({ json, width, hasLineNumbers }: JsonCodeEditorProps) => {
const jsonValue = JSON.stringify(json, null, 2);
// setting editor height based on lines height and count to stretch and fit its content
const setEditorCalculatedHeight = useCallback((editor) => {
@ -43,7 +48,7 @@ export const JsonCodeEditor = ({ hit }: DocViewRenderProps) => {
return (
<EuiFlexGroup className="dscJsonCodeEditor" direction="column" gutterSize="s">
<EuiFlexItem grow={true}>
<EuiFlexItem>
<EuiSpacer size="s" />
<div className="eui-textRight">
<EuiCopy textToCopy={jsonValue}>
@ -55,9 +60,10 @@ export const JsonCodeEditor = ({ hit }: DocViewRenderProps) => {
</EuiCopy>
</div>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<EuiFlexItem>
<CodeEditor
languageId={XJsonLang.ID}
width={width}
value={jsonValue}
onChange={() => {}}
editorDidMount={setEditorCalculatedHeight}
@ -65,6 +71,7 @@ export const JsonCodeEditor = ({ hit }: DocViewRenderProps) => {
options={{
automaticLayout: true,
fontSize: 12,
lineNumbers: hasLineNumbers ? 'on' : 'off',
minimap: {
enabled: false,
},

View file

@ -7,6 +7,7 @@
*/
import { i18n } from '@kbn/i18n';
import React from 'react';
import angular, { auto } from 'angular';
import { BehaviorSubject } from 'rxjs';
import { filter, map } from 'rxjs/operators';
@ -187,7 +188,7 @@ export class DiscoverPlugin
defaultMessage: 'JSON',
}),
order: 20,
component: JsonCodeEditor,
component: ({ hit }) => <JsonCodeEditor json={hit} hasLineNumbers />,
});
const {

View file

@ -10,11 +10,13 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const find = getService('find');
const dataGrid = getService('dataGrid');
const log = getService('log');
const retry = getService('retry');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const monacoEditor = getService('monacoEditor');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']);
const defaultSettings = {
defaultIndex: 'logstash-*',
@ -56,6 +58,24 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.timePicker.setDefaultAbsoluteRange();
});
it('should show popover with expanded cell content by click on expand button', async () => {
log.debug('open popover with expanded cell content to get json from the editor');
const documentCell = await dataGrid.getCellElement(1, 3);
await documentCell.click();
const expandCellContentButton = await documentCell.findByClassName(
'euiDataGridRowCell__expandButtonIcon'
);
await expandCellContentButton.click();
const popoverJson = await monacoEditor.getCodeEditorValue();
log.debug('open expanded document flyout to get json');
await dataGrid.clickRowToggle();
await find.clickByCssSelectorWhenNotDisabled('#kbn_doc_viewer_tab_1');
const flyoutJson = await monacoEditor.getCodeEditorValue();
expect(popoverJson).to.be(flyoutJson);
});
describe('expand a document row', function () {
const rowToInspect = 1;

View file

@ -14,6 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const log = getService('log');
const inspector = getService('inspector');
const filterBar = getService('filterBar');
const monacoEditor = getService('monacoEditor');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['visualize', 'visEditor', 'visChart', 'timePicker']);
@ -42,7 +43,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await inspector.openInspectorRequestsView();
const requestTab = await inspector.getOpenRequestDetailRequestButton();
await requestTab.click();
const requestJSON = JSON.parse(await inspector.getCodeEditorValue());
const requestJSON = JSON.parse(await monacoEditor.getCodeEditorValue());
expect(requestJSON.aggs['2'].max).property('missing', 10);
});

View file

@ -14,6 +14,7 @@ export function TileMapPageProvider({ getService, getPageObjects }: FtrProviderC
const retry = getService('retry');
const log = getService('log');
const inspector = getService('inspector');
const monacoEditor = getService('monacoEditor');
const { header } = getPageObjects(['header']);
class TileMapPage {
@ -40,7 +41,7 @@ export function TileMapPageProvider({ getService, getPageObjects }: FtrProviderC
await testSubjects.click('inspectorViewChooserRequests');
await testSubjects.click('inspectorRequestDetailRequest');
return await inspector.getCodeEditorValue();
return await monacoEditor.getCodeEditorValue();
}
public async getMapBounds(): Promise<object> {

View file

@ -46,6 +46,7 @@ import { ListingTableProvider } from './listing_table';
import { SavedQueryManagementComponentProvider } from './saved_query_management_component';
import { KibanaSupertestProvider } from './supertest';
import { MenuToggleProvider } from './menu_toggle';
import { MonacoEditorProvider } from './monaco_editor';
export const services = {
...commonServiceProviders,
@ -81,5 +82,6 @@ export const services = {
elasticChart: ElasticChartProvider,
supertest: KibanaSupertestProvider,
managementMenu: ManagementMenuProvider,
monacoEditor: MonacoEditorProvider,
MenuToggle: MenuToggleProvider,
};

View file

@ -12,7 +12,6 @@ import { FtrProviderContext } from '../ftr_provider_context';
export function InspectorProvider({ getService }: FtrProviderContext) {
const log = getService('log');
const retry = getService('retry');
const browser = getService('browser');
const renderable = getService('renderable');
const flyout = getService('flyout');
const testSubjects = getService('testSubjects');
@ -235,18 +234,6 @@ export function InspectorProvider({ getService }: FtrProviderContext) {
public getOpenRequestDetailResponseButton() {
return testSubjects.find('inspectorRequestDetailResponse');
}
public async getCodeEditorValue() {
let request: string = '';
await retry.try(async () => {
request = await browser.execute(
() => (window as any).MonacoEnvironment.monaco.editor.getModels()[0].getValue() as string
);
});
return request;
}
}
return new Inspector();

View file

@ -0,0 +1,28 @@
/*
* 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 { FtrProviderContext } from '../ftr_provider_context';
export function MonacoEditorProvider({ getService }: FtrProviderContext) {
const retry = getService('retry');
const browser = getService('browser');
return new (class MonacoEditor {
public async getCodeEditorValue() {
let request: string = '';
await retry.try(async () => {
request = await browser.execute(
() => (window as any).MonacoEnvironment.monaco.editor.getModels()[0].getValue() as string
);
});
return request;
}
})();
}

View file

@ -10,6 +10,7 @@ import expect from '@kbn/expect';
export default function ({ getPageObjects, getService }) {
const PageObjects = getPageObjects(['maps']);
const inspector = getService('inspector');
const monacoEditor = getService('monacoEditor');
const testSubjects = getService('testSubjects');
const security = getService('security');
@ -27,7 +28,7 @@ export default function ({ getPageObjects, getService }) {
await inspector.open();
await inspector.openInspectorRequestsView();
await testSubjects.click('inspectorRequestDetailResponse');
const responseBody = await inspector.getCodeEditorValue();
const responseBody = await monacoEditor.getCodeEditorValue();
await inspector.close();
return JSON.parse(responseBody);
}