@cdc/waffle-chart 1.0.0 → 1.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/waffle-chart",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "React component for displaying a single piece of data in a card module",
5
5
  "main": "dist/cdcwafflechart",
6
6
  "scripts": {
@@ -19,25 +19,23 @@
19
19
  },
20
20
  "license": "Apache-2.0",
21
21
  "homepage": "https://github.com/CDCgov/cdc-open-viz#readme",
22
- "devDependencies": {
23
- "@cdc/core": "^1.1.2",
24
- "bootstrap": "^4.3.1",
25
- "html-react-parser": "^0.14.0",
26
- "react-accessible-accordion": "^3.3.4",
27
- "react-beautiful-dnd": "^13.0.0",
28
- "react-select": "^3.0.8",
29
- "react-tooltip": "4.2.8",
30
- "use-debounce": "^6.0.1",
31
- "whatwg-fetch": "^3.6.2"
32
- },
33
22
  "dependencies": {
23
+ "@cdc/core": "^1.1.3",
34
24
  "@visx/shape": "^2.1.1",
35
25
  "chroma": "0.0.1",
36
26
  "chroma-js": "^2.1.0",
37
- "link": "^0.1.5"
27
+ "html-react-parser": "1.4.9",
28
+ "react-accessible-accordion": "^3.3.4",
29
+ "react-tooltip": "4.2.8",
30
+ "use-debounce": "^6.0.1",
31
+ "whatwg-fetch": "^3.6.2"
38
32
  },
39
33
  "peerDependencies": {
40
34
  "react": "^17.0.2",
41
35
  "react-dom": ">=16"
42
- }
36
+ },
37
+ "resolutions": {
38
+ "@types/react": "17.x"
39
+ },
40
+ "gitHead": "ff89a7aea74c533413c62ef8859cc011e6b3cbfa"
43
41
  }
@@ -2,15 +2,19 @@ import React, { useCallback, useEffect, useState } from 'react'
2
2
  import parse from 'html-react-parser'
3
3
  import { Group } from '@visx/group'
4
4
  import { Circle, Bar } from '@visx/shape'
5
+
5
6
  import ResizeObserver from 'resize-observer-polyfill'
6
7
  import getViewport from '@cdc/core/helpers/getViewport'
7
8
 
8
9
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
9
10
  import Loading from '@cdc/core/components/Loading'
10
11
 
12
+ import ConfigContext from './ConfigContext'
11
13
  import EditorPanel from './components/EditorPanel'
12
14
  import defaults from './data/initial-state'
13
- import Context from './context'
15
+
16
+ import { publish } from '@cdc/core/helpers/events';
17
+
14
18
  import './scss/main.scss'
15
19
 
16
20
  const themeColor = {
@@ -179,62 +183,36 @@ const WaffleChart = ({ config, isEditor }) => {
179
183
  return include
180
184
  }).map(Number)
181
185
 
186
+ // Calculate numerator ------------------
182
187
  let waffleNumerator = ''
183
188
 
