@cdc/chart 4.24.5 → 4.24.7

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 (68) hide show
  1. package/dist/cdcchart.js +39128 -35959
  2. package/examples/feature/annotations/index.json +542 -0
  3. package/examples/xaxis.json +493 -0
  4. package/index.html +5 -4
  5. package/package.json +5 -4
  6. package/src/CdcChart.tsx +104 -64
  7. package/src/_stories/Chart.stories.tsx +18 -171
  8. package/src/_stories/ChartAnnotation.stories.tsx +32 -0
  9. package/src/_stories/_mock/annotation_category_mock.json +473 -0
  10. package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
  11. package/src/_stories/_mock/annotation_date-time_mock.json +530 -0
  12. package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
  13. package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
  14. package/src/_stories/_mock/lollipop.json +171 -0
  15. package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
  16. package/src/components/Annotations/components/AnnotationDraggable.tsx +154 -0
  17. package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
  18. package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
  19. package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
  20. package/src/components/Annotations/components/AnnotationList.tsx +42 -0
  21. package/src/components/Annotations/components/findNearestDatum.ts +138 -0
  22. package/src/components/Annotations/components/helpers/index.tsx +46 -0
  23. package/src/components/Annotations/index.tsx +13 -0
  24. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
  25. package/src/components/AreaChart/components/AreaChart.jsx +1 -1
  26. package/src/components/BarChart/components/BarChart.Horizontal.tsx +44 -42
  27. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -2
  28. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -11
  29. package/src/components/BarChart/components/BarChart.Vertical.tsx +50 -22
  30. package/src/components/BarChart/helpers/index.ts +102 -0
  31. package/src/components/EditorPanel/EditorPanel.tsx +232 -98
  32. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +306 -0
  33. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +117 -6
  34. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +2 -3
  35. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +3 -2
  36. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  37. package/src/components/EditorPanel/components/panels.scss +4 -0
  38. package/src/components/EditorPanel/editor-panel.scss +19 -0
  39. package/src/components/EditorPanel/useEditorPermissions.js +19 -2
  40. package/src/components/Legend/Legend.Component.tsx +7 -8
  41. package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
  42. package/src/components/Legend/helpers/index.ts +5 -0
  43. package/src/components/LineChart/LineChartProps.ts +3 -0
  44. package/src/components/LineChart/helpers.ts +21 -7
  45. package/src/components/LineChart/index.tsx +7 -7
  46. package/src/components/LinearChart.jsx +179 -136
  47. package/src/components/PairedBarChart.jsx +9 -9
  48. package/src/components/PieChart/PieChart.tsx +4 -4
  49. package/src/components/Sankey/index.tsx +73 -20
  50. package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
  51. package/src/components/ZoomBrush.tsx +90 -44
  52. package/src/data/initial-state.js +14 -6
  53. package/src/helpers/handleChartTabbing.ts +8 -0
  54. package/src/helpers/isConvertLineToBarGraph.ts +4 -0
  55. package/src/hooks/{useBarChart.js → useBarChart.ts} +2 -40
  56. package/src/hooks/useColorScale.ts +1 -1
  57. package/src/hooks/useMinMax.ts +8 -3
  58. package/src/hooks/useScales.ts +25 -3
  59. package/src/hooks/useTooltip.tsx +58 -11
  60. package/src/scss/main.scss +21 -14
  61. package/src/types/ChartConfig.ts +50 -3
  62. package/src/types/ChartContext.ts +9 -0
  63. package/tests-examples/helpers/testZeroValue.test.ts +30 -0
  64. package/LICENSE +0 -201
  65. package/src/helpers/filterData.ts +0 -18
  66. package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
  67. /package/src/hooks/{useLegendClasses.js → useLegendClasses.ts} +0 -0
  68. /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
