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

Commit 466902f

Browse files
committed
fix: Refactored project tag/delete updates
1 parent f45cf24 commit 466902f

File tree

10 files changed

+217
-150
lines changed

10 files changed

+217
-150
lines changed

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/react/components/common/tagInput/tagInput.tsx

Lines changed: 28 additions & 19 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";
@@ -10,6 +10,7 @@ import TagInputItem, { ITagInputItemProps, ITagClickProps } from "./tagInputItem
1010
import TagInputToolbar from "./tagInputToolbar";
1111
import { toast } from "react-toastify";
1212
import { strings } from "../../../../common/strings";
13+
import { string } from "prop-types";
1314
// tslint:disable-next-line:no-var-requires
1415
const tagColors = require("../../common/tagColors.json");
1516

@@ -72,7 +73,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
7273
portalElement: defaultDOMNode(),
7374
};
7475

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

7879
public render() {
@@ -109,7 +110,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
109110
}
110111
{this.getColorPickerPortal()}
111112
<div className="tag-input-items">
112-
{this.getTagItems()}
113+
{this.renderTagItems()}
113114
</div>
114115
{
115116
this.state.addTags &&
@@ -154,11 +155,13 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
154155
}
155156
}
156157

157-
private getTagNode = (tag: ITag) => {
158+
private getTagNode = (tag: ITag): Element => {
158159
if (!tag) {
159160
return defaultDOMNode();
160161
}
161-
return ReactDOM.findDOMNode(this.tagItemRefs[tag.name]) as Element;
162+
163+
const itemRef = this.tagItemRefs.get(tag.name);
164+
return (itemRef ? ReactDOM.findDOMNode(itemRef.current) : defaultDOMNode()) as Element;
162165
}
163166

164167
private onEditTag = (tag: ITag) => {
@@ -298,12 +301,15 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
298301
return this.state.editingTagNode || document;
299302
}
300303

301-
private getTagItems = () => {
302-
let props = this.getTagItemProps();
304+
private renderTagItems = () => {
305+
let props = this.createTagItemProps();
303306
const query = this.state.searchQuery;
307+
this.tagItemRefs.clear();
308+
304309
if (query.length) {
305310
props = props.filter((prop) => prop.tag.name.toLowerCase().includes(query.toLowerCase()));
306311
}
312+
307313
return props.map((prop) =>
308314
<TagInputItem
309315
key={prop.tag.name}
@@ -313,16 +319,16 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
313319
}
314320

315321
private setTagItemRef = (item, tag) => {
316-
if (item) {
317-
this.tagItemRefs[tag.name] = item;
318-
}
322+
this.tagItemRefs.set(tag.name, item);
323+
return item;
319324
}
320325

321-
private getTagItemProps = (): ITagInputItemProps[] => {
326+
private createTagItemProps = (): ITagInputItemProps[] => {
322327
const tags = this.state.tags;
323328
const selectedRegionTagSet = this.getSelectedRegionTagSet();
324-
return tags.map((tag) => {
325-
const item: ITagInputItemProps = {
329+
330+
return tags.map((tag) => (
331+
{
326332
tag,
327333
index: tags.findIndex((t) => t.name === tag.name),
328334
isLocked: this.props.lockedTags && this.props.lockedTags.findIndex((t) => t === tag.name) > -1,
@@ -331,9 +337,8 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
331337
appliedToSelectedRegions: selectedRegionTagSet.has(tag.name),
332338
onClick: this.handleClick,
333339
onChange: this.updateTag,
334-
};
335-
return item;
336-
});
340+
} as ITagInputItemProps
341+
));
337342
}
338343

339344
private getSelectedRegionTagSet = (): Set<string> => {
@@ -351,6 +356,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
351356
private onAltClick = (tag: ITag, clickedColor: boolean) => {
352357
const { editingTag } = this.state;
353358
const newEditingTag = editingTag && editingTag.name === tag.name ? null : tag;
359+
354360
this.setState({
355361
editingTag: newEditingTag,
356362
editingTagNode: this.getTagNode(newEditingTag),
@@ -360,16 +366,16 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
360366
}
361367

362368
private handleClick = (tag: ITag, props: ITagClickProps) => {
369+
// Lock tags
363370
if (props.ctrlKey && this.props.onCtrlTagClick) {
364371
this.props.onCtrlTagClick(tag);
365372
this.setState({ clickedColor: props.clickedColor });
366-
} else if (props.altKey) {
373+
} else if (props.altKey) { // Edit tag
367374
this.onAltClick(tag, props.clickedColor);
368-
} else {
375+
} else { // Select tag
369376
const { editingTag, selectedTag } = this.state;
370377
const inEditMode = editingTag && tag.name === editingTag.name;
371378
const alreadySelected = selectedTag && selectedTag.name === tag.name;
372-
373379
const newEditingTag = inEditMode ? null : editingTag;
374380

375381
this.setState({
@@ -398,12 +404,15 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
398404
this.props.onTagDeleted(tag.name);
399405
return;
400406
}
407+
401408
const index = this.state.tags.indexOf(tag);
402409
const tags = this.state.tags.filter((t) => t.name !== tag.name);
410+
403411
this.setState({
404412
tags,
405413
selectedTag: this.getNewSelectedTag(tags, index),
406414
}, () => this.props.onChange(tags));
415+
407416
if (this.props.lockedTags.find((l) => l === tag.name)) {
408417
this.props.onLockedTagsChange(
409418
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", () => {

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

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { CanvasTools } from "vott-ct";
44
import { RegionData } from "vott-ct/lib/js/CanvasTools/Core/RegionData";
55
import {
66
EditorMode, IAssetMetadata,
7-
IProject, IRegion, RegionType, IBoundingBox, ISize,
7+
IProject, IRegion, RegionType,
88
} from "../../../../models/applicationState";
99
import CanvasHelpers from "./canvasHelpers";
1010
import { AssetPreview, ContentSource } from "../../common/assetPreview/assetPreview";
@@ -73,7 +73,8 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
7373
}
7474

7575
public componentDidUpdate = async (prevProps: Readonly<ICanvasProps>, prevState: Readonly<ICanvasState>) => {
76-
if (this.props.selectedAsset.asset.id !== prevProps.selectedAsset.asset.id) {
76+
// Handles asset changing
77+
if (this.props.selectedAsset !== prevProps.selectedAsset) {
7778
this.setState({ currentAsset: this.props.selectedAsset });
7879
}
7980

@@ -83,9 +84,16 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
8384
this.editor.AS.setSelectionMode({ mode: this.props.selectionMode, template: options });
8485
}
8586

87+
const assetIdChanged = this.state.currentAsset.asset.id !== prevState.currentAsset.asset.id;
88+
89+
// When the selected asset has changed but is still the same asset id
90+
if (!assetIdChanged && this.state.currentAsset !== prevState.currentAsset) {
91+
this.refreshCanvasToolsRegions();
92+
}
93+
8694
// When the project tags change re-apply tags to regions
8795
if (this.props.project.tags !== prevProps.project.tags) {
88-
this.updateCanvasToolsRegions();
96+
this.updateCanvasToolsRegionTags();
8997
}
9098

9199
// Handles when the canvas is enabled & disabled
@@ -192,20 +200,12 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
192200
return this.state.currentAsset.regions.filter((r) => selectedRegions.find((id) => r.id === id));
193201
}
194202

195-
public updateCanvasToolsRegions = (asset?: IAssetMetadata): void => {
196-
if (asset) {
197-
this.setState({
198-
currentAsset: asset,
199-
});
200-
this.clearAllRegions();
201-
this.addRegionsToCanvasTools(asset.regions);
202-
} else {
203-
for (const region of this.state.currentAsset.regions) {
204-
this.editor.RM.updateTagsById(
205-
region.id,
206-
CanvasHelpers.getTagsDescriptor(this.props.project.tags, region),
207-
);
208-
}
203+
public updateCanvasToolsRegionTags = (): void => {
204+
for (const region of this.state.currentAsset.regions) {
205+
this.editor.RM.updateTagsById(
206+
region.id,
207+
CanvasHelpers.getTagsDescriptor(this.props.project.tags, region),
208+
);
209209
}
210210
}
211211

@@ -501,7 +501,7 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
501501
this.editor.RM.updateTagsById(update.id, CanvasHelpers.getTagsDescriptor(this.props.project.tags, update));
502502
}
503503
this.updateAssetRegions(updatedRegions);
504-
this.updateCanvasToolsRegions();
504+
this.updateCanvasToolsRegionTags();
505505
}
506506

507507
/**

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

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,10 @@ describe("Editor Page Component", () => {
126126

127127
const wrapper = createComponent(store, props);
128128
const editorPage = wrapper.find(EditorPage).childAt(0);
129-
expect(getState(wrapper).project).toBeNull();
130129

131130
editorPage.props().project = testProject;
132131
await MockFactory.flushUi();
133132
expect(editorPage.props().project).toEqual(testProject);
134-
expect(getState(wrapper).project).toEqual(testProject);
135133
});
136134

137135
it("Loads and merges project assets with asset provider assets when state changes", async () => {
@@ -671,7 +669,7 @@ describe("Editor Page Component", () => {
671669
});
672670

673671
const wrapper = createComponent(store, MockFactory.editorPageProps());
674-
expect(getState(wrapper).project.tags).toEqual(project.tags);
672+
expect(wrapper.props().project.tags).toEqual(project.tags);
675673
});
676674

677675
it("create a new tag from text box", () => {
@@ -681,16 +679,16 @@ describe("Editor Page Component", () => {
681679
currentProject: project,
682680
});
683681
const wrapper = createComponent(store, MockFactory.editorPageProps());
684-
expect(getState(wrapper).project.tags).toEqual(project.tags);
682+
expect(wrapper.props().project.tags).toEqual(project.tags);
685683

686684
const newTagName = "My new tag";
687685
wrapper.find("div.tag-input-toolbar-item.plus").simulate("click");
688686
wrapper.find(".tag-input-box").simulate("keydown", { key: "Enter", target: { value: newTagName } });
689687

690-
const stateTags = getState(wrapper).project.tags;
688+
const projectTags = wrapper.props().project.tags;
691689

692-
expect(stateTags).toHaveLength(project.tags.length + 1);
693-
expect(stateTags[stateTags.length - 1].name).toEqual(newTagName);
690+
expect(projectTags).toHaveLength(project.tags.length + 1);
691+
expect(projectTags[projectTags.length - 1].name).toEqual(newTagName);
694692
});
695693

696694
it("Remove a tag", async () => {
@@ -703,15 +701,15 @@ describe("Editor Page Component", () => {
703701
const wrapper = createComponent(store, MockFactory.editorPageProps());
704702
await waitForSelectedAsset(wrapper);
705703

706-
expect(getState(wrapper).project.tags).toEqual(project.tags);
704+
expect(wrapper.props().project.tags).toEqual(project.tags);
707705
wrapper.find(".tag-content").last().simulate("click");
708706
wrapper.find("i.tag-input-toolbar-icon.fas.fa-trash").simulate("click");
709707
wrapper.find("button.btn.btn-danger").simulate("click");
710708

711709
await MockFactory.flushUi();
712710

713-
const stateTags = getState(wrapper).project.tags;
714-
expect(stateTags).toHaveLength(project.tags.length - 1);
711+
const projectTags = wrapper.props().project.tags;
712+
expect(projectTags).toHaveLength(project.tags.length - 1);
715713
});
716714

717715
it("Adds tag to locked tags when CmdOrCtrl clicked", async () => {

0 commit comments

Comments
 (0)