@cdc/core 4.24.1 → 4.24.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.
@@ -1,4 +1,5 @@
1
1
  import { useEffect, useState, useMemo } from 'react'
2
+ import { timeParse } from 'd3-time-format'
2
3
 
3
4
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
4
5
  import MediaControls from '@cdc/core/components/MediaControls'
@@ -17,13 +18,12 @@ import regionCellMatrix from './helpers/regionCellMatrix'
17
18
  import boxplotCellMatrix from './helpers/boxplotCellMatrix'
18
19
  import customColumns from './helpers/customColumns'
19
20
  import { TableConfig } from './types/TableConfig'
21
+ import { Column } from '../../types/Column'
20
22
 
21
23
  export type DataTableProps = {
22
24
  applyLegendToRow?: Function
23
25
  colorScale?: Function
24
- columns?: { navigate: { name: string } }
25
- // determines if columns should be wrapped in the table
26
- wrapColumns?: boolean
26
+ columns?: Record<string, Column>
27
27
  config: TableConfig
28
28
  dataConfig?: Object
29
29
  displayDataAsText?: Function
@@ -32,15 +32,22 @@ export type DataTableProps = {
32
32
  formatLegendLocation?: Function
33
33
  groupBy?: string
34
34
  headerColor?: string
35
+ imageRef?: string
35
36
  indexTitle?: string
37
+ isDebug?: boolean
38
+ isEditor?: boolean
36
39
  navigationHandler?: Function
40
+ outerContainerRef?: Function
37
41
  rawData: Object[]
38
42
  runtimeData: Object[] | Record<string, Object> // UNSAFE
39
43
  setFilteredCountryCode?: Function // used for Maps only
44
+ showDownloadButton?: boolean
40
45
  tabbingId: string
41
46
  tableTitle: string
42
47
  viewport: string
43
48
  vizTitle?: string
49
+ // determines if columns should be wrapped in the table
50
+ wrapColumns?: boolean
44
51
  }
45
52
 
46
53
  /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
@@ -92,9 +99,9 @@ const DataTable = (props: DataTableProps) => {
92
99
  break
93
100
  }
94
101
 
95
- const _runtimeData = config.table.customTableConfig ? customColumns(runtimeData, config.table.excludeColumns) : runtimeData
102
+ const _runtimeData = config.table.customTableConfig ? customColumns(rawData, config.table.excludeColumns) : runtimeData
96
103
 
97
- const rawRows = Object.keys(_runtimeData)
104
+ const rawRows = Object.keys(_runtimeData).filter(column => column != 'columns')
98
105
  const rows = isVertical
99
106
  ? rawRows.sort((a, b) => {
100
107
  let dataA
@@ -108,6 +115,10 @@ const DataTable = (props: DataTableProps) => {
108
115
  dataA = _runtimeData[a][sortBy.column]
109
116
  dataB = _runtimeData[b][sortBy.column]
110
117
  }
118
+ if (!dataA && !dataB && config.type === 'chart' && config.xAxis && config.xAxis.type === 'date' && config.xAxis.sortDates) {
119
+ dataA = timeParse(config.runtime.xAxis.dateParseFormat)(_runtimeData[a][config.xAxis.dataKey])
120
+ dataB = timeParse(config.runtime.xAxis.dateParseFormat)(_runtimeData[b][config.xAxis.dataKey])
121
+ }
111
122
  return dataA && dataB ? customSort(dataA, dataB, sortBy, config) : 0
112
123
  })
113
124
  : rawRows
@@ -117,7 +128,7 @@ const DataTable = (props: DataTableProps) => {
117
128
  OverflowY: 'scroll'
118
129
  }
119
130
 
120
- const hasRowType = !!Object.keys(rawData[0] || {}).find((v: string) => v.match(/row[_-]?type/i))
131
+ const hasRowType = !!Object.keys(rawData?.[0] || {}).find((v: string) => v.match(/row[_-]?type/i))
121
132
 
122
133
  const caption = useMemo(() => {
123
134
  if (config.type === 'map') {
@@ -8,7 +8,16 @@ export const customSort = (a, b, sortBy, config) => {
8
8
  if (config.type === 'map') {
9
9
  valueA = standardizeStateName(a)
10
10
  valueB = standardizeStateName(b)
11
+ // sort for Regions table for Map
12
+ if (String(valueA).toLowerCase().includes('region') && String(valueB).toLowerCase().includes('region')) {
13
+ const numberA = parseInt(a.match(/\d+$/)[0], 10)
14
+ const numberB = parseInt(b.match(/\d+$/)[0], 10)
15
+
16
+ // Compare the numeric parts
17
+ return !sortBy.asc ? Number(numberA) - Number(numberB) : Number(numberB) - Number(numberA)
18
+ }
11
19
  }
20
+
12
21
  // Treat booleans and nulls as an empty string
13
22
  valueA = valueA === false || valueA === true || valueA === null ? '' : valueA
14
23
  valueB = valueB === false || valueB === true || valueB === null ? '' : valueB
@@ -30,7 +30,7 @@ export const getChartCellValue = (row, column, config, runtimeData) => {
30
30
  let labelValue = rowObj[column] // just raw X axis string
31
31
  if (column === config.xAxis?.dataKey) {
32
32
  // not the prettiest, but helper functions work nicely here.
33
- cellValue = config.xAxis?.type === 'date' ? formatDate(config.xAxis?.dateDisplayFormat, parseDate(config.xAxis?.dateParseFormat, labelValue)) : labelValue
33
+ cellValue = config.xAxis?.type === 'date' ? formatDate(config.table?.dateDisplayFormat || config.xAxis?.dateDisplayFormat, parseDate(config.xAxis?.dateParseFormat, labelValue)) : labelValue
34
34
  } else {
35
35
  let resolvedAxis = 'left'
36
36
  let leftAxisItems = config.series ? config.series.filter(item => item?.axis === 'Left') : []
@@ -11,7 +11,7 @@ export const getDataSeriesColumns = (config, isVertical, runtimeData): string[]
11
11
  tmpSeriesColumns = Object.keys(runtimeData[0])
12
12
  }
13
13
  } else {
14
- tmpSeriesColumns = [config.xAxis?.dataKey, config.yAxis?.dataKey] //Object.keys(runtimeData[0])
14
+ tmpSeriesColumns = isVertical ? [config.xAxis?.dataKey, config.yAxis?.dataKey] : [config.yAxis?.dataKey]
15
15
  }
16
16
 
17
17
  // then add the additional Columns
@@ -1,26 +1,21 @@
1
- const getLabel = (name, config) => {
2
- let custLabel = ''
3
- if (config.columns && Object.keys(config.columns).length > 0) {
4
- Object.keys(config.columns).forEach(function (key) {
5
- var tmpColumn = config.columns[key]
6
- // add if not the index AND it is enabled to be added to data table
7
- if (tmpColumn.name === name) {
8
- custLabel = tmpColumn.label
9
- }
10
- })
11
- return custLabel
1
+ import { TableConfig } from '../types/TableConfig'
2
+
3
+ const getLabel = (name: string, config: TableConfig) => {
4
+ const columns = Object.values(config.columns || {})
5
+ const matchingConfiguredColumn = columns.find(col => col.name === name)
6
+ if (matchingConfiguredColumn?.label) {
7
+ return matchingConfiguredColumn.label
12
8
  }
9
+ return name
13
10
  }
14
11
 
15
- export const getSeriesName = (column, config) => {
12
+ export const getSeriesName = (column: string, config: TableConfig) => {
16
13
  // If a user sets the name on a series use that.
17
- let userUpdatedSeriesName = config.series ? config.series.filter(series => series.dataKey === column)?.[0]?.name : ''
18
- if (userUpdatedSeriesName) return userUpdatedSeriesName
19
-
14
+ const userDefinedSeries = config.series?.find(series => series.dataKey === column)
15
+ if (userDefinedSeries?.name) {
16
+ return userDefinedSeries.name
17
+ }
20
18
  if (config.runtimeSeriesLabels && config.runtimeSeriesLabels[column]) return config.runtimeSeriesLabels[column]
21
-
22
- let custLabel = getLabel(column, config) ? getLabel(column, config) : column
23
- let text = column === config.xAxis?.dataKey ? config.table.indexLabel : custLabel
24
-
25
- return text
19
+ const columnIsDataKey = column === config.xAxis?.dataKey
20
+ return columnIsDataKey ? config.table.indexLabel : getLabel(column, config)
26
21
  }
@@ -21,7 +21,11 @@ const mapCellArray = ({ rows, columns, runtimeData, config, applyLegendToRow, di
21
21
  let labelValue
22
22
  const mapZoomHandler = config.general.type === 'bubble' && config.general.allowMapZoom && config.general.geoType === 'world' ? () => setFilteredCountryCode(row) : undefined
23
23
  if ((config.general.geoType !== 'single-state' && config.general.geoType !== 'us-county') || config.general.type === 'us-geocode') {
24
+ const capitalize = str => {
25
+ return str.charAt(0).toUpperCase() + str.slice(1)
26
+ }
24
27
  labelValue = displayGeoName(row)
28
+ labelValue = String(labelValue).startsWith('region') ? capitalize(labelValue) : labelValue
25
29
  } else {
26
30
  labelValue = formatLegendLocation(row)
27
31
  }
@@ -2,43 +2,19 @@ import { Axis } from '@cdc/core/types/Axis'
2
2
  import { Series } from '@cdc/core/types/Series'
3
3
  import { Runtime } from '@cdc/core/types/Runtime'
4
4
  import { Table } from '@cdc/core/types/Table'
5
+ import { BoxPlot } from '../../../types/BoxPlot'
6
+ import { General } from '../../../types/General'
7
+ import { Column } from '../../../types/Column'
5
8
 
6
9
  export type TableConfig = {
7
10
  type?: string
8
11
  table: Table
9
12
  xAxis?: Axis
10
13
  yAxis?: Axis
11
- boxplot?: {
12
- tableData: Object[]
13
- labels: {
14
- mean: string
15
- maximum: string
16
- minimum: string
17
- iqr: string
18
- median: string
19
- q1: string
20
- q3: string
21
- outliers: string
22
- values: string
23
- total: string
24
- lowerBounds: string
25
- upperBounds: string
26
- }
27
- plots: []
28
- categories: string[]
29
- }
14
+ boxplot?: BoxPlot
30
15
  visualizationType?: string
31
- general?: {
32
- geoType: string
33
- type: string
34
- showDownloadButton: boolean
35
- allowMapZoom?: boolean
36
- }
37
- columns?: {
38
- geo: {
39
- name: string
40
- }
41
- }
16
+ general?: General
17
+ columns?: Record<string, Column>
42
18
  legend?: {
43
19
  specialClasses?: { key: string; label: string; value: string }[]
44
20
  hide?: boolean
@@ -74,21 +74,16 @@ export const useFilters = props => {
74
74
  const changeFilterActive = (index, value) => {
75
75
  let newFilters = visualizationConfig.type === 'map' ? [...filteredData] : [...visualizationConfig.filters]
76
76
 
77
- newFilters[index].active = value
78
- setConfig({ ...visualizationConfig })
79
-
80
- // If this is a button filter type show the button.
81
77
  if (visualizationConfig.filterBehavior === 'Apply Button') {
78
+ newFilters[index].queuedActive = value
82
79
  setShowApplyButton(true)
80
+ } else {
81
+ newFilters[index].active = value
83
82
  }
84
-
85
- // If we're not using the apply button we can set the filters right away.
86
- if (visualizationConfig.filterBehavior !== 'Apply Button') {
87
- setConfig({
88
- ...visualizationConfig,
89
- filters: newFilters
90
- })
91
- }
83
+ setConfig({
84
+ ...visualizationConfig,
85
+ filters: newFilters
86
+ })
92
87
 
93
88
  // Used for setting active filter, fromHash breaks the filteredData functionality.
94
89
  if (visualizationConfig.type === 'map' && visualizationConfig.filterBehavior === 'Filter Change') {
@@ -102,6 +97,13 @@ export const useFilters = props => {
102
97
  }
103
98
 
104
99
  const handleApplyButton = newFilters => {
100
+ newFilters.forEach(newFilter => {
101
+ if(newFilter.queuedActive){
102
+ newFilter.active = newFilter.queuedActive
103
+ delete newFilter.queuedActive
104
+ }
105
+ })
106
+
105
107
  setConfig({ ...visualizationConfig, filters: newFilters })
106
108
 
107
109
  if (type === 'map') {
@@ -51,7 +51,7 @@ const MultiSelect: React.FC<MultiSelectProps> = ({ section = null, subsection =
51
51
  update(newItems)
52
52
  }
53
53
 
54
- const handleItemRemove = (option: Option, caller: string) => {
54
+ const handleItemRemove = (option: Option) => {
55
55
  const newItems = selectedItems.filter(item => item.value !== option.value)
56
56
  setSelectedItems(newItems)
57
57
  update(newItems)
@@ -68,9 +68,9 @@ const MultiSelect: React.FC<MultiSelectProps> = ({ section = null, subsection =
68
68
 
69
69
  <div aria-labelledby={label ? multiID : undefined} className='selected'>
70
70
  {selectedItems.map(item => (
71
- <div key={item.value} role='button' tabIndex={0} onClick={() => handleItemRemove(item, 'button click')} onKeyUp={() => handleItemRemove(item, 'button key up')}>
71
+ <div key={item.value} role='button' tabIndex={0} onClick={() => handleItemRemove(item)} onKeyUp={() => handleItemRemove(item)}>
72
72
  {item.label}
73
- <button aria-label='Remove' onClick={() => handleItemRemove(item, 'X')}>
73
+ <button aria-label='Remove' onClick={() => handleItemRemove(item)}>
74
74
  x
75
75
  </button>
76
76
  </div>
@@ -0,0 +1,92 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+
3
+ const COLORS = [
4
+ ['baseColor', '#333'],
5
+ ['blue', '#005eaa'],
6
+ ['lightestGray', '#f2f2f2'],
7
+ ['lightGray', '#c7c7c7'],
8
+ ['mediumGray', '#565656'],
9
+ ['darkGray', '#333'],
10
+ ['red', '#d8000c'],
11
+ ['white', '#fff'],
12
+
13
+ ['primary', '#005eaa'],
14
+ ['secondary', '#88c3ea'],
15
+ ['tertiary', '#c0e9ff'],
16
+ ['quaternary', '#edf9ff'],
17
+
18
+ ['purple-primary', '#712177'],
19
+ ['purple-secondary', '#b890bb'],
20
+ ['purple-tertiary', '#e3d3e4'],
21
+ ['purple-quaternary', '#f7f2f7'],
22
+
23
+ ['brown-primary', '#705043'],
24
+ ['brown-secondary', '#ad907b'],
25
+ ['brown-tertiary', '#d7ccc8'],
26
+ ['brown-quaternary', '#f2ebe8'],
27
+
28
+ ['teal-primary', '#00695c'],
29
+ ['teal-secondary', '#4ebaaa'],
30
+ ['teal-tertiary', '#ceece7'],
31
+ ['teal-quaternary', '#ebf7f5'],
32
+
33
+ ['pink-primary', '#af4448'],
34
+ ['pink-secondary', '#e57373'],
35
+ ['pink-tertiary', '#ffc2c2'],
36
+ ['pink-quaternary', '#ffe7e7'],
37
+
38
+ ['orange-primary', '#bb4d00'],
39
+ ['orange-secondary', '#ffad42'],
40
+ ['orange-tertiary', '#ffe97d'],
41
+ ['orange-quaternary', '#fff4cf'],
42
+
43
+ ['slate-primary', '#29434e'],
44
+ ['slate-secondary', '#7e9ba5'],
45
+ ['slate-tertiary', '#b6c6d2'],
46
+ ['slate-quaternary', '#e2e8ed'],
47
+
48
+ ['indigo-primary', '#26418f'],
49
+ ['indigo-secondary', '#92a6dd'],
50
+ ['indigo-tertiary', '#dee8ff'],
51
+ ['indigo-quaternary', '#f2f6ff'],
52
+
53
+ ['cyan-primary', '#006778'],
54
+ ['cyan-secondary', '#65b0bd'],
55
+ ['cyan-tertiary', '#cce5e9'],
56
+ ['cyan-quaternary', '#ebf5f6'],
57
+
58
+ ['green-primary', '#4b830d'],
59
+ ['green-secondary', '#84bc49'],
60
+ ['green-tertiary', '#dcedc8'],
61
+ ['green-quaternary', '#f1f8e9'],
62
+
63
+ ['amber-primary', '#fbab18'],
64
+ ['amber-secondary', '#ffd54f'],
65
+ ['amber-tertiary', '#ffecb3'],
66
+ ['amber-quaternary', '#fff7e1']
67
+ ]
68
+
69
+ const ColorArray = () => {
70
+ return (
71
+ <table className='table'>
72
+ {COLORS.map(([name, hex]) => (
73
+ <tr>
74
+ <td style={{ background: hex, width: '60px' }}></td>
75
+ <td>{name}</td>
76
+ <td>{hex}</td>
77
+ </tr>
78
+ ))}
79
+ </table>
80
+ )
81
+ }
82
+
83
+ const meta: Meta<typeof ColorArray> = {
84
+ title: 'Components/Atoms/Colors',
85
+ component: ColorArray
86
+ }
87
+
88
+ type Story = StoryObj<typeof ColorArray>
89
+
90
+ export const Primary: Story = {}
91
+
92
+ export default meta
@@ -3,20 +3,27 @@ import type { Meta, StoryObj } from '@storybook/react'
3
3
 
4
4
  import Icon, { ICON_TYPES } from '../Icon'
5
5
 
6
- const meta: Meta<typeof Icon> = {
6
+ const IconArray = () => {
7
+ return (
8
+ <>
9
+ {ICON_TYPES.map(name => (
10
+ <div>
11
+ <span>
12
+ <Icon display={name} /> {name}{' '}
13
+ </span>
14
+ </div>
15
+ ))}
16
+ </>
17
+ )
18
+ }
19
+
20
+ const meta: Meta<typeof IconArray> = {
7
21
  title: 'Components/Atoms/Icon',
8
- component: Icon,
9
- parameters: {
10
- display: ICON_TYPES
11
- }
22
+ component: IconArray
12
23
  }
13
24
 
14
25
  type Story = StoryObj<typeof Icon>
15
26
 
16
- export const Primary: Story = {
17
- args: {
18
- display: 'question'
19
- }
20
- }
27
+ export const Primary: Story = {}
21
28
 
22
29
  export default meta
@@ -1,14 +1,14 @@
1
1
  import Papa from 'papaparse'
2
+ import { isSolrCsv, isSolrJson } from '@cdc/core/helpers/isSolr'
2
3
 
3
- export default async function (url, visualizationType = '') {
4
+ export default async function (_url, visualizationType = '') {
5
+ let url = new URL(_url, window.location.origin)
4
6
  try {
5
- url = new URL(url, window.location.origin)
6
-
7
7
  const path = url.pathname
8
8
  const regex = /(?:\.([^.]+))?$/
9
9
  const ext = regex.exec(path)[1]
10
10
 
11
- if ('csv' === ext) {
11
+ if ('csv' === ext || isSolrCsv(_url)) {
12
12
  return await fetch(url.href)
13
13
  .then(response => response.text())
14
14
  .then(responseText => {
@@ -30,7 +30,7 @@ export default async function (url, visualizationType = '') {
30
30
  return parsedCsv.data
31
31
  })
32
32
  } else {
33
- return await fetch(url.href).then(response => response.json())
33
+ return await fetch(isSolrCsv(_url) ? _url : url.href).then(response => response.json())
34
34
  }
35
35
  } catch {
36
36
  // If we can't parse it, still attempt to fetch it
@@ -0,0 +1,23 @@
1
+ import { ViewPort } from '../types/ViewPort'
2
+
3
+ export const viewports = {
4
+ lg: 1200,
5
+ md: 992,
6
+ sm: 768,
7
+ xs: 576,
8
+ xxs: 350
9
+ }
10
+
11
+ export default function getViewport(size): ViewPort {
12
+ let result: ViewPort = 'lg'
13
+
14
+ if (size > 1200) return result
15
+
16
+ for (let viewport in viewports) {
17
+ if (size <= viewports[viewport]) {
18
+ result = viewport as ViewPort
19
+ }
20
+ }
21
+
22
+ return result
23
+ }
@@ -0,0 +1,13 @@
1
+ export const isSolrCsv = dataUrl => {
2
+ if (dataUrl.includes('wt=csv')) {
3
+ return true
4
+ }
5
+ return false
6
+ }
7
+
8
+ export const isSolrJson = dataUrl => {
9
+ if (dataUrl?.includes('wt=json')) {
10
+ return true
11
+ }
12
+ return false
13
+ }
@@ -0,0 +1,50 @@
1
+ // This package hooks into the Redux Dev Tools extension
2
+ // It works as a wrapper for any reducer function
3
+
4
+ // Based on: https://github.com/MacKentoch/react-bootstrap-webpack-starter/blob/master/front/src/contexts/withDevTools/index.ts
5
+
6
+ // types
7
+ type DevToolsMessageType = 'DISPATCH' | string
8
+
9
+ type DevToolsMessagePayload = {
10
+ type?: string
11
+ state?: any
12
+ }
13
+
14
+ type DevToolsMessage = {
15
+ type?: DevToolsMessageType
16
+ payload?: DevToolsMessagePayload
17
+ }
18
+
19
+ type Action = {
20
+ type: string
21
+ payload?: any
22
+ }
23
+
24
+ // the only method the devToolsWrapper hooks into is .send()
25
+ // all other type definitions are kept for reference.
26
+ // it's possible there's a DevTools type in the Redux library
27
+ // however this allows us to avoid another dependency as this project
28
+ // doesn't use Redux.
29
+ type DevTools = {
30
+ init: () => void
31
+ connect: () => any
32
+ subscribe: (message: DevToolsMessage) => any
33
+ send: (action: Action, newState: any) => any
34
+ unsubscribe: () => any
35
+ dispatch: (action: Action) => any
36
+ disconnect: () => any
37
+ }
38
+
39
+ // constants
40
+ const withDevTools = typeof window !== 'undefined' && (window as any).__REDUX_DEVTOOLS_EXTENSION__
41
+ const devTools: DevTools = !withDevTools ? null : (window as any).__REDUX_DEVTOOLS_EXTENSION__.connect()
42
+ export const devToolsStore = !withDevTools ? null : devTools
43
+
44
+ export const devToolsWrapper =
45
+ <StateType, ActionTypes>(_reducer: (s: StateType, a: ActionTypes) => StateType) =>
46
+ (state: StateType, action: ActionTypes): StateType => {
47
+ const newState = _reducer(state, action)
48
+ devToolsStore?.send(action as Action, newState)
49
+ return newState
50
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/core",
3
- "version": "4.24.1",
3
+ "version": "4.24.2",
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": "a352a3f74f4b681191e3244061dbb3621f36eec3"
33
+ "gitHead": "edde49c96dee146de5e3a4537880b1bcf4dbee08"
34
34
  }
@@ -31,9 +31,6 @@ div.data-table-heading {
31
31
  }
32
32
 
33
33
  table.horizontal {
34
- tr {
35
- display: flex;
36
- }
37
34
 
38
35
  th,
39
36
  td {
@@ -42,7 +39,7 @@ table.horizontal {
42
39
  }
43
40
 
44
41
  table.data-table {
45
- width: 100%;
42
+ min-width: 100%;
46
43
  background: #fff;
47
44
  position: relative;
48
45
  border: none;
@@ -51,7 +48,6 @@ table.data-table {
51
48
  overflow: auto;
52
49
  appearance: none;
53
50
  table-layout: fixed;
54
- display: grid;
55
51
  * {
56
52
  box-sizing: border-box;
57
53
  }
@@ -138,7 +134,6 @@ table.data-table {
138
134
  }
139
135
 
140
136
  tbody {
141
- display: table-cell;
142
137
  tr {
143
138
  width: 100%;
144
139
  &:hover {
@@ -162,6 +157,7 @@ table.data-table {
162
157
 
163
158
  th,
164
159
  td {
160
+ width: 1% !important;
165
161
  white-space: nowrap;
166
162
  text-overflow: ellipsis;
167
163
  overflow: hidden;
@@ -170,7 +166,6 @@ table.data-table {
170
166
  }
171
167
  }
172
168
  tr {
173
- display: flex;
174
169
  & > * {
175
170
  width: 100%;
176
171
  }
@@ -0,0 +1,75 @@
1
+ $colors: (
2
+ 'baseColor': #333,
3
+ 'blue': #005eaa,
4
+ 'lightestGray': #f2f2f2,
5
+ 'lightGray': #c7c7c7,
6
+ 'mediumGray': #565656,
7
+ 'darkGray': #333,
8
+ 'red': #d8000c,
9
+ 'white': #fff,
10
+
11
+ 'primary': #005eaa,
12
+ 'secondary': #88c3ea,
13
+ 'tertiary': #c0e9ff,
14
+ 'quaternary': #edf9ff,
15
+
16
+ 'purple-primary': #712177,
17
+ 'purple-secondary': #b890bb,
18
+ 'purple-tertiary': #e3d3e4,
19
+ 'purple-quaternary': #f7f2f7,
20
+
21
+ 'brown-primary': #705043,
22
+ 'brown-secondary': #ad907b,
23
+ 'brown-tertiary': #d7ccc8,
24
+ 'brown-quaternary': #f2ebe8,
25
+
26
+ 'teal-primary': #00695c,
27
+ 'teal-secondary': #4ebaaa,
28
+ 'teal-tertiary': #ceece7,
29
+ 'teal-quaternary': #ebf7f5,
30
+
31
+ 'pink-primary': #af4448,
32
+ 'pink-secondary': #e57373,
33
+ 'pink-tertiary': #ffc2c2,
34
+ 'pink-quaternary': #ffe7e7,
35
+
36
+ 'orange-primary': #bb4d00,
37
+ 'orange-secondary': #ffad42,
38
+ 'orange-tertiary': #ffe97d,
39
+ 'orange-quaternary': #fff4cf,
40
+
41
+ 'slate-primary': #29434e,
42
+ 'slate-secondary': #7e9ba5,
43
+ 'slate-tertiary': #b6c6d2,
44
+ 'slate-quaternary': #e2e8ed,
45
+
46
+ 'indigo-primary': #26418f,
47
+ 'indigo-secondary': #92a6dd,
48
+ 'indigo-tertiary': #dee8ff,
49
+ 'indigo-quaternary': #f2f6ff,
50
+
51
+ 'cyan-primary': #006778,
52
+ 'cyan-secondary': #65b0bd,
53
+ 'cyan-tertiary': #cce5e9,
54
+ 'cyan-quaternary': #ebf5f6,
55
+
56
+ 'green-primary': #4b830d,
57
+ 'green-secondary': #84bc49,
58
+ 'green-tertiary': #dcedc8,
59
+ 'green-quaternary': #f1f8e9,
60
+
61
+ 'amber-primary': #fbab18,
62
+ 'amber-secondary': #ffd54f,
63
+ 'amber-tertiary': #ffecb3,
64
+ 'amber-quaternary': #fff7e1,
65
+ );
66
+
67
+ @mixin theme() {
68
+ :root {
69
+ @each $key, $value in $colors {
70
+ --#{$key}: #{$value};
71
+ }
72
+ }
73
+ }
74
+
75
+ @include theme();
package/styles/base.scss CHANGED
@@ -51,6 +51,7 @@
51
51
  // Imports
52
52
  @import 'reset';
53
53
  @import 'variables';
54
+ @import 'global-variables';
54
55
  @import 'mixins';
55
56
  @import 'filters';
56
57
 
@@ -58,6 +59,10 @@ body.post-type-cdc_visualization .visx-tooltip {
58
59
  z-index: 1000000;
59
60
  }
60
61
 
62
+ body.post-type-cdc_visualization .cdc-editor .configure .editor-panel {
63
+ top: 0 !important;
64
+ }
65
+
61
66
  .cdc-open-viz-module {
62
67
  position: relative;
63
68
  color: $baseColor;
@@ -71,73 +76,88 @@ body.post-type-cdc_visualization .visx-tooltip {
71
76
  @import 'button-section';
72
77
  @import 'series-list';
73
78
  @import 'typography';
74
- }
75
79
 
76
- $theme: (
77
- 'amber': (
78
- '#fbab18',
79
- '#ffd54f',
80
- '#ffecb3',
81
- '#fff7e1'
82
- ),
83
- 'blue': (
84
- '#005eaa',
85
- '#88c3ea',
86
- '#c0e9ff',
87
- '#edf9ff'
88
- ),
89
- 'brown': (
90
- '#705043',
91
- '#ad907b',
92
- '#d7ccc8',
93
- '#f2ebe8'
94
- ),
95
- 'cyan': (
96
- '#006778',
97
- '#65b0bd',
98
- '#cce5e9',
99
- '#ebf5f6'
100
- ),
101
- 'green': (
102
- '#4b830d',
103
- '#84bc49',
104
- '#dcedc8',
105
- '#f1f8e9'
106
- ),
107
- 'indigo': (
108
- '#26418f',
109
- '#92a6dd',
110
- '#dee8ff',
111
- '#f2f6ff'
112
- ),
113
- 'orange': (
114
- '#bb4d00',
115
- '#ffad42',
116
- '#ffe97d',
117
- '#fff4cf'
118
- ),
119
- 'pink': (
120
- '#af4448',
121
- '#e57373',
122
- '#ffc2c2',
123
- '#ffe7e7'
124
- ),
125
- 'purple': (
126
- '#712177',
127
- '#b890bb',
128
- '#e3d3e4',
129
- '#f7f2f7'
130
- ),
131
- 'slate': (
132
- '#29434e',
133
- '#7e9ba5',
134
- '#b6c6d2',
135
- '#e2e8ed'
136
- ),
137
- 'teal': (
138
- '#00695c',
139
- '#4ebaaa',
140
- '#ceece7',
141
- '#ebf7f5'
142
- )
143
- );
80
+ .editor-panel.cove {
81
+ position: absolute;
82
+ height: 100vh;
83
+ top: 0;
84
+ .editor-toggle {
85
+ position: absolute !important;
86
+ top: 10px !important;
87
+ }
88
+
89
+ .editor-panel {
90
+ position: absolute !important;
91
+ top: 0 !important;
92
+ }
93
+ }
94
+
95
+ $theme: (
96
+ 'amber': (
97
+ '#fbab18',
98
+ '#ffd54f',
99
+ '#ffecb3',
100
+ '#fff7e1'
101
+ ),
102
+ 'blue': (
103
+ '#005eaa',
104
+ '#88c3ea',
105
+ '#c0e9ff',
106
+ '#edf9ff'
107
+ ),
108
+ 'brown': (
109
+ '#705043',
110
+ '#ad907b',
111
+ '#d7ccc8',
112
+ '#f2ebe8'
113
+ ),
114
+ 'cyan': (
115
+ '#006778',
116
+ '#65b0bd',
117
+ '#cce5e9',
118
+ '#ebf5f6'
119
+ ),
120
+ 'green': (
121
+ '#4b830d',
122
+ '#84bc49',
123
+ '#dcedc8',
124
+ '#f1f8e9'
125
+ ),
126
+ 'indigo': (
127
+ '#26418f',
128
+ '#92a6dd',
129
+ '#dee8ff',
130
+ '#f2f6ff'
131
+ ),
132
+ 'orange': (
133
+ '#bb4d00',
134
+ '#ffad42',
135
+ '#ffe97d',
136
+ '#fff4cf'
137
+ ),
138
+ 'pink': (
139
+ '#af4448',
140
+ '#e57373',
141
+ '#ffc2c2',
142
+ '#ffe7e7'
143
+ ),
144
+ 'purple': (
145
+ '#712177',
146
+ '#b890bb',
147
+ '#e3d3e4',
148
+ '#f7f2f7'
149
+ ),
150
+ 'slate': (
151
+ '#29434e',
152
+ '#7e9ba5',
153
+ '#b6c6d2',
154
+ '#e2e8ed'
155
+ ),
156
+ 'teal': (
157
+ '#00695c',
158
+ '#4ebaaa',
159
+ '#ceece7',
160
+ '#ebf7f5'
161
+ )
162
+ );
163
+ }
@@ -0,0 +1 @@
1
+ export type Action<T, P> = { type: T; payload?: P }
package/types/Axis.ts CHANGED
@@ -5,6 +5,7 @@ export type Anchor = {
5
5
  }
6
6
 
7
7
  export type Axis = {
8
+ scalePadding: number
8
9
  anchors?: Anchor[]
9
10
  dataKey: string
10
11
  dateDisplayFormat: string
@@ -31,6 +32,7 @@ export type Axis = {
31
32
  rightMax?: string
32
33
  rightNumTicks?: number
33
34
  sortDates?: boolean
35
+ sortKey?: string
34
36
  showTargetLabel?: boolean
35
37
  size?: number
36
38
  target?: number
@@ -0,0 +1,21 @@
1
+ export type BoxPlot = {
2
+ tableData: Object[]
3
+ labels: {
4
+ mean: string
5
+ maximum: string
6
+ minimum: string
7
+ iqr: string
8
+ median: string
9
+ q1: string
10
+ q3: string
11
+ outliers: string
12
+ values: string
13
+ total: string
14
+ lowerBounds: string
15
+ upperBounds: string
16
+ }
17
+ plots: []
18
+ categories: string[]
19
+ firstQuartilePercentage: number
20
+ geoType: string
21
+ }
package/types/Column.ts CHANGED
@@ -6,6 +6,7 @@ export type Column = {
6
6
  roundToPlace: number
7
7
  commas: boolean
8
8
  dataTable: boolean
9
+ showInViz: boolean
9
10
  startingPoint: string
10
11
  series?: string
11
12
  tooltips: boolean
@@ -0,0 +1,9 @@
1
+ import { BoxPlot } from './BoxPlot'
2
+
3
+ export type General = {
4
+ boxplot: BoxPlot
5
+ geoType: string
6
+ type: string
7
+ showDownloadButton: boolean
8
+ allowMapZoom?: boolean
9
+ }
package/types/Runtime.ts CHANGED
@@ -23,7 +23,7 @@ export type Runtime = {
23
23
  xAxis: Axis
24
24
  yAxis: Axis
25
25
  seriesKeys: any[]
26
- seriesLabels: string[]
26
+ seriesLabels: Record<string, any>
27
27
  seriesLabelsAll: string[]
28
28
  editorErrorMessage: string
29
29
  }
package/types/Table.ts CHANGED
@@ -11,8 +11,11 @@ export type Table = {
11
11
  showDataTableLink?: boolean
12
12
  showDownloadUrl?: boolean
13
13
  download?: boolean
14
- showDownloadImgButton?: boolean
15
- showDownloadPdfButton?: boolean
16
14
  customTableConfig?: boolean
17
15
  excludeColumns?: string[]
16
+ showDownloadImgButton?: boolean
17
+ showDownloadPdfButton?: boolean
18
+ downloadImageButton?: boolean
19
+ downloadPdfButton?: boolean
20
+ dateDisplayFormat: string
18
21
  }
@@ -0,0 +1,2 @@
1
+ import { viewports } from '../helpers/getViewport'
2
+ export type ViewPort = keyof typeof viewports
@@ -2,20 +2,26 @@ export type Visualization = {
2
2
  autoLoad: boolean
3
3
  data: any
4
4
  dataDescription: Object
5
+ dataFileName: string
6
+ dataFileSourceType: string
5
7
  dataKey: string
8
+ datasets: Record<string, any>
6
9
  editing: boolean
7
- formattedData: any
10
+ formattedData?: Object[]
8
11
  general: any
9
12
  hide: any[]
13
+ multiDashboards?: any[]
10
14
  newViz: boolean
11
15
  openModal: boolean
12
16
  originalFormattedData: any
17
+ orientation: 'vertical' | 'horizontal'
13
18
  table: {
14
19
  showDataTableLink: boolean
15
20
  }
16
21
  title: string
17
- type: 'chart' | 'map' | 'data-bite' | 'waffle-chart' | 'markup-include' | 'filtered-text' | 'filter-dropdowns'
22
+ type: 'dashboard' | 'chart' | 'map' | 'data-bite' | 'waffle-chart' | 'markup-include' | 'filtered-text' | 'filter-dropdowns'
18
23
  uid: string
19
24
  usesSharedFilter: any
20
25
  visualizationType: string
26
+ visualizationSubType: string
21
27
  }
@@ -0,0 +1,11 @@
1
+ import { ReactElement } from 'react'
2
+
3
+ export type WCMSProps = {
4
+ config?: any
5
+ configUrl?: string
6
+ isEditor?: boolean
7
+ isDebug?: boolean
8
+ hostname?: string
9
+ containerEl?: ReactElement
10
+ sharepath?: string
11
+ }
@@ -1,21 +0,0 @@
1
- export default function getViewport(size) {
2
- let result = 'lg'
3
-
4
- const viewports = {
5
- lg: 1200,
6
- md: 992,
7
- sm: 768,
8
- xs: 576,
9
- xxs: 350
10
- }
11
-
12
- if (size > 1200) return result
13
-
14
- for (let viewport in viewports) {
15
- if (size <= viewports[viewport]) {
16
- result = viewport
17
- }
18
- }
19
-
20
- return result
21
- }