diff --git a/addons/spreadsheet_dashboard/__manifest__.py b/addons/spreadsheet_dashboard/__manifest__.py index 50c232a21ff..44f2e3877fb 100644 --- a/addons/spreadsheet_dashboard/__manifest__.py +++ b/addons/spreadsheet_dashboard/__manifest__.py @@ -21,11 +21,11 @@ "assets": { "spreadsheet.o_spreadsheet": [ "spreadsheet_dashboard/static/src/bundle/**/*.js", + "spreadsheet_dashboard/static/src/bundle/**/*.xml", ], "web.assets_backend": [ "spreadsheet_dashboard/static/src/assets/**/*.js", "spreadsheet_dashboard/static/src/**/*.scss", - "spreadsheet_dashboard/static/src/**/*.xml", ], "web.qunit_suite_tests": [ "spreadsheet_dashboard/static/tests/**/*", diff --git a/addons/spreadsheet_dashboard/static/src/bundle/grid_cursor/grid.js b/addons/spreadsheet_dashboard/static/src/bundle/grid_cursor/grid.js new file mode 100644 index 00000000000..2a4441e1a46 --- /dev/null +++ b/addons/spreadsheet_dashboard/static/src/bundle/grid_cursor/grid.js @@ -0,0 +1,90 @@ +/** @odoo-module */ + +import { getNumberOfListFormulas } from "@spreadsheet/list/list_helpers"; +import spreadsheet from "@spreadsheet/o_spreadsheet/o_spreadsheet_extended"; +import { getNumberOfPivotFormulas } from "@spreadsheet/pivot/pivot_helpers"; +import { patch } from "@web/core/utils/patch"; + +const { Grid } = spreadsheet.components; + +function positionToZone(position) { + return { left: position.col, right: position.col, top: position.row, bottom: position.row }; +} + +/** + * @typedef ClickableCell + * @property {number} left Left position of the cell + * @property {number} top Top position of the cell + * @property {number} width Width of the cell + * @property {number} height Height of the cell + * @property {number} key Unique identifier + */ + +/* Used for t-key */ +let key = 0; + +patch(Grid.prototype, "spreadsheet_dashboard_grid_cursor", { + getCellClickableStyle(coordinates) { + return ` + top: ${coordinates.top}px; + left: ${coordinates.left}px; + width: ${coordinates.width}px; + height: ${coordinates.height}px; + `; + }, + + /** + * Get all the boxes for the cell in the sheet view that are clickable. + * This function is used to render an overlay over each clickable cell in + * order to display a pointer cursor. + * + * @returns {Array} + */ + getClickableCells() { + const cells = []; + const sheetId = this.env.model.getters.getActiveSheetId(); + for (const col of this.env.model.getters.getSheetViewVisibleCols()) { + for (const row of this.env.model.getters.getSheetViewVisibleRows()) { + const cell = this.env.model.getters.getCell(sheetId, col, row); + if (this.isClickable(cell)) { + let zone; + if (this.env.model.getters.isInMerge(sheetId, col, row)) { + zone = this.env.model.getters.getMerge(sheetId, col, row); + } else { + zone = positionToZone({ col, row }); + } + const rect = this.env.model.getters.getVisibleRect(zone); + cells.push({ + left: rect.x, + top: rect.y, + width: rect.width, + height: rect.height, + key: ++key, + }); + } + } + } + return cells; + }, + + isClickable(cell) { + if (!cell) { + return false; + } + // Links + if (cell.isLink()) { + return true; + } + // Pivot / Lists + if ( + cell.isFormula() && + cell.evaluated.value !== "" && + !cell.evaluated.error && + (getNumberOfPivotFormulas(cell.content) === 1 || + getNumberOfListFormulas(cell.content) === 1) + ) { + return true; + } + return false; + }, +}); diff --git a/addons/spreadsheet_dashboard/static/src/bundle/grid_cursor/grid.scss b/addons/spreadsheet_dashboard/static/src/bundle/grid_cursor/grid.scss new file mode 100644 index 00000000000..de5f9efb778 --- /dev/null +++ b/addons/spreadsheet_dashboard/static/src/bundle/grid_cursor/grid.scss @@ -0,0 +1,4 @@ +.o-dashboard-clickable-cell { + position: absolute; + cursor: pointer; +} diff --git a/addons/spreadsheet_dashboard/static/src/bundle/grid_cursor/grid.xml b/addons/spreadsheet_dashboard/static/src/bundle/grid_cursor/grid.xml new file mode 100644 index 00000000000..42cc0ffddc0 --- /dev/null +++ b/addons/spreadsheet_dashboard/static/src/bundle/grid_cursor/grid.xml @@ -0,0 +1,12 @@ + + + +
+ + + diff --git a/addons/spreadsheet_dashboard/static/tests/grid_cursor/grid_cursor_test.js b/addons/spreadsheet_dashboard/static/tests/grid_cursor/grid_cursor_test.js new file mode 100644 index 00000000000..43201487390 --- /dev/null +++ b/addons/spreadsheet_dashboard/static/tests/grid_cursor/grid_cursor_test.js @@ -0,0 +1,89 @@ +/** @odoo-module */ + +import spreadsheet from "@spreadsheet/o_spreadsheet/o_spreadsheet_extended"; +import { getFixture } from "@web/../tests/helpers/utils"; +import { getDashboardServerData } from "../utils/data"; +import { createSpreadsheetDashboard } from "../utils/dashboard_action"; +import { getBasicData } from "@spreadsheet/../tests/utils/data"; + +const { Model } = spreadsheet; +const { functionRegistry } = spreadsheet.registries; +const { args } = spreadsheet.helpers; + +async function createDashboardWithModel(model) { + const serverData = getDashboardServerData(); + const json = JSON.stringify(model.exportData()); + const dashboard = serverData.models["spreadsheet.dashboard"].records[0]; + dashboard.raw = json; + dashboard.json_data = json; + serverData.models = { + ...serverData.models, + ...getBasicData(), + }; + await createSpreadsheetDashboard({ serverData, spreadsheetId: dashboard.id }); + return getFixture(); +} + +QUnit.module("spreadsheet_dashboard > grid cursor"); + +QUnit.test("A link in a dashboard should be clickable", async (assert) => { + const data = { + sheets: [ + { + cells: { A1: { content: "[Odoo](https://odoo.com)" } }, + }, + ], + }; + const model = new Model(data, { mode: "dashboard" }); + const target = await createDashboardWithModel(model); + assert.containsOnce(target, ".o-dashboard-clickable-cell"); +}); + +QUnit.test("Invalid pivot/list formulas should not be clickable", async (assert) => { + const data = { + sheets: [ + { + cells: { + A1: { content: `=ODOO.PIVOT("1", "measure")` }, + A2: { content: `=ODOO.LIST("1", 1, "name")` }, + }, + }, + ], + }; + const model = new Model(data, { mode: "dashboard" }); + const target = await createDashboardWithModel(model); + assert.containsNone(target, ".o-dashboard-clickable-cell"); +}); + +QUnit.test("pivot/list formulas should be clickable", async (assert) => { + const list = functionRegistry.get("ODOO.LIST"); + const pivot = functionRegistry.get("ODOO.PIVOT"); + + const mock = { + description: "Mock function to avoid setup all data sources process", + compute: () => 1, + args: args(``), + returns: ["NUMBER"], + }; + + functionRegistry.add("ODOO.LIST", mock); + functionRegistry.add("ODOO.PIVOT", mock); + + const data = { + sheets: [ + { + cells: { + A1: { content: `=ODOO.PIVOT()` }, + A2: { content: `=ODOO.LIST()` }, + }, + }, + ], + }; + + const model = new Model(data); + const target = await createDashboardWithModel(model); + assert.containsN(target, ".o-dashboard-clickable-cell", 2); + + functionRegistry.add("ODOO.LIST", list); + functionRegistry.add("ODOO.PIVOT", pivot); +});