@cdc/core 4.26.2 → 4.26.3

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 (186) hide show
  1. package/LICENSE +201 -0
  2. package/_stories/Gallery.Charts.stories.tsx +1 -1
  3. package/_stories/Gallery.DataBite.stories.tsx +1 -1
  4. package/_stories/Gallery.Maps.stories.tsx +1 -1
  5. package/_stories/PageART.stories.tsx +1 -1
  6. package/_stories/PageBRFSS.stories.tsx +1 -1
  7. package/_stories/PageCancerRegistries.stories.tsx +1 -1
  8. package/_stories/PageEasternEquineEncephalitis.stories.tsx +3 -3
  9. package/_stories/PageExcessiveAlcoholUse.stories.tsx +1 -1
  10. package/_stories/PageMaternalMortality.stories.tsx +1 -1
  11. package/_stories/PageOralHealth.stories.tsx +1 -1
  12. package/_stories/PageRespiratory.stories.tsx +4 -4
  13. package/_stories/PageSmokingTobacco.stories.tsx +1 -1
  14. package/_stories/PageStateDiabetesProfiles.stories.tsx +1 -1
  15. package/_stories/PageWastewater.stories.tsx +4 -4
  16. package/_stories/VegaImport.stories.tsx +3 -3
  17. package/assets/callout-flag.svg +7 -0
  18. package/components/AdvancedEditor/EmbedEditor.tsx +1 -1
  19. package/components/Alert/components/Alert.styles.css +2 -2
  20. package/components/ComboBox/combobox.styles.css +48 -48
  21. package/components/CustomColorsEditor/CustomColorsEditor.css +53 -53
  22. package/components/DataTable/DataTable.tsx +46 -18
  23. package/components/DataTable/DataTableStandAlone.tsx +1 -0
  24. package/components/DataTable/components/ChartHeader.tsx +21 -12
  25. package/components/DataTable/components/MapHeader.tsx +34 -28
  26. package/components/DataTable/components/SortIcon/sort-icon.css +5 -5
  27. package/components/DataTable/data-table.css +50 -52
  28. package/components/DataTable/helpers/applyCustomOrder.ts +17 -0
  29. package/components/DataTable/helpers/getChartCellValue.ts +10 -7
  30. package/components/DataTable/helpers/getMapDataTableColumnKeys.ts +22 -0
  31. package/components/DataTable/helpers/mapCellMatrix.tsx +33 -23
  32. package/components/DataTable/helpers/tests/mapCellMatrix.test.ts +33 -0
  33. package/components/DownloadButton.tsx +14 -6
  34. package/components/EditorPanel/ColumnsEditor.tsx +38 -31
  35. package/components/EditorPanel/CustomSortOrder.tsx +94 -0
  36. package/components/EditorPanel/DataTableEditor.tsx +139 -23
  37. package/components/EditorPanel/EditorPanel.styles.css +71 -71
  38. package/components/EditorPanel/EditorPanel.tsx +3 -8
  39. package/components/EditorPanel/EditorPanelDispatch.tsx +4 -4
  40. package/components/EditorPanel/FootnotesEditor.tsx +2 -2
  41. package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +7 -6
  42. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +12 -10
  43. package/components/EditorPanel/components/MarkupVariablesEditor.tsx +160 -106
  44. package/components/EditorPanel/components/PanelMarkup.tsx +5 -1
  45. package/{styles/v2/components → components/EditorPanel}/editor.scss +67 -13
  46. package/components/EditorPanel/sections/StyleTreatmentSection.tsx +99 -0
  47. package/components/EditorPanel/sections/VisualSection.tsx +11 -0
  48. package/components/EditorWrapper/editor-wrapper.style.css +1 -1
  49. package/components/Filters/Filters.tsx +3 -5
  50. package/components/Filters/components/Tabs.tsx +19 -7
  51. package/{styles → components/Filters}/filters.scss +3 -3
  52. package/components/Footnotes/FootnotesStandAlone.tsx +4 -2
  53. package/components/HeaderThemeSelector/HeaderThemeSelector.css +61 -5
  54. package/components/Layout/components/Responsive.tsx +14 -6
  55. package/components/Layout/components/Sidebar/components/Sidebar.tsx +1 -1
  56. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +12 -18
  57. package/components/Layout/components/Visualization/index.tsx +39 -38
  58. package/components/Layout/components/Visualization/visualizations.scss +232 -15
  59. package/components/Layout/components/VisualizationContainer.test.tsx +67 -0
  60. package/components/Layout/components/VisualizationContainer.tsx +37 -0
  61. package/components/Layout/components/VisualizationContent.test.tsx +182 -0
  62. package/components/Layout/components/VisualizationContent.tsx +75 -0
  63. package/components/Layout/index.tsx +5 -5
  64. package/components/Layout/styles/editor-utils.scss +3 -3
  65. package/components/Layout/styles/editor.scss +4 -4
  66. package/components/Legend/Legend.Gradient.tsx +7 -1
  67. package/components/Loader/loader.styles.css +2 -2
  68. package/components/Loading.jsx +1 -1
  69. package/components/MediaControls.tsx +10 -2
  70. package/components/MultiSelect/multiselect.styles.css +19 -19
  71. package/components/NestedDropdown/nesteddropdown.styles.css +15 -15
  72. package/components/PaletteSelector/PaletteSelector.css +15 -15
  73. package/components/RichTooltip/richTooltip.css +6 -6
  74. package/components/Table/table.styles.css +2 -2
  75. package/components/Waiting.tsx +1 -1
  76. package/components/_stories/Filters.stories.tsx +1 -1
  77. package/components/_stories/styles.scss +0 -1
  78. package/components/elements/Button.jsx +1 -1
  79. package/components/elements/Card.jsx +1 -1
  80. package/{styles/v2/components → components/elements}/button.scss +9 -8
  81. package/components/inputs/InputCheckbox.jsx +1 -1
  82. package/components/inputs/InputSelect.tsx +1 -1
  83. package/components/inputs/InputText.jsx +1 -1
  84. package/components/inputs/InputToggle.tsx +1 -1
  85. package/{styles/v2/components/input → components/inputs}/_input-check-radio.scss +2 -2
  86. package/{styles/v2/components/input → components/inputs}/_input-group.scss +3 -3
  87. package/{styles/v2/components/input → components/inputs}/_input-slider.scss +2 -2
  88. package/{styles/v2/components/input → components/inputs}/_input.scss +5 -5
  89. package/{styles/v2/components/input → components/inputs}/index.scss +2 -2
  90. package/{styles → components}/loading.scss +1 -1
  91. package/components/managers/DataDesigner.tsx +1 -1
  92. package/{styles/v2/components → components/managers}/data-designer.scss +6 -7
  93. package/components/ui/Accordion.jsx +1 -1
  94. package/components/ui/Icon.tsx +1 -1
  95. package/components/ui/LoadSpin.jsx +1 -1
  96. package/components/ui/Modal.jsx +1 -1
  97. package/components/ui/Overlay.jsx +1 -1
  98. package/components/ui/Title/index.test.tsx +34 -0
  99. package/components/ui/Title/index.tsx +24 -7
  100. package/components/ui/Title/title.styles.css +119 -25
  101. package/components/ui/Tooltip.tsx +1 -1
  102. package/components/ui/_stories/Title.stories.tsx +1 -1
  103. package/{styles/v2/components → components/ui}/accordion.scss +3 -3
  104. package/components/ui/accordion.styles.css +11 -11
  105. package/{styles/v2/components → components/ui}/modal.scss +2 -2
  106. package/{styles/v2/components → components/ui}/overlay.scss +6 -6
  107. package/{styles/v2/components → components}/ui/tooltip.scss +1 -1
  108. package/{styles → components}/waiting.scss +9 -3
  109. package/devTemplate/dev.js +50 -0
  110. package/dist/cove-main.css +528 -231
  111. package/dist/cove-main.css.map +1 -1
  112. package/generateViteConfig.js +2 -2
  113. package/helpers/backfillDefaults.ts +35 -0
  114. package/helpers/constants.ts +12 -0
  115. package/helpers/cove/date.ts +32 -3
  116. package/helpers/cove/number.ts +29 -15
  117. package/helpers/coveUpdateWorker.ts +12 -8
  118. package/helpers/displayDataAsText.ts +1 -1
  119. package/helpers/embed/embedHelper.js +13 -2
  120. package/helpers/embed/index.ts +0 -4
  121. package/helpers/extractDataAndMetadata.ts +20 -0
  122. package/helpers/fetchRemoteData.ts +14 -8
  123. package/helpers/labelHash.ts +9 -0
  124. package/helpers/markupProcessor.ts +56 -38
  125. package/helpers/prepareScreenshot.ts +6 -3
  126. package/helpers/testing.ts +1 -1
  127. package/helpers/tests/abbreviateNumber.test.ts +59 -0
  128. package/helpers/tests/backfillDefaults.test.ts +253 -0
  129. package/helpers/tests/date.test.ts +46 -0
  130. package/helpers/tests/extractDataAndMetadata.test.ts +93 -0
  131. package/helpers/tests/markupProcessor.test.ts +315 -124
  132. package/helpers/tests/number.test.ts +42 -0
  133. package/helpers/tests/prepareScreenshot.test.ts +28 -28
  134. package/helpers/tests/testStandaloneBuild.ts +36 -26
  135. package/helpers/tests/useDataVizClasses.test.ts +66 -0
  136. package/helpers/tests/visualizationWrapperUsage.test.ts +57 -0
  137. package/helpers/useDataVizClasses.ts +13 -7
  138. package/helpers/ver/4.24.4.ts +24 -0
  139. package/helpers/ver/4.26.3.ts +44 -0
  140. package/helpers/ver/4.26.4.ts +31 -0
  141. package/helpers/ver/tests/4.26.3.test.ts +168 -0
  142. package/helpers/ver/tests/4.26.4.test.ts +88 -0
  143. package/helpers/ver/tests/coveUpdateWorker.test.ts +57 -0
  144. package/package.json +2 -2
  145. package/styles/_global.scss +7 -7
  146. package/styles/_reset.scss +2 -2
  147. package/styles/{v2/base → base}/_file-selector.scss +4 -4
  148. package/styles/{v2/base → base}/_general.scss +2 -4
  149. package/styles/{v2/base → base}/index.scss +1 -1
  150. package/styles/base.scss +107 -165
  151. package/styles/cove-main.scss +3 -6
  152. package/styles/layout/_component.scss +110 -0
  153. package/styles/{v2/layout → layout}/_data-table.scss +7 -7
  154. package/styles/layout/_wrapper-padding.scss +27 -0
  155. package/styles/{v2/main.scss → main.scss} +3 -1
  156. package/styles/{v2/themes → themes}/_color-definitions.scss +46 -41
  157. package/styles/{_accessibility.scss → utils/_accessibility.scss} +1 -1
  158. package/styles/{_global-variables.scss → utils/_properties.scss} +133 -112
  159. package/styles/{v2/utils → utils}/index.scss +2 -1
  160. package/types/Axis.ts +2 -0
  161. package/types/ComponentStyles.ts +1 -0
  162. package/types/ConfigureData.ts +1 -0
  163. package/types/MarkupInclude.ts +1 -0
  164. package/types/MarkupVariable.ts +2 -1
  165. package/types/Palette.ts +1 -0
  166. package/types/Table.ts +9 -0
  167. package/types/Visualization.ts +1 -0
  168. package/styles/_common-components.css +0 -73
  169. package/styles/_variables.scss +0 -63
  170. package/styles/v2/layout/_component.scss +0 -21
  171. package/styles/v2/utils/_variables.scss +0 -9
  172. package/{styles/v2/components/card.scss → components/elements/card.css} +2 -2
  173. /package/{styles/v2/components → components/ui}/icon.scss +0 -0
  174. /package/{styles/v2/components → components/ui}/loadspin.scss +0 -0
  175. /package/styles/{v2/base → base}/_heading.scss +0 -0
  176. /package/styles/{v2/base → base}/_reset.scss +0 -0
  177. /package/styles/{v2/layout → layout}/_alert.scss +0 -0
  178. /package/styles/{v2/layout → layout}/_progression.scss +0 -0
  179. /package/styles/{v2/layout → layout}/_tooltip.scss +0 -0
  180. /package/styles/{v2/layout → layout}/index.scss +0 -0
  181. /package/styles/{v2/themes → themes}/index.scss +0 -0
  182. /package/styles/{v2/utils → utils}/_align.scss +0 -0
  183. /package/styles/{v2/utils → utils}/_animations.scss +0 -0
  184. /package/styles/{v2/utils → utils}/_breakpoints.scss +0 -0
  185. /package/styles/{v2/utils → utils}/_grid.scss +0 -0
  186. /package/styles/{v2/utils → utils}/_mixins.scss +0 -0
