@cdc/data-bite 4.24.2 → 4.24.4

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.
@@ -0,0 +1,6 @@
1
+ [
2
+ {
3
+ "Text": "Number of Deaths",
4
+ "Value": "4736"
5
+ }
6
+ ]
@@ -0,0 +1,6 @@
1
+ [
2
+ {
3
+ "Text": "Number of EMS Responses",
4
+ "Value": "467136"
5
+ }
6
+ ]
@@ -0,0 +1,110 @@
1
+ {
2
+ "type": "data-bite",
3
+ "data": [
4
+ {
5
+ "Text": "Number of EMS Responses",
6
+ "Value": "467136"
7
+ }
8
+ ],
9
+ "dataBite": "",
10
+ "dataFunction": "Max",
11
+ "dataColumn": "Value",
12
+ "bitePosition": "Left",
13
+ "biteFontSize": 24,
14
+ "fontSize": "medium",
15
+ "biteBody": "",
16
+ "imageData": {
17
+ "display": "none",
18
+ "url": "",
19
+ "alt": "",
20
+ "options": []
21
+ },
22
+ "dataFormat": {
23
+ "roundToPlace": "",
24
+ "commas": true,
25
+ "prefix": "",
26
+ "suffix": ""
27
+ },
28
+ "biteStyle": "gradient",
29
+ "filters": [],
30
+ "subtext": "",
31
+ "title": "Total number of suicide EMS responses",
32
+ "theme": "theme-blue",
33
+ "shadow": false,
34
+ "visual": {
35
+ "border": false,
36
+ "accent": false,
37
+ "background": false,
38
+ "hideBackgroundColor": false,
39
+ "borderColorTheme": false
40
+ },
41
+ "general": {
42
+ "isCompactStyle": false
43
+ },
44
+ "showTitle": true,
45
+ "enableTooltips": true,
46
+ "enableStoryNodes": true,
47
+ "legend": {
48
+ "hide": true
49
+ },
50
+ "heights": {
51
+ "vertical": 300,
52
+ "horizontal": 369.59999999999997
53
+ },
54
+ "xAxis": {
55
+ "anchors": [],
56
+ "type": "categorical",
57
+ "showTargetLabel": true,
58
+ "targetLabel": "Target",
59
+ "hideAxis": true,
60
+ "hideLabel": true,
61
+ "hideTicks": true,
62
+ "size": "59",
63
+ "tickRotation": 0,
64
+ "min": "",
65
+ "max": "",
66
+ "labelColor": "#333",
67
+ "tickLabelColor": "#333",
68
+ "tickColor": "#333",
69
+ "numTicks": "",
70
+ "labelOffset": 180,
71
+ "axisPadding": 0,
72
+ "target": 0,
73
+ "dataKey": "Disposition",
74
+ "label": "X Axis Example Label"
75
+ },
76
+ "series": [
77
+ {
78
+ "dataKey": "Type of Treatment",
79
+ "type": "Bar"
80
+ },
81
+ {
82
+ "dataKey": "Final Outcome",
83
+ "type": "Bar"
84
+ },
85
+ {
86
+ "dataKey": "Number of Patients",
87
+ "type": "Bar"
88
+ }
89
+ ],
90
+ "table": {
91
+ "label": "Sankey Data Table",
92
+ "expanded": true,
93
+ "limitHeight": false,
94
+ "height": "",
95
+ "caption": "",
96
+ "showDownloadUrl": true,
97
+ "showDataTableLink": true,
98
+ "indexLabel": "Disposition",
99
+ "download": true,
100
+ "showVertical": true,
101
+ "show": true
102
+ },
103
+ "footnotes": "**Counts fewer than 20 are suppressed for confidentiality reasons.<br></br><br><strong>Footnotes</strong></br> 2022 Biospatial Emergency Medical Services (EMS) records. Biospatial combines electronic EMS patient care records with other electronic healthcare data. EMS records are generated by medics to document prehospital care and collected according to the National EMS Information System (NEMSIS) standard. Currently the Biospatial platform receives EMS data from 24 full states and 18 partial states.",
104
+ "visualizationType": "Sankey",
105
+ "validated": 4.23,
106
+ "runtime": {
107
+ "uniqueId": 1706633133063,
108
+ "editorErrorMessage": ""
109
+ }
110
+ }
package/index.html CHANGED
@@ -6,15 +6,22 @@
6
6
  <style>
