@cdc/core 4.24.5 → 4.24.9-1

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 (109) hide show
  1. package/assets/icon-gear-multi.svg +23 -0
  2. package/components/AdvancedEditor/AdvancedEditor.tsx +93 -0
  3. package/components/AdvancedEditor/advanced-editor-styles.css +3 -0
  4. package/components/AdvancedEditor/index.ts +1 -0
  5. package/components/Alert/components/Alert.styles.css +15 -0
  6. package/components/Alert/components/Alert.tsx +39 -0
  7. package/components/Alert/index.tsx +3 -0
  8. package/components/DataTable/DataTable.tsx +127 -32
  9. package/components/DataTable/DataTableStandAlone.tsx +4 -25
  10. package/components/DataTable/components/DataTableEditorPanel.tsx +4 -4
  11. package/components/DataTable/components/ExpandCollapse.tsx +1 -1
  12. package/components/DataTable/helpers/chartCellMatrix.tsx +6 -12
  13. package/components/DataTable/helpers/getChartCellValue.ts +9 -5
  14. package/components/DataTable/helpers/getDataSeriesColumns.ts +10 -7
  15. package/components/DataTable/helpers/getRowType.ts +6 -0
  16. package/components/DataTable/helpers/mapCellMatrix.tsx +3 -3
  17. package/components/DataTable/types/TableConfig.ts +2 -1
  18. package/components/EditorPanel/ColumnsEditor.tsx +3 -30
  19. package/components/EditorPanel/DataTableEditor.tsx +66 -22
  20. package/components/EditorPanel/FieldSetWrapper.tsx +51 -0
  21. package/components/EditorPanel/FootnotesEditor.tsx +77 -0
  22. package/components/EditorPanel/Inputs.tsx +13 -4
  23. package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +268 -0
  24. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +306 -0
  25. package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +40 -0
  26. package/components/EditorPanel/VizFilterEditor/index.ts +1 -0
  27. package/components/EditorWrapper/EditorWrapper.tsx +3 -4
  28. package/components/EditorWrapper/index.ts +1 -0
  29. package/components/Filters.tsx +520 -0
  30. package/components/Footnotes/Footnotes.tsx +25 -0
  31. package/components/Footnotes/FootnotesStandAlone.tsx +45 -0
  32. package/components/Footnotes/footnotes.css +5 -0
  33. package/components/Footnotes/index.ts +1 -0
  34. package/components/Layout/components/Responsive.tsx +14 -4
  35. package/components/Layout/components/Sidebar/components/Sidebar.tsx +14 -5
  36. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +23 -20
  37. package/components/Layout/components/Visualization/index.tsx +19 -6
  38. package/components/Layout/components/Visualization/visualizations.scss +32 -26
  39. package/components/Layout/styles/editor.scss +0 -8
  40. package/components/Legend/Legend.Gradient.tsx +133 -0
  41. package/components/LegendShape.tsx +28 -0
  42. package/components/MultiSelect/MultiSelect.tsx +41 -11
  43. package/components/MultiSelect/multiselect.styles.css +0 -3
  44. package/components/NestedDropdown/NestedDropdown.tsx +47 -52
  45. package/components/NestedDropdown/nesteddropdown.styles.css +19 -25
  46. package/components/Table/Table.tsx +8 -5
  47. package/components/Table/components/Cell.tsx +2 -2
  48. package/components/Table/components/Row.tsx +25 -7
  49. package/components/_stories/Footnotes.stories.tsx +17 -0
  50. package/components/_stories/Layout.Debug.stories.tsx +91 -0
  51. package/components/_stories/_mocks/bar-chart-suppressed.json +474 -0
  52. package/components/_stories/styles.scss +14 -1
  53. package/components/createBarElement.jsx +4 -4
  54. package/components/inputs/InputSelect.tsx +17 -6
  55. package/components/ui/Icon.tsx +22 -16
  56. package/components/ui/Title/Title.scss +0 -8
  57. package/helpers/DataTransform.ts +2 -2
  58. package/helpers/addValuesToFilters.ts +135 -0
  59. package/helpers/cove/accessibility.ts +17 -4
  60. package/helpers/cove/fontSettings.ts +2 -0
  61. package/helpers/coveUpdateWorker.ts +30 -9
  62. package/helpers/filterVizData.ts +49 -0
  63. package/helpers/formatConfigBeforeSave.ts +110 -0
  64. package/helpers/gatherQueryParams.ts +24 -7
  65. package/helpers/getGradientLegendWidth.ts +15 -0
  66. package/helpers/getTextWidth.ts +18 -0
  67. package/helpers/lineChartHelpers.js +2 -1
  68. package/helpers/pivotData.ts +18 -0
  69. package/helpers/queryStringUtils.ts +29 -0
  70. package/helpers/scaling.ts +7 -0
  71. package/helpers/tests/addValuesToFilters.test.ts +55 -0
  72. package/helpers/tests/filterVizData.test.ts +31 -0
  73. package/helpers/tests/gatherQueryParams.test.ts +22 -0
  74. package/helpers/tests/invertValue.test.ts +35 -0
  75. package/helpers/tests/updateFieldFactory.test.ts +1 -0
  76. package/helpers/updateFieldFactory.ts +1 -1
  77. package/helpers/updatePaletteNames.ts +19 -0
  78. package/helpers/{useDataVizClasses.js → useDataVizClasses.ts} +3 -2
  79. package/helpers/ver/4.24.5.ts +3 -3
  80. package/helpers/ver/4.24.7.ts +123 -0
  81. package/helpers/ver/4.24.9.ts +63 -0
  82. package/helpers/ver/tests/4.24.9.test.ts +22 -0
  83. package/helpers/ver/versionNeedsUpdate.ts +9 -0
  84. package/package.json +6 -4
  85. package/styles/_button-section.scss +7 -2
  86. package/styles/_data-table.scss +0 -1
  87. package/styles/_global.scss +6 -2
  88. package/styles/base.scss +4 -0
  89. package/styles/filters.scss +4 -0
  90. package/styles/v2/themes/_color-definitions.scss +1 -0
  91. package/types/Annotation.ts +46 -0
  92. package/types/Axis.ts +3 -2
  93. package/types/ConfigureData.ts +1 -1
  94. package/types/Dimensions.ts +1 -0
  95. package/types/Footnotes.ts +17 -0
  96. package/types/General.ts +5 -0
  97. package/types/Runtime.ts +2 -7
  98. package/types/Table.ts +6 -0
  99. package/types/Visualization.ts +31 -9
  100. package/types/VizFilter.ts +39 -7
  101. package/components/AdvancedEditor.jsx +0 -74
  102. package/components/EditorPanel/VizFilterEditor.tsx +0 -234
  103. package/components/Filters.jsx +0 -461
  104. package/components/LegendCircle.jsx +0 -17
  105. package/helpers/queryStringUtils.js +0 -26
  106. package/helpers/updatePaletteNames.js +0 -16
  107. package/types/BaseVisualizationType.ts +0 -1
  108. /package/components/{Waiting.jsx → Waiting.tsx} +0 -0
  109. /package/helpers/ver/{4.23.4.ts → 4.24.4.ts} +0 -0
