mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Discover] Highlight filtered values in badges for custom cell renderers (#213941)
## Summary Closes https://github.com/elastic/kibana/issues/213216 This PR adds the functionality to properly highlight filtered values within badges. Previously, the content was treated as `text` instead of `html`, which prevented the highlighted values from being displayed correctly. The content is now rendered with the `<mark>` tag, allowing matching values to be properly highlighted within the badges. >[!NOTE] >By looking at the code I assumed the `<mark>` tag is the only one we introduce, so the proposed solution only handles that. |Before|After| |-|-| ||| ### How to test - Make sure you are in a space with Observability as solution view - Select the "All logs" data view - Add any filter that matches the displayed badges value
This commit is contained in:
parent
26d84c4e13
commit
44d49a9501
7 changed files with 110 additions and 15 deletions
5
.github/CODEOWNERS
vendored
5
.github/CODEOWNERS
vendored
|
@ -1168,8 +1168,9 @@ src/platform/plugins/shared/discover/public/context_awareness/profile_providers/
|
|||
# TODO: this deprecation_logs folder should be owned by kibana management team after 9.0
|
||||
src/platform/plugins/shared/discover/public/context_awareness/profile_providers/common/deprecation_logs @elastic/kibana-data-discovery @elastic/kibana-core
|
||||
src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability @elastic/kibana-data-discovery @elastic/obs-ux-logs-team
|
||||
src/platform/plugins/shared/discover/public/context_awareness/profile_providers/traces_document_profile @elastic/obs-ux-infra_services-team
|
||||
src/platform/plugins/shared/discover/public/context_awareness/profile_providers/traces_data_source_profile @elastic/obs-ux-infra_services-team
|
||||
src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile @elastic/obs-ux-infra_services-team
|
||||
src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_data_source_profile @elastic/obs-ux-infra_services-team
|
||||
src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/observability_root_profile @elastic/obs-ux-logs-team @elastic/obs-ux-infra_services-team
|
||||
|
||||
# Platform Docs
|
||||
/x-pack/test_serverless/functional/test_suites/security/screenshot_creation/index.ts @elastic/platform-docs
|
||||
|
|
|
@ -36,6 +36,7 @@ import {
|
|||
filterOutText,
|
||||
openCellActionPopoverAriaText,
|
||||
} from './translations';
|
||||
import { truncateAndPreserveHighlightTags } from './utils';
|
||||
|
||||
interface CellActionsPopoverProps {
|
||||
onFilter?: DocViewFilterFn;
|
||||
|
@ -192,6 +193,8 @@ export function FieldBadgeWithActions({
|
|||
rawValue,
|
||||
color = 'hollow',
|
||||
}: FieldBadgeWithActionsPropsAndDependencies) {
|
||||
const MAX_LENGTH = 20;
|
||||
|
||||
return (
|
||||
<CellActionsPopover
|
||||
onFilter={onFilter}
|
||||
|
@ -201,19 +204,14 @@ export function FieldBadgeWithActions({
|
|||
renderValue={renderValue}
|
||||
renderPopoverTrigger={({ popoverTriggerProps }) => (
|
||||
<EuiBadge {...popoverTriggerProps} color={color} iconType={icon} iconSide="left">
|
||||
{truncateMiddle(value)}
|
||||
<span
|
||||
// eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: truncateAndPreserveHighlightTags(value, MAX_LENGTH),
|
||||
}}
|
||||
/>
|
||||
</EuiBadge>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const MAX_LENGTH = 20;
|
||||
|
||||
function truncateMiddle(value: string): string {
|
||||
if (value.length < MAX_LENGTH) {
|
||||
return value;
|
||||
}
|
||||
const halfLength = MAX_LENGTH / 2;
|
||||
return `${value.slice(0, halfLength)}...${value.slice(-halfLength)}`;
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ export const createResourceFields = ({
|
|||
fieldFormats,
|
||||
dataView,
|
||||
dataView.getFieldByName(name),
|
||||
'text'
|
||||
'html'
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
@ -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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export * from './truncate_preserve_highlight_tags';
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { truncateAndPreserveHighlightTags } from '.';
|
||||
|
||||
describe('truncateAndPreserveHighlightTags', () => {
|
||||
const MAX_LENGTH = 10;
|
||||
const SHORT_TEXT = 'short';
|
||||
const LONG_TEXT = 'Long text that needs truncation';
|
||||
|
||||
describe("when there aren't <mark> tags", () => {
|
||||
describe('and text is shorter than maxLength', () => {
|
||||
const result = truncateAndPreserveHighlightTags(SHORT_TEXT, MAX_LENGTH);
|
||||
|
||||
it('should return the original string', () => {
|
||||
expect(result).toBe(SHORT_TEXT);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and text is longer than or equal to maxLength', () => {
|
||||
const result = truncateAndPreserveHighlightTags(LONG_TEXT, MAX_LENGTH);
|
||||
|
||||
it('should truncate the middle of a long string ', () => {
|
||||
expect(result).toBe('Long ...ation');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are <mark> tags', () => {
|
||||
describe('and text is shorter than maxLength', () => {
|
||||
const result = truncateAndPreserveHighlightTags(`<mark>${SHORT_TEXT}</mark>`, MAX_LENGTH);
|
||||
|
||||
it('should return the original string with the tags', () => {
|
||||
expect(result).toBe(`<mark>${SHORT_TEXT}</mark>`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and text is longer than or equal to maxLength', () => {
|
||||
const result = truncateAndPreserveHighlightTags(`<mark>${LONG_TEXT}</mark>`, MAX_LENGTH);
|
||||
|
||||
it('should truncate the middle of a long string and add the tags ', () => {
|
||||
expect(result).toBe('<mark>Long ...ation</mark>');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
function extractTextAndMarkTags(html: string) {
|
||||
const markTags: string[] = [];
|
||||
const cleanText = html.replace(/<\/?mark>/g, (match) => {
|
||||
markTags.push(match);
|
||||
return '';
|
||||
});
|
||||
|
||||
return { cleanText, markTags };
|
||||
}
|
||||
export function truncateAndPreserveHighlightTags(value: string, maxLength: number): string {
|
||||
const { cleanText, markTags } = extractTextAndMarkTags(value);
|
||||
|
||||
if (cleanText.length < maxLength) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const halfLength = maxLength / 2;
|
||||
const truncatedText = `${cleanText.slice(0, halfLength)}...${cleanText.slice(-halfLength)}`;
|
||||
|
||||
if (markTags.length === 2) {
|
||||
return `${markTags[0]}${truncatedText}${markTags[1]}`;
|
||||
}
|
||||
|
||||
return truncatedText;
|
||||
}
|
|
@ -51,7 +51,7 @@ export const getServiceNameCell =
|
|||
props.fieldFormats,
|
||||
props.dataView,
|
||||
field,
|
||||
'text'
|
||||
'html'
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue