From 22f97f8b0b11f90d68ceae0dc19827ee8526ea0b Mon Sep 17 00:00:00 2001 From: Alan Greene Date: Fri, 15 May 2026 21:30:07 +0100 Subject: [PATCH] Remove tab layout feature flag and unused code The tab layout for TaskRun and PipelineRun details has been enabled for a number of releases now with no user-facing option to revert to the previous design. Remove the remaining feature flag and the code related to the old layout. Also restore the options in the individual log containers to view the raw logs in a new tab or to download them to a file. These are only enabled in the Dashboard application and are available on an opt-in basis to consumers of the UI components. --- .../DetailsHeader/_DetailsHeader.scss | 6 - .../components/src/components/Log/Log.jsx | 3 +- .../components/src/components/Log/_Log.scss | 34 +-- .../components/LogsToolbar/LogsToolbar.jsx | 110 ++----- .../LogsToolbar/LogsToolbar.stories.jsx | 26 +- .../components/PipelineRun/PipelineRun.jsx | 233 +++++--------- .../PipelineRun/PipelineRun.stories.jsx | 3 +- .../PipelineRun/PipelineRun.test.jsx | 49 +-- .../components/src/components/Step/Step.jsx | 127 -------- .../src/components/Step/Step.stories.js | 53 ---- .../src/components/Step/Step.test.jsx | 99 ------ .../components/src/components/Step/_Step.scss | 36 +-- .../components/src/components/Step/index.js | 15 - .../components/StepDetails/StepDetails.jsx | 113 ------- .../StepDetails/StepDetails.stories.jsx | 119 -------- .../StepDetails/StepDetails.test.jsx | 64 ---- .../StepLogToolbar/StepLogToolbar.jsx | 55 ++++ .../StepLogToolbar.stories.jsx} | 31 +- .../{StepDetails => StepLogToolbar}/index.js | 5 +- .../components/src/components/Task/Task.jsx | 280 ----------------- .../src/components/Task/Task.stories.jsx | 100 ------ .../src/components/Task/Task.test.jsx | 257 ---------------- .../components/src/components/Task/_Task.scss | 108 +------ .../components/src/components/Task/index.js | 14 - .../TaskRunDetails/TaskRunDetails.stories.jsx | 289 +++++++++--------- .../TaskRunDetails/_TaskRunDetails.scss | 6 +- .../components/TaskRunStep/TaskRunStep.jsx | 1 - .../src/components/TaskTree/TaskTree.jsx | 126 -------- .../components/TaskTree/TaskTree.stories.jsx | 127 -------- .../src/components/TaskTree/TaskTree.test.jsx | 175 ----------- .../src/components/TaskTree/_TaskTree.scss | 17 -- packages/components/src/components/index.js | 7 +- packages/components/src/scss/_Run.scss | 14 +- src/api/utils.js | 7 +- src/containers/LogsToolbar/LogsToolbar.jsx | 20 +- .../LogsToolbar/LogsToolbar.test.jsx | 55 +--- src/containers/PipelineRun/PipelineRun.jsx | 12 +- .../StepLogToolbar/StepLogToolbar.jsx | 42 +++ .../StepLogToolbar/StepLogToolbar.test.jsx | 80 +++++ .../containers/StepLogToolbar}/index.js | 4 +- src/containers/TaskRun/TaskRun.jsx | 199 ++++-------- src/nls/messages_de.json | 6 - src/nls/messages_en.json | 6 - src/nls/messages_es.json | 6 - src/nls/messages_fr.json | 6 - src/nls/messages_it.json | 6 - src/nls/messages_ja.json | 6 - src/nls/messages_ko.json | 6 - src/nls/messages_pt.json | 6 - src/nls/messages_zh-Hans.json | 6 - src/nls/messages_zh-Hant.json | 6 - src/scss/App.scss | 22 +- 52 files changed, 556 insertions(+), 2647 deletions(-) delete mode 100644 packages/components/src/components/Step/Step.jsx delete mode 100644 packages/components/src/components/Step/Step.stories.js delete mode 100644 packages/components/src/components/Step/Step.test.jsx delete mode 100644 packages/components/src/components/Step/index.js delete mode 100644 packages/components/src/components/StepDetails/StepDetails.jsx delete mode 100644 packages/components/src/components/StepDetails/StepDetails.stories.jsx delete mode 100644 packages/components/src/components/StepDetails/StepDetails.test.jsx create mode 100644 packages/components/src/components/StepLogToolbar/StepLogToolbar.jsx rename packages/components/src/components/{StepDetails/_StepDetails.scss => StepLogToolbar/StepLogToolbar.stories.jsx} (57%) rename packages/components/src/components/{StepDetails => StepLogToolbar}/index.js (86%) delete mode 100644 packages/components/src/components/Task/Task.jsx delete mode 100644 packages/components/src/components/Task/Task.stories.jsx delete mode 100644 packages/components/src/components/Task/Task.test.jsx delete mode 100644 packages/components/src/components/Task/index.js delete mode 100644 packages/components/src/components/TaskTree/TaskTree.jsx delete mode 100644 packages/components/src/components/TaskTree/TaskTree.stories.jsx delete mode 100644 packages/components/src/components/TaskTree/TaskTree.test.jsx delete mode 100644 packages/components/src/components/TaskTree/_TaskTree.scss create mode 100644 src/containers/StepLogToolbar/StepLogToolbar.jsx create mode 100644 src/containers/StepLogToolbar/StepLogToolbar.test.jsx rename {packages/components/src/components/TaskTree => src/containers/StepLogToolbar}/index.js (87%) diff --git a/packages/components/src/components/DetailsHeader/_DetailsHeader.scss b/packages/components/src/components/DetailsHeader/_DetailsHeader.scss index ca1a7ad71..df994919e 100644 --- a/packages/components/src/components/DetailsHeader/_DetailsHeader.scss +++ b/packages/components/src/components/DetailsHeader/_DetailsHeader.scss @@ -42,12 +42,6 @@ header.tkn--step-details-header { .#{$prefix}--btn-set { margin-inline: 0.5rem; align-self: center; - align-items: baseline; - - .cds--btn--icon-only { - inline-size: 2rem; - block-size: 2rem; - } } .tkn--run-details-name { diff --git a/packages/components/src/components/Log/Log.jsx b/packages/components/src/components/Log/Log.jsx index 9d3eeabf3..106d4aa77 100644 --- a/packages/components/src/components/Log/Log.jsx +++ b/packages/components/src/components/Log/Log.jsx @@ -97,8 +97,7 @@ export class LogContainer extends Component { componentDidUpdate(prevProps, prevState) { if ( this.props.enableLogAutoScroll && - (prevState.logs?.length !== this.state.logs?.length || - prevProps.isLogsMaximized !== this.props.isLogsMaximized) + prevState.logs?.length !== this.state.logs?.length ) { if (this.shouldAutoScroll()) { this.scrollToBottomLog(); diff --git a/packages/components/src/components/Log/_Log.scss b/packages/components/src/components/Log/_Log.scss index 4f8168594..75c2ae35f 100644 --- a/packages/components/src/components/Log/_Log.scss +++ b/packages/components/src/components/Log/_Log.scss @@ -1,5 +1,5 @@ /* -Copyright 2019-2025 The Tekton Authors +Copyright 2019-2026 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -72,37 +72,7 @@ pre.tkn--log { .#{$prefix}--btn-set { position: absolute; inset-block-start: 0; - inset-inline-end: var(--tkn-log-inline-padding); - align-items: center; - } - - .button-container { - position: absolute; - clip-path: inset(0); // ensure the children with position:fixed are not shown outside this element. - inset-block-start: 3.125rem; //equals the maximum between padding-block-start of pre.tkn--log and between the page header height - inset-block-end: 0; - inset-inline-end: 0; - inline-size: var(--tkn-log-inline-padding); - } - - .#{$prefix}--btn--ghost, - .#{$prefix}--copy-btn { - inline-size: 2rem; - block-size: 2rem; - min-block-size: 2rem; - background-color: $background; - - &:hover { - background-color: $layer-hover; - } - - &:focus { - outline-color: white; - } - - svg { - fill: $icon-primary; - } + inset-inline-end: 0.5rem; // var(--tkn-log-inline-padding); } .tkn--log-trailer { diff --git a/packages/components/src/components/LogsToolbar/LogsToolbar.jsx b/packages/components/src/components/LogsToolbar/LogsToolbar.jsx index 3df54b781..bf6538e33 100644 --- a/packages/components/src/components/LogsToolbar/LogsToolbar.jsx +++ b/packages/components/src/components/LogsToolbar/LogsToolbar.jsx @@ -1,5 +1,5 @@ /* -Copyright 2020-2025 The Tekton Authors +Copyright 2020-2026 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -12,98 +12,51 @@ limitations under the License. */ /* istanbul ignore file */ import { useIntl } from 'react-intl'; +import { Maximize, Minimize, Settings } from '@carbon/react/icons'; import { - Download, - Launch, - Maximize, - Minimize, - Settings -} from '@carbon/react/icons'; -import { + Button, + ButtonSet, Checkbox, CheckboxGroup, Popover, - PopoverContent, - usePrefix + PopoverContent } from '@carbon/react'; import { useState } from 'react'; const LogsToolbar = ({ id, isMaximized, - name, logLevels, onToggleShowTimestamps, onToggleLogLevel, onToggleMaximized, - showTimestamps, - url + showTimestamps }) => { - const carbonPrefix = usePrefix(); const intl = useIntl(); const [isSettingsOpen, setIsSettingsOpen] = useState(false); return ( -
+ {onToggleMaximized ? ( - - ) : null} - {url ? ( - <> - - - - {intl.formatMessage({ - id: 'dashboard.logs.launchButtonTooltip', - defaultMessage: 'Open logs in a new window' - })} - - - - - - - {intl.formatMessage({ - id: 'dashboard.logs.downloadButtonTooltip', - defaultMessage: 'Download logs' - })} - - - - + }) + } + kind="ghost" + onClick={onToggleMaximized} + renderIcon={isMaximized ? Minimize : Maximize} + size="sm" + /> ) : null} - + renderIcon={Settings} + size="sm" + /> -
+ ); }; diff --git a/packages/components/src/components/LogsToolbar/LogsToolbar.stories.jsx b/packages/components/src/components/LogsToolbar/LogsToolbar.stories.jsx index d0daec77f..7f51445c3 100644 --- a/packages/components/src/components/LogsToolbar/LogsToolbar.stories.jsx +++ b/packages/components/src/components/LogsToolbar/LogsToolbar.stories.jsx @@ -1,5 +1,5 @@ /* -Copyright 2019-2025 The Tekton Authors +Copyright 2019-2026 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -97,27 +97,3 @@ export const WithMaximize = { ); } }; - -export const WithURL = { - args: { - ...WithMaximize.args, - name: 'some_filename.txt', - url: '/some/logs/url' - }, - render: args => { - const [, updateArgs] = useArgs(); - - return ( - - updateArgs({ logLevels: { ...args.logLevels, ...logLevel } }) - } - onToggleMaximized={() => updateArgs({ isMaximized: !args.isMaximized })} - onToggleShowTimestamps={showTimestamps => - updateArgs({ showTimestamps }) - } - /> - ); - } -}; diff --git a/packages/components/src/components/PipelineRun/PipelineRun.jsx b/packages/components/src/components/PipelineRun/PipelineRun.jsx index ef35ce7fd..41a7b4d8c 100644 --- a/packages/components/src/components/PipelineRun/PipelineRun.jsx +++ b/packages/components/src/components/PipelineRun/PipelineRun.jsx @@ -11,25 +11,19 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Fragment, useState } from 'react'; +import { useState } from 'react'; import { InlineNotification, SkeletonText, TabsVertical } from '@carbon/react'; import { useIntl } from 'react-intl'; import { getErrorMessage, getStatus, - getStepDefinition, - getStepStatus, labels as labelConstants } from '@tektoncd/dashboard-utils'; import Log from '../Log'; -import Portal from '../Portal'; import RunHeader from '../RunHeader'; -import StepDetails from '../StepDetails'; -import TaskRunDetails from '../TaskRunDetails'; import TaskRunTabPanels from '../TaskRunTabPanels'; import TaskRunTabs from '../TaskRunTabs'; -import TaskTree from '../TaskTree'; function getPipelineTask({ pipeline, pipelineRun, selectedTaskId, taskRun }) { const memberOf = taskRun?.metadata?.labels?.[labelConstants.MEMBER_OF]; @@ -48,17 +42,16 @@ export default /* istanbul ignore next */ function PipelineRun({ duration, enableLogAutoScroll, enableLogScrollButtons, - enableTabLayout, error, fetchLogs, forceLogPolling, getLogsToolbar, + getStepLogToolbar, handlePipelineRunInfo = () => {}, handleTaskSelected = /* istanbul ignore next */ () => {}, ignoredSidecars = {}, loading, logLevels, - maximizedLogsContainer, onRetryChange, onViewChange = /* istanbul ignore next */ () => {}, pipeline, @@ -79,7 +72,6 @@ export default /* istanbul ignore next */ function PipelineRun({ view = null }) { const intl = useIntl(); - const [isLogsMaximized, setIsLogsMaximized] = useState(false); const [isTaskRunMaximized, setIsTaskRunMaximized] = useState(false); const [expandedSteps, setExpandedSteps] = useState(() => selectedStepId ? { [selectedStepId]: true } : {} @@ -106,60 +98,34 @@ export default /* istanbul ignore next */ function PipelineRun({ return status === 'False' && reason !== 'Cancelled'; } - function onToggleLogsMaximized() { - setIsLogsMaximized(prevIsLogsMaximized => !prevIsLogsMaximized); - } - function onToggleTaskRunMaximized() { setIsTaskRunMaximized(prevIsTaskRunMaximized => !prevIsTaskRunMaximized); } - function getLogContainer({ - disableLogsToolbar, - isSidecar, - stepName, - stepStatus, - taskRun - }) { + function getLogContainer({ isSidecar, stepName, stepStatus, taskRun }) { if ((!selectedStepId && !stepName) || !stepStatus) { return null; } - const LogsRoot = - isLogsMaximized && maximizedLogsContainer ? Portal : Fragment; - return ( - - fetchLogs({ stepName, stepStatus, taskRun })} - forcePolling={forceLogPolling} - isLogsMaximized={isLogsMaximized} - isSidecar={isSidecar} - key={`${selectedTaskId}:${stepName}:${selectedRetry}`} - logLevels={logLevels} - pollingInterval={pollingInterval} - showLevels={showLogLevels} - showTimestamps={showLogTimestamps} - stepStatus={stepStatus} - toolbar={ - !disableLogsToolbar && - getLogsToolbar && - stepStatus && - getLogsToolbar({ - id: `${selectedTaskId}-${stepName}-${selectedRetry}-logs-toolbar`, - isMaximized: isLogsMaximized, - onToggleMaximized: - !!maximizedLogsContainer && onToggleLogsMaximized, - stepStatus, - taskRun - }) - } - /> - + fetchLogs({ stepName, stepStatus, taskRun })} + forcePolling={forceLogPolling} + isSidecar={isSidecar} + key={`${selectedTaskId}:${stepName}:${selectedRetry}`} + logLevels={logLevels} + pollingInterval={pollingInterval} + showLevels={showLogLevels} + showTimestamps={showLogTimestamps} + stepStatus={stepStatus} + toolbar={ + getStepLogToolbar && + stepStatus && + getStepLogToolbar({ stepStatus, taskRun }) + } + /> ); } @@ -338,33 +304,12 @@ export default /* istanbul ignore next */ function PipelineRun({ tasks?.find(t => t.metadata.name === taskRun.spec.taskRef.name)) || {}; - const stepStatus = getStepStatus({ - selectedStepId, - taskRun - }); - - let definition; - let logContainer; - if (!enableTabLayout) { - definition = getStepDefinition({ - selectedStepId, - task, - taskRun - }); - - logContainer = getLogContainer({ - stepName: selectedStepId, - stepStatus, - taskRun - }); - } - const skippedTasks = pipelineRun.status?.skippedTasks || []; const skippedTask = skippedTasks.find( skipped => skipped.name === selectedTaskId ); - if (enableTabLayout && !selectedTaskId) { + if (!selectedTaskId) { const selectedTaskRun = taskRunsToUse[0]; const { labels = {} } = selectedTaskRun?.metadata || {}; const { [labelConstants.PIPELINE_TASK]: pipelineTaskName } = labels; @@ -399,93 +344,55 @@ export default /* istanbul ignore next */ function PipelineRun({ {(taskRunsToUse.length > 0 || preTaskRun) && (
- {enableTabLayout ? ( - { + if (preTaskRun && newSelectedIndex === 0) { + onTaskSelected({}); + return; + } + + const selectedTaskRun = + taskRunsToUse[newSelectedIndex - preTaskRunOffset]; + const { labels } = selectedTaskRun.metadata; + const { [labelConstants.PIPELINE_TASK]: pipelineTaskName } = + labels; + + setExpandedSteps({}); + onTaskSelected({ + selectedTaskId: pipelineTaskName, + taskRunName: selectedTaskRun.metadata?.name + }); + }} + > + + { - if (preTaskRun && newSelectedIndex === 0) { - onTaskSelected({}); - return; - } - - const selectedTaskRun = - taskRunsToUse[newSelectedIndex - preTaskRunOffset]; - const { labels } = selectedTaskRun.metadata; - const { [labelConstants.PIPELINE_TASK]: pipelineTaskName } = - labels; - - setExpandedSteps({}); - onTaskSelected({ - selectedTaskId: pipelineTaskName, - taskRunName: selectedTaskRun.metadata?.name - }); - }} - > - - - - ) : ( - <> - - {(selectedStepId && ( - - )) || - (selectedTaskId && ( - - ))} - - )} + selectedRetry={selectedRetry} + selectedTaskId={selectedTaskId} + selectedStepId={selectedStepId} + skippedTask={skippedTask} + task={task} + taskRun={taskRun} + taskRuns={taskRunsToUse} + view={view} + /> +
)} diff --git a/packages/components/src/components/PipelineRun/PipelineRun.stories.jsx b/packages/components/src/components/PipelineRun/PipelineRun.stories.jsx index 20519603c..66387f79f 100644 --- a/packages/components/src/components/PipelineRun/PipelineRun.stories.jsx +++ b/packages/components/src/components/PipelineRun/PipelineRun.stories.jsx @@ -1,5 +1,5 @@ /* -Copyright 2019-2025 The Tekton Authors +Copyright 2019-2026 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -231,7 +231,6 @@ const pipelineRunWithMinimalStatus = { export default { args: { - enableTabLayout: false, selectedRetry: '', selectedStepId: undefined, selectedTaskId: undefined, diff --git a/packages/components/src/components/PipelineRun/PipelineRun.test.jsx b/packages/components/src/components/PipelineRun/PipelineRun.test.jsx index d94c23fb5..01e416c4f 100644 --- a/packages/components/src/components/PipelineRun/PipelineRun.test.jsx +++ b/packages/components/src/components/PipelineRun/PipelineRun.test.jsx @@ -1,5 +1,5 @@ /* -Copyright 2019-2024 The Tekton Authors +Copyright 2019-2026 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -30,11 +30,15 @@ it('PipelineRunContainer handles init step failures', async () => { const initStepName = 'my-failed-init-step'; const pipelineRunName = 'fake_pipelineRunName'; const taskRunName = 'fake_taskRunName'; + const pipelineTaskName = 'fake_pipelineTaskName'; + const handleTaskSelected = vi.fn(); const taskRun = { metadata: { name: taskRunName, - labels: {}, + labels: { + 'tekton.dev/pipelineTask': pipelineTaskName + }, uid: '41deea80-53bc-4500-a20e-3b18549e23ab' }, spec: { @@ -60,15 +64,22 @@ it('PipelineRunContainer handles init step failures', async () => { } }; - const { getByText } = render( + render( {}} + handleTaskSelected={handleTaskSelected} pipelineRun={pipelineRun} taskRuns={[taskRun]} tasks={[]} /> ); - await waitFor(() => getByText(initStepName)); + await waitFor(() => + expect(handleTaskSelected).toHaveBeenCalledWith({ + selectedRetry: undefined, + selectedStepId: undefined, + selectedTaskId: pipelineTaskName, + taskRunName: undefined + }) + ); }); it('PipelineRunContainer handles init step failures for retry', async () => { @@ -78,7 +89,9 @@ it('PipelineRunContainer handles init step failures for retry', async () => { const taskRun = { metadata: { - labels: {}, + labels: { + 'tekton.dev/pipelineTask': 'task-1' + }, name: taskRunName, uid: 'b4402feb-69fe-4a5a-a0c2-5e390aa58894' }, @@ -115,7 +128,6 @@ it('PipelineRunContainer handles init step failures for retry', async () => { ] } }; - const pipelineRun = { metadata: { name: pipelineRunName @@ -123,7 +135,8 @@ it('PipelineRunContainer handles init step failures for retry', async () => { status: { childReferences: [ { - name: taskRunName + name: taskRunName, + pipelineTaskName: 'task-1' } ] } @@ -132,21 +145,21 @@ it('PipelineRunContainer handles init step failures for retry', async () => { class TestWrapper extends ReactComponent { state = { selectedStepId: null, - selectedTaskId: null + selectedTaskId: 'task-1' }; - handleTaskSelected = ({ selectedStepId, selectedTaskId }) => { - this.setState({ selectedStepId, selectedTaskId }); + handleTaskSelected = ({ selectedTaskId }) => { + this.setState({ + selectedTaskId: selectedTaskId ?? this.state.selectedTaskId + }); }; render() { const { children: Component } = this.props; - const { selectedStepId, selectedTaskId } = this.state; return ( ); } @@ -154,12 +167,11 @@ it('PipelineRunContainer handles init step failures for retry', async () => { const { getByText } = render( - {({ handleTaskSelected, selectedStepId, selectedTaskId }) => ( + {({ handleTaskSelected, selectedTaskId }) => ( {}} handleTaskSelected={handleTaskSelected} pipelineRun={pipelineRun} - selectedStepId={selectedStepId} + selectedRetry="0" selectedTaskId={selectedTaskId} taskRuns={[taskRun]} tasks={[]} @@ -167,6 +179,5 @@ it('PipelineRunContainer handles init step failures for retry', async () => { )} ); - await waitFor(() => getByText(/(retry 1)/)); - await waitFor(() => getByText(initStepName, { selector: '.tkn--step-name' })); + await waitFor(() => getByText('task-1')); }); diff --git a/packages/components/src/components/Step/Step.jsx b/packages/components/src/components/Step/Step.jsx deleted file mode 100644 index 2a705f1ff..000000000 --- a/packages/components/src/components/Step/Step.jsx +++ /dev/null @@ -1,127 +0,0 @@ -/* -Copyright 2019-2025 The Tekton Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { useIntl } from 'react-intl'; -import { Pending as DefaultIcon } from '@carbon/react/icons'; - -import StatusIcon from '../StatusIcon'; - -export default function Step({ - exitCode, - id, - onSelect, - reason, - selected, - status, - stepName = 'unknown', - terminationReason -}) { - const intl = useIntl(); - - function handleClick(event) { - event.preventDefault(); - onSelect(id); - } - - function getStatusLabel() { - if (terminationReason === 'Skipped') { - return intl.formatMessage({ - id: 'dashboard.taskRun.status.skipped', - defaultMessage: 'Skipped' - }); - } - if ( - status === 'cancelled' || - (status === 'terminated' && - (reason === 'TaskRunCancelled' || reason === 'TaskRunTimeout')) - ) { - return intl.formatMessage({ - id: 'dashboard.taskRun.status.cancelled', - defaultMessage: 'Cancelled' - }); - } - - if (status === 'running') { - return intl.formatMessage({ - id: 'dashboard.taskRun.status.running', - defaultMessage: 'Running' - }); - } - - if (status === 'terminated') { - if (reason === 'Completed') { - return exitCode !== 0 - ? intl.formatMessage( - { - id: 'dashboard.taskRun.status.succeeded.warning', - defaultMessage: 'Completed with exit code {exitCode}' - }, - { exitCode } - ) - : intl.formatMessage({ - id: 'dashboard.taskRun.status.succeeded', - defaultMessage: 'Completed' - }); - } - return intl.formatMessage({ - id: 'dashboard.taskRun.status.failed', - defaultMessage: 'Failed' - }); - } - - if (status === 'waiting') { - return intl.formatMessage({ - id: 'dashboard.taskRun.status.waiting', - defaultMessage: 'Waiting' - }); - } - - return intl.formatMessage({ - id: 'dashboard.taskRun.status.notRun', - defaultMessage: 'Not run' - }); - } - - const statusLabel = getStatusLabel(); - - return ( -
  • - e.key === 'Enter' && handleClick(e)} - role="button" - > - } - hasWarning={exitCode !== 0} - reason={reason} - status={status} - terminationReason={terminationReason} - title={statusLabel} - type="inverse" - /> - - {stepName} - - -
  • - ); -} diff --git a/packages/components/src/components/Step/Step.stories.js b/packages/components/src/components/Step/Step.stories.js deleted file mode 100644 index 6fe032753..000000000 --- a/packages/components/src/components/Step/Step.stories.js +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2019-2025 The Tekton Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { action } from 'storybook/actions'; - -import Step from './Step'; - -export default { - args: { - exitCode: 0, - onSelect: action('selected'), - stepName: 'build' - }, - component: Step, - title: 'Step' -}; - -export const Default = {}; -export const Selected = { args: { selected: true } }; -export const Waiting = { args: { status: 'waiting' } }; -export const Running = { args: { status: 'running' } }; - -export const Completed = { - args: { reason: 'Completed', status: 'terminated' } -}; - -export const CompletedWithWarning = { - args: { - ...Completed.args, - exitCode: 1 - }, - name: 'Completed with warning' -}; - -export const Skipped = { - args: { - reason: 'Completed', - status: 'terminated', - terminationReason: 'Skipped' - } -}; - -export const Error = { args: { reason: 'Error', status: 'terminated' } }; diff --git a/packages/components/src/components/Step/Step.test.jsx b/packages/components/src/components/Step/Step.test.jsx deleted file mode 100644 index c7cfdbe48..000000000 --- a/packages/components/src/components/Step/Step.test.jsx +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright 2019-2024 The Tekton Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { fireEvent } from '@testing-library/react'; -import Step from './Step'; -import { render } from '../../utils/test'; - -it('Step renders default content', () => { - const { queryByText } = render(); - - expect(queryByText(/Unknown/i)).toBeTruthy(); -}); - -it('Step renders the provided content', () => { - const stepName = 'build'; - const { queryByText } = render(); - expect(queryByText(/build/i)).toBeTruthy(); -}); - -it('Step renders selected state', () => { - render(); -}); - -it('Step renders waiting state', () => { - const { queryByText } = render(); - expect(queryByText(/waiting/i)).toBeTruthy(); -}); - -it('Step renders running state', () => { - const { queryByText } = render(); - expect(queryByText(/running/i)).toBeTruthy(); -}); - -it('Step renders cancelled state', () => { - const { queryByText } = render(); - expect(queryByText(/Cancelled/i)).toBeTruthy(); -}); - -it('Step renders cancelled state for TaskRunCancelled', () => { - const { queryByText } = render( - - ); - expect(queryByText(/Cancelled/i)).toBeTruthy(); -}); - -it('Step renders cancelled state for TaskRunTimeout', () => { - const { queryByText } = render( - - ); - expect(queryByText(/Cancelled/i)).toBeTruthy(); -}); - -it('Step renders completed state', () => { - const { queryByText } = render( - - ); - expect(queryByText(/Completed/i)).toBeTruthy(); -}); - -it('Step renders completed with warning state', () => { - const { queryByText } = render( - - ); - expect(queryByText(/Completed with exit code 1/i)).toBeTruthy(); -}); - -it('Step renders skipped state', () => { - const { queryByText } = render( - - ); - expect(queryByText(/Skipped/i)).toBeTruthy(); -}); - -it('Step renders error state', () => { - const { queryByText } = render(); - expect(queryByText(/Failed/i)).toBeTruthy(); -}); - -it('Step handles click event', () => { - const onSelect = vi.fn(); - const { getByText } = render(); - fireEvent.click(getByText(/build/i)); - expect(onSelect).toHaveBeenCalledTimes(1); -}); diff --git a/packages/components/src/components/Step/_Step.scss b/packages/components/src/components/Step/_Step.scss index 1b17a505a..2d7eb27f4 100644 --- a/packages/components/src/components/Step/_Step.scss +++ b/packages/components/src/components/Step/_Step.scss @@ -1,5 +1,5 @@ /* -Copyright 2019-2024 The Tekton Authors +Copyright 2019-2026 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -12,42 +12,8 @@ limitations under the License. */ @use '@carbon/react/scss/theme' as *; -@use '@carbon/react/scss/type' as *; .tkn--step { - list-style-type: none; - - &:hover > .tkn--step-link, - &:hover > .tkn--step-link:hover, - &[data-selected] > .tkn--step-link:hover { - background-color: $layer-hover; - text-decoration: none; - } - - &:hover > .tkn--step-link, - > .tkn--step-link:hover { - border-inline-start-color: $layer-hover; - } - - &[data-selected] > .tkn--step-link { - border-inline-start: 3px solid $border-interactive; - } - - > .tkn--step-link { - cursor: pointer; - display: flex; - align-items: baseline; - position: relative; - padding-block: 0; - padding-inline-start: 0.75rem; - padding-inline-end: 1rem; - line-height: 2.2rem; - font-size: 0.78rem; - letter-spacing: 0.02rem; - text-decoration: none; - border-inline-start: 3px solid $layer; - } - .tkn--status-icon { flex-shrink: 0; align-self: center; diff --git a/packages/components/src/components/Step/index.js b/packages/components/src/components/Step/index.js deleted file mode 100644 index 96c860cdd..000000000 --- a/packages/components/src/components/Step/index.js +++ /dev/null @@ -1,15 +0,0 @@ -/* -Copyright 2019-2021 The Tekton Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -/* istanbul ignore file */ - -export { default } from './Step'; diff --git a/packages/components/src/components/StepDetails/StepDetails.jsx b/packages/components/src/components/StepDetails/StepDetails.jsx deleted file mode 100644 index 3d5b1e1e4..000000000 --- a/packages/components/src/components/StepDetails/StepDetails.jsx +++ /dev/null @@ -1,113 +0,0 @@ -/* -Copyright 2019-2024 The Tekton Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import PropTypes from 'prop-types'; -import { useIntl } from 'react-intl'; -import { getStatus, getStepStatusReason } from '@tektoncd/dashboard-utils'; -import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@carbon/react'; - -import DetailsHeader from '../DetailsHeader'; -import Log from '../Log'; -import StepDefinition from '../StepDefinition'; - -const tabs = ['logs', 'details']; - -const defaults = { - onViewChange: /* istanbul ignore next */ () => {}, - taskRun: {} -}; - -const StepDetails = ({ - definition, - logContainer, - onViewChange = defaults.onViewChange, - skippedTask, - stepName, - stepStatus, - taskRun = defaults.taskRun, - view -}) => { - const intl = useIntl(); - const { exitCode, reason, status } = getStepStatusReason(stepStatus); - const statusValue = - getStatus(taskRun).reason === 'TaskRunCancelled' && status !== 'terminated' - ? 'cancelled' - : status; - - let selectedTabIndex = tabs.indexOf(view); - if (selectedTabIndex === -1) { - selectedTabIndex = 0; - } - - return ( -
    - - onViewChange(tabs[event.selectedIndex])} - selectedIndex={selectedTabIndex} - > - - - {intl.formatMessage({ - id: 'dashboard.taskRun.logs', - defaultMessage: 'Logs' - })} - - - {intl.formatMessage({ - id: 'dashboard.resource.detailsTab', - defaultMessage: 'Details' - })} - - - - - {selectedTabIndex === 0 && skippedTask ? ( - - intl.formatMessage({ - id: 'dashboard.taskRun.logs.skipped', - defaultMessage: - 'This step did not run as the task was skipped. See status for more details.' - }) - } - /> - ) : ( - logContainer - )} - - - {selectedTabIndex === 1 && ( - - )} - - - -
    - ); -}; - -StepDetails.propTypes = { - onViewChange: PropTypes.func, - taskRun: PropTypes.shape({}) -}; - -export default StepDetails; diff --git a/packages/components/src/components/StepDetails/StepDetails.stories.jsx b/packages/components/src/components/StepDetails/StepDetails.stories.jsx deleted file mode 100644 index b1f22a12c..000000000 --- a/packages/components/src/components/StepDetails/StepDetails.stories.jsx +++ /dev/null @@ -1,119 +0,0 @@ -/* -Copyright 2019-2025 The Tekton Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { useArgs } from 'storybook/preview-api'; - -import Log from '../Log'; -import StepDetails from './StepDetails'; - -function getStepStatus({ exitCode = 0, terminationReason } = {}) { - return { terminated: { exitCode, reason: 'Completed' }, terminationReason }; -} - -const ansiLog = - '\n=== demo-pipeline-run-1-build-skaffold-app-2mrdg-pod-59e217: build-step-git-source-skaffold-git-ml8j4 ===\n{"level":"info","ts":1553865693.943092,"logger":"fallback-logger","caller":"git-init/main.go:100","msg":"Successfully cloned https://github.com/GoogleContainerTools/skaffold @ \\"master\\" in path \\"/workspace\\""}\n\n=== demo-pipeline-run-1-build-skaffold-app-2mrdg-pod-59e217: build-step-build-and-push ===\n\u001b[36mINFO\u001b[0m[0000] Downloading base image golang:1.10.1-alpine3.7\n2019/03/29 13:21:34 No matching credentials were found, falling back on anonymous\n\u001b[36mINFO\u001b[0m[0001] Executing 0 build triggers\n\u001b[36mINFO\u001b[0m[0001] Unpacking rootfs as cmd RUN go build -o /app . requires it.\n\u001b[36mINFO\u001b[0m[0010] Taking snapshot of full filesystem...\n\u001b[36mINFO\u001b[0m[0015] Using files from context: [/workspace/examples/microservices/leeroy-app/app.go]\n\u001b[36mINFO\u001b[0m[0015] COPY app.go .\n\u001b[36mINFO\u001b[0m[0015] Taking snapshot of files...\n\u001b[36mINFO\u001b[0m[0015] RUN go build -o /app .\n\u001b[36mINFO\u001b[0m[0015] cmd: /bin/sh\n\u001b[36mINFO\u001b[0m[0015] args: [-c go build -o /app .]\n\u001b[36mINFO\u001b[0m[0016] Taking snapshot of full filesystem...\n\u001b[36mINFO\u001b[0m[0036] CMD ["./app"]\n\u001b[36mINFO\u001b[0m[0036] COPY --from=builder /app .\n\u001b[36mINFO\u001b[0m[0036] Taking snapshot of files...\nerror pushing image: failed to push to destination gcr.io/christiewilson-catfactory/leeroy-app:latest: Get https://gcr.io/v2/token?scope=repository%3Achristiewilson-catfactory%2Fleeroy-app%3Apush%2Cpull\u0026scope=repository%3Alibrary%2Falpine%3Apull\u0026service=gcr.io exit status 1\n\n=== demo-pipeline-run-1-build-skaffold-app-2mrdg-pod-59e217: nop ===\nBuild successful\n'; - -function getLogContainer({ - exitCode = 0, - logContent = ansiLog, - terminationReason -} = {}) { - return ( - logContent} - stepStatus={getStepStatus({ exitCode, terminationReason })} - /> - ); -} - -export default { - args: { - definition: 'this will show the Task.spec or TaskRun.spec.taskSpec', - stepName: 'build', - taskRun: {} - }, - component: StepDetails, - subcomponents: { Log }, - title: 'StepDetails' -}; - -export const Default = { - args: { - logContainer: getLogContainer(), - stepStatus: getStepStatus() - }, - render: args => { - const [, updateArgs] = useArgs(); - return ( - updateArgs({ view: selectedView })} - /> - ); - } -}; - -export const WithWarning = { - args: { - logContainer: getLogContainer({ exitCode: 1 }), - stepStatus: getStepStatus({ exitCode: 1 }) - }, - render: args => { - const [, updateArgs] = useArgs(); - return ( - updateArgs({ view: selectedView })} - /> - ); - } -}; - -export const SkippedTask = { - args: { - logContainer: getLogContainer(), - skippedTask: {} - }, - render: args => { - const [, updateArgs] = useArgs(); - return ( - updateArgs({ view: selectedView })} - /> - ); - } -}; - -export const SkippedStep = { - args: { - logContainer: getLogContainer({ - logContent: - 'Step was skipped due to when expressions were evaluated to false.', - terminationReason: 'Skipped' - }), - stepStatus: { - terminated: { exitCode: 0, reason: 'Completed' }, - terminationReason: 'Skipped' - } - }, - render: args => { - const [, updateArgs] = useArgs(); - return ( - updateArgs({ view: selectedView })} - /> - ); - } -}; diff --git a/packages/components/src/components/StepDetails/StepDetails.test.jsx b/packages/components/src/components/StepDetails/StepDetails.test.jsx deleted file mode 100644 index 7cef08880..000000000 --- a/packages/components/src/components/StepDetails/StepDetails.test.jsx +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2019-2024 The Tekton Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { fireEvent, waitFor } from '@testing-library/react'; -import { renderWithRouter } from '../../utils/test'; - -import StepDetails from './StepDetails'; - -describe('StepDetails', () => { - it('renders', () => { - renderWithRouter(); - }); - - it('renders terminated state', () => { - renderWithRouter( - - ); - }); - - it('renders cancelled state', () => { - renderWithRouter( - - ); - }); - - it('renders with selected view', () => { - const { getByText } = renderWithRouter( - - ); - - fireEvent.click(getByText(/logs/i)); - }); - - it('renders skipped Task state', async () => { - const { getByText } = renderWithRouter( - - ); - - await waitFor(() => getByText(/task was skipped/i)); - }); -}); diff --git a/packages/components/src/components/StepLogToolbar/StepLogToolbar.jsx b/packages/components/src/components/StepLogToolbar/StepLogToolbar.jsx new file mode 100644 index 000000000..34d59b725 --- /dev/null +++ b/packages/components/src/components/StepLogToolbar/StepLogToolbar.jsx @@ -0,0 +1,55 @@ +/* +Copyright 2020-2026 The Tekton Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +/* istanbul ignore file */ + +import { useIntl } from 'react-intl'; +import { Download, Launch } from '@carbon/react/icons'; +import { Button, ButtonSet } from '@carbon/react'; + +const StepLogToolbar = ({ name, url }) => { + const intl = useIntl(); + + return ( + +