@cdc/map 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 (65) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcmap-vr9HZwRt.es.js +6 -0
  3. package/dist/cdcmap.js +26781 -24615
  4. package/examples/private/annotation-bug.json +642 -0
  5. package/package.json +3 -3
  6. package/src/CdcMap.tsx +3 -14
  7. package/src/CdcMapComponent.tsx +214 -159
  8. package/src/_stories/CdcMap.Defaults.stories.tsx +76 -0
  9. package/src/_stories/CdcMap.Editor.stories.tsx +187 -14
  10. package/src/_stories/CdcMap.stories.tsx +11 -1
  11. package/src/_stories/Map.HTMLInDataTable.stories.tsx +385 -0
  12. package/src/_stories/_mock/multi-state-show-unselected.json +82 -0
  13. package/src/cdcMapComponent.styles.css +2 -2
  14. package/src/components/Annotation/Annotation.Draggable.styles.css +4 -4
  15. package/src/components/Annotation/AnnotationDropdown.styles.css +1 -1
  16. package/src/components/Annotation/AnnotationList.styles.css +13 -13
  17. package/src/components/EditorPanel/components/EditorPanel.tsx +426 -58
  18. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings-style.css +1 -1
  19. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +5 -2
  20. package/src/components/EditorPanel/components/editorPanel.styles.css +34 -24
  21. package/src/components/Legend/components/Legend.tsx +9 -4
  22. package/src/components/Legend/components/LegendGroup/legend.group.css +5 -5
  23. package/src/components/Legend/components/index.scss +2 -3
  24. package/src/components/NavigationMenu.tsx +2 -1
  25. package/src/components/SmallMultiples/SmallMultiples.css +5 -5
  26. package/src/components/UsaMap/components/SingleState/SingleState.StateOutput.tsx +32 -17
  27. package/src/components/UsaMap/components/TerritoriesSection.tsx +3 -2
  28. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +13 -8
  29. package/src/components/UsaMap/components/UsaMap.County.tsx +410 -183
  30. package/src/components/UsaMap/components/UsaMap.Region.styles.css +1 -1
  31. package/src/components/UsaMap/components/UsaMap.SingleState.styles.css +2 -2
  32. package/src/components/UsaMap/components/UsaMap.State.tsx +13 -8
  33. package/src/components/WorldMap/WorldMap.tsx +10 -13
  34. package/src/components/WorldMap/data/world-topo-updated.json +1 -0
  35. package/src/components/WorldMap/data/world-topo.json +1 -1
  36. package/src/components/WorldMap/worldMap.styles.css +1 -1
  37. package/src/components/ZoomControls.tsx +49 -18
  38. package/src/components/zoomControls.styles.css +27 -11
  39. package/src/data/initial-state.js +14 -5
  40. package/src/data/legacy-defaults.ts +8 -0
  41. package/src/data/supported-geos.js +19 -0
  42. package/src/helpers/colors.ts +2 -1
  43. package/src/helpers/dataTableHelpers.ts +56 -0
  44. package/src/helpers/displayGeoName.ts +19 -11
  45. package/src/helpers/getMapContainerClasses.ts +8 -2
  46. package/src/helpers/getMatchingPatternForRow.ts +67 -0
  47. package/src/helpers/getPatternForRow.ts +11 -18
  48. package/src/helpers/tests/dataTableHelpers.test.ts +78 -0
  49. package/src/helpers/tests/displayGeoName.test.ts +17 -0
  50. package/src/helpers/tests/getMatchingPatternForRow.test.ts +150 -0
  51. package/src/helpers/tests/getPatternForRow.test.ts +140 -2
  52. package/src/helpers/urlDataHelpers.ts +7 -1
  53. package/src/hooks/useResizeObserver.ts +36 -22
  54. package/src/hooks/useTooltip.test.tsx +64 -0
  55. package/src/hooks/useTooltip.ts +28 -8
  56. package/src/scss/editor-panel.scss +1 -1
  57. package/src/scss/main.scss +140 -6
  58. package/src/scss/map.scss +9 -4
  59. package/src/store/map.actions.ts +2 -0
  60. package/src/store/map.reducer.ts +4 -0
  61. package/src/test/CdcMap.test.jsx +2 -2
  62. package/src/types/MapConfig.ts +22 -4
  63. package/src/types/MapContext.ts +3 -1
  64. package/dist/cdcmap-Cf9_fbQf.es.js +0 -6
  65. package/src/helpers/componentHelpers.ts +0 -8
