@cdc/chart 4.26.3 → 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 (128) hide show
  1. package/CONFIG.md +169 -0
  2. package/README.md +42 -17
  3. package/dist/cdcchart.js +34725 -33730
  4. package/examples/feature/bar/additional-column-tooltip.json +3 -3
  5. package/examples/feature/deviation/planet-deviation-config.json +2 -2
  6. package/examples/feature/paired-bar/paired-bar-example.json +2 -2
  7. package/examples/feature/tests-case-rate/case-rate-example-config.json +2 -2
  8. package/examples/feature/tests-covid/covid-confidence-example-config.json +2 -2
  9. package/examples/feature/tests-covid/covid-example-config.json +2 -2
  10. package/examples/feature/tests-cutoff/cutoff-example-config.json +2 -2
  11. package/examples/feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json +2 -2
  12. package/examples/metadata-variables.json +1 -1
  13. package/examples/minimal-example.json +24 -0
  14. package/examples/private/test2.json +35012 -0
  15. package/package.json +4 -4
  16. package/src/CdcChartComponent.tsx +9 -6
  17. package/src/_stories/{Chart.HTMLInDataTable.stories.tsx → Chart.HTMLInDataTable.smoke.stories.tsx} +10 -0
  18. package/src/_stories/Chart.Minimal.smoke.stories.tsx +25 -0
  19. package/src/_stories/{Chart.SmallMultiples.stories.tsx → Chart.SmallMultiples.smoke.stories.tsx} +17 -1
  20. package/src/_stories/{Chart.stories.tsx → Chart.smoke.stories.tsx} +12 -0
  21. package/src/_stories/{Chart.tooltip.stories.tsx → Chart.tooltip.smoke.stories.tsx} +68 -0
  22. package/src/_stories/ChartBar.Editor.stories.tsx +88 -85
  23. package/src/_stories/ChartEditor.Editor.stories.tsx +23 -0
  24. package/src/_stories/ChartLine.DashedDates.smoke.stories.tsx +23 -0
  25. package/src/_stories/_mock/line_chart_dashed_dates.json +276 -0
  26. package/src/_stories/_mock/paired-bar-abbr.json +1 -1
  27. package/src/_stories/_mock/paired-bar.json +1 -1
  28. package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -1
  29. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -2
  30. package/src/components/Axis/BottomAxis.tsx +1 -1
  31. package/src/components/Axis/LeftAxis.tsx +2 -4
  32. package/src/components/Axis/LeftAxisGridlines.tsx +1 -3
  33. package/src/components/BarChart/components/BarChart.Horizontal.tsx +9 -5
  34. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +9 -5
  35. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +9 -5
  36. package/src/components/BarChart/components/BarChart.Vertical.tsx +9 -6
  37. package/src/components/BarChart/components/BarChart.tsx +2 -6
  38. package/src/components/BarChart/helpers/useBarChart.ts +1 -1
  39. package/src/components/BoxPlot/BoxPlot.Vertical.tsx +3 -3
  40. package/src/components/EditorPanel/EditorPanel.tsx +633 -908
  41. package/src/components/EditorPanel/EditorPanelContext.ts +1 -1
  42. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +216 -165
  43. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +168 -116
  44. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +154 -109
  45. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
  46. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +12 -26
  47. package/src/components/EditorPanel/useEditorPermissions.ts +1 -10
  48. package/src/components/Forecasting/Forecasting.tsx +2 -2
  49. package/src/components/HorizonChart/HorizonChart.tsx +10 -2
  50. package/src/components/HoverLine/HoverLine.tsx +6 -8
  51. package/src/components/Legend/Legend.Component.tsx +4 -4
  52. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +5 -0
  53. package/src/components/Legend/LegendGroup/LegendGroup.tsx +3 -1
  54. package/src/components/Legend/helpers/index.ts +0 -2
  55. package/src/components/LineChart/LineChartProps.ts +1 -0
  56. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +3 -2
  57. package/src/components/LineChart/index.tsx +11 -4
  58. package/src/components/LinearChart/VisualizationRenderer.tsx +11 -1
  59. package/src/components/LinearChart/tests/LinearChart.test.tsx +147 -0
  60. package/src/components/LinearChart.tsx +98 -41
  61. package/src/components/PairedBarChart.jsx +2 -2
  62. package/src/components/PieChart/PieChart.tsx +11 -9
  63. package/src/components/Regions/components/Regions.tsx +30 -20
  64. package/src/components/ScatterPlot/ScatterPlot.jsx +19 -17
  65. package/src/components/SmallMultiples/SmallMultipleTile.tsx +1 -0
  66. package/src/components/Sparkline/components/SparkLine.tsx +1 -1
  67. package/src/components/WarmingStripes/WarmingStripes.tsx +5 -3
  68. package/src/data/initial-state.js +1 -0
  69. package/src/helpers/calculateLeftYAxisWidth.ts +193 -0
  70. package/src/helpers/tests/tooltipHelpers.test.ts +151 -0
  71. package/src/helpers/tooltipHelpers.ts +176 -0
  72. package/src/hooks/useHighlightedBars.ts +9 -16
  73. package/src/hooks/useProgrammaticTooltip.ts +16 -2
  74. package/src/hooks/useSmallMultipleSynchronization.ts +3 -4
  75. package/src/hooks/useTooltip.tsx +167 -38
  76. package/src/test/CdcChart.test.jsx +24 -0
  77. package/src/types/ChartConfig.ts +1 -0
  78. package/LICENSE +0 -201
  79. package/examples/chart-regression-2.json +0 -2360
  80. package/examples/feature/__data__/age-adjusted-rates.json +0 -1486
  81. package/examples/feature/__data__/city-temperature.json +0 -2198
  82. package/examples/feature/bar/Barchart_with_negative.json +0 -34
  83. package/examples/feature/boxplot/box-plot-data.json +0 -71
  84. package/examples/feature/boxplot/boxplot-data.json +0 -156
  85. package/examples/feature/combo/combochart-categories_are_numbers .json +0 -18
  86. package/examples/feature/forest-plot/log.json +0 -26
  87. package/examples/feature/tests-date-exclusions/date-exclusions-data.json +0 -162
  88. package/examples/region-issue.json +0 -2065
  89. package/examples/xaxis.json +0 -493
  90. /package/examples/{data → __data__}/data-with-metadata.json +0 -0
  91. /package/examples/feature/bar/{tall-data.json → __data__/tall-data.json} +0 -0
  92. /package/examples/feature/deviation/{planet-deviation-data.json → __data__/planet-deviation-data.json} +0 -0
  93. /package/examples/feature/paired-bar/{paired-bar-data.json → __data__/paired-bar-data.json} +0 -0
  94. /package/examples/feature/paired-bar/{paired-bar-formatted.json → __data__/paired-bar-formatted.json} +0 -0
  95. /package/examples/feature/tests-case-rate/{case-rate-example-data.json → __data__/case-rate-example-data.json} +0 -0
  96. /package/examples/feature/tests-covid/{covid-example-data-confidence.json → __data__/covid-example-data-confidence.json} +0 -0
  97. /package/examples/feature/tests-covid/{covid-example-data.json → __data__/covid-example-data.json} +0 -0
  98. /package/examples/feature/tests-cutoff/{cutoff-example-data.json → __data__/cutoff-example-data.json} +0 -0
  99. /package/examples/feature/tests-non-numerics/{planet-example-data-nonnumeric.json → __data__/planet-example-data-nonnumeric.json} +0 -0
  100. /package/src/_stories/{Chart.Anchors.stories.tsx → Chart.Anchors.smoke.stories.tsx} +0 -0
  101. /package/src/_stories/{Chart.BoxPlot.stories.tsx → Chart.BoxPlot.smoke.stories.tsx} +0 -0
  102. /package/src/_stories/{Chart.CI.stories.tsx → Chart.CI.smoke.stories.tsx} +0 -0
  103. /package/src/_stories/{Chart.Combo.stories.tsx → Chart.Combo.smoke.stories.tsx} +0 -0
  104. /package/src/_stories/{Chart.CustomColors.stories.tsx → Chart.CustomColors.smoke.stories.tsx} +0 -0
  105. /package/src/_stories/{Chart.Defaults.stories.tsx → Chart.Defaults.smoke.stories.tsx} +0 -0
  106. /package/src/_stories/{Chart.DynamicSeries.stories.tsx → Chart.DynamicSeries.smoke.stories.tsx} +0 -0
  107. /package/src/_stories/{Chart.Filters.stories.tsx → Chart.Filters.smoke.stories.tsx} +0 -0
  108. /package/src/_stories/{Chart.Forecast.stories.tsx → Chart.Forecast.smoke.stories.tsx} +0 -0
  109. /package/src/_stories/{Chart.Legend.Gradient.stories.tsx → Chart.Legend.Gradient.smoke.stories.tsx} +0 -0
  110. /package/src/_stories/{Chart.Patterns.stories.tsx → Chart.Patterns.smoke.stories.tsx} +0 -0
  111. /package/src/_stories/{Chart.PreserveDecimals.stories.tsx → Chart.PreserveDecimals.smoke.stories.tsx} +0 -0
  112. /package/src/_stories/{Chart.Regions.Categorical.stories.tsx → Chart.Regions.Categorical.smoke.stories.tsx} +0 -0
  113. /package/src/_stories/{Chart.Regions.DateScale.stories.tsx → Chart.Regions.DateScale.smoke.stories.tsx} +0 -0
  114. /package/src/_stories/{Chart.Regions.DateTimeScale.stories.tsx → Chart.Regions.DateTimeScale.smoke.stories.tsx} +0 -0
  115. /package/src/_stories/{Chart.ScatterPlot.stories.tsx → Chart.ScatterPlot.smoke.stories.tsx} +0 -0
  116. /package/src/_stories/{Chart.SmallestLeftAxisMax.stories.tsx → Chart.SmallestLeftAxisMax.smoke.stories.tsx} +0 -0
  117. /package/src/_stories/{ChartAnnotation.stories.tsx → ChartAnnotation.smoke.stories.tsx} +0 -0
  118. /package/src/_stories/{ChartAxisLabels.stories.tsx → ChartAxisLabels.smoke.stories.tsx} +0 -0
  119. /package/src/_stories/{ChartAxisTitles.stories.tsx → ChartAxisTitles.smoke.stories.tsx} +0 -0
  120. /package/src/_stories/{ChartBrush.Matrix.Continuous.stories.tsx → ChartBrush.Matrix.Continuous.smoke.stories.tsx} +0 -0
  121. /package/src/_stories/{ChartBrush.Matrix.Date.stories.tsx → ChartBrush.Matrix.Date.smoke.stories.tsx} +0 -0
  122. /package/src/_stories/{ChartBrush.Matrix.DateTime.stories.tsx → ChartBrush.Matrix.DateTime.smoke.stories.tsx} +0 -0
  123. /package/src/_stories/{ChartBrush.stories.tsx → ChartBrush.smoke.stories.tsx} +0 -0
  124. /package/src/_stories/{ChartLine.QuadrantAngles.stories.tsx → ChartLine.QuadrantAngles.smoke.stories.tsx} +0 -0
  125. /package/src/_stories/{ChartLine.Suppression.stories.tsx → ChartLine.Suppression.smoke.stories.tsx} +0 -0
  126. /package/src/_stories/{ChartLine.Symbols.stories.tsx → ChartLine.Symbols.smoke.stories.tsx} +0 -0
  127. /package/src/_stories/{ChartPrefixSuffix.stories.tsx → ChartPrefixSuffix.smoke.stories.tsx} +0 -0
  128. /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'
