Skip to content

Commit f417394

Browse files
AlanGreenetekton-robot
authored andcommitted
Refactor TaskRunTabPanels to split components
Split TaskRunTabPanels to extract the child components to their own files. Add Storybook and unit test coverage for each.
1 parent 876a1ca commit f417394

14 files changed

Lines changed: 1566 additions & 319 deletions

File tree

.storybook/Container.scss

Lines changed: 7 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-2026 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
@@ -28,3 +28,9 @@ limitations under the License.
2828
background-color: inherit !important;
2929
}
3030
}
31+
32+
[role="main"] > .tkn--task-logs {
33+
.cds--accordion__heading {
34+
inset-block-start: 0; // demonstrate the sticky header behaviour when displayed in isolation
35+
}
36+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
Copyright 2025-2026 The Tekton Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
import { AccordionItem as CarbonAccordionItem } from '@carbon/react';
15+
16+
function AccordionItem({ children, open, ...rest }) {
17+
return (
18+
<CarbonAccordionItem open={open} {...rest}>
19+
{open ? children : null}
20+
</CarbonAccordionItem>
21+
);
22+
}
23+
24+
export default AccordionItem;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright 2026 The Tekton Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
import { Accordion } from '@carbon/react';
15+
16+
import AccordionItem from './AccordionItem';
17+
18+
export default {
19+
component: AccordionItem,
20+
title: 'AccordionItem'
21+
};
22+
23+
export const Closed = {
24+
args: {
25+
open: false,
26+
title: 'Closed Accordion Item',
27+
children: <div>This content is not rendered when closed</div>
28+
},
29+
decorators: [
30+
Story => (
31+
<Accordion>
32+
<Story />
33+
</Accordion>
34+
)
35+
]
36+
};
37+
38+
export const Open = {
39+
args: {
40+
open: true,
41+
title: 'Open Accordion Item',
42+
children: (
43+
<div>
44+
<p>This content is rendered when open</p>
45+
<p>
46+
The AccordionItem optimizes rendering by only rendering children when
47+
open
48+
</p>
49+
</div>
50+
)
51+
},
52+
decorators: [
53+
Story => (
54+
<Accordion>
55+
<Story />
56+
</Accordion>
57+
)
58+
]
59+
};
60+
61+
export const MultipleItems = {
62+
render: () => (
63+
<Accordion>
64+
<AccordionItem open title="First Item">
65+
<p>Content of first item</p>
66+
</AccordionItem>
67+
<AccordionItem open={false} title="Second Item">
68+
<p>Content of second item (not rendered)</p>
69+
</AccordionItem>
70+
<AccordionItem open title="Third Item">
71+
<p>Content of third item</p>
72+
</AccordionItem>
73+
</Accordion>
74+
)
75+
};
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
Copyright 2026 The Tekton Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
import { render } from '../../utils/test';
15+
16+
import AccordionItem from './AccordionItem';
17+
18+
describe('AccordionItem', () => {
19+
it('should render children when open is true', () => {
20+
const { queryByText } = render(
21+
<AccordionItem open title="Test Title">
22+
<div>Test Content</div>
23+
</AccordionItem>
24+
);
25+
expect(queryByText('Test Content')).toBeTruthy();
26+
});
27+
28+
it('should not render children when open is false', () => {
29+
const { queryByText } = render(
30+
<AccordionItem open={false} title="Test Title">
31+
<div>Test Content</div>
32+
</AccordionItem>
33+
);
34+
expect(queryByText('Test Content')).toBeFalsy();
35+
});
36+
37+
it('should pass through other props to CarbonAccordionItem', () => {
38+
const { container } = render(
39+
<AccordionItem
40+
open
41+
title="Test Title"
42+
className="custom-class"
43+
data-testid="test-accordion"
44+
>
45+
<div>Test Content</div>
46+
</AccordionItem>
47+
);
48+
const accordionItem = container.querySelector('.custom-class');
49+
expect(accordionItem).toBeTruthy();
50+
});
51+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
Copyright 2026 The Tekton Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
/* istanbul ignore file */
14+
15+
export { default } from './AccordionItem';
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
Copyright 2025-2026 The Tekton Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
import { useIntl } from 'react-intl';
15+
import { Accordion } from '@carbon/react';
16+
import { getStatus } from '@tektoncd/dashboard-utils';
17+
18+
import TaskRunStep from '../TaskRunStep';
19+
20+
function TaskRunLogs({
21+
expandedSteps,
22+
getLogContainer,
23+
ignoredSidecars,
24+
onStepSelected,
25+
selectedRetry,
26+
selectedTaskId,
27+
skippedTask,
28+
task,
29+
taskRun
30+
}) {
31+
const intl = useIntl();
32+
33+
const taskRunStatus = getStatus(taskRun);
34+
const { reason } = taskRunStatus;
35+
const { sidecars, steps } = taskRun.status || {};
36+
const sidecarsToRender =
37+
sidecars?.filter(sidecar => !ignoredSidecars[sidecar.name]) || [];
38+
39+
if (!steps) {
40+
return (
41+
<span>
42+
{intl.formatMessage({
43+
id: 'dashboard.taskRun.logs.unavailable',
44+
defaultMessage: 'No logs are available. See status for more details.'
45+
})}
46+
</span>
47+
);
48+
}
49+
if (skippedTask) {
50+
return (
51+
<span className="tkn--task-skipped">
52+
{intl.formatMessage({
53+
id: 'dashboard.taskRun.logs.skipped',
54+
defaultMessage:
55+
'This step did not run as the task was skipped. See status for more details.'
56+
})}
57+
</span>
58+
);
59+
}
60+
61+
return (
62+
<Accordion align="end" className="tkn--task-logs" ordered size="md">
63+
{steps.map(step => (
64+
<TaskRunStep
65+
expandedSteps={{
66+
...expandedSteps,
67+
// automatically expand the step when there's only one
68+
...(steps.length === 1 ? { [step.name]: true } : null)
69+
}}
70+
getLogContainer={getLogContainer}
71+
key={step.name}
72+
onStepSelected={onStepSelected}
73+
selectedRetry={selectedRetry}
74+
selectedTaskId={selectedTaskId}
75+
taskRunReason={reason}
76+
step={step}
77+
steps={steps}
78+
task={task}
79+
taskRun={taskRun}
80+
/>
81+
))}
82+
{sidecarsToRender.map(sidecar => (
83+
<TaskRunStep
84+
expandedSteps={expandedSteps}
85+
getLogContainer={getLogContainer}
86+
isSidecar
87+
key={sidecar.name}
88+
onStepSelected={onStepSelected}
89+
selectedRetry={selectedRetry}
90+
selectedTaskId={selectedTaskId}
91+
taskRunReason={reason}
92+
step={sidecar}
93+
steps={sidecars}
94+
task={task}
95+
taskRun={taskRun}
96+
/>
97+
))}
98+
</Accordion>
99+
);
100+
}
101+
102+
export default TaskRunLogs;

0 commit comments

Comments
 (0)