@cdc/chart 4.26.2 → 4.26.4

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 (165) hide show
  1. package/CONFIG.md +169 -0
  2. package/README.md +42 -17
  3. package/dist/cdcchart.js +39419 -35180
  4. package/examples/__data__/data-with-metadata.json +10 -0
  5. package/examples/feature/bar/additional-column-tooltip.json +3 -3
  6. package/examples/feature/deviation/planet-deviation-config.json +2 -2
  7. package/examples/feature/paired-bar/paired-bar-example.json +2 -2
  8. package/examples/feature/pie/planet-pie-example-config.json +2 -1
  9. package/examples/feature/tests-case-rate/case-rate-example-config.json +2 -2
  10. package/examples/feature/tests-covid/covid-confidence-example-config.json +2 -2
  11. package/examples/feature/tests-covid/covid-example-config.json +2 -2
  12. package/examples/feature/tests-cutoff/cutoff-example-config.json +2 -2
  13. package/examples/feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json +2 -2
  14. package/examples/metadata-variables.json +58 -0
  15. package/examples/minimal-example.json +24 -0
  16. package/examples/private/test2.json +35012 -0
  17. package/package.json +4 -4
  18. package/src/CdcChart.tsx +8 -4
  19. package/src/CdcChartComponent.tsx +326 -290
  20. package/src/_stories/Chart.CustomColors.smoke.stories.tsx +97 -0
  21. package/src/_stories/Chart.Defaults.smoke.stories.tsx +95 -0
  22. package/src/_stories/{Chart.HTMLInDataTable.stories.tsx → Chart.HTMLInDataTable.smoke.stories.tsx} +10 -0
  23. package/src/_stories/Chart.Minimal.smoke.stories.tsx +25 -0
  24. package/src/_stories/{Chart.SmallMultiples.stories.tsx → Chart.SmallMultiples.smoke.stories.tsx} +17 -1
  25. package/src/_stories/Chart.SmallestLeftAxisMax.smoke.stories.tsx +64 -0
  26. package/src/_stories/{Chart.stories.tsx → Chart.smoke.stories.tsx} +48 -2
  27. package/src/_stories/{Chart.tooltip.stories.tsx → Chart.tooltip.smoke.stories.tsx} +68 -0
  28. package/src/_stories/ChartBar.Editor.stories.tsx +177 -115
  29. package/src/_stories/ChartBrush.Editor.stories.tsx +11 -25
  30. package/src/_stories/ChartEditor.Editor.stories.tsx +24 -1
  31. package/src/_stories/{Chart.CustomColors.stories.tsx → ChartLine.DashedDates.smoke.stories.tsx} +4 -4
  32. package/src/_stories/_mock/line_chart_dashed_dates.json +276 -0
  33. package/src/_stories/_mock/paired-bar-abbr.json +421 -0
  34. package/src/_stories/_mock/paired-bar.json +1 -1
  35. package/src/_stories/_mock/pie_custom_colors.json +268 -0
  36. package/src/_stories/_mock/smallest_left_axis_max.json +104 -0
  37. package/src/components/Annotations/components/AnnotationDraggable.styles.css +10 -10
  38. package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -1
  39. package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -1
  40. package/src/components/Annotations/components/AnnotationList.styles.css +11 -11
  41. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -2
  42. package/src/components/Axis/BottomAxis.tsx +10 -3
  43. package/src/components/Axis/LeftAxis.tsx +2 -4
  44. package/src/components/Axis/LeftAxisGridlines.tsx +1 -3
  45. package/src/components/Axis/PairedBarAxis.tsx +10 -4
  46. package/src/components/BarChart/components/BarChart.Horizontal.tsx +21 -33
  47. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +21 -35
  48. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +21 -36
  49. package/src/components/BarChart/components/BarChart.Vertical.tsx +21 -34
  50. package/src/components/BarChart/components/BarChart.tsx +2 -6
  51. package/src/components/BarChart/helpers/getPatternUrl.ts +94 -0
  52. package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +134 -0
  53. package/src/components/BarChart/helpers/useBarChart.ts +4 -1
  54. package/src/components/BoxPlot/BoxPlot.Vertical.tsx +3 -3
  55. package/src/components/Brush/BrushSelector.tsx +2 -1
  56. package/src/components/Brush/MiniChartPreview.tsx +21 -26
  57. package/src/components/EditorPanel/EditorPanel.tsx +692 -954
  58. package/src/components/EditorPanel/EditorPanelContext.ts +1 -1
  59. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +218 -167
  60. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +0 -78
  61. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +39 -1
  62. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +169 -135
  63. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +154 -109
  64. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
  65. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +95 -28
  66. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +45 -42
  67. package/src/components/EditorPanel/editor-panel.scss +1 -1
  68. package/src/components/EditorPanel/useEditorPermissions.ts +1 -10
  69. package/src/components/Forecasting/Forecasting.tsx +2 -2
  70. package/src/components/ForestPlot/ForestPlot.tsx +26 -22
  71. package/src/components/HorizonChart/HorizonChart.tsx +10 -2
  72. package/src/components/HoverLine/HoverLine.tsx +6 -8
  73. package/src/components/Legend/Legend.Component.tsx +4 -4
  74. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +9 -4
  75. package/src/components/Legend/LegendGroup/LegendGroup.tsx +3 -1
  76. package/src/components/Legend/helpers/createFormatLabels.tsx +3 -2
  77. package/src/components/Legend/helpers/index.ts +0 -2
  78. package/src/components/LineChart/LineChartProps.ts +1 -0
  79. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +3 -2
  80. package/src/components/LineChart/index.tsx +11 -4
  81. package/src/components/LinearChart/VisualizationRenderer.tsx +11 -1
  82. package/src/components/LinearChart/tests/LinearChart.test.tsx +224 -0
  83. package/src/components/LinearChart/tests/mockConfigContext.ts +2 -0
  84. package/src/components/LinearChart.tsx +124 -47
  85. package/src/components/PairedBarChart.jsx +2 -2
  86. package/src/components/PieChart/PieChart.tsx +30 -13
  87. package/src/components/RadarChart/RadarChart.tsx +1 -1
  88. package/src/components/Regions/components/Regions.tsx +36 -26
  89. package/src/components/Sankey/components/Sankey.tsx +3 -3
  90. package/src/components/Sankey/sankey.scss +1 -1
  91. package/src/components/ScatterPlot/ScatterPlot.jsx +19 -17
  92. package/src/components/SmallMultiples/SmallMultipleTile.tsx +1 -0
  93. package/src/components/SmallMultiples/SmallMultiples.css +5 -5
  94. package/src/components/Sparkline/components/SparkLine.tsx +1 -1
  95. package/src/components/Sparkline/index.scss +4 -2
  96. package/src/components/WarmingStripes/WarmingStripes.tsx +5 -3
  97. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +8 -8
  98. package/src/data/initial-state.js +24 -14
  99. package/src/data/legacy-defaults.ts +18 -0
  100. package/src/helpers/abbreviateNumber.ts +24 -17
  101. package/src/helpers/calculateLeftYAxisWidth.ts +193 -0
  102. package/src/helpers/getChartPatternId.ts +17 -0
  103. package/src/helpers/getMinMax.ts +16 -2
  104. package/src/helpers/seriesColumnSettings.ts +114 -0
  105. package/src/helpers/tests/countNumOfTicks.test.ts +77 -0
  106. package/src/helpers/tests/seriesColumnSettings.test.ts +84 -0
  107. package/src/helpers/tests/tooltipHelpers.test.ts +151 -0
  108. package/src/helpers/tooltipHelpers.ts +176 -0
  109. package/src/hooks/useHighlightedBars.ts +9 -16
  110. package/src/hooks/useProgrammaticTooltip.ts +16 -2
  111. package/src/hooks/useRightAxis.ts +14 -0
  112. package/src/hooks/useScales.ts +92 -56
  113. package/src/hooks/useSmallMultipleSynchronization.ts +3 -4
  114. package/src/hooks/useTooltip.tsx +186 -40
  115. package/src/scss/main.scss +152 -79
  116. package/src/test/CdcChart.test.jsx +26 -2
  117. package/src/types/ChartConfig.ts +5 -0
  118. package/tests/fixtures/chart-config-with-metadata.json +29 -0
  119. package/tests/fixtures/data-with-metadata.json +10 -0
  120. package/examples/chart-regression-2.json +0 -2360
  121. package/examples/feature/__data__/age-adjusted-rates.json +0 -1486
  122. package/examples/feature/__data__/city-temperature.json +0 -2198
  123. package/examples/feature/bar/Barchart_with_negative.json +0 -34
  124. package/examples/feature/boxplot/box-plot-data.json +0 -71
  125. package/examples/feature/boxplot/boxplot-data.json +0 -156
  126. package/examples/feature/combo/combochart-categories_are_numbers .json +0 -18
  127. package/examples/feature/forest-plot/log.json +0 -26
  128. package/examples/feature/tests-date-exclusions/date-exclusions-data.json +0 -162
  129. package/examples/region-issue.json +0 -2065
  130. package/examples/xaxis.json +0 -493
  131. /package/examples/feature/bar/{tall-data.json → __data__/tall-data.json} +0 -0
  132. /package/examples/feature/deviation/{planet-deviation-data.json → __data__/planet-deviation-data.json} +0 -0
  133. /package/examples/feature/paired-bar/{paired-bar-data.json → __data__/paired-bar-data.json} +0 -0
  134. /package/examples/feature/paired-bar/{paired-bar-formatted.json → __data__/paired-bar-formatted.json} +0 -0
  135. /package/examples/feature/tests-case-rate/{case-rate-example-data.json → __data__/case-rate-example-data.json} +0 -0
  136. /package/examples/feature/tests-covid/{covid-example-data-confidence.json → __data__/covid-example-data-confidence.json} +0 -0
  137. /package/examples/feature/tests-covid/{covid-example-data.json → __data__/covid-example-data.json} +0 -0
  138. /package/examples/feature/tests-cutoff/{cutoff-example-data.json → __data__/cutoff-example-data.json} +0 -0
  139. /package/examples/feature/tests-non-numerics/{planet-example-data-nonnumeric.json → __data__/planet-example-data-nonnumeric.json} +0 -0
  140. /package/src/_stories/{Chart.Anchors.stories.tsx → Chart.Anchors.smoke.stories.tsx} +0 -0
  141. /package/src/_stories/{Chart.BoxPlot.stories.tsx → Chart.BoxPlot.smoke.stories.tsx} +0 -0
  142. /package/src/_stories/{Chart.CI.stories.tsx → Chart.CI.smoke.stories.tsx} +0 -0
  143. /package/src/_stories/{Chart.Combo.stories.tsx → Chart.Combo.smoke.stories.tsx} +0 -0
  144. /package/src/_stories/{Chart.DynamicSeries.stories.tsx → Chart.DynamicSeries.smoke.stories.tsx} +0 -0
  145. /package/src/_stories/{Chart.Filters.stories.tsx → Chart.Filters.smoke.stories.tsx} +0 -0
  146. /package/src/_stories/{Chart.Forecast.stories.tsx → Chart.Forecast.smoke.stories.tsx} +0 -0
  147. /package/src/_stories/{Chart.Legend.Gradient.stories.tsx → Chart.Legend.Gradient.smoke.stories.tsx} +0 -0
  148. /package/src/_stories/{Chart.Patterns.stories.tsx → Chart.Patterns.smoke.stories.tsx} +0 -0
  149. /package/src/_stories/{Chart.PreserveDecimals.stories.tsx → Chart.PreserveDecimals.smoke.stories.tsx} +0 -0
  150. /package/src/_stories/{Chart.Regions.Categorical.stories.tsx → Chart.Regions.Categorical.smoke.stories.tsx} +0 -0
  151. /package/src/_stories/{Chart.Regions.DateScale.stories.tsx → Chart.Regions.DateScale.smoke.stories.tsx} +0 -0
  152. /package/src/_stories/{Chart.Regions.DateTimeScale.stories.tsx → Chart.Regions.DateTimeScale.smoke.stories.tsx} +0 -0
  153. /package/src/_stories/{Chart.ScatterPlot.stories.tsx → Chart.ScatterPlot.smoke.stories.tsx} +0 -0
  154. /package/src/_stories/{ChartAnnotation.stories.tsx → ChartAnnotation.smoke.stories.tsx} +0 -0
  155. /package/src/_stories/{ChartAxisLabels.stories.tsx → ChartAxisLabels.smoke.stories.tsx} +0 -0
  156. /package/src/_stories/{ChartAxisTitles.stories.tsx → ChartAxisTitles.smoke.stories.tsx} +0 -0
  157. /package/src/_stories/{ChartBrush.Matrix.Continuous.stories.tsx → ChartBrush.Matrix.Continuous.smoke.stories.tsx} +0 -0
  158. /package/src/_stories/{ChartBrush.Matrix.Date.stories.tsx → ChartBrush.Matrix.Date.smoke.stories.tsx} +0 -0
  159. /package/src/_stories/{ChartBrush.Matrix.DateTime.stories.tsx → ChartBrush.Matrix.DateTime.smoke.stories.tsx} +0 -0
  160. /package/src/_stories/{ChartBrush.stories.tsx → ChartBrush.smoke.stories.tsx} +0 -0
  161. /package/src/_stories/{ChartLine.QuadrantAngles.stories.tsx → ChartLine.QuadrantAngles.smoke.stories.tsx} +0 -0
  162. /package/src/_stories/{ChartLine.Suppression.stories.tsx → ChartLine.Suppression.smoke.stories.tsx} +0 -0
  163. /package/src/_stories/{ChartLine.Symbols.stories.tsx → ChartLine.Symbols.smoke.stories.tsx} +0 -0
  164. /package/src/_stories/{ChartPrefixSuffix.stories.tsx → ChartPrefixSuffix.smoke.stories.tsx} +0 -0
  165. /package/src/_stories/{TechAdoptionWithLinks.stories.tsx → TechAdoptionWithLinks.smoke.stories.tsx} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { useState, useEffect, useCallback, memo, useContext, useMemo } from 'react'
