@cdc/editor 4.26.3 → 4.26.5

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.
Files changed (35) hide show
  1. package/dist/cdceditor-CY9IcPSi.es.js +6 -0
  2. package/dist/cdceditor-DlpiY3fQ.es.js +4 -0
  3. package/dist/cdceditor.js +78238 -74548
  4. package/example/private/dashboard-filter-issue/dashboard-filter-issue.json +957 -0
  5. package/package.json +9 -9
  6. package/src/_stories/Editor.stories.tsx +124 -0
  7. package/src/assets.d.ts +4 -0
  8. package/src/components/ChooseTab.test.tsx +64 -1
  9. package/src/components/ChooseTab.tsx +82 -4
  10. package/src/components/DataImport/components/DataImport.tsx +135 -50
  11. package/src/components/DataImport/components/SampleData.test.tsx +16 -0
  12. package/src/components/DataImport/components/SampleData.tsx +6 -0
  13. package/src/components/DataImport/components/samples/valid-heatmap-varicella-cases.csv +13 -0
  14. package/src/components/DataImport/helpers/applyAutoDetectedDateParseFormat.ts +35 -0
  15. package/src/components/DataImport/tests/applyAutoDetectedDateParseFormat.test.ts +128 -0
  16. package/src/components/PreviewDataTable.test.tsx +184 -0
  17. package/src/components/PreviewDataTable.tsx +18 -7
  18. package/src/components/modal/Confirmation.jsx +5 -4
  19. package/src/scss/choose-vis-tab.scss +7 -1
  20. package/src/scss/main.scss +14 -6
  21. package/dist/cdceditor-vr9HZwRt.es.js +0 -6
  22. package/example/data-horizontal-filters.json +0 -8
  23. package/example/data-horizontal-multiseries-filters.json +0 -18
  24. package/example/data-horizontal-multiseries.json +0 -6
  25. package/example/data-horizontal.json +0 -4
  26. package/example/data-vertical-filters.json +0 -10
  27. package/example/data-vertical-multiseries-filters.json +0 -18
  28. package/example/data-vertical-multiseries-multirow-filters.json +0 -50
  29. package/example/data-vertical-multiseries-multirow.json +0 -14
  30. package/example/data-vertical-multiseries.json +0 -6
  31. package/example/data-vertical.json +0 -6
  32. package/example/region-map.json +0 -33
  33. package/example/test.json +0 -110280
  34. package/example/valid-county-data.json +0 -3049
  35. package/example/valid-scatterplot.csv +0 -17
