Skip to content

Commit 695c5ae

Browse files
feat: generate and save colorMapping for cells that do not have it (#3495)
1 parent 75f4c0f commit 695c5ae

File tree

5 files changed

+113
-58
lines changed

5 files changed

+113
-58
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
"codemirror": "^5.58.2",
174174
"connected-react-router": "^6.8.0",
175175
"d3-dsv": "^3.0.1",
176+
"deep-object-diff": "^1.1.0",
176177
"fix-date": "^1.1.6",
177178
"history": "^4.7.2",
178179
"honeybadger-js": "^1.0.2",

src/visualization/types/Graph/view.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
2828
import {INVALID_DATA_COPY} from 'src/visualization/constants'
2929

3030
// Types
31-
import {XYViewProperties} from 'src/types'
31+
import {AppState, ResourceType, View, XYViewProperties} from 'src/types'
3232
import {VisualizationProps} from 'src/visualization'
3333

3434
// Utils
@@ -54,6 +54,10 @@ import {addAnnotationLayer} from 'src/visualization/utils/annotationUtils'
5454
import {getColorMappingObjects} from 'src/visualization/utils/colorMappingUtils'
5555
import {isFlagEnabled} from '../../../shared/utils/featureFlag'
5656

57+
// Selectors
58+
import {getByID} from 'src/resources/selectors'
59+
import {updateViewAndVariables} from 'src/views/actions/thunks'
60+
5761
interface Props extends VisualizationProps {
5862
properties: XYViewProperties
5963
}
@@ -73,6 +77,13 @@ const XYPlot: FC<Props> = ({
7377
const tooltipOrientationThreshold = properties.legendOrientationThreshold
7478
const staticLegend = useStaticLegend(properties)
7579
const dispatch = useDispatch()
80+
const view = useSelector((state: AppState) =>
81+
getByID<View<XYViewProperties>>(state, ResourceType.Views, cellID)
82+
)
83+
const {activeTimeMachineID} = useSelector(
84+
(state: AppState) => state.timeMachines
85+
)
86+
const isVeoOpen = activeTimeMachineID === 'veo'
7687

7788
// these two values are set in the dashboard, and used whether or not this view
7889
// is in a dashboard or in configuration/single cell popout mode
@@ -186,11 +197,22 @@ const XYPlot: FC<Props> = ({
186197

187198
if (isFlagEnabled('graphColorMapping')) {
188199
const [, fillColumnMap] = createGroupIDColumn(result.table, groupKey)
189-
const {colorMappingForGiraffe} = getColorMappingObjects(
190-
fillColumnMap,
191-
properties
192-
)
200+
const {
201+
colorMappingForGiraffe,
202+
colorMappingForIDPE,
203+
needsToSaveToIDPE,
204+
} = getColorMappingObjects(fillColumnMap, properties)
193205
colorMapping = colorMappingForGiraffe
206+
207+
// when the view is in a dashboard cell, and there is a need to save to IDPE, save it.
208+
// when VEO is open, prevent from saving because it causes state issues. It will be handled in the timemachine code separately.
209+
if (needsToSaveToIDPE && view?.dashboardID && !isVeoOpen) {
210+
const newView = {...view}
211+
newView.properties.colorMapping = colorMappingForIDPE
212+
213+
// save to IDPE
214+
dispatch(updateViewAndVariables(view.dashboardID, newView))
215+
}
194216
}
195217

196218
const config: Config = {

src/visualization/utils/colorMappingUtils.test.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,9 @@ const dataFromIDPE_sameColorMapping = {
134134
},
135135
],
136136
colorMapping: {
137-
'co-airSensors-TLM0100-mean-': 0,
138-
'co-airSensors-TLM0101-mean-': 1,
139-
'co-airSensors-TLM0102-mean-': 2,
137+
'co-airSensors-TLM0100-mean-': '#31C0F6',
138+
'co-airSensors-TLM0101-mean-': '#A500A5',
139+
'co-airSensors-TLM0102-mean-': '#FF7E27',
140140
},
141141
},
142142
}
@@ -264,9 +264,9 @@ const expectedData = {
264264
},
265265
mappingForIDPE: {
266266
colorMapping: {
267-
'co-airSensors-TLM0100-mean-': 0,
268-
'co-airSensors-TLM0101-mean-': 1,
269-
'co-airSensors-TLM0102-mean-': 2,
267+
'co-airSensors-TLM0100-mean-': '#31C0F6',
268+
'co-airSensors-TLM0101-mean-': '#A500A5',
269+
'co-airSensors-TLM0102-mean-': '#FF7E27',
270270
},
271271
},
272272
},
@@ -280,7 +280,7 @@ const expectedData = {
280280
_measurement: 'airSensors',
281281
sensor_id: 'TLM0100',
282282
result: 'mean',
283-
color: '#31C0F6',
283+
color: 'rgb(49, 192, 246)',
284284
},
285285
{
286286
_start: `${MOCK_START}`,
@@ -289,7 +289,7 @@ const expectedData = {
289289
_measurement: 'airSensors',
290290
sensor_id: 'TLM0101',
291291
result: 'mean',
292-
color: '#A500A5',
292+
color: 'rgb(106, 103, 205)',
293293
},
294294
{
295295
_start: `${MOCK_START}`,
@@ -298,7 +298,7 @@ const expectedData = {
298298
_measurement: 'airSensors',
299299
sensor_id: 'TLM0102',
300300
result: 'mean',
301-
color: '#FF7E27',
301+
color: 'rgb(161, 53, 158)',
302302
},
303303
{
304304
_start: `${MOCK_START}`,
@@ -307,7 +307,7 @@ const expectedData = {
307307
_measurement: 'airSensors',
308308
sensor_id: 'TLM0103',
309309
result: 'mean',
310-
color: '#31C0F6',
310+
color: 'rgb(209, 70, 101)',
311311
},
312312
{
313313
_start: `${MOCK_START}`,
@@ -316,7 +316,7 @@ const expectedData = {
316316
_measurement: 'airSensors',
317317
sensor_id: 'TLM0104',
318318
result: 'mean',
319-
color: '#A500A5',
319+
color: 'rgb(255, 126, 39)',
320320
},
321321
],
322322
columnKeys: [
@@ -330,11 +330,11 @@ const expectedData = {
330330
},
331331
mappingForIDPE: {
332332
colorMapping: {
333-
'co-airSensors-TLM0100-mean-': 0,
334-
'co-airSensors-TLM0101-mean-': 1,
335-
'co-airSensors-TLM0102-mean-': 2,
336-
'co-airSensors-TLM0103-mean-': 0,
337-
'co-airSensors-TLM0104-mean-': 1,
333+
'co-airSensors-TLM0100-mean-': 'rgb(49, 192, 246)',
334+
'co-airSensors-TLM0101-mean-': 'rgb(106, 103, 205)',
335+
'co-airSensors-TLM0102-mean-': 'rgb(161, 53, 158)',
336+
'co-airSensors-TLM0103-mean-': 'rgb(209, 70, 101)',
337+
'co-airSensors-TLM0104-mean-': 'rgb(255, 126, 39)',
338338
},
339339
},
340340
},

src/visualization/utils/colorMappingUtils.ts

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,29 @@
11
import {XYViewProperties} from 'src/types'
2+
import {addedDiff, deletedDiff} from 'deep-object-diff'
3+
import {getNominalColorScale} from '@influxdata/giraffe'
24

35
/**
4-
* Evaluates deeper equality for two map objects based on their key:value pair
6+
* Evaluates whether mappings need to be updated
57
* @param map1
68
* @param map2
79
*/
8-
const areMappingsSame = (map1, map2) => {
9-
if (!map1 || !map2) {
10-
return false
10+
const compareIDPEandLocalMappings = (map1, map2) => {
11+
// SPECIAL CASE: handle null case when colorMapping from IDPE is `null`
12+
if (!map1) {
13+
return {isMappingReusable: false, additions: map2}
1114
}
1215

13-
// Create arrays of property names
14-
const aProps = Object.getOwnPropertyNames(map1)
15-
const bProps = Object.getOwnPropertyNames(map2)
16-
17-
// If number of properties is different,
18-
// objects are not equivalent
19-
if (aProps.length !== bProps.length) {
20-
return false
16+
function isEmpty(obj) {
17+
return Object.keys(obj).length === 0
2118
}
2219

23-
for (let i = 0; i < aProps.length; i++) {
24-
const propName = aProps[i]
20+
const additions = addedDiff(map1, map2)
21+
const deletions = deletedDiff(map1, map2)
2522

26-
// If values of same property are not equal,
27-
// objects are not equivalent
28-
if (map1[propName] !== map2[propName]) {
29-
return false
30-
}
31-
}
23+
// if no additions or no deletions we are good to go
24+
const isMappingReusable = isEmpty(additions) && isEmpty(deletions)
3225

33-
return true
26+
return {isMappingReusable, additions, deletions}
3427
}
3528

3629
/**
@@ -44,26 +37,29 @@ export const getColorMappingObjects = (
4437
columnGroupMap,
4538
properties: XYViewProperties
4639
) => {
47-
const seriesToColorIndexMap = generateSeriesToColorIndexMap(
40+
const seriesToColorHexMap = generateSeriesToColorHex(
4841
columnGroupMap,
4942
properties
5043
)
5144

45+
const {isMappingReusable} = compareIDPEandLocalMappings(
46+
properties.colorMapping,
47+
seriesToColorHexMap
48+
)
49+
5250
// if the mappings from the IDPE and the *required* one's for the current view are the same, we don't need to generate new mappings
53-
if (areMappingsSame(properties.colorMapping, seriesToColorIndexMap)) {
51+
if (isMappingReusable) {
5452
const columnKeys = columnGroupMap.columnKeys
5553
const mappings = {...columnGroupMap}
54+
const needsToSaveToIDPE = false
5655

5756
mappings.mappings.forEach(graphLine => {
5857
const seriesID = getSeriesId(graphLine, columnKeys)
5958

60-
const colors = properties.colors
61-
6259
// this is needed for giraffe
63-
graphLine.color = colors[properties.colorMapping[seriesID]].hex
60+
graphLine.color = properties.colorMapping[seriesID]
6461
})
6562

66-
const needsToSaveToIDPE = false
6763
return {
6864
colorMappingForGiraffe: {columnKeys, ...mappings},
6965
needsToSaveToIDPE,
@@ -76,18 +72,33 @@ export const getColorMappingObjects = (
7672
mappings.mappings.forEach(graphLine => {
7773
const seriesID = getSeriesId(graphLine, columnKeys)
7874

79-
const colors = properties.colors
80-
8175
// this is needed for giraffe
82-
graphLine.color = colors[seriesToColorIndexMap[seriesID]].hex
76+
graphLine.color = seriesToColorHexMap[seriesID]
8377
})
8478

8579
const newColorMappingForGiraffe = {
8680
...mappings,
8781
}
8882

83+
const {additions, deletions} = compareIDPEandLocalMappings(
84+
properties.colorMapping,
85+
seriesToColorHexMap
86+
)
87+
88+
const colorMappingForIDPE = {...properties.colorMapping}
89+
90+
// perform additions
91+
for (const add in additions) {
92+
colorMappingForIDPE[add] = seriesToColorHexMap[add]
93+
}
94+
95+
// perform deletions
96+
for (const minus in deletions) {
97+
delete colorMappingForIDPE[minus]
98+
}
99+
89100
return {
90-
colorMappingForIDPE: seriesToColorIndexMap,
101+
colorMappingForIDPE: colorMappingForIDPE,
91102
colorMappingForGiraffe: newColorMappingForGiraffe,
92103
needsToSaveToIDPE,
93104
}
@@ -136,19 +147,21 @@ const getSeriesId = (graphLine, columnKeys) => {
136147
* This function generates a map that maps the series ID to the color Index.
137148
* @param columnGroupMap - generated using the createGroupIDColumn function
138149
* @param properties - XYViewProperties
139-
* @returns - a map that contains the series ID and it's color index (value bounded by the properties.colors array)
150+
* @returns - a map that contains the series ID and it's color hex value
140151
*/
141152

142-
export const generateSeriesToColorIndexMap = (
153+
const generateSeriesToColorHex = (
143154
columnGroupMap,
144155
properties: XYViewProperties
145156
) => {
146-
const seriesToColorIndexMap = {}
157+
const seriesToColorHex = {}
147158
const cgMap = {...columnGroupMap}
148159
cgMap.mappings.forEach((graphLine, colorIndex) => {
149160
const id = getSeriesId(graphLine, columnGroupMap.columnKeys)
150-
seriesToColorIndexMap[id] = colorIndex % properties.colors.length
161+
const colors = properties.colors.map(value => value.hex)
162+
const fillScale = getNominalColorScale(columnGroupMap, colors)
163+
seriesToColorHex[id] = fillScale(colorIndex)
151164
})
152165

153-
return {...seriesToColorIndexMap}
166+
return {...seriesToColorHex}
154167
}

yarn.lock

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2571,6 +2571,13 @@ aws4@^1.8.0:
25712571
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
25722572
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
25732573

2574+
axios@^0.19.0:
2575+
version "0.19.2"
2576+
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
2577+
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
2578+
dependencies:
2579+
follow-redirects "1.5.10"
2580+
25742581
babel-jest@^27.4.4:
25752582
version "27.4.4"
25762583
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.4.4.tgz#a012441f8a155df909839543a09510ab3477aa11"
@@ -4100,7 +4107,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
41004107
dependencies:
41014108
ms "2.0.0"
41024109

4103-
debug@3.1.0:
4110+
debug@3.1.0, debug@=3.1.0:
41044111
version "3.1.0"
41054112
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
41064113
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
@@ -4158,6 +4165,11 @@ deep-is@~0.1.3:
41584165
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
41594166
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
41604167

4168+
deep-object-diff@^1.1.0:
4169+
version "1.1.0"
4170+
resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.0.tgz#d6fabf476c2ed1751fc94d5ca693d2ed8c18bc5a"
4171+
integrity sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw==
4172+
41614173
deepmerge@^4.2.2:
41624174
version "4.2.2"
41634175
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
@@ -5238,6 +5250,13 @@ flush-write-stream@^1.0.0:
52385250
inherits "^2.0.3"
52395251
readable-stream "^2.3.6"
52405252

5253+
follow-redirects@1.5.10:
5254+
version "1.5.10"
5255+
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
5256+
integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
5257+
dependencies:
5258+
debug "=3.1.0"
5259+
52415260
follow-redirects@^1.0.0:
52425261
version "1.14.6"
52435262
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd"

0 commit comments

Comments
 (0)