@cdc/core 4.24.5 → 4.24.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 (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 +95 -0
  64. package/helpers/gatherQueryParams.ts +14 -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/invertValue.test.ts +35 -0
  74. package/helpers/tests/updateFieldFactory.test.ts +1 -0
  75. package/helpers/updateFieldFactory.ts +1 -1
  76. package/helpers/updatePaletteNames.ts +19 -0
  77. package/helpers/{useDataVizClasses.js → useDataVizClasses.ts} +3 -2
  78. package/helpers/ver/4.24.5.ts +3 -3
  79. package/helpers/ver/4.24.7.ts +123 -0
  80. package/helpers/ver/4.24.9.ts +63 -0
  81. package/helpers/ver/tests/4.24.9.test.ts +22 -0
  82. package/helpers/ver/versionNeedsUpdate.ts +9 -0
  83. package/package.json +6 -4
  84. package/styles/_button-section.scss +7 -2
  85. package/styles/_data-table.scss +0 -1
  86. package/styles/_global.scss +6 -2
  87. package/styles/base.scss +4 -0
  88. package/styles/filters.scss +4 -0
  89. package/styles/v2/themes/_color-definitions.scss +1 -0
  90. package/types/Annotation.ts +46 -0
  91. package/types/Axis.ts +3 -2
  92. package/types/ConfigureData.ts +1 -1
  93. package/types/Dimensions.ts +1 -0
  94. package/types/Footnotes.ts +17 -0
  95. package/types/General.ts +5 -0
  96. package/types/Runtime.ts +2 -7
  97. package/types/Table.ts +6 -0
  98. package/types/Visualization.ts +31 -9
  99. package/types/VizFilter.ts +39 -7
  100. package/LICENSE +0 -201
  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
@@ -0,0 +1,268 @@
1
+ import _ from 'lodash'
2
+ import { SubGrouping, VizFilter, OrderBy } from '../../../types/VizFilter'
3
+ import { filterOrderOptions, handleSorting } from '../../Filters'
4
+ import FilterOrder from './components/FilterOrder'
5
+ import { Visualization } from '../../../types/Visualization'
6
+
7
+ type NestedDropdownEditorProps = {
8
+ config: Visualization
9
+ dataColumns: string[]
10
+ filterIndex: number
11
+ handleNameChange: Function
12
+ rawData: Object[]
13
+ updateField: Function
14
+ updateFilterStyle: Function
15
+ handleGroupingCustomOrder: (index1: number, index2: number) => void
16
+ }
17
+
18
+ const NestedDropdownEditor: React.FC<NestedDropdownEditorProps> = ({
19
+ config,
20
+ dataColumns,
21
+ handleGroupingCustomOrder,
22
+ handleNameChange: handleGroupColumnNameChange,
23
+ filterIndex,
24
+ rawData,
25
+ updateField
26
+ }) => {
27
+ const filter = config.filters[filterIndex]
28
+ const subGrouping = filter?.subGrouping
29
+ const listOfUsedColumnNames: string[] = []
30
+
31
+ config.filters.forEach((filter: VizFilter, index) => {
32
+ if (filterIndex === index) return
33
+ listOfUsedColumnNames.push(filter.columnName)
34
+ if (subGrouping?.columnName) listOfUsedColumnNames.push(subGrouping.columnName)
35
+ })
36
+
37
+ const updateGroupingFilterProp = (prop, value) => {
38
+ updateField('filters', filterIndex, prop, value)
39
+ }
40
+
41
+ const handleGroupingOrderBy = (order: OrderBy) => {
42
+ const groupSortObject = {
43
+ values: _.cloneDeep(filter.values),
44
+ order
45
+ }
46
+ const newOrderedValues = handleSorting(groupSortObject).values
47
+
48
+ const newAllFilters = _.cloneDeep(config.filters)
49
+ newAllFilters[filterIndex] = { ...filter, values: newOrderedValues, order }
50
+ if (order === 'cust') {
51
+ newAllFilters[filterIndex].orderedValues = newOrderedValues
52
+ } else {
53
+ delete newAllFilters[filterIndex].orderedValues
54
+ }
55
+ updateField(null, null, 'filters', newAllFilters)
56
+ }
57
+
58
+ const updateSubGroupingFilterProperty = (newSubGrouping: SubGrouping) => {
59
+ updateField('filters', filterIndex, 'subGrouping', newSubGrouping)
60
+ }
61
+
62
+ const handleSubGroupColumnNameChange = value => {
63
+ const filterGroups = filter.orderedValues?.length ? filter.orderedValues : filter.values
64
+
65
+ const valuesLookup = filterGroups.reduce((acc, groupName) => {
66
+ const values: string[] = _.uniq(
67
+ rawData
68
+ .map(d => {
69
+ return d[filter.columnName] === groupName ? d[value] : ''
70
+ })
71
+ .filter(value => value !== '')
72
+ ).sort()
73
+
74
+ acc[groupName] = {
75
+ values // add temp values when column changes
76
+ }
77
+ return acc
78
+ }, {})
79
+ const newSubGrouping: SubGrouping = {
80
+ ...subGrouping,
81
+ columnName: value,
82
+ valuesLookup
83
+ }
84
+
85
+ updateSubGroupingFilterProperty(newSubGrouping)
86
+ }
87
+
88
+ const handleSubGroupingOrderBy = (order: OrderBy) => {
89
+ const newValuesLookup = Object.keys(subGrouping.valuesLookup).reduce((acc, groupName) => {
90
+ const subGroup = subGrouping.valuesLookup[groupName]
91
+
92
+ const { values } = handleSorting({ values: subGroup.values, order })
93
+ acc[groupName] = {
94
+ values
95
+ }
96
+ if (order === 'cust') {
97
+ acc[groupName].orderedValues = values
98
+ } else {
99
+ delete acc[groupName].orderedValues
100
+ }
101
+ return acc
102
+ }, {})
103
+ const newSortedSubGrouping = { ...subGrouping, order, valuesLookup: newValuesLookup }
104
+ updateSubGroupingFilterProperty(newSortedSubGrouping)
105
+ }
106
+
107
+ const handleSubGroupingCustomOrder = (
108
+ currentIndex,
109
+ newIndex,
110
+ subGroupingFitlerOrder: string[],
111
+ groupName: string
112
+ ) => {
113
+ const updatedGroupOrderedValues = _.cloneDeep(subGroupingFitlerOrder)
114
+ const [movedItem] = updatedGroupOrderedValues.splice(currentIndex, 1)
115
+ updatedGroupOrderedValues.splice(newIndex, 0, movedItem)
116
+ const newSubGrouping = _.cloneDeep(subGrouping)
117
+ newSubGrouping.valuesLookup[groupName].values = updatedGroupOrderedValues
118
+ newSubGrouping.valuesLookup[groupName].orderedValues = updatedGroupOrderedValues
119
+ updateSubGroupingFilterProperty({ ...newSubGrouping, order: 'cust' })
120
+ }
121
+
122
+ const columnNameOptions = dataColumns.filter(columnName => !listOfUsedColumnNames.includes(columnName))
123
+
124
+ return (
125
+ <div className='nesteddropdown-editor'>
126
+ <label>
127
+ <span className='edit-label column-heading mt-2'>Label</span>
128
+ <input
129
+ type='text'
130
+ value={filter.label}
131
+ onChange={e => {
132
+ updateGroupingFilterProp('label', e.target.value)
133
+ }}
134
+ />
135
+ </label>
136
+
137
+ <label>
138
+ <div className='edit-label column-heading mt-2'>
139
+ Filter Grouping
140
+ <span></span>
141
+ </div>
142
+ <select value={filter.columnName} onChange={e => handleGroupColumnNameChange(e.target.value)}>
143
+ <option value=''>- Select Option -</option>
144
+ {columnNameOptions.map((option, index) => (
145
+ <option value={option} key={index}>
146
+ {option}
147
+ </option>
148
+ ))}
149
+ </select>
150
+ </label>
151
+ <label>
152
+ <div className='edit-label column-heading mt-2'>
153
+ Filter SubGrouping
154
+ <span></span>
155
+ </div>
156
+ <select
157
+ value={subGrouping?.columnName ?? ''}
158
+ onChange={e => {
159
+ handleSubGroupColumnNameChange(e.target.value)
160
+ }}
161
+ >
162
+ <option value=''>- Select Option -</option>
163
+ {columnNameOptions.map((option, index) => {
164
+ if (option !== filter.columnName) {
165
+ return (
166
+ <option value={option} key={index}>
167
+ {option}
168
+ </option>
169
+ )
170
+ }
171
+ })}
172
+ </select>
173
+ </label>
174
+
175
+ <label>
176
+ <input
177
+ type='checkbox'
178
+ checked={!!filter.setByQueryParameter}
179
+ aria-label='Create query parameters'
180
+ onChange={e => {
181
+ updateGroupingFilterProp('setByQueryParameter', filter.columnName)
182
+ updateSubGroupingFilterProperty({ ...subGrouping, setByQueryParameter: subGrouping.columnName })
183
+ }}
184
+ />
185
+ <span> Create query parameters</span>
186
+ {!!filter.setByQueryParameter && (
187
+ <>
188
+ <span className='edit-label column-heading mt-2'>
189
+ Grouping: Default Value Set By Query String Parameter
190
+ </span>
191
+ <input
192
+ type='text'
193
+ value={filter.setByQueryParameter}
194
+ onChange={e => {
195
+ updateGroupingFilterProp('setByQueryParameter', e.target.value)
196
+ }}
197
+ />
198
+ <span className='edit-label column-heading mt-2'>
199
+ SubGrouping: Default Value Set By Query String Parameter
200
+ </span>
201
+ <input
202
+ type='text'
203
+ value={subGrouping.setByQueryParameter}
204
+ onChange={e => {
205
+ const setByQueryParameter = e.target.value
206
+ updateSubGroupingFilterProperty({ ...subGrouping, setByQueryParameter })
207
+ }}
208
+ />
209
+ </>
210
+ )}
211
+ </label>
212
+
213
+ <label className='mt-2'>
214
+ <div className='edit-label column-heading float-right'>{filter.columnName} </div>
215
+ <span className={'edit-filterOrder column-heading '}>Group Order</span>
216
+ <select value={filter.order} onChange={e => handleGroupingOrderBy(e.target.value as OrderBy)}>
217
+ {filterOrderOptions.map((option, index) => {
218
+ return (
219
+ <option value={option.value} key={`filter-${option.label}-${index}`}>
220
+ {option.label}
221
+ </option>
222
+ )
223
+ })}
224
+ </select>
225
+ {filter.order === 'cust' && (
226
+ <FilterOrder orderedValues={filter.orderedValues} handleFilterOrder={handleGroupingCustomOrder} />
227
+ )}
228
+ </label>
229
+
230
+ {subGrouping?.columnName && (
231
+ <label className='mt-2'>
232
+ <span className={'edit-filterOrder column-heading'}>SubGrouping Order</span>
233
+ <div className='edit-label column-heading float-right'>{subGrouping.columnName} </div>
234
+ <select
235
+ value={subGrouping.order ? subGrouping.order : 'asc'}
236
+ onChange={e => handleSubGroupingOrderBy(e.target.value as OrderBy)}
237
+ >
238
+ {filterOrderOptions.map((option, index) => {
239
+ return (
240
+ <option value={option.value} key={`filter-${index}`}>
241
+ {option.label}
242
+ </option>
243
+ )
244
+ })}
245
+ </select>
246
+ {subGrouping?.order === 'cust' &&
247
+ filter.values.map((groupName, i) => {
248
+ const orderedSubGroupValues = subGrouping.valuesLookup[groupName].orderedValues
249
+ return (
250
+ <div key={`group-subgroup-values-${groupName}-${i}`}>
251
+ <span className='font-weight-bold'>{groupName}</span>
252
+ <FilterOrder
253
+ key={`subgroup-values-${groupName}-${i}`}
254
+ orderedValues={orderedSubGroupValues}
255
+ handleFilterOrder={(sourceIndex, destinationIndex) => {
256
+ handleSubGroupingCustomOrder(sourceIndex, destinationIndex, orderedSubGroupValues, groupName)
257
+ }}
258
+ />
259
+ </div>
260
+ )
261
+ })}
262
+ </label>
263
+ )}
264
+ </div>
265
+ )
266
+ }
267
+
268
+ export default NestedDropdownEditor
@@ -0,0 +1,306 @@
1
+ import { Select, TextField } from '../Inputs'
2
+ import Tooltip from '../../ui/Tooltip'
3
+ import Icon from '../../ui/Icon'
4
+ import { Visualization } from '../../../types/Visualization'
5
+ import { UpdateFieldFunc } from '../../../types/UpdateFieldFunc'
6
+ import _ from 'lodash'
7
+ import { MultiSelectFilter, VizFilter, VizFilterStyle } from '../../../types/VizFilter'
8
+ import { filterStyleOptions, handleSorting, filterOrderOptions } from '../../Filters'
9
+ import FieldSetWrapper from '../FieldSetWrapper'
10
+
11
+ import FilterOrder from './components/FilterOrder'
12
+ import { useMemo, useState } from 'react'
13
+ import MultiSelect from '../../MultiSelect'
14
+ import NestedDropdownEditor from './NestedDropdownEditor'
15
+
16
+ type VizFilterProps = {
17
+ config: Visualization
18
+ updateField: UpdateFieldFunc<string | VizFilter[] | VizFilter>
19
+ rawData: Object[]
20
+ }
21
+
22
+ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawData }) => {
23
+ const openControls = useState({})
24
+ const dataColumns = useMemo(() => {
25
+ return _.uniq(_.flatten(rawData?.map(row => Object.keys(row))))
26
+ }, [rawData])
27
+
28
+ const removeFilter = index => {
29
+ let filters = _.cloneDeep(config.filters)
30
+
31
+ filters.splice(index, 1)
32
+
33
+ updateField(null, null, 'filters', filters)
34
+ }
35
+
36
+ const updateFilterProp = (prop, filterIndex, value) => {
37
+ updateField('filters', filterIndex, prop, value)
38
+ }
39
+
40
+ const updateFilterStyle = (index, style: VizFilterStyle) => {
41
+ const filters = _.cloneDeep(config.filters)
42
+ const currentFilter = { ...filters[index], orderedValues: filters[index].values }
43
+ currentFilter.filterStyle = style
44
+ if (style === 'multi-select') {
45
+ currentFilter.active = Array.isArray(currentFilter.active) ? currentFilter.active : [currentFilter.active]
46
+ } else if (Array.isArray(currentFilter.active)) {
47
+ currentFilter.active = currentFilter.active[0]
48
+ }
49
+ if (style === 'nested-dropdown') {
50
+ currentFilter.showDropdown = true
51
+ }
52
+ filters[index] = currentFilter
53
+ updateField(null, null, 'filters', filters)
54
+ }
55
+
56
+ const handleNameChange = (filterIndex, columnName) => {
57
+ const values = _.uniq(rawData.map(row => row[columnName]))
58
+ const copiedFilter = { ..._.cloneDeep(config.filters[filterIndex]), columnName, values }
59
+ handleSorting(copiedFilter) // sorts dropdown values in place
60
+ copiedFilter.active = copiedFilter.values[0]
61
+ const newFilters = config.filters.map((filter, index) => {
62
+ if (index === filterIndex) return copiedFilter
63
+ return filter
64
+ })
65
+ updateField(null, null, 'filters', newFilters)
66
+ }
67
+
68
+ const addNewFilter = () => {
69
+ const filters = config.filters ? [...config.filters] : []
70
+ const newVizFilter: VizFilter = { values: [], filterStyle: 'dropdown', id: Date.now() } as VizFilter
71
+ filters.push(newVizFilter)
72
+ updateField(null, null, 'filters', filters)
73
+ }
74
+
75
+ const handleFilterOrder = (idx1, idx2, filterIndex) => {
76
+ const filter = config.filters[filterIndex]
77
+ // Create a shallow copy of the filter values array & update position of the values
78
+ const updatedValues = [...(filter.orderedValues || filter.values)]
79
+
80
+ const [movedItem] = updatedValues.splice(idx1, 1)
81
+ updatedValues.splice(idx2, 0, movedItem)
82
+
83
+ const filtersCopy = _.cloneDeep(config.filters)
84
+ const filterItem = { ...filtersCopy[filterIndex] }
85
+
86
+ // Overwrite filterItem.values since thats what we map through in the editor panel
87
+ filterItem.values = updatedValues
88
+ filterItem.orderedValues = updatedValues
89
+ filterItem.active = updatedValues[0]
90
+
91
+ filterItem.order = 'cust'
92
+
93
+ // Update the filters
94
+ filtersCopy[filterIndex] = filterItem
95
+ updateField(null, null, 'filters', filtersCopy)
96
+ }
97
+
98
+ const getParentFilterOptions = (index: number): { label: string; value: number }[] => {
99
+ return config.filters
100
+ .filter((f, i) => i !== index)
101
+ .map(({ label, columnName, id }) => ({ label: label || columnName, value: id }))
102
+ }
103
+
104
+ return (
105
+ <>
106
+ {config.filters && (
107
+ <>
108
+ <Select
109
+ value={config.filterBehavior}
110
+ fieldName='filterBehavior'
111
+ label='Filter Behavior'
112
+ updateField={updateField}
113
+ options={['Apply Button', 'Filter Change']}
114
+ tooltip={
115
+ <Tooltip style={{ textTransform: 'none' }}>
116
+ <Tooltip.Target>
117
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
118
+ </Tooltip.Target>
119
+ <Tooltip.Content>
120
+ <p>
121
+ The Apply Button option changes the visualization when the user clicks "apply". The Filter Change
122
+ option immediately changes the visualization when the selection is changed.
123
+ </p>
124
+ </Tooltip.Content>
125
+ </Tooltip>
126
+ }
127
+ />
128
+ <br />
129
+ <ul className='filters-list'>
130
+ {/* Whether filters should apply onChange or Apply Button */}
131
+
132
+ {config.filters.map((filter, filterIndex) => {
133
+ if (filter.type === 'url') return <></>
134
+ return (
135
+ <FieldSetWrapper
136
+ key={filter.columnName}
137
+ fieldName={filter.columnName}
138
+ fieldKey={filterIndex}
139
+ fieldType='Filter'
140
+ controls={openControls}
141
+ deleteField={() => removeFilter(filterIndex)}
142
+ >
143
+ <label>
144
+ <span className='edit-label column-heading'>Filter Style</span>
145
+
146
+ <select
147
+ value={filter.filterStyle}
148
+ onChange={e => {
149
+ updateFilterStyle(filterIndex, e.target.value)
150
+ }}
151
+ >
152
+ {filterStyleOptions.map((item, index) => {
153
+ return (
154
+ <option key={`filter-style-${index}`} value={item}>
155
+ {item}
156
+ </option>
157
+ )
158
+ })}
159
+ </select>
160
+ </label>
161
+
162
+ {filter.filterStyle !== 'nested-dropdown' ? (
163
+ <>
164
+ <label>
165
+ <span className='edit-label column-heading'>Filter</span>
166
+ <select
167
+ value={filter.columnName}
168
+ onChange={e => {
169
+ handleNameChange(filterIndex, e.target.value)
170
+ }}
171
+ >
172
+ <option value=''>- Select Option -</option>
173
+ {dataColumns.map((dataKey, filterIndex) => (
174
+ <option value={dataKey} key={filterIndex}>
175
+ {dataKey}
176
+ </option>
177
+ ))}
178
+ </select>
179
+ </label>
180
+
181
+ <label>
182
+ <span className='edit-showDropdown column-heading'>Show Filter Input</span>
183
+ <input
184
+ type='checkbox'
185
+ checked={filter.showDropdown === undefined ? true : filter.showDropdown}
186
+ onChange={e => {
187
+ updateFilterProp('showDropdown', filterIndex, e.target.checked)
188
+ }}
189
+ />
190
+ </label>
191
+
192
+ <label>
193
+ <span className='edit-label column-heading'>Label</span>
194
+ <input
195
+ type='text'
196
+ value={filter.label}
197
+ onChange={e => {
198
+ updateFilterProp('label', filterIndex, e.target.value)
199
+ }}
200
+ />
201
+ </label>
202
+
203
+ {filter.filterStyle === 'multi-select' && (
204
+ <TextField
205
+ label='Select Limit'
206
+ value={(filter as MultiSelectFilter).selectLimit}
207
+ updateField={updateField}
208
+ section='filters'
209
+ subsection={filterIndex}
210
+ fieldName='selectLimit'
211
+ type='number'
212
+ tooltip={
213
+ <Tooltip style={{ textTransform: 'none' }}>
214
+ <Tooltip.Target>
215
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
216
+ </Tooltip.Target>
217
+ <Tooltip.Content>
218
+ <p>The maximum number of items that can be selected.</p>
219
+ </Tooltip.Content>
220
+ </Tooltip>
221
+ }
222
+ />
223
+ )}
224
+
225
+ <label>
226
+ <span className='edit-label column-heading'>Default Value Set By Query String Parameter</span>
227
+ <input
228
+ type='text'
229
+ value={filter.setByQueryParameter}
230
+ onChange={e => {
231
+ updateFilterProp('setByQueryParameter', filterIndex, e.target.value)
232
+ }}
233
+ />
234
+ </label>
235
+
236
+ <label>
237
+ <span className='edit-filterOrder column-heading'>Filter Order</span>
238
+ <select
239
+ value={filter.order ? filter.order : 'asc'}
240
+ onChange={e => updateFilterProp('order', filterIndex, e.target.value)}
241
+ >
242
+ {filterOrderOptions.map((option, index) => {
243
+ return (
244
+ <option value={option.value} key={`filter-${index}`}>
245
+ {option.label}
246
+ </option>
247
+ )
248
+ })}
249
+ </select>
250
+ {filter.order === 'cust' && (
251
+ <FilterOrder
252
+ orderedValues={filter.orderedValues || filter.values}
253
+ handleFilterOrder={(index1, index2) => handleFilterOrder(index1, index2, filterIndex)}
254
+ />
255
+ )}
256
+ </label>
257
+ </>
258
+ ) : (
259
+ <NestedDropdownEditor
260
+ config={config}
261
+ dataColumns={dataColumns}
262
+ filterIndex={filterIndex}
263
+ rawData={rawData}
264
+ handleGroupingCustomOrder={(index1, index2) => handleFilterOrder(index1, index2, filterIndex)}
265
+ handleNameChange={value => handleNameChange(filterIndex, value)}
266
+ updateField={updateField}
267
+ updateFilterStyle={updateFilterStyle}
268
+ />
269
+ )}
270
+ <label>
271
+ <span className='edit-label column-heading'>
272
+ Filter Parents{' '}
273
+ <Tooltip style={{ textTransform: 'none' }}>
274
+ <Tooltip.Target>
275
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
276
+ </Tooltip.Target>
277
+ <Tooltip.Content>
278
+ <p>
279
+ A selected parent's value will be used to filter the available options of this child filter.
280
+ </p>
281
+ </Tooltip.Content>
282
+ </Tooltip>
283
+ </span>
284
+ <MultiSelect
285
+ fieldName='parents'
286
+ updateField={(_section, _subsection, _fieldname, value) => {
287
+ updateFilterProp('parents', filterIndex, value)
288
+ }}
289
+ options={getParentFilterOptions(filterIndex)}
290
+ />
291
+ </label>
292
+ </FieldSetWrapper>
293
+ )
294
+ })}
295
+ </ul>
296
+ </>
297
+ )}
298
+ {!config.filters && <p style={{ textAlign: 'center' }}>There are currently no filters.</p>}
299
+ <button type='button' onClick={addNewFilter} className='btn btn-primary full-width'>
300
+ Add Filter
301
+ </button>
302
+ </>
303
+ )
304
+ }
305
+
306
+ export default VizFilterEditor
@@ -0,0 +1,40 @@
1
+ import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
2
+
3
+ type FilterOrderProps = {
4
+ orderedValues: string[]
5
+ handleFilterOrder?: (index1: number, index2: number) => void
6
+ }
7
+ const FilterOrder: React.FC<FilterOrderProps> = ({ orderedValues, handleFilterOrder }) => {
8
+ return (
9
+ <DragDropContext onDragEnd={({ source, destination }) => handleFilterOrder(source?.index, destination?.index)}>
10
+ <Droppable droppableId='filter_order'>
11
+ {provided => (
12
+ <ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef} style={{ marginTop: '1em' }}>
13
+ {orderedValues?.map((value, index) => {
14
+ return (
15
+ <Draggable key={value} draggableId={`draggableFilter-${value}`} index={index}>
16
+ {(provided, snapshot) => (
17
+ <li>
18
+ <div
19
+ className={snapshot.isDragging ? 'currently-dragging' : ''}
20
+ style={provided.draggableProps.style}
21
+ ref={provided.innerRef}
22
+ {...provided.draggableProps}
23
+ {...provided.dragHandleProps}
24
+ >
25
+ {value}
26
+ </div>
27
+ </li>
28
+ )}
29
+ </Draggable>
30
+ )
31
+ })}
32
+ {provided.placeholder}
33
+ </ul>
34
+ )}
35
+ </Droppable>
36
+ </DragDropContext>
37
+ )
38
+ }
39
+
40
+ export default FilterOrder
@@ -0,0 +1 @@
1
+ export { default } from './VizFilterEditor'
@@ -2,7 +2,6 @@ import React from 'react'
2
2
  import { Visualization } from '../../types/Visualization'