@@ -11,7 +11,6 @@
11
11
 
12
12
  .cdc-editor .configure .cdc-open-viz-module:not(.type-dashboard) .editor-panel__toggle {
13
13
  position: absolute;
14
- top: 10px;
15
14
  }
16
15
 
17
16
  .cdc-editor .configure .cdc-open-viz-module:not(.type-dashboard) .sidebar {
@@ -25,24 +24,14 @@
25
24
  }
26
25
 
27
26
  .sidebar {
28
- position: fixed;
29
- height: 100vh;
27
+ position: static;
28
+ height: 100%; // take up the whole container
30
29
  top: 0;
31
30
  max-width: 350px;
32
31
  width: 350px;
33
32
  background-color: var(--white);
34
33
  grid-area: panel;
35
34
 
36
- .editor-toggle {
37
- position: fixed !important;
38
- top: 10px !important;
39
- }
40
-
41
- .editor-panel {
42
- position: fixed !important;
43
- top: 0 !important;
44
- }
45
-
46
35
  &.editor-panel--hidden &.hidden {
47
36
  display: none;
48
37
  }
@@ -601,7 +590,7 @@
601
590
  /* clicking anywhere will focus the input */
602
591
  cursor: text;
603
592
 
604
- span {
593
+ span:not(.cove-tooltip, .cove-icon) {
605
594
  display: inline;
606
595
  }
607
596
  }
@@ -723,6 +712,10 @@
723
712
  margin-right: 5px;
724
713
  }
