fix: Chart display fix (#5154)
This commit is contained in:
parent
64d330a332
commit
9e8e047aae
121
src/charts.test.ts
Normal file
121
src/charts.test.ts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import {
|
||||||
|
Spreadsheet,
|
||||||
|
tryParseCells,
|
||||||
|
tryParseNumber,
|
||||||
|
VALID_SPREADSHEET,
|
||||||
|
} from "./charts";
|
||||||
|
|
||||||
|
describe("charts", () => {
|
||||||
|
describe("tryParseNumber", () => {
|
||||||
|
it.each<[string, number]>([
|
||||||
|
["1", 1],
|
||||||
|
["0", 0],
|
||||||
|
["-1", -1],
|
||||||
|
["0.1", 0.1],
|
||||||
|
[".1", 0.1],
|
||||||
|
["1.", 1],
|
||||||
|
["424.", 424],
|
||||||
|
["$1", 1],
|
||||||
|
["-.1", -0.1],
|
||||||
|
["-$1", -1],
|
||||||
|
["$-1", -1],
|
||||||
|
])("should correctly identify %s as numbers", (given, expected) => {
|
||||||
|
expect(tryParseNumber(given)).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each<[string]>([["a"], ["$"], ["$a"], ["-$a"]])(
|
||||||
|
"should correctly identify %s as not a number",
|
||||||
|
(given) => {
|
||||||
|
expect(tryParseNumber(given)).toBeNull();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("tryParseCells", () => {
|
||||||
|
it("Successfully parses a spreadsheet", () => {
|
||||||
|
const spreadsheet = [
|
||||||
|
["time", "value"],
|
||||||
|
["01:00", "61"],
|
||||||
|
["02:00", "-60"],
|
||||||
|
["03:00", "85"],
|
||||||
|
["04:00", "-67"],
|
||||||
|
["05:00", "54"],
|
||||||
|
["06:00", "95"],
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = tryParseCells(spreadsheet);
|
||||||
|
|
||||||
|
expect(result.type).toBe(VALID_SPREADSHEET);
|
||||||
|
|
||||||
|
const { title, labels, values } = (
|
||||||
|
result as { type: typeof VALID_SPREADSHEET; spreadsheet: Spreadsheet }
|
||||||
|
).spreadsheet;
|
||||||
|
|
||||||
|
expect(title).toEqual("value");
|
||||||
|
expect(labels).toEqual([
|
||||||
|
"01:00",
|
||||||
|
"02:00",
|
||||||
|
"03:00",
|
||||||
|
"04:00",
|
||||||
|
"05:00",
|
||||||
|
"06:00",
|
||||||
|
]);
|
||||||
|
expect(values).toEqual([61, -60, 85, -67, 54, 95]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Uses the second column as the label if it is not a number", () => {
|
||||||
|
const spreadsheet = [
|
||||||
|
["time", "value"],
|
||||||
|
["01:00", "61"],
|
||||||
|
["02:00", "-60"],
|
||||||
|
["03:00", "85"],
|
||||||
|
["04:00", "-67"],
|
||||||
|
["05:00", "54"],
|
||||||
|
["06:00", "95"],
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = tryParseCells(spreadsheet);
|
||||||
|
|
||||||
|
expect(result.type).toBe(VALID_SPREADSHEET);
|
||||||
|
|
||||||
|
const { title, labels, values } = (
|
||||||
|
result as { type: typeof VALID_SPREADSHEET; spreadsheet: Spreadsheet }
|
||||||
|
).spreadsheet;
|
||||||
|
|
||||||
|
expect(title).toEqual("value");
|
||||||
|
expect(labels).toEqual([
|
||||||
|
"01:00",
|
||||||
|
"02:00",
|
||||||
|
"03:00",
|
||||||
|
"04:00",
|
||||||
|
"05:00",
|
||||||
|
"06:00",
|
||||||
|
]);
|
||||||
|
expect(values).toEqual([61, -60, 85, -67, 54, 95]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("treats the first column as labels if both columns are numbers", () => {
|
||||||
|
const spreadsheet = [
|
||||||
|
["time", "value"],
|
||||||
|
["01", "61"],
|
||||||
|
["02", "-60"],
|
||||||
|
["03", "85"],
|
||||||
|
["04", "-67"],
|
||||||
|
["05", "54"],
|
||||||
|
["06", "95"],
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = tryParseCells(spreadsheet);
|
||||||
|
|
||||||
|
expect(result.type).toBe(VALID_SPREADSHEET);
|
||||||
|
|
||||||
|
const { title, labels, values } = (
|
||||||
|
result as { type: typeof VALID_SPREADSHEET; spreadsheet: Spreadsheet }
|
||||||
|
).spreadsheet;
|
||||||
|
|
||||||
|
expect(title).toEqual("value");
|
||||||
|
expect(labels).toEqual(["01", "02", "03", "04", "05", "06"]);
|
||||||
|
expect(values).toEqual([61, -60, 85, -67, 54, 95]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -29,18 +29,24 @@ type ParseSpreadsheetResult =
|
|||||||
| { type: typeof NOT_SPREADSHEET; reason: string }
|
| { type: typeof NOT_SPREADSHEET; reason: string }
|
||||||
| { type: typeof VALID_SPREADSHEET; spreadsheet: Spreadsheet };
|
| { type: typeof VALID_SPREADSHEET; spreadsheet: Spreadsheet };
|
||||||
|
|
||||||
const tryParseNumber = (s: string): number | null => {
|
/**
|
||||||
const match = /^[$€£¥₩]?([0-9,]+(\.[0-9]+)?)$/.exec(s);
|
* @private exported for testing
|
||||||
|
*/
|
||||||
|
export const tryParseNumber = (s: string): number | null => {
|
||||||
|
const match = /^([-+]?)[$€£¥₩]?([-+]?)([\d.,]+)[%]?$/.exec(s);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return parseFloat(match[1].replace(/,/g, ""));
|
return parseFloat(`${(match[1] || match[2]) + match[3]}`.replace(/,/g, ""));
|
||||||
};
|
};
|
||||||
|
|
||||||
const isNumericColumn = (lines: string[][], columnIndex: number) =>
|
const isNumericColumn = (lines: string[][], columnIndex: number) =>
|
||||||
lines.slice(1).every((line) => tryParseNumber(line[columnIndex]) !== null);
|
lines.slice(1).every((line) => tryParseNumber(line[columnIndex]) !== null);
|
||||||
|
|
||||||
const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
|
/**
|
||||||
|
* @private exported for testing
|
||||||
|
*/
|
||||||
|
export const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
|
||||||
const numCols = cells[0].length;
|
const numCols = cells[0].length;
|
||||||
|
|
||||||
if (numCols > 2) {
|
if (numCols > 2) {
|
||||||
@ -71,13 +77,16 @@ const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const valueColumnIndex = isNumericColumn(cells, 0) ? 0 : 1;
|
const labelColumnNumeric = isNumericColumn(cells, 0);
|
||||||
|
const valueColumnNumeric = isNumericColumn(cells, 1);
|
||||||
|
|
||||||
if (!isNumericColumn(cells, valueColumnIndex)) {
|
if (!labelColumnNumeric && !valueColumnNumeric) {
|
||||||
return { type: NOT_SPREADSHEET, reason: "Value is not numeric" };
|
return { type: NOT_SPREADSHEET, reason: "Value is not numeric" };
|
||||||
}
|
}
|
||||||
|
|
||||||
const labelColumnIndex = (valueColumnIndex + 1) % 2;
|
const [labelColumnIndex, valueColumnIndex] = valueColumnNumeric
|
||||||
|
? [0, 1]
|
||||||
|
: [1, 0];
|
||||||
const hasHeader = tryParseNumber(cells[0][valueColumnIndex]) === null;
|
const hasHeader = tryParseNumber(cells[0][valueColumnIndex]) === null;
|
||||||
const rows = hasHeader ? cells.slice(1) : cells;
|
const rows = hasHeader ? cells.slice(1) : cells;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user