@cdc/chart 1.3.1 → 1.3.3

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 (56) hide show
  1. package/dist/cdcchart.js +85 -0
  2. package/examples/age-adjusted-rates.json +1218 -0
  3. package/examples/case-rate-example-config.json +36 -0
  4. package/examples/case-rate-example-data.json +33602 -0
  5. package/examples/covid-confidence-example-config.json +35 -0
  6. package/examples/covid-example-config.json +36 -0
  7. package/examples/covid-example-data-confidence.json +32 -0
  8. package/examples/covid-example-data.json +22 -0
  9. package/examples/cutoff-example-config.json +36 -0
  10. package/examples/cutoff-example-data.json +38 -0
  11. package/examples/date-exclusions-config.json +62 -0
  12. package/examples/date-exclusions-data.json +162 -0
  13. package/examples/horizontal-chart.json +35 -0
  14. package/examples/horizontal-stacked-bar-chart.json +36 -0
  15. package/examples/line-chart.json +76 -0
  16. package/examples/paired-bar-data.json +14 -0
  17. package/examples/paired-bar-example.json +48 -0
  18. package/examples/paired-bar-formatted.json +37 -0
  19. package/examples/planet-chart-horizontal-example-config.json +35 -0
  20. package/examples/planet-combo-example-config.json +31 -0
  21. package/examples/planet-example-config.json +35 -0
  22. package/examples/planet-example-data.json +56 -0
  23. package/examples/planet-pie-example-config.json +28 -0
  24. package/examples/private/newtest.csv +101 -0
  25. package/examples/private/test.json +10124 -0
  26. package/examples/temp-example-config.json +57 -0
  27. package/examples/temp-example-data.json +130 -0
  28. package/package.json +9 -8
  29. package/src/CdcChart.tsx +836 -0
  30. package/src/components/BarChart.tsx +571 -0
  31. package/src/components/BarStackVertical.js +0 -0
  32. package/src/components/DataTable.tsx +229 -0
  33. package/src/components/EditorPanel.js +1319 -0
  34. package/src/components/LineChart.tsx +76 -0
  35. package/src/components/LinearChart.tsx +459 -0
  36. package/src/components/PairedBarChart.tsx +144 -0
  37. package/src/components/PieChart.tsx +189 -0
  38. package/src/components/SparkLine.js +206 -0
  39. package/src/context.tsx +5 -0
  40. package/src/data/initial-state.js +61 -0
  41. package/src/hooks/useActiveElement.js +19 -0
  42. package/src/hooks/useColorPalette.ts +83 -0
  43. package/src/hooks/useReduceData.ts +43 -0
  44. package/src/images/active-checkmark.svg +1 -0
  45. package/src/images/asc.svg +1 -0
  46. package/src/images/desc.svg +1 -0
  47. package/src/images/inactive-checkmark.svg +1 -0
  48. package/src/images/warning.svg +1 -0
  49. package/src/index.html +68 -0
  50. package/src/index.tsx +21 -0
  51. package/src/scss/DataTable.scss +23 -0
  52. package/src/scss/LinearChart.scss +0 -0
  53. package/src/scss/editor-panel.scss +693 -0
  54. package/src/scss/main.scss +426 -0
  55. package/src/scss/mixins.scss +0 -0
  56. package/src/scss/variables.scss +1 -0
