From 48fb51deae6376a32e3eaf215d0f7a4028215bd3 Mon Sep 17 00:00:00 2001 From: alec_dev Date: Fri, 1 May 2026 14:02:47 -0500 Subject: [PATCH 1/2] Fix WorkBench export file extension for selected delimiter --- .../lib/components/WorkBench/helpers.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/WorkBench/helpers.ts b/specifyweb/frontend/js_src/lib/components/WorkBench/helpers.ts index 4dc12cdf717..089b5509bf1 100644 --- a/specifyweb/frontend/js_src/lib/components/WorkBench/helpers.ts +++ b/specifyweb/frontend/js_src/lib/components/WorkBench/helpers.ts @@ -3,6 +3,21 @@ import { stringify } from 'csv-stringify/browser/esm'; import type { RA } from '../../utils/types'; import { downloadFile } from '../Molecules/FilePicker'; +const delimiterFileExtensions = new Map([ + [',', 'csv'], + [';', 'csv'], + ['\t', 'tsv'], + ['|', 'psv'], + [' ', 'txt'], +]); + +const knownDelimitedFileExtension = /\.(csv|psv|tsv|txt)$/iu; + +export const getDelimitedFileName = (name: string, delimiter: string): string => + `${name.replace(knownDelimitedFileExtension, '')}.${ + delimiterFileExtensions.get(delimiter) ?? 'txt' + }`; + export const downloadDataSet = async ( name: string, rows: RA>, @@ -19,9 +34,7 @@ export const downloadDataSet = async ( }, (error, output) => { if (error === undefined) - resolve( - downloadFile(name.endsWith('.csv') ? name : `${name}.tsv`, output) - ); + resolve(downloadFile(getDelimitedFileName(name, delimiter), output)); else reject(error); } ) From 3893bf60cbfe108bc8fe239ec39476c970e7395c Mon Sep 17 00:00:00 2001 From: alec_dev Date: Fri, 1 May 2026 14:18:30 -0500 Subject: [PATCH 2/2] Add tests for choosing file formatting --- .../WorkBench/__tests__/helpers.test.ts | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 specifyweb/frontend/js_src/lib/components/WorkBench/__tests__/helpers.test.ts diff --git a/specifyweb/frontend/js_src/lib/components/WorkBench/__tests__/helpers.test.ts b/specifyweb/frontend/js_src/lib/components/WorkBench/__tests__/helpers.test.ts new file mode 100644 index 00000000000..92e6cfb2373 --- /dev/null +++ b/specifyweb/frontend/js_src/lib/components/WorkBench/__tests__/helpers.test.ts @@ -0,0 +1,40 @@ +import { theories } from '../../../tests/utils'; +import { getDelimitedFileName } from '../helpers'; + +theories(getDelimitedFileName, [ + { + name: 'uses csv for comma delimiters', + in: ['Data Set', ','], + out: 'Data Set.csv', + }, + { + name: 'uses csv for semicolon delimiters', + in: ['Data Set', ';'], + out: 'Data Set.csv', + }, + { + name: 'uses tsv for tab delimiters', + in: ['Data Set', '\t'], + out: 'Data Set.tsv', + }, + { + name: 'uses psv for pipe delimiters', + in: ['Data Set', '|'], + out: 'Data Set.psv', + }, + { + name: 'uses txt for space delimiters', + in: ['Data Set', ' '], + out: 'Data Set.txt', + }, + { + name: 'replaces known delimited file extensions', + in: ['Data.Set.TSV', ','], + out: 'Data.Set.csv', + }, + { + name: 'preserves periods that are not known extensions', + in: ['Dr. Smith Data Set', ','], + out: 'Dr. Smith Data Set.csv', + }, +]);