@cdc/core 4.24.3 → 4.24.5

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 (72) hide show
  1. package/assets/icon-command.svg +3 -0
  2. package/assets/icon-rotate-left.svg +3 -0
  3. package/components/AdvancedEditor.jsx +9 -0
  4. package/components/DataTable/DataTable.tsx +20 -15
  5. package/components/DataTable/DataTableStandAlone.tsx +51 -4
  6. package/components/DataTable/components/ChartHeader.tsx +3 -2
  7. package/components/DataTable/components/DataTableEditorPanel.tsx +20 -3
  8. package/components/DataTable/components/ExpandCollapse.tsx +22 -16
  9. package/components/DataTable/helpers/chartCellMatrix.tsx +3 -2
  10. package/components/DataTable/helpers/getChartCellValue.ts +21 -1
  11. package/components/DataTable/helpers/getDataSeriesColumns.ts +37 -16
  12. package/components/DataTable/helpers/getSeriesName.ts +2 -1
  13. package/components/DataTable/helpers/mapCellMatrix.tsx +2 -2
  14. package/components/DataTable/helpers/{customColumns.ts → removeNullColumns.ts} +3 -3
  15. package/components/DataTable/types/TableConfig.ts +11 -21
  16. package/components/EditorPanel/ColumnsEditor.tsx +304 -260
  17. package/components/EditorPanel/DataTableEditor.tsx +119 -48
  18. package/components/EditorPanel/VizFilterEditor.tsx +234 -0
  19. package/components/EditorWrapper/EditorWrapper.tsx +48 -0
  20. package/components/EditorWrapper/editor-wrapper.style.css +14 -0
  21. package/components/Filters.jsx +78 -61
  22. package/components/Layout/components/Responsive.tsx +184 -0
  23. package/components/Layout/components/Sidebar/components/Sidebar.tsx +47 -0
  24. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +902 -0
  25. package/components/Layout/components/Sidebar/index.tsx +3 -0
  26. package/components/Layout/components/Visualization/index.tsx +81 -0
  27. package/components/Layout/components/Visualization/visualizations.scss +33 -0
  28. package/components/Layout/index.tsx +11 -0
  29. package/components/Layout/styles/editor-grid-view.scss +156 -0
  30. package/components/Layout/styles/editor-utils.scss +197 -0
  31. package/components/Layout/styles/editor.scss +144 -0
  32. package/components/LegendCircle.jsx +4 -3
  33. package/components/MediaControls.jsx +1 -1
  34. package/components/Table/Table.tsx +7 -5
  35. package/components/Table/components/Row.tsx +6 -2
  36. package/components/Table/types/RowType.ts +3 -0
  37. package/components/Waiting.jsx +11 -1
  38. package/components/_stories/DataTable.stories.tsx +1 -2
  39. package/components/_stories/EditorPanel.stories.tsx +1 -0
  40. package/components/createBarElement.jsx +37 -34
  41. package/components/elements/SkipTo.tsx +37 -5
  42. package/components/managers/DataDesigner.tsx +18 -18
  43. package/components/ui/Icon.tsx +5 -1
  44. package/helpers/DataTransform.ts +9 -32
  45. package/helpers/coveUpdateWorker.ts +21 -0
  46. package/helpers/footnoteSymbols.ts +11 -0
  47. package/helpers/useDataVizClasses.js +1 -5
  48. package/helpers/ver/4.23.4.ts +27 -0
  49. package/helpers/ver/4.24.3.ts +56 -0
  50. package/helpers/ver/4.24.5.ts +32 -0
  51. package/package.json +2 -2
  52. package/styles/_data-table.scss +8 -0
  53. package/styles/_global.scss +7 -4
  54. package/styles/_reset.scss +7 -6
  55. package/styles/_variables.scss +3 -0
  56. package/styles/base.scss +0 -18
  57. package/styles/v2/base/index.scss +1 -1
  58. package/styles/v2/components/ui/tooltip.scss +0 -21
  59. package/types/Axis.ts +4 -0
  60. package/types/Column.ts +1 -0
  61. package/types/ConfigureData.ts +8 -0
  62. package/types/DataDescription.ts +9 -0
  63. package/types/Legend.ts +1 -0
  64. package/types/MarkupInclude.ts +26 -0
  65. package/types/Runtime.ts +1 -0
  66. package/types/Series.ts +1 -1
  67. package/types/Table.ts +15 -13
  68. package/types/Visualization.ts +14 -10
  69. package/types/VizFilter.ts +13 -0
  70. package/helpers/coveUpdateWorker.js +0 -19
  71. package/helpers/ver/4.23.js +0 -10
  72. package/helpers/ver/4.24.3.js +0 -25
