From 1f51f94c348ca1ea2b4f8de4de9d23ef49e2c3c0 Mon Sep 17 00:00:00 2001 From: Vitalii Shytikov Date: Mon, 10 Jun 2024 15:05:18 +0200 Subject: [PATCH 1/4] receive table data from custom event --- cypress/e2e/table.cy.js | 354 ++++------------------------------------ src/components/table.js | 130 ++++----------- 2 files changed, 64 insertions(+), 420 deletions(-) diff --git a/cypress/e2e/table.cy.js b/cypress/e2e/table.cy.js index af869c6..7d9da14 100644 --- a/cypress/e2e/table.cy.js +++ b/cypress/e2e/table.cy.js @@ -1,5 +1,5 @@ context("table element", function() { - describe("when element have data-os-element=loop and data-os-view attributes", () => { + describe("when element have data-os-element=loop", () => { beforeEach(() => { cy.visit("/").then(() => { cy.request("/").then((response) => { @@ -19,67 +19,44 @@ context("table element", function() { }); }); - it("calls api/v1/response_views/view_uuid/items", function() { - cy.intercept( - "GET", - "**/api/v1/response_views/view_uuid/items", - { - data: [], - }, - ).as("responseViewsItems"); - - cy.visit("/"); - - cy.wait("@responseViewsItems").then((interceptor) => { - expect(interceptor.request.headers["workspace-id"]).to.equal("1"); - expect(interceptor.request.url).to.match( - /response_views\/view_uuid\/items/, - ); - }); - }); - - describe("when response view have items", () => { + describe("when table-success event has been triggered", () => { it("renders table with data", function() { - cy.intercept( - "GET", - "**/api/v1/response_views/view_uuid/items", - { - data: [ - [ - { - "columnId": 1, - "uuid": "column_uuid_1_1", - "name": "Name", - "value": "Mohamed Salah", - }, - { - "columnId": 2, - "uuid": "column_uuid_1_2", - "name": "City", - "value": "Zhytomyr", - }, - ], - [ - { - "columnId": 1, - "uuid": "column_uuid_1_1", - "name": "Name", - "value": "Lewis Hamilton", - }, - { - "columnId": 2, - "uuid": "column_uuid_1_2", - "name": "City", - "value": "Brovary", - }, - ], - ], - }, - ).as("responseViewsItems"); + const data = [ + [ + { + "columnId": 1, + "uuid": "column_uuid_1_1", + "name": "Name", + "value": "Mohamed Salah", + }, + { + "columnId": 2, + "uuid": "column_uuid_1_2", + "name": "City", + "value": "Zhytomyr", + }, + ], + [ + { + "columnId": 1, + "uuid": "column_uuid_1_1", + "name": "Name", + "value": "Lewis Hamilton", + }, + { + "columnId": 2, + "uuid": "column_uuid_1_2", + "name": "City", + "value": "Brovary", + }, + ], + ]; cy.visit("/"); - - cy.wait("@responseViewsItems").then(() => { + cy.get("os-table").trigger("table-success", { + detail: { data }, + force: true, + }).then(() => { cy.get("os-table").find("table").should("exist"); cy.get("os-table").find("thead").should("exist"); cy.get("os-table").find("tbody").should("exist"); @@ -119,265 +96,6 @@ context("table element", function() { }); }); }); - - it("renders empty element with os-hidden class", function() { - cy.intercept( - "GET", - "**/api/v1/response_views/view_uuid/items", - { - data: [ - [ - { - "columnId": 1, - "uuid": "column_uuid_1_1", - "name": "Name", - "value": "Mohamed Salah", - }, - ], - ], - }, - ).as("responseViewsItems"); - - cy.visit("/"); - - cy.wait("@responseViewsItems").then(() => { - cy.get("[data-os-element=empty]").should("have.class", "os-hidden"); - cy.get("[data-os-element=empty]").should( - "have.attr", - "data-os-for", - "view_uuid", - ); - cy.get("[data-os-element=empty]").should( - "have.text", - "No data found", - ); - }); - }); - - it("dispatches a table-success event", function() { - const data = [ - [ - { - columnId: 1, - uuid: "column_uuid_1_1", - name: "Name", - value: "Mohamed Salah", - }, - ], - ]; - cy.intercept( - "GET", - "**/api/v1/response_views/view_uuid/items", - { data }, - ).as("responseViewsItems"); - - cy.visit("/"); - - cy.get("os-table") - .then(($field) => { - cy.spy($field[0], "dispatchEvent").as("dispatchEventSpy"); - }); - cy.wait("@responseViewsItems").then(() => { - cy.get("@dispatchEventSpy").should((spy) => { - const { detail, type } = spy.args[0][0]; - expect(type).to.equal("table-success"); - expect(detail.data).to.deep.equal(data); - expect(detail.responseViewUuid).to.equal("view_uuid"); - }); - }); - }); - - it("dispatches a table-loading event with value: false", function() { - cy.intercept( - "GET", - "**/api/v1/response_views/view_uuid/items", - { - data: [ - [ - { - "columnId": 1, - "uuid": "column_uuid_1_1", - "name": "Name", - "value": "Mohamed Salah", - }, - ], - ], - }, - ).as("responseViewsItems"); - cy.visit("/"); - - cy.get("os-table") - .then(($field) => { - cy.spy($field[0], "dispatchEvent").as("dispatchEventSpy"); - }); - cy.wait("@responseViewsItems").then(() => { - cy.get("@dispatchEventSpy").should((spy) => { - const { detail, type } = spy.args[1][0]; - expect(type).to.equal("table-loading"); - expect(detail.value).to.equal(false); - }); - }); - }); - }); - - describe("when response view does not have items", () => { - it("removes os-hidden class from an empty element", function() { - cy.intercept( - "GET", - "**/api/v1/response_views/view_uuid/items", - { - data: [], - }, - ).as("responseViewsItems"); - - cy.visit("/"); - - cy.wait("@responseViewsItems").then(() => { - cy.get("[data-os-element=empty]").should( - "not.have.class", - "os-hidden", - ); - cy.get("[data-os-element=empty]").should("be.visible"); - }); - }); - - it("dispatches a table-empty event", function() { - cy.intercept( - "GET", - "**/api/v1/response_views/view_uuid/items", - { data: [] }, - ).as("responseViewsItems"); - - cy.visit("/"); - - cy.get("os-table") - .then(($field) => { - cy.spy($field[0], "dispatchEvent").as("dispatchEventSpy"); - }); - cy.wait("@responseViewsItems").then(() => { - cy.get("@dispatchEventSpy").should((spy) => { - const { detail, type } = spy.args[0][0]; - expect(type).to.equal("table-empty"); - expect(detail.responseViewUuid).to.equal("view_uuid"); - }); - }); - }); - }); - - describe("when items request does not succeeded", () => { - it("dispatches a table-error event", function() { - cy.intercept( - "GET", - "**/api/v1/response_views/view_uuid/items", - { forceNetworkError: true }, - ).as( - "itemsError", - ); - cy.visit("/"); - - cy.get("os-table") - .then(($field) => { - cy.spy($field[0], "dispatchEvent").as("dispatchEventSpy"); - }); - cy.wait("@itemsError").then(() => { - cy.get("@dispatchEventSpy").should((spy) => { - const { detail, type } = spy.args[0][0]; - expect(type).to.equal("table-error"); - expect(detail.error.message).to.equal("Failed to fetch"); - }); - }); - }); - - it("dispatches a table-loading event with value: false", function() { - cy.intercept( - "GET", - "**/api/v1/response_views/view_uuid/items", - { forceNetworkError: true }, - ).as( - "itemsError", - ); - cy.visit("/"); - - cy.get("os-table") - .then(($field) => { - cy.spy($field[0], "dispatchEvent").as("dispatchEventSpy"); - }); - cy.wait("@itemsError").then(() => { - cy.get("@dispatchEventSpy").should((spy) => { - const { detail, type } = spy.args[1][0]; - expect(type).to.equal("table-loading"); - expect(detail.value).to.equal(false); - }); - }); - }); - }); - }); - - describe("when element does not have data-os-element=loop attribute", () => { - beforeEach(() => { - cy.visit("/").then(() => { - cy.request("/").then((response) => { - const body = ` - - - - `; - - const modifiedHtml = response.body.replace( - /[\s\S]*<\/body>/, - body, - ); - - cy.intercept("/", modifiedHtml); - }); - }); - }); - - it("warns user in console", function() { - cy.visit("/", { - onBeforeLoad(win) { - cy.stub(win.console, "warn").as("consoleWarn"); - }, - }); - - cy.get("@consoleWarn").should( - "be.calledWith", - 'data-os-element="loop" is not set for element', - ); - }); - }); - - describe("when element does not have data-os-view attribute", () => { - beforeEach(() => { - cy.visit("/").then(() => { - cy.request("/").then((response) => { - const body = ` - - - - `; - - const modifiedHtml = response.body.replace( - /[\s\S]*<\/body>/, - body, - ); - - cy.intercept("/", modifiedHtml); - }); - }); - }); - - it("warns user in console", function() { - cy.visit("/", { - onBeforeLoad(win) { - cy.stub(win.console, "warn").as("consoleWarn"); - }, - }); - - cy.get("@consoleWarn").should( - "be.calledWith", - "data-os-view is not set for element", - ); }); }); }); diff --git a/src/components/table.js b/src/components/table.js index bec8fa5..1467be6 100644 --- a/src/components/table.js +++ b/src/components/table.js @@ -1,5 +1,3 @@ -import { host, workspaceId } from "../utils/script_attributes"; - export class OSTable extends HTMLElement { constructor() { super(); @@ -7,111 +5,39 @@ export class OSTable extends HTMLElement { connectedCallback() { const haveLoopAttribute = this.getAttribute("data-os-element") === "loop"; - const requestHost = host || "https://app.formli.com"; if (haveLoopAttribute) { - const responseViewUuid = this.getAttribute("data-os-view"); - if (responseViewUuid) { - const emptyElement = document.createElement("div"); - emptyElement.innerText = "No data found"; - emptyElement.classList.add("os-hidden"); - emptyElement.setAttribute("data-os-element", "empty"); - emptyElement.setAttribute("data-os-for", responseViewUuid); - this.appendChild(emptyElement); - this.dispatchLoadingEvent(true); - - fetch( - `${requestHost}/api/v1/response_views/${responseViewUuid}/items`, - { - credentials: "include", - headers: { - "Workspace-Id": workspaceId, - }, - }, - ).then(async (response) => { - if (response.ok) { - const { data } = await response.json(); - if (data.length > 0) { - const table = document.createElement("table"); - const thead = document.createElement("thead"); - const tbody = document.createElement("tbody"); - table.appendChild(thead); - table.appendChild(tbody); - // hide an empty element - emptyElement.classList.add("os-hidden"); - this.dispatchSuccessEvent(data, responseViewUuid); - data.forEach((columns, index) => { - const tbodyRow = document.createElement("tr"); - const theadRow = document.createElement("tr"); - columns.forEach((column) => { - if (index === 0) { - // create thead - const th = document.createElement("th"); - th.innerHTML = column.name; - theadRow.appendChild(th); - thead.appendChild(theadRow); - } - // create tbody - const td = document.createElement("td"); - td.innerHTML = column.value; - td.setAttribute("data-os-default", "column"); - td.setAttribute("data-os-column", column.uuid); - tbodyRow.appendChild(td); - }); - tbody.appendChild(tbodyRow); - }); - - this.appendChild(table); - } else { - // show an empty element - emptyElement.classList.remove("os-hidden"); - this.dispatchTableEmptyEvent(responseViewUuid); + this.addEventListener("table-success", (event) => { + const data = event.detail.data; + const table = document.createElement("table"); + const thead = document.createElement("thead"); + const tbody = document.createElement("tbody"); + table.appendChild(thead); + table.appendChild(tbody); + data.forEach((columns, index) => { + const tbodyRow = document.createElement("tr"); + const theadRow = document.createElement("tr"); + columns.forEach((column) => { + if (index === 0) { + // create thead + const th = document.createElement("th"); + th.innerHTML = column.name; + theadRow.appendChild(th); + thead.appendChild(theadRow); } - } - this.dispatchLoadingEvent(false); - }).catch((error) => { - console.error(error); - this.dispatchErrorEvent(error); - this.dispatchLoadingEvent(false); + // create tbody + const td = document.createElement("td"); + td.innerHTML = column.value; + td.setAttribute("data-os-default", "column"); + td.setAttribute("data-os-column", column.uuid); + tbodyRow.appendChild(td); + }); + tbody.appendChild(tbodyRow); }); - } else { - console.warn( - "data-os-view is not set for element", - ); - } - } else { - console.warn( - 'data-os-element="loop" is not set for element', - ); - } - } - - dispatchLoadingEvent(value) { - this.dispatchEvent( - new CustomEvent("table-loading", { detail: { value } }), - ); - } - - dispatchSuccessEvent(data, responseViewUuid) { - this.dispatchEvent( - new CustomEvent("table-success", { - detail: { data, responseViewUuid }, - }), - ); - } - dispatchTableEmptyEvent(responseViewUuid) { - this.dispatchEvent( - new CustomEvent("table-empty", { - detail: { responseViewUuid }, - }), - ); - } - - dispatchErrorEvent(error) { - this.dispatchEvent( - new CustomEvent("table-error", { detail: { error } }), - ); + this.appendChild(table); + }); + } } } From 03ec6ca05bcf01dd3e77f453acfc5754710b9b41 Mon Sep 17 00:00:00 2001 From: Vitalii Shytikov Date: Mon, 10 Jun 2024 15:37:03 +0200 Subject: [PATCH 2/4] send table-loaded custom event --- src/components/table.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/table.js b/src/components/table.js index 1467be6..cad5244 100644 --- a/src/components/table.js +++ b/src/components/table.js @@ -7,6 +7,8 @@ export class OSTable extends HTMLElement { const haveLoopAttribute = this.getAttribute("data-os-element") === "loop"; if (haveLoopAttribute) { + this.dispatchEvent(new CustomEvent("table-loaded", { detail: { loaded: true } })); + this.addEventListener("table-success", (event) => { const data = event.detail.data; const table = document.createElement("table"); From 76d7b71b90a8b714d305ad9010e1108a77a68287 Mon Sep 17 00:00:00 2001 From: Vitalii Shytikov Date: Mon, 10 Jun 2024 17:03:51 +0200 Subject: [PATCH 3/4] Revert "send table-loaded custom event" This reverts commit 03ec6ca05bcf01dd3e77f453acfc5754710b9b41. --- src/components/table.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/table.js b/src/components/table.js index cad5244..1467be6 100644 --- a/src/components/table.js +++ b/src/components/table.js @@ -7,8 +7,6 @@ export class OSTable extends HTMLElement { const haveLoopAttribute = this.getAttribute("data-os-element") === "loop"; if (haveLoopAttribute) { - this.dispatchEvent(new CustomEvent("table-loaded", { detail: { loaded: true } })); - this.addEventListener("table-success", (event) => { const data = event.detail.data; const table = document.createElement("table"); From 75c8a0b2e74021b13aa2d0107e8f6a063607b223 Mon Sep 17 00:00:00 2001 From: Vitalii Shytikov Date: Fri, 27 Sep 2024 13:39:33 +0200 Subject: [PATCH 4/4] listen to initialized event --- cypress/e2e/table.cy.js | 6 +++--- src/components/table.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cypress/e2e/table.cy.js b/cypress/e2e/table.cy.js index 7d9da14..0475ae9 100644 --- a/cypress/e2e/table.cy.js +++ b/cypress/e2e/table.cy.js @@ -19,7 +19,7 @@ context("table element", function() { }); }); - describe("when table-success event has been triggered", () => { + describe("when initialized event has been triggered", () => { it("renders table with data", function() { const data = [ [ @@ -53,8 +53,8 @@ context("table element", function() { ]; cy.visit("/"); - cy.get("os-table").trigger("table-success", { - detail: { data }, + cy.get("os-table").trigger("initialized", { + detail: { value: data }, force: true, }).then(() => { cy.get("os-table").find("table").should("exist"); diff --git a/src/components/table.js b/src/components/table.js index 1467be6..40c0297 100644 --- a/src/components/table.js +++ b/src/components/table.js @@ -7,8 +7,8 @@ export class OSTable extends HTMLElement { const haveLoopAttribute = this.getAttribute("data-os-element") === "loop"; if (haveLoopAttribute) { - this.addEventListener("table-success", (event) => { - const data = event.detail.data; + this.addEventListener("initialized", (event) => { + const data = event.detail.value; const table = document.createElement("table"); const thead = document.createElement("thead"); const tbody = document.createElement("tbody");