@@ -158,324 +160,363 @@ const PreliminaryData: React.FC<PreliminaryProps> = ({ config, updateConfig, dat
158
160
  updateConfig({ ...config, preliminaryData })
159
161
  }
160
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
+
161
170
  return (
162
171
  <>
163
- {config.preliminaryData &&
164
- config.preliminaryData?.map(
165
- (
166
- {
167
- displayGray,
168
- circleSize,
169
- column,
170
- displayLegend,
171
- displayTable,
172
- displayTooltip,
173
- label,
174
- seriesKeys,
175
- style,
176
- symbol,
177
- type,
178
- value,
179
- hideBarSymbol,
180
- hideLineStyle
181
- },
182
- i
183
- ) => {
184
- return (
185
- <div key={`preliminaryData-${i}`} className='edit-block'>
186
- <p> {type === 'suppression' ? 'Suppressed' : 'Effect'} Data</p>
187
- <button
188
- type='button'
189
- className='btn btn-danger'
190
- onClick={event => {
191
- event.preventDefault()
192
- removeColumn(i)
193
- }}
194
- >
195
- Remove
196
- </button>
197
-
198
- <Select
199
- value={type}
200
- initial={config.visualizationType == 'Bar' ? '' : 'Select'}
201
- fieldName='type'
202
- label='Type'
203
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
204
- options={getTypeOptions()}
205
- />
206
- {type === 'suppression' ? (
207
- <>
208
- <Select
209
- tooltip={
210
- <Tooltip style={{ textTransform: 'none' }}>
211
- <Tooltip.Target>
212
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
213
- </Tooltip.Target>
214
- <Tooltip.Content>
215
- <p>
216
- {' '}
217
- Without a selected "Data Series", the suppression symbol will be applied for all series in
218
- the current dataset visualization. However, choosing a specific "data series" will isolate
219
- the suppression to that series.
220
- </p>
221
- </Tooltip.Content>
222
- </Tooltip>
223
- }
224
- value={column}
225
- initial='Select'
226
- fieldName='column'
227
- label='Add Data Series'
228
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
229
- options={config.runtime?.seriesKeys}
230
- />
231
- <TextField
232
- value={value}
233
- fieldName='value'
234
- label='Suppressed Data Value'
235
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
236
- />
237
- {(hasComboLineSeries || config.visualizationType === 'Line') && (
238
- <>
239
- <Select
240
- tooltip={
241
- <Tooltip style={{ textTransform: 'none' }}>
242
- <Tooltip.Target>
243
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
244
- </Tooltip.Target>
245
- <Tooltip.Content>
246
- <p>
247
- The recommended approach for presenting data is to include a footnote indicating any
248
- data suppression.
249
- </p>
250
- </Tooltip.Content>
251
- </Tooltip>
252
- }
253
- value={style}
254
- initial='Select'
255
- fieldName='style'
256
- label={'suppression line style'}
257
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
258
- options={getStyleOptions(type)}
259
- />
260
- <CheckBox
261
- value={hideLineStyle}
262
- fieldName='hideLineStyle'
263
- label='Hide Suppressed line Style'
264
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
265
- />
266
- </>
267
- )}
268
-
269
- {(hasComboBarSeries || config.visualizationType === 'Bar') && (
270
- <>
271
- <Select
272
- tooltip={
273
- <Tooltip style={{ textTransform: 'none' }}>
274
- <Tooltip.Target>
275
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
276
- </Tooltip.Target>
277
- <Tooltip.Content>
278
- <p>
279
- A symbol is <i>required</i> to indicate suppressed data. We suggest "double
280
- asterisks." If "double asterisks" are already used elsewhere (e.g., footnotes), please
281
- select an alternative symbol from the menu to denote data suppression.
282
- </p>
283
- </Tooltip.Content>
284
- </Tooltip>
285
- }
286
- value={symbol}
287
- initial='Select'
288
- fieldName='symbol'
289
- label={config.visualizationType === 'Combo' ? 'suppression bar symbol' : 'suppression symbol'}
290
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
291
- options={getSymbolOptions()}
292
- />
293
- <CheckBox
294
- value={hideBarSymbol}
295
- fieldName='hideBarSymbol'
296
- label='Hide Suppressed Bar Symbol '
297
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
298
- />
299
- </>
300
- )}
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>
301
231
 
302
- <TextField
303
- tooltip={
304
- <Tooltip style={{ textTransform: 'none' }}>
305
- <Tooltip.Target>
306
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
307
- </Tooltip.Target>
308
- <Tooltip.Content>
309
- <p>This label will display in the tooltip and legend.</p>
310
- </Tooltip.Content>
311
- </Tooltip>
312
- }
313
- value={label ? label : 'Suppressed'}
314
- fieldName='label'
315
- label='Suppressed Data Label'
316
- placeholder=''
317
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
318
- />
319
- <CheckBox
320
- display={config.visualizationSubType === 'regular'}
321
- tooltip={
322
- <Tooltip style={{ textTransform: 'none' }}>
323
- <Tooltip.Target>
324
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
325
- </Tooltip.Target>
326
- <Tooltip.Content>
327
- <p>
328
- Deselecting the "Display In Tooltips" option prevents suppressed values from appearing in
329
- tooltips.
330
- </p>
331
- </Tooltip.Content>
332
- </Tooltip>
333
- }
334
- value={displayTooltip}
335
- fieldName='displayTooltip'
336
- label='Display in tooltips'
337
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
338
- />
339
- <CheckBox
340
- display={config.visualizationSubType === 'regular'}
341
- tooltip={
342
- <Tooltip style={{ textTransform: 'none' }}>
343
- <Tooltip.Target>
344
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
345
- </Tooltip.Target>
346
- <Tooltip.Content>
347
- <p>
348
- Deselecting "Display in Legend" indicates that you do not want to display suppressed data
349
- in the legend.
350
- </p>
351
- </Tooltip.Content>
352
- </Tooltip>
353
- }
354
- value={displayLegend}
355
- fieldName='displayLegend'
356
- label='Display in legend'
357
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
358
- />
359
- <CheckBox
360
- display={config.visualizationSubType === 'regular'}
361
- tooltip={
362
- <Tooltip style={{ textTransform: 'none' }}>
363
- <Tooltip.Target>
364
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
365
- </Tooltip.Target>
366
- <Tooltip.Content>
367
- <p>
368
- Deselecting "Display In Data Table" indicates that you do not want to display suppressed
369
- data in the data table.
370
- </p>
371
- </Tooltip.Content>
372
- </Tooltip>
373
- }
374
- value={displayTable}
375
- fieldName='displayTable'
376
- label='Display in table'
377
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
378
- />
379
- <CheckBox
380
- display={config.visualizationSubType === 'regular'}
381
- tooltip={
382
- <Tooltip style={{ textTransform: 'none' }}>
383
- <Tooltip.Target>
384
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
385
- </Tooltip.Target>
386
- <Tooltip.Content>
387
- <p>Selecting this option will apply to chart, tooltip hover, legend, and data table.</p>
388
- </Tooltip.Content>
389
- </Tooltip>
390
- }
391
- value={displayGray}
392
- fieldName='displayGray'
393
- label='Highlight Suppressed Data In Gray'
394
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
395
- />
396
- </>
397
- ) : (
398
- <>
399
- <label>
400
- <span className='edit-label'>ASSOCIATE TO THESE SERIES</span>
401
- <MultiSelect
402
- fieldName='seriesKeys'
403
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
404
- options={(config.runtime.lineSeriesKeys ?? config.runtime?.seriesKeys).map(c => ({
405
- label: c,
406
- value: c
407
- }))}
408
- selected={seriesKeys}
409
- />
410
- </label>
411
- <Select
412
- value={column}
413
- initial='Select'
414
- fieldName='column'
415
- label='COLUMN WITH CONFIGURATION VALUE'
416
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
417
- options={getColumnOptions()}
418
- />
419
- <TextField
420
- tooltip={
421
- <Tooltip style={{ textTransform: 'none' }}>
422
- <Tooltip.Target>
423
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
424
- </Tooltip.Target>
425
- <Tooltip.Content>
426
- <p>
427
- If 'Filled Circles' is selected as the style, this field is optional, and the style
428
- 'Filled Circles' will apply to all points within the associated series data.
429
- </p>
430
- </Tooltip.Content>
431
- </Tooltip>
432
- }
433
- value={value}
434
- fieldName='value'
435
- label='VALUE TO TRIGGER'
436
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
437
- />
438
- <Select
439
- value={style}
440
- initial='Select'
441
- fieldName='style'
442
- label='Style'
443
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
444
- options={getStyleOptions(type)}
445
- />
446
- {style.includes('Circles') && (
447
- <TextField
448
- className='number-narrow'
449
- type='number'
450
- value={circleSize}
451
- fieldName='circleSize'
452
- label='circle size'
453
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
454
- />
455
- )}
456
- {style !== 'Filled Circles' && (
457
- <TextField
458
- value={label}
459
- fieldName='label'
460
- label='Label'
461
- placeholder=''
232
+ <Select
233
+ value={type}
234
+ initial={config.visualizationType == 'Bar' ? '' : 'Select'}
235
+ fieldName='type'
236
+ label='Type'
462
237
  updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
238
+ options={getTypeOptions()}
463
239
  />
464
- )}
465
- </>
466
- )}
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>
467
508
  </div>
468
- )
469
- }
509
+ )}
510
+ </Draggable>
470
511
  )}
