[Logs UI] Log stream row rendering (#60773)

This commit is contained in:
Alejandro Fernández 2020-03-31 13:12:44 +02:00 committed by GitHub
parent 65e8f2b603
commit 4632f3ef85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 131 additions and 166 deletions

View file

@ -0,0 +1,98 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useCallback } from 'react';
import { EuiButtonIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { LogEntryColumnContent } from './log_entry_column';
import {
euiStyled,
ActionMenu,
Section,
SectionTitle,
SectionLinks,
SectionLink,
} from '../../../../../observability/public';
interface LogEntryActionsColumnProps {
isHovered: boolean;
isMenuOpen: boolean;
onOpenMenu: () => void;
onCloseMenu: () => void;
onViewDetails: () => void;
}
const MENU_LABEL = i18n.translate('xpack.infra.logEntryItemView.logEntryActionsMenuToolTip', {
defaultMessage: 'View Details',
});
const LOG_DETAILS_LABEL = i18n.translate('xpack.infra.logs.logEntryActionsDetailsButton', {
defaultMessage: 'View actions for line',
});
export const LogEntryActionsColumn: React.FC<LogEntryActionsColumnProps> = ({
isHovered,
isMenuOpen,
onOpenMenu,
onCloseMenu,
onViewDetails,
}) => {
const handleClickViewDetails = useCallback(() => {
onCloseMenu();
onViewDetails();
}, [onCloseMenu, onViewDetails]);
const button = (
<ButtonWrapper>
<EuiButtonIcon
aria-label={MENU_LABEL}
color="ghost"
iconType="boxesHorizontal"
onClick={onOpenMenu}
/>
</ButtonWrapper>
);
return (
<ActionsColumnContent>
{isHovered || isMenuOpen ? (
<AbsoluteWrapper>
<ActionMenu closePopover={onCloseMenu} isOpen={isMenuOpen} button={button}>
<Section>
<SectionTitle>
<FormattedMessage
id="xpack.infra.logs.logEntryActionsMenuTitle"
defaultMessage="Log line details"
/>
</SectionTitle>
<SectionLinks>
<SectionLink label={LOG_DETAILS_LABEL} onClick={handleClickViewDetails} />
</SectionLinks>
</Section>
</ActionMenu>
</AbsoluteWrapper>
) : null}
</ActionsColumnContent>
);
};
const ActionsColumnContent = euiStyled(LogEntryColumnContent)`
overflow: hidden;
user-select: none;
`;
const ButtonWrapper = euiStyled.div`
background: ${props => props.theme.eui.euiColorPrimary};
border-radius: 50%;
`;
// this prevents the button from influencing the line height
const AbsoluteWrapper = euiStyled.div`
overflow: hidden;
position: absolute;
`;

View file

@ -25,8 +25,6 @@ describe('LogEntryFieldColumn', () => {
columnValue={column}
highlights={[]}
isActiveHighlight={false}
isHighlighted={false}
isHovered={false}
wrapMode="pre-wrapped"
/>,
{ wrappingComponent: EuiThemeProvider } as any // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/36075
@ -58,8 +56,6 @@ describe('LogEntryFieldColumn', () => {
columnValue={column}
highlights={[]}
isActiveHighlight={false}
isHighlighted={false}
isHovered={false}
wrapMode="pre-wrapped"
/>,
{ wrappingComponent: EuiThemeProvider } as any // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/36075
@ -81,8 +77,6 @@ describe('LogEntryFieldColumn', () => {
columnValue={column}
highlights={[]}
isActiveHighlight={false}
isHighlighted={false}
isHovered={false}
wrapMode="pre-wrapped"
/>,
{ wrappingComponent: EuiThemeProvider } as any // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/36075

View file

@ -13,7 +13,6 @@ import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './h
import { LogEntryColumnContent } from './log_entry_column';
import { LogColumn } from '../../../../common/http_api';
import {
hoveredContentStyle,
longWrappedContentStyle,
preWrappedContentStyle,
unwrappedContentStyle,
@ -24,8 +23,6 @@ interface LogEntryFieldColumnProps {
columnValue: LogColumn;
highlights: LogColumn[];
isActiveHighlight: boolean;
isHighlighted: boolean;
isHovered: boolean;
wrapMode: WrapMode;
}
@ -33,8 +30,6 @@ export const LogEntryFieldColumn: React.FunctionComponent<LogEntryFieldColumnPro
columnValue,
highlights: [firstHighlight], // we only support one highlight for now
isActiveHighlight,
isHighlighted,
isHovered,
wrapMode,
}) => {
const value = useMemo(() => {
@ -63,11 +58,7 @@ export const LogEntryFieldColumn: React.FunctionComponent<LogEntryFieldColumnPro
)
);
return (
<FieldColumnContent isHighlighted={isHighlighted} isHovered={isHovered} wrapMode={wrapMode}>
{formattedValue}
</FieldColumnContent>
);
return <FieldColumnContent wrapMode={wrapMode}>{formattedValue}</FieldColumnContent>;
};
const CommaSeparatedLi = euiStyled.li`
@ -81,15 +72,12 @@ const CommaSeparatedLi = euiStyled.li`
`;
interface LogEntryColumnContentProps {
isHighlighted: boolean;
isHovered: boolean;
wrapMode: WrapMode;
}
const FieldColumnContent = euiStyled(LogEntryColumnContent)<LogEntryColumnContentProps>`
text-overflow: ellipsis;
${props => (props.isHovered || props.isHighlighted ? hoveredContentStyle : '')};
${props =>
props.wrapMode === 'long'
? longWrappedContentStyle

View file

@ -1,67 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiButtonIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { LogEntryColumnContent } from './log_entry_column';
import { hoveredContentStyle } from './text_styles';
import { euiStyled } from '../../../../../observability/public';
interface LogEntryIconColumnProps {
isHighlighted: boolean;
isHovered: boolean;
}
export const LogEntryIconColumn: React.FunctionComponent<LogEntryIconColumnProps> = ({
children,
isHighlighted,
isHovered,
}) => {
return (
<IconColumnContent isHighlighted={isHighlighted} isHovered={isHovered}>
{children}
</IconColumnContent>
);
};
export const LogEntryDetailsIconColumn: React.FunctionComponent<LogEntryIconColumnProps & {
openFlyout: () => void;
}> = ({ isHighlighted, isHovered, openFlyout }) => {
const label = i18n.translate('xpack.infra.logEntryItemView.viewDetailsToolTip', {
defaultMessage: 'View Details',
});
return (
<LogEntryIconColumn isHighlighted={isHighlighted} isHovered={isHovered}>
{isHovered ? (
<AbsoluteIconButtonWrapper>
<EuiButtonIcon onClick={openFlyout} iconType="expand" title={label} aria-label={label} />
</AbsoluteIconButtonWrapper>
) : null}
</LogEntryIconColumn>
);
};
interface IconColumnContentProps {
isHighlighted: boolean;
isHovered: boolean;
}
const IconColumnContent = euiStyled(LogEntryColumnContent)<IconColumnContentProps>`
background-color: ${props => props.theme.eui.euiColorEmptyShade};
overflow: hidden;
user-select: none;
${props => (props.isHovered || props.isHighlighted ? hoveredContentStyle : '')};
`;
// this prevents the button from influencing the line height
const AbsoluteIconButtonWrapper = euiStyled.div`
overflow: hidden;
position: absolute;
`;

View file

@ -18,7 +18,6 @@ import {
import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './highlighting';
import { LogEntryColumnContent } from './log_entry_column';
import {
hoveredContentStyle,
longWrappedContentStyle,
preWrappedContentStyle,
unwrappedContentStyle,
@ -30,13 +29,11 @@ interface LogEntryMessageColumnProps {
columnValue: LogColumn;
highlights: LogColumn[];
isActiveHighlight: boolean;
isHighlighted: boolean;
isHovered: boolean;
wrapMode: WrapMode;
}
export const LogEntryMessageColumn = memo<LogEntryMessageColumnProps>(
({ columnValue, highlights, isActiveHighlight, isHighlighted, isHovered, wrapMode }) => {
({ columnValue, highlights, isActiveHighlight, wrapMode }) => {
const message = useMemo(
() =>
isMessageColumn(columnValue)
@ -45,24 +42,16 @@ export const LogEntryMessageColumn = memo<LogEntryMessageColumnProps>(
[columnValue, highlights, isActiveHighlight]
);
return (
<MessageColumnContent isHighlighted={isHighlighted} isHovered={isHovered} wrapMode={wrapMode}>
{message}
</MessageColumnContent>
);
return <MessageColumnContent wrapMode={wrapMode}>{message}</MessageColumnContent>;
}
);
interface MessageColumnContentProps {
isHovered: boolean;
isHighlighted: boolean;
wrapMode: WrapMode;
}
const MessageColumnContent = euiStyled(LogEntryColumnContent)<MessageColumnContentProps>`
text-overflow: ellipsis;
${props => (props.isHovered || props.isHighlighted ? hoveredContentStyle : '')};
${props =>
props.wrapMode === 'long'
? longWrappedContentStyle

View file

@ -17,10 +17,10 @@ import {
import { TextScale } from '../../../../common/log_text_scale';
import { LogEntryColumn, LogEntryColumnWidths, iconColumnId } from './log_entry_column';
import { LogEntryFieldColumn } from './log_entry_field_column';
import { LogEntryDetailsIconColumn } from './log_entry_icon_column';
import { LogEntryActionsColumn } from './log_entry_actions_column';
import { LogEntryMessageColumn } from './log_entry_message_column';
import { LogEntryTimestampColumn } from './log_entry_timestamp_column';
import { monospaceTextStyle } from './text_styles';
import { monospaceTextStyle, hoveredContentStyle, highlightedContentStyle } from './text_styles';
import { LogEntry, LogColumn } from '../../../../common/http_api';
interface LogEntryRowProps {
@ -50,14 +50,13 @@ export const LogEntryRow = memo(
wrap,
}: LogEntryRowProps) => {
const [isHovered, setIsHovered] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const setItemIsHovered = useCallback(() => {
setIsHovered(true);
}, []);
const openMenu = useCallback(() => setIsMenuOpen(true), []);
const closeMenu = useCallback(() => setIsMenuOpen(false), []);
const setItemIsNotHovered = useCallback(() => {
setIsHovered(false);
}, []);
const setItemIsHovered = useCallback(() => setIsHovered(true), []);
const setItemIsNotHovered = useCallback(() => setIsHovered(false), []);
const openFlyout = useCallback(() => openFlyoutWithItem?.(logEntry.id), [
openFlyoutWithItem,
@ -105,6 +104,7 @@ export const LogEntryRow = memo(
}
onMouseEnter={setItemIsHovered}
onMouseLeave={setItemIsNotHovered}
isHighlighted={isHighlighted}
scale={scale}
>
{columnConfigurations.map(columnConfiguration => {
@ -119,11 +119,7 @@ export const LogEntryRow = memo(
{...columnWidth}
>
{isTimestampColumn(column) ? (
<LogEntryTimestampColumn
isHighlighted={isHighlighted}
isHovered={isHovered}
time={column.timestamp}
/>
<LogEntryTimestampColumn time={column.timestamp} />
) : null}
</LogEntryColumn>
);
@ -141,9 +137,7 @@ export const LogEntryRow = memo(
<LogEntryMessageColumn
columnValue={column}
highlights={highlightsByColumnId[column.columnId] || []}
isHighlighted={isHighlighted}
isActiveHighlight={isActiveHighlight}
isHovered={isHovered}
wrapMode={wrap ? 'long' : 'pre-wrapped'}
/>
) : null}
@ -164,8 +158,6 @@ export const LogEntryRow = memo(
columnValue={column}
highlights={highlightsByColumnId[column.columnId] || []}
isActiveHighlight={isActiveHighlight}
isHighlighted={isHighlighted}
isHovered={isHovered}
wrapMode={wrap ? 'long' : 'pre-wrapped'}
/>
) : null}
@ -177,10 +169,12 @@ export const LogEntryRow = memo(
key="logColumn iconLogColumn iconLogColumn:details"
{...columnWidths[iconColumnId]}
>
<LogEntryDetailsIconColumn
isHighlighted={isHighlighted}
<LogEntryActionsColumn
isHovered={isHovered}
openFlyout={openFlyout}
isMenuOpen={isMenuOpen}
onOpenMenu={openMenu}
onCloseMenu={closeMenu}
onViewDetails={openFlyout}
/>
</LogEntryColumn>
</LogEntryRowWrapper>
@ -190,6 +184,7 @@ export const LogEntryRow = memo(
interface LogEntryRowWrapperProps {
scale: TextScale;
isHighlighted?: boolean;
}
export const LogEntryRowWrapper = euiStyled.div.attrs(() => ({
@ -204,4 +199,9 @@ export const LogEntryRowWrapper = euiStyled.div.attrs(() => ({
overflow: hidden;
${props => monospaceTextStyle(props.scale)};
${props => (props.isHighlighted ? highlightedContentStyle : '')}
&:hover {
${hoveredContentStyle}
}
`;

View file

@ -4,54 +4,28 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { darken, transparentize } from 'polished';
import React, { memo } from 'react';
import { euiStyled, css } from '../../../../../observability/public';
import { euiStyled } from '../../../../../observability/public';
import { TimeFormat, useFormattedTime } from '../../formatted_time';
import { LogEntryColumnContent } from './log_entry_column';
interface LogEntryTimestampColumnProps {
format?: TimeFormat;
isHighlighted: boolean;
isHovered: boolean;
time: number;
}
export const LogEntryTimestampColumn = memo<LogEntryTimestampColumnProps>(
({ format = 'time', isHighlighted, isHovered, time }) => {
({ format = 'time', time }) => {
const formattedTime = useFormattedTime(time, { format });
return (
<TimestampColumnContent isHovered={isHovered} isHighlighted={isHighlighted}>
{formattedTime}
</TimestampColumnContent>
);
return <TimestampColumnContent>{formattedTime}</TimestampColumnContent>;
}
);
const hoveredContentStyle = css`
background-color: ${props =>
props.theme.darkMode
? transparentize(0.9, darken(0.05, props.theme.eui.euiColorHighlight))
: darken(0.05, props.theme.eui.euiColorHighlight)};
border-color: ${props =>
props.theme.darkMode
? transparentize(0.7, darken(0.2, props.theme.eui.euiColorHighlight))
: darken(0.2, props.theme.eui.euiColorHighlight)};
color: ${props => props.theme.eui.euiColorFullShade};
`;
interface TimestampColumnContentProps {
isHovered: boolean;
isHighlighted: boolean;
}
const TimestampColumnContent = euiStyled(LogEntryColumnContent)<TimestampColumnContentProps>`
const TimestampColumnContent = euiStyled(LogEntryColumnContent)`
color: ${props => props.theme.eui.euiColorDarkShade};
overflow: hidden;
text-overflow: clip;
white-space: pre;
${props => (props.isHovered || props.isHighlighted ? hoveredContentStyle : '')};
`;

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { darken, transparentize } from 'polished';
import React, { useMemo, useState, useCallback } from 'react';
import { euiStyled, css } from '../../../../../observability/public';
@ -30,10 +29,11 @@ export const monospaceTextStyle = (scale: TextScale) => css`
`;
export const hoveredContentStyle = css`
background-color: ${props =>
props.theme.darkMode
? transparentize(0.9, darken(0.05, props.theme.eui.euiColorHighlight))
: darken(0.05, props.theme.eui.euiColorHighlight)};
background-color: ${props => props.theme.eui.euiFocusBackgroundColor};
`;
export const highlightedContentStyle = css`
background-color: ${props => props.theme.eui.euiFocusBackgroundColor};
`;
export const longWrappedContentStyle = css`

View file

@ -34,12 +34,7 @@ export const CategoryExampleMessage: React.FunctionComponent<{
return (
<LogEntryRowWrapper scale={exampleMessageScale}>
<LogEntryColumn {...columnWidths[timestampColumnId]}>
<LogEntryTimestampColumn
format={exampleTimestampFormat}
isHighlighted={false}
isHovered={false}
time={timestamp}
/>
<LogEntryTimestampColumn format={exampleTimestampFormat} time={timestamp} />
</LogEntryColumn>
<LogEntryColumn {...columnWidths[messageColumnId]}>
<LogEntryMessageColumn
@ -48,8 +43,6 @@ export const CategoryExampleMessage: React.FunctionComponent<{
message: [{ field: 'message', value: message, highlights: [] }],
}}
highlights={noHighlights}
isHovered={false}
isHighlighted={false}
isActiveHighlight={false}
wrapMode="none"
/>
@ -63,8 +56,6 @@ export const CategoryExampleMessage: React.FunctionComponent<{
highlights: [],
}}
highlights={noHighlights}
isHovered={false}
isHighlighted={false}
isActiveHighlight={false}
wrapMode="none"
/>

View file

@ -6303,7 +6303,6 @@
"xpack.infra.logEntryActionsMenu.apmActionLabel": "APMで表示",
"xpack.infra.logEntryActionsMenu.buttonLabel": "アクション",
"xpack.infra.logEntryActionsMenu.uptimeActionLabel": "監視ステータスを表示",
"xpack.infra.logEntryItemView.viewDetailsToolTip": "詳細を表示",
"xpack.infra.logFlyout.fieldColumnLabel": "フィールド",
"xpack.infra.logFlyout.filterAriaLabel": "フィルター",
"xpack.infra.logFlyout.flyoutTitle": "ログイベントドキュメントの詳細",

View file

@ -6303,7 +6303,6 @@
"xpack.infra.logEntryActionsMenu.apmActionLabel": "在 APM 中查看",
"xpack.infra.logEntryActionsMenu.buttonLabel": "操作",
"xpack.infra.logEntryActionsMenu.uptimeActionLabel": "查看监测状态",
"xpack.infra.logEntryItemView.viewDetailsToolTip": "查看详情",
"xpack.infra.logFlyout.fieldColumnLabel": "字段",
"xpack.infra.logFlyout.filterAriaLabel": "筛选",
"xpack.infra.logFlyout.flyoutTitle": "日志事件文档详情",