@cdc/chart 4.22.11 → 4.23.2

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 (65) hide show
  1. package/dist/cdcchart.js +54569 -16
  2. package/examples/Barchart_with_negative.json +34 -0
  3. package/examples/box-plot-data.json +71 -0
  4. package/examples/box-plot.csv +5 -0
  5. package/examples/box-plot.json +124 -0
  6. package/examples/dynamic-legends.json +1 -1
  7. package/examples/example-bar-chart-nonnumeric.json +36 -0
  8. package/examples/example-bar-chart.json +33 -0
  9. package/examples/example-combo-bar-nonnumeric.json +105 -0
  10. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +3 -1
  11. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +1 -1
  12. package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +86 -17
  13. package/examples/gallery/paired-bar/paired-bar-chart.json +65 -13
  14. package/examples/line-chart-nonnumeric.json +32 -0
  15. package/examples/line-chart.json +21 -63
  16. package/examples/new-data.csv +17 -0
  17. package/examples/newdata.json +90 -0
  18. package/examples/planet-combo-example-config.json +143 -20
  19. package/examples/planet-example-data-nonnumeric.json +56 -0
  20. package/examples/planet-example-data.json +2 -2
  21. package/examples/planet-pie-example-config-nonnumeric.json +30 -0
  22. package/examples/scatterplot-continuous.csv +17 -0
  23. package/examples/{private/yaxis-test.json → scatterplot.json} +53 -50
  24. package/examples/sparkline-chart-nonnumeric.json +76 -0
  25. package/examples/stacked-vertical-bar-example-negative.json +154 -0
  26. package/examples/stacked-vertical-bar-example-nonnumerics.json +154 -0
  27. package/{src/index.html → index.html} +18 -11
  28. package/package.json +29 -22
  29. package/src/{CdcChart.tsx → CdcChart.jsx} +193 -119
  30. package/src/components/BarChart.jsx +517 -0
  31. package/src/components/BoxPlot.jsx +88 -0
  32. package/src/components/{DataTable.tsx → DataTable.jsx} +125 -32
  33. package/src/components/{EditorPanel.js → EditorPanel.jsx} +376 -115
  34. package/src/components/Filters.jsx +125 -0
  35. package/src/components/Legend.jsx +303 -0
  36. package/src/components/{LineChart.tsx → LineChart.jsx} +87 -22
  37. package/src/components/{LinearChart.tsx → LinearChart.jsx} +172 -113
  38. package/src/components/{PairedBarChart.tsx → PairedBarChart.jsx} +46 -79
  39. package/src/components/{PieChart.tsx → PieChart.jsx} +29 -34
  40. package/src/components/ScatterPlot.jsx +48 -0
  41. package/src/components/{SparkLine.js → SparkLine.jsx} +49 -18
  42. package/src/components/useIntersectionObserver.jsx +29 -0
  43. package/src/data/initial-state.js +44 -8
  44. package/src/hooks/{useColorPalette.ts → useColorPalette.js} +10 -28
  45. package/src/hooks/{useReduceData.ts → useReduceData.js} +27 -13
  46. package/src/hooks/useRightAxis.js +3 -1
  47. package/src/index.jsx +16 -0
  48. package/src/scss/DataTable.scss +23 -1
  49. package/src/scss/main.scss +83 -32
  50. package/vite.config.js +4 -0
  51. package/examples/private/filters.json +0 -170
  52. package/examples/private/line-test-data.json +0 -22
  53. package/examples/private/line-test-two.json +0 -210
  54. package/examples/private/line-test.json +0 -102
  55. package/examples/private/new.json +0 -48800
  56. package/examples/private/newtest.csv +0 -101
  57. package/examples/private/shawn.json +0 -1106
  58. package/examples/private/test.json +0 -10124
  59. package/examples/private/yaxis-testing.csv +0 -27
  60. package/examples/private/yaxis.json +0 -28
  61. package/src/components/BarChart.tsx +0 -579
  62. package/src/components/Legend.js +0 -284
  63. package/src/components/useIntersectionObserver.tsx +0 -27
  64. package/src/index.tsx +0 -18
  65. /package/src/{context.tsx → ConfigContext.jsx} +0 -0
