@cdc/chart 1.3.3 → 4.22.10

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 (49) 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/dynamic-legends.json +125 -0
  5. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart-with-numbers-on-bar.json +198 -0
  6. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json +241 -0
  7. package/examples/gallery/bar-chart-horizontal/horizontal-stacked.json +248 -0
  8. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +137 -0
  9. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +80 -0
  10. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +81 -0
  11. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-with-confidence.json +68 -0
  12. package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +111 -0
  13. package/examples/gallery/lollipop/lollipop-style-horizontal.json +220 -0
  14. package/examples/gallery/paired-bar/paired-bar-chart.json +196 -0
  15. package/examples/horizontal-chart.json +3 -0
  16. package/examples/paired-bar-data.json +1 -1
  17. package/examples/paired-bar-example.json +2 -0
  18. package/examples/planet-combo-example-config.json +2 -0
  19. package/examples/planet-example-config.json +2 -2
  20. package/examples/planet-example-data.json +1 -1
  21. package/examples/planet-pie-example-config.json +2 -0
  22. package/examples/private/line-test-data.json +22 -0
  23. package/examples/private/line-test-two.json +216 -0
  24. package/examples/private/line-test.json +102 -0
  25. package/examples/private/shawn.json +1296 -0
  26. package/examples/private/yaxis-test.json +132 -0
  27. package/examples/private/yaxis-testing.csv +27 -0
  28. package/examples/private/yaxis.json +28 -0
  29. package/examples/stacked-vertical-bar-example.json +228 -0
  30. package/package.json +3 -3
  31. package/src/CdcChart.tsx +121 -168
  32. package/src/components/BarChart.tsx +92 -40
  33. package/src/components/DataTable.tsx +28 -13
  34. package/src/components/EditorPanel.js +286 -182
  35. package/src/components/Legend.js +334 -0
  36. package/src/components/LineChart.tsx +57 -17
  37. package/src/components/LinearChart.tsx +171 -77
  38. package/src/components/PairedBarChart.tsx +139 -42
  39. package/src/components/PieChart.tsx +33 -6
  40. package/src/components/SparkLine.js +28 -27
  41. package/src/components/useIntersectionObserver.tsx +30 -0
  42. package/src/data/initial-state.js +23 -7
  43. package/src/hooks/useChartClasses.js +35 -0
  44. package/src/hooks/useLegendClasses.js +20 -0
  45. package/src/hooks/useReduceData.ts +72 -24
  46. package/src/index.html +29 -30
  47. package/src/scss/editor-panel.scss +34 -4
  48. package/src/scss/main.scss +201 -5
  49. 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,17 @@ 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
+ }, [paletteName])
199
+
198
200
 
201
+
199
202
  useEffect(()=>{
200
203
  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
-
204
+ }, [dispatch, config.palette]);
207
205
 