7
7
  body {
8
8
  margin: 0;
9
+ border-top: none !important;
10
+ }
11
+
12
+ .cdc-open-viz-module {
13
+ min-height: 100vh;
9
14
  }
10
15
  </style>
16
+ <link rel="stylesheet prefetch" href="https://www.cdc.gov/TemplatePackage/contrib/libs/bootstrap/latest/css/bootstrap.min.css?_=39423" />
17
+ <link rel="stylesheet prefetch" href="https://www.cdc.gov/TemplatePackage/4.0/assets/css/app.min.css?_=39423" />
11
18
  </head>
12
19
  <body>
13
20
  <!-- Original -->
14
- <div class="react-container" data-config="/examples/example-config.json"></div>
21
+ <!-- <div class="react-container" data-config="/examples/sankey-example-data.json"></div> -->
15
22
 
16
23
  <!-- DATA PRESENTATION GALLERY: https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/data_bites.html#examples -->
17
- <!-- <div class="react-container" data-config="/examples/gallery/calculated-average.json"></div> -->
24
+ <div class="react-container" data-config="/examples/gallery/calculated-average.json"></div>
18
25
  <!-- <div class="react-container" data-config="/examples/gallery/calculated-with-pic.json"></div> -->
19
26
  <div class="react-container" data-config="/examples/gallery/max-value.json"></div>
20
27
  <!-- <div class="react-container" data-config="/examples/gallery/sum-of-data.json"></div> -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/data-bite",
3
- "version": "4.24.2",
3
+ "version": "4.24.4",
4
4
  "description": "React component for displaying a single piece of data in a card module",
5
5
  "moduleName": "CdcDataBite",
6
6
  "main": "dist/cdcdatabite",
@@ -26,7 +26,7 @@
26
26
  "license": "Apache-2.0",
27
27
  "homepage": "https://github.com/CDCgov/cdc-open-viz#readme",
28
28
  "dependencies": {
29
- "@cdc/core": "^4.24.2",
29
+ "@cdc/core": "^4.24.4",
30
30
  "chroma": "0.0.1",
31
31
  "chroma-js": "^2.1.0",
32
32
  "html-react-parser": "^3.0.8",
@@ -39,5 +39,5 @@
39
39
  "react": "^18.2.0",
40
40
  "react-dom": "^18.2.0"
41
41
  },
42
- "gitHead": "edde49c96dee146de5e3a4537880b1bcf4dbee08"
42
+ "gitHead": "1843b4632140d582af6a87606374cbd4fe25ad5c"
43
43
  }
@@ -10,6 +10,8 @@ import EditorPanel from './components/EditorPanel'
10
10
  import Loading from '@cdc/core/components/Loading'
11
11
  import Title from '@cdc/core/components/ui/Title'
12
12
  import CircleCallout from './components/CircleCallout'
13
+ import GradientBite from './components/GradientBite'
14
+ import Layout from '@cdc/core/components/Layout'
13
15
 
14
16
  // external
15
17
  import ResizeObserver from 'resize-observer-polyfill'
@@ -447,13 +449,75 @@ const CdcDataBite = (props: CdcDataBiteProps) => {
447
449
  break
448
450
  }
449
451
 