184
- switch (dataFunction) {
185
- case DATA_FUNCTION_COUNT:
186
- waffleNumerator = String(numericalData.length)
187
- break
188
- case DATA_FUNCTION_SUM:
189
- waffleNumerator = String(getColumnSum(numericalData))
190
- break
191
- case DATA_FUNCTION_MEAN:
192
- waffleNumerator = String(getColumnMean(numericalData))
193
- break
194
- case DATA_FUNCTION_MEDIAN:
195
- waffleNumerator = getMedian(numericalData).toString()
196
- break
197
- case DATA_FUNCTION_MAX:
198
- waffleNumerator = Math.max(...numericalData).toString()
199
- break
200
- case DATA_FUNCTION_MIN:
201
- waffleNumerator = Math.min(...numericalData).toString()
202
- break
203
- case DATA_FUNCTION_MODE:
204
- waffleNumerator = getMode(numericalData).join(', ')
205
- break
206
- default:
207
- console.log('Function not recognized: ' + dataFunction)
189
+ const numerFunctionList = {
190
+ [DATA_FUNCTION_COUNT]: String(numericalData.length),
191
+ [DATA_FUNCTION_SUM]: String(getColumnSum(numericalData)),
192
+ [DATA_FUNCTION_MEAN]: String(getColumnMean(numericalData)),
193
+ [DATA_FUNCTION_MEDIAN]: getMedian(numericalData).toString(),
194
+ [DATA_FUNCTION_MAX]: Math.max(...numericalData).toString(),
195
+ [DATA_FUNCTION_MIN]: Math.min(...numericalData).toString(),
196
+ [DATA_FUNCTION_MODE]: getMode(numericalData).join(', ')
208
197
  }
209
198
 
199
+ waffleNumerator = numerFunctionList[dataFunction]
200
+
201
+ // Calculate denominator ------------------
210
202
  let waffleDenominator = null
211
203
 
204
+ const denomFunctionList = {
205
+ [DATA_FUNCTION_COUNT]: String(numericalDenomData.length),
206
+ [DATA_FUNCTION_SUM]: String(getColumnSum(numericalDenomData)),
207
+ [DATA_FUNCTION_MEAN]: String(getColumnMean(numericalDenomData)),
208
+ [DATA_FUNCTION_MEDIAN]: getMedian(numericalDenomData).toString(),
209
+ [DATA_FUNCTION_MAX]: Math.max(...numericalDenomData).toString(),
210
+ [DATA_FUNCTION_MIN]: Math.min(...numericalDenomData).toString(),
211
+ [DATA_FUNCTION_MODE]: getMode(numericalDenomData).join(', '),
212
+ }
213
+
212
214
  if (customDenom && dataDenomColumn && dataDenomFunction) {
213
- switch (dataDenomFunction) {
214
- case DATA_FUNCTION_COUNT:
215
- waffleDenominator = String(numericalDenomData.length)
216
- break
217
- case DATA_FUNCTION_SUM:
218
- waffleDenominator = String(getColumnSum(numericalDenomData))
219
- break
220
- case DATA_FUNCTION_MEAN:
221
- waffleDenominator = String(getColumnMean(numericalDenomData))
222
- break
223
- case DATA_FUNCTION_MEDIAN:
224
- waffleDenominator = getMedian(numericalDenomData).toString()
225
- break
226
- case DATA_FUNCTION_MAX:
227
- waffleDenominator = Math.max(...numericalDenomData).toString()
228
- break
229
- case DATA_FUNCTION_MIN:
230
- waffleDenominator = Math.min(...numericalDenomData).toString()
231
- break
232
- case DATA_FUNCTION_MODE:
233
- waffleDenominator = getMode(numericalDenomData).join(', ')
234
- break
235
- default:
236
- console.log('Function not recognized: ' + dataFunction)
237
- }
215
+ waffleDenominator = denomFunctionList[dataDenomFunction]
238
216
  } else {
239
217
  waffleDenominator = dataDenom > 0 ? dataDenom : 100
240
218
  }
@@ -301,43 +279,64 @@ const WaffleChart = ({ config, isEditor }) => {
301
279
  return (nodeWidth * 10) + (nodeSpacer * 9)
302
280
  }, [ nodeWidth, nodeSpacer ])
303
281
 
304
- let dataFontSize = config.fontSize ? {fontSize: config.fontSize + 'px'} : null
282
+ let dataFontSize = config.fontSize ? { fontSize: config.fontSize + 'px' } : null
283
+
284
+
285
+ let innerContainerClasses = ['cove-component__inner']
286
+ config.title && innerContainerClasses.push('component--has-title')
287
+ config.subtext && innerContainerClasses.push('component--has-subtext')
288
+ config.biteStyle && innerContainerClasses.push(`bite__style--${config.biteStyle}`)
289
+ config.general?.isCompactStyle && innerContainerClasses.push(`component--isCompactStyle`)
290
+
291
+ let contentClasses = ['cove-component__content'];
292
+ !config.visual?.border && contentClasses.push('no-borders');
293
+ config.visual?.borderColorTheme && contentClasses.push('component--has-borderColorTheme');
294
+ config.visual?.accent && contentClasses.push('component--has-accent');
295
+ config.visual?.background && contentClasses.push('component--has-background');
296
+ config.visual?.hideBackgroundColor && contentClasses.push('component--hideBackgroundColor');
297
+
298
+ // ! these two will be retired.
299
+ config.shadow && innerContainerClasses.push('shadow')
300
+ config?.visual?.roundedBorders && innerContainerClasses.push('bite--has-rounded-borders')
305
301
 