471
-
472
- <button type='button' onClick={addColumn} className='btn btn-primary full-width'>
512
+ />
513
+ <Button type='button' variant='editor-primary' onClick={addColumn}>
473
514
  {config.visualizationType === 'Line'
474
515
  ? 'Add Special Line'
475
516
  : config.visualizationType === 'Bar'
476
517
  ? ' Add Special Bar'
477
518
  : 'Add Special Bar/Line'}
478
- </button>
519
+ </Button>
479
520
  </>
480
521
  )
481
522
  }
@@ -486,6 +527,158 @@ interface CategoricalAxisProps {
486
527
  display: boolean
487
528
  }
488
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>
621
+ <Tooltip style={{ textTransform: 'none' }}>
622
+ <Tooltip.Target>
623
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
624
+ </Tooltip.Target>
625
+ <Tooltip.Content>
626
+ <p>Enter the value as its shown in the data column</p>
627
+ </Tooltip.Content>
628
+ </Tooltip>
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>
663
+ </div>
664
+ )}
665
+ </Draggable>
666
+ )}
667
+ />
668
+
669
+ <Button
670
+ variant='editor-primary'
671
+ onClick={e => {
672
+ e.preventDefault()
673
+ addAnchor()
674
+ }}
675
+ >
676
+ Add Anchor
677
+ </Button>
678
+ </>
679
+ )
680
+ }
681
+
489
682
  const CategoricalAxis: React.FC<CategoricalAxisProps> = ({ config, updateConfig, display }) => {
490
683
  const maxHeight = config?.yAxis?.maxValue
491
684
 
@@ -547,7 +740,7 @@ const CategoricalAxis: React.FC<CategoricalAxisProps> = ({ config, updateConfig,
547
740
  return (
548
741
  <div key={`preliminaryData-${i}`} className='edit-block'>
549
742
  <p>Axis Category {i + 1}</p>
550
- <button
743
+ <Button
551
744
  type='button'
552
745
  className='btn btn-danger'
553
746
  onClick={event => {
@@ -556,7 +749,7 @@ const CategoricalAxis: React.FC<CategoricalAxisProps> = ({ config, updateConfig,
556
749
  }}
557
750
  >
558
751
  Remove
559
- </button>
752
+ </Button>
560
753
  <TextField
561
754
  tooltip={
562
755
  <Tooltip style={{ textTransform: 'none' }}>
@@ -602,9 +795,9 @@ const CategoricalAxis: React.FC<CategoricalAxisProps> = ({ config, updateConfig,
602
795
  )
603
796
  })}
604
797
 
605
- <button type='button' onClick={addColumn} className='btn btn-primary full-width'>
798
+ <Button type='button' variant='editor-primary' onClick={addColumn}>
606
799
  Add Axis Category
607
- </button>
800
+ </Button>
608
801
  </>
609
802
  )
610
803
  }
@@ -725,7 +918,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
725
918
  }
726
919
  }, [config.visualizationType])