450
- const showBite = undefined !== dataColumn && undefined !== dataFunction
452
+ const missingRequiredSections = () => {
453
+ //Whether to show error message if something is required to show a data-bite and isn't filled in
454
+ return false
455
+ }
456
+
457
+ const Error = () => {
458
+ const styles = {
459
+ position: 'absolute',
460
+ background: 'white',
461
+ zIndex: '999',
462
+ height: '100vh',
463
+ width: '100%',
464
+ display: 'flex',
465
+ justifyContent: 'center',
466
+ alignItems: 'center',
467
+ gridArea: 'content'
468
+ }
469
+ return (
470
+ <section className='waiting' style={styles}>
471
+ <section className='waiting-container'>
472
+ <h3>Error With Configuration</h3>
473
+ <p>{config.runtime.editorErrorMessage}</p>
474
+ </section>
475
+ </section>
476
+ )
477
+ }
478
+
479
+ const Confirm = () => {
480
+ const styles = {
481
+ position: 'absolute',
482
+ background: 'white',
483
+ zIndex: '999',
484
+ height: '100vh',
485
+ width: '100%',
486
+ display: 'flex',
487
+ justifyContent: 'center',
488
+ alignItems: 'center',
489
+ gridArea: 'content'
490
+ }
491
+
492
+ return (
493
+ <section className='waiting' style={styles}>
494
+ <section className='waiting-container'>
495
+ <h3>Finish Configuring</h3>
496
+ <p>Set all required options to the left and confirm below to display a preview of the chart.</p>
497
+ <button
498
+ className='btn'
499
+ style={{ margin: '1em auto' }}
500
+ disabled={missingRequiredSections()}
501
+ onClick={e => {
502
+ e.preventDefault()
503
+ updateConfig({ ...config, newViz: false })
504
+ }}
505
+ >
506
+ I'm Done
507
+ </button>
508
+ </section>
509
+ </section>
510
+ )
511
+ }
451
512
 
513
+ const showBite = undefined !== dataColumn && undefined !== dataFunction
452
514
  body = (
453
515
  <>
454
516
  {isEditor && <EditorPanel />}
455
- <div className={isEditor ? 'spacing-wrapper' : ''}>
456
- <div className={innerContainerClasses.join(' ')}>
517
+ <Layout.Responsive isEditor={isEditor}>
518
+ <div className={`${contentClasses.join(' ')}`}>
519
+ {!config.newViz && config.runtime && config.runtime.editorErrorMessage && <Error />}
520
+ {(!config.dataColumn || !config.dataFunction) && <Confirm />}
457
521
  <Title config={config} title={title} isDashboard={isDashboard} classes={['bite-header', `${config.theme}`]} />
458
522
  <div className={`bite ${biteClasses.join(' ')}`}>
459
523
  <div className={`bite-content-container ${contentClasses.join(' ')}`}>
@@ -495,21 +559,28 @@ const CdcDataBite = (props: CdcDataBiteProps) => {
495
559
  </div>
496
560
  </div>
497
561
  {link && link}
498
- </div>
562
+ </Layout.Responsive>
499
563
  </>
500
564
  )
501
565
  }
502
566
 
503
- let classNames = ['cove', 'cdc-open-viz-module', 'type-data-bite', currentViewport, config.theme, 'font-' + config.fontSize]
504
- if (isEditor) {
505
- classNames.push('is-editor')
506
- }
507
-
508
567
  return (
509
568
  <Context.Provider value={{ config, updateConfig, loading, data: config.data, setParentConfig, isDashboard }}>
510
- <div className={classNames.join(' ')} ref={outerContainerRef}>
511
- {body}
512
- </div>
569
+ {biteStyle !== 'gradient' && (
570
+ <Layout.VisualizationWrapper ref={outerContainerRef} config={config} isEditor={isEditor} showEditorPanel={config?.showEditorPanel}>
571
+ {body}
572
+ </Layout.VisualizationWrapper>
573
+ )}
574
+ {'gradient' === biteStyle && (
575
+ <Layout.VisualizationWrapper ref={outerContainerRef} config={config} isEditor={isEditor} showEditorPanel={config?.showEditorPanel}>
576
+ {isEditor && <EditorPanel />}
577
+ <Layout.Responsive isEditor={isEditor}>
578
+ {!config.newViz && config.runtime && config.runtime.editorErrorMessage && <Error />}
579
+ {(!config.dataColumn || !config.dataFunction) && <Confirm />}
580
+ <GradientBite label={config.title} value={calculateDataBite()} />
581
+ </Layout.Responsive>
582
+ </Layout.VisualizationWrapper>
583
+ )}
513
584
  </Context.Provider>
514
585
  )
