Skip to content

Commit fc484f1

Browse files
authored
Merge pull request #5 from openscript/develop
Enhance callback handling
2 parents 71d5cd2 + cd5f116 commit fc484f1

7 files changed

+82
-24
lines changed

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"react"
88
],
99
"homepage": "https://openscript.github.io/react-dsv-import/",
10-
"version": "0.1.3",
10+
"version": "0.1.4",
1111
"main": "dist/index.js",
1212
"module": "dist/es/index.js",
1313
"types": "dist/index.d.ts",
@@ -25,6 +25,7 @@
2525
"@storybook/react": "^5.3.18",
2626
"@testing-library/jest-dom": "^5.5.0",
2727
"@testing-library/react": "^10.0.2",
28+
"@testing-library/react-hooks": "^3.2.1",
2829
"@types/jest": "^25.2.1",
2930
"@types/node": "^13.11.1",
3031
"@types/react": "^16.9.34",
@@ -40,6 +41,7 @@
4041
"jest": "^25.3.0",
4142
"prettier": "^2.0.4",
4243
"react-is": "^16.13.1",
44+
"react-test-renderer": "^16.13.1",
4345
"rollup": "^2.6.1",
4446
"ts-jest": "^25.4.0",
4547
"ts-node": "^8.8.2",

src/DSVImport.test.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ describe('DSVImport', () => {
3131
fireEvent.change(textarea, { target: { value: 'Max' } });
3232
}
3333

34-
expect(onChangeMock).toBeCalledTimes(2);
34+
expect(onChangeMock).toBeCalledTimes(1);
3535
expect(onChangeMock).toBeCalledWith([{ email: undefined, forename: 'Max', surname: undefined }]);
3636
});
3737
});

src/DSVImport.tsx

+20-3
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,38 @@
1-
import React, { PropsWithChildren, useReducer } from 'react';
1+
import React, { PropsWithChildren, useReducer, useEffect } from 'react';
22
import { ColumnsType } from './models/column';
3-
import { getDSVImportContext } from './features/context';
3+
import { getDSVImportContext, useDSVImport } from './features/context';
44
import { createSimpleParserMiddleware } from './middlewares/simpleParserMiddleware';
55
import { State } from './models/state';
66

7+
interface EventListenerProps<T> {
8+
onChange?: (value: T[]) => void;
9+
}
10+
11+
const EventListener = <T extends { [key: string]: string }>(props: EventListenerProps<T>) => {
12+
const [context] = useDSVImport<T>();
13+
14+
useEffect(() => {
15+
if (context.parsed && props.onChange) {
16+
props.onChange(context.parsed);
17+
}
18+
});
19+
20+
return null;
21+
};
22+
723
export interface Props<T> {
824
onChange?: (value: T[]) => void;
925
columns: ColumnsType<T>;
1026
}
1127

1228
export const DSVImport = <T extends { [key: string]: string }>(props: PropsWithChildren<Props<T>>) => {
1329
const DSVImportContext = getDSVImportContext<T>();
14-
const middleware = createSimpleParserMiddleware<T>(props.onChange);
30+
const middleware = createSimpleParserMiddleware<T>();
1531
const initialValues: State<T> = { columns: props.columns };
1632

1733
return (
1834
<DSVImportContext.Provider value={useReducer(middleware, initialValues)}>
35+
<EventListener<T> onChange={props.onChange} />
1936
{props.children}
2037
</DSVImportContext.Provider>
2138
);

src/features/context.test.ts

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,44 @@
11
import { reducer, getDSVImportContext } from './context';
22
import { State } from '../models/state';
3+
import { useContext } from 'react';
4+
import { renderHook } from '@testing-library/react-hooks';
35

46
describe('context', () => {
57
type TestType = {};
68
const defaultState: State<TestType> = { columns: [] };
79

8-
it('should reduce raw data to state', () => {
10+
it('should reduce raw data action to state', () => {
911
const newState = reducer<TestType>(defaultState, { type: 'setRaw', raw: 'Raw data' });
1012
expect(newState).toStrictEqual({ ...defaultState, raw: 'Raw data' });
1113
});
1214

13-
it('should reduce parsed data to state', () => {
15+
it('should reduce parsed data action to state', () => {
1416
const newState = reducer<TestType>(defaultState, { type: 'setParsed', parsed: ['Parsed data'] });
1517
expect(newState).toStrictEqual({ ...defaultState, parsed: ['Parsed data'] });
1618
});
1719

20+
it('should return state on unknown action', () => {
21+
const untypedReducer = reducer as <T>(state: State<T>, action: unknown) => State<T>;
22+
const newState = untypedReducer<TestType>(defaultState, { type: 'unknown' });
23+
expect(newState).toStrictEqual(defaultState);
24+
});
25+
1826
it('should create the context as singleton', () => {
1927
const first = getDSVImportContext<TestType>();
2028
const second = getDSVImportContext<TestType>();
2129

2230
expect(first).toStrictEqual(second);
2331
});
32+
33+
it('should throw an error when the context is not initialized', () => {
34+
const context = getDSVImportContext<TestType>();
35+
const { result } = renderHook(() => useContext(context));
36+
const [, dispatch] = result.current;
37+
38+
const provokeError = () => {
39+
dispatch({ type: 'setRaw', raw: 'fail' });
40+
};
41+
42+
expect(provokeError).toThrowError('Not initialized');
43+
});
2444
});

src/middlewares/simpleParserMiddleware.test.ts

-11
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,4 @@ describe('simpleParserMiddleware', () => {
3434
});
3535
});
3636
});
37-
38-
it('should invoke the onChange callback', () => {
39-
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
40-
const onChangeMock = jest.fn((_value: TestType[]) => {});
41-
const middlewareWithOnChange = createSimpleParserMiddleware<TestType>(onChangeMock);
42-
43-
middlewareWithOnChange(defaultState, { type: 'setRaw', raw: 'Max' });
44-
45-
expect(onChangeMock).toBeCalledTimes(1);
46-
expect(onChangeMock).toBeCalledWith([{ forename: 'Max', surname: undefined, email: undefined }]);
47-
});
4837
});