@@ -12,16 +12,17 @@ import {
12
12
  } from 'react-accessible-accordion'
13
13
  import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'
14
14
  import { useDebounce } from 'use-debounce'
15
- import _ from 'lodash'
15
+ import cloneDeep from 'lodash/cloneDeep'
16
+ import includes from 'lodash/includes'
16
17
  import { Tooltip as ReactTooltip } from 'react-tooltip'
17
18
  import 'react-tooltip/dist/react-tooltip.css'
18
19
  import Panels from './Panels'
19
- import Layout from '@cdc/core/components/Layout'
20
20
 
21
21
  // Data
22
22
  import { mapColorPalettes as colorPalettes } from '@cdc/core/data/colorPalettes'
23
23
  import { supportedStatesFipsCodes, supportedCountries } from '../../../data/supported-geos.js'
24
24
  import { getSupportedCountryOptions } from '../../../helpers/getCountriesPicked'
25
+ import { displayGeoName } from '../../../helpers/displayGeoName'
25
26
 
26
27
  // Components - Core
27
28
  import { EditorPanel as BaseEditorPanel } from '@cdc/core/components/EditorPanel/EditorPanel'
@@ -49,19 +50,25 @@ import { MapContext } from '../../../types/MapContext.js'
49
50
  import Alert from '@cdc/core/components/Alert'
50
51
  import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
51
52
  import { CheckBox, Select, TextField } from '@cdc/core/components/EditorPanel/Inputs'
53
+ import StyleTreatmentSection from '@cdc/core/components/EditorPanel/sections/StyleTreatmentSection'
52
54
  import { HeaderThemeSelector } from '@cdc/core/components/HeaderThemeSelector'
53
55
  import useColumnsRequiredChecker from '../../../hooks/useColumnsRequiredChecker'
54
56
  import { addUIDs } from '../../../helpers'
55
57
  import generateRuntimeData from '../../../helpers/generateRuntimeData'
56
58
 
57
- import '@cdc/core/styles/v2/components/editor.scss'
59
+ import '@cdc/core/components/EditorPanel/editor.scss'
58
60
  import './editorPanel.styles.css'
59
61
  import FootnotesEditor from '@cdc/core/components/EditorPanel/FootnotesEditor'
62
+ import CustomSortOrder from '@cdc/core/components/EditorPanel/CustomSortOrder'
60
63
  import { Datasets } from '@cdc/core/types/DataSet'
61
64
  import MultiSelect from '@cdc/core/components/MultiSelect'
62
65
  import { paletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
63
66
  import { isV1Palette, getCurrentPaletteName, migratePaletteWithMap } from '@cdc/core/helpers/palettes/utils'
64
- import { USE_V2_MIGRATION } from '@cdc/core/helpers/constants'
67
+ import {
68
+ ENABLE_CHART_MAP_TP5_TREATMENT_SELECTION,
69
+ ENABLE_MAP_DATA_BITE_VISUAL_SETTINGS,
70
+ USE_V2_MIGRATION
71
+ } from '@cdc/core/helpers/constants'
65
72
  import { isCoveDeveloperMode } from '@cdc/core/helpers/queryStringUtils'
66
73
  import { PaletteSelector, DeveloperPaletteRollback } from '@cdc/core/components/PaletteSelector'
67
74
  import PaletteConversionModal from '@cdc/core/components/PaletteConversionModal'
@@ -71,6 +78,39 @@ type MapEditorPanelProps = {
71
78
  datasets?: Datasets
72
79
  }
73
80
 
81
+ type ColumnSectionProps = {
82
+ fieldKey: 'geo' | 'primary'
83
+ fieldName: string
84
+ show: boolean
85
+ setShow: (fieldKey: 'geo' | 'primary', value: boolean) => void
86
+ children: React.ReactNode
87
+ }
88
+
89
+ const ColumnSection = ({ fieldKey, fieldName, show, setShow, children }: ColumnSectionProps) => {
90
+ if (!show) {
91
+ return (
92
+ <div className='mb-1'>
93
+ <button type='button' className='btn btn-light' onClick={() => setShow(fieldKey, true)}>
94
+ <Icon display='caretDown' />
95
+ </button>
96
+ <span> {fieldName}</span>
97
+ </div>
98
+ )
99
+ }
100
+
101
+ return (
102
+ <fieldset className='primary-fieldset edit-block column-section' key={fieldKey}>
103
+ <div className='column-section__header'>
104
+ <button type='button' className='btn btn-light' onClick={() => setShow(fieldKey, false)}>
105
+ <Icon display='caretUp' />
106
+ </button>
107
+ <span className='column-section__title'>{fieldName}</span>
108
+ </div>
109
+ {children}
110
+ </fieldset>
111
+ )
112
+ }
113
+
74
114
  const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
75
115
  const {
76
116
  setParentConfig,
@@ -114,11 +154,16 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
114
154
  const [loadedDefault, setLoadedDefault] = useState(false)
115
155
  const [activeFilterValueForDescription, setActiveFilterValueForDescription] = useState([0, 0])
116
156
  const [showConversionModal, setShowConversionModal] = useState(false)
157
+ const [columnSectionsOpen, setColumnSectionsOpen] = useState({ geo: true, primary: true })
117
158
  const [pendingPaletteSelection, setPendingPaletteSelection] = useState<{
118
159
  palette: string
119
160
  action: () => void
120
161
  } | null>(null)
121
162
 
163
+ const setColumnSectionOpen = (fieldKey: 'geo' | 'primary', value: boolean) => {
164
+ setColumnSectionsOpen(prev => ({ ...prev, [fieldKey]: value }))
165
+ }
166
+
122
167
  const {
123
168
  MapLayerHandlers: { handleMapLayer, handleAddLayer, handleRemoveLayer }
124
169
  } = useMapLayers(config, setConfig, false, tooltipId)
@@ -296,7 +341,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
296
341
  legend: {
297
342
  ...config.legend,
298
343
  position: value,
299
- hideBorder: _.includes(['top', 'bottom'], value)
344
+ hideBorder: includes(['top', 'bottom'], value)
300
345
  }
301
346
  })
