mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Adding traceback to trigger formulas
Summary: Traceback is available on the Creator Panel in the formula editor. It is evaluated the same way as for normal formulas. In case when the traceback is not available, only the error name is displayed with information that traceback is not available. Cell with an error, when edited, shows the previous valid value that was used before the error happened (or None for new rows). Value is stored inside the RaisedException object that is stored in a cell. Test Plan: Created tests Reviewers: alexmojaki Reviewed By: alexmojaki Subscribers: alexmojaki, dsagal Differential Revision: https://phab.getgrist.com/D3033
This commit is contained in:
@@ -18,6 +18,9 @@ export interface ColumnRec extends IRowModel<"_grist_Tables_column"> {
|
||||
// Is a real formula column (not an empty column; i.e. contains a non-empty formula).
|
||||
isRealFormula: ko.Computed<boolean>;
|
||||
|
||||
// Is a trigger formula column (not formula, but contains non-empty formula)
|
||||
hasTriggerFormula: ko.Computed<boolean>;
|
||||
|
||||
// Used for transforming a column.
|
||||
// Reference to the original column for a transform column, or to itself for a non-transforming column.
|
||||
origColRef: ko.Observable<number>;
|
||||
@@ -56,6 +59,8 @@ export function createColumnRec(this: ColumnRec, docModel: DocModel): void {
|
||||
|
||||
// Is this a real formula column (not an empty column; i.e. contains a non-empty formula).
|
||||
this.isRealFormula = ko.pureComputed(() => this.isFormula() && this.formula() !== '');
|
||||
// If this column has a trigger formula defined
|
||||
this.hasTriggerFormula = ko.pureComputed(() => !this.isFormula() && this.formula() !== '');
|
||||
|
||||
// Used for transforming a column.
|
||||
// Reference to the original column for a transform column, or to itself for a non-transforming column.
|
||||
|
||||
@@ -158,7 +158,8 @@ function createFormulaErrorObs(owner: MultiHolder, gristDoc: GristDoc, origColum
|
||||
function countErrors() {
|
||||
if (owner.isDisposed()) { return; }
|
||||
const tableData = gristDoc.docData.getTable(origColumn.table.peek().tableId.peek());
|
||||
if (tableData && origColumn.isRealFormula.peek()) {
|
||||
const isFormula = origColumn.isRealFormula.peek() || origColumn.hasTriggerFormula.peek();
|
||||
if (tableData && isFormula) {
|
||||
const colId = origColumn.colId.peek();
|
||||
const numCells = tableData.getColValues(colId)?.length || 0;
|
||||
const numErrors = tableData.countErrors(colId) || 0;
|
||||
|
||||
@@ -469,13 +469,16 @@ function getFormulaError(
|
||||
gristDoc: GristDoc, editRow: DataRowModel, column: ColumnRec
|
||||
): Observable<CellValue>|undefined {
|
||||
const colId = column.colId.peek();
|
||||
let formulaError: Observable<CellValue>|undefined;
|
||||
const cellCurrentValue = editRow.cells[colId].peek();
|
||||
if (column.isFormula() && isRaisedException(cellCurrentValue)) {
|
||||
const fv = formulaError = Observable.create(null, cellCurrentValue);
|
||||
const isFormula = column.isFormula() || column.hasTriggerFormula();
|
||||
if (isFormula && isRaisedException(cellCurrentValue)) {
|
||||
const formulaError = Observable.create(null, cellCurrentValue);
|
||||
gristDoc.docData.getFormulaError(column.table().tableId(), colId, editRow.getRowId())
|
||||
.then(value => { fv.set(value); })
|
||||
.then(value => {
|
||||
formulaError.set(value);
|
||||
})
|
||||
.catch(reportError);
|
||||
return formulaError;
|
||||
}
|
||||
return formulaError;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import {createMobileButtons, getButtonMargins} from 'app/client/widgets/EditorBu
|
||||
import {EditorPlacement, ISize} from 'app/client/widgets/EditorPlacement';
|
||||
import {NewBaseEditor, Options} from 'app/client/widgets/NewBaseEditor';
|
||||
import {undef} from 'app/common/gutil';
|
||||
import {dom, Observable, styled} from 'grainjs';
|
||||
import {Computed, dom, Observable, styled} from 'grainjs';
|
||||
import {isRaisedException} from "app/common/gristTypes";
|
||||
import {decodeObject, RaisedException} from "app/plugin/objtypes";
|
||||
|
||||
// How wide to expand the FormulaEditor when an error is shown in it.
|
||||
const minFormulaErrorWidth = 400;
|
||||
@@ -59,8 +61,28 @@ export class FormulaEditor extends NewBaseEditor {
|
||||
: options.commands;
|
||||
this._commandGroup = this.autoDispose(createGroup(allCommands, this, options.field.editingFormula));
|
||||
|
||||
const hideErrDetails = Observable.create(null, true);
|
||||
const formulaError = options.formulaError;
|
||||
const hideErrDetails = Observable.create(this, true);
|
||||
const raisedException = Computed.create(this, use => {
|
||||
if (!options.formulaError) {
|
||||
return null;
|
||||
}
|
||||
const error = isRaisedException(use(options.formulaError)) ?
|
||||
decodeObject(use(options.formulaError)) as RaisedException:
|
||||
new RaisedException(["Unknown error"]);
|
||||
return error;
|
||||
});
|
||||
const errorText = Computed.create(this, raisedException, (_, error) => {
|
||||
if (!error) {
|
||||
return "";
|
||||
}
|
||||
return error.message ? `${error.name} : ${error.message}` : error.name;
|
||||
});
|
||||
const errorDetails = Computed.create(this, raisedException, (_, error) => {
|
||||
if (!error) {
|
||||
return "";
|
||||
}
|
||||
return error.details ?? "";
|
||||
});
|
||||
|
||||
this.autoDispose(this._formulaEditor);
|
||||
this._dom = dom('div.default_editor',
|
||||
@@ -103,21 +125,27 @@ export class FormulaEditor extends NewBaseEditor {
|
||||
aceObj.once("change", () => options.field.editingFormula(true));
|
||||
})
|
||||
),
|
||||
(formulaError ?
|
||||
(options.formulaError ?
|
||||
dom('div.error_box',
|
||||
dom('div.error_msg', testId('formula-error-msg'),
|
||||
dom.on('click', () => {
|
||||
hideErrDetails.set(!hideErrDetails.get());
|
||||
this._formulaEditor.resize();
|
||||
if (errorDetails.get()){
|
||||
hideErrDetails.set(!hideErrDetails.get());
|
||||
this._formulaEditor.resize();
|
||||
}
|
||||
}),
|
||||
dom.domComputed(hideErrDetails, (hide) => cssCollapseIcon(hide ? 'Expand' : 'Collapse')),
|
||||
dom.text((use) => { const f = use(formulaError) as string[]; return `${f[1]}: ${f[2]}`; }),
|
||||
),
|
||||
dom('div.error_details',
|
||||
dom.hide(hideErrDetails),
|
||||
dom.text((use) => (use(formulaError) as string[])[3]),
|
||||
testId('formula-error-details'),
|
||||
dom.maybe(errorDetails, () =>
|
||||
dom.domComputed(hideErrDetails, (hide) => cssCollapseIcon(hide ? 'Expand' : 'Collapse'))
|
||||
),
|
||||
dom.text(errorText),
|
||||
),
|
||||
dom.maybe(errorDetails, () =>
|
||||
dom('div.error_details',
|
||||
dom.hide(hideErrDetails),
|
||||
dom.text(errorDetails),
|
||||
testId('formula-error-details'),
|
||||
)
|
||||
)
|
||||
) : null
|
||||
)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user