@@ -0,0 +1,94 @@
1
+ import { useCallback, useMemo } from 'react'
2
+ import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
3
+ import _ from 'lodash'
4
+
5
+ type CustomSortOrderProps = {
6
+ column: string
7
+ data: Record<string, any>[]
8
+ customOrder?: string[]
9
+ updateField: Function
10
+ /** Optional transform for display labels (e.g. displayGeoName for maps) */
11
+ displayTransform?: (value: string) => string
12
+ }
13
+
14
+ /**
15
+ * Editor component for drag-and-drop custom sort ordering.
16
+ * Shows unique values from the selected column and allows reordering via DnD.
17
+ */
18
+ const CustomSortOrder: React.FC<CustomSortOrderProps> = ({
19
+ column,
20
+ data,
21
+ customOrder,
22
+ updateField,
23
+ displayTransform
24
+ }) => {
25
+ // Compute unique values from the selected column
26
+ const uniqueValues = useMemo(() => {
27
+ if (!column || !data?.length) return []
28
+ const values = data.map(row => String(row[column] ?? '')).filter(v => v !== '')
29
+ return _.uniq(values)
30
+ }, [column, data])
31
+
32
+ // Use customOrder if set, otherwise fall back to natural unique order
33
+ const orderedValues = useMemo(() => {
34
+ if (customOrder?.length) {
35
+ // Include any new values from data that aren't in customOrder
36
+ const extra = uniqueValues.filter(v => !customOrder.includes(v))
37
+ return [...customOrder.filter(v => uniqueValues.includes(v)), ...extra]
38
+ }
39
+ return uniqueValues
40
+ }, [customOrder, uniqueValues])
41
+
42
+ const handleDragEnd = useCallback(
43
+ ({ source, destination }) => {
44
+ if (!destination || source.index === destination.index) return
45
+ const reordered = [...orderedValues]
46
+ const [moved] = reordered.splice(source.index, 1)
47
+ reordered.splice(destination.index, 0, moved)
48
+ updateField('table', 'defaultSort', 'customOrder', reordered)
49
+ },
50
+ [orderedValues, updateField]
51
+ )
52
+
53
+ if (!orderedValues.length) return null
54
+
55
+ return (
56
+ <div>
57
+ <DragDropContext onDragEnd={handleDragEnd}>
58
+ <Droppable droppableId='custom_sort_order'>
59
+ {provided => (
60
+ <ul
61
+ {...provided.droppableProps}
62
+ className='sort-list'
63
+ ref={provided.innerRef}
64
+ style={{ marginTop: '0.5em', paddingLeft: 0, listStyle: 'none' }}
65
+ >
66
+ {orderedValues.map((value, index) => (
67
+ <Draggable key={value} draggableId={`customSort-${value}`} index={index}>
68
+ {(provided, snapshot) => (
69
+ <li style={{ marginBottom: '2px' }}>
70
+ <div
71
+ className={snapshot.isDragging ? 'currently-dragging' : ''}
72
+ style={{
73
+ ...provided.draggableProps.style
74
+ }}
75
+ ref={provided.innerRef}
76
+ {...provided.draggableProps}
77
+ {...provided.dragHandleProps}
78
+ >
79
+ {displayTransform ? displayTransform(value) : value}
80
+ </div>
81
+ </li>
82
+ )}
83
+ </Draggable>
84
+ ))}
85
+ {provided.placeholder}
86
+ </ul>
87
+ )}
88
+ </Droppable>
89
+ </DragDropContext>
90
+ </div>
91
+ )
92
+ }
93
+
94
+ export default CustomSortOrder
@@ -7,6 +7,7 @@ import { UpdateFieldFunc } from '../../types/UpdateFieldFunc'
7
7
  import { Visualization } from '../../types/Visualization'