727
920
 
728
- 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 })
729
922
 
730
923
  const getItemStyle = (isDragging, draggableStyle) => ({
731
924
  ...draggableStyle
@@ -774,6 +967,17 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
774
967
  }
775
968
 
776
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
+
777
981
  const updateFieldDeprecated = (section, subsection, fieldName, newValue) => {
778
982
  if (isDebug)
779
983
  console.error(
@@ -816,6 +1020,23 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
816
1020
  })
817
1021
  return
818
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
+
819
1040
  if (null === section && null === subsection) {
820
1041
  // special case that allows for updating the config object directly
821
1042
  if (!truthy(fieldName)) console.error('fieldName is required')
@@ -1024,7 +1245,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
1024
1245
  // prettier-ignore
1025
1246
  const {
1026
1247
  highlightedBarValues,
1027
- highlightedSeriesValues,
1248
+ getHighlightedSeriesValues,
1028
1249
  handleUpdateHighlightedBar,
1029
1250
  handleAddNewHighlightedBar,
1030
1251
  handleRemoveHighlightedBar,
@@ -1634,6 +1855,27 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
1634
1855
  const hasDynamicCategory = ![undefined, '- Select - '].includes(config.series?.[0]?.dynamicCategory)
1635
1856
  const hasMultipleSeries = config.series?.length > 1
1636
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
+
1637
1879
  const editorContextValues = {
1638
1880
  addNewExclusion,
1639
1881
  data,
@@ -1652,7 +1894,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
1652
1894
  handleUpdateHighlightedBar,
1653
1895
  handleRemoveHighlightedBar,
1654
1896
  isPaletteReversed: config.general?.palette?.isReversed,
1655
- highlightedSeriesValues,
1897
+ getHighlightedSeriesValues,
1656
1898
  handleUpdateHighlightedBorderWidth,
1657
1899
  handleUpdateHighlightedBarColor,
1658
1900
  setLollipopShape,
@@ -1775,49 +2017,40 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
1775
2017
  getColumns={getColumns}
1776
2018
  handleForecastPaletteSelection={handleForecastPaletteSelection}
1777
2019
  >
1778
- <fieldset>
1779
- <legend className='edit-label d-flex align-items-center'>
1780
- Displaying
1781
- <Tooltip style={{ textTransform: 'none' }}>
1782
- <Tooltip.Target>
1783
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1784
- </Tooltip.Target>
1785
- <Tooltip.Content>
1786
- <p>
1787
- A data series is a set of related data points plotted in a chart and
1788
- typically represented in the chart legend.
1789
- </p>
1790
- </Tooltip.Content>
1791
- </Tooltip>
1792
- </legend>
1793
- </fieldset>
1794
-
1795
- <DragDropContext
1796
- onDragEnd={({ source, destination }) =>
1797
- 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
+ </>
1798
2038
  }
1799
- >
1800
- <Droppable droppableId='filter_order'>
1801
- {/* prettier-ignore */}
1802
- {provided => {
1803
- return (
1804
- <ul
1805
- {...provided.droppableProps}
1806
- className='series-list'
1807
- ref={provided.innerRef}
1808
- >
1809
- <Panels.Series.List
1810
- series={config.series}
1811
- getItemStyle={getItemStyle}
1812
- sortableItemStyles={sortableItemStyles}
1813
- chartsWithOptions={chartsWithOptions}
1814
- />
1815
- {provided.placeholder}
1816
- </ul>
1817
- )
1818
- }}
1819
- </Droppable>
1820
- </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
+ />
1821
2054
  </Panels.Series.Wrapper>
1822
2055
  )}
1823
2056
  </>
@@ -2045,38 +2278,6 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
2045
2278
  }
2046
2279
  updateField={updateFieldDeprecated}
2047
2280
  />
2048
- <TextField
2049
- value={config.yAxis.size}
2050
- type='number'
2051
- section='yAxis'
2052
- fieldName='size'
2053
- label={config.orientation === 'horizontal' ? 'Size (Height)' : 'Size (Width)'}
2054
- className='number-narrow'
2055
- updateField={updateFieldDeprecated}
2056
- tooltip={
2057
- <Tooltip style={{ textTransform: 'none' }}>
2058
- <Tooltip.Target>
2059
- <Icon
2060
- display='question'
2061
- style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2062
- />
2063
- </Tooltip.Target>
2064
- <Tooltip.Content>
2065
- <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>
2066
- </Tooltip.Content>
2067
- </Tooltip>
2068
- }
2069
- />
2070
- <TextField
2071
- display={!visHasCategoricalAxis()}
2072
- value={config.yAxis.labelOffset}
2073
- section='yAxis'
2074
- fieldName='labelOffset'
2075
- label='Label offset'
2076
- type='number'
2077
- className='number-narrow'
2078
- updateField={updateFieldDeprecated}
2079
- />
2080
2281
  {config.orientation === 'horizontal' && (
2081
2282
  <CheckBox
2082
2283
  value={config.isResponsiveTicks}
@@ -2481,251 +2682,20 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
2481
2682
  )}
2482
2683
  {/* start: anchors */}
2483
2684
  {visHasAnchors() && config.orientation !== 'horizontal' && (
2484
- <div className='edit-block'>
2485
- <span className='edit-label column-heading'>Anchors</span>
2486
- <Accordion allowZeroExpanded>
2487
- {config.yAxis?.anchors?.map((anchor, index) => (
2488
- <AccordionItem
2489
- className='series-item series-item--chart'
2490
- key={`yaxis-anchors-2-${index}`}
2491
- >
2492
- <AccordionItemHeading className='series-item__title'>
2493
- <>
2494
- <AccordionItemButton className={'accordion__button accordion__button'}>
2495
- Anchor {index + 1}
2496
- <button
2497
- className='series-list__remove'
2498
- onClick={e => {
2499
- e.preventDefault()
2500
- const copiedAnchorGroups = [...config.yAxis.anchors]
2501
- copiedAnchorGroups.splice(index, 1)
2502
- updateConfig({
2503
- ...config,
2504
- yAxis: {
2505
- ...config.yAxis,
2506
- anchors: copiedAnchorGroups
2507
- }
2508
- })
2509
- }}
2510
- >
2511
- Remove
2512
- </button>
2513
- </AccordionItemButton>
2514
- </>
2515
- </AccordionItemHeading>
2516
- <AccordionItemPanel>
2517
- <label>
2518
- <span>Anchor Value</span>
2519
- <Tooltip style={{ textTransform: 'none' }}>
2520
- <Tooltip.Target>
2521
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2522
- </Tooltip.Target>
2523
- <Tooltip.Content>
2524
- <p>Enter the value as its shown in the data column</p>
2525
- </Tooltip.Content>
2526
- </Tooltip>
2527
- <input
2528
- type='text'
2529
- value={config.yAxis.anchors[index].value ? config.yAxis.anchors[index].value : ''}
2530
- onChange={e => {
2531
- e.preventDefault()
2532
- const copiedAnchors = [...config.yAxis.anchors]
2533
- copiedAnchors[index].value = e.target.value
2534
- updateConfig({
2535
- ...config,
2536
- yAxis: {
2537
- ...config.yAxis,
2538
- anchors: copiedAnchors
2539
- }
2540
- })
2541
- }}
2542
- />
2543
- </label>
2544
-
2545
- <label>
2546
- <span>Anchor Color</span>
2547
- <input
2548
- type='text'
2549
- value={config.yAxis.anchors[index].color ? config.yAxis.anchors[index].color : ''}
2550
- onChange={e => {
2551
- e.preventDefault()
2552
- const copiedAnchors = [...config.yAxis.anchors]
2553
- copiedAnchors[index].color = e.target.value
2554
- updateConfig({
2555
- ...config,
2556
- yAxis: {
2557
- ...config.yAxis,
2558
- anchors: copiedAnchors
2559
- }
2560
- })
2561
- }}
2562
- />
2563
- </label>
2564
-
2565
- <Select
2566
- value={config.yAxis.anchors[index].lineStyle || ''}
2567
- label='Anchor Line Style'
2568
- onChange={e => {
2569
- const copiedAnchors = [...config.yAxis.anchors]
2570
- copiedAnchors[index].lineStyle = e.target.value
2571
- updateConfig({
2572
- ...config,
2573
- yAxis: {
2574
- ...config.yAxis,
2575
- anchors: copiedAnchors
2576
- }
2577
- })
2578
- }}
2579
- options={[
2580
- { value: '', label: 'Select' },
2581
- ...lineOptions.map(line => ({ value: line.value, label: line.value }))
2582
- ]}
2583
- />
2584
- </AccordionItemPanel>
2585
- </AccordionItem>
2586
- ))}
2587
- </Accordion>
2588
-
2589
- <button
2590
- className='btn btn-primary full-width'
2591
- onClick={e => {
2592
- e.preventDefault()
2593
- const anchors = [...config.yAxis.anchors]
2594
- anchors.push({} as Anchor)
2595
- updateConfig({
2596
- ...config,
2597
- yAxis: {
2598
- ...config.yAxis,
2599
- anchors
2600
- }
2601
- })
2602
- }}
2603
- >
2604
- Add Anchor
2605
- </button>
2606
- </div>
2685
+ <AxisAnchorEditor
2686
+ axisKey='yAxis'
2687
+ config={config}
2688
+ lineOptions={lineOptions}
2689
+ updateConfig={updateConfig}
2690
+ />
2607
2691
  )}
2608
2692
  {visHasAnchors() && config.orientation === 'horizontal' && (
2609
- <div className='edit-block'>
2610
- <span className='edit-label column-heading'>Anchors</span>
2611
- <Accordion allowZeroExpanded>
2612
- {config.xAxis?.anchors?.map((anchor, index) => (
2613
- <AccordionItem className='series-item series-item--chart' key={`xaxis-anchors-${index}`}>
2614
- <AccordionItemHeading className='series-item__title'>
2615
- <>
2616
- <AccordionItemButton className={'accordion__button accordion__button'}>
2617
- Anchor {index + 1}
2618
- <button
2619
- className='series-list__remove'
2620
- onClick={e => {
2621
- e.preventDefault()
2622
- const copiedAnchorGroups = [...config.xAxis.anchors]
2623
- copiedAnchorGroups.splice(index, 1)
2624
- updateConfig({
2625
- ...config,
2626
- xAxis: {
2627
- ...config.xAxis,
2628
- anchors: copiedAnchorGroups
2629
- }
2630
- })
2631
- }}
2632
- >
2633
- Remove
2634
- </button>
2635
- </AccordionItemButton>
2636
- </>
2637
- </AccordionItemHeading>
2638
- <AccordionItemPanel>
2639
- <label>
2640
- <span>Anchor Value</span>
2641
- <Tooltip style={{ textTransform: 'none' }}>
2642
- <Tooltip.Target>
2643
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2644
- </Tooltip.Target>
2645
- <Tooltip.Content>
2646
- <p>Enter the value as its shown in the data column</p>
2647
- </Tooltip.Content>
2648
- </Tooltip>
2649
- <input
2650
- type='text'
2651
- value={config.xAxis.anchors[index].value ? config.xAxis.anchors[index].value : ''}
2652
- onChange={e => {
2653
- e.preventDefault()
2654
- const copiedAnchors = [...config.xAxis.anchors]
2655
- copiedAnchors[index].value = e.target.value
2656
- updateConfig({
2657
- ...config,
2658
- xAxis: {
2659
- ...config.xAxis,
2660
- anchors: copiedAnchors
2661
- }
2662
- })
2663
- }}
2664
- />
2665
- </label>
2666
-
2667
- <label>
2668
- <span>Anchor Color</span>
2669
- <input
2670
- type='text'
2671
- value={config.xAxis.anchors[index].color ? config.xAxis.anchors[index].color : ''}
2672
- onChange={e => {
2673
- e.preventDefault()
2674
- const copiedAnchors = [...config.xAxis.anchors]
2675
- copiedAnchors[index].color = e.target.value
2676
- updateConfig({
2677
- ...config,
2678
- xAxis: {
2679
- ...config.xAxis,
2680
- anchors: copiedAnchors
2681
- }
2682
- })
2683
- }}
2684
- />
2685
- </label>
2686
-
2687
- <Select
2688
- value={config.xAxis.anchors[index].lineStyle || ''}
2689
- label='Anchor Line Style'
2690
- onChange={e => {
2691
- const copiedAnchors = [...config.xAxis.anchors]
2692
- copiedAnchors[index].lineStyle = e.target.value
2693
- updateConfig({
2694
- ...config,
2695
- xAxis: {
2696
- ...config.xAxis,
2697
- anchors: copiedAnchors
2698
- }
2699
- })
2700
- }}
2701
- options={[
2702
- { value: '', label: 'Select' },
2703
- ...lineOptions.map(line => ({ value: line.value, label: line.value }))
2704
- ]}
2705
- />
2706
- </AccordionItemPanel>
2707
- </AccordionItem>
2708
- ))}
2709
- </Accordion>
2710
-
2711
- <button
2712
- className='btn btn-primary full-width'
2713
- onClick={e => {
2714
- e.preventDefault()
2715
- const anchors = [...config.xAxis.anchors]
2716
- anchors.push({} as Anchor)
2717
- updateConfig({
2718
- ...config,
2719
- xAxis: {
2720
- ...config.xAxis,
2721
- anchors
2722
- }
2723
- })
2724
- }}
2725
- >
2726
- Add Anchor
2727
- </button>
2728
- </div>
2693
+ <AxisAnchorEditor
2694
+ axisKey='xAxis'
2695
+ config={config}
2696
+ lineOptions={lineOptions}
2697
+ updateConfig={updateConfig}
2698
+ />
2729
2699
  )}
2730
2700
  {/* end: anchors */}
2731
2701
  </AccordionItemPanel>
@@ -2961,11 +2931,19 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
2961
2931
  section='xAxis'
2962
2932
  fieldName='type'
2963
2933
  updateField={(_section, _subsection, _fieldName, value) => {
2934
+ const autoDetectedDateParseFormat = getAutoDetectedXAxisDateParseFormat(
2935
+ config.xAxis.dataKey,
2936
+ value
2937
+ )
2938
+
2964
2939
  updateConfig({
2965
2940
  ...config,
2966
2941
  xAxis: {
2967
2942
  ...config.xAxis,
2968
- type: value
2943
+ type: value,
2944
+ ...(autoDetectedDateParseFormat
2945
+ ? { dateParseFormat: autoDetectedDateParseFormat }
2946
+ : {})
2969
2947
  }
2970
2948
  })
2971
2949
  }}
@@ -3587,18 +3565,6 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
3587
3565
  updateField={updateFieldDeprecated}
3588
3566
  />
3589
3567
  )}
