Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { sum } from "../../../functions/helper_math";
import { average, countAny, countNumbers, max, min } from "../../../functions/helper_statistical";
import { isDateTimeFormat } from "../../../helpers/format/format";
import { recomputeZones } from "../../../helpers/recompute_zones";
import {
SelectionStatisticFunction,
Expand All @@ -17,31 +18,40 @@ const selectionStatisticFunctions: SelectionStatisticFunction[] = [
name: _t("Sum"),
types: [CellValueType.number],
compute: (values, locale) => sum([[values]], locale),
visible: (values, locale) =>
!values.length || values.some((cell) => !cell.format || !isDateTimeFormat(cell.format)),
computeFormat: (values, locale) =>
values.find((cell) => !cell.format || !isDateTimeFormat(cell.format))?.format ?? "",
},
{
name: _t("Avg"),
types: [CellValueType.number],
compute: (values, locale) => average([[values]], locale),
computeFormat: (values, locale) => values?.[0].format ?? "",
},
{
name: _t("Min"),
types: [CellValueType.number],
compute: (values, locale) => min([[values]], locale).value,
computeFormat: (values, locale) => values?.[0].format ?? "",
},
{
name: _t("Max"),
types: [CellValueType.number],
compute: (values, locale) => max([[values]], locale).value,
computeFormat: (values, locale) => values?.[0].format ?? "",
},
{
name: _t("Count"),
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
compute: (values) => countAny([[values]]),
computeFormat: (values, locale) => "",
},
{
name: _t("Count Numbers"),
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
compute: (values, locale) => countNumbers([[values]], locale),
computeFormat: (values, locale) => "",
},
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ export class BottomBarStatistic extends Component<Props, SpreadsheetChildEnv> {
return (
fnName +
": " +
(fnValue?.value !== undefined ? formatValue(fnValue.value(), { locale }) : "__")
(fnValue?.value !== undefined
? formatValue(fnValue.value(), { locale, format: fnValue.format() })
: "__")
);
}
}
16 changes: 12 additions & 4 deletions src/components/side_panel/column_stats/column_stats_store.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { sum } from "../../../functions/helper_math";
import { average, max, median, min } from "../../../functions/helper_statistical";
import { formatValue } from "../../../helpers/format/format";
import { formatValue, isDateTimeFormat } from "../../../helpers/format/format";
import { isDefined } from "../../../helpers/misc";
import {
computeStatisticFnResults,
Expand All @@ -20,7 +20,7 @@ const columnStatisticFunctions: SelectionStatisticFunction[] = [
name: _t("Total rows"),
types: Object.values(CellValueType),
compute: (values, locale) => values.length,
format: "0",
computeFormat: (values, locale) => "0",
},
{
name: _t("Unique values"),
Expand All @@ -32,32 +32,40 @@ const columnStatisticFunctions: SelectionStatisticFunction[] = [
}
return uniqueValues.size;
},
format: "0",
computeFormat: (values, locale) => "0",
},
{
name: _t("Sum"),
types: [CellValueType.number],
compute: (values, locale) => sum([[values]], locale),
visible: (values, locale) =>
values.some((cell) => !cell.format || !isDateTimeFormat(cell.format)),
computeFormat: (values, locale) =>
values.find((cell) => !cell.format || !isDateTimeFormat(cell.format))?.format ?? "0.00",
},
{
name: _t("Average"),
types: [CellValueType.number],
compute: (values, locale) => average([[values]], locale),
computeFormat: (values, locale) => values?.[0].format ?? "0.00",
},
{
name: _t("Median"),
types: [CellValueType.number],
compute: (values, locale) => median([[values]], locale) ?? "",
computeFormat: (values, locale) => values?.[0].format ?? "0.00",
},
{
name: _t("Minimum value"),
types: [CellValueType.number],
compute: (values, locale) => min([[values]], locale).value,
computeFormat: (values, locale) => values?.[0].format ?? "0.00",
},
{
name: _t("Maximum value"),
types: [CellValueType.number],
compute: (values, locale) => max([[values]], locale).value,
computeFormat: (values, locale) => values?.[0].format ?? "0.00",
},
];

