Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/pluggableWidgets/datagrid-web/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

- We fixed an issue where missing consistency checks for the captions were causing runtime errors instead of in Studio Pro

- We added a new property for export to excel. The new property allows to set the cell export type and also the format for type number and date.

## [3.6.1] - 2025-10-14

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ export function getProperties(
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 (!values.advanced && platform === "web") {
hideNestedPropertiesIn(defaultProperties, values, "columns", index, [
"columnClass",
Expand Down Expand Up @@ -214,7 +222,10 @@ export const getPreview = (
minWidth: "auto",
minWidthLimit: 100,
allowEventPropagation: true,
exportValue: ""
exportValue: "",
exportType: "text",
exportDateFormat: "",
exportNumberFormat: ""
}
];
const columns = rowLayout({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ const initColumns: ColumnsPreviewType[] = [
minWidth: "auto",
minWidthLimit: 100,
allowEventPropagation: true,
exportValue: ""
exportValue: "",
exportDateFormat: "",
exportNumberFormat: "",
exportType: "text"
}
];

Expand Down
20 changes: 20 additions & 0 deletions packages/pluggableWidgets/datagrid-web/src/Datagrid.xml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,26 @@
<caption>Export value</caption>
<description />
</property>
<property key="exportType" type="enumeration" defaultValue="text">
<caption>Export type</caption>
<description />
<enumerationValues>
<enumerationValue key="text">Text</enumerationValue>
<enumerationValue key="number">Number</enumerationValue>
<enumerationValue key="date">Date</enumerationValue>
<enumerationValue key="boolean">Boolean</enumerationValue>
</enumerationValues>
</property>
<property key="exportNumberFormat" type="expression" required="false">
<caption>Export number format</caption>
<description>Optional Excel number format for exported numeric values (e.g. "#,##0.00", "$0.00", "0.00%"). See all formats https://docs.sheetjs.com/docs/csf/features/nf/</description>
<returnType type="String" />
</property>
<property key="exportDateFormat" type="expression" required="false">
<caption>Export date format</caption>
<description>Excel date format for exported Date/DateTime values (e.g. "yyyy-mm-dd", "dd/mm/yyyy hh mm").</description>
<returnType type="String" />
</property>
<property key="header" type="textTemplate" required="false">
<caption>Caption</caption>
<description />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import { isAvailable } from "@mendix/widget-plugin-platform/framework/is-available";
import Big from "big.js";
import { ListValue, ObjectItem, ValueStatus } from "mendix";
import { DynamicValue, ListValue, ObjectItem, ValueStatus } from "mendix";
import { createNanoEvents, Emitter, Unsubscribe } from "nanoevents";
import { ColumnsType, ShowContentAsEnum } from "../../../typings/DatagridProps";

type RowData = Array<string | number | boolean>;
/** Represents a single Excel cell (SheetJS compatible) */
interface ExcelCell {
/** Cell type: 's' = string, 'n' = number, 'b' = boolean, 'd' = date */
t: "s" | "n" | "b" | "d";
/** Underlying value */
v: string | number | boolean | Date;
/** Optional Excel number/date format, e.g. "yyyy-mm-dd" or "$0.00" */
z?: string;
/** Optional pre-formatted display text */
w?: string;
}

type RowData = ExcelCell[];

type HeaderDefinition = {
name: string;
type: string;
};

type ValueReader = (item: ObjectItem, props: ColumnsType) => string | boolean | number;
type ValueReader = (item: ObjectItem, props: ColumnsType) => ExcelCell;

type ReadersByType = Record<ShowContentAsEnum, ValueReader>;

Expand Down Expand Up @@ -253,48 +265,107 @@ export class DSExportRequest {
const readers: ReadersByType = {
attribute(item, props) {
if (props.attribute === undefined) {
return "";
return makeEmptyCell();
}

const data = props.attribute.get(item);

if (data.status !== "available") {
return "";
return makeEmptyCell();
}

const value = data.value;
const format = getCellFormat({
exportType: props.exportType,
exportDateFormat: props.exportDateFormat,
exportNumberFormat: props.exportNumberFormat
});

if (value instanceof Date) {
return {
t: format === undefined ? "s" : "d",
v: format === undefined ? data.displayValue : value,
z: format
};
}

if (typeof data.value === "boolean") {
return data.value;
if (typeof value === "boolean") {
return {
t: "b",
v: value,
w: value ? "TRUE" : "FALSE"
};
}

if (data.value instanceof Big) {
return data.value.toNumber();
if (value instanceof Big || typeof value === "number") {
const num = value instanceof Big ? value.toNumber() : value;
return {
t: "n",
v: num,
z: format
};
}

return data.displayValue;
return {
t: "s",
v: data.displayValue ?? ""
};
},

dynamicText(item, props) {
if (props.dynamicText === undefined) {
return "";
return makeEmptyCell();
}

const data = props.dynamicText.get(item);

switch (data.status) {
case "available":
return data.value;
const format = getCellFormat({
exportType: props.exportType,
exportDateFormat: props.exportDateFormat,
exportNumberFormat: props.exportNumberFormat
});
return { t: "s", v: data.value ?? "", z: format };
case "unavailable":
return "n/a";
return { t: "s", v: "n/a" };
default:
return "";
return makeEmptyCell();
}
},

customContent(item, props) {
return props.exportValue?.get(item).value ?? "";
const value = props.exportValue?.get(item).value ?? "";
const format = getCellFormat({
exportType: props.exportType,
exportDateFormat: props.exportDateFormat,
exportNumberFormat: props.exportNumberFormat
});
return { t: "s", v: value, z: format };
}
};

function makeEmptyCell(): ExcelCell {
return { t: "s", v: "" };
}

interface DataExportProps {
exportType: "text" | "number" | "date" | "boolean";
exportDateFormat?: DynamicValue<string>;
exportNumberFormat?: DynamicValue<string>;
}

function getCellFormat({ exportType, exportDateFormat, exportNumberFormat }: DataExportProps): string | undefined {
switch (exportType) {
case "date":
return exportDateFormat?.status === "available" ? exportDateFormat.value : undefined;
case "number":
return exportNumberFormat?.status === "available" ? exportNumberFormat.value : undefined;
default:
return undefined;
}
}

function createRowReader(columns: ColumnsType[]): RowReader {
return item =>
columns.map(col => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export const column = (header = "Test", patch?: (col: ColumnsType) => void): Col
visible: dynamicValue(true),
minWidth: "auto",
minWidthLimit: 100,
allowEventPropagation: true
allowEventPropagation: true,
exportType: "text"
};

if (patch) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export type LoadingTypeEnum = "spinner" | "skeleton";

export type ShowContentAsEnum = "attribute" | "dynamicText" | "customContent";

export type ExportTypeEnum = "text" | "number" | "date" | "boolean";

export type HidableEnum = "yes" | "hidden" | "no";

export type WidthEnum = "autoFill" | "autoFit" | "manual";
Expand All @@ -31,6 +33,9 @@ export interface ColumnsType {
content?: ListWidgetValue;
dynamicText?: ListExpressionValue<string>;
exportValue?: ListExpressionValue<string>;
exportType: ExportTypeEnum;
exportNumberFormat?: DynamicValue<string>;
exportDateFormat?: DynamicValue<string>;
header?: DynamicValue<string>;
tooltip?: ListExpressionValue<string>;
filter?: ReactNode;
Expand Down Expand Up @@ -67,6 +72,9 @@ export interface ColumnsPreviewType {
content: { widgetCount: number; renderer: ComponentType<{ children: ReactNode; caption?: string }> };
dynamicText: string;
exportValue: string;
exportType: ExportTypeEnum;
exportNumberFormat: string;
exportDateFormat: string;
header: string;
tooltip: string;
filter: { widgetCount: number; renderer: ComponentType<{ children: ReactNode; caption?: string }> };
Expand Down
Loading