@cdc/core 4.25.11 → 4.26.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 (77) hide show
  1. package/_stories/Gallery.Charts.stories.tsx +307 -0
  2. package/_stories/Gallery.DataBite.stories.tsx +72 -0
  3. package/_stories/Gallery.Maps.stories.tsx +230 -0
  4. package/_stories/Gallery.WaffleChart.stories.tsx +187 -0
  5. package/_stories/PageART.stories.tsx +192 -0
  6. package/_stories/PageBRFSS.stories.tsx +289 -0
  7. package/_stories/PageCancerRegistries.stories.tsx +199 -0
  8. package/_stories/PageEasternEquineEncephalitis.stories.tsx +202 -0
  9. package/_stories/PageExcessiveAlcoholUse.stories.tsx +196 -0
  10. package/_stories/PageMaternalMortality.stories.tsx +192 -0
  11. package/_stories/PageOralHealth.stories.tsx +196 -0
  12. package/_stories/PageRespiratory.stories.tsx +332 -0
  13. package/_stories/PageSmokingTobacco.stories.tsx +195 -0
  14. package/_stories/PageStateDiabetesProfiles.stories.tsx +196 -0
  15. package/_stories/PageWastewater.stories.tsx +463 -0
  16. package/assets/icon-magnifying-glass.svg +5 -0
  17. package/assets/icon-warming-stripes.svg +13 -0
  18. package/components/AdvancedEditor/AdvancedEditor.tsx +4 -0
  19. package/components/AdvancedEditor/EmbedEditor.tsx +281 -0
  20. package/components/ComboBox/ComboBox.tsx +345 -0
  21. package/components/ComboBox/combobox.styles.css +185 -0
  22. package/components/ComboBox/index.ts +1 -0
  23. package/components/DataTable/DataTable.tsx +132 -58
  24. package/components/DataTable/data-table.css +216 -215
  25. package/components/DataTable/helpers/mapCellMatrix.tsx +14 -6
  26. package/components/EditorPanel/ColumnsEditor.tsx +37 -19
  27. package/components/EditorPanel/DataTableEditor.tsx +51 -25
  28. package/components/EditorPanel/EditorPanel.styles.css +16 -0
  29. package/components/EditorPanel/EditorPanel.tsx +144 -0
  30. package/components/EditorPanel/EditorPanelDispatch.tsx +75 -0
  31. package/components/EditorPanel/FieldSetWrapper.tsx +66 -23
  32. package/components/EditorPanel/Inputs.tsx +33 -7
  33. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +236 -175
  34. package/components/EditorPanel/sections/VisualSection.tsx +169 -0
  35. package/components/Filters/Filters.tsx +31 -5
  36. package/components/Filters/helpers/getNestedOptions.ts +2 -1
  37. package/components/Filters/helpers/handleSorting.ts +1 -1
  38. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +82 -0
  39. package/components/Layout/components/Visualization/index.tsx +16 -1
  40. package/components/Layout/components/Visualization/visualizations.scss +7 -0
  41. package/components/Legend/Legend.Gradient.tsx +1 -1
  42. package/components/MediaControls.tsx +53 -27
  43. package/components/ui/Icon.tsx +3 -1
  44. package/components/ui/Title/index.tsx +30 -2
  45. package/components/ui/Title/title.styles.css +42 -0
  46. package/dist/cove-main.css +26 -3
  47. package/dist/cove-main.css.map +1 -1
  48. package/generateViteConfig.js +8 -1
  49. package/helpers/addValuesToFilters.ts +6 -1
  50. package/helpers/coveUpdateWorker.ts +19 -12
  51. package/helpers/embedCodeGenerator.ts +109 -0
  52. package/helpers/getUniqueValues.ts +19 -0
  53. package/helpers/hashObj.ts +25 -0
  54. package/helpers/isRightAlignedTableValue.js +5 -0
  55. package/helpers/metrics/helpers.ts +1 -0
  56. package/helpers/pivotData.ts +2 -2
  57. package/helpers/prepareScreenshot.ts +268 -0
  58. package/helpers/queryStringUtils.ts +29 -0
  59. package/helpers/tests/prepareScreenshot.test.ts +414 -0
  60. package/helpers/tests/queryStringUtils.test.ts +381 -0
  61. package/helpers/tests/testStandaloneBuild.ts +23 -5
  62. package/helpers/useDataVizClasses.ts +0 -1
  63. package/helpers/ver/4.26.1.ts +80 -0
  64. package/hooks/useDataColumns.ts +63 -0
  65. package/hooks/useFilterManagement.ts +94 -0
  66. package/hooks/useLegendSeparators.ts +26 -0
  67. package/hooks/useListManagement.ts +192 -0
  68. package/package.json +4 -3
  69. package/styles/_button-section.scss +0 -3
  70. package/types/Axis.ts +1 -0
  71. package/types/ForecastingSeriesKey.ts +1 -0
  72. package/types/MarkupInclude.ts +1 -0
  73. package/types/Series.ts +3 -0
  74. package/types/Table.ts +1 -0
  75. package/types/Visualization.ts +1 -0
  76. package/types/VizFilter.ts +1 -0
  77. package/LICENSE +0 -201