Expand Down Expand Up @@ -335,7 +343,7 @@ export class ColumnStatisticsStore extends SpreadsheetStore {
name,
value: formatValue(fnValue.value(), {
locale: localeFormat.locale,
format: fnValue.format ?? localeFormat.format,
format: fnValue.format() ?? localeFormat.format,
}),
};
});
Expand Down
11 changes: 8 additions & 3 deletions src/helpers/selection_statistic_functions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CellValueType, EvaluatedCell } from "../types/cells";
import { Format } from "../types/format";
import { Locale } from "../types/locale";
import { Lazy } from "../types/misc";
import { lazy, memoize } from "./misc";
Expand All @@ -7,7 +8,7 @@ export interface StatisticFnResults {
[name: string]:
| {
value: Lazy<number | string> | undefined;
format?: string;
format: Lazy<string>;
}
| undefined;
}
Expand All @@ -16,7 +17,8 @@ export interface SelectionStatisticFunction {
name: string;
compute: (data: EvaluatedCell[], locale: Locale) => number | string;
types: CellValueType[];
format?: string;
visible?: (data: EvaluatedCell[], locale: Locale) => boolean;
computeFormat: (data: EvaluatedCell[], locale: Locale) => Format;
}

export function computeStatisticFnResults(
Expand All @@ -33,12 +35,15 @@ export function computeStatisticFnResults(
for (const fn of selectionStatisticFunctions) {
let fnResult: Lazy<number | string> | undefined = undefined;
const evaluatedCells = getCells(fn.types.sort().join(","));
if (fn.visible && !fn.visible(evaluatedCells, locale)) {
continue;
}
if (evaluatedCells.length) {
fnResult = lazy(() => fn.compute(evaluatedCells, locale));
}
statisticFnResults[fn.name] = {
value: fnResult,
format: fn.format,
format: lazy(() => fn?.computeFormat?.(evaluatedCells, locale) ?? ""),
};
}
return statisticFnResults;
Expand Down
53 changes: 53 additions & 0 deletions tests/bottom_bar/aggregate_statistics_store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,59 @@ describe("Aggregate statistic functions", () => {
expect(statisticFnResults["Count"]?.value?.()).toBe(3);
});

test.each(["Count", "Count Numbers"])("%s aggregate does not inherit format", (statName) => {
const { store, model } = makeStore(AggregateStatisticsStore);
setCellContent(model, "A1", "1");
setCellContent(model, "A2", "2");
setCellContent(model, "A3", "3");

setFormat(model, "A1:A3", "0.00%");

setSelection(model, ["A1:A3"]);
const statisticFnResults = store.statisticFnResults;
expect(statisticFnResults[statName]?.format()).toBe("");
});

test.each(["Avg", "Min", "Max"])("%s aggregate inherits first found format", (statName) => {
const { store, model } = makeStore(AggregateStatisticsStore);
setCellContent(model, "A1", "Hello");
setCellContent(model, "A2", "1");
setCellContent(model, "A3", "2");

setFormat(model, "A2", "0.00%");

setSelection(model, ["A1:A3"]);
const statisticFnResults = store.statisticFnResults;
expect(statisticFnResults[statName]?.format()).toBe("0.00%");
});

test("Sum inherits first found format that is not a date", () => {
const { store, model } = makeStore(AggregateStatisticsStore);
setCellContent(model, "A1", "Hello");
setCellContent(model, "A2", "1");
setCellContent(model, "A3", "2");

setFormat(model, "A2", "mm/dd/yyyy");
setFormat(model, "A3", "0.00%");

setSelection(model, ["A1:A3"]);
const statisticFnResults = store.statisticFnResults;
expect(statisticFnResults["Sum"]?.format()).toBe("0.00%");
});

test("Sum is not visible when only selecting numbers that are dates", () => {
const { store, model } = makeStore(AggregateStatisticsStore);
setCellContent(model, "A1", "Hello");
setCellContent(model, "A2", "1");
setCellContent(model, "A3", "2");

setFormat(model, "A2:A3", "mm/dd/yyyy");

setSelection(model, ["A1:A3"]);
const statisticFnResults = store.statisticFnResults;
expect(statisticFnResults["Sum"]).toBeUndefined();
});

test("statistic function should not include hidden rows/columns in calculations", () => {
const { store, model } = makeStore(AggregateStatisticsStore);
setCellContent(model, "A1", "1");
Expand Down