Skip to content

Commit

Permalink
split standardizer modal component
Browse files Browse the repository at this point in the history
  • Loading branch information
sanghoonio committed Sep 17, 2024
1 parent b0998c6 commit 012b801
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 120 deletions.
4 changes: 2 additions & 2 deletions web/src/components/forms/create-schema-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const CombinedErrorMessage = (props: CombinedErrorMessageProps) => {
if (nameError == 'empty') {
msg = 'Schema Name must not be empty.';
} else if (nameError == 'invalid') {
msg = "Schema Name must contain only alphanumeric characters, '-', or '_'.";
msg = "Schema Name must contain only alphanumeric characters, '.', '-', or '_'.";
}

if (nameError) {
Expand Down Expand Up @@ -134,7 +134,7 @@ export const CreateSchemaForm = (props: Props) => {
message: "empty",
},
pattern: {
value: /^[a-zA-Z0-9_-]+$/,
value: /^[a-zA-Z0-9_.-]+$/,
message: "invalid",
},
})}
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/forms/upload-schema-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const CombinedErrorMessage = (props: CombinedErrorMessageProps) => {
if (nameError == 'empty') {
msg = 'Schema Name must not be empty.';
} else if (nameError == 'invalid') {
msg = "Schema Name must contain only alphanumeric characters, '-', or '_'.";
msg = "Schema Name must contain only alphanumeric characters, '.', '-', or '_'.";
}

if (nameError) {
Expand Down Expand Up @@ -125,7 +125,7 @@ export const SchemaUploadForm = (props: Props) => {
message: "empty",
},
pattern: {
value: /^[a-zA-Z0-9_-]+$/,
value: /^[a-zA-Z0-9_.-]+$/,
message: "invalid",
},
})}
Expand Down
128 changes: 12 additions & 116 deletions web/src/components/modals/standardize-metadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { formatToPercentage } from '../../utils/etc';
import { arraysToSampleList, sampleListToArrays } from '../../utils/sample-table';
import { ProjectMetaEditForm } from '../forms/edit-project-meta';
import { LoadingSpinner } from '../spinners/loading-spinner';
import { StandardizerTable } from '../tables/standardizer-table';

