Skip to content

Commit 3d0d8e2

Browse files
committed
Show errors in preview
1 parent 04c1620 commit 3d0d8e2

File tree

5 files changed

+85
-10
lines changed

5 files changed

+85
-10
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
"dependencies": {},
1515
"devDependencies": {
1616
"@babel/core": "^7.9.6",
17+
"@emotion/core": "^10.0.28",
18+
"@emotion/styled": "^10.0.27",
1719
"@rollup/plugin-typescript": "^4.1.1",
1820
"@storybook/addon-actions": "^5.3.18",
1921
"@storybook/addon-docs": "^5.3.18",

src/DSVImport.stories.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useState } from 'react';
22
import { DSVImport, ColumnsType } from './';
33
import { action } from '@storybook/addon-actions';
4+
import styled from '@emotion/styled';
45

56
export default { title: 'Usage' };
67

@@ -50,6 +51,12 @@ export const UsingOnChangeCallback = () => {
5051
};
5152
UsingOnChangeCallback.story = { name: 'Using on change callback' };
5253

54+
const CustomTablePreview = styled(DSVImport.TablePreview)`
55+
.error {
56+
border: 1px solid red;
57+
}
58+
`;
59+
5360
export const UsingOnValidationCallback = () => {
5461
const columns: ColumnsType<BasicType> = [
5562
{ key: 'forename', label: 'Forename' },
@@ -62,7 +69,7 @@ export const UsingOnValidationCallback = () => {
6269
return (
6370
<DSVImport<BasicType> columns={columns} onChange={onChangeAction} onValidation={onValidationAction}>
6471
<DSVImport.TextareaInput />
65-
<DSVImport.TablePreview />
72+
<CustomTablePreview />
6673
</DSVImport>
6774
);
6875
};

src/components/previews/TablePreview.test.tsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,37 @@ describe('TablePreview', () => {
4040
const { container } = renderComponent({
4141
...defaultState,
4242
parsed: [
43-
{ forename: 'Max', surname: 'Muster', email: 'max@example.com' },
44-
{ forename: '', surname: '', email: 'unknown@example.com' }
43+
{ forename: 'Max', surname: 'Muster', email: 'example@example.com' },
44+
{ forename: '', surname: '', email: 'example@example.com' }
4545
]
4646
});
4747
const tableBody = container.querySelector('tbody');
4848

4949
expect(tableBody?.childElementCount).toBe(2);
5050

5151
expect(tableBody?.children[0].children[0]).toHaveTextContent('Max');
52-
expect(tableBody?.children[1].children[2]).toHaveTextContent('[email protected]');
52+
expect(tableBody?.children[1].children[2]).toHaveTextContent('[email protected]');
53+
});
54+
55+
it('should render the parsed content in the table body', () => {
56+
const { container } = renderComponent({
57+
...defaultState,
58+
parsed: [
59+
{ forename: 'Max', surname: 'Muster', email: '[email protected]' },
60+
{ forename: '', surname: '', email: '[email protected]' }
61+
],
62+
validation: [
63+
{ column: 'email', message: 'Contains duplicates' },
64+
{ column: 'forename', row: 1, message: 'Forename is required' }
65+
]
66+
});
67+
const tableBody = container.querySelector('tbody');
68+
const tableHead = container.querySelector('thead tr');
69+
70+
expect(tableHead?.children[2]).toHaveClass('error');
71+
expect(tableHead?.children[2]).toHaveAttribute('title', 'Contains duplicates');
72+
73+
expect(tableBody?.children[1].children[0]).toHaveClass('error');
74+
expect(tableBody?.children[1].children[0]).toHaveAttribute('title', 'Forename is required');
5375
});
5476
});

src/components/previews/TablePreview.tsx

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,55 @@
11
import React from 'react';
22
import { useDSVImport } from '../../features/context';
33

4-
export const TablePreview: React.FC = () => {
4+
interface Props {
5+
className?: string;
6+
}
7+
8+
export const TablePreview: React.FC<Props> = (props) => {
59
const [context] = useDSVImport();
610

11+
const getColumnValidationError = (columnKey: string) => {
12+
if (context.validation) {
13+
return context.validation.filter((e) => e.column === columnKey && !e.row);
14+
}
15+
};
16+
17+
const getCellValidationError = (columnKey: string, rowIndex: number) => {
18+
if (context.validation) {
19+
return context.validation.filter((e) => e.column === columnKey && e.row === rowIndex);
20+
}
21+
};
22+
23+
const ColumnHead: React.FC<{ columnKey: string }> = (props) => {
24+
const errors = getColumnValidationError(props.columnKey);
25+
const messages = errors?.map((e) => e?.message).join(';');
26+
27+
return (
28+
<th className={messages ? 'error' : ''} title={messages}>
29+
{props.children}
30+
</th>
31+
);
32+
};
33+
34+
const Cell: React.FC<{ columnKey: string; rowIndex: number }> = (props) => {
35+
const errors = getCellValidationError(props.columnKey, props.rowIndex);
36+
const messages = errors?.map((e) => e?.message).join(';');
37+
38+
return (
39+
<td className={messages ? 'error' : ''} title={messages}>
40+
{props.children}
41+
</td>
42+
);
43+
};
44+
745
return (
8-
<table>
46+
<table className={props.className}>
947
<thead>
1048
<tr>
1149
{context.columns.map((column, columnIndex) => (
12-
<th key={columnIndex}>{column.label}</th>
50+
<ColumnHead key={columnIndex} columnKey={column.key.toString()}>
51+
{column.label}
52+
</ColumnHead>
1353
))}
1454
</tr>
1555
</thead>
@@ -18,7 +58,11 @@ export const TablePreview: React.FC = () => {
1858
? context.parsed.map((row, rowIndex) => (
1959
<tr key={rowIndex}>
2060
{context.columns.map((column, columnIndex) => {
21-
return <td key={columnIndex}>{row[column.key]}</td>;
61+
return (
62+
<Cell key={columnIndex} columnKey={column.key.toString()} rowIndex={rowIndex}>
63+
{row[column.key]}
64+
</Cell>
65+
);
2266
})}
2367
</tr>
2468
))

yarn.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,7 @@
11221122
"@emotion/utils" "0.11.3"
11231123
"@emotion/weak-memoize" "0.2.5"
11241124

1125-
"@emotion/core@^10.0.20":
1125+
"@emotion/core@^10.0.20", "@emotion/core@^10.0.28":
11261126
version "10.0.28"
11271127
resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.0.28.tgz#bb65af7262a234593a9e952c041d0f1c9b9bef3d"
11281128
integrity sha512-pH8UueKYO5jgg0Iq+AmCLxBsvuGtvlmiDCOuv8fGNYn3cowFpLN98L8zO56U0H1PjDIyAlXymgL3Wu7u7v6hbA==
@@ -1186,7 +1186,7 @@
11861186
"@emotion/serialize" "^0.11.15"
11871187
"@emotion/utils" "0.11.3"
11881188

1189-
"@emotion/styled@^10.0.17":
1189+
"@emotion/styled@^10.0.17", "@emotion/styled@^10.0.27":
11901190
version "10.0.27"
11911191
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-10.0.27.tgz#12cb67e91f7ad7431e1875b1d83a94b814133eaf"
11921192
integrity sha512-iK/8Sh7+NLJzyp9a5+vIQIXTYxfT4yB/OJbjzQanB2RZpvmzBQOHZWhpAMZWYEKRNNbsD6WfBw5sVWkb6WzS/Q==

0 commit comments

Comments
 (0)