@cdc/chart 4.23.3 → 4.23.5

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 (98) hide show
  1. package/dist/cdcchart.js +52543 -50830
  2. package/examples/feature/__data__/area-chart.json +56 -0
  3. package/examples/{planet-example-data.json → feature/__data__/planet-example-data.json} +3 -8
  4. package/examples/feature/__data__/planet-logaritmic-data.json +56 -0
  5. package/examples/feature/area/area-chart-category.json +240 -0
  6. package/examples/{area-chart.json → feature/area/area-chart-date.json} +70 -13
  7. package/examples/feature/bar/example-bar-chart.json +558 -0
  8. package/examples/{horizontal-chart-max-increase.json → feature/bar/horizontal-chart-max-increase.json} +10 -4
  9. package/examples/{horizontal-chart.json → feature/bar/horizontal-chart.json} +10 -4
  10. package/examples/{horizontal-stacked-bar-chart.json → feature/bar/horizontal-stacked-bar-chart.json} +7 -3
  11. package/examples/{planet-chart-horizontal-example-config.json → feature/bar/planet-chart-horizontal-example-config.json} +8 -3
  12. package/examples/feature/bar/planet-chart-logaritmic-config.json +170 -0
  13. package/examples/{planet-example-config.json → feature/bar/planet-example-config.json} +2 -2
  14. package/examples/{box-plot.json → feature/boxplot/boxplot.json} +7 -7
  15. package/examples/feature/boxplot/testing.csv +38 -0
  16. package/examples/feature/boxplot/valid-boxplot.csv +17 -0
  17. package/examples/feature/combo/combochart-categories_are_numbers .json +18 -0
  18. package/examples/{planet-combo-example-config.json → feature/combo/planet-combo-example-config.json} +1 -1
  19. package/examples/{planet-deviation-config.json → feature/deviation/planet-deviation-config.json} +2 -2
  20. package/examples/{planet-deviation-data.json → feature/deviation/planet-deviation-data.json} +9 -9
  21. package/examples/feature/filters/filter-testing.json +212 -0
  22. package/examples/feature/forecasting/case_date_example.csv +130 -0
  23. package/examples/feature/forecasting/effective_reproduction.json +202 -0
  24. package/examples/feature/forecasting/r_data.csv +130 -0
  25. package/examples/feature/forecasting/random_data.csv +366 -0
  26. package/examples/feature/line/line-chart.json +124 -0
  27. package/examples/{paired-bar-example.json → feature/paired-bar/paired-bar-example.json} +10 -4
  28. package/examples/{planet-pie-example-config.json → feature/pie/planet-pie-example-config.json} +2 -2
  29. package/examples/{scatterplot.json → feature/scatterplot/scatterplot.json} +1 -1
  30. package/examples/feature/test-highlight/test-highlight-2.json +789 -0
  31. package/examples/feature/test-highlight/test-highlight-vertical.json +561 -0
  32. package/examples/feature/test-highlight/test-highlight.json +100 -0
  33. package/examples/{case-rate-example-config.json → feature/tests-case-rate/case-rate-example-config.json} +2 -2
  34. package/examples/{covid-confidence-example-config.json → feature/tests-covid/covid-confidence-example-config.json} +8 -3
  35. package/examples/{covid-example-config.json → feature/tests-covid/covid-example-config.json} +7 -3
  36. package/examples/{cutoff-example-config.json → feature/tests-cutoff/cutoff-example-config.json} +7 -3
  37. package/examples/{date-exclusions-config.json → feature/tests-date-exclusions/date-exclusions-config.json} +2 -2
  38. package/examples/{example-bar-chart-nonnumeric.json → feature/tests-non-numerics/example-bar-chart-nonnumeric.json} +1 -1
  39. package/examples/{planet-pie-example-config-nonnumeric.json → feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json} +2 -2
  40. package/examples/{sparkline-chart-nonnumeric.json → feature/tests-non-numerics/sparkline-chart-nonnumeric.json} +1 -1
  41. package/examples/{stacked-vertical-bar-example-nonnumerics.json → feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json} +1 -2
  42. package/examples/gallery/bar-chart-horizontal/horizontal-highlight.json +345 -0
  43. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +145 -7
  44. package/examples/gallery/paired-bar/paired-bar-chart.json +1 -0
  45. package/index.html +73 -49
  46. package/package.json +2 -2
  47. package/src/CdcChart.jsx +405 -40
  48. package/src/components/AreaChart.jsx +122 -80
  49. package/src/components/BarChart.jsx +126 -49
  50. package/src/components/BoxPlot.jsx +28 -20
  51. package/src/components/DataTable.jsx +7 -6
  52. package/src/components/DeviationBar.jsx +34 -34
  53. package/src/components/EditorPanel.jsx +1332 -352
  54. package/src/components/Legend.jsx +40 -4
  55. package/src/components/LineChart.jsx +10 -23
  56. package/src/components/LinearChart.jsx +133 -286
  57. package/src/components/PairedBarChart.jsx +6 -6
  58. package/src/components/PieChart.jsx +2 -4
  59. package/src/components/SparkLine.jsx +6 -42
  60. package/src/data/initial-state.js +23 -4
  61. package/src/hooks/useHighlightedBars.js +154 -0
  62. package/src/hooks/useMinMax.js +92 -0
  63. package/src/hooks/useReduceData.js +31 -57
  64. package/src/hooks/useScales.js +202 -0
  65. package/src/index.jsx +2 -1
  66. package/src/scss/editor-panel.scss +15 -0
  67. package/src/scss/main.scss +8 -6
  68. package/examples/box-plot.csv +0 -5
  69. package/examples/dynamic-legends.json +0 -125
  70. package/examples/example-bar-chart.json +0 -36
  71. package/examples/line-chart.json +0 -34
  72. package/examples/temp-example-config.json +0 -64
  73. package/examples/temp-example-data.json +0 -130
  74. package/src/components/Filters.jsx +0 -126
  75. /package/examples/{age-adjusted-rates.json → feature/__data__/age-adjusted-rates.json} +0 -0
  76. /package/examples/{new-data.csv → feature/__data__/new-data.csv} +0 -0
  77. /package/examples/{planet-example-data-max-increase.json → feature/__data__/planet-example-data-max-increase.json} +0 -0
  78. /package/examples/{Barchart_with_negative.json → feature/bar/Barchart_with_negative.json} +0 -0
  79. /package/examples/{stacked-vertical-bar-example-negative.json → feature/bar/stacked-vertical-bar-example-negative.json} +0 -0
  80. /package/examples/{stacked-vertical-bar-example.json → feature/bar/stacked-vertical-bar-example.json} +0 -0
  81. /package/examples/{box-plot-data.json → feature/boxplot/box-plot-data.json} +0 -0
  82. /package/examples/{newdata.json → feature/boxplot/boxplot-data.json} +0 -0
  83. /package/examples/{line-chart-max-increase.json → feature/line/line-chart-max-increase.json} +0 -0
  84. /package/examples/{paired-bar-data.json → feature/paired-bar/paired-bar-data.json} +0 -0
  85. /package/examples/{paired-bar-formatted.json → feature/paired-bar/paired-bar-formatted.json} +0 -0
  86. /package/examples/{scatterplot-continuous.csv → feature/scatterplot/scatterplot-continuous.csv} +0 -0
  87. /package/examples/{example-sparkline.json → feature/sparkline/example-sparkline.json} +0 -0
  88. /package/examples/{big-small-test-bar.json → feature/tests-big-small/big-small-test-bar.json} +0 -0
  89. /package/examples/{big-small-test-line.json → feature/tests-big-small/big-small-test-line.json} +0 -0
  90. /package/examples/{big-small-test-negative.json → feature/tests-big-small/big-small-test-negative.json} +0 -0
  91. /package/examples/{case-rate-example-data.json → feature/tests-case-rate/case-rate-example-data.json} +0 -0
  92. /package/examples/{covid-example-data-confidence.json → feature/tests-covid/covid-example-data-confidence.json} +0 -0
  93. /package/examples/{covid-example-data.json → feature/tests-covid/covid-example-data.json} +0 -0
  94. /package/examples/{cutoff-example-data.json → feature/tests-cutoff/cutoff-example-data.json} +0 -0
  95. /package/examples/{date-exclusions-data.json → feature/tests-date-exclusions/date-exclusions-data.json} +0 -0
  96. /package/examples/{example-combo-bar-nonnumeric.json → feature/tests-non-numerics/example-combo-bar-nonnumeric.json} +0 -0
  97. /package/examples/{line-chart-nonnumeric.json → feature/tests-non-numerics/line-chart-nonnumeric.json} +0 -0
  98. /package/examples/{planet-example-data-nonnumeric.json → feature/tests-non-numerics/planet-example-data-nonnumeric.json} +0 -0
@@ -17,7 +17,8 @@ import Tooltip from '@cdc/core/components/ui/Tooltip'
17
17
  import Icon from '@cdc/core/components/ui/Icon'
18
18
  import useReduceData from '../hooks/useReduceData'
19
19
  import useRightAxis from '../hooks/useRightAxis'
20
- import * as allCurves from '@visx/curve'
20
+ import { useFilters } from '@cdc/core/components/Filters'
21
+ import { useHighlightedBars } from '../hooks/useHighlightedBars'
21
22
 
22
23
  /* eslint-disable react-hooks/rules-of-hooks */
