@cdc/dashboard 4.22.10 → 4.23.1
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.
- package/README.md +36 -36
- package/dist/495.js +3 -0
- package/dist/703.js +1 -0
- package/dist/cdcdashboard.js +2405 -134
- package/examples/default-data.json +367 -367
- package/examples/default-filter-control.json +205 -174
- package/examples/default-multi-dataset.json +505 -497
- package/examples/default.json +161 -155
- package/examples/private/chart-issue.json +3462 -3466
- package/examples/private/no-issue.json +3462 -3466
- package/examples/private/totals-two.json +103 -103
- package/examples/private/totals.json +102 -102
- package/examples/temp-example-data.json +1 -1
- package/examples/test-example.json +163 -1
- package/package.json +7 -7
- package/src/CdcDashboard.js +200 -124
- package/src/CdcDashboard.jsx +292 -279
- package/src/ConfigContext.js +3 -3
- package/src/components/Column.jsx +10 -9
- package/src/components/DataTable.tsx +97 -108
- package/src/components/EditorPanel.js +184 -246
- package/src/components/Grid.jsx +8 -4
- package/src/components/Header.jsx +244 -119
- package/src/components/Row.js +30 -29
- package/src/components/Row.jsx +36 -52
- package/src/components/Widget.js +68 -61
- package/src/components/Widget.jsx +85 -62
- package/src/data/initial-state.js +5 -10
- package/src/index.html +3 -3
- package/src/index.js +12 -12
- package/src/scss/editor-panel.scss +494 -489
- package/src/scss/grid.scss +20 -19
- package/src/scss/main.scss +66 -17
- package/src/scss/variables.scss +1 -1
package/src/ConfigContext.js
CHANGED
|
@@ -7,7 +7,7 @@ import Widget from './Widget'
|
|
|
7
7
|
const Column = ({ data, rowIdx, colIdx }) => {
|
|
8
8
|
const { visualizations } = useContext(ConfigContext)
|
|
9
9
|
|
|
10
|
-
const [
|
|
10
|
+
const [{ isOver, canDrop }, drop] = useDrop(() => ({
|
|
11
11
|
accept: 'vis-widget',
|
|
12
12
|
drop: () => ({
|
|
13
13
|
rowIdx,
|
|
@@ -15,7 +15,7 @@ const Column = ({ data, rowIdx, colIdx }) => {
|
|
|
15
15
|
canDrop
|
|
16
16
|
}),
|
|
17
17
|
canDrop: () => !data.widget,
|
|
18
|
-
collect:
|
|
18
|
+
collect: monitor => ({
|
|
19
19
|
isOver: monitor.isOver(),
|
|
20
20
|
canDrop: !!monitor.canDrop()
|
|
21
21
|
})
|
|
@@ -23,10 +23,7 @@ const Column = ({ data, rowIdx, colIdx }) => {
|
|
|
23
23
|
|
|
24
24
|
const widget = data.widget ? visualizations[data.widget] : null
|
|
25
25
|
|
|
26
|
-
let classNames = [
|
|
27
|
-
'builder-column',
|
|
28
|
-
'column-size--' + data.width,
|
|
29
|
-
]
|
|
26
|
+
let classNames = ['builder-column', 'column-size--' + data.width]
|
|
30
27
|
|
|
31
28
|
if (isOver && canDrop) {
|
|
32
29
|
classNames.push('column--drop')
|
|
@@ -38,9 +35,13 @@ const Column = ({ data, rowIdx, colIdx }) => {
|
|
|
38
35
|
|
|
39
36
|
return (
|
|
40
37
|
<div className={classNames.join(' ')} ref={drop}>
|
|
41
|
-
{widget ?
|
|
42
|
-
<Widget data={{ rowIdx, colIdx, ...widget }} type={widget.visualizationType ?? widget.general?.geoType}/>
|
|
43
|
-
|
|
38
|
+
{widget ? (
|
|
39
|
+
<Widget data={{ rowIdx, colIdx, ...widget }} type={widget.visualizationType ?? widget.general?.geoType} />
|
|
40
|
+
) : (
|
|
41
|
+
<p className='builder-column__text'>
|
|
42
|
+
Drag and drop <br /> visualization
|
|
43
|
+
</p>
|
|
44
|
+
)}
|
|
44
45
|
</div>
|
|
45
46
|
)
|
|
46
47
|
}
|
|
@@ -1,170 +1,159 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
memo } from 'react';
|
|
7
|
-
import {
|
|
8
|
-
useTable,
|
|
9
|
-
useSortBy,
|
|
10
|
-
useResizeColumns,
|
|
11
|
-
useBlockLayout
|
|
12
|
-
} from 'react-table';
|
|
13
|
-
import Papa from 'papaparse';
|
|
14
|
-
import { Base64 } from 'js-base64';
|
|
15
|
-
|
|
16
|
-
import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
|
|
1
|
+
import React, { useContext, useEffect, useState, useMemo, memo } from 'react'
|
|
2
|
+
import { useTable, useSortBy, useResizeColumns, useBlockLayout } from 'react-table'
|
|
3
|
+
import Papa from 'papaparse'
|
|
4
|
+
import { Base64 } from 'js-base64'
|
|
5
|
+
import CoveMediaControls from '@cdc/core/helpers/CoveMediaControls'
|
|
17
6
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const { data, datasetKey, config } = props;
|
|
7
|
+
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
21
8
|
|
|
22
|
-
|
|
23
|
-
const
|
|
9
|
+
export default function DataTable(props) {
|
|
10
|
+
const { data, datasetKey, config, imageRef, dataFileSourceType } = props
|
|
11
|
+
const [tableExpanded, setTableExpanded] = useState<boolean>(config.table ? config.table.expanded : false)
|
|
12
|
+
const [accessibilityLabel, setAccessibilityLabel] = useState('')
|
|
24
13
|
|
|
25
14
|
const DownloadButton = memo(({ data }: any) => {
|
|
26
|
-
const fileName = `${config.title ? config.title.substring(0, 50) : 'cdc-open-viz'}.csv
|
|
15
|
+
const fileName = `${config.title ? config.title.substring(0, 50) : 'cdc-open-viz'}.csv`
|
|
27
16
|
|
|
28
|
-
const csvData = Papa.unparse(data)
|
|
17
|
+
const csvData = Papa.unparse(data)
|
|
29
18
|
|
|
30
19
|
const saveBlob = () => {
|
|
31
20
|
//@ts-ignore
|
|
32
21
|
if (typeof window.navigator.msSaveBlob === 'function') {
|
|
33
|
-
const dataBlob = new Blob([csvData], { type:
|
|
22
|
+
const dataBlob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' })
|
|
34
23
|
//@ts-ignore
|
|
35
|
-
window.navigator.msSaveBlob(dataBlob, fileName)
|
|
24
|
+
window.navigator.msSaveBlob(dataBlob, fileName)
|
|
36
25
|
}
|
|
37
26
|
}
|
|
38
27
|
|
|
39
28
|
return (
|
|
40
|
-
<a
|
|
41
|
-
download={fileName}
|
|
42
|
-
onClick={saveBlob}
|
|
43
|
-
href={`data:text/csv;base64,${Base64.encode(csvData)}`}
|
|
44
|
-
aria-label="Download this data in a CSV file format."
|
|
45
|
-
className={`btn btn-download no-border`}
|
|
46
|
-
>
|
|
29
|
+
<a download={fileName} onClick={saveBlob} href={`data:text/csv;base64,${Base64.encode(csvData)}`} aria-label='Download this data in a CSV file format.' className={`no-border dashboard-download-link`}>
|
|
47
30
|
Download {datasetKey ? `"${datasetKey}"` : 'Data'} (CSV)
|
|
48
31
|
</a>
|
|
49
32
|
)
|
|
50
|
-
})
|
|
51
|
-
|
|
33
|
+
})
|
|
34
|
+
|
|
52
35
|
// Creates columns structure for the table
|
|
53
36
|
const tableColumns = useMemo(() => {
|
|
54
|
-
const newTableColumns = []
|
|
37
|
+
const newTableColumns = []
|
|
55
38
|
|
|
56
39
|
// catch no data errors and update the table header.
|
|
57
|
-
if(data.length === 0) {
|
|
58
|
-
return [
|
|
59
|
-
|
|
60
|
-
|
|
40
|
+
if (data.length === 0) {
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
Header: 'No Data Found'
|
|
44
|
+
}
|
|
45
|
+
]
|
|
61
46
|
} else {
|
|
62
|
-
Object.keys(data[0]).map(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
newTableColumns.push(newCol);
|
|
77
|
-
});
|
|
47
|
+
Object.keys(data[0]).map(key => {
|
|
48
|
+
const newCol = {
|
|
49
|
+
Header: key,
|
|
50
|
+
Cell: ({ row }) => {
|
|
51
|
+
return <>{row.original[key]}</>
|
|
52
|
+
},
|
|
53
|
+
id: key,
|
|
54
|
+
canSort: true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
newTableColumns.push(newCol)
|
|
58
|
+
})
|
|
78
59
|
}
|
|
79
60
|
|
|
80
|
-
return newTableColumns
|
|
81
|
-
}, [config])
|
|
61
|
+
return newTableColumns
|
|
62
|
+
}, [config])
|
|
82
63
|
|
|
83
|
-
const tableData = useMemo(
|
|
84
|
-
() => data,
|
|
85
|
-
[data]
|
|
86
|
-
);
|
|
64
|
+
const tableData = useMemo(() => data, [data])
|
|
87
65
|
|
|
88
66
|
// Change accessibility label depending on expanded status
|
|
89
67
|
useEffect(() => {
|
|
90
|
-
const expandedLabel = 'Accessible data table.'
|
|
91
|
-
const collapsedLabel = 'Accessible data table. This table is currently collapsed visually but can still be read using a screen reader.'
|
|
68
|
+
const expandedLabel = 'Accessible data table.'
|
|
69
|
+
const collapsedLabel = 'Accessible data table. This table is currently collapsed visually but can still be read using a screen reader.'
|
|
92
70
|
|
|
93
71
|
if (tableExpanded === true && accessibilityLabel !== expandedLabel) {
|
|
94
|
-
setAccessibilityLabel(expandedLabel)
|
|
72
|
+
setAccessibilityLabel(expandedLabel)
|
|
95
73
|
}
|
|
96
74
|
|
|
97
75
|
if (tableExpanded === false && accessibilityLabel !== collapsedLabel) {
|
|
98
|
-
setAccessibilityLabel(collapsedLabel)
|
|
76
|
+
setAccessibilityLabel(collapsedLabel)
|
|
99
77
|
}
|
|
100
78
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
101
|
-
}, [tableExpanded])
|
|
79
|
+
}, [tableExpanded])
|
|
102
80
|
|
|
103
81
|
const defaultColumn = useMemo(
|
|
104
82
|
() => ({
|
|
105
83
|
minWidth: 150,
|
|
106
84
|
width: 200,
|
|
107
|
-
maxWidth: 400
|
|
85
|
+
maxWidth: 400
|
|
108
86
|
}),
|
|
109
87
|
[]
|
|
110
|
-
)
|
|
88
|
+
)
|
|
111
89
|
|
|
112
|
-
const {
|
|
113
|
-
getTableProps,
|
|
114
|
-
getTableBodyProps,
|
|
115
|
-
headerGroups,
|
|
116
|
-
rows,
|
|
117
|
-
prepareRow,
|
|
118
|
-
} = useTable({ columns: tableColumns, data: tableData, defaultColumn }, useSortBy, useBlockLayout, useResizeColumns);
|
|
90
|
+
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable({ columns: tableColumns, data: tableData, defaultColumn }, useSortBy, useBlockLayout, useResizeColumns)
|
|
119
91
|
|
|
120
92
|
return (
|
|
121
|
-
<ErrorBoundary component=
|
|
122
|
-
<
|
|
93
|
+
<ErrorBoundary component='DataTable'>
|
|
94
|
+
<CoveMediaControls.Section classes={['download-links']}>
|
|
95
|
+
{config.table.showDownloadUrl && dataFileSourceType === 'url' && (
|
|
96
|
+
<a className='dashboard-download-link' href={config.datasets[datasetKey].dataFileName} title='Link to View Dataset' target='_blank'>
|
|
97
|
+
Link to View Dataset
|
|
98
|
+
</a>
|
|
99
|
+
)}
|
|
100
|
+
{config.table.download && <DownloadButton data={data} />}
|
|
101
|
+
</CoveMediaControls.Section>
|
|
102
|
+
{config.table.show && (
|
|
103
|
+
<section className={`data-table-container`} aria-label={accessibilityLabel}>
|
|
123
104
|
<div
|
|
124
|
-
role=
|
|
105
|
+
role='button'
|
|
125
106
|
className={tableExpanded ? 'data-table-heading' : 'collapsed data-table-heading'}
|
|
126
|
-
onClick={() => {
|
|
107
|
+
onClick={() => {
|
|
108
|
+
setTableExpanded(!tableExpanded)
|
|
109
|
+
}}
|
|
127
110
|
tabIndex={0}
|
|
128
|
-
onKeyDown={
|
|
111
|
+
onKeyDown={e => {
|
|
112
|
+
if (e.keyCode === 13) {
|
|
113
|
+
setTableExpanded(!tableExpanded)
|
|
114
|
+
}
|
|
115
|
+
}}
|
|
129
116
|
>
|
|
130
|
-
{config.table.label}
|
|
117
|
+
{config.table.label}
|
|
118
|
+
{datasetKey ? `: ${datasetKey}` : ''}
|
|
131
119
|
</div>
|
|
132
|
-
<div className=
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
<caption className="visually-hidden">{config.table.label}</caption>
|
|
120
|
+
<div className='table-container' style={{ maxHeight: config.table && config.table.limitHeight && `${config.table.height}px`, overflowY: 'scroll' }}>
|
|
121
|
+
<table className={tableExpanded ? 'data-table' : 'data-table cdcdataviz-sr-only'} hidden={!tableExpanded} {...getTableProps()}>
|
|
122
|
+
<caption className='visually-hidden'>{config.table.label}</caption>
|
|
136
123
|
<thead>
|
|
137
|
-
{headerGroups &&
|
|
138
|
-
|
|
139
|
-
{headerGroup.
|
|
140
|
-
|
|
141
|
-
{column.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
124
|
+
{headerGroups &&
|
|
125
|
+
headerGroups.map(headerGroup => (
|
|
126
|
+
<tr {...headerGroup.getHeaderGroupProps()}>
|
|
127
|
+
{headerGroup.headers.map(column => (
|
|
128
|
+
<th tabIndex='0' {...column.getHeaderProps(column.getSortByToggleProps())} className={column.isSorted ? (column.isSortedDesc ? 'sort sort-desc' : 'sort sort-asc') : 'sort'} title={column.Header}>
|
|
129
|
+
{column.render('Header')}
|
|
130
|
+
<div {...column.getResizerProps()} className='resizer' />
|
|
131
|
+
</th>
|
|
132
|
+
))}
|
|
133
|
+
</tr>
|
|
134
|
+
))}
|
|
147
135
|
</thead>
|
|
148
|
-
{rows &&
|
|
136
|
+
{rows && (
|
|
149
137
|
<tbody {...getTableBodyProps()}>
|
|
150
|
-
{rows.map(
|
|
151
|
-
prepareRow(row)
|
|
138
|
+
{rows.map(row => {
|
|
139
|
+
prepareRow(row)
|
|
152
140
|
return (
|
|
153
141
|
<tr {...row.getRowProps()}>
|
|
154
|
-
{row.cells &&
|
|
155
|
-
|
|
156
|
-
{cell.
|
|
157
|
-
|
|
158
|
-
|
|
142
|
+
{row.cells &&
|
|
143
|
+
row.cells.map(cell => (
|
|
144
|
+
<td tabIndex='0' {...cell.getCellProps()}>
|
|
145
|
+
{cell.render('Cell')}
|
|
146
|
+
</td>
|
|
147
|
+
))}
|
|
159
148
|
</tr>
|
|
160
|
-
)
|
|
149
|
+
)
|
|
161
150
|
})}
|
|
162
151
|
</tbody>
|
|
163
|
-
}
|
|
152
|
+
)}
|
|
164
153
|
</table>
|
|
165
154
|
</div>
|
|
166
|
-
|
|
167
|
-
|
|
155
|
+
</section>
|
|
156
|
+
)}
|
|
168
157
|
</ErrorBoundary>
|
|
169
|
-
)
|
|
158
|
+
)
|
|
170
159
|
}
|