@cdc/waffle-chart 4.25.10 → 4.25.11
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.
- package/dist/cdcwafflechart.js +8535 -7913
- package/package.json +3 -4
- package/src/CdcWaffleChart.tsx +9 -23
- package/src/_stories/WaffleChart.Editor.stories.tsx +54 -50
- package/src/_stories/WaffleChart.stories.tsx +18 -1
- package/src/components/EditorPanel.jsx +91 -179
- package/src/scss/main.scss +2 -12
- package/src/scss/waffle-chart.scss +10 -24
- package/src/store/chart.reducer.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/waffle-chart",
|
|
3
|
-
"version": "4.25.
|
|
3
|
+
"version": "4.25.11",
|
|
4
4
|
"description": "React component for displaying a single piece of data in a card module",
|
|
5
5
|
"moduleName": "CdcWaffleChart",
|
|
6
6
|
"main": "dist/cdcwafflechart",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"license": "Apache-2.0",
|
|
28
28
|
"homepage": "https://github.com/CDCgov/cdc-open-viz#readme",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@cdc/core": "^4.25.
|
|
30
|
+
"@cdc/core": "^4.25.11",
|
|
31
31
|
"@visx/shape": "^3.12.0",
|
|
32
32
|
"@visx/text": "^3.12.0",
|
|
33
33
|
"chroma": "0.0.1",
|
|
@@ -40,11 +40,10 @@
|
|
|
40
40
|
"react-dom": "^18.2.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@rollup/plugin-dsv": "^3.0.2",
|
|
44
43
|
"@vitejs/plugin-react": "^4.3.4",
|
|
45
44
|
"vite": "^4.4.11",
|
|
46
45
|
"vite-plugin-css-injected-by-js": "^2.4.0",
|
|
47
46
|
"vite-plugin-svgr": "^2.4.0"
|
|
48
47
|
},
|
|
49
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "5f09a137c22f454111ab5f4cd7fdf1d2d58e31bd"
|
|
50
49
|
}
|
package/src/CdcWaffleChart.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useReducer, useState } from 'react'
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react'
|
|
2
2
|
|
|
3
3
|
// visx
|
|
4
4
|
import { Circle, Bar } from '@visx/shape'
|
|
@@ -13,6 +13,7 @@ import ResizeObserver from 'resize-observer-polyfill'
|
|
|
13
13
|
import { Config } from './types/Config'
|
|
14
14
|
import getViewport from '@cdc/core/helpers/getViewport'
|
|
15
15
|
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
16
|
+
import { DATA_OPERATORS } from '@cdc/core/helpers/constants'
|
|
16
17
|
|
|
17
18
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
18
19
|
import Loading from '@cdc/core/components/Loading'
|
|
@@ -67,7 +68,7 @@ const WaffleChart = ({ config, isEditor, link = '', showConfigConfirm, updateCon
|
|
|
67
68
|
const gaugeColor = config.visual.colors[config.theme]
|
|
68
69
|
let dataFontSize = config.fontSize ? { fontSize: config.fontSize + 'px' } : null
|
|
69
70
|
|
|
70
|
-
const
|
|
71
|
+
const [dataPercentage, waffleDenominator, waffleNumerator] = useMemo(() => {
|
|
71
72
|
//If either the column or function aren't set, do not calculate
|
|
72
73
|
if (!dataColumn || !dataFunction) {
|
|
73
74
|
return ''
|
|
@@ -239,22 +240,20 @@ const WaffleChart = ({ config, isEditor, link = '', showConfigConfirm, updateCon
|
|
|
239
240
|
applyPrecision(waffleNumerator)
|
|
240
241
|
]
|
|
241
242
|
}, [
|
|
242
|
-
dataColumn,
|
|
243
|
-
dataFunction,
|
|
244
243
|
config.data,
|
|
245
244
|
filters,
|
|
245
|
+
dataColumn,
|
|
246
|
+
dataFunction,
|
|
246
247
|
dataConditionalColumn,
|
|
247
248
|
dataConditionalOperator,
|
|
248
249
|
dataConditionalComparate,
|
|
249
250
|
customDenom,
|
|
251
|
+
dataDenom,
|
|
250
252
|
dataDenomColumn,
|
|
251
253
|
dataDenomFunction,
|
|
252
|
-
roundToPlace
|
|
253
|
-
dataDenom
|
|
254
|
+
roundToPlace
|
|
254
255
|
])
|
|
255
256
|
|
|
256
|
-
const [dataPercentage, waffleDenominator, waffleNumerator] = calculateData()
|
|
257
|
-
|
|
258
257
|
const buildWaffle = useCallback(() => {
|
|
259
258
|
let waffleData = []
|
|
260
259
|
let nodeWidthNum = parseInt(nodeWidth, 10)
|
|
@@ -579,18 +578,5 @@ export const DATA_FUNCTIONS = [
|
|
|
579
578
|
DATA_FUNCTION_SUM
|
|
580
579
|
]
|
|
581
580
|
|
|
582
|
-
export
|
|
583
|
-
export
|
|
584
|
-
export const DATA_OPERATOR_LESSEQUAL = '<='
|
|
585
|
-
export const DATA_OPERATOR_GREATEREQUAL = '>='
|
|
586
|
-
export const DATA_OPERATOR_EQUAL = '='
|
|
587
|
-
export const DATA_OPERATOR_NOTEQUAL = '≠'
|
|
588
|
-
|
|
589
|
-
export const DATA_OPERATORS = [
|
|
590
|
-
DATA_OPERATOR_LESS,
|
|
591
|
-
DATA_OPERATOR_GREATER,
|
|
592
|
-
DATA_OPERATOR_LESSEQUAL,
|
|
593
|
-
DATA_OPERATOR_GREATEREQUAL,
|
|
594
|
-
DATA_OPERATOR_EQUAL,
|
|
595
|
-
DATA_OPERATOR_NOTEQUAL
|
|
596
|
-
]
|
|
581
|
+
// Re-export DATA_OPERATORS for backward compatibility
|
|
582
|
+
export { DATA_OPERATORS }
|
|
@@ -100,8 +100,12 @@ export const GeneralSectionTests: Story = {
|
|
|
100
100
|
// TEST 3: Show Title Toggle
|
|
101
101
|
// Expectation: Header region appears / disappears (DOM visibility change).
|
|
102
102
|
// ============================================================================
|
|
103
|
-
|
|
104
|
-
const
|
|
103
|
+
// Find show title checkbox by label text
|
|
104
|
+
const showTitleCheckbox = Array.from(canvasElement.querySelectorAll('input[type="checkbox"]')).find(input => {
|
|
105
|
+
const label = input.closest('label')
|
|
106
|
+
return label?.textContent?.includes('show title')
|
|
107
|
+
}) as HTMLInputElement
|
|
108
|
+
const checkboxWrapper = showTitleCheckbox?.closest('label.checkbox')
|
|
105
109
|
expect(showTitleCheckbox).toBeTruthy()
|
|
106
110
|
expect(checkboxWrapper).toBeTruthy()
|
|
107
111
|
|
|
@@ -110,7 +114,7 @@ export const GeneralSectionTests: Story = {
|
|
|
110
114
|
'Title Toggle',
|
|
111
115
|
() => showTitleCheckbox.checked,
|
|
112
116
|
async () => {
|
|
113
|
-
await userEvent.click(
|
|
117
|
+
await userEvent.click(showTitleCheckbox)
|
|
114
118
|
},
|
|
115
119
|
(before, after) => after === !wasChecked
|
|
116
120
|
)
|
|
@@ -128,7 +132,7 @@ export const GeneralSectionTests: Story = {
|
|
|
128
132
|
'Title Toggle Reset',
|
|
129
133
|
() => showTitleCheckbox.checked,
|
|
130
134
|
async () => {
|
|
131
|
-
await userEvent.click(
|
|
135
|
+
await userEvent.click(showTitleCheckbox)
|
|
132
136
|
},
|
|
133
137
|
(before, after) => after === wasChecked
|
|
134
138
|
)
|
|
@@ -253,7 +257,15 @@ export const DataSectionTests: Story = {
|
|
|
253
257
|
'Clear Conditional Value',
|
|
254
258
|
getValueText,
|
|
255
259
|
async () => {
|
|
260
|
+
// Clear the conditional column to fully reset the filter
|
|
261
|
+
const conditionalColumnSelect = canvasElement.querySelector(
|
|
262
|
+
'select[name="dataConditionalColumn"]'
|
|
263
|
+
) as HTMLSelectElement
|
|
264
|
+
await userEvent.selectOptions(conditionalColumnSelect, '')
|
|
256
265
|
await userEvent.clear(conditionalValueInput)
|
|
266
|
+
conditionalValueInput.blur() // Trigger change event
|
|
267
|
+
// Wait for debounced input processing (TextField uses 500ms debounce)
|
|
268
|
+
await new Promise(resolve => setTimeout(resolve, 600))
|
|
257
269
|
},
|
|
258
270
|
(before, after) => after !== before
|
|
259
271
|
)
|
|
@@ -312,7 +324,7 @@ export const DataSectionTests: Story = {
|
|
|
312
324
|
'Custom Denominator Toggle',
|
|
313
325
|
getValueText,
|
|
314
326
|
async () => {
|
|
315
|
-
await userEvent.click(
|
|
327
|
+
await userEvent.click(customDenomCheckbox)
|
|
316
328
|
},
|
|
317
329
|
(before, after) => after !== before
|
|
318
330
|
)
|
|
@@ -404,32 +416,6 @@ export const DataSectionTests: Story = {
|
|
|
404
416
|
},
|
|
405
417
|
(before, after) => after !== before && after.endsWith('deaths')
|
|
406
418
|
)
|
|
407
|
-
|
|
408
|
-
// ============================================================================
|
|
409
|
-
// TEST 16: Add Filter (state = Alaska)
|
|
410
|
-
// Expectation: Primary value text changes after filter applied.
|
|
411
|
-
// ============================================================================
|
|
412
|
-
const addFilterButton = Array.from(canvasElement.querySelectorAll('button')).find(
|
|
413
|
-
b => (b as HTMLButtonElement).textContent?.trim() === 'Add Filter'
|
|
414
|
-
) as HTMLButtonElement
|
|
415
|
-
await performAndAssert(
|
|
416
|
-
'Add Filter',
|
|
417
|
-
getValueText,
|
|
418
|
-
async () => {
|
|
419
|
-
await userEvent.click(addFilterButton)
|
|
420
|
-
|
|
421
|
-
await waitForPresence('.filters-list .edit-block:last-of-type', canvasElement)
|
|
422
|
-
|
|
423
|
-
const newFilter = canvasElement.querySelector('.filters-list .edit-block:last-of-type') as HTMLElement
|
|
424
|
-
const [colSelect, valSelect] = Array.from(newFilter.querySelectorAll('select')) as HTMLSelectElement[]
|
|
425
|
-
await userEvent.selectOptions(colSelect, 'state')
|
|
426
|
-
|
|
427
|
-
await waitForOptionsToPopulate(valSelect)
|
|
428
|
-
|
|
429
|
-
await userEvent.selectOptions(valSelect, 'Alaska')
|
|
430
|
-
},
|
|
431
|
-
(before, after) => after !== before
|
|
432
|
-
)
|
|
433
419
|
}
|
|
434
420
|
}
|
|
435
421
|
|
|
@@ -618,7 +604,7 @@ export const VisualSectionTests: Story = {
|
|
|
618
604
|
return node?.getAttribute('fill') || ''
|
|
619
605
|
}
|
|
620
606
|
|
|
621
|
-
const themeButtons = Array.from(canvasElement.querySelectorAll('.color-palette
|
|
607
|
+
const themeButtons = Array.from(canvasElement.querySelectorAll('.color-palette button')) as HTMLElement[]
|
|
622
608
|
expect(themeButtons.length).toBeGreaterThan(1)
|
|
623
609
|
await performAndAssert(
|
|
624
610
|
'Theme Change',
|
|
@@ -638,9 +624,13 @@ export const VisualSectionTests: Story = {
|
|
|
638
624
|
// ============================================================================
|
|
639
625
|
const contentClassSig = () => Array.from(contentContainer().classList).sort().join(' ')
|
|
640
626
|
|
|
641
|
-
|
|
627
|
+
// Find border checkbox by label text instead of name attribute
|
|
628
|
+
const borderCheckbox = Array.from(canvasElement.querySelectorAll('input[type="checkbox"]')).find(input => {
|
|
629
|
+
const label = input.closest('label')
|
|
630
|
+
return label?.textContent?.includes('Display Border')
|
|
631
|
+
}) as HTMLInputElement
|
|
642
632
|
expect(borderCheckbox).toBeTruthy()
|
|
643
|
-
const borderWrapper = borderCheckbox.closest('.
|
|
633
|
+
const borderWrapper = borderCheckbox.closest('label.checkbox') as HTMLElement
|
|
644
634
|
expect(borderWrapper).toBeTruthy()
|
|
645
635
|
const borderStyleSig = () => {
|
|
646
636
|
const el = contentContainer()
|
|
@@ -656,7 +646,7 @@ export const VisualSectionTests: Story = {
|
|
|
656
646
|
'Border Toggle',
|
|
657
647
|
borderStyleSig,
|
|
658
648
|
async () => {
|
|
659
|
-
await userEvent.click(
|
|
649
|
+
await userEvent.click(borderCheckbox)
|
|
660
650
|
},
|
|
661
651
|
(before, after) =>
|
|
662
652
|
before.classes !== after.classes ||
|
|
@@ -669,17 +659,21 @@ export const VisualSectionTests: Story = {
|
|
|
669
659
|
// TEST 9: Theme Border Color Toggle
|
|
670
660
|
// Expectation: Class 'component--has-borderColorTheme' toggles.
|
|
671
661
|
// ============================================================================
|
|
672
|
-
|
|
673
|
-
|
|
662
|
+
// Find border color theme checkbox by label text
|
|
663
|
+
const borderColorThemeCheckbox = Array.from(canvasElement.querySelectorAll('input[type="checkbox"]')).find(
|
|
664
|
+
input => {
|
|
665
|
+
const label = input.closest('label')
|
|
666
|
+
return label?.textContent?.includes('Use theme border color')
|
|
667
|
+
}
|
|
674
668
|
) as HTMLInputElement
|
|
675
669
|
expect(borderColorThemeCheckbox).toBeTruthy()
|
|
676
|
-
const borderColorThemeWrapper = borderColorThemeCheckbox.closest('.
|
|
670
|
+
const borderColorThemeWrapper = borderColorThemeCheckbox.closest('label.checkbox') as HTMLElement
|
|
677
671
|
expect(borderColorThemeWrapper).toBeTruthy()
|
|
678
672
|
await performAndAssert(
|
|
679
673
|
'Border Color Theme Toggle',
|
|
680
674
|
contentClassSig,
|
|
681
675
|
async () => {
|
|
682
|
-
await userEvent.click(
|
|
676
|
+
await userEvent.click(borderColorThemeCheckbox)
|
|
683
677
|
},
|
|
684
678
|
(before, after) => before !== after && (after.includes('borderColorTheme') || before.includes('borderColorTheme'))
|
|
685
679
|
)
|
|
@@ -688,15 +682,19 @@ export const VisualSectionTests: Story = {
|
|
|
688
682
|
// TEST 10: Accent Style Toggle
|
|
689
683
|
// Expectation: Class 'component--has-accent' toggles.
|
|
690
684
|
// ============================================================================
|
|
691
|
-
|
|
685
|
+
// Find accent checkbox by label text
|
|
686
|
+
const accentCheckbox = Array.from(canvasElement.querySelectorAll('input[type="checkbox"]')).find(input => {
|
|
687
|
+
const label = input.closest('label')
|
|
688
|
+
return label?.textContent?.includes('Use Accent Style')
|
|
689
|
+
}) as HTMLInputElement
|
|
692
690
|
expect(accentCheckbox).toBeTruthy()
|
|
693
|
-
const accentWrapper = accentCheckbox.closest('.
|
|
691
|
+
const accentWrapper = accentCheckbox.closest('label.checkbox') as HTMLElement
|
|
694
692
|
expect(accentWrapper).toBeTruthy()
|
|
695
693
|
await performAndAssert(
|
|
696
694
|
'Accent Toggle',
|
|
697
695
|
contentClassSig,
|
|
698
696
|
async () => {
|
|
699
|
-
await userEvent.click(
|
|
697
|
+
await userEvent.click(accentCheckbox)
|
|
700
698
|
},
|
|
701
699
|
(before, after) => before !== after
|
|
702
700
|
)
|
|
@@ -705,15 +703,19 @@ export const VisualSectionTests: Story = {
|
|
|
705
703
|
// TEST 11: Theme Background Color Toggle
|
|
706
704
|
// Expectation: Class 'component--has-background' toggles.
|
|
707
705
|
// ============================================================================
|
|
708
|
-
|
|
706
|
+
// Find background checkbox by label text
|
|
707
|
+
const backgroundCheckbox = Array.from(canvasElement.querySelectorAll('input[type="checkbox"]')).find(input => {
|
|
708
|
+
const label = input.closest('label')
|
|
709
|
+
return label?.textContent?.includes('Use Theme Background Color')
|
|
710
|
+
}) as HTMLInputElement
|
|
709
711
|
expect(backgroundCheckbox).toBeTruthy()
|
|
710
|
-
const backgroundWrapper = backgroundCheckbox.closest('.
|
|
712
|
+
const backgroundWrapper = backgroundCheckbox.closest('label.checkbox') as HTMLElement
|
|
711
713
|
expect(backgroundWrapper).toBeTruthy()
|
|
712
714
|
await performAndAssert(
|
|
713
715
|
'Background Toggle',
|
|
714
716
|
contentClassSig,
|
|
715
717
|
async () => {
|
|
716
|
-
await userEvent.click(
|
|
718
|
+
await userEvent.click(backgroundCheckbox)
|
|
717
719
|
},
|
|
718
720
|
(before, after) => before !== after
|
|
719
721
|
)
|
|
@@ -722,17 +724,19 @@ export const VisualSectionTests: Story = {
|
|
|
722
724
|
// TEST 12: Hide Background Color Toggle
|
|
723
725
|
// Expectation: Class 'component--hideBackgroundColor' toggles.
|
|
724
726
|
// ============================================================================
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
727
|
+
// Find hide background checkbox by label text
|
|
728
|
+
const hideBackgroundCheckbox = Array.from(canvasElement.querySelectorAll('input[type="checkbox"]')).find(input => {
|
|
729
|
+
const label = input.closest('label')
|
|
730
|
+
return label?.textContent?.includes('Hide Background Color')
|
|
731
|
+
}) as HTMLInputElement
|
|
728
732
|
expect(hideBackgroundCheckbox).toBeTruthy()
|
|
729
|
-
const hideBackgroundWrapper = hideBackgroundCheckbox.closest('.
|
|
733
|
+
const hideBackgroundWrapper = hideBackgroundCheckbox.closest('label.checkbox') as HTMLElement
|
|
730
734
|
expect(hideBackgroundWrapper).toBeTruthy()
|
|
731
735
|
await performAndAssert(
|
|
732
736
|
'Hide Background Toggle',
|
|
733
737
|
contentClassSig,
|
|
734
738
|
async () => {
|
|
735
|
-
await userEvent.click(
|
|
739
|
+
await userEvent.click(hideBackgroundCheckbox)
|
|
736
740
|
},
|
|
737
741
|
(before, after) => before !== after
|
|
738
742
|
)
|
|
@@ -75,12 +75,29 @@ export const Person: Story = {
|
|
|
75
75
|
showDenominator: false,
|
|
76
76
|
showPercent: true,
|
|
77
77
|
valueDescription: 'testing',
|
|
78
|
+
gauge: {
|
|
79
|
+
height: 35,
|
|
80
|
+
width: 400
|
|
81
|
+
},
|
|
78
82
|
visual: {
|
|
79
83
|
border: true,
|
|
80
84
|
accent: true,
|
|
81
85
|
background: true,
|
|
82
86
|
hideBackgroundColor: true,
|
|
83
|
-
borderColorTheme: true
|
|
87
|
+
borderColorTheme: true,
|
|
88
|
+
colors: {
|
|
89
|
+
'theme-blue': '#005eaa',
|
|
90
|
+
'theme-purple': '#712177',
|
|
91
|
+
'theme-brown': '#705043',
|
|
92
|
+
'theme-teal': '#00695c',
|
|
93
|
+
'theme-pink': '#af4448',
|
|
94
|
+
'theme-orange': '#bb4d00',
|
|
95
|
+
'theme-slate': '#29434e',
|
|
96
|
+
'theme-indigo': '#26418f',
|
|
97
|
+
'theme-cyan': '#006778',
|
|
98
|
+
'theme-green': '#4b830d',
|
|
99
|
+
'theme-amber': '#fbab18'
|
|
100
|
+
}
|
|
84
101
|
}
|
|
85
102
|
}
|
|
86
103
|
},
|