8
8
  import _ from 'lodash'
9
9
  import { Column } from '../../types/Column'
10
+ import CustomSortOrder from './CustomSortOrder'
10
11
 
11
12
  interface DataTableProps {
12
13
  config: Partial<Visualization>
@@ -27,7 +28,7 @@ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDash
27
28
  }, [config.columns])
28
29
 
29
30
  const groupPivotColumns = useMemo(() => {
30
- const columns: string[] = config.data.flatMap(Object.keys)
31
+ const columns: string[] = (config.data ?? []).flatMap(Object.keys)
31
32
  const cols = _.uniq(columns).filter(key => {
32
33
  return true
33
34
  })
@@ -39,6 +40,56 @@ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDash
39
40
  updateField('table', null, 'groupBy', value)
40
41
  }
41
42
 
43
+ const changeSortColumn = (value: string) => {
44
+ if (value === '' || value === PLACEHOLDER) {
45
+ // Clear entire defaultSort when column is deselected
46
+ updateField('table', null, 'defaultSort', {})
47
+ return
48
+ }
49
+ // Set column and reset to ascending, clear custom order
50
+ updateField('table', null, 'defaultSort', { column: value, sortDirection: 'asc' })
51
+ }
52
+
53
+ const changeSortDirection = (value: string) => {
54
+ const newDefaultSort = { ...config.table.defaultSort, sortDirection: value }
55
+ // Clear customOrder when switching away from custom
56
+ if (value !== 'custom') {
57
+ delete newDefaultSort.customOrder
58
+ } else if (!newDefaultSort.customOrder?.length) {
59
+ // Auto-populate customOrder with unique values so the table updates immediately
60
+ const col = newDefaultSort.column
61
+ const dataCol = config.type === 'map' && config.columns?.[col]?.name ? config.columns[col].name : col
62
+ if (dataCol && config.data?.length) {
63
+ newDefaultSort.customOrder = _.uniq(config.data.map(row => String(row[dataCol] ?? '')).filter(v => v !== ''))
64
+ }
65
+ }
66
+ updateField('table', null, 'defaultSort', newDefaultSort)
67
+ }
68
+
69
+ // Build sort column options based on visualization type
70
+ // Maps use config.columns keys (geo, primary, etc.); other types use data column names
71
+ const sortColumnOptions = useMemo(() => {
72
+ if (config.type === 'map' && config.columns) {
73
+ return Object.keys(config.columns)
74
+ .filter(key => config.columns[key].dataTable !== false && config.columns[key].name)
75
+ .map(key => ({
76
+ label: config.columns[key].label || config.columns[key].name || key,
77
+ value: key
78
+ }))
79
+ }
80
+ return dataColumns.map(col => ({ label: col, value: col }))
81
+ }, [config.type, config.columns, dataColumns])
82
+
83
+ // For custom sort, resolve the actual data column name to get unique values
84
+ const customSortDataColumn = useMemo(() => {
85
+ const col = config.table?.defaultSort?.column
86
+ if (!col) return ''
87
+ if (config.type === 'map' && config.columns?.[col]?.name) {
88
+ return config.columns[col].name
89
+ }
90
+ return col
91
+ }, [config.type, config.columns, config.table?.defaultSort?.column])
92
+
42
93
  const excludeColumns = (section, subSection, fieldName, excludedColNames: string[]) => {
43
94
  const newColumns = _.cloneDeep(config.columns)
44
95
 
@@ -234,6 +285,49 @@ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDash
234
285
  updateField={updateField}
235
286
  />
236
287
  )}