2
- import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
2
+ import { Draggable } from '@hello-pangea/dnd'
3
3
  import chroma from 'chroma-js'
4
- import { isDateScale } from '@cdc/core/helpers/cove/date'
4
+ import { getAutoDetectedDateParseFormat, isDateScale } from '@cdc/core/helpers/cove/date'
5
5
  import {
6
6
  Accordion,
7
7
  AccordionItem,
@@ -16,10 +16,12 @@ import AdvancedEditor from '@cdc/core/components/AdvancedEditor'
16
16
  import Icon from '@cdc/core/components/ui/Icon'
17
17
  import ColumnsEditor from '@cdc/core/components/EditorPanel/ColumnsEditor'
18
18
  import DataTableEditor from '@cdc/core/components/EditorPanel/DataTableEditor'
19
+ import GroupedList from '@cdc/core/components/EditorPanel/GroupedList'
19
20
  import VizFilterEditor from '@cdc/core/components/EditorPanel/VizFilterEditor'
20
21
  import Tooltip from '@cdc/core/components/ui/Tooltip'
21
22
  import { Select, TextField, CheckBox } from '@cdc/core/components/EditorPanel/Inputs'
22
23
  import MultiSelect from '@cdc/core/components/MultiSelect'
24
+ import Button from '@cdc/core/components/elements/Button'
23
25
  import { viewports } from '@cdc/core/helpers/getViewport'
24
26
  import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
25
27
  import PanelMarkup from '@cdc/core/components/EditorPanel/components/PanelMarkup'
@@ -45,7 +47,10 @@ import '@cdc/core/components/EditorPanel/EditorPanel.styles.css'
45
47
  import './editor-panel.scss'
46
48
  import { Anchor } from '@cdc/core/types/Axis'
47
49
  import EditorPanelContext from './EditorPanelContext'
48
- import _ from 'lodash'
50
+ import cloneDeep from 'lodash/cloneDeep'
51
+ import flatMap from 'lodash/flatMap'
52
+ import keys from 'lodash/keys'
53
+ import uniq from 'lodash/uniq'
49
54
  import { adjustedSymbols as symbolCodes } from '@cdc/core/helpers/footnoteSymbols'
50
55
  import { updateFieldRankByValue } from './helpers/updateFieldRankByValue'
51
56
  import cloneConfig from '@cdc/core/helpers/cloneConfig'
@@ -55,6 +60,7 @@ import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
55
60
  import { paletteMigrationMap, twoColorPaletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
56
61
  import { isV1Palette, migratePaletteWithMap } from '@cdc/core/helpers/palettes/utils'
57
62
  import { USE_V2_MIGRATION } from '@cdc/core/helpers/constants'
63
+ import { getSeriesOwnedColumnNames } from '../../helpers/seriesColumnSettings'
58
64
 
59
65
  interface PreliminaryProps {
60
66
  config: ChartConfig
@@ -70,7 +76,7 @@ const PreliminaryData: React.FC<PreliminaryProps> = ({ config, updateConfig, dat
70
76
  const hasComboBarSeries = isCombo && barSeriesExists
71
77
 
72
78
  const getColumnOptions = () => {
73
- return _.uniq(_.flatMap(data, _.keys))
79
+ return uniq(flatMap(data, keys))
74
80
  }
75
81
 
76
82
  const getTypeOptions = () => {
@@ -154,334 +160,525 @@ const PreliminaryData: React.FC<PreliminaryProps> = ({ config, updateConfig, dat
154
160
  updateConfig({ ...config, preliminaryData })
155
161
  }
156
162
 
163
+ const moveColumn = (sourceIndex, destinationIndex) => {
164
+ const preliminaryData = config.preliminaryData ? [...config.preliminaryData] : []
165
+ const [movedItem] = preliminaryData.splice(sourceIndex, 1)
166
+ preliminaryData.splice(destinationIndex, 0, movedItem)
167
+ updateConfig({ ...config, preliminaryData })
168
+ }
169
+
157
170
  return (
158
171
  <>
159
- {config.preliminaryData &&
160
- config.preliminaryData?.map(
161
- (
162
- {
163
- displayGray,
164
- circleSize,
165
- column,
166
- displayLegend,
167
- displayTable,
168
- displayTooltip,
169
- label,
170
- seriesKeys,
171
- style,
172
- symbol,
173
- type,
174
- value,
175
- hideBarSymbol,
176
- hideLineStyle
177
- },
178
- i
179
- ) => {
180
- return (
181
- <div key={`preliminaryData-${i}`} className='edit-block'>
182
- <p> {type === 'suppression' ? 'Suppressed' : 'Effect'} Data</p>
183
- <button
184
- type='button'
185
- className='btn btn-danger'
186
- onClick={event => {
187
- event.preventDefault()
188
- removeColumn(i)
189
- }}
190
- >
191
- Remove
192
- </button>
193
-
194
- <Select
195
- value={type}
196
- initial={config.visualizationType == 'Bar' ? '' : 'Select'}
197
- fieldName='type'
198
- label='Type'
199
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
200
- options={getTypeOptions()}
201
- />
202
- {type === 'suppression' ? (
203
- <>
204
- <Select
205
- tooltip={
206
- <Tooltip style={{ textTransform: 'none' }}>
207
- <Tooltip.Target>
208
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
209
- </Tooltip.Target>
210
- <Tooltip.Content>
211
- <p>
212
- {' '}
213
- Without a selected "Data Series", the suppression symbol will be applied for all series in
214
- the current dataset visualization. However, choosing a specific "data series" will isolate
215
- the suppression to that series.
216
- </p>
217
- </Tooltip.Content>
218
- </Tooltip>
219
- }
220
- value={column}
221
- initial='Select'
222
- fieldName='column'
223
- label='Add Data Series'
224
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
225
- options={config.runtime?.seriesKeys}
226
- />
227
- <TextField
228
- value={value}
229
- fieldName='value'
230
- label='Suppressed Data Value'
231
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
232
- />
233
- {(hasComboLineSeries || config.visualizationType === 'Line') && (
234
- <>
235
- <Select
236
- tooltip={
237
- <Tooltip style={{ textTransform: 'none' }}>
238
- <Tooltip.Target>
239
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
240
- </Tooltip.Target>
241
- <Tooltip.Content>
242
- <p>
243
- The recommended approach for presenting data is to include a footnote indicating any
244
- data suppression.
245
- </p>
246
- </Tooltip.Content>
247
- </Tooltip>
248
- }
249
- value={style}
250
- initial='Select'
251
- fieldName='style'
252
- label={'suppression line style'}
253
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
254
- options={getStyleOptions(type)}
255
- />
256
- <CheckBox
257
- value={hideLineStyle}
258
- fieldName='hideLineStyle'
259
- label='Hide Suppressed line Style'
260
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
261
- />
262
- </>
263
- )}
264
-
265
- {(hasComboBarSeries || config.visualizationType === 'Bar') && (
266
- <>
267
- <Select
268
- tooltip={
269
- <Tooltip style={{ textTransform: 'none' }}>
270
- <Tooltip.Target>
271
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
272
- </Tooltip.Target>
273
- <Tooltip.Content>
274
- <p>
275
- A symbol is <i>required</i> to indicate suppressed data. We suggest "double
276
- asterisks." If "double asterisks" are already used elsewhere (e.g., footnotes), please
277
- select an alternative symbol from the menu to denote data suppression.
278
- </p>
279
- </Tooltip.Content>
280
- </Tooltip>
281
- }
282
- value={symbol}
283
- initial='Select'
284
- fieldName='symbol'
285
- label={config.visualizationType === 'Combo' ? 'suppression bar symbol' : 'suppression symbol'}
286
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
287
- options={getSymbolOptions()}
288
- />
289
- <CheckBox
290
- value={hideBarSymbol}
291
- fieldName='hideBarSymbol'
292
- label='Hide Suppressed Bar Symbol '
293
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
294
- />
295
- </>
296
- )}
172
+ <GroupedList
173
+ items={config.preliminaryData}
174
+ label='Suppressed Data'
175
+ droppableId='chart-preliminary-data-order'
176
+ onDragEnd={({ source, destination }) => {
177
+ if (!destination || source.index === destination.index) return
178
+ moveColumn(source.index, destination.index)
179
+ }}
180
+ renderItem={(
181
+ {
182
+ displayGray,
183
+ circleSize,
184
+ column,
185
+ displayLegend,
186
+ displayTable,
187
+ displayTooltip,
188
+ label,
189
+ seriesKeys,
190
+ style,
191
+ symbol,
192
+ type,
193
+ value,
194
+ hideBarSymbol,
195
+ hideLineStyle
196
+ },
197
+ i
198
+ ) => (
199
+ <Draggable key={`preliminaryData-${i}`} draggableId={`preliminaryData-${i}`} index={i}>
200
+ {(provided, snapshot) => (
201
+ <div
202
+ className={snapshot.isDragging ? 'currently-dragging' : ''}
203
+ style={provided.draggableProps.style}
204
+ ref={provided.innerRef}
205
+ {...provided.draggableProps}
206
+ {...provided.dragHandleProps}
207
+ >
208
+ <Accordion allowZeroExpanded>
209
+ <AccordionItem className='series-item series-item--chart'>
210
+ <AccordionItemHeading className='series-item__title'>
211
+ <AccordionItemButton className='accordion__button'>
212
+ <Icon display='move' size={15} style={{ cursor: 'default' }} />
213
+ {type === 'suppression' ? 'Suppressed Data' : `Effect Data ${i + 1}`}
214
+ </AccordionItemButton>
215
+ </AccordionItemHeading>
216
+ <AccordionItemPanel>
217
+ <div className='series-item__panel-actions'>
218
+ <Button
219
+ type='button'
220
+ variant='danger'
221
+ size='sm'
222
+ className='grouped-list__remove'
223
+ onClick={event => {
224
+ event.preventDefault()
225
+ removeColumn(i)
226
+ }}
227
+ >
228
+ Remove
229
+ </Button>
230
+ </div>
297
231
 
298
- <TextField
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>This label will display in the tooltip and legend.</p>
306
- </Tooltip.Content>
307
- </Tooltip>
308
- }
309
- value={label ? label : 'Suppressed'}
310
- fieldName='label'
311
- label='Suppressed Data Label'
312
- placeholder=''
313
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
314
- />
315
- <CheckBox
316
- display={config.visualizationSubType === 'regular'}
317
- tooltip={
318
- <Tooltip style={{ textTransform: 'none' }}>
319
- <Tooltip.Target>
320
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
321
- </Tooltip.Target>
322
- <Tooltip.Content>
323
- <p>
324
- Deselecting the "Display In Tooltips" option prevents suppressed values from appearing in
325
- tooltips.
326
- </p>
327
- </Tooltip.Content>
328
- </Tooltip>
329
- }
330
- value={displayTooltip}
331
- fieldName='displayTooltip'
332
- label='Display in tooltips'
333
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
334
- />
335
- <CheckBox
336
- display={config.visualizationSubType === 'regular'}
337
- tooltip={
338
- <Tooltip style={{ textTransform: 'none' }}>
339
- <Tooltip.Target>
340
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
341
- </Tooltip.Target>
342
- <Tooltip.Content>
343
- <p>
344
- Deselecting "Display in Legend" indicates that you do not want to display suppressed data
345
- in the legend.
346
- </p>
347
- </Tooltip.Content>
348
- </Tooltip>
349
- }
350
- value={displayLegend}
351
- fieldName='displayLegend'
352
- label='Display in legend'
353
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
354
- />
355
- <CheckBox
356
- display={config.visualizationSubType === 'regular'}
357
- tooltip={
358
- <Tooltip style={{ textTransform: 'none' }}>
359
- <Tooltip.Target>
360
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
361
- </Tooltip.Target>
362
- <Tooltip.Content>
363
- <p>
364
- Deselecting "Display In Data Table" indicates that you do not want to display suppressed
365
- data in the data table.
366
- </p>
367
- </Tooltip.Content>
368
- </Tooltip>
369
- }
370
- value={displayTable}
371
- fieldName='displayTable'
372
- label='Display in table'
373
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
374
- />
375
- <CheckBox
376
- display={config.visualizationSubType === 'regular'}
377
- tooltip={
378
- <Tooltip style={{ textTransform: 'none' }}>
379
- <Tooltip.Target>
380
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
381
- </Tooltip.Target>
382
- <Tooltip.Content>
383
- <p>Selecting this option will apply to chart, tooltip hover, legend, and data table.</p>
384
- </Tooltip.Content>
385
- </Tooltip>
386
- }
387
- value={displayGray}
388
- fieldName='displayGray'
389
- label='Highlight Suppressed Data In Gray'
390
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
391
- />
392
- </>
393
- ) : (
394
- <>
395
- <label>
396
- <span className='edit-label'>ASSOCIATE TO THESE SERIES</span>
397
- <MultiSelect
398
- fieldName='seriesKeys'
232
+ <Select
233
+ value={type}
234
+ initial={config.visualizationType == 'Bar' ? '' : 'Select'}
235
+ fieldName='type'
236
+ label='Type'
399
237
  updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
400
- options={(config.runtime.lineSeriesKeys ?? config.runtime?.seriesKeys).map(c => ({
401
- label: c,
402
- value: c
403
- }))}
404
- selected={seriesKeys}
238
+ options={getTypeOptions()}
405
239
  />
406
- </label>
407
- <Select
408
- value={column}
409
- initial='Select'
410
- fieldName='column'
411
- label='COLUMN WITH CONFIGURATION VALUE'
412
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
413
- options={getColumnOptions()}
414
- />
415
- <TextField
416
- tooltip={
240
+ {type === 'suppression' ? (
241
+ <>
242
+ <Select
243
+ tooltip={
244
+ <Tooltip style={{ textTransform: 'none' }}>
245
+ <Tooltip.Target>
246
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
247
+ </Tooltip.Target>
248
+ <Tooltip.Content>
249
+ <p>
250
+ {' '}
251
+ Without a selected "Data Series", the suppression symbol will be applied for all
252
+ series in the current dataset visualization. However, choosing a specific "data
253
+ series" will isolate the suppression to that series.
254
+ </p>
255
+ </Tooltip.Content>
256
+ </Tooltip>
257
+ }
258
+ value={column}
259
+ initial='Select'
260
+ fieldName='column'
261
+ label='Add Data Series'
262
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
263
+ options={config.runtime?.seriesKeys}
264
+ />
265
+ <TextField
266
+ value={value}
267
+ fieldName='value'
268
+ label='Suppressed Data Value'
269
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
270
+ />
271
+ {(hasComboLineSeries || config.visualizationType === 'Line') && (
272
+ <>
273
+ <Select
274
+ tooltip={
275
+ <Tooltip style={{ textTransform: 'none' }}>
276
+ <Tooltip.Target>
277
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
278
+ </Tooltip.Target>
279
+ <Tooltip.Content>
280
+ <p>
281
+ The recommended approach for presenting data is to include a footnote indicating
282
+ any data suppression.
283
+ </p>
284
+ </Tooltip.Content>
285
+ </Tooltip>
286
+ }
287
+ value={style}
288
+ initial='Select'
289
+ fieldName='style'
290
+ label={'suppression line style'}
291
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
292
+ options={getStyleOptions(type)}
293
+ />
294
+ <CheckBox
295
+ value={hideLineStyle}
296
+ fieldName='hideLineStyle'
297
+ label='Hide Suppressed line Style'
298
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
299
+ />
300
+ </>
301
+ )}
302
+
303
+ {(hasComboBarSeries || config.visualizationType === 'Bar') && (
304
+ <>
305
+ <Select
306
+ tooltip={
307
+ <Tooltip style={{ textTransform: 'none' }}>
308
+ <Tooltip.Target>
309
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
310
+ </Tooltip.Target>
311
+ <Tooltip.Content>
312
+ <p>
313
+ A symbol is <i>required</i> to indicate suppressed data. We suggest "double
314
+ asterisks." If "double asterisks" are already used elsewhere (e.g., footnotes),
315
+ please select an alternative symbol from the menu to denote data suppression.
316
+ </p>
317
+ </Tooltip.Content>
318
+ </Tooltip>
319
+ }
320
+ value={symbol}
321
+ initial='Select'
322
+ fieldName='symbol'
323
+ label={
324
+ config.visualizationType === 'Combo' ? 'suppression bar symbol' : 'suppression symbol'
325
+ }
326
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
327
+ options={getSymbolOptions()}
328
+ />
329
+ <CheckBox
330
+ value={hideBarSymbol}
331
+ fieldName='hideBarSymbol'
332
+ label='Hide Suppressed Bar Symbol '
333
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
334
+ />
335
+ </>
336
+ )}
337
+
338
+ <TextField
339
+ tooltip={
340
+ <Tooltip style={{ textTransform: 'none' }}>
341
+ <Tooltip.Target>
342
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
343
+ </Tooltip.Target>
344
+ <Tooltip.Content>
345
+ <p>This label will display in the tooltip and legend.</p>
346
+ </Tooltip.Content>
347
+ </Tooltip>
348
+ }
349
+ value={label ? label : 'Suppressed'}
350
+ fieldName='label'
351
+ label='Suppressed Data Label'
352
+ placeholder=''
353
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
354
+ />
355
+ <CheckBox
356
+ display={config.visualizationSubType === 'regular'}
357
+ tooltip={
358
+ <Tooltip style={{ textTransform: 'none' }}>
359
+ <Tooltip.Target>
360
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
361
+ </Tooltip.Target>
362
+ <Tooltip.Content>
363
+ <p>
364
+ Deselecting the "Display In Tooltips" option prevents suppressed values from
365
+ appearing in tooltips.
366
+ </p>
367
+ </Tooltip.Content>
368
+ </Tooltip>
369
+ }
370
+ value={displayTooltip}
371
+ fieldName='displayTooltip'
372
+ label='Display in tooltips'
373
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
374
+ />
375
+ <CheckBox
376
+ display={config.visualizationSubType === 'regular'}
377
+ tooltip={
378
+ <Tooltip style={{ textTransform: 'none' }}>
379
+ <Tooltip.Target>
380
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
381
+ </Tooltip.Target>
382
+ <Tooltip.Content>
383
+ <p>
384
+ Deselecting "Display in Legend" indicates that you do not want to display suppressed
385
+ data in the legend.
386
+ </p>
387
+ </Tooltip.Content>
388
+ </Tooltip>
389
+ }
390
+ value={displayLegend}
391
+ fieldName='displayLegend'
392
+ label='Display in legend'
393
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
394
+ />
395
+ <CheckBox
396
+ display={config.visualizationSubType === 'regular'}
397
+ tooltip={
398
+ <Tooltip style={{ textTransform: 'none' }}>
399
+ <Tooltip.Target>
400
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
401
+ </Tooltip.Target>
402
+ <Tooltip.Content>
403
+ <p>
404
+ Deselecting "Display In Data Table" indicates that you do not want to display
405
+ suppressed data in the data table.
406
+ </p>
407
+ </Tooltip.Content>
408
+ </Tooltip>
409
+ }
410
+ value={displayTable}
411
+ fieldName='displayTable'
412
+ label='Display in table'
413
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
414
+ />
415
+ <CheckBox
416
+ display={config.visualizationSubType === 'regular'}
417
+ tooltip={
418
+ <Tooltip style={{ textTransform: 'none' }}>
419
+ <Tooltip.Target>
420
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
421
+ </Tooltip.Target>
422
+ <Tooltip.Content>
423
+ <p>
424
+ Selecting this option will apply to chart, tooltip hover, legend, and data table.
425
+ </p>
426
+ </Tooltip.Content>
427
+ </Tooltip>
428
+ }
429
+ value={displayGray}
430
+ fieldName='displayGray'
431
+ label='Highlight Suppressed Data In Gray'
432
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
433
+ />
434
+ </>
435
+ ) : (
436
+ <>
437
+ <label>
438
+ <span className='edit-label'>ASSOCIATE TO THESE SERIES</span>
439
+ <MultiSelect
440
+ fieldName='seriesKeys'
441
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
442
+ options={(config.runtime.lineSeriesKeys ?? config.runtime?.seriesKeys).map(c => ({
443
+ label: c,
444
+ value: c
445
+ }))}
446
+ selected={seriesKeys}
447
+ />
448
+ </label>
449
+ <Select
450
+ value={column}
451
+ initial='Select'
452
+ fieldName='column'
453
+ label='COLUMN WITH CONFIGURATION VALUE'
454
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
455
+ options={getColumnOptions()}
456
+ />
457
+ <TextField
458
+ tooltip={
459
+ <Tooltip style={{ textTransform: 'none' }}>
460
+ <Tooltip.Target>
461
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
462
+ </Tooltip.Target>
463
+ <Tooltip.Content>
464
+ <p>
465
+ If 'Filled Circles' is selected as the style, this field is optional, and the style
466
+ 'Filled Circles' will apply to all points within the associated series data.
467
+ </p>
468
+ </Tooltip.Content>
469
+ </Tooltip>
470
+ }
471
+ value={value}
472
+ fieldName='value'
473
+ label='VALUE TO TRIGGER'
474
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
475
+ />
476
+ <Select
477
+ value={style}
478
+ initial='Select'
479
+ fieldName='style'
480
+ label='Style'
481
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
482
+ options={getStyleOptions(type)}
483
+ />
484
+ {style?.includes('Circles') && (
485
+ <TextField
486
+ className='number-narrow'
487
+ type='number'
488
+ value={circleSize}
489
+ fieldName='circleSize'
490
+ label='circle size'
491
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
492
+ />
493
+ )}
494
+ {style !== 'Filled Circles' && (
495
+ <TextField
496
+ value={label}
497
+ fieldName='label'
498
+ label='Label'
499
+ placeholder=''
500
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
501
+ />
502
+ )}
503
+ </>
504
+ )}
505
+ </AccordionItemPanel>
506
+ </AccordionItem>
507
+ </Accordion>
508
+ </div>
509
+ )}
510
+ </Draggable>
511
+ )}
512
+ />
513
+ <Button type='button' variant='editor-primary' onClick={addColumn}>
514
+ {config.visualizationType === 'Line'
515
+ ? 'Add Special Line'
516
+ : config.visualizationType === 'Bar'
517
+ ? ' Add Special Bar'
518
+ : 'Add Special Bar/Line'}
519
+ </Button>
520
+ </>
521
+ )
522
+ }
523
+
524
+ interface CategoricalAxisProps {
525
+ config: ChartConfig
526
+ updateConfig: Function
527
+ display: boolean
528
+ }
529
+
530
+ type AxisAnchorEditorProps = {
531
+ axisKey: 'xAxis' | 'yAxis'
532
+ config: ChartConfig
533
+ lineOptions: { value: string; label?: string }[]
534
+ updateConfig: Function
535
+ }
536
+
537
+ const AxisAnchorEditor: React.FC<AxisAnchorEditorProps> = ({ axisKey, config, lineOptions, updateConfig }) => {
538
+ const anchors = config[axisKey]?.anchors || []
539
+
540
+ const updateAnchors = (nextAnchors: Anchor[]) => {
541
+ updateConfig({
542
+ ...config,
543
+ [axisKey]: {
544
+ ...config[axisKey],
545
+ anchors: nextAnchors
546
+ }
547
+ })
548
+ }
549
+
550
+ const updateAnchorField = (index: number, field: keyof Anchor, value: string) => {
551
+ const copiedAnchors = [...anchors]
552
+ copiedAnchors[index] = {
553
+ ...copiedAnchors[index],
554
+ [field]: value
555
+ }
556
+ updateAnchors(copiedAnchors)
557
+ }
558
+
559
+ const removeAnchor = (index: number) => {
560
+ const copiedAnchors = [...anchors]
561
+ copiedAnchors.splice(index, 1)
562
+ updateAnchors(copiedAnchors)
563
+ }
564
+
565
+ const addAnchor = () => {
566
+ updateAnchors([...anchors, {} as Anchor])
567
+ }
568
+
569
+ const reorderAnchors = (sourceIndex: number, destinationIndex: number) => {
570
+ const copiedAnchors = [...anchors]
571
+ const [movedAnchor] = copiedAnchors.splice(sourceIndex, 1)
572
+ copiedAnchors.splice(destinationIndex, 0, movedAnchor)
573
+ updateAnchors(copiedAnchors)
574
+ }
575
+
576
+ return (
577
+ <>
578
+ <GroupedList
579
+ items={anchors}
580
+ label='Anchors'
581
+ droppableId={`${axisKey}-anchors-order`}
582
+ onDragEnd={({ source, destination }) => {
583
+ if (!destination || source.index === destination.index) return
584
+ reorderAnchors(source.index, destination.index)
585
+ }}
586
+ renderItem={(anchor, index) => (
587
+ <Draggable key={`${axisKey}-anchor-${index}`} draggableId={`${axisKey}-anchor-${index}`} index={index}>
588
+ {(provided, snapshot) => (
589
+ <div
590
+ ref={provided.innerRef}
591
+ {...provided.draggableProps}
592
+ {...provided.dragHandleProps}
593
+ className={snapshot.isDragging ? 'currently-dragging' : ''}
594
+ style={provided.draggableProps.style}
595
+ >
596
+ <Accordion allowZeroExpanded>
597
+ <AccordionItem className='series-item series-item--chart'>
598
+ <AccordionItemHeading className='series-item__title'>
599
+ <AccordionItemButton className='accordion__button'>
600
+ <Icon display='move' size={15} style={{ cursor: 'default' }} />
601
+ {`Anchor ${index + 1}`}
602
+ </AccordionItemButton>
603
+ </AccordionItemHeading>
604
+ <AccordionItemPanel>
605
+ <div className='series-item__panel-actions'>
606
+ <Button
607
+ type='button'
608
+ variant='danger'
609
+ size='sm'
610
+ className='grouped-list__remove'
611
+ onClick={e => {
612
+ e.preventDefault()
613
+ removeAnchor(index)
614
+ }}
615
+ >
616
+ Remove
617
+ </Button>
618
+ </div>
619
+ <label>
620
+ <span>Anchor Value</span>
417
621
  <Tooltip style={{ textTransform: 'none' }}>
418
622
  <Tooltip.Target>
419
623
  <Icon display='question' style={{ marginLeft: '0.5rem' }} />
420
624
  </Tooltip.Target>
421
625
  <Tooltip.Content>
422
- <p>
423
- If 'Filled Circles' is selected as the style, this field is optional, and the style
424
- 'Filled Circles' will apply to all points within the associated series data.
425
- </p>
626
+ <p>Enter the value as its shown in the data column</p>
426
627
  </Tooltip.Content>
427
628
  </Tooltip>
428
- }
429
- value={value}
430
- fieldName='value'
431
- label='VALUE TO TRIGGER'
432
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
433
- />
434
- <Select
435
- value={style}
436
- initial='Select'
437
- fieldName='style'
438
- label='Style'
439
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
440
- options={getStyleOptions(type)}
441
- />
442
- {style.includes('Circles') && (
443
- <TextField
444
- className='number-narrow'
445
- type='number'
446
- value={circleSize}
447
- fieldName='circleSize'
448
- label='circle size'
449
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
450
- />
451
- )}
452
- {style !== 'Filled Circles' && (
453
- <TextField
454
- value={label}
455
- fieldName='label'
456
- label='Label'
457
- placeholder=''
458
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
459
- />
460
- )}
461
- </>
462
- )}
629
+ <input
630
+ type='text'
631
+ value={anchor?.value || ''}
632
+ onChange={e => {
633
+ e.preventDefault()
634
+ updateAnchorField(index, 'value', e.target.value)
635
+ }}
636
+ />
637
+ </label>
638
+
639
+ <label>
640
+ <span>Anchor Color</span>
641
+ <input
642
+ type='text'
643
+ value={anchor?.color || ''}
644
+ onChange={e => {
645
+ e.preventDefault()
646
+ updateAnchorField(index, 'color', e.target.value)
647
+ }}
648
+ />
649
+ </label>
650
+
651
+ <Select
652
+ value={anchor?.lineStyle || ''}
653
+ label='Anchor Line Style'
654
+ onChange={e => updateAnchorField(index, 'lineStyle', e.target.value)}
655
+ options={[
656
+ { value: '', label: 'Select' },
657
+ ...lineOptions.map(line => ({ value: line.value, label: line.value }))
658
+ ]}
659
+ />
660
+ </AccordionItemPanel>
661
+ </AccordionItem>
662
+ </Accordion>
463
663
  </div>
464
- )
465
- }
664
+ )}
665
+ </Draggable>
466
666
  )}
667
+ />
467
668
 
468
- <button type='button' onClick={addColumn} className='btn btn-primary full-width'>
469
- {config.visualizationType === 'Line'
470
- ? 'Add Special Line'
471
- : config.visualizationType === 'Bar'
472
- ? ' Add Special Bar'
473
- : 'Add Special Bar/Line'}
474
- </button>
669
+ <Button
670
+ variant='editor-primary'
671
+ onClick={e => {
672
+ e.preventDefault()
673
+ addAnchor()
674
+ }}
675
+ >
676
+ Add Anchor
677
+ </Button>
475
678
  </>
476
679
  )
477
680
  }
478
681
 
479
- interface CategoricalAxisProps {
480
- config: ChartConfig
481
- updateConfig: Function
482
- display: boolean
483
- }
484
-
485
682
  const CategoricalAxis: React.FC<CategoricalAxisProps> = ({ config, updateConfig, display }) => {
486
683
  const maxHeight = config?.yAxis?.maxValue
487
684
 
@@ -543,7 +740,7 @@ const CategoricalAxis: React.FC<CategoricalAxisProps> = ({ config, updateConfig,
543
740
  return (
544
741
  <div key={`preliminaryData-${i}`} className='edit-block'>
545
742
  <p>Axis Category {i + 1}</p>
546
- <button
743
+ <Button
547
744
  type='button'
548
745
  className='btn btn-danger'
549
746
  onClick={event => {
@@ -552,7 +749,7 @@ const CategoricalAxis: React.FC<CategoricalAxisProps> = ({ config, updateConfig,
552
749
  }}
553
750
  >
554
751
  Remove
555
- </button>
752
+ </Button>
556
753
  <TextField
557
754
  tooltip={
558
755
  <Tooltip style={{ textTransform: 'none' }}>
@@ -598,9 +795,9 @@ const CategoricalAxis: React.FC<CategoricalAxisProps> = ({ config, updateConfig,
598
795
  )
599
796
  })}
600
797
 
601
- <button type='button' onClick={addColumn} className='btn btn-primary full-width'>
798
+ <Button type='button' variant='editor-primary' onClick={addColumn}>
602
799
  Add Axis Category
603
- </button>
800
+ </Button>
604
801
  </>
605
802
  )
606
803
  }
@@ -721,7 +918,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
721
918
  }
722
919
  }, [config.visualizationType])
723
920
 
724
- const { hasRightAxis } = useRightAxis({ config: config, yMax: config.yAxis.size, data: config.data, updateConfig })
921
+ const { hasRightAxis } = useRightAxis({ config: config, yMax: 0, data: config.data, updateConfig })
725
922
 
726
923
  const getItemStyle = (isDragging, draggableStyle) => ({
727
924
  ...draggableStyle
@@ -770,6 +967,17 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
770
967
  }
771
968
 
772
969
  const updateField = updateFieldFactory(config, updateConfig)
970
+ const getAutoDetectedXAxisDateParseFormat = (dataKey = config.xAxis.dataKey, axisType = config.xAxis.type) => {
971
+ const hasExistingDateParseFormat =
972
+ typeof config.xAxis.dateParseFormat === 'string' && config.xAxis.dateParseFormat.trim() !== ''
973
+
974
+ if (hasExistingDateParseFormat || !['date', 'date-time'].includes(axisType)) {
975
+ return undefined
976
+ }
977
+
978
+ return getAutoDetectedDateParseFormat(config.data, dataKey)
979
+ }
980
+
773
981
  const updateFieldDeprecated = (section, subsection, fieldName, newValue) => {
774
982
  if (isDebug)
775
983
  console.error(
@@ -812,6 +1020,23 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
812
1020
  })
813
1021
  return
814
1022
  }
1023
+
1024
+ if (section === 'xAxis' && !truthy(subsection) && fieldName === 'dataKey') {
1025
+ const autoDetectedDateParseFormat = getAutoDetectedXAxisDateParseFormat(newValue)
1026
+ const updatedConfig = {
1027
+ ...config,
1028
+ xAxis: {
1029
+ ...config.xAxis,
1030
+ dataKey: newValue,
1031
+ ...(autoDetectedDateParseFormat ? { dateParseFormat: autoDetectedDateParseFormat } : {})
1032
+ }
1033
+ }
1034
+
1035
+ enforceRestrictions(updatedConfig)
1036
+ updateConfig(updatedConfig)
1037
+ return
1038
+ }
1039
+
815
1040
  if (null === section && null === subsection) {
816
1041
  // special case that allows for updating the config object directly
817
1042
  if (!truthy(fieldName)) console.error('fieldName is required')
@@ -935,6 +1160,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
935
1160
 
936
1161
  // Extract column names from data with memoization (replaces getColumns)
937
1162
  const allColumns = useDataColumns(dataSourceForColumns)
1163
+ const seriesOwnedColumnNames = useMemo(() => getSeriesOwnedColumnNames(config.series), [config.series])
938
1164
 
939
1165
  // Filter out series columns and confidence key columns (except lower and upper)
940
1166
  const filteredColumns = useMemo(() => {
@@ -1019,7 +1245,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
1019
1245
  // prettier-ignore
1020
1246
  const {
1021
1247
  highlightedBarValues,
1022
- highlightedSeriesValues,
1248
+ getHighlightedSeriesValues,
1023
1249
  handleUpdateHighlightedBar,
1024
1250
  handleAddNewHighlightedBar,
1025
1251
  handleRemoveHighlightedBar,
@@ -1519,46 +1745,8 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
1519
1745
  'Deviation Bar'
1520
1746
  ]
1521
1747
 
1522
- const columnsOptions = [
1523
- <option value='' key={'Select Option'}>
1524
- - Select Option -
1525
- </option>
1526
- ]
1527
-
1528
- if (config.data && config.series) {
1529
- Object.keys(config.data?.[0] || []).map(colName => {
1530
- // OMIT ANY COLUMNS THAT ARE IN DATA SERIES!
1531
- const found = config?.series.some(series => series.dataKey === colName)
1532
- if (colName !== config.xAxis.dataKey && !found) {
1533
- // if not the index then add it
1534
- return columnsOptions.push(
1535
- <option value={colName} key={colName}>
1536
- {colName}
1537
- </option>
1538
- )
1539
- }
1540
- })
1541
- }
1542
-
1543
- // for pie charts
1544
- if (!config.data && data) {
1545
- if (!data[0]) return
1546
- Object.keys(data[0]).map(colName => {
1547
- // OMIT ANY COLUMNS THAT ARE IN DATA SERIES!
1548
- const found = data.some(el => el.dataKey === colName)
1549
- if (colName !== config.xAxis.dataKey && !found) {
1550
- // if not the index then add it
1551
- return columnsOptions.push(
1552
- <option value={colName} key={colName}>
1553
- {colName}
1554
- </option>
1555
- )
1556
- }
1557
- })
1558
- }
1559
-
1560
1748
  const removeAdditionalColumn = columnName => {
1561
- const newColumns = _.cloneDeep(config.columns)
1749
+ const newColumns = cloneDeep(config.columns)
1562
1750
 
1563
1751
  delete newColumns[columnName]
1564
1752
 
@@ -1667,6 +1855,27 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
1667
1855
  const hasDynamicCategory = ![undefined, '- Select - '].includes(config.series?.[0]?.dynamicCategory)
1668
1856
  const hasMultipleSeries = config.series?.length > 1
1669
1857
 
1858
+ const highlightedSeriesOptions = useMemo(() => {
1859
+ if (
1860
+ !(
1861
+ config.visualizationType === 'Bar' &&
1862
+ config.series?.length === 1 &&
1863
+ config.highlightedBarValues?.length > 0 &&
1864
+ getHighlightedSeriesValues
1865
+ )
1866
+ ) {
1867
+ return []
1868
+ }
1869
+
1870
+ return [...new Set(getHighlightedSeriesValues())].sort().map(option => ({ value: option, label: option }))
1871
+ }, [
1872
+ config.visualizationType,
1873
+ config.series?.length,
1874
+ config.highlightedBarValues?.length,
1875
+ config.data,
1876
+ config.xAxis.dataKey
1877
+ ])
1878
+
1670
1879
  const editorContextValues = {
1671
1880
  addNewExclusion,
1672
1881
  data,
@@ -1685,7 +1894,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
1685
1894
  handleUpdateHighlightedBar,
1686
1895
  handleRemoveHighlightedBar,
1687
1896
  isPaletteReversed: config.general?.palette?.isReversed,
1688
- highlightedSeriesValues,
1897
+ getHighlightedSeriesValues,
1689
1898
  handleUpdateHighlightedBorderWidth,
1690
1899
  handleUpdateHighlightedBarColor,
1691
1900
  setLollipopShape,
@@ -1808,49 +2017,40 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
1808
2017
  getColumns={getColumns}
1809
2018
  handleForecastPaletteSelection={handleForecastPaletteSelection}
1810
2019
  >
1811
- <fieldset>
1812
- <legend className='edit-label d-flex align-items-center'>
1813
- Displaying
1814
- <Tooltip style={{ textTransform: 'none' }}>
1815
- <Tooltip.Target>
1816
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1817
- </Tooltip.Target>
1818
- <Tooltip.Content>
1819
- <p>
1820
- A data series is a set of related data points plotted in a chart and
1821
- typically represented in the chart legend.
1822
- </p>
1823
- </Tooltip.Content>
1824
- </Tooltip>
1825
- </legend>
1826
- </fieldset>
1827
-
1828
- <DragDropContext
1829
- onDragEnd={({ source, destination }) =>
1830
- handleSeriesChange(source.index, destination.index)
2020
+ <GroupedList
2021
+ items={config.series}
2022
+ droppableId='chart-series-order'
2023
+ label={
2024
+ <>
2025
+ Displaying
2026
+ <Tooltip style={{ textTransform: 'none' }}>
2027
+ <Tooltip.Target>
2028
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2029
+ </Tooltip.Target>
2030
+ <Tooltip.Content>
2031
+ <p>
2032
+ A data series is a set of related data points plotted in a chart and
2033
+ typically represented in the chart legend.
2034
+ </p>
2035
+ </Tooltip.Content>
2036
+ </Tooltip>
2037
+ </>
1831
2038
  }
1832
- >
1833
- <Droppable droppableId='filter_order'>
1834
- {/* prettier-ignore */}
1835
- {provided => {
1836
- return (
1837
- <ul
1838
- {...provided.droppableProps}
1839
- className='series-list'
1840
- ref={provided.innerRef}
1841
- >
1842
- <Panels.Series.List
1843
- series={config.series}
1844
- getItemStyle={getItemStyle}
1845
- sortableItemStyles={sortableItemStyles}
1846
- chartsWithOptions={chartsWithOptions}
1847
- />
1848
- {provided.placeholder}
1849
- </ul>
1850
- )
1851
- }}
1852
- </Droppable>
1853
- </DragDropContext>
2039
+ onDragEnd={({ source, destination }) => {
2040
+ if (!destination || source.index === destination.index) return
2041
+ handleSeriesChange(source.index, destination.index)
2042
+ }}
2043
+ renderItem={(series, index) => (
2044
+ <Panels.Series.Item
2045
+ getItemStyle={getItemStyle}
2046
+ sortableItemStyles={sortableItemStyles}
2047
+ chartsWithOptions={chartsWithOptions}
2048
+ series={series}
2049
+ index={index}
2050
+ key={`series-list-${index}`}
2051
+ />
2052
+ )}
2053
+ />
1854
2054
  </Panels.Series.Wrapper>
1855
2055
  )}
1856
2056
  </>
@@ -2078,38 +2278,6 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
2078
2278
  }
2079
2279
  updateField={updateFieldDeprecated}
2080
2280
  />
2081
- <TextField
2082
- value={config.yAxis.size}
2083
- type='number'
2084
- section='yAxis'
2085
- fieldName='size'
2086
- label={config.orientation === 'horizontal' ? 'Size (Height)' : 'Size (Width)'}
2087
- className='number-narrow'
2088
- updateField={updateFieldDeprecated}
2089
- tooltip={
2090
- <Tooltip style={{ textTransform: 'none' }}>
2091
- <Tooltip.Target>
2092
- <Icon
2093
- display='question'
2094
- style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2095
- />
2096
- </Tooltip.Target>
2097
- <Tooltip.Content>
2098
- <p>{`Increase the size if elements in the ${config.orientation} axis are being crowded or hidden behind other elements. Decrease if less space is required for the value axis.`}</p>
2099
- </Tooltip.Content>
2100
- </Tooltip>
2101
- }
2102
- />
2103
- <TextField
2104
- display={!visHasCategoricalAxis()}
2105
- value={config.yAxis.labelOffset}
2106
- section='yAxis'
2107
- fieldName='labelOffset'
2108
- label='Label offset'
2109
- type='number'
2110
- className='number-narrow'
2111
- updateField={updateFieldDeprecated}
2112
- />
2113
2281
  {config.orientation === 'horizontal' && (
2114
2282
  <CheckBox
2115
2283
  value={config.isResponsiveTicks}
@@ -2487,256 +2655,47 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
2487
2655
  <span style={{ color: 'red', display: 'block' }}>{warningMsg.minMsg}</span>
2488
2656
  </>
2489
2657
  )}
2658
+ <TextField
2659
+ value={config.yAxis.smallestLeftAxisMax}
2660
+ section='yAxis'
2661
+ fieldName='smallestLeftAxisMax'
2662
+ type='number'
2663
+ label='Smallest Left Axis Maximum'
2664
+ placeholder='Auto'
2665
+ tooltip={
2666
+ <Tooltip style={{ textTransform: 'none' }}>
2667
+ <Tooltip.Target>
2668
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2669
+ </Tooltip.Target>
2670
+ <Tooltip.Content>
2671
+ <p>
2672
+ Example: If your data only goes up to 1, the axis might show 0, 0.2, 0.4, 0.6,
2673
+ 0.8, 1. Setting this to 5 would make the axis show 0, 1, 2, 3, 4, 5 instead.
2674
+ </p>
2675
+ </Tooltip.Content>
2676
+ </Tooltip>
2677
+ }
2678
+ updateField={updateFieldDeprecated}
2679
+ />
2490
2680
  </>
2491
2681
  )
2492
2682
  )}
2493
2683
  {/* start: anchors */}
2494
2684
  {visHasAnchors() && config.orientation !== 'horizontal' && (
2495
- <div className='edit-block'>
2496
- <span className='edit-label column-heading'>Anchors</span>
2497
- <Accordion allowZeroExpanded>
2498
- {config.yAxis?.anchors?.map((anchor, index) => (
2499
- <AccordionItem
2500
- className='series-item series-item--chart'
2501
- key={`yaxis-anchors-2-${index}`}
2502
- >
2503
- <AccordionItemHeading className='series-item__title'>
2504
- <>
2505
- <AccordionItemButton className={'accordion__button accordion__button'}>
2506
- Anchor {index + 1}
2507
- <button
2508
- className='series-list__remove'
2509
- onClick={e => {
2510
- e.preventDefault()
2511
- const copiedAnchorGroups = [...config.yAxis.anchors]
2512
- copiedAnchorGroups.splice(index, 1)
2513
- updateConfig({
2514
- ...config,
2515
- yAxis: {
2516
- ...config.yAxis,
2517
- anchors: copiedAnchorGroups
2518
- }
2519
- })
2520
- }}
2521
- >
2522
- Remove
2523
- </button>
2524
- </AccordionItemButton>
2525
- </>
2526
- </AccordionItemHeading>
2527
- <AccordionItemPanel>
2528
- <label>
2529
- <span>Anchor Value</span>
2530
- <Tooltip style={{ textTransform: 'none' }}>
2531
- <Tooltip.Target>
2532
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2533
- </Tooltip.Target>
2534
- <Tooltip.Content>
2535
- <p>Enter the value as its shown in the data column</p>
2536
- </Tooltip.Content>
2537
- </Tooltip>
2538
- <input
2539
- type='text'
2540
- value={config.yAxis.anchors[index].value ? config.yAxis.anchors[index].value : ''}
2541
- onChange={e => {
2542
- e.preventDefault()
2543
- const copiedAnchors = [...config.yAxis.anchors]
2544
- copiedAnchors[index].value = e.target.value
2545
- updateConfig({
2546
- ...config,
2547
- yAxis: {
2548
- ...config.yAxis,
2549
- anchors: copiedAnchors
2550
- }
2551
- })
2552
- }}
2553
- />
2554
- </label>
2555
-
2556
- <label>
2557
- <span>Anchor Color</span>
2558
- <input
2559
- type='text'
2560
- value={config.yAxis.anchors[index].color ? config.yAxis.anchors[index].color : ''}
2561
- onChange={e => {
2562
- e.preventDefault()
2563
- const copiedAnchors = [...config.yAxis.anchors]
2564
- copiedAnchors[index].color = e.target.value
2565
- updateConfig({
2566
- ...config,
2567
- yAxis: {
2568
- ...config.yAxis,
2569
- anchors: copiedAnchors
2570
- }
2571
- })
2572
- }}
2573
- />
2574
- </label>
2575
-
2576
- <Select
2577
- value={config.yAxis.anchors[index].lineStyle || ''}
2578
- label='Anchor Line Style'
2579
- onChange={e => {
2580
- const copiedAnchors = [...config.yAxis.anchors]
2581
- copiedAnchors[index].lineStyle = e.target.value
2582
- updateConfig({
2583
- ...config,
2584
- yAxis: {
2585
- ...config.yAxis,
2586
- anchors: copiedAnchors
2587
- }
2588
- })
2589
- }}
2590
- options={[
2591
- { value: '', label: 'Select' },
2592
- ...lineOptions.map(line => ({ value: line.value, label: line.value }))
2593
- ]}
2594
- />
2595
- </AccordionItemPanel>
2596
- </AccordionItem>
2597
- ))}
2598
- </Accordion>
2599
-
2600
- <button
2601
- className='btn btn-primary full-width'
2602
- onClick={e => {
2603
- e.preventDefault()
2604
- const anchors = [...config.yAxis.anchors]
2605
- anchors.push({} as Anchor)
2606
- updateConfig({
2607
- ...config,
2608
- yAxis: {
2609
- ...config.yAxis,
2610
- anchors
2611
- }
2612
- })
2613
- }}
2614
- >
2615
- Add Anchor
2616
- </button>
2617
- </div>
2685
+ <AxisAnchorEditor
2686
+ axisKey='yAxis'
2687
+ config={config}
2688
+ lineOptions={lineOptions}
2689
+ updateConfig={updateConfig}
2690
+ />
2618
2691
  )}
2619
2692
  {visHasAnchors() && config.orientation === 'horizontal' && (
2620
- <div className='edit-block'>
2621
- <span className='edit-label column-heading'>Anchors</span>
2622
- <Accordion allowZeroExpanded>
2623
- {config.xAxis?.anchors?.map((anchor, index) => (
2624
- <AccordionItem className='series-item series-item--chart' key={`xaxis-anchors-${index}`}>
2625
- <AccordionItemHeading className='series-item__title'>
2626
- <>
2627
- <AccordionItemButton className={'accordion__button accordion__button'}>
2628
- Anchor {index + 1}
2629
- <button
2630
- className='series-list__remove'
2631
- onClick={e => {
2632
- e.preventDefault()
2633
- const copiedAnchorGroups = [...config.xAxis.anchors]
2634
- copiedAnchorGroups.splice(index, 1)
2635
- updateConfig({
2636
- ...config,
2637
- xAxis: {
2638
- ...config.xAxis,
2639
- anchors: copiedAnchorGroups
2640
- }
2641
- })
2642
- }}
2643
- >
2644
- Remove
2645
- </button>
2646
- </AccordionItemButton>
2647
- </>
2648
- </AccordionItemHeading>
2649
- <AccordionItemPanel>
2650
- <label>
2651
- <span>Anchor Value</span>
2652
- <Tooltip style={{ textTransform: 'none' }}>
2653
- <Tooltip.Target>
2654
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2655
- </Tooltip.Target>
2656
- <Tooltip.Content>
2657
- <p>Enter the value as its shown in the data column</p>
2658
- </Tooltip.Content>
2659
- </Tooltip>
2660
- <input
2661
- type='text'
2662
- value={config.xAxis.anchors[index].value ? config.xAxis.anchors[index].value : ''}
2663
- onChange={e => {
2664
- e.preventDefault()
2665
- const copiedAnchors = [...config.xAxis.anchors]
2666
- copiedAnchors[index].value = e.target.value
2667
- updateConfig({
2668
- ...config,
2669
- xAxis: {
2670
- ...config.xAxis,
2671
- anchors: copiedAnchors
2672
- }
2673
- })
2674
- }}
2675
- />
2676
- </label>
2677
-
2678
- <label>
2679
- <span>Anchor Color</span>
2680
- <input
2681
- type='text'
2682
- value={config.xAxis.anchors[index].color ? config.xAxis.anchors[index].color : ''}
2683
- onChange={e => {
2684
- e.preventDefault()
2685
- const copiedAnchors = [...config.xAxis.anchors]
2686
- copiedAnchors[index].color = e.target.value
2687
- updateConfig({
2688
- ...config,
2689
- xAxis: {
2690
- ...config.xAxis,
2691
- anchors: copiedAnchors
2692
- }
2693
- })
2694
- }}
2695
- />
2696
- </label>
2697
-
2698
- <Select
2699
- value={config.xAxis.anchors[index].lineStyle || ''}
2700
- label='Anchor Line Style'
2701
- onChange={e => {
2702
- const copiedAnchors = [...config.xAxis.anchors]
2703
- copiedAnchors[index].lineStyle = e.target.value
2704
- updateConfig({
2705
- ...config,
2706
- xAxis: {
2707
- ...config.xAxis,
2708
- anchors: copiedAnchors
2709
- }
2710
- })
2711
- }}
2712
- options={[
2713
- { value: '', label: 'Select' },
2714
- ...lineOptions.map(line => ({ value: line.value, label: line.value }))
2715
- ]}
2716
- />
2717
- </AccordionItemPanel>
2718
- </AccordionItem>
2719
- ))}
2720
- </Accordion>
2721
-
2722
- <button
2723
- className='btn btn-primary full-width'
2724
- onClick={e => {
2725
- e.preventDefault()
2726
- const anchors = [...config.xAxis.anchors]
2727
- anchors.push({} as Anchor)
2728
- updateConfig({
2729
- ...config,
2730
- xAxis: {
2731
- ...config.xAxis,
2732
- anchors
2733
- }
2734
- })
2735
- }}
2736
- >
2737
- Add Anchor
2738
- </button>
2739
- </div>
2693
+ <AxisAnchorEditor
2694
+ axisKey='xAxis'
2695
+ config={config}
2696
+ lineOptions={lineOptions}
2697
+ updateConfig={updateConfig}
2698
+ />
2740
2699
  )}
2741
2700
  {/* end: anchors */}
2742
2701
  </AccordionItemPanel>
@@ -2886,7 +2845,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
2886
2845
  />
2887
2846
 
2888
2847
  <TextField
2889
- value={config.yAxis.max}
2848
+ value={config.yAxis.rightMax}
2890
2849
  section='yAxis'
2891
2850
  fieldName='rightMax'
2892
2851
  type='number'
@@ -2896,7 +2855,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
2896
2855
  />
2897
2856
  <span style={{ color: 'red', display: 'block' }}>{warningMsg.rightMaxMessage}</span>
2898
2857
  <TextField
2899
- value={config.yAxis.min}
2858
+ value={config.yAxis.rightMin}
2900
2859
  section='yAxis'
2901
2860
  fieldName='rightMin'
2902
2861
  type='number'
@@ -2905,6 +2864,28 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
2905
2864
  updateField={updateFieldDeprecated}
2906
2865
  />
2907
2866
  <span style={{ color: 'red', display: 'block' }}>{warningMsg.minMsg}</span>
2867
+ <TextField
2868
+ value={config.yAxis.smallestRightAxisMax}
2869
+ section='yAxis'
2870
+ fieldName='smallestRightAxisMax'
2871
+ type='number'
2872
+ label='Smallest Right Axis Maximum'
2873
+ placeholder='Auto'
2874
+ tooltip={
2875
+ <Tooltip style={{ textTransform: 'none' }}>
2876
+ <Tooltip.Target>
2877
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2878
+ </Tooltip.Target>
2879
+ <Tooltip.Content>
2880
+ <p>
2881
+ Example: If your data only goes up to 1, the axis might show 0, 0.2, 0.4, 0.6, 0.8, 1.
2882
+ Setting this to 5 would make the axis show 0, 1, 2, 3, 4, 5 instead.
2883
+ </p>
2884
+ </Tooltip.Content>
2885
+ </Tooltip>
2886
+ }
2887
+ updateField={updateFieldDeprecated}
2888
+ />
2908
2889
  </AccordionItemPanel>
2909
2890
  </AccordionItem>
2910
2891
  )}
@@ -2950,11 +2931,19 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
2950
2931
  section='xAxis'
2951
2932
  fieldName='type'
2952
2933
  updateField={(_section, _subsection, _fieldName, value) => {
2934
+ const autoDetectedDateParseFormat = getAutoDetectedXAxisDateParseFormat(
2935
+ config.xAxis.dataKey,
2936
+ value
2937
+ )
2938
+
2953
2939
  updateConfig({
2954
2940
  ...config,
2955
2941
  xAxis: {
2956
2942
  ...config.xAxis,
2957
- type: value
2943
+ type: value,
2944
+ ...(autoDetectedDateParseFormat
2945
+ ? { dateParseFormat: autoDetectedDateParseFormat }
2946
+ : {})
2958
2947
  }
2959
2948
  })
2960
2949
  }}
@@ -3576,18 +3565,6 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
3576
3565
  updateField={updateFieldDeprecated}
3577
3566
  />
3578
3567
  )}
3579
- {config.orientation === 'horizontal' && (
3580
- <TextField
3581
- value={config.xAxis.labelOffset}
3582
- section='xAxis'
3583
- fieldName='labelOffset'
3584
- label='Label offset'
3585
- type='number'
3586
- className='number-narrow'
3587
- updateField={updateFieldDeprecated}
3588
- />
3589
- )}
3590
-
3591
3568
  {/* Hiding this for now, not interested in moving the axis lines away from chart comp. right now. */}
3592
3569
  {/* <TextField value={config.xAxis.axisPadding} type='number' max={10} min={0} section='xAxis' fieldName='axisPadding' label={'Axis Padding'} className='number-narrow' updateField={updateFieldDeprecated} /> */}
3593
3570
  {(config.xAxis.type === 'continuous' || config.forestPlot.type === 'Logarithmic') && (
@@ -3726,12 +3703,12 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
3726
3703
  highlightedBarValues.map((highlightedBarValue, i) => (
3727
3704
  <fieldset>
3728
3705
  <div className='edit-block' key={`highlighted-bar-${i}`}>
3729
- <button
3706
+ <Button
3730
3707
  className='btn btn-danger'
3731
3708
  onClick={e => handleRemoveHighlightedBar(e, i)}
3732
3709
  >
3733
3710
  Remove
3734
- </button>
3711
+ </Button>
3735
3712
  <p>Highlighted Bar {i + 1}</p>
3736
3713
  <Select
3737
3714
  value={config.highlightedBarValues[i].value}
@@ -3739,11 +3716,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
3739
3716
  onChange={e => handleUpdateHighlightedBar(e, i)}
3740
3717
  options={[
3741
3718
  { value: '', label: '- Select Value -' },
3742
- ...(highlightedSeriesValues
3743
- ? [...new Set(highlightedSeriesValues)]
3744
- .sort()
3745
- .map(option => ({ value: option, label: option }))
3746
- : [])
3719
+ ...highlightedSeriesOptions
3747
3720
  ]}
3748
3721
  />
3749
3722
  <label>
@@ -3787,12 +3760,9 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
3787
3760
  </div>
3788
3761
  </fieldset>
3789
3762
  ))}
3790
- <button
3791
- className='btn btn-primary full-width'
3792
- onClick={e => handleAddNewHighlightedBar(e)}
3793
- >
3763
+ <Button variant='editor-primary' onClick={e => handleAddNewHighlightedBar(e)}>
3794
3764
  Add Highlighted Bar
3795
- </button>
3765
+ </Button>
3796
3766
  </>
3797
3767
  )}
3798
3768
  </>
@@ -3877,255 +3847,21 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
3877
3847
 
3878
3848
  {/* anchors */}
3879
3849
  {visHasAnchors() && config.orientation !== 'horizontal' && (
3880
- <div className='edit-block'>
3881
- <span className='edit-label column-heading'>Anchors</span>
3882
- <Accordion allowZeroExpanded>
3883
- {config.xAxis?.anchors?.map((anchor, index) => (
3884
- <AccordionItem
3885
- className='series-item series-item--chart'
3886
- key={`xaxis-anchors-2-${index}`}
3887
- >
3888
- <AccordionItemHeading className='series-item__title'>
3889
- <>
3890
- <AccordionItemButton className={'accordion__button accordion__button'}>
3891
- Anchor {index + 1}
3892
- <button
3893
- className='series-list__remove'
3894
- onClick={e => {
3895
- e.preventDefault()
3896
- const copiedAnchorGroups = [...config.xAxis.anchors]
3897
- copiedAnchorGroups.splice(index, 1)
3898
- updateConfig({
3899
- ...config,
3900
- xAxis: {
3901
- ...config.xAxis,
3902
- anchors: copiedAnchorGroups
3903
- }
3904
- })
3905
- }}
3906
- >
3907
- Remove
3908
- </button>
3909
- </AccordionItemButton>
3910
- </>
3911
- </AccordionItemHeading>
3912
- <AccordionItemPanel>
3913
- <label>
3914
- <span>Anchor Value</span>
3915
- <Tooltip style={{ textTransform: 'none' }}>
3916
- <Tooltip.Target>
3917
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
3918
- </Tooltip.Target>
3919
- <Tooltip.Content>
3920
- <p>Enter the value as its shown in the data column</p>
3921
- </Tooltip.Content>
3922
- </Tooltip>
3923
- <input
3924
- type='text'
3925
- value={config.xAxis.anchors[index].value ? config.xAxis.anchors[index].value : ''}
3926
- onChange={e => {
3927
- e.preventDefault()
3928
- const copiedAnchors = [...config.xAxis.anchors]
3929
- copiedAnchors[index].value = e.target.value
3930
- updateConfig({
3931
- ...config,
3932
- xAxis: {
3933
- ...config.xAxis,
3934
- anchors: copiedAnchors
3935
- }
3936
- })
3937
- }}
3938
- />
3939
- </label>
3940
-
3941
- <label>
3942
- <span>Anchor Color</span>
3943
- <input
3944
- type='text'
3945
- value={config.xAxis.anchors[index].color ? config.xAxis.anchors[index].color : ''}
3946
- onChange={e => {
3947
- e.preventDefault()
3948
- const copiedAnchors = [...config.xAxis.anchors]
3949
- copiedAnchors[index].color = e.target.value
3950
- updateConfig({
3951
- ...config,
3952
- xAxis: {
3953
- ...config.xAxis,
3954
- anchors: copiedAnchors
3955
- }
3956
- })
3957
- }}
3958
- />
3959
- </label>
3960
-
3961
- <Select
3962
- value={config.xAxis.anchors[index].lineStyle || ''}
3963
- label='Anchor Line Style'
3964
- onChange={e => {
3965
- const copiedAnchors = [...config.xAxis.anchors]
3966
- copiedAnchors[index].lineStyle = e.target.value
3967
- updateConfig({
3968
- ...config,
3969
- xAxis: {
3970
- ...config.xAxis,
3971
- anchors: copiedAnchors
3972
- }
3973
- })
3974
- }}
3975
- options={[
3976
- { value: '', label: 'Select' },
3977
- ...lineOptions.map(line => ({ value: line.value, label: line.value }))
3978
- ]}
3979
- />
3980
- </AccordionItemPanel>
3981
- </AccordionItem>
3982
- ))}
3983
- </Accordion>
3984
-
3985
- <button
3986
- className='btn btn-primary full-width'
3987
- onClick={e => {
3988
- e.preventDefault()
3989
- const anchors = [...config.xAxis.anchors]
3990
- anchors.push({} as Anchor)
3991
- updateConfig({
3992
- ...config,
3993
- xAxis: {
3994
- ...config.xAxis,
3995
- anchors
3996
- }
3997
- })
3998
- }}
3999
- >
4000
- Add Anchor
4001
- </button>
4002
- </div>
3850
+ <AxisAnchorEditor
3851
+ axisKey='xAxis'
3852
+ config={config}
3853
+ lineOptions={lineOptions}
3854
+ updateConfig={updateConfig}
3855
+ />
4003
3856
  )}
4004
3857
 
4005
3858
  {visHasAnchors() && config.orientation === 'horizontal' && (
4006
- <div className='edit-block'>
4007
- <span className='edit-label column-heading'>Anchors</span>
4008
- <Accordion allowZeroExpanded>
4009
- {config.yAxis?.anchors?.map((anchor, index) => (
4010
- <AccordionItem
4011
- className='series-item series-item--chart'
4012
- key={`accordion-yaxis-anchors-${index}`}
4013
- >
4014
- <AccordionItemHeading className='series-item__title'>
4015
- <>
4016
- <AccordionItemButton className={'accordion__button accordion__button'}>
4017
- Anchor {index + 1}
4018
- <button
4019
- className='series-list__remove'
4020
- onClick={e => {
4021
- e.preventDefault()
4022
- const copiedAnchorGroups = [...config.yAxis.anchors]
4023
- copiedAnchorGroups.splice(index, 1)
4024
- updateConfig({
4025
- ...config,
4026
- yAxis: {
4027
- ...config.yAxis,
4028
- anchors: copiedAnchorGroups
4029
- }
4030
- })
4031
- }}
4032
- >
4033
- Remove
4034
- </button>
4035
- </AccordionItemButton>
4036
- </>
4037
- </AccordionItemHeading>
4038
- <AccordionItemPanel>
4039
- <label>
4040
- <span>Anchor Value</span>
4041
- <Tooltip style={{ textTransform: 'none' }}>
4042
- <Tooltip.Target>
4043
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
4044
- </Tooltip.Target>
4045
- <Tooltip.Content>
4046
- <p>Enter the value as its shown in the data column</p>
4047
- </Tooltip.Content>
4048
- </Tooltip>
4049
- <input
4050
- type='text'
4051
- value={config.yAxis.anchors[index].value ? config.yAxis.anchors[index].value : ''}
4052
- onChange={e => {
4053
- e.preventDefault()
4054
- const copiedAnchors = [...config.yAxis.anchors]
4055
- copiedAnchors[index].value = e.target.value
4056
- updateConfig({
4057
- ...config,
4058
- yAxis: {
4059
- ...config.yAxis,
4060
- anchors: copiedAnchors
4061
- }
4062
- })
4063
- }}
4064
- />
4065
- </label>
4066
-
4067
- <label>
4068
- <span>Anchor Color</span>
4069
- <input
4070
- type='text'
4071
- value={config.yAxis.anchors[index].color ? config.yAxis.anchors[index].color : ''}
4072
- onChange={e => {
4073
- e.preventDefault()
4074
- const copiedAnchors = [...config.yAxis.anchors]
4075
- copiedAnchors[index].color = e.target.value
4076
- updateConfig({
4077
- ...config,
4078
- yAxis: {
4079
- ...config.yAxis,
4080
- anchors: copiedAnchors
4081
- }
4082
- })
4083
- }}
4084
- />
4085
- </label>
4086
-
4087
- <Select
4088
- value={config.yAxis.anchors[index].lineStyle || ''}
4089
- label='Anchor Line Style'
4090
- onChange={e => {
4091
- const copiedAnchors = [...config.yAxis.anchors]
4092
- copiedAnchors[index].lineStyle = e.target.value
4093
- updateConfig({
4094
- ...config,
4095
- yAxis: {
4096
- ...config.yAxis,
4097
- anchors: copiedAnchors
4098
- }
4099
- })
4100
- }}
4101
- options={[
4102
- { value: '', label: 'Select' },
4103
- ...lineOptions.map(line => ({ value: line.value, label: line.value }))
4104
- ]}
4105
- />
4106
- </AccordionItemPanel>
4107
- </AccordionItem>
4108
- ))}
4109
- </Accordion>
4110
-
4111
- <button
4112
- className='btn btn-primary full-width'
4113
- onClick={e => {
4114
- e.preventDefault()
4115
- const anchors = [...config.yAxis.anchors]
4116
- anchors.push({} as Anchor)
4117
- updateConfig({
4118
- ...config,
4119
- yAxis: {
4120
- ...config.yAxis,
4121
- anchors
4122
- }
4123
- })
4124
- }}
4125
- >
4126
- Add Anchor
4127
- </button>
4128
- </div>
3859
+ <AxisAnchorEditor
3860
+ axisKey='yAxis'
3861
+ config={config}
3862
+ lineOptions={lineOptions}
3863
+ updateConfig={updateConfig}
3864
+ />
4129
3865
  )}
4130
3866
  </AccordionItemPanel>
4131
3867
  </AccordionItem>
@@ -4143,6 +3879,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
4143
3879
  config={config}
4144
3880
  updateField={updateFieldDeprecated}
4145
3881
  deleteColumn={removeAdditionalColumn}
3882
+ hiddenColumnNames={seriesOwnedColumnNames}
4146
3883
  />{' '}
4147
3884
  </AccordionItemPanel>
4148
3885
  </AccordionItem>
@@ -4367,7 +4104,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
4367
4104
  {config.legend.seriesHighlight &&
4368
4105
  config.legend.seriesHighlight.map((val, i) => (
4369
4106
  <fieldset className='edit-block' key={`${val}-${i}`}>
4370
- <button
4107
+ <Button
4371
4108
  className='btn btn-danger'
4372
4109
  onClick={event => {
4373
4110
  event.preventDefault()
@@ -4380,7 +4117,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
4380
4117
  }}
4381
4118
  >
4382
4119
  Remove
4383
- </button>
4120
+ </Button>
4384
4121
  <Select
4385
4122
  value={config.legend.seriesHighlight[i]}
4386
4123
  fieldName='seriesHighlight'
@@ -4396,8 +4133,8 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
4396
4133
  />
4397
4134
  </fieldset>
4398
4135
  ))}
4399
- <button
4400
- className={'btn btn-primary full-width'}
4136
+ <Button
4137
+ variant='editor-primary'
4401
4138
  onClick={event => {
4402
4139
  event.preventDefault()
4403
4140
  const legendColumns = getLegendColumns()
@@ -4413,7 +4150,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
4413
4150
  }}
4414
4151
  >
4415
4152
  Add Isolate Value
4416
- </button>
4153
+ </Button>
4417
4154
  </fieldset>
4418
4155
  )}
4419
4156
  {/* end: isolated values */}
@@ -4600,6 +4337,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
4600
4337
  enableMarkupVariables={config.enableMarkupVariables || false}
4601
4338
  onMarkupVariablesChange={variables => updateField(null, null, 'markupVariables', variables)}
4602
4339
  onToggleEnable={enabled => updateField(null, null, 'enableMarkupVariables', enabled)}
4340
+ dataMetadata={config.dataMetadata}
4603
4341
  />
4604
4342
  )}
4605
4343
  <Panels.SmallMultiples name='Small Multiples' />