@@ -14,6 +14,7 @@ import FilterOrder from './components/FilterOrder'
14
14
  import { useMemo, useState } from 'react'
15
15
  import MultiSelect from '../../MultiSelect'
16
16
  import NestedDropdownEditor from './NestedDropdownEditor'
17
+ import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
17
18
 
18
19
  type VizFilterProps = {
19
20
  config: Visualization
@@ -128,6 +129,26 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
128
129
  .map(({ label, columnName, id }) => ({ label: label || columnName, value: id }))
129
130
  }
130
131
 
132
+ const handleFilterReorder = (idx1: number, idx2: number) => {
133
+ if (idx1 === undefined || idx2 === undefined || idx1 === idx2) return
134
+ const filters = _.cloneDeep(config.filters)
135
+ const [movedFilter] = filters.splice(idx1, 1)
136
+ filters.splice(idx2, 0, movedFilter)
137
+ updateField(null, null, 'filters', filters)
138
+ }
139
+
140
+ const getItemStyle = (isDragging: boolean, draggableStyle: any) => ({
141
+ ...draggableStyle
142
+ })
143
+
144
+ const sortableItemStyles = {
145
+ animate: false,
146
+ animateReplay: true,
147
+ display: 'block',
148
+ boxSizing: 'border-box' as const,
149
+ border: '1px solid #D1D1D1'
150
+ }
151
+
131
152
  return (
132
153
  <>
133
154
  {config.filters && (
@@ -160,188 +181,228 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
160
181
  fieldName='filterIntro'
161
182
  />
162
183
  <br />
163
- <ul className='filters-list'>
164
- {/* Whether filters should apply onChange or Apply Button */}
184
+ <DragDropContext
185
+ onDragEnd={({ source, destination }) => handleFilterReorder(source.index, destination?.index)}
186
+ >
187
+ <Droppable droppableId='filters_list'>
188
+ {provided => (
189
+ <ul {...provided.droppableProps} ref={provided.innerRef} className='draggable-field-list'>
190
+ {/* Whether filters should apply onChange or Apply Button */}
165
191
 
166
- {config.filters.map((filter, filterIndex) => {
167
- if (filter.type === 'url') return <></>
168
- return (
169
- <FieldSetWrapper
170
- key={filter.columnName}
171
- fieldName={filter.columnName}
172
- fieldKey={filterIndex}
173
- fieldType='Filter'
174
- controls={openControls}
175
- deleteField={() => removeFilter(filterIndex)}
176
- >
177
- <Select
178
- value={filter.filterStyle}
179
- fieldName='filterStyle'
180
- label='Filter Style'
181
- updateField={(_section, _subsection, _field, value) => updateFilterStyle(filterIndex, value)}
182
- options={filterStyleOptions}
183
- />
192
+ {config.filters.map((filter, filterIndex) => {
193
+ if (filter.type === 'url') return <></>
194
+ return (
195
+ <Draggable
196
+ key={filter.id || `filter-${filterIndex}`}
197
+ draggableId={`filter-${filter.id || filterIndex}`}
198
+ index={filterIndex}
199
+ >
200
+ {(provided, snapshot) => (
201
+ <div
202
+ ref={provided.innerRef}
203
+ {...provided.draggableProps}
204
+ {...provided.dragHandleProps}
205
+ className={snapshot.isDragging ? 'currently-dragging' : ''}
206
+ style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
207
+ >
208
+ <FieldSetWrapper
209
+ key={filter.columnName}
210
+ fieldName={filter.columnName}
211
+ fieldKey={filterIndex}
212
+ fieldType='Filter'
213
+ controls={openControls}
214
+ deleteField={() => removeFilter(filterIndex)}
215
+ draggable={true}
216
+ >
217
+ <Select
218
+ value={filter.filterStyle}
219
+ fieldName='filterStyle'
220
+ label='Filter Style'
221
+ updateField={(_section, _subsection, _field, value) =>
222
+ updateFilterStyle(filterIndex, value)
223
+ }
224
+ options={filterStyleOptions}
225
+ />
184
226
 
185
- {filter.filterStyle !== 'nested-dropdown' ? (
186
- <>
187
- <Select
188
- value={filter.columnName}
189
- fieldName='columnName'
190
- label='Filter'
191
- updateField={(_section, _subsection, _field, value) => handleNameChange(filterIndex, value)}
192
- options={dataColumns}
193
- initial='- Select Option -'
194
- />
227
+ {filter.filterStyle !== 'nested-dropdown' ? (
228
+ <>
229
+ <Select
230
+ value={filter.columnName}
231
+ fieldName='columnName'
232
+ label='Filter'
233
+ updateField={(_section, _subsection, _field, value) =>
234
+ handleNameChange(filterIndex, value)
235
+ }
236
+ options={dataColumns}
237
+ initial='- Select Option -'
238
+ />
195
239
 
196
- {filter.columnName && (
197
- <Select
198
- value={filter.defaultValue}
199
- options={
200
- filter.resetLabel
201
- ? [filter.resetLabel, ...getFilterValues(filter)]
202
- : getFilterValues(filter)
203
- }
204
- updateField={(_section, _subSection, _key, value) => {
205
- updateFilterDefaultValue(filterIndex, value)
206
- }}
207
- label='Filter Default Value'
208
- initial='Select'
209
- />
210
- )}
240
+ {filter.columnName && (
241
+ <Select
242
+ value={filter.defaultValue}
243
+ options={
244
+ filter.resetLabel
245
+ ? [filter.resetLabel, ...getFilterValues(filter)]
246
+ : getFilterValues(filter)
247
+ }
248
+ updateField={(_section, _subSection, _key, value) => {
249
+ updateFilterDefaultValue(filterIndex, value)
250
+ }}
251
+ label={`Filter Default Value${
252
+ filter.columnName ? ` (${filter.columnName})` : ''
253
+ }`}
254
+ initial='Select'
255
+ />
256
+ )}
211
257
 
212
- <label>
213
- <span className='edit-label column-heading'>Label</span>
214
- <input
215
- type='text'
216
- value={filter.label}
217
- onChange={e => {
218
- updateFilterProp('label', filterIndex, e.target.value)
219
- }}
220
- />
221
- </label>
258
+ <label>
259
+ <span className='edit-label column-heading'>Label</span>
260
+ <input
261
+ type='text'
262
+ value={filter.label}
263
+ onChange={e => {
264
+ updateFilterProp('label', filterIndex, e.target.value)
265
+ }}
266
+ />
267
+ </label>
222
268
 
223
- {filter.filterStyle === 'multi-select' && (
224
- <TextField
225
- label='Select Limit'
226
- value={(filter as MultiSelectFilter).selectLimit}
227
- updateField={updateField}
228
- section='filters'
229
- subsection={filterIndex}
230
- fieldName='selectLimit'
231
- type='number'
232
- tooltip={
233
- <Tooltip style={{ textTransform: 'none' }}>
234
- <Tooltip.Target>
235
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
236
- </Tooltip.Target>
237
- <Tooltip.Content>
238
- <p>The maximum number of items that can be selected.</p>
239
- </Tooltip.Content>
240
- </Tooltip>
241
- }
242
- />
243
- )}
269
+ {filter.filterStyle === 'multi-select' && (
270
+ <TextField
271
+ label='Select Limit'
272
+ value={(filter as MultiSelectFilter).selectLimit}
273
+ updateField={updateField}
274
+ section='filters'
275
+ subsection={filterIndex}
276
+ fieldName='selectLimit'
277
+ type='number'
278
+ tooltip={
279
+ <Tooltip style={{ textTransform: 'none' }}>
280
+ <Tooltip.Target>
281
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
282
+ </Tooltip.Target>
283
+ <Tooltip.Content>
284
+ <p>The maximum number of items that can be selected.</p>
285
+ </Tooltip.Content>
286
+ </Tooltip>
287
+ }
288
+ />
289
+ )}
244
290
 
245
- <Select
246
- value={filter.order || 'asc'}
247
- fieldName='order'
248
- label='Filter Order'
249
- updateField={(_section, _subSection, _field, value) => {
250
- updateFilterProp('order', filterIndex, value)
251
- if (filter.orderColumn && value !== 'column') updateFilterProp('orderColumn', filterIndex, '')
252
- }}
253
- options={filterOrderOptions}
254
- />
255
- {filter.order === 'cust' && (
256
- <FilterOrder
257
- orderedValues={filter.orderedValues || filter.values}
258
- handleFilterOrder={(index1, index2) => handleFilterOrder(index1, index2, filterIndex)}
259
- />
260
- )}
261
- {filter.order === 'column' && (
262
- <Select
263
- value={filter.orderColumn}
264
- fieldName='orderColumn'
265
- label='Order Column'
266
- updateField={(_section, _subSection, _field, value) =>
267
- updateFilterProp('orderColumn', filterIndex, value)
268
- }
269
- options={dataColumns}
270
- />
271
- )}
272
- <label>
273
- <span className='edit-label column-heading'>Default Value Set By Query String Parameter</span>
274
- <input
275
- type='text'
276
- value={filter.setByQueryParameter}
277
- onChange={e => {
278
- updateFilterProp('setByQueryParameter', filterIndex, e.target.value)
279
- }}
280
- />
281
- </label>
291
+ <Select
292
+ value={filter.order || 'asc'}
293
+ fieldName='order'
294
+ label='Filter Order'
295
+ updateField={(_section, _subSection, _field, value) => {
296
+ updateFilterProp('order', filterIndex, value)
297
+ if (filter.orderColumn && value !== 'column')
298
+ updateFilterProp('orderColumn', filterIndex, '')
299
+ }}
300
+ options={filterOrderOptions}
301
+ />
302
+ {filter.order === 'cust' && (
303
+ <FilterOrder
304
+ orderedValues={filter.orderedValues || filter.values}
305
+ handleFilterOrder={(index1, index2) =>
306
+ handleFilterOrder(index1, index2, filterIndex)
307
+ }
308
+ />
309
+ )}
310
+ {filter.order === 'column' && (
311
+ <Select
312
+ value={filter.orderColumn}
313
+ fieldName='orderColumn'
314
+ label='Order Column'
315
+ updateField={(_section, _subSection, _field, value) =>
316
+ updateFilterProp('orderColumn', filterIndex, value)
317
+ }
318
+ options={dataColumns}
319
+ />
320
+ )}
321
+ <label>
322
+ <span className='edit-label column-heading'>
323
+ Default Value Set By Query String Parameter
324
+ </span>
325
+ <input
326
+ type='text'
327
+ value={filter.setByQueryParameter}
328
+ onChange={e => {
329
+ updateFilterProp('setByQueryParameter', filterIndex, e.target.value)
330
+ }}
331
+ />
332
+ </label>
282
333
 
283
- <label>
284
- <input
285
- type='checkbox'
286
- checked={filter.showDropdown === undefined ? true : filter.showDropdown}
287
- onChange={e => {
288
- updateFilterProp('showDropdown', filterIndex, e.target.checked)
289
- }}
290
- />
291
- <span className='edit-showDropdown column-heading'>Show Filter</span>
292
- </label>
293
- </>
294
- ) : (
295
- <NestedDropdownEditor
296
- config={config}
297
- dataColumns={dataColumns}
298
- filterIndex={filterIndex}
299
- rawData={rawData}
300
- handleGroupingCustomOrder={(index1, index2) => handleFilterOrder(index1, index2, filterIndex)}
301
- handleNameChange={value => handleNameChange(filterIndex, value)}
302
- updateField={updateField}
303
- updateFilterStyle={updateFilterStyle}
304
- />
305
- )}
306
- {hasFootnotes && (
307
- <label>
308
- <input
309
- type='checkbox'
310
- checked={!!filter.filterFootnotes}
311
- onChange={e => {
312
- updateFilterProp('filterFootnotes', filterIndex, e.target.checked)
313
- }}
314
- />
315
- <span className='edit-showDropdown column-heading'>Filter Footnotes</span>
316
- </label>
317
- )}
318
- <label>
319
- <span className='edit-label column-heading'>
320
- Filter Parents{' '}
321
- <Tooltip style={{ textTransform: 'none' }}>
322
- <Tooltip.Target>
323
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
324
- </Tooltip.Target>
325
- <Tooltip.Content>
326
- <p>
327
- A selected parent's value will be used to filter the available options of this child filter.
328
- </p>
329
- </Tooltip.Content>
330
- </Tooltip>
331
- </span>
332
- <MultiSelect
333
- fieldName='parents'
334
- updateField={(_section, _subsection, _fieldname, value) => {
335
- updateFilterProp('parents', filterIndex, value)
336
- }}
337
- options={getParentFilterOptions(filterIndex)}
338
- selected={config.filters[filterIndex].parents}
339
- />
340
- </label>
341
- </FieldSetWrapper>
342
- )
343
- })}
344
- </ul>
334
+ <label>
335
+ <input
336
+ type='checkbox'
337
+ checked={filter.showDropdown === undefined ? true : filter.showDropdown}
338
+ onChange={e => {
339
+ updateFilterProp('showDropdown', filterIndex, e.target.checked)
340
+ }}
341
+ />
342
+ <span className='edit-showDropdown column-heading'>Show Filter</span>
343
+ </label>
344
+ </>
345
+ ) : (
346
+ <NestedDropdownEditor
347
+ config={config}
348
+ dataColumns={dataColumns}
349
+ filterIndex={filterIndex}
350
+ rawData={rawData}
351
+ handleGroupingCustomOrder={(index1, index2) =>
352
+ handleFilterOrder(index1, index2, filterIndex)
353
+ }
354
+ handleNameChange={value => handleNameChange(filterIndex, value)}
355
+ updateField={updateField}
356
+ updateFilterStyle={updateFilterStyle}
357
+ />
358
+ )}
359
+ {hasFootnotes && (
360
+ <label>
361
+ <input
362
+ type='checkbox'
363
+ checked={!!filter.filterFootnotes}
364
+ onChange={e => {
365
+ updateFilterProp('filterFootnotes', filterIndex, e.target.checked)
366
+ }}
367
+ />
368
+ <span className='edit-showDropdown column-heading'>Filter Footnotes</span>
369
+ </label>
370
+ )}
371
+ <label>
372
+ <span className='edit-label column-heading'>
373
+ Filter Parents{' '}
374
+ <Tooltip style={{ textTransform: 'none' }}>
375
+ <Tooltip.Target>
376
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
377
+ </Tooltip.Target>
378
+ <Tooltip.Content>
379
+ <p>
380
+ A selected parent's value will be used to filter the available options of this
381
+ child filter.
382
+ </p>
383
+ </Tooltip.Content>
384
+ </Tooltip>
385
+ </span>
386
+ <MultiSelect
387
+ fieldName='parents'
388
+ updateField={(_section, _subsection, _fieldname, value) => {
389
+ updateFilterProp('parents', filterIndex, value)
390
+ }}
391
+ options={getParentFilterOptions(filterIndex)}
392
+ selected={config.filters[filterIndex].parents}
393
+ />
394
+ </label>
395
+ </FieldSetWrapper>
396
+ </div>
397
+ )}
398
+ </Draggable>
399
+ )
400
+ })}
401
+ {provided.placeholder}
402
+ </ul>
403
+ )}
404
+ </Droppable>
405
+ </DragDropContext>
345
406
  </>
346
407
  )}
347
408
  {!config.filters && <p style={{ textAlign: 'center' }}>There are currently no filters.</p>}
@@ -0,0 +1,169 @@
1
+ import { ReactNode } from 'react'
2
+ import { CheckBox } from '../Inputs'
3
+ import { HeaderThemeSelector } from '../../HeaderThemeSelector'
4
+ import { UpdateFieldFunc } from '../../../types/UpdateFieldFunc'
5
+
6
+ export interface VisualSectionConfig {
7
+ visual?: {
8
+ border?: boolean
9
+ borderColorTheme?: boolean
10
+ accent?: boolean
11
+ background?: boolean
12
+ hideBackgroundColor?: boolean
13
+ }
14
+ theme?: string
15
+ }
16
+
17
+ export interface VisualSectionProps<TConfig extends VisualSectionConfig = VisualSectionConfig> {
18
+ /** The visualization config object */
19
+ config: TConfig
20
+
21
+ /** Update function for individual fields */
22
+ updateField: UpdateFieldFunc<TConfig>
23
+
24
+ /** Update function for the entire config (used by HeaderThemeSelector) */
25
+ updateConfig: (config: TConfig) => void
26
+
27
+ /** Optional content to render before the standard checkboxes */
28
+ beforeCheckboxes?: ReactNode
29
+
30
+ /** Optional content to render after the standard checkboxes */
31
+ afterCheckboxes?: ReactNode
32
+
33
+ /** Position of HeaderThemeSelector. Defaults to 'before' */
34
+ themeSelectorPosition?: 'before' | 'after' | 'none'
35
+
36
+ /** Whether to show the border checkbox. Defaults to true */
37
+ showBorder?: boolean
38
+
39
+ /** Whether to show the borderColorTheme checkbox. Defaults to true */
40
+ showBorderColorTheme?: boolean
41
+
42
+ /** Whether to show the accent checkbox. Defaults to true */
43
+ showAccent?: boolean
44
+
45
+ /** Whether to show the background checkbox. Defaults to true */
46
+ showBackground?: boolean
47
+
48
+ /** Whether to show the hideBackgroundColor checkbox. Defaults to true */
49
+ showHideBackgroundColor?: boolean
50
+ }
51
+
52
+ /**
53
+ * Reusable Visual section component for EditorPanels
54
+ *
55
+ * Provides common visual configuration options including:
56
+ * - Theme selection
57
+ * - Border controls
58
+ * - Background and accent styling
59
+ *
60
+ * Note: Must be wrapped in an Accordion.Section when used
61
+ *
62
+ * @example
63
+ * ```tsx
64
+ * <Accordion.Section title='Visual'>
65
+ * <VisualSection
66
+ * config={config}
67
+ * updateField={updateField}
68
+ * updateConfig={updateConfig}
69
+ * beforeCheckboxes={
70
+ * <Select
71
+ * value={config.fontSize}
72
+ * fieldName='fontSize'
73
+ * label='Font Size'
74
+ * updateField={updateField}
75
+ * options={['small', 'medium', 'large']}
76
+ * />
77
+ * }
78
+ * />
79
+ * </Accordion.Section>
80
+ * ```
81
+ */
82
+ export const VisualSection = <TConfig extends VisualSectionConfig = VisualSectionConfig>({
83
+ config,
84
+ updateField,
85
+ updateConfig,
86
+ beforeCheckboxes,
87
+ afterCheckboxes,
88
+ themeSelectorPosition = 'before',
89
+ showBorder = true,
90
+ showBorderColorTheme = true,
91
+ showAccent = true,
92
+ showBackground = true,
93
+ showHideBackgroundColor = true
94
+ }: VisualSectionProps<TConfig>) => {
95
+ const visual = config.visual || {}
96
+ const theme = config.theme
97
+
98
+ const renderThemeSelector = () => {
99
+ if (themeSelectorPosition === 'none') return null
100
+
101
+ return (
102
+ <HeaderThemeSelector
103
+ selectedTheme={theme}
104
+ onThemeSelect={theme => updateConfig({ ...config, theme } as TConfig)}
105
+ label='Theme'
106
+ />
107
+ )
108
+ }
109
+
110
+ const renderCheckboxes = () => (
111
+ <div className='checkbox-group'>
112
+ {showBorder && (
113
+ <CheckBox
114
+ value={visual.border}
115
+ section='visual'
116
+ fieldName='border'
117
+ label='Display Border'
118
+ updateField={updateField}
119
+ />
120
+ )}
121
+ {showBorderColorTheme && (
122
+ <CheckBox
123
+ value={visual.borderColorTheme}
124
+ section='visual'
125
+ fieldName='borderColorTheme'
126
+ label='Use Border Color Theme'
127
+ updateField={updateField}
128
+ />
129
+ )}
130
+ {showAccent && (
131
+ <CheckBox
132
+ value={visual.accent}
133
+ section='visual'
134
+ fieldName='accent'
135
+ label='Use Accent Style'
136
+ updateField={updateField}
137
+ />
138
+ )}
139
+ {showBackground && (
140
+ <CheckBox
141
+ value={visual.background}
142
+ section='visual'
143
+ fieldName='background'
144
+ label='Use Theme Background Color'
145
+ updateField={updateField}
146
+ />
147
+ )}
148
+ {showHideBackgroundColor && (
149
+ <CheckBox
150
+ value={visual.hideBackgroundColor}
151
+ section='visual'
152
+ fieldName='hideBackgroundColor'
153
+ label='Hide Background Color'
154
+ updateField={updateField}
155
+ />
156
+ )}
157
+ </div>
158
+ )
159
+
160
+ return (
161
+ <>
162
+ {beforeCheckboxes}
163
+ {themeSelectorPosition === 'before' && renderThemeSelector()}
164
+ {renderCheckboxes()}
165
+ {themeSelectorPosition === 'after' && renderThemeSelector()}
166
+ {afterCheckboxes}
167
+ </>
168
+ )
169
+ }