@cdc/editor 4.24.2 → 4.24.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,8 +48,6 @@ export default function DataImport() {
48
48
 
49
49
  const [editingDataset, setEditingDataset] = useState<string>(undefined)
50
50
 
51
- const [asyncPreviewData, setAsyncPreviewData] = useState<any[]>()
52
-
53
51
  const dispatch = useContext(EditorDispatchContext)
54
52
 
55
53
  const supportedDataTypes = {
@@ -89,6 +87,10 @@ export default function DataImport() {
89
87
  return true
90
88
  }
91
89
 
90
+ const getFileExtension = (url: URL) => {
91
+ return isSolrCsv(externalURL) ? '.csv' : isSolrJson(externalURL) ? '.json' : Object.keys(supportedDataTypes).find(extension => url.pathname.endsWith(extension))
92
+ }
93
+
92
94
  const loadExternal = async () => {
93
95
  let dataURL: URL
94
96
  // Is URL valid?
@@ -98,9 +100,9 @@ export default function DataImport() {
98
100
  } catch {
99
101
  throw errorMessages.urlInvalid
100
102
  }
101
- let responseBlob = null
103
+ let responseBlob: Blob = null
102
104
 
103
- const fileExtension = isSolrCsv(externalURL) ? '.csv' : isSolrJson(externalURL) ? '.json' : Object?.keys(supportedDataTypes).find(extension => dataURL.pathname.endsWith(extension))
105
+ const fileExtension = getFileExtension(dataURL)
104
106
  try {
105
107
  // eslint-disable-next-line no-unused-vars
106
108
  await axios
@@ -111,9 +113,10 @@ export default function DataImport() {
111
113
  responseBlob = response.data
112
114
 
113
115
  // Sometimes the files are coming in as plain text types... Maybe when saved from Macs
114
- if ((fileExtension === '.csv' && responseBlob.type === 'text/plain') || isSolrCsv(externalURL)) {
116
+ const csvTypes = ['text/csv', 'text/plain']
117
+ if ((fileExtension === '.csv' && csvTypes.includes(responseBlob.type)) || isSolrCsv(externalURL)) {
115
118
  responseBlob = responseBlob.slice(0, responseBlob.size, 'text/csv')
116
- } else if ((fileExtension === '.json' && responseBlob.type === 'text/plain') || isSolrJson(externalURL)) {
119
+ } else if (responseBlob.type === 'application/json' || (fileExtension === '.json' && responseBlob.type === 'text/plain') || isSolrJson(externalURL)) {
117
120
  responseBlob = responseBlob.slice(0, responseBlob.size, 'application/json')
118
121
  }
119
122
  })
@@ -139,22 +142,20 @@ export default function DataImport() {
139
142
  */
140
143
  const loadData = async (fileBlob = null, fileName, editingDatasetKey) => {
141
144
  let fileData = fileBlob
142
- let fileSource = fileData?.path ?? fileName ?? null
143
- let fileSourceType = 'file'
145
+ const fileSource = fileData?.path ?? fileName ?? externalURL
146
+ const fileSourceType = fileBlob ? 'file' : 'url'
144
147
 
145
148
  // Get the raw data as text from the file
146
- if (null === fileData) {
147
- fileSourceType = 'url'
149
+ if (fileSourceType === 'url') {
148
150
  try {
149
151
  fileData = await loadExternal()
150
- fileSource = externalURL
151
152
  } catch (error) {
152
153
  setErrors([error])
153
154
  return
154
155
  }
155
156
  }
156
157
 
157
- let fileSize = fileData.size
158
+ const fileSize = fileData.size
158
159
 
159
160
  // Check if file is too big
160
161
  if (fileSize > maxFileSize * 1048576) {
@@ -162,105 +163,92 @@ export default function DataImport() {
162
163
  return
163
164
  }
164
165
 
165
- let path = fileBlob?.name || externalURL || fileName
166
-
167
166
  // checking the file source type allows us to handle real urls better
168
167
  // For example, query parameters in API's and cache busting strings
169
168
  // file matching can handle .csv and .json, but doesn't handle
170
169
  // .csv?version=1 or .json?version=1
171
- const handleFileExtension = fileSourceType => {
172
- let fileExtension
170
+ const getMimeType = () => {
171
+ const path = fileBlob?.name || externalURL || fileName
173
172
  if (fileSourceType === 'file') {
174
- fileExtension = path.match(/(?:\.([^.]+))?$/g)
175
-
176
- if (fileExtension.length === 0) {
177
- fileExtension = '.csv'
178
- } else {
179
- fileExtension = fileExtension[0]
180
- }
173
+ const pathMatch = path.match(/(?:\.([^.]+))?$/g)
174
+ const ext = pathMatch.length === 0 ? '.csv' : pathMatch[0]
175
+ return supportedDataTypes[ext]
181
176
  }
182
177
 
183
178
  if (fileSourceType === 'url') {
184
- let urlData = new URL(path, window.location.origin)
185
- fileExtension = isSolrCsv(externalURL) ? '.csv' : isSolrJson(externalURL) ? '.json' : Object.keys(supportedDataTypes).find(extension => urlData.pathname.endsWith(extension))
179
+ return fileData.type
186
180
  }
187
-
188
- return fileExtension
189
181
  }
190
182
 
191
- let fileExtension = handleFileExtension(fileSourceType)
192
- let mimeType = supportedDataTypes[fileExtension]
183
+ const mimeType = getMimeType()
193
184
 
194
185
  // Convert from blob into raw text
195
186
  // Have to use FileReader instead of just .text because IE11 and the polyfills for this are bugged
196
- let filereader = new FileReader()
197
-
198
- // Set encoding for CSV files - needed to render special characters properly
199
- let encoding = mimeType === 'text/csv' ? 'utf-8' : ''
200
-
201
- filereader.onload = function () {
202
- let text = this.result
187
+ const filereader = new FileReader()
203
188
 
189
+ const getText = resultText => {
204
190
  switch (mimeType) {
205
191
  case 'text/csv':
206
- text = csvParse(text)
207
- break
192
+ return csvParse(resultText)
208
193
  case 'text/plain':
209
194
  case 'application/json':
210
195
  try {
211
- text = isSolrJson(externalURL) ? JSON.parse(text).response.docs : JSON.parse(text)
196
+ return isSolrJson(externalURL) ? JSON.parse(resultText).response.docs : JSON.parse(resultText)
212
197
  } catch (errors) {
213
198
  setErrors([errorMessages.formatting])
214
199
  return
215
200
  }
216
- break
217
201
  default:
218
202
  setErrors([errorMessages.fileType])
219
203
  return
220
204
  }
205
+ }
206
+
207
+ filereader.onload = function () {
208
+ const handleSetConfig = (text: string, useTempConfig = false) => {
209
+ if (config.type === 'dashboard') {
210
+ let newDatasets = { ...config.datasets }
211
+
212
+ Object.keys(newDatasets).forEach(datasetKey => (newDatasets[datasetKey].preview = false))
213
+ const dataFileFormat = mimeType.split('/')[1].toUpperCase()
214
+ newDatasets[editingDatasetKey || fileSource] = {
215
+ data: text, // new data
216
+ dataFileSize: fileSize,
217
+ dataFileName: fileSource, // new file source
218
+ dataFileSourceType: fileSourceType, // new file source type
219
+ dataFileFormat,
220
+ preview: true
221
+ }
222
+
223
+ if (keepURL) {
224
+ newDatasets[editingDatasetKey || fileSource].dataUrl = fileSource
225
+ }
226
+
227
+ const conf = useTempConfig ? { ...config, ...tempConfig } : config
228
+ setConfig({ ...conf, datasets: newDatasets })
229
+ } else {
230
+ let newConfig = {
231
+ ...config,
232
+ ...tempConfig,
233
+ data: text, // new data
234
+ dataFileName: fileSource, // new file source
235
+ dataFileSourceType: fileSourceType, // new file source type
236
+ formattedData: transform.developerStandardize(text, config.dataDescription)
237
+ }
238
+ if (keepURL) {
239
+ newConfig.dataUrl = fileSource
240
+ }
241
+ setConfig(newConfig)
242
+ }
243
+ }
221
244
 
222
245
  // Validate parsed data and set if no issues.
223
246
  try {
224
- text = transform.autoStandardize(text)
247
+ const result = getText(this.result.toString())
248
+ const text = transform.autoStandardize(result)
225
249
  if (config.data && config.series) {
226
250
  if (dataExists(text, config.series, config?.xAxis.dataKey)) {
227
- if (config.type === 'dashboard') {
228
- let newDatasets = { ...config.datasets }
229
-
230
- Object.keys(newDatasets).forEach(datasetKey => (newDatasets[datasetKey].preview = false))
231
-
232
- newDatasets[editingDatasetKey || fileSource] = {
233
- data: text, // new data
234
- dataFileSize: fileSize,
235
- dataFileName: fileSource, // new file source
236
- dataFileSourceType: fileSourceType, // new file source type
237
- dataFileFormat: fileExtension.replace('.', '').toUpperCase(),
238
- preview: true
239
- }
240
-
241
- if (keepURL) {
242
- newDatasets[editingDatasetKey || fileSource].dataUrl = fileSource
243
- }
244
-
245
- setConfig({
246
- ...config,
247
- ...tempConfig,
248
- dataset: newDatasets
249
- })
250
- } else {
251
- let newConfig = {
252
- ...config,
253
- ...tempConfig,
254
- data: text, // new data
255
- dataFileName: fileSource, // new file source
256
- dataFileSourceType: fileSourceType, // new file source type
257
- formattedData: transform.developerStandardize(text, config.dataDescription)
258
- }
259
- if (keepURL) {
260
- newConfig.dataUrl = fileSource
261
- }
262
- setConfig(newConfig)
263
- }
251
+ handleSetConfig(text, true)
264
252
  } else {
265
253
  resetEditor(
266
254
  {
@@ -272,38 +260,7 @@ export default function DataImport() {
272
260
  )
273
261
  }
274
262
  } else {
275
- if (config.type === 'dashboard') {
276
- let newDatasets = { ...config.datasets }
277
- Object.keys(newDatasets).forEach(datasetKey => (newDatasets[datasetKey].preview = false))
278
-
279
- newDatasets[editingDatasetKey || fileSource] = {
280
- data: text, // new data
281
- dataFileSize: fileSize,
282
- dataFileName: fileSource, // new file source
283
- dataFileSourceType: fileSourceType, // new file source type
284
- dataFileFormat: fileExtension.replace('.', '').toUpperCase(),
285
- preview: true
286
- }
287
-
288
- if (keepURL) {
289
- newDatasets[editingDatasetKey || fileSource].dataUrl = fileSource
290
- }
291
-
292
- setConfig({ ...config, datasets: newDatasets })
293
- } else {
294
- let newConfig = {
295
- ...config,
296
- ...tempConfig,
297
- data: text, // new data
298
- dataFileName: fileSource, // new file source
299
- dataFileSourceType: fileSourceType, // new file source type
300
- formattedData: transform.developerStandardize(text, config.dataDescription) // new file source type
301
- }
302
- if (keepURL) {
303
- newConfig.dataUrl = fileSource
304
- }
305
- setConfig(newConfig)
306
- }
263
+ handleSetConfig(text)
307
264
  }
308
265
 
309
266
  if (editingDataset) {
@@ -314,6 +271,8 @@ export default function DataImport() {
314
271
  setErrors(err)
315
272
  }
316
273
  }
274
+ // Set encoding for CSV files - needed to render special characters properly
275
+ const encoding = mimeType === 'text/csv' ? 'utf-8' : ''
317
276
  filereader.readAsText(fileData, encoding)
318
277
  }
319
278
 
@@ -329,36 +288,8 @@ export default function DataImport() {
329
288
  dispatch({ type: 'EDITOR_SAVE', payload: newConfig })
330
289
  }, []) // eslint-disable-line
331
290
 
332
- useEffect(() => {
333
- const asyncWrapper = async () => {
334
- if (config.type === 'dashboard') {
335
- Object.keys(config.datasets).forEach(async datasetKey => {
336
- if (config.datasets[datasetKey].preview) {
337
- if (config.datasets[datasetKey].dataUrl) {
338
- const remoteData = await fetchRemoteData(config.datasets[datasetKey].dataUrl)
339
- if (Array.isArray(remoteData)) {
340
- setAsyncPreviewData(remoteData)
341
- }
342
- } else if (Array.isArray(config.datasets[datasetKey].data)) {
343
- setAsyncPreviewData(config.datasets[datasetKey].data)
344
- }
345
- }
346
- })
347
- } else {
348
- if (config.dataUrl) {
349
- const remoteData = await fetchRemoteData(config.dataUrl)
350
- if (Array.isArray(remoteData)) {
351
- setAsyncPreviewData(remoteData)
352
- }
353
- }
354
- }
355
- }
356
-
357
- asyncWrapper()
358
- }, [config.datasets]) // eslint-disable-line
359
-
360
291
  // todo: code repetition in Widget.jsx?
361
- const updateDescriptionProp = (visualizationKey, datasetKey, key, value) => {
292
+ const updateDescriptionProp = (datasetKey, key, value) => {
362
293
  if (config.type === 'dashboard') {
363
294
  let dataDescription = { ...config.datasets[datasetKey].dataDescription, [key]: value }
364
295
  let formattedData = transform.developerStandardize(config.datasets[datasetKey].data, dataDescription)
@@ -399,19 +330,17 @@ export default function DataImport() {
399
330
  const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop })
400
331
  const { getRootProps: getRootProps2, getInputProps: getInputProps2, isDragActive: isDragActive2 } = useDropzone({ onDrop })
401
332
 
402
- const loadFileFromUrl = (url, editingDatasetKey) => {
403
- // const extUrl = (url) ? url : config.dataFileName // set url to what is saved in config unless the user has entered something
404
-
333
+ const loadDataFromUrl = () => {
405
334
  return (
406
335
  <>
407
336
  <form className='input-group d-flex' onSubmit={e => e.preventDefault()}>
408
337
  <input id='external-data' type='text' className='form-control flex-grow-1 border-right-0' placeholder='e.g., https://data.cdc.gov/resources/file.json' aria-label='Load data from external URL' aria-describedby='load-data' value={externalURL} onChange={e => setExternalURL(e.target.value)} />
409
- <button className='input-group-text btn btn-primary px-4' type='submit' id='load-data' onClick={() => loadData(null, externalURL, editingDatasetKey)}>
338
+ <button className='input-group-text btn btn-primary px-4' type='submit' id='load-data' onClick={() => loadData(null, externalURL, editingDataset)}>
410
339
  Load
411
340
  </button>
412
341
  </form>
413
342
  <label htmlFor='keep-url' className='mt-1 d-flex keep-url'>
414
- <input type='checkbox' id='keep-url' checked={keepURL} onChange={() => changeKeepURL(!keepURL, editingDatasetKey)} /> Always load from URL (normally will only pull once)
343
+ <input type='checkbox' id='keep-url' checked={keepURL} onChange={() => changeKeepURL(!keepURL, editingDataset)} /> Always load from URL (normally will only pull once)
415
344
  </label>
416
345
  </>
417
346
  )
@@ -453,12 +382,9 @@ export default function DataImport() {
453
382
  let newDatasets = { ...config.datasets }
454
383
 
455
384
  if (value === true) {
456
- Object.keys(newDatasets).forEach(datasetKeyIter => {
457
- if (datasetKeyIter !== datasetKey) {
458
- newDatasets[datasetKeyIter][prop] = false
459
- } else {
460
- newDatasets[datasetKeyIter][prop] = true
461
- }
385
+ Object.keys(newDatasets).forEach(key => {
386
+ const match = key === datasetKey
387
+ newDatasets[key][prop] = match
462
388
  })
463
389
  } else {
464
390
  newDatasets[datasetKey][prop] = value
@@ -507,18 +433,11 @@ export default function DataImport() {
507
433
  setConfig({ ...config, datasets: newDatasets, visualizations: newVisualizations })
508
434
  }
509
435
 
510
- let previewData,
511
- configureData,
436
+ let configureData,
512
437
  readyToConfigure = false
513
438
  if (config.type === 'dashboard') {
514
439
  readyToConfigure = Object.keys(config.datasets).length > 0
515
- Object.keys(config.datasets).forEach(datasetKey => {
516
- if (config.datasets[datasetKey].preview && Array.isArray(config.datasets[datasetKey].data)) {
517
- previewData = config.datasets[datasetKey].data
518
- }
519
- })
520
440
  } else {
521
- previewData = config.data
522
441
  configureData = config
523
442
  readyToConfigure = !!config.formattedData || (config.data && config.dataDescription && transform.autoStandardize(config.data))
524
443
  }
@@ -725,7 +644,7 @@ export default function DataImport() {
725
644
  </>
726
645
  )}
727
646
 
728
- {configureData && configureData.data && (
647
+ {configureData?.data && (
729
648
  <>
730
649
  {config.type !== 'dashboard' && (
731
650
  <>
@@ -750,7 +669,7 @@ export default function DataImport() {
750
669
  {config.dataFileSourceType === 'url' && (
751
670
  <>
752
671
  <div className='url-source-options'>
753
- <div>{loadFileFromUrl(externalURL, editingDataset)}</div>
672
+ <div>{loadDataFromUrl()}</div>
754
673
  <div>{resetButton()}</div>
755
674
  </div>
756
675
  {config.dataUrl && (config.type === 'chart' || config.type === 'map') && urlFilters}
@@ -760,7 +679,7 @@ export default function DataImport() {
760
679
  </>
761
680
  )}
762
681
 
763
- {showDataDesigner && <DataDesigner visualizationKey={null} dataKey={configureData.dataFileName} configureData={configureData} updateDescriptionProp={updateDescriptionProp} config={config} setConfig={setConfig} />}
682
+ {showDataDesigner && <DataDesigner visualizationKey={null} configureData={configureData} updateDescriptionProp={(key, value) => updateDescriptionProp(configureData.dataFileName, key, value)} config={config} setConfig={setConfig} />}
764
683
  </>
765
684
  )}
766
685
 
@@ -780,9 +699,12 @@ export default function DataImport() {
780
699
  </p>
781
700
  )}
782
701
  </div>
702
+ <p className='footnote'>
703
+ Supported file types: {Object.keys(supportedDataTypes).join(', ')}. Maximum file size {maxFileSize}MB.
704
+ </p>
783
705
  </TabPane>
784
706
  <TabPane title='Load from URL' icon={<LinkIcon className='inline-icon' />}>
785
- {loadFileFromUrl(editingDataset && config.datasets[editingDataset].dataFileSourceType === 'url' ? config.datasets[editingDataset].dataFileName : externalURL, editingDataset)}
707
+ {loadDataFromUrl()}
786
708
  </TabPane>
787
709
  </Tabs>
788
710
  {errors &&
@@ -793,9 +715,6 @@ export default function DataImport() {
793
715
  </div>
794
716
  ))
795
717
  : errors.message)}
796
- <p className='footnote'>
797
- Supported file types: {Object.keys(supportedDataTypes).join(', ')}. Maximum file size {maxFileSize}MB.
798
- </p>
799
718
 
800
719
  {/* prettier-ignore */}
801
720
  <SampleDataContext.Provider value={{ loadData, editingDataset, config }}>
@@ -828,7 +747,7 @@ export default function DataImport() {
828
747
  </a>
829
748
  </div>
830
749
  <div className='right-col'>
831
- <PreviewDataTable data={asyncPreviewData || previewData} />
750
+ <PreviewDataTable />
832
751
  </div>
833
752
  </>
834
753
  )
@@ -2,10 +2,12 @@ import React, { useState, useContext, useMemo, useCallback, useEffect, memo } fr
2
2
  import { useTable, useBlockLayout, useGlobalFilter, useSortBy, useResizeColumns, usePagination } from 'react-table'
3
3
  import ConfigContext, { EditorDispatchContext } from '../ConfigContext'
4
4
  import { useDebounce } from 'use-debounce'
5
+ import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
5
6
 
6
7
  // Core
7
8
  import validateFipsCodeLength from '@cdc/core/helpers/validateFipsCodeLength'
8
9
  import { errorMessages } from '../helpers/errorMessages'
10
+ import { DataSet } from '@cdc/dashboard/src/types/DataSet'
9
11
 
10
12
  const TableFilter = memo(({ globalFilter, setGlobalFilter, disabled = false }: any) => {
11
13
  const [filterValue, setFilterValue] = useState(globalFilter)
@@ -48,11 +50,61 @@ const Footer = memo(({ previousPage, nextPage, canPreviousPage, canNextPage, pag
48
50
  </footer>
49
51
  ))
50
52
 
51
- const PreviewDataTable = ({ data }) => {
52
- const [tableData, setTableData] = useState(data ?? [])
53
+ const PreviewDataTable = () => {
54
+ const { config } = useContext(ConfigContext)
55
+ const previewData = useMemo(() => {
56
+ if (config.type === 'dashboard') {
57
+ return Object.values(config.datasets).find((dataset: DataSet) => {
58
+ return dataset.preview && Array.isArray(dataset.data)
59
+ })
60
+ }
61
+ return config.data
62
+ }, [config.type, config.data, config.datasets])
63
+ const [tableData, _setTableData] = useState(previewData)
64
+ const runSideEffects = (td: any[]) => {
65
+ const isSankey = Object.keys(td[0]).includes('tableData')
66
+ const newData = generateColumns(isSankey ? td[0].tableData : td)
67
+ validateFipsCodeLength(newData)
68
+ return newData
69
+ }
70
+ const setTableData = td => _setTableData(runSideEffects(td))
71
+
53
72
  const dispatch = useContext(EditorDispatchContext)
54
73
 
74
+ const fetchAsyncData = async () => {
75
+ if (config.type === 'dashboard') {
76
+ Object.keys(config.datasets).forEach(async datasetKey => {
77
+ if (config.datasets[datasetKey].preview) {
78
+ if (config.datasets[datasetKey].dataUrl) {
79
+ const remoteData = await fetchRemoteData(config.datasets[datasetKey].dataUrl)
80
+ if (Array.isArray(remoteData)) {
81
+ setTableData(remoteData)
82
+ }
83
+ } else if (Array.isArray(config.datasets[datasetKey].data)) {
84
+ setTableData(config.datasets[datasetKey].data)
85
+ }
86
+ }
87
+ })
88
+ } else {
89
+ if (config.dataUrl) {
90
+ const remoteData = await fetchRemoteData(config.dataUrl)
91
+ if (Array.isArray(remoteData)) {
92
+ setTableData(remoteData)
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ useEffect(() => {
99
+ if (!config.data) {
100
+ fetchAsyncData()
101
+ } else {
102
+ setTableData(previewData)
103
+ }
104
+ }, [config.data]) // eslint-disable-line
105
+
55
106
  const tableColumns = useMemo(() => {
107
+ if (!tableData) return []
56
108
  const columns = tableData.columns ?? []
57
109
  if (columns.length > 0 && columns.includes('')) {
58
110
  // todo find a way to call the errors. Currently they are in DataImport.js
@@ -60,7 +112,7 @@ const PreviewDataTable = ({ data }) => {
60
112
  dispatch({ type: 'EDITOR_SET_ERRORS', payload: [errorMessages.emptyCols] })
61
113
  }
62
114
 
63
- return columns.map((columnName, idx) => {
115
+ return columns.map(columnName => {
64
116
  const columnConfig = {
65
117
  id: `column-${columnName}`,
66
118
  accessor: row => row[columnName],
@@ -73,7 +125,7 @@ const PreviewDataTable = ({ data }) => {
73
125
  }, [tableData])
74
126
 
75
127
  // This adds a columns property just like the D3 function for JSON parsing.
76
- const generateColumns = useCallback(data => {
128
+ const generateColumns = data => {
77
129
  let columns = []
78
130
 
79
131
  data.forEach(rowObj => {
@@ -92,19 +144,7 @@ const PreviewDataTable = ({ data }) => {
92
144
  newData.columns = columns
93
145
  return newData
94
146
  }
95
- }, [])
96
-
97
- useEffect(() => {
98
- if (!data) {
99
- return
100
- }
101
-
102
- let newData = [...data]
103
-
104
- newData = generateColumns(newData)
105
- validateFipsCodeLength(newData)
106
- setTableData(newData)
107
- }, [data, generateColumns])
147
+ }
108
148
 
109
149
  const {
110
150
  getTableProps,
@@ -121,79 +161,34 @@ const PreviewDataTable = ({ data }) => {
121
161
  previousPage
122
162
  } = useTable({ columns: tableColumns, data: tableData, initialState: { pageSize: 25 } }, useBlockLayout, useGlobalFilter, useSortBy, useResizeColumns, usePagination)
123
163
 
124
- const NoData = () => (
125
- <section className='no-data-message'>
126
- <section>
127
- <h3>No Data</h3>
128
- <p>Import data to preview</p>
129
- </section>
130
- </section>
131
- )
132
-
133
164
  const PlaceholderTable = () => {
134
165
  return (
135
166
  <section className='no-data'>
136
- <NoData />
167
+ <section className='no-data-message'>
168
+ <section>
169
+ <h3>No Data</h3>
170
+ <p>Import data to preview</p>
171
+ </section>
172
+ </section>
137
173
  <div className='table-container'>
138
174
  <table className='editor data-table' role='table'>
139
175
  <thead>
140
- <tr role='row'>
176
+ <tr>
141
177
  <th scope='col' colSpan={1} role='columnheader'></th>
142
178
  <th scope='col' colSpan={1} role='columnheader'></th>
143
179
  <th scope='col' colSpan={1} role='columnheader'></th>
144
180
  </tr>
145
181
  </thead>
146
182
  <tbody>
147
- <tr role='row'>
148
- <td role='cell'></td>
149
- <td role='cell'></td>
150
- <td role='cell'></td>
151
- </tr>
152
- <tr role='row'>
153
- <td role='cell'></td>
154
- <td role='cell'></td>
155
- <td role='cell'></td>
156
- </tr>
157
- <tr role='row'>
158
- <td role='cell'></td>
159
- <td role='cell'></td>
160
- <td role='cell'></td>
161
- </tr>
162
- <tr role='row'>
163
- <td role='cell'></td>
164
- <td role='cell'></td>
165
- <td role='cell'></td>
166
- </tr>
167
- <tr role='row'>
168
- <td role='cell'></td>
169
- <td role='cell'></td>
170
- <td role='cell'></td>
171
- </tr>
172
- <tr role='row'>
173
- <td role='cell'></td>
174
- <td role='cell'></td>
175
- <td role='cell'></td>
176
- </tr>
177
- <tr role='row'>
178
- <td role='cell'></td>
179
- <td role='cell'></td>
180
- <td role='cell'></td>
181
- </tr>
182
- <tr role='row'>
183
- <td role='cell'></td>
184
- <td role='cell'></td>
185
- <td role='cell'></td>
186
- </tr>
187
- <tr role='row'>
188
- <td role='cell'></td>
189
- <td role='cell'></td>
190
- <td role='cell'></td>
191
- </tr>
192
- <tr role='row'>
193
- <td role='cell'></td>
194
- <td role='cell'></td>
195
- <td role='cell'></td>
196
- </tr>
183
+ <>
184
+ {[...Array(10)].map((_, i) => (
185
+ <tr key={i}>
186
+ <td></td>
187
+ <td></td>
188
+ <td></td>
189
+ </tr>
190
+ ))}
191
+ </>
197
192
  </tbody>
198
193
  </table>
199
194
  </div>
@@ -201,7 +196,7 @@ const PreviewDataTable = ({ data }) => {
201
196
  )
202
197
  }
203
198
 
204
- if (!data) return [<Header key='header' />, <PlaceholderTable key='table' />]
199
+ if (!tableData) return [<Header key='header' />, <PlaceholderTable key='table' />]
205
200
 
206
201
  const footerProps = { previousPage, nextPage, canPreviousPage, canNextPage, pageNumber: pageIndex + 1, totalPages: pageOptions.length }
207
202
 
@@ -243,7 +238,7 @@ const PreviewDataTable = ({ data }) => {
243
238
  </>
244
239
  )
245
240
 
246
- return [<Header key='header' data={data} setGlobalFilter={setGlobalFilter} globalFilter={globalFilter} />, <Table key='table' />]
241
+ return [<Header key='header' data={tableData} setGlobalFilter={setGlobalFilter} globalFilter={globalFilter} />, <Table key='table' />]
247
242
  }
248
243
 
249
244
  export default PreviewDataTable