@cdc/core 4.26.1 → 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 (249) hide show
  1. package/.claude/agents/qa-test-developer.md +126 -0
  2. package/CLAUDE.local.md +67 -0
  3. package/LICENSE +201 -0
  4. package/_stories/Gallery.Charts.stories.tsx +35 -42
  5. package/_stories/Gallery.DataBite.stories.tsx +15 -8
  6. package/_stories/Gallery.Maps.stories.tsx +37 -28
  7. package/_stories/Gallery.WaffleChart.stories.tsx +1 -1
  8. package/_stories/PageART.stories.tsx +5 -4
  9. package/_stories/PageBRFSS.stories.tsx +21 -16
  10. package/_stories/PageCancerRegistries.stories.tsx +15 -15
  11. package/_stories/PageEasternEquineEncephalitis.stories.tsx +33 -19
  12. package/_stories/PageExcessiveAlcoholUse.stories.tsx +148 -143
  13. package/_stories/PageMaternalMortality.stories.tsx +5 -4
  14. package/_stories/PageOralHealth.stories.tsx +15 -10
  15. package/_stories/PageRespiratory.stories.tsx +4 -4
  16. package/_stories/PageSmokingTobacco.stories.tsx +15 -10
  17. package/_stories/PageStateDiabetesProfiles.stories.tsx +15 -10
  18. package/_stories/PageWastewater.stories.tsx +44 -30
  19. package/_stories/VegaImport.stories.tsx +401 -0
  20. package/_stories/vega-fixtures/bars-with-line.json +444 -0
  21. package/_stories/vega-fixtures/bars.json +58 -0
  22. package/_stories/vega-fixtures/combo-bar-rolling-mean.json +88 -0
  23. package/_stories/vega-fixtures/combo.json +68 -0
  24. package/_stories/vega-fixtures/grouped-horizontal-bars.json +83 -0
  25. package/_stories/vega-fixtures/grouped-horizontal-bars2.json +231 -0
  26. package/_stories/vega-fixtures/horizontal-bar.json +427 -0
  27. package/_stories/vega-fixtures/horizontal-bars-with-bad-colors.json +197 -0
  28. package/_stories/vega-fixtures/horizontal-bars2.json +58 -0
  29. package/_stories/vega-fixtures/lines.json +227 -0
  30. package/_stories/vega-fixtures/measles-bars.json +348 -0
  31. package/_stories/vega-fixtures/measles-map.json +11101 -0
  32. package/_stories/vega-fixtures/measles-stacked-bars.json +2147 -0
  33. package/_stories/vega-fixtures/multi-dataset.json +255 -0
  34. package/_stories/vega-fixtures/no-data.json +14 -0
  35. package/_stories/vega-fixtures/pie-chart.json +94 -0
  36. package/_stories/vega-fixtures/repeat-spec.json +47 -0
  37. package/_stories/vega-fixtures/stacked-area.json +222 -0
  38. package/_stories/vega-fixtures/stacked-bar-with-rect.json +3412 -0
  39. package/_stories/vega-fixtures/stacked-bars-with-line.json +364 -0
  40. package/_stories/vega-fixtures/stacked-bars.json +212 -0
  41. package/_stories/vega-fixtures/stacked-horizontal-bars.json +140 -0
  42. package/_stories/vega-fixtures/warning-combo.json +59 -0
  43. package/_stories/vega-fixtures/warning-scatter-and-line.json +1182 -0
  44. package/assets/callout-flag.svg +7 -0
  45. package/assets/icon-chart-area.svg +1 -0
  46. package/assets/icon-chart-radar.svg +23 -0
  47. package/assets/logo2.svg +31 -0
  48. package/components/AdvancedEditor/EmbedEditor.tsx +270 -38
  49. package/components/Alert/components/Alert.styles.css +2 -2
  50. package/components/ComboBox/combobox.styles.css +48 -48
  51. package/components/CustomColorsEditor/CustomColorsEditor.css +53 -53
  52. package/components/CustomColorsEditor/CustomColorsEditor.tsx +3 -10
  53. package/components/DataTable/DataTable.tsx +46 -18
  54. package/components/DataTable/DataTableStandAlone.tsx +1 -0
  55. package/components/DataTable/components/ChartHeader.tsx +21 -12
  56. package/components/DataTable/components/MapHeader.tsx +34 -28
  57. package/components/DataTable/components/SortIcon/sort-icon.css +5 -5
  58. package/components/DataTable/data-table.css +50 -52
  59. package/components/DataTable/helpers/applyCustomOrder.ts +17 -0
  60. package/components/DataTable/helpers/getChartCellValue.ts +10 -7
  61. package/components/DataTable/helpers/getMapDataTableColumnKeys.ts +22 -0
  62. package/components/DataTable/helpers/getSeriesName.ts +6 -0
  63. package/components/DataTable/helpers/mapCellMatrix.tsx +33 -23
  64. package/components/DataTable/helpers/tests/mapCellMatrix.test.ts +33 -0
  65. package/components/DownloadButton.tsx +14 -6
  66. package/components/EditorPanel/ColumnsEditor.tsx +38 -31
  67. package/components/EditorPanel/CustomSortOrder.tsx +94 -0
  68. package/components/EditorPanel/DataTableEditor.tsx +139 -23
  69. package/components/EditorPanel/EditorPanel.styles.css +71 -71
  70. package/components/EditorPanel/EditorPanel.tsx +3 -8
  71. package/components/EditorPanel/EditorPanelDispatch.tsx +4 -4
  72. package/components/EditorPanel/FootnotesEditor.tsx +2 -2
  73. package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +21 -12
  74. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +16 -10
  75. package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +33 -29
  76. package/components/EditorPanel/components/MarkupVariablesEditor.tsx +160 -106
  77. package/components/EditorPanel/components/PanelMarkup.tsx +5 -1
  78. package/{styles/v2/components → components/EditorPanel}/editor.scss +76 -22
  79. package/components/EditorPanel/sections/StyleTreatmentSection.tsx +99 -0
  80. package/components/EditorPanel/sections/VisualSection.tsx +11 -0
  81. package/components/EditorWrapper/editor-wrapper.style.css +1 -1
  82. package/components/Filters/Filters.tsx +3 -5
  83. package/components/Filters/components/Tabs.tsx +19 -7
  84. package/{styles → components/Filters}/filters.scss +3 -3
  85. package/components/Footnotes/FootnotesStandAlone.tsx +4 -2
  86. package/components/HeaderThemeSelector/HeaderThemeSelector.css +61 -5
  87. package/components/Layout/components/Responsive.tsx +14 -6
  88. package/components/Layout/components/Sidebar/components/Sidebar.tsx +1 -1
  89. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +14 -20
  90. package/components/Layout/components/Visualization/index.tsx +50 -38
  91. package/components/Layout/components/Visualization/visualizations.scss +232 -15
  92. package/components/Layout/components/VisualizationContainer.test.tsx +67 -0
  93. package/components/Layout/components/VisualizationContainer.tsx +37 -0
  94. package/components/Layout/components/VisualizationContent.test.tsx +182 -0
  95. package/components/Layout/components/VisualizationContent.tsx +75 -0
  96. package/components/Layout/index.tsx +5 -5
  97. package/components/Layout/styles/editor-utils.scss +3 -3
  98. package/components/Layout/styles/editor.scss +4 -4
  99. package/components/Legend/Legend.Gradient.tsx +7 -1
  100. package/components/Loader/loader.styles.css +2 -2
  101. package/components/Loading.jsx +1 -1
  102. package/components/MediaControls.tsx +10 -3
  103. package/components/MultiSelect/multiselect.styles.css +19 -19
  104. package/components/NestedDropdown/nesteddropdown.styles.css +15 -15
  105. package/components/PaletteSelector/PaletteSelector.css +15 -15
  106. package/components/RichTooltip/richTooltip.css +6 -6
  107. package/components/Table/table.styles.css +2 -2
  108. package/components/Waiting.tsx +1 -1
  109. package/components/_stories/CustomColorsEditor.stories.tsx +37 -0
  110. package/components/_stories/DataTable.stories.tsx +1 -0
  111. package/components/_stories/Filters.stories.tsx +1 -1
  112. package/components/_stories/styles.scss +0 -1
  113. package/components/elements/Button.jsx +1 -1
  114. package/components/elements/Card.jsx +1 -1
  115. package/{styles/v2/components → components/elements}/button.scss +9 -8
  116. package/components/inputs/InputCheckbox.jsx +1 -1
  117. package/components/inputs/InputSelect.tsx +1 -1
  118. package/components/inputs/InputText.jsx +1 -1
  119. package/components/inputs/InputToggle.tsx +1 -1
  120. package/{styles/v2/components/input → components/inputs}/_input-check-radio.scss +2 -2
  121. package/{styles/v2/components/input → components/inputs}/_input-group.scss +3 -3
  122. package/{styles/v2/components/input → components/inputs}/_input-slider.scss +2 -2
  123. package/{styles/v2/components/input → components/inputs}/_input.scss +5 -5
  124. package/{styles/v2/components/input → components/inputs}/index.scss +2 -2
  125. package/{styles → components}/loading.scss +1 -1
  126. package/components/managers/DataDesigner.tsx +1 -1
  127. package/{styles/v2/components → components/managers}/data-designer.scss +6 -7
  128. package/components/ui/Accordion.jsx +1 -1
  129. package/components/ui/Icon.tsx +1 -1
  130. package/components/ui/LoadSpin.jsx +1 -1
  131. package/components/ui/Modal.jsx +1 -1
  132. package/components/ui/Overlay.jsx +1 -1
  133. package/components/ui/Title/index.test.tsx +34 -0
  134. package/components/ui/Title/index.tsx +24 -7
  135. package/components/ui/Title/title.styles.css +119 -25
  136. package/components/ui/Tooltip.tsx +1 -1
  137. package/components/ui/_stories/Title.stories.tsx +1 -1
  138. package/{styles/v2/components → components/ui}/accordion.scss +3 -3
  139. package/components/ui/accordion.styles.css +11 -11
  140. package/{styles/v2/components → components/ui}/modal.scss +2 -2
  141. package/{styles/v2/components → components/ui}/overlay.scss +6 -6
  142. package/{styles/v2/components → components}/ui/tooltip.scss +1 -1
  143. package/{styles → components}/waiting.scss +9 -3
  144. package/data/colorPalettes.ts +18 -5
  145. package/data/mapColorPalettes.ts +10 -0
  146. package/devTemplate/dev.js +285 -0
  147. package/devTemplate/index.html +30 -0
  148. package/devTemplate/preview.html +1503 -0
  149. package/devTemplate/sidebar.css +151 -0
  150. package/dist/cove-main.css +2530 -3901
  151. package/dist/cove-main.css.map +1 -1
  152. package/generateViteConfig.js +111 -2
  153. package/helpers/DataTransform.ts +1 -5
  154. package/helpers/backfillDefaults.ts +35 -0
  155. package/helpers/constants.ts +12 -0
  156. package/helpers/cove/date.ts +64 -3
  157. package/helpers/cove/number.ts +29 -15
  158. package/helpers/cove/string.ts +29 -0
  159. package/helpers/coveUpdateWorker.ts +14 -8
  160. package/helpers/displayDataAsText.ts +1 -1
  161. package/helpers/embed/embedCodeGenerator.ts +80 -0
  162. package/helpers/embed/embedHelper.js +169 -0
  163. package/helpers/embed/filterUtils.ts +121 -0
  164. package/helpers/embed/index.ts +17 -0
  165. package/helpers/embed/urlValidation.ts +119 -0
  166. package/helpers/extractDataAndMetadata.ts +20 -0
  167. package/helpers/fetchRemoteData.ts +14 -8
  168. package/helpers/filterVizData.ts +6 -1
  169. package/helpers/getFileExtension.ts +0 -6
  170. package/helpers/labelHash.ts +9 -0
  171. package/helpers/markupProcessor.ts +56 -38
  172. package/helpers/metrics/types.ts +3 -0
  173. package/helpers/palettes/colorDistributions.ts +1 -1
  174. package/helpers/palettes/utils.ts +12 -12
  175. package/helpers/parseCsvWithQuotes.ts +15 -14
  176. package/helpers/prepareScreenshot.ts +33 -10
  177. package/helpers/testing.ts +44 -0
  178. package/helpers/tests/DataTransform.test.ts +125 -0
  179. package/helpers/tests/abbreviateNumber.test.ts +59 -0
  180. package/helpers/tests/backfillDefaults.test.ts +253 -0
  181. package/helpers/tests/date.test.ts +110 -0
  182. package/helpers/tests/extractDataAndMetadata.test.ts +93 -0
  183. package/helpers/tests/markupProcessor.test.ts +315 -124
  184. package/helpers/tests/number.test.ts +42 -0
  185. package/helpers/tests/prepareScreenshot.test.ts +28 -28
  186. package/helpers/tests/testStandaloneBuild.ts +36 -26
  187. package/helpers/tests/useDataVizClasses.test.ts +66 -0
  188. package/helpers/tests/visualizationWrapperUsage.test.ts +57 -0
  189. package/helpers/useDataVizClasses.ts +13 -7
  190. package/helpers/vegaConfig.ts +1 -1
  191. package/helpers/vegaConfigImport.ts +160 -0
  192. package/helpers/ver/4.24.4.ts +24 -0
  193. package/helpers/ver/4.26.1.ts +1 -1
  194. package/helpers/ver/4.26.2.ts +84 -0
  195. package/helpers/ver/4.26.3.ts +44 -0
  196. package/helpers/ver/4.26.4.ts +31 -0
  197. package/helpers/ver/tests/4.26.1.test.ts +105 -0
  198. package/helpers/ver/tests/4.26.2.test.ts +298 -0
  199. package/helpers/ver/tests/4.26.3.test.ts +168 -0
  200. package/helpers/ver/tests/4.26.4.test.ts +88 -0
  201. package/helpers/ver/tests/coveUpdateWorker.test.ts +57 -0
  202. package/helpers/viewports.ts +2 -0
  203. package/package.json +27 -32
  204. package/styles/_global.scss +7 -7
  205. package/styles/_reset.scss +2 -2
  206. package/styles/{v2/base → base}/_file-selector.scss +4 -4
  207. package/styles/{v2/base → base}/_general.scss +2 -4
  208. package/styles/{v2/base → base}/index.scss +1 -1
  209. package/styles/base.scss +107 -165
  210. package/styles/cove-main.scss +3 -6
  211. package/styles/layout/_component.scss +110 -0
  212. package/styles/{v2/layout → layout}/_data-table.scss +7 -7
  213. package/styles/layout/_wrapper-padding.scss +27 -0
  214. package/styles/{v2/main.scss → main.scss} +3 -1
  215. package/styles/{v2/themes → themes}/_color-definitions.scss +46 -41
  216. package/styles/{_accessibility.scss → utils/_accessibility.scss} +1 -1
  217. package/styles/{v2/utils → utils}/_grid.scss +8 -3
  218. package/styles/{_global-variables.scss → utils/_properties.scss} +133 -112
  219. package/styles/{v2/utils → utils}/index.scss +2 -1
  220. package/types/Annotation.ts +10 -11
  221. package/types/Axis.ts +2 -0
  222. package/types/ComponentStyles.ts +1 -0
  223. package/types/ConfigureData.ts +1 -0
  224. package/types/General.ts +2 -0
  225. package/types/MarkupInclude.ts +1 -0
  226. package/types/MarkupVariable.ts +2 -1
  227. package/types/Palette.ts +22 -0
  228. package/types/Table.ts +9 -0
  229. package/types/Visualization.ts +7 -0
  230. package/_stories/StoryRenderingTests.stories.tsx +0 -164
  231. package/helpers/embedCodeGenerator.ts +0 -109
  232. package/styles/_common-components.css +0 -73
  233. package/styles/_variables.scss +0 -63
  234. package/styles/v2/layout/_component.scss +0 -21
  235. package/styles/v2/utils/_variables.scss +0 -9
  236. package/{styles/v2/components/card.scss → components/elements/card.css} +2 -2
  237. /package/{styles/v2/components → components/ui}/icon.scss +0 -0
  238. /package/{styles/v2/components → components/ui}/loadspin.scss +0 -0
  239. /package/styles/{v2/base → base}/_heading.scss +0 -0
  240. /package/styles/{v2/base → base}/_reset.scss +0 -0
  241. /package/styles/{v2/layout → layout}/_alert.scss +0 -0
  242. /package/styles/{v2/layout → layout}/_progression.scss +0 -0
  243. /package/styles/{v2/layout → layout}/_tooltip.scss +0 -0
  244. /package/styles/{v2/layout → layout}/index.scss +0 -0
  245. /package/styles/{v2/themes → themes}/index.scss +0 -0
  246. /package/styles/{v2/utils → utils}/_align.scss +0 -0
  247. /package/styles/{v2/utils → utils}/_animations.scss +0 -0
  248. /package/styles/{v2/utils → utils}/_breakpoints.scss +0 -0
  249. /package/styles/{v2/utils → utils}/_mixins.scss +0 -0