288
+ <Select
289
+ value={config.table.defaultSort?.column || ''}
290
+ fieldName='column'
291
+ section='table'
292
+ subsection='defaultSort'
293
+ label='Default Sort Column'
294
+ initial={PLACEHOLDER}
295
+ options={sortColumnOptions}
296
+ updateField={(_section, _subSection, _fieldName, value) => changeSortColumn(value)}
297
+ tooltip={
298
+ <Tooltip style={{ textTransform: 'none' }}>
299
+ <Tooltip.Target>
300
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
301
+ </Tooltip.Target>
302
+ <Tooltip.Content>
303
+ <p>Choose a column to sort the data table by when it first loads.</p>
304
+ </Tooltip.Content>
305
+ </Tooltip>
306
+ }
307
+ />
308
+ {config.table.defaultSort?.column && (
309
+ <Select
310
+ value={config.table.defaultSort?.sortDirection || 'asc'}
311
+ fieldName='sortDirection'
312
+ section='table'
313
+ subsection='defaultSort'
314
+ label='Sort Direction'
315
+ options={[
316
+ { label: 'Ascending', value: 'asc' },
317
+ { label: 'Descending', value: 'desc' },
318
+ { label: 'Custom', value: 'custom' }
319
+ ]}
320
+ updateField={(_section, _subSection, _fieldName, value) => changeSortDirection(value)}
321
+ />
322
+ )}
323
+ {config.table.defaultSort?.column && config.table.defaultSort?.sortDirection === 'custom' && (
324
+ <CustomSortOrder
325
+ column={customSortDataColumn}
326
+ data={config.data}
327
+ customOrder={config.table.defaultSort.customOrder}
328
+ updateField={updateField}
329
+ />
330
+ )}
237
331
  <CheckBox