src/middlewares/simpleParserMiddleware.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,13 @@ const parseData = <T>(value: string, columns: ColumnsType<T>, delimiter: Delimit
2828
});
2929
};
3030

31-
export const createSimpleParserMiddleware = <T>(onChange?: (value: T[]) => void) => {
31+
export const createSimpleParserMiddleware = <T>() => {
3232
return (state: State<T>, action: Actions<T>) => {
3333
let newState = reducer<T>(state, action);
3434

3535
if (action.type === 'setRaw') {
3636
const delimiter = detectDelimiterFromValue(action.raw);
3737
const parsed = parseData<T>(action.raw, state.columns, delimiter);
38-
if (onChange) {
39-
onChange(parsed);
40-
}
4138
newState = reducer<T>(state, { type: 'setParsed', parsed });
4239
}
4340

yarn.lock

+35-2
Original file line numberDiff line numberDiff line change
@@ -1042,7 +1042,7 @@
10421042
dependencies:
10431043
regenerator-runtime "^0.13.4"
10441044

1045-
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
1045+
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
10461046
version "7.9.2"
10471047
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06"
10481048
integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==
@@ -2138,6 +2138,14 @@
21382138
lodash "^4.17.15"
21392139
redent "^3.0.0"
21402140

2141+
"@testing-library/react-hooks@^3.2.1":
2142+
version "3.2.1"
2143+
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-3.2.1.tgz#19b6caa048ef15faa69d439c469033873ea01294"
2144+
integrity sha512-1OB6Ksvlk6BCJA1xpj8/WWz0XVd1qRcgqdaFAq+xeC6l61Ucj0P6QpA5u+Db/x9gU4DCX8ziR5b66Mlfg0M2RA==
2145+
dependencies:
2146+
"@babel/runtime" "^7.5.4"
2147+
"@types/testing-library__react-hooks" "^3.0.0"
2148+
21412149
"@testing-library/react@^10.0.2":
21422150
version "10.0.2"
21432151
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-10.0.2.tgz#8eca7aa52d810cf7150048a2829fdc487162006d"
@@ -2312,6 +2320,13 @@
23122320
dependencies:
23132321
"@types/react" "*"
23142322

2323+
"@types/react-test-renderer@*":
2324+
version "16.9.2"
2325+
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.9.2.tgz#e1c408831e8183e5ad748fdece02214a7c2ab6c5"
2326+
integrity sha512-4eJr1JFLIAlWhzDkBCkhrOIWOvOxcCAfQh+jiKg7l/nNZcCIL2MHl2dZhogIFKyHzedVWHaVP1Yydq/Ruu4agw==
2327+
dependencies:
2328+
"@types/react" "*"
2329+
23152330
"@types/react-textarea-autosize@^4.3.3":
23162331
version "4.3.5"
23172332
resolved "https://registry.yarnpkg.com/@types/react-textarea-autosize/-/react-textarea-autosize-4.3.5.tgz#6c4d2753fa1864c98c0b2b517f67bb1f6e4c46de"
@@ -2356,6 +2371,14 @@
23562371
dependencies:
23572372
"@types/jest" "*"
23582373

2374+
"@types/testing-library__react-hooks@^3.0.0":
2375+
version "3.2.0"
2376+
resolved "https://registry.yarnpkg.com/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.2.0.tgz#52f3a109bef06080e3b1e3ae7ea1c014ce859897"
2377+
integrity sha512-dE8iMTuR5lzB+MqnxlzORlXzXyCL0EKfzH0w/lau20OpkHD37EaWjZDz0iNG8b71iEtxT4XKGmSKAGVEqk46mw==
2378+
dependencies:
2379+
"@types/react" "*"
2380+
"@types/react-test-renderer" "*"
2381+
23592382
"@types/testing-library__react@^10.0.0":
23602383
version "10.0.1"
23612384
resolved "https://registry.yarnpkg.com/@types/testing-library__react/-/testing-library__react-10.0.1.tgz#92bb4a02394bf44428e35f1da2970ed77f803593"
@@ -9594,7 +9617,7 @@ react-inspector@^4.0.0:
95949617
is-dom "^1.0.9"
95959618
prop-types "^15.6.1"
95969619

9597-
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.3:
9620+
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.3, react-is@^16.8.6:
95989621
version "16.13.1"
95999622
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
96009623
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -9646,6 +9669,16 @@ react-syntax-highlighter@^11.0.2:
96469669
prismjs "^1.8.4"
96479670
refractor "^2.4.1"
96489671

9672+
react-test-renderer@^16.13.1:
9673+
version "16.13.1"
9674+
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.13.1.tgz#de25ea358d9012606de51e012d9742e7f0deabc1"
9675+
integrity sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==
9676+
dependencies:
9677+
object-assign "^4.1.1"
9678+
prop-types "^15.6.2"
9679+
react-is "^16.8.6"
9680+
scheduler "^0.19.1"
9681+
96499682
react-textarea-autosize@^7.1.0:
96509683
version "7.1.2"
96519684
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-7.1.2.tgz#70fdb333ef86bcca72717e25e623e90c336e2cda"

0 commit comments

Comments
 (0)