Skip to content

Commit

Permalink
Frontend: Validate: ValidationTable: Add error messages inline (#134)
Browse files Browse the repository at this point in the history
So that the error messages are shown below the row with the error.
  • Loading branch information
illume authored Aug 22, 2023
1 parent 903eaf8 commit c6c754f
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 61 deletions.
11 changes: 10 additions & 1 deletion FrontEnd/src/components/Validate/Validate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,18 @@ export function ValidatePure({
return (
<>
<ValidationTable
error={true}
errorMessage={errorsData}
uploadedData={filesWithErrorsList}
headers={[
// @todo: these are hardcoded.
{ label: 'Tag' },
{ label: 'Subquadrat' },
{ label: 'SpCode' },
{ label: 'DBH' },
{ label: 'Htmeas' },
{ label: 'Codes' },
{ label: 'Comments' },
]}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ComponentMeta, Story } from '@storybook/react';
import { FileWithPath } from 'react-dropzone';
import ValidationTable, { ValidationTableProps } from './ValidationTable';
import fs from 'fs';

Check warning on line 4 in FrontEnd/src/components/ValidationTable/ValidationTable.stories.tsx

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 16.x)

'fs' is defined but never used

Check warning on line 4 in FrontEnd/src/components/ValidationTable/ValidationTable.stories.tsx

View workflow job for this annotation

GitHub Actions / build (windows-latest, 16.x)

'fs' is defined but never used

export default {
title: 'ValidationTable',
Expand All @@ -12,26 +13,82 @@ const Template: Story<ValidationTableProps> = (args) => (
<ValidationTable {...args} />
);

// @todo: need realistic example data for this validation table.
const HEADERS = [
{ label: 'Tag' },
{ label: 'Subquadrat' },
{ label: 'SpCode' },
{ label: 'DBH' },
{ label: 'Htmeas' },
{ label: 'Codes' },
{ label: 'Comments' },
];

const uploadedData: FileWithPath[] = [];
// some example uploaded data
const uploadedData: FileWithPath[] = [
new File(
[
new Blob(
[
[
'Tag,Subquadrat,SpCode,DBH,Htmeas,Codes,Comments',
'Tag0,Subquadrat0,SpCode0,DBH0,Htmeas0,Codes0,Comment0',
'Tag1,Subquadrat1,SpCode1,DBH1,Htmeas1,Codes1,Comment1',
'Tag2,Subquadrat2,SpCode2,DBH2,Htmeas2,Codes2,Comment2',
'Tag3,Subquadrat3,SpCode3,DBH3,Htmeas3,Codes3,Comment3',
'Tag4,Subquadrat4,SpCode4,DBH4,Htmeas4,Codes4,Comment4',
'Tag5,Subquadrat5,SpCode5,DBH5,Htmeas5,Codes5,Comment5',
].join('\n'),
],
{ type: 'text/plain' }
),
],
'test1.csv',
{ lastModified: new Date().getTime() }
),
new File(
[
new Blob(
[
[
'Tag,Subquadrat,SpCode,DBH,Htmeas,Codes,Comments',
'Tag0,Subquadrat0,SpCode0,DBH0,Htmeas0,Codes0,Comment0',
'Tag1,Subquadrat1,SpCode1,DBH1,Htmeas1,Codes1,Comment1',
'Tag2,Subquadrat2,SpCode2,DBH2,Htmeas2,Codes2,Comment2',
'Tag3,Subquadrat3,SpCode3,DBH3,Htmeas3,Codes3,Comment3',
'Tag4,Subquadrat4,SpCode4,DBH4,Htmeas4,Codes4,Comment4',
'Tag5,Subquadrat5,SpCode5,DBH5,Htmeas5,Codes5,Comment5',
].join('\n'),
],
{ type: 'text/plain' }
),
],
'test2.csv',
{ lastModified: new Date().getTime() }
),
];

export const NoError = Template.bind({});
NoError.args = {
error: false,
errorMessage: {},
errorMessage: {
'test1.csv': {},
'test2.csv': {},
},
uploadedData: uploadedData,
headers: HEADERS,
};

export const Error = Template.bind({});
Error.args = {
error: true,
errorMessage: {
filename: {
0: 'ERROR: testing message 0',
3: 'ERROR: testing message 3',
5: 'ERROR: testing message 5',
'test1.csv': {
0: 'ERROR: testing error message on row 0',
3: 'ERROR: testing error message on row 3',
4: 'ERROR: testing error message on row 4',
},
'test2.csv': {
1: 'ERROR: testing error message on row 1',
},
},
uploadedData: uploadedData,
headers: HEADERS,
};
82 changes: 31 additions & 51 deletions FrontEnd/src/components/ValidationTable/ValidationTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,50 +7,34 @@ import {
TableCell,
Typography,
Paper,
TableFooter,
} from '@mui/material';
import { parse } from 'papaparse';
import { useState } from 'react';
import { FileWithPath } from 'react-dropzone';
import { useNavigate } from 'react-router-dom';
import Button from '../Button';
import './ValidationTable.css';

export interface ValidationTableProps {
/** An array of uploaded data. */
uploadedData: FileWithPath[];
/** If there is an error this is true. */
error: boolean;
/** If there are errors, these errors are indexed into the uploadedData field. */
errorMessage: { [fileName: string]: { [currentRow: string]: string } };
/** The headers for the table. */
headers: { label: string }[];
children?: React.ReactNode | React.ReactNode[];
}

export interface dataStructure {
[key: string]: string;
}

// @todo: are these headers really fixed?
// @todo: Maybe these headers should be passed in as a prop?
const HEADERS = [
{ label: 'Tag' },
{ label: 'Subquadrat' },
{ label: 'SpCode' },
{ label: 'DBH' },
{ label: 'Htmeas' },
{ label: 'Codes' },
{ label: 'Comments' },
];

/**
* Shows a data table with the possibility of showing errors.
*/
export default function ValidationTable({
uploadedData,
error,
errorMessage,
headers,
}: ValidationTableProps): JSX.Element {
let navigate = useNavigate();
let tempData: { fileName: string; data: dataStructure[] }[] = [];
const initState: { fileName: string; data: dataStructure[] }[] = [];
const [data, setData] = useState(initState);
Expand Down Expand Up @@ -94,49 +78,45 @@ export default function ValidationTable({
<>
<TableHead>
<TableRow>
{HEADERS.map((row, index) => {
{headers.map((row, index) => {
return <TableCell key={index}>{row.label}</TableCell>;
})}
</TableRow>
</TableHead>
<TableBody>
{fileData!.data.map((data: dataStructure, rowIdx) => {
return (
<>
<TableRow>
{headers.map((header, i) => (
<TableCell key={i}>
{data[header.label]}
</TableCell>
))}
</TableRow>

{fileData!.data.map((data: dataStructure) => {
return (
<TableBody>
<TableRow>
{HEADERS.map((header, i) => (
<TableCell key={i}>{data[header.label]}</TableCell>
))}
</TableRow>
</TableBody>
);
})}
{errorMessage[fileName][rowIdx] && (
<TableRow className="errorMessage">
<TableCell colSpan={headers.length}>
<Typography
className="errorMessage"
component={'span'}
>
^ {errorMessage[fileName][rowIdx]}
</Typography>
</TableCell>
</TableRow>
)}
</>
);
})}
</TableBody>
</>
)}
{errorMessage && (
<TableFooter>
<TableRow className="errorMessage">
<TableCell colSpan={HEADERS.length}>
<Typography className="errorMessage" component={'span'}>
<ul>
{Object.keys(errorMessage[fileName]).map((row) => {
return (
<li key={parseInt(row[0])}>
Row {row}: {errorMessage[fileName][row]}
</li>
);
})}
</ul>
</Typography>
</TableCell>
</TableRow>
</TableFooter>
)}
</Table>
</TableContainer>
);
})}
<Button label="Return to main page" onClick={() => navigate('/')} />
</>
);
}

0 comments on commit c6c754f

Please sign in to comment.