Skip to content

Commit 54112a2

Browse files
committed
Improve log settings experience
Switch from the OverflowMenu to a custom Popover to improve the user experience of toggling log levels. The OverflowMenu approach closes the menu on each change requiring multiple additional clicks for each log level the user wishes to toggle. With the Popover we can now keep the menu open until explicitly dismissed, meaning users can much more easily make multiple changes. Also display a notice at the start of the logs if lines are hidden due to the currently selected log levels.
1 parent 2803ac6 commit 54112a2

File tree

11 files changed

+260
-86
lines changed

11 files changed

+260
-86
lines changed

packages/components/src/components/Log/Log.jsx

Lines changed: 82 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2019-2024 The Tekton Authors
2+
Copyright 2019-2025 The Tekton Authors
33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.
55
You may obtain a copy of the License at
@@ -14,9 +14,9 @@ limitations under the License.
1414
import { Component, createRef } from 'react';
1515
import { Button, PrefixContext, SkeletonText } from '@carbon/react';
1616
import { FixedSizeList as List } from 'react-window';
17-
import { injectIntl } from 'react-intl';
17+
import { injectIntl, useIntl } from 'react-intl';
1818
import { getStepStatusReason, isRunning } from '@tektoncd/dashboard-utils';
19-
import { DownToBottom, UpToTop } from '@carbon/react/icons';
19+
import { DownToBottom, Information, UpToTop } from '@carbon/react/icons';
2020

2121
import {
2222
hasElementPositiveVerticalScrollBottom,
@@ -33,6 +33,52 @@ const defaultHeight = itemSize * 100 + itemSize / 2;
3333
const logFormatRegex =
3434
/^((?<timestamp>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3,9}Z)\s?)?(::(?<level>error|warning|info|notice|debug)::)?(?<message>.*)?$/s;
3535

36+
function LogsFilteredNotification({ displayedLogLines, totalLogLines }) {
37+
const intl = useIntl();
38+
39+
if (displayedLogLines === totalLogLines) {
40+
return null;
41+
}
42+
43+
if (displayedLogLines === 0) {
44+
return (
45+
<span className="tkn--log-filtered">
46+
<Information />{' '}
47+
{intl.formatMessage({
48+
id: 'dashboard.logs.hidden.all',
49+
defaultMessage: 'All lines hidden due to selected log levels'
50+
})}
51+
</span>
52+
);
53+
}
54+
55+
const hiddenLines = totalLogLines - displayedLogLines;
56+
const message =
57+
hiddenLines === 1
58+
? intl.formatMessage(
59+
{
60+
id: 'dashboard.logs.hidden.one',
61+
defaultMessage: '1 line hidden due to selected log levels'
62+
},
63+
{ numHiddenLines: totalLogLines - displayedLogLines }
64+
)
65+
: intl.formatMessage(
66+
{
67+
id: 'dashboard.logs.hidden',
68+
defaultMessage:
69+
'{numHiddenLines, plural, other {# lines}} hidden due to selected log levels'
70+
},
71+
{ numHiddenLines: totalLogLines - displayedLogLines }
72+
);
73+
74+
return (
75+
<span className="tkn--log-filtered">
76+
<Information />
77+
{message}
78+
</span>
79+
);
80+
}
81+
3682
export class LogContainer extends Component {
3783
constructor(props) {
3884
super(props);
@@ -294,12 +340,19 @@ export class LogContainer extends Component {
294340
}
295341
return acc;
296342
}, []);
343+
297344
if (parsedLogs.length < 20_000) {
298345
return (
299-
<LogFormat
300-
fields={{ level: showLevels, timestamp: showTimestamps }}
301-
logs={parsedLogs}
302-
/>
346+
<>
347+
<LogsFilteredNotification
348+
displayedLogLines={parsedLogs.length}
349+
totalLogLines={logs.length}
350+
/>
351+
<LogFormat
352+
fields={{ level: showLevels, timestamp: showTimestamps }}
353+
logs={parsedLogs}
354+
/>
355+
</>
303356
);
304357
}
305358

@@ -308,22 +361,28 @@ export class LogContainer extends Component {
308361
: defaultHeight;
309362

310363
return (
311-
<List
312-
height={height}
313-
itemCount={parsedLogs.length}
314-
itemData={parsedLogs}
315-
itemSize={itemSize}
316-
width="100%"
317-
>
318-
{({ data, index, style }) => (
319-
<div style={style}>
320-
<LogFormat
321-
fields={{ level: showLevels, timestamp: showTimestamps }}
322-
logs={[data[index]]}
323-
/>
324-
</div>
325-
)}
326-
</List>
364+
<>
365+
<LogsFilteredNotification
366+
displayedLogLines={parsedLogs.length}
367+
totalLogLines={logs.length}
368+
/>
369+
<List
370+
height={height}
371+
itemCount={parsedLogs.length}
372+
itemData={parsedLogs}
373+
itemSize={itemSize}
374+
width="100%"
375+
>
376+
{({ data, index, style }) => (
377+
<div style={style}>
378+
<LogFormat
379+
fields={{ level: showLevels, timestamp: showTimestamps }}
380+
logs={[data[index]]}
381+
/>
382+
</div>
383+
)}
384+
</List>
385+
</>
327386
);
328387
};
329388

packages/components/src/components/Log/Log.stories.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2019-2024 The Tekton Authors
2+
Copyright 2019-2025 The Tekton Authors
33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.
55
You may obtain a copy of the License at
@@ -135,6 +135,7 @@ export const Toolbar = {
135135
{...args}
136136
toolbar={
137137
<LogsToolbar
138+
id="logs-toolbar"
138139
logLevels={args.showLevels ? args.logLevels : null}
139140
name="step_log_filename.txt"
140141
onToggleLogLevel={logLevel =>

packages/components/src/components/Log/_Log.scss

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2019-2024 The Tekton Authors
2+
Copyright 2019-2025 The Tekton Authors
33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.
55
You may obtain a copy of the License at
@@ -29,7 +29,7 @@ pre.tkn--log {
2929
}
3030

3131
line-height: 1rem; // Update the react-window List itemSize if changing this
32-
overflow: hidden;
32+
// overflow: hidden; // TODO: logs - verify it's ok to remove this as it's interfering with the popover
3333
background-color: $background;
3434
color: $text-primary;
3535

@@ -95,7 +95,6 @@ pre.tkn--log {
9595
block-size: 2rem;
9696
min-block-size: 2rem;
9797
background-color: $background;
98-
9998
&:hover {
10099
background-color: $layer-hover;
101100
}
@@ -124,9 +123,33 @@ pre.tkn--log {
124123

125124
.tkn--log-container {
126125
overflow-x: auto;
126+
127+
.tkn--log-filtered {
128+
display: flex;
129+
margin-block-end: 1rem;
130+
131+
svg {
132+
margin-inline-end: 0.5rem;
133+
}
134+
}
127135
}
128136

129-
.tkn--log-container:not(:empty) + .tkn--log-trailer {
137+
.tkn--log-container:has(code:not(:empty)) + .tkn--log-trailer {
130138
margin-block-start: 1rem;
131139
}
140+
141+
.tkn--log-settings-menu-content {
142+
padding-block-end: .5rem;
143+
padding-block-start: 1rem;
144+
padding-inline: 1rem;
145+
font-family: 'IBM Plex Sans', sans-serif;
146+
color: $text-secondary;
147+
148+
hr {
149+
border: none;
150+
margin-block: 1rem;
151+
background: $border-subtle;
152+
block-size: 1px;
153+
}
154+
}
132155
}

0 commit comments

Comments
 (0)