@cdc/waffle-chart 4.25.11 → 4.26.2

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.
@@ -1,10 +1,9 @@
1
- import React, { useState, useEffect, memo, useContext } from 'react'
2
- import cloneConfig from '@cdc/core/helpers/cloneConfig'
1
+ import React, { useEffect, memo, useContext } from 'react'
3
2
  import _ from 'lodash'
4
- import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
5
3
 
6
4
  import ConfigContext from '../ConfigContext'
7
5
 
6
+ import { EditorPanel as BaseEditorPanel } from '@cdc/core/components/EditorPanel/EditorPanel'
8
7
  import AdvancedEditor from '@cdc/core/components/AdvancedEditor'
9
8
  import Accordion from '@cdc/core/components/ui/Accordion'
10
9
  import Button from '@cdc/core/components/elements/Button'
@@ -12,8 +11,9 @@ import Icon from '@cdc/core/components/ui/Icon'
12
11
  import Tooltip from '@cdc/core/components/ui/Tooltip'
13
12
  import { TextField, Select, CheckBox } from '@cdc/core/components/EditorPanel/Inputs'
14
13
  import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
15
- import Layout from '@cdc/core/components/Layout'
16
- import { HeaderThemeSelector } from '@cdc/core/components/HeaderThemeSelector'
14
+ import { useFilterManagement } from '@cdc/core/hooks/useFilterManagement'
15
+ import { useDataColumns } from '@cdc/core/hooks/useDataColumns'
16
+ import { VisualSection } from '@cdc/core/components/EditorPanel/sections/VisualSection'
17
17
 
18
18
  import '@cdc/core/styles/v2/components/editor.scss'
19
19
  import WarningImage from '../images/warning.svg'
