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

Commit

Permalink
fix: Refactored project tag/delete updates
Browse files Browse the repository at this point in the history
  • Loading branch information
wbreza committed Apr 19, 2019
1 parent f45cf24 commit 466902f
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 150 deletions.
16 changes: 9 additions & 7 deletions src/common/mockFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -810,14 +810,16 @@ export default class MockFactory {
*/
public static projectActions(): IProjectActions {
return {
loadProject: jest.fn((project: IProject) => Promise.resolve()),
saveProject: jest.fn((project: IProject) => Promise.resolve()),
deleteProject: jest.fn((project: IProject) => Promise.resolve()),
loadProject: jest.fn(() => Promise.resolve()),
saveProject: jest.fn(() => Promise.resolve()),
deleteProject: jest.fn(() => Promise.resolve()),
closeProject: jest.fn(() => Promise.resolve()),
loadAssets: jest.fn((project: IProject) => Promise.resolve()),
exportProject: jest.fn((project: IProject) => Promise.resolve()),
loadAssetMetadata: jest.fn((project: IProject, asset: IAsset) => Promise.resolve()),
saveAssetMetadata: jest.fn((project: IProject, assetMetadata: IAssetMetadata) => Promise.resolve()),
loadAssets: jest.fn(() => Promise.resolve()),
exportProject: jest.fn(() => Promise.resolve()),
loadAssetMetadata: jest.fn(() => Promise.resolve()),
saveAssetMetadata: jest.fn(() => Promise.resolve()),
updateProjectTag: jest.fn(() => Promise.resolve()),
deleteProjectTag: jest.fn(() => Promise.resolve()),
};
}

Expand Down
47 changes: 28 additions & 19 deletions src/react/components/common/tagInput/tagInput.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { KeyboardEvent } from "react";
import React, { KeyboardEvent, RefObject } from "react";
import ReactDOM from "react-dom";
import Align from "rc-align";
import { randomIntInRange } from "../../../../common/utils";
Expand All @@ -10,6 +10,7 @@ import TagInputItem, { ITagInputItemProps, ITagClickProps } from "./tagInputItem
import TagInputToolbar from "./tagInputToolbar";
import { toast } from "react-toastify";
import { strings } from "../../../../common/strings";
import { string } from "prop-types";
// tslint:disable-next-line:no-var-requires
const tagColors = require("../../common/tagColors.json");

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

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

public render() {
Expand Down Expand Up @@ -109,7 +110,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
}
{this.getColorPickerPortal()}
<div className="tag-input-items">
{this.getTagItems()}
{this.renderTagItems()}
</div>
{
this.state.addTags &&
Expand Down Expand Up @@ -154,11 +155,13 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
}
}

