@cdc/chart 1.3.3 → 9.22.9

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 (42) hide show
  1. package/dist/cdcchart.js +6 -6
  2. package/examples/cutoff-example-config.json +2 -0
  3. package/examples/cutoff-example-data.json +1 -1
  4. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart-with-numbers-on-bar.json +198 -0
  5. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json +241 -0
  6. package/examples/gallery/bar-chart-horizontal/horizontal-stacked.json +248 -0
  7. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +137 -0
  8. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +80 -0
  9. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +81 -0
  10. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-with-confidence.json +68 -0
  11. package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +111 -0
  12. package/examples/gallery/lollipop/lollipop-style-horizontal.json +220 -0
  13. package/examples/gallery/paired-bar/paired-bar-chart.json +196 -0
  14. package/examples/horizontal-chart.json +3 -0
  15. package/examples/paired-bar-data.json +1 -1
  16. package/examples/paired-bar-example.json +2 -0
  17. package/examples/planet-combo-example-config.json +2 -0
  18. package/examples/planet-example-config.json +2 -2
  19. package/examples/planet-example-data.json +1 -1
  20. package/examples/planet-pie-example-config.json +2 -0
  21. package/examples/private/line-test-data.json +22 -0
  22. package/examples/private/line-test-two.json +216 -0
  23. package/examples/private/line-test.json +102 -0
  24. package/examples/stacked-vertical-bar-example.json +228 -0
  25. package/package.json +3 -3
  26. package/src/CdcChart.tsx +79 -47
  27. package/src/components/BarChart.tsx +82 -39
  28. package/src/components/DataTable.tsx +17 -10
  29. package/src/components/EditorPanel.js +233 -169
  30. package/src/components/LineChart.tsx +3 -0
  31. package/src/components/LinearChart.tsx +171 -77
  32. package/src/components/PairedBarChart.tsx +139 -42
  33. package/src/components/PieChart.tsx +31 -6
  34. package/src/components/SparkLine.js +4 -1
  35. package/src/components/useIntersectionObserver.tsx +32 -0
  36. package/src/data/initial-state.js +17 -7
  37. package/src/hooks/useReduceData.ts +50 -23
  38. package/src/index.html +5 -9
  39. package/src/scss/editor-panel.scss +34 -4
  40. package/src/scss/main.scss +165 -5
  41. package/LICENSE +0 -201
  42. package/src/components/BarStackVertical.js +0 -0
@@ -16,8 +16,10 @@ import Context from '../context'
16
16
  import WarningImage from '../images/warning.svg'
17
17
  import AdvancedEditor from '@cdc/core/components/AdvancedEditor';
18
18
 
19
- import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
20
- import { useColorPalette } from '../hooks/useColorPalette'
19
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
20
+ import Waiting from '@cdc/core/components/Waiting';
21
+ import QuestionIcon from '@cdc/core/assets/icon-question-circle.svg'; //TODO: Update with Icon component
22
+ import {useColorPalette} from '../hooks/useColorPalette';
21
23
 
22
24
  import InputCheckbox from '@cdc/core/components/inputs/InputCheckbox';
23
25
  import InputToggle from '@cdc/core/components/inputs/InputToggle';
@@ -26,7 +28,7 @@ import Icon from '@cdc/core/components/ui/Icon'
26
28
  import useReduceData from '../hooks/useReduceData';
27
29
 