23
24
  const TextField = memo(({ label, tooltip, section = null, subsection = null, fieldName, updateField, value: stateValue, type = 'input', i = null, min = null, ...attributes }) => {
@@ -71,7 +72,7 @@ const TextField = memo(({ label, tooltip, section = null, subsection = null, fie
71
72
  })
72
73
 
73
74
  const CheckBox = memo(({ label, value, fieldName, section = null, subsection = null, tooltip, updateField, ...attributes }) => (
74
- <label className='checkbox'>
75
+ <label className='checkbox column-heading'>
75
76
  <input
76
77
  type='checkbox'
77
78
  name={fieldName}
@@ -206,12 +207,23 @@ const Regions = memo(({ config, updateConfig }) => {
206
207
  const headerColors = ['theme-blue', 'theme-purple', 'theme-brown', 'theme-teal', 'theme-pink', 'theme-orange', 'theme-slate', 'theme-indigo', 'theme-cyan', 'theme-green', 'theme-amber']
207
208
 
208
209
  const EditorPanel = () => {
209
- const { config, updateConfig, transformedData: data, loading, colorPalettes, twoColorPalette, unfilteredData, excludedData, isDashboard, setParentConfig, missingRequiredSections } = useContext(ConfigContext)
210
+ const { config, updateConfig, transformedData: data, loading, colorPalettes, twoColorPalette, unfilteredData, excludedData, isDashboard, setParentConfig, missingRequiredSections, isDebug, setFilteredData, lineOptions } = useContext(ConfigContext)
210
211
 
211
212
  const { minValue, maxValue, existPositiveValue, isAllLine } = useReduceData(config, unfilteredData)
212
213
 
213
214
  const { twoColorPalettes, sequential, nonSequential } = useColorPalette(config, updateConfig)
214
215
 
216
+ // argument acts as props
217
+ const { handleFilterOrder, filterOrderOptions, filterStyleOptions } = useFilters({ config, setConfig: updateConfig, filteredData: data, setFilteredData })
218
+
219
+ const approvedCurveTypes = {
220
+ Linear: 'curveLinear',
221
+ Cardinal: 'curveCardinal',
222
+ Natural: 'curveNatural',
223
+ 'Monotone X': 'curveMonotoneX',
224
+ Step: 'curveStep'
225
+ }
226
+
215
227
  // when the visualization type changes we
216
228
  // have to update the individual series type & axis details
217
229
  // dataKey is unchanged here.
@@ -249,21 +261,6 @@ const EditorPanel = () => {
249
261
 
250
262
  const { hasRightAxis } = useRightAxis({ config: config, yMax: config.yAxis.size, data: config.data, updateConfig })
251
263
 
252
- const filterOptions = [
253
- {
254
- label: 'Ascending Alphanumeric',
255
- value: 'asc'
256
- },
257
- {
258
- label: 'Descending Alphanumeric',
259
- value: 'desc'
260
- },
261
- {
262
- label: 'Custom',
263
- value: 'cust'
264
- }
265
- ]
266
-
267
264
  const getItemStyle = (isDragging, draggableStyle) => ({
268
265
  ...draggableStyle
269
266
  })
@@ -298,6 +295,8 @@ const EditorPanel = () => {
298
295
  }
299
296
 
300
297
  const updateField = (section, subsection, fieldName, newValue) => {
298
+ if (isDebug) console.log('#COVE: CHART: EditorPanel: section, subsection, fieldName, newValue', section, subsection, fieldName, newValue) // eslint-disable-line
299
+
301
300
  if (section === 'boxplot' && subsection === 'legend') {
302
301
  updateConfig({
303
302
  ...config,
@@ -325,6 +324,20 @@ const EditorPanel = () => {
325
324
  })
326
325
  return
327
326
  }
327
+
328
+ if (section === 'columns' && subsection !== '' && fieldName !== '') {
329
+ updateConfig({
330
+ ...config,
331
+ [section]: {
332
+ ...config[section],
333
+ [subsection]: {
334
+ ...config[section][subsection],
335
+ [fieldName]: newValue
336
+ }
337
+ }
338
+ })
339
+ return
340
+ }
328
341
  if (null === section && null === subsection) {
329
342
  let updatedConfig = { ...config, [fieldName]: newValue }
330
343
  enforceRestrictions(updatedConfig)
@@ -481,13 +494,22 @@ const EditorPanel = () => {
481
494
  }
482
495
  }
483
496
 
484
- const getColumns = (filter = true) => {
497
+ const getFilters = () => {
485
498
  let columns = {}
486
499
 
487
500
  unfilteredData.forEach(row => {
488
501
  Object.keys(row).forEach(columnName => (columns[columnName] = true))
489
502
  })
490
503
 
504
+ return Object.keys(columns)
505
+ }
506
+
507
+ const getColumns = (filter = true) => {
508
+ let columns = {}
509
+ unfilteredData.forEach(row => {
510
+ Object.keys(row).forEach(columnName => (columns[columnName] = true))
511
+ })
512
+
491
513
  if (filter) {
492
514
  Object.keys(columns).forEach(key => {
493
515
  if (
@@ -591,6 +613,24 @@ const EditorPanel = () => {
591
613
  // eslint-disable-next-line react-hooks/exhaustive-deps
592
614
  }, [config])
593
615
 
616
+ // when the orientation changes, swap x and y axis anchors
617
+ useEffect(() => {
618
+ const prevXAnchors = config.xAxis.anchors.length > 0 ? config.xAxis.anchors : []
619
+ const prevYAnchors = config.yAxis.anchors.length > 0 ? config.yAxis.anchors : []
620
+
621
+ updateConfig({
622
+ ...config,
623
+ xAxis: {
624
+ ...config.xAxis,
625
+ anchors: prevYAnchors
626
+ },
627
+ yAxis: {
628
+ ...config.yAxis,
629
+ anchors: prevXAnchors
630
+ }
631
+ })
632
+ }, [config.orientation])
633
+
594
634
  // Set paired bars to be horizontal, even though that option doesn't display
595
635
  useEffect(() => {
596
636
  if (config.visualizationType === 'Paired Bar') {
@@ -637,23 +677,6 @@ const EditorPanel = () => {
637
677
  )
638
678
  }, [config]) // eslint-disable-line
639
679
 
640
- const checkIsLine = type => {
641
- return type === ('Line' || 'dashed-sm')
642
- }
643
-
644
- const handleFilterChange = (idx1, idx2, filterIndex, filter) => {
645
- let filterOrder = filter.values
646
- let [movedItem] = filterOrder.splice(idx1, 1)
647
- filterOrder.splice(idx2, 0, movedItem)
648
- let filters = [...config.filters]
649
- let filterItem = { ...config.filters[filterIndex] }
650
- filterItem.active = filter.values[0]
651
- filterItem.orderedValues = filterOrder
652
- filterItem.order = 'cust'
653
- filters[filterIndex] = filterItem
654
- updateConfig({ ...config, filters })
655
- }
656
-
657
680
  const visHasLegend = () => {
658
681
  const { visualizationType } = config
659
682
 
@@ -668,6 +691,8 @@ const EditorPanel = () => {
668
691
  const visCanAnimate = () => {
669
692
  const { visualizationType } = config
670
693
  switch (visualizationType) {
694
+ case 'Area Chart':
695
+ return false
671
696
  case 'Scatter Plot':
672
697
  return false
673
698
  case 'Box Plot':
@@ -692,6 +717,8 @@ const EditorPanel = () => {
692
717
  const visHasLabelOnData = () => {
693
718
  const { visualizationType } = config
694
719
  switch (visualizationType) {
720
+ case 'Area Chart':
721
+ return false
695
722
  case 'Box Plot':
696
723
  return false
697
724
  case 'Pie':
@@ -703,6 +730,24 @@ const EditorPanel = () => {
703
730
  }
704
731
  }
705
732
 
733
+ const visHasAnchors = () => {
734
+ const { visualizationType } = config
735
+ switch (visualizationType) {
736
+ case 'Area Chart':
737
+ return true
738
+ case 'Combo':
739
+ return true
740
+ case 'Line':
741
+ return true
742
+ case 'Bar':
743
+ return true
744
+ case 'Scatter Plot':
745
+ return true
746
+ default:
747
+ return false
748
+ }
749
+ }
750
+
706
751
  const visHasBarBorders = () => {
707
752
  const { series, visualizationType } = config
708
753
  if (visualizationType === 'Box Plot') return false
@@ -743,9 +788,7 @@ const EditorPanel = () => {
743
788
  default:
744
789
  message = ''
745
790
  }
746
- setWarningMsg(function (prevMsg) {
747
- return { ...prevMsg, maxMsg: message }
748
- })
791
+ setWarningMsg(prevMsg => ({ ...prevMsg, maxMsg: message }))
749
792
  }
750
793
 
751
794
  const validateMinValue = () => {
@@ -754,43 +797,226 @@ const EditorPanel = () => {
754
797
  let message = ''
755
798
 
756
799
  switch (true) {
800
+ case config.useLogScale && ['Line', 'Combo', 'Bar'].includes(config.visualizationType) && enteredValue < 0:
801
+ message = 'Negative numbers are not supported in logarithmic scale'
802
+ break
757
803
  case (config.visualizationType === 'Line' || config.visualizationType === 'Spark Line') && enteredValue && parseFloat(enteredValue) > minVal:
758
- message = 'Value must be less than ' + minValue
804
+ message = 'Value should not exceed ' + minValue
759
805
  break
760
806
  case config.visualizationType === 'Combo' && isAllLine && enteredValue && parseFloat(enteredValue) > minVal:
761
- message = 'Value must be less than ' + minValue
807
+ message = 'Value should not exceed ' + minValue
808
+ break
809
+ case (config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && enteredValue && minVal > 0 && parseFloat(enteredValue) > 0:
810
+ message = config.useLogScale ? 'Value must be equal to 0' : 'Value must be less than or equal to 0'
762
811
  break
763
- case (config.visualizationType === 'Bar' || config.visualizationType === 'Deviation Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && enteredValue && minVal > 0 && parseFloat(enteredValue) > 0:
764
- message = 'Value must be less than or equal to 0'
812
+ case config.visualizationType === 'Deviation Bar' && parseFloat(enteredValue) >= Math.min(minVal, config.xAxis.target):
813
+ message = 'Value must be less than ' + Math.min(minVal, config.xAxis.target)
765
814
  break
766
- case enteredValue && minVal < 0 && parseFloat(enteredValue) > minVal:
767
- message = 'Value must be less than ' + minValue
815
+ case config.visualizationType !== 'Deviation Bar' && enteredValue && minVal < 0 && parseFloat(enteredValue) > minVal:
816
+ message = 'Value should not exceed ' + minValue
768
817
  break
769
818
  default:
770
819
  message = ''
771
820
  }
772
- setWarningMsg(function (prevMsg) {
773
- return { ...prevMsg, minMsg: message }
774
- })
821
+ setWarningMsg(prevMsg => ({ ...prevMsg, minMsg: message }))
775
822
  }
776
823
  useEffect(() => {
777
824
  validateMinValue()
778
825
  validateMaxValue()
779
826
  }, [minValue, maxValue, config]) // eslint-disable-line
780
827
 
828
+ // prettier-ignore
781
829
  const enabledChartTypes = [
782
- 'Pie',
783
- 'Line',
830
+ 'Area Chart',
784
831
  'Bar',
832
+ 'Box Plot',
785
833
  'Combo',
834
+ 'Deviation Bar',
835
+ 'Line',
786
836
  'Paired Bar',
787
- 'Spark Line',
788
- // 'Area Chart',
837
+ 'Pie',
789
838
  'Scatter Plot',
790
- 'Box Plot',
791
- 'Deviation Bar'
839
+ 'Spark Line'
792
840
  ]
793
841
 
842
+ const isLoadedFromUrl = config?.dataKey?.includes('http://') || config?.dataKey?.includes('https://')
843
+
844
+ // if isDebug = true, then try to set the category and data col to reduce clicking
845
+ const setCategoryAxis = () => {
846
+ // only for debug mode
847
+ if (undefined !== isDebug && isDebug && !config?.xAxis?.dataKey) {
848
+ // then try to set the x axis to appropriate value so we dont have to manually do it
849
+ let datakeys = getColumns(false)
850
+ if (datakeys.includes('Date')) return 'Date'
851
+ if (datakeys.includes('Race')) return 'Race'
852
+ // add other known Category cols here to extend debug
853
+ }
854
+ return ''
855
+ }
856
+ const setDataColumn = () => {
857
+ // only for debug mode
858
+ if (undefined !== isDebug && isDebug && getColumns(false).length > 0) {
859
+ // then try to set the x axis to appropriate value so we dont have to manually do it
860
+ let datacols = getColumns(false).filter(x => x !== 'Date' && x !== 'Race')
861
+ if (datacols.length > 0) {
862
+ return datacols[0]
863
+ }
864
+ }
865
+ return ''
866
+ }
867
+ if (isDebug && !config.xAxis.dataKey) config.xAxis.dataKey = setCategoryAxis()
868
+ if (isDebug && config?.series?.length === 0) {
869
+ let setdatacol = setDataColumn()
870
+ if (setdatacol !== '') addNewSeries(setdatacol)
871
+ if (isDebug) console.log('### COVE DEBUG: Chart: Setting default datacol=', setdatacol) // eslint-disable-line
872
+ }
873
+
874
+ const chartsWithOptions = ['Area Chart', 'Combo', 'Line']
875
+
876
+ const columnsOptions = [
877
+ <option value='' key={'Select Option'}>
878
+ - Select Option -
879
+ </option>
880
+ ]
881
+
882
+ if (config.data && config.series) {
883
+ Object.keys(config.data[0]).map(colName => {
884
+ // OMIT ANY COLUMNS THAT ARE IN DATA SERIES!
885
+ const found = config?.series.some(el => el.dataKey === colName)
886
+ if (colName !== config.xAxis.dataKey && !found) {
887
+ // if not the index then add it
888
+ return columnsOptions.push(
889
+ <option value={colName} key={colName}>
890
+ {colName}
891
+ </option>
892
+ )
893
+ }
894
+ })
895
+
896
+ let columnsByKey = {}
897
+ config.data.forEach(datum => {
898
+ Object.keys(datum).forEach(key => {
899
+ columnsByKey[key] = columnsByKey[key] || []
900
+ const value = typeof datum[key] === 'number' ? datum[key].toString() : datum[key]
901
+
902
+ if (columnsByKey[key].indexOf(value) === -1) {
903
+ columnsByKey[key].push(value)
904
+ }
905
+ })
906
+ })
907
+ }
908
+
909
+ // for pie charts
910
+ if (!config.data && data) {
911
+ if (!data[0]) return
912
+ Object.keys(data[0]).map(colName => {
913
+ // OMIT ANY COLUMNS THAT ARE IN DATA SERIES!
914
+ const found = data.some(el => el.dataKey === colName)
915
+ if (colName !== config.xAxis.dataKey && !found) {
916
+ // if not the index then add it
917
+ return columnsOptions.push(
918
+ <option value={colName} key={colName}>
919
+ {colName}
920
+ </option>
921
+ )
922
+ }
923
+ })
924
+
925
+ let columnsByKey = {}
926
+ data.forEach(datum => {
927
+ Object.keys(datum).forEach(key => {
928
+ columnsByKey[key] = columnsByKey[key] || []
929
+ const value = typeof datum[key] === 'number' ? datum[key].toString() : datum[key]
930
+
931
+ if (columnsByKey[key].indexOf(value) === -1) {
932
+ columnsByKey[key].push(value)
933
+ }
934
+ })
935
+ })
936
+ }
937
+
938
+ // prevents adding duplicates
939
+ const additionalColumns = Object.keys(config.columns).filter(value => {
940
+ const defaultCols = [config.xAxis.dataKey] // ['geo', 'navigate', 'primary', 'latitude', 'longitude']
941
+
942
+ if (true === defaultCols.includes(value)) {
943
+ return false
944
+ }
945
+ return true
946
+ })
947
+
948
+ // just adds a new column but not set to any data yet
949
+ const addAdditionalColumn = number => {
950
+ const columnKey = `additionalColumn${number}`
951
+
952
+ updateConfig({
953
+ ...config,
954
+ columns: {
955
+ ...config.columns,
956
+ [columnKey]: {
957
+ label: 'New Column',
958
+ dataTable: false,
959
+ tooltips: false,
960
+ prefix: '',
961
+ suffix: ''
962
+ }
963
+ }
964
+ })
965
+ }
966
+
967
+ const removeAdditionalColumn = columnName => {
968
+ const newColumns = config.columns
969
+
970
+ delete newColumns[columnName]
971
+
972
+ updateConfig({
973
+ ...config,
974
+ columns: newColumns
975
+ })
976
+ }
977
+
978
+ const editColumn = async (addCol, columnName, setval) => {
979
+ // not using special classes like in map editorpanel so removed those cases
980
+ switch (columnName) {
981
+ case 'name':
982
+ updateConfig({
983
+ ...config,
984
+ columns: {
985
+ ...config.columns,
986
+ [addCol]: {
987
+ ...config.columns[addCol],
988
+ [columnName]: setval
989
+ }
990
+ }
991
+ })
992
+ break
993
+ default:
994
+ updateConfig({
995
+ ...config,
996
+ columns: {
997
+ ...config.columns,
998
+ [addCol]: {
999
+ ...config.columns[addCol],
1000
+ [columnName]: setval
1001
+ }
1002
+ }
1003
+ })
1004
+ break
1005
+ }
1006
+ }
1007
+
1008
+ // prettier-ignore
1009
+ const {
1010
+ highlightedBarValues,
1011
+ highlightedSeriesValues,
1012
+ handleUpdateHighlightedBar,
1013
+ handleAddNewHighlightedBar,
1014
+ handleRemoveHighlightedBar,
1015
+ handleUpdateHighlightedBarColor,
1016
+ handleHighlightedBarLegendLabel,
1017
+ handleUpdateHighlightedBorderWidth
1018
+ } = useHighlightedBars(config, updateConfig)
1019
+
794
1020
  return (
795
1021
  <ErrorBoundary component='EditorPanel'>
796
1022
  {config.newViz && <Confirm />}
@@ -923,7 +1149,6 @@ const EditorPanel = () => {
923
1149
  {config.orientation === 'vertical' && <TextField type='number' value={config.heights.vertical} section='heights' fieldName='vertical' label='Chart Height' updateField={updateField} />}
924
1150
  </AccordionItemPanel>
925
1151
  </AccordionItem>
926
-
927
1152
  {config.visualizationType !== 'Pie' && (
928
1153
  <AccordionItem>
929
1154
  <AccordionItemHeading>
@@ -932,6 +1157,18 @@ const EditorPanel = () => {
932
1157
  <AccordionItemPanel>
933
1158
  {(!config.series || config.series.length === 0) && config.visualizationType !== 'Paired Bar' && <p className='warning'>At least one series is required</p>}
934
1159
  {(!config.series || config.series.length === 0 || config.series.length < 2) && config.visualizationType === 'Paired Bar' && <p className='warning'>Select two data series for paired bar chart (e.g., Male and Female).</p>}
1160
+ <Select
1161
+ fieldName='visualizationType'
1162
+ label='Add Data Series'
1163
+ initial='Select'
1164
+ onChange={e => {
1165
+ if (e.target.value !== '' && e.target.value !== 'Select') {
1166
+ addNewSeries(e.target.value)
1167
+ }
1168
+ e.target.value = ''
1169
+ }}
1170
+ options={getColumns()}
1171
+ />
935
1172
  {config.series && config.series.length !== 0 && (
936
1173
  <>
937
1174
  <fieldset>
@@ -949,7 +1186,7 @@ const EditorPanel = () => {
949
1186
  <DragDropContext onDragEnd={({ source, destination }) => handleSeriesChange(source.index, destination.index)}>
950
1187
  <Droppable droppableId='filter_order'>
951
1188
  {provided => (
952
- <ul {...provided.droppableProps} className='series-list' ref={provided.innerRef} style={{ marginTop: '1em' }}>
1189
+ <ul {...provided.droppableProps} className='series-list' ref={provided.innerRef}>
953
1190
  {config.series.map((series, i) => {
954
1191
  if (config.visualizationType === 'Combo' || 'Area Chart') {
955
1192
  let changeType = (i, value) => {
@@ -968,78 +1205,129 @@ const EditorPanel = () => {
968
1205
  }
969
1206
 
970
1207
  let typeDropdown = (
971
- <select
972
- value={series.type}
973
- onChange={event => {
974
- changeType(i, event.target.value)
975
- }}
976
- style={{ width: '100px', marginRight: '10px' }}
977
- >
978
- <option value='' default key='default'>
979
- Select
980
- </option>
981
- {config.visualizationType === 'Combo' && <option value='Bar'>Bar</option>}
982
- <option value='Line' key='Line'>
983
- Solid Line
984
- </option>
985
- <option value='dashed-sm' key='dashed-sm'>
986
- Small Dashed
987
- </option>
988
- <option value='dashed-md' key='dashed-md'>
989
- Medium Dashed
990
- </option>
991
- <option value='dashed-lg' key='dashed-lg'>
992
- Large Dashed
993
- </option>
994
- </select>
1208
+ <>
1209
+ <label htmlFor='type-dropdown'>Series Type</label>
1210
+ <select
1211
+ name='type-dropdown'
1212
+ value={series.type}
1213
+ onChange={event => {
1214
+ changeType(i, event.target.value)
1215
+ }}
1216
+ >
1217
+ <option value='' default key='default'>
1218
+ Select
1219
+ </option>
1220
+ {config.visualizationType === 'Combo' && <option value='Bar'>Bar</option>}
1221
+ <option value='Line' key='Line'>
1222
+ Solid Line
1223
+ </option>
1224
+ <option value='dashed-sm' key='dashed-sm'>
1225
+ Small Dashed
1226
+ </option>
1227
+ <option value='dashed-md' key='dashed-md'>
1228
+ Medium Dashed
1229
+ </option>
1230
+ <option value='dashed-lg' key='dashed-lg'>
1231
+ Large Dashed
1232
+ </option>
1233
+ <option value='Area Chart' key='Area Chart'>
1234
+ Area
1235
+ </option>
1236
+ </select>
1237
+ </>
1238
+ )
1239
+
1240
+ // used for assigning axis
1241
+ let changeAxis = (i, value) => {
1242
+ let series = [...config.series]
1243
+ series[i].axis = value
1244
+ updateConfig({ ...config, series })
1245
+ }
1246
+
1247
+ // assign an axis dropdown
1248
+ let axisDropdown = (
1249
+ <>
1250
+ <label htmlFor='assign-axis'>Assign an axis</label>
1251
+ <select
1252
+ name='assign-axis'
1253
+ value={series.axis}
1254
+ onChange={event => {
1255
+ changeAxis(i, event.target.value)
1256
+ }}
1257
+ >
1258
+ <option value='Left' default key='left'>
1259
+ left
1260
+ </option>
1261
+ <option value='Right' key='right'>
1262
+ right
1263
+ </option>
1264
+ </select>
1265
+ </>
995
1266
  )
996
1267
 
1268
+ // line type dropdown
997
1269
  const lineType = (
998
- <select
999
- value={series.lineStyle}
1000
- onChange={event => {
1001
- changeLineType(i, event.target.value)
1002
- }}
1003
- style={{ width: '100px', marginRight: '10px' }}
1004
- key='lineTypeSelection'
1005
- >
1006
- <option value='' default>
1007
- Select
1008
- </option>
1009
-
1010
- {Object.keys(allCurves).map(curveName => (
1011
- <option key={`curve-option-${curveName}`} value={curveName}>
1012
- {curveName}
1270
+ <>
1271
+ <label htmlFor='line-type'>Line Type</label>
1272
+ <select
1273
+ name='line-type'
1274
+ value={series.lineStyle}
1275
+ onChange={event => {
1276
+ changeLineType(i, event.target.value)
1277
+ }}
1278
+ key='lineTypeSelection'
1279
+ >
1280
+ <option value='' default>
1281
+ Select
1013
1282
  </option>
1014
- ))}
1015
- </select>
1283
+
1284
+ {Object.keys(approvedCurveTypes).map(curveName => {
1285
+ return (
1286
+ <option key={`curve-option-${approvedCurveTypes[curveName]}`} value={approvedCurveTypes[curveName]}>
1287
+ {curveName}
1288
+ </option>
1289
+ )
1290
+ })}
1291
+ </select>
1292
+ </>
1016
1293
  )
1017
1294
 
1018
1295
  return (
1019
1296
  <Draggable key={series.dataKey} draggableId={`draggableFilter-${series.dataKey}`} index={i}>
1020
1297
  {(provided, snapshot) => (
1021
- <li key={i}>
1022
- <div className={snapshot.isDragging ? 'currently-dragging' : ''} style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
1023
- <div className={`series-list__name${series.dataKey.length > 15 ? ' series-list__name--truncate' : ''}`} data-title={series.dataKey}>
1024
- <div className='series-list__name-text'>{series.dataKey}</div>
1025
- </div>
1026
- <span>
1027
- <>
1028
- {(config.visualizationType === 'Combo' || config.visualizationType === 'Area Chart') && (
1029
- <>
1030
- <span className='series-list__dropdown'>{typeDropdown}</span>
1031
- {config.visualizationType === 'Area Chart' && <span className='series-list__dropdown series-list__dropdown--lineType'>{lineType}</span>}
1032
- </>
1033
- )}
1034
- {config.series && config.series.length > 1 && (
1035
- <button className='series-list__remove' onClick={() => removeSeries(series.dataKey)}>
1036
- &#215;
1037
- </button>
1298
+ <>
1299
+ <div key={i} className={snapshot.isDragging ? 'currently-dragging' : ''} style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
1300
+ <div className={`series-list__name ${series.dataKey.length > 15 ? ' series-list__name--truncate' : ''}`} data-title={series.dataKey}></div>
1301
+ <Accordion allowZeroExpanded>
1302
+ <AccordionItem className='series-item series-item--chart'>
1303
+ <AccordionItemHeading className='series-item__title'>
1304
+ <AccordionItemButton className={chartsWithOptions.includes(config.visualizationType) ? 'accordion__button' : 'accordion__button hide-arrow'}>
1305
+ <Icon display='move' size={15} style={{ cursor: 'default' }} />
1306
+ {series.dataKey}
1307
+ {config.series && config.series.length > 1 && (
1308
+ <button className='series-list__remove' onClick={() => removeSeries(series.dataKey)}>
1309
+ Remove
1310
+ </button>
1311
+ )}
1312
+ </AccordionItemButton>
1313
+ </AccordionItemHeading>
1314
+ {chartsWithOptions.includes(config.visualizationType) && (
1315
+ <AccordionItemPanel>
1316
+ <div className={snapshot.isDragging ? 'currently-dragging' : ''} style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
1317
+ {config.visualizationType === 'Combo' && (
1318
+ <>
1319
+ <span className='series-list__dropdown series-item__dropdown'>{typeDropdown}</span>
1320
+ {hasRightAxis && config.series && (series.type === 'Line' || series.type === 'dashed-sm' || series.type === 'dashed-md' || series.type === 'dashed-lg') && <span className='series-item__dropdown series-list__dropdown'>{axisDropdown}</span>}
1321
+ </>
1322
+ )}
1323
+ {['Line', 'dashed-sm', 'dashed-md', 'dashed-lg', 'Area Chart'].some(item => item.includes(series.type)) && <span className='series-item__dropdown series-list__dropdown series-list__dropdown--lineType'>{lineType}</span>}
1324
+ </div>
1325
+ </AccordionItemPanel>
1038
1326
  )}
1039
- </>
1040
- </span>
1327
+ </AccordionItem>
1328
+ </Accordion>
1041
1329
  </div>
1042
- </li>
1330
+ </>
1043
1331
  )}
1044
1332
  </Draggable>
1045
1333
  )
@@ -1079,19 +1367,6 @@ const EditorPanel = () => {
1079
1367
  </>
1080
1368
  )}
1081
1369
 
1082
- <Select
1083
- fieldName='visualizationType'
1084
- label='Add Data Series'
1085
- initial='Select'
1086
- onChange={e => {
1087
- if (e.target.value !== '' && e.target.value !== 'Select') {
1088
- addNewSeries(e.target.value)
1089
- }
1090
- e.target.value = ''
1091
- }}
1092
- options={getColumns()}
1093
- />
1094
-
1095
1370
  {config.series && config.series.length <= 1 && config.visualizationType === 'Bar' && (
1096
1371
  <>
1097
1372
  <span className='divider-heading'>Confidence Keys</span>
@@ -1104,7 +1379,6 @@ const EditorPanel = () => {
1104
1379
  </AccordionItemPanel>
1105
1380
  </AccordionItem>
1106
1381
  )}
1107
-
1108
1382
  {config.visualizationType === 'Box Plot' && (
1109
1383
  <AccordionItem>
1110
1384
  <AccordionItemHeading>
@@ -1281,73 +1555,6 @@ const EditorPanel = () => {
1281
1555
  </AccordionItemPanel>
1282
1556
  </AccordionItem>
1283
1557
  )}
1284
-
1285
- {hasRightAxis && config.series && config.visualizationType === 'Combo' && (
1286
- <AccordionItem>
1287
- <AccordionItemHeading>
1288
- <AccordionItemButton>Assign Data Series Axis</AccordionItemButton>
1289
- </AccordionItemHeading>
1290
- <AccordionItemPanel>
1291
- <p>Only line series data can be assigned to the right axis. Check the data series section above.</p>
1292
- {config.series && config.series.filter(series => checkIsLine(series.type)) && (
1293
- <>
1294
- <fieldset>
1295
- <legend className='edit-label float-left'>Displaying</legend>
1296
- <Tooltip style={{ textTransform: 'none' }}>
1297
- <Tooltip.Target>
1298
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1299
- </Tooltip.Target>
1300
- <Tooltip.Content>
1301
- <p>Assign an axis for the series</p>
1302
- </Tooltip.Content>
1303
- </Tooltip>
1304
- </fieldset>
1305
- <ul className='series-list'>
1306
- {config.series &&
1307
- config.series.map((series, i) => {
1308
- if (series.type === 'Bar') return false // can't set individual bars atm.
1309
-
1310
- let changeAxis = (i, value) => {
1311
- let series = [...config.series]
1312
- series[i].axis = value
1313
- updateConfig({ ...config, series })
1314
- }
1315
-
1316
- let axisDropdown = (
1317
- <select
1318
- value={series.axis}
1319
- onChange={event => {
1320
- changeAxis(i, event.target.value)
1321
- }}
1322
- style={{ width: '100px', marginRight: '10px' }}
1323
- >
1324
- <option value='Left' default key='left'>
1325
- left
1326
- </option>
1327
- <option value='Right' key='right'>
1328
- right
1329
- </option>
1330
- </select>
1331
- )
1332
-
1333
- return (
1334
- <li key={series.dataKey}>
1335
- <div className={`series-list__name${series.dataKey.length > 15 ? ' series-list__name--truncate' : ''}`} data-title={series.dataKey}>
1336
- <div className='series-list__name-text'>{series.dataKey}</div>
1337
- </div>
1338
- <span>
1339
- <span className='series-list__dropdown'>{axisDropdown}</span>
1340
- </span>
1341
- </li>
1342
- )
1343
- })}
1344
- </ul>
1345
- </>
1346
- )}
1347
- </AccordionItemPanel>
1348
- </AccordionItem>
1349
- )}
1350
-
1351
1558
  <AccordionItem>
1352
1559
  <AccordionItemHeading>
1353
1560
  <AccordionItemButton>
@@ -1408,6 +1615,7 @@ const EditorPanel = () => {
1408
1615
  {config.orientation === 'horizontal' && <TextField value={config.xAxis.labelOffset} section='xAxis' fieldName='labelOffset' label='Label offset' type='number' className='number-narrow' updateField={updateField} />}
1409
1616
  {config.orientation !== 'horizontal' && <CheckBox value={config.yAxis.gridLines} section='yAxis' fieldName='gridLines' label='Display Gridlines' updateField={updateField} />}
1410
1617
  <CheckBox value={config.yAxis.enablePadding} section='yAxis' fieldName='enablePadding' label='Add Padding to Value Axis Scale' updateField={updateField} />
1618
+ {config.visualizationSubType === 'regular' && <CheckBox value={config.useLogScale} fieldName='useLogScale' label='use logarithmic scale' updateField={updateField} />}
1411
1619
  </>
1412
1620
  )}
1413
1621
  <span className='divider-heading'>Number Formatting</span>
@@ -1476,10 +1684,10 @@ const EditorPanel = () => {
1476
1684
  <CheckBox value={config.xAxis.hideTicks} section='xAxis' fieldName='hideTicks' label='Hide Ticks' updateField={updateField} />
1477
1685
  <TextField value={config.xAxis.max} section='xAxis' fieldName='max' label='max value' type='number' placeholder='Auto' updateField={updateField} />
1478
1686
  <span style={{ color: 'red', display: 'block' }}>{warningMsg.maxMsg}</span>
1687
+ <TextField value={config.xAxis.min} section='xAxis' fieldName='min' type='number' label='min value' placeholder='Auto' updateField={updateField} />
1688
+ <span style={{ color: 'red', display: 'block' }}>{warningMsg.minMsg}</span>
1479
1689
  {config.visualizationType === 'Deviation Bar' && (
1480
1690
  <>
1481
- <TextField value={config.xAxis.min} section='xAxis' fieldName='min' type='number' label='min value' placeholder='Auto' updateField={updateField} />
1482
- <span style={{ color: 'red', display: 'block' }}>{warningMsg.minMsg}</span>
1483
1691
  <TextField value={config.xAxis.target} section='xAxis' fieldName='target' type='number' label='Deviation point' placeholder='Auto' updateField={updateField} />
1484
1692
  <TextField value={config.xAxis.targetLabel || 'Target'} section='xAxis' fieldName='targetLabel' type='text' label='Deviation point Label' updateField={updateField} />
1485
1693
  <CheckBox value={config.xAxis.showTargetLabel} section='xAxis' fieldName='showTargetLabel' label='Display Deviation point label' updateField={updateField} />
@@ -1492,6 +1700,7 @@ const EditorPanel = () => {
1492
1700
  <CheckBox value={config.yAxis.hideAxis} section='yAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />
1493
1701
  <CheckBox value={config.yAxis.hideLabel} section='yAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />
1494
1702
  <CheckBox value={config.yAxis.hideTicks} section='yAxis' fieldName='hideTicks' label='Hide Ticks' updateField={updateField} />
1703
+
1495
1704
  <TextField value={config.yAxis.max} section='yAxis' fieldName='max' type='number' label='max value' placeholder='Auto' updateField={updateField} />
1496
1705
  <span style={{ color: 'red', display: 'block' }}>{warningMsg.maxMsg}</span>
1497
1706
  <TextField value={config.yAxis.min} section='yAxis' fieldName='min' type='number' label='min value' placeholder='Auto' updateField={updateField} />
@@ -1499,9 +1708,260 @@ const EditorPanel = () => {
1499
1708
  </>
1500
1709
  )
1501
1710
  )}
1711
+
1712
+ {/* start: anchors */}
1713
+ {visHasAnchors() && config.orientation !== 'horizontal' ? (
1714
+ <div className='edit-block'>
1715
+ <h3>Anchors</h3>
1716
+ <Accordion allowZeroExpanded>
1717
+ {config.yAxis?.anchors?.map((anchor, index) => (
1718
+ <AccordionItem className='series-item series-item--chart'>
1719
+ <AccordionItemHeading className='series-item__title'>
1720
+ <>
1721
+ <AccordionItemButton className={'accordion__button accordion__button'}>
1722
+ Anchor {index + 1}
1723
+ <button
1724
+ className='series-list__remove'
1725
+ onClick={e => {
1726
+ e.preventDefault()
1727
+ const copiedAnchorGroups = [...config.yAxis.anchors]
1728
+ copiedAnchorGroups.splice(index, 1)
1729
+ updateConfig({
1730
+ ...config,
1731
+ yAxis: {
1732
+ ...config.yAxis,
1733
+ anchors: copiedAnchorGroups
1734
+ }
1735
+ })
1736
+ }}
1737
+ >
1738
+ Remove
1739
+ </button>
1740
+ </AccordionItemButton>
1741
+ </>
1742
+ </AccordionItemHeading>
1743
+ <AccordionItemPanel>
1744
+ <label>
1745
+ <span>Anchor Value</span>
1746
+ <Tooltip style={{ textTransform: 'none' }}>
1747
+ <Tooltip.Target>
1748
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1749
+ </Tooltip.Target>
1750
+ <Tooltip.Content>
1751
+ <p>Enter the value as its shown in the data column</p>
1752
+ </Tooltip.Content>
1753
+ </Tooltip>
1754
+ <input
1755
+ type='text'
1756
+ value={config.yAxis.anchors[index].value ? config.yAxis.anchors[index].value : ''}
1757
+ onChange={e => {
1758
+ e.preventDefault()
1759
+ const copiedAnchors = [...config.yAxis.anchors]
1760
+ copiedAnchors[index].value = e.target.value
1761
+ updateConfig({
1762
+ ...config,
1763
+ yAxis: {
1764
+ ...config.yAxis,
1765
+ anchors: copiedAnchors
1766
+ }
1767
+ })
1768
+ }}
1769
+ />
1770
+ </label>
1771
+
1772
+ <label>
1773
+ <span>Anchor Color</span>
1774
+ <input
1775
+ type='text'
1776
+ value={config.yAxis.anchors[index].color ? config.yAxis.anchors[index].color : ''}
1777
+ onChange={e => {
1778
+ e.preventDefault()
1779
+ const copiedAnchors = [...config.yAxis.anchors]
1780
+ copiedAnchors[index].color = e.target.value
1781
+ updateConfig({
1782
+ ...config,
1783
+ yAxis: {
1784
+ ...config.yAxis,
1785
+ anchors: copiedAnchors
1786
+ }
1787
+ })
1788
+ }}
1789
+ />
1790
+ </label>
1791
+
1792
+ <label>
1793
+ Anchor Line Style
1794
+ <select
1795
+ value={config.yAxis.anchors[index].lineStyle || ''}
1796
+ onChange={e => {
1797
+ const copiedAnchors = [...config.yAxis.anchors]
1798
+ copiedAnchors[index].lineStyle = e.target.value
1799
+ updateConfig({
1800
+ ...config,
1801
+ yAxis: {
1802
+ ...config.yAxis,
1803
+ anchors: copiedAnchors
1804
+ }
1805
+ })
1806
+ }}
1807
+ >
1808
+ <option>Select</option>
1809
+ {lineOptions.map(line => (
1810
+ <option key={line.key}>{line.value}</option>
1811
+ ))}
1812
+ </select>
1813
+ </label>
1814
+ </AccordionItemPanel>
1815
+ </AccordionItem>
1816
+ ))}
1817
+ </Accordion>
1818
+
1819
+ <button
1820
+ className='btn full-width'
1821
+ onClick={e => {
1822
+ e.preventDefault()
1823
+ const anchors = [...config.yAxis.anchors]
1824
+ anchors.push({})
1825
+ updateConfig({
1826
+ ...config,
1827
+ yAxis: {
1828
+ ...config.yAxis,
1829
+ anchors
1830
+ }
1831
+ })
1832
+ }}
1833
+ >
1834
+ Add Anchor
1835
+ </button>
1836
+ </div>
1837
+ ) : (
1838
+ <div className='edit-block'>
1839
+ <h3>Anchors</h3>
1840
+ <Accordion allowZeroExpanded>
1841
+ {config.xAxis?.anchors?.map((anchor, index) => (
1842
+ <AccordionItem className='series-item series-item--chart'>
1843
+ <AccordionItemHeading className='series-item__title'>
1844
+ <>
1845
+ <AccordionItemButton className={'accordion__button accordion__button'}>
1846
+ Anchor {index + 1}
1847
+ <button
1848
+ className='series-list__remove'
1849
+ onClick={e => {
1850
+ e.preventDefault()
1851
+ const copiedAnchorGroups = [...config.xAxis.anchors]
1852
+ copiedAnchorGroups.splice(index, 1)
1853
+ updateConfig({
1854
+ ...config,
1855
+ xAxis: {
1856
+ ...config.xAxis,
1857
+ anchors: copiedAnchorGroups
1858
+ }
1859
+ })
1860
+ }}
1861
+ >
1862
+ Remove
1863
+ </button>
1864
+ </AccordionItemButton>
1865
+ </>
1866
+ </AccordionItemHeading>
1867
+ <AccordionItemPanel>
1868
+ <label>
1869
+ <span>Anchor Value</span>
1870
+ <Tooltip style={{ textTransform: 'none' }}>
1871
+ <Tooltip.Target>
1872
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1873
+ </Tooltip.Target>
1874
+ <Tooltip.Content>
1875
+ <p>Enter the value as its shown in the data column</p>
1876
+ </Tooltip.Content>
1877
+ </Tooltip>
1878
+ <input
1879
+ type='text'
1880
+ value={config.xAxis.anchors[index].value ? config.xAxis.anchors[index].value : ''}
1881
+ onChange={e => {
1882
+ e.preventDefault()
1883
+ const copiedAnchors = [...config.xAxis.anchors]
1884
+ copiedAnchors[index].value = e.target.value
1885
+ updateConfig({
1886
+ ...config,
1887
+ xAxis: {
1888
+ ...config.xAxis,
1889
+ anchors: copiedAnchors
1890
+ }
1891
+ })
1892
+ }}
1893
+ />
1894
+ </label>
1895
+
1896
+ <label>
1897
+ <span>Anchor Color</span>
1898
+ <input
1899
+ type='text'
1900
+ value={config.xAxis.anchors[index].color ? config.xAxis.anchors[index].color : ''}
1901
+ onChange={e => {
1902
+ e.preventDefault()
1903
+ const copiedAnchors = [...config.xAxis.anchors]
1904
+ copiedAnchors[index].color = e.target.value
1905
+ updateConfig({
1906
+ ...config,
1907
+ xAxis: {
1908
+ ...config.xAxis,
1909
+ anchors: copiedAnchors
1910
+ }
1911
+ })
1912
+ }}
1913
+ />
1914
+ </label>
1915
+
1916
+ <label>
1917
+ Anchor Line Style
1918
+ <select
1919
+ value={config.xAxis.anchors[index].lineStyle || ''}
1920
+ onChange={e => {
1921
+ const copiedAnchors = [...config.xAxis.anchors]
1922
+ copiedAnchors[index].lineStyle = e.target.value
1923
+ updateConfig({
1924
+ ...config,
1925
+ xAxis: {
1926
+ ...config.xAxis,
1927
+ anchors: copiedAnchors
1928
+ }
1929
+ })
1930
+ }}
1931
+ >
1932
+ <option>Select</option>
1933
+ {lineOptions.map(line => (
1934
+ <option key={line.key}>{line.value}</option>
1935
+ ))}
1936
+ </select>
1937
+ </label>
1938
+ </AccordionItemPanel>
1939
+ </AccordionItem>
1940
+ ))}
1941
+ </Accordion>
1942
+
1943
+ <button
1944
+ className='btn full-width'
1945
+ onClick={e => {
1946
+ e.preventDefault()
1947
+ const anchors = [...config.xAxis.anchors]
1948
+ anchors.push({})
1949
+ updateConfig({
1950
+ ...config,
1951
+ xAxis: {
1952
+ ...config.xAxis,
1953
+ anchors
1954
+ }
1955
+ })
1956
+ }}
1957
+ >
1958
+ Add Anchor
1959
+ </button>
1960
+ </div>
1961
+ )}
1962
+ {/* end: anchors */}
1502
1963
  </AccordionItemPanel>
1503
1964
  </AccordionItem>
1504
-
1505
1965
  {/* Right Value Axis Settings */}
1506
1966
  {hasRightAxis && (
1507
1967
  <AccordionItem>
@@ -1562,7 +2022,6 @@ const EditorPanel = () => {
1562
2022
  </AccordionItemPanel>
1563
2023
  </AccordionItem>
1564
2024
  )}
1565
-
1566
2025
  <AccordionItem>
1567
2026
  <AccordionItemHeading>
1568
2027
  <AccordionItemButton>
@@ -1575,7 +2034,7 @@ const EditorPanel = () => {
1575
2034
  <>
1576
2035
  <Select value={config.xAxis.type} section='xAxis' fieldName='type' label='Data Type' updateField={updateField} options={config.visualizationType !== 'Scatter Plot' ? ['categorical', 'date'] : ['categorical', 'continuous', 'date']} />
1577
2036
  <Select
1578
- value={config.xAxis.dataKey || ''}
2037
+ value={config.xAxis.dataKey || setCategoryAxis() || ''}
1579
2038
  section='xAxis'
1580
2039
  fieldName='dataKey'
1581
2040
  label='Data Key'
@@ -1779,6 +2238,46 @@ const EditorPanel = () => {
1779
2238
  <CheckBox value={config.xAxis.hideTicks} section='xAxis' fieldName='hideTicks' label='Hide Ticks' updateField={updateField} />
1780
2239
  </>
1781
2240
  )}
2241
+
2242
+ {config.series?.length === 1 && config.visualizationType === 'Bar' && (
2243
+ <>
2244
+ {/* HIGHLIGHTED BARS */}
2245
+ <label htmlFor='barHighlight'>Bar Highlighting</label>
2246
+ {config.series.length === 1 &&
2247
+ highlightedBarValues.map((highlightedBarValue, i) => (
2248
+ <fieldset>
2249
+ <div className='edit-block' key={`highlighted-bar-${i}`}>
2250
+ <button className='remove-column' onClick={e => handleRemoveHighlightedBar(e, i)}>
2251
+ Remove
2252
+ </button>
2253
+ <p>Highlighted Bar {i + 1}</p>
2254
+ <label>
2255
+ <span className='edit-label column-heading'>Value</span>
2256
+ <select value={config.highlightedBarValues[i].value} onChange={e => handleUpdateHighlightedBar(e, i)}>
2257
+ <option value=''>- Select Value -</option>
2258
+ {highlightedSeriesValues && [...new Set(highlightedSeriesValues)].sort().map(option => <option key={`special-class-value-option-${i}-${option}`}>{option}</option>)}
2259
+ </select>
2260
+ </label>
2261
+ <label>
2262
+ <span className='edit-label column-heading'>Color</span>
2263
+ <input type='text' value={config.highlightedBarValues[i].color ? config.highlightedBarValues[i].color : ''} onChange={e => handleUpdateHighlightedBarColor(e, i)} />
2264
+ </label>
2265
+ <label>
2266
+ <span className='edit-label column-heading'>Border Width</span>
2267
+ <input max='5' min='0' type='number' value={config.highlightedBarValues[i].borderWidth ? config.highlightedBarValues[i].borderWidth : ''} onChange={e => handleUpdateHighlightedBorderWidth(e, i)} />
2268
+ </label>
2269
+ <label>
2270
+ <span className='edit-label column-heading'>Legend Label</span>
2271
+ <input type='text' value={config.highlightedBarValues[i].legendLabel ? config.highlightedBarValues[i].legendLabel : ''} onChange={e => handleHighlightedBarLegendLabel(e, i)} />
2272
+ </label>
2273
+ </div>
2274
+ </fieldset>
2275
+ ))}
2276
+ <button className='btn full-width' onClick={e => handleAddNewHighlightedBar(e)}>
2277
+ Add Highlighted Bar
2278
+ </button>
2279
+ </>
2280
+ )}
1782
2281
  </>
1783
2282
  )}
1784
2283
 
@@ -1828,9 +2327,259 @@ const EditorPanel = () => {
1828
2327
  )}
1829
2328
  </>
1830
2329
  )}
2330
+
2331
+ {/* anchors */}
2332
+ {visHasAnchors() && config.orientation !== 'horizontal' ? (
2333
+ <div className='edit-block'>
2334
+ <h3>Anchors</h3>
2335
+ <Accordion allowZeroExpanded>
2336
+ {config.xAxis?.anchors?.map((anchor, index) => (
2337
+ <AccordionItem className='series-item series-item--chart'>
2338
+ <AccordionItemHeading className='series-item__title'>
2339
+ <>
2340
+ <AccordionItemButton className={'accordion__button accordion__button'}>
2341
+ Anchor {index + 1}
2342
+ <button
2343
+ className='series-list__remove'
2344
+ onClick={e => {
2345
+ e.preventDefault()
2346
+ const copiedAnchorGroups = [...config.xAxis.anchors]
2347
+ copiedAnchorGroups.splice(index, 1)
2348
+ updateConfig({
2349
+ ...config,
2350
+ xAxis: {
2351
+ ...config.xAxis,
2352
+ anchors: copiedAnchorGroups
2353
+ }
2354
+ })
2355
+ }}
2356
+ >
2357
+ Remove
2358
+ </button>
2359
+ </AccordionItemButton>
2360
+ </>
2361
+ </AccordionItemHeading>
2362
+ <AccordionItemPanel>
2363
+ <label>
2364
+ <span>Anchor Value</span>
2365
+ <Tooltip style={{ textTransform: 'none' }}>
2366
+ <Tooltip.Target>
2367
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2368
+ </Tooltip.Target>
2369
+ <Tooltip.Content>
2370
+ <p>Enter the value as its shown in the data column</p>
2371
+ </Tooltip.Content>
2372
+ </Tooltip>
2373
+ <input
2374
+ type='text'
2375
+ value={config.xAxis.anchors[index].value ? config.xAxis.anchors[index].value : ''}
2376
+ onChange={e => {
2377
+ e.preventDefault()
2378
+ const copiedAnchors = [...config.xAxis.anchors]
2379
+ copiedAnchors[index].value = e.target.value
2380
+ updateConfig({
2381
+ ...config,
2382
+ xAxis: {
2383
+ ...config.xAxis,
2384
+ anchors: copiedAnchors
2385
+ }
2386
+ })
2387
+ }}
2388
+ />
2389
+ </label>
2390
+
2391
+ <label>
2392
+ <span>Anchor Color</span>
2393
+ <input
2394
+ type='text'
2395
+ value={config.xAxis.anchors[index].color ? config.xAxis.anchors[index].color : ''}
2396
+ onChange={e => {
2397
+ e.preventDefault()
2398
+ const copiedAnchors = [...config.xAxis.anchors]
2399
+ copiedAnchors[index].color = e.target.value
2400
+ updateConfig({
2401
+ ...config,
2402
+ xAxis: {
2403
+ ...config.xAxis,
2404
+ anchors: copiedAnchors
2405
+ }
2406
+ })
2407
+ }}
2408
+ />
2409
+ </label>
2410
+
2411
+ <label>
2412
+ Anchor Line Style
2413
+ <select
2414
+ value={config.xAxis.anchors[index].lineStyle || ''}
2415
+ onChange={e => {
2416
+ const copiedAnchors = [...config.xAxis.anchors]
2417
+ copiedAnchors[index].lineStyle = e.target.value
2418
+ updateConfig({
2419
+ ...config,
2420
+ xAxis: {
2421
+ ...config.xAxis,
2422
+ anchors: copiedAnchors
2423
+ }
2424
+ })
2425
+ }}
2426
+ >
2427
+ <option>Select</option>
2428
+ {lineOptions.map(line => (
2429
+ <option key={line.key}>{line.value}</option>
2430
+ ))}
2431
+ </select>
2432
+ </label>
2433
+ </AccordionItemPanel>
2434
+ </AccordionItem>
2435
+ ))}
2436
+ </Accordion>
2437
+
2438
+ <button
2439
+ className='btn full-width'
2440
+ onClick={e => {
2441
+ e.preventDefault()
2442
+ const anchors = [...config.xAxis.anchors]
2443
+ anchors.push({})
2444
+ updateConfig({
2445
+ ...config,
2446
+ xAxis: {
2447
+ ...config.xAxis,
2448
+ anchors
2449
+ }
2450
+ })
2451
+ }}
2452
+ >
2453
+ Add Anchor
2454
+ </button>
2455
+ </div>
2456
+ ) : (
2457
+ <div className='edit-block'>
2458
+ <h3>Anchors</h3>
2459
+ <Accordion allowZeroExpanded>
2460
+ {config.yAxis?.anchors?.map((anchor, index) => (
2461
+ <AccordionItem className='series-item series-item--chart'>
2462
+ <AccordionItemHeading className='series-item__title'>
2463
+ <>
2464
+ <AccordionItemButton className={'accordion__button accordion__button'}>
2465
+ Anchor {index + 1}
2466
+ <button
2467
+ className='series-list__remove'
2468
+ onClick={e => {
2469
+ e.preventDefault()
2470
+ const copiedAnchorGroups = [...config.yAxis.anchors]
2471
+ copiedAnchorGroups.splice(index, 1)
2472
+ updateConfig({
2473
+ ...config,
2474
+ yAxis: {
2475
+ ...config.yAxis,
2476
+ anchors: copiedAnchorGroups
2477
+ }
2478
+ })
2479
+ }}
2480
+ >
2481
+ Remove
2482
+ </button>
2483
+ </AccordionItemButton>
2484
+ </>
2485
+ </AccordionItemHeading>
2486
+ <AccordionItemPanel>
2487
+ <label>
2488
+ <span>Anchor Value</span>
2489
+ <Tooltip style={{ textTransform: 'none' }}>
2490
+ <Tooltip.Target>
2491
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2492
+ </Tooltip.Target>
2493
+ <Tooltip.Content>
2494
+ <p>Enter the value as its shown in the data column</p>
2495
+ </Tooltip.Content>
2496
+ </Tooltip>
2497
+ <input
2498
+ type='text'
2499
+ value={config.yAxis.anchors[index].value ? config.yAxis.anchors[index].value : ''}
2500
+ onChange={e => {
2501
+ e.preventDefault()
2502
+ const copiedAnchors = [...config.yAxis.anchors]
2503
+ copiedAnchors[index].value = e.target.value
2504
+ updateConfig({
2505
+ ...config,
2506
+ yAxis: {
2507
+ ...config.yAxis,
2508
+ anchors: copiedAnchors
2509
+ }
2510
+ })
2511
+ }}
2512
+ />
2513
+ </label>
2514
+
2515
+ <label>
2516
+ <span>Anchor Color</span>
2517
+ <input
2518
+ type='text'
2519
+ value={config.yAxis.anchors[index].color ? config.yAxis.anchors[index].color : ''}
2520
+ onChange={e => {
2521
+ e.preventDefault()
2522
+ const copiedAnchors = [...config.yAxis.anchors]
2523
+ copiedAnchors[index].color = e.target.value
2524
+ updateConfig({
2525
+ ...config,
2526
+ yAxis: {
2527
+ ...config.yAxis,
2528
+ anchors: copiedAnchors
2529
+ }
2530
+ })
2531
+ }}
2532
+ />
2533
+ </label>
2534
+
2535
+ <label>
2536
+ Anchor Line Style
2537
+ <select
2538
+ value={config.yAxis.anchors[index].lineStyle || ''}
2539
+ onChange={e => {
2540
+ const copiedAnchors = [...config.yAxis.anchors]
2541
+ copiedAnchors[index].lineStyle = e.target.value
2542
+ updateConfig({
2543
+ ...config,
2544
+ yAxis: {
2545
+ ...config.yAxis,
2546
+ anchors: copiedAnchors
2547
+ }
2548
+ })
2549
+ }}
2550
+ >
2551
+ <option>Select</option>
2552
+ {lineOptions.map(line => (
2553
+ <option key={line.key}>{line.value}</option>
2554
+ ))}
2555
+ </select>
2556
+ </label>
2557
+ </AccordionItemPanel>
2558
+ </AccordionItem>
2559
+ ))}
2560
+ </Accordion>
2561
+
2562
+ <button
2563
+ className='btn full-width'
2564
+ onClick={e => {
2565
+ e.preventDefault()
2566
+ const anchors = [...config.yAxis.anchors]
2567
+ anchors.push({})
2568
+ updateConfig({
2569
+ ...config,
2570
+ yAxis: {
2571
+ ...config.yAxis,
2572
+ anchors
2573
+ }
2574
+ })
2575
+ }}
2576
+ >
2577
+ Add Anchor
2578
+ </button>
2579
+ </div>
2580
+ )}
1831
2581
  </AccordionItemPanel>
1832
2582
  </AccordionItem>
1833
-
1834
2583
  {config.visualizationType !== 'Pie' && config.visualizationType !== 'Paired Bar' && (
1835
2584
  <AccordionItem>
1836
2585
  <AccordionItemHeading>
@@ -1840,8 +2589,173 @@ const EditorPanel = () => {
1840
2589
  <Regions config={config} updateConfig={updateConfig} />
1841
2590
  </AccordionItemPanel>
1842
2591
  </AccordionItem>
2592
+ )}{' '}
2593
+ {/* Columns */}
2594
+ {config.visualizationType !== 'Box Plot' && config.table.showVertical && (
2595
+ <AccordionItem>
2596
+ <AccordionItemHeading>
2597
+ <AccordionItemButton>Columns</AccordionItemButton>
2598
+ </AccordionItemHeading>
2599
+ <AccordionItemPanel>
2600
+ {'navigation' !== config.type && (
2601
+ <fieldset className='primary-fieldset edit-block'>
2602
+ <label>
2603
+ <span className='edit-label'>
2604
+ Additional Columns
2605
+ <Tooltip style={{ textTransform: 'none' }}>
2606
+ <Tooltip.Target>
2607
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2608
+ </Tooltip.Target>
2609
+ <Tooltip.Content>
2610
+ <p>You can specify additional columns to display in tooltips and / or the supporting data table.</p>
2611
+ </Tooltip.Content>
2612
+ </Tooltip>
2613
+ </span>
2614
+ </label>
2615
+ {additionalColumns.map(val => (
2616
+ <fieldset className='edit-block' key={val}>
2617
+ <button
2618
+ className='remove-column'
2619
+ onClick={event => {
2620
+ event.preventDefault()
2621
+ removeAdditionalColumn(val)
2622
+ }}
2623
+ >
2624
+ Remove
2625
+ </button>
2626
+ <label>
2627
+ <span className='edit-label column-heading'>Column</span>
2628
+ <select
2629
+ value={config.columns[val] ? config.columns[val].name : columnsOptions[0]}
2630
+ onChange={event => {
2631
+ editColumn(val, 'name', event.target.value)
2632
+ }}
2633
+ >
2634
+ {columnsOptions}
2635
+ </select>
2636
+ </label>
2637
+ <TextField value={config.columns[val].label} section='columns' subsection={val} fieldName='label' label='Label' updateField={updateField} />
2638
+ <ul className='column-edit'>
2639
+ <li className='three-col'>
2640
+ <TextField value={config.columns[val].prefix} section='columns' subsection={val} fieldName='prefix' label='Prefix' updateField={updateField} />
2641
+ <TextField value={config.columns[val].suffix} section='columns' subsection={val} fieldName='suffix' label='Suffix' updateField={updateField} />
2642
+ <TextField type='number' value={config.columns[val].roundToPlace} section='columns' subsection={val} fieldName='roundToPlace' label='Round' updateField={updateField} />
2643
+ </li>
2644
+ <li>
2645
+ <label className='checkbox'>
2646
+ <input
2647
+ type='checkbox'
2648
+ checked={config.columns[val].useCommas}
2649
+ onChange={event => {
2650
+ editColumn(val, 'useCommas', event.target.checked)
2651
+ }}
2652
+ />
2653
+ <span className='edit-label'>Add Commas to Numbers</span>
2654
+ </label>
2655
+ </li>
2656
+ <li>
2657
+ <label className='checkbox'>
2658
+ <input
2659
+ type='checkbox'
2660
+ checked={config.columns[val].dataTable}
2661
+ onChange={event => {
2662
+ editColumn(val, 'dataTable', event.target.checked)
2663
+ }}
2664
+ />
2665
+ <span className='edit-label'>Display in Data Table</span>
2666
+ </label>
2667
+ </li>
2668
+ {/* disable for now */}
2669
+ {/*
2670
+ <li>
2671
+ <label className='checkbox'>
2672
+ <input
2673
+ type='checkbox'
2674
+ checked={config.columns[val].tooltip}
2675
+ onChange={event => {
2676
+ editColumn(val, 'tooltip', event.target.checked)
2677
+ }}
2678
+ />
2679
+ <span className='edit-label'>Display in Tooltips</span>
2680
+ </label>
2681
+ </li>
2682
+ */}
2683
+ </ul>
2684
+ </fieldset>
2685
+ ))}
2686
+ <button
2687
+ className={'btn full-width'}
2688
+ onClick={event => {
2689
+ event.preventDefault()
2690
+ addAdditionalColumn(additionalColumns.length + 1)
2691
+ }}
2692
+ >
2693
+ Add Column
2694
+ </button>
2695
+ </fieldset>
2696
+ )}
2697
+ {'category' === config.legend.type && (
2698
+ <fieldset className='primary-fieldset edit-block'>
2699
+ <label>
2700
+ <span className='edit-label'>
2701
+ Additional Category
2702
+ <Tooltip style={{ textTransform: 'none' }}>
2703
+ <Tooltip.Target>
2704
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2705
+ </Tooltip.Target>
2706
+ <Tooltip.Content>
2707
+ <p>You can provide additional categories to ensure they appear in the legend</p>
2708
+ </Tooltip.Content>
2709
+ </Tooltip>
2710
+ </span>
2711
+ </label>
2712
+ {config.legend.additionalCategories &&
2713
+ config.legend.additionalCategories.map((val, i) => (
2714
+ <fieldset className='edit-block' key={val}>
2715
+ <button
2716
+ className='remove-column'
2717
+ onClick={event => {
2718
+ event.preventDefault()
2719
+ const updatedAdditionaCategories = [...config.legend.additionalCategories]
2720
+ updatedAdditionaCategories.splice(i, 1)
2721
+ updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
2722
+ }}
2723
+ >
2724
+ Remove
2725
+ </button>
2726
+ <label>
2727
+ <span className='edit-label column-heading'>Category</span>
2728
+ <TextField
2729
+ value={val}
2730
+ section='legend'
2731
+ subsection={null}
2732
+ fieldName='additionalCategories'
2733
+ updateField={(section, subsection, fieldName, value) => {
2734
+ const updatedAdditionaCategories = [...config.legend.additionalCategories]
2735
+ updatedAdditionaCategories[i] = value
2736
+ updateField(section, subsection, fieldName, updatedAdditionaCategories)
2737
+ }}
2738
+ />
2739
+ </label>
2740
+ </fieldset>
2741
+ ))}
2742
+ <button
2743
+ className={'btn full-width'}
2744
+ onClick={event => {
2745
+ event.preventDefault()
2746
+ const updatedAdditionaCategories = [...(config.legend.additionalCategories || [])]
2747
+ updatedAdditionaCategories.push('')
2748
+ updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
2749
+ }}
2750
+ >
2751
+ Add Category
2752
+ </button>
2753
+ </fieldset>
2754
+ )}
2755
+ </AccordionItemPanel>
2756
+ </AccordionItem>
1843
2757
  )}
1844
-
2758
+ {/* End Columns */}
1845
2759
  {visHasLegend() && (
1846
2760
  <AccordionItem>
1847
2761
  <AccordionItemHeading>
@@ -1898,91 +2812,134 @@ const EditorPanel = () => {
1898
2812
  </AccordionItemPanel>
1899
2813
  </AccordionItem>
1900
2814
  )}
1901
-
1902
2815
  <AccordionItem>
1903
2816
  <AccordionItemHeading>
1904
2817
  <AccordionItemButton>Filters</AccordionItemButton>
1905
2818
  </AccordionItemHeading>
1906
2819
  <AccordionItemPanel>
2820
+ {config.filters && (
2821
+ <>
2822
+ {/* prettier-ignore */}
2823
+ <Select
2824
+ value={config.filterBehavior}
2825
+ fieldName='filterBehavior'
2826
+ label='Filter Behavior'
2827
+ updateField={updateField}
2828
+ options={['Apply Button', 'Filter Change']}
2829
+ tooltip={
2830
+ <Tooltip style={{ textTransform: 'none' }}>
2831
+ <Tooltip.Target>
2832
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2833
+ </Tooltip.Target>
2834
+ <Tooltip.Content>
2835
+ <p>The Apply Button option changes the visualization when the user clicks "apply". The Filter Change option immediately changes the visualization when the selection is changed.</p>
2836
+ </Tooltip.Content>
2837
+ </Tooltip>
2838
+ }
2839
+ />
2840
+ <br />
2841
+ </>
2842
+ )}
1907
2843
  {config.filters && (
1908
2844
  <ul className='filters-list'>
1909
- {config.filters.map((filter, index) => (
1910
- <fieldset className='edit-block' key={index}>
1911
- <button
1912
- type='button'
1913
- className='remove-column'
1914
- onClick={() => {
1915
- removeFilter(index)
1916
- }}
1917
- >
1918
- Remove
1919
- </button>
1920
- <label>
1921
- <span className='edit-label column-heading'>Filter</span>
1922
- <select
1923
- value={filter.columnName}
1924
- onChange={e => {
1925
- updateFilterProp('columnName', index, e.target.value)
2845
+ {/* Whether filters should apply onChange or Apply Button */}
2846
+
2847
+ {config.filters.map((filter, index) => {
2848
+ if (filter.type === 'url') return <></>
2849
+
2850
+ return (
2851
+ <fieldset className='edit-block' key={index}>
2852
+ <button
2853
+ type='button'
2854
+ className='remove-column'
2855
+ onClick={() => {
2856
+ removeFilter(index)
1926
2857
  }}
1927
2858
  >
1928
- <option value=''>- Select Option -</option>
1929
- {getColumns().map((dataKey, index) => (
1930
- <option value={dataKey} key={index}>
1931
- {dataKey}
1932
- </option>
1933
- ))}
1934
- </select>
1935
- </label>
1936
- <label>
1937
- <span className='edit-label column-heading'>Label</span>
1938
- <input
1939
- type='text'
1940
- value={filter.label}
1941
- onChange={e => {
1942
- updateFilterProp('label', index, e.target.value)
1943
- }}
1944
- />
1945
- </label>
1946
-
1947
- <label>
1948
- <span className='edit-filterOrder column-heading'>Filter Order</span>
1949
- <select value={filter.order ? filter.order : 'asc'} onChange={e => updateFilterProp('order', index, e.target.value)}>
1950
- {filterOptions.map((option, index) => {
1951
- return (
1952
- <option value={option.value} key={`filter-${index}`}>
1953
- {option.label}
2859
+ Remove
2860
+ </button>
2861
+ <label>
2862
+ <span className='edit-label column-heading'>Filter</span>
2863
+ <select
2864
+ value={filter.columnName}
2865
+ onChange={e => {
2866
+ updateFilterProp('columnName', index, e.target.value)
2867
+ }}
2868
+ >
2869
+ <option value=''>- Select Option -</option>
2870
+ {getFilters(true).map((dataKey, index) => (
2871
+ <option value={dataKey} key={index}>
2872
+ {dataKey}
1954
2873
  </option>
1955
- )
1956
- })}
1957
- </select>
1958
-
1959
- {filter.order === 'cust' && (
1960
- <DragDropContext onDragEnd={({ source, destination }) => handleFilterChange(source.index, destination.index, index, config.filters[index])}>
1961
- <Droppable droppableId='filter_order'>
1962
- {provided => (
1963
- <ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef} style={{ marginTop: '1em' }}>
1964
- {config.filters[index]?.values.map((value, index) => {
1965
- return (
1966
- <Draggable key={value} draggableId={`draggableFilter-${value}`} index={index}>
1967
- {(provided, snapshot) => (
1968
- <li>
1969
- <div className={snapshot.isDragging ? 'currently-dragging' : ''} style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
1970
- {value}
1971
- </div>
1972
- </li>
1973
- )}
1974
- </Draggable>
1975
- )
1976
- })}
1977
- {provided.placeholder}
1978
- </ul>
1979
- )}
1980
- </Droppable>
1981
- </DragDropContext>
1982
- )}
1983
- </label>
1984
- </fieldset>
1985
- ))}
2874
+ ))}
2875
+ </select>
2876
+ </label>
2877
+
2878
+ <label>
2879
+ <span className='edit-label column-heading'>Filter Style</span>
2880
+
2881
+ <select
2882
+ value={filter.filterStyle}
2883
+ onChange={e => {
2884
+ updateFilterProp('filterStyle', index, e.target.value)
2885
+ }}
2886
+ >
2887
+ {filterStyleOptions.map(item => {
2888
+ return <option value={item}>{item}</option>
2889
+ })}
2890
+ </select>
2891
+ </label>
2892
+ <label>
2893
+ <span className='edit-label column-heading'>Label</span>
2894
+ <input
2895
+ type='text'
2896
+ value={filter.label}
2897
+ onChange={e => {
2898
+ updateFilterProp('label', index, e.target.value)
2899
+ }}
2900
+ />
2901
+ </label>
2902
+
2903
+ <label>
2904
+ <span className='edit-filterOrder column-heading'>Filter Order</span>
2905
+ <select value={filter.order ? filter.order : 'asc'} onChange={e => updateFilterProp('order', index, e.target.value)}>
2906
+ {filterOrderOptions.map((option, index) => {
2907
+ return (
2908
+ <option value={option.value} key={`filter-${index}`}>
2909
+ {option.label}
2910
+ </option>
2911
+ )
2912
+ })}
2913
+ </select>
2914
+
2915
+ {filter.order === 'cust' && (
2916
+ <DragDropContext onDragEnd={({ source, destination }) => handleFilterOrder(source.index, destination.index, index, config.filters[index])}>
2917
+ <Droppable droppableId='filter_order'>
2918
+ {provided => (
2919
+ <ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef} style={{ marginTop: '1em' }}>
2920
+ {config.filters[index]?.values.map((value, index) => {
2921
+ return (
2922
+ <Draggable key={value} draggableId={`draggableFilter-${value}`} index={index}>
2923
+ {(provided, snapshot) => (
2924
+ <li>
2925
+ <div className={snapshot.isDragging ? 'currently-dragging' : ''} style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
2926
+ {value}
2927
+ </div>
2928
+ </li>
2929
+ )}
2930
+ </Draggable>
2931
+ )
2932
+ })}
2933
+ {provided.placeholder}
2934
+ </ul>
2935
+ )}
2936
+ </Droppable>
2937
+ </DragDropContext>
2938
+ )}
2939
+ </label>
2940
+ </fieldset>
2941
+ )
2942
+ })}
1986
2943
  </ul>
1987
2944
  )}
1988
2945
  {!config.filters && <p style={{ textAlign: 'center' }}>There are currently no filters.</p>}
@@ -1991,7 +2948,6 @@ const EditorPanel = () => {
1991
2948
  </button>
1992
2949
  </AccordionItemPanel>
1993
2950
  </AccordionItem>
1994
-
1995
2951
  <AccordionItem>
1996
2952
  <AccordionItemHeading>
1997
2953
  <AccordionItemButton>Visual</AccordionItemButton>
@@ -2205,77 +3161,101 @@ const EditorPanel = () => {
2205
3161
  )}
2206
3162
  </AccordionItemPanel>
2207
3163
  </AccordionItem>
2208
-
2209
- <AccordionItem>
2210
- <AccordionItemHeading>
2211
- <AccordionItemButton>Data Table</AccordionItemButton>
2212
- </AccordionItemHeading>
2213
- <AccordionItemPanel>
2214
- <TextField
2215
- value={config.table.label}
2216
- updateField={updateField}
2217
- section='table'
2218
- fieldName='label'
2219
- id='tableLabel'
2220
- label='Data Table Title'
2221
- placeholder='Data Table'
2222
- tooltip={
2223
- <Tooltip style={{ textTransform: 'none' }}>
2224
- <Tooltip.Target>
2225
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2226
- </Tooltip.Target>
2227
- <Tooltip.Content>
2228
- <p>Label is required for Data Table for 508 Compliance</p>
2229
- </Tooltip.Content>
2230
- </Tooltip>
2231
- }
2232
- />
2233
- <CheckBox
2234
- value={config.table.show}
2235
- section='table'
2236
- fieldName='show'
2237
- label='Show Table'
2238
- updateField={updateField}
2239
- tooltip={
2240
- <Tooltip style={{ textTransform: 'none' }}>
2241
- <Tooltip.Target>
2242
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2243
- </Tooltip.Target>
2244
- <Tooltip.Content>
2245
- <p>Hiding the data table may affect accessibility. An alternate form of accessing visualization data is a 508 requirement.</p>
2246
- </Tooltip.Content>
2247
- </Tooltip>
2248
- }
2249
- />
2250
- <TextField
2251
- value={config.table.caption}
2252
- updateField={updateField}
2253
- section='table'
2254
- type='textarea'
2255
- fieldName='caption'
2256
- label='Data Table Caption'
2257
- placeholder=' Data table'
2258
- tooltip={
2259
- <Tooltip style={{ textTransform: 'none' }}>
2260
- <Tooltip.Target>
2261
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2262
- </Tooltip.Target>
2263
- <Tooltip.Content>
2264
- <p>Enter a description of the data table to be read by screen readers.</p>
2265
- </Tooltip.Content>
2266
- </Tooltip>
2267
- }
2268
- />
2269
- <CheckBox value={config.table.limitHeight} section='table' fieldName='limitHeight' label='Limit Table Height' updateField={updateField} />
2270
- {config.table.limitHeight && <TextField value={config.table.height} section='table' fieldName='height' label='Data Table Height' type='number' min='0' max='500' placeholder='Height(px)' updateField={updateField} />}
2271
- <CheckBox value={config.table.expanded} section='table' fieldName='expanded' label='Expanded by Default' updateField={updateField} />
2272
- <CheckBox value={config.table.download} section='table' fieldName='download' label='Display Download Button' updateField={updateField} />
2273
- <CheckBox value={config.table.showDownloadUrl} section='table' fieldName='showDownloadUrl' label='Display Link to Dataset' updateField={updateField} />
2274
- {/* <CheckBox value={config.table.showDownloadImgButton} section='table' fieldName='showDownloadImgButton' label='Display Image Button' updateField={updateField} /> */}
2275
- {/* <CheckBox value={config.table.showDownloadPdfButton} section='table' fieldName='showDownloadPdfButton' label='Display PDF Button' updateField={updateField} /> */}
2276
- {config.visualizationType !== 'Pie' && <TextField value={config.table.indexLabel} section='table' fieldName='indexLabel' label='Index Column Header' updateField={updateField} />}
2277
- </AccordionItemPanel>
2278
- </AccordionItem>
3164
+ {/* Spark Line has no data table */}
3165
+ {config.visualizationType !== 'Spark Line' && (
3166
+ <AccordionItem>
3167
+ <AccordionItemHeading>
3168
+ <AccordionItemButton>Data Table</AccordionItemButton>
3169
+ </AccordionItemHeading>
3170
+ <AccordionItemPanel>
3171
+ <TextField
3172
+ value={config.table.label}
3173
+ updateField={updateField}
3174
+ section='table'
3175
+ fieldName='label'
3176
+ id='tableLabel'
3177
+ label='Data Table Title'
3178
+ placeholder='Data Table'
3179
+ tooltip={
3180
+ <Tooltip style={{ textTransform: 'none' }}>
3181
+ <Tooltip.Target>
3182
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
3183
+ </Tooltip.Target>
3184
+ <Tooltip.Content>
3185
+ <p>Label is required for Data Table for 508 Compliance</p>
3186
+ </Tooltip.Content>
3187
+ </Tooltip>
3188
+ }
3189
+ />
3190
+ <CheckBox
3191
+ value={config.table.show}
3192
+ section='table'
3193
+ fieldName='show'
3194
+ label='Show Data Table'
3195
+ updateField={updateField}
3196
+ className='column-heading'
3197
+ tooltip={
3198
+ <Tooltip style={{ textTransform: 'none' }}>
3199
+ <Tooltip.Target>
3200
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
3201
+ </Tooltip.Target>
3202
+ <Tooltip.Content>
3203
+ <p>Hiding the data table may affect accessibility. An alternate form of accessing visualization data is a 508 requirement.</p>
3204
+ </Tooltip.Content>
3205
+ </Tooltip>
3206
+ }
3207
+ />
3208
+ {config.visualizationType !== 'Box Plot' && (
3209
+ <CheckBox
3210
+ value={config.table.showVertical}
3211
+ section='table'
3212
+ fieldName='showVertical'
3213
+ label='Show Vertical Data'
3214
+ updateField={updateField}
3215
+ className='column-heading'
3216
+ tooltip={
3217
+ <Tooltip style={{ textTransform: 'none' }}>
3218
+ <Tooltip.Target>
3219
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
3220
+ </Tooltip.Target>
3221
+ <Tooltip.Content>
3222
+ <p>This will draw the data table with vertical data instead of horizontal.</p>
3223
+ </Tooltip.Content>
3224
+ </Tooltip>
3225
+ }
3226
+ />
3227
+ )}
3228
+ <TextField value={config.table.indexLabel} section='table' fieldName='indexLabel' label='Index Column Header' updateField={updateField} />
3229
+ <TextField
3230
+ value={config.table.caption}
3231
+ updateField={updateField}
3232
+ section='table'
3233
+ type='textarea'
3234
+ fieldName='caption'
3235
+ label='Data Table Caption'
3236
+ placeholder=' Data table'
3237
+ tooltip={
3238
+ <Tooltip style={{ textTransform: 'none' }}>
3239
+ <Tooltip.Target>
3240
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
3241
+ </Tooltip.Target>
3242
+ <Tooltip.Content>
3243
+ <p>Enter a description of the data table to be read by screen readers.</p>
3244
+ </Tooltip.Content>
3245
+ </Tooltip>
3246
+ }
3247
+ />
3248
+ <CheckBox value={config.table.limitHeight} section='table' fieldName='limitHeight' label='Limit Table Height' updateField={updateField} />
3249
+ {config.table.limitHeight && <TextField value={config.table.height} section='table' fieldName='height' label='Data Table Height' type='number' min='0' max='500' placeholder='Height(px)' updateField={updateField} />}
3250
+ <CheckBox value={config.table.expanded} section='table' fieldName='expanded' label='Expanded by Default' updateField={updateField} />
3251
+ {isDashboard && <CheckBox value={config.table.showDataTableLink} section='table' fieldName='showDataTableLink' label='Show Data Table Name & Link' updateField={updateField} />}
3252
+ {isLoadedFromUrl && <CheckBox value={config.table.showDownloadUrl} section='table' fieldName='showDownloadUrl' label='Show URL to Automatically Updated Data' updateField={updateField} />}
3253
+ <CheckBox value={config.table.download} section='table' fieldName='download' label='Show Download CSV Link' updateField={updateField} />
3254
+ {/* <CheckBox value={config.table.showDownloadImgButton} section='table' fieldName='showDownloadImgButton' label='Display Image Button' updateField={updateField} /> */}
3255
+ {/* <CheckBox value={config.table.showDownloadPdfButton} section='table' fieldName='showDownloadPdfButton' label='Display PDF Button' updateField={updateField} /> */}
3256
+ </AccordionItemPanel>
3257
+ </AccordionItem>
3258
+ )}
2279
3259
  </Accordion>
2280
3260
  </form>
2281
3261
  {config.type !== 'Spark Line' && <AdvancedEditor loadConfig={updateConfig} state={config} convertStateToConfig={convertStateToConfig} />}