Skip to content

Commit e637630

Browse files
Merge pull request #3254 from adamviktora/refactoring-desc-items
CNV-73006: refactor duplicated DescriptionItem usage
2 parents 9c9240d + a72711e commit e637630

File tree

16 files changed

+117
-268
lines changed

16 files changed

+117
-268
lines changed

src/utils/components/DescriptionItem/DescriptionItem.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { FC, ReactNode, useMemo } from 'react';
2+
import classNames from 'classnames';
23

34
import { DescriptionItemHeader } from '@kubevirt-utils/components/DescriptionItem/DescriptionItemHeader';
45
import MutedTextSpan from '@kubevirt-utils/components/MutedTextSpan/MutedTextSpan';
@@ -26,9 +27,9 @@ type DescriptionItemProps = {
2627
'data-test-id'?: string;
2728
descriptionData: any;
2829
descriptionHeader?: ReactNode;
29-
editOnTitleJustify?: boolean;
3030
isDisabled?: boolean;
3131
isEdit?: boolean;
32+
isLabelEditor?: boolean;
3233
isPopover?: boolean;
3334
label?: ReactNode;
3435
messageOnDisabled?: string;
@@ -46,9 +47,9 @@ const DescriptionItem: FC<DescriptionItemProps> = ({
4647
'data-test-id': testId,
4748
descriptionData,
4849
descriptionHeader,
49-
editOnTitleJustify = false,
5050
isDisabled,
5151
isEdit,
52+
isLabelEditor = false,
5253
isPopover,
5354
label,
5455
messageOnDisabled,
@@ -93,8 +94,9 @@ const DescriptionItem: FC<DescriptionItemProps> = ({
9394
<DescriptionListTermHelpText>
9495
<Flex
9596
justifyContent={{
96-
default: editOnTitleJustify ? 'justifyContentSpaceBetween' : 'justifyContentFlexStart',
97+
default: isLabelEditor ? 'justifyContentSpaceBetween' : 'justifyContentFlexStart',
9798
}}
99+
className={classNames({ 'pf-v6-u-w-100': isLabelEditor })}
98100
>
99101
{(bodyContent || breadcrumb || descriptionHeader || label || moreInfoURL) && (
100102
<FlexItem>
@@ -127,7 +129,10 @@ const DescriptionItem: FC<DescriptionItemProps> = ({
127129
</Flex>
128130
</DescriptionListTermHelpText>
129131

130-
<DescriptionListDescription data-test-id={testId}>
132+
<DescriptionListDescription
133+
className={classNames({ 'co-editable-label-group': isLabelEditor })}
134+
data-test-id={testId}
135+
>
131136
{subTitle && <div className="pf-v6-c-description-list__text pf-v6-u-my-sm">{subTitle}</div>}
132137
{description}
133138
</DescriptionListDescription>

src/utils/components/DescriptionItem/components/DescriptionItemAnnotations.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { FC } from 'react';
1+
import React, { FC, ReactNode } from 'react';
22

33
import { AnnotationsModal } from '@kubevirt-utils/components/AnnotationsModal/AnnotationsModal';
44
import DescriptionItem from '@kubevirt-utils/components/DescriptionItem/DescriptionItem';
@@ -9,16 +9,22 @@ import { getAnnotations } from '@kubevirt-utils/resources/shared';
99
import { K8sModel, k8sPatch } from '@openshift-console/dynamic-plugin-sdk';
1010

1111
type DescriptionItemAnnotationsProps = {
12+
className?: string;
13+
descriptionHeaderWrapper?: (children: string) => ReactNode;
1214
editable?: boolean;
1315
label?: string;
1416
model: K8sModel;
17+
onAnnotationsSubmit?: (annotations: { [key: string]: string }) => Promise<any>;
1518
resource: K8sResourceCommon;
1619
};
1720

1821
const DescriptionItemAnnotations: FC<DescriptionItemAnnotationsProps> = ({
22+
className,
23+
descriptionHeaderWrapper,
1924
editable = true,
2025
label,
2126
model,
27+
onAnnotationsSubmit,
2228
resource,
2329
}) => {
2430
const { createModal } = useModal();
@@ -28,7 +34,7 @@ const DescriptionItemAnnotations: FC<DescriptionItemAnnotationsProps> = ({
2834
annotationsCount,
2935
});
3036

31-
const onAnnotationsSubmit = (updatedAnnotations: { [key: string]: string }) =>
37+
const onAnnotationsSubmitInternal = (updatedAnnotations: { [key: string]: string }) =>
3238
k8sPatch({
3339
data: [
3440
{
@@ -47,19 +53,23 @@ const DescriptionItemAnnotations: FC<DescriptionItemAnnotationsProps> = ({
4753
isOpen={isOpen}
4854
obj={resource}
4955
onClose={onClose}
50-
onSubmit={onAnnotationsSubmit}
56+
onSubmit={onAnnotationsSubmit ?? onAnnotationsSubmitInternal}
5157
/>
5258
));
5359

60+
const annotationsHeader = t('Annotations');
61+
const descriptionHeader = descriptionHeaderWrapper?.(annotationsHeader) ?? annotationsHeader;
62+
5463
return (
5564
<DescriptionItem
5665
// body-content text copied from: https://github.com/kubevirt-ui/kubevirt-api/blob/main/containerized-data-importer/models/V1ObjectMeta.ts#L32
5766
bodyContent={t(
5867
'Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects.',
5968
)}
6069
breadcrumb={`${label ?? model.label}.metadata.annotations`}
70+
className={className}
6171
descriptionData={annotationsText}
62-
descriptionHeader={t('Annotations')}
72+
descriptionHeader={descriptionHeader}
6373
isEdit={editable}
6474
isPopover
6575
moreInfoURL={documentationURL.ANNOTATIONS}

src/utils/components/DescriptionItem/components/DescriptionItemLabels.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { FC } from 'react';
1+
import React, { FC, ReactNode } from 'react';
22

33
import DescriptionItem from '@kubevirt-utils/components/DescriptionItem/DescriptionItem';
44
import { LabelsModal } from '@kubevirt-utils/components/LabelsModal/LabelsModal';
@@ -10,22 +10,28 @@ import { getLabels, getName } from '@kubevirt-utils/resources/shared';
1010
import { K8sModel, k8sPatch } from '@openshift-console/dynamic-plugin-sdk';
1111

1212
type DescriptionItemLabelsProps = {
13+
className?: string;
14+
descriptionHeaderWrapper?: (children: string) => ReactNode;
1315
editable?: boolean;
1416
label?: string;
1517
model: K8sModel;
18+
onLabelsSubmit?: (labels: { [key: string]: string }) => Promise<any>;
1619
resource: K8sResourceCommon;
1720
};
1821

1922
const DescriptionItemLabels: FC<DescriptionItemLabelsProps> = ({
23+
className,
24+
descriptionHeaderWrapper,
2025
editable = true,
2126
label,
2227
model,
28+
onLabelsSubmit,
2329
resource,
2430
}) => {
2531
const { createModal } = useModal();
2632
const { t } = useKubevirtTranslation();
2733

28-
const onLabelsSubmit = (labels: { [key: string]: string }) =>
34+
const onLabelsSubmitInternal = (labels: { [key: string]: string }) =>
2935
k8sPatch({
3036
data: [
3137
{
@@ -44,21 +50,26 @@ const DescriptionItemLabels: FC<DescriptionItemLabelsProps> = ({
4450
isOpen={isOpen}
4551
obj={resource}
4652
onClose={onClose}
47-
onLabelsSubmit={onLabelsSubmit}
53+
onLabelsSubmit={onLabelsSubmit ?? onLabelsSubmitInternal}
4854
/>
4955
));
5056

57+
const labelsHeader = t('Labels');
58+
const descriptionHeader = descriptionHeaderWrapper?.(labelsHeader) ?? labelsHeader;
59+
5160
return (
5261
<DescriptionItem
5362
// body-content text copied from: https://github.com/kubevirt-ui/kubevirt-api/blob/main/containerized-data-importer/models/V1ObjectMeta.ts#L84
5463
bodyContent={t(
5564
'Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services.',
5665
)}
5766
breadcrumb={`${label ?? model.label}.metadata.labels`}
67+
className={className}
5868
data-test-id={`${getName(resource)}-labels`}
5969
descriptionData={<MetadataLabels labels={getLabels(resource)} model={model} />}
60-
descriptionHeader={t('Labels')}
70+
descriptionHeader={descriptionHeader}
6171
isEdit={editable}
72+
isLabelEditor
6273
isPopover
6374
moreInfoURL={documentationURL.LABELS}
6475
onEditClick={onEditClick}

src/utils/components/DescriptionItem/components/DescriptionItemNamespace.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ import { documentationURL } from '@kubevirt-utils/constants/documentation';
55
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
66
import { NamespaceModel } from '@kubevirt-utils/models';
77
import { getNamespace } from '@kubevirt-utils/resources/shared';
8-
import {
9-
getGroupVersionKindForModel,
10-
K8sModel,
11-
ResourceLink,
12-
} from '@openshift-console/dynamic-plugin-sdk';
8+
import { getCluster } from '@multicluster/helpers/selectors';
9+
import { getGroupVersionKindForModel, K8sModel } from '@openshift-console/dynamic-plugin-sdk';
10+
import { FleetResourceLink } from '@stolostron/multicluster-sdk';
1311

1412
type DescriptionItemNamespaceProps = {
1513
label?: string;
@@ -24,6 +22,7 @@ const DescriptionItemNamespace: FC<DescriptionItemNamespaceProps> = ({
2422
}) => {
2523
const { t } = useKubevirtTranslation();
2624

25+
const cluster = getCluster(resource);
2726
const namespace = getNamespace(resource);
2827

2928
return (
@@ -33,7 +32,8 @@ const DescriptionItemNamespace: FC<DescriptionItemNamespaceProps> = ({
3332
'Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty. Must be a DNS_LABEL. Cannot be updated.',
3433
)}
3534
descriptionData={
36-
<ResourceLink
35+
<FleetResourceLink
36+
cluster={cluster}
3737
groupVersionKind={getGroupVersionKindForModel(NamespaceModel)}
3838
name={namespace}
3939
/>

src/utils/components/MetadataLabels/MetadataLabels.scss

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/utils/components/MetadataLabels/MetadataLabels.tsx

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,41 @@
11
import React, { FC } from 'react';
2-
import { Link } from 'react-router-dom-v5-compat';
2+
import { useNavigate } from 'react-router-dom-v5-compat';
33

44
import { modelToRef } from '@kubevirt-ui/kubevirt-api/console';
5+
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
6+
import { isEmpty } from '@kubevirt-utils/utils/utils';
57
import { K8sModel } from '@openshift-console/dynamic-plugin-sdk';
68
import { Label, LabelGroup } from '@patternfly/react-core';
79

8-
import './MetadataLabels.scss';
9-
1010
type MetadataLabelsProps = {
1111
labels?: { [key: string]: string };
1212
model: K8sModel;
1313
};
1414

1515
const MetadataLabels: FC<MetadataLabelsProps> = ({ labels, model }) => {
16+
const { t } = useKubevirtTranslation();
1617
const modelRef = modelToRef(model);
18+
const navigate = useNavigate();
19+
20+
const labelsKeys = Object.keys(labels || {});
21+
22+
if (isEmpty(labelsKeys)) {
23+
return <div className="pf-v6-u-text-color-subtle">{t('No labels')}</div>;
24+
}
1725

1826
return (
19-
<LabelGroup className="metadata-labels-group" numLabels={10}>
20-
{Object.keys(labels || {})?.map((key) => {
27+
<LabelGroup numLabels={10}>
28+
{labelsKeys.map((key) => {
2129
const labelText = labels[key] ? `${key}=${labels[key]}` : key;
2230

2331
return (
24-
<Label className="co-label" key={key}>
25-
<Link
26-
className="pf-v6-c-label__content"
27-
to={`/search?kind=${modelRef}&q=${encodeURIComponent(labelText)}`}
28-
>
29-
{labelText}
30-
</Link>
32+
<Label
33+
className="co-label"
34+
isClickable
35+
key={key}
36+
onClick={() => navigate(`/search?kind=${modelRef}&q=${encodeURIComponent(labelText)}`)}
37+
>
38+
{labelText}
3139
</Label>
3240
);
3341
})}

src/views/catalog/CustomizeInstanceType/tabs/configuration/utils/tabs/CustomizeInstanceTypeMetadataTab.tsx

Lines changed: 14 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
import React from 'react';
22

3-
import { AnnotationsModal } from '@kubevirt-utils/components/AnnotationsModal/AnnotationsModal';
4-
import DescriptionItem from '@kubevirt-utils/components/DescriptionItem/DescriptionItem';
5-
import { LabelsModal } from '@kubevirt-utils/components/LabelsModal/LabelsModal';
3+
import VirtualMachineModel from '@kubevirt-ui/kubevirt-api/console/models/VirtualMachineModel';
4+
import DescriptionItemAnnotations from '@kubevirt-utils/components/DescriptionItem/components/DescriptionItemAnnotations';
5+
import DescriptionItemLabels from '@kubevirt-utils/components/DescriptionItem/components/DescriptionItemLabels';
66
import Loading from '@kubevirt-utils/components/Loading/Loading';
7-
import { useModal } from '@kubevirt-utils/components/ModalProvider/ModalProvider';
87
import SearchItem from '@kubevirt-utils/components/SearchItem/SearchItem';
9-
import { documentationURL } from '@kubevirt-utils/constants/documentation';
108
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
11-
import { getName } from '@kubevirt-utils/resources/shared';
129
import { updateCustomizeInstanceType, vmSignal } from '@kubevirt-utils/store/customizeInstanceType';
1310
import { DescriptionList, Grid, PageSection, Title } from '@patternfly/react-core';
14-
import MetadataTabAnnotations from '@virtualmachines/details/tabs/configuration/metadata/components/MetadataTabAnnotations/MetadataTabAnnotations';
15-
import MetadataTabLabels from '@virtualmachines/details/tabs/configuration/metadata/components/MetadataTabLabels/MetadataTabLabels';
1611

1712
const CustomizeInstanceTypeMetadataTab = () => {
18-
const { createModal } = useModal();
1913
const { t } = useKubevirtTranslation();
2014
const vm = vmSignal.value;
2115

@@ -40,50 +34,19 @@ const CustomizeInstanceTypeMetadataTab = () => {
4034
</Title>
4135
<Grid span={6}>
4236
<DescriptionList>
43-
<DescriptionItem
44-
bodyContent={t(
45-
'Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services.',
46-
)}
47-
onEditClick={() =>
48-
createModal(({ isOpen, onClose }) => (
49-
<LabelsModal
50-
isOpen={isOpen}
51-
obj={vm}
52-
onClose={onClose}
53-
onLabelsSubmit={(labels) => updateMetadata(labels, 'labels')}
54-
/>
55-
))
56-
}
57-
breadcrumb="VirtualMachine.metadata.labels"
58-
data-test-id={`${getName(vm)}-labels`}
59-
descriptionData={<MetadataTabLabels labels={vm?.metadata?.labels} />}
60-
descriptionHeader={<SearchItem id="labels">{t('Labels')}</SearchItem>}
61-
editOnTitleJustify
62-
isEdit
63-
isPopover
64-
moreInfoURL={documentationURL.LABELS}
65-
showEditOnTitle
37+
<DescriptionItemLabels
38+
descriptionHeaderWrapper={(children) => <SearchItem id="labels">{children}</SearchItem>}
39+
model={VirtualMachineModel}
40+
onLabelsSubmit={(labels) => updateMetadata(labels, 'labels')}
41+
resource={vm}
6642
/>
67-
<DescriptionItem
68-
bodyContent={t(
69-
'Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects.',
43+
<DescriptionItemAnnotations
44+
descriptionHeaderWrapper={(children) => (
45+
<SearchItem id="metadata">{children}</SearchItem>
7046
)}
71-
onEditClick={() =>
72-
createModal(({ isOpen, onClose }) => (
73-
<AnnotationsModal
74-
isOpen={isOpen}
75-
obj={vm}
76-
onClose={onClose}
77-
onSubmit={(annotations) => updateMetadata(annotations, 'annotations')}
78-
/>
79-
))
80-
}
81-
breadcrumb="VirtualMachine.metadata.annotations"
82-
descriptionData={<MetadataTabAnnotations annotations={vm?.metadata?.annotations} />}
83-
descriptionHeader={<SearchItem id="metadata">{t('Annotations')}</SearchItem>}
84-
isEdit
85-
isPopover
86-
moreInfoURL={documentationURL.ANNOTATIONS}
47+
model={VirtualMachineModel}
48+
onAnnotationsSubmit={(annotations) => updateMetadata(annotations, 'annotations')}
49+
resource={vm}
8750
/>
8851
</DescriptionList>
8952
</Grid>

src/views/catalog/wizard/components/WizardDescriptionItem/WizardDescriptionItem.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { FC, ReactNode } from 'react';
2+
import classNames from 'classnames';
23

34
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
45
import {
@@ -33,6 +34,8 @@ type WizardDescriptionItemProps = {
3334
isDisabled?: boolean;
3435
/** is the description group editable */
3536
isEdit?: boolean;
37+
/** flag indicating if the description item is a label editor */
38+
isLabelEditor?: boolean;
3639
label?: ReactNode;
3740
/** onClick callback for the edit button */
3841
onEditClick?: () => void;
@@ -55,6 +58,7 @@ export const WizardDescriptionItem: FC<WizardDescriptionItemProps> = React.memo(
5558
helpTextIcon,
5659
isDisabled,
5760
isEdit,
61+
isLabelEditor = false,
5862
label,
5963
onEditClick,
6064
onTitleClick,
@@ -135,7 +139,9 @@ export const WizardDescriptionItem: FC<WizardDescriptionItemProps> = React.memo(
135139
</DescriptionListDescription>
136140
) : (
137141
<div data-test-id={testId}>
138-
<DescriptionListDescription>
142+
<DescriptionListDescription
143+
className={classNames({ 'co-editable-label-group': isLabelEditor })}
144+
>
139145
{description ?? (
140146
<span className="pf-v6-u-text-color-subtle">{t('Not available')}</span>
141147
)}

0 commit comments

Comments
 (0)