@cdc/core 4.23.9 → 4.23.10
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/components/AdvancedEditor.jsx +1 -1
- package/components/DataTable.jsx +242 -201
- package/components/Filters.jsx +7 -5
- package/components/{GlobalContext.jsx → GlobalContext.tsx} +16 -5
- package/components/MediaControls.jsx +7 -6
- package/components/elements/_stories/Button.stories.tsx +28 -0
- package/components/elements/_stories/Card.stories.tsx +21 -0
- package/components/managers/_stories/DataDesigner.stories.tsx +26 -0
- package/components/ui/Accordion.jsx +1 -1
- package/components/ui/Icon.jsx +3 -1
- package/components/ui/Modal.jsx +1 -1
- package/components/ui/Select.jsx +30 -0
- package/components/ui/Tooltip.jsx +3 -2
- package/components/ui/_stories/Accordion.stories.tsx +36 -0
- package/components/ui/_stories/Icon.stories.tsx +22 -0
- package/data/colorPalettes.js +11 -0
- package/helpers/{DataTransform.js → DataTransform.ts} +26 -18
- package/helpers/fetchRemoteData.js +3 -9
- package/helpers/gatherQueryParams.ts +7 -0
- package/helpers/getFileExtension.ts +5 -0
- package/helpers/lineChartHelpers.js +7 -0
- package/helpers/useDataVizClasses.js +9 -3
- package/package.json +2 -2
- package/styles/_data-table.scss +25 -9
- package/styles/_reset.scss +6 -0
- package/styles/_typography.scss +20 -0
- package/styles/base.scss +1 -0
- package/styles/v2/layout/_component.scss +3 -3
- package/styles/v2/themes/_color-definitions.scss +27 -45
|
@@ -1,14 +1,25 @@
|
|
|
1
|
-
import React, { createContext, useContext, useState } from 'react'
|
|
1
|
+
import React, { ReactElement, createContext, useContext, useState } from 'react'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
type Global = {
|
|
4
|
+
overlay: {
|
|
5
|
+
object: ReactElement,
|
|
6
|
+
show: boolean,
|
|
7
|
+
disableBgClose: boolean,
|
|
8
|
+
actions: {
|
|
9
|
+
openOverlay: (any, boolean?) => void,
|
|
10
|
+
toggleOverlay: (boolean?) => void
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export const GlobalContext = createContext<Global>({} as Global)
|
|
4
15
|
export const useGlobalContext = () => useContext(GlobalContext)
|
|
5
16
|
|
|
6
17
|
export const GlobalContextProvider = ({ children }) => {
|
|
7
|
-
const [globalContextData, setGlobalContextData] = useState({})
|
|
18
|
+
const [globalContextData, setGlobalContextData] = useState<Global>({} as Global)
|
|
8
19
|
|
|
9
20
|
const openOverlay = (obj, disableBgClose = false) => {
|
|
10
21
|
let payload = { object: obj, show: true, disableBgClose: disableBgClose }
|
|
11
|
-
setGlobalContextData(context => ({ ...context, overlay: { ...payload } }))
|
|
22
|
+
setGlobalContextData(context => ({ ...context, overlay: { ...payload } } as Global))
|
|
12
23
|
}
|
|
13
24
|
|
|
14
25
|
const toggleOverlay = (display = false) => {
|
|
@@ -21,7 +32,7 @@ export const GlobalContextProvider = ({ children }) => {
|
|
|
21
32
|
}))
|
|
22
33
|
}
|
|
23
34
|
|
|
24
|
-
const globalSettings = {
|
|
35
|
+
const globalSettings: Global = {
|
|
25
36
|
overlay: {
|
|
26
37
|
object: globalContextData.overlay?.object || null,
|
|
27
38
|
show: globalContextData.overlay?.show || false,
|
|
@@ -59,7 +59,7 @@ const generateMedia = (state, type, elementToCapture) => {
|
|
|
59
59
|
|
|
60
60
|
switch (type) {
|
|
61
61
|
case 'image':
|
|
62
|
-
html2canvas(baseSvg).then(canvas => {
|
|
62
|
+
html2canvas(baseSvg, {foreignObjectRendering: true}).then(canvas => {
|
|
63
63
|
saveImageAs(canvas.toDataURL(), filename + '.png')
|
|
64
64
|
})
|
|
65
65
|
return
|
|
@@ -107,19 +107,20 @@ const Button = ({ state, text, type, title, elementToCapture }) => {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// Link to CSV/JSON data
|
|
110
|
-
const Link = ({ config }) => {
|
|
110
|
+
const Link = ({ config, dashboardDataConfig }) => {
|
|
111
|
+
let dataConfig = dashboardDataConfig || config;
|
|
111
112
|
// Handles Maps & Charts
|
|
112
|
-
if (
|
|
113
|
+
if (dataConfig.dataFileSourceType === 'url' && dataConfig.dataFileName && config.table.showDownloadUrl) {
|
|
113
114
|
return (
|
|
114
|
-
<a href={
|
|
115
|
+
<a href={dataConfig.dataFileName} title={buttonText.link} target='_blank'>
|
|
115
116
|
{buttonText.link}
|
|
116
117
|
</a>
|
|
117
118
|
)
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
// Handles Dashboards
|
|
121
|
-
return config?.table?.showDownloadUrl &&
|
|
122
|
-
<a href={
|
|
122
|
+
return config?.table?.showDownloadUrl && dataConfig.dataUrl ? (
|
|
123
|
+
<a href={dataConfig.dataUrl} title='Link to view full data set' target='_blank'>
|
|
123
124
|
{buttonText.link}
|
|
124
125
|
</a>
|
|
125
126
|
) : null
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
3
|
+
|
|
4
|
+
import Button from '../Button'
|
|
5
|
+
|
|
6
|
+
const classNames = ['', 'cove-button--success', 'cove-button--warn', 'cove-button--muted']
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Button> = {
|
|
9
|
+
title: 'Components/Atoms/Button',
|
|
10
|
+
component: Button
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof Button>
|
|
14
|
+
|
|
15
|
+
export const Primary: Story = {
|
|
16
|
+
render: args => (
|
|
17
|
+
<>
|
|
18
|
+
<Button {...args} className={classNames[0]}>
|
|
19
|
+
Button
|
|
20
|
+
</Button>
|
|
21
|
+
<Button {...args} className={classNames[1]}>
|
|
22
|
+
Success
|
|
23
|
+
</Button>
|
|
24
|
+
</>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default meta
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
3
|
+
|
|
4
|
+
import Card from '../Card'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Card> = {
|
|
7
|
+
title: 'Components/Atoms/Card',
|
|
8
|
+
component: Card
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type Story = StoryObj<typeof Card>
|
|
12
|
+
|
|
13
|
+
export const Primary: Story = {
|
|
14
|
+
render: args => (
|
|
15
|
+
<>
|
|
16
|
+
<Card {...args}>Card Content Here</Card>
|
|
17
|
+
</>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default meta
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
3
|
+
|
|
4
|
+
import DataDesigner from '../DataDesigner'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof DataDesigner> = {
|
|
7
|
+
title: 'Components/Organisms/DataDesigner',
|
|
8
|
+
component: DataDesigner
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type Story = StoryObj<typeof DataDesigner>
|
|
12
|
+
|
|
13
|
+
export const Primary: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
configureData: {
|
|
16
|
+
dataDescription: 'some description'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
render: args => (
|
|
20
|
+
<>
|
|
21
|
+
<DataDesigner {...args}>DataDesigner Content Here</DataDesigner>
|
|
22
|
+
</>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default meta
|
|
@@ -8,7 +8,7 @@ import Tooltip from './Tooltip'
|
|
|
8
8
|
import '../../styles/v2/components/accordion.scss'
|
|
9
9
|
|
|
10
10
|
//Define the "slots" to be populated by subcomponents
|
|
11
|
-
const AccordionSection =
|
|
11
|
+
const AccordionSection = children => children
|
|
12
12
|
|
|
13
13
|
const Accordion = ({ children }) => {
|
|
14
14
|
const childNodes = Children.toArray(children)
|
package/components/ui/Icon.jsx
CHANGED
|
@@ -67,7 +67,9 @@ const iconHash = {
|
|
|
67
67
|
'filter-dropdowns': iconDropdowns
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
const
|
|
70
|
+
export const ICON_TYPES = Object.keys(iconHash)
|
|
71
|
+
|
|
72
|
+
const Icon = ({ display = '', base, alt = '', size, color, style, ...attributes }) => {
|
|
71
73
|
const IconObj = iconHash[display] || null
|
|
72
74
|
|
|
73
75
|
const filteredAttrs = { ...attributes }
|
package/components/ui/Modal.jsx
CHANGED
|
@@ -8,7 +8,7 @@ import '../../styles/v2/components/modal.scss'
|
|
|
8
8
|
|
|
9
9
|
//Define the "slots" to be populated by subcomponents
|
|
10
10
|
const ModalHeader = () => null
|
|
11
|
-
const ModalContent = () =>
|
|
11
|
+
const ModalContent = (children) => children
|
|
12
12
|
const ModalFooter = () => null
|
|
13
13
|
|
|
14
14
|
const Modal = ({ fontTheme = 'dark', headerBgColor = '#fff', showDividerTop = true, showDividerBottom = true, showClose = true, children, override = null }) => {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
const Select = ({ label, value, options, fieldName, section = null, subsection = null, required = false, tooltip, onchange, initial: initialValue, ...attributes }) => {
|
|
4
|
+
let optionsJsx = options.map((optionName, index) => (
|
|
5
|
+
<option value={optionName} key={index}>
|
|
6
|
+
{optionName}
|
|
7
|
+
</option>
|
|
8
|
+
))
|
|
9
|
+
|
|
10
|
+
if (initialValue) {
|
|
11
|
+
optionsJsx.unshift(
|
|
12
|
+
<option value='' key='initial'>
|
|
13
|
+
{initialValue}
|
|
14
|
+
</option>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<label>
|
|
20
|
+
<span className='edit-label'>
|
|
21
|
+
{label}
|
|
22
|
+
{tooltip}
|
|
23
|
+
</span>
|
|
24
|
+
<select className={required && !value ? 'warning' : ''} name={fieldName} value={value} onChange={onchange} {...attributes}>
|
|
25
|
+
{optionsJsx}
|
|
26
|
+
</select>
|
|
27
|
+
</label>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
export default Select
|
|
@@ -6,8 +6,8 @@ import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
|
6
6
|
// Styles
|
|
7
7
|
import '../../styles/v2/components/ui/tooltip.scss'
|
|
8
8
|
|
|
9
|
-
const TooltipTarget =
|
|
10
|
-
const TooltipContent =
|
|
9
|
+
const TooltipTarget = children => children
|
|
10
|
+
const TooltipContent = children => children
|
|
11
11
|
|
|
12
12
|
const Tooltip = ({ place = 'top', trigger = 'hover', float = false, shadow = true, border = false, children, style, ...attributes }) => {
|
|
13
13
|
const tooltipTargetChildren = children.find(el => el.type === TooltipTarget)
|
|
@@ -32,6 +32,7 @@ const Tooltip = ({ place = 'top', trigger = 'hover', float = false, shadow = tru
|
|
|
32
32
|
{/* prettier-ignore */}
|
|
33
33
|
<ReactTooltip
|
|
34
34
|
id={uid}
|
|
35
|
+
// ! do not remove the deprecated property, it will break tooltips in the editor.
|
|
35
36
|
anchorId={uid}
|
|
36
37
|
className={'cove-tooltip__content' + (' place-' + place) + (!float ? ' cove-tooltip__content--animated' : '') + (trigger === 'click' ? ' interactive' : '') + (border ? (' cove-tooltip--border') : '') + (shadow ? ' has-shadow' : '')}
|
|
37
38
|
globalEventOff="click"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Meta, StoryObj } from '@storybook/react'
|
|
3
|
+
|
|
4
|
+
import Accordion from '../Accordion'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Accordion> = {
|
|
7
|
+
title: 'Components/Molecules/Accordion',
|
|
8
|
+
component: Accordion
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default meta
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof Accordion.Section>
|
|
14
|
+
|
|
15
|
+
export const Primary: Story = {
|
|
16
|
+
args: {
|
|
17
|
+
title: 'Collapsed Section Content',
|
|
18
|
+
warnIf: 1 === 1,
|
|
19
|
+
tooltipText: 'This is an <strong>example</strong> tooltip.'
|
|
20
|
+
},
|
|
21
|
+
argTypes: {
|
|
22
|
+
warnIf: { control: 'boolean' }
|
|
23
|
+
},
|
|
24
|
+
render: args => (
|
|
25
|
+
<Accordion>
|
|
26
|
+
<Accordion.Section {...args}>
|
|
27
|
+
<p>An accordion is used to show (and hide) HTML content.</p>
|
|
28
|
+
<p>Both the element that is used to open the accordion and the accordion's content can be any HTML element.</p>
|
|
29
|
+
</Accordion.Section>
|
|
30
|
+
<Accordion.Section {...args}>
|
|
31
|
+
<p>An accordion is used to show (and hide) HTML content.</p>
|
|
32
|
+
<p>Both the element that is used to open the accordion and the accordion's content can be any HTML element.</p>
|
|
33
|
+
</Accordion.Section>
|
|
34
|
+
</Accordion>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
3
|
+
|
|
4
|
+
import Icon, { ICON_TYPES } from '../Icon'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Icon> = {
|
|
7
|
+
title: 'Components/Atoms/Icon',
|
|
8
|
+
component: Icon,
|
|
9
|
+
parameters: {
|
|
10
|
+
display: ICON_TYPES
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type Story = StoryObj<typeof Icon>
|
|
15
|
+
|
|
16
|
+
export const Primary: Story = {
|
|
17
|
+
args: {
|
|
18
|
+
display: 'question'
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default meta
|
package/data/colorPalettes.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { update } from 'lodash'
|
|
1
2
|
import { updatePaletteNames } from '../helpers/updatePaletteNames'
|
|
2
3
|
|
|
3
4
|
const colorPalettesMap = {
|
|
@@ -63,6 +64,16 @@ export const colorPalettes3 = {
|
|
|
63
64
|
'complementary-5': ['#df168c', '#1EB386']
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
const sequentialColors = {
|
|
68
|
+
'Sequential Blue': ['#C6DBEF', '#9ECAE1', '#6BAED6', '#4292C6', '#2171B5', '#084594'],
|
|
69
|
+
'Sequential Blue Two': ['#D5F6F9', '#99E2ED', '#5FB6D1', '#3189B0', '#116091', '#0A3E72'],
|
|
70
|
+
'Sequential Blue Three': ['#F5FEFF', '#E1FBFF', '#C0F2FD', '#94E2ED', '#5EBAD4', '#3695BE', '#2273A0', '#0E5181', '#093460'],
|
|
71
|
+
'Sequential Orange': ['#FFEFCF', '#FFD49C', '#F7A866', '#EB7723', '#B95117', '#862B0B'],
|
|
72
|
+
'Sequential Orange Two': ['#FFFDF0', '#FFF7DC', '#FFE9C2', '#FFD097', '#F7A866', '#EB7723', '#B95117', '#853209', '#661F00'],
|
|
73
|
+
'Sequential Green': ['#C7E9C0', '#A1D99B', '#74C476', '#41AB5D', '#238B45', '#005A32']
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const sequentialPalettes = sequentialColors
|
|
66
77
|
export const colorPalettesChart = updatePaletteNames(colorPalettes2)
|
|
67
78
|
const colorPalettes = updatePaletteNames(colorPalettesMap)
|
|
68
79
|
export const twoColorPalette = updatePaletteNames(colorPalettes3)
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
type DataArray = Record<string,any>[]
|
|
2
|
+
|
|
1
3
|
export class DataTransform {
|
|
4
|
+
constants: any
|
|
2
5
|
constructor() {
|
|
3
6
|
this.constants = {
|
|
4
7
|
errorMessageEmtpyData: 'Your data file is empty.',
|
|
@@ -11,7 +14,7 @@ export class DataTransform {
|
|
|
11
14
|
|
|
12
15
|
//Performs standardizations that can be completed automatically without use input
|
|
13
16
|
autoStandardize(data) {
|
|
14
|
-
const errorsFound = []
|
|
17
|
+
const errorsFound: any[] = []
|
|
15
18
|
|
|
16
19
|
// Empty data
|
|
17
20
|
if (0 === data.length) {
|
|
@@ -30,7 +33,7 @@ export class DataTransform {
|
|
|
30
33
|
|
|
31
34
|
//Convert array of arrays, to array of objects
|
|
32
35
|
if (data.filter(row => row.constructor !== Object).length > 0) {
|
|
33
|
-
let standardizedData = []
|
|
36
|
+
let standardizedData: Record<string, any>[] = []
|
|
34
37
|
for (let row = 1; row < data.length; row++) {
|
|
35
38
|
let standardizedRow = {}
|
|
36
39
|
data[row].forEach((datum, col) => {
|
|
@@ -66,9 +69,9 @@ export class DataTransform {
|
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
let standardizedMapped = {}
|
|
69
|
-
let standardized = []
|
|
72
|
+
let standardized: string[] = []
|
|
70
73
|
data.forEach(row => {
|
|
71
|
-
let nonNumericKeys = []
|
|
74
|
+
let nonNumericKeys: any[] = []
|
|
72
75
|
Object.keys(row).forEach(key => {
|
|
73
76
|
if (key !== description.seriesKey && isNaN(parseFloat(row[key]))) {
|
|
74
77
|
nonNumericKeys.push(key)
|
|
@@ -95,10 +98,10 @@ export class DataTransform {
|
|
|
95
98
|
|
|
96
99
|
return standardized
|
|
97
100
|
} else {
|
|
98
|
-
let standardized = []
|
|
101
|
+
let standardized: {key: string, value: any}[] = []
|
|
99
102
|
|
|
100
103
|
data.forEach(row => {
|
|
101
|
-
let nonNumericKeys = []
|
|
104
|
+
let nonNumericKeys: string[] = []
|
|
102
105
|
Object.keys(row).forEach(key => {
|
|
103
106
|
if (isNaN(parseFloat(row[key]))) {
|
|
104
107
|
nonNumericKeys.push(key)
|
|
@@ -124,7 +127,7 @@ export class DataTransform {
|
|
|
124
127
|
if (description.seriesKey !== undefined && description.xKey !== undefined && (description.valueKey !== undefined || (description.valueKeys !== undefined && description.valueKeys.length > 0))) {
|
|
125
128
|
if (description.valueKeys !== undefined) {
|
|
126
129
|
let standardizedMapped = {}
|
|
127
|
-
let standardized = []
|
|
130
|
+
let standardized: string[] = []
|
|
128
131
|
let valueKeys = description.valueKeys
|
|
129
132
|
if (description.ignoredKeys && description.ignoredKeys.length > 0) {
|
|
130
133
|
valueKeys = valueKeys.concat(description.ignoredKeys)
|
|
@@ -132,7 +135,7 @@ export class DataTransform {
|
|
|
132
135
|
|
|
133
136
|
data.forEach(row => {
|
|
134
137
|
valueKeys.forEach(valueKey => {
|
|
135
|
-
let extraKeys = []
|
|
138
|
+
let extraKeys: string[] = []
|
|
136
139
|
let uniqueKey = row[description.xKey] + '|' + valueKey
|
|
137
140
|
Object.keys(row).forEach(key => {
|
|
138
141
|
if (key !== description.xKey && key !== description.seriesKey && valueKeys.indexOf(key) === -1) {
|
|
@@ -161,10 +164,10 @@ export class DataTransform {
|
|
|
161
164
|
return standardized
|
|
162
165
|
} else {
|
|
163
166
|
let standardizedMapped = {}
|
|
164
|
-
let standardized = []
|
|
167
|
+
let standardized: any[] = []
|
|
165
168
|
|
|
166
169
|
data.forEach(row => {
|
|
167
|
-
let extraKeys = []
|
|
170
|
+
let extraKeys: string[] = []
|
|
168
171
|
let uniqueKey = row[description.xKey]
|
|
169
172
|
Object.keys(row).forEach(key => {
|
|
170
173
|
if (key !== description.xKey && key !== description.seriesKey && key !== description.valueKey) {
|
|
@@ -213,8 +216,9 @@ export class DataTransform {
|
|
|
213
216
|
* Set testing = true if you need to see before and after data
|
|
214
217
|
*
|
|
215
218
|
*/
|
|
216
|
-
cleanData(data, excludeKey, testing = false) {
|
|
217
|
-
let cleanedupData = []
|
|
219
|
+
cleanData(data: DataArray, excludeKey, testing = false): DataArray {
|
|
220
|
+
let cleanedupData: DataArray = []
|
|
221
|
+
if(!Array.isArray(data)) debugger;
|
|
218
222
|
if (testing) console.log('## Data to clean=', data)
|
|
219
223
|
if (excludeKey === undefined) {
|
|
220
224
|
console.log('COVE: cleanData excludeKey undefined')
|
|
@@ -224,19 +228,23 @@ export class DataTransform {
|
|
|
224
228
|
if (testing) console.log('clean', i, ' d', d)
|
|
225
229
|
let cleaned = {}
|
|
226
230
|
Object.keys(d).forEach(function (key) {
|
|
231
|
+
const value = d[key];
|
|
227
232
|
if (key === excludeKey) {
|
|
228
233
|
// pass thru
|
|
229
|
-
cleaned[key] =
|
|
234
|
+
cleaned[key] = value
|
|
230
235
|
} else {
|
|
231
236
|
// remove comma and dollar signs
|
|
232
|
-
if (testing) console.log('typeof
|
|
237
|
+
if (testing) console.log('typeof value is ', typeof value)
|
|
233
238
|
let tmp = ''
|
|
234
|
-
|
|
235
|
-
|
|
239
|
+
const removeCommasAndDollars = (num: string) => num.replace(/[,\$]/g, '')
|
|
240
|
+
if (typeof value === 'string') {
|
|
241
|
+
tmp = value ? removeCommasAndDollars(value) : ''
|
|
236
242
|
} else {
|
|
237
|
-
tmp =
|
|
243
|
+
tmp = value ? value : ''
|
|
238
244
|
}
|
|
239
|
-
|
|
245
|
+
// UNSAFE_isANumber: matches for any string of digits optionally interrupted by a period.
|
|
246
|
+
const UNSAFE_isANumber = (num: any) => /\d+\.?\d*/.test(num)
|
|
247
|
+
if (!isNaN(parseFloat(tmp)) || UNSAFE_isANumber(tmp)) {
|
|
240
248
|
cleaned[key] = tmp
|
|
241
249
|
} else {
|
|
242
250
|
cleaned[key] = ''
|
|
@@ -8,10 +8,8 @@ export default async function (url, visualizationType = '') {
|
|
|
8
8
|
const regex = /(?:\.([^.]+))?$/
|
|
9
9
|
const ext = regex.exec(path)[1]
|
|
10
10
|
|
|
11
|
-
let data = []
|
|
12
|
-
|
|
13
11
|
if ('csv' === ext) {
|
|
14
|
-
|
|
12
|
+
return await fetch(url.href)
|
|
15
13
|
.then(response => response.text())
|
|
16
14
|
.then(responseText => {
|
|
17
15
|
// for every comma NOT inside quotes, replace with a pipe delimiter
|
|
@@ -31,13 +29,9 @@ export default async function (url, visualizationType = '') {
|
|
|
31
29
|
})
|
|
32
30
|
return parsedCsv.data
|
|
33
31
|
})
|
|
32
|
+
} else {
|
|
33
|
+
return await fetch(url.href).then(response => response.json())
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
if ('json' === ext) {
|
|
37
|
-
data = await fetch(url.href).then(response => response.json())
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return data
|
|
41
35
|
} catch {
|
|
42
36
|
// If we can't parse it, still attempt to fetch it
|
|
43
37
|
try {
|
|
@@ -16,7 +16,14 @@ export default function useDataVizClasses(config, viewport = null) {
|
|
|
16
16
|
let innerContainerClasses = ['cove-component__inner']
|
|
17
17
|
let contentClasses = ['cove-component__content']
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
const { visualizationType, title, showTitle } = config
|
|
20
|
+
|
|
21
|
+
if (visualizationType === 'Spark Line' || visualizationType === 'chart') {
|
|
22
|
+
if (title && showTitle) contentClasses.push('component--has-title')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
config.showTitle && contentClasses.push('component--has-title')
|
|
26
|
+
config.title && config.visualizationType !== 'chart' && config.visualizationType !== 'Spark Line' && contentClasses.push('component--has-title')
|
|
20
27
|
config.subtext && innerContainerClasses.push('component--has-subtext')
|
|
21
28
|
config.biteStyle && innerContainerClasses.push(`bite__style--${config.biteStyle}`)
|
|
22
29
|
config.general?.isCompactStyle && innerContainerClasses.push(`component--isCompactStyle`)
|
|
@@ -33,8 +40,7 @@ export default function useDataVizClasses(config, viewport = null) {
|
|
|
33
40
|
config?.visual?.roundedBorders && innerContainerClasses.push('bite--has-rounded-borders')
|
|
34
41
|
|
|
35
42
|
let sparkLineStyles = {
|
|
36
|
-
width: '100%'
|
|
37
|
-
height: '100px'
|
|
43
|
+
width: '100%'
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
// Starting work on combining legend classes.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/core",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.10",
|
|
4
4
|
"description": "Core components, styles, hooks, and helpers, for the CDC Open Visualization project",
|
|
5
5
|
"moduleName": "CdcCore",
|
|
6
6
|
"main": "dist/cdccore",
|
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
"react": "^18.2.0",
|
|
31
31
|
"react-dom": "^18.2.0"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "b3f3263e444bbf99d525b05040be22f06553760e"
|
|
34
34
|
}
|
package/styles/_data-table.scss
CHANGED
|
@@ -30,6 +30,17 @@ div.data-table-heading {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
table.horizontal {
|
|
34
|
+
tr {
|
|
35
|
+
display: flex;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
th,
|
|
39
|
+
td {
|
|
40
|
+
min-width: 200px;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
table.data-table {
|
|
34
45
|
width: 100%;
|
|
35
46
|
background: #fff;
|
|
@@ -108,14 +119,14 @@ table.data-table {
|
|
|
108
119
|
|
|
109
120
|
// format the white triangle sort icon in data table headers
|
|
110
121
|
.sort-icon {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
fill: white;
|
|
123
|
+
width: 30px;
|
|
124
|
+
float: right;
|
|
125
|
+
align-self: center;
|
|
126
|
+
position: absolute;
|
|
127
|
+
right: 10px;
|
|
128
|
+
top: 50%;
|
|
129
|
+
transform: translateY(-50%);
|
|
119
130
|
}
|
|
120
131
|
|
|
121
132
|
th:last-child,
|
|
@@ -155,7 +166,12 @@ table.data-table {
|
|
|
155
166
|
border-right: 0 !important;
|
|
156
167
|
}
|
|
157
168
|
}
|
|
158
|
-
|
|
169
|
+
tr {
|
|
170
|
+
display: flex;
|
|
171
|
+
& > * {
|
|
172
|
+
width: 100%;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
159
175
|
th {
|
|
160
176
|
flex-grow: 1;
|
|
161
177
|
}
|
package/styles/_reset.scss
CHANGED
|
@@ -141,6 +141,12 @@
|
|
|
141
141
|
top: -0.5em;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
header sup {
|
|
145
|
+
/* Allow line breaks using html in the header. */
|
|
146
|
+
top: initial;
|
|
147
|
+
line-height: initial;
|
|
148
|
+
}
|
|
149
|
+
|
|
144
150
|
sub {
|
|
145
151
|
/* Move the subscripted text down, but only
|
|
146
152
|
half as far down as the superscript moved up */
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
&.font-small {
|
|
2
|
+
.chart-container {
|
|
3
|
+
font-size: 0.9em !important;
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
&.font-medium {
|
|
8
|
+
font-size: 1em !important;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
&.font-large {
|
|
12
|
+
.chart-container {
|
|
13
|
+
font-size: 1.1em !important;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.subtext {
|
|
18
|
+
font-style: italic;
|
|
19
|
+
font-size: 0.9em !important;
|
|
20
|
+
}
|
package/styles/base.scss
CHANGED
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
border: none !important;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
.cdc-data-bite-inner-container:not(.component--has-title) .cove-component__content:not(.no-borders) {
|
|
28
|
-
|
|
29
|
-
}
|
|
27
|
+
// .cdc-data-bite-inner-container:not(.component--has-title) .cove-component__content:not(.no-borders) {
|
|
28
|
+
// border-top: 1px solid #ccc;
|
|
29
|
+
// }
|
|
30
30
|
|
|
31
31
|
.cove-component__content:not(.component--hideBackgroundColor) {
|
|
32
32
|
background: $lightestGray;
|