@react-magma/charts 13.1.1-next.0 → 13.2.0-next.0

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 (35) hide show
  1. package/dist/charts.js +224 -115
  2. package/dist/charts.js.map +1 -1
  3. package/dist/charts.modern.module.js +224 -115
  4. package/dist/charts.modern.module.js.map +1 -1
  5. package/dist/charts.umd.js +574 -293
  6. package/dist/charts.umd.js.map +1 -1
  7. package/dist/components/CarbonChart/CarbonChart.d.ts +5 -0
  8. package/dist/components/CarbonChart/CarbonChartBar.stories.d.ts +27 -9
  9. package/package.json +3 -3
  10. package/src/components/CarbonChart/CarbonChart.test.js +185 -0
  11. package/src/components/CarbonChart/CarbonChart.tsx +262 -120
  12. package/src/components/CarbonChart/CarbonChartArea.stories.tsx +1 -1
  13. package/src/components/CarbonChart/CarbonChartAreaStacked.stories.tsx +1 -1
  14. package/src/components/CarbonChart/CarbonChartBar.stories.tsx +9 -2
  15. package/src/components/CarbonChart/CarbonChartBarFloating.stories.tsx +1 -1
  16. package/src/components/CarbonChart/CarbonChartBarGrouped.stories.tsx +1 -1
  17. package/src/components/CarbonChart/CarbonChartBarStacked.stories.tsx +1 -1
  18. package/src/components/CarbonChart/CarbonChartBoxplot.stories.tsx +1 -1
  19. package/src/components/CarbonChart/CarbonChartBubble.stories.tsx +1 -1
  20. package/src/components/CarbonChart/CarbonChartBullet.stories.tsx +1 -1
  21. package/src/components/CarbonChart/CarbonChartCombo.stories.tsx +1 -1
  22. package/src/components/CarbonChart/CarbonChartDonut.stories.tsx +3 -1
  23. package/src/components/CarbonChart/CarbonChartGauge.stories.tsx +1 -1
  24. package/src/components/CarbonChart/CarbonChartHistogram.stories.tsx +1 -1
  25. package/src/components/CarbonChart/CarbonChartLine.stories.tsx +1 -1
  26. package/src/components/CarbonChart/CarbonChartLollipop.stories.tsx +1 -1
  27. package/src/components/CarbonChart/CarbonChartMeter.stories.tsx +1 -1
  28. package/src/components/CarbonChart/CarbonChartPie.stories.tsx +1 -1
  29. package/src/components/CarbonChart/CarbonChartRadar.stories.tsx +1 -1
  30. package/src/components/CarbonChart/CarbonChartScatter.stories.tsx +1 -1
  31. package/src/components/CarbonChart/CarbonChartSparkline.stories.tsx +1 -1
  32. package/src/components/CarbonChart/CarbonChartStep.stories.tsx +1 -1
  33. package/src/components/ChartTable/ChartMoreOptionsButton.tsx +1 -0
  34. package/dist/components/ChartTable/ChartTable.test.d.ts +0 -1
  35. /package/src/components/ChartTable/{ChartTable.test.tsx → ChartTable.test.js} +0 -0
@@ -56,6 +56,11 @@ export interface ChartToolbarConfig {
56
56
  * @default 2
57
57
  */
58
58
  tableHeaderLevel?: 1 | 2 | 3 | 4 | 5 | 6;
59
+ /**
60
+ * Heading level for the chart title.
61
+ * @default 2
62
+ */
63
+ titleLevel?: 1 | 2 | 3 | 4 | 5 | 6;
59
64
  }