306
302
  return (
307
- <div className={isEditor ? 'spacing-wrapper' : ''}>
308
- <section className={`cdc-waffle-chart ${theme}${config.overallFontSize ? ' font-' + config.overallFontSize : ''}`}>
309
- <div className="cdc-waffle-chart__container">
310
- {title &&
311
- <header aria-hidden="true">
312
- <div className="cdc-waffle-chart__header">{parse(title)}</div>
313
- </header>
314
- }
315
- <div className={`cdc-waffle-chart__inner-container${orientation === 'vertical' ? ' cdc-waffle-chart--verical' : ''}`}>
316
- <div className="cdc-waffle-chart__chart" style={{width: setRatio()}}>
317
- <svg width={setRatio()} height={setRatio()}>
318
- <Group>
319
- {buildWaffle()}
320
- </Group>
321
- </svg>
322
- </div>
323
- { (dataPercentage || content) &&
324
- <div className="cdc-waffle-chart__data">
325
- {dataPercentage &&
326
- <div className="cdc-waffle-chart__data--primary" style={dataFontSize}>
327
- {prefix ? prefix : null}{dataPercentage}{suffix ? suffix : null}
303
+ <div className={innerContainerClasses.join(' ')}>
304
+ <>
305
+ {title &&
306
+ <div className={`cove-component__header chart-title ${config.theme}`} >
307
+ {parse(title)}
308
+ </div>
309
+ }
310
+ <div className={contentClasses.join(' ')}>
311
+ <div className="cove-component__content-wrap">
312
+ <div
313
+ className={`cove-waffle-chart${orientation === 'vertical' ? ' cove-waffle-chart--verical' : ''}${config.overallFontSize ? ' font-' + config.overallFontSize : ''}`}>
314
+ <div className="cove-waffle-chart__chart" style={{ width: setRatio() }}>
315
+ <svg width={setRatio()} height={setRatio()}>
316
+ <Group>
317
+ {buildWaffle()}
318
+ </Group>
319
+ </svg>
320
+ </div>
321
+ {(dataPercentage || content) &&
322
+ <div className="cove-waffle-chart__data">
323
+ {dataPercentage &&
324
+ <div className="cove-waffle-chart__data--primary" style={dataFontSize}>
325
+ {prefix ? prefix : null}{dataPercentage}{suffix ? suffix : null}
326
+ </div>
327
+ }
328
+ <div className="cove-waffle-chart__data--text" >{parse(content)}</div>
328
329
  </div>
329
330
  }
330
- <div className="cdc-waffle-chart__data--text">{parse(content)}</div>
331
331
  </div>
332
- }
333
- </div>
334
- {subtext &&
335
- <div className="cdc-waffle-chart__subtext">
332
+ {subtext &&
333
+ <div className="cove-waffle-chart__subtext">
336
334
  {parse(subtext)}
337
335
  </div>
338
- }
336
+ }
337
+ </div>
339
338
  </div>
340
- </section>
339
+ </>
341
340
  </div>
342
341
  )
343
342
  }
@@ -351,42 +350,32 @@ const CdcWaffleChart = (
351
350
  setConfig: setParentConfig
352
351
  }