515
586
  }
@@ -535,7 +606,8 @@ export const BITE_LOCATIONS = {
535
606
  split: 'Split Graphic and Message',
536
607
  title: 'Value above Message',
537
608
  body: 'Value before Message',
538
- end: 'Value after Message'
609
+ end: 'Value after Message',
610
+ gradient: 'Gradient'
539
611
  }
540
612
 
541
613
  export const IMAGE_POSITION_LEFT = 'Left'
@@ -8,7 +8,9 @@ import WarningImage from '@cdc/core/assets/icon-warning-circle.svg'
8
8
  import Tooltip from '@cdc/core/components/ui/Tooltip'
9
9
  import Icon from '@cdc/core/components/ui/Icon'
10
10
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
11
+ import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
11
12
  import { BITE_LOCATIONS, DATA_FUNCTIONS, IMAGE_POSITIONS, DATA_OPERATORS } from '../CdcDataBite'
13
+ import Layout from '@cdc/core/components/Layout'
12
14
 
13
15
  const TextField = memo(({ label, section = null, subsection = null, fieldName, updateField, value: stateValue, tooltip, type = 'input', i = null, min = null, max = null, ...attributes }) => {
14
16
  const [value, setValue] = useState(stateValue)
@@ -128,49 +130,11 @@ const Select = memo(({ label, value, options, fieldName, section = null, subsect
128
130
  const headerColors = ['theme-blue', 'theme-purple', 'theme-brown', 'theme-teal', 'theme-pink', 'theme-orange', 'theme-slate', 'theme-indigo', 'theme-cyan', 'theme-green', 'theme-amber']
129
131
 
130
132
  const EditorPanel = memo(() => {
131
- const { config, updateConfig, loading, data, setParentConfig, isDashboard } = useContext(Context)
133
+ const { config, updateConfig, loading, data, setParentConfig, isDashboard, isEditor } = useContext(Context)
132
134
 
133
135
  const [displayPanel, setDisplayPanel] = useState(true)
134
- const enforceRestrictions = updatedConfig => {
135
- //If there are any dependencies between fields, etc../
136
- }
137
-
138
- const updateField = (section, subsection, fieldName, newValue) => {
139
- // Top level
140
- if (null === section && null === subsection) {
141
- let updatedConfig = { ...config, [fieldName]: newValue }
142
-
143
- if ('filterColumn' === fieldName) {
144
- updatedConfig.filterValue = ''
145
- }
146
-
147
- enforceRestrictions(updatedConfig)
148
-
149
- updateConfig(updatedConfig)
150
- return
151
- }
152
-
153
- const isArray = Array.isArray(config[section])
154
-
155
- let sectionValue = isArray ? [...config[section], newValue] : { ...config[section], [fieldName]: newValue }
156
-
157
- if (null !== subsection) {
158
- if (isArray) {
159
- sectionValue = [...config[section]]
160
- sectionValue[subsection] = { ...sectionValue[subsection], [fieldName]: newValue }
161
- } else if (typeof newValue === 'string') {
162
- sectionValue[subsection] = newValue
163
- } else {
164
- sectionValue = { ...config[section], [subsection]: { ...config[section][subsection], [fieldName]: newValue } }
165
- }
166
- }
167
-
168
- let updatedConfig = { ...config, [section]: sectionValue }
169
-
170
- enforceRestrictions(updatedConfig)
171
136
 
172
- updateConfig(updatedConfig)
173
- }
137
+ const updateField = updateFieldFactory(config, updateConfig, true)
174
138
 
175
139
  const missingRequiredSections = () => {
176
140
  //Whether to show error message if something is required to show a data-bite and isn't filled in
@@ -190,39 +154,10 @@ const EditorPanel = memo(() => {
190
154
 
191
155
  const onBackClick = () => {
192
156
  setDisplayPanel(!displayPanel)
193
- }
194
-
195
- const Error = () => {
196
- return (
197
- <section className='waiting'>
198
- <section className='waiting-container'>
199
- <h3>Error With Configuration</h3>
200
- <p>{config.runtime.editorErrorMessage}</p>
201
- </section>
202
- </section>
203
- )
204
- }
205
-
206
- const Confirm = () => {
207
- return (
208
- <section className='waiting'>
209
- <section className='waiting-container'>
210
- <h3>Finish Configuring</h3>
211
- <p>Set all required options to the left and confirm below to display a preview of the chart.</p>
212
- <button
213
- className='btn'
214
- style={{ margin: '1em auto' }}
215
- disabled={missingRequiredSections()}
216
- onClick={e => {
217
- e.preventDefault()
218
- updateConfig({ ...config, newViz: false })
219
- }}
220
- >
221
- I'm Done
222
- </button>
223
- </section>
224
- </section>
225
- )
157
+ updateConfig({
158
+ ...config,
159
+ showEditorPanel: !displayPanel
160
+ })
226
161
  }
227
162
 
228
163
  const convertStateToConfig = () => {
@@ -339,11 +274,7 @@ const EditorPanel = memo(() => {
339
274
 
340
275
  return (
341
276
  <ErrorBoundary component='EditorPanel'>
342
- {!config.newViz && config.runtime && config.runtime.editorErrorMessage && <Error />}
343
- {(!config.dataColumn || !config.dataFunction) && <Confirm />}
344
- <button className={displayPanel ? `editor-toggle` : `editor-toggle collapsed`} title={displayPanel ? `Collapse Editor` : `Expand Editor`} onClick={onBackClick} />
345
- <section className={displayPanel ? 'editor-panel cove' : 'hidden editor-panel cove'}>
346
- <div className='heading-2'>Configure Data Bite</div>
277
+ <Layout.Sidebar isEditor={true} config={config} title='Configure Data Bites' onBackClick={onBackClick} displayPanel={displayPanel}>
347
278
  <section className='form-container'>
348
279
  <form>
349
280
  <Accordion allowZeroExpanded={true}>
@@ -701,7 +632,7 @@ const EditorPanel = memo(() => {
701
632
  </Accordion>
702
633
  </form>
703
634
  </section>
704
- </section>
635
+ </Layout.Sidebar>
705
636
  </ErrorBoundary>
706
637
  )
707
638
  })
@@ -0,0 +1,23 @@
1
+ import React from 'react'
2
+ import '../scss/kpi.scss'
3
+
4
+ export const KPIComponent = ({ label, value }) => {
5
+ return (
6
+ <div className='cove-component__content kpi-container'>
7
+ <div className='kpi-content'>
8
+ <div className='label-container'>
9
+ <span className='label'>
10
+ <strong>{label}</strong>
11
+ </span>
12
+ </div>
13
+ <div className='value-container'>
14
+ <span className='value'>
15
+ <strong>{value}</strong>
16
+ </span>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ )
21
+ }
22
+
23
+ export default KPIComponent
@@ -1,12 +1,5 @@
1
1
  @import '../../../core/styles/variables';
2
2
 
3
- &.is-editor {
4
- position: relative;
5
- .cove-component__inner {
6
- margin: 3em auto 0;
7
- max-width: 35em;
8
- }
9
- }
10
3
  &.theme-blue {
11
4
  .bite-value {
12
5
  color: $primary;
@@ -76,14 +69,27 @@
76
69
  // TODO after v2 refactor remove header reference colors
77
70
  // Use .font-color/.bg-color, etc. in color definitions or something similar
78
71
  // Remove box shadows if we're no longer using them
79
- .cove-component__inner {
72
+ .sidebar + .cove-component__content .bite-content-container:not(.component--hide-background-color) {
73
+ margin: 3em auto 0;
74
+ max-width: 35em;
75
+ }
76
+
77
+ .sidebar + .cove-component__content .bite-content-container:not(.component--hide-background-color) {
78
+ background-color: white;
79
+ }
80
+
81
+ .cove-component__content {
80
82
  // Visual: Shadow
81
83
  &.shadow {
82
84
  box-shadow: rgba(0, 0, 0, 0.2) 0 3px 10px;
83
85
  }
84
86
 
87
+ .cove-component__inner {
88
+ padding: 1rem !important;
89
+ }
90
+
85
91
  // General: Bite Style > Graphic
86
- .bite__style--graphic {
92
+ &.bite__style--graphic {
87
93
  .bite-content {
88
94
  flex: 1;
89
95
  }
@@ -135,7 +141,6 @@
135
141
  border-top-left-radius: 3px;
136
142
  border-top-right-radius: 3px;
137
143
  }
138
-
139
144
  .bite-content-container.component--hideBackgroundColor {
140
145
  background: transparent;
141
146
  }
@@ -231,7 +236,7 @@
231
236
 
232
237
  .cove-component__content {
233
238
  display: flex;
234
- padding: 1em;
239
+ padding: 1em !important;
235
240
  justify-content: space-between;
236
241
 
237
242
  .bite-content {
@@ -58,7 +58,7 @@
58
58
  }
59
59
  }
60
60
  background: #fff;
61
- width: $editorWidth;
61
+ // width: $editorWidth;
62
62
  overflow-y: overlay;
63
63
  position: absolute;
64
64
  z-index: 8;
@@ -0,0 +1,68 @@
1
+ .kpi-container {
2
+ border-radius: 10px;
3
+ border: 1px solid rgba(0, 0, 0, 0.25) !important;
4
+ box-sizing: border-box;
5
+ box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
6
+ display: flex;
7
+ padding: 5px !important;
8
+ height: 70px;
9
+ width: 100%;
10
+ position: relative;
11
+ display: flex;
12
+ }
13
+
14
+ .kpi-content {
15
+ background: linear-gradient(90deg, rgba(8, 132, 136, 1) 19%, rgba(255, 255, 255, 1) 74%);
16
+ border-radius: 10px;
17
+ display: flex;
18
+ flex: 1;
19
+ }
20
+
21
+ .label-container,
22
+ .value-container {
23
+ display: flex;
24
+ width: 100%;
25
+ height: 100%;
26
+ align-items: center;
27
+ }
28
+
29
+ .label-container {
30
+ padding-left: 30px !important;
31
+ }
32
+
33
+ .value-container {
34
+ justify-content: center;
35
+ }
36
+
37
+ .label {
38
+ color: white;
39
+ font-size: 18px;
40
+ }
41
+
42
+ .value {
43
+ color: brown;
44
+ font-size: 24px;
45
+ }
46
+
47
+ //small
48
+ @media only screen and (min-width: 768px) and (max-width: 991px) {
49
+ .label {
50
+ font-size: 14px;
51
+ }
52
+ .value {
53
+ font-size: 20px;
54
+ }
55
+ }
56
+ //xs
57
+ @media only screen and (max-width: 767px) {
58
+ .label-container {
59
+ justify-content: flex-start;
60
+ padding-left: 10px !important;
61
+ }
62
+ .label {
63
+ font-size: 10px;
64
+ }
65
+ .value {
66
+ font-size: 14px;
67
+ }
68
+ }
@@ -5,7 +5,7 @@
5
5
 
6
6
  .cdc-open-viz-module.type-data-bite {
7
7
  @import 'bite';
8
- @import 'editor-panel';
8
+ // @import 'editor-panel';
9
9
  .visually-hidden {
10
10
  position: fixed;
11
11
  left: -10000px;