Skip to content

Commit 81ef25b

Browse files
committed
feat: Add CDPipeline description filed (#552)
1 parent ede3b93 commit 81ef25b

File tree

8 files changed

+194
-64
lines changed

8 files changed

+194
-64
lines changed

src/components/ResponsiveChips/index.tsx

+81-34
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
1-
import { Chip, darken, Grid, Stack, Tooltip } from '@mui/material';
1+
import { Box, Chip, ChipProps, darken, Stack, Tooltip } from '@mui/material';
22
import React from 'react';
33
import ReactDOM from 'react-dom';
44
import { MemoryRouter } from 'react-router-dom';
55

6-
const defaultChipRender = (label, key) => (
7-
<Chip sx={{ backgroundColor: (t) => t.palette.secondary.main }} label={label} key={key} />
6+
const defaultChipRender = (label, key, size = 'small') => (
7+
<Chip
8+
sx={{ backgroundColor: (t) => t.palette.secondary.main }}
9+
label={label}
10+
size={size as ChipProps['size']}
11+
key={key}
12+
/>
13+
);
14+
15+
const defaultTooltipRender = (chipsToHide) => (
16+
<Box sx={{ py: (t) => t.typography.pxToRem(6), px: (t) => t.typography.pxToRem(10) }}>
17+
<Stack spacing={1.5} flexWrap="wrap" sx={{ fontWeight: 400 }}>
18+
{chipsToHide.map((chip) => defaultChipRender(chip, chip))}
19+
</Stack>
20+
</Box>
821
);
922

1023
const stackGap = 1;
@@ -13,52 +26,98 @@ const themeGapMultiplier = 8;
1326
export const ResponsiveChips = ({
1427
chipsData,
1528
renderChip = defaultChipRender,
29+
renderTooltip = defaultTooltipRender,
1630
}: {
1731
chipsData: string[];
18-
renderChip: (chip: string, key: string) => React.ReactElement;
32+
renderChip: (chip: string, key: string, size?: string) => React.ReactElement;
33+
renderTooltip: (chipsToHide: string[], chipsToShow: string[]) => React.ReactElement;
1934
}) => {
2035
const containerRef = React.useRef(null);
2136
const showMoreButtonRef = React.useRef(null);
2237

23-
const ghostContainerRef = React.useRef(null);
24-
2538
const [data, setData] = React.useState({
2639
chipsToShow: [],
2740
chipsToHide: [],
2841
occupiedWidth: 0,
2942
});
3043

3144
const calculateToFit = React.useCallback(() => {
32-
const freeSpaceWidth = containerRef.current ? containerRef.current.offsetWidth : 0;
33-
const buttonWidth = showMoreButtonRef.current ? showMoreButtonRef.current.offsetWidth : 0;
34-
const availableWidth = freeSpaceWidth - buttonWidth;
45+
const freeSpaceWidth = containerRef.current
46+
? containerRef.current.getBoundingClientRect().width
47+
: 0;
48+
49+
const buttonWidth = showMoreButtonRef.current
50+
? showMoreButtonRef.current.getBoundingClientRect().width
51+
: 0;
52+
53+
const margin = stackGap * themeGapMultiplier;
54+
const availableWidth = freeSpaceWidth - buttonWidth - margin;
3555

3656
let accumulatingWidth = 0;
37-
const chipsToShow = [];
38-
const chipsToHide = [];
57+
const chipsToShow: string[] = [];
58+
const chipsToHide: string[] = [];
3959
let overflowStarted = false;
4060

41-
chipsData.forEach((chip) => {
42-
const ChipElement = <MemoryRouter>{renderChip(chip, chip)}</MemoryRouter>; //TODO: find a way to get rid of temporary router wrapper
43-
ReactDOM.render(ChipElement, ghostContainerRef.current);
61+
const shadowHost = document.createElement('div');
62+
document.body.appendChild(shadowHost);
63+
const shadowRoot = shadowHost.attachShadow({ mode: 'open' });
64+
65+
const chipRenderingContainer = document.createElement('div');
66+
shadowRoot.appendChild(chipRenderingContainer);
67+
68+
shadowHost.style.cssText = `
69+
visibility: hidden;
70+
position: absolute;
71+
top: 0;
72+
left: 0;
73+
width: auto;
74+
height: auto;
75+
pointer-events: none;
76+
`;
4477

45-
const renderedElement = ghostContainerRef.current.firstChild;
46-
if (renderedElement instanceof HTMLElement) {
47-
const chipWidth = renderedElement.offsetWidth + stackGap * themeGapMultiplier;
78+
ReactDOM.render(
79+
<MemoryRouter>
80+
{chipsData.map((chip, index) => (
81+
<div key={index} id={`chip-${index}`}>
82+
{renderChip(chip, chip)}
83+
</div>
84+
))}
85+
</MemoryRouter>,
86+
chipRenderingContainer
87+
);
4888

49-
if (!overflowStarted && accumulatingWidth + chipWidth <= availableWidth) {
89+
chipsData.forEach((chip, index) => {
90+
const chipElement = chipRenderingContainer.querySelector(`#chip-${index}`);
91+
if (chipElement instanceof HTMLElement) {
92+
const chipWidth = chipElement.offsetWidth + stackGap * themeGapMultiplier;
93+
94+
const remainingSpace = availableWidth - accumulatingWidth;
95+
96+
if (
97+
!overflowStarted &&
98+
(accumulatingWidth + chipWidth <= availableWidth || remainingSpace > chipWidth - margin)
99+
) {
50100
accumulatingWidth += chipWidth;
51101
chipsToShow.push(chip);
52102
} else {
53103
overflowStarted = true;
54104
chipsToHide.push(chip);
55105
}
56106
}
57-
58-
ReactDOM.unmountComponentAtNode(ghostContainerRef.current);
59107
});
60108

61-
setData({ chipsToShow, chipsToHide, occupiedWidth: accumulatingWidth });
109+
ReactDOM.unmountComponentAtNode(chipRenderingContainer);
110+
document.body.removeChild(shadowHost);
111+
112+
setData((prevData) => {
113+
if (
114+
JSON.stringify(prevData.chipsToShow) !== JSON.stringify(chipsToShow) ||
115+
JSON.stringify(prevData.chipsToHide) !== JSON.stringify(chipsToHide)
116+
) {
117+
return { chipsToShow, chipsToHide, occupiedWidth: accumulatingWidth };
118+
}
119+
return prevData;
120+
});
62121
}, [chipsData, renderChip]);
63122

64123
React.useEffect(() => {
@@ -74,24 +133,12 @@ export const ResponsiveChips = ({
74133

75134
return (
76135
<>
77-
<div
78-
ref={ghostContainerRef}
79-
style={{ visibility: 'hidden', position: 'absolute', zIndex: -1, pointerEvents: 'none' }}
80-
/>
81136
<div ref={containerRef} style={{ width: '100%' }}>
82137
<Stack direction="row" spacing={stackGap} alignItems="center">
83138
{data.chipsToShow.map((chip) => renderChip(chip, chip))}
84139
{data.chipsToHide.length > 0 && (
85140
<>
86-
<Tooltip
87-
title={
88-
<Grid container spacing={2} flexWrap="wrap" sx={{ fontWeight: 400 }}>
89-
{data.chipsToHide.map((chip) => (
90-
<Grid item>{renderChip(chip, chip)}</Grid>
91-
))}
92-
</Grid>
93-
}
94-
>
141+
<Tooltip title={renderTooltip(data.chipsToHide, data.chipsToShow)}>
95142
<Chip
96143
ref={showMoreButtonRef}
97144
sx={{

src/pages/cdpipeline-list/components/CDPipelineList/hooks/useColumns.tsx

+36-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Link } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
2-
import { Chip, Typography } from '@mui/material';
2+
import { Box, Chip, Stack, Typography } from '@mui/material';
33
import React from 'react';
44
import { ResponsiveChips } from '../../../../../components/ResponsiveChips';
55
import { StatusIcon } from '../../../../../components/StatusIcon';
@@ -67,7 +67,7 @@ export const useColumns = (): TableColumn<HeadlampKubeObject<CDPipelineKubeObjec
6767
);
6868
},
6969
sort: (a, b) => sortByName(a.metadata.name, b.metadata.name),
70-
width: '20%',
70+
width: '15%',
7171
},
7272
{
7373
id: 'description',
@@ -94,6 +94,7 @@ export const useColumns = (): TableColumn<HeadlampKubeObject<CDPipelineKubeObjec
9494
backgroundColor: MAIN_COLOR.GREEN,
9595
borderColor: 'transparent',
9696
}}
97+
size="small"
9798
label={
9899
<Link
99100
routeName={routeComponentDetails.path}
@@ -109,9 +110,42 @@ export const useColumns = (): TableColumn<HeadlampKubeObject<CDPipelineKubeObjec
109110
/>
110111
);
111112
}}
113+
renderTooltip={(chipsToHide) => {
114+
return (
115+
<Box
116+
sx={{ py: (t) => t.typography.pxToRem(6), px: (t) => t.typography.pxToRem(10) }}
117+
>
118+
<Stack spacing={1.5} flexWrap="wrap" sx={{ fontWeight: 400 }}>
119+
{chipsToHide.map((label) => (
120+
<Chip
121+
key={label}
122+
sx={{
123+
backgroundColor: MAIN_COLOR.GREEN,
124+
borderColor: 'transparent',
125+
}}
126+
size="small"
127+
label={
128+
<Link
129+
routeName={routeComponentDetails.path}
130+
params={{
131+
name: label,
132+
namespace,
133+
}}
134+
style={{ color: 'white' }}
135+
>
136+
{label}
137+
</Link>
138+
}
139+
/>
140+
))}
141+
</Stack>
142+
</Box>
143+
);
144+
}}
112145
/>
113146
);
114147
},
148+
width: '45%',
115149
},
116150
{
117151
id: 'actions',

src/pages/component-details/components/Overview/hooks/useInfoRows.tsx

+32-23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import { Chip, Grid, Tooltip, Typography } from '@mui/material';
2-
import makeStyles from '@mui/styles/makeStyles';
3-
import clsx from 'clsx';
42
import React from 'react';
53
import { useParams } from 'react-router-dom';
64
import { InfoRow } from '../../../../../components/InfoColumns/types';
@@ -13,7 +11,7 @@ import {
1311
} from '../../../../../configs/icon-mappings';
1412
import { CODEBASE_TYPES } from '../../../../../constants/codebaseTypes';
1513
import { CODEBASE_VERSIONING_TYPES } from '../../../../../constants/codebaseVersioningTypes';
16-
import { STATUS_COLOR } from '../../../../../constants/colors';
14+
import { MAIN_COLOR } from '../../../../../constants/colors';
1715
import { RESOURCE_ICON_NAMES } from '../../../../../icons/sprites/Resources/names';
1816
import { CodebaseKubeObject } from '../../../../../k8s/groups/EDP/Codebase';
1917
import { capitalizeFirstLetter } from '../../../../../utils/format/capitalizeFirstLetter';
@@ -22,30 +20,39 @@ import { useDynamicDataContext } from '../../../providers/DynamicData/hooks';
2220
import { ComponentDetailsRouteParams } from '../../../types';
2321
import { Pipeline } from '../../Pipeline';
2422

25-
const useStyles = makeStyles((theme) => ({
26-
labelChip: {
27-
height: theme.typography.pxToRem(24),
28-
lineHeight: 1,
29-
paddingTop: theme.typography.pxToRem(2),
30-
},
31-
labelChipBlue: {
32-
backgroundColor: STATUS_COLOR.SUCCESS,
33-
color: '#fff',
34-
},
35-
labelChipGreen: {
36-
backgroundColor: STATUS_COLOR.SUCCESS,
37-
color: '#fff',
38-
},
39-
}));
23+
const getColorByType = (type: string) => {
24+
switch (type) {
25+
case CODEBASE_TYPES.SYSTEM:
26+
return MAIN_COLOR.GREY;
27+
case CODEBASE_TYPES.INFRASTRUCTURE:
28+
return MAIN_COLOR.DARK_PURPLE;
29+
case CODEBASE_TYPES.APPLICATION:
30+
return MAIN_COLOR.GREEN;
31+
case CODEBASE_TYPES.AUTOTEST:
32+
return MAIN_COLOR.ORANGE;
33+
case CODEBASE_TYPES.LIBRARY:
34+
return MAIN_COLOR.BLUE;
35+
default:
36+
return MAIN_COLOR.GREY;
37+
}
38+
};
39+
40+
const getChipSX = (type: string) => {
41+
const color = getColorByType(type);
42+
43+
return {
44+
color: (t) => t.palette.common.white,
45+
backgroundColor: color,
46+
borderColor: 'transparent',
47+
};
48+
};
4049

4150
export const useInfoRows = (): InfoRow[] | null => {
4251
const {
4352
component: { data: component },
4453
pipelines: { data: pipelines },
4554
} = useDynamicDataContext();
4655

47-
const classes = useStyles();
48-
4956
const { namespace } = useParams<ComponentDetailsRouteParams>();
5057

5158
return React.useMemo(() => {
@@ -115,8 +122,10 @@ export const useInfoRows = (): InfoRow[] | null => {
115122
text: (
116123
<Tooltip title={'Codebase Type'}>
117124
<Chip
118-
label={component?.spec.type}
119-
className={clsx([classes.labelChip, classes.labelChipGreen])}
125+
sx={getChipSX(type)}
126+
size="small"
127+
variant="outlined"
128+
label={capitalizeFirstLetter(type)}
120129
/>
121130
</Tooltip>
122131
),
@@ -191,5 +200,5 @@ export const useInfoRows = (): InfoRow[] | null => {
191200
: []),
192201
],
193202
];
194-
}, [classes.labelChip, classes.labelChipGreen, component, namespace, pipelines]);
203+
}, [component, namespace, pipelines]);
195204
};

src/widgets/PipelineRunList/hooks/useColumns.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export const useColumns = ({
7070
</Link>
7171
);
7272
},
73-
width: '25%',
73+
width: '15%',
7474
},
7575
{
7676
id: 'pipeline',
@@ -96,7 +96,7 @@ export const useColumns = ({
9696
</Link>
9797
);
9898
},
99-
width: '25%',
99+
width: '15%',
100100
},
101101
{
102102
id: 'pullRequestUrl',
@@ -193,7 +193,7 @@ export const useColumns = ({
193193

194194
return activeDuration;
195195
},
196-
width: '10%',
196+
width: '30%',
197197
},
198198
{
199199
id: 'diagram',

src/widgets/dialogs/ManageCDPipeline/components/Create/components/Form/index.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { getUsedValues } from '../../../../../../../utils/forms/getUsedValues';
99
import { FORM_STEPPER, FORM_STEPPER_STEPS } from '../../../../constants';
1010
import { useTypedFormContext } from '../../../../hooks/useFormContext';
1111
import { CDPIPELINE_FORM_NAMES } from '../../../../names';
12-
import { Applications, PipelineName } from '../../../fields';
12+
import { Applications, Description, PipelineName } from '../../../fields';
1313
import { FormProps } from './types';
1414

1515
export const Form = ({ editorOpen, editorData, setEditorOpen }: FormProps) => {
@@ -52,7 +52,10 @@ export const Form = ({ editorOpen, editorData, setEditorOpen }: FormProps) => {
5252
</Box>
5353
<Box sx={{ p: `${theme.typography.pxToRem(24)} ${theme.typography.pxToRem(8)}` }}>
5454
<TabPanel value={activeStep} index={FORM_STEPPER.PIPELINE.idx}>
55-
<PipelineName />
55+
<Stack spacing={2}>
56+
<PipelineName />
57+
<Description />
58+
</Stack>
5659
</TabPanel>
5760
<TabPanel value={activeStep} index={FORM_STEPPER.APPLICATIONS.idx}>
5861
<Applications />

0 commit comments

Comments
 (0)