353
352
  ) => {
353
+
354
+ // Default States
354
355
  const [ config, setConfig ] = useState({ ...defaults })
355
356
  const [ loading, setLoading ] = useState(true)
356
357
 
357
358
  const [ currentViewport, setCurrentViewport ] = useState<String>('lg')
359
+ const [ coveLoadedHasRan, setCoveLoadedHasRan ] = useState(false)
360
+ const [ container, setContainer ] = useState()
358
361
 
359
- //Observes changes to outermost container and changes viewport size in state
360
- const resizeObserver = new ResizeObserver(entries => {
361
- for (let entry of entries) {
362
- let newViewport = getViewport(entry.contentRect.width * 2) // Data bite is usually presented as small, so we scale it up for responsive calculations
363
-
364
- setCurrentViewport(newViewport)
365
- }
366
- })
367
-
362
+ // Default Functions
368
363
  const updateConfig = (newConfig) => {
369
-
370
- // Deeper copy
371
364
  Object.keys(defaults).forEach(key => {
372
365
  if (newConfig[key] && 'object' === typeof newConfig[key] && !Array.isArray(newConfig[key])) {
373
366
  newConfig[key] = { ...defaults[key], ...newConfig[key] }
374
367
  }
375
368
  })
376
369
 
377
- //Enforce default values that need to be calculated at runtime
378
370
  newConfig.runtime = {}
379
371
  newConfig.runtime.uniqueId = Date.now()
380
372
 
381
- //Check things that are needed and set error messages if needed
382
373
  newConfig.runtime.editorErrorMessage = ''
383
374
  setConfig(newConfig)
384
375
  }
385
376
 
386
- const loadConfig = async () => {
377
+ const loadConfig = useCallback(async () => {
387
378
  let response = configObj || await (await fetch(configUrl)).json()
388
-
389
- // If data is included through a URL, fetch that and store
390
379
  let responseData = response.data ?? {}
391
380
 
392
381
  if (response.dataUrl) {
@@ -398,38 +387,44 @@ const CdcWaffleChart = (
398
387
 
399
388
  updateConfig({ ...defaults, ...response })
400
389
  setLoading(false)
401
- }
390
+ }, [])
391
+
392
+ // Custom Functions
393
+
394
+ // --Observes changes to outermost container and changes viewport size in state
395
+ const resizeObserver = new ResizeObserver(entries => {
396
+ for (let entry of entries) {
397
+ let newViewport = getViewport(entry.contentRect.width * 2) // Data bite is usually presented as small, so we scale it up for responsive calculations
398
+
399
+ setCurrentViewport(newViewport)
400
+ }
401
+ })
402
402
 
403
- // Load data when component first mounts
404
403
  const outerContainerRef = useCallback(node => {
405
404
  if (node !== null) {
406
405
  resizeObserver.observe(node)
407
406
  }
407
+ setContainer(node)
408
408
  }, [])
409
409
 
410
+ //Load initial config
410
411
  useEffect(() => {
411
- console.log('Running empty useEFfect')
412
- loadConfig()
412
+ loadConfig().catch((err) => console.log(err))
413
413
  }, [])
414
414
 
415
- if (configObj) {
416
- useEffect(() => {
417
- console.log('Running last useEFfect')
418
- loadConfig()
419
- }, [ configObj.data ])
420
- }
421
-
422
415
  useEffect(() => {
423
- loadConfig()
424
- }, [])
416
+ if (config && !coveLoadedHasRan && container) {
417
+ publish('cove_loaded', { config: config })
418
+ setCoveLoadedHasRan(true)
419
+ }
420
+ }, [config, container]);
425
421
 
426
- if (configObj) {
427
- useEffect(() => {
428
- loadConfig()
429
- }, [ configObj.data ])
430
- }
422
+ //Reload config if config object provided/updated
423
+ useEffect(() => {
424
+ loadConfig().catch((err) => console.log(err))
425
+ }, [ configObj?.data ])
431
426
 
432
- let body = (<Loading/>)
427
+ let content = (<Loading/>)
433
428
 
434
429
  if (loading === false) {
435
430
  let classNames = [
@@ -440,27 +435,34 @@ const CdcWaffleChart = (
440
435
  'font-' + config.overallFontSize
441
436
  ]
442
437
 
443
-
444
438
  if (isEditor) {
445
439
  classNames.push('is-editor')
446
440
  }
447
441
 
448
- body = (
449
- <>
450
- <div className={classNames.join(' ')} ref={outerContainerRef}>
451
- {isEditor && <EditorPanel/>}
452
- <WaffleChart config={config} isEditor={isEditor}/>
453
- </div>
454
- </>
442
+ let bodyClasses = ['cove-component', 'waffle-chart']
443
+
444
+
445
+
446
+ let body = (
447
+ <div className={`${bodyClasses.join(' ')}`} ref={outerContainerRef}>
448
+ <WaffleChart config={config} isEditor={isEditor}/>
449
+ </div>
450
+ )
451
+
452
+ content = (
453
+ <div className={`cove ${config.theme}`}>
454
+ {isEditor && <EditorPanel>{body}</EditorPanel>}
455
+ {!isEditor && body}
456
+ </div>
455
457
  )
456
458
  }
457
459
 
458
460
  return (
459
461
  <ErrorBoundary component="WaffleChart">
460
- <Context.Provider
462
+ <ConfigContext.Provider
461
463
  value={{ config, updateConfig, loading, data: config.data, setParentConfig, isDashboard, outerContainerRef }}>
462
- {body}
463
- </Context.Provider>
464
+ {content}
465
+ </ConfigContext.Provider>
464
466
  </ErrorBoundary>
465
467
  )
466
468
  }
File without changes