@cdc/core 4.25.11 → 4.26.1

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 (77) hide show
  1. package/_stories/Gallery.Charts.stories.tsx +307 -0
  2. package/_stories/Gallery.DataBite.stories.tsx +72 -0
  3. package/_stories/Gallery.Maps.stories.tsx +230 -0
  4. package/_stories/Gallery.WaffleChart.stories.tsx +187 -0
  5. package/_stories/PageART.stories.tsx +192 -0
  6. package/_stories/PageBRFSS.stories.tsx +289 -0
  7. package/_stories/PageCancerRegistries.stories.tsx +199 -0
  8. package/_stories/PageEasternEquineEncephalitis.stories.tsx +202 -0
  9. package/_stories/PageExcessiveAlcoholUse.stories.tsx +196 -0
  10. package/_stories/PageMaternalMortality.stories.tsx +192 -0
  11. package/_stories/PageOralHealth.stories.tsx +196 -0
  12. package/_stories/PageRespiratory.stories.tsx +332 -0
  13. package/_stories/PageSmokingTobacco.stories.tsx +195 -0
  14. package/_stories/PageStateDiabetesProfiles.stories.tsx +196 -0
  15. package/_stories/PageWastewater.stories.tsx +463 -0
  16. package/assets/icon-magnifying-glass.svg +5 -0
  17. package/assets/icon-warming-stripes.svg +13 -0
  18. package/components/AdvancedEditor/AdvancedEditor.tsx +4 -0
  19. package/components/AdvancedEditor/EmbedEditor.tsx +281 -0
  20. package/components/ComboBox/ComboBox.tsx +345 -0
  21. package/components/ComboBox/combobox.styles.css +185 -0
  22. package/components/ComboBox/index.ts +1 -0
  23. package/components/DataTable/DataTable.tsx +132 -58
  24. package/components/DataTable/data-table.css +216 -215
  25. package/components/DataTable/helpers/mapCellMatrix.tsx +14 -6
  26. package/components/EditorPanel/ColumnsEditor.tsx +37 -19
  27. package/components/EditorPanel/DataTableEditor.tsx +51 -25
  28. package/components/EditorPanel/EditorPanel.styles.css +16 -0
  29. package/components/EditorPanel/EditorPanel.tsx +144 -0
  30. package/components/EditorPanel/EditorPanelDispatch.tsx +75 -0
  31. package/components/EditorPanel/FieldSetWrapper.tsx +66 -23
  32. package/components/EditorPanel/Inputs.tsx +33 -7
  33. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +236 -175
  34. package/components/EditorPanel/sections/VisualSection.tsx +169 -0
  35. package/components/Filters/Filters.tsx +31 -5
  36. package/components/Filters/helpers/getNestedOptions.ts +2 -1
  37. package/components/Filters/helpers/handleSorting.ts +1 -1
  38. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +82 -0
  39. package/components/Layout/components/Visualization/index.tsx +16 -1
  40. package/components/Layout/components/Visualization/visualizations.scss +7 -0
  41. package/components/Legend/Legend.Gradient.tsx +1 -1
  42. package/components/MediaControls.tsx +53 -27
  43. package/components/ui/Icon.tsx +3 -1
  44. package/components/ui/Title/index.tsx +30 -2
  45. package/components/ui/Title/title.styles.css +42 -0
  46. package/dist/cove-main.css +26 -3
  47. package/dist/cove-main.css.map +1 -1
  48. package/generateViteConfig.js +8 -1
  49. package/helpers/addValuesToFilters.ts +6 -1
  50. package/helpers/coveUpdateWorker.ts +19 -12
  51. package/helpers/embedCodeGenerator.ts +109 -0
  52. package/helpers/getUniqueValues.ts +19 -0
  53. package/helpers/hashObj.ts +25 -0
  54. package/helpers/isRightAlignedTableValue.js +5 -0
  55. package/helpers/metrics/helpers.ts +1 -0
  56. package/helpers/pivotData.ts +2 -2
  57. package/helpers/prepareScreenshot.ts +268 -0
  58. package/helpers/queryStringUtils.ts +29 -0
  59. package/helpers/tests/prepareScreenshot.test.ts +414 -0
  60. package/helpers/tests/queryStringUtils.test.ts +381 -0
  61. package/helpers/tests/testStandaloneBuild.ts +23 -5
  62. package/helpers/useDataVizClasses.ts +0 -1
  63. package/helpers/ver/4.26.1.ts +80 -0
  64. package/hooks/useDataColumns.ts +63 -0
  65. package/hooks/useFilterManagement.ts +94 -0
  66. package/hooks/useLegendSeparators.ts +26 -0
  67. package/hooks/useListManagement.ts +192 -0
  68. package/package.json +4 -3
  69. package/styles/_button-section.scss +0 -3
  70. package/types/Axis.ts +1 -0
  71. package/types/ForecastingSeriesKey.ts +1 -0
  72. package/types/MarkupInclude.ts +1 -0
  73. package/types/Series.ts +3 -0
  74. package/types/Table.ts +1 -0
  75. package/types/Visualization.ts +1 -0
  76. package/types/VizFilter.ts +1 -0
  77. package/LICENSE +0 -201
