Skip to content

Commit

Permalink
Add ability to add/edit/remove pulp labels (#123)
Browse files Browse the repository at this point in the history
  • Loading branch information
gerrod3 authored Oct 26, 2024
1 parent de631f0 commit 609389e
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 12 deletions.
23 changes: 23 additions & 0 deletions src/components/ansible-repository-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
FormFieldHelper,
HelpButton,
LazyDistributions,
PulpLabels,
Spinner,
Typeahead,
} from 'src/components';
Expand Down Expand Up @@ -164,6 +165,28 @@ export const AnsibleRepositoryForm = ({
</>,
)}

{formGroup(
'pulp_labels',
t`Labels`,
t`Pulp Labels in the form of 'key:value'.`,
<>
<div
// prevents "N more" clicks from submitting the form
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<PulpLabels
labels={repository.pulp_labels}
updateLabels={(labels) =>
updateRepository({ ...repository, pulp_labels: labels })
}
/>
</div>
</>,
)}

{formGroup(
'private',
t`Make private`,
Expand Down
115 changes: 103 additions & 12 deletions src/components/pulp-labels.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,112 @@
import { t } from '@lingui/macro';
import { Label } from '@patternfly/react-core';
import { Button, Label } from '@patternfly/react-core';
import InfoCircleIcon from '@patternfly/react-icons/dist/esm/icons/info-circle-icon';
import PlusCircleIcon from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon';
import React from 'react';
import { LabelGroup } from 'src/components';

export const PulpLabels = ({ labels }: { labels: Record<string, string> }) => {
if (!labels || !Object.keys(labels).length) {
return <>{t`None`}</>;
}
interface Label {
key: string;
value: string;
id: number;
valid: boolean;
}

return (
export const PulpLabels = ({
labels,
updateLabels,
}: {
labels: Record<string, string>;
updateLabels?: (l) => void;
}) => {
const [labelS, setLabels] = React.useState(
!labels || !Object.keys(labels).length
? []
: Object.entries(labels).map(([k, v], i) => ({
key: k,
value: v,
id: i,
valid: true,
})),
);
const [idIndex, setIdIndex] = React.useState(labelS.length);
const update = (newLabels: Label[]) => {
const newLabelsObj = newLabels.reduce(
(o, label) => ({ ...o, [label.key]: label.value }),
{},
);
updateLabels(newLabelsObj);
};

const onClose = (labelId: number) => {
const newLabels = labelS
.filter((l) => l.id !== labelId)
.map((v, i) => ({ ...v, id: i }));
update(newLabels);
setLabels(newLabels);
setIdIndex(newLabels.length);
};

const onEdit = (newText: string, index: number) => {
const copy = [...labelS];
const [k, v] = newText.split(':', 2);
const [ks, vs] = [k.trim(), v ? v.trim() : ''];
const valid = !!ks.match(/^[\w ]+$/) && vs.search(/[,()]/) == -1;
copy[index] = { key: ks, value: vs, id: labelS[index].id, valid: valid };
update(copy);
setLabels(copy);
};

const onAdd = () => {
const newLabels = [
...labelS,
{ key: 'key', value: 'value', id: idIndex, valid: true },
];
update(newLabels);
setLabels(newLabels);
setIdIndex(idIndex + 1);
};

return updateLabels ? (
<LabelGroup
isEditable
addLabelControl={
<Button variant='plain' aria-label='Create new label' onClick={onAdd}>
<PlusCircleIcon />
</Button>
}
style={{ alignItems: 'center' }}
>
{labelS.length ? (
labelS.map(({ key, value, id, valid }) => (
<Label
key={id}
onClose={() => onClose(id)}
onEditCancel={(_e, prevText) => onEdit(prevText, id)}
onEditComplete={(_e, newText) => onEdit(newText, id)}
isEditable
icon={!valid ? <InfoCircleIcon /> : null}
color={!valid ? 'red' : null}
>
{key + (value ? ': ' + value : '')}
</Label>
))
) : (
<>{t`None`}</>
)}
</LabelGroup>
) : (
<LabelGroup>
{Object.entries(labels).map(([k, v]) => (
<Label key={k}>
{k}
{v ? ': ' + v : null}
</Label>
))}
{labelS.length ? (
labelS.map(({ key, value, id }) => (
<Label key={id}>
{key}
{value ? ': ' + value : null}
</Label>
))
) : (
<>{t`None`}</>
)}
</LabelGroup>
);
};

0 comments on commit 609389e

Please sign in to comment.