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
13 changes: 9 additions & 4 deletions src/utils/components/DescriptionItem/DescriptionItem.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { FC, ReactNode, useMemo } from 'react';
import classNames from 'classnames';

import { DescriptionItemHeader } from '@kubevirt-utils/components/DescriptionItem/DescriptionItemHeader';
import MutedTextSpan from '@kubevirt-utils/components/MutedTextSpan/MutedTextSpan';
Expand Down Expand Up @@ -26,9 +27,9 @@ type DescriptionItemProps = {
'data-test-id'?: string;
descriptionData: any;
descriptionHeader?: ReactNode;
editOnTitleJustify?: boolean;
isDisabled?: boolean;
isEdit?: boolean;
isLabelEditor?: boolean;
isPopover?: boolean;
label?: ReactNode;
messageOnDisabled?: string;
Expand All @@ -46,9 +47,9 @@ const DescriptionItem: FC<DescriptionItemProps> = ({
'data-test-id': testId,
descriptionData,
descriptionHeader,
editOnTitleJustify = false,
isDisabled,
isEdit,
isLabelEditor = false,
isPopover,
label,
messageOnDisabled,
Expand Down Expand Up @@ -93,8 +94,9 @@ const DescriptionItem: FC<DescriptionItemProps> = ({
<DescriptionListTermHelpText>
<Flex
justifyContent={{
default: editOnTitleJustify ? 'justifyContentSpaceBetween' : 'justifyContentFlexStart',
default: isLabelEditor ? 'justifyContentSpaceBetween' : 'justifyContentFlexStart',
}}
className={classNames({ 'pf-v6-u-w-100': isLabelEditor })}
>
{(bodyContent || breadcrumb || descriptionHeader || label || moreInfoURL) && (
<FlexItem>
Expand Down Expand Up @@ -127,7 +129,10 @@ const DescriptionItem: FC<DescriptionItemProps> = ({
</Flex>
</DescriptionListTermHelpText>

<DescriptionListDescription data-test-id={testId}>
<DescriptionListDescription
className={classNames({ 'co-editable-label-group': isLabelEditor })}
data-test-id={testId}
>
{subTitle && <div className="pf-v6-c-description-list__text pf-v6-u-my-sm">{subTitle}</div>}
{description}
</DescriptionListDescription>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC } from 'react';
import React, { FC, ReactNode } from 'react';

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

type DescriptionItemAnnotationsProps = {
className?: string;
descriptionHeaderWrapper?: (children: string) => ReactNode;
editable?: boolean;
label?: string;
model: K8sModel;
onAnnotationsSubmit?: (annotations: { [key: string]: string }) => Promise<any>;
resource: K8sResourceCommon;
};

const DescriptionItemAnnotations: FC<DescriptionItemAnnotationsProps> = ({
className,
descriptionHeaderWrapper,
editable = true,
label,
model,
onAnnotationsSubmit,
resource,
}) => {
const { createModal } = useModal();
Expand All @@ -28,7 +34,7 @@ const DescriptionItemAnnotations: FC<DescriptionItemAnnotationsProps> = ({
annotationsCount,
});

const onAnnotationsSubmit = (updatedAnnotations: { [key: string]: string }) =>
const onAnnotationsSubmitInternal = (updatedAnnotations: { [key: string]: string }) =>
k8sPatch({
data: [
{
Expand All @@ -47,19 +53,23 @@ const DescriptionItemAnnotations: FC<DescriptionItemAnnotationsProps> = ({
isOpen={isOpen}
obj={resource}
onClose={onClose}
onSubmit={onAnnotationsSubmit}
onSubmit={onAnnotationsSubmit ?? onAnnotationsSubmitInternal}
/>
));

const annotationsHeader = t('Annotations');
const descriptionHeader = descriptionHeaderWrapper?.(annotationsHeader) ?? annotationsHeader;

return (
<DescriptionItem
// body-content text copied from: https://github.com/kubevirt-ui/kubevirt-api/blob/main/containerized-data-importer/models/V1ObjectMeta.ts#L32
bodyContent={t(
'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.',
)}
breadcrumb={`${label ?? model.label}.metadata.annotations`}
className={className}
descriptionData={annotationsText}
descriptionHeader={t('Annotations')}
descriptionHeader={descriptionHeader}
isEdit={editable}
isPopover
moreInfoURL={documentationURL.ANNOTATIONS}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC } from 'react';
import React, { FC, ReactNode } from 'react';

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

type DescriptionItemLabelsProps = {
className?: string;
descriptionHeaderWrapper?: (children: string) => ReactNode;
editable?: boolean;
label?: string;
model: K8sModel;
onLabelsSubmit?: (labels: { [key: string]: string }) => Promise<any>;
resource: K8sResourceCommon;
};

const DescriptionItemLabels: FC<DescriptionItemLabelsProps> = ({
className,
descriptionHeaderWrapper,
editable = true,
label,
model,
onLabelsSubmit,
resource,
}) => {
const { createModal } = useModal();
const { t } = useKubevirtTranslation();

const onLabelsSubmit = (labels: { [key: string]: string }) =>
const onLabelsSubmitInternal = (labels: { [key: string]: string }) =>
k8sPatch({
data: [
{
Expand All @@ -44,21 +50,26 @@ const DescriptionItemLabels: FC<DescriptionItemLabelsProps> = ({
isOpen={isOpen}
obj={resource}
onClose={onClose}
onLabelsSubmit={onLabelsSubmit}
onLabelsSubmit={onLabelsSubmit ?? onLabelsSubmitInternal}
/>
));

const labelsHeader = t('Labels');
const descriptionHeader = descriptionHeaderWrapper?.(labelsHeader) ?? labelsHeader;

return (
<DescriptionItem
// body-content text copied from: https://github.com/kubevirt-ui/kubevirt-api/blob/main/containerized-data-importer/models/V1ObjectMeta.ts#L84
bodyContent={t(
'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.',
)}
breadcrumb={`${label ?? model.label}.metadata.labels`}
className={className}
data-test-id={`${getName(resource)}-labels`}
descriptionData={<MetadataLabels labels={getLabels(resource)} model={model} />}
descriptionHeader={t('Labels')}
descriptionHeader={descriptionHeader}
isEdit={editable}
isLabelEditor
isPopover
moreInfoURL={documentationURL.LABELS}
onEditClick={onEditClick}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import { documentationURL } from '@kubevirt-utils/constants/documentation';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { NamespaceModel } from '@kubevirt-utils/models';
import { getNamespace } from '@kubevirt-utils/resources/shared';
import {
getGroupVersionKindForModel,
K8sModel,
ResourceLink,
} from '@openshift-console/dynamic-plugin-sdk';
import { getCluster } from '@multicluster/helpers/selectors';
import { getGroupVersionKindForModel, K8sModel } from '@openshift-console/dynamic-plugin-sdk';
import { FleetResourceLink } from '@stolostron/multicluster-sdk';

type DescriptionItemNamespaceProps = {
label?: string;
Expand All @@ -24,6 +22,7 @@ const DescriptionItemNamespace: FC<DescriptionItemNamespaceProps> = ({
}) => {
const { t } = useKubevirtTranslation();

const cluster = getCluster(resource);
const namespace = getNamespace(resource);

return (
Expand All @@ -33,7 +32,8 @@ const DescriptionItemNamespace: FC<DescriptionItemNamespaceProps> = ({
'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.',
)}
descriptionData={
<ResourceLink
<FleetResourceLink
cluster={cluster}
groupVersionKind={getGroupVersionKindForModel(NamespaceModel)}
name={namespace}
/>
Expand Down
8 changes: 0 additions & 8 deletions src/utils/components/MetadataLabels/MetadataLabels.scss

This file was deleted.

32 changes: 20 additions & 12 deletions src/utils/components/MetadataLabels/MetadataLabels.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
import React, { FC } from 'react';
import { Link } from 'react-router-dom-v5-compat';
import { useNavigate } from 'react-router-dom-v5-compat';

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

import './MetadataLabels.scss';

type MetadataLabelsProps = {
labels?: { [key: string]: string };
model: K8sModel;
};

const MetadataLabels: FC<MetadataLabelsProps> = ({ labels, model }) => {
const { t } = useKubevirtTranslation();
const modelRef = modelToRef(model);
const navigate = useNavigate();

const labelsKeys = Object.keys(labels || {});

if (isEmpty(labelsKeys)) {
return <div className="pf-v6-u-text-color-subtle">{t('No labels')}</div>;
}

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

return (
<Label className="co-label" key={key}>
<Link
className="pf-v6-c-label__content"
to={`/search?kind=${modelRef}&q=${encodeURIComponent(labelText)}`}
>
{labelText}
</Link>
<Label
className="co-label"
isClickable
key={key}
onClick={() => navigate(`/search?kind=${modelRef}&q=${encodeURIComponent(labelText)}`)}
>
{labelText}
</Label>
);
})}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import React from 'react';

import { AnnotationsModal } from '@kubevirt-utils/components/AnnotationsModal/AnnotationsModal';
import DescriptionItem from '@kubevirt-utils/components/DescriptionItem/DescriptionItem';
import { LabelsModal } from '@kubevirt-utils/components/LabelsModal/LabelsModal';
import VirtualMachineModel from '@kubevirt-ui/kubevirt-api/console/models/VirtualMachineModel';
import DescriptionItemAnnotations from '@kubevirt-utils/components/DescriptionItem/components/DescriptionItemAnnotations';
import DescriptionItemLabels from '@kubevirt-utils/components/DescriptionItem/components/DescriptionItemLabels';
import Loading from '@kubevirt-utils/components/Loading/Loading';
import { useModal } from '@kubevirt-utils/components/ModalProvider/ModalProvider';
import SearchItem from '@kubevirt-utils/components/SearchItem/SearchItem';
import { documentationURL } from '@kubevirt-utils/constants/documentation';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { getName } from '@kubevirt-utils/resources/shared';
import { updateCustomizeInstanceType, vmSignal } from '@kubevirt-utils/store/customizeInstanceType';
import { DescriptionList, Grid, PageSection, Title } from '@patternfly/react-core';
import MetadataTabAnnotations from '@virtualmachines/details/tabs/configuration/metadata/components/MetadataTabAnnotations/MetadataTabAnnotations';
import MetadataTabLabels from '@virtualmachines/details/tabs/configuration/metadata/components/MetadataTabLabels/MetadataTabLabels';

const CustomizeInstanceTypeMetadataTab = () => {
const { createModal } = useModal();
const { t } = useKubevirtTranslation();
const vm = vmSignal.value;

Expand All @@ -40,50 +34,19 @@ const CustomizeInstanceTypeMetadataTab = () => {
</Title>
<Grid span={6}>
<DescriptionList>
<DescriptionItem
bodyContent={t(
'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.',
)}
onEditClick={() =>
createModal(({ isOpen, onClose }) => (
<LabelsModal
isOpen={isOpen}
obj={vm}
onClose={onClose}
onLabelsSubmit={(labels) => updateMetadata(labels, 'labels')}
/>
))
}
breadcrumb="VirtualMachine.metadata.labels"
data-test-id={`${getName(vm)}-labels`}
descriptionData={<MetadataTabLabels labels={vm?.metadata?.labels} />}
descriptionHeader={<SearchItem id="labels">{t('Labels')}</SearchItem>}
editOnTitleJustify
isEdit
isPopover
moreInfoURL={documentationURL.LABELS}
showEditOnTitle
<DescriptionItemLabels
descriptionHeaderWrapper={(children) => <SearchItem id="labels">{children}</SearchItem>}
model={VirtualMachineModel}
onLabelsSubmit={(labels) => updateMetadata(labels, 'labels')}
resource={vm}
/>
<DescriptionItem
bodyContent={t(
'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.',
<DescriptionItemAnnotations
descriptionHeaderWrapper={(children) => (
<SearchItem id="metadata">{children}</SearchItem>
)}
onEditClick={() =>
createModal(({ isOpen, onClose }) => (
<AnnotationsModal
isOpen={isOpen}
obj={vm}
onClose={onClose}
onSubmit={(annotations) => updateMetadata(annotations, 'annotations')}
/>
))
}
breadcrumb="VirtualMachine.metadata.annotations"
descriptionData={<MetadataTabAnnotations annotations={vm?.metadata?.annotations} />}
descriptionHeader={<SearchItem id="metadata">{t('Annotations')}</SearchItem>}
isEdit
isPopover
moreInfoURL={documentationURL.ANNOTATIONS}
model={VirtualMachineModel}
onAnnotationsSubmit={(annotations) => updateMetadata(annotations, 'annotations')}
resource={vm}
/>
</DescriptionList>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { FC, ReactNode } from 'react';
import classNames from 'classnames';

import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import {
Expand Down Expand Up @@ -33,6 +34,8 @@ type WizardDescriptionItemProps = {
isDisabled?: boolean;
/** is the description group editable */
isEdit?: boolean;
/** flag indicating if the description item is a label editor */
isLabelEditor?: boolean;
label?: ReactNode;
/** onClick callback for the edit button */
onEditClick?: () => void;
Expand All @@ -55,6 +58,7 @@ export const WizardDescriptionItem: FC<WizardDescriptionItemProps> = React.memo(
helpTextIcon,
isDisabled,
isEdit,
isLabelEditor = false,
label,
onEditClick,
onTitleClick,
Expand Down Expand Up @@ -135,7 +139,9 @@ export const WizardDescriptionItem: FC<WizardDescriptionItemProps> = React.memo(
</DescriptionListDescription>
) : (
<div data-test-id={testId}>
<DescriptionListDescription>
<DescriptionListDescription
className={classNames({ 'co-editable-label-group': isLabelEditor })}
>
{description ?? (
<span className="pf-v6-u-text-color-subtle">{t('Not available')}</span>
)}
Expand Down
Loading