238
332
  value={config.table.download}
239
333
  fieldName='download'
@@ -259,6 +353,16 @@ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDash
259
353
  section='table'
260
354
  updateField={updateField}
261
355
  />
356
+ <div className='ms-4 mt-2' style={{ maxWidth: 'calc(100% - 1.5rem)' }}>
357
+ <TextField
358
+ value={config.table.downloadDataLabel}
359
+ section='table'
360
+ fieldName='downloadDataLabel'
361
+ label='Download Data Link Text'
362
+ placeholder='Download Data (CSV)'
363
+ updateField={updateField}
364
+ />
365
+ </div>
262
366
  </>
263
367
  )}
264
368
  {isDashboard && config.type !== 'table' && (
@@ -289,28 +393,40 @@ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDash
289
393
  />
290
394
  )}
291
395
  {config.type !== 'table' && config.table.showDownloadImgButton && (
292
- <CheckBox
293
- value={config.table.includeContextInDownload}
294
- fieldName='includeContextInDownload'
295
- className='ms-4'
296
- label='Include Heading & Context'
297
- section='table'
298
- updateField={updateField}
299
- tooltip={
300
- <Tooltip style={{ textTransform: 'none' }}>
301
- <Tooltip.Target>
302
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
303
- </Tooltip.Target>
304
- <Tooltip.Content>
305
- <p>
306
- When enabled, the image download will include the section heading (H2 or H3) and any explanatory
307
- paragraphs that appear immediately before the visualization. Be sure to test the image download on the
308
- published page to ensure the correct context is included.
309
- </p>
310
- </Tooltip.Content>
311
- </Tooltip>
312
- }
313
- />
396
+ <>
397
+ <CheckBox
398
+ value={config.table.includeContextInDownload}
399
+ fieldName='includeContextInDownload'
400
+ className='ms-4'
401
+ label='Include Heading & Context'
402
+ section='table'
403
+ updateField={updateField}
404
+ tooltip={
405
+ <Tooltip style={{ textTransform: 'none' }}>
406
+ <Tooltip.Target>
407
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
408
+ </Tooltip.Target>
409
+ <Tooltip.Content>
410
+ <p>
411
+ When enabled, the image download will include the section heading (H2 or H3) and any explanatory
412
+ paragraphs that appear immediately before the visualization. Be sure to test the image download on
413
+ the published page to ensure the correct context is included.
414
+ </p>
415
+ </Tooltip.Content>
416
+ </Tooltip>
417
+ }
418
+ />
419
+ <div className='ms-4 mt-2' style={{ maxWidth: 'calc(100% - 1.5rem)' }}>
420
+ <TextField
421
+ value={config.table.downloadImageLabel}
422
+ section='table'
423
+ fieldName='downloadImageLabel'
424
+ label='Download Image Link Text'
425
+ placeholder={`Download ${config.type === 'map' ? 'Map' : 'Chart'} (PNG)`}
426
+ updateField={updateField}
427
+ />
428
+ </div>
429
+ </>
314
430
  )}
