Skip to content

Commit

Permalink
[Logs UI] Allow for jumping to the previous and next highlight (#40010)
Browse files Browse the repository at this point in the history
This PR adds buttons to the highlighting popover that allow for jumping to the previous and next highlights. The intended user experience should be close to the "find" experience of many text editors.
  • Loading branch information
Kerry350 authored and weltenwort committed Jul 24, 2019
1 parent b5fff51 commit 5e26720
Show file tree
Hide file tree
Showing 30 changed files with 6,308 additions and 131 deletions.
7 changes: 6 additions & 1 deletion x-pack/legacy/plugins/infra/common/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,14 @@ export interface InfraTimeKeyInput {

tiebreaker: number;
}

/** A highlighting definition */
export interface InfraLogEntryHighlightInput {
/** The query to highlight by */
query: string;
/** The number of highlighted documents to include beyond the beginning of the interval */
countBefore: number;
/** The number of highlighted documents to include beyond the end of the interval */
countAfter: number;
}

export interface InfraTimerangeInput {
Expand Down
4 changes: 4 additions & 0 deletions x-pack/legacy/plugins/infra/common/time/time_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export interface TimeKey {
gid?: string;
}

export interface UniqueTimeKey extends TimeKey {
gid: string;
}

export type Comparator = (firstValue: any, secondValue: any) => number;

export const isTimeKey = (value: any): value is TimeKey =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,20 @@ interface LogHighlightsMenuProps {
onChange: (highlightTerms: string[]) => void;
isLoading: boolean;
activeHighlights: boolean;
hasPreviousHighlight: boolean;
hasNextHighlight: boolean;
goToPreviousHighlight: () => void;
goToNextHighlight: () => void;
}

export const LogHighlightsMenu: React.FC<LogHighlightsMenuProps> = ({
onChange,
isLoading,
activeHighlights,
hasPreviousHighlight,
goToPreviousHighlight,
hasNextHighlight,
goToNextHighlight,
}) => {
const {
isVisible: isPopoverOpen,
Expand Down Expand Up @@ -62,7 +70,6 @@ export const LogHighlightsMenu: React.FC<LogHighlightsMenuProps> = ({
{activeHighlights ? <ActiveHighlightsIndicator /> : null}
</EuiButtonEmpty>
);

return (
<EuiPopover
id="popover"
Expand All @@ -83,10 +90,29 @@ export const LogHighlightsMenu: React.FC<LogHighlightsMenuProps> = ({
aria-label={termsFieldLabel}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
aria-label={goToPreviousHighlightLabel}
iconType="arrowUp"
onClick={goToPreviousHighlight}
title={goToPreviousHighlightLabel}
isDisabled={!hasPreviousHighlight}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
aria-label={goToNextHighlightLabel}
iconType="arrowDown"
onClick={goToNextHighlight}
title={goToNextHighlightLabel}
isDisabled={!hasNextHighlight}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
aria-label={clearTermsButtonLabel}
color="danger"
isDisabled={highlightTerm === ''}
iconType="trash"
onClick={clearHighlightTerm}
title={clearTermsButtonLabel}
Expand All @@ -109,6 +135,20 @@ const clearTermsButtonLabel = i18n.translate(
}
);

const goToPreviousHighlightLabel = i18n.translate(
'xpack.infra.logs.highlights.goToPreviousHighlightButtonLabel',
{
defaultMessage: 'Jump to previous highlight',
}
);

const goToNextHighlightLabel = i18n.translate(
'xpack.infra.logs.highlights.goToNextHighlightButtonLabel',
{
defaultMessage: 'Jump to next highlight',
}
);

const ActiveHighlightsIndicator = euiStyled(EuiIcon).attrs({
type: 'checkInCircleFilled',
size: 'm',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,32 @@
import React from 'react';

import euiStyled from '../../../../../../common/eui_styled_components';
import { tintOrShade } from '../../../utils/styles';
import { chooseLightOrDarkColor, tintOrShade } from '../../../utils/styles';

export const ActiveHighlightMarker = euiStyled.mark`
color: ${props =>
chooseLightOrDarkColor(
props.theme.eui.euiColorAccent,
props.theme.eui.euiColorEmptyShade,
props.theme.eui.euiColorDarkestShade
)};
background-color: ${props => props.theme.eui.euiColorAccent};
outline: 1px solid ${props => props.theme.eui.euiColorAccent};
};
`;

export const HighlightMarker = euiStyled.mark`
color: ${props =>
chooseLightOrDarkColor(
tintOrShade(props.theme.eui.euiTextColor, props.theme.eui.euiColorAccent, 0.7, 0.5),
props.theme.eui.euiColorEmptyShade,
props.theme.eui.euiColorDarkestShade
)};
background-color: ${props =>
tintOrShade(props.theme.eui.euiTextColor, props.theme.eui.euiColorAccent, 0.7, 0.5)};
outline: 1px solid ${props =>
tintOrShade(props.theme.eui.euiTextColor, props.theme.eui.euiColorAccent, 0.7, 0.5)};
};
`;

export const highlightFieldValue = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('LogEntryFieldColumn', () => {
<LogEntryFieldColumn
columnValue={column}
highlights={[]}
isActiveHighlight={false}
isHighlighted={false}
isHovered={false}
isWrapped={false}
Expand Down Expand Up @@ -50,6 +51,7 @@ describe('LogEntryFieldColumn', () => {
<LogEntryFieldColumn
columnValue={column}
highlights={[]}
isActiveHighlight={false}
isHighlighted={false}
isHovered={false}
isWrapped={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import {
LogEntryColumn,
LogEntryHighlightColumn,
} from '../../../utils/log_entry';
import { highlightFieldValue, HighlightMarker } from './highlighting';
import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './highlighting';
import { LogEntryColumnContent } from './log_entry_column';

interface LogEntryFieldColumnProps {
columnValue: LogEntryColumn;
highlights: LogEntryHighlightColumn[];
isActiveHighlight: boolean;
isHighlighted: boolean;
isHovered: boolean;
isWrapped: boolean;
Expand All @@ -28,6 +29,7 @@ interface LogEntryFieldColumnProps {
export const LogEntryFieldColumn: React.FunctionComponent<LogEntryFieldColumnProps> = ({
columnValue,
highlights: [firstHighlight], // we only support one highlight for now
isActiveHighlight,
isHighlighted,
isHovered,
isWrapped,
Expand All @@ -42,7 +44,7 @@ export const LogEntryFieldColumn: React.FunctionComponent<LogEntryFieldColumnPro
{highlightFieldValue(
entry,
isHighlightFieldColumn(firstHighlight) ? firstHighlight.highlights : [],
HighlightMarker
isActiveHighlight ? ActiveHighlightMarker : HighlightMarker
)}
</CommaSeparatedLi>
))}
Expand All @@ -51,7 +53,7 @@ export const LogEntryFieldColumn: React.FunctionComponent<LogEntryFieldColumnPro
highlightFieldValue(
value,
isHighlightFieldColumn(firstHighlight) ? firstHighlight.highlights : [],
HighlightMarker
isActiveHighlight ? ActiveHighlightMarker : HighlightMarker
)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,27 @@ import {
LogEntryHighlightColumn,
LogEntryMessageSegment,
} from '../../../utils/log_entry';
import { highlightFieldValue, HighlightMarker } from './highlighting';
import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './highlighting';
import { LogEntryColumnContent } from './log_entry_column';
import { hoveredContentStyle } from './text_styles';

interface LogEntryMessageColumnProps {
columnValue: LogEntryColumn;
highlights: LogEntryHighlightColumn[];
isActiveHighlight: boolean;
isHighlighted: boolean;
isHovered: boolean;
isWrapped: boolean;
}

export const LogEntryMessageColumn = memo<LogEntryMessageColumnProps>(
({ columnValue, highlights, isHighlighted, isHovered, isWrapped }) => {
({ columnValue, highlights, isActiveHighlight, isHighlighted, isHovered, isWrapped }) => {
const message = useMemo(
() =>
isMessageColumn(columnValue)
? formatMessageSegments(columnValue.message, highlights)
? formatMessageSegments(columnValue.message, highlights, isActiveHighlight)
: null,
[columnValue, highlights]
[columnValue, highlights, isActiveHighlight]
);

return (
Expand Down Expand Up @@ -75,23 +76,30 @@ const MessageColumnContent = LogEntryColumnContent.extend.attrs<{

const formatMessageSegments = (
messageSegments: LogEntryMessageSegment[],
highlights: LogEntryHighlightColumn[]
highlights: LogEntryHighlightColumn[],
isActiveHighlight: boolean
) =>
messageSegments.map((messageSegment, index) =>
formatMessageSegment(
messageSegment,
highlights.map(highlight =>
isHighlightMessageColumn(highlight) ? highlight.message[index].highlights : []
)
),
isActiveHighlight
)
);

const formatMessageSegment = (
messageSegment: LogEntryMessageSegment,
[firstHighlight = []]: string[][] // we only support one highlight for now
[firstHighlight = []]: string[][], // we only support one highlight for now
isActiveHighlight: boolean
): React.ReactNode => {
if (isFieldSegment(messageSegment)) {
return highlightFieldValue(messageSegment.value, firstHighlight, HighlightMarker);
return highlightFieldValue(
messageSegment.value,
firstHighlight,
isActiveHighlight ? ActiveHighlightMarker : HighlightMarker
);
} else if (isConstantSegment(messageSegment)) {
return messageSegment.constant;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ interface LogEntryRowProps {
columnConfigurations: LogColumnConfiguration[];
columnWidths: LogEntryColumnWidths;
highlights: LogEntryHighlight[];
isActiveHighlight: boolean;
isHighlighted: boolean;
logEntry: LogEntry;
openFlyoutWithItem: (id: string) => void;
Expand All @@ -45,6 +46,7 @@ export const LogEntryRow = ({
columnConfigurations,
columnWidths,
highlights,
isActiveHighlight,
isHighlighted,
logEntry,
openFlyoutWithItem,
Expand Down Expand Up @@ -144,6 +146,7 @@ export const LogEntryRow = ({
columnValue={column}
highlights={highlightsByColumnId[column.columnId] || []}
isHighlighted={isHighlighted}
isActiveHighlight={isActiveHighlight}
isHovered={isHovered}
isWrapped={wrap}
/>
Expand All @@ -164,6 +167,7 @@ export const LogEntryRow = ({
<LogEntryFieldColumn
columnValue={column}
highlights={highlightsByColumnId[column.columnId] || []}
isActiveHighlight={isActiveHighlight}
isHighlighted={isHighlighted}
isHovered={isHovered}
isWrapped={wrap}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import React, { useMemo } from 'react';

import euiStyled from '../../../../../../common/eui_styled_components';
import { TextScale } from '../../../../common/log_text_scale';
import { TimeKey } from '../../../../common/time';
import { TimeKey, UniqueTimeKey } from '../../../../common/time';
import { callWithoutRepeats } from '../../../utils/handlers';
import { LogColumnConfiguration } from '../../../utils/source_configuration';
import { AutoSizer } from '../../auto_sizer';
Expand Down Expand Up @@ -51,6 +51,7 @@ interface ScrollableLogTextStreamViewProps {
showColumnConfiguration: () => void;
intl: InjectedIntl;
highlightedItem: string | null;
currentHighlightKey: UniqueTimeKey | null;
}

interface ScrollableLogTextStreamViewState {
Expand Down Expand Up @@ -97,6 +98,7 @@ class ScrollableLogTextStreamViewClass extends React.PureComponent<
public render() {
const {
columnConfigurations,
currentHighlightKey,
hasMoreAfterEnd,
hasMoreBeforeStart,
highlightedItem,
Expand Down Expand Up @@ -187,6 +189,10 @@ class ScrollableLogTextStreamViewClass extends React.PureComponent<
boundingBoxRef={itemMeasureRef}
logEntry={item.logEntry}
highlights={item.highlights}
isActiveHighlight={
!!currentHighlightKey &&
currentHighlightKey.gid === item.logEntry.gid
}
scale={scale}
wrap={wrap}
isHighlighted={
Expand Down
Loading

0 comments on commit 5e26720

Please sign in to comment.