Skip to content

Commit 71757e1

Browse files
authored
Merge pull request #2781 from devtron-labs/feat/condition-data-table-variable-type
feat: ConditionDataTable -improve table with variable type handling and validations
2 parents 7ef8b55 + 345fafd commit 71757e1

File tree

11 files changed

+203
-73
lines changed

11 files changed

+203
-73
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"homepage": "/dashboard",
66
"dependencies": {
7-
"@devtron-labs/devtron-fe-common-lib": "1.16.0-pre-1",
7+
"@devtron-labs/devtron-fe-common-lib": "1.16.0-pre-2",
88
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
99
"@rjsf/core": "^5.13.3",
1010
"@rjsf/utils": "^5.13.3",

src/components/CIPipelineN/ConditionDataTable/ConditionDataTable.component.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
getConditionDataTableInitialCellError,
3737
getConditionDataTableRowEmptyValidationState,
3838
getConditionDataTableRows,
39+
getConditionVariableTypeFormat,
3940
} from './utils'
4041

4142
export const ConditionDataTable = ({ type, conditionType, handleConditionTypeChange }: ConditionDataTableProps) => {
@@ -141,6 +142,10 @@ export const ConditionDataTable = ({ type, conditionType, handleConditionTypeCha
141142

142143
if (rowAction.headerKey === ConditionDataTableHeaderKeys.VARIABLE) {
143144
selectedRow.data.operator.value = EQUAL_NOT_EQUAL_TO_OPERATOR_OPTIONS[0].value
145+
selectedRow.customState.variableType = getConditionVariableTypeFormat({
146+
ioVariables,
147+
conditionOnVariable: rowAction.actionValue,
148+
})
144149
}
145150

146151
Object.values(ConditionDataTableHeaderKeys).forEach((key: ConditionDataTableHeaderKeys) => {

src/components/CIPipelineN/ConditionDataTable/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import { ChangeEvent } from 'react'
22

3-
import { ConditionDataTableHeaderKeys, ConditionType, DynamicDataTableProps } from '@devtron-labs/devtron-fe-common-lib'
3+
import {
4+
ConditionDataTableHeaderKeys,
5+
ConditionType,
6+
DynamicDataTableProps,
7+
VariableTypeFormat,
8+
} from '@devtron-labs/devtron-fe-common-lib'
49

510
import { ConditionContainerType } from '@Components/ciPipeline/types'
611

712
export interface ConditionDataTableCustomState {
813
conditionType: ConditionType
14+
variableType: VariableTypeFormat | null
915
}
1016

1117
export type ConditionDataTableType = DynamicDataTableProps<ConditionDataTableHeaderKeys, ConditionDataTableCustomState>

src/components/CIPipelineN/ConditionDataTable/utils.ts

Lines changed: 154 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ import {
44
ConditionType,
55
DynamicDataTableCellValidationState,
66
DynamicDataTableRowDataType,
7+
IO_VARIABLES_VALUE_COLUMN_BOOL_OPTIONS,
8+
IO_VARIABLES_VALUE_COLUMN_DATE_OPTIONS,
9+
PATTERNS,
710
PluginType,
811
SelectPickerOptionType,
12+
VALUE_COLUMN_DROPDOWN_LABEL,
913
VariableType,
1014
VariableTypeFormat,
1115
} from '@devtron-labs/devtron-fe-common-lib'
@@ -36,16 +40,27 @@ export const getConditionDataTableHeaders = (conditionType: ConditionType): Cond
3640

3741
const getConditionDataTableVariableOptions = (ioVariables: VariableType[]): SelectPickerOptionType<string>[] =>
3842
(ioVariables || [])
39-
.filter((variable) => variable.name)
43+
.filter((variable) => variable.name && variable.format !== VariableTypeFormat.FILE)
4044
.map((variable) => ({ label: variable.name, value: variable.name }))
4145

42-
const getOperatorOptionsBasedOnVariableTypeFormat = (
43-
conditionOnVariable: ConditionDetails['conditionOnVariable'],
44-
ioVariables: VariableType[],
45-
) => {
46-
const type = ioVariables.find(({ name }) => name === conditionOnVariable)?.format
46+
const getIOVariableBasedOnConditionOnVariable = ({
47+
conditionOnVariable,
48+
ioVariables,
49+
}: {
50+
conditionOnVariable: ConditionDetails['conditionOnVariable']
51+
ioVariables: VariableType[]
52+
}) => ioVariables.find(({ name }) => name === conditionOnVariable)
53+
54+
export const getConditionVariableTypeFormat = ({
55+
conditionOnVariable,
56+
ioVariables,
57+
}: {
58+
conditionOnVariable: ConditionDetails['conditionOnVariable']
59+
ioVariables: VariableType[]
60+
}) => getIOVariableBasedOnConditionOnVariable({ conditionOnVariable, ioVariables })?.format ?? null
4761

48-
switch (type) {
62+
const getOperatorOptionsBasedOnVariableTypeFormat = (variableType: VariableTypeFormat) => {
63+
switch (variableType) {
4964
case VariableTypeFormat.STRING:
5065
case VariableTypeFormat.BOOL:
5166
case VariableTypeFormat.FILE:
@@ -57,6 +72,81 @@ const getOperatorOptionsBasedOnVariableTypeFormat = (
5772
}
5873
}
5974

75+
const getConditionDataTableValColumnProps = ({
76+
conditionalValue,
77+
conditionOnVariable,
78+
ioVariables,
79+
}: {
80+
ioVariables: VariableType[]
81+
} & Pick<
82+
ConditionDetails,
83+
'conditionOnVariable' | 'conditionalValue'
84+
>): ConditionDataTableType['rows'][number]['data']['val'] => {
85+
const { valueConstraint, format } =
86+
getIOVariableBasedOnConditionOnVariable({ conditionOnVariable, ioVariables }) ?? {}
87+
88+
const choices = (valueConstraint?.choices || []).map<SelectPickerOptionType<string>>((value) => ({
89+
label: value,
90+
value,
91+
}))
92+
93+
if (format === VariableTypeFormat.NUMBER || format === VariableTypeFormat.STRING) {
94+
if (!choices.length) {
95+
return {
96+
type: DynamicDataTableRowDataType.TEXT,
97+
props: { placeholder: 'Enter value' },
98+
value: conditionalValue,
99+
}
100+
}
101+
102+
if (valueConstraint?.blockCustomValue) {
103+
return {
104+
type: DynamicDataTableRowDataType.DROPDOWN,
105+
props: {
106+
options: [{ label: VALUE_COLUMN_DROPDOWN_LABEL.CHOICES, options: choices }],
107+
placeholder: 'Select value',
108+
},
109+
value: conditionalValue,
110+
}
111+
}
112+
}
113+
114+
const formatConfig = {
115+
[VariableTypeFormat.BOOL]: {
116+
type: DynamicDataTableRowDataType.DROPDOWN,
117+
props: {
118+
options: [
119+
{ label: VALUE_COLUMN_DROPDOWN_LABEL.CHOICES, options: IO_VARIABLES_VALUE_COLUMN_BOOL_OPTIONS },
120+
],
121+
placeholder: 'Select value',
122+
},
123+
},
124+
[VariableTypeFormat.DATE]: {
125+
type: DynamicDataTableRowDataType.SELECT_TEXT,
126+
props: {
127+
options: [
128+
{
129+
label: VALUE_COLUMN_DROPDOWN_LABEL.SUPPORTED_DATE_FORMATS,
130+
options: IO_VARIABLES_VALUE_COLUMN_DATE_OPTIONS,
131+
},
132+
],
133+
placeholder: 'Enter value',
134+
},
135+
},
136+
}
137+
138+
return {
139+
...(formatConfig[format] || {
140+
type: DynamicDataTableRowDataType.SELECT_TEXT,
141+
props: {
142+
options: [{ label: VALUE_COLUMN_DROPDOWN_LABEL.CHOICES, options: choices }],
143+
placeholder: 'Enter value',
144+
},
145+
}),
146+
value: conditionalValue,
147+
}
148+
}
149+
60150
export const getConditionDataTableRows = ({
61151
conditionDetails,
62152
ioVariables,
@@ -69,39 +159,40 @@ export const getConditionDataTableRows = ({
69159
(conditionDetails || [])
70160
.filter(({ conditionType }) => conditionType === parentConditionType)
71161
.map<ConditionDataTableType['rows'][number]>(
72-
({ conditionOnVariable, conditionOperator, conditionalValue, conditionType, id }) => ({
73-
data: {
74-
variable: {
75-
type: DynamicDataTableRowDataType.DROPDOWN,
76-
props: {
77-
options: getConditionDataTableVariableOptions(ioVariables),
78-
placeholder: 'Select variable',
79-
isSearchable: false,
80-
autoFocus: true,
162+
({ conditionOnVariable, conditionOperator, conditionalValue, conditionType, id }) => {
163+
const variableType = getConditionVariableTypeFormat({ conditionOnVariable, ioVariables })
164+
return {
165+
data: {
166+
variable: {
167+
type: DynamicDataTableRowDataType.DROPDOWN,
168+
props: {
169+
options: getConditionDataTableVariableOptions(ioVariables),
170+
placeholder: 'Select variable',
171+
autoFocus: true,
172+
},
173+
value: conditionOnVariable,
81174
},
82-
value: conditionOnVariable,
83-
},
84-
operator: {
85-
type: DynamicDataTableRowDataType.DROPDOWN,
86-
props: {
87-
options: getOperatorOptionsBasedOnVariableTypeFormat(conditionOnVariable, ioVariables),
88-
isSearchable: false,
175+
operator: {
176+
type: DynamicDataTableRowDataType.DROPDOWN,
177+
props: {
178+
options: getOperatorOptionsBasedOnVariableTypeFormat(variableType),
179+
isSearchable: false,
180+
},
181+
value: conditionOperator,
89182
},
90-
value: conditionOperator,
183+
val: getConditionDataTableValColumnProps({
184+
conditionalValue,
185+
conditionOnVariable,
186+
ioVariables,
187+
}),
91188
},
92-
val: {
93-
type: DynamicDataTableRowDataType.TEXT,
94-
props: {
95-
placeholder: 'Enter value',
96-
},
97-
value: conditionalValue,
189+
id,
190+
customState: {
191+
conditionType,
192+
variableType,
98193
},
99-
},
100-
id,
101-
customState: {
102-
conditionType,
103-
},
104-
}),
194+
}
195+
},
105196
)
106197

107198
export const getConditionDataTableInitialCellError = (
@@ -199,16 +290,38 @@ export const convertConditionDataTableToFormData = ({
199290
export const validateConditionDataCell = ({
200291
key,
201292
condition: { conditionOnVariable, conditionOperator, conditionalValue },
293+
variableType,
202294
}: {
203295
key: ConditionDataTableHeaderKeys
204296
condition: Pick<ConditionDetails, 'conditionOnVariable' | 'conditionOperator' | 'conditionalValue'>
205-
}): DynamicDataTableCellValidationState => {
297+
} & Pick<
298+
ConditionDataTableType['rows'][number]['customState'],
299+
'variableType'
300+
>): DynamicDataTableCellValidationState => {
206301
if (key === ConditionDataTableHeaderKeys.VARIABLE && !conditionOnVariable) {
207302
return { errorMessages: ['Condition on variable is required'], isValid: false }
208303
}
209304

210-
if (key === ConditionDataTableHeaderKeys.VALUE && !conditionalValue) {
211-
return { errorMessages: ['Conditional value is required'], isValid: false }
305+
if (key === ConditionDataTableHeaderKeys.VALUE) {
306+
const numberReg = new RegExp(PATTERNS.NUMBERS_WITH_SCOPE_VARIABLES)
307+
const boolReg = new RegExp(PATTERNS.BOOLEAN_WITH_SCOPE_VARIABLES)
308+
309+
if (!conditionalValue) {
310+
return { errorMessages: ['Conditional value is required'], isValid: false }
311+
}
312+
313+
if (variableType === VariableTypeFormat.NUMBER && !numberReg.test(conditionalValue)) {
314+
return {
315+
errorMessages: ['Conditional value is not a number'],
316+
isValid: false,
317+
}
318+
}
319+
if (variableType === VariableTypeFormat.BOOL && !boolReg.test(conditionalValue)) {
320+
return {
321+
errorMessages: ['Conditional value is not a boolean'],
322+
isValid: false,
323+
}
324+
}
212325
}
213326

214327
if (key === ConditionDataTableHeaderKeys.OPERATOR && !conditionOperator) {
@@ -219,7 +332,7 @@ export const validateConditionDataCell = ({
219332
}
220333

221334
export const getConditionDataTableCellValidateState = ({
222-
row: { data },
335+
row: { data, customState },
223336
key,
224337
value,
225338
}: {
@@ -234,4 +347,5 @@ export const getConditionDataTableCellValidateState = ({
234347
conditionOnVariable: key === ConditionDataTableHeaderKeys.VARIABLE ? value : data.variable.value,
235348
conditionOperator: key === ConditionDataTableHeaderKeys.OPERATOR ? value : data.operator.value,
236349
},
350+
variableType: customState.variableType,
237351
})

src/components/CIPipelineN/VariableDataTable/constants.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,6 @@ export const getVariableDataTableHeaders = (
4747
},
4848
]
4949

50-
export const VAL_COLUMN_DROPDOWN_LABEL = {
51-
CHOICES: 'Choices',
52-
SUPPORTED_DATE_FORMATS: 'Supported date formats',
53-
SYSTEM_VARIABLES: 'System variables',
54-
PRE_BUILD_STAGE: 'From Pre-build Stage',
55-
POST_BUILD_STAGE: 'From Post-build Stage',
56-
PREVIOUS_STEPS: 'From Previous Steps',
57-
}
58-
5950
export const FORMAT_OPTIONS_MAP: Record<VariableTypeFormat, string> = {
6051
[VariableTypeFormat.STRING]: 'String',
6152
[VariableTypeFormat.NUMBER]: 'Number',

src/components/CIPipelineN/VariableDataTable/utils.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
RefVariableType,
3131
SelectPickerOptionType,
3232
SystemVariableIcon,
33+
VALUE_COLUMN_DROPDOWN_LABEL,
3334
VariableType,
3435
VariableTypeFormat,
3536
} from '@devtron-labs/devtron-fe-common-lib'
@@ -43,7 +44,6 @@ import {
4344
FILE_MOUNT_DIR,
4445
FILE_UPLOAD_SIZE_UNIT_OPTIONS,
4546
FORMAT_COLUMN_OPTIONS,
46-
VAL_COLUMN_DROPDOWN_LABEL,
4747
VARIABLE_DATA_TABLE_CELL_BOOL_VALUES,
4848
} from './constants'
4949
import {
@@ -175,34 +175,34 @@ const getOptionsForValColumn = ({
175175

176176
return [
177177
{
178-
label: VAL_COLUMN_DROPDOWN_LABEL.CHOICES,
178+
label: VALUE_COLUMN_DROPDOWN_LABEL.CHOICES,
179179
options: choices,
180180
},
181181
{
182-
label: VAL_COLUMN_DROPDOWN_LABEL.SUPPORTED_DATE_FORMATS,
182+
label: VALUE_COLUMN_DROPDOWN_LABEL.SUPPORTED_DATE_FORMATS,
183183
options: supportedDataFormats,
184184
},
185185
...(!valueConstraint?.blockCustomValue
186186
? [
187187
...(isBuildStagePostBuild
188188
? [
189189
{
190-
label: VAL_COLUMN_DROPDOWN_LABEL.PRE_BUILD_STAGE,
190+
label: VALUE_COLUMN_DROPDOWN_LABEL.PRE_BUILD_STAGE,
191191
options: filteredPreBuildStageVariablesBasedOnFormat,
192192
},
193193
{
194-
label: VAL_COLUMN_DROPDOWN_LABEL.POST_BUILD_STAGE,
194+
label: VALUE_COLUMN_DROPDOWN_LABEL.POST_BUILD_STAGE,
195195
options: filteredPreviousStepVariablesBasedOnFormat,
196196
},
197197
]
198198
: [
199199
{
200-
label: VAL_COLUMN_DROPDOWN_LABEL.PREVIOUS_STEPS,
200+
label: VALUE_COLUMN_DROPDOWN_LABEL.PREVIOUS_STEPS,
201201
options: filteredPreviousStepVariablesBasedOnFormat,
202202
},
203203
]),
204204
{
205-
label: VAL_COLUMN_DROPDOWN_LABEL.SYSTEM_VARIABLES,
205+
label: VALUE_COLUMN_DROPDOWN_LABEL.SYSTEM_VARIABLES,
206206
options: filteredGlobalVariablesBasedOnFormat,
207207
},
208208
]

src/components/ClusterNodes/ClusterList/ClusterSelectionBody.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ const ClusterSelectionBody: React.FC<ClusterSelectionBodyTypes> = ({
135135
return (
136136
<>
137137
{renderClusterBulkSelection()}
138-
<div className="flexbox-col flex-grow-1">
138+
<div className="flexbox-col flex-grow-1 dc__overflow-auto">
139139
{renderClusterList()}
140140
{showKubeConfigModal && KubeConfigModal && (
141141
<KubeConfigModal

0 commit comments

Comments
 (0)