302
347
  break
@@ -518,7 +563,13 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
518
563
  break
519
564
  case 'geoType':
520
565
  addUIDs(config, config.columns.geo.name)
521
- dispatch({ type: 'SET_POSITION', payload: [0, 30] })
566
+ dispatch({
567
+ type: 'SET_POSITION',
568
+ payload: {
569
+ coordinates: value === 'world' ? [0, 30] : [0, 0],
570
+ zoom: 1
571
+ }
572
+ })
522
573
 
523
574
  // If we're still working with default data, switch to the world default to show it as an example
524
575
  if (true === loadedDefault && 'world' === value) {
@@ -866,6 +917,11 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
866
917
  }
867
918
  }
868
919
 
920
+ const updateColumnOrder = (columnName, value) => {
921
+ const parsedValue = Number.parseInt(value, 10)
922
+ editColumn(columnName, 'order', Number.isNaN(parsedValue) ? undefined : parsedValue)
923
+ }
924
+
869
925
  // just adds a new column but not set to any data yet
870
926
  const addAdditionalColumn = number => {
871
927
  const columnKey = `additionalColumn${number}`
@@ -931,7 +987,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
931
987
  }
932
988
 
933
989
  // Remove the legend
934
- let strippedLegend = _.cloneDeep(config.legend)
990
+ let strippedLegend = cloneDeep(config.legend)
935
991
 
936
992
  delete strippedLegend.disabledAmt
937
993
 
@@ -941,7 +997,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
941
997
  delete strippedState.defaultData
942
998
 
943
999
  // Remove tooltips if they're active in the editor
944
- strippedState.general = _.cloneDeep(config.general)
1000
+ strippedState.general = cloneDeep(config.general)
945
1001
 
946
1002
  // Add columns property back to data if it's there
