Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import type { FormEvent, ReactElement } from 'react';
import { Flex, Form, FormGroup, TextInput } from '@patternfly/react-core';

import TypeaheadSelect from 'Components/TypeaheadSelect/TypeaheadSelect';
import type { TypeaheadSelectOption } from 'Components/TypeaheadSelect/TypeaheadSelect';
import type { ClusterScopeObject } from 'services/RolesService';
import type { PolicyExcludedDeployment } from 'types/policy.proto';

import PolicyScopeCardBase from './PolicyScopeCardBase';

type ExclusionScopeCardProps = {
excludedDeploymentScope: PolicyExcludedDeployment;
index: number;
clusters: ClusterScopeObject[];
handleChange: (event: FormEvent<HTMLInputElement>, value: string) => void;
setFieldValue: (field: string, value: unknown, shouldValidate?: boolean) => void;
onDelete: () => void;
};

function ExclusionScopeCard({
excludedDeploymentScope,
index,
clusters,
handleChange,
setFieldValue,
onDelete,
}: ExclusionScopeCardProps): ReactElement {
const scopePath = `excludedDeploymentScopes[${index}]`;

const clusterOptions: TypeaheadSelectOption[] = clusters.map((cluster) => ({
value: cluster.id,
label: cluster.name,
}));

return (
<PolicyScopeCardBase title="Exclusion scope" onDelete={onDelete}>
<Form>
<FormGroup label="Cluster">
<Flex direction={{ default: 'column' }}>
<TypeaheadSelect
id={`${scopePath}-cluster`}
value={excludedDeploymentScope.scope?.cluster ?? ''}
onChange={(clusterId) =>
setFieldValue(`${scopePath}.scope.cluster`, clusterId)
}
options={clusterOptions}
placeholder="Select a cluster"
isClearable
/>
</Flex>
</FormGroup>
<FormGroup label="Namespace">
<TextInput
aria-label="Namespace name"
name={`${scopePath}.scope.namespace`}
onChange={handleChange}
placeholder="Namespace name"
type="text"
value={excludedDeploymentScope.scope?.namespace ?? ''}
/>
</FormGroup>
<FormGroup label="Deployment">
<TextInput
aria-label="Deployment name"
name={`${scopePath}.name`}
onChange={handleChange}
placeholder="Deployment name"
type="text"
value={excludedDeploymentScope.name ?? ''}
/>
</FormGroup>
<FormGroup label="Deployment label">
<Flex direction={{ default: 'row' }} flexWrap={{ default: 'nowrap' }}>
<TextInput
aria-label="Deployment label key"
name={`${scopePath}.scope.label.key`}
onChange={handleChange}
placeholder="Label key"
type="text"
value={excludedDeploymentScope.scope?.label?.key ?? ''}
/>
<TextInput
aria-label="Deployment label value"
name={`${scopePath}.scope.label.value`}
onChange={handleChange}
placeholder="Label value"
type="text"
value={excludedDeploymentScope.scope?.label?.value ?? ''}
/>
</Flex>
</FormGroup>
</Form>
</PolicyScopeCardBase>
);
}