type Props = {
namespace: string;
Expand Down Expand Up @@ -265,122 +266,17 @@ export const StandardizeMetadataModal = (props: Props) => {

<form>
{Object.keys(standardizedData).map((key, index) => (
<div className="mb-3" key={key}>
<div
className={
key === sampleTableIndex
? 'row border shadow-sm rounded-3 m-1 pb-3 pt-2'
: 'row border shadow-sm rounded-3 m-1 py-3'
}
style={{
backgroundColor: whereDuplicates?.includes(index)
? '#dc354520'
: key === sampleTableIndex
? '#ffc10720'
: 'white',
}}
>
{key === sampleTableIndex ? (
<p className="text-center text-xs mb-2 p-0 fw-bold">
SampleTableIndex must also be updated in project config!
</p>
) : null}
<div className="col-6 text-center">
<div
className="w-100 h-100 overflow-auto border border-secondary-subtle rounded-2 shadow-sm"
style={{ bottom: '-1px' }}
>
<HotTable
data={prepareHandsontableData(key)}
colHeaders={false}
rowHeaders={true}
width="100%"
height="100%"
colWidths="100%"
stretchH="all"
autoColumnSize={false}
readOnly={true}
columns={[
{
data: 0,
type: typeof tabData[key] === 'number' ? 'numeric' : 'text',
renderer: function (
instance: Handsontable.Core,
td: HTMLTableCellElement,
row: number,
col: number,
prop: string | number,
value: any,
cellProperties: Handsontable.CellProperties,
) {
Handsontable.renderers.TextRenderer.apply(this, [
instance,
td,
row,
col,
prop,
value,
cellProperties,
]);
if (row === 0) {
td.style.fontWeight = 'bold';
if (whereDuplicates?.includes(index)) {
td.style.color = 'red';
}
}
},
},
]}
licenseKey="non-commercial-and-evaluation"
className="custom-handsontable"
/>
</div>
</div>
<div className="col-6" role="group" aria-label="radio_group">
<div className="w-100 h-100 rounded-2 outer-container">
<div className="btn-group-vertical w-100 h-100 bg-white rounded-2">
<input
className="btn-check"
type="radio"
name={key}
id={`${key}-original`}
value={key}
checked={selectedValues[key] === key} // Check if the selected value is the same as the key
onChange={() => handleRadioChange(key, null)}
/>
<label
className="btn btn-outline-secondary selected-outline shadow-sm bg-white"
htmlFor={`${key}-original`}
>
<strong className="fw-semibold">{key}</strong> (original value)
</label>

{Object.entries(standardizedData[key]).map(([subKey, value], index, array) => (
<React.Fragment key={subKey}>
<input
className="btn-check"
type="radio"
name={key}
id={`${key}-suggested-${subKey}`}
value={subKey}
checked={selectedValues[key] === subKey}
disabled={standardizedData[key]['Not Predictable'] === 0}
onChange={() => handleRadioChange(key, subKey)}
/>
<label
className="btn btn-outline-secondary selected-outline shadow-sm bg-white"
htmlFor={`${key}-suggested-${subKey}`}
>
{subKey} ({formatToPercentage(value)})
</label>
</React.Fragment>
))}
</div>
</div>
</div>
<br />
</div>
</div>
<StandardizerTable
columnKey={key}
columnIndex={index}
standardizedData={standardizedData[key]}
selectedValues={selectedValues[key]}
whereDuplicates={whereDuplicates}
sampleTableIndex={sampleTableIndex}
tabData={tabData[key]}
handleRadioChange={handleRadioChange}
tableData={prepareHandsontableData(key)}
/>
))}
</form>
</>
Expand Down
151 changes: 151 additions & 0 deletions web/src/components/tables/standardizer-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import React from 'react';
import { HotTable } from '@handsontable/react';
import Handsontable from 'handsontable';
import { formatToPercentage } from '../../utils/etc';

type StandardizerTableProps = {
columnKey: string;
columnIndex: number;
standardizedData: Record<string, number>;
selectedValues: string;
whereDuplicates: number[] | null;
sampleTableIndex: string;
tabData: string[];
handleRadioChange: (key: string, value: string | null) => void;
tableData: string[][];
};

export const StandardizerTable = (props: StandardizerTableProps) => {
const {
columnKey,
columnIndex,
standardizedData,
selectedValues,
whereDuplicates,
sampleTableIndex,
tabData,
handleRadioChange,
tableData,
} = props;

return (
<>
<div className="mb-3" key={columnKey}>
<div
className={
columnKey === sampleTableIndex
? 'row border shadow-sm rounded-3 m-1 pb-3 pt-2'
: 'row border shadow-sm rounded-3 m-1 py-3'
}
style={{
backgroundColor: whereDuplicates?.includes(columnIndex)
? '#dc354520'
: columnKey === sampleTableIndex
? '#ffc10720'
: 'white',
}}
>
{columnKey === sampleTableIndex ? (
<p className="text-center text-xs mb-2 p-0 fw-bold">
SampleTableIndex must also be updated in project config!
</p>
) : null}
<div className="col-6 text-center">
<div
className="w-100 h-100 overflow-auto border border-secondary-subtle rounded-2 shadow-sm"
style={{ bottom: '-1px' }}
>
<HotTable
data={tableData}
colHeaders={false}
rowHeaders={true}
width="100%"
height="100%"
colWidths="100%"
stretchH="all"
autoColumnSize={false}
readOnly={true}
columns={[
{
data: 0,
type: typeof tabData === 'number' ? 'numeric' : 'text',
renderer: function (
instance: Handsontable.Core,
td: HTMLTableCellElement,
row: number,
col: number,
prop: string | number,
value: any,
cellProperties: Handsontable.CellProperties,
) {
Handsontable.renderers.TextRenderer.apply(this, [
instance,
td,
row,
col,
prop,
value,
cellProperties,
]);
if (row === 0) {
td.style.fontWeight = 'bold';
if (whereDuplicates?.includes(columnIndex)) {
td.style.color = 'red';
}
}
},
},
]}
licenseKey="non-commercial-and-evaluation"
className="custom-handsontable"
/>
</div>
</div>
<div className="col-6" role="group" aria-label="radio_group">
<div className="w-100 h-100 rounded-2 outer-container">
<div className="btn-group-vertical w-100 h-100 bg-white rounded-2">
<input
className="btn-check"
type="radio"
name={columnKey}
id={`${columnKey}-original`}
value={columnKey}
checked={selectedValues === columnKey}
onChange={() => handleRadioChange(columnKey, null)}
/>
<label
className="btn btn-outline-secondary selected-outline shadow-sm bg-white"
htmlFor={`${columnKey}-original`}
>
<strong className="fw-semibold">{columnKey}</strong> (original value)
</label>

{Object.entries(standardizedData).map(([subKey, value]) => (
<React.Fragment key={subKey}>
<input
className="btn-check"
type="radio"
name={columnKey}
id={`${columnKey}-suggested-${subKey}`}
value={subKey}
checked={selectedValues === subKey}
disabled={standardizedData['Not Predictable'] === 0}
onChange={() => handleRadioChange(columnKey, subKey)}
/>
<label
className="btn btn-outline-secondary selected-outline shadow-sm bg-white"
htmlFor={`${columnKey}-suggested-${subKey}`}
>
{subKey} ({formatToPercentage(value)})
</label>
</React.Fragment>
))}
</div>
</div>
</div>
<br />
</div>
</div>
</>
);
};

0 comments on commit 012b801

Please sign in to comment.