@@ -0,0 +1,229 @@
1
+ import React, {
2
+ useContext,
3
+ useEffect,
4
+ useState,
5
+ useMemo,
6
+ memo,
7
+ Fragment} from 'react';
8
+ import {
9
+ useTable,
10
+ useSortBy,
11
+ useResizeColumns,
12
+ useBlockLayout
13
+ } from 'react-table';
14
+ import Papa from 'papaparse';
15
+ import { Base64 } from 'js-base64';
16
+
17
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
18
+ import LegendCircle from '@cdc/core/components/LegendCircle';
19
+
20
+ import Context from '../context';
21
+
22
+ export default function DataTable() {
23
+ const { rawData, transformedData: data, config, colorScale, parseDate, formatDate, formatNumber:numberFormatter } = useContext<any>(Context);
24
+
25
+ const legendGlyphSize = 15;
26
+ const legendGlyphSizeHalf = legendGlyphSize / 2;
27
+ const section = config.orientation ==='horizontal' ? 'yAxis' :'xAxis';
28
+ const [tableExpanded, setTableExpanded] = useState<boolean>(config.table.expanded);
29
+ const [accessibilityLabel, setAccessibilityLabel] = useState('');
30
+
31
+ const DownloadButton = ({ data }: any) => {
32
+ const fileName = `${config.title.substring(0, 50)}.csv`;
33
+
34
+ const csvData = Papa.unparse(data);
35
+
36
+ const saveBlob = () => {
37
+ //@ts-ignore
38
+ if (typeof window.navigator.msSaveBlob === 'function') {
39
+ const dataBlob = new Blob([csvData], { type: "text/csv;charset=utf-8;" });
40
+ //@ts-ignore
41
+ window.navigator.msSaveBlob(dataBlob, fileName);
42
+ }
43
+ }
44
+
45
+ return (
46
+ <a
47
+ download={fileName}
48
+ onClick={saveBlob}
49
+ href={`data:text/csv;base64,${Base64.encode(csvData)}`}
50
+ aria-label="Download this data in a CSV file format."
51
+ className={`btn btn-download no-border`}
52
+ >
53
+ Download Data (CSV)
54
+ </a>
55
+ )
56
+ };
57
+
58
+ // Creates columns structure for the table
59
+ const tableColumns = useMemo(() => {
60
+ const newTableColumns = config.visualizationType === 'Pie' ? [] : [{
61
+ Header: '',
62
+ Cell: ({ row }) => {
63
+ const seriesLabel = config.runtime.seriesLabels ? config.runtime.seriesLabels[row.original] : row.original;
64
+ return (
65
+ <Fragment>
66
+ {config.visualizationType !== 'Pie' && <LegendCircle fill={colorScale(seriesLabel)} />}
67
+ <span>{seriesLabel}</span>
68
+ </Fragment>
69
+ )
70
+ },
71
+ id: 'series-label'
72
+ }];
73
+
74
+ data.forEach((d) => {
75
+ const newCol = {
76
+ Header: config.runtime[section].type === 'date' ? formatDate(parseDate(d[config.runtime.originalXAxis.dataKey])) : d[config.runtime.originalXAxis.dataKey],
77
+ Cell: ({ row }) => {
78
+ return (
79
+ <>
80
+ {numberFormatter(d[row.original])}
81
+ </>
82
+ );
83
+ },
84
+ id: d[config.runtime.originalXAxis.dataKey],
85
+ canSort: true
86
+ };
87
+
88
+ newTableColumns.push(newCol);
89
+ });
90
+
91
+ return newTableColumns;
92
+ }, [config,colorScale]);
93
+
94
+
95
+
96
+ const tableData = useMemo(
97
+ () => config.visualizationType === 'Pie' ? [config.yAxis.dataKey] : config.runtime.seriesKeys,
98
+ [config.runtime.seriesKeys]
99
+ );
100
+
101
+ // Change accessibility label depending on expanded status
102
+ useEffect(() => {
103
+ const expandedLabel = 'Accessible data table.';
104
+ const collapsedLabel = 'Accessible data table. This table is currently collapsed visually but can still be read using a screen reader.';
105
+
106
+ if (tableExpanded === true && accessibilityLabel !== expandedLabel) {
107
+ setAccessibilityLabel(expandedLabel);
108
+ }
109
+
110
+ if (tableExpanded === false && accessibilityLabel !== collapsedLabel) {
111
+ setAccessibilityLabel(collapsedLabel);
112
+ }
113
+ // eslint-disable-next-line react-hooks/exhaustive-deps
114
+ }, [tableExpanded]);
115
+
116
+ const defaultColumn = useMemo(
117
+ () => ({
118
+ minWidth: 150,
119
+ width: 200,
120
+ maxWidth: 400,
121
+ }),
122
+ []
123
+ );
124
+
125
+ const {
126
+ getTableProps,
127
+ getTableBodyProps,
128
+ headerGroups,
129
+ rows,
130
+ prepareRow,
131
+ } = useTable({ columns: tableColumns, data: tableData, defaultColumn }, useSortBy, useBlockLayout, useResizeColumns);
132
+ return (
133
+ <ErrorBoundary component="DataTable">
134
+ <section id={config?.title ? `dataTableSection__${config?.title.replace(/\s/g, '')}` : `dataTableSection`} className={`data-table-container`} aria-label={accessibilityLabel}>
135
+ <div
136
+ role="button"
137
+ className={tableExpanded ? 'data-table-heading' : 'collapsed data-table-heading'}
138
+ tabIndex={0}
139
+ onClick={() => { setTableExpanded(!tableExpanded); }}
140
+ onKeyDown={(e) => { if (e.keyCode === 13) { setTableExpanded(!tableExpanded); } }}
141
+ >
142
+ {config.table.label}
143
+ </div>
144
+ <div className="table-container">
145
+ <table
146
+ className={tableExpanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}
147
+ hidden={!tableExpanded}
148
+ {...getTableProps()}
149
+ aria-rowcount={ config?.series?.length ? config?.series?.length : '-1' }
150
+ >
151
+ <caption className="visually-hidden">{config.table.label}</caption>
152
+ <thead>
153
+ {headerGroups.map((headerGroup,index) => (
154
+ <tr {...headerGroup.getHeaderGroupProps()} key={`headerGroups--${index}`}>
155
+ {headerGroup.headers.map((column, index) => (
156
+ <th
157
+ tabIndex="0"
158
+ title={column.Header}
159
+ key={`trth--${index}`}
160
+ role="columnheader"
161
+ scope="col"
162
+ {...column.getHeaderProps(column.getSortByToggleProps())}
163
+ className={column.isSorted ? column.isSortedDesc ? 'sort sort-desc' : 'sort sort-asc' : 'sort'}
164
+ {...(column.isSorted ? column.isSortedDesc ? { 'aria-sort': 'descending' } : { 'aria-sort': 'ascending' } : null)}
165
+ >
166
+ {index === 0
167
+ ? config.table.indexLabel
168
+ ? config.table.indexLabel : column.render('Header')
169
+ : column.render('Header')
170
+ }
171
+ <button>
172
+ <span className="cdcdataviz-sr-only">{`Sort by ${typeof column.render('Header') === 'string' ? column.render('Header').toLowerCase() : column.render('Header') } in ${ column.isSorted ? column.isSortedDesc ? 'descending' : 'ascending' : 'no'} `} order</span>
173
+ </button>
174
+ <div {...column.getResizerProps()} className="resizer" />
175
+ </th>
176
+ ))}
177
+ </tr>
178
+ ))}
179
+ </thead>
180
+ <tbody {...getTableBodyProps()}>
181
+ {rows.map((row, index) => {
182
+ prepareRow(row);
183
+ return (
184
+ <tr {...row.getRowProps()} key={`tbody__tr-${index}`}>
185
+ {row.cells.map((cell, index) => (
186
+ <td
187
+ tabIndex="0"
188
+ {...cell.getCellProps()}
189
+ key={`tbody__tr__td-${index}`}
190
+ role="gridcell">
191
+ {cell.render('Cell')}
192
+ </td>
193
+ ))}
194
+ </tr>
195
+ );
196
+ })}
197
+ </tbody>
198
+ </table>
199
+ {config.regions && config.regions.length > 0 ? (
200
+ <table className="region-table data-table">
201
+ <caption className="visually-hidden">Table of the highlighted regions in the visualization</caption>
202
+ <thead>
203
+ <tr>
204
+ <th>Region Name</th>
205
+ <th>Start Date</th>
206
+ <th>End Date</th>
207
+ </tr>
208
+ </thead>
209
+ <tbody>
210
+ {config.regions.map((region,index) => {
211
+ if(!Object.keys(region).includes('from') || !Object.keys(region).includes('to')) return null
212
+
213
+ return (
214
+ <tr key={`row-${region.label}--${index}`}>
215
+ <td>{region.label}</td>
216
+ <td>{formatDate(parseDate(region.from))}</td>
217
+ <td>{formatDate(parseDate(region.to))}</td>
218
+ </tr>
219
+ )
220
+ })}
221
+ </tbody>
222
+ </table>
223
+ ) : ''}
224
+ </div>
225
+ {config.table.download && <DownloadButton data={rawData} />}
226
+ </section>
227
+ </ErrorBoundary>
228
+ );
229
+ }