3
3
  import { ViewPort } from '../../types/ViewPort'
4
4
  import './editor-wrapper.style.css'
5
- import { Accordion } from 'react-accessible-accordion'
6
5
 
7
6
  type StandAloneComponentProps = {
8
7
  visualizationKey: string
@@ -12,6 +11,8 @@ type StandAloneComponentProps = {
12
11
  setEditing: Function
13
12
  hostname: string
14
13
  viewport?: ViewPort
14
+
15
+ [key: string]: any
15
16
  }
16
17
 
17
18
  type EditorProps = {
@@ -33,9 +34,7 @@ const EditorWrapper: React.FC<React.PropsWithChildren<EditorProps>> = ({ childre
33
34
  <div aria-level={2} role='heading' className='heading-2'>
34
35
  Configure {type}
35
36
  </div>
36
- <section>
37
- <Accordion allowZeroExpanded={true}>{children}</Accordion>
38
- </section>
37
+ <section>{children}</section>
39
38
  </section>
40
39
  <div className='preview-wrapper'>
41
40
  <Component visualizationKey={visualizationKey} config={visualizationConfig} updateConfig={updateConfig} configUrl={undefined} setEditing={undefined} hostname={undefined} viewport={viewport} />
@@ -0,0 +1 @@
1
+ export { default } from './EditorWrapper'