export default ExclusionScopeCard;
Original file line number Diff line number Diff line change
Expand Up @@ -63,103 +63,106 @@ function InclusionScopeCard({
<PolicyScopeCardBase title="Inclusion scope" onDelete={onDelete}>
<Form>
<FormGroup label="Cluster" role="radiogroup">
<Flex direction={{ default: 'row' }}>
<Radio
id={`scope-${index}-cluster-by-name`}
name={`inclusion-scope-${index}-cluster-mode`}
label="By name"
isChecked={clusterMode === 'name'}
onChange={() => handleChangeClusterMode('name')}
/>
<Radio
id={`scope-${index}-cluster-by-label`}
name={`inclusion-scope-${index}-cluster-mode`}
label="By label"
isChecked={clusterMode === 'label'}
onChange={() => handleChangeClusterMode('label')}
/>
</Flex>
{clusterMode === 'name' ? (
<TypeaheadSelect
id={`${scopePath}-cluster`}
value={scope.cluster}
onChange={(clusterId) =>
setFieldValue(`${scopePath}.cluster`, clusterId)
}
options={clusterOptions}
placeholder="Select a cluster"
className="pf-v6-u-w-100"
isClearable
/>
) : (
<Flex direction={{ default: 'row' }} flexWrap={{ default: 'nowrap' }}>
<TextInput
aria-label="Cluster label key"
name={`${scopePath}.clusterLabel.key`}
onChange={handleChange}
placeholder="Label key"
type="text"
value={scope.clusterLabel?.key ?? ''}
<Flex direction={{ default: 'column' }}>
<Flex direction={{ default: 'row' }}>
<Radio
id={`scope-${index}-cluster-by-name`}
name={`inclusion-scope-${index}-cluster-mode`}
label="By name"
isChecked={clusterMode === 'name'}
onChange={() => handleChangeClusterMode('name')}
/>
<TextInput
aria-label="Cluster label value"
name={`${scopePath}.clusterLabel.value`}
onChange={handleChange}
placeholder="Label value"
type="text"
value={scope.clusterLabel?.value ?? ''}
<Radio
id={`scope-${index}-cluster-by-label`}
name={`inclusion-scope-${index}-cluster-mode`}
label="By label"
isChecked={clusterMode === 'label'}
onChange={() => handleChangeClusterMode('label')}
/>
</Flex>
)}
{clusterMode === 'name' ? (
<TypeaheadSelect
id={`${scopePath}-cluster`}
value={scope.cluster}
onChange={(clusterId) =>
setFieldValue(`${scopePath}.cluster`, clusterId)
}
options={clusterOptions}
placeholder="Select a cluster"
isClearable
/>
) : (
<Flex direction={{ default: 'row' }} flexWrap={{ default: 'nowrap' }}>
<TextInput
aria-label="Cluster label key"
name={`${scopePath}.clusterLabel.key`}
onChange={handleChange}
placeholder="Label key"
type="text"
value={scope.clusterLabel?.key ?? ''}
/>
<TextInput
aria-label="Cluster label value"
name={`${scopePath}.clusterLabel.value`}
onChange={handleChange}
placeholder="Label value"
type="text"
value={scope.clusterLabel?.value ?? ''}
/>
</Flex>
)}
</Flex>
</FormGroup>
<FormGroup label="Namespace" role="radiogroup">
<Flex direction={{ default: 'row' }}>
<Radio
id={`scope-${index}-namespace-by-name`}
name={`inclusion-scope-${index}-namespace-mode`}
label="By name"
isChecked={namespaceMode === 'name'}
onChange={() => handleChangeNamespaceMode('name')}
/>
<Radio
id={`scope-${index}-namespace-by-label`}
name={`inclusion-scope-${index}-namespace-mode`}
label="By label"
isChecked={namespaceMode === 'label'}
onChange={() => handleChangeNamespaceMode('label')}
/>
</Flex>
{namespaceMode === 'name' ? (
<TextInput
aria-label="Namespace name"
name={`${scopePath}.namespace`}
onChange={handleChange}
placeholder="Namespace name"
type="text"
value={scope.namespace}
/>
) : (
<Flex direction={{ default: 'row' }} flexWrap={{ default: 'nowrap' }}>
<TextInput
aria-label="Namespace label key"
name={`${scopePath}.namespaceLabel.key`}
onChange={handleChange}
placeholder="Label key"
type="text"
value={scope.namespaceLabel?.key ?? ''}
<Flex direction={{ default: 'column' }}>
<Flex direction={{ default: 'row' }}>
<Radio
id={`scope-${index}-namespace-by-name`}
name={`inclusion-scope-${index}-namespace-mode`}
label="By name"
isChecked={namespaceMode === 'name'}
onChange={() => handleChangeNamespaceMode('name')}
/>
<Radio
id={`scope-${index}-namespace-by-label`}
name={`inclusion-scope-${index}-namespace-mode`}
label="By label"
isChecked={namespaceMode === 'label'}
onChange={() => handleChangeNamespaceMode('label')}
/>
</Flex>
{namespaceMode === 'name' ? (
<TextInput
aria-label="Namespace label value"
name={`${scopePath}.namespaceLabel.value`}
aria-label="Namespace name"
name={`${scopePath}.namespace`}
onChange={handleChange}
placeholder="Label value"
placeholder="Namespace name"
type="text"
value={scope.namespaceLabel?.value ?? ''}
value={scope.namespace}
/>
</Flex>
)}
) : (
<Flex direction={{ default: 'row' }} flexWrap={{ default: 'nowrap' }}>
<TextInput
aria-label="Namespace label key"
name={`${scopePath}.namespaceLabel.key`}
onChange={handleChange}
placeholder="Label key"
type="text"
value={scope.namespaceLabel?.key ?? ''}
/>
<TextInput
aria-label="Namespace label value"
name={`${scopePath}.namespaceLabel.value`}
onChange={handleChange}
placeholder="Label value"
type="text"
value={scope.namespaceLabel?.value ?? ''}
/>
</Flex>
)}
</Flex>
</FormGroup>
<FormGroup label="Deployment">
<FormGroup label="Deployment label">
<Flex direction={{ default: 'row' }} flexWrap={{ default: 'nowrap' }}>
<TextInput
aria-label="Deployment label key"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { getImages } from 'services/imageService';
import { initialExcludedDeployment, initialScope } from '../../policies.utils';
import PolicyScopeCardLegacy from './PolicyScopeCardLegacy';
import InclusionScopeCard from './InclusionScopeCard';
import ExclusionScopeCard from './ExclusionScopeCard';

function PolicyScopeForm(): ReactElement {
const [isExcludeImagesOpen, setIsExcludeImagesOpen] = useState(false);
Expand Down Expand Up @@ -209,13 +210,24 @@ function PolicyScopeForm(): ReactElement {
{excludedDeploymentScopes?.map((_, index) => (
// eslint-disable-next-line react/no-array-index-key
<GridItem key={index}>
<PolicyScopeCardLegacy
type="exclusion"
name={`excludedDeploymentScopes[${index}]`}
clusters={clusters}
onDelete={() => deleteExclusionDeploymentScope(index)}
hasAuditLogEventSource={hasAuditLogEventSource}
/>
{isFeatureFlagEnabled('ROX_LABEL_BASED_POLICY_SCOPING') ? (
<ExclusionScopeCard
index={index}
excludedDeploymentScope={excludedDeploymentScopes[index]}
clusters={clusters}
handleChange={handleChange}
setFieldValue={setFieldValue}
onDelete={() => deleteExclusionDeploymentScope(index)}
/>
) : (
<PolicyScopeCardLegacy
type="exclusion"
name={`excludedDeploymentScopes[${index}]`}
clusters={clusters}
onDelete={() => deleteExclusionDeploymentScope(index)}
hasAuditLogEventSource={hasAuditLogEventSource}
/>
)}
</GridItem>
))}
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ describe('Step 4', () => {
key: 'non-empty',
value: 'non-empty',
},
clusterLabel: null,
namespaceLabel: null,
},
},
],
Expand Down Expand Up @@ -395,6 +397,8 @@ describe('Step 4', () => {
key: 'non-empty',
value: 'non-empty',
},
clusterLabel: null,
namespaceLabel: null,
},
},
initialExcludedDeployment,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,9 @@ export const validationSchemaStep4: yup.ObjectSchema<WizardPolicyStep4> = yup.ob
scope: yup
.object({
cluster: yup.string().ensure(),
clusterLabel: labelSchema,
namespace: yup.string().ensure(),
namespaceLabel: labelSchema,
label: labelSchema,
})
.nullable(),
Expand All @@ -236,7 +238,11 @@ export const validationSchemaStep4: yup.ObjectSchema<WizardPolicyStep4> = yup.ob
value?.scope?.cluster.trim() ||
value?.scope?.namespace.trim() ||
value?.scope?.label?.key.trim() ||
value?.scope?.label?.value.trim()
value?.scope?.label?.value.trim() ||
value?.scope?.clusterLabel?.key.trim() ||
value?.scope?.clusterLabel?.value.trim() ||
value?.scope?.namespaceLabel?.key.trim() ||
value?.scope?.namespaceLabel?.value.trim()
)
)
)
Expand Down
Loading
Loading