From a71d3caa6c095e92abb42c77c557a7e27ffb82f8 Mon Sep 17 00:00:00 2001 From: Stephen Liu <750188453@qq.com> Date: Sat, 6 Nov 2021 11:41:50 +0800 Subject: [PATCH] feat(legacy-plugin-chart-calendar): increase the contrast of calendar heatmap color and label (#1452) * feat(legacy-plugin-chart-calendar): increasecontrast of calendar heatmap * rename getContrastingColor and put it core * fix: unit test --- .../superset-ui-core/src/color/index.ts | 1 + .../superset-ui-core/src/color/utils.ts | 53 ++++++++++++++++ .../superset-ui-core/test/color/utils.test.ts | 63 +++++++++++++++++++ .../src/vendor/cal-heatmap.css | 1 - .../src/vendor/cal-heatmap.js | 8 ++- 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/color/utils.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/test/color/utils.test.ts diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/color/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/color/index.ts index c6d404892df1..f81fcbba72b6 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/color/index.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/color/index.ts @@ -27,5 +27,6 @@ export { default as getSequentialSchemeRegistry } from './SequentialSchemeRegist export { default as SequentialScheme } from './SequentialScheme'; export { default as ColorSchemeRegistry } from './ColorSchemeRegistry'; export * from './colorSchemes'; +export * from './utils'; export const BRAND_COLOR = '#00A699'; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/color/utils.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/color/utils.ts new file mode 100644 index 000000000000..47a936aaa618 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/color/utils.ts @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const rgbRegex = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/; +export function getContrastingColor(color: string, thresholds = 186) { + let r = 0; + let g = 0; + let b = 0; + if (color.length > 7) { + // rgb + const matchColor = rgbRegex.exec(color); + if (!matchColor) { + throw new Error(`Invalid color: ${color}`); + } + r = parseInt(matchColor[1], 10); + g = parseInt(matchColor[2], 10); + b = parseInt(matchColor[3], 10); + } else { + // hex + let hex = color; + if (hex.startsWith('#')) { + hex = hex.substring(1); + } + // #FFF + if (hex.length === 3) { + hex = [hex[0], hex[0], hex[1], hex[1], hex[2], hex[2]].join(''); + } + if (hex.length !== 6) { + throw new Error(`Invalid color: ${color}`); + } + r = parseInt(hex.slice(0, 2), 16); + g = parseInt(hex.slice(2, 4), 16); + b = parseInt(hex.slice(4, 6), 16); + } + + return r * 0.299 + g * 0.587 + b * 0.114 > thresholds ? '#000' : '#FFF'; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/test/color/utils.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/test/color/utils.test.ts new file mode 100644 index 000000000000..bf6db2708ce9 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/test/color/utils.test.ts @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { getContrastingColor } from '@superset-ui/core/src/color'; + +describe('color utils', () => { + describe('getContrastingColor', () => { + it('when called with 3-digit hex color', () => { + const color = getContrastingColor('#000'); + expect(color).toBe('#FFF'); + }); + + it('when called with 6-digit hex color', () => { + const color = getContrastingColor('#000000'); + expect(color).toBe('#FFF'); + }); + + it('when called with no # prefix hex color', () => { + const color = getContrastingColor('000000'); + expect(color).toBe('#FFF'); + }); + + it('when called with rgb color', () => { + const color = getContrastingColor('rgb(0, 0, 0)'); + expect(color).toBe('#FFF'); + }); + + it('when called with thresholds', () => { + const color1 = getContrastingColor('rgb(255, 255, 255)'); + const color2 = getContrastingColor('rgb(255, 255, 255)', 255); + expect(color1).toBe('#000'); + expect(color2).toBe('#FFF'); + }); + + it('when called with rgba color, throw error', () => { + expect(() => { + getContrastingColor('rgba(0, 0, 0, 0.1)'); + }).toThrow(); + }); + + it('when called with invalid color, throw error', () => { + expect(() => { + getContrastingColor('#0000'); + }).toThrow(); + }); + }); +}); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.css b/superset-frontend/temporary_superset_ui/superset-ui/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.css index d55251ec2718..de282154a509 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.css +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.css @@ -26,7 +26,6 @@ .cal-heatmap-container .subdomain-text { font-size: 8px; - fill: #999; pointer-events: none; } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.js index a8da0b374afb..980ac1c1ea85 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.js +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.js @@ -9,6 +9,7 @@ /* eslint-disable */ import d3tip from 'd3-tip'; +import { getContrastingColor } from '@superset-ui/core'; import './d3tip.css'; var d3 = typeof require === 'function' ? require('d3') : window.d3; @@ -1664,7 +1665,12 @@ CalHeatMap.prototype = { .attr('class', function (d) { return 'subdomain-text' + parent.getHighlightClassName(d.t); }) - .call(formatSubDomainText); + .call(formatSubDomainText) + .attr('fill', d => { + if (!d.v) return '#000'; + const rgb = parent.legendScale(Math.min(d.v, options.legend[options.legend.length - 1])); + return getContrastingColor(rgb, 135); + }); }, /**