@@ -23,27 +23,16 @@ import { DATA_OPERATORS, DATA_FUNCTIONS } from '../CdcWaffleChart'
23
23
  const EditorPanel = memo(props => {
24
24
  const { config, updateConfig, loading, data, setParentConfig, isDashboard } = useContext(ConfigContext)
25
25
  const { showConfigConfirm } = props
26
- const [displayPanel, setDisplayPanel] = useState(true)
27
26
  const inputSelectStyle = condition => (condition ? { backgroundColor: '#ffd2d2', color: '#d8000c' } : {})
28
27
 
29
28
  const updateField = updateFieldFactory(config, updateConfig, true)
30
29
 
31
- useEffect(() => {
32
- // Pass up to Editor if needed
33
- if (setParentConfig) {
34
- const newConfig = convertStateToConfig()
35
- setParentConfig(newConfig)
36
- }
37
- // eslint-disable-next-line react-hooks/exhaustive-deps
38
- }, [config])
39
-
40
- useEffect(() => {
41
- if (!showConfigConfirm) {
42
- let newConfig = { ...config }
43
- delete newConfig.newViz
44
- updateConfig(newConfig)
45
- }
46
- }, [])
30
+ // Filters
31
+ const { addNewFilter, removeFilter, updateFilterProp, getFilterColumnValues } = useFilterManagement(
32
+ config,
33
+ updateConfig,
34
+ data
35
+ )
47
36
 
48
37
  useEffect(() => {
49
38
  //Verify comparate data type
@@ -61,70 +50,16 @@ const EditorPanel = memo(props => {
61
50
  }
62
51
  }, [config.dataConditionalOperator, config.dataConditionalComparate])
63
52
 
64
- const onBackClick = () => {
65
- setDisplayPanel(!displayPanel)
66
- updateConfig({
67
- ...config,
68
- showEditorPanel: !displayPanel
69
- })
70
-
71
- // if (isDashboard) {
72
- // updateConfig({ ...config, editing: false })
73
- // } else {
74
- // setDisplayPanel(!displayPanel)
75
- // }
76
- }
77
-
78
- const convertStateToConfig = () => {
79
- let strippedState = cloneConfig(config)
80
- delete strippedState.newViz
81
- delete strippedState.runtime
82
-
83
- return strippedState
84
- }
85
-
86
- const addNewFilter = () => {
87
- let filters = config.filters ? [...config.filters] : []
88
- filters.push({ values: [] })
89
- updateConfig({ ...config, filters })
90
- }
91
-
92
- const removeFilter = index => {
93
- let filters = [...config.filters]
94
- filters.splice(index, 1)
95
- updateConfig({ ...config, filters })
96
- }
97
-
98
- const updateFilterProp = (name, index, value) => {
99
- let filters = [...config.filters]
100
- filters[index][name] = value
101
- updateConfig({ ...config, filters })
102
- }
103
-
104
- const getColumns = (filter = true) => {
105
- let columns = {}
106
-
107
- data.map(row => Object.keys(row).forEach(columnName => (columns[columnName] = true)))
108
-
109
- return Object.keys(columns)
110
- }
111
-
112
- const getFilterColumnValues = index => {
113
- let filterDataOptions = []
114
- const filterColumnName = config.filters[index].columnName
115
- if (data && filterColumnName) {
116
- data.forEach(function (row) {
117
- if (undefined !== row[filterColumnName] && -1 === filterDataOptions.indexOf(row[filterColumnName])) {
118
- filterDataOptions.push(row[filterColumnName])
119
- }
120
- })
121
- filterDataOptions.sort()
122
- }
123
- return filterDataOptions
124
- }
53
+ // Extract column names from data with memoization (replaces getColumns)
54
+ const columns = useDataColumns(data)
125
55
  //visualizationType
126
56
 
127
- const approvedWaffleChartOptions = ['Waffle', 'Gauge']
57
+ const approvedWaffleChartOptions = [
58
+ { value: 'Waffle', label: 'Waffle' },
59
+ { value: 'TP5 Waffle', label: 'TP5 Style Waffle' },
60
+ { value: 'Gauge', label: 'Gauge' },
61
+ { value: 'TP5 Gauge', label: 'TP5 Style Gauge' }
62
+ ]
128
63
 
129
64
  const editorContent = (
130
65
  <Accordion>
@@ -136,15 +71,6 @@ const EditorPanel = memo(props => {
136
71
  updateField={updateField}
137
72
  options={approvedWaffleChartOptions}
138
73
  />
139
- {config.visualizationType === 'Gauge' && (
140
- <Select
141
- value={config.visualizationSubType}
142
- fieldName='visualizationSubType'
143
- label='Chart Subtype'
144
- updateField={updateField}
145
- options={['Linear']}
146
- />
147
- )}
148
74
  <TextField
149
75
  value={config.title}
150
76
  fieldName='title'
@@ -210,7 +136,7 @@ const EditorPanel = memo(props => {
210
136
  label='Data Column'
211
137
  updateField={updateField}
212
138
  initial='Select'
213
- options={getColumns()}
139
+ options={columns}
214
140
  />
215
141
  </div>
216
142
 
@@ -237,7 +163,7 @@ const EditorPanel = memo(props => {
237
163
  fieldName='dataConditionalColumn'
238
164
  updateField={updateField}
239
165
  initial='Select'
240
- options={getColumns()}
166
+ options={columns}
241
167
  />
242
168
  </div>
243
169
  <div className='cove-accordion__panel-col'>
@@ -297,7 +223,7 @@ const EditorPanel = memo(props => {
297
223
  label='Data Column'
298
224
  updateField={updateField}
299
225
  initial='Select'
300
- options={getColumns()}
226
+ options={columns}
301
227
  />
302
228
  <Select
303
229
  value={config.dataDenomFunction || ''}
@@ -329,7 +255,7 @@ const EditorPanel = memo(props => {
329
255
  </div>
330
256
  </li>
331
257
  </ul>
332
- {config.visualizationType === 'Gauge' && (
258
+ {(config.visualizationType === 'Gauge' || config.visualizationType === 'TP5 Gauge') && (
333
259
  <>
334
260
  <hr className='cove-accordion__divider' />
335
261
  <div className='cove-accordion__panel-section reverse-labels'>
@@ -387,7 +313,7 @@ const EditorPanel = memo(props => {
387
313
  <Select
388
314
  label='Column'
389
315
  value={filter.columnName || ''}
390
- options={getColumns()}
316
+ options={columns}
391
317
  initial='- Select Option -'
392
318
  onChange={e => {
393
319
  updateFilterProp('columnName', index, e.target.value)
@@ -410,8 +336,8 @@ const EditorPanel = memo(props => {
410
336
  Add Filter
411
337
  </Button>
412
338
  </Accordion.Section>
413
- <Accordion.Section title='Visual' className='panel-visual'>
414
- {config.visualizationType !== 'Gauge' && (
339
+ {config.visualizationType !== 'Gauge' && config.visualizationType !== 'TP5 Gauge' && (
340
+ <Accordion.Section title='Chart Settings'>
415
341
  <Select
416
342
  value={config.shape}
417
343
  fieldName='shape'
@@ -419,134 +345,111 @@ const EditorPanel = memo(props => {
419
345
  updateField={updateField}
420
346
  options={['circle', 'square', 'person']}
421
347
  />
422
- )}
423
- {config.visualizationType !== 'Gauge' && (
424
- <div
425
- className='cove-accordion__panel-row cove-accordion__small-inputs'
426
- style={{ marginTop: '1rem', marginBottom: '1rem' }}
427
- >
428
- <div className='cove-accordion__panel-col'>
429
- <TextField
430
- type='number'
431
- value={config.nodeWidth}
432
- fieldName='nodeWidth'
433
- label='Width'
434
- updateField={updateField}
435
- />
436
- </div>
437
- <div className='cove-accordion__panel-col'>
438
- <TextField
439
- type='number'
440
- value={config.nodeSpacer}
441
- fieldName='nodeSpacer'
442
- label='Spacer'
443
- updateField={updateField}
444
- />
445
- </div>
446
- </div>
447
- )}
448
-
449
- <div className='cove-input-group'>
450
- {config.visualizationType !== 'Gauge' && (
451
- <Select
452
- value={config.orientation}
453
- fieldName='orientation'
454
- label='Layout'
455
- updateField={updateField}
456
- options={['horizontal', 'vertical']}
457
- />
458
- )}
459
- </div>
460
-
461
- <div className='cove-input-group'>
462
- <label>
463
- <span className='edit-label column-heading cove-input__label'>Data Point Font Size</span>
464
- </label>
465
- <div className='cove-accordion__panel-row cove-accordion__small-inputs align-center'>
466
- <div className='cove-accordion__panel-col'>
467
- <TextField type='number' value={config.fontSize} fieldName='fontSize' updateField={updateField} />
468
- </div>
469
- <div className='cove-accordion__panel-col' style={{ display: 'flex', alignItems: 'center' }}>
470
- <label className='accordion__panel-label--muted'> default (50px)</label>
471
- </div>
472
- </div>
473
- </div>
348
+ {config.visualizationType !== 'TP5 Waffle' && (
349
+ <>
350
+ <div
351
+ className='cove-accordion__panel-row cove-accordion__small-inputs'
352
+ style={{ marginTop: '1rem', marginBottom: '1rem' }}
353
+ >
354
+ <div className='cove-accordion__panel-col'>
355
+ <TextField
356
+ type='number'
357
+ value={config.nodeWidth}
358
+ fieldName='nodeWidth'
359
+ label='Width'
360
+ updateField={updateField}
361
+ />
362
+ </div>
363
+ <div className='cove-accordion__panel-col'>
364
+ <TextField
365
+ type='number'
366
+ value={config.nodeSpacer}
367
+ fieldName='nodeSpacer'
368
+ label='Spacer'
369
+ updateField={updateField}
370
+ />
371
+ </div>
372
+ </div>
474
373
 
475
- <Select
476
- value={config.overallFontSize}
477
- fieldName='overallFontSize'
478
- label='Overall Font Size'
479
- updateField={updateField}
480
- options={['small', 'medium', 'large']}
481
- />
374
+ <div className='cove-input-group'>
375
+ <Select
376
+ value={config.orientation}
377
+ fieldName='orientation'
378
+ label='Layout'
379
+ updateField={updateField}
380
+ options={['horizontal', 'vertical']}
381
+ />
382
+ </div>
482
383
 
483
- <HeaderThemeSelector
484
- selectedTheme={config.theme}
485
- onThemeSelect={theme => updateConfig({ ...config, theme })}
486
- label='Theme'
487
- />
384
+ <div className='cove-input-group'>
385
+ <label>
386
+ <span className='edit-label column-heading cove-input__label'>Data Point Font Size</span>
387
+ </label>
388
+ <div className='cove-accordion__panel-row cove-accordion__small-inputs align-center'>
389
+ <div className='cove-accordion__panel-col'>
390
+ <TextField type='number' value={config.fontSize} fieldName='fontSize' updateField={updateField} />
391
+ </div>
392
+ <div className='cove-accordion__panel-col' style={{ display: 'flex', alignItems: 'center' }}>
393
+ <label className='accordion__panel-label--muted'> default (50px)</label>
394
+ </div>
395
+ </div>
396
+ </div>
397
+ </>
398
+ )}
399
+ </Accordion.Section>
400
+ )}
488
401
 
489
- <div className='cove-accordion__panel-section reverse-labels'>
490
- <CheckBox
491
- value={config.visual.border}
492
- section='visual'
493
- fieldName='border'
494
- label='Display Border'
495
- updateField={updateField}
496
- />
497
- <CheckBox
498
- value={config.visual.borderColorTheme}
499
- section='visual'
500
- fieldName='borderColorTheme'
501
- label='Use theme border color'
502
- updateField={updateField}
503
- />
504
- <CheckBox
505
- value={config.visual.accent}
506
- section='visual'
507
- fieldName='accent'
508
- label='Use Accent Style'
509
- updateField={updateField}
510
- />
402
+ {/* Visual section for TP5 style */}
403
+ {(config.visualizationType === 'TP5 Waffle' || config.visualizationType === 'TP5 Gauge') && (
404
+ <Accordion.Section title='Visual'>
511
405
  <CheckBox
512
- value={config.visual.background}
406
+ value={config.visual?.whiteBackground}
513
407
  section='visual'
514
- fieldName='background'
515
- label='Use Theme Background Color'
408
+ fieldName='whiteBackground'
409
+ label='Use White Background Style'
516
410
  updateField={updateField}
517
411
  />
518
- <CheckBox
519
- value={config.visual.hideBackgroundColor}
520
- section='visual'
521
- fieldName='hideBackgroundColor'
522
- label='Hide Background Color'
412
+ </Accordion.Section>
413
+ )}
414
+
415
+ {/* Visual section for other styles */}
416
+ {config.visualizationType !== 'TP5 Waffle' && config.visualizationType !== 'TP5 Gauge' && (
417
+ <Accordion.Section title='Visual'>
418
+ <VisualSection
419
+ config={config}
523
420
  updateField={updateField}
421
+ updateConfig={updateConfig}
422
+ beforeCheckboxes={
423
+ <Select
424
+ value={config.overallFontSize}
425
+ fieldName='overallFontSize'
426
+ label='Overall Font Size'
427
+ updateField={updateField}
428
+ options={['small', 'medium', 'large']}
429
+ />
430
+ }
524
431
  />
525
- </div>
526
- </Accordion.Section>
432
+ </Accordion.Section>
433
+ )}
527
434
  </Accordion>
528
435
  )
529
436
 
530
- if (loading) return null
531
-
532
437
  return (
533
- <ErrorBoundary component='EditorPanel'>
534
- <>
535
- <Layout.Sidebar
536
- displayPanel={displayPanel}
537
- onBackClick={onBackClick}
538
- isDashboard={isDashboard}
539
- title='Configure Waffle Chart'
540
- showEditorPanel={displayPanel}
541
- >
542
- <>
543
- {editorContent}
544
- <AdvancedEditor loadConfig={updateConfig} config={config} convertStateToConfig={convertStateToConfig} />
545
- </>
546
- </Layout.Sidebar>
547
- {props.children}
548
- </>
549
- </ErrorBoundary>
438
+ <BaseEditorPanel
439
+ config={config}
440
+ updateConfig={updateConfig}
441
+ loading={loading}
442
+ setParentConfig={setParentConfig}
443
+ isDashboard={isDashboard}
444
+ title='Configure Waffle Chart'
445
+ >
446
+ {({ convertStateToConfig }) => (
447
+ <>
448
+ {editorContent}
449
+ <AdvancedEditor loadConfig={updateConfig} config={config} convertStateToConfig={convertStateToConfig} />
450
+ </>
451
+ )}
452
+ </BaseEditorPanel>
550
453
  )
551
454
  })
552
455
 
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
3
+ <svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="481.000000pt" height="563.000000pt" viewBox="0 0 481.000000 563.000000" preserveAspectRatio="xMidYMid meet">
4
+ <g transform="translate(0.000000,563.000000) scale(0.100000,-0.100000)" fill="#2c7a99" stroke="none">
5
+ <path d="M890 5514 c-161 -43 -308 -201 -354 -381 -14 -55 -16 -314 -16 -2527 0 -2015 2 -2466 13 -2466 7 0 51 14 97 30 47 17 92 33 100 35 8 2 16 4 18 5 1 2 7 4 12 5 6 1 11 3 13 4 1 2 13 4 26 7 14 2 30 9 38 15 7 6 13 8 13 4 0 -4 7 -2 15 5 8 7 15 10 15 6 0 -3 17 1 38 10 49 21 856 293 905 305 21 5 45 13 55 19 9 5 40 16 67 24 28 8 162 52 299 99 l249 85 151 -53 c83 -29 253 -90 379 -134 264 -94 556 -198 974 -346 164 -58 308 -110 321 -115 13 -6 27 -10 33 -10 11 0 8 4951 -4 4994 -22 80 -84 183 -152 252 -75 76 -141 114 -233 133 -38 8 -477 11 -1535 10 -1268 0 -1490 -3 -1537 -15z m2687 -1570 c49 -31 82 -134 64 -201 -10 -37 -41 -64 -422 -369 -227 -181 -430 -341 -452 -356 -43 -30 -84 -35 -136 -17 -17 6 -156 107 -309 224 l-277 213 -40 -31 c-50 -39 -318 -259 -325 -267 -3 -3 -27 -24 -55 -45 -27 -22 -55 -44 -61 -50 -86 -77 -253 -202 -277 -207 -20 -4 -53 -1 -84 8 -69 19 -105 64 -111 140 -6 70 8 100 69 149 105 84 134 107 168 133 19 15 92 74 164 132 71 58 144 116 162 130 86 68 114 90 187 150 124 102 156 120 206 120 23 0 57 -7 75 -16 18 -9 145 -103 282 -209 283 -217 293 -224 303 -210 4 6 42 39 86 74 43 35 88 71 100 81 31 27 153 127 231 190 37 30 111 91 164 135 142 119 217 145 288 99z m-1431 -983 c60 -44 59 -29 62 -601 3 -596 4 -589 -70 -638 -76 -49 -171 -29 -228 50 l-30 41 0 526 c0 591 -3 569 75 623 35 25 48 28 100 25 44 -2 69 -9 91 -26z m1252 15 c37 -19 67 -55 81 -95 8 -25 10 -183 9 -555 -3 -579 -1 -555 -71 -603 -67 -45 -144 -35 -211 27 l-41 39 -3 543 -2 544 23 35 c46 68 149 99 215 65z m-610 -343 c71 -57 71 -57 71 -448 0 -322 -1 -353 -19 -392 -27 -60 -83 -95 -155 -95 -63 -1 -95 16 -132 72 -23 33 -23 36 -23 407 0 406 -1 401 57 455 34 32 54 38 116 34 35 -2 57 -11 85 -33z m-1313 -142 c54 -24 92 -71 100 -124 4 -23 5 -159 3 -302 -3 -293 -3 -293 -82 -343 -66 -41 -160 -30 -207 24 -34 40 -39 84 -39 361 0 303 3 322 61 366 53 39 105 45 164 18z"/>
6
+ </g>
7
+ </svg>
@@ -1,6 +1,5 @@
1
1
  // Remove custom theme mixin - use core styles instead
2
2
 
3
-
4
3
  .type-dashboard .type-waffle-chart .cove-component__content {
5
4
  padding-top: 0;
6
5
  }
@@ -130,4 +129,220 @@
130
129
  font-size: 20px;
131
130
  }
132
131
  }
132
+ }
133
+
134
+ // TP5 Style for Waffle Chart
135
+ .type-waffle-chart {
136
+ &.waffle__style--tp5 {
137
+ .cove-component__content {
138
+ background: none !important;
139
+ container-type: inline-size;
140
+ }
141
+
142
+ .cdc-callout {
143
+ box-shadow: 0 2px 4px rgb(159 159 159 / 10%);
144
+ margin: 0 !important;
145
+ padding: 1.5rem;
146
+ background-color: var(--colors-cyan-10, #eff9fa);
147
+ border: 1px solid var(--colors-cyan-15, #dff2f6);
148
+ position: relative;
149
+ border-radius: 0.25rem;
150
+ flex-wrap: wrap;
151
+ gap: 0.5rem;
152
+ }
153
+
154
+ .cdc-callout__icon {
155
+ position: absolute;
156
+ font-family: var(--icons-cdc);
157
+ top: -0.65rem;
158
+ right: 1rem;
159
+ font-size: 2rem;
160
+ color: var(--colors-cyan-60v);
161
+ }
162
+
163
+ .cdc-callout__flag {
164
+ position: absolute;
165
+ top: -0.25rem;
166
+ right: 1.08rem;
167
+ width: 1.85rem;
168
+ height: auto;
169
+ }
170
+
171
+ .cove-waffle-chart {
172
+ align-items: flex-start;
173
+ padding: 0;
174
+ row-gap: 1rem;
175
+ flex-wrap: nowrap;
176
+ }
177
+
178
+ // Container query: wrap when width is less than 576px
179
+ @container (max-width: 576px) {
180
+ .cove-waffle-chart {
181
+ flex-wrap: wrap;
182
+
183
+ .cove-waffle-chart__chart {
184
+ width: 100% !important;
185
+ }
186
+ }
187
+ }
188
+
189
+ .cove-waffle-chart__chart {
190
+ margin-bottom: 0;
191
+ }
192
+
193
+ .cove-waffle-chart__data {
194
+ margin-bottom: 0;
195
+ align-content: flex-start;
196
+ }
197
+
198
+ .cdc-callout__heading {
199
+ font-family: var(--fonts-nunito) !important;
200
+ width: 100%;
201
+ font-size: 1.1rem;
202
+ }
203
+
204
+ .cove-waffle-chart__data--text {
205
+ margin-top: 0.5rem;
206
+ font-size: 1rem;
207
+ }
208
+
209
+ .cove-waffle-chart__subtext {
210
+ font-size: 1rem;
211
+ }
212
+
213
+ .cove-waffle-chart__data--primary {
214
+ font-size: 2rem;
215
+ color: var(--colors-cyan-60v);
216
+ font-weight: 400;
217
+ }
218
+
219
+ // White background variant (when "Use White Background Style" is checked)
220
+ &.white-background-style {
221
+ .cove-component__content {
222
+ background: white !important;
223
+ }
224
+
225
+ .cdc-callout {
226
+ background: transparent !important;
227
+ border: 1px solid #009ec1 !important;
228
+ box-shadow: 0 2px 4px rgb(159 159 159 / 10%);
229
+ }
230
+ }
231
+ }
232
+
233
+ // TP5 Style for Gauge Chart
234
+ &.gauge__style--tp5 {
235
+ .cove-component__content {
236
+ background: none !important;
237
+ container-type: inline-size;
238
+ }
239
+
240
+ .cdc-callout {
241
+ box-shadow: 0 2px 4px rgb(159 159 159 / 10%);
242
+ margin: 0 !important;
243
+ padding: 1.5rem;
244
+ background-color: var(--colors-cyan-10, #eff9fa);
245
+ border: 1px solid var(--colors-cyan-15, #dff2f6);
246
+ position: relative;
247
+ border-radius: 0.25rem;
248
+ gap: 0.5rem;
249
+ }
250
+
251
+ .cdc-callout__flag {
252
+ position: absolute;
253
+ top: -0.25rem;
254
+ right: 1.08rem;
255
+ width: 1.85rem;
256
+ height: auto;
257
+ }
258
+
259
+ .cdc-callout__heading {
260
+ font-family: var(--fonts-nunito) !important;
261
+ width: 100%;
262
+ font-size: 1.1rem;
263
+ }
264
+
265
+ .cove-gauge-chart {
266
+ padding: 0;
267
+ }
268
+
269
+ .cove-gauge-chart__chart {
270
+ width: 100%;
271
+ }
272
+
273
+ .cove-gauge-chart__chart>svg {
274
+ display: block;
275
+ width: 100%;
276
+ }
277
+
278
+ .cove-gauge-chart__body {
279
+ flex-wrap: nowrap;
280
+ gap: 1rem;
281
+ row-gap: 0.5rem;
282
+ margin-bottom: 0.5rem;
283
+ align-items: flex-start; // Align to top instead of center when content varies
284
+ }
285
+
286
+ // Container query: wrap when width is less than 576px
287
+ @container (max-width: 576px) {
288
+ .cove-gauge-chart__body {
289
+ flex-wrap: wrap;
290
+
291
+ .cove-gauge-chart__value-section {
292
+ width: 100%;
293
+ }
294
+ }
295
+ }
296
+
297
+ .cove-gauge-chart__value-section {
298
+ width: auto;
299
+ padding-top: 1px;
300
+ display: flex;
301
+ flex-direction: column;
302
+ justify-content: flex-start;
303
+ }
304
+
305
+ .cove-gauge-chart__content {
306
+ font-size: 1rem;
307
+ min-height: 1rem; // Ensure minimum height even when empty
308
+ }
309
+
310
+ .cove-waffle-chart__data--primary {
311
+ font-size: 2rem;
312
+ color: var(--colors-cyan-60v);
313
+ font-weight: 400;
314
+ line-height: 1;
315
+ }
316
+
317
+ .cove-waffle-chart__data--text {
318
+ margin-top: 0;
319
+ font-size: 1rem;
320
+ }
321
+
322
+ .cove-waffle-chart__subtext {
323
+ font-size: 1rem;
324
+ margin-top: auto;
325
+ }
326
+
327
+ // White background variant (when "Use White Background Style" is checked)
328
+ &.white-background-style {
329
+ .cove-component__content {
330
+ background: white !important;
331
+ }
332
+
333
+ .cdc-callout {
334
+ background: transparent !important;
335
+ border: 1px solid #009ec1 !important;
336
+ box-shadow: 0 2px 4px rgb(159 159 159 / 10%);
337
+ }
338
+ }
339
+ }
340
+ }
341
+
342
+ // Center gauge value when there's no message (standalone only, not in dashboard rows)
343
+ .type-waffle-chart.gauge__style--tp5:not(.row *) {
344
+ .cove-gauge-chart__body.d-flex.justify-content-center {
345
+ justify-content: center;
346
+ width: 100%;
347
+ }
133
348
  }
@@ -3,7 +3,7 @@ import ChartActions from './chart.actions'
3
3
 
4
4
  type ChartState = {
5
5
  config?: Config
6
- container: any
6
+ container: HTMLElement | null
7
7
  coveLoadedHasRan: boolean
8
8
  loading: boolean
9
9
  preview: boolean
@@ -7,5 +7,5 @@ describe('Waffle Chart', () => {
7
7
  const pkgDir = path.join(__dirname, '..')
8
8
  const result = testStandaloneBuild(pkgDir)
9
9
  expect(result).toBe(true)
10
- })
10
+ }, 300000)
11
11
  })