@@ -1,21 +1,66 @@
1
- import React from 'react'
1
+ import React, { useMemo } from 'react'
2
2
  import Tooltip from '@cdc/core/components/ui/Tooltip'
3
3
  import Icon from '../ui/Icon'
4
4
  import { CheckBox, TextField } from './Inputs'
5
- import type { Table } from '@cdc/core/types/Table'
6
5
  import MultiSelect from '../MultiSelect'
7
6
  import { UpdateFieldFunc } from '../../types/UpdateFieldFunc'
8
7
  import { Visualization } from '../../types/Visualization'
8
+ import _ from 'lodash'
9
+ import { Column } from '../../types/Column'
9
10
 
10
11
  interface DataTableProps {
11
- config: Visualization
12
- updateField: UpdateFieldFunc<string | boolean | string[] | number>
12
+ config: Partial<Visualization>
13
+ updateField: UpdateFieldFunc<string | boolean | string[] | number | Record<string, Partial<Column>>>
13
14
  isDashboard: boolean
14
15
  columns: string[]
15
16
  }
16
17
 
17
- const DataTable: React.FC<DataTableProps> = ({ config, updateField, isDashboard, columns }) => {
18
+ const PLACEHOLDER = '-Select-'
19
+
20
+ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDashboard, columns: dataColumns }) => {
18
21
  const isLoadedFromUrl = config.dataKey?.includes('http://') || config?.dataKey?.includes('https://')
22
+ const excludedColumns = useMemo(() => {
23
+ return Object.keys(config.columns)
24
+ .map<[string, boolean]>(key => [key, config.columns[key].dataTable])
25
+ .filter(([key, dataTable]) => !dataTable)
26
+ .map(([key]) => key)
27
+ }, [config.columns])
28
+
29
+ const getGroupableColumns = () => {
30
+ const columns: string[] = config.data.flatMap(Object.keys)
31
+ const configuredColumns = Object.values(config.columns).map(col => col.name)
32
+ const cols = _.uniq(columns).filter(key => {
33
+ if (configuredColumns.includes(key)) return false
34
+ return true
35
+ })
36
+ return cols
37
+ }
38
+
39
+ const changeGroupBy = (value: string) => {
40
+ if (value === PLACEHOLDER) value = undefined
41
+ updateField('table', null, 'groupBy', value)
42
+ }
43
+
44
+ const excludeColumns = (section, subSection, fieldName, excludedColNames: string[]) => {
45
+ const newColumns = _.cloneDeep(config.columns)
46
+ const colNames: string[] = []
47
+ for (let colKey in newColumns) {
48
+ const col = newColumns[colKey]
49
+ colNames.push(col.name) // keep track of all column names
50
+ if (excludedColNames.includes(col.name)) {
51
+ // ensure all excluded columns are set to false
52
+ newColumns[colKey].dataTable = false
53
+ }
54
+ }
55
+ excludedColNames.forEach(colName => {
56
+ if (!colNames.includes(colName)) {
57
+ // make sure there is a column config to set to dataTable: false
58
+ newColumns[colName] = { name: colName, dataTable: false }
59
+ }
60
+ })
61
+ updateField(null, null, 'columns', newColumns)
62
+ }
63
+
19
64
  return (
20
65
  <>
21
66
  <TextField
@@ -37,25 +82,28 @@ const DataTable: React.FC<DataTableProps> = ({ config, updateField, isDashboard,
37
82
  </Tooltip>
38
83
  }
39
84
  />
40
- <CheckBox
41
- value={config.table.show}
42
- fieldName='show'
43
- label='Show Data Table'
44
- section='table'
45
- updateField={updateField}
46
- className='column-heading'
47
- tooltip={
48
- <Tooltip style={{ textTransform: 'none' }}>
49
- <Tooltip.Target>
50
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
51
- </Tooltip.Target>
52
- <Tooltip.Content>
53
- <p>Hiding the data table may affect accessibility. An alternate form of accessing visualization data is a 508 requirement.</p>
54
- </Tooltip.Content>
55
- </Tooltip>
56
- }
57
- />
58
- {config.visualizationType !== 'Box Plot' && (
85
+ {config.type !== 'table' && (
86
+ <CheckBox
87
+ value={config.table.show}
88
+ fieldName='show'
89
+ label='Show Data Table'
90
+ section='table'
91
+ updateField={updateField}
92
+ className='column-heading'
93
+ tooltip={
94
+ <Tooltip style={{ textTransform: 'none' }}>
95
+ <Tooltip.Target>
96
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
97
+ </Tooltip.Target>
98
+ <Tooltip.Content>
99
+ <p>Hiding the data table may affect accessibility. An alternate form of accessing visualization data is a 508 requirement.</p>
100
+ </Tooltip.Content>
101
+ </Tooltip>
102
+ }
103
+ />
104
+ )}
105
+
106
+ {config.visualizationType !== 'Box Plot' && config.type !== 'table' && (
59
107
  <CheckBox
60
108
  value={config.table.showVertical}
61
109
  fieldName='showVertical'
@@ -75,7 +123,25 @@ const DataTable: React.FC<DataTableProps> = ({ config, updateField, isDashboard,
75
123
  }
76
124
  />
77
125
  )}
78
- <TextField value={config.table.indexLabel} section='table' fieldName='indexLabel' label='Index Column Header' updateField={updateField} />
126
+ {config.type !== 'table' && (
127
+ <TextField
128
+ value={config.table.indexLabel}
129
+ section='table'
130
+ fieldName='indexLabel'
131
+ label='Index Column Header'
132
+ updateField={updateField}
133
+ tooltip={
134
+ <Tooltip style={{ textTransform: 'none' }}>
135
+ <Tooltip.Target>
136
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
137
+ </Tooltip.Target>
138
+ <Tooltip.Content>
139
+ <p>To comply with 508 standards, if the first column in the data table has no header, enter a brief one here.</p>
140
+ </Tooltip.Content>
141
+ </Tooltip>
142
+ }
143
+ />
144
+ )}
79
145
  <TextField
80
146
  value={config.table.caption}
81
147
  updateField={updateField}
@@ -95,29 +161,11 @@ const DataTable: React.FC<DataTableProps> = ({ config, updateField, isDashboard,
95
161
  </Tooltip>
96
162
  }
97
163
  />
98
- <CheckBox value={config.table.limitHeight} section='table' fieldName='limitHeight' label='Limit Table Height' updateField={updateField} />
99
- {config.type !== 'table' && (
100
- <CheckBox
101
- value={config.table.customTableConfig}
102
- fieldName='customTableConfig'
103
- label='Customize Table Config'
104
- section='table'
105
- updateField={updateField}
106
- tooltip={
107
- <Tooltip style={{ textTransform: 'none' }}>
108
- <Tooltip.Target>
109
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
110
- </Tooltip.Target>
111
- <Tooltip.Content>
112
- <p>This will display all available columns in the data set. It will not show any columns where all of the column cells are null.</p>
113
- </Tooltip.Content>
114
- </Tooltip>
115
- }
116
- />
117
- )}
118
- {config.table.customTableConfig && <MultiSelect options={columns.map(c => ({ label: c, value: c }))} fieldName='excludeColumns' label='Exclude Columns' section='table' updateField={updateField} />}
164
+ <CheckBox value={config.table.limitHeight} section='table' fieldName='limitHeight' label=' Limit Table Height' updateField={updateField} />
119
165
  {config.table.limitHeight && <TextField value={config.table.height} section='table' fieldName='height' label='Data Table Height' type='number' min={0} max={500} placeholder='Height(px)' updateField={updateField} />}
120
- <CheckBox value={config.table.expanded} fieldName='expanded' label='Expanded by Default' section='table' updateField={updateField} />
166
+ <MultiSelect key={excludedColumns.join('') + 'excluded'} options={dataColumns.map(c => ({ label: c, value: c }))} selected={excludedColumns} fieldName='dataTable' label='Exclude Columns' section='columns' updateField={excludeColumns} />
167
+ <CheckBox value={config.table.collapsible} fieldName='collapsible' label=' Collapsible' section='table' updateField={updateField} />
168
+ {config.table.collapsible !== false && <CheckBox value={config.table.expanded} fieldName='expanded' label=' Expanded by Default' section='table' updateField={updateField} />}
121
169
  {isDashboard && config.type !== 'table' && <CheckBox value={config.table.showDataTableLink} fieldName='showDataTableLink' label='Show Data Table Name & Link' section='table' updateField={updateField} />}
122
170
  {isLoadedFromUrl && <CheckBox value={config.table.showDownloadUrl} fieldName='showDownloadUrl' label='Show URL to Automatically Updated Data' section='table' updateField={updateField} />}
123
171
  {config.type !== 'table' && <CheckBox value={config.table.showDownloadImgButton} fieldName='showDownloadImgButton' label='Display Image Button' section='table' updateField={updateField} />}
@@ -125,8 +173,31 @@ const DataTable: React.FC<DataTableProps> = ({ config, updateField, isDashboard,
125
173
  <span className='edit-label column-heading'>Table Cell Min Width</span>
126
174
  <input type='number' value={config.table.cellMinWidth ? config.table.cellMinWidth : 0} onChange={e => updateField('table', null, 'cellMinWidth', e.target.value)} />
127
175
  </label>
176
+ <label>
177
+ <span className='edit-label column-heading'>
178
+ Group By{' '}
179
+ <Tooltip style={{ textTransform: 'none' }}>
180
+ <Tooltip.Target>
181
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
182
+ </Tooltip.Target>
183
+ <Tooltip.Content>
184
+ <p>Choose a column to use for grouping data rows. The selected column will not be shown in the data table. You will only be able to choose a column which does not have a column configuration.</p>
185
+ </Tooltip.Content>
186
+ </Tooltip>
187
+ </span>
188
+ <select
189
+ value={config.table.groupBy}
190
+ onChange={event => {
191
+ changeGroupBy(event.target.value)
192
+ }}
193
+ >
194
+ {[PLACEHOLDER, ...getGroupableColumns()].map(option => (
195
+ <option key={option}>{option}</option>
196
+ ))}
197
+ </select>
198
+ </label>
128
199
  </>
129
200
  )
130
201
  }
131
202
 
132
- export default DataTable
203
+ export default DataTableEditor
@@ -0,0 +1,234 @@
1
+ import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
2
+ import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
3
+ import { Select } from './Inputs'
4
+ import Tooltip from '../ui/Tooltip'
5
+ import Icon from '../ui/Icon'
6
+ import { Visualization } from '../../types/Visualization'
7
+ import { UpdateFieldFunc } from '../../types/UpdateFieldFunc'
8
+ import _ from 'lodash'
9
+ import { VizFilter } from '../../types/VizFilter'
10
+ import { filterStyleOptions, filterOrderOptions, handleSorting } from '../Filters'
11
+
12
+ type VizFilterProps = {
13
+ config: Visualization
14
+ updateField: UpdateFieldFunc<string | VizFilter[] | VizFilter>
15
+ rawData: Object[]
16
+ }
17
+
18
+ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawData }) => {
19
+ const getFilters = () => {
20
+ return _.uniq(_.flatten(rawData.map(row => Object.keys(row))))
21
+ }
22
+ const removeFilter = index => {
23
+ let filters = _.cloneDeep(config.filters)
24
+
25
+ filters.splice(index, 1)
26
+
27
+ updateField(null, null, 'filters', filters)
28
+ }
29
+
30
+ const updateFilterProp = (prop, index, value) => {
31
+ updateField('filters', index, prop, value)
32
+ }
33
+
34
+ const handleNameChange = (filterIndex, columnName) => {
35
+ const values = _.uniq(rawData.map(row => row[columnName]))
36
+ const copiedFilter = { ..._.cloneDeep(config.filters[filterIndex]), columnName, values }
37
+ handleSorting(copiedFilter) // sorts dropdown values in place
38
+ copiedFilter.active = copiedFilter.values[0]
39
+ const newFilters = config.filters.map((filter, index) => {
40
+ if (index === filterIndex) return copiedFilter
41
+ return filter
42
+ })
43
+ updateField(null, null, 'filters', newFilters)
44
+ }
45
+
46
+ const addNewFilter = () => {
47
+ const filters = config.filters ? [...config.filters] : []
48
+ const newVizFilter: VizFilter = { values: [], filterStyle: 'dropdown' } as VizFilter
49
+ filters.push(newVizFilter)
50
+ updateField(null, null, 'filters', filters)
51
+ }
52
+
53
+ const handleFilterOrder = (idx1, idx2, filterIndex, filter) => {
54
+ // Create a shallow copy of the filter values array & update position of the values
55
+ const updatedValues = [...filter.values]
56
+ const [movedItem] = updatedValues.splice(idx1, 1)
57
+ updatedValues.splice(idx2, 0, movedItem)
58
+
59
+ const filtersCopy = _.cloneDeep(config.filters)
60
+ const filterItem = { ...filtersCopy[filterIndex] }
61
+
62
+ // Overwrite filterItem.values since thats what we map through in the editor panel
63
+ filterItem.values = updatedValues
64
+ filterItem.orderedValues = updatedValues
65
+ filterItem.active = updatedValues[0]
66
+ filterItem.order = 'cust'
67
+
68
+ // Update the filters
69
+ filtersCopy[filterIndex] = filterItem
70
+
71
+ updateField(null, null, 'filters', filtersCopy)
72
+ }
73
+
74
+ return (
75
+ <>
76
+ {config.filters && (
77
+ <>
78
+ <Select
79
+ value={config.filterBehavior}
80
+ fieldName='filterBehavior'
81
+ label='Filter Behavior'
82
+ updateField={updateField}
83
+ options={['Apply Button', 'Filter Change']}
84
+ tooltip={
85
+ <Tooltip style={{ textTransform: 'none' }}>
86
+ <Tooltip.Target>
87
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
88
+ </Tooltip.Target>
89
+ <Tooltip.Content>
90
+ <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>
91
+ </Tooltip.Content>
92
+ </Tooltip>
93
+ }
94
+ />
95
+ <br />
96
+ </>
97
+ )}
98
+ {config.filters && (
99
+ <ul className='filters-list'>
100
+ {/* Whether filters should apply onChange or Apply Button */}
101
+
102
+ {config.filters.map((filter, index) => {
103
+ if (filter.type === 'url') return <></>
104
+
105
+ return (
106
+ <fieldset className='edit-block' key={index}>
107
+ <button
108
+ type='button'
109
+ className='remove-column'
110
+ onClick={() => {
111
+ removeFilter(index)
112
+ }}
113
+ >
114
+ Remove
115
+ </button>
116
+ <label>
117
+ <span className='edit-label column-heading'>Filter</span>
118
+ <select
119
+ value={filter.columnName}
120
+ onChange={e => {
121
+ handleNameChange(index, e.target.value)
122
+ }}
123
+ >
124
+ <option value=''>- Select Option -</option>
125
+ {getFilters().map((dataKey, index) => (
126
+ <option value={dataKey} key={index}>
127
+ {dataKey}
128
+ </option>
129
+ ))}
130
+ </select>
131
+ </label>
132
+
133
+ <label>
134
+ <span className='edit-showDropdown column-heading'>Show Filter Input</span>
135
+ <input
136
+ type='checkbox'
137
+ checked={filter.showDropdown === undefined ? true : filter.showDropdown}
138
+ onChange={e => {
139
+ updateFilterProp('showDropdown', index, e.target.checked)
140
+ }}
141
+ />
142
+ </label>
143
+
144
+ <label>
145
+ <span className='edit-label column-heading'>Filter Style</span>
146
+
147
+ <select
148
+ value={filter.filterStyle}
149
+ onChange={e => {
150
+ updateFilterProp('filterStyle', index, e.target.value)
151
+ }}
152
+ >
153
+ {filterStyleOptions.map((item, index) => {
154
+ return (
155
+ <option key={`filter-style-${index}`} value={item}>
156
+ {item}
157
+ </option>
158
+ )
159
+ })}
160
+ </select>
161
+ </label>
162
+ <label>
163
+ <span className='edit-label column-heading'>Label</span>
164
+ <input
165
+ type='text'
166
+ value={filter.label}
167
+ onChange={e => {
168
+ updateFilterProp('label', index, e.target.value)
169
+ }}
170
+ />
171
+ </label>
172
+
173
+ <label>
174
+ <span className='edit-label column-heading'>Default Value Set By Query String Parameter</span>
175
+ <input
176
+ type='text'
177
+ value={filter.setByQueryParameter}
178
+ onChange={e => {
179
+ updateFilterProp('setByQueryParameter', index, e.target.value)
180
+ }}
181
+ />
182
+ </label>
183
+
184
+ <label>
185
+ <span className='edit-filterOrder column-heading'>Filter Order</span>
186
+ <select value={filter.order ? filter.order : 'asc'} onChange={e => updateFilterProp('order', index, e.target.value)}>
187
+ {filterOrderOptions.map((option, index) => {
188
+ return (
189
+ <option value={option.value} key={`filter-${index}`}>
190
+ {option.label}
191
+ </option>
192
+ )
193
+ })}
194
+ </select>
195
+
196
+ {filter.order === 'cust' && (
197
+ <DragDropContext onDragEnd={({ source, destination }) => handleFilterOrder(source.index, destination.index, index, config.filters[index])}>
198
+ <Droppable droppableId='filter_order'>
199
+ {provided => (
200
+ <ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef} style={{ marginTop: '1em' }}>
201
+ {config.filters[index]?.values.map((value, index) => {
202
+ return (
203
+ <Draggable key={value} draggableId={`draggableFilter-${value}`} index={index}>
204
+ {(provided, snapshot) => (
205
+ <li>
206
+ <div className={snapshot.isDragging ? 'currently-dragging' : ''} style={provided.draggableProps.style} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
207
+ {value}
208
+ </div>
209
+ </li>
210
+ )}
211
+ </Draggable>
212
+ )
213
+ })}
214
+ {provided.placeholder}
215
+ </ul>
216
+ )}
217
+ </Droppable>
218
+ </DragDropContext>
219
+ )}
220
+ </label>
221
+ </fieldset>
222
+ )
223
+ })}
224
+ </ul>
225
+ )}
226
+ {!config.filters && <p style={{ textAlign: 'center' }}>There are currently no filters.</p>}
227
+ <button type='button' onClick={addNewFilter} className='btn btn-primary full-width'>
228
+ Add Filter
229
+ </button>
230
+ </>
231
+ )
232
+ }
233
+
234
+ export default VizFilterEditor
@@ -0,0 +1,48 @@
1
+ import React from 'react'
2
+ import { Visualization } from '../../types/Visualization'
3
+ import { ViewPort } from '../../types/ViewPort'
4
+ import './editor-wrapper.style.css'
5
+ import { Accordion } from 'react-accessible-accordion'
6
+
7
+ type StandAloneComponentProps = {
8
+ visualizationKey: string
9
+ config
10
+ updateConfig: (Visualization) => void
11
+ configUrl: string
12
+ setEditing: Function
13
+ hostname: string
14
+ viewport?: ViewPort
15
+ }
16
+
17
+ type EditorProps = {
18
+ component: React.JSXElementConstructor<StandAloneComponentProps>
19
+ type: string
20
+ visualizationKey: string
21
+ visualizationConfig: Visualization
22
+ updateConfig: (Visualization) => void
23
+ viewport?: ViewPort
24
+ }
25
+
26
+ const EditorWrapper: React.FC<React.PropsWithChildren<EditorProps>> = ({ children, visualizationKey, visualizationConfig, type, component: Component, updateConfig, viewport }) => {
27
+ const [displayPanel, setDisplayPanel] = React.useState(true)
28
+ return (
29
+ <>
30
+ <div className='editor-wrapper'>
31
+ <button className={`editor-toggle ${displayPanel ? '' : 'collapsed'}`} title={displayPanel ? `Collapse Editor` : `Expand Editor`} onClick={() => setDisplayPanel(!displayPanel)} />
32
+ <section className={`${displayPanel ? '' : 'hidden'} editor-panel cove`}>
33
+ <div aria-level={2} role='heading' className='heading-2'>
34
+ Configure {type}
35
+ </div>
36
+ <section>
37
+ <Accordion allowZeroExpanded={true}>{children}</Accordion>
38
+ </section>
39
+ </section>
40
+ <div className='preview-wrapper'>
41
+ <Component visualizationKey={visualizationKey} config={visualizationConfig} updateConfig={updateConfig} configUrl={undefined} setEditing={undefined} hostname={undefined} viewport={viewport} />
42
+ </div>
43
+ </div>
44
+ </>
45
+ )
46
+ }
47
+
48
+ export default EditorWrapper
@@ -0,0 +1,14 @@
1
+ .editor-wrapper {
2
+ --editorPanelWidth: 350px;
3
+ position: relative;
4
+ min-height: 80vh;
5
+ .editor-panel {
6
+ :is(form) {
7
+ border-right: var(--lightGray) 1px solid;
8
+ flex-grow: 1;
9
+ }
10
+ }
11
+ .preview-wrapper {
12
+ padding-left: var(--editorPanelWidth);
13
+ }
14
+ }