Skip to content

Commit 19fdffe

Browse files
authored
feat(import): Make analyzeCSVFields() skippable COMPASS-6638 (#4190)
* WIP: skippable analyzeCSVFields() * design tweaks * dark mode color * remove FieldType non-component * unnecessary useCallback * remove tests that test things that are removed
1 parent b049dfd commit 19fdffe

File tree

4 files changed

+207
-151
lines changed

4 files changed

+207
-151
lines changed

packages/compass-import-export/src/components/import-modal.tsx

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ import {
77
ModalFooter,
88
ModalHeader,
99
css,
10+
cx,
1011
spacing,
1112
FormFieldContainer,
13+
Body,
14+
palette,
15+
useDarkMode,
1216
} from '@mongodb-js/compass-components';
1317
import { useTrackOnChange } from '@mongodb-js/compass-logging';
1418

@@ -31,6 +35,7 @@ import formatNumber from '../utils/format-number';
3135
import {
3236
startImport,
3337
cancelImport,
38+
skipCSVAnalyze,
3439
selectImportFileName,
3540
setDelimiter,
3641
setStopOnErrors,
@@ -44,6 +49,7 @@ import type { RootImportState } from '../stores/import-store';
4449
import type { CSVDelimiter, FieldFromCSV } from '../modules/import';
4550
import { ImportFileInput } from './import-file-input';
4651
import type { CSVParsableFieldType } from '../utils/csv';
52+
import { SpinLoader } from '@mongodb-js/compass-components';
4753

4854
/**
4955
* Progress messages.
@@ -61,11 +67,41 @@ const closeButtonStyles = css({
6167
marginRight: spacing[2],
6268
});
6369

70+
const analyzeStyles = css({
71+
display: 'flex',
72+
flexDirection: 'column',
73+
alignItems: 'center',
74+
padding: `${spacing[4]}px 0`,
75+
});
76+
77+
const analyzeStylesDark = css({
78+
backgroundColor: palette.gray.dark3,
79+
});
80+
81+
const analyzeStylesLight = css({
82+
backgroundColor: palette.gray.light3,
83+
});
84+
85+
const loaderStyles = css({
86+
marginTop: spacing[3],
87+
display: 'flex',
88+
flexDirection: 'row',
89+
gap: spacing[1],
90+
alignItems: 'center',
91+
});
92+
93+
const explanationTextStyles = css({
94+
margin: `${spacing[3]}px 0`,
95+
width: '350px',
96+
textAlign: 'center',
97+
});
98+
6499
type ImportModalProps = {
65100
isOpen: boolean;
66101
ns: string;
67102
startImport: () => void;
68103
cancelImport: () => void;
104+
skipCSVAnalyze: () => void;
69105
closeImport: () => void;
70106
errors: Error[];
71107
status: ProcessStatus;
@@ -92,6 +128,9 @@ type ImportModalProps = {
92128
guesstimatedDocsTotal: number;
93129
guesstimatedDocsProcessed: number;
94130

131+
analyzeBytesProcessed: number;
132+
analyzeBytesTotal: number;
133+
95134
/**
96135
* See `<ImportPreview />`
97136
*/
@@ -114,6 +153,8 @@ function ImportModal({
114153
cancelImport,
115154
closeImport,
116155

156+
skipCSVAnalyze,
157+
117158
errors,
118159
status,
119160

@@ -133,6 +174,9 @@ function ImportModal({
133174
guesstimatedDocsTotal,
134175
guesstimatedDocsProcessed,
135176

177+
analyzeBytesProcessed,
178+
analyzeBytesTotal,
179+
136180
fields,
137181
values,
138182
toggleIncludeField,
@@ -141,18 +185,11 @@ function ImportModal({
141185
csvAnalyzed,
142186
}: ImportModalProps) {
143187
const modalBodyRef = useRef<HTMLDivElement>(null);
144-
const handleCancel = useCallback(() => {
145-
cancelImport();
146-
}, [cancelImport]);
147188

148189
const handleClose = useCallback(() => {
149-
handleCancel();
190+
cancelImport();
150191
closeImport();
151-
}, [closeImport, handleCancel]);
152-
153-
const handleImportBtnClicked = useCallback(() => {
154-
startImport();
155-
}, [startImport]);
192+
}, [closeImport, cancelImport]);
156193

157194
// docsTotal is set to actual value only at the very end of processing a
158195
// stream of documents
@@ -184,6 +221,8 @@ function ImportModal({
184221
React
185222
);
186223

224+
const darkMode = useDarkMode();
225+
187226
if (isOpen && !fileName && errors.length === 0) {
188227
// Show the file input when we don't have a file to import yet.
189228
return (
@@ -216,18 +255,45 @@ function ImportModal({
216255
ignoreBlanks={ignoreBlanks}
217256
setIgnoreBlanks={setIgnoreBlanks}
218257
/>
219-
{fileType === 'csv' && (
258+
{fileType === 'csv' && csvAnalyzed && (
220259
<FormFieldContainer>
221260
<ImportPreview
222261
loaded={previewLoaded}
223-
analyzed={csvAnalyzed}
224262
onFieldCheckedChanged={toggleIncludeField}
225263
setFieldType={setFieldType}
226264
values={values}
227265
fields={fields as FieldFromCSV[]}
228266
/>
229267
</FormFieldContainer>
230268
)}
269+
270+
{fileType === 'csv' && !csvAnalyzed && (
271+
<FormFieldContainer
272+
className={cx(
273+
analyzeStyles,
274+
darkMode ? analyzeStylesDark : analyzeStylesLight
275+
)}
276+
>
277+
<Body weight="medium">Detecting field types</Body>
278+
{analyzeBytesTotal && (
279+
<div className={loaderStyles}>
280+
<SpinLoader />
281+
<Body>
282+
{Math.round(
283+
(analyzeBytesProcessed / analyzeBytesTotal) * 100
284+
)}
285+
%
286+
</Body>
287+
</div>
288+
)}
289+
<Body className={explanationTextStyles}>
290+
We are scanning your CSV file row by row to detect the field
291+
types. You can skip this step and manually assign field types at
292+
any point during the process.
293+
</Body>
294+
<Button onClick={skipCSVAnalyze}>Skip</Button>
295+
</FormFieldContainer>
296+
)}
231297
<ProgressBar
232298
status={status}
233299
withErrors={errors.length > 0}
@@ -269,7 +335,7 @@ function ImportModal({
269335
<>
270336
<Button
271337
data-testid="import-button"
272-
onClick={handleImportBtnClicked}
338+
onClick={startImport}
273339
disabled={
274340
!fileName ||
275341
status === STARTED ||
@@ -308,6 +374,8 @@ const mapStateToProps = (state: RootImportState) => ({
308374
docsWritten: state.importData.docsWritten,
309375
guesstimatedDocsTotal: state.importData.guesstimatedDocsTotal,
310376
guesstimatedDocsProcessed: state.importData.guesstimatedDocsProcessed,
377+
analyzeBytesProcessed: state.importData.analyzeBytesProcessed,
378+
analyzeBytesTotal: state.importData.analyzeBytesTotal,
311379
delimiter: state.importData.delimiter,
312380
stopOnErrors: state.importData.stopOnErrors,
313381
ignoreBlanks: state.importData.ignoreBlanks,
@@ -323,6 +391,7 @@ const mapStateToProps = (state: RootImportState) => ({
323391
export default connect(mapStateToProps, {
324392
startImport,
325393
cancelImport,
394+
skipCSVAnalyze,
326395
selectImportFileName,
327396
setDelimiter,
328397
setStopOnErrors,

packages/compass-import-export/src/components/import-preview.spec.tsx

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ describe('ImportPreview [Component]', function () {
3232
loaded={false}
3333
onFieldCheckedChanged={onFieldCheckedChangedSpy}
3434
setFieldType={setFieldTypeSpy}
35-
analyzed={false}
3635
/>
3736
);
3837
});
@@ -51,7 +50,6 @@ describe('ImportPreview [Component]', function () {
5150
loaded
5251
onFieldCheckedChanged={onFieldCheckedChangedSpy}
5352
setFieldType={setFieldTypeSpy}
54-
analyzed={true}
5553
/>
5654
);
5755
});
@@ -70,7 +68,6 @@ describe('ImportPreview [Component]', function () {
7068
loaded
7169
onFieldCheckedChanged={onFieldCheckedChangedSpy}
7270
setFieldType={setFieldTypeSpy}
73-
analyzed={true}
7471
/>
7572
);
7673
});
@@ -95,7 +92,6 @@ describe('ImportPreview [Component]', function () {
9592
loaded
9693
onFieldCheckedChanged={onFieldCheckedChangedSpy}
9794
setFieldType={setFieldTypeSpy}
98-
analyzed={true}
9995
/>
10096
);
10197
});
@@ -105,7 +101,7 @@ describe('ImportPreview [Component]', function () {
105101
});
106102
});
107103

108-
it('renders placeholders when not analyzed', function () {
104+
it('renders field type selects', function () {
109105
render(
110106
<ImportPreview
111107
fields={[testField]}
@@ -119,31 +115,6 @@ describe('ImportPreview [Component]', function () {
119115
loaded
120116
onFieldCheckedChanged={onFieldCheckedChangedSpy}
121117
setFieldType={setFieldTypeSpy}
122-
analyzed={false}
123-
/>
124-
);
125-
126-
expect(screen.queryByTestId('import-preview-placeholder-_id')).to.be
127-
.visible;
128-
expect(screen.queryByTestId('import-preview-field-type-select-menu-_id')).to
129-
.not.exist;
130-
});
131-
132-
it('renders field type selects once analyzed', function () {
133-
render(
134-
<ImportPreview
135-
fields={[testField]}
136-
values={
137-
[
138-
{
139-
_id: 25,
140-
},
141-
] as any
142-
}
143-
loaded
144-
onFieldCheckedChanged={onFieldCheckedChangedSpy}
145-
setFieldType={setFieldTypeSpy}
146-
analyzed={true}
147118
/>
148119
);
149120

0 commit comments

Comments
 (0)