@@ -0,0 +1,127 @@
1
+ {
2
+ "type": "chart",
3
+ "visualizationType": "Line",
4
+ "xAxis": {
5
+ "dataKey": "STATE"
6
+ },
7
+ "series": [
8
+ {
9
+ "dataKey": "Rate"
10
+ }
11
+ ],
12
+ "filters": [
13
+ {
14
+ "values": ["Home", "School", "Vehicle", "Work"],
15
+ "active": "Home",
16
+ "order": "asc",
17
+ "columnName": "Location",
18
+ "setByQueryParameter": "location"
19
+ }
20
+ ],
21
+ "data": [
22
+ {
23
+ "STATE": "AL",
24
+ "Rate": "10",
25
+ "Location": "Home",
26
+ "URL": "https://www.cdc.gov/",
27
+ "Intensity Level": "Low",
28
+ "Trajectory": "Increasing"
29
+ },
30
+ {
31
+ "STATE": "AK",
32
+ "Rate": "12",
33
+ "Location": "Home",
34
+ "URL": "https://www.cdc.gov/",
35
+ "Intensity Level": "Low",
36
+ "Trajectory": "Increasing"
37
+ },
38
+ {
39
+ "STATE": "FL",
40
+ "Rate": "10",
41
+ "Location": "Work",
42
+ "URL": "https://www.cdc.gov/",
43
+ "Intensity Level": "Low",
44
+ "Trajectory": "Increasing"
45
+ },
46
+ {
47
+ "STATE": "GA",
48
+ "Rate": "12",
49
+ "Location": "Work",
50
+ "URL": "https://www.cdc.gov/",
51
+ "Intensity Level": "Low",
52
+ "Trajectory": "Increasing"
53
+ },
54
+ {
55
+ "STATE": "AL",
56
+ "Rate": "10",
57
+ "Location": "Work",
58
+ "URL": "https://www.cdc.gov/",
59
+ "Intensity Level": "Low",
60
+ "Trajectory": "Increasing"
61
+ },
62
+ {
63
+ "STATE": "AK",
64
+ "Rate": "12",
65
+ "Location": "Work",
66
+ "URL": "https://www.cdc.gov/",
67
+ "Intensity Level": "Low",
68
+ "Trajectory": "Increasing"
69
+ }
70
+ ],
71
+ "dataFileName": "hex-data.csv",
72
+ "dataFileSourceType": "file",
73
+ "formattedData": [
74
+ {
75
+ "STATE": "AL",
76
+ "Rate": "10",
77
+ "Location": "Home",
78
+ "URL": "https://www.cdc.gov/",
79
+ "Intensity Level": "Low",
80
+ "Trajectory": "Increasing"
81
+ },
82
+ {
83
+ "STATE": "AK",
84
+ "Rate": "12",
85
+ "Location": "Home",
86
+ "URL": "https://www.cdc.gov/",
87
+ "Intensity Level": "Low",
88
+ "Trajectory": "Increasing"
89
+ },
90
+ {
91
+ "STATE": "AL",
92
+ "Rate": "10",
93
+ "Location": "Work",
94
+ "URL": "https://www.cdc.gov/",
95
+ "Intensity Level": "Low",
96
+ "Trajectory": "Increasing"
97
+ },
98
+ {
99
+ "STATE": "AK",
100
+ "Rate": "12",
101
+ "Location": "Work",
102
+ "URL": "https://www.cdc.gov/",
103
+ "Intensity Level": "Low",
104
+ "Trajectory": "Increasing"
105
+ },
106
+ {
107
+ "STATE": "FL",
108
+ "Rate": "10",
109
+ "Location": "Work",
110
+ "URL": "https://www.cdc.gov/",
111
+ "Intensity Level": "Low",
112
+ "Trajectory": "Increasing"
113
+ },
114
+ {
115
+ "STATE": "GA",
116
+ "Rate": "12",
117
+ "Location": "Work",
118
+ "URL": "https://www.cdc.gov/",
119
+ "Intensity Level": "Low",
120
+ "Trajectory": "Increasing"
121
+ }
122
+ ],
123
+ "dataDescription": {
124
+ "horizontal": false,
125
+ "series": false
126
+ }
127
+ }
@@ -0,0 +1,171 @@
1
+ {
2
+ "type": "chart",
3
+ "title": "Lollipop Style Horizontal Bar Chart - Number of Spills Occurring in the Home",
4
+ "showTitle": true,
5
+ "showDownloadMediaButton": false,
6
+ "theme": "theme-blue",
7
+ "animate": true,
8
+ "fontSize": "medium",
9
+ "lineDatapointStyle": "hover",
10
+ "barHasBorder": "false",
11
+ "isLollipopChart": true,
12
+ "lollipopShape": "circle",
13
+ "lollipopColorStyle": "two-tone",
14
+ "visualizationSubType": "horizontal",
15
+ "barStyle": "",
16
+ "roundingStyle": "standard",
17
+ "tipRounding": "top",
18
+ "isResponsiveTicks": false,
19
+ "general": { "showDownloadButton": false },
20
+ "padding": { "left": 5, "right": 5 },
21
+ "yAxis": {
22
+ "hideAxis": true,
23
+ "displayNumbersOnBar": true,
24
+ "hideLabel": false,
25
+ "hideTicks": false,
26
+ "size": "13",
27
+ "gridLines": false,
28
+ "enablePadding": false,
29
+ "min": "",
30
+ "max": "",
31
+ "labelColor": "#333",
32
+ "tickLabelColor": "#333",
33
+ "tickColor": "#333",
34
+ "rightHideAxis": true,
35
+ "rightAxisSize": 50,
36
+ "rightLabel": "",
37
+ "rightLabelOffsetSize": 0,
38
+ "rightAxisLabelColor": "#333",
39
+ "rightAxisTickLabelColor": "#333",
40
+ "rightAxisTickColor": "#333",
41
+ "numTicks": "9",
42
+ "axisPadding": 0,
43
+ "tickRotation": 0,
44
+ "anchors": [],
45
+ "type": "chart",
46
+ "title": "Lollipop Style Horizontal Bar Chart",
47
+ "theme": "theme-blue",
48
+ "fontSize": "medium",
49
+ "lineDatapointStyle": "hover",
50
+ "barHasBorder": "false",
51
+ "isLollipopChart": false,
52
+ "lollipopShape": "circle",
53
+ "lollipopColorStyle": "two-tone",
54
+ "visualizationSubType": "horizontal",
55
+ "padding": { "left": 5, "right": 5 },
56
+ "yAxis": { "size": 50, "gridLines": false },
57
+ "barThickness": 0.35,
58
+ "height": 260,
59
+ "xAxis": { "type": "categorical", "size": 75, "tickRotation": 0, "dataKey": "Vehicle" },
60
+ "table": { "label": "Data Table", "expanded": true, "show": true },
61
+ "legend": { "behavior": "isolate", "position": "right" },
62
+ "exclusions": { "active": false, "keys": [] },
63
+ "palette": "qualitative-bold",
64
+ "labels": false,
65
+ "dataFormat": {},
66
+ "confidenceKeys": {},
67
+ "data": [
68
+ { "Group": "Combined Total of Group A", "Vehicle": "100", "Home": "120", "Work": "140", "Office": "120" },
69
+ { "Group": "Combined Total of Group B", "Vehicle": "150", "Home": "140", "Work": "100", "Office": "90" },
70
+ { "Group": "Combined Total of Group C", "Vehicle": "90", "Home": "90", "Work": "80", "Office": "80" },
71
+ { "Group": "Combined Total of Group D", "Vehicle": "70", "Home": "60", "Work": "50", "Office": "70" }
72
+ ],
73
+ "dataFileName": "CSV_Source_Example_for_Horizontal_Bar_viz-cdcwp1619811744363.csv",
74
+ "dataFileSourceType": "file",
75
+ "visualizationType": "Bar",
76
+ "runtime": {
77
+ "seriesLabels": { "Vehicle": "Vehicle" },
78
+ "seriesLabelsAll": ["Vehicle"],
79
+ "originalXAxis": { "type": "categorical", "size": 75, "tickRotation": 0, "dataKey": "Vehicle" },
80
+ "seriesKeys": ["Vehicle"],
81
+ "xAxis": { "size": 50, "gridLines": false },
82
+ "yAxis": { "type": "categorical", "size": 75, "tickRotation": 0, "dataKey": "Vehicle" },
83
+ "horizontal": true,
84
+ "uniqueId": 1651765968212,
85
+ "editorErrorMessage": ""
86
+ },
87
+ "description": "Subtext can be added here for options like citing data sources or insight into reading the bar chart.",
88
+ "series": [{ "dataKey": "Vehicle", "type": "Bar" }],
89
+ "barHeight": 25,
90
+ "barPadding": 40,
91
+ "labelPlacement": "Below Bar",
92
+ "label": "Number of Accidents"
93
+ },
94
+ "boxplot": [],
95
+ "topAxis": { "hasLine": false },
96
+ "isLegendValue": false,
97
+ "barThickness": 0.35,
98
+ "barHeight": 6,
99
+ "barSpace": 15,
100
+ "heights": { "vertical": 300, "horizontal": 170.39999999999998 },
101
+ "xAxis": {
102
+ "anchors": [],
103
+ "type": "categorical",
104
+ "showTargetLabel": true,
105
+ "targetLabel": "Target",
106
+ "hideAxis": true,
107
+ "hideLabel": true,
108
+ "hideTicks": true,
109
+ "size": "16",
110
+ "tickRotation": 0,
111
+ "min": "",
112
+ "max": "160",
113
+ "labelColor": "#333",
114
+ "tickLabelColor": "#333",
115
+ "tickColor": "#333",
116
+ "numTicks": "",
117
+ "labelOffset": 65,
118
+ "axisPadding": 0,
119
+ "target": 0,
120
+ "maxTickRotation": 0,
121
+ "dataKey": "Group"
122
+ },
123
+ "table": { "label": "Data Table", "expanded": false, "limitHeight": false, "height": "", "caption": "", "showDownloadUrl": false, "showDataTableLink": true, "indexLabel": "Group", "download": false, "showVertical": true, "show": true },
124
+ "orientation": "horizontal",
125
+ "color": "pinkpurple",
126
+ "columns": {},
127
+ "legend": {
128
+ "behavior": "isolate",
129
+ "singleRow": false,
130
+ "colorCode": "",
131
+ "reverseLabelOrder": false,
132
+ "description": "",
133
+ "dynamicLegend": false,
134
+ "dynamicLegendDefaultText": "Show All",
135
+ "dynamicLegendItemLimit": 5,
136
+ "dynamicLegendItemLimitMessage": "Dynamic Legend Item Limit Hit.",
137
+ "dynamicLegendChartMessage": "Select Options from the Legend",
138
+ "position": "right",
139
+ "hide": true,
140
+ "label": "Accident Location"
141
+ },
142
+ "exclusions": { "active": false, "keys": [] },
143
+ "palette": "qualitative-bold",
144
+ "isPaletteReversed": false,
145
+ "twoColor": { "palette": "monochrome-1", "isPaletteReversed": false },
146
+ "labels": false,
147
+ "dataFormat": { "commas": false, "prefix": "", "suffix": "", "abbreviated": false, "bottomSuffix": "", "bottomPrefix": "", "bottomAbbreviated": false },
148
+ "confidenceKeys": {},
149
+ "visual": { "border": true, "accent": true, "background": true, "verticalHoverLine": false, "horizontalHoverLine": false },
150
+ "useLogScale": false,
151
+ "filterBehavior": "Filter Change",
152
+ "highlightedBarValues": [],
153
+ "series": [{ "dataKey": "Home", "type": "Bar", "tooltip": true }],
154
+ "tooltips": { "opacity": 90 },
155
+ "height": 212,
156
+ "data": [
157
+ { "Group": "Combined Total of Group A", "Vehicle": "100", "Home": "120", "Work": "140", "Office": "120" },
158
+ { "Group": "Combined Total of Group B", "Vehicle": "150", "Home": "140", "Work": "100", "Office": "90" },
159
+ { "Group": "Combined Total of Group C", "Vehicle": "90", "Home": "90", "Work": "80", "Office": "80" },
160
+ { "Group": "Combined Total of Group D", "Vehicle": "70", "Home": "60", "Work": "50", "Office": "70" }
161
+ ],
162
+ "dataFileName": "CSV_Source_Example_for_Horizontal_Bar_viz-cdcwp1619811744363.csv",
163
+ "dataFileSourceType": "file",
164
+ "visualizationType": "Bar",
165
+ "description": "Subtext can be added here for options like citing data sources or insight into reading the bar chart.",
166
+ "barPadding": 47,
167
+ "filters": [],
168
+ "lollipopSize": "medium",
169
+ "validated": 4.23,
170
+ "dynamicMarginTop": 0
171
+ }
@@ -0,0 +1,31 @@
1
+ .cdc-open-viz-module .annotation__desktop-label {
2
+ display: none;
3
+ }
4
+
5
+ .cdc-open-viz-module {
6
+ .annotation__mobile-label-circle {
7
+ stroke-width: 2;
8
+ }
9
+
10
+ .annotation__has-dropdown-number {
11
+ display: flex;
12
+ justify-content: center;
13
+ align-items: center;
14
+ border-radius: 50%;
15
+ padding: 8px;
16
+ width: 16px;
17
+ height: 16px;
18
+ border: 2px solid #666;
19
+ text-align: center;
20
+ font-size: 14px;
21
+ }
22
+ }
23
+
24
+ @container content (min-width: 700px) {
25
+ .cdc-open-viz-module .annotation__mobile-label {
26
+ display: none;
27
+ }
28
+ .cdc-open-viz-module .annotation__desktop-label {
29
+ display: block;
30
+ }
31
+ }
@@ -0,0 +1,154 @@
1
+ import { useContext, useEffect, useRef, useState } from 'react'
2
+ import ConfigContext from '../../../ConfigContext'
3
+ import DOMPurify from 'dompurify'
4
+
5
+ // helpers
6
+ import { findNearestDatum } from './findNearestDatum'
7
+
8
+ // prettier-ignore
9
+ import {
10
+ applyBandScaleOffset,
11
+ handleConnectionHorizontalType,
12
+ handleConnectionVerticalType,
13
+ handleMobileXPosition,
14
+ handleMobileYPosition,
15
+ handleTextX,
16
+ handleTextY
17
+ } from './helpers'
18
+
19
+ import useColorScale from '../../../hooks/useColorScale'
20
+
21
+ // visx
22
+ import { HtmlLabel, CircleSubject, EditableAnnotation, Connector, Annotation as VisxAnnotation } from '@visx/annotation'
23
+ import { Drag } from '@visx/drag'
24
+ import { MarkerArrow } from '@visx/marker'
25
+ import { LinePath } from '@visx/shape'
26
+ import { fontSizes } from '@cdc/core/helpers/cove/fontSettings'
27
+
28
+ // styles
29
+ import './AnnotationDraggable.styles.css'
30
+
31
+ const Annotations = ({ xScale, yScale, xScaleAnnotation, xMax, svgRef, onDragStateChange }) => {
32
+ // prettier-ignore
33
+ const {
34
+ config,
35
+ currentViewport,
36
+ dimensions,
37
+ isDraggingAnnotation,
38
+ isEditor,
39
+ isLegendBottom,
40
+ updateConfig
41
+ } = useContext(ConfigContext)
42
+
43
+ // destructure config items here...
44
+ const { annotations } = config
45
+ const [height] = dimensions
46
+ const { colorScale } = useColorScale()
47
+ const AnnotationComponent = isEditor ? EditableAnnotation : VisxAnnotation
48
+
49
+ return (
50
+ annotations &&
51
+ annotations.map((annotation, index) => {
52
+ const text = annotation.text || ''
53
+
54
+ const annotationX = xScaleAnnotation(annotation.x)
55
+
56
+ // sanitize the text for setting dangerouslySetInnerHTML
57
+ const sanitizedData = () => ({
58
+ __html: DOMPurify.sanitize(text)
59
+ })
60
+
61
+ return (
62
+ <AnnotationComponent
63
+ width={200}
64
+ height={height}
65
+ dx={annotation.dx} // label position
66
+ dy={annotation.dy} // label postion
67
+ x={annotationX}
68
+ y={annotation.y}
69
+ canEditLabel={annotation.edit.label || false}
70
+ canEditSubject={(annotation.edit.subject && annotation.connectionType !== 'none') || false}
71
+ onDragStart={() => onDragStateChange(true)}
72
+ onDragEnd={props => {
73
+ onDragStateChange(false)
74
+
75
+ let updatedAnnotations = [...annotations]
76
+
77
+ if (annotation.x === xScaleAnnotation.invert(props.x) && annotation.y === props.y) {
78
+ updatedAnnotations[index] = { ...updatedAnnotations[index], dx: props.dx, dy: props.dy }
79
+ } else {
80
+ if (annotation.snapToNearestPoint) {
81
+ let nearestDatum = findNearestDatum(
82
+ {
83
+ data: config.data,
84
+ xScale,
85
+ yScale,
86
+ config,
87
+ xMax: xMax - config.yAxis.size / 2,
88
+ annotationSeriesKey: annotation.seriesKey
89
+ },
90
+ props.x
91
+ )
92
+
93
+ updatedAnnotations[index] = { ...updatedAnnotations[index], x: xScaleAnnotation.invert(xScale(nearestDatum.x)), y: yScale(nearestDatum.y) }
94
+ } else {
95
+ updatedAnnotations[index] = { ...updatedAnnotations[index], x: xScaleAnnotation.invert(props.x), y: props.y }
96
+ }
97
+ }
98
+
99
+ updateConfig({
100
+ ...config,
101
+ annotations: updatedAnnotations
102
+ })
103
+ }}
104
+ >
105
+ <HtmlLabel className='annotation__desktop-label' showAnchorLine={false} horizontalAnchor={handleConnectionHorizontalType(annotation, xScale, config)} verticalAnchor={handleConnectionVerticalType(annotation, xScale, config)}>
106
+ <div
107
+ style={{
108
+ borderRadius: 5, // Optional: set border radius
109
+ backgroundColor: `rgba(255, 255, 255, ${annotation?.opacity ? Number(annotation?.opacity) / 100 : 1})`,
110
+ padding: '10px',
111
+ width: 'auto',
112
+ display: config.general.showAnnotationDropdown ? 'inline-flex' : 'flex',
113
+ justifyContent: 'start',
114
+ flexDirection: 'row'
115
+ }}
116
+ role='presentation'
117
+ tabIndex={0}
118
+ aria-label={`Annotation text that reads: ${annotation.text}`}
119
+ >
120
+ {config?.general?.showAnnotationDropdown && (
121
+ <>
122
+ <p className='annotation__has-dropdown-number' style={{ margin: '2px 6px' }}>
123
+ {index + 1}
124
+ </p>
125
+ </>
126
+ )}
127
+ <div style={{ fontSize: fontSizes[config.fontSize] }} dangerouslySetInnerHTML={sanitizedData()} />
128
+ </div>
129
+ </HtmlLabel>
130
+ {annotation.connectionType === 'line' && <Connector type='line' pathProps={{ markerStart: `url(#marker-start--${index})` }} />}
131
+ {annotation.connectionType === 'elbow' && <Connector type='elbow' pathProps={{ markerStart: `url(#marker-start--${index})` }} />}
132
+ {annotation.connectionType === 'curve' && (
133
+ <LinePath
134
+ d={`M ${annotationX},${annotation.y}
135
+ Q ${annotationX + annotation.dx / 2}, ${annotation.y + annotation.dy / 2 + Number(annotation?.bezier) || 0} ${annotationX + annotation.dx},${annotation.y + annotation.dy}`}
136
+ stroke='black'
137
+ strokeWidth='2'
138
+ fill='none'
139
+ marker-start={`url(#marker-start--${index})`}
140
+ />
141
+ )}
142
+ {annotation.marker === 'circle' && <CircleSubject id={`marker-start--${index}`} className='circle-subject' stroke={colorScale(annotation.seriesKey)} radius={8} />}
143
+ {annotation.marker === 'arrow' && <MarkerArrow fill='black' id={`marker-start--${index}`} x={annotationX} y={annotation.y} stroke='#333' markerWidth={10} size={10} strokeWidth={1} orient='auto-start-reverse' markerUnits='userSpaceOnUse' />}
144
+ <circle fill='white' cx={annotationX + annotation.dx} cy={annotation.y + annotation.dy} r={16} className='annotation__mobile-label annotation__mobile-label-circle' stroke={colorScale(annotation.seriesKey)} />
145
+ <text height={16} x={annotationX + annotation.dx} y={annotation.y + annotation.dy} className='annotation__mobile-label' alignmentBaseline='middle' textAnchor='middle'>
146
+ {index + 1}
147
+ </text>
148
+ </AnnotationComponent>
149
+ )
150
+ })
151
+ )
152
+ }
153
+
154
+ export default Annotations
@@ -0,0 +1,14 @@
1
+ .cdc-open-viz-module {
2
+ .annotation__dropdown-list {
3
+ border: 1px solid red;
4
+ list-style: none;
5
+ }
6
+
7
+ .annotation-dropdown__panel {
8
+ padding: 10px;
9
+ }
10
+
11
+ .data-table-heading.annotation__dropdown-list {
12
+ margin-top: 10px;
13
+ }
14
+ }
@@ -0,0 +1,72 @@
1
+ import React, { useContext, useState } from 'react'
2
+ import ConfigContext from '../../../ConfigContext'
3
+ import './AnnotationDropdown.styles.css'
4
+ import Icon from '@cdc/core/components/ui/Icon'
5
+ import Annotation from '..'
6
+ import { fontSizes } from '@cdc/core/helpers/cove/fontSettings'
7
+
8
+ const AnnotationDropdown = () => {
9
+ const { currentViewport: viewport, config } = useContext(ConfigContext)
10
+ const [expanded, setExpanded] = useState(false)
11
+
12
+ const titleFontSize = ['sm', 'xs', 'xxs'].includes(viewport) ? '13px' : `${fontSizes[config?.fontSize]}px`
13
+
14
+ const {
15
+ config: { annotations }
16
+ } = useContext(ConfigContext)
17
+
18
+ const limitHeight = {
19
+ maxHeight: config.table.limitHeight && `${config.table.height}px`,
20
+ OverflowY: 'scroll'
21
+ }
22
+
23
+ const handleAccordionClassName = () => {
24
+ const classNames = ['data-table-heading', 'annotation__dropdown-list']
25
+ if (!expanded) {
26
+ classNames.push('collapsed')
27
+ }
28
+
29
+ return classNames.join(' ')
30
+ }
31
+
32
+ const handleSectionClasses = () => {
33
+ const classes = [`data-table-container`, viewport, `d-block`, `d-lg-none`]
34
+
35
+ if (config.general.showAnnotationDropdown) {
36
+ classes.push('d-lg-block')
37
+ classes.splice(classes.indexOf('d-lg-none'), 1)
38
+ }
39
+ return classes.join(' ')
40
+ }
41
+
42
+ return (
43
+ <>
44
+ <section className={handleSectionClasses()}>
45
+ <div
46
+ style={{ fontSize: titleFontSize }}
47
+ role='button'
48
+ className={handleAccordionClassName()}
49
+ onClick={() => {
50
+ setExpanded(!expanded)
51
+ }}
52
+ tabIndex={0}
53
+ onKeyDown={e => {
54
+ if (e.keyCode === 13) {
55
+ setExpanded(!expanded)
56
+ }
57
+ }}
58
+ >
59
+ <Icon display={expanded ? 'minus' : 'plus'} base />
60
+ {config.general.annotationDropdownText === '' ? 'Annotations' : config?.general?.annotationDropdownText}
61
+ </div>
62
+ {expanded && (
63
+ <div className='table-container annotation-dropdown__panel' style={limitHeight}>
64
+ <Annotation.List useBootstrapVisibilityClasses={false} />
65
+ </div>
66
+ )}
67
+ </section>
68
+ </>
69
+ )
70
+ }
71
+
72
+ export default AnnotationDropdown
@@ -0,0 +1,45 @@
1
+ .cdc-open-viz-module {
2
+ .annotation__title-circle {
3
+ display: flex;
4
+ justify-content: center;
5
+ align-items: center;
6
+ border-radius: 50%;
7
+ padding: 8px;
8
+ width: 16px;
9
+ height: 16px;
10
+ margin-right: 5px;
11
+ line-height: 1.5rem;
12
+
13
+ border: 2px solid #666;
14
+ text-align: center;
15
+ font-size: 14px;
16
+ }
17
+
18
+ .annotation__title-wrapper {
19
+ display: flex;
20
+ flex-wrap: nowrap;
21
+ align-items: center;
22
+ }
23
+
24
+ .annotation__title-wrapper .annotation__title-text {
25
+ margin-left: 5px;
26
+ font-size: 16px;
27
+ }
28
+
29
+ .annotation__subtext {
30
+ font-size: 12px;
31
+ }
32
+
33
+ .annotation-list {
34
+ list-style: none;
35
+ }
36
+
37
+ .annotation-list li {
38
+ margin-top: 5px;
39
+ }
40
+
41
+ .cove-component__content {
42
+ container-type: inline-size;
43
+ container-name: content;
44
+ }
45
+ }
@@ -0,0 +1,42 @@
1
+ import React, { useContext } from 'react'
2
+ import ConfigContext from '../../../ConfigContext'
3
+ import './AnnotationList.styles.css'
4
+ import DOMPurify from 'dompurify'
5
+
6
+ type AnnotationListProps = {
7
+ useBootstrapVisibilityClasses?: boolean
8
+ }
9
+
10
+ const AnnotationList: React.FC<AnnotationListProps> = ({ useBootstrapVisibilityClasses = true }) => {
11
+ const { config } = useContext(ConfigContext)
12
+ const annotations = config.annotations || []
13
+
14
+ const ulClasses = () => {
15
+ const classes = ['annotation-list']
16
+ if (useBootstrapVisibilityClasses) {
17
+ classes.push('d-block', 'd-md-none')
18
+ }
19
+ return classes.join(' ')
20
+ }
21
+
22
+ const annotationListItems = annotations.map((annotation, annotationIndex) => {
23
+ const text = annotation.text || ''
24
+
25
+ // sanitize the text for setting dangerouslySetInnerHTML
26
+ const sanitizedData = () => ({
27
+ __html: DOMPurify.sanitize(text)
28
+ })
29
+ return (
30
+ <li key={`annotation-li-item__annotationIndex`}>
31
+ <div className='annotation__title-wrapper'>
32
+ <div className='annotation__title-circle'>{annotationIndex + 1}</div>
33
+ <p className='annotation__subtext' dangerouslySetInnerHTML={sanitizedData()} />
34
+ </div>
35
+ </li>
36
+ )
37
+ })
38
+
39
+ return <ul className={ulClasses()}>{annotationListItems}</ul>
40
+ }
41
+
42
+ export default AnnotationList