[IMP] spreadsheet_dashboard: grid cursor
closes odoo/odoo#101946 X-original-commit: 26ab6164e92b4ab7149a85b74cbff127c35fb4a0 Signed-off-by: Rémi Rahir (rar) <rar@odoo.com> Signed-off-by: Pierre Rousseau (pro) <pro@odoo.com>
This commit is contained in:
@@ -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/**/*",
|
||||
|
||||
@@ -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<ClickableCell>}
|
||||
*/
|
||||
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;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
.o-dashboard-clickable-cell {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<odoo>
|
||||
<t t-name="spreadsheet_dashboard.o-spreadsheet-Grid" t-inherit="spreadsheet.o-spreadsheet-Grid" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('o-grid-overlay')]" position="inside">
|
||||
<div t-if="env.model.getters.isDashboard()"
|
||||
t-foreach="getClickableCells()"
|
||||
t-as="cell"
|
||||
class="o-dashboard-clickable-cell"
|
||||
t-att-style="getCellClickableStyle(cell)"
|
||||
t-key="cell.key"/>
|
||||
</xpath>
|
||||
</t>
|
||||
</odoo>
|
||||
@@ -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);
|
||||
});
|
||||
Reference in New Issue
Block a user