Skip to content
This repository was archived by the owner on Dec 7, 2021. It is now read-only.

Commit 54ecbe1

Browse files
authored
fix: Rename/delete tags throughout assets (#764)
Renames or deletes tags from asset/project files when tags are renamed/deleted in tag input component Solves [AB#17862]
2 parents 1e0a460 + 4d99c1c commit 54ecbe1

15 files changed

Lines changed: 572 additions & 79 deletions

File tree

‎package-lock.json‎

Lines changed: 14 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/common/localization/en-us.ts‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,15 @@ export const english: IAppStrings = {
256256
apply: "Apply Tag with Hot Key",
257257
lock: "Lock Tag with Hot Key",
258258
},
259+
rename: {
260+
title: "Rename Tag",
261+
confirmation: "Are you sure you want to rename this tag? It will be renamed throughout all assets",
262+
},
263+
delete: {
264+
title: "Delete Tag",
265+
confirmation: "Are you sure you want to delete this tag? It will be deleted throughout all assets \
266+
and any regions where this is the only tag will also be deleted",
267+
},
259268
},
260269
canvas: {
261270
removeAllRegions: {
@@ -267,7 +276,8 @@ export const english: IAppStrings = {
267276
enforceTaggedRegions: {
268277
title: "Invalid region(s) detected",
269278
// tslint:disable-next-line:max-line-length
270-
description: "1 or more regions have not been tagged. Ensure all regions are tagged before continuing to next asset.",
279+
description: "1 or more regions have not been tagged. Ensure all regions are tagged before \
280+
continuing to next asset.",
271281
},
272282
},
273283
},

‎src/common/localization/es-cl.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,16 @@ export const spanish: IAppStrings = {
258258
apply: "Aplicar etiqueta con tecla de acceso rÃĄpido",
259259
lock: "Bloquear etiqueta con tecla de acceso rÃĄpido",
260260
},
261+
rename: {
262+
title: "Cambiar el nombre de la etiqueta",
263+
confirmation: "ÂŋEstÃĄ seguro que quiere cambiar el nombre de esta etiqueta? \
264+
SerÃĄ cambiada en todos los activos",
265+
},
266+
delete: {
267+
title: "Delete Tag",
268+
confirmation: "ÂŋEstÃĄ seguro que quiere borrar esta etiqueta? SerÃĄ borrada en todos \
269+
los activos y en las regiones donde esta etiqueta sea la Única, la region tambiÃĐn serÃĄ borrada",
270+
},
261271
},
262272
canvas: {
263273
removeAllRegions: {

‎src/common/mockFactory.ts‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -810,14 +810,16 @@ export default class MockFactory {
810810
*/
811811
public static projectActions(): IProjectActions {
812812
return {
813-
loadProject: jest.fn((project: IProject) => Promise.resolve()),
814-
saveProject: jest.fn((project: IProject) => Promise.resolve()),
815-
deleteProject: jest.fn((project: IProject) => Promise.resolve()),
813+
loadProject: jest.fn(() => Promise.resolve()),
814+
saveProject: jest.fn(() => Promise.resolve()),
815+
deleteProject: jest.fn(() => Promise.resolve()),
816816
closeProject: jest.fn(() => Promise.resolve()),
817-
loadAssets: jest.fn((project: IProject) => Promise.resolve()),
818-
exportProject: jest.fn((project: IProject) => Promise.resolve()),
819-
loadAssetMetadata: jest.fn((project: IProject, asset: IAsset) => Promise.resolve()),
820-
saveAssetMetadata: jest.fn((project: IProject, assetMetadata: IAssetMetadata) => Promise.resolve()),
817+
loadAssets: jest.fn(() => Promise.resolve()),
818+
exportProject: jest.fn(() => Promise.resolve()),
819+
loadAssetMetadata: jest.fn(() => Promise.resolve()),
820+
saveAssetMetadata: jest.fn(() => Promise.resolve()),
821+
updateProjectTag: jest.fn(() => Promise.resolve()),
822+
deleteProjectTag: jest.fn(() => Promise.resolve()),
821823
};
822824
}
823825

‎src/common/strings.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,14 @@ export interface IAppStrings {
255255
apply: string;
256256
lock: string;
257257
},
258+
rename: {
259+
title: string;
260+
confirmation: string;
261+
},
262+
delete: {
263+
title: string;
264+
confirmation: string;
265+
},
258266
}
259267
canvas: {
260268
removeAllRegions: {

‎src/react/components/common/tagInput/tagInput.tsx‎

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { KeyboardEvent } from "react";
1+
import React, { KeyboardEvent, RefObject } from "react";
22
import ReactDOM from "react-dom";
33
import Align from "rc-align";
44
import { randomIntInRange } from "../../../../common/utils";
@@ -31,9 +31,9 @@ export interface ITagInputProps {
3131
/** Function to call on clicking individual tag while holding CTRL key */
3232
onCtrlTagClick?: (tag: ITag) => void;
3333
/** Function to call when tag is renamed */
34-
onTagRenamed?: (oldTag: string, newTag: string) => void;
34+
onTagRenamed?: (tagName: string, newTagName: string) => void;
3535
/** Function to call when tag is deleted */
36-
onTagDeleted?: (tag: ITag) => void;
36+
onTagDeleted?: (tagName: string) => void;
3737
/** Always show tag input box */
3838
showTagInputBox?: boolean;
3939
/** Always show tag search box */
@@ -72,7 +72,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
7272
portalElement: defaultDOMNode(),
7373
};
7474

75-
private tagItemRefs: { [id: string]: TagInputItem } = {};
75+
private tagItemRefs: Map<string, RefObject<TagInputItem>> = new Map<string, RefObject<TagInputItem>>();
7676
private portalDiv = document.createElement("div");
7777

7878
public render() {
@@ -109,7 +109,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
109109
}
110110
{this.getColorPickerPortal()}
111111
<div className="tag-input-items">
112-
{this.getTagItems()}
112+
{this.renderTagItems()}
113113
</div>
114114
{
115115
this.state.addTags &&
@@ -154,11 +154,13 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
154154
}
155155
}
156156

157-
private getTagNode = (tag: ITag) => {
157+
private getTagNode = (tag: ITag): Element => {
158158
if (!tag) {
159159
return defaultDOMNode();
160160
}
161-
return ReactDOM.findDOMNode(this.tagItemRefs[tag.name]) as Element;
161+
162+
const itemRef = this.tagItemRefs.get(tag.name);
163+
return (itemRef ? ReactDOM.findDOMNode(itemRef.current) : defaultDOMNode()) as Element;
162164
}
163165

164166
private onEditTag = (tag: ITag) => {
@@ -219,20 +221,25 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
219221
}, () => this.props.onChange(tags));
220222
}
221223

222-
private updateTag = (oldTag: ITag, newTag: ITag) => {
223-
if (oldTag === newTag) {
224+
private updateTag = (tag: ITag, newTag: ITag) => {
225+
if (tag === newTag) {
224226
return;
225227
}
226228
if (!newTag.name.length) {
227229
toast.warn(strings.tags.warnings.emptyName);
228230
return;
229231
}
230-
if (newTag.name !== oldTag.name && this.state.tags.some((t) => t.name === newTag.name)) {
232+
const nameChange = tag.name !== newTag.name;
233+
if (nameChange && this.state.tags.some((t) => t.name === newTag.name)) {
231234
toast.warn(strings.tags.warnings.existingName);
232235
return;
233236
}
237+
if (nameChange && this.props.onTagRenamed) {
238+
this.props.onTagRenamed(tag.name, newTag.name);
239+
return;
240+
}
234241
const tags = this.state.tags.map((t) => {
235-
return (t.name === oldTag.name) ? newTag : t;
242+
return (t.name === tag.name) ? newTag : t;
236243
});
237244
this.setState({
238245
tags,
@@ -293,12 +300,15 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
293300
return this.state.editingTagNode || document;
294301
}
295302

296-
private getTagItems = () => {
297-
let props = this.getTagItemProps();
303+
private renderTagItems = () => {
304+
let props = this.createTagItemProps();
298305
const query = this.state.searchQuery;
306+
this.tagItemRefs.clear();
307+
299308
if (query.length) {
300309
props = props.filter((prop) => prop.tag.name.toLowerCase().includes(query.toLowerCase()));
301310
}
311+
302312
return props.map((prop) =>
303313
<TagInputItem
304314
key={prop.tag.name}
@@ -308,16 +318,16 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
308318
}
309319

310320
private setTagItemRef = (item, tag) => {
311-
if (item) {
312-
this.tagItemRefs[tag.name] = item;
313-
}
321+
this.tagItemRefs.set(tag.name, item);
322+
return item;
314323
}
315324

316-
private getTagItemProps = (): ITagInputItemProps[] => {
325+
private createTagItemProps = (): ITagInputItemProps[] => {
317326
const tags = this.state.tags;
318327
const selectedRegionTagSet = this.getSelectedRegionTagSet();
319-
return tags.map((tag) => {
320-
const item: ITagInputItemProps = {
328+
329+
return tags.map((tag) => (
330+
{
321331
tag,
322332
index: tags.findIndex((t) => t.name === tag.name),
323333
isLocked: this.props.lockedTags && this.props.lockedTags.findIndex((t) => t === tag.name) > -1,
@@ -326,9 +336,8 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
326336
appliedToSelectedRegions: selectedRegionTagSet.has(tag.name),
327337
onClick: this.handleClick,
328338
onChange: this.updateTag,
329-
};
330-
return item;
331-
});
339+
} as ITagInputItemProps
340+
));
332341
}
333342

334343
private getSelectedRegionTagSet = (): Set<string> => {
@@ -346,6 +355,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
346355
private onAltClick = (tag: ITag, clickedColor: boolean) => {
347356
const { editingTag } = this.state;
348357
const newEditingTag = editingTag && editingTag.name === tag.name ? null : tag;
358+
349359
this.setState({
350360
editingTag: newEditingTag,
351361
editingTagNode: this.getTagNode(newEditingTag),
@@ -355,16 +365,16 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
355365
}
356366

357367
private handleClick = (tag: ITag, props: ITagClickProps) => {
368+
// Lock tags
358369
if (props.ctrlKey && this.props.onCtrlTagClick) {
359370
this.props.onCtrlTagClick(tag);
360371
this.setState({ clickedColor: props.clickedColor });
361-
} else if (props.altKey) {
372+
} else if (props.altKey) { // Edit tag
362373
this.onAltClick(tag, props.clickedColor);
363-
} else {
374+
} else { // Select tag
364375
const { editingTag, selectedTag } = this.state;
365376
const inEditMode = editingTag && tag.name === editingTag.name;
366377
const alreadySelected = selectedTag && selectedTag.name === tag.name;
367-
368378
const newEditingTag = inEditMode ? null : editingTag;
369379

370380
this.setState({
@@ -389,12 +399,19 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
389399
if (!tag) {
390400
return;
391401
}
402+
if (this.props.onTagDeleted) {
403+
this.props.onTagDeleted(tag.name);
404+
return;
405+
}
406+
392407
const index = this.state.tags.indexOf(tag);
393408
const tags = this.state.tags.filter((t) => t.name !== tag.name);
409+
394410
this.setState({
395411
tags,
396412
selectedTag: this.getNewSelectedTag(tags, index),
397413
}, () => this.props.onChange(tags));
414+
398415
if (this.props.lockedTags.find((l) => l === tag.name)) {
399416
this.props.onLockedTagsChange(
400417
this.props.lockedTags.filter((lockedTag) => lockedTag !== tag.name),

‎src/react/components/pages/editorPage/canvas.test.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ describe("Editor Canvas", () => {
179179
});
180180
const canvas = wrapper.instance() as Canvas;
181181
expect(wrapper.state().currentAsset).toEqual(assetMetadata);
182-
expect(() => canvas.updateCanvasToolsRegions()).not.toThrowError();
182+
expect(() => canvas.updateCanvasToolsRegionTags()).not.toThrowError();
183183
});
184184

185185
it("canvas content source is updated when asset is deactivated", () => {

0 commit comments

Comments
 (0)