@cdc/core 4.24.7 → 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 (69) hide show
  1. package/LICENSE +201 -0
  2. package/assets/icon-gear-multi.svg +23 -0
  3. package/components/Alert/components/Alert.styles.css +15 -0
  4. package/components/Alert/components/Alert.tsx +39 -0
  5. package/components/Alert/index.tsx +3 -0
  6. package/components/DataTable/DataTable.tsx +106 -30
  7. package/components/DataTable/helpers/chartCellMatrix.tsx +3 -3
  8. package/components/DataTable/helpers/getChartCellValue.ts +1 -1
  9. package/components/DataTable/helpers/getDataSeriesColumns.ts +2 -2
  10. package/components/DataTable/helpers/mapCellMatrix.tsx +3 -3
  11. package/components/DataTable/types/TableConfig.ts +1 -1
  12. package/components/EditorPanel/Inputs.tsx +13 -4
  13. package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +268 -0
  14. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +161 -82
  15. package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +31 -45
  16. package/components/Filters.tsx +223 -180
  17. package/components/Layout/components/Responsive.tsx +14 -4
  18. package/components/Layout/components/Sidebar/components/Sidebar.tsx +14 -5
  19. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +15 -16
  20. package/components/Layout/components/Visualization/index.tsx +7 -1
  21. package/components/Layout/components/Visualization/visualizations.scss +32 -26
  22. package/components/Layout/styles/editor.scss +0 -8
  23. package/components/Legend/Legend.Gradient.tsx +133 -0
  24. package/components/LegendShape.tsx +28 -0
  25. package/components/MultiSelect/MultiSelect.tsx +6 -3
  26. package/components/NestedDropdown/NestedDropdown.tsx +47 -52
  27. package/components/NestedDropdown/nesteddropdown.styles.css +19 -25
  28. package/components/Table/Table.tsx +8 -5
  29. package/components/Table/components/Cell.tsx +2 -2
  30. package/components/Table/components/Row.tsx +25 -7
  31. package/components/_stories/Layout.Debug.stories.tsx +91 -0
  32. package/components/_stories/_mocks/bar-chart-suppressed.json +474 -0
  33. package/components/_stories/styles.scss +13 -1
  34. package/components/createBarElement.jsx +4 -4
  35. package/components/ui/Icon.tsx +21 -14
  36. package/components/ui/Title/Title.scss +0 -8
  37. package/helpers/DataTransform.ts +2 -2
  38. package/helpers/addValuesToFilters.ts +95 -16
  39. package/helpers/cove/accessibility.ts +16 -4
  40. package/helpers/coveUpdateWorker.ts +24 -10
  41. package/helpers/filterVizData.ts +23 -4
  42. package/helpers/formatConfigBeforeSave.ts +22 -2
  43. package/helpers/gatherQueryParams.ts +12 -2
  44. package/helpers/getGradientLegendWidth.ts +15 -0
  45. package/helpers/getTextWidth.ts +18 -0
  46. package/helpers/scaling.ts +7 -0
  47. package/helpers/tests/addValuesToFilters.test.ts +55 -0
  48. package/helpers/tests/filterVizData.test.ts +31 -0
  49. package/helpers/tests/gatherQueryParams.test.ts +22 -0
  50. package/helpers/tests/invertValue.test.ts +35 -0
  51. package/helpers/updatePaletteNames.ts +19 -0
  52. package/helpers/{useDataVizClasses.js → useDataVizClasses.ts} +3 -2
  53. package/helpers/ver/4.24.5.ts +3 -3
  54. package/helpers/ver/4.24.7.ts +34 -3
  55. package/helpers/ver/4.24.9.ts +63 -0
  56. package/helpers/ver/tests/4.24.9.test.ts +22 -0
  57. package/helpers/ver/versionNeedsUpdate.ts +9 -0
  58. package/package.json +3 -3
  59. package/styles/_button-section.scss +1 -1
  60. package/styles/_global.scss +6 -2
  61. package/styles/filters.scss +4 -0
  62. package/types/Axis.ts +3 -0
  63. package/types/Dimensions.ts +1 -0
  64. package/types/General.ts +1 -1
  65. package/types/VizFilter.ts +24 -3
  66. package/components/LegendCircle.jsx +0 -17
  67. package/helpers/updatePaletteNames.js +0 -16
  68. /package/components/{Waiting.jsx → Waiting.tsx} +0 -0
  69. /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