@@ -0,0 +1,184 @@
1
+ import React from 'react'
2
+ import { render, screen, waitFor } from '@testing-library/react'
3
+ import ConfigContext, { EditorDispatchContext } from '@cdc/core/contexts/EditorContext'
4
+ import PreviewDataTable from './PreviewDataTable'
5
+
6
+ describe('PreviewDataTable', () => {
7
+ const renderPreview = config => {
8
+ const dispatch = vi.fn()
9
+
10
+ return render(
11
+ <ConfigContext.Provider
12
+ value={
13
+ {
14
+ config,
15
+ errors: [],
16
+ currentViewport: 'lg',
17
+ globalActive: 1,
18
+ setTempConfig: vi.fn()
19
+ } as any
20
+ }
21
+ >
22
+ <EditorDispatchContext.Provider value={dispatch}>
23
+ <PreviewDataTable />
24
+ </EditorDispatchContext.Provider>
25
+ </ConfigContext.Provider>
26
+ )
27
+ }
28
+
29
+ it('updates the rendered preview rows when the dashboard preview source changes', async () => {
30
+ const initialConfig = {
31
+ type: 'dashboard',
32
+ datasets: {
33
+ source_a: {
34
+ data: [{ label: 'Old Source Row', value: 'A' }],
35
+ preview: true
36
+ },
37
+ source_b: {
38
+ data: [{ label: 'Secondary Row', value: 'B' }],
39
+ preview: false
40
+ }
41
+ }
42
+ }
43
+
44
+ const nextConfig = {
45
+ ...initialConfig,
46
+ datasets: {
47
+ source_b: {
48
+ data: [{ label: 'New Source Row', value: 'B2' }],
49
+ preview: true
50
+ },
51
+ source_a: {
52
+ data: [{ label: 'Old Source Row', value: 'A' }],
53
+ preview: false
54
+ }
55
+ }
56
+ }
57
+
58
+ const view = renderPreview(initialConfig)
59
+
60
+ expect(await screen.findByText('Old Source Row')).toBeInTheDocument()
61
+ expect(screen.queryByText('New Source Row')).not.toBeInTheDocument()
62
+
63
+ view.rerender(
64
+ <ConfigContext.Provider
65
+ value={
66
+ {
67
+ config: nextConfig,
68
+ errors: [],
69
+ currentViewport: 'lg',
70
+ globalActive: 1,
71
+ setTempConfig: vi.fn()
72
+ } as any
73
+ }
74
+ >
75
+ <EditorDispatchContext.Provider value={vi.fn()}>
76
+ <PreviewDataTable />
77
+ </EditorDispatchContext.Provider>
78
+ </ConfigContext.Provider>
79
+ )
80
+
81
+ await waitFor(() => {
82
+ expect(screen.getByText('New Source Row')).toBeInTheDocument()
83
+ })
84
+ expect(screen.queryByText('Old Source Row')).not.toBeInTheDocument()
85
+ })
86
+
87
+ it('shows another loaded dataset after the active preview dataset is removed', async () => {
88
+ const initialConfig = {
89
+ type: 'dashboard',
90
+ datasets: {
91
+ source_a: {
92
+ data: [{ label: 'Removed Source Row', value: 'A' }],
93
+ preview: true
94
+ },
95
+ source_b: {
96
+ data: [{ label: 'Fallback Source Row', value: 'B' }],
97
+ preview: false
98
+ }
99
+ }
100
+ }
101
+
102
+ const nextConfig = {
103
+ type: 'dashboard',
104
+ datasets: {
105
+ source_b: {
106
+ data: [{ label: 'Fallback Source Row', value: 'B' }],
107
+ preview: true
108
+ }
109
+ }
110
+ }
111
+
112
+ const view = renderPreview(initialConfig)
113
+
114
+ expect(await screen.findByText('Removed Source Row')).toBeInTheDocument()
115
+
116
+ view.rerender(
117
+ <ConfigContext.Provider
118
+ value={
119
+ {
120
+ config: nextConfig,
121
+ errors: [],
122
+ currentViewport: 'lg',
123
+ globalActive: 1,
124
+ setTempConfig: vi.fn()
125
+ } as any
126
+ }
127
+ >
128
+ <EditorDispatchContext.Provider value={vi.fn()}>
129
+ <PreviewDataTable />
130
+ </EditorDispatchContext.Provider>
131
+ </ConfigContext.Provider>
132
+ )
133
+
134
+ await waitFor(() => {
135
+ expect(screen.getByText('Fallback Source Row')).toBeInTheDocument()
136
+ })
137
+ expect(screen.queryByText('Removed Source Row')).not.toBeInTheDocument()
138
+ })
139
+
140
+ it('returns to the initial empty preview when the final dataset is removed', async () => {
141
+ const initialConfig = {
142
+ type: 'dashboard',
143
+ datasets: {
144
+ source_a: {
145
+ data: [{ label: 'Last Source Row', value: 'A' }],
146
+ preview: true
147
+ }
148
+ }
149
+ }
150
+
151
+ const nextConfig = {
152
+ type: 'dashboard',
153
+ datasets: {}
154
+ }
155
+
156
+ const view = renderPreview(initialConfig)
157
+
158
+ expect(await screen.findByText('Last Source Row')).toBeInTheDocument()
159
+
160
+ view.rerender(
161
+ <ConfigContext.Provider
162
+ value={
163
+ {
164
+ config: nextConfig,
165
+ errors: [],
166
+ currentViewport: 'lg',
167
+ globalActive: 1,
168
+ setTempConfig: vi.fn()
169
+ } as any
170
+ }
171
+ >
172
+ <EditorDispatchContext.Provider value={vi.fn()}>
173
+ <PreviewDataTable />
174
+ </EditorDispatchContext.Provider>
175
+ </ConfigContext.Provider>
176
+ )
177
+
178
+ await waitFor(() => {
179
+ expect(screen.getByText('No Data')).toBeInTheDocument()
180
+ })
181
+ expect(screen.getByText('Import data to preview')).toBeInTheDocument()
182
+ expect(screen.queryByText('Last Source Row')).not.toBeInTheDocument()
183
+ })
184
+ })
@@ -1,4 +1,4 @@
1
- import React, { useState, useContext, useMemo, useCallback, useEffect, useRef, memo } from 'react'
1
+ import React, { useState, useContext, useMemo, useEffect, useRef, memo } from 'react'
2
2
  import {
3
3
  useTable,
4
4
  useBlockLayout,
@@ -18,7 +18,7 @@ import { GrFormPrevious } from 'react-icons/gr'
18
18
  import validateFipsCodeLength from '@cdc/core/helpers/validateFipsCodeLength'
19
19
  import { errorMessages } from '../helpers/errorMessages'
20
20
  import { DataSet } from '@cdc/core/types/DataSet'
21
- import Icon from '@cdc/core/components/ui/Icon'
21
+ import Button from '@cdc/core/components/elements/Button'
22
22
 
23
23
  const TableFilter = memo(({ globalFilter, setGlobalFilter = () => {}, disabled = false }: any) => {
24
24
  const [filterValue, setFilterValue] = useState(globalFilter ?? '')
@@ -61,25 +61,27 @@ const Footer = memo(({ previousPage, nextPage, canPreviousPage, canNextPage, pag
61
61
  <footer className='data-table-pagination mt-2'>
62
62
  <ul>
63
63
  <li>
64
- <button
64
+ <Button
65
65
  onClick={() => previousPage()}
66
66
  className='btn btn-prev display-flex align-items-center justify-content-center'
67
67
  disabled={!canPreviousPage}
68
68
  title='Previous Page'
69
+ flexCenter
69
70
  >
70
71
  {' '}
71
72
  <GrFormPrevious />
72
- </button>
73
+ </Button>
73
74
  </li>
74
75
  <li className='me-2'>
75
- <button
76
+ <Button
76
77
  onClick={() => nextPage()}
77
78
  className='btn btn-next display-flex align-items-center justify-content-center'
78
79
  disabled={!canNextPage}
79
80
  title='Next Page'
81
+ flexCenter
80
82
  >
81
83
  <MdNavigateNext />
82
- </button>
84
+ </Button>
83
85
  </li>
84
86
  </ul>
85
87
  <span>
@@ -110,7 +112,12 @@ const PreviewDataTable = () => {
110
112
  return normalizedData
111
113
  }
112
114
  const setTableData = td => {
113
- if (!Array.isArray(td) || td.length === 0) return
115
+ if (!Array.isArray(td) || td.length === 0) {
116
+ // When the active dataset is removed, clear the cached source so the placeholder can render again.
117
+ lastDataSourceRef.current = null
118
+ _setTableData(null)
119
+ return
120
+ }
114
121
  if (lastDataSourceRef.current === td) return
115
122
 
116
123
  lastDataSourceRef.current = td
@@ -142,6 +149,10 @@ const PreviewDataTable = () => {
142
149
  const loadData = async () => {
143
150
  if (!config.data) {
144
151
  if (config.type === 'dashboard') {
152
+ if (!previewData) {
153
+ setTableData(null)
154
+ return
155
+ }
145
156
  await handleDashboardData(config.datasets)
146
157
  } else {
147
158
  if (config.dataUrl) {
@@ -1,16 +1,17 @@
1
1
  import React from 'react'
2
+ import Button from '@cdc/core/components/elements/Button'
2
3
 
3
4
  export const ConfirmationModal = props => {
4
5
  return (
5
6
  <>
6
7
  <p className='message'>{props.message}</p>
7
8
  <div className='confirmation-buttons'>
8
- <div className='btn btn-inline' onClick={props.onCancel}>
9
+ <Button type='button' className='btn btn-inline' onClick={props.onCancel}>
9
10
  No
10
- </div>
11
- <div className='btn btn-inline' onClick={props.onConfirm}>
11
+ </Button>
12
+ <Button type='button' className='btn btn-inline' onClick={props.onConfirm}>
12
13
  Yes
13
- </div>
14
+ </Button>
14
15
  </div>
15
16
  </>
16
17
  )
@@ -84,7 +84,8 @@
84
84
  }
85
85
  }
86
86
 
87
- svg {
87
+ svg,
88
+ .choose-vis__heatmap-icon {
88
89
  display: block;
89
90
  margin: 0 auto;
90
91
  box-sizing: border-box;
@@ -92,6 +93,11 @@
92
93
  height: 80px;
93
94
  flex-shrink: 0;
94
95
  }
96
+
97
+ .choose-vis__heatmap-icon {
98
+ object-fit: contain;
99
+ mix-blend-mode: multiply;
100
+ }
95
101
  }
96
102
  }
97
103
  }
@@ -31,7 +31,6 @@
31
31
  background: #f2f2f2;
32
32
  line-height: 3rem;
33
33
  border-bottom: 1px solid var(--lightGray);
34
- color: #333;
35
34
  flex-grow: 1;
36
35
  flex-shrink: 0;
37
36
  width: 25%;
@@ -202,9 +201,22 @@
202
201
  font-size: 1em;
203
202
  display: block;
204
203
  border-radius: 5px;
205
- transition: 0.1s all;
204
+ transition: background-color 160ms ease, box-shadow 160ms ease, transform 160ms ease;
206
205
  cursor: pointer;
207
206
 
207
+ @media (hover: hover) {
208
+ &:hover:not(:disabled) {
209
+ background: #0b4778;
210
+ transform: translateY(-1px);
211
+ box-shadow: 0 0.5rem 1.1rem rgb(0 94 170 / 22%);
212
+ }
213
+ }
214
+
215
+ &:active:not(:disabled) {
216
+ transform: translateY(0);
217
+ box-shadow: 0 0.15rem 0.45rem rgb(0 94 170 / 12%);
218
+ }
219
+
208
220
  &.btn-inline {
209
221
  display: inline-block;
210
222
  margin: 10px 0 0 10px;
@@ -212,10 +224,6 @@
212
224
  }
213
225
  }
214
226
 
215
- section.introText {
216
- padding: 15px 0;
217
- }
218
-
219
227
  section.footnotes {
220
228
  border-top: 1px solid #ddd;
221
229
  margin-top: 70px;