315
431
  <label>
316
432
  <span className='edit-label column-heading'>Table Cell Min Width</span>
@@ -3,9 +3,9 @@
3
3
 
4
4
  /* Color palette in editor panel context */
5
5
  .editor-panel .header .color-palette li {
6
- width: 1.5em;
7
- height: 1.5em;
8
6
  display: inline-block;
7
+ height: 1.5em;
8
+ width: 1.5em;
9
9
  }
10
10
 
11
11
  .editor-panel .color-palette {
@@ -13,12 +13,12 @@
13
13
  }
14
14
 
15
15
  .editor-panel .color-palette li {
16
- width: 1.5em;
17
- height: 1.5em;
16
+ border: rgba(0, 0, 0, 0.3) 3px solid;
17
+ cursor: pointer;
18
18
  display: inline-block;
19
+ height: 1.5em;
19
20
  margin-right: 0.5em;
20
- cursor: pointer;
21
- border: rgba(0, 0, 0, 0.3) 3px solid;
21
+ width: 1.5em;
22
22
  }
23
23
 
24
24
  .editor-panel .color-palette li.active {
@@ -26,24 +26,24 @@
26
26
  }
27
27
 
28
28
  .editor-panel .color-palette a {
29
- display: inline-block;
30
29
  border-bottom: 1px solid rgba(0, 0, 0, 0.8);
30
+ display: inline-block;
31
31
  }
32
32
 
33
33
  .editor-panel .color-palette.series-list {
34
+ border: none;
34
35
  flex-direction: column;
35
36
  padding: 0;
36
- border: none;
37
37
  }
38
38
 
39
39
  .editor-panel .color-palette.series-list li {
40
- padding: 0.3em 0.5em;
41
- display: flex;
42
40
  align-items: center;
41
+ border: 0;
42
+ display: flex;
43
+ height: auto;
43
44
  justify-content: space-between;
45
+ padding: 0.3em 0.5em;
44
46
  width: auto;
45
- height: auto;
46
- border: 0;
47
47
  }
48
48
 
49
49
  .editor-panel .color-palette.series-list li:not(:last-child) {
@@ -61,10 +61,10 @@
61
61
  }
62
62
 
63
63
  .editor-panel .guidance-link svg {
64
- width: 60px;
65
64
  color: var(--blue);
66
- margin-right: 1rem;
67
65
  height: 60px;
66
+ margin-right: 1rem;
67
+ width: 60px;
68
68
  }
69
69
 
70
70
  .editor-panel .guidance-link svg path {
@@ -72,18 +72,18 @@
72
72
  }
73
73
 
74
74
  .editor-panel .warning {
75
- color: #d8000c;
76
75
  background-color: #ffd2d2;
77
- padding: 0.75em 1em;
78
- margin: 1em 0;
79
- font-size: 0.8em;
80
76
  border: #d8000c 1px solid;
81
77
  border-radius: 0.4em;
78
+ color: #d8000c;
79
+ font-size: 0.8em;
80
+ margin: 1em 0;
81
+ padding: 0.75em 1em;
82
82
  }
83
83
 
84
84
  .editor-panel .warning strong {
85
- font-weight: 600;
86
85
  display: block;
86
+ font-weight: 600;
87
87
  }
88
88
 
89
89
  .editor-panel .advanced {
@@ -96,18 +96,18 @@
96
96
  }
97
97
 
98
98
  .editor-panel .advanced .advanced-toggle-link {
99
- padding-top: 1em;
99
+ cursor: pointer;
100
100
  display: block;
101
+ padding-top: 1em;
101
102
  text-align: left;
102
- cursor: pointer;
103
103
  text-decoration: underline;
104
104
  }
105
105
 
