Skip to content

Commit 158cc5b

Browse files
AlanGreenetekton-robot
authored andcommitted
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 ff551f1 commit 158cc5b

24 files changed

+269
-120
lines changed

docs/logs.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The Tekton Dashboard log viewer supports ANSI colour codes and text styles, and
1515

1616
## Toolbar
1717

18-
The toolbar diplayed in the log viewer includes a number of additional features, including:
18+
The toolbar displayed in the log viewer includes a number of additional features, including:
1919

2020
- maximize: increase the area available to the log viewer by hiding the task list and run header. This allows the user to eliminate distractions from other parts of the app and focus on the log content.
2121
- open in new window: open the raw logs in a separate browser window. This provides an unmodified and unprocessed view of the logs, without any of the added features provided by the log viewer.
@@ -26,7 +26,7 @@ The toolbar diplayed in the log viewer includes a number of additional features,
2626

2727
The Dashboard will always request logs with timestamps from the Kubernetes pod logs API, and show / hide the timestamps in the log viewer based on the user's preference. This can be toggled from the settings menu in the toolbar at the top of the log viewer.
2828

29-
The timestamps are localised based on users' browser settings, with the raw timestamp value received from the API provided as a tooltip on hover.
29+
The timestamps are localised based on the user's browser settings, with the raw timestamp value received from the API provided as a tooltip on hover.
3030

3131
In releases prior to Tekton Dashboard v0.54, the timestamp preference was found on the Settings page, and governed whether or not timestamps were requested from the Kubernetes API server.
3232

package-lock.json

Lines changed: 25 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"test": "vitest"
2525
},
2626
"dependencies": {
27-
"@carbon/react": "^1.72.0",
27+
"@carbon/react": "^1.73.0",
2828
"@codemirror/legacy-modes": "^6.4.2",
2929
"@tanstack/react-query": "^4.36.1",
3030
"@tektoncd/dashboard-components": "*",

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: 26 additions & 3 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,6 @@ pre.tkn--log {
2929
}
3030

3131
line-height: 1rem; // Update the react-window List itemSize if changing this
32-
overflow: hidden;
3332
background-color: $background;
3433
color: $text-primary;
3534

@@ -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)