3590
- {config.orientation === 'horizontal' && (
3591
- <TextField
3592
- value={config.xAxis.labelOffset}
3593
- section='xAxis'
3594
- fieldName='labelOffset'
3595
- label='Label offset'
3596
- type='number'
3597
- className='number-narrow'
3598
- updateField={updateFieldDeprecated}
3599
- />
3600
- )}
3601
-
3602
3568
  {/* Hiding this for now, not interested in moving the axis lines away from chart comp. right now. */}
3603
3569
  {/* <TextField value={config.xAxis.axisPadding} type='number' max={10} min={0} section='xAxis' fieldName='axisPadding' label={'Axis Padding'} className='number-narrow' updateField={updateFieldDeprecated} /> */}
3604
3570
  {(config.xAxis.type === 'continuous' || config.forestPlot.type === 'Logarithmic') && (
@@ -3737,12 +3703,12 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
3737
3703
  highlightedBarValues.map((highlightedBarValue, i) => (
3738
3704
  <fieldset>
3739
3705
  <div className='edit-block' key={`highlighted-bar-${i}`}>
3740
- <button
3706
+ <Button
3741
3707
  className='btn btn-danger'
3742
3708
  onClick={e => handleRemoveHighlightedBar(e, i)}
3743
3709
  >
3744
3710
  Remove
3745
- </button>
3711
+ </Button>
3746
3712
  <p>Highlighted Bar {i + 1}</p>
3747
3713
  <Select
3748
3714
  value={config.highlightedBarValues[i].value}
@@ -3750,11 +3716,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
3750
3716
  onChange={e => handleUpdateHighlightedBar(e, i)}
3751
3717
  options={[
3752
3718
  { value: '', label: '- Select Value -' },
3753
- ...(highlightedSeriesValues
3754
- ? [...new Set(highlightedSeriesValues)]
3755
- .sort()
3756
- .map(option => ({ value: option, label: option }))
3757
- : [])
3719
+ ...highlightedSeriesOptions
3758
3720
  ]}
3759
3721
  />
3760
3722
  <label>
@@ -3798,12 +3760,9 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
3798
3760
  </div>
3799
3761
  </fieldset>
3800
3762
  ))}
3801
- <button
3802
- className='btn btn-primary full-width'
3803
- onClick={e => handleAddNewHighlightedBar(e)}
3804
- >
3763
+ <Button variant='editor-primary' onClick={e => handleAddNewHighlightedBar(e)}>
3805
3764
  Add Highlighted Bar
3806
- </button>
3765
+ </Button>
3807
3766
  </>
3808
3767
  )}