106
106
  .editor-panel .advanced .advanced-toggle-link span {
107
- text-decoration: none;
108
107
  display: inline-block;
109
108
  font-family: monospace;
110
109
  padding-right: 5px;
110
+ text-decoration: none;
111
111
  }
112
112
 
113
113
  .editor-panel .advanced .advanced-toggle-link:hover {
@@ -115,29 +115,29 @@
115
115
  }
116
116
 
117
117
  .editor-panel .advanced textarea {
118
- height: 400px;
119
- width: 100%;
118
+ box-sizing: border-box;
119
+ font-family: monospace;
120
120
  font-size: 0.9em;
121
+ height: 400px;
121
122
  padding: 0.5em;
122
- font-family: monospace;
123
- box-sizing: border-box;
123
+ width: 100%;
124
124
  }
125
125
 
126
126
  .editor-panel .heading-2 {
127
127
  background: #565656;
128
+ border-bottom: #565656 3px solid;
128
129
  color: #fff;
129
130
  font-size: 1.1em;
130
131
  padding: 0.6em 1em;
131
132
  position: relative;
132
- border-bottom: #565656 3px solid;
133
133
  z-index: 3;
134
134
  }
135
135
 
136
136
  .editor-panel label {
137
- text-transform: uppercase;
138
137
  display: block;
139
138
  font-size: 0.8em;
140
139
  font-weight: 600;
140
+ text-transform: uppercase;
141
141
  }
142
142
 
143
143
  .editor-panel label:not(:first-child) {
@@ -145,8 +145,8 @@
145
145
  }
146
146
 
147
147
  .editor-panel label span.edit-label {
148
- margin-bottom: 0.3em;
149
148
  display: block;
149
+ margin-bottom: 0.3em;
150
150
  }
151
151
 
152
152
  .editor-panel label span.column-heading {
@@ -158,25 +158,25 @@
158
158
  }
159
159
 
160
160
  .editor-panel label.checkbox span {
161
- text-transform: none;
162
161
  font-size: 1em;
163
162
  font-weight: 400;
163
+ text-transform: none;
164
164
  }
165
165
 
166
166
  .editor-panel label.checkbox input {
167
- margin-top: 0;
168
167
  margin-right: 0.5em;
168
+ margin-top: 0;
169
169
  }
170
170
 
171
171
  .editor-panel input[type='checkbox'],
172
172
  .editor-panel input[type='radio'] {
173
+ cursor: pointer;
173
174
  display: inline-block;
174
- width: auto !important;
175
175
  height: auto !important;
176
- padding: 0;
177
176
  margin-right: 0.5em;
178
- cursor: pointer;
177
+ padding: 0;
179
178
  user-select: none;
179
+ width: auto !important;
180
180
  }
181
181
 
182
182
  .editor-panel input[type='text'],
@@ -193,8 +193,8 @@
193
193
 
194
194
  .editor-panel .primary-fieldset {
195
195
  border-top: rgba(0, 0, 0, 0.3) 2px solid;
196
- padding-top: 2em;
197
196
  margin-top: 2em;
197
+ padding-top: 2em;
198
198
  }
199
199
 
200
200
  .editor-panel ul.column-edit {
@@ -223,28 +223,28 @@
223
223
 
224
224
  .editor-panel .edit-block {
225
225
  border: 1px solid rgba(0, 0, 0, 0.3);
226
- padding: 1em;
227
226
  margin-top: 1em;
227
+ padding: 1em;
228
228
  position: relative;
229
229
  }
230
230
 
231
231
  .editor-panel .edit-block .remove-column {
232
- position: absolute;
233
- top: 0;
234
- right: 0;
235
- border: 0;
236
232
  background: rgba(0, 0, 0, 0.1);
233
+ border: 0;
237
234
  color: #000;
235
+ cursor: pointer;
238
236
  font-size: 0.8em;
239
237
  padding: 0.3em;
240
- cursor: pointer;
238
+ position: absolute;
239
+ right: 0;
240
+ top: 0;
241
241
  }
242
242
 
243
243
  .editor-panel span.subtext {
244
- text-transform: none;
245
244
  display: block;
246
245
  font-size: 0.8em;
247
246
  font-weight: 400;
247
+ text-transform: none;
248
248
  }
249
249
 
250
250
  .editor-panel .sort-list {
@@ -252,12 +252,12 @@
252
252
  }
253
253
 
254
254
  .editor-panel .sort-list li {
255
- border: 1px solid rgba(0, 0, 0, 0.2);
256
- padding: 0.3em;
257
- display: flex;
258
255
  align-items: center;
256
+ border: 1px solid rgba(0, 0, 0, 0.2);
259
257
  cursor: move;
258
+ display: flex;
260
259
  margin-bottom: 0.3em;
260
+ padding: 0.3em;
261
261
  }
262
262
 
263
263
  .editor-panel .sort-list li svg {
@@ -265,8 +265,8 @@
265
265
  }
266
266
 
267
267
  .editor-panel .info {
268
- margin-top: 1em;
269
268
  font-size: 0.9em;
269
+ margin-top: 1em;
270
270
  }
271
271
 
272
272
  /* React Tags Component styles (third-party library) */
@@ -275,8 +275,8 @@
275
275
  }