private getTagNode = (tag: ITag) => {
private getTagNode = (tag: ITag): Element => {
if (!tag) {
return defaultDOMNode();
}
return ReactDOM.findDOMNode(this.tagItemRefs[tag.name]) as Element;

const itemRef = this.tagItemRefs.get(tag.name);
return (itemRef ? ReactDOM.findDOMNode(itemRef.current) : defaultDOMNode()) as Element;
}

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

private getTagItems = () => {
let props = this.getTagItemProps();
private renderTagItems = () => {
let props = this.createTagItemProps();
const query = this.state.searchQuery;
this.tagItemRefs.clear();

if (query.length) {
props = props.filter((prop) => prop.tag.name.toLowerCase().includes(query.toLowerCase()));
}

return props.map((prop) =>
<TagInputItem
key={prop.tag.name}
Expand All @@ -313,16 +319,16 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
}

private setTagItemRef = (item, tag) => {
if (item) {
this.tagItemRefs[tag.name] = item;
}
this.tagItemRefs.set(tag.name, item);
return item;
}

private getTagItemProps = (): ITagInputItemProps[] => {
private createTagItemProps = (): ITagInputItemProps[] => {
const tags = this.state.tags;
const selectedRegionTagSet = this.getSelectedRegionTagSet();
return tags.map((tag) => {
const item: ITagInputItemProps = {

return tags.map((tag) => (
{
tag,
index: tags.findIndex((t) => t.name === tag.name),
isLocked: this.props.lockedTags && this.props.lockedTags.findIndex((t) => t === tag.name) > -1,
Expand All @@ -331,9 +337,8 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
appliedToSelectedRegions: selectedRegionTagSet.has(tag.name),
onClick: this.handleClick,
onChange: this.updateTag,
};
return item;
});
} as ITagInputItemProps
));
}

private getSelectedRegionTagSet = (): Set<string> => {
Expand All @@ -351,6 +356,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
private onAltClick = (tag: ITag, clickedColor: boolean) => {
const { editingTag } = this.state;
const newEditingTag = editingTag && editingTag.name === tag.name ? null : tag;

this.setState({
editingTag: newEditingTag,
editingTagNode: this.getTagNode(newEditingTag),
Expand All @@ -360,16 +366,16 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
}

private handleClick = (tag: ITag, props: ITagClickProps) => {
// Lock tags
if (props.ctrlKey && this.props.onCtrlTagClick) {
this.props.onCtrlTagClick(tag);
this.setState({ clickedColor: props.clickedColor });
} else if (props.altKey) {
} else if (props.altKey) { // Edit tag
this.onAltClick(tag, props.clickedColor);
} else {
} else { // Select tag
const { editingTag, selectedTag } = this.state;
const inEditMode = editingTag && tag.name === editingTag.name;
const alreadySelected = selectedTag && selectedTag.name === tag.name;

const newEditingTag = inEditMode ? null : editingTag;

this.setState({
Expand Down Expand Up @@ -398,12 +404,15 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
this.props.onTagDeleted(tag.name);
return;
}

const index = this.state.tags.indexOf(tag);
const tags = this.state.tags.filter((t) => t.name !== tag.name);

this.setState({
tags,
selectedTag: this.getNewSelectedTag(tags, index),
}, () => this.props.onChange(tags));

if (this.props.lockedTags.find((l) => l === tag.name)) {
this.props.onLockedTagsChange(
this.props.lockedTags.filter((lockedTag) => lockedTag !== tag.name),
Expand Down
2 changes: 1 addition & 1 deletion src/react/components/pages/editorPage/canvas.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ describe("Editor Canvas", () => {
});
const canvas = wrapper.instance() as Canvas;
expect(wrapper.state().currentAsset).toEqual(assetMetadata);
expect(() => canvas.updateCanvasToolsRegions()).not.toThrowError();
expect(() => canvas.updateCanvasToolsRegionTags()).not.toThrowError();
});

it("canvas content source is updated when asset is deactivated", () => {
Expand Down
36 changes: 18 additions & 18 deletions src/react/components/pages/editorPage/canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CanvasTools } from "vott-ct";
import { RegionData } from "vott-ct/lib/js/CanvasTools/Core/RegionData";
import {
EditorMode, IAssetMetadata,
IProject, IRegion, RegionType, IBoundingBox, ISize,
IProject, IRegion, RegionType,
} from "../../../../models/applicationState";
import CanvasHelpers from "./canvasHelpers";
import { AssetPreview, ContentSource } from "../../common/assetPreview/assetPreview";
Expand Down Expand Up @@ -73,7 +73,8 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
}

public componentDidUpdate = async (prevProps: Readonly<ICanvasProps>, prevState: Readonly<ICanvasState>) => {
if (this.props.selectedAsset.asset.id !== prevProps.selectedAsset.asset.id) {
// Handles asset changing
if (this.props.selectedAsset !== prevProps.selectedAsset) {
this.setState({ currentAsset: this.props.selectedAsset });
}

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

const assetIdChanged = this.state.currentAsset.asset.id !== prevState.currentAsset.asset.id;

// When the selected asset has changed but is still the same asset id
if (!assetIdChanged && this.state.currentAsset !== prevState.currentAsset) {
this.refreshCanvasToolsRegions();
}

// When the project tags change re-apply tags to regions
if (this.props.project.tags !== prevProps.project.tags) {
this.updateCanvasToolsRegions();
this.updateCanvasToolsRegionTags();
}

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

public updateCanvasToolsRegions = (asset?: IAssetMetadata): void => {
if (asset) {
this.setState({
currentAsset: asset,
});
this.clearAllRegions();
this.addRegionsToCanvasTools(asset.regions);
} else {
for (const region of this.state.currentAsset.regions) {
this.editor.RM.updateTagsById(
region.id,
CanvasHelpers.getTagsDescriptor(this.props.project.tags, region),
);
}
public updateCanvasToolsRegionTags = (): void => {
for (const region of this.state.currentAsset.regions) {
this.editor.RM.updateTagsById(
region.id,
CanvasHelpers.getTagsDescriptor(this.props.project.tags, region),
);
}
}

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

/**
Expand Down
18 changes: 8 additions & 10 deletions src/react/components/pages/editorPage/editorPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,10 @@ describe("Editor Page Component", () => {

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

editorPage.props().project = testProject;
await MockFactory.flushUi();
expect(editorPage.props().project).toEqual(testProject);
expect(getState(wrapper).project).toEqual(testProject);
});

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

const wrapper = createComponent(store, MockFactory.editorPageProps());
expect(getState(wrapper).project.tags).toEqual(project.tags);
expect(wrapper.props().project.tags).toEqual(project.tags);
});

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

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

const stateTags = getState(wrapper).project.tags;
const projectTags = wrapper.props().project.tags;

expect(stateTags).toHaveLength(project.tags.length + 1);
expect(stateTags[stateTags.length - 1].name).toEqual(newTagName);
expect(projectTags).toHaveLength(project.tags.length + 1);
expect(projectTags[projectTags.length - 1].name).toEqual(newTagName);
});

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

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

await MockFactory.flushUi();

const stateTags = getState(wrapper).project.tags;
expect(stateTags).toHaveLength(project.tags.length - 1);
const projectTags = wrapper.props().project.tags;
expect(projectTags).toHaveLength(project.tags.length - 1);
});

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

0 comments on commit 466902f

Please sign in to comment.