3809
3768
  </>
@@ -3888,255 +3847,21 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
3888
3847
 
3889
3848
  {/* anchors */}
3890
3849
  {visHasAnchors() && config.orientation !== 'horizontal' && (
3891
- <div className='edit-block'>
3892
- <span className='edit-label column-heading'>Anchors</span>
3893
- <Accordion allowZeroExpanded>
3894
- {config.xAxis?.anchors?.map((anchor, index) => (
3895
- <AccordionItem
3896
- className='series-item series-item--chart'
3897
- key={`xaxis-anchors-2-${index}`}
3898
- >
3899
- <AccordionItemHeading className='series-item__title'>
3900
- <>
3901
- <AccordionItemButton className={'accordion__button accordion__button'}>
3902
- Anchor {index + 1}
3903
- <button
3904
- className='series-list__remove'
3905
- onClick={e => {
3906
- e.preventDefault()
3907
- const copiedAnchorGroups = [...config.xAxis.anchors]
3908
- copiedAnchorGroups.splice(index, 1)
3909
- updateConfig({
3910
- ...config,
3911
- xAxis: {
3912
- ...config.xAxis,
3913
- anchors: copiedAnchorGroups
3914
- }
3915
- })
3916
- }}
3917
- >
3918
- Remove
3919
- </button>
3920
- </AccordionItemButton>
3921
- </>
3922
- </AccordionItemHeading>
3923
- <AccordionItemPanel>
3924
- <label>
3925
- <span>Anchor Value</span>
3926
- <Tooltip style={{ textTransform: 'none' }}>
3927
- <Tooltip.Target>
3928
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
3929
- </Tooltip.Target>
3930
- <Tooltip.Content>
3931
- <p>Enter the value as its shown in the data column</p>
3932
- </Tooltip.Content>
3933
- </Tooltip>
3934
- <input
3935
- type='text'
3936
- value={config.xAxis.anchors[index].value ? config.xAxis.anchors[index].value : ''}
3937
- onChange={e => {
3938
- e.preventDefault()
3939
- const copiedAnchors = [...config.xAxis.anchors]
3940
- copiedAnchors[index].value = e.target.value
3941
- updateConfig({
3942
- ...config,
3943
- xAxis: {
3944
- ...config.xAxis,
3945
- anchors: copiedAnchors
3946
- }
3947
- })
3948
- }}
3949
- />
3950
- </label>
3951
-
3952
- <label>
3953
- <span>Anchor Color</span>
3954
- <input
3955
- type='text'
3956
- value={config.xAxis.anchors[index].color ? config.xAxis.anchors[index].color : ''}
3957
- onChange={e => {
3958
- e.preventDefault()
3959
- const copiedAnchors = [...config.xAxis.anchors]
3960
- copiedAnchors[index].color = e.target.value
3961
- updateConfig({
3962
- ...config,
3963
- xAxis: {
3964
- ...config.xAxis,
3965
- anchors: copiedAnchors
3966
- }
3967
- })
3968
- }}
3969
- />
3970
- </label>
3971
-
3972
- <Select
3973
- value={config.xAxis.anchors[index].lineStyle || ''}
3974
- label='Anchor Line Style'
3975
- onChange={e => {
3976
- const copiedAnchors = [...config.xAxis.anchors]
3977
- copiedAnchors[index].lineStyle = e.target.value
3978
- updateConfig({
3979
- ...config,
3980
- xAxis: {
3981
- ...config.xAxis,
3982
- anchors: copiedAnchors
3983
- }
3984
- })
3985
- }}
3986
- options={[
3987
- { value: '', label: 'Select' },
3988
- ...lineOptions.map(line => ({ value: line.value, label: line.value }))
3989
- ]}
3990
- />
3991
- </AccordionItemPanel>
3992
- </AccordionItem>
3993
- ))}
3994
- </Accordion>
3995
-
3996
- <button
3997
- className='btn btn-primary full-width'
3998
- onClick={e => {
3999
- e.preventDefault()
4000
- const anchors = [...config.xAxis.anchors]
4001
- anchors.push({} as Anchor)
4002
- updateConfig({
4003
- ...config,
4004
- xAxis: {
4005
- ...config.xAxis,
4006
- anchors
4007
- }
4008
- })
4009
- }}
4010
- >
4011
- Add Anchor
4012
- </button>
4013
- </div>
3850
+ <AxisAnchorEditor
3851
+ axisKey='xAxis'
3852
+ config={config}
3853
+ lineOptions={lineOptions}
3854
+ updateConfig={updateConfig}
3855
+ />
4014
3856
  )}