276
276
 
277
277
  .editor-panel .react-tags {
278
- position: relative;
279
278
  cursor: text;
279
+ position: relative;
280
280
  }
281
281
 
282
282
  .editor-panel .react-tags input.react-tags__search-input {
@@ -296,20 +296,20 @@
296
296
  }
297
297
 
298
298
  .editor-panel .react-tags__selected-tag {
299
- display: inline-block;
300
- box-sizing: border-box;
299
+ background: #f1f1f1;
301
300
  border: 1px solid #d1d1d1;
302
301
  border-radius: 2px;
303
- background: #f1f1f1;
304
- padding: 0.4em 0.6em;
302
+ box-sizing: border-box;
303
+ display: inline-block;
305
304
  font-size: 0.8em;
306
- margin-right: 0.3em;
307
305
  margin-bottom: 0.3em;
306
+ margin-right: 0.3em;
307
+ padding: 0.4em 0.6em;
308
308
  }
309
309
 
310
310
  .editor-panel .react-tags__selected-tag:after {
311
- content: '\2715';
312
311
  color: #aaa;
312
+ content: '\2715';
313
313
  margin-left: 8px;
314
314
  }
315
315
 
@@ -330,12 +330,12 @@
330
330
  }
331
331
 
332
332
  .editor-panel .react-tags__search input {
333
- max-width: 100%;
333
+ font-size: inherit;
334
+ line-height: inherit;
334
335
  margin: 0;
336
+ max-width: 100%;
335
337
  outline: none;
336
338
  padding: 0.5em 0.3em;
337
- font-size: inherit;
338
- line-height: inherit;
339
339
  }
340
340
 
341
341
  .editor-panel .react-tags__search input::-ms-clear {
@@ -343,9 +343,9 @@
343
343
  }
344
344
 
345
345
  .editor-panel .react-tags__suggestions {
346
+ left: 0;
346
347
  position: absolute;
347
348
  top: 100%;
348
- left: 0;
349
349
  width: 100%;
350
350
  }
351
351
 
@@ -356,13 +356,13 @@
356
356
  }
357
357
 
358
358
  .editor-panel .react-tags__suggestions ul {
359
- margin: 4px -1px;
360
- padding: 0;
361
- list-style: none;
362
359
  background: white;
363
360
  border: 1px solid #d1d1d1;
364
361
  border-radius: 2px;
365
362
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
363
+ list-style: none;
364
+ margin: 4px -1px;
365
+ padding: 0;
366
366
  }
367
367
 
368
368
  .editor-panel .react-tags__suggestions li {
@@ -371,14 +371,14 @@
371
371
  }
372
372
 
373
373
  .editor-panel .react-tags__suggestions li mark {
374
- text-decoration: underline;
375
374
  background: none;
376
375
  font-weight: 600;
376
+ text-decoration: underline;
377
377
  }
378
378
 
379
379
  .editor-panel .react-tags__suggestions li:hover {
380
- cursor: pointer;
381
380
  background: #eee;
381
+ cursor: pointer;
382
382
  }
383
383
 
384
384
  .editor-panel .react-tags__suggestions li.is-active {
@@ -386,23 +386,23 @@
386
386
  }
387
387
 
388
388
  .editor-panel .react-tags__suggestions li.is-disabled {
389
- opacity: 0.5;
390
389
  cursor: auto;
390
+ opacity: 0.5;
391
391
  }
392
392
 
393
393
  .editor-toggle {
394
- position: fixed;
395
- top: 1em;
396
- right: 1em;
397
394
  background: var(--blue);
398
- color: white;
399
395
  border: 0;
400
396
  border-radius: 0.5em;
401
- padding: 0.5em 1em;
402
- font-size: 1em;
397
+ color: white;
403
398
  cursor: pointer;
404
- z-index: 9999999999;
405
399
  display: none;
400
+ font-size: 1em;
401
+ padding: 0.5em 1em;
402
+ position: fixed;
403
+ right: 1em;
404
+ top: 1em;
405
+ z-index: 9999999999;
406
406
  }
407
407
 
408
408
  .editor-toggle.active {
@@ -429,8 +429,8 @@
429
429
  }
430
430
 
431
431
  :is(span).edit-label {
432
- margin-bottom: 0.3em;
433
432
  display: block;
433
+ margin-bottom: 0.3em;
434
434
  }
435
435
 
436
436
  .react-tooltip {