208
206
  const filterOptions = [
209
207
  {
@@ -225,6 +223,8 @@ const EditorPanel = () => {
225
223
  })
226
224
 
227
225
  const sortableItemStyles = {
226
+ animate: false,
227
+ animateReplay: true,
228
228
  display: 'block',
229
229
  boxSizing: 'border-box',
230
230
  border: '1px solid #D1D1D1',
@@ -238,8 +238,6 @@ const EditorPanel = () => {
238
238
  zIndex: '999',
239
239
  }
240
240
 
241
- let hasLineChart = false
242
-
243
241
  const enforceRestrictions = (updatedConfig) => {
244
242
  if (updatedConfig.orientation === 'horizontal') {
245
243
  updatedConfig.labels = false
@@ -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
@@ -420,6 +424,19 @@ const EditorPanel = () => {
420
424
  return Object.keys(columns)
421
425
  }
422
426
 
427
+
428
+ const getDataValueOptions = (data)=>{
429
+ if(!data) return [];
430
+ const set = new Set();
431
+ for (let i=0; i<data.length; i++){
432
+ for (const [key, value] of Object.entries(data[i])) {
433
+ set.add(key)
434
+ }
435
+
436
+ }
437
+ return Array.from(set)
438
+ };
439
+
423
440
  const getDataValues = (dataKey, unique = false) => {
424
441
  let values = []
425
442
  excludedData.map(e => {
@@ -428,13 +445,11 @@ const EditorPanel = () => {
428
445
  return unique ? [ ...new Set(values) ] : values
429
446
  }
430
447
 
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
448
+ const showBarStyleOptions = ()=>{
449
+ if (config.visualizationType === 'Bar' && config.visualizationSubType !== 'stacked' && (config.orientation==='horizontal' || config.orientation==='vertical') ) {
450
+ return ['flat','rounded','lollipop']
436
451
  } else {
437
- return false
452
+ return ['flat','rounded']
438
453
  }
439
454
  }
440
455
 
@@ -494,6 +509,16 @@ const EditorPanel = () => {
494
509
 
495
510
  // eslint-disable-next-line react-hooks/exhaustive-deps
496
511
  }, [ config ])
512
+
513
+ // Set paired bars to be horizontal, even though that option doesn't display
514
+ useEffect(() => {
515
+ if(config.visualizationType === 'Paired Bar') {
516
+ updateConfig({
517
+ ...config,
518
+ orientation: 'horizontal'
519
+ })
520
+ }
521
+ }, []);
497
522
 
498
523
  useEffect(() => {
499
524
  if (config.orientation === 'horizontal') {
@@ -516,7 +541,7 @@ const EditorPanel = () => {
516
541
  {exclusion}
517
542
  </div>
518
543
  </div>
519
- <span className="series-list__remove" onClick={() => removeExclusion(exclusion)}>&#215;</span>
544
+ <button className="series-list__remove" onClick={() => removeExclusion(exclusion)}>&#215;</button>
520
545
  </li>
521
546
  )
522
547
  })}
@@ -534,8 +559,8 @@ const EditorPanel = () => {
534
559
  </section>
535
560
  )
536
561
  }
537
- const handleFilterChange = (idx1, idx2, filterIndex, filter) => {
538
562
 
563
+ const handleFilterChange = (idx1, idx2, filterIndex, filter) => {
539
564
  let filterOrder = filter.values
540
565
  let [ movedItem ] = filterOrder.splice(idx1, 1)
541
566
  filterOrder.splice(idx2, 0, movedItem)
@@ -556,94 +581,51 @@ const EditorPanel = () => {
556
581
  config.runtime.editorErrorMessage = 'Add a data series'
557
582
  }
558
583
 
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
-
584
+ const section = config.orientation==='horizontal' ? 'xAxis' : 'yAxis';
585
+ const [warningMsg,setWarningMsg] = useState({maxMsg:'',minMsg:''});
586
+
587
+ const validateMaxValue = () => {
588
+ const enteredValue = config[section].max;
589
+ let message = '';
590
+
591
+ switch(true){
592
+ case (enteredValue && parseFloat(enteredValue) < parseFloat(maxValue) && existPositiveValue):
593
+ message = 'Max value must be more than '+ maxValue;
594
+ break;
595
+ case (enteredValue && parseFloat(enteredValue) < 0 && !existPositiveValue):
596
+ message = 'Value must be more than or equal to 0';
597
+ break;
598
+ default : message = '' ;
599
+ }
600
+ setWarningMsg(function(prevMsg){return{...prevMsg,maxMsg:message}});
601
+ };
602
+
603
+
604
+ const validateMinValue = ()=>{
605
+ const enteredValue = config[section].min;
606
+ let minVal = Number(minValue);
607
+ let message = '';
608
+
609
+ switch(true){
610
+ case ((config.visualizationType === 'Line' || config.visualizationType === 'Spark Line') && (enteredValue && parseFloat(enteredValue) > minVal)):
611
+ message = 'Value must be less than ' + minValue;
612
+ break;
613
+ case ((config.visualizationType === 'Bar' || config.visualizationType === 'Combo' ) && enteredValue && minVal > 0 && parseFloat(enteredValue) > 0):
614
+ message = 'Value must be less than or equal to 0';
615
+ break;
616
+ case ( enteredValue && minVal < 0 && parseFloat(enteredValue) > minVal) :
617
+ message = 'Value must be less than ' + minValue;
618
+ break;
619
+ default : message = ''
620
+ };
621
+ setWarningMsg(function (prevMsg) { return {...prevMsg, minMsg: message}});
622
+ };
623
+
624
+ useEffect(()=>{
625
+ validateMinValue();
626
+ validateMaxValue();
627
+ },[minValue,maxValue,config]);
625
628
 
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
629
  return (
648
630
  <ErrorBoundary component="EditorPanel">
649
631
  {config.newViz && <Confirm/>}
@@ -661,27 +643,54 @@ const EditorPanel = () => {
661
643
  </AccordionItemButton>
662
644
  </AccordionItemHeading>
663
645
  <AccordionItemPanel>
664
- <Select value={config.visualizationType} fieldName="visualizationType" label="Chart Type" updateField={updateField} options={[ 'Pie', 'Line', 'Bar', 'Combo', 'Paired Bar']}/>
665
- {config.visualizationType === 'Bar' && <Select value={config.visualizationSubType || 'Regular'} fieldName="visualizationSubType" label="Chart Subtype" updateField={updateField} options={[ 'regular', 'stacked' ]}/>}
646
+ <Select value={config.visualizationType} fieldName="visualizationType" label="Chart Type" updateField={updateField} options={[ 'Pie', 'Line', 'Bar', 'Combo', 'Paired Bar', 'Spark Line' ]}/>
647
+ {(config.visualizationType === 'Bar'|| config.visualizationType === 'Combo') && <Select value={config.visualizationSubType || 'Regular'} fieldName="visualizationSubType" label="Chart Subtype" updateField={updateField} options={[ 'regular', 'stacked' ]}/>}
666
648
  {config.visualizationType === 'Bar' && <Select value={config.orientation || 'vertical'} fieldName="orientation" label="Orientation" updateField={updateField} options={[ 'vertical', 'horizontal' ]}/>}
649
+ {config.visualizationType === 'Bar' && <Select value={ config.isLollipopChart? 'lollipop': config.barStyle || 'flat'} fieldName="barStyle" label="bar style" updateField={updateField} options={showBarStyleOptions()}/>}
650
+ {(config.visualizationType === 'Bar' && config.barStyle==='rounded' ) && <Select value={config.tipRounding||'top'} fieldName="tipRounding" label="tip rounding" updateField={updateField} options={['top','full']}/>}
651
+ {(config.visualizationType === 'Bar' && config.barStyle==='rounded' ) && <Select value={config.roundingStyle||'standard'} fieldName="roundingStyle" label="rounding style" updateField={updateField} options={['standard','shallow','finger']}/>}
667
652
  {(config.visualizationType === 'Bar' && config.orientation === 'horizontal') &&
668
653
  <Select value={config.yAxis.labelPlacement || 'Below Bar'} section="yAxis" fieldName="labelPlacement" label="Label Placement" updateField={updateField} options={[ 'Below Bar', 'On Date/Category Axis' ]}/>
669
654
  }
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>
655
+ {config.orientation === 'horizontal' && (config.yAxis.labelPlacement === 'Below Bar' || config.yAxis.labelPlacement === 'On Date/Category Axis' || config.visualizationType === 'Paired Bar' ) ? (
656
+ <CheckBox value={config.yAxis.displayNumbersOnBar} section="yAxis" fieldName="displayNumbersOnBar" label={config.isLollipopChart ? 'Display Numbers after Bar' : 'Display Numbers on Bar'} updateField={updateField}/>
657
+ ): config.visualizationType !== 'Pie' && (
658
+ <CheckBox value={config.labels} fieldName="labels" label="Display label on data" updateField={updateField}/>
659
+ )}
660
+ {config.visualizationType === 'Pie' && <Select fieldName="pieType" label="Pie Chart Type" updateField={updateField} options={[ 'Regular', 'Donut' ]}/>}
661
+ <TextField value={config.title} fieldName="title" label="Title" updateField={updateField} />
662
+
663
+ <TextField
664
+ value={config.superTitle}
665
+ updateField={updateField}
666
+ fieldName='superTitle'
667
+ label='Super Title'
668
+ placeholder='Super Title'
669
+ tooltip={
670
+ <Tooltip style={{textTransform: 'none'}}>
671
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
674
672
  <Tooltip.Content>
675
- <p>Select this option to replace each bar with a line and a dot at the end.</p>
673
+ <p>Super Title</p>
676
674
  </Tooltip.Content>
677
675
  </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}/>
676
+ }
677
+ />
678
+
679
+ <TextField
680
+ type='textarea'
681
+ value={config.introText}
682
+ updateField={updateField}
683
+ fieldName='introText'
684
+ label='Intro Text'
685
+ tooltip={
686
+ <Tooltip style={{textTransform: 'none'}}>
687
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
688
+ <Tooltip.Content>
689
+ <p>Intro Text</p>
690
+ </Tooltip.Content>
691
+ </Tooltip>
692
+ }
693
+ />
685
694
 
686
695
  <TextField type="textarea" value={config.description} fieldName="description" label="Subtext" updateField={updateField} tooltip={
687
696
  <Tooltip style={{ textTransform: 'none' }}>
@@ -690,7 +699,23 @@ const EditorPanel = () => {
690
699
  <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
700
  </Tooltip.Content>
692
701
  </Tooltip>
693
- }/>
702
+ } />
703
+
704
+ <TextField
705
+ type='textarea'
706
+ value={config.footnotes}
707
+ updateField={updateField}
708
+ fieldName='footnotes'
709
+ label='Footnotes'
710
+ tooltip={
711
+ <Tooltip style={{textTransform: 'none'}}>
712
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
713
+ <Tooltip.Content>
714
+ <p>Footnotes</p>
715
+ </Tooltip.Content>
716
+ </Tooltip>
717
+ }
718
+ />
694
719
 
695
720
  {config.visualizationSubType !== 'horizontal' &&
696
721
  <TextField type="number" value={config.height} fieldName="height" label="Chart Height" updateField={updateField}/>
@@ -711,17 +736,17 @@ const EditorPanel = () => {
711
736
  {((!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
737
  {config.series && config.series.length !== 0 && (
713
738
  <>
714
- <label>
715
- <span className="edit-label">
739
+ <fieldset>
740
+ <legend className="edit-label float-left">
716
741
  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>
742
+ </legend>
743
+ <Tooltip style={{ textTransform: 'none' }}>
744
+ <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
745
+ <Tooltip.Content>
746
+ <p>A data series is a set of related data points plotted in a chart and typically represented in the chart legend.</p>
747
+ </Tooltip.Content>
748
+ </Tooltip>
749
+ </fieldset>
725
750
  <ul className="series-list">
726
751
  {config.series.map((series, i) => {
727
752
 
@@ -738,7 +763,10 @@ const EditorPanel = () => {
738
763
  }} style={{ width: '100px', marginRight: '10px' }}>
739
764
  <option value="" default>Select</option>
740
765
  <option value="Bar">Bar</option>
741
- <option value="Line">Line</option>
766
+ <option value="Line">Solid Line</option>
767
+ <option value="dashed-sm">Small Dashed</option>
768
+ <option value="dashed-md">Medium Dashed</option>
769
+ <option value="dashed-lg">Large Dashed</option>
742
770
  </select>
743
771
  )
744
772
 
@@ -749,8 +777,8 @@ const EditorPanel = () => {
749
777
  </div>
750
778
  <span>
751
779
  <span className="series-list__dropdown">{typeDropdown}</span>
752
- {config.series.length > 1 &&
753
- <span className="series-list__remove" onClick={() => removeSeries(series.dataKey)}>&#215;</span>
780
+ {config.series && config.series.length > 1 &&
781
+ <button className="series-list__remove" onClick={() => removeSeries(series.dataKey)}>&#215;</button>
754
782
  }
755
783
  </span>
756
784
  </li>
@@ -764,8 +792,8 @@ const EditorPanel = () => {
764
792
  {series.dataKey}
765
793
  </div>
766
794
  </div>
767
- {config.series.length > 1 &&
768
- <span className="series-list__remove" onClick={() => removeSeries(series.dataKey)}>&#215;</span>
795
+ {config.series && config.series.length > 1 &&
796
+ <button className="series-list__remove" onClick={() => removeSeries(series.dataKey)}>&#215;</button>
769
797
  }
770
798
  </li>
771
799
  )
@@ -786,6 +814,14 @@ const EditorPanel = () => {
786
814
  <Select value={config.confidenceKeys.lower || ''} section="confidenceKeys" fieldName="lower" label="Lower" updateField={updateField} initial="Select" options={getColumns()}/>
787
815
  </>
788
816
  )}
817
+
818
+ {config.series && config.series.length === 1 && <Select
819
+ fieldName="visualizationType"
820
+ label="Rank by Value"
821
+ initial="Select"
822
+ onChange={(e) => sortSeries(e.target.value)}
823
+ options={['asc', 'desc']} />}
824
+
789
825
  </AccordionItemPanel>
790
826
  </AccordionItem>
791
827
  }
@@ -815,7 +851,14 @@ const EditorPanel = () => {
815
851
  <>
816
852
  <TextField value={config.yAxis.label} section="yAxis" fieldName="label" label="Label" updateField={updateField}/>
817
853
  <TextField value={config.yAxis.numTicks} placeholder="Auto" type="number" section="yAxis" fieldName="numTicks" label="Number of ticks" className="number-narrow" updateField={updateField}/>
818
- <TextField value={config.yAxis.size} type="number" section="yAxis" fieldName="size" label={config.orientation === 'horizontal' ? 'Size (Height)' : 'Size (Width)'} className="number-narrow" updateField={updateField}/>
854
+ <TextField value={config.yAxis.size} type="number" section="yAxis" fieldName="size" label={config.orientation === 'horizontal' ? 'Size (Height)' : 'Size (Width)'} className="number-narrow" updateField={updateField} tooltip={
855
+ <Tooltip style={{ textTransform: 'none' }}>
856
+ <Tooltip.Target><Icon display="question"/></Tooltip.Target>
857
+ <Tooltip.Content>
858
+ <p>{`Increase the size if elements in the ${config.orientation} axis are being crowded or hidden behind other elements. Decrease if less space is required for the value axis.`}</p>
859
+ </Tooltip.Content>
860
+ </Tooltip>
861
+ }/>
819
862
  {config.orientation !== 'horizontal' && <CheckBox value={config.yAxis.gridLines} section="yAxis" fieldName="gridLines" label="Display Gridlines" updateField={updateField}/>}
820
863
  </>
821
864
  )}
@@ -848,17 +891,17 @@ const EditorPanel = () => {
848
891
  <CheckBox value={config.xAxis.hideAxis} section="xAxis" fieldName="hideAxis" label="Hide Axis" updateField={updateField} />
849
892
  <CheckBox value={config.xAxis.hideLabel} section="xAxis" fieldName="hideLabel" label="Hide Label" updateField={updateField} />
850
893
  <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)} />
894
+ <TextField value={config.xAxis.max} section='xAxis' fieldName='max' label='update max value' type='number' placeholder='Auto' updateField={updateField} />
852
895
  <span style={{color:'red',display:'block'}} >{warningMsg.maxMsg}</span>
853
896
  </>
854
- : config.visualizationType !=='Pie' &&
897
+ : (config.visualizationType !=='Pie') &&
855
898
  <>
856
899
  <CheckBox value={config.yAxis.hideAxis} section="yAxis" fieldName="hideAxis" label="Hide Axis" updateField={updateField} />
857
900
  <CheckBox value={config.yAxis.hideLabel} section="yAxis" fieldName="hideLabel" label="Hide Label" updateField={updateField} />
858
901
  <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)} />
902
+ <TextField value={config.yAxis.max} section='yAxis' fieldName='max' type='number' label='update max value' placeholder='Auto' updateField={updateField} />
860
903
  <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)} />
904
+ <TextField value={config.yAxis.min} section='yAxis' fieldName='min' type='number' label='update min value' placeholder='Auto' updateField={updateField} />
862
905
  <span style={{color:'red',display:'block'}} >{warningMsg.minMsg}</span>
863
906
  </>
864
907
  }
@@ -926,7 +969,9 @@ const EditorPanel = () => {
926
969
  <>
927
970
  {config.exclusions.keys.length > 0 &&
928
971
  <>
929
- <label><span className="edit-label">Excluded Keys</span></label>
972
+ <fieldset>
973
+ <legend className="edit-label">Excluded Keys</legend>
974
+ </fieldset>
930
975
  <ExclusionsList/>
931
976
  </>
932
977
  }
@@ -989,7 +1034,9 @@ const EditorPanel = () => {
989
1034
  <>
990
1035
  {config.exclusions.keys.length > 0 &&
991
1036
  <>
992
- <label><span className="edit-label">Excluded Keys</span></label>
1037
+ <fieldset>
1038
+ <legend className="edit-label">Excluded Keys</legend>
1039
+ </fieldset>
993
1040
  <ExclusionsList/>
994
1041
  </>
995
1042
  }
@@ -1027,7 +1074,18 @@ const EditorPanel = () => {
1027
1074
  </AccordionItemButton>
1028
1075
  </AccordionItemHeading>
1029
1076
  <AccordionItemPanel>
1030
- <CheckBox value={config.legend.reverseLabelOrder} section="legend" fieldName="reverseLabelOrder" label="Reverse Labels" updateField={updateField}/>
1077
+ <CheckBox value={config.legend.reverseLabelOrder} section="legend" fieldName="reverseLabelOrder" label="Reverse Labels" updateField={updateField}/>
1078
+ {/* <fieldset className="checkbox-group">
1079
+ <CheckBox value={config.legend.dynamicLegend} section="legend" fieldName="dynamicLegend" label="Dynamic Legend" updateField={updateField}/>
1080
+ {config.legend.dynamicLegend && (
1081
+ <>
1082
+ <TextField value={config.legend.dynamicLegendDefaultText} section="legend" fieldName="dynamicLegendDefaultText" label="Dynamic Legend Default Text" updateField={updateField} />
1083
+ <TextField value={config.legend.dynamicLegendItemLimit} type="number" min="0" section="legend" fieldName="dynamicLegendItemLimit" label={'Dynamic Legend Limit'} className="number-narrow" updateField={updateField}/>
1084
+ <TextField value={config.legend.dynamicLegendItemLimitMessage} section="legend" fieldName="dynamicLegendItemLimitMessage" label="Dynamic Legend Item Limit Message" updateField={updateField} />
1085
+ <TextField value={config.legend.dynamicLegendChartMessage} section="legend" fieldName="dynamicLegendChartMessage" label="Dynamic Legend Chart Message" updateField={updateField} />
1086
+ </>
1087
+ )}
1088
+ </fieldset> */}
1031
1089
  <CheckBox value={config.legend.hide} section="legend" fieldName="hide" label="Hide Legend" updateField={updateField} tooltip={
1032
1090
  <Tooltip style={{ textTransform: 'none' }}>
1033
1091
  <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
@@ -1036,9 +1094,14 @@ const EditorPanel = () => {
1036
1094
  </Tooltip.Content>
1037
1095
  </Tooltip>
1038
1096
  }/>
1097
+
1098
+ {(config.visualizationType==='Bar' && config.visualizationSubType ==="regular" && config.runtime.seriesKeys.length===1 )&& (
1099
+ <Select value={config.legend.colorCode} section="legend" fieldName="colorCode" label="Color code by category" initial="Select" updateField={updateField} options={getDataValueOptions(data)}/>
1100
+ )}
1039
1101
  <Select value={config.legend.behavior} section="legend" fieldName="behavior" label="Legend Behavior (When clicked)" updateField={updateField} options={[ 'highlight', 'isolate' ]}/>
1040
1102
  <TextField value={config.legend.label} section="legend" fieldName="label" label="Title" updateField={updateField}/>
1041
1103
  <Select value={config.legend.position} section="legend" fieldName="position" label="Position" updateField={updateField} options={[ 'right', 'left' ]}/>
1104
+ <TextField type='textarea' value={config.legend.description} updateField={updateField} section='legend' fieldName='description' label='Legend Description' />
1042
1105
  </AccordionItemPanel>
1043
1106
  </AccordionItem>
1044
1107
 
@@ -1139,12 +1202,12 @@ const EditorPanel = () => {
1139
1202
 
1140
1203
  {config.isLollipopChart &&
1141
1204
  <>
1142
- <label className="header">
1143
- <span className="edit-label">Lollipop Shape</span>
1205
+ <fieldset className="header">
1206
+ <legend className="edit-label">Lollipop Shape</legend>
1144
1207
  <div onChange={(e) => {
1145
1208
  setLollipopShape(e.target.value)
1146
1209
  }}>
1147
- <label>
1210
+ <label className="radio-label">
1148
1211
  <input
1149
1212
  type="radio"
1150
1213
  name="lollipopShape"
@@ -1153,7 +1216,7 @@ const EditorPanel = () => {
1153
1216
  />
1154
1217
  Circle
1155
1218
  </label>
1156
- <label>
1219
+ <label className="radio-label">
1157
1220
  <input
1158
1221
  type="radio"
1159
1222
  name="lollipopShape"
@@ -1164,7 +1227,7 @@ const EditorPanel = () => {
1164
1227
  </label>
1165
1228
  </div>
1166
1229
 
1167
- </label>
1230
+ </fieldset>
1168
1231
  <Select value={config.lollipopColorStyle ? config.lollipopColorStyle : 'two-tone'} fieldName="lollipopColorStyle" label="Lollipop Color Style" updateField={updateField} options={[ 'regular', 'two-tone' ]}/>
1169
1232
  <Select value={config.lollipopSize ? config.lollipopSize : 'small'} fieldName="lollipopSize" label="Lollipop Size" updateField={updateField} options={[ 'small', 'medium', 'large' ]}/>
1170
1233
  </>
@@ -1172,11 +1235,15 @@ const EditorPanel = () => {
1172
1235
 
1173
1236
  <Select value={config.fontSize} fieldName="fontSize" label="Font Size" updateField={updateField} options={[ 'small', 'medium', 'large' ]}/>
1174
1237
 
1175
- {config.series?.some(series => series.type === 'Bar') &&
1238
+ {config.series?.some(series => series.type === 'Bar' || series.type === 'Paired Bar') &&
1176
1239
  <Select value={config.barHasBorder} fieldName="barHasBorder" label="Bar Borders" updateField={updateField} options={[ 'true', 'false' ]}/>
1177
1240
  }
1178
1241
 
1179
- {((config.series?.some(series => series.type === 'Line') && config.visualizationType === 'Combo') || config.visualizationType === 'Line') &&
1242
+ {/* <CheckBox value={config.animate} fieldName="animate" label="Animate Visualization" updateField={updateField} /> */}
1243
+
1244
+ {/*<CheckBox value={config.animateReplay} fieldName="animateReplay" label="Replay Animation When Filters Are Changed" updateField={updateField} />*/}
1245
+
1246
+ {((config.series?.some(series => series.type === 'Line') && config.visualizationType === 'Combo') || config.visualizationType === 'Line' || config.visualizationType === "Spark Line") &&
1180
1247
  <Select value={config.lineDatapointStyle} fieldName="lineDatapointStyle" label="Line Datapoint Style" updateField={updateField} options={[ 'hidden', 'hover', 'always show' ]}/>
1181
1248
  }
1182
1249
 
@@ -1184,10 +1251,15 @@ const EditorPanel = () => {
1184
1251
  <span className="edit-label">Header Theme</span>
1185
1252
  <ul className="color-palette">
1186
1253
  {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>
1254
+ <button
1255
+ title={palette}
1256
+ key={palette}
1257
+ onClick={(e) => {
1258
+ e.preventDefault();
1259
+ updateConfig({ ...config, theme: palette })
1260
+ }}
1261
+ className={config.theme === palette ? 'selected ' + palette : palette}>
1262
+ </button>
1191
1263
  ))}
1192
1264
  </ul>
1193
1265
  </label>
@@ -1213,13 +1285,19 @@ const EditorPanel = () => {
1213
1285
  }
1214
1286
 
1215
1287
  return (
1216
- <li title={palette} key={palette} onClick={() => {
1217
- updateConfig({ ...config, palette })
1218
- }} className={config.palette === palette ? 'selected' : ''}>
1288
+ <button
1289
+ title={palette}
1290
+ key={palette}
1291
+ onClick={(e) => {
1292
+ e.preventDefault();
1293
+ updateConfig({ ...config, palette })
1294
+ }}
1295
+ className={config.palette === palette ? 'selected' : ''}
1296
+ >
1219
1297
  <span style={colorOne}></span>
1220
1298
  <span style={colorTwo}></span>
1221
1299
  <span style={colorThree}></span>
1222
- </li>
1300
+ </button>
1223
1301
  )
1224
1302
  })}
1225
1303
  </ul>
@@ -1241,22 +1319,25 @@ const EditorPanel = () => {
1241
1319
 
1242
1320
 
1243
1321
  return (
1244
- <li title={palette} key={palette} onClick={() => {
1245
- updateConfig({ ...config, palette })
1246
- }} className={config.palette === palette ? 'selected' : ''}>
1322
+ <button
1323
+ title={palette}
1324
+ key={palette}
1325
+ onClick={(e) => {
1326
+ e.preventDefault();
1327
+ updateConfig({ ...config, palette })
1328
+ }}
1329
+ className={config.palette === palette ? 'selected' : ''}
1330
+ >
1247
1331
  <span style={colorOne}></span>
1248
1332
  <span style={colorTwo}></span>
1249
1333
  <span style={colorThree}></span>
1250
- </li>
1334
+ </button>
1251
1335
  )
1252
1336
  })}
1253
1337
  </ul>
1254
1338
 
1255
1339
  {config.visualizationType !== 'Pie' && (
1256
1340
  <>
1257
- {config.orientation !== 'horizontal' &&
1258
- <CheckBox value={config.labels} fieldName="labels" label="Display label on data" updateField={updateField}/>
1259
- }
1260
1341
  <TextField value={config.dataCutoff} type="number" fieldName="dataCutoff" className="number-narrow" label="Data Cutoff" updateField={updateField} tooltip={
1261
1342
  <Tooltip style={{ textTransform: 'none' }}>
1262
1343
  <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
@@ -1274,13 +1355,15 @@ const EditorPanel = () => {
1274
1355
  <TextField value={config.barThickness} type="number" fieldName="barThickness" label="Bar Thickness" updateField={updateField}/>
1275
1356
  }
1276
1357
 
1277
- {/* <div className="cove-accordion__panel-section">
1278
- <CheckBox value={config.visual?.border} section="visual" fieldName="border" label="Display Border" updateField={updateField} />
1279
- <CheckBox value={config.visual?.borderColorTheme} section="visual" fieldName="borderColorTheme" label="Use Border Color Theme" updateField={updateField} />
1280
- <CheckBox value={config.visual?.accent} section="visual" fieldName="accent" label="Use Accent Style" updateField={updateField} />
1281
- <CheckBox value={config.visual?.background} section="visual" fieldName="background" label="Use Theme Background Color" updateField={updateField} />
1282
- <CheckBox value={config.visual?.hideBackgroundColor} section="visual" fieldName="hideBackgroundColor" label="Hide Background Color" updateField={updateField} />
1283
- </div> */}
1358
+ {config.visualizationType === "Spark Line" &&
1359
+ <div className="cove-accordion__panel-section checkbox-group">
1360
+ <CheckBox value={config.visual?.border} section="visual" fieldName="border" label="Display Border" updateField={updateField} />
1361
+ <CheckBox value={config.visual?.borderColorTheme} section="visual" fieldName="borderColorTheme" label="Use Border Color Theme" updateField={updateField} />
1362
+ <CheckBox value={config.visual?.accent} section="visual" fieldName="accent" label="Use Accent Style" updateField={updateField} />
1363
+ <CheckBox value={config.visual?.background} section="visual" fieldName="background" label="Use Theme Background Color" updateField={updateField} />
1364
+ <CheckBox value={config.visual?.hideBackgroundColor} section="visual" fieldName="hideBackgroundColor" label="Hide Background Color" updateField={updateField} />
1365
+ </div>
1366
+ }
1284
1367
  </AccordionItemPanel>
1285
1368
  </AccordionItem>
1286
1369
 
@@ -1299,10 +1382,31 @@ const EditorPanel = () => {
1299
1382
  </Tooltip.Content>
1300
1383
  </Tooltip>
1301
1384
  }/>
1385
+ <TextField
1386
+ value={config.table.caption}
1387
+ updateField={updateField}
1388
+ section='table'
1389
+ type='textarea'
1390
+ fieldName='caption'
1391
+ label='Data Table Caption'
1392
+ placeholder=' Data table'
1393
+ tooltip={
1394
+ <Tooltip style={{textTransform: 'none'}}>
1395
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1396
+ <Tooltip.Content>
1397
+ <p>Enter a description of the data table to be read by screen readers.</p>
1398
+ </Tooltip.Content>
1399
+ </Tooltip>
1400
+ }
1401
+ />
1402
+ <CheckBox value={config.table.limitHeight} section="table" fieldName="limitHeight" label="Limit Table Height" updateField={updateField}/>
1403
+ {config.table.limitHeight && (
1404
+ <TextField value={config.table.height} section="table" fieldName='height' label='Data Table Height' type="number" min="0" max="500" placeholder='Height(px)' updateField={updateField}/>
1405
+ )}
1302
1406
  <CheckBox value={config.table.expanded} section="table" fieldName="expanded" label="Expanded by Default" updateField={updateField}/>
1303
1407
  <CheckBox value={config.table.download} section="table" fieldName="download" label="Display Download Button" updateField={updateField}/>
1304
1408
  <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}/>
1409
+ {config.visualizationType !== 'Pie' && <TextField value={config.table.indexLabel} section="table" fieldName="indexLabel" label="Index Column Header" updateField={updateField}/>}
1306
1410
  </AccordionItemPanel>
1307
1411
  </AccordionItem>
1308
1412
  </Accordion>