60
65
  export interface CarbonChartProps extends React.HTMLAttributes<HTMLDivElement> {
61
66
  dataSet: Array<Object>;
@@ -2,7 +2,9 @@ import { CarbonChartProps, CarbonChartType } from '.';
2
2
  declare const _default: import("@storybook/csf").ComponentAnnotations<import("@storybook/react/types-6-0").ReactFramework, import("@storybook/react/types-6-0").Args>;
3
3
  export default _default;
4
4
  export declare const VerticalSimpleBarDiscrete: {
5
- render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps>;
5
+ render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps & {
6
+ titleLevel?: 2 | 1 | 3 | 4 | 5 | 6 | undefined;
7
+ }>;
6
8
  args: {
7
9
  isInverse: boolean;
8
10
  type: CarbonChartType;
@@ -30,7 +32,9 @@ export declare const VerticalSimpleBarDiscrete: {
30
32
  };
31
33
  };
32
34
  export declare const VerticalSimpleBarTimeSeries: {
33
- render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps>;
35
+ render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps & {
36
+ titleLevel?: 2 | 1 | 3 | 4 | 5 | 6 | undefined;
37
+ }>;
34
38
  args: {
35
39
  isInverse: boolean;
36
40
  type: CarbonChartType;
@@ -55,7 +59,9 @@ export declare const VerticalSimpleBarTimeSeries: {
55
59
  };
56
60
  };
57
61
  export declare const VerticalSimpleBarTimeSeriesDenseDataTurkish: {
58
- render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps>;
62
+ render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps & {
63
+ titleLevel?: 2 | 1 | 3 | 4 | 5 | 6 | undefined;
64
+ }>;
59
65
  args: {
60
66
  isInverse: boolean;
61
67
  type: CarbonChartType;
@@ -86,7 +92,9 @@ export declare const VerticalSimpleBarTimeSeriesDenseDataTurkish: {
86
92
  };
87
93
  };
88
94
  export declare const VerticalSimpleBarEmptyState: {
89
- render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps>;
95
+ render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps & {
96
+ titleLevel?: 2 | 1 | 3 | 4 | 5 | 6 | undefined;
97
+ }>;
90
98
  args: {
91
99
  isInverse: boolean;
92
100
  type: CarbonChartType;
@@ -104,7 +112,9 @@ export declare const VerticalSimpleBarEmptyState: {
104
112
  };
105
113
  };
106
114
  export declare const VerticalSimpleBarSkeleton: {
107
- render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps>;
115
+ render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps & {
116
+ titleLevel?: 2 | 1 | 3 | 4 | 5 | 6 | undefined;
117
+ }>;
108
118
  args: {
109
119
  isInverse: boolean;
110
120
  type: CarbonChartType;
@@ -125,7 +135,9 @@ export declare const VerticalSimpleBarSkeleton: {
125
135
  };
126
136
  };
127
137
  export declare const HorizontalSimpleBarTimeSeries: {
128
- render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps>;
138
+ render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps & {
139
+ titleLevel?: 2 | 1 | 3 | 4 | 5 | 6 | undefined;
140
+ }>;
129
141
  args: {
130
142
  isInverse: boolean;
131
143
  type: CarbonChartType;
@@ -150,7 +162,9 @@ export declare const HorizontalSimpleBarTimeSeries: {
150
162
  };
151
163
  };
152
164
  export declare const HorizontalSimpleBarDiscrete: {
153
- render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps>;
165
+ render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps & {
166
+ titleLevel?: 2 | 1 | 3 | 4 | 5 | 6 | undefined;
167
+ }>;
154
168
  args: {
155
169
  isInverse: boolean;
156
170
  type: CarbonChartType;
@@ -174,7 +188,9 @@ export declare const HorizontalSimpleBarDiscrete: {
174
188
  };
175
189
  };
176
190
  export declare const HorizontalSimpleBarSkeleton: {
177
- render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps>;
191
+ render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps & {
192
+ titleLevel?: 2 | 1 | 3 | 4 | 5 | 6 | undefined;
193
+ }>;
178
194
  args: {
179
195
  isInverse: boolean;
180
196
  type: CarbonChartType;
@@ -195,7 +211,9 @@ export declare const HorizontalSimpleBarSkeleton: {
195
211
  };
196
212
  };
197
213
  export declare const HorizontalSimpleBarEmptyState: {
198
- render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps>;
214
+ render: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react/types-6-0").ReactFramework, CarbonChartProps & {
215
+ titleLevel?: 2 | 1 | 3 | 4 | 5 | 6 | undefined;
216
+ }>;
199
217
  args: {
200
218
  isInverse: boolean;
201
219
  type: CarbonChartType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-magma/charts",
3
- "version": "13.1.1-next.0",
3
+ "version": "13.2.0-next.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -47,7 +47,7 @@
47
47
  "identity-obj-proxy": "^3.0.0",
48
48
  "react": "^17.0.2",
49
49
  "react-dom": "^17.0.2",
50
- "react-magma-dom": "^4.13.1-next.0",
50
+ "react-magma-dom": "^4.14.0",
51
51
  "react-magma-icons": "^3.2.5",
52
52
  "rollup": "^4.52.4",
53
53
  "rollup-plugin-postcss": "^4.0.2"
@@ -57,7 +57,7 @@
57
57
  "@emotion/styled": "^11.13.0",
58
58
  "react": "^17.0.2",
59
59
  "react-dom": "^17.0.2",
60
- "react-magma-dom": "^4.13.1-next.0",
60
+ "react-magma-dom": "^4.14.0",
61
61
  "react-magma-icons": "^3.2.5"
62
62
  },
63
63
  "engines": {
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
 
3
3
  import { act, render, screen, fireEvent } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
4
5
  import { ThemeContext, magma, DropdownMenuItem } from 'react-magma-dom';
5
6
 
6
7
  import { CarbonChart, CarbonChartType } from '.';
@@ -588,6 +589,32 @@ describe('CarbonChart', () => {
588
589
  }
589
590
  );
590
591
  });
592
+
593
+ it('should move focus back after closing the More options dropdown and chart toolbar', () => {
594
+ const testId = 'modal-footer-border-test';
595
+ const { getByTestId, getByText } = render(
596
+ <ThemeContext.Provider value={magma}>
597
+ <CarbonChart
598
+ testId={testId}
599
+ dataSet={dataSet}
600
+ options={chartOptions}
601
+ type={CarbonChartType.bar}
602
+ isInverse={false}
603
+ chartToolbar={{}}
604
+ />
605
+ </ThemeContext.Provider>
606
+ );
607
+
608
+ const moreOptionsButton = getByTestId('chart-more-options-button');
609
+ userEvent.click(moreOptionsButton);
610
+
611
+ expect(getByText('Download as CSV')).toBeVisible();
612
+ expect(getByText('Download as PNG')).toBeVisible();
613
+ expect(getByText('Download as JPG')).toBeVisible();
614
+
615
+ userEvent.keyboard('{esc}');
616
+ expect(moreOptionsButton).toHaveFocus();
617
+ });
591
618
  });
592
619
 
593
620
  describe('Modal Focus Management', () => {
@@ -842,6 +869,27 @@ describe('CarbonChart', () => {
842
869
  ).not.toBeInTheDocument();
843
870
  });
844
871
 
872
+ it('should render the chart title as h2 by default', () => {
873
+ render(<CarbonChart {...toolbarProps} />);
874
+
875
+ expect(
876
+ screen.getByRole('heading', { level: 2, name: chartOptions.title })
877
+ ).toBeInTheDocument();
878
+ });
879
+
880
+ it('should render the chart title at the level set by titleLevel', () => {
881
+ render(
882
+ <CarbonChart {...toolbarProps} chartToolbar={{ titleLevel: 3 }} />
883
+ );
884
+
885
+ expect(
886
+ screen.getByRole('heading', { level: 3, name: chartOptions.title })
887
+ ).toBeInTheDocument();
888
+ expect(
889
+ screen.queryByRole('heading', { level: 2, name: chartOptions.title })
890
+ ).not.toBeInTheDocument();
891
+ });
892
+
845
893
  it('should always render the more options dropdown with built-in download items', () => {
846
894
  render(<CarbonChart {...toolbarProps} />);
847
895
 
@@ -895,4 +943,141 @@ describe('CarbonChart', () => {
895
943
  ).toBeInTheDocument();
896
944
  });
897
945
  });
946
+
947
+ describe('dot keyboard accessibility', () => {
948
+ let rafCallbacks;
949
+
950
+ beforeEach(() => {
951
+ rafCallbacks = [];
952
+ jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => {
953
+ rafCallbacks.push(cb);
954
+ return rafCallbacks.length - 1;
955
+ });
956
+ });
957
+
958
+ afterEach(() => {
959
+ window.requestAnimationFrame.mockRestore();
960
+ rafCallbacks = [];
961
+ });
962
+
963
+ function renderChart() {
964
+ const { getByTestId } = render(
965
+ <CarbonChart
966
+ testId="dot-tab-test"
967
+ dataSet={dataSet}
968
+ options={chartOptions}
969
+ type={CarbonChartType.scatter}
970
+ />
971
+ );
972
+ return getByTestId('dot-tab-test');
973
+ }
974
+
975
+ function addDot(wrapper) {
976
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
977
+ const dot = document.createElementNS(
978
+ 'http://www.w3.org/2000/svg',
979
+ 'circle'
980
+ );
981
+ dot.classList.add('dot');
982
+ svg.appendChild(dot);
983
+ wrapper.appendChild(svg);
984
+ return dot;
985
+ }
986
+
987
+ describe('tabbing', () => {
988
+ it('should stamp tabindex="0" on circle.dot elements so they are reachable by Tab', () => {
989
+ const wrapper = renderChart();
990
+ const dot = addDot(wrapper);
991
+
992
+ // The last captured RAF is the dots-stamping callback (no ariaLabel provided)
993
+ act(() => rafCallbacks[rafCallbacks.length - 1](0));
994
+
995
+ expect(dot).toHaveAttribute('tabindex', '0');
996
+ });
997
+
998
+ it('should not overwrite an existing tabindex on circle.dot', () => {
999
+ const wrapper = renderChart();
1000
+ const dot = addDot(wrapper);
1001
+ dot.setAttribute('tabindex', '-1');
1002
+
1003
+ act(() => rafCallbacks[rafCallbacks.length - 1](0));
1004
+
1005
+ expect(dot).toHaveAttribute('tabindex', '-1');
1006
+ });
1007
+ });
1008
+
1009
+ describe('focus on dot content', () => {
1010
+ it('should set opacity to 1 when a dot receives focus', () => {
1011
+ const wrapper = renderChart();
1012
+ const dot = addDot(wrapper);
1013
+
1014
+ fireEvent.focusIn(dot);
1015
+
1016
+ expect(dot.style.opacity).toBe('1');
1017
+ });
1018
+
1019
+ it('should dispatch mouseover on dot focusin to reveal tooltip data', () => {
1020
+ const wrapper = renderChart();
1021
+ const dot = addDot(wrapper);
1022
+
1023
+ const mouseoverSpy = jest.fn();
1024
+ dot.addEventListener('mouseover', mouseoverSpy);
1025
+
1026
+ fireEvent.focusIn(dot);
1027
+
1028
+ expect(mouseoverSpy).toHaveBeenCalledTimes(1);
1029
+ });
1030
+
1031
+ it('should dispatch mousemove on dot focusin', () => {
1032
+ const wrapper = renderChart();
1033
+ const dot = addDot(wrapper);
1034
+
1035
+ const mousemoveSpy = jest.fn();
1036
+ dot.addEventListener('mousemove', mousemoveSpy);
1037
+
1038
+ fireEvent.focusIn(dot);
1039
+
1040
+ expect(mousemoveSpy).toHaveBeenCalledTimes(1);
1041
+ });
1042
+
1043
+ it('should not change opacity for non-dot circle elements on focusin', () => {
1044
+ const wrapper = renderChart();
1045
+ const nonDot = document.createElementNS(
1046
+ 'http://www.w3.org/2000/svg',
1047
+ 'circle'
1048
+ );
1049
+ wrapper.appendChild(nonDot);
1050
+
1051
+ fireEvent.focusIn(nonDot);
1052
+
1053
+ expect(nonDot.style.opacity).toBe('');
1054
+ });
1055
+ });
1056
+
1057
+ describe('data visibility', () => {
1058
+ it('should reset dot opacity when dot loses focus', () => {
1059
+ const wrapper = renderChart();
1060
+ const dot = addDot(wrapper);
1061
+
1062
+ fireEvent.focusIn(dot);
1063
+ expect(dot.style.opacity).toBe('1');
1064
+
1065
+ fireEvent.focusOut(dot);
1066
+ expect(dot.style.opacity).toBe('');
1067
+ });
1068
+
1069
+ it('should dispatch mouseout on dot focusout to hide tooltip', () => {
1070
+ const wrapper = renderChart();
1071
+ const dot = addDot(wrapper);
1072
+
1073
+ const mouseoutSpy = jest.fn();
1074
+ dot.addEventListener('mouseout', mouseoutSpy);
1075
+
1076
+ fireEvent.focusIn(dot);
1077
+ fireEvent.focusOut(dot);
1078
+
1079
+ expect(mouseoutSpy).toHaveBeenCalledTimes(1);
1080
+ });
1081
+ });
1082
+ });
898
1083
  });