@@ -1,9 +1,10 @@
1
1
  import { DataTableProps } from '../DataTable'
2
2
  import ScreenReaderText from '../../elements/ScreenReaderText'
3
- import { SortIcon } from './SortIcon'
4
- import { getNewSortBy } from '../helpers/getNewSortBy'
5
- import { publishAnalyticsEvent } from '../../../helpers/metrics/helpers'
6
- import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
3
+ import { SortIcon } from './SortIcon'
4
+ import { getNewSortBy } from '../helpers/getNewSortBy'
5
+ import { publishAnalyticsEvent } from '../../../helpers/metrics/helpers'
6
+ import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
7
+ import { getMapDataTableColumnKeys } from '../helpers/getMapDataTableColumnKeys'
7
8
 
8
9
  type MapHeaderProps = DataTableProps & {
9
10
  sortBy: { column; asc }
@@ -19,23 +20,23 @@ const ColumnHeadingText = ({ text, config }) => {
19
20
  return text
20
21
  }
21
22
 
22
- const MapHeader = ({
23
- columns,
24
- config,
23
+ const MapHeader = ({
24
+ columns,
25
+ config,
25
26
  indexTitle,
26
27
  sortBy,
27
28
  setSortBy,
28
29
  rightAlignedCols,
29
- interactionLabel = ''
30
- }: MapHeaderProps) => {
31
- return (
32
- <tr>
33
- {Object.keys(columns)
34
- .filter(column => columns[column].dataTable === true && columns[column].name)
35
- .map((column, index) => {
36
- let text
37
- if (column && column !== 'geo') {
38
- text = columns[column].label ? columns[column].label : columns[column].name
30
+ interactionLabel = ''
31
+ }: MapHeaderProps) => {
32
+ const orderedColumnKeys = getMapDataTableColumnKeys(columns)
33
+
34
+ return (
35
+ <tr>
36
+ {orderedColumnKeys.map((column, index) => {
37
+ let text
38
+ if (column && column !== 'geo') {
39
+ text = columns[column].label ? columns[column].label : columns[column].name
39
40
  } else {
40
41
  text = config.type === 'map' ? indexTitle : config.xAxis?.dataKey
41
42
  }
@@ -49,7 +50,7 @@ const MapHeader = ({
49
50
  style={{
50
51
  minWidth: (config.table.cellMinWidth || 0) + 'px',
51
52
  textAlign: rightAlignedCols && rightAlignedCols[index] ? 'right' : '',
52
- paddingRight: '1.3em'
53
+ paddingRight: '1.8em'
53
54
  }}
54
55
  key={`col-header-${column}__${index}`}
55
56
  id={column}
@@ -64,7 +65,9 @@ const MapHeader = ({
64
65
  eventAction: 'click',
65
66
  eventLabel: interactionLabel,
66
67
  vizTitle: getVizTitle(config),
67
- specifics: `column: ${newSortBy.column || 'none'}, order: ${newSortBy.asc === true ? 'asc' : newSortBy.asc === false ? 'desc' : 'none'}`
68
+ specifics: `column: ${newSortBy.column || 'none'}, order: ${
69
+ newSortBy.asc === true ? 'asc' : newSortBy.asc === false ? 'desc' : 'none'
70
+ }`
68
71
  })
69
72
  setSortBy(newSortBy)
70
73
  }}
@@ -77,7 +80,9 @@ const MapHeader = ({
77
80
  eventAction: 'keyboard',
78
81
  eventLabel: interactionLabel,
79
82
  vizTitle: getVizTitle(config),
80
- specifics: `column: ${newSortBy.column || 'none'}, order: ${newSortBy.asc === true ? 'asc' : newSortBy.asc === false ? 'desc' : 'none'}`
83
+ specifics: `column: ${newSortBy.column || 'none'}, order: ${
84
+ newSortBy.asc === true ? 'asc' : newSortBy.asc === false ? 'desc' : 'none'
85
+ }`
81
86
  })
82
87
  setSortBy(newSortBy)
83
88
  }
@@ -91,13 +96,14 @@ const MapHeader = ({
91
96
  >
92
97
  <ColumnHeadingText text={text} config={config} />
93
98
  <SortIcon ascending={sortByAsc} />
94
- <span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'
95
- } order`}</span>
96
- </th>
97
- )
98
- })}
99
- </tr >
100
- )
101
- }
99
+ <span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${
100
+ sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'
101
+ } order`}</span>
102
+ </th>
103
+ )
104
+ })}
105
+ </tr>
106
+ )
107
+ }
102
108
 
103
109
  export default MapHeader
@@ -1,22 +1,22 @@
1
1
  .sort-icon {
2
+ align-items: center;
2
3
  display: flex;
3
4
  flex-direction: column;
4
- align-items: center;
5
5
  justify-content: center;
6
6
 
7
7
  position: absolute;
8
- right: 4px;
8
+ right: 13px;
9
9
  top: 50%;
10
10
  transform: translateY(-50%);
11
11
  z-index: 1;
12
12
 
13
13
  svg {
14
- width: 0.75rem;
15
- height: 0.75rem;
16
14
  fill: rgba(255, 255, 255, 0.5);
15
+ height: 0.75rem;
16
+ line-height: 1;
17
17
  margin: 0;
18
18
  padding: 0;
19
- line-height: 1;
19
+ width: 0.75rem;
20
20
 
21
21
  &.active {
22
22
  fill: white;
@@ -1,46 +1,44 @@
1
- .cove,
2
- .cdc-open-viz-module {
1
+ .cove-visualization {
3
2
  .table {
4
- width: unset;
5
3
  min-width: 100%;
4
+ width: unset;
6
5
  }
7
6
 
8
7
  .bs4 .table.table-width-unset {
9
8
  width: unset;
10
9
  }
11
10
 
12
- .collapsed+.table-container {
11
+ .collapsed + .table-container {
13
12
  border-bottom: none;
14
13
  }
15
14
 
16
15
  .table-container {
17
- overflow-x: auto;
18
- border-right: 1px solid var(--lightGray);
19
- border-left: 1px solid var(--lightGray);
20
16
  border-bottom: 1px solid var(--lightGray);
17
+ border-left: 1px solid var(--lightGray);
18
+ border-right: 1px solid var(--lightGray);
19
+ overflow-x: auto;
21
20
  }
22
21
 
23
22
  div.data-table-heading {
24
- position: relative;
25
23
  border: var(--cool-gray-10) 1px solid;
26
24
  border-radius: 6px;
25
+ position: relative;
27
26
 
28
27
  svg {
29
- position: absolute;
30
28
  height: 100%;
31
- width: 15px;
32
- top: 0;
29
+ position: absolute;
33
30
  right: 1em;
31
+ top: 0;
32
+ width: 15px;
34
33
  }
35
34
 
36
35
  &:focus {
37
- z-index: 2;
38
36
  position: relative;
37
+ z-index: 2;
39
38
  }
40
39
  }
41
40
 
42
41
  table.horizontal {
43
-
44
42
  th,
45
43
  td {
46
44
  min-width: 200px;
@@ -48,12 +46,12 @@
48
46
  }
49
47
 
50
48
  table.data-table {
51
- margin-bottom: 0;
49
+ appearance: none;
52
50
  background: #fff;
53
- position: relative;
54
51
  border: none;
55
52
  border-collapse: collapse;
56
- appearance: none;
53
+ margin-bottom: 0;
54
+ position: relative;
57
55
  table-layout: fixed;
58
56
 
59
57
  * {
@@ -61,15 +59,15 @@
61
59
  }
62
60
 
63
61
  thead {
64
- user-select: none;
65
62
  -moz-user-select: none;
66
63
  user-select: none;
64
+ user-select: none;
67
65
 
68
66
  button {
69
67
  background: none;
70
- font-size: initial;
71
- color: #fff !important;
72
68
  border: 0;
69
+ color: #fff !important;
70
+ font-size: initial;
73
71
  }
74
72
 
75
73
  tr {
@@ -81,13 +79,13 @@
81
79
  color: #fff !important;
82
80
 
83
81
  .resizer {
82
+ bottom: 0;
84
83
  cursor: e-resize;
85
- width: 10px;
86
84
  position: absolute;
87
- top: 0;
88
- bottom: 0;
89
85
  right: 0;
86
+ top: 0;
90
87
  touch-action: none;
88
+ width: 10px;
91
89
  }
92
90
 
93
91
  tr {
@@ -96,19 +94,20 @@
96
94
 
97
95
  th,
98
96
  td {
99
- padding: 0.5em 0.7em;
97
+ border-right: 1px solid var(--lightGray) !important;
100
98
  line-height: normal;
99
+ padding: 0.5em 0.7em;
101
100
  position: relative;
102
101
  text-align: left;
103
- border-right: 1px solid var(--lightGray) !important;
104
102
  }
105
103
 
106
104
  th {
107
105
  background-color: var(--primary);
108
- background-repeat: no-repeat;
109
106
  background-position: right 0.5em center;
107
+ background-repeat: no-repeat;
110
108
  background-size: 10px 5px;
111
- color: #fff !important
109
+ color: #fff !important;
110
+ cursor: pointer;
112
111
  }
113
112
 
114
113
  th:last-child,
@@ -134,8 +133,8 @@
134
133
 
135
134
  th,
136
135
  td {
137
- padding: 0.3em 0.7em;
138
136
  border-right: 1px solid rgba(0, 0, 0, 0.1);
137
+ padding: 0.3em 0.7em;
139
138
  white-space: nowrap;
140
139
 
141
140
  &:last-child {
@@ -154,36 +153,35 @@
154
153
  margin-left: 0 !important;
155
154
  }
156
155
  }
157
-
158
156
  }
159
157
 
160
158
  td a {
159
+ bottom: 0;
160
+ color: inherit;
161
+ display: block;
162
+ left: 0;
161
163
  padding: 0.3em 0.7em;
162
164
  position: absolute;
163
- top: 0;
164
- bottom: 0;
165
165
  right: 0;
166
- left: 0;
167
- display: block;
168
- color: inherit;
169
166
  text-decoration: none;
167
+ top: 0;
170
168
  }
171
169
 
172
170
  td div a {
173
- position: relative;
174
- padding: 0;
175
171
  display: inline;
172
+ padding: 0;
173
+ position: relative;
176
174
  }
177
175
 
178
176
  td span.table-link {
179
- text-decoration: underline;
180
- cursor: pointer;
181
177
  color: #075290;
178
+ cursor: pointer;
179
+ text-decoration: underline;
182
180
 
183
181
  svg {
182
+ margin-left: 5px;
184
183
  max-width: 13px;
185
184
  vertical-align: baseline;
186
- margin-left: 5px;
187
185
  }
188
186
  }
189
187
 
@@ -197,16 +195,16 @@
197
195
  position: relative;
198
196
 
199
197
  .no-data-message {
198
+ align-items: center;
200
199
  background: rgba(255, 255, 255, 0.5);
201
- top: 0;
202
- left: 0;
203
- right: 0;
204
200
  bottom: 0;
205
- position: absolute;
206
- text-align: center;
207
201
  display: flex;
208
- align-items: center;
209
202
  justify-content: center;
203
+ left: 0;
204
+ position: absolute;
205
+ right: 0;
206
+ text-align: center;
207
+ top: 0;
210
208
  z-index: 7;
211
209
 
212
210
  :is(h3) {
@@ -231,16 +229,16 @@
231
229
  }
232
230
 
233
231
  .data-table-pagination {
234
- margin: 1rem 0;
235
- display: flex;
236
232
  align-items: center;
233
+ display: flex;
234
+ margin: 1rem 0;
237
235
 
238
236
  ul {
237
+ display: flex;
239
238
  list-style: none;
240
239
  margin: 0 1rem 0 0;
241
- display: flex;
242
240
 
243
- li+li {
241
+ li + li {
244
242
  margin-left: 0.3rem;
245
243
  }
246
244
 
@@ -258,8 +256,8 @@
258
256
 
259
257
  button[disabled] {
260
258
  background: var(--mediumGray);
261
- opacity: 0.3;
262
259
  cursor: default;
260
+ opacity: 0.3;
263
261
 
264
262
  &:hover {
265
263
  background: var(--mediumGray);
@@ -271,9 +269,9 @@
271
269
  .btn-download {
272
270
  color: #fff;
273
271
  float: right;
272
+ margin: 1em 0;
274
273
  text-decoration: none;
275
274
  transition: 0.3s all;
276
- margin: 1em 0;
277
275
 
278
276
  &:hover {
279
277
  transition: 0.3s all;
@@ -281,7 +279,7 @@
281
279
  }
282
280
 
283
281
  .download-links a:not(:last-child) {
284
- margin-right: 10px;
285
282
  display: inline-block;
283
+ margin-right: 10px;
286
284
  }
287
- }
285
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Sorts two values based on a user-defined custom order array.
3
+ * Values not found in customOrder are pushed to the end.
4
+ */
5
+ export const applyCustomOrder = (valueA: string, valueB: string, customOrder: string[]): number => {
6
+ const indexA = customOrder.indexOf(String(valueA))
7
+ const indexB = customOrder.indexOf(String(valueB))
8
+
9
+ // Both found in custom order — sort by position
10
+ if (indexA !== -1 && indexB !== -1) return indexA - indexB
11
+ // Only A found — A comes first
12
+ if (indexA !== -1) return -1
13
+ // Only B found — B comes first
14
+ if (indexB !== -1) return 1
15
+ // Neither found — maintain relative order
16
+ return 0
17
+ }
@@ -31,8 +31,13 @@ const isAdditionalColumn = (column: string, config, rowData) => {
31
31
  return formattingParams
32
32
  }
33
33
 
34
- export const getChartCellValue = (row: string, column: string, config: TableConfig, runtimeData: Object[], rightAxisItemsMap) => {
35
-
34
+ export const getChartCellValue = (
35
+ row: string,
36
+ column: string,
37
+ config: TableConfig,
38
+ runtimeData: Object[],
39
+ rightAxisItemsMap
40
+ ) => {
36
41
  // Variables for xAxis config
37
42
  const { type, dateDisplayFormat, dateParseFormat, dataKey: xAxisDataKey } = config.xAxis || {}
38
43
  const { showMissingDataLabel } = config.general || {}
@@ -54,14 +59,13 @@ export const getChartCellValue = (row: string, column: string, config: TableConf
54
59
  if (column === xAxisDataKey) {
55
60
  const dateFormat = config.table?.dateDisplayFormat || dateDisplayFormat
56
61
  if (type === 'date' || type === 'date-time') {
57
- cellValue = formatDate(dateFormat, parseDate(dateParseFormat, labelValue))
62
+ cellValue = formatDate(dateFormat, parseDate(dateParseFormat, labelValue), config.locale)
58
63
  } else if (type === 'continuous') {
59
64
  cellValue = formatNumber(runtimeData[row][column], 'bottom', false, config)
60
65
  } else {
61
66
  cellValue = labelValue
62
67
  }
63
68
  } else {
64
-
65
69
  let addColParams = isAdditionalColumn(column, config, rowObj)
66
70
 
67
71
  let piePercent = 0
@@ -69,9 +73,8 @@ export const getChartCellValue = (row: string, column: string, config: TableConf
69
73
  piePercent = (_.toNumber(runtimeData[row][column]) / _.sumBy(runtimeData, d => _.toNumber(d[column]))) * 100 || 0
70
74
  }
71
75
 
72
- const valueToFormat = config.visualizationType === 'Pie' && !config.dataFormat.showPiePercent
73
- ? piePercent
74
- : runtimeData[row][column]
76
+ const valueToFormat =
77
+ config.visualizationType === 'Pie' && !config.dataFormat.showPiePercent ? piePercent : runtimeData[row][column]
75
78
 
76
79
  const hasAdditionalParams = Object.keys(addColParams).length > 0
77
80
 
@@ -0,0 +1,22 @@
1
+ import { Column } from '../../../types/Column'
2
+
3
+ const isVisibleDataTableColumn = ([, column]: [string, Column]) => {
4
+ return column?.dataTable === true && !!column?.name
5
+ }
6
+
7
+ export const getMapDataTableColumnKeys = (columns: Record<string, Column> = {}): string[] => {
8
+ return Object.entries(columns)
9
+ .filter(isVisibleDataTableColumn)
10
+ .map(([key, column], declarationIndex) => ({ key, order: column.order, declarationIndex }))
11
+ .sort((a, b) => {
12
+ const aOrder = a.order ?? Number.MAX_SAFE_INTEGER
13
+ const bOrder = b.order ?? Number.MAX_SAFE_INTEGER
14
+
15
+ if (aOrder !== bOrder) {
16
+ return aOrder - bOrder
17
+ }
18
+
19
+ return a.declarationIndex - b.declarationIndex
20
+ })
21
+ .map(({ key }) => key)
22
+ }
@@ -16,6 +16,12 @@ export const getSeriesName = (column: string, config: TableConfig) => {
16
16
  return userDefinedSeries.name
17
17
  }
18
18
  if (config.runtimeSeriesLabels && config.runtimeSeriesLabels[column]) return config.runtimeSeriesLabels[column]
19
+
20
+ // For pie charts, use yAxis.label if the column is the yAxis data key
21
+ if (config.visualizationType === 'Pie' && column === config.yAxis?.dataKey && config.yAxis?.label) {
22
+ return config.yAxis.label
23
+ }
24
+
19
25
  const columnIsDataKey = column === config.xAxis?.dataKey
20
26
  const indexLabel = config.table?.indexLabel
21
27
  return columnIsDataKey && indexLabel ? indexLabel : getLabel(column, config)
@@ -3,8 +3,11 @@ import CellAnchor from '../components/CellAnchor'
3
3
  import { DataTableProps } from '../DataTable'
4
4
  import { ReactNode } from 'react'
5
5
  import { displayDataAsText } from '@cdc/core/helpers/displayDataAsText'
6
+ import parse from 'html-react-parser'
6
7
  import _ from 'lodash'
7
8
  import { hashObj } from '../../../helpers/hashObj'
9
+ import { sanitizeToSvgId } from '../../../helpers/cove/string'
10
+ import { getMapDataTableColumnKeys } from './getMapDataTableColumnKeys'
8
11
 
9
12
  type MapRowsProps = DataTableProps & {
10
13
  rows: string[]
@@ -22,9 +25,10 @@ const getGeoLabel = (config, row, formatLegendLocation, displayGeoName, runtimeD
22
25
  const { geoType, type } = config.general
23
26
 
24
27
  let labelValue
28
+ const displayOverride = runtimeData?.[row]?.[config.columns?.geo?.displayColumn]
25
29
  if (!['single-state', 'us-county'].includes(geoType) || type === 'us-geocode') {
26
30
  // Use the row (UID) for lookup - this allows "US-AL" to become "Alabama"
27
- labelValue = displayGeoName(row)
31
+ labelValue = displayGeoName(row, displayOverride)
28
32
 
29
33
  // If displayGeoName returned the same value (not found in lookups), use the raw imported data
30
34
  if (labelValue === row && runtimeData && config.columns?.geo?.name) {
@@ -67,11 +71,13 @@ export const getMapRowData = (
67
71
  displayGeoName: (row: string) => string,
68
72
  filterColumns: string[]
69
73
  ) => {
74
+ const orderedColumnKeys = getMapDataTableColumnKeys(columns as any)
75
+
70
76
  return rows.map((row: string) => {
71
77
  const dataRow = {}
72
78
  ;[
73
79
  ...filterColumns,
74
- ...Object.keys(columns).filter(column => columns[column].dataTable === true && columns[column].name)
80
+ ...orderedColumnKeys
75
81
  ].map(column => {
76
82
  const label = columns[column]?.label || columns[column]?.name || column
77
83
  if (column === 'geo') {
@@ -103,10 +109,10 @@ const mapCellArray = ({
103
109
  getPatternForRow
104
110
  }: MapRowsProps): ReactNode[][] => {
105
111
  const { allowMapZoom, geoType, type } = config.general
112
+ const orderedColumnKeys = getMapDataTableColumnKeys(columns as any)
113
+
106
114
  return rows.map(row =>
107
- Object.keys(columns)
108
- .filter(column => columns[column].dataTable === true && columns[column].name)
109
- .map(column => {
115
+ orderedColumnKeys.map(column => {
110
116
  if (column === 'geo') {
111
117
  const rowObj = runtimeData[row]
112
118
  if (!rowObj) {
@@ -128,26 +134,29 @@ const mapCellArray = ({
128
134
  // Check for pattern information
129
135
  const patternInfo = getPatternForRow(rowObj, config)
130
136
  const mapId = config.runtime?.uniqueId || 'map'
137
+ const sanitizedPatternDataKey = sanitizeToSvgId(patternInfo?.dataKey || '')
131
138
 
132
139
  return (
133
- <div className='col-12'>
134
- {validColor ? (
135
- patternInfo ? (
136
- <LegendShape
137
- fill={legendColor[0]}
138
- patternInfo={{
139
- pattern: patternInfo.pattern,
140
- patternId: `${mapId}--${patternInfo.dataKey}--${patternInfo.patternIndex}--table`,
141
- size: patternInfo.size,
142
- color: patternInfo.color
143
- }}
144
- />
140
+ <div style={{ display: 'flex', alignItems: 'flex-start', flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
141
+ <div style={{ flexShrink: 0 }}>
142
+ {validColor ? (
143
+ patternInfo ? (
144
+ <LegendShape
145
+ fill={legendColor[0]}
146
+ patternInfo={{
147
+ pattern: patternInfo.pattern,
148
+ patternId: `${mapId}--${sanitizedPatternDataKey}--${patternInfo.patternIndex}--table`,
149
+ size: patternInfo.size,
150
+ color: patternInfo.color
151
+ }}
152
+ />
153
+ ) : (
154
+ <LegendShape fill={legendColor[0]} />
155
+ )
145
156
  ) : (
146
- <LegendShape fill={legendColor[0]} />
147
- )
148
- ) : (
149
- <div className='d-inline-block me-2' style={{ width: '1rem', height: '1rem' }} />
150
- )}
157
+ <div className='me-2' style={{ width: '1rem', height: '1rem' }} />
158
+ )}
159
+ </div>
151
160
  <CellAnchor
152
161
  markup={labelValue}
153
162
  row={rowObj}
@@ -160,7 +169,8 @@ const mapCellArray = ({
160
169
  } else {
161
170
  const rowData = runtimeData[row]
162
171
  const dataValue = getDataValue(config, rowData, column)
163
- return displayDataAsText(dataValue, column, config)
172
+ const text = displayDataAsText(dataValue, column, config)
173
+ return typeof text === 'string' ? parse(text) : text
164
174
  }
165
175
  })
166
176
  )
@@ -1,5 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest'
2
2
  import { getMapRowData } from '../mapCellMatrix'
3
+ import { getMapDataTableColumnKeys } from '../getMapDataTableColumnKeys'
3
4
 
4
5
  describe('getMapRowData', () => {
5
6
  const columns = {
@@ -77,4 +78,36 @@ describe('getMapRowData', () => {
77
78
  }
78
79
  ])
79
80
  })
81
+
82
+ it('orders visible columns using display order instead of config key order', () => {
83
+ const orderedConfig = {
84
+ ...config,
85
+ general: { ...config.general, geoType: 'us-state' }
86
+ }
87
+ const orderedColumns = {
88
+ geo: { dataTable: true, name: 'geo', label: 'Geo', order: 3 },
89
+ column1: { dataTable: true, name: 'column1', label: 'Column 1', order: 2 },
90
+ column2: { dataTable: true, name: 'column2', label: 'Column 2', order: 1 },
91
+ hidden: { dataTable: false, name: 'hidden', label: 'Hidden', order: 4 }
92
+ }
93
+
94
+ expect(getMapDataTableColumnKeys(orderedColumns)).toEqual(['column2', 'column1', 'geo'])
95
+
96
+ const orderedResult = getMapRowData(
97
+ rows,
98
+ orderedColumns,
99
+ orderedConfig,
100
+ formatLegendLocation,
101
+ runtimeData,
102
+ displayGeoName,
103
+ []
104
+ )
105
+
106
+ expect(Object.keys(orderedResult[0])).toEqual(['Column 2', 'Column 1', 'Geo'])
107
+ expect(orderedResult[0]).toEqual({
108
+ 'Column 2': 'data4',
109
+ 'Column 1': 'data3',
110
+ Geo: 'displayGeoName -> row2'
111
+ })
112
+ })
80
113
  })
@@ -6,14 +6,22 @@ import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
6
6
  type DownloadButtonProps = {
7
7
  rawData: any[]
8
8
  fileName: string
9
- headerColor: string
10
- skipId: string | number
9
+ skipId?: string | number
11
10
  configUrl?: string
12
11
  interactionLabel?: string
13
12
  title?: string
13
+ config?: any
14
14
  }
15
15
 
16
- const DownloadButton = ({ rawData, fileName, headerColor, skipId, interactionLabel, configUrl, title, config }: DownloadButtonProps) => {
16
+ const DownloadButton = ({
17
+ rawData,
18
+ fileName,
19
+ skipId,
20
+ interactionLabel,
21
+ configUrl,
22
+ title,
23
+ config
24
+ }: DownloadButtonProps) => {
17
25
  const linkRef = useRef<HTMLAnchorElement>(null)
18
26
 
19
27
  const handleDownload = (event: React.MouseEvent<HTMLAnchorElement>) => {
@@ -59,13 +67,13 @@ const DownloadButton = ({ rawData, fileName, headerColor, skipId, interactionLab
59
67
  type='button'
60
68
  onClick={handleDownload}
61
69
  aria-label='Download this data in a CSV file format.'
62
- className={`${headerColor} no-border`}
63
- id={`${skipId}`}
70
+ className='no-border'
71
+ id={skipId != null ? `${skipId}` : undefined}
64
72
  data-html2canvas-ignore
65
73
  role='button'
66
74
  style={{ cursor: 'pointer' }}
67
75
  >
68
- Download Data (CSV)
76
+ {config?.table?.downloadDataLabel || 'Download Data (CSV)'}
69
77
  </a>
70
78
  )
71
79
  }