Skip to content

Commit fadc1b8

Browse files
authored
Add Tag Modal Enhancements (#1187)
1 parent 4b0ab83 commit fadc1b8

4 files changed

Lines changed: 30 additions & 4 deletions

File tree

ui-next/src/components/features/tags/AddTagDialog.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { TagDto } from "types/Tag";
1717
import { useActionWithPath, useTags } from "utils/query";
1818
import { getErrorMessage } from "utils/utils";
1919
import ReplaceTagsInput from "components/features/tags/ReplaceTagsInput";
20+
import { isValidTag } from "components/features/tags/tagUtils";
2021

2122
export type TagDialogProps = {
2223
open: boolean;
@@ -56,6 +57,7 @@ export default function AddTagDialog({
5657
const [newTags, setNewTags] = useState<string[]>(
5758
tags.map((tag: TagDto) => tag && `${tag.key}:${tag.value}`),
5859
);
60+
const [pendingInput, setPendingInput] = useState("");
5961
// Only fetch all tags when the dialog is open (avoids slow /metadata/tags on every page that mounts this dialog).
6062
const { data: existingTags } = useTags<TagDto[]>({ enabled: open });
6163

@@ -78,6 +80,9 @@ export default function AddTagDialog({
7880
const hasNoChanges =
7981
_differenceWith(tags, parsedTags(newTags), isTagEqual).length === 0 &&
8082
newTags.length === tags.length;
83+
const hasInvalidTags = newTags.some((tag) => !isValidTag(tag));
84+
const isSaveDisabled =
85+
hasNoChanges || pendingInput.trim().length > 0 || hasInvalidTags;
8186

8287
function replaceTags(newTags: any) {
8388
for (const tag of newTags) {
@@ -137,6 +142,9 @@ export default function AddTagDialog({
137142
onChange={(tags) => {
138143
setNewTags(_uniq(tags));
139144
}}
145+
onInputChange={(value) => {
146+
setPendingInput(value);
147+
}}
140148
/>
141149
</Box>
142150
</DialogContent>
@@ -155,7 +163,7 @@ export default function AddTagDialog({
155163
variant="contained"
156164
color="primary"
157165
progress={loading}
158-
disabled={hasNoChanges}
166+
disabled={isSaveDisabled}
159167
onClick={() => replaceTags(newTags)}
160168
startIcon={<SaveIcon />}
161169
>

ui-next/src/components/features/tags/ReplaceTagsInput.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,21 @@ import { TagDto } from "types/Tag";
99

1010
type ReplaceTagsInputProps = {
1111
onChange?: (tags: string[]) => void | null;
12+
onInputChange?: (value: string) => void;
1213
label?: ReactNode;
1314
tags: TagDto[];
1415
options: TagDto[];
1516
};
1617
type SuggestValueType = { title: string; inputValue: string };
1718

19+
import { isValidTag } from "components/features/tags/tagUtils";
20+
1821
const filter = createFilterOptions<SuggestValueType>();
1922

2023
const ReplaceTagsInput = ({
2124
label = "Tags",
2225
onChange = () => null,
26+
onInputChange,
2327
tags = [],
2428
options = [],
2529
}: ReplaceTagsInputProps) => {
@@ -50,11 +54,13 @@ const ReplaceTagsInput = ({
5054
option?.inputValue ? option.inputValue : option
5155
) as string;
5256
const { key, ...otherTagProps } = getTagProps({ index });
57+
const invalid = !isValidTag(label);
5358
return (
5459
<TagChip
5560
key={key}
5661
id={`${label}-tag-chip`}
5762
label={label}
63+
color={invalid ? "error" : undefined}
5864
{...otherTagProps}
5965
sx={{
6066
"& .MuiSvgIcon-root": {
@@ -96,6 +102,9 @@ const ReplaceTagsInput = ({
96102

97103
return filtered;
98104
}}
105+
onInputChange={(_event, value) => {
106+
onInputChange?.(value);
107+
}}
99108
onChange={(_event, newValue: (string | SuggestValueType)[]) => {
100109
onChange(
101110
newValue.map((val: string | SuggestValueType) =>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const isValidTag = (value: string) => /^[^:]+:[^:]+$/.test(value.trim());

ui-next/src/components/ui/TagChip.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,18 @@ const customStyles = {
1010
fontSize: "12px",
1111
};
1212

13+
const sharedStyles = {
14+
fontWeight: customStyles.fontWeight,
15+
borderRadius: customStyles.borderRadius,
16+
fontSize: customStyles.fontSize,
17+
};
18+
1319
const TagChip = forwardRef<HTMLDivElement, ChipProps>(
14-
({ style = {}, ...props }, ref) => {
15-
const combinedStyles = { ...customStyles, ...style };
16-
return <Chip ref={ref} style={combinedStyles} {...props} />;
20+
({ style = {}, color, ...props }, ref) => {
21+
const combinedStyles = color
22+
? { ...sharedStyles, ...style }
23+
: { ...customStyles, ...style };
24+
return <Chip ref={ref} style={combinedStyles} color={color} {...props} />;
1725
},
1826
);
1927

0 commit comments

Comments
 (0)