@@ -1,284 +1,285 @@
1
- .table {
2
- width: unset;
3
- min-width: 100%;
4
- }
5
-
6
- .bs4 .table.table-width-unset {
7
- width: unset;
8
- }
9
-
10
- .collapsed+.table-container {
11
- border-bottom: none;
12
- }
13
-
14
- .table-container {
15
- overflow-x: auto;
16
- border-right: 1px solid var(--lightGray);
17
- border-left: 1px solid var(--lightGray);
18
- border-bottom: 1px solid var(--lightGray);
19
- }
20
-
21
- .cdc-open-viz-module div.data-table-heading {
22
- position: relative;
23
- border: var(--cool-gray-10) 1px solid;
24
- border-radius: 6px;
25
-
26
- svg {
27
- position: absolute;
28
- height: 100%;
29
- width: 15px;
30
- top: 0;
31
- right: 1em;
1
+ .cove,
2
+ .cdc-open-viz-module {
3
+ .table {
4
+ width: unset;
5
+ min-width: 100%;
32
6
  }
33
7
 
34
- &:focus {
35
- z-index: 2;
36
- position: relative;
8
+ .bs4 .table.table-width-unset {
9
+ width: unset;
37
10
  }
38
- }
39
11
 
40
- table.horizontal {
41
-
42
- th,
43
- td {
44
- min-width: 200px;
45
- }
46
- }
47
-
48
- table.data-table {
49
- margin-bottom: 0;
50
- background: #fff;
51
- position: relative;
52
- border: none;
53
- border-collapse: collapse;
54
- appearance: none;
55
- table-layout: fixed;
56
-
57
- * {
58
- box-sizing: border-box;
12
+ .collapsed+.table-container {
13
+ border-bottom: none;
59
14
  }
60
15
 
61
- thead {
62
- user-select: none;
63
- -moz-user-select: none;
64
- user-select: none;
65
-
66
- button {
67
- background: none;
68
- font-size: initial;
69
- color: #fff;
70
- border: 0;
71
- }
72
-
73
- tr {
74
- background: none;
75
- }
16
+ .table-container {
17
+ overflow-x: auto;
18
+ border-right: 1px solid var(--lightGray);
19
+ border-left: 1px solid var(--lightGray);
20
+ border-bottom: 1px solid var(--lightGray);
76
21
  }
77
22
 
78
- thead {
79
- color: #fff;
23
+ div.data-table-heading {
24
+ position: relative;
25
+ border: var(--cool-gray-10) 1px solid;
26
+ border-radius: 6px;
80
27
 
81
- .resizer {
82
- cursor: e-resize;
83
- width: 10px;
28
+ svg {
84
29
  position: absolute;
30
+ height: 100%;
31
+ width: 15px;
85
32
  top: 0;
86
- bottom: 0;
87
- right: 0;
88
- touch-action: none;
33
+ right: 1em;
89
34
  }
90
35
 
91
- tr {
92
- text-align: left;
36
+ &:focus {
37
+ z-index: 2;
38
+ position: relative;
93
39
  }
40
+ }
41
+
42
+ table.horizontal {
94
43
 
95
44
  th,
96
45
  td {
97
- padding: 0.5em 0.7em;
98
- line-height: normal;
99
- position: relative;
100
- text-align: left;
101
- border-right: 1px solid var(--lightGray) !important;
46
+ min-width: 200px;
102
47
  }
48
+ }
103
49
 
104
- th {
105
- background-color: var(--primary);
106
- background-repeat: no-repeat;
107
- background-position: right 0.5em center;
108
- background-size: 10px 5px;
109
- }
50
+ table.data-table {
51
+ margin-bottom: 0;
52
+ background: #fff;
53
+ position: relative;
54
+ border: none;
55
+ border-collapse: collapse;
56
+ appearance: none;
57
+ table-layout: fixed;
110
58
 
111
- th:last-child,
112
- td:last-child {
113
- border-right: 0;
59
+ * {
60
+ box-sizing: border-box;
114
61
  }
115
- }
116
62
 
117
- tr {
118
- &.row-group {
119
- background-color: var(--tertiary) !important;
120
- font-weight: bold;
121
- }
63
+ thead {
64
+ user-select: none;
65
+ -moz-user-select: none;
66
+ user-select: none;
122
67
 
123
- border-bottom: solid 1px #e5e5e5;
124
- min-width: 100%;
68
+ button {
69
+ background: none;
70
+ font-size: initial;
71
+ color: #fff !important;
72
+ border: 0;
73
+ }
125
74
 
126
- /* Needed to fill content up*/
127
- &:last-child {
128
- border-bottom: 0;
75
+ tr {
76
+ background: none;
77
+ }
129
78
  }
130
- }
131
79
 
132
- th,
133
- td {
134
- padding: 0.3em 0.7em;
135
- border-right: 1px solid rgba(0, 0, 0, 0.1);
136
- white-space: nowrap;
80
+ thead {
81
+ color: #fff !important;
82
+
83
+ .resizer {
84
+ cursor: e-resize;
85
+ width: 10px;
86
+ position: absolute;
87
+ top: 0;
88
+ bottom: 0;
89
+ right: 0;
90
+ touch-action: none;
91
+ }
137
92
 
138
- &:last-child {
139
- border-right: 0 !important;
93
+ tr {
94
+ text-align: left;
95
+ }
96
+
97
+ th,
98
+ td {
99
+ padding: 0.5em 0.7em;
100
+ line-height: normal;
101
+ position: relative;
102
+ text-align: left;
103
+ border-right: 1px solid var(--lightGray) !important;
104
+ }
105
+
106
+ th {
107
+ background-color: var(--primary);
108
+ background-repeat: no-repeat;
109
+ background-position: right 0.5em center;
110
+ background-size: 10px 5px;
111
+ color: #fff !important
112
+ }
113
+
114
+ th:last-child,
115
+ td:last-child {
116
+ border-right: 0;
117
+ }
140
118
  }
141
- }
142
119
 
143
- td {
144
- position: relative;
120
+ tr {
121
+ &.row-group {
122
+ background-color: var(--tertiary) !important;
123
+ font-weight: bold;
124
+ }
145
125
 
146
- svg {
147
- margin-left: 1rem;
126
+ border-bottom: solid 1px #e5e5e5;
127
+ min-width: 100%;
148
128
 
149
- &.legend-shape-svg {
150
- display: flex;
151
- margin-left: 0 !important;
129
+ /* Needed to fill content up*/
130
+ &:last-child {
131
+ border-bottom: 0;
152
132
  }
153
133
  }
154
134
 
155
- }
135
+ th,
136
+ td {
137
+ padding: 0.3em 0.7em;
138
+ border-right: 1px solid rgba(0, 0, 0, 0.1);
139
+ white-space: nowrap;
156
140
 
157
- td a {
158
- padding: 0.3em 0.7em;
159
- position: absolute;
160
- top: 0;
161
- bottom: 0;
162
- right: 0;
163
- left: 0;
164
- display: block;
165
- color: inherit;
166
- text-decoration: none;
167
- }
141
+ &:last-child {
142
+ border-right: 0 !important;
143
+ }
144
+ }
168
145
 
169
- td div a {
170
- position: relative;
171
- padding: 0;
172
- display: inline;
173
- }
146
+ td {
147
+ position: relative;
174
148
 
175
- td span.table-link {
176
- text-decoration: underline;
177
- cursor: pointer;
178
- color: #075290;
149
+ svg {
150
+ margin-left: 1rem;
151
+
152
+ &.legend-shape-svg {
153
+ display: flex;
154
+ margin-left: 0 !important;
155
+ }
156
+ }
179
157
 
180
- svg {
181
- max-width: 13px;
182
- vertical-align: baseline;
183
- margin-left: 5px;
184
158
  }
185
- }
186
159
 
187
- .boxplot-td {
188
- table-layout: fixed;
189
- width: 200;
190
- }
191
- }
192
-
193
- .no-data {
194
- position: relative;
195
-
196
- .no-data-message {
197
- background: rgba(255, 255, 255, 0.5);
198
- top: 0;
199
- left: 0;
200
- right: 0;
201
- bottom: 0;
202
- position: absolute;
203
- text-align: center;
204
- display: flex;
205
- align-items: center;
206
- justify-content: center;
207
- z-index: 7;
160
+ td a {
161
+ padding: 0.3em 0.7em;
162
+ position: absolute;
163
+ top: 0;
164
+ bottom: 0;
165
+ right: 0;
166
+ left: 0;
167
+ display: block;
168
+ color: inherit;
169
+ text-decoration: none;
170
+ }
208
171
 
209
- :is(h3) {
210
- font-size: 1.3rem;
211
- font-weight: 600;
212
- margin-bottom: 0.3rem;
172
+ td div a {
173
+ position: relative;
174
+ padding: 0;
175
+ display: inline;
213
176
  }
214
- }
215
177
 
216
- tr:hover {
217
- background: #fff;
218
- }
178
+ td span.table-link {
179
+ text-decoration: underline;
180
+ cursor: pointer;
181
+ color: #075290;
219
182
 
220
- th,
221
- td {
222
- width: 50%;
183
+ svg {
184
+ max-width: 13px;
185
+ vertical-align: baseline;
186
+ margin-left: 5px;
187
+ }
188
+ }
223
189
 
224
- &::before {
225
- content: '\00a0';
190
+ .boxplot-td {
191
+ table-layout: fixed;
192
+ width: 200;
226
193
  }
227
194
  }
228
- }
229
195
 
230
- .data-table-pagination {
231
- margin: 1rem 0;
232
- display: flex;
233
- align-items: center;
196
+ .no-data {
197
+ position: relative;
234
198
 
235
- ul {
236
- list-style: none;
237
- margin: 0 1rem 0 0;
238
- display: flex;
199
+ .no-data-message {
200
+ background: rgba(255, 255, 255, 0.5);
201
+ top: 0;
202
+ left: 0;
203
+ right: 0;
204
+ bottom: 0;
205
+ position: absolute;
206
+ text-align: center;
207
+ display: flex;
208
+ align-items: center;
209
+ justify-content: center;
210
+ z-index: 7;
211
+
212
+ :is(h3) {
213
+ font-size: 1.3rem;
214
+ font-weight: 600;
215
+ margin-bottom: 0.3rem;
216
+ }
217
+ }
239
218
 
240
- li+li {
241
- margin-left: 0.3rem;
219
+ tr:hover {
220
+ background: #fff;
242
221
  }
243
222
 
244
- button {
245
- background: var(--mediumGray);
223
+ th,
224
+ td {
225
+ width: 50%;
246
226
 
247
- &:hover {
248
- background: lighten(var(--mediumGray), 5%);
227
+ &::before {
228
+ content: '\00a0';
249
229
  }
250
230
  }
231
+ }
251
232
 
252
- button.btn svg {
253
- margin: 0px 0 3px 0;
254
- }
233
+ .data-table-pagination {
234
+ margin: 1rem 0;
235
+ display: flex;
236
+ align-items: center;
237
+
238
+ ul {
239
+ list-style: none;
240
+ margin: 0 1rem 0 0;
241
+ display: flex;
242
+
243
+ li+li {
244
+ margin-left: 0.3rem;
245
+ }
255
246
 
256
- button[disabled] {
257
- background: var(--mediumGray);
258
- opacity: 0.3;
259
- cursor: default;
247
+ button {
248
+ background: var(--mediumGray);
249
+
250
+ &:hover {
251
+ background: lighten(var(--mediumGray), 5%);
252
+ }
253
+ }
260
254
 
261
- &:hover {
255
+ button.btn svg {
256
+ margin: 0px 0 3px 0;
257
+ }
258
+
259
+ button[disabled] {
262
260
  background: var(--mediumGray);
261
+ opacity: 0.3;
262
+ cursor: default;
263
+
264
+ &:hover {
265
+ background: var(--mediumGray);
266
+ }
263
267
  }
264
268
  }
265
269
  }
266
- }
267
-
268
- .btn-download {
269
- color: #fff;
270
- float: right;
271
- text-decoration: none;
272
- transition: 0.3s all;
273
- margin: 1em 0;
274
270
 
275
- &:hover {
271
+ .btn-download {
272
+ color: #fff;
273
+ float: right;
274
+ text-decoration: none;
276
275
  transition: 0.3s all;
276
+ margin: 1em 0;
277
+
278
+ &:hover {
279
+ transition: 0.3s all;
280
+ }
277
281
  }
278
- }
279
282
 
280
- .cove,
281
- .cdc-open-viz-module {
282
283
  .download-links a:not(:last-child) {
283
284
  margin-right: 10px;
284
285
  display: inline-block;
@@ -4,12 +4,18 @@ import { DataTableProps } from '../DataTable'
4
4
  import { ReactNode } from 'react'
5
5
  import { displayDataAsText } from '@cdc/core/helpers/displayDataAsText'
6
6
  import _ from 'lodash'
7
- import { applyLegendToRow } from '@cdc/map/src/helpers/applyLegendToRow'
8
- import { hashObj } from '@cdc/map/src/helpers'
9
- import { getPatternForRow } from '@cdc/map/src/helpers/getPatternForRow'
7
+ import { hashObj } from '../../../helpers/hashObj'
10
8
 
11
9
  type MapRowsProps = DataTableProps & {
12
10
  rows: string[]
11
+ applyLegendToRow: (
12
+ rowObj: any,
13
+ config: any,
14
+ runtimeLegend: any,
15
+ legendMemo: any,
16
+ legendSpecialClassLastMemo: any
17
+ ) => string[]
18
+ getPatternForRow: (rowObj: any, config: any) => any
13
19
  }
14
20
 
15
21
  const getGeoLabel = (config, row, formatLegendLocation, displayGeoName, runtimeData = null) => {
@@ -92,7 +98,9 @@ const mapCellArray = ({
92
98
  setFilteredCountryCode,
93
99
  legendMemo,
94
100
  legendSpecialClassLastMemo,
95
- runtimeLegend
101
+ runtimeLegend,
102
+ applyLegendToRow,
103
+ getPatternForRow
96
104
  }: MapRowsProps): ReactNode[][] => {
97
105
  const { allowMapZoom, geoType, type } = config.general
98
106
  return rows.map(row =>
@@ -116,11 +124,11 @@ const mapCellArray = ({
116
124
  type === 'bubble' && allowMapZoom && geoType === 'world' ? () => setFilteredCountryCode(row) : undefined
117
125
 
118
126
  const validColor = legendColor && legendColor.length > 0 && !noColor
119
-
127
+
120
128
  // Check for pattern information
121
129
  const patternInfo = getPatternForRow(rowObj, config)
122
130
  const mapId = config.runtime?.uniqueId || 'map'
123
-
131
+
124
132
  return (
125
133
  <div className='col-12'>
126
134
  {validColor ? (
@@ -5,8 +5,10 @@ import { Visualization } from '../../types/Visualization'
5
5
  import { UpdateFieldFunc } from '../../types/UpdateFieldFunc'
6
6
  import { Column } from '../../types/Column'
7
7
  import _ from 'lodash'
8
- import React, { useState } from 'react'
8
+ import React, { useState, useMemo } from 'react'
9
9
  import FieldSetWrapper from './FieldSetWrapper'
10
+ import { useDataColumns } from '../../hooks/useDataColumns'
11
+ import Alert from '../Alert/components/Alert'
10
12
 
11
13
  interface ColumnsEditorProps {
12
14
  config: Partial<Visualization>
@@ -52,19 +54,21 @@ const FieldSet: React.FC<ColumnsEditorProps & { colKey: string; controls: OpenCo
52
54
  updateField(null, null, 'columns', newColumns)
53
55
  }
54
56
 
55
- const getColumns = () => {
56
- const columns: string[] = config.data.flatMap(row => {
57
- return Object.keys(row).map(columnName => columnName)
58
- })
57
+ // Extract column names from data with memoization (replaces getColumns)
58
+ const allColumns = useDataColumns(config.data)
59
+
60
+ // Filter out groupBy and already configured columns
61
+ const availableColumns = useMemo(() => {
59
62
  const configuredColumns = Object.values(config.columns).map(col => col.name)
60
- const cols = _.uniq(columns).filter(key => {
63
+ const cols = allColumns.filter(key => {
61
64
  if (config.table.groupBy === key) return false
62
65
  if (configuredColumns.includes(key)) return false
63
66
  return true
64
67
  })
68
+ // Add current column name if it exists
65
69
  if (config.columns[colKey]?.name) cols.push(config.columns[colKey].name)
66
70
  return cols
67
- }
71
+ }, [allColumns, config.table.groupBy, config.columns, colKey])
68
72
 
69
73
  const colName = config.columns[colKey]?.name
70
74
 
@@ -82,7 +86,7 @@ const FieldSet: React.FC<ColumnsEditorProps & { colKey: string; controls: OpenCo
82
86
  fieldName='name'
83
87
  section={'columns'}
84
88
  initial={'-Select-'}
85
- options={getColumns()}
89
+ options={availableColumns}
86
90
  updateField={(_section, _subsection, _fieldName, value) => changeName(value)}
87
91
  />
88
92
  {config.type !== 'table' && (
@@ -92,7 +96,11 @@ const FieldSet: React.FC<ColumnsEditorProps & { colKey: string; controls: OpenCo
92
96
  fieldName={'series'}
93
97
  section='columns'
94
98
  initial={'Select series'}
95
- options={config.series?.map(series => series.dataKey) || []}
99
+ options={
100
+ config.visualizationType === 'Pie'
101
+ ? config.runtime?.seriesKeys || []
102
+ : config.series?.map(series => series.dataKey) || []
103
+ }
96
104
  updateField={(_section, _subsection, _fieldName, value) => editColumn('series', value)}
97
105
  />
98
106
  )}
@@ -147,16 +155,26 @@ const FieldSet: React.FC<ColumnsEditorProps & { colKey: string; controls: OpenCo
147
155
  </li>
148
156
  <li>
149
157
  {config.table.showVertical && (
150
- <label className='checkbox'>
151
- <input
152
- type='checkbox'
153
- checked={config.columns[colKey].dataTable ?? true}
154
- onChange={event => {
155
- editColumn('dataTable', event.target.checked)
156
- }}
157
- />
158
- <span className='edit-label'>Show in Data Table</span>
159
- </label>
158
+ <>
159
+ <label className='checkbox'>
160
+ <input
161
+ type='checkbox'
162
+ checked={config.columns[colKey].dataTable ?? true}
163
+ onChange={event => {
164
+ editColumn('dataTable', event.target.checked)
165
+ }}
166
+ />
167
+ <span className='edit-label'>Show in Data Table</span>
168
+ </label>
169
+ {(config.confidenceKeys?.upper === colName || config.confidenceKeys?.lower === colName) && (
170
+ <Alert
171
+ type="danger"
172
+ message="Confidence Interval column - required for 508 compliance"
173
+ showCloseButton={false}
174
+ iconSize={14}
175
+ />
176
+ )}
177
+ </>
160
178
  )}
161
179
  </li>
162
180
  {config.visualizationType === 'Pie' && (