@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.
@@ -420,8 +420,8 @@ export const DataSectionTests: Story = {
420
420
  }
421
421
 
422
422
  /**
423
- * VISUAL SECTION TESTS
424
- * Tests all functionality within the Visual accordion
423
+ * CHART SETTINGS AND VISUAL SECTION TESTS
424
+ * Tests functionality within the Chart Settings and Visual accordions
425
425
  */
426
426
  export const VisualSectionTests: Story = {
427
427
  args: {
@@ -431,7 +431,8 @@ export const VisualSectionTests: Story = {
431
431
  play: async ({ canvasElement }) => {
432
432
  const canvas = within(canvasElement)
433
433
  await waitForEditor(canvas)
434
- await openAccordion(canvas, 'Visual')
434
+ // Open Chart Settings accordion first for tests 1-5
435
+ await openAccordion(canvas, 'Chart Settings')
435
436
  // Core helper functions used throughout the visual tests
436
437
  const waffleRoot = () => canvasElement.querySelector('.cove-waffle-chart') as HTMLElement
437
438
  const contentContainer = () => canvasElement.querySelector('.cove-component__content > div') as HTMLElement
@@ -569,6 +570,9 @@ export const VisualSectionTests: Story = {
569
570
  // TEST 6: Overall Font Size Change
570
571
  // Expectation: font-small|font-medium|font-large class changes on waffle root.
571
572
  // ============================================================================
573
+ // Open Visual accordion for tests 6+
574
+ await openAccordion(canvas, 'Visual')
575
+
572
576
  const overallFontClass = () => {
573
577
  const root = waffleRoot()
574
578
  if (!root) return ''
@@ -624,11 +628,8 @@ export const VisualSectionTests: Story = {
624
628
  // ============================================================================
625
629
  const contentClassSig = () => Array.from(contentContainer().classList).sort().join(' ')
626
630
 
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
631
+ // Find border checkbox by exact label text
632
+ const borderCheckbox = canvas.getByLabelText('Display Border') as HTMLInputElement
632
633
  expect(borderCheckbox).toBeTruthy()
633
634
  const borderWrapper = borderCheckbox.closest('label.checkbox') as HTMLElement
634
635
  expect(borderWrapper).toBeTruthy()
@@ -659,13 +660,8 @@ export const VisualSectionTests: Story = {
659
660
  // TEST 9: Theme Border Color Toggle
660
661
  // Expectation: Class 'component--has-borderColorTheme' toggles.
661
662
  // ============================================================================
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
- }
668
- ) as HTMLInputElement
663
+ // Find border color theme checkbox by exact label text
664
+ const borderColorThemeCheckbox = canvas.getByLabelText('Use Border Color Theme') as HTMLInputElement
669
665
  expect(borderColorThemeCheckbox).toBeTruthy()
670
666
  const borderColorThemeWrapper = borderColorThemeCheckbox.closest('label.checkbox') as HTMLElement
671
667
  expect(borderColorThemeWrapper).toBeTruthy()
@@ -682,11 +678,8 @@ export const VisualSectionTests: Story = {
682
678
  // TEST 10: Accent Style Toggle
683
679
  // Expectation: Class 'component--has-accent' toggles.
684
680
  // ============================================================================
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
681
+ // Find accent checkbox by exact label text
682
+ const accentCheckbox = canvas.getByLabelText('Use Accent Style') as HTMLInputElement
690
683
  expect(accentCheckbox).toBeTruthy()
691
684
  const accentWrapper = accentCheckbox.closest('label.checkbox') as HTMLElement
692
685
  expect(accentWrapper).toBeTruthy()
@@ -703,11 +696,8 @@ export const VisualSectionTests: Story = {
703
696
  // TEST 11: Theme Background Color Toggle
704
697
  // Expectation: Class 'component--has-background' toggles.
705
698
  // ============================================================================
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
699
+ // Find background checkbox by exact label text
700
+ const backgroundCheckbox = canvas.getByLabelText('Use Theme Background Color') as HTMLInputElement
711
701
  expect(backgroundCheckbox).toBeTruthy()
712
702
  const backgroundWrapper = backgroundCheckbox.closest('label.checkbox') as HTMLElement
713
703
  expect(backgroundWrapper).toBeTruthy()
@@ -724,11 +714,8 @@ export const VisualSectionTests: Story = {
724
714
  // TEST 12: Hide Background Color Toggle
725
715
  // Expectation: Class 'component--hideBackgroundColor' toggles.
726
716
  // ============================================================================
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
717
+ // Find hide background checkbox by exact label text
718
+ const hideBackgroundCheckbox = canvas.getByLabelText('Hide Background Color') as HTMLInputElement
732
719
  expect(hideBackgroundCheckbox).toBeTruthy()
733
720
  const hideBackgroundWrapper = hideBackgroundCheckbox.closest('label.checkbox') as HTMLElement
734
721
  expect(hideBackgroundWrapper).toBeTruthy()
@@ -742,3 +729,240 @@ export const VisualSectionTests: Story = {
742
729
  )
743
730
  }
744
731
  }
732
+
733
+ /**
734
+ * TP5 GAUGE - GENERAL SECTION TESTS
735
+ * Tests all functionality within the General accordion for TP5 Gauge configs.
736
+ */
737
+ export const TP5GaugeGeneralSectionTests: Story = {
738
+ args: {
739
+ configUrl: '/packages/waffle-chart/examples/tp5-gauge.json',
740
+ isEditor: true
741
+ },
742
+ play: async ({ canvasElement }) => {
743
+ const canvas = within(canvasElement)
744
+ await waitForEditor(canvas)
745
+ await openAccordion(canvas, 'General')
746
+ await waitForPresence('.cdc-callout', canvasElement)
747
+
748
+ // ============================================================================
749
+ // TEST 1: Chart Type Change (TP5 Gauge <-> TP5 Waffle)
750
+ // Expectation: Gauge container swaps with waffle container.
751
+ // ============================================================================
752
+ const chartTypeSelect = canvas.getByLabelText(/chart type/i) as HTMLSelectElement
753
+ expect(chartTypeSelect).toBeTruthy()
754
+
755
+ const getChartTypeState = () => ({
756
+ hasGauge: !!canvasElement.querySelector('.cove-gauge-chart'),
757
+ hasWaffle: !!canvasElement.querySelector('.cove-waffle-chart')
758
+ })
759
+
760
+ await performAndAssert(
761
+ 'TP5 Gauge to TP5 Waffle',
762
+ getChartTypeState,
763
+ async () => {
764
+ await userEvent.selectOptions(chartTypeSelect, 'TP5 Waffle')
765
+ },
766
+ (before, after) => before.hasGauge !== after.hasGauge && after.hasWaffle
767
+ )
768
+
769
+ await performAndAssert(
770
+ 'TP5 Waffle to TP5 Gauge',
771
+ getChartTypeState,
772
+ async () => {
773
+ await userEvent.selectOptions(chartTypeSelect, 'TP5 Gauge')
774
+ },
775
+ (before, after) => before.hasGauge !== after.hasGauge && after.hasGauge
776
+ )
777
+
778
+ // ============================================================================
779
+ // TEST 2: Title Update
780
+ // Expectation: Callout heading text updates.
781
+ // ============================================================================
782
+ const titleInput = canvas.getByLabelText(/^title$/i, { selector: 'input' }) as HTMLInputElement
783
+ await userEvent.clear(titleInput)
784
+ await userEvent.type(titleInput, 'TP5 Gauge Title Update')
785
+
786
+ await performAndAssert(
787
+ 'TP5 Gauge Title Update',
788
+ () => canvasElement.querySelector('.cdc-callout__heading')?.textContent?.trim() || '',
789
+ async () => {}, // action already performed above
790
+ (before, after) => after === 'TP5 Gauge Title Update'
791
+ )
792
+
793
+ // ============================================================================
794
+ // TEST 3: Show Title Toggle
795
+ // Expectation: Callout heading appears/disappears.
796
+ // ============================================================================
797
+ const showTitleCheckbox = canvasElement.querySelector('input[name="showTitle"]') as HTMLInputElement
798
+ expect(showTitleCheckbox).toBeTruthy()
799
+
800
+ const headingVisible = () => !!canvasElement.querySelector('.cdc-callout__heading')
801
+ const wasVisible = headingVisible()
802
+
803
+ await performAndAssert(
804
+ 'TP5 Gauge Title Toggle',
805
+ headingVisible,
806
+ async () => {
807
+ await userEvent.click(showTitleCheckbox)
808
+ },
809
+ (before, after) => after !== before
810
+ )
811
+
812
+ await performAndAssert(
813
+ 'TP5 Gauge Title Toggle Reset',
814
+ headingVisible,
815
+ async () => {
816
+ await userEvent.click(showTitleCheckbox)
817
+ },
818
+ (before, after) => after === wasVisible
819
+ )
820
+
821
+ // ============================================================================
822
+ // TEST 4: Message Update
823
+ // Expectation: Gauge content text updates.
824
+ // ============================================================================
825
+ const messageTextarea = canvas.getByLabelText(/message/i) as HTMLTextAreaElement
826
+ const newMessage = 'Updated TP5 gauge message text'
827
+ await userEvent.clear(messageTextarea)
828
+ await userEvent.type(messageTextarea, newMessage)
829
+ await waitForTextContent(
830
+ canvasElement.querySelector('.cove-gauge-chart__content .cove-waffle-chart__data--text') as HTMLElement,
831
+ newMessage
832
+ )
833
+
834
+ // ============================================================================
835
+ // TEST 5: Subtext Update
836
+ // Expectation: Subtext text updates.
837
+ // ============================================================================
838
+ const subtextInput = canvas.getByLabelText(/subtext/i) as HTMLInputElement
839
+ const newSubtext = 'Updated TP5 gauge subtext'
840
+ await userEvent.clear(subtextInput)
841
+ await userEvent.type(subtextInput, newSubtext)
842
+ await waitForTextContent(
843
+ canvasElement.querySelector('.cove-gauge-chart .cove-waffle-chart__subtext') as HTMLElement,
844
+ newSubtext
845
+ )
846
+ }
847
+ }
848
+
849
+ /**
850
+ * TP5 GAUGE - DATA SECTION TESTS
851
+ * Tests all functionality within the Data accordion for TP5 Gauge configs.
852
+ */
853
+ export const TP5GaugeDataSectionTests: Story = {
854
+ args: {
855
+ configUrl: '/packages/waffle-chart/examples/tp5-gauge.json',
856
+ isEditor: true
857
+ },
858
+ play: async ({ canvasElement }) => {
859
+ const canvas = within(canvasElement)
860
+ await waitForEditor(canvas)
861
+ await openAccordion(canvas, 'Data')
862
+
863
+ const getPrimaryText = () =>
864
+ canvasElement.querySelector('.cove-waffle-chart__data--primary')?.textContent?.replace(/\s+/g, ' ').trim() || ''
865
+
866
+ // ============================================================================
867
+ // TEST 1: Data Function Change
868
+ // Expectation: Primary value text changes.
869
+ // ============================================================================
870
+ const dataFunctionSelect = canvasElement.querySelector('select[name="dataFunction"]') as HTMLSelectElement
871
+ await waitForOptionsToPopulate(dataFunctionSelect, 2)
872
+ const dataFunctionOptions = Array.from(dataFunctionSelect.options).map(opt => opt.value)
873
+ const nextFunction = dataFunctionOptions.find(opt => opt && opt !== dataFunctionSelect.value)
874
+ expect(nextFunction).toBeTruthy()
875
+
876
+ await performAndAssert(
877
+ 'TP5 Gauge Data Function Change',
878
+ getPrimaryText,
879
+ async () => {
880
+ await userEvent.selectOptions(dataFunctionSelect, nextFunction as string)
881
+ },
882
+ (before, after) => after !== before
883
+ )
884
+
885
+ // ============================================================================
886
+ // TEST 2: Value Description Update
887
+ // Expectation: Primary value text includes new descriptor.
888
+ // ============================================================================
889
+ const valueDescriptionInput = canvasElement.querySelector(
890
+ 'input[name="null-null-valueDescription"]'
891
+ ) as HTMLInputElement
892
+ expect(valueDescriptionInput).toBeTruthy()
893
+ const newDescription = 'out of total'
894
+ await userEvent.clear(valueDescriptionInput)
895
+ await userEvent.type(valueDescriptionInput, newDescription)
896
+ await performAndAssert(
897
+ 'TP5 Gauge Value Description Update',
898
+ getPrimaryText,
899
+ async () => {}, // action already performed above
900
+ (before, after) => after.includes(newDescription)
901
+ )
902
+
903
+ // ============================================================================
904
+ // TEST 3: Denominator Update
905
+ // Expectation: Primary value text changes to include new denominator.
906
+ // ============================================================================
907
+ const denominatorInput = canvasElement.querySelector('input[name="null-null-dataDenom"]') as HTMLInputElement
908
+ expect(denominatorInput).toBeTruthy()
909
+ await performAndAssert(
910
+ 'TP5 Gauge Denominator Update',
911
+ getPrimaryText,
912
+ async () => {
913
+ await userEvent.clear(denominatorInput)
914
+ await userEvent.type(denominatorInput, '250')
915
+ },
916
+ (before, after) => after !== before && after.includes('250')
917
+ )
918
+ }
919
+ }
920
+
921
+ /**
922
+ * TP5 GAUGE - VISUAL SECTION TESTS
923
+ * Tests all functionality within the Visual accordion for TP5 Gauge configs.
924
+ */
925
+ export const TP5GaugeVisualSectionTests: Story = {
926
+ args: {
927
+ configUrl: '/packages/waffle-chart/examples/tp5-gauge.json',
928
+ isEditor: true
929
+ },
930
+ play: async ({ canvasElement }) => {
931
+ const canvas = within(canvasElement)
932
+ await waitForEditor(canvas)
933
+ await openAccordion(canvas, 'Visual')
934
+ await waitForPresence('.cdc-callout', canvasElement)
935
+
936
+ // ============================================================================
937
+ // TEST 1: White Background Toggle
938
+ // Expectation: Callout classes and flag visibility toggle.
939
+ // ============================================================================
940
+ const whiteBackgroundCheckbox = canvas.getByLabelText(/use white background style/i) as HTMLInputElement
941
+ expect(whiteBackgroundCheckbox).toBeTruthy()
942
+
943
+ const getCalloutState = () => {
944
+ const callout = canvasElement.querySelector('.cdc-callout') as HTMLElement
945
+ return {
946
+ classes: callout ? Array.from(callout.classList).sort().join(' ') : '',
947
+ hasFlag: !!canvasElement.querySelector('.cdc-callout__flag')
948
+ }
949
+ }
950
+
951
+ await performAndAssert(
952
+ 'TP5 Gauge White Background Toggle',
953
+ getCalloutState,
954
+ async () => {
955
+ await userEvent.click(whiteBackgroundCheckbox)
956
+ },
957
+ (before, after) => before.classes !== after.classes || before.hasFlag !== after.hasFlag,
958
+ after => {
959
+ if (after.hasFlag) {
960
+ expect(after.classes.includes('cdc-callout--data')).toBe(true)
961
+ } else {
962
+ expect(after.classes.includes('cdc-callout--data')).toBe(false)
963
+ expect(after.classes.includes('dfe-block')).toBe(false)
964
+ }
965
+ }
966
+ )
967
+ }
968
+ }
@@ -2,6 +2,7 @@ import React from 'react'
2
2
  import type { Meta, StoryObj } from '@storybook/react-vite'
3
3
 
4
4
  import WaffleChart from '../CdcWaffleChart'
5
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
5
6
 
6
7
  const meta: Meta<typeof WaffleChart> = {
7
8
  title: 'Components/Templates/WaffleChart',
@@ -105,7 +106,10 @@ export const Person: Story = {
105
106
  <>
106
107
  <WaffleChart {...args} />
107
108
  </>
108
- )
109
+ ),
110
+ play: async ({ canvasElement }) => {
111
+ await assertVisualizationRendered(canvasElement)
112
+ }
109
113
  }
110
114
 
111
115
  export const Gauge: Story = {
@@ -186,7 +190,78 @@ export const Gauge: Story = {
186
190
  <>
187
191
  <WaffleChart {...args} />
188
192
  </>
189
- )
193
+ ),
194
+ play: async ({ canvasElement }) => {
195
+ await assertVisualizationRendered(canvasElement)
196
+ }
197
+ }
198
+
199
+ export const Waffle_Chart_TP5_Style: Story = {
200
+ args: {
201
+ configUrl: '/packages/waffle-chart/examples/tp5-style.json'
202
+ },
203
+ parameters: {
204
+ docs: {
205
+ description: {
206
+ story:
207
+ 'TP5 Style - A new layout style that displays the waffle chart in a callout component with a blue background and icon. The title appears inside the callout header. This style mimics the CDC Template Package 5.0 callout component design and includes an optional white background variant.'
208
+ }
209
+ }
210
+ },
211
+ play: async ({ canvasElement }) => {
212
+ await assertVisualizationRendered(canvasElement)
213
+ }
214
+ }
215
+
216
+ export const Waffle_Chart_TP5_Style_White_Background: Story = {
217
+ args: {
218
+ configUrl: '/packages/waffle-chart/examples/tp5-style-white.json'
219
+ },
220
+ parameters: {
221
+ docs: {
222
+ description: {
223
+ story:
224
+ 'TP5 Style with White Background - The white background variant of the TP5 style features a white background with a teal border instead of the default blue background. The icon remains visible in this variant.'
225
+ }
226
+ }
227
+ },
228
+ play: async ({ canvasElement }) => {
229
+ await assertVisualizationRendered(canvasElement)
230
+ }
231
+ }
232
+
233
+ export const TP5_Gauge: Story = {
234
+ args: {
235
+ configUrl: '/packages/waffle-chart/examples/tp5-gauge.json'
236
+ },
237
+ parameters: {
238
+ docs: {
239
+ description: {
240
+ story:
241
+ 'TP5 Gauge - A TP5 callout-style gauge with a blue background, callout flag, and title inside the callout header.'
242
+ }
243
+ }
244
+ },
245
+ play: async ({ canvasElement }) => {
246
+ await assertVisualizationRendered(canvasElement)
247
+ }
248
+ }
249
+
250
+ export const TP5_Gauge_White_Background: Story = {
251
+ args: {
252
+ configUrl: '/packages/waffle-chart/examples/tp5-gauge-white.json'
253
+ },
254
+ parameters: {
255
+ docs: {
256
+ description: {
257
+ story:
258
+ 'TP5 Gauge with White Background - The white background variant of the TP5 gauge features a white callout with a teal border and no callout flag.'
259
+ }
260
+ }
261
+ },
262
+ play: async ({ canvasElement }) => {
263
+ await assertVisualizationRendered(canvasElement)
264
+ }
190
265
  }
191
266
 
192
267
  export default meta