947
1003
  if (config.columns) {
@@ -963,7 +1019,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
963
1019
  const isV1PaletteConfig = isV1Palette(config)
964
1020
 
965
1021
  const executeSelection = () => {
966
- const _newConfig = _.cloneDeep(config)
1022
+ const _newConfig = cloneDeep(config)
967
1023
 
968
1024
  // If v2 migration is disabled, use the original palette name and keep v1 version
969
1025
  if (!USE_V2_MIGRATION) {
@@ -1062,6 +1118,44 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1062
1118
 
1063
1119
  const updateField = updateFieldFactory(config, setConfig)
1064
1120
 
1121
+ const handleStyleTreatmentChange = (value: string) => {
1122
+ const useTp5Treatment = value === 'tp5'
1123
+
1124
+ setConfig({
1125
+ ...config,
1126
+ general: {
1127
+ ...config.general,
1128
+ titleStyle: useTp5Treatment ? 'small' : 'legacy'
1129
+ },
1130
+ visual: {
1131
+ ...config.visual,
1132
+ tp5Treatment: useTp5Treatment,
1133
+ border: useTp5Treatment ? false : config.visual?.border,
1134
+ borderColorTheme: useTp5Treatment ? false : config.visual?.borderColorTheme,
1135
+ accent: useTp5Treatment ? false : config.visual?.accent
1136
+ }
1137
+ })
1138
+ }
1139
+
1140
+ const handleTitleStyleChange = (newTitleStyle: string) => {
1141
+ setConfig({
1142
+ ...config,
1143
+ general: {
1144
+ ...config.general,
1145
+ titleStyle: newTitleStyle
1146
+ },
1147
+ visual:
1148
+ newTitleStyle === 'legacy'
1149
+ ? config.visual
1150
+ : {
1151
+ ...config.visual,
1152
+ border: undefined,
1153
+ borderColorTheme: undefined,
1154
+ accent: undefined
1155
+ }
1156
+ })
1157
+ }
1158
+
1065
1159
  const StateOptionList = () => {
1066
1160
  const arrOfArrays = Object.entries(supportedStatesFipsCodes)
1067
1161
 
@@ -1163,7 +1257,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1163
1257
  }
1164
1258
 
1165
1259
  // Remove the legend
1166
- let strippedLegend = _.cloneDeep(config.legend)
1260
+ let strippedLegend = cloneDeep(config.legend)
1167
1261
 
1168
1262
  delete strippedLegend.disabledAmt
1169
1263
 
@@ -1173,7 +1267,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1173
1267
  delete strippedState.defaultData
1174
1268
 
1175
1269
  // Remove tooltips if they're active in the editor
1176
- strippedState.general = _.cloneDeep(config.general)
1270
+ strippedState.general = cloneDeep(config.general)
1177
1271
 
1178
1272
  strippedState.general.showSidebar = 'hidden'
1179
1273
 
@@ -1319,20 +1413,31 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1319
1413
  {/* Type */}
1320
1414
  {/* Select > Filter a state */}
1321
1415
  {config.general.geoType === 'single-state' && (
1322
- <label>
1323
- <span>States Selector</span>
1324
- <MultiSelect
1325
- selected={config.general.statesPicked.map(state => state.stateName)}
1326
- options={StateOptionList().map(option => ({
1327
- value: option.props.value,
1328
- label: option.props.children
1329
- }))}
1330
- fieldName={'statesPicked'}
1331
- updateField={(_, __, ___, selectedOptions) => {
1332
- handleEditorChanges('chooseState', selectedOptions)
1333
- }}
1334
- />
1335
- </label>
1416
+ <>
1417
+ <label>
1418
+ <span>States Selector</span>
1419
+ <MultiSelect
1420
+ selected={config.general.statesPicked.map(state => state.stateName)}
1421
+ options={StateOptionList().map(option => ({
1422
+ value: option.props.value,
1423
+ label: option.props.children
1424
+ }))}
1425
+ fieldName={'statesPicked'}
1426
+ updateField={(_, __, ___, selectedOptions) => {
1427
+ handleEditorChanges('chooseState', selectedOptions)
1428
+ }}
1429
+ />
1430
+ </label>
1431
+ {config.general.statesPicked && config.general.statesPicked.length > 0 && (
1432
+ <CheckBox
1433
+ value={config.general.hideUnselectedStates !== false}
1434
+ fieldName='hideUnselectedStates'
1435
+ label='Hide Unselected States'
1436
+ updateField={updateField}
1437
+ section='general'
1438
+ />
1439
+ )}
1440
+ </>
1336
1441
  )}
1337
1442
  {/* Country Selection for World Maps */}
1338
1443
  {config.general.geoType === 'world' && (
@@ -1541,12 +1646,12 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1541
1646
  section='general'
1542
1647
  fieldName='titleStyle'
1543
1648
  label='Title Style'
1544
- updateField={updateField}
1545
1649
  options={[
1546
1650
  { value: 'small', label: 'Small (h3)' },
1547
1651
  { value: 'large', label: 'Large (h2)' },
1548
1652
  { value: 'legacy', label: 'Legacy' }
1549
1653
  ]}
1654
+ onChange={event => handleTitleStyleChange(event.target.value)}
1550
1655
  tooltip={
1551
1656
  <Tooltip style={{ textTransform: 'none' }}>
1552
1657
  <Tooltip.Target>
@@ -1649,6 +1754,29 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1649
1754
  <input type="checkbox" checked={ state.general.showDownloadMediaButton } onChange={(event) => { handleEditorChanges("toggleDownloadMediaButton", event.target.checked) }} />
1650
1755
  <span className="edit-label">Enable Media Download</span>
1651
1756
  </label> */}
1757
+ <Select
1758
+ value={config.locale}
1759
+ fieldName='locale'
1760
+ label='Language for dates and numbers'
1761
+ updateField={updateField}
1762
+ options={[
1763
+ { value: 'en-US', label: 'English (en-US)' },
1764
+ { value: 'es-MX', label: 'Spanish (es-MX)' }
1765
+ ]}
1766
+ tooltip={
1767
+ <Tooltip style={{ textTransform: 'none' }}>
1768
+ <Tooltip.Target>
1769
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1770
+ </Tooltip.Target>
1771
+ <Tooltip.Content>
1772
+ <p>
1773
+ Change the language (locale) for this visualization to alter the way dates and numbers are
1774
+ formatted.
1775
+ </p>
1776
+ </Tooltip.Content>
1777
+ </Tooltip>
1778
+ }
1779
+ />
1652
1780
  </AccordionItemPanel>
1653
1781
  </AccordionItem>
1654
1782
  <AccordionItem>
@@ -1658,7 +1786,12 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1658
1786
  <AccordionItemButton>Columns</AccordionItemButton>
1659
1787
  </AccordionItemHeading>
1660
1788
  <AccordionItemPanel>
1661
- <fieldset className='primary-fieldset edit-block'>
1789
+ <ColumnSection
1790
+ fieldKey='geo'
1791
+ fieldName='Geography'
1792
+ show={columnSectionsOpen.geo}
1793
+ setShow={setColumnSectionOpen}
1794
+ >
1662
1795
  <label>
1663
1796
  <span className='edit-label column-heading'>
1664
1797
  Geography
@@ -1701,6 +1834,24 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1701
1834
  label='Hide Geography Column Name in Tooltip'
1702
1835
  updateField={updateField}
1703
1836
  />
1837
+ <TextField
1838
+ value={columns.geo.label}
1839
+ section='columns'
1840
+ subsection='geo'
1841
+ fieldName='label'
1842
+ label='Geography Column Label'
1843
+ updateField={updateField}
1844
+ tooltip={
1845
+ <Tooltip style={{ textTransform: 'none' }}>
1846
+ <Tooltip.Target>
1847
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1848
+ </Tooltip.Target>
1849
+ <Tooltip.Content>
1850
+ <p>Enter a label for the geography column in tooltips and the data table.</p>
1851
+ </Tooltip.Content>
1852
+ </Tooltip>
1853
+ }
1854
+ />
1704
1855
  <TextField
1705
1856
  value={config.general.geoLabelOverride}
1706
1857
  section='general'
@@ -1719,9 +1870,66 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1719
1870
  </Tooltip>
1720
1871
  }
1721
1872
  />
1722
- </fieldset>
1873
+ <label className='mt-2'>
1874
+ <span className='edit-label column-heading'>
1875
+ Geography Display Column
1876
+ <Tooltip style={{ textTransform: 'none' }}>
1877
+ <Tooltip.Target>
1878
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1879
+ </Tooltip.Target>
1880
+ <Tooltip.Content>
1881
+ <p>
1882
+ Optional. Select a column containing alternate display names for geographies (e.g.,
1883
+ translated names). These will be shown in tooltips, the data table, and navigation menus
1884
+ instead of the geography column values.
1885
+ </p>
1886
+ </Tooltip.Content>
1887
+ </Tooltip>
1888
+ </span>
1889
+ <Select
1890
+ value={config.columns.geo?.displayColumn || ''}
1891
+ options={columnsOptions.map(c => c.key)}
1892
+ onChange={event => {
1893
+ editColumn('geo', 'displayColumn', event.target.value)
1894
+ }}
1895
+ />
1896
+ </label>
1897
+ <CheckBox
1898
+ value={config.columns.geo?.dataTable || false}
1899
+ section='columns'
1900
+ subsection='geo'
1901
+ fieldName='dataTable'
1902
+ label='Show in Data Table'
1903
+ updateField={updateField}
1904
+ />
1905
+ <CheckBox
1906
+ value={config.columns.geo?.tooltip || false}
1907
+ section='columns'
1908
+ subsection='geo'
1909
+ fieldName='tooltip'
1910
+ label='Show in Tooltips'
1911
+ updateField={updateField}
1912
+ />
1913
+ <label className='mt-2'>
1914
+ <span className='edit-label column-heading'>Order</span>
1915
+ <input
1916
+ onWheel={e => e.currentTarget.blur()}
1917
+ type='number'
1918
+ min='1'
1919
+ value={config.columns.geo?.order ?? ''}
1920
+ onChange={event => {
1921
+ updateColumnOrder('geo', event.target.value)
1922
+ }}
1923
+ />
1924
+ </label>
1925
+ </ColumnSection>
1723
1926
  {'navigation' !== config.general.type && (
1724
- <fieldset className='primary-fieldset edit-block'>
1927
+ <ColumnSection
1928
+ fieldKey='primary'
1929
+ fieldName='Data'
1930
+ show={columnSectionsOpen.primary}
1931
+ setShow={setColumnSectionOpen}
1932
+ >
1725
1933
  <Select
1726
1934
  label='Data Column'
1727
1935
  value={columns.primary.name}
@@ -1829,8 +2037,20 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1829
2037
  label='Show in Tooltips'
1830
2038
  updateField={updateField}
1831
2039
  />
2040
+ <label>
2041
+ <span className='edit-label column-heading'>Order</span>
2042
+ <input
2043
+ onWheel={e => e.currentTarget.blur()}
2044
+ type='number'
2045
+ min='1'
2046
+ value={config.columns.primary?.order ?? ''}
2047
+ onChange={event => {
2048
+ updateColumnOrder('primary', event.target.value)
2049
+ }}
2050
+ />
2051
+ </label>
1832
2052
  </ul>
1833
- </fieldset>
2053
+ </ColumnSection>
1834
2054
  )}
1835
2055
 
1836
2056
  {config.general.type === 'bubble' && config.legend.type === 'category' && (
@@ -2035,10 +2255,19 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2035
2255
  <Select
2036
2256
  label='Column'
2037
2257
  value={config.columns[val] ? config.columns[val].name : ''}
2038
- options={columnsOptions.map(option => ({
2039
- value: option.props.value,
2040
- label: option.props.children
2041
- }))}
2258
+ options={columnsOptions
2259
+ .filter(option => {
2260
+ const optionValue = option.props.value
2261
+ return (
2262
+ optionValue === '' ||
2263
+ optionValue !== config.columns.geo?.name ||
2264
+ optionValue === config.columns[val]?.name
2265
+ )
2266
+ })
2267
+ .map(option => ({
2268
+ value: option.props.value,
2269
+ label: option.props.children
2270
+ }))}
2042
2271
  onChange={event => {
2043
2272
  editColumn(val, 'name', event.target.value)
2044
2273
  }}
@@ -2112,6 +2341,18 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2112
2341
  editColumn(val, 'tooltip', event.target.checked)
2113
2342
  }}
2114
2343
  />
2344
+ <label>
2345
+ <span className='edit-label column-heading'>Order</span>
2346
+ <input
2347
+ onWheel={e => e.currentTarget.blur()}
2348
+ type='number'
2349
+ min='1'
2350
+ value={config.columns[val]?.order ?? ''}
2351
+ onChange={event => {
2352
+ updateColumnOrder(val, event.target.value)
2353
+ }}
2354
+ />
2355
+ </label>
2115
2356
  </ul>
2116
2357
  </fieldset>
2117
2358
  ))}
@@ -2878,6 +3119,78 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2878
3119
  label='Show collapse below table'
2879
3120
  updateField={updateField}
2880
3121
  />
3122
+ <Select
3123
+ value={config.table.defaultSort?.column || ''}
3124
+ fieldName='column'
3125
+ section='table'
3126
+ subsection='defaultSort'
3127
+ label='Default Sort Column'
3128
+ initial='-Select-'
3129
+ options={Object.keys(config.columns)
3130
+ .filter(key => config.columns[key].dataTable !== false && config.columns[key].name)
3131
+ .map(key => ({
3132
+ label: config.columns[key].label || config.columns[key].name || key,
3133
+ value: key
3134
+ }))}
3135
+ updateField={(_section, _subSection, _fieldName, value) => {
3136
+ if (value === '' || value === '-Select-') {
3137
+ updateField('table', null, 'defaultSort', {})
3138
+ } else {
3139
+ updateField('table', null, 'defaultSort', { column: value, sortDirection: 'asc' })
3140
+ }
3141
+ }}
3142
+ tooltip={
3143
+ <Tooltip style={{ textTransform: 'none' }}>
3144
+ <Tooltip.Target>
3145
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
3146
+ </Tooltip.Target>
3147
+ <Tooltip.Content>
3148
+ <p>Choose a column to sort the data table by when it first loads.</p>
3149
+ </Tooltip.Content>
3150
+ </Tooltip>
3151
+ }
3152
+ />
3153
+ {config.table.defaultSort?.column && (
3154
+ <Select
3155
+ value={config.table.defaultSort?.sortDirection || 'asc'}
3156
+ fieldName='sortDirection'
3157
+ section='table'
3158
+ subsection='defaultSort'
3159
+ label='Sort Direction'
3160
+ options={[
3161
+ { label: 'Ascending', value: 'asc' },
3162
+ { label: 'Descending', value: 'desc' },
3163
+ { label: 'Custom', value: 'custom' }
3164
+ ]}
3165
+ updateField={(_section, _subSection, _fieldName, value) => {
3166
+ const newDefaultSort = { ...config.table.defaultSort, sortDirection: value }
3167
+ if (value !== 'custom') {
3168
+ delete newDefaultSort.customOrder
3169
+ } else if (!newDefaultSort.customOrder?.length) {
3170
+ // Auto-populate customOrder with unique values so the table updates immediately
3171
+ const col = newDefaultSort.column
3172
+ const dataCol = config.columns?.[col]?.name || col
3173
+ if (dataCol && config.data?.length) {
3174
+ newDefaultSort.customOrder = Array.from(
3175
+ new Set(config.data.map(row => String(row[dataCol] ?? '')).filter(v => v !== ''))
3176
+ )
3177
+ }
3178
+ }
3179
+ updateField('table', null, 'defaultSort', newDefaultSort)
3180
+ }}
3181
+ />
3182
+ )}
3183
+ {config.table.defaultSort?.column && config.table.defaultSort?.sortDirection === 'custom' && (
3184
+ <CustomSortOrder
3185
+ column={
3186
+ config.columns[config.table.defaultSort.column]?.name || config.table.defaultSort.column
3187
+ }
3188
+ data={config.data}
3189
+ customOrder={config.table.defaultSort.customOrder}
3190
+ updateField={updateField}
3191
+ displayTransform={config.table.defaultSort.column === 'geo' ? displayGeoName : undefined}
3192
+ />
3193
+ )}
2881
3194
  <CheckBox
2882
3195
  value={config.table.download}
2883
3196
  fieldName='download'
@@ -2907,6 +3220,16 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2907
3220
  section='table'
2908
3221
  updateField={updateField}
2909
3222
  />
3223
+ <div className='ms-4 mt-2' style={{ maxWidth: 'calc(100% - 1.5rem)' }}>
3224
+ <TextField
3225
+ value={config.table.downloadDataLabel}
3226
+ section='table'
3227
+ fieldName='downloadDataLabel'
3228
+ label='Download Data Link Text'
3229
+ placeholder='Download Data (CSV)'
3230
+ updateField={updateField}
3231
+ />
3232
+ </div>
2910
3233
  </>
2911
3234
  )}