@@ -4,12 +4,14 @@ import Icon from '../../ui/Icon'
4
4
  import { Visualization } from '../../../types/Visualization'
5
5
  import { UpdateFieldFunc } from '../../../types/UpdateFieldFunc'
6
6
  import _ from 'lodash'
7
- import { MultiSelectFilter, VizFilter } from '../../../types/VizFilter'
8
- import { filterStyleOptions, handleSorting } from '../../Filters'
7
+ import { MultiSelectFilter, VizFilter, VizFilterStyle } from '../../../types/VizFilter'
8
+ import { filterStyleOptions, handleSorting, filterOrderOptions } from '../../Filters'
9
9
  import FieldSetWrapper from '../FieldSetWrapper'
10
10
 
11
11
  import FilterOrder from './components/FilterOrder'
12
12
  import { useMemo, useState } from 'react'
13
+ import MultiSelect from '../../MultiSelect'
14
+ import NestedDropdownEditor from './NestedDropdownEditor'
13
15
 
14
16
  type VizFilterProps = {
15
17
  config: Visualization
@@ -31,19 +33,22 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
31
33
  updateField(null, null, 'filters', filters)
32
34
  }
33
35
 
34
- const updateFilterProp = (prop, index, value) => {
35
- updateField('filters', index, prop, value)
36
+ const updateFilterProp = (prop, filterIndex, value) => {
37
+ updateField('filters', filterIndex, prop, value)
36
38
  }
37
39
 
38
- const updateFilterStyle = (index, value) => {
40
+ const updateFilterStyle = (index, style: VizFilterStyle) => {
39
41
  const filters = _.cloneDeep(config.filters)
40
- const currentFilter = filters[index]
41
- currentFilter.filterStyle = value
42
- if (value === 'multi-select') {
42
+ const currentFilter = { ...filters[index], orderedValues: filters[index].values }
43
+ currentFilter.filterStyle = style
44
+ if (style === 'multi-select') {
43
45
  currentFilter.active = Array.isArray(currentFilter.active) ? currentFilter.active : [currentFilter.active]
44
46
  } else if (Array.isArray(currentFilter.active)) {
45
47
  currentFilter.active = currentFilter.active[0]
46
48
  }
49
+ if (style === 'nested-dropdown') {
50
+ currentFilter.showDropdown = true
51
+ }
47
52
  filters[index] = currentFilter
48
53
  updateField(null, null, 'filters', filters)
49
54
  }
@@ -62,14 +67,16 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
62
67
 
63
68
  const addNewFilter = () => {
64
69
  const filters = config.filters ? [...config.filters] : []
65
- const newVizFilter: VizFilter = { values: [], filterStyle: 'dropdown' } as VizFilter
70
+ const newVizFilter: VizFilter = { values: [], filterStyle: 'dropdown', id: Date.now() } as VizFilter
66
71
  filters.push(newVizFilter)
67
72
  updateField(null, null, 'filters', filters)
68
73
  }
69
74
 
70
- const handleFilterOrder = (idx1, idx2, filterIndex, filter) => {
75
+ const handleFilterOrder = (idx1, idx2, filterIndex) => {
76
+ const filter = config.filters[filterIndex]
71
77
  // Create a shallow copy of the filter values array & update position of the values
72
- const updatedValues = [...filter.values]
78
+ const updatedValues = [...(filter.orderedValues || filter.values)]
79
+
73
80
  const [movedItem] = updatedValues.splice(idx1, 1)
74
81
  updatedValues.splice(idx2, 0, movedItem)
75
82
 
@@ -80,14 +87,20 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
80
87
  filterItem.values = updatedValues
81
88
  filterItem.orderedValues = updatedValues
82
89
  filterItem.active = updatedValues[0]
90
+
83
91
  filterItem.order = 'cust'
84
92
 
85
93
  // Update the filters
86
94
  filtersCopy[filterIndex] = filterItem
87
-
88
95
  updateField(null, null, 'filters', filtersCopy)
89
96
  }
90
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
+
91
104
  return (
92
105
  <>
93
106
  {config.filters && (
@@ -104,7 +117,10 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
104
117
  <Icon display='question' style={{ marginLeft: '0.5rem' }} />
105
118
  </Tooltip.Target>
106
119
  <Tooltip.Content>
107
- <p>The Apply Button option changes the visualization when the user clicks "apply". The Filter Change option immediately changes the visualization when the selection is changed.</p>
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>
108
124
  </Tooltip.Content>
109
125
  </Tooltip>
110
126
  }
@@ -113,46 +129,24 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
113
129
  <ul className='filters-list'>
114
130
  {/* Whether filters should apply onChange or Apply Button */}
115
131
 
116
- {config.filters.map((filter, index) => {
132
+ {config.filters.map((filter, filterIndex) => {
117
133
  if (filter.type === 'url') return <></>
118
-
119
134
  return (
120
- <FieldSetWrapper fieldName={filter.columnName} fieldKey={index} fieldType='Filter' controls={openControls} deleteField={() => removeFilter(index)}>
121
- <label>
122
- <span className='edit-label column-heading'>Filter</span>
123
- <select
124
- value={filter.columnName}
125
- onChange={e => {
126
- handleNameChange(index, e.target.value)
127
- }}
128
- >
129
- <option value=''>- Select Option -</option>
130
- {dataColumns.map((dataKey, index) => (
131
- <option value={dataKey} key={index}>
132
- {dataKey}
133
- </option>
134
- ))}
135
- </select>
136
- </label>
137
-
138
- <label>
139
- <span className='edit-showDropdown column-heading'>Show Filter Input</span>
140
- <input
141
- type='checkbox'
142
- checked={filter.showDropdown === undefined ? true : filter.showDropdown}
143
- onChange={e => {
144
- updateFilterProp('showDropdown', index, e.target.checked)
145
- }}
146
- />
147
- </label>
148
-
135
+ <FieldSetWrapper
136
+ key={filter.columnName}
137
+ fieldName={filter.columnName}
138
+ fieldKey={filterIndex}
139
+ fieldType='Filter'
140
+ controls={openControls}
141
+ deleteField={() => removeFilter(filterIndex)}
142
+ >
149
143
  <label>
150
144
  <span className='edit-label column-heading'>Filter Style</span>
151
145
 
152
146
  <select
153
147
  value={filter.filterStyle}
154
148
  onChange={e => {
155
- updateFilterStyle(index, e.target.value)
149
+ updateFilterStyle(filterIndex, e.target.value)
156
150
  }}
157
151
  >
158
152
  {filterStyleOptions.map((item, index) => {
@@ -165,51 +159,136 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
165
159
  </select>
166
160
  </label>
167
161
 
168
- <label>
169
- <span className='edit-label column-heading'>Label</span>
170
- <input
171
- type='text'
172
- value={filter.label}
173
- onChange={e => {
174
- updateFilterProp('label', index, e.target.value)
175
- }}
176
- />
177
- </label>
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
+ )}
178
224
 
179
- {filter.filterStyle === 'multi-select' && (
180
- <TextField
181
- label='Select Limit'
182
- value={(filter as MultiSelectFilter).selectLimit}
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)}
183
266
  updateField={updateField}
184
- section='filters'
185
- subsection={index}
186
- fieldName='selectLimit'
187
- type='number'
188
- tooltip={
189
- <Tooltip style={{ textTransform: 'none' }}>
190
- <Tooltip.Target>
191
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
192
- </Tooltip.Target>
193
- <Tooltip.Content>
194
- <p>The maximum number of items that can be selected.</p>
195
- </Tooltip.Content>
196
- </Tooltip>
197
- }
267
+ updateFilterStyle={updateFilterStyle}
198
268
  />
199
269
  )}
200
-
201
270
  <label>
202
- <span className='edit-label column-heading'>Default Value Set By Query String Parameter</span>
203
- <input
204
- type='text'
205
- value={filter.setByQueryParameter}
206
- onChange={e => {
207
- updateFilterProp('setByQueryParameter', index, e.target.value)
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)
208
288
  }}
289
+ options={getParentFilterOptions(filterIndex)}
209
290
  />
210
291
  </label>
211
-
212
- <FilterOrder filterIndex={index} filter={filter} updateFilterProp={updateFilterProp} handleFilterOrder={handleFilterOrder} />
213
292
  </FieldSetWrapper>
214
293
  )
215
294
  })}