mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[Discover] Format JSON messages in Observability Logs profile (#205666)](https://github.com/elastic/kibana/pull/205666) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Davis McPhee","email":"davis.mcphee@elastic.co"},"sourceCommit":{"committedDate":"2025-01-10T00:05:18Z","message":"[Discover] Format JSON messages in Observability Logs profile (#205666)\n\n## Summary\r\n\r\nThis PR updates the Observability Logs profile to detect and auto format\r\nJSON message values within both the Log overview doc viewer tab and the\r\nSummary cell popover. Additionally, it enables CTRL/CMD + F find\r\nfunctionality within the doc viewer JSON tab for all contexts to make it\r\neasier for users to search the JSON output.\r\n\r\nJSON message formatting:\r\n\r\n\r\n\r\nJSON tab find functionality:\r\n\r\n\r\n\r\n### Checklist\r\n\r\n- [ ] Any text added follows [EUI's writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\r\nsentence case text and includes [i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\r\n- [ ]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas added for features that require explanation or tutorials\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n- [ ] If a plugin configuration key changed, check if it needs to be\r\nallowlisted in the cloud and added to the [docker\r\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\r\n- [ ] This was checked for breaking HTTP API changes, and any breaking\r\nchanges have been approved by the breaking-change committee. The\r\n`release_note:breaking` label should be applied in these situations.\r\n- [ ] [Flaky Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\r\nused on any tests changed\r\n- [x] The PR description includes the appropriate Release Notes section,\r\nand the correct `release_note:*` label is applied per the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"518e0afbde3f0391d7c99bfbb1ef6252a2623d12","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:enhancement","Feature:Discover","v9.0.0","Team:DataDiscovery","backport:prev-minor","Team:obs-ux-logs"],"title":"[Discover] Format JSON messages in Observability Logs profile","number":205666,"url":"https://github.com/elastic/kibana/pull/205666","mergeCommit":{"message":"[Discover] Format JSON messages in Observability Logs profile (#205666)\n\n## Summary\r\n\r\nThis PR updates the Observability Logs profile to detect and auto format\r\nJSON message values within both the Log overview doc viewer tab and the\r\nSummary cell popover. Additionally, it enables CTRL/CMD + F find\r\nfunctionality within the doc viewer JSON tab for all contexts to make it\r\neasier for users to search the JSON output.\r\n\r\nJSON message formatting:\r\n\r\n\r\n\r\nJSON tab find functionality:\r\n\r\n\r\n\r\n### Checklist\r\n\r\n- [ ] Any text added follows [EUI's writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\r\nsentence case text and includes [i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\r\n- [ ]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas added for features that require explanation or tutorials\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n- [ ] If a plugin configuration key changed, check if it needs to be\r\nallowlisted in the cloud and added to the [docker\r\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\r\n- [ ] This was checked for breaking HTTP API changes, and any breaking\r\nchanges have been approved by the breaking-change committee. The\r\n`release_note:breaking` label should be applied in these situations.\r\n- [ ] [Flaky Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\r\nused on any tests changed\r\n- [x] The PR description includes the appropriate Release Notes section,\r\nand the correct `release_note:*` label is applied per the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"518e0afbde3f0391d7c99bfbb1ef6252a2623d12"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/205666","number":205666,"mergeCommit":{"message":"[Discover] Format JSON messages in Observability Logs profile (#205666)\n\n## Summary\r\n\r\nThis PR updates the Observability Logs profile to detect and auto format\r\nJSON message values within both the Log overview doc viewer tab and the\r\nSummary cell popover. Additionally, it enables CTRL/CMD + F find\r\nfunctionality within the doc viewer JSON tab for all contexts to make it\r\neasier for users to search the JSON output.\r\n\r\nJSON message formatting:\r\n\r\n\r\n\r\nJSON tab find functionality:\r\n\r\n\r\n\r\n### Checklist\r\n\r\n- [ ] Any text added follows [EUI's writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\r\nsentence case text and includes [i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\r\n- [ ]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas added for features that require explanation or tutorials\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n- [ ] If a plugin configuration key changed, check if it needs to be\r\nallowlisted in the cloud and added to the [docker\r\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\r\n- [ ] This was checked for breaking HTTP API changes, and any breaking\r\nchanges have been approved by the breaking-change committee. The\r\n`release_note:breaking` label should be applied in these situations.\r\n- [ ] [Flaky Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\r\nused on any tests changed\r\n- [x] The PR description includes the appropriate Release Notes section,\r\nand the correct `release_note:*` label is applied per the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"518e0afbde3f0391d7c99bfbb1ef6252a2623d12"}}]}] BACKPORT--> Co-authored-by: Davis McPhee <davis.mcphee@elastic.co>
This commit is contained in:
parent
73e60b1d49
commit
37b4a5c2ce
11 changed files with 192 additions and 92 deletions
|
@ -10,7 +10,12 @@
|
|||
import React from 'react';
|
||||
import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import SummaryColumn, { SummaryColumnFactoryDeps, SummaryColumnProps } from './summary_column';
|
||||
import SummaryColumn, {
|
||||
AllSummaryColumnProps,
|
||||
SummaryCellPopover,
|
||||
SummaryColumnFactoryDeps,
|
||||
SummaryColumnProps,
|
||||
} from './summary_column';
|
||||
import { DataGridDensity, ROWS_HEIGHT_OPTIONS } from '@kbn/unified-data-table';
|
||||
import * as constants from '@kbn/discover-utils/src/data_types/logs/constants';
|
||||
import { sharePluginMock } from '@kbn/share-plugin/public/mocks';
|
||||
|
@ -18,32 +23,46 @@ import { coreMock as corePluginMock } from '@kbn/core/public/mocks';
|
|||
import { DataTableRecord, buildDataTableRecord } from '@kbn/discover-utils';
|
||||
import { dataViewMock } from '@kbn/discover-utils/src/__mocks__/data_view';
|
||||
|
||||
jest.mock('@elastic/eui', () => ({
|
||||
...jest.requireActual('@elastic/eui'),
|
||||
EuiCodeBlock: ({
|
||||
children,
|
||||
dangerouslySetInnerHTML,
|
||||
}: {
|
||||
children?: string;
|
||||
dangerouslySetInnerHTML?: { __html: string };
|
||||
}) => <code data-test-subj="codeBlock">{children ?? dangerouslySetInnerHTML?.__html ?? ''}</code>,
|
||||
}));
|
||||
|
||||
const getSummaryProps = (
|
||||
record: DataTableRecord,
|
||||
opts: Partial<SummaryColumnProps & SummaryColumnFactoryDeps> = {}
|
||||
): AllSummaryColumnProps => ({
|
||||
rowIndex: 0,
|
||||
colIndex: 0,
|
||||
columnId: '_source',
|
||||
isExpandable: true,
|
||||
isExpanded: false,
|
||||
isDetails: false,
|
||||
row: record,
|
||||
dataView: dataViewMock,
|
||||
fieldFormats: fieldFormatsMock,
|
||||
setCellProps: () => {},
|
||||
closePopover: () => {},
|
||||
density: DataGridDensity.COMPACT,
|
||||
rowHeight: ROWS_HEIGHT_OPTIONS.single,
|
||||
onFilter: jest.fn(),
|
||||
shouldShowFieldHandler: () => true,
|
||||
core: corePluginMock.createStart(),
|
||||
share: sharePluginMock.createStartContract(),
|
||||
...opts,
|
||||
});
|
||||
|
||||
const renderSummary = (
|
||||
record: DataTableRecord,
|
||||
opts: Partial<SummaryColumnProps & SummaryColumnFactoryDeps> = {}
|
||||
) => {
|
||||
render(
|
||||
<SummaryColumn
|
||||
rowIndex={0}
|
||||
colIndex={0}
|
||||
columnId="_source"
|
||||
isExpandable={true}
|
||||
isExpanded={false}
|
||||
isDetails={false}
|
||||
row={record}
|
||||
dataView={dataViewMock}
|
||||
fieldFormats={fieldFormatsMock}
|
||||
setCellProps={() => {}}
|
||||
closePopover={() => {}}
|
||||
density={DataGridDensity.COMPACT}
|
||||
rowHeight={ROWS_HEIGHT_OPTIONS.single}
|
||||
onFilter={jest.fn()}
|
||||
shouldShowFieldHandler={() => true}
|
||||
core={corePluginMock.createStart()}
|
||||
share={sharePluginMock.createStartContract()}
|
||||
{...opts}
|
||||
/>
|
||||
);
|
||||
render(<SummaryColumn {...getSummaryProps(record, opts)} />);
|
||||
};
|
||||
|
||||
const getBaseRecord = (overrides: Record<string, unknown> = {}) =>
|
||||
|
@ -174,3 +193,18 @@ describe('SummaryColumn', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('SummaryCellPopover', () => {
|
||||
it('should render message value', async () => {
|
||||
const message = 'This is a message';
|
||||
render(<SummaryCellPopover {...getSummaryProps(getBaseRecord({ message }))} />);
|
||||
expect(screen.queryByTestId('codeBlock')?.innerHTML).toBe(message);
|
||||
});
|
||||
|
||||
it('should render formatted JSON message value', async () => {
|
||||
const json = { foo: { bar: true } };
|
||||
const message = JSON.stringify(json);
|
||||
render(<SummaryCellPopover {...getSummaryProps(getBaseRecord({ message }))} />);
|
||||
expect(screen.queryByTestId('codeBlock')?.innerHTML).toBe(JSON.stringify(json, null, 2));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -98,14 +98,19 @@ const SummaryCell = ({
|
|||
);
|
||||
};
|
||||
|
||||
const SummaryCellPopover = (props: AllSummaryColumnProps) => {
|
||||
export const SummaryCellPopover = (props: AllSummaryColumnProps) => {
|
||||
const { row, dataView, fieldFormats, onFilter, closePopover, share, core } = props;
|
||||
|
||||
const resourceFields = createResourceFields(row, core, share);
|
||||
const shouldRenderResource = resourceFields.length > 0;
|
||||
|
||||
const documentOverview = getLogDocumentOverview(row, { dataView, fieldFormats });
|
||||
const { field, value } = getMessageFieldWithFallbacks(documentOverview);
|
||||
const { field, value, formattedValue } = getMessageFieldWithFallbacks(documentOverview, {
|
||||
includeFormattedValue: true,
|
||||
});
|
||||
const messageCodeBlockProps = formattedValue
|
||||
? { language: 'json', children: formattedValue }
|
||||
: { language: 'txt', dangerouslySetInnerHTML: { __html: value ?? '' } };
|
||||
const shouldRenderContent = Boolean(field && value);
|
||||
|
||||
const shouldRenderSource = !shouldRenderContent;
|
||||
|
@ -142,11 +147,9 @@ const SummaryCellPopover = (props: AllSummaryColumnProps) => {
|
|||
overflowHeight={100}
|
||||
paddingSize="s"
|
||||
isCopyable
|
||||
language="txt"
|
||||
fontSize="s"
|
||||
>
|
||||
{value}
|
||||
</EuiCodeBlock>
|
||||
{...messageCodeBlockProps}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
{shouldRenderSource && (
|
||||
|
|
|
@ -7,10 +7,14 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { unescape } from 'lodash';
|
||||
import { fieldConstants } from '..';
|
||||
import { LogDocumentOverview } from '../types';
|
||||
|
||||
export const getMessageFieldWithFallbacks = (doc: LogDocumentOverview) => {
|
||||
export const getMessageFieldWithFallbacks = (
|
||||
doc: LogDocumentOverview,
|
||||
{ includeFormattedValue = false }: { includeFormattedValue?: boolean } = {}
|
||||
) => {
|
||||
const rankingOrder = [
|
||||
fieldConstants.MESSAGE_FIELD,
|
||||
fieldConstants.ERROR_MESSAGE_FIELD,
|
||||
|
@ -18,8 +22,20 @@ export const getMessageFieldWithFallbacks = (doc: LogDocumentOverview) => {
|
|||
] as const;
|
||||
|
||||
for (const rank of rankingOrder) {
|
||||
if (doc[rank] !== undefined && doc[rank] !== null) {
|
||||
return { field: rank, value: doc[rank] };
|
||||
const value = doc[rank];
|
||||
|
||||
if (value !== undefined && value !== null) {
|
||||
let formattedValue: string | undefined;
|
||||
|
||||
if (includeFormattedValue) {
|
||||
try {
|
||||
formattedValue = JSON.stringify(JSON.parse(unescape(value)), null, 2);
|
||||
} catch {
|
||||
// If the value is not a valid JSON, leave it unformatted
|
||||
}
|
||||
}
|
||||
|
||||
return { field: rank, value, formattedValue };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,17 @@ import { setUnifiedDocViewerServices } from '../../plugin';
|
|||
import { mockUnifiedDocViewerServices } from '../../__mocks__';
|
||||
import { merge } from 'lodash';
|
||||
|
||||
jest.mock('@elastic/eui', () => ({
|
||||
...jest.requireActual('@elastic/eui'),
|
||||
EuiCodeBlock: ({
|
||||
children,
|
||||
dangerouslySetInnerHTML,
|
||||
}: {
|
||||
children?: string;
|
||||
dangerouslySetInnerHTML?: { __html: string };
|
||||
}) => <code data-test-subj="codeBlock">{children ?? dangerouslySetInnerHTML?.__html ?? ''}</code>,
|
||||
}));
|
||||
|
||||
const DATASET_NAME = 'logs.overview';
|
||||
const NAMESPACE = 'default';
|
||||
const DATA_STREAM_NAME = `logs-${DATASET_NAME}-${NAMESPACE}`;
|
||||
|
@ -58,51 +69,55 @@ dataView.fields.getByName = (name: string) => {
|
|||
return dataView.fields.getAll().find((field) => field.name === name);
|
||||
};
|
||||
|
||||
const fullHit = buildDataTableRecord(
|
||||
{
|
||||
_index: DATA_STREAM_NAME,
|
||||
_id: DATA_STREAM_NAME,
|
||||
_score: 1,
|
||||
_source: {
|
||||
'@timestamp': NOW + 1000,
|
||||
message: 'full document',
|
||||
log: { level: 'info', file: { path: '/logs.overview.log' } },
|
||||
data_stream: {
|
||||
type: 'logs',
|
||||
dataset: DATASET_NAME,
|
||||
namespace: NAMESPACE,
|
||||
const buildHit = (fields?: Record<string, unknown>) =>
|
||||
buildDataTableRecord(
|
||||
{
|
||||
_index: DATA_STREAM_NAME,
|
||||
_id: DATA_STREAM_NAME,
|
||||
_score: 1,
|
||||
_source: {
|
||||
'@timestamp': NOW + 1000,
|
||||
message: 'full document',
|
||||
log: { level: 'info', file: { path: '/logs.overview.log' } },
|
||||
data_stream: {
|
||||
type: 'logs',
|
||||
dataset: DATASET_NAME,
|
||||
namespace: NAMESPACE,
|
||||
},
|
||||
'service.name': DATASET_NAME,
|
||||
'host.name': 'gke-edge-oblt-pool',
|
||||
'trace.id': 'abcdef',
|
||||
orchestrator: {
|
||||
cluster: {
|
||||
id: 'my-cluster-id',
|
||||
name: 'my-cluster-name',
|
||||
},
|
||||
resource: {
|
||||
id: 'orchestratorResourceId',
|
||||
},
|
||||
},
|
||||
cloud: {
|
||||
provider: ['gcp'],
|
||||
region: 'us-central-1',
|
||||
availability_zone: MORE_THAN_1024_CHARS,
|
||||
project: {
|
||||
id: 'elastic-project',
|
||||
},
|
||||
instance: {
|
||||
id: 'BgfderflkjTheUiGuy',
|
||||
},
|
||||
},
|
||||
'agent.name': 'node',
|
||||
...fields,
|
||||
},
|
||||
'service.name': DATASET_NAME,
|
||||
'host.name': 'gke-edge-oblt-pool',
|
||||
'trace.id': 'abcdef',
|
||||
orchestrator: {
|
||||
cluster: {
|
||||
id: 'my-cluster-id',
|
||||
name: 'my-cluster-name',
|
||||
},
|
||||
resource: {
|
||||
id: 'orchestratorResourceId',
|
||||
},
|
||||
ignored_field_values: {
|
||||
'cloud.availability_zone': [MORE_THAN_1024_CHARS],
|
||||
},
|
||||
cloud: {
|
||||
provider: ['gcp'],
|
||||
region: 'us-central-1',
|
||||
availability_zone: MORE_THAN_1024_CHARS,
|
||||
project: {
|
||||
id: 'elastic-project',
|
||||
},
|
||||
instance: {
|
||||
id: 'BgfderflkjTheUiGuy',
|
||||
},
|
||||
},
|
||||
'agent.name': 'node',
|
||||
},
|
||||
ignored_field_values: {
|
||||
'cloud.availability_zone': [MORE_THAN_1024_CHARS],
|
||||
},
|
||||
},
|
||||
dataView
|
||||
);
|
||||
dataView
|
||||
);
|
||||
|
||||
const fullHit = buildHit();
|
||||
|
||||
const getCustomUnifedDocViewerServices = (params?: {
|
||||
showApm: boolean;
|
||||
|
@ -306,3 +321,18 @@ describe('LogsOverview with APM links', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('LogsOverview content breakdown', () => {
|
||||
it('should render message value', async () => {
|
||||
const message = 'This is a message';
|
||||
renderLogsOverview({ hit: buildHit({ message }) });
|
||||
expect(screen.queryByTestId('codeBlock')?.innerHTML).toBe(message);
|
||||
});
|
||||
|
||||
it('should render formatted JSON message value', async () => {
|
||||
const json = { foo: { bar: true } };
|
||||
const message = JSON.stringify(json);
|
||||
renderLogsOverview({ hit: buildHit({ message }) });
|
||||
expect(screen.queryByTestId('codeBlock')?.innerHTML).toBe(JSON.stringify(json, null, 2));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -34,7 +34,12 @@ export const contentLabel = i18n.translate('unifiedDocViewer.docView.logsOvervie
|
|||
export function LogsOverviewHeader({ doc }: { doc: LogDocumentOverview }) {
|
||||
const hasLogLevel = Boolean(doc[fieldConstants.LOG_LEVEL_FIELD]);
|
||||
const hasTimestamp = Boolean(doc[fieldConstants.TIMESTAMP_FIELD]);
|
||||
const { field, value } = getMessageFieldWithFallbacks(doc);
|
||||
const { field, value, formattedValue } = getMessageFieldWithFallbacks(doc, {
|
||||
includeFormattedValue: true,
|
||||
});
|
||||
const messageCodeBlockProps = formattedValue
|
||||
? { language: 'json', children: formattedValue }
|
||||
: { language: 'txt', dangerouslySetInnerHTML: { __html: value ?? '' } };
|
||||
const hasBadges = hasTimestamp || hasLogLevel;
|
||||
const hasMessageField = field && value;
|
||||
const hasFlyoutHeader = hasMessageField || hasBadges;
|
||||
|
@ -80,14 +85,19 @@ export function LogsOverviewHeader({ doc }: { doc: LogDocumentOverview }) {
|
|||
</EuiText>
|
||||
<EuiFlexItem grow={false}>{logLevelAndTimestamp}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<HoverActionPopover value={value} field={field} anchorPosition="downCenter" display="block">
|
||||
<HoverActionPopover
|
||||
value={value}
|
||||
formattedValue={formattedValue}
|
||||
field={field}
|
||||
anchorPosition="downCenter"
|
||||
display="block"
|
||||
>
|
||||
<EuiCodeBlock
|
||||
overflowHeight={100}
|
||||
paddingSize="s"
|
||||
isCopyable
|
||||
language="txt"
|
||||
fontSize="s"
|
||||
dangerouslySetInnerHTML={{ __html: value }}
|
||||
{...messageCodeBlockProps}
|
||||
/>
|
||||
</HoverActionPopover>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -23,6 +23,7 @@ interface HoverPopoverActionProps {
|
|||
children: React.ReactChild;
|
||||
field: string;
|
||||
value: unknown;
|
||||
formattedValue?: string;
|
||||
title?: unknown;
|
||||
anchorPosition?: PopoverAnchorPosition;
|
||||
display?: EuiPopoverProps['display'];
|
||||
|
@ -33,12 +34,13 @@ export const HoverActionPopover = ({
|
|||
title,
|
||||
field,
|
||||
value,
|
||||
formattedValue,
|
||||
anchorPosition = 'upCenter',
|
||||
display = 'inline-block',
|
||||
}: HoverPopoverActionProps) => {
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||
const leaveTimer = useRef<NodeJS.Timeout | null>(null);
|
||||
const uiFieldActions = useUIFieldActions({ field, value });
|
||||
const uiFieldActions = useUIFieldActions({ field, value, formattedValue });
|
||||
|
||||
// The timeout hack is required because we are using a Popover which ideally should be used with a mouseclick,
|
||||
// but we are using it as a Tooltip. Which means we now need to manually handle the open and close
|
||||
|
|
|
@ -42,7 +42,6 @@ describe('Source Viewer component', () => {
|
|||
index={'index1'}
|
||||
dataView={mockDataView}
|
||||
width={123}
|
||||
hasLineNumbers={true}
|
||||
onRefresh={() => {}}
|
||||
/>
|
||||
);
|
||||
|
@ -59,7 +58,6 @@ describe('Source Viewer component', () => {
|
|||
index={'index1'}
|
||||
dataView={mockDataView}
|
||||
width={123}
|
||||
hasLineNumbers={true}
|
||||
onRefresh={() => {}}
|
||||
/>
|
||||
);
|
||||
|
@ -97,7 +95,6 @@ describe('Source Viewer component', () => {
|
|||
index={'index1'}
|
||||
dataView={mockDataView}
|
||||
width={123}
|
||||
hasLineNumbers={true}
|
||||
onRefresh={() => {}}
|
||||
/>
|
||||
);
|
||||
|
@ -105,5 +102,7 @@ describe('Source Viewer component', () => {
|
|||
expect(jsonCodeEditor).not.toBe(null);
|
||||
expect(jsonCodeEditor.props().jsonValue).toContain('_source');
|
||||
expect(jsonCodeEditor.props().jsonValue).not.toContain('_score');
|
||||
expect(jsonCodeEditor.props().hasLineNumbers).toBe(true);
|
||||
expect(jsonCodeEditor.props().enableFindAction).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -29,10 +29,8 @@ interface SourceViewerProps {
|
|||
index: string | undefined;
|
||||
dataView: DataView;
|
||||
textBasedHits?: DataTableRecord[];
|
||||
hasLineNumbers: boolean;
|
||||
width?: number;
|
||||
decreaseAvailableHeightBy?: number;
|
||||
requestState?: ElasticRequestState;
|
||||
onRefresh: () => void;
|
||||
}
|
||||
|
||||
|
@ -46,9 +44,8 @@ export const DocViewerSource = ({
|
|||
id,
|
||||
index,
|
||||
dataView,
|
||||
width,
|
||||
hasLineNumbers,
|
||||
textBasedHits,
|
||||
width,
|
||||
decreaseAvailableHeightBy,
|
||||
onRefresh,
|
||||
}: SourceViewerProps) => {
|
||||
|
@ -150,7 +147,8 @@ export const DocViewerSource = ({
|
|||
jsonValue={jsonValue}
|
||||
width={width}
|
||||
height={editorHeight}
|
||||
hasLineNumbers={hasLineNumbers}
|
||||
hasLineNumbers
|
||||
enableFindAction
|
||||
onEditorDidMount={(editorNode: monaco.editor.IStandaloneCodeEditor) => setEditor(editorNode)}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -29,6 +29,7 @@ interface JsonCodeEditorCommonProps {
|
|||
height?: string | number;
|
||||
hasLineNumbers?: boolean;
|
||||
hideCopyButton?: boolean;
|
||||
enableFindAction?: boolean;
|
||||
}
|
||||
|
||||
export const JsonCodeEditorCommon = ({
|
||||
|
@ -38,6 +39,7 @@ export const JsonCodeEditorCommon = ({
|
|||
hasLineNumbers,
|
||||
onEditorDidMount,
|
||||
hideCopyButton,
|
||||
enableFindAction,
|
||||
}: JsonCodeEditorCommonProps) => {
|
||||
if (jsonValue === '') {
|
||||
return null;
|
||||
|
@ -66,6 +68,7 @@ export const JsonCodeEditorCommon = ({
|
|||
wordWrap: 'on',
|
||||
wrappingIndent: 'indent',
|
||||
}}
|
||||
enableFindAction={enableFindAction}
|
||||
/>
|
||||
);
|
||||
if (hideCopyButton) {
|
||||
|
|
|
@ -21,7 +21,9 @@ interface WithValueParam {
|
|||
value: unknown;
|
||||
}
|
||||
|
||||
interface TFieldActionParams extends WithFieldParam, WithValueParam {}
|
||||
interface TFieldActionParams extends WithFieldParam, WithValueParam {
|
||||
formattedValue?: string;
|
||||
}
|
||||
|
||||
export interface TFieldAction {
|
||||
id: string;
|
||||
|
@ -66,7 +68,11 @@ export const [FieldActionsProvider, useFieldActionsContext] = createContainer(us
|
|||
/**
|
||||
* This is a preset of the UI elements and related actions that can be used to build an action bar anywhere in a DocView
|
||||
*/
|
||||
export const useUIFieldActions = ({ field, value }: TFieldActionParams): TFieldAction[] => {
|
||||
export const useUIFieldActions = ({
|
||||
field,
|
||||
value,
|
||||
formattedValue,
|
||||
}: TFieldActionParams): TFieldAction[] => {
|
||||
const actions = useFieldActionsContext();
|
||||
|
||||
return useMemo(
|
||||
|
@ -99,10 +105,10 @@ export const useUIFieldActions = ({ field, value }: TFieldActionParams): TFieldA
|
|||
id: 'copyToClipboardAction',
|
||||
iconType: 'copyClipboard',
|
||||
label: copyToClipboardLabel,
|
||||
onClick: () => actions.copyToClipboard(value as string),
|
||||
onClick: () => actions.copyToClipboard(formattedValue ?? (value as string)),
|
||||
},
|
||||
],
|
||||
[actions, field, value]
|
||||
[actions, field, formattedValue, value]
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -92,7 +92,6 @@ export class UnifiedDocViewerPublicPlugin
|
|||
id={hit.raw._id ?? hit.id}
|
||||
dataView={dataView}
|
||||
textBasedHits={textBasedHits}
|
||||
hasLineNumbers
|
||||
decreaseAvailableHeightBy={decreaseAvailableHeightBy}
|
||||
onRefresh={() => {}}
|
||||
/>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue