Skip to content

Commit 5733260

Browse files
committed
Enhance transformer api
1 parent 4a5a037 commit 5733260

File tree

6 files changed

+50
-34
lines changed

6 files changed

+50
-34
lines changed

src/DSVImport.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import { State } from './models/state';
66
import { applyMiddlewares } from './middlewares/middleware';
77
import { createValidatorMiddleware } from './middlewares/validatorMiddleware';
88
import { ValidationError } from './models/validation';
9-
import { Transformers } from './models/transformer';
109
import { createTransformerMiddleware } from './middlewares/transformerMiddleware';
10+
import { Transformer } from './models/transformer';
1111

1212
interface EventListenerProps<T> {
1313
onChange?: (value: T[]) => void;
@@ -35,7 +35,7 @@ const EventListener = <T extends { [key: string]: string }>(props: EventListener
3535
export interface Props<T> {
3636
onChange?: (value: T[]) => void;
3737
onValidation?: (errors: ValidationError<T>[]) => void;
38-
transformers?: Transformers<T>;
38+
transformers?: Transformer[];
3939
columns: ColumnsType<T>;
4040
}
4141

src/middlewares/transformerMiddleware.test.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ describe('validatorMiddleware', () => {
1515
{ forename: 'Heidi', surname: ' Muster', email: '[email protected]' },
1616
{ forename: 'Joe', surname: 'Doe', email: ' [email protected] ' }
1717
];
18+
const trimTransformer = (value: string) => value.trim();
19+
const markTransformer = (value: string) => `${value}!`;
1820

1921
it('should not dispatch if there are no transformers', () => {
2022
const state: State<TestType> = { columns: defaultColumns };
@@ -25,8 +27,7 @@ describe('validatorMiddleware', () => {
2527
});
2628

2729
it('should run a transformer on all values', () => {
28-
const trimTransformer = (value: string) => value.trim();
29-
const state: State<TestType> = { columns: defaultColumns, transformers: [{ transformer: trimTransformer }] };
30+
const state: State<TestType> = { columns: defaultColumns, transformers: [trimTransformer] };
3031
const dispatchMock = jest.fn();
3132

3233
middleware(state, dispatchMock, { type: 'setParsed', parsed });
@@ -42,10 +43,12 @@ describe('validatorMiddleware', () => {
4243
});
4344

4445
it('should run transformers on values of a certain column', () => {
45-
const trimTransformer = (value: string) => value.trim();
4646
const state: State<TestType> = {
47-
columns: defaultColumns,
48-
transformers: [{ transformer: trimTransformer, column: 'surname' }]
47+
columns: [
48+
{ key: 'forename', label: 'Forename' },
49+
{ key: 'surname', label: 'Surname', transformers: [trimTransformer, markTransformer] },
50+
{ key: 'email', label: 'Email' }
51+
]
4952
};
5053
const dispatchMock = jest.fn();
5154

@@ -54,9 +57,9 @@ describe('validatorMiddleware', () => {
5457
expect(dispatchMock).toBeCalledWith({
5558
type: 'setParsed',
5659
parsed: [
57-
{ forename: 'Hans', surname: 'Muster', email: '[email protected]' },
58-
{ forename: 'Heidi', surname: 'Muster', email: '[email protected]' },
59-
{ forename: 'Joe', surname: 'Doe', email: ' [email protected] ' }
60+
{ forename: 'Hans', surname: 'Muster!', email: '[email protected]' },
61+
{ forename: 'Heidi', surname: 'Muster!', email: '[email protected]' },
62+
{ forename: 'Joe', surname: 'Doe!', email: ' [email protected] ' }
6063
]
6164
});
6265
});
Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,50 @@
11
import { State } from '../models/state';
22
import { Dispatch } from 'react';
33
import { Actions } from '../models/actions';
4-
import { TransformerConfiguration } from '../models/transformer';
54
import { ColumnsType } from '../models/column';
5+
import { Transformer } from '../models/transformer';
66

7-
const executeTransformer = <T>(
8-
values: T[],
9-
transformerConfiguration: TransformerConfiguration<T>,
10-
columns: ColumnsType<T>
11-
) => {
12-
const currentColumns = transformerConfiguration.column
13-
? columns.filter((c) => c.key === transformerConfiguration.column)
14-
: columns;
7+
const executeGlobalTransformers = <T>(values: T[], transformer: Transformer, columns: ColumnsType<T>) => {
158
return values.map<T>((r) => {
169
const transformed = { ...r };
17-
currentColumns.forEach((c) => {
18-
transformed[c.key] = (transformerConfiguration.transformer(
19-
new String(r[c.key]).toString()
20-
) as unknown) as T[keyof T];
10+
columns.forEach((c) => {
11+
transformed[c.key] = (transformer(new String(r[c.key]).toString()) as unknown) as T[keyof T];
12+
});
13+
return transformed;
14+
});
15+
};
16+
17+
const executeColumnTransformers = <T>(values: T[], columns: ColumnsType<T>) => {
18+
return values.map<T>((r) => {
19+
const transformed = { ...r };
20+
columns.forEach((c) => {
21+
if (c.transformers) {
22+
transformed[c.key] = (c.transformers.reduce(
23+
(acc, t) => t(acc),
24+
new String(r[c.key]).toString()
25+
) as unknown) as T[keyof T];
26+
}
2127
});
2228
return transformed;
2329
});
2430
};
2531

2632
export const createTransformerMiddleware = <T>() => {
2733
return (state: State<T>, next: Dispatch<Actions<T>>, action: Actions<T>) => {
28-
if (action.type === 'setParsed' && state.transformers) {
29-
const parsed = state.transformers.reduce<T[]>(
30-
(acc, t) => executeTransformer(acc, t, state.columns),
31-
action.parsed
32-
);
33-
next({ type: 'setParsed', parsed });
34+
if (action.type === 'setParsed') {
35+
let parsed = action.parsed;
36+
if (state.transformers) {
37+
parsed = state.transformers.reduce<T[]>((acc, t) => executeGlobalTransformers(acc, t, state.columns), parsed);
38+
}
39+
40+
const hasColumnTransformers = state.columns.find((c) => c.transformers) ? true : false;
41+
if (hasColumnTransformers) {
42+
parsed = executeColumnTransformers(parsed, state.columns);
43+
}
44+
45+
if (state.transformers || hasColumnTransformers) {
46+
next({ type: 'setParsed', parsed });
47+
}
3448
}
3549
};
3650
};

src/models/column.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import { Rule } from './rule';
2+
import { Transformer } from './transformer';
23

3-
export type ColumnsType<T> = { key: keyof T; label: string; rules?: Rule[] }[];
4+
export type ColumnsType<T> = { key: keyof T; label: string; rules?: Rule[]; transformers?: Transformer[] }[];

src/models/state.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { ColumnsType } from './column';
22
import { ValidationError } from './validation';
3-
import { Transformers } from './transformer';
3+
import { Transformer } from './transformer';
44

55
export interface State<T> {
66
raw?: string;
77
parsed?: T[];
88
validation?: ValidationError<T>[];
9-
transformers?: Transformers<T>;
9+
transformers?: Transformer[];
1010
columns: ColumnsType<T>;
1111
}
1212

src/models/transformer.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
11
export type Transformer = (value: string) => string;
2-
export type TransformerConfiguration<T> = { transformer: Transformer; column?: keyof T };
3-
export type Transformers<T> = TransformerConfiguration<T>[];

0 commit comments

Comments
 (0)