2912
3235
  {isDashboard && (
@@ -2952,28 +3275,40 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2952
3275
  }}
2953
3276
  />
2954
3277
  {config.general.showDownloadImgButton && (
2955
- <CheckBox
2956
- value={config.general.includeContextInDownload}
2957
- section='general'
2958
- subsection={null}
2959
- className='ms-4'
2960
- fieldName='includeContextInDownload'
2961
- label='Include Heading & Context'
2962
- updateField={updateField}
2963
- tooltip={
2964
- <Tooltip style={{ textTransform: 'none' }}>
2965
- <Tooltip.Target>
2966
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2967
- </Tooltip.Target>
2968
- <Tooltip.Content>
2969
- <p>
2970
- When enabled, the image download will include the section heading (H2 or H3) and any
2971
- explanatory paragraphs that appear before the visualization
2972
- </p>
2973
- </Tooltip.Content>
2974
- </Tooltip>
2975
- }
2976
- />
3278
+ <>
3279
+ <CheckBox
3280
+ value={config.general.includeContextInDownload}
3281
+ section='general'
3282
+ subsection={null}
3283
+ className='ms-4'
3284
+ fieldName='includeContextInDownload'
3285
+ label='Include Heading & Context'
3286
+ updateField={updateField}
3287
+ tooltip={
3288
+ <Tooltip style={{ textTransform: 'none' }}>
3289
+ <Tooltip.Target>
3290
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
3291
+ </Tooltip.Target>
3292
+ <Tooltip.Content>
3293
+ <p>
3294
+ When enabled, the image download will include the section heading (H2 or H3) and any
3295
+ explanatory paragraphs that appear before the visualization
3296
+ </p>
3297
+ </Tooltip.Content>
3298
+ </Tooltip>
3299
+ }
3300
+ />
3301
+ <div className='ms-4 mt-2' style={{ maxWidth: 'calc(100% - 1.5rem)' }}>
3302
+ <TextField
3303
+ value={config.table.downloadImageLabel}
3304
+ section='table'
3305
+ fieldName='downloadImageLabel'
3306
+ label='Download Image Link Text'
3307
+ placeholder='Download Map (PNG)'
3308
+ updateField={updateField}
3309
+ />
3310
+ </div>
3311
+ </>
2977
3312
  )}