725
714
 
715
+ .cove-tooltip {
716
+ position: relative;
717
+ }
718
+
726
719
  // tooltips
727
720
  .cove-label + .cove-tooltip {
728
721
  top: 1px;
@@ -731,9 +724,9 @@
731
724
  }
732
725
 
733
726
  .cove-accordion__button .cove-tooltip {
734
- display: inline-flex;
735
- right: 1.5rem;
736
- line-height: inherit;
727
+ // display: inline-flex;
728
+ // right: 1.5rem;
729
+ // line-height: inherit;
737
730
  }
738
731
 
739
732
  .cove-list-group__item .cove-tooltip {
@@ -854,6 +847,10 @@
854
847
  border-bottom: #565656 3px solid;
855
848
  z-index: 3;
856
849
  margin: 0;
850
+
851
+ &.collapsed {
852
+ display: none;
853
+ }
857
854
  }
858
855
 
859
856
  &.type-dashboard {
@@ -862,20 +859,26 @@
862
859
  }
863
860
  }
864
861
 
862
+ .editor-panel__toggle-wrapper {
863
+ position: absolute;
864
+ top: 0;
865
+ }
866
+
865
867
  .editor-panel__toggle {
866
868
  background: #f2f2f2;
867
869
  border-radius: 60px;
868
870
  color: #000;
869
871
  font-size: 1em;
870
872
  border: 0;
871
- position: fixed;
873
+ position: absolute;
872
874
  z-index: 100;
873
875
  transition: 0.1s background;
874
876
  cursor: pointer;
875
877
  width: 25px;
876
878
  height: 25px;
877
- left: 307px;
878
- top: 10px;
879
+ right: 10px;
880
+ top: 50%;
881
+ transform: translateY(-50%);
879
882
  box-shadow: rgba(0, 0, 0, 0.5) 0 1px 2px;
880
883
 
881
884
  &:before {
@@ -1,25 +1,31 @@
1
1
  // main visualization wrapper
2
2
  import { ChartConfig } from '@cdc/chart/src/types/ChartConfig'
3
- import React, { forwardRef, useRef } from 'react'
3
+ import React, { forwardRef } from 'react'
4
4
  import { Config as DataBiteConfig } from '@cdc/data-bite/src/types/Config'
5
5
  import './visualizations.scss'
6
6
  import { Config as WaffleChartConfig } from '@cdc/waffle-chart/src/types/Config'
7
7
  import { MarkupIncludeConfig } from '@cdc/core/types/MarkupInclude'
8
+ import { DashboardFilters } from '@cdc/dashboard/src/types/DashboardFilters'
8
9
 
9
10
  type VisualizationWrapper = {
10
11
  children: React.ReactNode
11
- config: ChartConfig | DataBiteConfig | WaffleChartConfig | MarkupIncludeConfig
12
- currentViewport: string
13
- imageId: string
12
+ config: ChartConfig | DataBiteConfig | WaffleChartConfig | MarkupIncludeConfig | DashboardFilters
13
+ currentViewport?: string
14
+ imageId?: string
14
15
  isEditor: boolean
15
- showEditorPanel: boolean
16
+ showEditorPanel?: boolean
16
17
  }
17
18
 
18
19
  const Visualization: React.FC<VisualizationWrapper> = forwardRef((props, ref) => {
19
- const { config = {}, isEditor = false, currentViewport = 'lg', imageId = '', showEditorPanel = true } = props
20
+ const { config = {}, isEditor = false, currentViewport = 'lg', imageId = '', showEditorPanel = true, className } = props
20
21
 
21
22
  const getWrappingClasses = () => {
22
23
  let classes = ['cdc-open-viz-module', `${currentViewport}`, `font-${config?.fontSize}`, `${config?.theme}`]
24
+
25
+ if (className) {
26
+ classes.push(className)
27
+ }
28
+
23
29
  isEditor && classes.push('spacing-wrapper')
24
30
  isEditor && classes.push('isEditor')
25
31
 
@@ -33,6 +39,12 @@ const Visualization: React.FC<VisualizationWrapper> = forwardRef((props, ref) =>
33
39
  classes.push('editor-panel--hidden')
34
40
  }
35
41
 
42
+ if (config.type === 'filtered-text') {
43
+ classes.push('type-filtered-text')
44
+ classes = classes.filter(item => item !== 'cove-component__content')
45
+ return classes
46
+ }
47
+
36
48
  if (config.type === 'chart') {
37
49
  classes.push('type-chart')
38
50
  config?.visualizationType === 'Spark Line' && classes.push(`type-sparkline`)
@@ -40,6 +52,7 @@ const Visualization: React.FC<VisualizationWrapper> = forwardRef((props, ref) =>
40
52
  }
41
53
  if (config.type === 'map') {
42
54
  classes.push(`type-map`)
55
+ if (config?.runtime?.editorErrorMessage.length !== 0) classes.push('type-map--has-error')
43
56
  }
44
57
 
45
58
  if (config.type === 'data-bite') {
@@ -1,33 +1,39 @@
1
- .cdc-open-viz-module.isEditor {
2
- overflow: auto;
3
- display: grid;
4
- transition: grid-template-columns 400ms ease-in-out;
5
-
6
- .editor-panel__toggle {
7
- transition: left 400ms ease-in-out;
1
+ .cdc-open-viz-module {
2
+ .cdc-chart-inner-container .cove-component__content {
3
+ padding: 25px 15px !important;
8
4
  }
5
+ &.isEditor {
6
+ overflow: auto;
7
+ display: grid;
8
+ transition: grid-template-columns 400ms ease-in-out;
9
+ min-height: 100vh;
9
10
 
10
- .sidebar {
11
- transition: left 400ms ease-in-out;
12
- }
11
+ .editor-panel__toggle {
12
+ transition: left 400ms ease-in-out;
13
+ }
13
14
 
14
- &.editor-panel--visible {
15
- grid-template-areas: 'panel content';
16
- grid-template-columns: 350px calc(100% - 350px);
17
- }
15
+ .sidebar {
16
+ transition: left 400ms ease-in-out;
17
+ }
18
18
 
19
- &.editor-panel--hidden {
20
- grid-template-areas: 'panel content';
21
- grid-template-columns: 0px 100%;
22
- }
19
+ &.editor-panel--visible {
20
+ grid-template-areas: 'panel content';
21
+ grid-template-columns: 350px calc(100% - 350px);
22
+ overflow: hidden;
23
+ }
24
+
25
+ &.editor-panel--hidden {
26
+ grid-template-areas: 'panel content';
27
+ grid-template-columns: 0px 100%;
28
+ }
23
29
 
24
- .cove-editor__content,
25
- .cove-component__content {
26
- grid-area: content;
27
- position: relative;
28
- left: 0;
29
- width: 100% !important;
30
- padding-left: 0px !important;
31
- grid-area: content;
30
+ .cove-editor__content,
31
+ .cove-component__content {
32
+ grid-area: content;
33
+ position: relative;
34
+ left: 0;
35
+ width: 100% !important;
36
+ grid-area: content;
37
+ }
32
38
  }
33
39
  }
@@ -4,14 +4,6 @@ $mediumGray: #e6e6e6;
4
4
 
5
5
  @import 'editor-grid-view.scss';
6
6
 
7
- .cdc-open-viz-module.isEditor {
8
- background: $mediumGray !important;
9
- }
10
-
11
- // .cdc-open-viz-module .form-container {
12
- // height: 100%;
13
- // }
14
-
15
7
  .cove-editor {
16
8
  display: grid;
17
9
  grid-template-areas: 'panel content';
@@ -0,0 +1,133 @@
1
+ import { Group } from '@visx/group'
2
+ import { Text } from '@visx/text'
3
+ import { type ViewportSize, type MapConfig } from '@cdc/map/src/types/MapConfig'
4
+ import { type ChartConfig } from '@cdc/chart/src/types/ChartConfig'
5
+ import { getGradientLegendWidth } from '@cdc/core/helpers/getGradientLegendWidth'
6
+ import { DimensionsType } from '../../types/Dimensions'
7
+
8
+ type CombinedConfig = MapConfig | ChartConfig
9
+
10
+ interface GradientProps {
11
+ labels: string[]
12
+ colors: string[]
13
+ config: CombinedConfig
14
+ dimensions: DimensionsType
15
+ currentViewport: ViewportSize
16
+ getTextWidth: (text: string, font: string) => string
17
+ }
18
+
19
+ const LegendGradient = ({
20
+ labels,
21
+ colors,
22
+ config,
23
+ dimensions,
24
+ currentViewport,
25
+ getTextWidth
26
+ }: GradientProps): JSX.Element => {
27
+ let [width] = dimensions
28
+
29
+ const legendWidth = getGradientLegendWidth(width, currentViewport)
30
+ const uniqueID = `${config.uid}-${Date.now()}`
31
+
32
+ const numTicks = colors?.length
33
+
34
+ const longestLabel = labels && labels.length > 0 ? labels.reduce((a, b) => (a.length > b.length ? a : b)) : ''
35
+ const boxHeight = 20
36
+ let height = 50
37
+ const margin = 1
38
+
39
+ // configure tick witch and angle
40
+ const textWidth = getTextWidth(longestLabel, `normal 14px sans-serif`)
41
+ const rotationAngle = Number(config.legend.tickRotation) || 0
42
+ // Convert the angle from degrees to radians
43
+ const angleInRadians = rotationAngle * (Math.PI / 180)
44
+ const newHeight = height + Number(textWidth) * Math.sin(angleInRadians)
45
+
46
+ // configre gradient colors
47
+ const stops = colors.map((color, index) => {
48
+ const offset = (index / (colors.length - 1)) * 100
49
+ return <stop key={index} offset={`${offset}%`} style={{ stopColor: color, stopOpacity: 1 }} />
50
+ })
51
+
52
+ // render ticks and labels
53
+ const ticks = labels.map((key, index) => {
54
+ const segmentWidth = legendWidth / numTicks
55
+ const xPositionX = index * segmentWidth + segmentWidth
56
+ const textAnchor = rotationAngle ? 'end' : 'middle'
57
+ const verticalAnchor = rotationAngle ? 'middle' : 'start'
58
+
59
+ return (
60
+ <Group top={margin}>
61
+ <line x1={xPositionX} x2={xPositionX} y1={30} y2={boxHeight} stroke='black' />
62
+ <Text
63
+ angle={-config.legend.tickRotation}
64
+ x={xPositionX}
65
+ y={boxHeight}
66
+ dy={10}
67
+ dx={-segmentWidth / 2}
68
+ fontSize='14'
69
+ textAnchor={textAnchor}
70
+ verticalAnchor={verticalAnchor}
71
+ >
72
+ {key}
73
+ </Text>
74
+ </Group>
75
+ )
76
+ })
77
+ if ((config.type === 'map' && config.legend.position === 'side') || !config.legend.position) {
78
+ return
79
+ }
80
+ if (
81
+ config.type === 'chart' &&
82
+ (config.legend.position === 'left' || config.legend.position === 'right' || !config.legend.position)
83
+ ) {
84
+ return
85
+ }
86
+
87
+ if (config.legend.style === 'gradient') {
88
+ return (
89
+ <svg style={{ overflow: 'visible', width: '100%', marginTop: 10 }} height={newHeight}>
90
+ {/* background border*/}
91
+ <rect
92
+ x={0}
93
+ y={0}
94
+ width={legendWidth + margin * 2}
95
+ height={boxHeight + margin * 2}
96
+ fill='#d3d3d3'
97
+ strokeWidth='0.5'
98
+ />
99
+ {/* Define the gradient */}
100
+ <linearGradient id={`gradient-smooth-${uniqueID}`} x1='0%' y1='0%' x2='100%' y2='0%'>
101
+ {stops}
102
+ </linearGradient>
103
+
104
+ {config.legend.subStyle === 'smooth' && (
105
+ <rect x={1} y={1} width={legendWidth} height={boxHeight} fill={`url(#gradient-smooth-${uniqueID})`} />
106
+ )}
107
+
108
+ {config.legend.subStyle === 'linear blocks' &&
109
+ colors.map((color, index) => {
110
+ const segmentWidth = legendWidth / numTicks
111
+ const xPosition = index * segmentWidth
112
+ return (
113
+ <Group>
114
+ <rect
115
+ key={index}
116
+ x={xPosition}
117
+ y={0}
118
+ width={segmentWidth}
119
+ height={boxHeight}
120
+ fill={color}
121
+ stroke='white'
122
+ strokeWidth='0'
123
+ />
124
+ </Group>
125
+ )
126
+ })}
127
+ {/* Ticks and labels */}
128
+ <g>{ticks}</g>
129
+ </svg>
130
+ )
131
+ }
132
+ }
133
+ export default LegendGradient
@@ -0,0 +1,28 @@
1
+ import React from 'react'
2
+
3
+ interface LegendShapeProps {
4
+ fill: string
5
+ borderColor?: string
6
+ display?: 'inline-block' | 'block' | 'inline'
7
+ shape?: 'circle' | 'square'
8
+ }
9
+
10
+ const LegendShape: React.FC<LegendShapeProps> = props => {
11
+ const { fill, borderColor, display = 'inline-block', shape = 'circle' } = props
12
+ const dimensions = { width: '1em', height: '1em' }
13
+ const marginRight = ['circle', 'square'].includes(shape) ? '5px' : '0'
14
+ const styles = {
15
+ marginRight: marginRight,
16
+ borderRadius: shape === 'circle' ? '50%' : '0px',
17
+ verticalAlign: 'middle',
18
+ display: display,
19
+ height: dimensions.height,
20
+ width: dimensions.width,
21
+ border: borderColor ? `${borderColor} 1px solid` : 'rgba(0,0,0,.3) 1px solid',
22
+ backgroundColor: fill
23
+ }
24
+
25
+ return <span className='legend-item' style={styles} />
26
+ }
27
+
28
+ export default LegendShape
@@ -6,7 +6,7 @@ import './multiselect.styles.css'
6
6
  import { UpdateFieldFunc } from '../../types/UpdateFieldFunc'
7
7
 
8
8
  interface Option {
9
- value: string
9
+ value: string | number
10
10
  label: string
11
11
  }
12
12
 
@@ -17,12 +17,13 @@ interface MultiSelectProps {
17
17
  options: Option[]
18
18
  updateField: UpdateFieldFunc<string[]>
19
19
  label?: string
20
- selected?: string[]
20
+ selected?: (string | number)[]
21
21
  limit?: number
22
+ tooltip?: React.ReactNode
22
23
  }
23
24
 
24
- const MultiSelect: React.FC<MultiSelectProps> = ({ section = null, subsection = null, fieldName, label, options, updateField, selected, limit }) => {
25
- const preselectedItems = options.filter(opt => selected?.includes(opt.value)).slice(0, limit)
25
+ const MultiSelect: React.FC<MultiSelectProps> = ({ section = null, subsection = null, fieldName, label, options, updateField, selected = [], limit, tooltip }) => {
26
+ const preselectedItems = options.filter(opt => selected.includes(opt.value)).slice(0, limit)
26
27
  const [selectedItems, setSelectedItems] = useState<Option[]>(preselectedItems)
27
28
  const [expanded, setExpanded] = useState(false)
28
29
  const multiSelectRef = useRef(null)
@@ -68,22 +69,41 @@ const MultiSelect: React.FC<MultiSelectProps> = ({ section = null, subsection =
68
69
  return (
69
70
  <div ref={multiSelectRef} className='cove-multiselect'>
70
71
  {label && (
71
- <label id={multiID} className='cove-input__label'>
72
+ <span id={multiID} className='edit-label column-heading'>
72
73
  {label}
73
- </label>
74
+ </span>
74
75
  )}
75
76
 
77
+ {tooltip && tooltip}
78
+
76
79
  <div className='wrapper'>
77
80
  <div className='selected'>
78
81
  {selectedItems.map(item => (
79
- <div key={item.value} aria-labelledby={label ? multiID : undefined} role='button' onClick={() => handleItemRemove(item)} onKeyUp={e => handleItemRemove(item, e)}>
82
+ <div key={item.value} aria-labelledby={label ? multiID : undefined}>
80
83
  {item.label}
81
- <button aria-label='Remove' onClick={() => handleItemRemove(item)}>
84
+ <button
85
+ aria-label='Remove'
86
+ onClick={e => {
87
+ e.preventDefault()
88
+ handleItemRemove(item)
89
+ }}
90
+ onKeyUp={e => {
91
+ handleItemRemove(item, e)
92
+ }}
93
+ >
82
94
  x
83
95
  </button>
84
96
  </div>
85
97
  ))}
86
- <button aria-label={expanded ? 'Collapse' : 'Expand'} aria-labelledby={label ? multiID : undefined} className='expand' onClick={() => setExpanded(!expanded)}>
98
+ <button
99
+ aria-label={expanded ? 'Collapse' : 'Expand'}
100
+ aria-labelledby={label ? multiID : undefined}
101
+ className='expand'
102
+ onClick={e => {
103
+ e.preventDefault()
104
+ setExpanded(!expanded)
105
+ }}
106
+ >
87
107
  <Icon display={expanded ? 'caretDown' : 'caretUp'} style={{ cursor: 'pointer' }} />
88
108
  </button>
89
109
  </div>
@@ -98,11 +118,21 @@ const MultiSelect: React.FC<MultiSelectProps> = ({ section = null, subsection =
98
118
  </Tooltip>
99
119
  )}
100
120
  </div>
101
- <ul className={'dropdown' + (expanded ? '' : ' hide')}>
121
+ <ul className={'dropdown' + (expanded ? '' : ' d-none')}>
102
122
  {options
103
123
  .filter(option => !selectedItems.find(item => item.value === option.value))
104
124
  .map(option => (
105
- <li className='cove-multiselect-li' key={option.value} role='option' tabIndex={0} onClick={() => handleItemSelect(option)} onKeyUp={e => handleItemSelect(option, e)}>
125
+ <li
126
+ className='cove-multiselect-li'
127
+ key={option.value}
128
+ role='option'
129
+ tabIndex={0}
130
+ onClick={e => {
131
+ e.preventDefault()
132
+ handleItemSelect(option, e)
133
+ }}
134
+ onKeyUp={e => handleItemSelect(option, e)}
135
+ >
106
136
  {option.label}
107
137
  </li>
108
138
  ))}
@@ -51,9 +51,6 @@
51
51
  overflow-y: scroll;
52
52
  min-width: 200px;
53
53
  z-index: 4;
54
- &.hide {
55
- display: none;
56
- }
57
54
 
58
55
  :is(li) {
59
56
  cursor: pointer;