From 5a4cb9accdfe157b56237ea4fbefe6c2d0212ad7 Mon Sep 17 00:00:00 2001
From: davelopez <46503462+davelopez@users.noreply.github.com>
Date: Wed, 24 Jan 2024 11:33:33 +0100
Subject: [PATCH] Remove legacy Tags component
---
client/src/bundleEntries.js | 2 -
client/src/components/Tags/Tags.test.js | 132 ------------------
client/src/components/Tags/Tags.vue | 99 -------------
.../components/Tags/__mocks__/tagService.js | 60 --------
.../src/components/Tags/backboneTagService.js | 39 ------
client/src/components/Tags/index.js | 5 -
client/src/components/Tags/mounts.js | 68 ---------
client/src/components/Tags/tagService.js | 118 ----------------
client/src/components/Tags/tagService.test.js | 99 -------------
.../Tags/testData/autocompleteResponse.txt | 3 -
10 files changed, 625 deletions(-)
delete mode 100644 client/src/components/Tags/Tags.test.js
delete mode 100644 client/src/components/Tags/Tags.vue
delete mode 100644 client/src/components/Tags/__mocks__/tagService.js
delete mode 100644 client/src/components/Tags/backboneTagService.js
delete mode 100644 client/src/components/Tags/mounts.js
delete mode 100644 client/src/components/Tags/tagService.js
delete mode 100644 client/src/components/Tags/tagService.test.js
delete mode 100644 client/src/components/Tags/testData/autocompleteResponse.txt
diff --git a/client/src/bundleEntries.js b/client/src/bundleEntries.js
index d25a2e7a930..acfb8027b07 100644
--- a/client/src/bundleEntries.js
+++ b/client/src/bundleEntries.js
@@ -42,7 +42,5 @@ export function chart(options) {
return new Client(options);
}
-export { mountMakoTags } from "components/Tags";
-
// Used in common.mako
export { default as store } from "storemodern";
diff --git a/client/src/components/Tags/Tags.test.js b/client/src/components/Tags/Tags.test.js
deleted file mode 100644
index 3827c2f72ae..00000000000
--- a/client/src/components/Tags/Tags.test.js
+++ /dev/null
@@ -1,132 +0,0 @@
-import { mount } from "@vue/test-utils";
-import { tagStore } from "store/tagStore";
-import { getLocalVue } from "tests/jest/helpers";
-import Vuex from "vuex";
-
-import Tags from "./Tags";
-import { TagService } from "./tagService";
-
-jest.mock("./tagService");
-
-const localVue = getLocalVue();
-
-const testStore = new Vuex.Store({
- modules: {
- tagStore,
- },
-});
-
-describe("Tags/Tags.vue", () => {
- const id = "testId";
- const itemClass = "TestObject";
- const context = "testing";
- let tagService;
- let wrapper;
- let emitted;
- const startingTags = ["a", "b", "c"];
- const storeKey = "testingTagSet";
-
- const clickFirstTag = () => {
- if (wrapper) {
- const firstTag = wrapper.find(".ti-tag-center > div");
- firstTag.trigger("click");
- } else {
- console.log("missing tag");
- }
- };
-
- // Run before each as async so the lifecycle methods can run
- beforeEach(async () => {
- // Create the (mocked, above) TagService
- tagService = new TagService({ id, itemClass, context });
-
- // Mount the tags with sample props
-
- wrapper = mount(Tags, {
- store: testStore,
- localVue,
- propsData: {
- tags: startingTags,
- storeKey,
- tagService,
- },
- });
-
- emitted = wrapper.emitted();
-
- // Waits for lifecycle handlers to execute
- await wrapper.vm.$nextTick();
- });
-
- afterEach(() => {
- tagService = null;
- });
-
- it("should display the tags I give it", async () => {
- const tags = wrapper.findAll(".ti-tag-center .tag-name");
- expect(tags.length).toBe(3);
- expect(tags.at(0).text()).toBe(startingTags[0]);
- });
-
- it("should put the initialized tags into the store at initialization", async () => {
- const storedTags = testStore.getters.getTagsById(storeKey);
- expect(storedTags.length).toBe(3);
- startingTags.forEach((tag, i) => {
- expect(tag).toBe(storedTags[i]);
- });
- });
-
- it("should respond to click events on the tags", async () => {
- clickFirstTag();
- expect(emitted["tag-click"]).toBeTruthy();
- expect(emitted["tag-click"].length == 1).toBeTruthy();
- });
-
- it("should reflect changes in the store", async () => {
- const newTags = ["asdfadsadf", "gfhjfghjf"];
- testStore.dispatch("updateTags", { key: storeKey, tags: newTags });
-
- await wrapper.setProps({ storeKey });
-
- const observed = wrapper.vm.observedTags;
- expect(observed.length).toBe(newTags.length);
- observed.forEach((tagLabel, i) => {
- expect(newTags[i]).toBe(tagLabel);
- });
- });
-
- describe("autocomplete list", () => {
- let subscription;
-
- afterEach(() => {
- if (subscription) {
- subscription.unsubscribe();
- }
- });
-
- it("should generate autocomplete items when you set the search text", (done) => {
- const sampleTxt = "floobar";
- subscription = tagService.autocompleteOptions.subscribe((results) => {
- expect(results.length).toBe(1);
- results.forEach((r) => {
- expect(r.text).toBe(sampleTxt);
- });
- done();
- });
- tagService.autocompleteSearchText = sampleTxt;
- });
-
- it("should debounce autocomplete requests", (done) => {
- const sampleTxt = "floobar";
- subscription = tagService.autocompleteOptions.subscribe((results) => {
- expect(results.length).toBe(1);
- results.forEach((r) => {
- expect(r.text).toBe(sampleTxt);
- });
- done();
- });
- tagService.autocompleteSearchText = "foo";
- tagService.autocompleteSearchText = sampleTxt;
- });
- });
-});
diff --git a/client/src/components/Tags/Tags.vue b/client/src/components/Tags/Tags.vue
deleted file mode 100644
index b357f018fa2..00000000000
--- a/client/src/components/Tags/Tags.vue
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-
-
-
diff --git a/client/src/components/Tags/__mocks__/tagService.js b/client/src/components/Tags/__mocks__/tagService.js
deleted file mode 100644
index acbff9490fa..00000000000
--- a/client/src/components/Tags/__mocks__/tagService.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Generates a service object used by the tagging component to save, delete and
- * lookup potential tag options for the autocomplete feature. Standard typeahead
- * debouncing and ajax cancelling functionality is also provided here, though an
- * argument can be made that it more properly belongs in the component that is
- * handling the inputs.
- *
- * TODO: convert the associated python endpoint to a legit json REST service
- */
-
-import { Subject } from "rxjs";
-import { debounceTime, distinctUntilChanged, filter, map, switchMap } from "rxjs/operators";
-
-import { createTag } from "../model";
-
-export class TagService {
- constructor({ id, itemClass, context, debounceInterval = 150 }) {
- this.id = id;
- this.itemClass = itemClass;
- this.context = context;
- this.debounceInterval = debounceInterval;
-
- // Buffer for autocomplete text changes
- this._searchText = new Subject();
- }
-
- /**
- * Autocomplete observable. Subscribe to this object to get updates to
- * matching autocomplete results as the autocompleteSearchText property is
- * changed
- */
- get autocompleteOptions() {
- return this._searchText.pipe(
- map((txt) => txt.replace("name:", "")),
- filter((txt) => txt.length),
- debounceTime(this.debounceInterval),
- distinctUntilChanged(),
- switchMap((txt) => this.autocomplete(txt))
- );
- }
-
- /**
- * Set this property to start process of retrieving autocomplete results.
- */
- set autocompleteSearchText(txt) {
- this._searchText.next(txt);
- }
-
- async save(tag) {
- return createTag(tag);
- }
-
- async delete(tag) {
- return createTag(tag);
- }
-
- async autocomplete(txt) {
- return [txt].map(createTag);
- }
-}
diff --git a/client/src/components/Tags/backboneTagService.js b/client/src/components/Tags/backboneTagService.js
deleted file mode 100644
index 60912fa3d62..00000000000
--- a/client/src/components/Tags/backboneTagService.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import { createTag } from "./model";
-import { TagService } from "./tagService";
-
-// Some parts of the application save tags by saving model so that
-// it will update other parts of the app, which is a rudimentary way
-// of recreating reactivity, so I expect this will probably die soon
-
-export class BackboneTagService extends TagService {
- constructor(props) {
- super(props);
- this.model = props.model;
- }
-
- async save(rawTag) {
- const tag = createTag(rawTag);
- if (!tag.valid) {
- throw new Error("Invalid tag");
- }
-
- // update model
- let tags = new Set(this.model.attributes.tags);
- tags.add(tag.text);
- tags = Array.from(tags);
-
- await this.model.save({ tags });
- return tag;
- }
-
- async delete(rawTag) {
- const tag = createTag(rawTag);
-
- let tags = new Set(this.model.attributes.tags);
- tags.delete(tag.text);
- tags = Array.from(tags);
-
- await this.model.save({ tags });
- return tag;
- }
-}
diff --git a/client/src/components/Tags/index.js b/client/src/components/Tags/index.js
index d508d536d27..3adeb8c36c4 100644
--- a/client/src/components/Tags/index.js
+++ b/client/src/components/Tags/index.js
@@ -1,9 +1,4 @@
// Low level stateless UI component
export { default as StatelessTags } from "./StatelessTags";
-// Implements data storage logic and click event handling
-// This is usually what you'll want to use.
-export { default as Tags } from "./Tags";
-// functions for mounting the tag editor in non-Vue environments
-export { mountMakoTags, mountModelTags } from "./mounts";
diff --git a/client/src/components/Tags/mounts.js b/client/src/components/Tags/mounts.js
deleted file mode 100644
index 49668e5f09f..00000000000
--- a/client/src/components/Tags/mounts.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * These functions are for mounting the tag display/editor in non-Vue
- * environments such as the existing python scripts and Backbone views.
- */
-
-import { mountVueComponent } from "utils/mountVueComponent";
-
-import { BackboneTagService } from "./backboneTagService";
-import Tags from "./Tags";
-import { TagService } from "./tagService";
-
-/**
- * General mount function for the tags that were previously rendered
- * by the tagging_common.mako file
- */
-export const mountMakoTags = (options = {}, el) => {
- const { id, itemClass, tags = [], disabled = false, context = "unspecified" } = options;
-
- const propData = {
- storeKey: `${itemClass}-${id}`,
- tagService: new TagService({ id, itemClass, context }),
- tags,
- disabled,
- };
-
- const fn = mountVueComponent(Tags);
- const vm = fn(propData, el);
- vm.$on("tag-click", makoClickHandler(options, vm));
- return vm;
-};
-
-/**
- * Generate a click handler for the tags
- *
- * @param {object} options Passed options from mount fn
- */
-const makoClickHandler = (options, vm) =>
- function (tag) {
- if (!tag) {
- return;
- }
-
- console.error("MAKO Tag click handler deprecated");
- };
-
-/**
- * Mount function when a backbone model is provided.
- */
-export const mountModelTags = (options = {}, el) => {
- const { model, disabled = false, context = "unspecified" } = options;
-
- if (!model) {
- console.warn("Missing model in mountModelTags");
- return;
- }
-
- const { id, model_class: itemClass, tags = [] } = model.attributes;
-
- const propData = {
- storeKey: `${itemClass}-${id}`,
- tagService: new BackboneTagService({ id, itemClass, context, model }),
- tags,
- disabled,
- };
-
- const fn = mountVueComponent(Tags);
- return fn(propData, el);
-};
diff --git a/client/src/components/Tags/tagService.js b/client/src/components/Tags/tagService.js
deleted file mode 100644
index ab79a244757..00000000000
--- a/client/src/components/Tags/tagService.js
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
- * Generates a service object used by the tagging component to save, delete and
- * lookup potential tag options for the autocomplete feature. Standard typeahead
- * debouncing and ajax cancelling functionality is also provided here, though an
- * argument can be made that it more properly belongs in the component that is
- * handling the inputs.
- *
- * TODO: convert the associated python endpoint to a legit json REST service
- */
-import axios from "axios";
-import { getAppRoot } from "onload/loadConfig";
-import { Subject } from "rxjs";
-import { debounceTime, distinctUntilChanged, filter, map, switchMap } from "rxjs/operators";
-
-import { createTag } from "./model";
-
-export class TagService {
- constructor({ id, itemClass, context, debounceInterval = 150 }) {
- this.id = id;
- this.itemClass = itemClass;
- this.context = context;
- this.debounceInterval = debounceInterval;
-
- // Buffer for autocomplete text changes
- this._searchText = new Subject();
- }
-
- /**
- * Autocomplete observable. Subscribe to this object to get updates to
- * matching autocomplete results as the autocompleteSearchText property is
- * changed
- */
- get autocompleteOptions() {
- return this._searchText.pipe(
- map((txt) => txt.replace("name:", "")),
- filter((txt) => txt.length),
- debounceTime(this.debounceInterval),
- distinctUntilChanged(),
- switchMap((txt) => this.autocomplete(txt))
- );
- }
-
- /**
- * Set this property to start process of retrieving autocomplete results.
- */
- set autocompleteSearchText(txt) {
- this._searchText.next(txt);
- }
-
- /**
- * Save tag, input can be text string or tag model
- * @param {string|Tag} tag
- * @returns Promise yielding new tag model
- */
- async save(rawTag) {
- const { id, itemClass, context } = this;
- const tag = createTag(rawTag);
- if (!tag.valid) {
- throw new Error("Invalid tag");
- }
- const url = `${getAppRoot()}tag/add_tag_async`;
- const config = {
- params: { item_id: id, item_class: itemClass, context: context, new_tag: tag.text },
- };
- const response = await axios.get(url, config);
- if (response.status !== 200) {
- throw new Error(`Unable to save tag: ${tag}`);
- }
- return createTag(tag);
- }
-
- /**
- * Delete tag, input can be text string or tag model
- * @param {string|Tag} tag
- * @returns Promise yielding deleted tag model
- */
- async delete(rawTag) {
- const { id, itemClass, context } = this;
- const tag = createTag(rawTag);
- const url = `${getAppRoot()}tag/remove_tag_async`;
- const config = { params: { item_id: id, item_class: itemClass, context: context, tag_name: tag.text } };
- const response = await axios.get(url, config);
- if (response.status !== 200) {
- throw new Error(`Unable to delete tag: ${tag}`);
- }
- return tag;
- }
-
- /**
- * Looks up autocomplete options based on search text
- * @param {string} searchText
- * @returns Promise yielding an array of tag models
- */
- async autocomplete(searchText) {
- const { id, itemClass } = this;
- const url = `${getAppRoot()}tag/tag_autocomplete_data`;
- const config = { params: { item_id: id, item_class: itemClass, q: searchText } };
- const response = await axios.get(url, config);
- if (response.status !== 200) {
- throw new Error(`Unable to retrieve autocomplete tags for search string: ${searchText}`);
- }
- return parseAutocompleteResults(response.data).map(createTag);
- }
-}
-
-/**
- * Utility function parser for the archaic result format in the current API. See
- * testData/autocompleteResponse.txt for a sample.
- * @param {string} rawResponse
- */
-export function parseAutocompleteResults(rawResponse) {
- return rawResponse
- .split("\n")
- .filter((line) => line.includes("|"))
- .map((line) => line.split("|")[0])
- .filter((label) => label.length)
- .filter((label) => label !== "#Header");
-}
diff --git a/client/src/components/Tags/tagService.test.js b/client/src/components/Tags/tagService.test.js
deleted file mode 100644
index 5e73089eab3..00000000000
--- a/client/src/components/Tags/tagService.test.js
+++ /dev/null
@@ -1,99 +0,0 @@
-import axios from "axios";
-import MockAdapter from "axios-mock-adapter";
-
-import { createTag } from "./model";
-import { TagService } from "./tagService";
-import autocompleteResponse from "./testData/autocompleteResponse.txt";
-
-describe("Tags/tagService.js", () => {
- const axiosMock = new MockAdapter(axios);
- const svcParams = {
- id: 123,
- itemClass: "fooClass",
- context: "",
- debounceInterval: 50, // shorter value than default for unit tests
- };
- const testLabel = "fo0bar123";
- const expectedParams = {
- item_id: 123,
- item_class: "fooClass",
- context: "",
- };
-
- const svc = new TagService(svcParams);
- beforeEach(() => {
- axiosMock.onAny().reply(200, autocompleteResponse);
- });
-
- afterEach(() => {
- axiosMock.reset();
- });
-
- describe("save", () => {
- const testTag = createTag(testLabel);
-
- const expectedSaveUrl = `/tag/add_tag_async`;
-
- it("should save a string tag", async () => {
- const savedTag = await svc.save(testLabel);
- expect(savedTag.text).toBe(testLabel);
- expect(axiosMock.history.get[0].url).toBe(expectedSaveUrl);
- expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, new_tag: testLabel });
- });
-
- it("should save an object tag", async () => {
- const savedTag = await svc.save(testTag);
- expect(savedTag.text).toBe(testLabel);
- expect(axiosMock.history.get[0].url).toBe(expectedSaveUrl);
- expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, new_tag: testLabel });
- });
-
- // TODO: test error conditions
- });
- describe("delete", () => {
- const testTag = createTag(testLabel);
-
- const expectedDeleteUrl = `/tag/remove_tag_async`;
-
- it("should delete a text tag", async () => {
- const result = await svc.delete(testTag);
- expect(result).toBeTruthy();
- expect(axiosMock.history.get[0].url).toBe(expectedDeleteUrl);
- expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, tag_name: testLabel });
- });
-
- it("should delete an object tag", async () => {
- const result = await svc.delete(testTag);
- expect(result).toBeTruthy();
- expect(axiosMock.history.get[0].url).toBe(expectedDeleteUrl);
- expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, tag_name: testLabel });
- });
-
- // TODO: test error conditions
- });
-
- describe("autocomplete", () => {
- const searchString = "foo";
- const expectedSearchUrl = `/tag/tag_autocomplete_data`;
-
- const checkAutocompleteResult = (result) => {
- expect(result).toBeTruthy();
- expect(result instanceof Array).toBeTruthy();
- expect(result.length == 2).toBeTruthy();
- expect(result[0] instanceof Object).toBeTruthy();
- expect((result[0].text = "abc")).toBeTruthy();
- };
-
- // straight ajax request, unused in practice, but it's easier to
- // test this call if we just expose it
- const { ...searchParams } = expectedParams;
- delete searchParams.context;
-
- it("ajax call should return tag objects", async () => {
- const result = await svc.autocomplete(searchString);
- expect(axiosMock.history.get[0].url).toBe(expectedSearchUrl);
- expect(axiosMock.history.get[0].params).toEqual({ ...searchParams, q: searchString });
- checkAutocompleteResult(result);
- });
- });
-});
diff --git a/client/src/components/Tags/testData/autocompleteResponse.txt b/client/src/components/Tags/testData/autocompleteResponse.txt
deleted file mode 100644
index 7d8b85f44af..00000000000
--- a/client/src/components/Tags/testData/autocompleteResponse.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-#Header|Your Tags
-abc|abc
-def|def
\ No newline at end of file