Skip to content

Commit

Permalink
Explore: live tail UI fixes and improvements (#19187)
Browse files Browse the repository at this point in the history
  • Loading branch information
aocenas committed Sep 20, 2019
1 parent 9feac77 commit bf24cbb
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 75 deletions.
14 changes: 13 additions & 1 deletion public/app/features/explore/Explore.tsx
@@ -1,6 +1,7 @@
// Libraries
import React, { ComponentClass } from 'react';
import { hot } from 'react-hot-loader';
import { css } from 'emotion';
// @ts-ignore
import { connect } from 'react-redux';
import { AutoSizer } from 'react-virtualized';
Expand Down Expand Up @@ -52,6 +53,16 @@ import { ErrorContainer } from './ErrorContainer';
import { scanStopAction } from './state/actionTypes';
import { ExploreGraphPanel } from './ExploreGraphPanel';

const getStyles = memoizeOne(() => {
return {
logsMain: css`
label: logsMain;
// Is needed for some transition animations to work.
position: relative;
`,
};
});

interface ExploreProps {
StartPage?: ComponentClass<ExploreStartPageProps>;
changeSize: typeof changeSize;
Expand Down Expand Up @@ -257,6 +268,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
queryResponse,
} = this.props;
const exploreClass = split ? 'explore explore-split' : 'explore';
const styles = getStyles();

return (
<div className={exploreClass} ref={this.getRef}>
Expand Down Expand Up @@ -284,7 +296,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
}

return (
<main className="m-t-2" style={{ width }}>
<main className={`m-t-2 ${styles.logsMain}`} style={{ width }}>
<ErrorBoundaryAlert>
{showingStartPage && (
<div className="grafana-info-box grafana-info-box--max-lg">
Expand Down
29 changes: 21 additions & 8 deletions public/app/features/explore/ExploreToolbar.tsx
Expand Up @@ -4,6 +4,7 @@ import { connect } from 'react-redux';
import { hot } from 'react-hot-loader';
import memoizeOne from 'memoize-one';
import classNames from 'classnames';
import { css } from 'emotion';

import { ExploreId, ExploreItemState, ExploreMode } from 'app/types/explore';
import {
Expand Down Expand Up @@ -39,6 +40,14 @@ import { LiveTailButton } from './LiveTailButton';
import { ResponsiveButton } from './ResponsiveButton';
import { RunButton } from './RunButton';

const getStyles = memoizeOne(() => {
return {
liveTailButtons: css`
margin-left: 10px;
`,
};
});

interface OwnProps {
exploreId: ExploreId;
onChangeTime: (range: RawTimeRange, changedByScanner?: boolean) => void;
Expand Down Expand Up @@ -132,6 +141,7 @@ export class UnConnectedExploreToolbar extends PureComponent<Props, {}> {

stopLive = () => {
const { exploreId } = this.props;
this.pauseLive();
// TODO referencing this from perspective of refresh picker when there is designated button for it now is not
// great. Needs another refactor.
this.props.changeRefreshIntervalAction({ exploreId, refreshInterval: RefreshPicker.offOption.value });
Expand Down Expand Up @@ -174,6 +184,7 @@ export class UnConnectedExploreToolbar extends PureComponent<Props, {}> {
originPanelId,
} = this.props;

const styles = getStyles();
const originDashboardIsEditable = Number.isInteger(originPanelId);
const panelReturnClasses = classNames('btn', 'navbar-button', {
'btn--radius-right-0': originDashboardIsEditable,
Expand Down Expand Up @@ -293,14 +304,16 @@ export class UnConnectedExploreToolbar extends PureComponent<Props, {}> {
</div>

{hasLiveOption && (
<LiveTailButton
isLive={isLive}
isPaused={isPaused}
start={this.startLive}
pause={this.pauseLive}
resume={this.resumeLive}
stop={this.stopLive}
/>
<div className={`explore-toolbar-content-item ${styles.liveTailButtons}`}>
<LiveTailButton
isLive={isLive}
isPaused={isPaused}
start={this.startLive}
pause={this.pauseLive}
resume={this.resumeLive}
stop={this.stopLive}
/>
</div>
)}
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions public/app/features/explore/LiveLogs.tsx
Expand Up @@ -157,7 +157,7 @@ class LiveLogs extends PureComponent<Props, State> {
const { logsRow, logsRowLocalTime, logsRowMessage } = getLogRowStyles(theme);

return (
<>
<div>
<div
onScroll={isPaused ? undefined : this.onScroll}
className={cx(['logs-rows', styles.logsRowsLive])}
Expand Down Expand Up @@ -210,7 +210,7 @@ class LiveLogs extends PureComponent<Props, State> {
</span>
)}
</div>
</>
</div>
);
}
}
Expand Down
85 changes: 62 additions & 23 deletions public/app/features/explore/LiveTailButton.tsx
Expand Up @@ -2,28 +2,30 @@ import React from 'react';
import classNames from 'classnames';
import { css } from 'emotion';
import memoizeOne from 'memoize-one';
import { GrafanaTheme, GrafanaThemeType, useTheme } from '@grafana/ui';
import tinycolor from 'tinycolor2';
import { CSSTransition } from 'react-transition-group';

const orangeDark = '#FF780A';
const orangeDarkLighter = tinycolor(orangeDark)
.lighten(10)
.toString();
const orangeLight = '#ED5700';
const orangeLightLighter = tinycolor(orangeLight)
.lighten(10)
.toString();
import { GrafanaTheme, GrafanaThemeType, useTheme } from '@grafana/ui';

const getStyles = memoizeOne((theme: GrafanaTheme) => {
const orange = theme.type === GrafanaThemeType.Dark ? orangeDark : orangeLight;
const orangeLighter = theme.type === GrafanaThemeType.Dark ? orangeDarkLighter : orangeLightLighter;
const textColor = theme.type === GrafanaThemeType.Dark ? theme.colors.white : theme.colors.black;
const orange = theme.type === GrafanaThemeType.Dark ? '#FF780A' : '#ED5700';
const orangeLighter = tinycolor(orange)
.lighten(10)
.toString();
const pulseTextColor = tinycolor(orange)
.desaturate(90)
.toString();

return {
noRightBorderStyle: css`
label: noRightBorderStyle;
border-right: 0;
`,
liveButton: css`
label: liveButton;
transition: background-color 1s, border-color 1s, color 1s;
margin: 0;
`,
isLive: css`
label: isLive;
border-color: ${orange};
Expand All @@ -43,7 +45,7 @@ const getStyles = memoizeOne((theme: GrafanaTheme) => {
label: isPaused;
border-color: ${orange};
background: transparent;
animation: pulse 2s ease-out 0s infinite normal forwards;
animation: pulse 3s ease-out 0s infinite normal forwards;
&:focus {
border-color: ${orange};
}
Expand All @@ -53,16 +55,40 @@ const getStyles = memoizeOne((theme: GrafanaTheme) => {
}
@keyframes pulse {
0% {
color: ${textColor};
color: ${pulseTextColor};
}
50% {
color: ${orange};
}
100% {
color: ${textColor};
color: ${pulseTextColor};
}
}
`,
stopButtonEnter: css`
label: stopButtonEnter;
width: 0;
opacity: 0;
overflow: hidden;
`,
stopButtonEnterActive: css`
label: stopButtonEnterActive;
opacity: 1;
width: 32px;
transition: opacity 500ms ease-in 50ms, width 500ms ease-in 50ms;
`,
stopButtonExit: css`
label: stopButtonExit;
width: 32px;
opacity: 1;
overflow: hidden;
`,
stopButtonExitActive: css`
label: stopButtonExitActive;
opacity: 0;
width: 0;
transition: opacity 500ms ease-in 50ms, width 500ms ease-in 50ms;
`,
};
});

Expand All @@ -82,9 +108,9 @@ export function LiveTailButton(props: LiveTailButtonProps) {
const onClickMain = isLive ? (isPaused ? resume : pause) : start;

return (
<div className="explore-toolbar-content-item">
<>
<button
className={classNames('btn navbar-button', {
className={classNames('btn navbar-button', styles.liveButton, {
[`btn--radius-right-0 ${styles.noRightBorderStyle}`]: isLive,
[styles.isLive]: isLive && !isPaused,
[styles.isPaused]: isLive && isPaused,
Expand All @@ -94,11 +120,24 @@ export function LiveTailButton(props: LiveTailButtonProps) {
<i className={classNames('fa', isPaused || !isLive ? 'fa-play' : 'fa-pause')} />
&nbsp; Live tailing
</button>
{isLive && (
<button className={`btn navbar-button navbar-button--attached ${styles.isLive}`} onClick={stop}>
<i className={'fa fa-stop'} />
</button>
)}
</div>
<CSSTransition
mountOnEnter={true}
unmountOnExit={true}
timeout={500}
in={isLive}
classNames={{
enter: styles.stopButtonEnter,
enterActive: styles.stopButtonEnterActive,
exit: styles.stopButtonExit,
exitActive: styles.stopButtonExitActive,
}}
>
<div>
<button className={`btn navbar-button navbar-button--attached ${styles.isLive}`} onClick={stop}>
<i className={'fa fa-stop'} />
</button>
</div>
</CSSTransition>
</>
);
}
1 change: 0 additions & 1 deletion public/app/features/explore/Logs.tsx
@@ -1,4 +1,3 @@
import _ from 'lodash';
import React, { PureComponent } from 'react';

import {
Expand Down
75 changes: 39 additions & 36 deletions public/app/features/explore/LogsContainer.tsx
Expand Up @@ -27,6 +27,7 @@ import { deduplicatedLogsSelector, exploreItemUIStateSelector } from 'app/featur
import { getTimeZone } from '../profile/state/selectors';
import { LiveLogsWithTheme } from './LiveLogs';
import { Logs } from './Logs';
import { LogsCrossFadeTransition } from './utils/LogsCrossFadeTransition';

interface LogsContainerProps {
datasourceInstance: DataSourceApi | null;
Expand Down Expand Up @@ -64,6 +65,7 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {

onStopLive = () => {
const { exploreId } = this.props;
this.onPause();
this.props.stopLive({ exploreId, refreshInterval: RefreshPicker.offOption.value });
};

Expand Down Expand Up @@ -116,43 +118,44 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
isLive,
} = this.props;

if (isLive) {
return (
<Collapse label="Logs" loading={false} isOpen>
<LiveLogsWithTheme
logsResult={logsResult}
timeZone={timeZone}
stopLive={this.onStopLive}
isPaused={this.props.isPaused}
onPause={this.onPause}
onResume={this.onResume}
/>
</Collapse>
);
}

return (
<Collapse label="Logs" loading={loading} isOpen>
<Logs
dedupStrategy={this.props.dedupStrategy || LogsDedupStrategy.none}
data={logsResult}
dedupedData={dedupedResult}
highlighterExpressions={logsHighlighterExpressions}
loading={loading}
onChangeTime={this.onChangeTime}
onClickLabel={onClickLabel}
onStartScanning={onStartScanning}
onStopScanning={onStopScanning}
onDedupStrategyChange={this.handleDedupStrategyChange}
onToggleLogLevel={this.handleToggleLogLevel}
absoluteRange={absoluteRange}
timeZone={timeZone}
scanning={scanning}
scanRange={range.raw}
width={width}
getRowContext={this.getLogRowContext}
/>
</Collapse>
<>
<LogsCrossFadeTransition visible={isLive}>
<Collapse label="Logs" loading={false} isOpen>
<LiveLogsWithTheme
logsResult={logsResult}
timeZone={timeZone}
stopLive={this.onStopLive}
isPaused={this.props.isPaused}
onPause={this.onPause}
onResume={this.onResume}
/>
</Collapse>
</LogsCrossFadeTransition>
<LogsCrossFadeTransition visible={!isLive}>
<Collapse label="Logs" loading={loading} isOpen>
<Logs
dedupStrategy={this.props.dedupStrategy || LogsDedupStrategy.none}
data={logsResult}
dedupedData={dedupedResult}
highlighterExpressions={logsHighlighterExpressions}
loading={loading}
onChangeTime={this.onChangeTime}
onClickLabel={onClickLabel}
onStartScanning={onStartScanning}
onStopScanning={onStopScanning}
onDedupStrategyChange={this.handleDedupStrategyChange}
onToggleLogLevel={this.handleToggleLogLevel}
absoluteRange={absoluteRange}
timeZone={timeZone}
scanning={scanning}
scanRange={range.raw}
width={width}
getRowContext={this.getLogRowContext}
/>
</Collapse>
</LogsCrossFadeTransition>
</>
);
}
}
Expand Down
14 changes: 12 additions & 2 deletions public/app/features/explore/ResponsiveButton.tsx
Expand Up @@ -28,9 +28,19 @@ export const ResponsiveButton = (props: Props) => {
onClick={onClick}
disabled={disabled || false}
>
{iconClassName && iconSide === IconSide.left ? <i className={`${iconClassName}`} /> : null}
{iconClassName && iconSide === IconSide.left ? (
<>
<i className={`${iconClassName}`} />
&nbsp;
</>
) : null}
<span className="btn-title">{!splitted ? title : ''}</span>
{iconClassName && iconSide === IconSide.right ? <i className={`${iconClassName}`} /> : null}
{iconClassName && iconSide === IconSide.right ? (
<>
&nbsp;
<i className={`${iconClassName}`} />
</>
) : null}
</button>
);
};

0 comments on commit bf24cbb

Please sign in to comment.