@@ -1,284 +0,0 @@
1
- import React, { useContext, useEffect } from 'react'
2
- import Context from '../context'
3
- import parse from 'html-react-parser'
4
- import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
5
- import LegendCircle from '@cdc/core/components/LegendCircle'
6
-
7
- import useLegendClasses from './../hooks/useLegendClasses'
8
-
9
- const Legend = () => {
10
- const { config, legend, colorScale, seriesHighlight, highlight, highlightReset, setSeriesHighlight, dynamicLegendItems, setDynamicLegendItems, transformedData: data, setFilteredData, colorPalettes, rawData, setConfig } = useContext(Context)
11
-
12
- const { innerClasses, containerClasses } = useLegendClasses(config)
13
-
14
- useEffect(() => {
15
- if (dynamicLegendItems.length === 0) return
16
-
17
- let itemsToHighlight = dynamicLegendItems.map(item => item.text)
18
-
19
- setSeriesHighlight(itemsToHighlight)
20
-
21
- let colsToKeep = [...itemsToHighlight]
22
- let tmpLabels = []
23
-
24
- rawData.map(dataItem => {
25
- let tmp = {}
26
- colsToKeep.map(col => {
27
- tmp[col] = isNaN(dataItem[col]) ? dataItem[col] : dataItem[col]
28
- })
29
- return tmp
30
- })
31
-
32
- colsToKeep.map(col => {
33
- tmpLabels[col] = col
34
- })
35
-
36
- if (dynamicLegendItems.length > 0) {
37
- setConfig({
38
- ...config,
39
- runtime: {
40
- ...config.runtime,
41
- seriesKeys: colsToKeep,
42
- seriesLabels: tmpLabels
43
- }
44
- })
45
- }
46
- }, [dynamicLegendItems])
47
-
48
- useEffect(() => {
49
- if (dynamicLegendItems.length === 0) {
50
- // loop through all labels and add keys
51
- let resetSeriesNames = [...config.runtime.seriesLabelsAll]
52
- let tmpLabels = []
53
- config.runtime.seriesLabelsAll.map(item => {
54
- resetSeriesNames.map(col => {
55
- tmpLabels[col] = col
56
- })
57
- })
58
-
59
- setConfig({
60
- ...config,
61
- runtime: {
62
- ...config.runtime,
63
- seriesKeys: config.runtime.seriesLabelsAll,
64
- seriesLabels: tmpLabels
65
- }
66
- })
67
- }
68
- }, [dynamicLegendItems])
69
-
70
- const removeDynamicLegendItem = label => {
71
- let newLegendItems = dynamicLegendItems.filter(item => item.text !== label.text)
72
- let newLegendItemsText = newLegendItems.map(item => item.text)
73
- setDynamicLegendItems(newLegendItems)
74
- setSeriesHighlight(newLegendItemsText)
75
- }
76
- const handleDynamicLegendChange = e => {
77
- setDynamicLegendItems([...dynamicLegendItems, JSON.parse(e.target.value)])
78
- }
79
-
80
- const createLegendLabels = (data, defaultLabels) => {
81
- const colorCode = config.legend?.colorCode
82
- if (config.visualizationType !== 'Bar' || config.visualizationSubType !== 'regular' || !colorCode || config.series?.length > 1) {
83
- return defaultLabels
84
- }
85
- let palette = colorPalettes[config.palette]
86
-
87
- while (data.length > palette.length) {
88
- palette = palette.concat(palette)
89
- }
90
- palette = palette.slice(0, data.length)
91
- //store uniq values to Set by colorCode
92
- const set = new Set()
93
-
94
- data.forEach(d => set.add(d[colorCode]))
95
-
96
- // create labels with uniq values
97
- const uniqeLabels = Array.from(set).map((val, i) => {
98
- const newLabel = {
99
- datum: val,
100
- index: i,
101
- text: val,
102
- value: palette[i]
103
- }
104
- return newLabel
105
- })
106
-
107
- return uniqeLabels
108
- }
109
-
110
- if (!legend) return
111
-
112
- if (!legend.dynamicLegend)
113
- return (
114
- <aside id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
115
- {legend.label && <h2>{parse(legend.label)}</h2>}
116
- {legend.description && <p>{parse(legend.description)}</p>}
117
- <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
118
- {labels => (
119
- <div className={innerClasses.join(' ')}>
120
- {createLegendLabels(data, labels).map((label, i) => {
121
- let className = 'legend-item'
122
- let itemName = label.datum
123
-
124
- // Filter excluded data keys from legend
125
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
126
- return
127
- }
128
-
129
- if (config.runtime.seriesLabels) {
130
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
131
- itemName = config.runtime.seriesKeys[index]
132
- }
133
-
134
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
135
- className += ' inactive'
136
- }
137
-
138
- return (
139
- <LegendItem
140
- className={className}
141
- tabIndex={0}
142
- key={`legend-quantile-${i}`}
143
- onKeyPress={e => {
144
- if (e.key === 'Enter') {
145
- highlight(label)
146
- }
147
- }}
148
- onClick={() => {
149
- highlight(label)
150
- }}
151
- >
152
- <LegendCircle fill={label.value} />
153
- <LegendLabel align='left' margin='0 0 0 4px'>
154
- {label.text}
155
- </LegendLabel>
156
- </LegendItem>
157
- )
158
- })}
159
- {seriesHighlight.length > 0 && (
160
- <button className={`legend-reset ${config.theme}`} onClick={labels => highlightReset(labels)} tabIndex={0}>
161
- Reset
162
- </button>
163
- )}
164
- </div>
165
- )}
166
- </LegendOrdinal>
167
- </aside>
168
- )
169
-
170
- return (
171
- <aside id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
172
- {legend.label && <h2>{parse(legend.label)}</h2>}
173
- {legend.description && <p>{parse(legend.description)}</p>}
174
-
175
- <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
176
- {labels => {
177
- if (
178
- Number(config.legend.dynamicLegendItemLimit) > dynamicLegendItems.length && // legend items are less than limit
179
- dynamicLegendItems.length !== config.runtime.seriesLabelsAll.length
180
- ) {
181
- // legend items are equal to series length
182
- return (
183
- <select className='dynamic-legend-dropdown' onChange={e => handleDynamicLegendChange(e)}>
184
- <option className={'all'} tabIndex={0} value={JSON.stringify({ text: config.legend.dynamicLegendDefaultText })}>
185
- {config.legend.dynamicLegendDefaultText}
186
- </option>
187
- {labels.map((label, i) => {
188
- let className = 'legend-item'
189
- let itemName = label.datum
190
- let inDynamicList = false
191
-
192
- // Filter excluded data keys from legend
193
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
194
- return
195
- }
196
-
197
- if (config.runtime.seriesLabels) {
198
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
199
- itemName = config.runtime.seriesKeys[index]
200
- }
201
-
202
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
203
- className += ' inactive'
204
- }
205
-
206
- dynamicLegendItems.map(listItem => {
207
- if (listItem.text === label.text) {
208
- inDynamicList = true
209
- }
210
- })
211
-
212
- if (inDynamicList) return true
213
- let palette = colorPalettes[config.palette]
214
-
215
- label.value = palette[dynamicLegendItems.length]
216
-
217
- return (
218
- <option className={className} tabIndex={0} value={JSON.stringify(label)}>
219
- {label.text}
220
- </option>
221
- )
222
- })}
223
- </select>
224
- )
225
- } else {
226
- return config.legend.dynamicLegendItemLimitMessage
227
- }
228
- }}
229
- </LegendOrdinal>
230
-
231
- <div className='dynamic-legend-list'>
232
- {dynamicLegendItems.map((label, i) => {
233
- let className = ['legend-item']
234
- let itemName = label.text
235
- let palette = colorPalettes[config.palette]
236
-
237
- // Filter excluded data keys from legend
238
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
239
- return
240
- }
241
-
242
- if (config.runtime.seriesLabels && !config.legend.dynamicLegend) {
243
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
244
- itemName = config.runtime.seriesKeys[index]
245
- }
246
-
247
- if (seriesHighlight.length > 0 && !seriesHighlight.includes(itemName)) {
248
- className.push('inactive')
249
- }
250
-
251
- if (seriesHighlight.length === 0 && config.legend.dynamicLegend) {
252
- className.push('inactive')
253
- }
254
-
255
- return (
256
- <>
257
- <LegendItem className={className.join(' ')} tabIndex={0} key={`dynamic-legend-item-${i}`} alignItems='center'>
258
- <button
259
- className='btn-wrapper'
260
- onClick={() => {
261
- highlight(label)
262
- }}
263
- >
264
- <LegendCircle fill={palette[i]} config={config} />
265
- <LegendLabel align='space-between' margin='4px 0 0 4px'>
266
- {label.text}
267
- </LegendLabel>
268
- </button>
269
- <button onClick={() => removeDynamicLegendItem(label)}>x</button>
270
- </LegendItem>
271
- </>
272
- )
273
- })}
274
- </div>
275
- {seriesHighlight.length < dynamicLegendItems.length && (
276
- <button className={`legend-reset legend-reset--dynamic ${config.theme}`} onClick={highlightReset} tabIndex={0}>
277
- Reset
278
- </button>
279
- )}
280
- </aside>
281
- )
282
- }
283
-
284
- export default Legend
@@ -1,27 +0,0 @@
1
- import { useEffect, useState } from 'react'
2
-
3
- export default function useIntersectionObserver(elementRef, { threshold = 0, root = null, rootMargin = '0%', freezeOnceVisible = false }) {
4
- const [entry, setEntry] = useState<any>()
5
-
6
- const frozen = entry?.isIntersecting && freezeOnceVisible
7
-
8
- const updateEntry = ([entry]) => {
9
- setEntry(entry)
10
- }
11
-
12
- useEffect(() => {
13
- const node = elementRef?.current
14
- const hasIOSupport = !!window.IntersectionObserver
15
-
16
- if (!hasIOSupport || frozen || !node) return
17
-
18
- const observerParams = { threshold, root, rootMargin }
19
- const observer = new IntersectionObserver(updateEntry, observerParams)
20
-
21
- observer.observe(node)
22
-
23
- return () => observer.disconnect()
24
- }, [elementRef, threshold, root, rootMargin, frozen])
25
-
26
- return entry
27
- }
package/src/index.tsx DELETED
@@ -1,18 +0,0 @@
1
- import { publish, subscribe } from '@cdc/core/helpers/events'
2
- import React from 'react'
3
- import { render } from 'react-dom'
4
-
5
- import CdcChart from './CdcChart'
6
-
7
- const domContainers = document.querySelectorAll('.react-container')
8
-
9
- let isEditor = window.location.href.includes('editor=true')
10
-
11
- domContainers.forEach(domContainer => {
12
- render(
13
- <React.StrictMode>
14
- <CdcChart configUrl={domContainer.attributes['data-config'].value} isEditor={isEditor} />
15
- </React.StrictMode>,
16
- domContainer
17
- )
18
- })
File without changes