@cdc/core 4.23.5 → 4.23.7

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.
@@ -11,109 +11,109 @@ export class DataTransform {
11
11
 
12
12
  //Performs standardizations that can be completed automatically without use input
13
13
  autoStandardize(data) {
14
- const errorsFound = [];
14
+ const errorsFound = []
15
15
 
16
16
  // Empty data
17
17
  if (0 === data.length) {
18
- errorsFound.push(this.constants.errorMessageEmptyData);
18
+ errorsFound.push(this.constants.errorMessageEmptyData)
19
19
  }
20
20
 
21
21
  // Does it have the correct data structure?
22
22
  if (!data.filter || data.filter(row => typeof row !== 'object').length > 0) {
23
- errorsFound.push(this.constants.errorMessageFormat);
23
+ errorsFound.push(this.constants.errorMessageFormat)
24
24
  }
25
25
 
26
26
  if (errorsFound.length > 0) {
27
- console.error(errorsFound);
28
- return undefined;
27
+ console.error(errorsFound)
28
+ return undefined
29
29
  }
30
30
 
31
31
  //Convert array of arrays, to array of objects
32
32
  if (data.filter(row => row.constructor !== Object).length > 0) {
33
- let standardizedData = [];
33
+ let standardizedData = []
34
34
  for (let row = 1; row < data.length; row++) {
35
- let standardizedRow = {};
35
+ let standardizedRow = {}
36
36
  data[row].forEach((datum, col) => {
37
- standardizedRow[data[0][col]] = datum;
37
+ standardizedRow[data[0][col]] = datum
38
38
  })
39
- standardizedData.push(standardizedRow);
39
+ standardizedData.push(standardizedRow)
40
40
  }
41
- data = standardizedData;
41
+ data = standardizedData
42
42
  }
43
43
 
44
- return data;
44
+ return data
45
45
  }
46
46
 
47
47
  //Performs standardizations based on developer provided description of the data
48
48
  developerStandardize(data, description) {
49
49
  //Validate the description object
50
50
  if (!description) {
51
- return undefined;
51
+ return undefined
52
52
  }
53
53
 
54
54
  if (description.horizontal === undefined || description.series === undefined) {
55
- return undefined;
55
+ return undefined
56
56
  }
57
57
 
58
58
  if (description.series === true && description.horizontal === false && description.singleRow === undefined) {
59
- return undefined;
59
+ return undefined
60
60
  }
61
61
 
62
62
  if (description.horizontal === true) {
63
63
  if (description.series === true) {
64
64
  if (!description.seriesKey) {
65
- return undefined;
65
+ return undefined
66
66
  }
67
67
 
68
- let standardizedMapped = {};
69
- let standardized = [];
68
+ let standardizedMapped = {}
69
+ let standardized = []
70
70
  data.forEach(row => {
71
- let nonNumericKeys = [];
71
+ let nonNumericKeys = []
72
72
  Object.keys(row).forEach(key => {
73
73
  if (key !== description.seriesKey && isNaN(parseFloat(row[key]))) {
74
- nonNumericKeys.push(key);
74
+ nonNumericKeys.push(key)
75
75
  }
76
76
  })
77
77
 
78
78
  Object.keys(row).forEach(key => {
79
79
  if (key !== description.seriesKey && nonNumericKeys.indexOf(key) === -1) {
80
- let uniqueKey = key + '|' + nonNumericKeys.map(nonNumericKey => nonNumericKey + '=' + row[nonNumericKey]);
80
+ let uniqueKey = key + '|' + nonNumericKeys.map(nonNumericKey => nonNumericKey + '=' + row[nonNumericKey])
81
81
  if (!standardizedMapped[uniqueKey]) {
82
- standardizedMapped[uniqueKey] = { [row[description.seriesKey]]: row[key], key };
82
+ standardizedMapped[uniqueKey] = { [row[description.seriesKey]]: row[key], key }
83
83
  nonNumericKeys.forEach(nonNumericKey => {
84
- standardizedMapped[uniqueKey][nonNumericKey] = row[nonNumericKey];
84
+ standardizedMapped[uniqueKey][nonNumericKey] = row[nonNumericKey]
85
85
  })
86
86
  }
87
- standardizedMapped[uniqueKey][row[description.seriesKey]] = row[key];
87
+ standardizedMapped[uniqueKey][row[description.seriesKey]] = row[key]
88
88
  }
89
89
  })
90
90
  })
91
91
 
92
92
  Object.keys(standardizedMapped).forEach(key => {
93
- standardized.push(standardizedMapped[key]);
93
+ standardized.push(standardizedMapped[key])
94
94
  })
95
95
 
96
- return standardized;
96
+ return standardized
97
97
  } else {
98
- let standardized = [];
98
+ let standardized = []
99
99
 
100
100
  data.forEach(row => {
101
- let nonNumericKeys = [];
101
+ let nonNumericKeys = []
102
102
  Object.keys(row).forEach(key => {
103
103
  if (isNaN(parseFloat(row[key]))) {
104
- nonNumericKeys.push(key);
104
+ nonNumericKeys.push(key)
105
105
  }
106
106
  })
107
107
 
108
108
  Object.keys(row).forEach(key => {
109
109
  if (nonNumericKeys.indexOf(key) === -1) {
110
- let newRow = { key, value: row[key] };
110
+ let newRow = { key, value: row[key] }
111
111
 
112
112
  nonNumericKeys.forEach(nonNumericKey => {
113
- newRow[nonNumericKey] = row[nonNumericKey];
113
+ newRow[nonNumericKey] = row[nonNumericKey]
114
114
  })
115
115
 
116
- standardized.push(newRow);
116
+ standardized.push(newRow)
117
117
  }
118
118
  })
119
119
  })
@@ -122,43 +122,43 @@ export class DataTransform {
122
122
  }
123
123
  } else if (description.series === true && description.singleRow === false) {
124
124
  if (description.seriesKey !== undefined && description.xKey !== undefined && (description.valueKey !== undefined || (description.valueKeys !== undefined && description.valueKeys.length > 0))) {
125
- if(description.valueKeys !== undefined){
126
- let standardizedMapped = {};
127
- let standardized = [];
128
- let valueKeys = description.valueKeys;
129
- if(description.ignoredKeys && description.ignoredKeys.length > 0){
130
- valueKeys = valueKeys.concat(description.ignoredKeys);
125
+ if (description.valueKeys !== undefined) {
126
+ let standardizedMapped = {}
127
+ let standardized = []
128
+ let valueKeys = description.valueKeys
129
+ if (description.ignoredKeys && description.ignoredKeys.length > 0) {
130
+ valueKeys = valueKeys.concat(description.ignoredKeys)
131
131
  }
132
132
 
133
133
  data.forEach(row => {
134
134
  valueKeys.forEach(valueKey => {
135
- let extraKeys = [];
136
- let uniqueKey = row[description.xKey] + '|' + valueKey;
135
+ let extraKeys = []
136
+ let uniqueKey = row[description.xKey] + '|' + valueKey
137
137
  Object.keys(row).forEach(key => {
138
138
  if (key !== description.xKey && key !== description.seriesKey && valueKeys.indexOf(key) === -1) {
139
- uniqueKey += '|' + key + '=' + row[key];
140
- extraKeys.push(key);
139
+ uniqueKey += '|' + key + '=' + row[key]
140
+ extraKeys.push(key)
141
141
  }
142
142
  })
143
143
 
144
- if(!standardizedMapped[uniqueKey]){
145
- standardizedMapped[uniqueKey] = { [description.xKey]: row[description.xKey], '**Numeric Value Property**': valueKey };
144
+ if (!standardizedMapped[uniqueKey]) {
145
+ standardizedMapped[uniqueKey] = { [description.xKey]: row[description.xKey], '**Numeric Value Property**': valueKey }
146
146
  extraKeys.forEach(key => {
147
- standardizedMapped[uniqueKey][key] = row[key];
147
+ standardizedMapped[uniqueKey][key] = row[key]
148
148
  })
149
149
  }
150
150
 
151
- standardizedMapped[uniqueKey][row[description.seriesKey]] = row[valueKey];
152
- });
151
+ standardizedMapped[uniqueKey][row[description.seriesKey]] = row[valueKey]
152
+ })
153
153
  })
154
154
 
155
155
  Object.keys(standardizedMapped).forEach(key => {
156
- if(!description.ignoredKeys || description.ignoredKeys.indexOf(standardizedMapped[key]['**Numeric Value Property**']) === -1){
157
- standardized.push(standardizedMapped[key]);
156
+ if (!description.ignoredKeys || description.ignoredKeys.indexOf(standardizedMapped[key]['**Numeric Value Property**']) === -1) {
157
+ standardized.push(standardizedMapped[key])
158
158
  }
159
159
  })
160
160
 
161
- return standardized;
161
+ return standardized
162
162
  } else {
163
163
  let standardizedMapped = {}
164
164
  let standardized = []
@@ -190,14 +190,14 @@ export class DataTransform {
190
190
  return standardized
191
191
  }
192
192
  } else {
193
- return undefined;
193
+ return undefined
194
194
  }
195
195
  }
196
196
 
197
- return data;
197
+ return data
198
198
  }
199
199
 
200
- /**
200
+ /**
201
201
  * cleanData
202
202
  *
203
203
  // This cleans a data set by:
@@ -207,21 +207,21 @@ export class DataTransform {
207
207
  *
208
208
  * Inputs: data as array, excludeKey indicates which key to use to NOT clean
209
209
  * Example: "Date" should not be cleaned if part of the data
210
- *
210
+ *
211
211
  * Output: returns the cleanedData
212
- *
212
+ *
213
213
  * Set testing = true if you need to see before and after data
214
- *
214
+ *
215
215
  */
216
- cleanData (data, excludeKey, testing = false) {
216
+ cleanData(data, excludeKey, testing = false) {
217
217
  let cleanedupData = []
218
218
  if (testing) console.log('## Data to clean=', data)
219
219
  if (excludeKey === undefined) {
220
220
  console.log('COVE: cleanData excludeKey undefined')
221
- return data // because no excludeKey
221
+ return data // because no excludeKey
222
222
  }
223
223
  data.forEach(function (d, i) {
224
- if (testing) console.log("clean", i, " d", d);
224
+ if (testing) console.log('clean', i, ' d', d)
225
225
  let cleaned = {}
226
226
  Object.keys(d).forEach(function (key) {
227
227
  if (key === excludeKey) {
@@ -229,27 +229,27 @@ export class DataTransform {
229
229
  cleaned[key] = d[key]
230
230
  } else {
231
231
  // remove comma and dollar signs
232
- if (testing) console.log("typeof d[key] is ", typeof d[key]);
233
- let tmp = "";
232
+ if (testing) console.log('typeof d[key] is ', typeof d[key])
233
+ let tmp = ''
234
234
  if (typeof d[key] === 'string') {
235
235
  tmp = d[key] !== null && d[key] !== '' ? d[key].replace(/[,\$]/g, '') : ''
236
236
  } else {
237
- tmp = d[key] !== null && d[key] !== '' ? d[key] : ''
237
+ tmp = d[key] !== null && d[key] !== '' ? d[key] : ''
238
238
  }
239
239
  if ((tmp !== '' && tmp !== null && !isNaN(tmp)) || (tmp !== '' && tmp !== null && /\d+\.?\d*/.test(tmp))) {
240
240
  cleaned[key] = tmp
241
- } else { cleaned[key] = '' }
241
+ } else {
242
+ cleaned[key] = ''
243
+ }
242
244
  // if you get here, then return nothing to skip bad data point
243
245
  }
244
246
  })
245
- if (testing) console.log("cleaned=", cleaned)
247
+ if (testing) console.log('cleaned=', cleaned)
246
248
  cleanedupData.push(cleaned)
247
249
  })
248
250
  if (testing) console.log('## cleanedData =', cleanedupData)
249
251
  return cleanedupData
250
252
  }
251
-
252
-
253
253
  }
254
254
 
255
255
  export default DataTransform
@@ -0,0 +1,9 @@
1
+ import { timeFormat, timeParse } from 'd3-time-format'
2
+
3
+ export function formatDate(format = undefined, date) {
4
+ return timeFormat(format)(date)
5
+ }
6
+
7
+ export function parseDate(format = undefined, dateString) {
8
+ return timeParse(format)(dateString) || new Date()
9
+ }
@@ -0,0 +1,166 @@
1
+ import numberFromString from '@cdc/core/helpers/numberFromString'
2
+
3
+ const abbreviateNumber = num => {
4
+ let unit = ''
5
+ let absNum = Math.abs(num)
6
+
7
+ if (absNum >= 1e9) {
8
+ unit = 'B'
9
+ num = num / 1e9
10
+ } else if (absNum >= 1e6) {
11
+ unit = 'M'
12
+ num = num / 1e6
13
+ } else if (absNum >= 1e3) {
14
+ unit = 'K'
15
+ num = num / 1e3
16
+ }
17
+
18
+ return num + unit
19
+ }
20
+
21
+ // Format numeric data based on settings in config
22
+ const formatNumber = (num, axis, shouldAbbreviate = false, config = null, addColParams = null) => {
23
+ if (!config) console.error('no config found in formatNumber')
24
+ // if num is NaN return num
25
+ if (isNaN(num) || !num) return num
26
+ // Check if the input number is negative
27
+ const isNegative = num < 0
28
+
29
+ // If the input number is negative, take the absolute value
30
+ if (isNegative) {
31
+ num = Math.abs(num)
32
+ }
33
+
34
+ // destructure dataFormat values
35
+ let {
36
+ dataFormat: { commas, abbreviated, roundTo, prefix, suffix, rightRoundTo, bottomRoundTo, rightPrefix, rightSuffix, bottomPrefix, bottomSuffix, bottomAbbreviated }
37
+ } = config
38
+
39
+ // destructure Additional Col dataformat values
40
+ const { addColCommas, addColRoundTo, addColPrefix, addColSuffix } = addColParams
41
+
42
+ // check if value contains comma and remove it. later will add comma below.
43
+ if (String(num).indexOf(',') !== -1) num = num.replaceAll(',', '')
44
+
45
+ let original = num
46
+ let stringFormattingOptions
47
+ if (axis === 'left') {
48
+ let roundToPlace
49
+ if (addColRoundTo !== undefined) {
50
+ // if its an Additional Column
51
+ roundToPlace = addColRoundTo ? Number(addColRoundTo) : 0
52
+ } else {
53
+ roundToPlace = roundTo ? Number(roundTo) : 0
54
+ }
55
+ // Need to prevent negative values in rounding
56
+ if (roundToPlace < 0) roundToPlace = 0
57
+ let useCommas
58
+ if (addColCommas !== undefined) {
59
+ // if its an Additional Column
60
+ useCommas = addColCommas ? true : false
61
+ } else {
62
+ useCommas = config.dataFormat.commas ? true : false
63
+ }
64
+ stringFormattingOptions = {
65
+ useGrouping: useCommas,
66
+ minimumFractionDigits: roundToPlace,
67
+ maximumFractionDigits: roundToPlace
68
+ }
69
+ }
70
+
71
+ if (axis === 'right') {
72
+ stringFormattingOptions = {
73
+ useGrouping: config.dataFormat.rightCommas ? true : false,
74
+ minimumFractionDigits: rightRoundTo ? Number(rightRoundTo) : 0,
75
+ maximumFractionDigits: rightRoundTo ? Number(rightRoundTo) : 0
76
+ }
77
+ }
78
+
79
+ if (axis === 'bottom') {
80
+ stringFormattingOptions = {
81
+ useGrouping: config.dataFormat.bottomCommas ? true : false,
82
+ minimumFractionDigits: bottomRoundTo ? Number(bottomRoundTo) : 0,
83
+ maximumFractionDigits: bottomRoundTo ? Number(bottomRoundTo) : 0
84
+ }
85
+ }
86
+
87
+ num = numberFromString(num)
88
+
89
+ if (isNaN(num)) {
90
+ config.runtime.editorErrorMessage = `Unable to parse number from data ${original}. Try reviewing your data and selections in the Data Series section.`
91
+ return original
92
+ }
93
+
94
+ if (!config.dataFormat) return num
95
+ if (config.dataCutoff) {
96
+ let cutoff = numberFromString(config.dataCutoff)
97
+
98
+ if (num < cutoff) {
99
+ num = cutoff
100
+ }
101
+ }
102
+
103
+ // When we're formatting the left axis
104
+ // Use commas also updates bars and the data table
105
+ // We can't use commas when we're formatting the dataFormatted number
106
+ // Example: commas -> 12,000; abbreviated -> 12k (correct); abbreviated & commas -> 12 (incorrect)
107
+ //
108
+ // Edge case for small numbers with decimals
109
+ // - if roundTo undefined which means it is blank, then do not round
110
+
111
+ if ((axis === 'left' && commas && abbreviated && shouldAbbreviate) || (axis === 'bottom' && commas && abbreviated && shouldAbbreviate)) {
112
+ num = num // eslint-disable-line
113
+ } else {
114
+ num = num.toLocaleString('en-US', stringFormattingOptions)
115
+ }
116
+ let result = ''
117
+
118
+ if (abbreviated && axis === 'left' && shouldAbbreviate) {
119
+ num = abbreviateNumber(parseFloat(num))
120
+ }
121
+
122
+ if (bottomAbbreviated && axis === 'bottom' && shouldAbbreviate) {
123
+ num = abbreviateNumber(parseFloat(num))
124
+ }
125
+
126
+ if (addColPrefix !== undefined && axis === 'left') {
127
+ result = addColPrefix + result
128
+ } else {
129
+ if (prefix && axis === 'left') {
130
+ result = prefix + result
131
+ }
132
+ }
133
+
134
+ if (rightPrefix && axis === 'right') {
135
+ result += rightPrefix
136
+ }
137
+
138
+ if (bottomPrefix && axis === 'bottom') {
139
+ result += bottomPrefix
140
+ }
141
+
142
+ result += num
143
+
144
+ if (addColSuffix !== undefined && axis === 'left') {
145
+ result += addColSuffix
146
+ } else {
147
+ if (suffix && axis === 'left') {
148
+ result += suffix
149
+ }
150
+ }
151
+
152
+ if (rightSuffix && axis === 'right') {
153
+ result += rightSuffix
154
+ }
155
+
156
+ if (bottomSuffix && axis === 'bottom') {
157
+ result += bottomSuffix
158
+ }
159
+ if (isNegative) {
160
+ result = '-' + result
161
+ }
162
+
163
+ return String(result)
164
+ }
165
+
166
+ export { formatNumber }
@@ -0,0 +1,15 @@
1
+ // If config key names or position in the config have been changed with a version change,
2
+ // process those config entries and format old values into new
3
+ import update_4_23 from './ver/4.23'
4
+
5
+ // 4.23.6 ------------------------------------------------------
6
+ const coveUpdateWorker = async config => {
7
+ let genConfig = config
8
+
9
+ // v4.23
10
+ genConfig = await update_4_23(genConfig)
11
+
12
+ return genConfig
13
+ }
14
+
15
+ export default coveUpdateWorker
@@ -14,10 +14,20 @@ export default async function (url, visualizationType = '') {
14
14
  data = await fetch(url.href)
15
15
  .then(response => response.text())
16
16
  .then(responseText => {
17
+ // for every comma NOT inside quotes, replace with a pipe delimiter
18
+ // - this will let commas inside the quotes not be parsed as a new column
19
+ // - Limitation: if a delimiter other than comma is used in the csv this will break
20
+ // Examples of other delimiters that would break: tab
21
+ responseText = responseText.replace(/(".*?")|,/g, (...m) => m[1] || '|')
22
+ // now strip the double quotes
23
+ responseText = responseText.replace(/["]+/g, '')
17
24
  const parsedCsv = Papa.parse(responseText, {
25
+ //quotes: "true", // dont need these
26
+ //quoteChar: "'", // has no effect that I can tell
18
27
  header: true,
19
- dynamicTyping: true,
20
- skipEmptyLines: true
28
+ skipEmptyLines: true,
29
+ delimiter: '|', // we are using pipe symbol as delimiter so setting this explicitly for Papa.parse
30
+ dynamicTyping: false
21
31
  })
22
32
  return parsedCsv.data
23
33
  })
@@ -0,0 +1,10 @@
1
+ const update_4_23 = async config => {
2
+ const ver = 4.23
3
+
4
+ let newConfig = { ...config }
5
+
6
+ newConfig.validated = ver
7
+ return newConfig
8
+ }
9
+
10
+ export default update_4_23
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/core",
3
- "version": "4.23.5",
3
+ "version": "4.23.7",
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": "34add3436994ca3cf13e51f313add4d70377f53e"
33
+ "gitHead": "6c7ac5215dcf3bc1cc7d199089c8c2e75f53a93e"
34
34
  }
@@ -2,9 +2,9 @@
2
2
  display: block;
3
3
  position: relative;
4
4
  padding: 0.5em 0.5em;
5
- border-width: 1px;
6
- border-style: solid;
7
- border-color: #cbcbcb;
5
+ border-width: 1px !important; // intentional use of !important for CDC
6
+ border-style: solid !important; // intentional use of !important for CDC
7
+ border-color: #cbcbcb !important; // intentional use of !important for CDC
8
8
  border-radius: 0.1875rem;
9
9
  transition: border-color 200ms $transition-expo-out;
10
10
  font-family: sans-serif;