4015
3857
 
4016
3858
  {visHasAnchors() && config.orientation === 'horizontal' && (
4017
- <div className='edit-block'>
4018
- <span className='edit-label column-heading'>Anchors</span>
4019
- <Accordion allowZeroExpanded>
4020
- {config.yAxis?.anchors?.map((anchor, index) => (
4021
- <AccordionItem
4022
- className='series-item series-item--chart'
4023
- key={`accordion-yaxis-anchors-${index}`}
4024
- >
4025
- <AccordionItemHeading className='series-item__title'>
4026
- <>
4027
- <AccordionItemButton className={'accordion__button accordion__button'}>
4028
- Anchor {index + 1}
4029
- <button
4030
- className='series-list__remove'
4031
- onClick={e => {
4032
- e.preventDefault()
4033
- const copiedAnchorGroups = [...config.yAxis.anchors]
4034
- copiedAnchorGroups.splice(index, 1)
4035
- updateConfig({
4036
- ...config,
4037
- yAxis: {
4038
- ...config.yAxis,
4039
- anchors: copiedAnchorGroups
4040
- }
4041
- })
4042
- }}
4043
- >
4044
- Remove
4045
- </button>
4046
- </AccordionItemButton>
4047
- </>
4048
- </AccordionItemHeading>
4049
- <AccordionItemPanel>
4050
- <label>
4051
- <span>Anchor Value</span>
4052
- <Tooltip style={{ textTransform: 'none' }}>
4053
- <Tooltip.Target>
4054
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
4055
- </Tooltip.Target>
4056
- <Tooltip.Content>
4057
- <p>Enter the value as its shown in the data column</p>
4058
- </Tooltip.Content>
4059
- </Tooltip>
4060
- <input
4061
- type='text'
4062
- value={config.yAxis.anchors[index].value ? config.yAxis.anchors[index].value : ''}
4063
- onChange={e => {
4064
- e.preventDefault()
4065
- const copiedAnchors = [...config.yAxis.anchors]
4066
- copiedAnchors[index].value = e.target.value
4067
- updateConfig({
4068
- ...config,
4069
- yAxis: {
4070
- ...config.yAxis,
4071
- anchors: copiedAnchors
4072
- }
4073
- })
4074
- }}
4075
- />
4076
- </label>
4077
-
4078
- <label>
4079
- <span>Anchor Color</span>
4080
- <input
4081
- type='text'
4082
- value={config.yAxis.anchors[index].color ? config.yAxis.anchors[index].color : ''}
4083
- onChange={e => {
4084
- e.preventDefault()
4085
- const copiedAnchors = [...config.yAxis.anchors]
4086
- copiedAnchors[index].color = e.target.value
4087
- updateConfig({
4088
- ...config,
4089
- yAxis: {
4090
- ...config.yAxis,
4091
- anchors: copiedAnchors
4092
- }
4093
- })
4094
- }}
4095
- />
4096
- </label>
4097
-
4098
- <Select
4099
- value={config.yAxis.anchors[index].lineStyle || ''}
4100
- label='Anchor Line Style'
4101
- onChange={e => {
4102
- const copiedAnchors = [...config.yAxis.anchors]
4103
- copiedAnchors[index].lineStyle = e.target.value
4104
- updateConfig({
4105
- ...config,
4106
- yAxis: {
4107
- ...config.yAxis,
4108
- anchors: copiedAnchors
4109
- }
4110
- })
4111
- }}
4112
- options={[
4113
- { value: '', label: 'Select' },
4114
- ...lineOptions.map(line => ({ value: line.value, label: line.value }))
4115
- ]}
4116
- />
4117
- </AccordionItemPanel>
4118
- </AccordionItem>
4119
- ))}
4120
- </Accordion>
4121
-
4122
- <button
4123
- className='btn btn-primary full-width'
4124
- onClick={e => {
4125
- e.preventDefault()
4126
- const anchors = [...config.yAxis.anchors]
4127
- anchors.push({} as Anchor)
4128
- updateConfig({
4129
- ...config,
4130
- yAxis: {
4131
- ...config.yAxis,
4132
- anchors
4133
- }
4134
- })
4135
- }}
4136
- >
4137
- Add Anchor
4138
- </button>
4139
- </div>
3859
+ <AxisAnchorEditor
3860
+ axisKey='yAxis'
3861
+ config={config}
3862
+ lineOptions={lineOptions}
3863
+ updateConfig={updateConfig}
3864
+ />
4140
3865
  )}
