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