2978
3313
 
2979
3314
  {/* <label className='checkbox'>
@@ -3031,6 +3366,14 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3031
3366
  updateField={updateField}
3032
3367
  />
3033
3368
  )}
3369
+ <TextField
3370
+ value={tooltips.noDataLabel}
3371
+ section='tooltips'
3372
+ fieldName='noDataLabel'
3373
+ label='No Data Tooltip Text'
3374
+ placeholder='No Data'
3375
+ updateField={updateField}
3376
+ />
3034
3377
  </AccordionItemPanel>
3035
3378
  </AccordionItem>
3036
3379
  <AccordionItem>
@@ -3045,6 +3388,19 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3045
3388
  onThemeSelect={palette => handleEditorChanges('headerColor', palette)}
3046
3389
  label='Header Theme'
3047
3390
  />
3391
+ {ENABLE_MAP_DATA_BITE_VISUAL_SETTINGS && (
3392
+ <StyleTreatmentSection
3393
+ styleTreatment={
3394
+ config.general.titleStyle === 'legacy' && !config.visual?.tp5Treatment ? 'legacy' : 'tp5'
3395
+ }
3396
+ onStyleTreatmentChange={handleStyleTreatmentChange}
3397
+ showStyleTreatment={ENABLE_CHART_MAP_TP5_TREATMENT_SELECTION}
3398
+ border={config.visual?.border}
3399
+ borderColorTheme={config.visual?.borderColorTheme}
3400
+ accent={config.visual?.accent}
3401
+ updateField={updateField}
3402
+ />
3403
+ )}
3048
3404
  <CheckBox
3049
3405
  value={config.general.showTitle || false}
3050
3406
  section='general'
@@ -3276,7 +3632,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3276
3632
  type='checkbox'
3277
3633
  checked={config.visual.showBubbleZeros}
3278
3634
  onChange={event => {
3279
- const _newConfig = _.cloneDeep(config)
3635
+ const _newConfig = cloneDeep(config)
3280
3636
  _newConfig.visual.showBubbleZeros = event.target.checked
3281
3637
  setConfig(_newConfig)
3282
3638
  }}
@@ -3284,7 +3640,9 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3284
3640
  <span className='edit-label'>Show Data with Zero's on Bubble Map</span>
3285
3641
  </label>
3286
3642
  )}
3287
- {(config.general.geoType === 'world' || config.general.geoType === 'single-state') && (
3643
+ {(config.general.geoType === 'world' ||
3644
+ config.general.geoType === 'single-state' ||
3645
+ config.general.geoType === 'us-county') && (
3288
3646
  <label className='checkbox'>
3289
3647
  <input
3290
3648
  type='checkbox'
@@ -3446,6 +3804,15 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3446
3804
  />
3447
3805
  </>
3448
3806
  )}
3807
+ {isCoveDeveloperMode() && (
3808
+ <CheckBox
3809
+ value={config.visual?.highlightWrappers}
3810
+ section='visual'
3811
+ fieldName='highlightWrappers'
3812
+ label='Highlight Layout Wrappers'
3813
+ updateField={updateField}
3814
+ />
3815
+ )}
3449
3816
  </AccordionItemPanel>
3450
3817
  </AccordionItem>
3451
3818
  <AccordionItem>
@@ -3552,6 +3919,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3552
3919
  enableMarkupVariables={config.enableMarkupVariables || false}
3553
3920
  onMarkupVariablesChange={variables => setConfig({ ...config, markupVariables: variables })}
3554
3921
  onToggleEnable={enabled => setConfig({ ...config, enableMarkupVariables: enabled })}
3922
+ dataMetadata={config.dataMetadata}
3555
3923
  />
3556
3924
  <Panels.SmallMultiples name='Small Multiples' />
3557
3925
  </Accordion>