diff --git a/src/components/custom-aggrid/cell-renderers.tsx b/src/components/custom-aggrid/cell-renderers.tsx
index 639fc60e6d..12a580e037 100644
--- a/src/components/custom-aggrid/cell-renderers.tsx
+++ b/src/components/custom-aggrid/cell-renderers.tsx
@@ -11,6 +11,7 @@ import { isBlankOrEmpty } from 'components/utils/validation-functions';
import { ICellRendererParams } from 'ag-grid-community';
import { CustomCellRendererProps } from 'ag-grid-react';
import { mergeSx, type MuiStyles } from '@gridsuite/commons-ui';
+import { useIntl } from 'react-intl';
const styles = {
tableCell: (theme) => ({
@@ -35,6 +36,13 @@ const styles = {
},
} as const satisfies MuiStyles;
+const FORMULA_ERROR_KEY = 'spreadsheet/formula/error';
+
+interface BaseCellRendererProps {
+ value: string | undefined;
+ tooltip?: string;
+}
+
export const BooleanCellRenderer = (props: any) => {
const isChecked = props.value;
return (
@@ -105,15 +113,24 @@ export const NumericCellRenderer = (props: NumericCellRendererProps) => {
);
};
+const BaseCellRenderer = ({ value, tooltip }: BaseCellRendererProps) => (
+
+
+ {value}
+
+
+);
+
+export const ErrorCellRenderer = (props: CustomCellRendererProps) => {
+ const intl = useIntl();
+ const errorMessage = intl.formatMessage({ id: props.value?.error });
+ const errorValue = intl.formatMessage({ id: FORMULA_ERROR_KEY });
+ return ;
+};
+
export const DefaultCellRenderer = (props: CustomCellRendererProps) => {
- const cellValue = formatCell(props);
- return (
-
-
- {cellValue.value?.toString()}
-
-
- );
+ const cellValue = formatCell(props).value?.toString();
+ return ;
};
export const NetworkModificationNameCellRenderer = (props: CustomCellRendererProps) => {
diff --git a/src/components/custom-aggrid/custom-aggrid-filters/custom-aggrid-filter.type.ts b/src/components/custom-aggrid/custom-aggrid-filters/custom-aggrid-filter.type.ts
index 0559ee28ef..546a25cb10 100644
--- a/src/components/custom-aggrid/custom-aggrid-filters/custom-aggrid-filter.type.ts
+++ b/src/components/custom-aggrid/custom-aggrid-filters/custom-aggrid-filter.type.ts
@@ -12,6 +12,7 @@ import { ColumnMenuProps } from '../../spreadsheet-view/columns/column-menu';
import { SortParams } from '../hooks/use-custom-aggrid-sort';
import { COLUMN_TYPES, CustomCellType } from '../custom-aggrid-header.type';
import type { UUID } from 'node:crypto';
+import { type ValidationError } from '../../spreadsheet-view/columns/utils/formula-validator';
export enum FILTER_DATA_TYPES {
TEXT = 'text',
@@ -66,8 +67,10 @@ export interface ColumnContext
- extends ColDef {
+ extends ColDef {
colId: string;
context?: ColumnContext;
}
diff --git a/src/components/spreadsheet-view/columns/utils/column-mapper.ts b/src/components/spreadsheet-view/columns/utils/column-mapper.ts
index 0f38b2f67b..7e1da76d73 100644
--- a/src/components/spreadsheet-view/columns/utils/column-mapper.ts
+++ b/src/components/spreadsheet-view/columns/utils/column-mapper.ts
@@ -6,7 +6,7 @@
*/
import { ColumnMenu } from '../column-menu';
import { COLUMN_TYPES } from '../../../custom-aggrid/custom-aggrid-header.type';
-import { limitedEvaluate } from './math';
+import { limitedEvaluate, MathJsValidationError } from './math';
import { ColDef, ValueGetterParams } from 'ag-grid-community';
import {
booleanColumnDefinition,
@@ -14,14 +14,18 @@ import {
numberColumnDefinition,
textColumnDefinition,
} from '../common-column-definitions';
-import { validateFormulaResult } from './formula-validator';
+import { isValidationError, validateFormulaResult } from './formula-validator';
import { ColumnDefinition, SpreadsheetTabDefinition } from '../../types/spreadsheet.type';
-import { CustomColDef } from '../../../custom-aggrid/custom-aggrid-filters/custom-aggrid-filter.type';
+import {
+ type CustomAggridValue,
+ type CustomColDef,
+} from '../../../custom-aggrid/custom-aggrid-filters/custom-aggrid-filter.type';
import { isCalculationRow } from '../../utils/calculation-utils';
+import { ErrorCellRenderer } from '../../../custom-aggrid/cell-renderers';
const createValueGetter =
(colDef: ColumnDefinition) =>
- (params: ValueGetterParams): boolean | string | number | undefined => {
+ (params: ValueGetterParams): CustomAggridValue | undefined => {
try {
// Skip formula processing for pinned rows and use raw value
if (isCalculationRow(params.node?.data?.rowType)) {
@@ -34,13 +38,11 @@ const createValueGetter =
});
const escapedFormula = colDef.formula.replace(/\\/g, '\\\\');
const result = limitedEvaluate(escapedFormula, scope);
- const validation = validateFormulaResult(result, colDef.type);
-
- if (!validation.isValid) {
- return undefined;
- }
- return result;
+ return validateFormulaResult(result, colDef.type);
} catch (e) {
+ if (e instanceof MathJsValidationError) {
+ return { error: e.error };
+ }
return undefined;
}
};
@@ -82,6 +84,8 @@ export const mapColumns = (tableDefinition: SpreadsheetTabDefinition) =>
},
},
valueGetter: createValueGetter(colDef),
+ cellRendererSelector: (params) =>
+ isValidationError(params.value) ? { component: ErrorCellRenderer } : undefined, //Returning undefined make it so the originally defined renderer is used
hide: !colDef.visible,
editable: false,
enableCellChangeFlash: true,
diff --git a/src/components/spreadsheet-view/columns/utils/formula-validator.ts b/src/components/spreadsheet-view/columns/utils/formula-validator.ts
index 1a4ddc7761..f29c09848d 100644
--- a/src/components/spreadsheet-view/columns/utils/formula-validator.ts
+++ b/src/components/spreadsheet-view/columns/utils/formula-validator.ts
@@ -6,35 +6,37 @@
*/
import { COLUMN_TYPES } from 'components/custom-aggrid/custom-aggrid-header.type';
import { MAX_FORMULA_CHARACTERS } from '../../constants';
+import { type CustomAggridValue } from '../../../custom-aggrid/custom-aggrid-filters/custom-aggrid-filter.type';
-interface ValidationResult {
- isValid: boolean;
- error?: string;
+export interface ValidationError {
+ error: string;
}
-export const validateFormulaResult = (value: any, type: COLUMN_TYPES): ValidationResult => {
+export function isValidationError(value: unknown): value is ValidationError {
+ return !!(typeof value === 'object' && value?.hasOwnProperty('error'));
+}
+
+export const validateFormulaResult = (value: CustomAggridValue, type: COLUMN_TYPES): CustomAggridValue => {
switch (type) {
case COLUMN_TYPES.NUMBER:
- return {
- isValid:
- (typeof value === 'number' && !isNaN(value)) ||
- (typeof value !== 'boolean' && !isNaN(Number(value))),
- error: 'Formula must evaluate to a number',
- };
+ return (typeof value === 'number' && !Number.isNaN(value)) ||
+ (typeof value !== 'boolean' && !Number.isNaN(Number(value)))
+ ? value
+ : { error: 'spreadsheet/formula/type/number' };
case COLUMN_TYPES.BOOLEAN:
- return {
- isValid: typeof value === 'boolean',
- error: 'Formula must evaluate to a boolean',
- };
+ return typeof value === 'boolean'
+ ? value
+ : {
+ error: 'spreadsheet/formula/type/boolean',
+ };
case COLUMN_TYPES.ENUM:
- return {
- isValid: typeof value === 'string' || typeof value === 'number',
- error: 'Formula must evaluate to a string',
- };
+ return typeof value === 'string' || typeof value === 'number'
+ ? value
+ : { error: 'spreadsheet/formula/type/enum' };
case COLUMN_TYPES.TEXT:
- return { isValid: true }; // Text accepts any type
+ return value; // Text accepts any type
default:
- return { isValid: false, error: 'Unknown column type' };
+ return { error: 'spreadsheet/formula/type/unknown' };
}
};
diff --git a/src/components/spreadsheet-view/columns/utils/math.ts b/src/components/spreadsheet-view/columns/utils/math.ts
index 1d8af99e9c..c71c3a4bf7 100644
--- a/src/components/spreadsheet-view/columns/utils/math.ts
+++ b/src/components/spreadsheet-view/columns/utils/math.ts
@@ -12,25 +12,42 @@ const instance = create(all);
export const limitedEvaluate = instance.evaluate;
+// Custom error class for MathJS validation errors
+export class MathJsValidationError extends Error {
+ constructor(public error: string) {
+ super(error);
+ this.name = 'MathJsValidationError';
+ }
+}
+
instance.import(
{
- import: function () {
- throw new Error('Function import is disabled');
+ import: () => {
+ throw new MathJsValidationError('spreadsheet/formula/import/disabled');
+ },
+ createUnit: () => {
+ throw new MathJsValidationError('spreadsheet/formula/createUnit/disabled');
+ },
+ evaluate: () => {
+ throw new MathJsValidationError('spreadsheet/formula/evaluate/disabled');
+ },
+ parse: () => {
+ throw new MathJsValidationError('spreadsheet/formula/parse/disabled');
},
- createUnit: function () {
- throw new Error('Function createUnit is disabled');
+ simplify: () => {
+ throw new MathJsValidationError('spreadsheet/formula/simplify/disabled');
},
- evaluate: function () {
- throw new Error('Function evaluate is disabled');
+ derivative: () => {
+ throw new MathJsValidationError('spreadsheet/formula/derivative/disabled');
},
- parse: function () {
- throw new Error('Function parse is disabled');
+ compile: () => {
+ throw new MathJsValidationError('spreadsheet/formula/compile/disabled');
},
- simplify: function () {
- throw new Error('Function simplify is disabled');
+ help: () => {
+ throw new MathJsValidationError('spreadsheet/formula/help/disabled');
},
- derivative: function () {
- throw new Error('Function derivative is disabled');
+ parser: () => {
+ throw new MathJsValidationError('spreadsheet/formula/parser/disabled');
},
equal: function (a: any, b: any) {
// == instead of === to be able to compare strings to numbers
@@ -45,7 +62,7 @@ instance.import(
} else if (Array.isArray(obj)) {
return obj.length;
}
- throw new Error('length() expects an array or object');
+ throw new MathJsValidationError('spreadsheet/formula/length/error');
},
unitToKiloUnit,
unitToMicroUnit,
diff --git a/src/translations/spreadsheet-en.ts b/src/translations/spreadsheet-en.ts
index f782fc51e5..ad9faf23e7 100644
--- a/src/translations/spreadsheet-en.ts
+++ b/src/translations/spreadsheet-en.ts
@@ -149,6 +149,22 @@ const spreadsheetEn = {
'spreadsheet/calculation/min_abbrev': 'Min',
'spreadsheet/calculation/max_abbrev': 'Max',
+ //Formula errors
+ 'spreadsheet/formula/error': '#ERROR',
+ 'spreadsheet/formula/import/disabled': 'Function import is disabled',
+ 'spreadsheet/formula/createUnit/disabled': 'Function createUnit is disabled',
+ 'spreadsheet/formula/evaluate/disabled': 'Function evaluate is disabled',
+ 'spreadsheet/formula/parse/disabled': 'Function parse is disabled',
+ 'spreadsheet/formula/simplify/disabled': 'Function simplify is disabled',
+ 'spreadsheet/formula/derivative/disabled': 'Function derivative is disabled',
+ 'spreadsheet/formula/compile/disabled': 'Function compile is disabled',
+ 'spreadsheet/formula/help/disabled': 'Function help is disabled',
+ 'spreadsheet/formula/length/error': 'Function length expects an array or object',
+ 'spreadsheet/formula/type/number': 'Formula must evaluate to a number',
+ 'spreadsheet/formula/type/boolean': 'Formula must evaluate to a boolean',
+ 'spreadsheet/formula/type/enum': 'Formula must evaluate to a string or a number',
+ 'spreadsheet/formula/type/unknown': 'Unknown column type',
+
// Column types
TEXT: 'Text',
NUMBER: 'Number',
diff --git a/src/translations/spreadsheet-fr.ts b/src/translations/spreadsheet-fr.ts
index 335c1fa99d..22c374cba2 100644
--- a/src/translations/spreadsheet-fr.ts
+++ b/src/translations/spreadsheet-fr.ts
@@ -154,6 +154,22 @@ const spreadsheetFr = {
'spreadsheet/calculation/min_abbrev': 'Min',
'spreadsheet/calculation/max_abbrev': 'Max',
+ //Formula errors
+ 'spreadsheet/formula/error': '#ERREUR',
+ 'spreadsheet/formula/import/disabled': 'La fonction import est désactivée',
+ 'spreadsheet/formula/createUnit/disabled': 'La fonction createUnit est désactivée',
+ 'spreadsheet/formula/evaluate/disabled': 'La fonction evaluate est désactivée',
+ 'spreadsheet/formula/parse/disabled': 'La fonction parse est désactivée',
+ 'spreadsheet/formula/simplify/disabled': 'La fonction simplify est désactivée',
+ 'spreadsheet/formula/derivative/disabled': 'La fonction derivative est désactivée',
+ 'spreadsheet/formula/compile/disabled': 'La fonction compile est désactivée',
+ 'spreadsheet/formula/help/disabled': 'La fonction help est désactivée',
+ 'spreadsheet/formula/length/error': 'La fonction length attend une donnée de type tableau ou objet',
+ 'spreadsheet/formula/type/number': 'La formule doit exprimer une donnée numérique',
+ 'spreadsheet/formula/type/boolean': 'La formule doit exprimer une donnée booléenne',
+ 'spreadsheet/formula/type/enum': 'La formule doit exprimer une donnée textuelle ou numérique',
+ 'spreadsheet/formula/type/unknown': 'Type de donnée inconnu',
+
// Column types
TEXT: 'Texte',
NUMBER: 'Nombre',