28
30
  const TextField = memo(({label, tooltip, section = null, subsection = null, fieldName, updateField, value: stateValue, type = "input", i = null, min = null, ...attributes}) => {
29
- const [ value, setValue ] = useState(stateValue);
31
+ const [ value, setValue ] = useState(stateValue);
30
32
 
31
33
  const [ debouncedValue ] = useDebounce(value, 500);
32
34
 
@@ -39,7 +41,6 @@ const TextField = memo(({label, tooltip, section = null, subsection = null, fiel
39
41
  let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`
40
42
 
41
43
  const onChange = (e) => {
42
-
43
44
  if ('number' !== type || min === null) {
44
45
  setValue(e.target.value)
45
46
  } else {
@@ -190,20 +191,15 @@ const EditorPanel = () => {
190
191
  setFilteredData
191
192
  } = useContext(Context)
192
193
 
193
- const {minValue,maxValue} = useReduceData(config,data)
194
+ const {minValue,maxValue,existPositiveValue} = useReduceData(config,unfilteredData);
194
195
  const {paletteName,isPaletteReversed,filteredPallets,filteredQualitative,dispatch} = useColorPalette(colorPalettes,config);
195
196
  useEffect(()=>{
196
197
  if(paletteName) updateConfig({...config, palette:paletteName})
197
- },[paletteName])
198
-
198
+ }, [paletteName])
199
+
199
200
  useEffect(()=>{
200
201
  dispatch({type:"GET_PALETTE",payload:colorPalettes,paletteName:config.palette})
201
- },[dispatch,config.palette]);
202
-
203
- useEffect(() => {
204
- dispatch({ type: 'GET_PALETTE', payload: colorPalettes, paletteName: config.palette })
205
- }, [ dispatch, config.palette ])
206
-
202
+ }, [dispatch, config.palette]);
207
203
 
208
204
  const filterOptions = [
209
205
  {
@@ -225,6 +221,8 @@ const EditorPanel = () => {
225
221
  })
226
222
 
227
223
  const sortableItemStyles = {
224
+ animate: false,
225
+ animateReplay: true,
228
226
  display: 'block',
229
227
  boxSizing: 'border-box',
230
228
  border: '1px solid #D1D1D1',
@@ -315,19 +313,25 @@ const EditorPanel = () => {
315
313
  const addNewFilter = () => {
316
314
  let filters = config.filters ? [ ...config.filters ] : []
317
315
 
318
- filters.push({ values: [] })
316
+ filters.push({ values: [] });
319
317
 
320
- updateConfig({ ...config, filters })
318
+ updateConfig({ ...config, filters });
321
319
  }
322
320
 
323
321
  const addNewSeries = (seriesKey) => {
324
322
  let newSeries = config.series ? [ ...config.series ] : []
325
- newSeries.push({ dataKey: seriesKey, type: 'Bar' })
326
- updateConfig({ ...config, series: newSeries })
323
+ newSeries.push({ dataKey: seriesKey, type: 'Bar' });
324
+ updateConfig({ ...config, series: newSeries });
327
325
  }
328
326
 
329
- const removeSeries = (seriesKey) => {
327
+ const sortSeries = (e) => {
328
+ const series = config.series[0].dataKey
329
+ const sorted = data.sort((a, b) => a[series] - b[series]);
330
+ const newData = e === "asc" ? sorted : sorted.reverse();
331
+ updateConfig({ ...config }, newData);
332
+ }
330
333
 
334
+ const removeSeries = (seriesKey) => {
331
335
 
332
336
  let series = [ ...config.series ]
333
337
  let seriesIndex = -1
@@ -428,13 +432,11 @@ const EditorPanel = () => {
428
432
  return unique ? [ ...new Set(values) ] : values
429
433
  }
430
434
 
431
- // when to show lollipop checkbox.
432
- // update as the need grows (ie. vertical bars, divergeing, etc.)
433
- const showLollipopCheckbox = () => {
434
- if (config.visualizationType === 'Bar' && (config.orientation === 'horizontal' || config.orientation === 'regular') && config.visualizationSubType !== 'stacked') {
435
- return true
435
+ const showBarStyleOptions = ()=>{
436
+ if (config.visualizationType === 'Bar' && config.visualizationSubType !== 'stacked' && (config.orientation==='horizontal' || config.orientation==='vertical') ) {
437
+ return ['flat','rounded','lollipop']
436
438
  } else {
437
- return false
439
+ return ['flat','rounded']
438
440
  }
439
441
  }
440
442
 
@@ -494,6 +496,16 @@ const EditorPanel = () => {
494
496
 
495
497
  // eslint-disable-next-line react-hooks/exhaustive-deps
496
498
  }, [ config ])
499
+
500
+ // Set paired bars to be horizontal, even though that option doesn't display
501
+ useEffect(() => {
502
+ if(config.visualizationType === 'Paired Bar') {
503
+ updateConfig({
504
+ ...config,
505
+ orientation: 'horizontal'
506
+ })
507
+ }
508
+ }, []);
497
509
 
498
510
  useEffect(() => {
499
511
  if (config.orientation === 'horizontal') {
@@ -516,7 +528,7 @@ const EditorPanel = () => {
516
528
  {exclusion}
517
529
  </div>
518
530
  </div>
519
- <span className="series-list__remove" onClick={() => removeExclusion(exclusion)}>&#215;</span>
531
+ <button className="series-list__remove" onClick={() => removeExclusion(exclusion)}>&#215;</button>
520
532
  </li>
521
533
  )
522
534
  })}
@@ -534,8 +546,8 @@ const EditorPanel = () => {
534
546
  </section>
535
547
  )
536
548
  }
537
- const handleFilterChange = (idx1, idx2, filterIndex, filter) => {
538
549
 
550
+ const handleFilterChange = (idx1, idx2, filterIndex, filter) => {
539
551
  let filterOrder = filter.values
540
552
  let [ movedItem ] = filterOrder.splice(idx1, 1)
541
553
  filterOrder.splice(idx2, 0, movedItem)
@@ -556,94 +568,51 @@ const EditorPanel = () => {
556
568
  config.runtime.editorErrorMessage = 'Add a data series'
557
569
  }
558
570
 
559
- const section = config.orientation === 'horizontal' ? 'xAxis' : 'yAxis'
560
- const [warningMsg,updateWarningMsg] = useState({maxMsg:'',minMsg:''})
561
-
562
- const onMaxChangeHandler = (e) => {
563
- const enteredValue = e.target.value;
564
-
565
- var existPositiveValue;
566
- let value;
567
-
568
- // loop through series keys
569
- if (config.runtime.seriesKeys) {
570
- for(let i = 0; i < config.runtime.seriesKeys.length; i++) {
571
- existPositiveValue = data.some(d => d[config.runtime.seriesKeys[i]] >= 0);
572
- }
573
- }
574
-
575
- // input >= max
576
- if (Number(enteredValue) >= maxValue) {
577
- value = enteredValue
578
- updateWarningMsg(function(prevMsg){return{...prevMsg,maxMsg:''}})
579
- }
580
-
581
- // input < max && a positive number exists
582
- if (Number(enteredValue)< maxValue && existPositiveValue) {
583
- updateWarningMsg(function(presMsg){return{...presMsg,maxMsg:'Max value must be more than '+ maxValue}})
584
- }
585
-
586
- // input < max && all numbers negatice
587
- if (Number(enteredValue) < maxValue && !existPositiveValue) {
588
- updateWarningMsg(function(presMsg){return{...presMsg,maxMsg:'Value must be more than or equal to 0'}})
589
- }
590
- updateField(section, null, 'max', value)
591
-
592
- if (!enteredValue.length) {
593
- updateWarningMsg(function(prevMsg){return{...prevMsg,maxMsg:''}})
594
- }
595
- }
596
-
597
- const onMinChangeHandler = (e) => {
598
- const enteredValue = e.target.value;
599
- let value;
600
- if (config.visualizationType === 'Line') {
601
- if (Number(enteredValue) > minValue) {
602
- updateWarningMsg(function (presMsg) { return { ...presMsg, minMsg: 'Value must be less than ' + minValue}})
603
- } else {
604
- value = enteredValue
605
- updateWarningMsg(function (presMsg) { return { ...presMsg, minMsg: '' } })
606
- }
607
- } else {
608
- if (Number(enteredValue) > minValue) {
609
- updateWarningMsg(function (presMsg) { return { ...presMsg, minMsg: 'Value must be less than '+ minValue }})
610
- } else if (Number(enteredValue) > 0 ) {
611
- updateWarningMsg(function (presMsg) { return { ...presMsg, minMsg: 'Value must be less than or equal to 0' }})
612
- } else {
613
- value = enteredValue
614
- updateWarningMsg(function (presMsg) { return { ...presMsg, minMsg: '' }})
615
- }
616
-
617
- }
618
- updateField(section, null, 'min', value)
619
-
620
- if (!enteredValue.length) {
621
- updateWarningMsg(function (presMsg) { return {...presMsg, minMsg: ''}})
622
- }
623
- }
624
-
571
+ const section = config.orientation==='horizontal' ? 'xAxis' : 'yAxis';
572
+ const [warningMsg,setWarningMsg] = useState({maxMsg:'',minMsg:''});
573
+
574
+ const validateMaxValue = () => {
575
+ const enteredValue = config[section].max;
576
+ let message = '';
577
+
578
+ switch(true){
579
+ case (enteredValue && parseFloat(enteredValue) < parseFloat(maxValue) && existPositiveValue):
580
+ message = 'Max value must be more than '+ maxValue;
581
+ break;
582
+ case (enteredValue && parseFloat(enteredValue) < 0 && !existPositiveValue):
583
+ message = 'Value must be more than or equal to 0';
584
+ break;
585
+ default : message = '' ;
586
+ }
587
+ setWarningMsg(function(prevMsg){return{...prevMsg,maxMsg:message}});
588
+ };
589
+
590
+
591
+ const validateMinValue = ()=>{
592
+ const enteredValue = config[section].min;
593
+ let minVal = Number(minValue);
594
+ let message = '';
595
+
596
+ switch(true){
597
+ case (config.visualizationType === 'Line' && (enteredValue && parseFloat(enteredValue) > minVal)):
598
+ message = 'Value must be less than ' + minValue;
599
+ break;
600
+ case (enteredValue && minVal > 0 && parseFloat(enteredValue) > 0):
601
+ message = 'Value must be less than or equal to 0';
602
+ break;
603
+ case ( enteredValue && minVal < 0 && parseFloat(enteredValue) > minVal) :
604
+ message = 'Value must be less than ' + minValue;
605
+ break;
606
+ default : message = ''
607
+ };
608
+ setWarningMsg(function (prevMsg) { return {...prevMsg, minMsg: message}});
609
+ };
610
+
611
+ useEffect(()=>{
612
+ validateMinValue();
613
+ validateMaxValue();
614
+ },[minValue,maxValue,config]);
625
615
 
626
- useEffect(() => {
627
- if (config[section].max && config[section].max < maxValue) {
628
- updateField(section,null,'max',maxValue)
629
- updateWarningMsg(function (presMsg) {return {...presMsg, maxMsg: `Entered value ${config[section].max} is not valid `}})
630
- }
631
- }, [data,maxValue])
632
-
633
- useEffect(() => {
634
- if (config.visualizationType === 'Line') {
635
- if (config[section].min && config[section].min > minValue) {
636
- updateWarningMsg(function (presMsg) { return { ...presMsg, minMsg: `Entered value ${config[section].min} is not valid`}})
637
- updateField(section,null,'min',minValue)
638
- }
639
- } else {
640
- if (config[section].min && config[section].min < minValue) {
641
- updateWarningMsg(function (presMsg) { return { ...presMsg, minMsg: `Entered value ${config[section].min} is not valid`}})
642
- updateField(section,null,'min',minValue)
643
- }
644
- }
645
- }, [data,minValue])
646
-
647
616
  return (
648
617
  <ErrorBoundary component="EditorPanel">
649
618
  {config.newViz && <Confirm/>}
@@ -661,27 +630,54 @@ const EditorPanel = () => {
661
630
  </AccordionItemButton>
662
631
  </AccordionItemHeading>
663
632
  <AccordionItemPanel>
664
- <Select value={config.visualizationType} fieldName="visualizationType" label="Chart Type" updateField={updateField} options={[ 'Pie', 'Line', 'Bar', 'Combo', 'Paired Bar']}/>
633
+ <Select value={config.visualizationType} fieldName="visualizationType" label="Chart Type" updateField={updateField} options={[ 'Pie', 'Line', 'Bar', 'Combo', 'Paired Bar' ]}/>
665
634
  {config.visualizationType === 'Bar' && <Select value={config.visualizationSubType || 'Regular'} fieldName="visualizationSubType" label="Chart Subtype" updateField={updateField} options={[ 'regular', 'stacked' ]}/>}
666
635
  {config.visualizationType === 'Bar' && <Select value={config.orientation || 'vertical'} fieldName="orientation" label="Orientation" updateField={updateField} options={[ 'vertical', 'horizontal' ]}/>}
636
+ {config.visualizationType === 'Bar' && <Select value={ config.isLollipopChart? 'lollipop': config.barStyle || 'flat'} fieldName="barStyle" label="bar style" updateField={updateField} options={showBarStyleOptions()}/>}
637
+ {(config.visualizationType === 'Bar' && config.barStyle==='rounded' ) && <Select value={config.tipRounding||'top'} fieldName="tipRounding" label="tip rounding" updateField={updateField} options={['top','full']}/>}
638
+ {(config.visualizationType === 'Bar' && config.barStyle==='rounded' ) && <Select value={config.roundingStyle||'standard'} fieldName="roundingStyle" label="rounding style" updateField={updateField} options={['standard','shallow','finger']}/>}
667
639
  {(config.visualizationType === 'Bar' && config.orientation === 'horizontal') &&
668
640
  <Select value={config.yAxis.labelPlacement || 'Below Bar'} section="yAxis" fieldName="labelPlacement" label="Label Placement" updateField={updateField} options={[ 'Below Bar', 'On Date/Category Axis' ]}/>
669
641
  }
670
- {(showLollipopCheckbox()) &&
671
- <CheckBox value={config.isLollipopChart} fieldName="isLollipopChart" label="Use lollipop styling" updateField={updateField} tooltip={
672
- <Tooltip style={{ textTransform: 'none' }}>
673
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
642
+ {config.orientation === 'horizontal' && (config.yAxis.labelPlacement === 'Below Bar' || config.yAxis.labelPlacement === 'On Date/Category Axis' || config.visualizationType === 'Paired Bar' ) ? (
643
+ <CheckBox value={config.yAxis.displayNumbersOnBar} section="yAxis" fieldName="displayNumbersOnBar" label={config.isLollipopChart ? 'Display Numbers after Bar' : 'Display Numbers on Bar'} updateField={updateField}/>
644
+ ): config.visualizationType !== 'Pie' && (
645
+ <CheckBox value={config.labels} fieldName="labels" label="Display label on data" updateField={updateField}/>
646
+ )}
647
+ {config.visualizationType === 'Pie' && <Select fieldName="pieType" label="Pie Chart Type" updateField={updateField} options={[ 'Regular', 'Donut' ]}/>}
648
+ <TextField value={config.title} fieldName="title" label="Title" updateField={updateField} />
649
+
650
+ <TextField
651
+ value={config.superTitle}
652
+ updateField={updateField}
653
+ fieldName='superTitle'
654
+ label='Super Title'
655
+ placeholder='Super Title'
656
+ tooltip={
657
+ <Tooltip style={{textTransform: 'none'}}>
658
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
674
659
  <Tooltip.Content>
675
- <p>Select this option to replace each bar with a line and a dot at the end.</p>
660
+ <p>Super Title</p>
676
661
  </Tooltip.Content>
677
662
  </Tooltip>
678
- }/>
679
- }
680
- {config.orientation === 'horizontal' && (config.yAxis.labelPlacement === 'Below Bar' || config.yAxis.labelPlacement === 'On Date/Category Axis') &&
681
- <CheckBox value={config.yAxis.displayNumbersOnBar} section="yAxis" fieldName="displayNumbersOnBar" label={config.isLollipopChart ? 'Display Numbers after Bar' : 'Display Numbers on Bar'} updateField={updateField}/>
682
- }
683
- {config.visualizationType === 'Pie' && <Select fieldName="pieType" label="Pie Chart Type" updateField={updateField} options={[ 'Regular', 'Donut' ]}/>}
684
- <TextField value={config.title} fieldName="title" label="Title" updateField={updateField}/>
663
+ }
664
+ />
665
+
666
+ <TextField
667
+ type='textarea'
668
+ value={config.introText}
669
+ updateField={updateField}
670
+ fieldName='introText'
671
+ label='Intro Text'
672
+ tooltip={
673
+ <Tooltip style={{textTransform: 'none'}}>
674
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
675
+ <Tooltip.Content>
676
+ <p>Intro Text</p>
677
+ </Tooltip.Content>
678
+ </Tooltip>
679
+ }
680
+ />
685
681
 
686
682
  <TextField type="textarea" value={config.description} fieldName="description" label="Subtext" updateField={updateField} tooltip={
687
683
  <Tooltip style={{ textTransform: 'none' }}>
@@ -690,7 +686,23 @@ const EditorPanel = () => {
690
686
  <p>Enter supporting text to display below the data visualization, if applicable. The following HTML tags are supported: strong, em, sup, and sub.</p>
691
687
  </Tooltip.Content>
692
688
  </Tooltip>
693
- }/>
689
+ } />
690
+
691
+ <TextField
692
+ type='textarea'
693
+ value={config.footnotes}
694
+ updateField={updateField}
695
+ fieldName='footnotes'
696
+ label='Footnotes'
697
+ tooltip={
698
+ <Tooltip style={{textTransform: 'none'}}>
699
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
700
+ <Tooltip.Content>
701
+ <p>Footnotes</p>
702
+ </Tooltip.Content>
703
+ </Tooltip>
704
+ }
705
+ />
694
706
 
695
707
  {config.visualizationSubType !== 'horizontal' &&
696
708
  <TextField type="number" value={config.height} fieldName="height" label="Chart Height" updateField={updateField}/>
@@ -711,17 +723,17 @@ const EditorPanel = () => {
711
723
  {((!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>}
712
724
  {config.series && config.series.length !== 0 && (
713
725
  <>
714
- <label>
715
- <span className="edit-label">
726
+ <fieldset>
727
+ <legend className="edit-label float-left">
716
728
  Displaying
717
- <Tooltip style={{ textTransform: 'none' }}>
718
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
719
- <Tooltip.Content>
720
- <p>A data series is a set of related data points plotted in a chart and typically represented in the chart legend.</p>
721
- </Tooltip.Content>
722
- </Tooltip>
723
- </span>
724
- </label>
729
+ </legend>
730
+ <Tooltip style={{ textTransform: 'none' }}>
731
+ <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
732
+ <Tooltip.Content>
733
+ <p>A data series is a set of related data points plotted in a chart and typically represented in the chart legend.</p>
734
+ </Tooltip.Content>
735
+ </Tooltip>
736
+ </fieldset>
725
737
  <ul className="series-list">
726
738
  {config.series.map((series, i) => {
727
739
 
@@ -749,8 +761,8 @@ const EditorPanel = () => {
749
761
  </div>
750
762
  <span>
751
763
  <span className="series-list__dropdown">{typeDropdown}</span>
752
- {config.series.length > 1 &&
753
- <span className="series-list__remove" onClick={() => removeSeries(series.dataKey)}>&#215;</span>
764
+ {config.series && config.series.length > 1 &&
765
+ <button className="series-list__remove" onClick={() => removeSeries(series.dataKey)}>&#215;</button>
754
766
  }
755
767
  </span>
756
768
  </li>
@@ -764,8 +776,8 @@ const EditorPanel = () => {
764
776
  {series.dataKey}
765
777
  </div>
766
778
  </div>
767
- {config.series.length > 1 &&
768
- <span className="series-list__remove" onClick={() => removeSeries(series.dataKey)}>&#215;</span>
779
+ {config.series && config.series.length > 1 &&
780
+ <button className="series-list__remove" onClick={() => removeSeries(series.dataKey)}>&#215;</button>
769
781
  }
770
782
  </li>
771
783
  )
@@ -786,6 +798,14 @@ const EditorPanel = () => {
786
798
  <Select value={config.confidenceKeys.lower || ''} section="confidenceKeys" fieldName="lower" label="Lower" updateField={updateField} initial="Select" options={getColumns()}/>
787
799
  </>
788
800
  )}
801
+
802
+ {config.series && config.series.length === 1 && <Select
803
+ fieldName="visualizationType"
804
+ label="Rank by Value"
805
+ initial="Select"
806
+ onChange={(e) => sortSeries(e.target.value)}
807
+ options={['asc', 'desc']} />}
808
+
789
809
  </AccordionItemPanel>
790
810
  </AccordionItem>
791
811
  }
@@ -848,17 +868,17 @@ const EditorPanel = () => {
848
868
  <CheckBox value={config.xAxis.hideAxis} section="xAxis" fieldName="hideAxis" label="Hide Axis" updateField={updateField} />
849
869
  <CheckBox value={config.xAxis.hideLabel} section="xAxis" fieldName="hideLabel" label="Hide Label" updateField={updateField} />
850
870
  <CheckBox value={config.xAxis.hideTicks} section="xAxis" fieldName="hideTicks" label="Hide Ticks" updateField={updateField} />
851
- <TextField value={config.xAxis.max} type='number' label='update max value' placeholder='Auto' onChange={(e) => onMaxChangeHandler(e)} />
871
+ <TextField value={config.xAxis.max} section='xAxis' fieldName='max' label='update max value' type='number' placeholder='Auto' updateField={updateField} />
852
872
  <span style={{color:'red',display:'block'}} >{warningMsg.maxMsg}</span>
853
873
  </>
854
- : config.visualizationType !=='Pie' &&
874
+ : (config.visualizationType !=='Pie') &&
855
875
  <>
856
876
  <CheckBox value={config.yAxis.hideAxis} section="yAxis" fieldName="hideAxis" label="Hide Axis" updateField={updateField} />
857
877
  <CheckBox value={config.yAxis.hideLabel} section="yAxis" fieldName="hideLabel" label="Hide Label" updateField={updateField} />
858
878
  <CheckBox value={config.yAxis.hideTicks} section="yAxis" fieldName="hideTicks" label="Hide Ticks" updateField={updateField} />
859
- <TextField value={config.yAxis.max} type='number' label='update max value' placeholder='Auto' onChange={(e) => onMaxChangeHandler(e)} />
879
+ <TextField value={config.yAxis.max} section='yAxis' fieldName='max' type='number' label='update max value' placeholder='Auto' updateField={updateField} />
860
880
  <span style={{color:'red',display:'block'}} >{warningMsg.maxMsg}</span>
861
- <TextField value={config.yAxis.min} type='number' label='update min value' placeholder='Auto' onChange={(e)=>onMinChangeHandler(e)} />
881
+ <TextField value={config.yAxis.min} section='yAxis' fieldName='min' type='number' label='update min value' placeholder='Auto' updateField={updateField} />
862
882
  <span style={{color:'red',display:'block'}} >{warningMsg.minMsg}</span>
863
883
  </>
864
884
  }
@@ -926,7 +946,9 @@ const EditorPanel = () => {
926
946
  <>
927
947
  {config.exclusions.keys.length > 0 &&
928
948
  <>
929
- <label><span className="edit-label">Excluded Keys</span></label>
949
+ <fieldset>
950
+ <legend className="edit-label">Excluded Keys</legend>
951
+ </fieldset>
930
952
  <ExclusionsList/>
931
953
  </>
932
954
  }
@@ -989,7 +1011,9 @@ const EditorPanel = () => {
989
1011
  <>
990
1012
  {config.exclusions.keys.length > 0 &&
991
1013
  <>
992
- <label><span className="edit-label">Excluded Keys</span></label>
1014
+ <fieldset>
1015
+ <legend className="edit-label">Excluded Keys</legend>
1016
+ </fieldset>
993
1017
  <ExclusionsList/>
994
1018
  </>
995
1019
  }
@@ -1039,6 +1063,7 @@ const EditorPanel = () => {
1039
1063
  <Select value={config.legend.behavior} section="legend" fieldName="behavior" label="Legend Behavior (When clicked)" updateField={updateField} options={[ 'highlight', 'isolate' ]}/>
1040
1064
  <TextField value={config.legend.label} section="legend" fieldName="label" label="Title" updateField={updateField}/>
1041
1065
  <Select value={config.legend.position} section="legend" fieldName="position" label="Position" updateField={updateField} options={[ 'right', 'left' ]}/>
1066
+ <TextField type='textarea' value={config.legend.description} updateField={updateField} section='legend' fieldName='description' label='Legend Description' />
1042
1067
  </AccordionItemPanel>
1043
1068
  </AccordionItem>
1044
1069
 
@@ -1139,12 +1164,12 @@ const EditorPanel = () => {
1139
1164
 
1140
1165
  {config.isLollipopChart &&
1141
1166
  <>
1142
- <label className="header">
1143
- <span className="edit-label">Lollipop Shape</span>
1167
+ <fieldset className="header">
1168
+ <legend className="edit-label">Lollipop Shape</legend>
1144
1169
  <div onChange={(e) => {
1145
1170
  setLollipopShape(e.target.value)
1146
1171
  }}>
1147
- <label>
1172
+ <label className="radio-label">
1148
1173
  <input
1149
1174
  type="radio"
1150
1175
  name="lollipopShape"
@@ -1153,7 +1178,7 @@ const EditorPanel = () => {
1153
1178
  />
1154
1179
  Circle
1155
1180
  </label>
1156
- <label>
1181
+ <label className="radio-label">
1157
1182
  <input
1158
1183
  type="radio"
1159
1184
  name="lollipopShape"
@@ -1164,7 +1189,7 @@ const EditorPanel = () => {
1164
1189
  </label>
1165
1190
  </div>
1166
1191
 
1167
- </label>
1192
+ </fieldset>
1168
1193
  <Select value={config.lollipopColorStyle ? config.lollipopColorStyle : 'two-tone'} fieldName="lollipopColorStyle" label="Lollipop Color Style" updateField={updateField} options={[ 'regular', 'two-tone' ]}/>
1169
1194
  <Select value={config.lollipopSize ? config.lollipopSize : 'small'} fieldName="lollipopSize" label="Lollipop Size" updateField={updateField} options={[ 'small', 'medium', 'large' ]}/>
1170
1195
  </>
@@ -1172,10 +1197,14 @@ const EditorPanel = () => {
1172
1197
 
1173
1198
  <Select value={config.fontSize} fieldName="fontSize" label="Font Size" updateField={updateField} options={[ 'small', 'medium', 'large' ]}/>
1174
1199
 
1175
- {config.series?.some(series => series.type === 'Bar') &&
1200
+ {config.series?.some(series => series.type === 'Bar' || series.type === 'Paired Bar') &&
1176
1201
  <Select value={config.barHasBorder} fieldName="barHasBorder" label="Bar Borders" updateField={updateField} options={[ 'true', 'false' ]}/>
1177
1202
  }
1178
1203
 
1204
+ {/*<CheckBox value={config.animate} fieldName="animate" label="Animate Visualization" updateField={updateField} />*/}
1205
+
1206
+ {/*<CheckBox value={config.animateReplay} fieldName="animateReplay" label="Replay Animation When Filters Are Changed" updateField={updateField} />*/}
1207
+
1179
1208
  {((config.series?.some(series => series.type === 'Line') && config.visualizationType === 'Combo') || config.visualizationType === 'Line') &&
1180
1209
  <Select value={config.lineDatapointStyle} fieldName="lineDatapointStyle" label="Line Datapoint Style" updateField={updateField} options={[ 'hidden', 'hover', 'always show' ]}/>
1181
1210
  }
@@ -1184,10 +1213,15 @@ const EditorPanel = () => {
1184
1213
  <span className="edit-label">Header Theme</span>
1185
1214
  <ul className="color-palette">
1186
1215
  {headerColors.map((palette) => (
1187
- <li title={palette} key={palette} onClick={() => {
1188
- updateConfig({ ...config, theme: palette })
1189
- }} className={config.theme === palette ? 'selected ' + palette : palette}>
1190
- </li>
1216
+ <button
1217
+ title={palette}
1218
+ key={palette}
1219
+ onClick={(e) => {
1220
+ e.preventDefault();
1221
+ updateConfig({ ...config, theme: palette })
1222
+ }}
1223
+ className={config.theme === palette ? 'selected ' + palette : palette}>
1224
+ </button>
1191
1225
  ))}
1192
1226
  </ul>
1193
1227
  </label>
@@ -1213,13 +1247,19 @@ const EditorPanel = () => {
1213
1247
  }
1214
1248
 
1215
1249
  return (
1216
- <li title={palette} key={palette} onClick={() => {
1217
- updateConfig({ ...config, palette })
1218
- }} className={config.palette === palette ? 'selected' : ''}>
1250
+ <button
1251
+ title={palette}
1252
+ key={palette}
1253
+ onClick={(e) => {
1254
+ e.preventDefault();
1255
+ updateConfig({ ...config, palette })
1256
+ }}
1257
+ className={config.palette === palette ? 'selected' : ''}
1258
+ >
1219
1259
  <span style={colorOne}></span>
1220
1260
  <span style={colorTwo}></span>
1221
1261
  <span style={colorThree}></span>
1222
- </li>
1262
+ </button>
1223
1263
  )
1224
1264
  })}
1225
1265
  </ul>
@@ -1241,22 +1281,25 @@ const EditorPanel = () => {
1241
1281
 
1242
1282
 
1243
1283
  return (
1244
- <li title={palette} key={palette} onClick={() => {
1245
- updateConfig({ ...config, palette })
1246
- }} className={config.palette === palette ? 'selected' : ''}>
1284
+ <button
1285
+ title={palette}
1286
+ key={palette}
1287
+ onClick={(e) => {
1288
+ e.preventDefault();
1289
+ updateConfig({ ...config, palette })
1290
+ }}
1291
+ className={config.palette === palette ? 'selected' : ''}
1292
+ >
1247
1293
  <span style={colorOne}></span>
1248
1294
  <span style={colorTwo}></span>
1249
1295
  <span style={colorThree}></span>
1250
- </li>
1296
+ </button>
1251
1297
  )
1252
1298
  })}
1253
1299
  </ul>
1254
1300
 
1255
1301
  {config.visualizationType !== 'Pie' && (
1256
1302
  <>
1257
- {config.orientation !== 'horizontal' &&
1258
- <CheckBox value={config.labels} fieldName="labels" label="Display label on data" updateField={updateField}/>
1259
- }
1260
1303
  <TextField value={config.dataCutoff} type="number" fieldName="dataCutoff" className="number-narrow" label="Data Cutoff" updateField={updateField} tooltip={
1261
1304
  <Tooltip style={{ textTransform: 'none' }}>
1262
1305
  <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
@@ -1299,10 +1342,31 @@ const EditorPanel = () => {
1299
1342
  </Tooltip.Content>
1300
1343
  </Tooltip>
1301
1344
  }/>
1345
+ <TextField
1346
+ value={config.table.caption}
1347
+ updateField={updateField}
1348
+ section='table'
1349
+ type='textarea'
1350
+ fieldName='caption'
1351
+ label='Data Table Caption'
1352
+ placeholder=' Data table'
1353
+ tooltip={
1354
+ <Tooltip style={{textTransform: 'none'}}>
1355
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1356
+ <Tooltip.Content>
1357
+ <p>Enter a description of the data table to be read by screen readers.</p>
1358
+ </Tooltip.Content>
1359
+ </Tooltip>
1360
+ }
1361
+ />
1362
+ <CheckBox value={config.table.limitHeight} section="table" fieldName="limitHeight" label="Limit Table Height" updateField={updateField}/>
1363
+ {config.table.limitHeight && (
1364
+ <TextField value={config.table.height} section="table" fieldName='height' label='Data Table Height' type="number" min="0" max="500" placeholder='Height(px)' updateField={updateField}/>
1365
+ )}
1302
1366
  <CheckBox value={config.table.expanded} section="table" fieldName="expanded" label="Expanded by Default" updateField={updateField}/>
1303
1367
  <CheckBox value={config.table.download} section="table" fieldName="download" label="Display Download Button" updateField={updateField}/>
1304
1368
  <TextField value={config.table.label} section="table" fieldName="label" label="Label" updateField={updateField}/>
1305
- <TextField value={config.table.indexLabel} section="table" fieldName="indexLabel" label="Index Column Header" updateField={updateField}/>
1369
+ {config.visualizationType !== 'Pie' && <TextField value={config.table.indexLabel} section="table" fieldName="indexLabel" label="Index Column Header" updateField={updateField}/>}
1306
1370
  </AccordionItemPanel>
1307
1371
  </AccordionItem>
1308
1372
  </Accordion>