From aa923ad28144fd788001b6d2edc063a06b05e8fb Mon Sep 17 00:00:00 2001 From: Yordan Stoyanov Date: Mon, 22 Jun 2026 17:13:47 +0200 Subject: [PATCH 1/4] feat(datagrid-web): use attribute formatter for default Excel export type --- .../datagrid-web/CHANGELOG.md | 8 +++ .../datagrid-web/src/Datagrid.editorConfig.ts | 20 ++++--- .../__tests__/cell-readers.spec.ts | 58 +++++++++++++++++++ .../src/features/data-export/cell-readers.ts | 34 +++++++++-- 4 files changed, 108 insertions(+), 12 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-web/CHANGELOG.md b/packages/pluggableWidgets/datagrid-web/CHANGELOG.md index 5f08d33055..2ed5a9f445 100644 --- a/packages/pluggableWidgets/datagrid-web/CHANGELOG.md +++ b/packages/pluggableWidgets/datagrid-web/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- We added support for using the attribute's own formatting when exporting to Excel with the "Default" export type. Numbers and dates now export with their configured precision and pattern instead of raw values. + +### Fixed + +- We fixed an issue where export type and format properties were visible in Studio Pro for dynamic text columns, even though they have no effect. + ## [3.11.1] - 2026-06-18 ### Fixed diff --git a/packages/pluggableWidgets/datagrid-web/src/Datagrid.editorConfig.ts b/packages/pluggableWidgets/datagrid-web/src/Datagrid.editorConfig.ts index 49ca0a3436..0db6fb4b60 100644 --- a/packages/pluggableWidgets/datagrid-web/src/Datagrid.editorConfig.ts +++ b/packages/pluggableWidgets/datagrid-web/src/Datagrid.editorConfig.ts @@ -57,13 +57,19 @@ export function getProperties(values: DatagridPreviewProps, defaultProperties: P if (column.minWidth !== "manual") { hidePropertyIn(defaultProperties, values, "columns", index, "minWidthLimit"); } - // Hide exportNumberFormat if exportType is not 'number' - if (column.exportType !== "number") { - hidePropertyIn(defaultProperties, values, "columns", index, "exportNumberFormat" as any); - } - // Hide exportDateFormat if exportType is not 'date' - if (column.exportType !== "date") { - hidePropertyIn(defaultProperties, values, "columns", index, "exportDateFormat" as any); + if (column.showContentAs === "dynamicText") { + hideNestedPropertiesIn(defaultProperties, values, "columns", index, [ + "exportType", + "exportNumberFormat", + "exportDateFormat" + ] as any); + } else { + if (column.exportType !== "number") { + hidePropertyIn(defaultProperties, values, "columns", index, "exportNumberFormat" as any); + } + if (column.exportType !== "date") { + hidePropertyIn(defaultProperties, values, "columns", index, "exportDateFormat" as any); + } } }); diff --git a/packages/pluggableWidgets/datagrid-web/src/features/data-export/__tests__/cell-readers.spec.ts b/packages/pluggableWidgets/datagrid-web/src/features/data-export/__tests__/cell-readers.spec.ts index 73f8c2ef1f..791f25c87d 100644 --- a/packages/pluggableWidgets/datagrid-web/src/features/data-export/__tests__/cell-readers.spec.ts +++ b/packages/pluggableWidgets/datagrid-web/src/features/data-export/__tests__/cell-readers.spec.ts @@ -68,6 +68,64 @@ describe("cell-readers", () => { expect(cell.v).toBe(false); }); + it("uses attribute number formatter when exportType is default", () => { + const attr = listAttribute(() => new Big("1234.56")) as any; + attr.formatter = { type: "number", config: { groupDigits: true, decimalPrecision: 2 } }; + const col = column("Amount", c => { + c.showContentAs = "attribute"; + c.attribute = attr; + c.exportType = "default"; + }); + const cell = readSingleCell(col); + expect(cell.t).toBe("n"); + expect(cell.v).toBe(1234.56); + expect(cell.z).toBe("#,##0.00"); + }); + + it("uses attribute date formatter when exportType is default", () => { + const testDate = new Date("2024-06-15T10:30:00Z"); + const attr = listAttribute(() => testDate) as any; + attr.formatter = { type: "datetime", config: { type: "custom", pattern: "dd/MM/yyyy" } }; + const col = column("Created", c => { + c.showContentAs = "attribute"; + c.attribute = attr; + c.exportType = "default"; + }); + const cell = readSingleCell(col); + expect(cell.t).toBe("d"); + expect(cell.v).toEqual(new Date(Date.UTC(2024, 5, 15))); + expect(cell.z).toBe("dd/mm/yyyy"); + }); + + it("returns no format for default datetime with non-custom config", () => { + const testDate = new Date("2024-06-15T10:30:00Z"); + const attr = listAttribute(() => testDate) as any; + attr.formatter = { type: "datetime", config: { type: "date" } }; + const col = column("Created", c => { + c.showContentAs = "attribute"; + c.attribute = attr; + c.exportType = "default"; + }); + const cell = readSingleCell(col); + expect(cell.t).toBe("d"); + expect(cell.v).toEqual(new Date(Date.UTC(2024, 5, 15))); + expect(cell.z).toBe("dd-mm-yyyy"); + }); + + it("uses attribute number formatter without decimals", () => { + const attr = listAttribute(() => new Big("42")) as any; + attr.formatter = { type: "number", config: { groupDigits: false, decimalPrecision: 0 } }; + const col = column("Count", c => { + c.showContentAs = "attribute"; + c.attribute = attr; + c.exportType = "default"; + }); + const cell = readSingleCell(col); + expect(cell.t).toBe("n"); + expect(cell.v).toBe(42); + expect(cell.z).toBe("0"); + }); + it("exports date attribute with format as date cell", () => { const testDate = new Date("2024-06-15T10:30:00Z"); const col = column("Created", c => { diff --git a/packages/pluggableWidgets/datagrid-web/src/features/data-export/cell-readers.ts b/packages/pluggableWidgets/datagrid-web/src/features/data-export/cell-readers.ts index c1227592b9..1175bf5651 100644 --- a/packages/pluggableWidgets/datagrid-web/src/features/data-export/cell-readers.ts +++ b/packages/pluggableWidgets/datagrid-web/src/features/data-export/cell-readers.ts @@ -113,6 +113,27 @@ function countSignificantDigits(value: Big): number { return stripped.length || 1; } +function getAttributeDefaultFormat(props: ColumnsType): string | undefined { + const formatter = props.attribute?.formatter; + if (!formatter) { + return undefined; + } + + if (formatter.type === "datetime") { + const cfg = formatter.config; + return cfg.type === "custom" ? cfg.pattern.replace(/M/g, "m") : undefined; + } + + if (formatter.type === "number") { + const cfg = formatter.config; + const decimals = cfg.decimalPrecision ?? 0; + const base = cfg.groupDigits ? "#,##0" : "0"; + return decimals > 0 ? `${base}.${"0".repeat(decimals)}` : base; + } + + return undefined; +} + const readers: ReadersByType = { attribute(item, props) { const data = props.attribute?.get(item); @@ -122,11 +143,14 @@ const readers: ReadersByType = { } const value = data.value; - const format = getCellFormat({ - exportType: props.exportType, - exportDateFormat: props.exportDateFormat, - exportNumberFormat: props.exportNumberFormat - }); + const format = + props.exportType === "default" + ? getAttributeDefaultFormat(props) + : getCellFormat({ + exportType: props.exportType, + exportDateFormat: props.exportDateFormat, + exportNumberFormat: props.exportNumberFormat + }); if (value instanceof Date) { const dateValue = format && hasTimeComponent(format) ? value : stripTime(value); From 1e6e1fd7c9bb3c74a9a7d97f6f0b53942b751487 Mon Sep 17 00:00:00 2001 From: Yordan Stoyanov Date: Wed, 24 Jun 2026 14:50:16 +0200 Subject: [PATCH 2/4] docs(datagrid-web): add openspec for WC-3441 export column enhancements --- .../datagrid-web/openspec/.openspec.yaml | 2 + .../datagrid-web/openspec/design.md | 53 +++++++++++++++++++ .../datagrid-web/openspec/proposal.md | 30 +++++++++++ .../datagrid-web/openspec/tasks.md | 29 ++++++++++ 4 files changed, 114 insertions(+) create mode 100644 packages/pluggableWidgets/datagrid-web/openspec/.openspec.yaml create mode 100644 packages/pluggableWidgets/datagrid-web/openspec/design.md create mode 100644 packages/pluggableWidgets/datagrid-web/openspec/proposal.md create mode 100644 packages/pluggableWidgets/datagrid-web/openspec/tasks.md diff --git a/packages/pluggableWidgets/datagrid-web/openspec/.openspec.yaml b/packages/pluggableWidgets/datagrid-web/openspec/.openspec.yaml new file mode 100644 index 0000000000..39f46ea2fa --- /dev/null +++ b/packages/pluggableWidgets/datagrid-web/openspec/.openspec.yaml @@ -0,0 +1,2 @@ +schema: tdd-refactor +created: 2026-06-24 diff --git a/packages/pluggableWidgets/datagrid-web/openspec/design.md b/packages/pluggableWidgets/datagrid-web/openspec/design.md new file mode 100644 index 0000000000..587cbcb7a9 --- /dev/null +++ b/packages/pluggableWidgets/datagrid-web/openspec/design.md @@ -0,0 +1,53 @@ +## Test Cases + +### Reproduction Tests + +- Attribute number with default export type uses formatter - (unit) + - **Given**: Attribute column with `exportType = "default"`, attribute has `formatter = { type: "number", config: { groupDigits: true, decimalPrecision: 2 } }` + - **When**: Export reads the cell + - **Then**: Cell is `{ t: "n", v: 1234.56, z: "#,##0.00" }` + +- Attribute date with default export type uses custom pattern - (unit) + - **Given**: Attribute column with `exportType = "default"`, attribute has `formatter = { type: "datetime", config: { type: "custom", pattern: "dd/MM/yyyy" } }` + - **When**: Export reads the cell + - **Then**: Cell is `{ t: "d", v: , z: "dd/mm/yyyy" }` (M→m converted for Excel) + +### Edge Cases + +- Attribute date with non-custom datetime config falls back to locale default - (unit) + - **Given**: Attribute column with `exportType = "default"`, attribute has `formatter = { type: "datetime", config: { type: "date" } }` + - **When**: Export reads the cell + - **Then**: Cell has `z: "dd-mm-yyyy"` (locale fallback, not derived from formatter) + +- Attribute number with no decimal precision - (unit) + - **Given**: Attribute column with `exportType = "default"`, attribute has `formatter = { type: "number", config: { groupDigits: false, decimalPrecision: 0 } }` + - **When**: Export reads the cell + - **Then**: Cell has `z: "0"` (no grouping, no decimals) + +- Attribute with formatter lacking type field - (unit) + - **Given**: Attribute column with `exportType = "default"`, attribute has basic formatter (no `type` property, e.g., default mock) + - **When**: Export reads the cell + - **Then**: Cell has `z: undefined` for numbers, locale fallback for dates (graceful degradation) + +### Regression Tests + +- Custom export type still uses explicit format - (unit) + - **Given**: Attribute column with `exportType = "number"` and `exportNumberFormat = "#,##0.00"` + - **When**: Export reads the cell + - **Then**: Cell uses the explicit format, NOT the attribute formatter + +- Dynamic text always exports as string regardless of export settings - (unit) + - **Given**: Dynamic text column with any `exportType` set + - **When**: Export reads the cell + - **Then**: Cell is always `{ t: "s" }`, export type ignored + +- Custom content export unchanged - (unit) + - **Given**: Custom content column with `exportType = "number"` and `exportValue = "1234.56"` + - **When**: Export reads the cell + - **Then**: Cell is `{ t: "n", v: 1234.56, z: }` (same as before) + +## Notes + +- The `M → m` conversion is needed because Mendix uses Java-style date patterns (M = month) while Excel uses `m` for month. +- `getAttributeDefaultFormat` returns `undefined` for string, boolean, and enum attributes — these fall through to existing logic (displayValue for strings, native boolean cells). +- The `editorConfig` change is Studio Pro-only (design time) — no runtime test needed. diff --git a/packages/pluggableWidgets/datagrid-web/openspec/proposal.md b/packages/pluggableWidgets/datagrid-web/openspec/proposal.md new file mode 100644 index 0000000000..6c4896ea59 --- /dev/null +++ b/packages/pluggableWidgets/datagrid-web/openspec/proposal.md @@ -0,0 +1,30 @@ +## Why + +When exporting a Data Grid 2 to Excel with `exportType = "default"` on attribute columns, the exported cells had no Excel format applied. Numbers exported as raw values without thousand separators or decimal precision, and dates used only the browser locale fallback. Users expected "default" to mean "use the attribute's own configured format" (e.g., 2 decimal places for a Decimal, `dd/MM/yyyy` for a DateTime). + +Additionally, the export type and format properties were visible in Studio Pro for dynamic text columns even though the export logic ignores them — confusing for configurators. + +## Root Cause + +`getCellFormat()` returned `undefined` when `exportType === "default"`, meaning no Excel format code (`z` field) was written to the cell. The attribute's `formatter` property (available on `ListAttributeValue` since Mendix 10) was never consulted. + +The `editorConfig.ts` visibility logic only conditionally hid `exportNumberFormat`/`exportDateFormat` based on the export type value, but never hid all three export properties when `showContentAs === "dynamicText"`. + +## What Changes + +Package: `packages/pluggableWidgets/datagrid-web` + +- `src/features/data-export/cell-readers.ts` — New `getAttributeDefaultFormat(props)` function reads `props.attribute.formatter` and derives an Excel format string: + - `formatter.type === "number"` → builds format from `groupDigits` and `decimalPrecision` (e.g., `#,##0.00`) + - `formatter.type === "datetime"` with `config.type === "custom"` → converts Mendix pattern to Excel format (M→m replacement) + - Otherwise returns `undefined` (falls through to existing locale default for dates) + - Attribute reader now branches: `exportType === "default"` → `getAttributeDefaultFormat()`, else → existing `getCellFormat()`. + +- `src/Datagrid.editorConfig.ts` — When `showContentAs === "dynamicText"`, hide `exportType`, `exportNumberFormat`, and `exportDateFormat` properties in Studio Pro. + +## Impact + +- Attribute columns with `exportType = "default"` now export with their configured format. This is an enhancement, not a breaking change — previously these cells had no format, now they have one. +- No XML property changes. No migration needed. +- No behavioral change for `exportType = "number"` / `"date"` / `"boolean"` (custom path unchanged). +- Dynamic text columns: cosmetic-only change in Studio Pro property panel (properties hidden). Runtime behavior identical. diff --git a/packages/pluggableWidgets/datagrid-web/openspec/tasks.md b/packages/pluggableWidgets/datagrid-web/openspec/tasks.md new file mode 100644 index 0000000000..cbb1c4b7cd --- /dev/null +++ b/packages/pluggableWidgets/datagrid-web/openspec/tasks.md @@ -0,0 +1,29 @@ +## 1. Test Setup + +- [x] 1.1 Write test: attribute number with default exportType uses formatter config to derive Excel format +- [x] 1.2 Write test: attribute date with default exportType and custom pattern uses converted pattern +- [x] 1.3 Write test: attribute date with non-custom config falls back to locale default +- [x] 1.4 Write test: attribute number with groupDigits=false and decimalPrecision=0 produces format "0" + +## 2. Implementation + +- [x] 2.1 Add `getAttributeDefaultFormat(props)` function in `cell-readers.ts` that reads `props.attribute.formatter` and derives Excel format string +- [x] 2.2 Branch attribute reader: `exportType === "default"` calls `getAttributeDefaultFormat()`, else calls existing `getCellFormat()` +- [x] 2.3 Hide `exportType`, `exportNumberFormat`, `exportDateFormat` in editorConfig when `showContentAs === "dynamicText"` + +## 3. Refactoring + +- [x] 3.1 No refactoring needed — implementation is minimal and isolated + +## 4. Verification + +- [x] 4.1 All 221 tests passing (4 new + 217 existing) +- [x] 4.2 Full test suite passes (no regressions) +- [x] 4.3 Build compiles without TypeScript errors +- [x] 4.4 PR submitted and reviewed + +## Notes + +- No XML property changes were needed — existing `exportType` enum with "default" value covers the use case. +- The `formatter` property is available on `ListAttributeValue` since Mendix 10 runtime. +- iobuhov review feedback: use consistent assertion syntax (assert `cell.v` in all date tests). Fixed. From d3ef8c126e7855b1ba6266411e8137183edfdc39 Mon Sep 17 00:00:00 2001 From: Yordan Stoyanov Date: Wed, 1 Jul 2026 17:12:51 +0200 Subject: [PATCH 3/4] fix(datagrid-web): mirror grid decimals for default Excel export --- .../__tests__/cell-readers.spec.ts | 30 +++++++++++++++++++ .../src/features/data-export/cell-readers.ts | 11 +++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-web/src/features/data-export/__tests__/cell-readers.spec.ts b/packages/pluggableWidgets/datagrid-web/src/features/data-export/__tests__/cell-readers.spec.ts index 791f25c87d..49790ffaee 100644 --- a/packages/pluggableWidgets/datagrid-web/src/features/data-export/__tests__/cell-readers.spec.ts +++ b/packages/pluggableWidgets/datagrid-web/src/features/data-export/__tests__/cell-readers.spec.ts @@ -126,6 +126,36 @@ describe("cell-readers", () => { expect(cell.z).toBe("0"); }); + it("mirrors the grid when number formatter config omits decimalPrecision (grouped)", () => { + // Real Mendix Decimal attributes only expose `groupDigits` at runtime. + const attr = listAttribute(() => new Big("1234.56")) as any; + attr.formatter = { type: "number", config: { groupDigits: true } }; + const col = column("Amount", c => { + c.showContentAs = "attribute"; + c.attribute = attr; + c.exportType = "default"; + }); + const cell = readSingleCell(col); + expect(cell.t).toBe("n"); + expect(cell.v).toBe(1234.56); + expect(cell.z).toBe("#,##0.########"); + }); + + it("mirrors the grid when number formatter config omits decimalPrecision (ungrouped)", () => { + const attr = listAttribute(() => new Big("0.5")) as any; + attr.formatter = { type: "number", config: { groupDigits: false } }; + const col = column("Amount", c => { + c.showContentAs = "attribute"; + c.attribute = attr; + c.exportType = "default"; + }); + const cell = readSingleCell(col); + expect(cell.t).toBe("n"); + expect(cell.v).toBe(0.5); + // `#` suppresses trailing zeros, so Excel renders exactly what the grid shows. + expect(cell.z).toBe("0.########"); + }); + it("exports date attribute with format as date cell", () => { const testDate = new Date("2024-06-15T10:30:00Z"); const col = column("Created", c => { diff --git a/packages/pluggableWidgets/datagrid-web/src/features/data-export/cell-readers.ts b/packages/pluggableWidgets/datagrid-web/src/features/data-export/cell-readers.ts index 1175bf5651..7cac0a356f 100644 --- a/packages/pluggableWidgets/datagrid-web/src/features/data-export/cell-readers.ts +++ b/packages/pluggableWidgets/datagrid-web/src/features/data-export/cell-readers.ts @@ -126,9 +126,16 @@ function getAttributeDefaultFormat(props: ColumnsType): string | undefined { if (formatter.type === "number") { const cfg = formatter.config; - const decimals = cfg.decimalPrecision ?? 0; const base = cfg.groupDigits ? "#,##0" : "0"; - return decimals > 0 ? `${base}.${"0".repeat(decimals)}` : base; + // Mendix Decimal attributes do not expose a fixed `decimalPrecision` on the + // formatter config at runtime (only `groupDigits`). Honour it when present, + // otherwise mirror the grid: show up to 8 fractional digits (the Mendix DB + // maximum) with trailing zeros suppressed via `#`, so 1234.56 stays 1234.56 + // and integers stay integers — instead of collapsing to a whole number. + if (cfg.decimalPrecision != null) { + return cfg.decimalPrecision > 0 ? `${base}.${"0".repeat(cfg.decimalPrecision)}` : base; + } + return `${base}.########`; } return undefined; From 6a35ea6d0c82f2041d813cf39f66d8931de75f55 Mon Sep 17 00:00:00 2001 From: Yordan Stoyanov Date: Wed, 1 Jul 2026 17:13:07 +0200 Subject: [PATCH 4/4] chore: update changelog and openspec --- .../pluggableWidgets/datagrid-web/CHANGELOG.md | 2 +- .../datagrid-web/openspec/design.md | 18 ++++++++++++------ .../datagrid-web/openspec/proposal.md | 6 ++++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-web/CHANGELOG.md b/packages/pluggableWidgets/datagrid-web/CHANGELOG.md index 2ed5a9f445..886dd05c12 100644 --- a/packages/pluggableWidgets/datagrid-web/CHANGELOG.md +++ b/packages/pluggableWidgets/datagrid-web/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added -- We added support for using the attribute's own formatting when exporting to Excel with the "Default" export type. Numbers and dates now export with their configured precision and pattern instead of raw values. +- We added support for using the attribute's own formatting when exporting to Excel with the "Default" export type. Numbers now export as numeric cells that mirror the grid's decimals and thousands grouping, and dates export using their configured pattern, instead of unformatted values. ### Fixed diff --git a/packages/pluggableWidgets/datagrid-web/openspec/design.md b/packages/pluggableWidgets/datagrid-web/openspec/design.md index 587cbcb7a9..d74c659610 100644 --- a/packages/pluggableWidgets/datagrid-web/openspec/design.md +++ b/packages/pluggableWidgets/datagrid-web/openspec/design.md @@ -2,10 +2,10 @@ ### Reproduction Tests -- Attribute number with default export type uses formatter - (unit) - - **Given**: Attribute column with `exportType = "default"`, attribute has `formatter = { type: "number", config: { groupDigits: true, decimalPrecision: 2 } }` +- Attribute number with default export type mirrors the grid - (unit) + - **Given**: Attribute column with `exportType = "default"`, attribute has `formatter = { type: "number", config: { groupDigits: true } }` (Mendix Decimal attributes only expose `groupDigits` at runtime, not a fixed `decimalPrecision`) - **When**: Export reads the cell - - **Then**: Cell is `{ t: "n", v: 1234.56, z: "#,##0.00" }` + - **Then**: Cell is `{ t: "n", v: 1234.56, z: "#,##0.########" }` — `#` suppresses trailing zeros so Excel renders exactly what the grid shows (`1234.56` stays `1234.56`, `0.5` stays `0.5`, integers stay integers) - Attribute date with default export type uses custom pattern - (unit) - **Given**: Attribute column with `exportType = "default"`, attribute has `formatter = { type: "datetime", config: { type: "custom", pattern: "dd/MM/yyyy" } }` @@ -19,10 +19,15 @@ - **When**: Export reads the cell - **Then**: Cell has `z: "dd-mm-yyyy"` (locale fallback, not derived from formatter) -- Attribute number with no decimal precision - (unit) - - **Given**: Attribute column with `exportType = "default"`, attribute has `formatter = { type: "number", config: { groupDigits: false, decimalPrecision: 0 } }` +- Attribute number honours explicit decimalPrecision when present - (unit) + - **Given**: Attribute column with `exportType = "default"`, attribute has `formatter = { type: "number", config: { groupDigits: false, decimalPrecision: 0 } }` (precision explicitly provided) - **When**: Export reads the cell - - **Then**: Cell has `z: "0"` (no grouping, no decimals) + - **Then**: Cell has `z: "0"` (no grouping, no decimals) — an explicit `decimalPrecision` takes priority over the grid-mirroring fallback + +- Attribute number without grouping mirrors the grid - (unit) + - **Given**: Attribute column with `exportType = "default"`, attribute has `formatter = { type: "number", config: { groupDigits: false } }` (no `decimalPrecision`) + - **When**: Export reads the cell + - **Then**: Cell has `z: "0.########"` (no grouping, up to 8 fractional digits with trailing zeros suppressed) - Attribute with formatter lacking type field - (unit) - **Given**: Attribute column with `exportType = "default"`, attribute has basic formatter (no `type` property, e.g., default mock) @@ -48,6 +53,7 @@ ## Notes +- Mendix Decimal attributes do **not** expose a fixed `decimalPrecision` on the formatter config at runtime — only `groupDigits` is present (verified against the live client). The number-default format therefore mirrors the grid using `{base}.########` (8 = the Mendix DB fractional-digit maximum) with `#` suppressing trailing zeros, rather than deriving a fixed precision. The `decimalPrecision` branch is retained for the case where a future runtime does supply it. - The `M → m` conversion is needed because Mendix uses Java-style date patterns (M = month) while Excel uses `m` for month. - `getAttributeDefaultFormat` returns `undefined` for string, boolean, and enum attributes — these fall through to existing logic (displayValue for strings, native boolean cells). - The `editorConfig` change is Studio Pro-only (design time) — no runtime test needed. diff --git a/packages/pluggableWidgets/datagrid-web/openspec/proposal.md b/packages/pluggableWidgets/datagrid-web/openspec/proposal.md index 6c4896ea59..ef63a45d2d 100644 --- a/packages/pluggableWidgets/datagrid-web/openspec/proposal.md +++ b/packages/pluggableWidgets/datagrid-web/openspec/proposal.md @@ -1,6 +1,6 @@ ## Why -When exporting a Data Grid 2 to Excel with `exportType = "default"` on attribute columns, the exported cells had no Excel format applied. Numbers exported as raw values without thousand separators or decimal precision, and dates used only the browser locale fallback. Users expected "default" to mean "use the attribute's own configured format" (e.g., 2 decimal places for a Decimal, `dd/MM/yyyy` for a DateTime). +When exporting a Data Grid 2 to Excel with `exportType = "default"` on attribute columns, the exported cells had no Excel format applied. Numbers exported as raw values without thousand separators or decimal precision, and dates used only the browser locale fallback. Users expected "default" to mean "export the value the way the grid shows it" (matching decimals and grouping for a Decimal, `dd/MM/yyyy` for a DateTime). Additionally, the export type and format properties were visible in Studio Pro for dynamic text columns even though the export logic ignores them — confusing for configurators. @@ -8,6 +8,8 @@ Additionally, the export type and format properties were visible in Studio Pro f `getCellFormat()` returned `undefined` when `exportType === "default"`, meaning no Excel format code (`z` field) was written to the cell. The attribute's `formatter` property (available on `ListAttributeValue` since Mendix 10) was never consulted. +A first attempt derived the number format from `config.decimalPrecision`, but testing against the live client revealed that Mendix Decimal attributes do **not** expose `decimalPrecision` at runtime — the formatter config only carries `groupDigits`. This caused `decimalPrecision ?? 0` to collapse to `0` and export whole numbers (e.g. `1234.56` became `1235`). The format is therefore derived to mirror the grid instead of relying on a precision value that never arrives. + The `editorConfig.ts` visibility logic only conditionally hid `exportNumberFormat`/`exportDateFormat` based on the export type value, but never hid all three export properties when `showContentAs === "dynamicText"`. ## What Changes @@ -15,7 +17,7 @@ The `editorConfig.ts` visibility logic only conditionally hid `exportNumberForma Package: `packages/pluggableWidgets/datagrid-web` - `src/features/data-export/cell-readers.ts` — New `getAttributeDefaultFormat(props)` function reads `props.attribute.formatter` and derives an Excel format string: - - `formatter.type === "number"` → builds format from `groupDigits` and `decimalPrecision` (e.g., `#,##0.00`) + - `formatter.type === "number"` → builds format from `groupDigits`; when `decimalPrecision` is present it is honoured, otherwise the format mirrors the grid as `{base}.########` (up to 8 fractional digits, trailing zeros suppressed by `#`), so `1234.56` exports as `1234.56` and integers stay integers - `formatter.type === "datetime"` with `config.type === "custom"` → converts Mendix pattern to Excel format (M→m replacement) - Otherwise returns `undefined` (falls through to existing locale default for dates) - Attribute reader now branches: `exportType === "default"` → `getAttributeDefaultFormat()`, else → existing `getCellFormat()`.