4141
3866
  </AccordionItemPanel>
4142
3867
  </AccordionItem>
@@ -4379,7 +4104,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
4379
4104
  {config.legend.seriesHighlight &&
4380
4105
  config.legend.seriesHighlight.map((val, i) => (
4381
4106
  <fieldset className='edit-block' key={`${val}-${i}`}>
4382
- <button
4107
+ <Button
4383
4108
  className='btn btn-danger'
4384
4109
  onClick={event => {
4385
4110
  event.preventDefault()
@@ -4392,7 +4117,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
4392
4117
  }}
4393
4118
  >
4394
4119
  Remove
4395
- </button>
4120
+ </Button>
4396
4121
  <Select
4397
4122
  value={config.legend.seriesHighlight[i]}
4398
4123
  fieldName='seriesHighlight'
@@ -4408,8 +4133,8 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
4408
4133
  />
4409
4134
  </fieldset>
4410
4135
  ))}
4411
- <button
4412
- className={'btn btn-primary full-width'}
4136
+ <Button
4137
+ variant='editor-primary'
4413
4138
  onClick={event => {
4414
4139
  event.preventDefault()
4415
4140
  const legendColumns = getLegendColumns()
@@ -4425,7 +4150,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
4425
4150
  }}
4426
4151
  >
4427
4152
  Add Isolate Value
4428
- </button>
4153
+ </Button>
4429
4154
  </fieldset>
4430
4155
  )}
4431
4156
  {/* end: isolated values */}