diff --git a/.ci/vrts.sh b/.ci/vrts.sh index 1c3252f0ece..541f1b8ed33 100755 --- a/.ci/vrts.sh +++ b/.ci/vrts.sh @@ -5,6 +5,7 @@ ### source .ci/global_setup.sh + VRTS_FILES=$1 ### ### visual testing diff --git a/.eslintrc.js b/.eslintrc.js index 9dd38f55050..694b28038d5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -78,6 +78,7 @@ module.exports = { 'no-restricted-properties': 0, // need to find and filter desired options 'class-methods-use-this': 0, 'unicorn/prefer-number-properties': 0, + 'unicorn/number-literal-case': 0, // use prettier lower case preference 'global-require': 1, 'import/no-dynamic-require': 1, 'no-shadow': 1, diff --git a/NOTICE.txt b/NOTICE.txt index f038002ddfe..153ba3ca726 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -40,3 +40,38 @@ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +--- +This product includes code that is adapted from https://github.com/Myndex/SAPC-APCA +which is available under a "W3C SOFTWARE NOTICE AND LICENSE" license. + +Copyright (c) 2021 W3C® (MIT, ERCIM, Keio, Beihang) + +License + +By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply +with the following terms and conditions. + +Permission to copy, modify, and distribute this work, with or without modification, for any purpose and without fee +or royalty is hereby granted, provided that you include the following on ALL copies of the work or portions thereof, +including modifications: + + - The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. + - Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, + the W3C Software and Document Short Notice should be included, see + https://www.w3.org/Consortium/Legal/2015/copyright-software-short-notice.html + - Notice of any changes or modifications, through a copyright statement on the new code or document such as + "This software or document includes material copied from or derived from [title and URI of the W3C document]. + Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." + +Disclaimers +THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT +THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. + +The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining +to the work without specific, written prior permission. Title to copyright in this work will +at all times remain with copyright holders. diff --git a/docs/0-Intro/1-Overview.mdx b/docs/0-Intro/1-Overview.mdx index 5965aea4d56..9bbf59f84e6 100644 --- a/docs/0-Intro/1-Overview.mdx +++ b/docs/0-Intro/1-Overview.mdx @@ -265,154 +265,5 @@ type PointStyleAccessor = ( > Note: When overriding bar or point styles be mindful of performance and these accessor functions will be call on every bar/point is every series. Precomputing any expensive task before rendering. -### Background Colors and Text Contrast -You can provide the `backgroundColor` of the container that the chart will be placed onto. You can set the `textContrast` to a boolean value or a number. The default `textContrast` is set to 4.5 but you can always disable this or set your own numerical amount. - -> Note: This functionality is currently available for Partition charts. Please see the partition background and partition label stories. - -```js -config: { - fillLabel: { - textInvertible: true, - textContrast: true, // can also be set to a number - } -} -``` -`textInvertible` will have to be set to true for `textContrast` to be set as well. To see an example of where this applies, please see the Partitions Background story within Stylings. Charts are included below but are static. -If you have `textInvertible` set to true, but do not have `textContrast` set to true, then the red slices, Europe, North America, and Asia, will have white text: - - - - d.exportVal} - valueFormatter={(d) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d) => d.sitc1, - nodeLabel: (d) => productLookup[d].name, - shape: { - fillColor: (d) => { - return discreteColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex); - }, - }, - }, - { - groupByRollup: (d) => countryLookup[d.dest].continentCountry.substr(0, 2), - nodeLabel: (d) => regionLookup[d].regionName, - shape: { - fillColor: (d) => { - return discreteColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex); - }, - }, - }, - { - groupByRollup: (d) => d.dest, - nodeLabel: (d) => countryLookup[d].name, - shape: { - fillColor: (d) => { - return discreteColor(colorBrewerCategoricalStark9, 0.3)(d[MODEL_KEY].parent.sortIndex); - }, - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.sunburst, - linkLabel: { - maxCount: 0, - fontSize: 14, - }, - fontFamily: 'Arial', - fillLabel: { - valueFormatter: (d) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - fontStyle: 'italic', - textInvertible: true, - textContrast: false, - fontWeight: 900, - valueFont: { - fontFamily: 'Menlo', - fontStyle: 'normal', - fontWeight: 100, - }, - }, - margin: { top: 0, bottom: 0, left: 0, right: 0 }, - minFontSize: 1, - idealFontSizeJump: 1.1, - outerSizeRatio: 1, - emptySizeRatio: 0, - circlePadding: 4, - backgroundColor: 'rgba(229,229,229,1)', - }} - /> - - - - -Now if you set the `textContrast` to true as well, these slices also become black in text color: - - - - d.exportVal} - valueFormatter={(d) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d) => d.sitc1, - nodeLabel: (d) => productLookup[d].name, - shape: { - fillColor: (d) => { - return discreteColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex); - }, - }, - }, - { - groupByRollup: (d) => countryLookup[d.dest].continentCountry.substr(0, 2), - nodeLabel: (d) => regionLookup[d].regionName, - shape: { - fillColor: (d) => { - return discreteColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex); - }, - }, - }, - { - groupByRollup: (d) => d.dest, - nodeLabel: (d) => countryLookup[d].name, - shape: { - fillColor: (d) => { - return discreteColor(colorBrewerCategoricalStark9, 0.3)(d[MODEL_KEY].parent.sortIndex); - }, - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.sunburst, - linkLabel: { - maxCount: 0, - fontSize: 14, - }, - fontFamily: 'Arial', - fillLabel: { - valueFormatter: (d) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - fontStyle: 'italic', - textInvertible: true, - textContrast: true, - fontWeight: 900, - valueFont: { - fontFamily: 'Menlo', - fontStyle: 'normal', - fontWeight: 100, - }, - }, - margin: { top: 0, bottom: 0, left: 0, right: 0 }, - minFontSize: 1, - idealFontSizeJump: 1.1, - outerSizeRatio: 1, - emptySizeRatio: 0, - circlePadding: 4, - backgroundColor: 'rgba(229,229,229,1)', - }} - /> - +### Background Color +You can provide the `backgroundColor` of the container that the chart will be placed onto. diff --git a/docs/1-Typesofchart/4-Sunburts.mdx b/docs/1-Typesofchart/4-Sunburts.mdx index 8eaf1f71b38..96511a5965b 100644 --- a/docs/1-Typesofchart/4-Sunburts.mdx +++ b/docs/1-Typesofchart/4-Sunburts.mdx @@ -28,7 +28,6 @@ import { { groupByRollup: (d) => d.sitc1, nodeLabel: (d) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, @@ -52,7 +51,6 @@ The code sample can be found within the details below. { groupByRollup: (d) => d.sitc1, nodeLabel: (d) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, @@ -76,6 +74,6 @@ These props are for the `` component as seen in the code snippet abo | layers | Array | | | | groupByRollup| function | | | | nodeLabel | function | | | -| fillLabel | object { textInvertible : boolean} | | | +| fillLabel | object | | | | shape | object { fillColor: indexInterpolatedFillColor(interpolatorCET2s) } diff --git a/docs/1-Typesofchart/8-Donut.mdx b/docs/1-Typesofchart/8-Donut.mdx index 34707f6b7fd..e97a85d0bae 100644 --- a/docs/1-Typesofchart/8-Donut.mdx +++ b/docs/1-Typesofchart/8-Donut.mdx @@ -28,7 +28,6 @@ import { { groupByRollup: (d) => d.sitc1, nodeLabel: (d) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, @@ -69,7 +68,6 @@ import { { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/integration/jest.config.js b/integration/jest.config.js index 53688116e06..02dd72ec662 100644 --- a/integration/jest.config.js +++ b/integration/jest.config.js @@ -14,7 +14,6 @@ const { debug } = require('./config'); module.exports = { setupFilesAfterEnv: ['/jest_env_setup.ts'], - modulePathIgnorePatterns: ['/node_modules/canvas/.+'], globals: { 'ts-jest': { tsconfig: '/tsconfig.json', diff --git a/integration/page_objects/common.ts b/integration/page_objects/common.ts index 5eb537bc16b..c2590dbbdac 100644 --- a/integration/page_objects/common.ts +++ b/integration/page_objects/common.ts @@ -8,7 +8,6 @@ import Url from 'url'; -import { JSDOM } from 'jsdom'; import { AXNode } from 'puppeteer'; import { DRAG_DETECTION_TIMEOUT } from '../../packages/charts/src/state/reducers/interactions'; @@ -470,16 +469,6 @@ class CommonPage { }); return accessibilitySnapshot; } - - /** - * Get HTML for element to test aria labels etc - */ - // eslint-disable-next-line class-methods-use-this - async getSelectorHTML(url: string, tagName: string) { - await this.loadElementFromURL(url, '.echCanvasRenderer'); - const xml = await page.evaluate(() => new XMLSerializer().serializeToString(document)); - return new JSDOM(xml, { contentType: 'text/xml' }).window.document.getElementsByTagName(tagName); - } } export const common = new CommonPage(); diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-cpu-profile-flame-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-cpu-profile-flame-chart-visually-looks-correct-1-snap.png index 2cd9188fa7d..ec4323f8e08 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-cpu-profile-flame-chart-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-cpu-profile-flame-chart-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-flame-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-flame-chart-visually-looks-correct-1-snap.png index 35d8a6d8168..334d912c78b 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-flame-chart-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-flame-chart-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-icicle-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-icicle-chart-visually-looks-correct-1-snap.png index b36ea0553ad..cf19520c15a 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-icicle-chart-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-flame-alpha-icicle-chart-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-interactions-sunburst-slice-clicks-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-interactions-sunburst-slice-clicks-visually-looks-correct-1-snap.png index 16e311feede..70b8e9e9874 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-interactions-sunburst-slice-clicks-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-interactions-sunburst-slice-clicks-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-legend-piechart-repeated-labels-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-legend-piechart-repeated-labels-visually-looks-correct-1-snap.png index 964a522cbdd..ab20933833b 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-legend-piechart-repeated-labels-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-legend-piechart-repeated-labels-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-legend-piechart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-legend-piechart-visually-looks-correct-1-snap.png index 8cb05eb3282..7dd0ed1d919 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-legend-piechart-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-legend-piechart-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-mosaic-alpha-other-slices-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-mosaic-alpha-other-slices-visually-looks-correct-1-snap.png index 046a394514c..e7a529fc5a7 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-mosaic-alpha-other-slices-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-mosaic-alpha-other-slices-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-small-multiples-alpha-sunbursts-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-small-multiples-alpha-sunbursts-visually-looks-correct-1-snap.png index 1701853b5a8..147a5864cad 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-small-multiples-alpha-sunbursts-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-small-multiples-alpha-sunbursts-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-partial-custom-theme-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-partial-custom-theme-visually-looks-correct-1-snap.png index f74fb524892..e318efecc08 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-partial-custom-theme-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-partial-custom-theme-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-partition-background-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-partition-background-visually-looks-correct-1-snap.png index 71b28fc598d..aa2b4005fb4 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-partition-background-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-partition-background-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-partition-labels-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-partition-labels-visually-looks-correct-1-snap.png index 22271e0cfc3..0cf9b9cdb53 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-partition-labels-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-partition-labels-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-bold-link-value-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-bold-link-value-visually-looks-correct-1-snap.png index ca8bfae3122..60eccc4172e 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-bold-link-value-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-bold-link-value-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-clockwise-no-special-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-clockwise-no-special-visually-looks-correct-1-snap.png index 28235f8eee9..40a1e9740b3 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-clockwise-no-special-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-clockwise-no-special-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-counter-clockwise-special-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-counter-clockwise-special-visually-looks-correct-1-snap.png index dc5ca3973ef..52f6c0a6ed3 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-counter-clockwise-special-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-counter-clockwise-special-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-custom-stroke-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-custom-stroke-visually-looks-correct-1-snap.png index 0858553d29c..db94ab7b1dc 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-custom-stroke-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-custom-stroke-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-custom-tooltip-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-custom-tooltip-visually-looks-correct-1-snap.png index cfe7bd83fde..4582995729d 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-custom-tooltip-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-custom-tooltip-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-donut-chart-with-fill-labels-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-donut-chart-with-fill-labels-visually-looks-correct-1-snap.png index ec9cc29c842..acf0ca6fc3a 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-donut-chart-with-fill-labels-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-donut-chart-with-fill-labels-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-heterogeneous-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-heterogeneous-visually-looks-correct-1-snap.png index 0221467820e..5ad62704504 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-heterogeneous-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-heterogeneous-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-most-basic-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-most-basic-visually-looks-correct-1-snap.png index a5b2b157fe0..e291cf69f02 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-most-basic-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-most-basic-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-visually-looks-correct-1-snap.png index 3074e5e877e..21ad88da938 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-not-a-number-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-not-a-number-visually-looks-correct-1-snap.png index 3074e5e877e..21ad88da938 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-not-a-number-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-not-a-number-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-percentage-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-percentage-visually-looks-correct-1-snap.png index 6f38bf3e289..09037c45250 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-percentage-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-percentage-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-sunburst-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-sunburst-visually-looks-correct-1-snap.png index a1b92ec05b8..48fe53d1288 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-sunburst-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-sunburst-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-three-layers-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-three-layers-visually-looks-correct-1-snap.png index 6fdbe060d22..679e647bfb5 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-three-layers-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-three-layers-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-two-layers-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-two-layers-visually-looks-correct-1-snap.png index cfe7bd83fde..4582995729d 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-two-layers-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-two-layers-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-visually-looks-correct-1-snap.png index 73d5a2bc582..f73f342a6cc 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-fill-labels-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-fill-labels-visually-looks-correct-1-snap.png index 2e8fa5f7110..1ed2e2d8eee 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-fill-labels-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-fill-labels-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-custom-style-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-custom-style-visually-looks-correct-1-snap.png index b811100d297..74422d6d663 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-custom-style-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-custom-style-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-groove-text-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-groove-text-visually-looks-correct-1-snap.png index 8009ff21323..63815bcfdeb 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-groove-text-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-groove-text-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-mid-two-layers-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-mid-two-layers-visually-looks-correct-1-snap.png index f5ccaed9415..d37dd2ee371 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-mid-two-layers-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-mid-two-layers-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-multi-color-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-multi-color-visually-looks-correct-1-snap.png index 356be7cf98d..191b7897e8f 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-multi-color-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-multi-color-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-one-layer-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-one-layer-visually-looks-correct-1-snap.png index 83033e9706f..ebe3e933cb2 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-one-layer-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-one-layer-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-percentage-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-percentage-visually-looks-correct-1-snap.png index ba3cd4ab881..99e3ad68d21 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-percentage-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-percentage-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-three-layer-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-three-layer-visually-looks-correct-1-snap.png index 33654ba0a89..551a58eb429 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-three-layer-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-three-layer-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-two-layers-stress-test-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-two-layers-stress-test-visually-looks-correct-1-snap.png index 8f39c2fdb1a..ebc580aef42 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-two-layers-stress-test-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-two-layers-stress-test-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-zero-values-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-zero-values-visually-looks-correct-1-snap.png index bf531cc4ef0..9ec4d8908f6 100644 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-zero-values-visually-looks-correct-1-snap.png and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-treemap-zero-values-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/area-stories-test-ts-area-series-stories-negative-log-areas-shows-only-positive-values-when-hiding-negative-one-1-snap.png b/integration/tests/__image_snapshots__/area-stories-test-ts-area-series-stories-negative-log-areas-shows-only-positive-values-when-hiding-negative-one-1-snap.png index cbde9d60cab..33f3812c9a7 100644 Binary files a/integration/tests/__image_snapshots__/area-stories-test-ts-area-series-stories-negative-log-areas-shows-only-positive-values-when-hiding-negative-one-1-snap.png and b/integration/tests/__image_snapshots__/area-stories-test-ts-area-series-stories-negative-log-areas-shows-only-positive-values-when-hiding-negative-one-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/dark-mode-test-ts-small-multiples-dark-mode-renders-panel-titles-1-snap.png b/integration/tests/__image_snapshots__/dark-mode-test-ts-small-multiples-dark-mode-renders-panel-titles-1-snap.png index 70ff0f2b63a..a525d411099 100644 Binary files a/integration/tests/__image_snapshots__/dark-mode-test-ts-small-multiples-dark-mode-renders-panel-titles-1-snap.png and b/integration/tests/__image_snapshots__/dark-mode-test-ts-small-multiples-dark-mode-renders-panel-titles-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/goal-stories-test-ts-goal-stories-should-render-actual-tooltip-color-on-hover-1-snap.png b/integration/tests/__image_snapshots__/goal-stories-test-ts-goal-stories-should-render-actual-tooltip-color-on-hover-1-snap.png index 597b5660c9d..ed5b0865744 100644 Binary files a/integration/tests/__image_snapshots__/goal-stories-test-ts-goal-stories-should-render-actual-tooltip-color-on-hover-1-snap.png and b/integration/tests/__image_snapshots__/goal-stories-test-ts-goal-stories-should-render-actual-tooltip-color-on-hover-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/goal-stories-test-ts-goal-stories-theme-eui-dark-should-render-gauge-with-target-story-1-snap.png b/integration/tests/__image_snapshots__/goal-stories-test-ts-goal-stories-theme-eui-dark-should-render-gauge-with-target-story-1-snap.png index e75a99f71c3..b040d6a26b5 100644 Binary files a/integration/tests/__image_snapshots__/goal-stories-test-ts-goal-stories-theme-eui-dark-should-render-gauge-with-target-story-1-snap.png and b/integration/tests/__image_snapshots__/goal-stories-test-ts-goal-stories-theme-eui-dark-should-render-gauge-with-target-story-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/goal-stories-test-ts-goal-stories-theme-eui-light-should-render-gauge-with-target-story-1-snap.png b/integration/tests/__image_snapshots__/goal-stories-test-ts-goal-stories-theme-eui-light-should-render-gauge-with-target-story-1-snap.png index bbab206a541..08cfbd11dc2 100644 Binary files a/integration/tests/__image_snapshots__/goal-stories-test-ts-goal-stories-theme-eui-light-should-render-gauge-with-target-story-1-snap.png and b/integration/tests/__image_snapshots__/goal-stories-test-ts-goal-stories-theme-eui-light-should-render-gauge-with-target-story-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/interactions-test-ts-interactions-legend-items-with-color-picker-clicking-hidden-or-unhidden-legend-items-should-not-move-when-color-picker-series-is-hidden-or-unhidden-1-snap.png b/integration/tests/__image_snapshots__/interactions-test-ts-interactions-legend-items-with-color-picker-clicking-hidden-or-unhidden-legend-items-should-not-move-when-color-picker-series-is-hidden-or-unhidden-1-snap.png index 8c0e770bf8e..7963e08e665 100644 Binary files a/integration/tests/__image_snapshots__/interactions-test-ts-interactions-legend-items-with-color-picker-clicking-hidden-or-unhidden-legend-items-should-not-move-when-color-picker-series-is-hidden-or-unhidden-1-snap.png and b/integration/tests/__image_snapshots__/interactions-test-ts-interactions-legend-items-with-color-picker-clicking-hidden-or-unhidden-legend-items-should-not-move-when-color-picker-series-is-hidden-or-unhidden-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/interactions-test-ts-interactions-tooltips-should-show-tooltip-on-sunburst-1-snap.png b/integration/tests/__image_snapshots__/interactions-test-ts-interactions-tooltips-should-show-tooltip-on-sunburst-1-snap.png index ea83b37b4cb..3d4b16b213e 100644 Binary files a/integration/tests/__image_snapshots__/interactions-test-ts-interactions-tooltips-should-show-tooltip-on-sunburst-1-snap.png and b/integration/tests/__image_snapshots__/interactions-test-ts-interactions-tooltips-should-show-tooltip-on-sunburst-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-flat-legend-extra-values-on-sunburst-1-snap.png b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-flat-legend-extra-values-on-sunburst-1-snap.png index a8b357ea1a3..036dd50839d 100644 Binary files a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-flat-legend-extra-values-on-sunburst-1-snap.png and b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-flat-legend-extra-values-on-sunburst-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-flat-legend-extra-values-on-treemap-1-snap.png b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-flat-legend-extra-values-on-treemap-1-snap.png index 174f51b4e8f..4db277a7ee1 100644 Binary files a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-flat-legend-extra-values-on-treemap-1-snap.png and b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-flat-legend-extra-values-on-treemap-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-nested-legend-extra-values-on-sunburst-1-snap.png b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-nested-legend-extra-values-on-sunburst-1-snap.png index 15c8ae5ac4b..10b8d019731 100644 Binary files a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-nested-legend-extra-values-on-sunburst-1-snap.png and b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-nested-legend-extra-values-on-sunburst-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-nested-legend-extra-values-on-treemap-1-snap.png b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-nested-legend-extra-values-on-treemap-1-snap.png index 8d38e3f2d1e..afd88c6e136 100644 Binary files a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-nested-legend-extra-values-on-treemap-1-snap.png and b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-extra-values-should-display-nested-legend-extra-values-on-treemap-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-should-correctly-render-multiline-nested-legend-labels-1-snap.png b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-should-correctly-render-multiline-nested-legend-labels-1-snap.png index 39aa2fef0cc..2aa8799121c 100644 Binary files a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-should-correctly-render-multiline-nested-legend-labels-1-snap.png and b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-should-correctly-render-multiline-nested-legend-labels-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-should-have-the-same-order-as-nested-with-no-indent-even-if-there-are-repeated-labels-1-snap.png b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-should-have-the-same-order-as-nested-with-no-indent-even-if-there-are-repeated-labels-1-snap.png index ac4db31da34..54af0e87da1 100644 Binary files a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-should-have-the-same-order-as-nested-with-no-indent-even-if-there-are-repeated-labels-1-snap.png and b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-should-have-the-same-order-as-nested-with-no-indent-even-if-there-are-repeated-labels-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-should-render-legend-action-on-mouse-hover-1-snap.png b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-should-render-legend-action-on-mouse-hover-1-snap.png index 18305732c2a..437576577c3 100644 Binary files a/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-should-render-legend-action-on-mouse-hover-1-snap.png and b/integration/tests/__image_snapshots__/legend-stories-test-ts-legend-stories-should-render-legend-action-on-mouse-hover-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/styles-test-ts-styles-should-hide-the-value-label-with-0-text-border-1-snap.png b/integration/tests/__image_snapshots__/styles-test-ts-styles-should-hide-the-value-label-with-0-text-border-1-snap.png index b1e3d73aedc..e63be27d34d 100644 Binary files a/integration/tests/__image_snapshots__/styles-test-ts-styles-should-hide-the-value-label-with-0-text-border-1-snap.png and b/integration/tests/__image_snapshots__/styles-test-ts-styles-should-hide-the-value-label-with-0-text-border-1-snap.png differ diff --git a/integration/tests/styles.test.ts b/integration/tests/styles.test.ts index 82a2193efdc..8c02af8406e 100644 --- a/integration/tests/styles.test.ts +++ b/integration/tests/styles.test.ts @@ -11,12 +11,12 @@ import { common } from '../page_objects/common'; describe('Styles', () => { it('should hide the value label with 0 borderWidth', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/bar-chart--with-value-label-advanced&knob-show value label=true&knob-alternating value label=&knob-contain value label within bar element=&knob-hide clipped value=&knob-debug=&knob-textInverted=&knob-value color=rgba(0,0,0,1)&knob-value border color=rgba(0,0,0,1)&knob-value border width=0&knob-Fixed font size=10&knob-Use fixed font size=&knob-Max font size=25&knob-Min font size=10&knob-offsetX=0&knob-offsetY=0&knob-data volume size=s&knob-split series=&knob-stacked series=&knob-chartRotation=0', + 'http://localhost:9001/?path=/story/bar-chart--with-value-label-advanced&knob-show value label=true&knob-alternating value label=&knob-contain value label within bar element=&knob-hide clipped value=&knob-debug=&knob-useBorder=&knob-value color=rgba(0,0,0,1)&knob-value border color=rgba(0,0,0,1)&knob-value border width=0&knob-Fixed font size=10&knob-Use fixed font size=&knob-Max font size=25&knob-Min font size=10&knob-offsetX=0&knob-offsetY=0&knob-data volume size=s&knob-split series=&knob-stacked series=&knob-chartRotation=0', ); }); it('should hide the value label with 0 textBorder', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/bar-chart--with-value-label-advanced&knob-show value label=true&knob-alternating value label=&knob-contain value label within bar element=&knob-hide clipped value=&knob-debug=&knob-textInverted=true&knob-value color=rgba(0,0,0,1)&knob-value border color=rgba(0,0,0,1)&knob-value border width=0&knob-Fixed font size=10&knob-Use fixed font size=&knob-Max font size=25&knob-Min font size=10&knob-offsetX=0&knob-offsetY=0&knob-data volume size=s&knob-split series=&knob-stacked series=&knob-chartRotation=0', + 'http://localhost:9001/?path=/story/bar-chart--with-value-label-advanced&knob-show value label=true&knob-alternating value label=&knob-contain value label within bar element=&knob-hide clipped value=&knob-debug=&knob-useBorder=true&knob-value color=rgba(0,0,0,1)&knob-value border color=rgba(0,0,0,1)&knob-value border width=0&knob-Fixed font size=10&knob-Use fixed font size=&knob-Max font size=25&knob-Min font size=10&knob-offsetX=0&knob-offsetY=0&knob-data volume size=s&knob-split series=&knob-stacked series=&knob-chartRotation=0', ); }); }); diff --git a/package.json b/package.json index a22ca9d066d..2de754d5b9e 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,6 @@ "@types/core-js": "^2.5.2", "@types/d3-array": "^1.2.6", "@types/d3-collection": "^1.0.8", - "@types/d3-color": "^1.2.2", "@types/d3-interpolate": "^1.3.1", "@types/d3-scale": "^2.1.1", "@types/d3-shape": "^1.3.1", diff --git a/packages/charts/api/charts.api.md b/packages/charts/api/charts.api.md index 46078413b89..4bb3c9c78c9 100644 --- a/packages/charts/api/charts.api.md +++ b/packages/charts/api/charts.api.md @@ -687,8 +687,6 @@ export type DisplayValueStyle = Omit & { borderColor?: Color; borderWidth?: number; } | { - textInvertible: boolean; - textContrast?: number | boolean; textBorder?: number; }; alignment?: { @@ -2441,8 +2439,8 @@ export type YDomainRange = YDomainBase & DomainRange & LogScaleOptions; // src/chart_types/heatmap/layout/types/config_types.ts:19:13 - (ae-forgotten-export) The symbol "SizeRatio" needs to be exported by the entry point index.d.ts // src/chart_types/heatmap/layout/types/config_types.ts:47:5 - (ae-forgotten-export) The symbol "TextAlign" needs to be exported by the entry point index.d.ts // src/chart_types/heatmap/layout/types/config_types.ts:48:5 - (ae-forgotten-export) The symbol "TextBaseline" needs to be exported by the entry point index.d.ts -// src/chart_types/partition_chart/layout/types/config_types.ts:139:5 - (ae-forgotten-export) The symbol "TimeMs" needs to be exported by the entry point index.d.ts -// src/chart_types/partition_chart/layout/types/config_types.ts:140:5 - (ae-forgotten-export) The symbol "AnimKeyframe" needs to be exported by the entry point index.d.ts +// src/chart_types/partition_chart/layout/types/config_types.ts:136:5 - (ae-forgotten-export) The symbol "TimeMs" needs to be exported by the entry point index.d.ts +// src/chart_types/partition_chart/layout/types/config_types.ts:137:5 - (ae-forgotten-export) The symbol "AnimKeyframe" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/charts/package.json b/packages/charts/package.json index 1ea546cde39..594f3daf027 100644 --- a/packages/charts/package.json +++ b/packages/charts/package.json @@ -38,7 +38,6 @@ "d3-array": "^1.2.4", "d3-cloud": "^1.2.5", "d3-collection": "^1.0.7", - "d3-color": "^1.4.0", "d3-interpolate": "^1.4.0", "d3-scale": "^1.0.7", "d3-shape": "^1.3.4", diff --git a/packages/charts/src/chart_types/goal_chart/layout/types/viewmodel_types.ts b/packages/charts/src/chart_types/goal_chart/layout/types/viewmodel_types.ts index 966c8e5e9d7..4699e2b4447 100644 --- a/packages/charts/src/chart_types/goal_chart/layout/types/viewmodel_types.ts +++ b/packages/charts/src/chart_types/goal_chart/layout/types/viewmodel_types.ts @@ -6,8 +6,7 @@ * Side Public License, v 1. */ -import chroma from 'chroma-js'; - +import { getGreensColorScale } from '../../../../common/color_library_wrappers'; import { Pixels, PointObject } from '../../../../common/geometry'; import { SpecType } from '../../../../specs/constants'; import { LIGHT_THEME } from '../../../../utils/themes/light_theme'; @@ -71,8 +70,7 @@ export const defaultGoalSpec = { ...commonDefaults, bands: [50, 75, 100], bandFillColor: ({ value, highestValue, lowestValue }: BandFillColorAccessorInput) => { - const func = chroma.scale(chroma.brewer.Greens).gamma(0.5).domain([highestValue, lowestValue]); - return func(value).css(); + return getGreensColorScale(0.5, [highestValue, lowestValue])(value); }, tickValueFormatter: ({ value }: BandFillColorAccessorInput) => String(value), labelMajor: ({ base }: BandFillColorAccessorInput) => String(base), diff --git a/packages/charts/src/chart_types/goal_chart/renderer/canvas/canvas_renderers.ts b/packages/charts/src/chart_types/goal_chart/renderer/canvas/canvas_renderers.ts index 48c61da9152..9b81c40029f 100644 --- a/packages/charts/src/chart_types/goal_chart/renderer/canvas/canvas_renderers.ts +++ b/packages/charts/src/chart_types/goal_chart/renderer/canvas/canvas_renderers.ts @@ -7,10 +7,11 @@ */ import { clearCanvas, renderLayers, withContext } from '../../../../renderers/canvas'; +import { Color } from '../../../../utils/common'; import { Mark } from '../../layout/viewmodel/geoms'; /** @internal */ -export function renderCanvas2d(ctx: CanvasRenderingContext2D, dpr: number, geomObjects: Mark[]) { +export function renderCanvas2d(ctx: CanvasRenderingContext2D, dpr: number, geomObjects: Mark[], background: Color) { withContext(ctx, () => { // set some defaults for the overall rendering @@ -30,7 +31,7 @@ export function renderCanvas2d(ctx: CanvasRenderingContext2D, dpr: number, geomO renderLayers(ctx, [ // clear the canvas - clearCanvas, + () => clearCanvas(ctx, background), () => geomObjects.forEach((mark) => withContext(ctx, () => mark.render(ctx))), ]); }); diff --git a/packages/charts/src/chart_types/goal_chart/renderer/canvas/connected_component.tsx b/packages/charts/src/chart_types/goal_chart/renderer/canvas/connected_component.tsx index aa0f96b7398..a1b133eec29 100644 --- a/packages/charts/src/chart_types/goal_chart/renderer/canvas/connected_component.tsx +++ b/packages/charts/src/chart_types/goal_chart/renderer/canvas/connected_component.tsx @@ -19,7 +19,9 @@ import { DEFAULT_A11Y_SETTINGS, getA11ySettingsSelector, } from '../../../../state/selectors/get_accessibility_config'; +import { getChartThemeSelector } from '../../../../state/selectors/get_chart_theme'; import { getInternalIsInitializedSelector, InitStatus } from '../../../../state/selectors/get_internal_is_intialized'; +import { Color } from '../../../../utils/common'; import { Dimensions } from '../../../../utils/dimensions'; import { BandViewModel, nullShapeViewModel, ShapeViewModel } from '../../layout/types/viewmodel_types'; import { initialBoundingBox, Mark } from '../../layout/viewmodel/geoms'; @@ -37,6 +39,7 @@ interface ReactiveChartStateProps { bandLabels: BandViewModel[]; firstValue: number; captureBoundingBox: Rectangle; + background: Color; } interface ReactiveChartDispatchProps { @@ -148,7 +151,7 @@ class Component extends React.Component { private drawCanvas() { if (this.ctx) { - renderCanvas2d(this.ctx, this.devicePixelRatio, this.props.geoms); + renderCanvas2d(this.ctx, this.devicePixelRatio, this.props.geoms, this.props.background); } } } @@ -175,6 +178,7 @@ const DEFAULT_PROPS: ReactiveChartStateProps = { bandLabels: [], firstValue: 0, captureBoundingBox: initialBoundingBox(), + background: 'transparent', }; const mapStateToProps = (state: GlobalChartState): ReactiveChartStateProps => { @@ -190,6 +194,7 @@ const mapStateToProps = (state: GlobalChartState): ReactiveChartStateProps => { firstValue: getFirstTickValueSelector(state), geoms: getPrimitiveGeoms(state), captureBoundingBox: getCaptureBoundingBox(state), + background: getChartThemeSelector(state).background.color, }; }; diff --git a/packages/charts/src/chart_types/heatmap/layout/config/config.ts b/packages/charts/src/chart_types/heatmap/layout/config/config.ts index 80306ec9b81..a79d9f95cb6 100644 --- a/packages/charts/src/chart_types/heatmap/layout/config/config.ts +++ b/packages/charts/src/chart_types/heatmap/layout/config/config.ts @@ -44,7 +44,6 @@ export const config: Config = { textColor: 'black', fontVariant: 'normal', fontWeight: 'normal', - textOpacity: 1, align: 'center' as CanvasTextAlign, baseline: 'verticalAlign' as CanvasTextBaseline, padding: 6, @@ -60,7 +59,6 @@ export const config: Config = { textColor: 'black', fontVariant: 'normal', fontWeight: 'normal', - textOpacity: 1, baseline: 'verticalAlign' as CanvasTextBaseline, padding: 5, formatter: String, @@ -93,7 +91,6 @@ export const config: Config = { textColor: 'black', fontVariant: 'normal', fontWeight: 'normal', - textOpacity: 1, useGlobalMinFontSize: true, }, border: { diff --git a/packages/charts/src/chart_types/heatmap/layout/types/viewmodel_types.ts b/packages/charts/src/chart_types/heatmap/layout/types/viewmodel_types.ts index de51d264f2b..8372780e429 100644 --- a/packages/charts/src/chart_types/heatmap/layout/types/viewmodel_types.ts +++ b/packages/charts/src/chart_types/heatmap/layout/types/viewmodel_types.ts @@ -115,7 +115,7 @@ export const nullHeatmapViewModel: HeatmapViewModel = { gridLines: { x: [], y: [], - stroke: { width: 0, color: { r: 0, g: 0, b: 0, opacity: 0 } }, + stroke: { width: 0, color: [0, 0, 0, 0] }, }, cells: [], xValues: [], diff --git a/packages/charts/src/chart_types/heatmap/layout/viewmodel/viewmodel.ts b/packages/charts/src/chart_types/heatmap/layout/viewmodel/viewmodel.ts index 4022c3963f5..455af533286 100644 --- a/packages/charts/src/chart_types/heatmap/layout/viewmodel/viewmodel.ts +++ b/packages/charts/src/chart_types/heatmap/layout/viewmodel/viewmodel.ts @@ -9,7 +9,7 @@ import { bisectLeft } from 'd3-array'; import { scaleBand, scaleQuantize } from 'd3-scale'; -import { stringToRGB } from '../../../../common/color_library_wrappers'; +import { colorToRgba } from '../../../../common/color_library_wrappers'; import { fillTextColor } from '../../../../common/fill_text_color'; import { Pixels } from '../../../../common/geometry'; import { Box, maximiseFontSize, TextMeasure } from '../../../../common/text_utils'; @@ -21,6 +21,7 @@ import { snapDateToESInterval } from '../../../../utils/chrono/elasticsearch'; import { clamp, range } from '../../../../utils/common'; import { Dimensions } from '../../../../utils/dimensions'; import { ContinuousDomain } from '../../../../utils/domain'; +import { Logger } from '../../../../utils/logger'; import { Theme } from '../../../../utils/themes/theme'; import { PrimitiveValue } from '../../../partition_chart/layout/utils/group_by_rollup'; import { HeatmapSpec } from '../../specs'; @@ -183,16 +184,24 @@ export function shapeViewModel( const cellWidthInner = cellWidth - gridStrokeWidth * 2; const cellHeightInner = cellHeight - gridStrokeWidth * 2; + if (colorToRgba(theme.background.color)[3] < 1) { + Logger.expected( + `Text contrast requires a opaque background color, using white as fallback`, + 'an opaque color', + theme.background.color, + ); + } + // compute each available cell position, color and value const cellMap = table.reduce>((acc, d) => { const x = xScale(String(d.x)); const y = yScale(String(d.y))! + gridStrokeWidth; const yIndex = yValues.indexOf(d.y); - // cell background color - const color = colorScale(d.value); + if (x === undefined || y === undefined || yIndex === -1) { return acc; } + const cellBackgroundColor = colorScale(d.value); const cellKey = getCellKey(d.x, d.y); const formattedValue = spec.valueFormatter(d.value); @@ -217,23 +226,17 @@ export function shapeViewModel( height: cellHeightInner, datum: d, fill: { - color: stringToRGB(color), + color: colorToRgba(cellBackgroundColor), }, stroke: { - color: stringToRGB(config.cell.border.stroke), + color: colorToRgba(config.cell.border.stroke), width: config.cell.border.strokeWidth, }, value: d.value, visible: !isValueHidden(d.value, bandsToHide), formatted: formattedValue, fontSize, - textColor: fillTextColor( - config.cell.label.textColor, - true, - 4.5, - color, - theme.background.color === 'transparent' ? 'rgba(255, 255, 255, 1)' : theme.background.color, - ), + textColor: fillTextColor(cellBackgroundColor, theme.background.color), }; return acc; }, {}); @@ -398,7 +401,7 @@ export function shapeViewModel( x: xLines, y: yLines, stroke: { - color: stringToRGB(config.grid.stroke.color), + color: colorToRgba(config.grid.stroke.color), width: gridStrokeWidth, }, }, diff --git a/packages/charts/src/chart_types/heatmap/renderer/canvas/canvas_renderers.ts b/packages/charts/src/chart_types/heatmap/renderer/canvas/canvas_renderers.ts index 1b2bda57d92..c4640c6bf97 100644 --- a/packages/charts/src/chart_types/heatmap/renderer/canvas/canvas_renderers.ts +++ b/packages/charts/src/chart_types/heatmap/renderer/canvas/canvas_renderers.ts @@ -8,6 +8,7 @@ import { Font } from '../../../../common/text_utils'; import { clearCanvas, renderLayers, withContext } from '../../../../renderers/canvas'; +import { Color } from '../../../../utils/common'; import { renderMultiLine } from '../../../xy_chart/renderer/canvas/primitives/line'; import { renderRect } from '../../../xy_chart/renderer/canvas/primitives/rect'; import { renderText, wrapLines } from '../../../xy_chart/renderer/canvas/primitives/text'; @@ -18,6 +19,7 @@ export function renderCanvas2d( ctx: CanvasRenderingContext2D, dpr: number, { config, heatmapViewModel }: ShapeViewModel, + background: Color, ) { // eslint-disable-next-line no-empty-pattern const {} = config; @@ -45,8 +47,7 @@ export function renderCanvas2d( const filteredYValues = heatmapViewModel.yValues.filter((value, yIndex) => yIndex < heatmapViewModel.pageSize); renderLayers(ctx, [ - clearCanvas, - + () => clearCanvas(ctx, background), () => { withContext(ctx, () => { // render grid @@ -95,7 +96,6 @@ export function renderCanvas2d( fontVariant: 'normal', fontWeight: 'normal', textColor: 'black', - textOpacity: 1, }; const { padding } = config.yAxisLabel; const horizontalPadding = diff --git a/packages/charts/src/chart_types/heatmap/renderer/canvas/connected_component.tsx b/packages/charts/src/chart_types/heatmap/renderer/canvas/connected_component.tsx index baa570a22da..7af736738db 100644 --- a/packages/charts/src/chart_types/heatmap/renderer/canvas/connected_component.tsx +++ b/packages/charts/src/chart_types/heatmap/renderer/canvas/connected_component.tsx @@ -18,7 +18,9 @@ import { DEFAULT_A11Y_SETTINGS, getA11ySettingsSelector, } from '../../../../state/selectors/get_accessibility_config'; +import { getChartThemeSelector } from '../../../../state/selectors/get_chart_theme'; import { getInternalIsInitializedSelector, InitStatus } from '../../../../state/selectors/get_internal_is_intialized'; +import { Color } from '../../../../utils/common'; import { Dimensions } from '../../../../utils/dimensions'; import { nullShapeViewModel, ShapeViewModel } from '../../layout/types/viewmodel_types'; import { geometries } from '../../state/selectors/geometries'; @@ -30,6 +32,7 @@ interface ReactiveChartStateProps { geometries: ShapeViewModel; chartContainerDimensions: Dimensions; a11ySettings: A11ySettings; + background: Color; } interface ReactiveChartDispatchProps { @@ -86,10 +89,15 @@ class Component extends React.Component { private drawCanvas() { if (this.ctx) { const { width, height }: Dimensions = this.props.chartContainerDimensions; - renderCanvas2d(this.ctx, this.devicePixelRatio, { - ...this.props.geometries, - config: { ...this.props.geometries.config, width, height }, - }); + renderCanvas2d( + this.ctx, + this.devicePixelRatio, + { + ...this.props.geometries, + config: { ...this.props.geometries.config, width, height }, + }, + this.props.background, + ); } } @@ -143,6 +151,7 @@ const DEFAULT_PROPS: ReactiveChartStateProps = { top: 0, }, a11ySettings: DEFAULT_A11Y_SETTINGS, + background: 'transparent', }; const mapStateToProps = (state: GlobalChartState): ReactiveChartStateProps => { @@ -154,6 +163,7 @@ const mapStateToProps = (state: GlobalChartState): ReactiveChartStateProps => { geometries: geometries(state), chartContainerDimensions: getHeatmapContainerSizeSelector(state), a11ySettings: getA11ySettingsSelector(state), + background: getChartThemeSelector(state).background.color, }; }; diff --git a/packages/charts/src/chart_types/heatmap/state/selectors/get_debug_state.ts b/packages/charts/src/chart_types/heatmap/state/selectors/get_debug_state.ts index c9a76931345..b1259288cf9 100644 --- a/packages/charts/src/chart_types/heatmap/state/selectors/get_debug_state.ts +++ b/packages/charts/src/chart_types/heatmap/state/selectors/get_debug_state.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { RGBtoString } from '../../../../common/color_library_wrappers'; +import { RGBATupleToString } from '../../../../common/color_library_wrappers'; import { LegendItem } from '../../../../common/legend'; import { createCustomCachedSelector } from '../../../../state/create_selector'; import { DebugState, DebugStateLegend } from '../../../../state/types'; @@ -53,7 +53,7 @@ export const getDebugStateSelector = createCustomCachedSelector( cells: geoms.heatmapViewModel.cells.map(({ x, y, fill, formatted, value }) => ({ x, y, - fill: RGBtoString(fill.color), + fill: RGBATupleToString(fill.color), formatted, value, })), diff --git a/packages/charts/src/chart_types/heatmap/state/selectors/tooltip.ts b/packages/charts/src/chart_types/heatmap/state/selectors/tooltip.ts index 15218ae5b5e..216a3a2396d 100644 --- a/packages/charts/src/chart_types/heatmap/state/selectors/tooltip.ts +++ b/packages/charts/src/chart_types/heatmap/state/selectors/tooltip.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { RGBtoString } from '../../../../common/color_library_wrappers'; +import { RGBATupleToString } from '../../../../common/color_library_wrappers'; import { TooltipInfo } from '../../../../components/tooltip/types'; import { createCustomCachedSelector } from '../../../../state/create_selector'; import { getHeatmapConfigSelector } from './get_heatmap_config'; @@ -68,7 +68,7 @@ export const getTooltipInfoSelector = createCustomCachedSelector( // Cell value tooltipInfo.values.push({ label: spec.name ?? spec.id, - color: RGBtoString(shape.fill.color), + color: RGBATupleToString(shape.fill.color), isHighlighted: false, isVisible: true, seriesIdentifier: { diff --git a/packages/charts/src/chart_types/partition_chart/layout/config.ts b/packages/charts/src/chart_types/partition_chart/layout/config.ts index 7d7ff9b1208..ef9365a487d 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/config.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/config.ts @@ -196,8 +196,6 @@ export const configMetadata: Record = { type: 'group', values: { textColor: { type: 'color', dflt: '#000000' }, - textInvertible: { dflt: false, type: 'boolean' }, - textContrast: { dflt: false, type: 'boolean' || 'number' }, ...fontSettings, valueGetter: { dflt: sumValueGetter, @@ -285,9 +283,6 @@ export const configMetadata: Record = { documentation: 'Limits the total number of characters in linked labels.', }, textColor: { dflt: '#000000', type: 'color' }, - textInvertible: { dflt: false, type: 'boolean' }, - textContrast: { dflt: false, type: 'boolean' || 'number' }, - textOpacity: { dflt: 1, min: 0, max: 1, type: 'number' }, minimumStemLength: { dflt: 0, min: 0, diff --git a/packages/charts/src/chart_types/partition_chart/layout/types/config_types.ts b/packages/charts/src/chart_types/partition_chart/layout/types/config_types.ts index 92360860a55..e6d0a2ac8d4 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/types/config_types.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/types/config_types.ts @@ -9,7 +9,7 @@ import { $Values as Values } from 'utility-types'; import { Distance, Pixels, Radian, Radius, Ratio, SizeRatio, TimeMs } from '../../../../common/geometry'; -import { Font, FontFamily, PartialFont, TextContrast } from '../../../../common/text_utils'; +import { Font, FontFamily, PartialFont } from '../../../../common/text_utils'; import { Color, StrokeStyle, ValueFormatter } from '../../../../utils/common'; import { PerSideDistance } from '../../../../utils/dimensions'; @@ -34,9 +34,6 @@ export type Padding = Pixels | Partial; interface LabelConfig extends Font { textColor: Color; - textInvertible: boolean; - textContrast: TextContrast; - textOpacity: Ratio; valueFormatter: ValueFormatter; valueFont: PartialFont; padding: Padding; diff --git a/packages/charts/src/chart_types/partition_chart/layout/types/viewmodel_types.ts b/packages/charts/src/chart_types/partition_chart/layout/types/viewmodel_types.ts index dd90b8b9db5..aba665ad932 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/types/viewmodel_types.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/types/viewmodel_types.ts @@ -141,7 +141,6 @@ const defaultFont: Font = { fontFamily: '', fontWeight: 'normal', textColor: 'black', - textOpacity: 1, }; /** @internal */ @@ -170,7 +169,6 @@ export const nullPartitionSmallMultiplesModel = (partitionLayout: PartitionLayou fontWeight: 'normal', fontFamily: 'sans-serif', fontStyle: 'normal', - textOpacity: 1, textColor: 'black', }, }, diff --git a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/fill_text_layout.test.ts b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/fill_text_layout.test.ts index 156fe7bc9f7..a01c055aa11 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/fill_text_layout.test.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/fill_text_layout.test.ts @@ -263,27 +263,11 @@ describe('Test that getRectangleRowGeometry works with:', () => { }); }); }); -describe('Test getTextColor function', () => { - test('getTextColor works with textContrast greater than default ratio', () => { - const textColor = 'black'; - const textInvertible = true; - const textContrast = 6; +describe('Test fillTextColor function', () => { + test('get the right maximized contrast color', () => { const fillColor = 'rgba(55, 126, 184, 0.7)'; const containerBackgroundColor = 'white'; - const expectedAdjustedTextColor = 'black'; - expect(fillTextColor(textColor, textInvertible, textContrast, fillColor, containerBackgroundColor)).toEqual( - expectedAdjustedTextColor, - ); - }); - test('getTextColor works with textContrast not defined', () => { - const textColor = 'black'; - const textInvertible = true; - const textContrast = false; - const fillColor = 'rgba(55, 126, 184, 0.7)'; - const containerBackgroundColor = 'white'; - const expectedAdjustedTextColor = 'black'; - expect(fillTextColor(textColor, textInvertible, textContrast, fillColor, containerBackgroundColor)).toEqual( - expectedAdjustedTextColor, - ); + const expectedAdjustedTextColor = 'rgba(0, 0, 0, 1)'; // with WCAG 2 is black + expect(fillTextColor(fillColor, containerBackgroundColor)).toEqual(expectedAdjustedTextColor); }); }); diff --git a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/fill_text_layout.ts b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/fill_text_layout.ts index a3550300146..9a0fd3ab064 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/fill_text_layout.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/fill_text_layout.ts @@ -236,7 +236,7 @@ function fill( ? VerticalAlignments.bottom : VerticalAlignments.top; const fontSizes = allFontSizes[Math.min(node.depth, allFontSizes.length) - 1]; - const { fontStyle, fontVariant, fontFamily, fontWeight, valueFormatter, padding, textOpacity, clipText } = { + const { fontStyle, fontVariant, fontFamily, fontWeight, valueFormatter, padding, clipText } = { ...fillLabel, valueFormatter: formatter, ...layer.fillLabel, @@ -256,7 +256,6 @@ function fill( fontWeight, fontFamily, textColor: node.textColor, - textOpacity, }; const allBoxes = getAllBoxes(rawTextGetter, valueGetter, valueFormatter, sizeInvariantFont, valueFont, node); const [cx, cy] = textFillOrigin; diff --git a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/link_text_layout.ts b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/link_text_layout.ts index ca53cf609d4..fb08f60dfa7 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/link_text_layout.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/link_text_layout.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ -import { getOnPaperColorSet } from '../../../../common/color_calcs'; +import { colorToRgba } from '../../../../common/color_library_wrappers'; import { TAU } from '../../../../common/constants'; +import { fillTextColor } from '../../../../common/fill_text_color'; import { Distance, meanAngle, @@ -18,6 +19,7 @@ import { } from '../../../../common/geometry'; import { cutToLength, fitText, Font, measureOneBoxWidth, TextMeasure } from '../../../../common/text_utils'; import { Color, ValueFormatter } from '../../../../utils/common'; +import { Logger } from '../../../../utils/logger'; import { Point } from '../../../../utils/point'; import { Config, LinkLabelConfig } from '../types/config_types'; import { LinkLabelVM, RawTextGetter, ShapeTreeNode, ValueGetterFunction } from '../types/viewmodel_types'; @@ -44,21 +46,13 @@ export function linkTextLayout( valueFormatter: ValueFormatter, maxTextLength: number, diskCenter: Point, - containerBackgroundColor?: Color, + containerBgColor: Color = 'rgba(255,255,255,1)', ): LinkLabelsViewModelSpec { - const { linkLabel, sectorLineStroke } = config; + const { linkLabel } = config; const maxDepth = nodesWithoutRoom.reduce((p: number, n: ShapeTreeNode) => Math.max(p, n.depth), 0); const yRelativeIncrement = Math.sin(linkLabel.stemAngle) * linkLabel.minimumStemLength; const rowPitch = linkLabel.fontSize + linkLabel.spacing; - const { contrastTextColor, strokeColor } = getOnPaperColorSet( - linkLabel.textColor, - sectorLineStroke, - containerBackgroundColor, - ); - const labelFontSpec: Font = { ...linkLabel, textColor: contrastTextColor }; - const valueFontSpec: Font = { ...linkLabel, ...linkLabel.valueFont, textColor: contrastTextColor }; - const linkLabels: LinkLabelVM[] = nodesWithoutRoom .filter((n: ShapeTreeNode) => n.depth === maxDepth) // only the outermost ring can have links .sort((n1: ShapeTreeNode, n2: ShapeTreeNode) => Math.abs(n2.x0 - n2.x1) - Math.abs(n1.x0 - n1.x1)) @@ -83,7 +77,18 @@ export function linkTextLayout( ) .filter(({ text }) => text !== ''); // cull linked labels whose text was truncated to nothing; - return { linkLabels, valueFontSpec, labelFontSpec, strokeColor }; + if (colorToRgba(containerBgColor)[3] < 1) { + Logger.expected( + `Text contrast requires a opaque background color, using white as fallback`, + 'an opaque color', + containerBgColor, + ); + } + const textColor = fillTextColor(containerBgColor); + const labelFontSpec: Font = { ...linkLabel, textColor }; + const valueFontSpec: Font = { ...linkLabel, ...linkLabel.valueFont, textColor }; + + return { linkLabels, valueFontSpec, labelFontSpec, strokeColor: textColor }; } function linkLabelCompare(n1: ShapeTreeNode, n2: ShapeTreeNode) { diff --git a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts index 1ac9b509d89..71cc1245253 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { argsToRGBString, stringToRGB } from '../../../../common/color_library_wrappers'; +import { colorToRgba } from '../../../../common/color_library_wrappers'; import { TAU } from '../../../../common/constants'; import { fillTextColor } from '../../../../common/fill_text_color'; import { @@ -20,6 +20,7 @@ import { import { Part, TextMeasure } from '../../../../common/text_utils'; import { GroupByAccessor, SmallMultiplesStyle } from '../../../../specs'; import { StrokeStyle, ValueFormatter, Color, RecursivePartial } from '../../../../utils/common'; +import { Logger } from '../../../../utils/logger'; import { Layer } from '../../specs'; import { config as defaultConfig, MODEL_KEY, percentValueGetter } from '../config'; import { Config, FillLabelConfig, PartitionLayout } from '../types/config_types'; @@ -140,23 +141,23 @@ export function makeQuadViewModel( isSunburstLayout: boolean, containerBackgroundColor?: Color, ): Array { + if (colorToRgba(containerBackgroundColor ?? 'white')[3] < 1) { + Logger.expected( + `Text contrast requires a opaque background color, using white as fallback`, + 'an opaque color', + containerBackgroundColor, + ); + } return childNodes.map((node) => { - const opacityMultiplier = 1; // could alter in the future, eg. in response to interactions const layer = layers[node.depth - 1]; - const fillColorSpec = layer && layer.shape && layer.shape.fillColor; - const fill = fillColorSpec ?? 'rgba(128,0,0,0.5)'; - const shapeFillColor = typeof fill === 'function' ? fill(node, node.sortIndex, node[MODEL_KEY].children) : fill; - const { r, g, b, opacity } = stringToRGB(shapeFillColor); - const fillColor = argsToRGBString(r, g, b, opacity * opacityMultiplier); + const fill = layer?.shape?.fillColor ?? 'rgba(128, 0, 0, 0.5)'; + const fillColor = typeof fill === 'function' ? fill(node, node.sortIndex, node[MODEL_KEY].children) : fill; const strokeWidth = sectorLineWidth; const strokeStyle = sectorLineStroke; const textNegligible = node.y1px - node.y0px < minRectHeightForText; - const { textColor, textInvertible, textContrast } = { ...fillLabel, ...layer.fillLabel }; - const color = - !isSunburstLayout && textNegligible - ? 'transparent' - : fillTextColor(textColor, textInvertible, textContrast, fillColor, containerBackgroundColor); - return { index, innerIndex, smAccessorValue, strokeWidth, strokeStyle, fillColor, textColor: color, ...node }; + const textColor = + !isSunburstLayout && textNegligible ? 'transparent' : fillTextColor(fillColor, containerBackgroundColor); + return { index, innerIndex, smAccessorValue, strokeWidth, strokeStyle, fillColor, textColor, ...node }; }); } diff --git a/packages/charts/src/chart_types/partition_chart/renderer/canvas/canvas_renderers.ts b/packages/charts/src/chart_types/partition_chart/renderer/canvas/canvas_renderers.ts index 418aff27bdd..dfaaef13fc3 100644 --- a/packages/charts/src/chart_types/partition_chart/renderer/canvas/canvas_renderers.ts +++ b/packages/charts/src/chart_types/partition_chart/renderer/canvas/canvas_renderers.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { addOpacity } from '../../../../common/color_calcs'; +import { colorToRgba, RGBATupleToString } from '../../../../common/color_library_wrappers'; import { TAU } from '../../../../common/constants'; import { Pixels } from '../../../../common/geometry'; import { cssFontShorthand } from '../../../../common/text_utils'; @@ -189,8 +189,8 @@ function renderLinkLabels( { linkLabels: allLinkLabels, labelFontSpec, valueFontSpec, strokeColor }: LinkLabelsViewModelSpec, linkLineColor: Color, ) { - const labelColor = addOpacity(labelFontSpec.textColor, labelFontSpec.textOpacity); - const valueColor = addOpacity(valueFontSpec.textColor, valueFontSpec.textOpacity); + const labelColor = RGBATupleToString(colorToRgba(labelFontSpec.textColor)); + const valueColor = RGBATupleToString(colorToRgba(valueFontSpec.textColor)); const labelValueGap = linkLabelFontSize / 2; // one en space withContext(ctx, () => { ctx.lineWidth = linkLabelLineWidth; @@ -240,7 +240,7 @@ export function renderPartitionCanvas2d( ) { const { sectorLineWidth, sectorLineStroke, linkLabel } = config; - const linkLineColor = addOpacity(linkLabel.textColor, linkLabel.textOpacity); + const linkLineColor = RGBATupleToString(colorToRgba(linkLabel.textColor)); withContext(ctx, () => { // set some defaults for the overall rendering diff --git a/packages/charts/src/chart_types/partition_chart/renderer/canvas/partition.tsx b/packages/charts/src/chart_types/partition_chart/renderer/canvas/partition.tsx index 3e8d89e10b4..2af01408c6e 100644 --- a/packages/charts/src/chart_types/partition_chart/renderer/canvas/partition.tsx +++ b/packages/charts/src/chart_types/partition_chart/renderer/canvas/partition.tsx @@ -21,8 +21,10 @@ import { getA11ySettingsSelector, } from '../../../../state/selectors/get_accessibility_config'; import { getChartContainerDimensionsSelector } from '../../../../state/selectors/get_chart_container_dimensions'; +import { getChartThemeSelector } from '../../../../state/selectors/get_chart_theme'; import { getInternalIsInitializedSelector, InitStatus } from '../../../../state/selectors/get_internal_is_intialized'; import { getSettingsSpecSelector } from '../../../../state/selectors/get_settings_specs'; +import { Color } from '../../../../utils/common'; import { Dimensions } from '../../../../utils/dimensions'; import { MODEL_KEY } from '../../layout/config'; import { @@ -57,6 +59,7 @@ interface ReactiveChartStateProps { chartContainerDimensions: Dimensions; a11ySettings: A11ySettings; debug: SettingsSpec['debug']; + background: Color; } interface ReactiveChartDispatchProps { @@ -174,7 +177,7 @@ class PartitionComponent extends React.Component { private drawCanvas() { if (this.ctx) { const { ctx, devicePixelRatio, props } = this; - clearCanvas(ctx); + clearCanvas(ctx, props.background); props.multiGeometries.forEach((geometries, geometryIndex) => { const renderer = isSimpleLinear(geometries.config, geometries.layers) ? renderLinearPartitionCanvas2d @@ -208,6 +211,7 @@ const DEFAULT_PROPS: ReactiveChartStateProps = { }, a11ySettings: DEFAULT_A11Y_SETTINGS, debug: false, + background: 'transparent', }; const mapStateToProps = (state: GlobalChartState): ReactiveChartStateProps => { @@ -223,6 +227,7 @@ const mapStateToProps = (state: GlobalChartState): ReactiveChartStateProps => { geometriesFoci: partitionDrilldownFocus(state), a11ySettings: getA11ySettingsSelector(state), debug: getSettingsSpecSelector(state).debug, + background: getChartThemeSelector(state).background.color, }; }; diff --git a/packages/charts/src/chart_types/partition_chart/state/selectors/geometries.ts b/packages/charts/src/chart_types/partition_chart/state/selectors/geometries.ts index a12286e2438..22a083f68d4 100644 --- a/packages/charts/src/chart_types/partition_chart/state/selectors/geometries.ts +++ b/packages/charts/src/chart_types/partition_chart/state/selectors/geometries.ts @@ -178,7 +178,6 @@ export const partitionMultiGeometries = createCustomCachedSelector( fontWeight: 'normal', fontVariant: 'normal', textColor: axisPanelTitle.fill, - textOpacity: 1, }; return getShapeViewModel(spec, parentDimensions, t, background.color, style, { index, diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/annotations/lines.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/annotations/lines.ts index cd58e238476..e98fdbdfb00 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/annotations/lines.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/annotations/lines.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { stringToRGB } from '../../../../../common/color_library_wrappers'; +import { colorToRgba, overrideOpacity } from '../../../../../common/color_library_wrappers'; import { Stroke } from '../../../../../geoms/types'; import { Rotation } from '../../../../../utils/common'; import { Dimensions } from '../../../../../utils/dimensions'; @@ -23,8 +23,10 @@ export function renderLineAnnotations( rotation: Rotation, renderingArea: Dimensions, ) { - const strokeColor = stringToRGB(lineStyle.line.stroke); - strokeColor.opacity *= lineStyle.line.opacity; + const strokeColor = overrideOpacity( + colorToRgba(lineStyle.line.stroke), + (opacity) => opacity * lineStyle.line.opacity, + ); const stroke: Stroke = { color: strokeColor, width: lineStyle.line.strokeWidth, diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/annotations/rect.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/annotations/rect.ts index 51b420575f1..6b8d8e7fb04 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/annotations/rect.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/annotations/rect.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { stringToRGB } from '../../../../../common/color_library_wrappers'; +import { colorToRgba, overrideOpacity } from '../../../../../common/color_library_wrappers'; import { Fill, Stroke } from '../../../../../geoms/types'; import { Rotation } from '../../../../../utils/common'; import { Dimensions } from '../../../../../utils/dimensions'; @@ -23,12 +23,13 @@ export function renderRectAnnotations( rotation: Rotation, renderingArea: Dimensions, ) { - const fillColor = stringToRGB(rectStyle.fill); - fillColor.opacity *= rectStyle.opacity; - const fill: Fill = { color: fillColor }; - const strokeColor = stringToRGB(rectStyle.stroke); - strokeColor.opacity *= rectStyle.opacity; - const stroke: Stroke = { color: strokeColor, width: rectStyle.strokeWidth }; + const fill: Fill = { + color: overrideOpacity(colorToRgba(rectStyle.fill), (opacity) => opacity * rectStyle.opacity), + }; + const stroke: Stroke = { + color: overrideOpacity(colorToRgba(rectStyle.stroke), (opacity) => opacity * rectStyle.opacity), + width: rectStyle.strokeWidth, + }; annotations.forEach(({ rect, panel }) => withPanelTransform(ctx, panel, rotation, renderingArea, () => renderRect(ctx, rect, fill, stroke)), diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/axes/tick.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/axes/tick.ts index 64bededbcc0..fc131f44ef5 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/axes/tick.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/axes/tick.ts @@ -7,7 +7,7 @@ */ import { AxisProps } from '.'; -import { stringToRGB } from '../../../../../common/color_library_wrappers'; +import { colorToRgba } from '../../../../../common/color_library_wrappers'; import { Position } from '../../../../../utils/common'; import { isHorizontalAxis } from '../../../utils/axis_type_utils'; import { AxisTick } from '../../../utils/axis_utils'; @@ -30,5 +30,5 @@ export function renderTick( y2: tickPosition, ...(axisPosition === Position.Left ? { x1: width, x2: width - tickLine.size } : { x1: 0, x2: tickLine.size }), }; - renderMultiLine(ctx, [xy], { color: stringToRGB(tickLine.stroke), width: tickLine.strokeWidth }); + renderMultiLine(ctx, [xy], { color: colorToRgba(tickLine.stroke), width: tickLine.strokeWidth }); } diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/axes/tick_label.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/axes/tick_label.ts index 6cf6a8da972..3cde7b4fb87 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/axes/tick_label.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/axes/tick_label.ts @@ -53,7 +53,6 @@ export function renderTickLabel( fontVariant: 'normal', fontWeight: 'normal', textColor: labelStyle.fill, - textOpacity: 1, fontSize: labelStyle.fontSize, align: tickLabelProps.horizontalAlign, baseline: tickLabelProps.verticalAlign, diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/panels/panels.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/panels/panels.ts index 553bc265209..7baa2b4d1ed 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/panels/panels.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/panels/panels.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { stringToRGB } from '../../../../../common/color_library_wrappers'; import { withContext } from '../../../../../renderers/canvas'; import { Position } from '../../../../../utils/common'; import { AxisId } from '../../../../../utils/ids'; @@ -26,8 +25,8 @@ export function renderGridPanels(ctx: CanvasRenderingContext2D, { x: chartX, y: renderRect( ctx, { x: chartX + panelX, y: chartY + panelY, width, height }, - { color: stringToRGB('rgba(0,0,0,0)') }, - { color: stringToRGB('rgb(0,0,0)'), width: 1 }, + { color: [0, 0, 0, 0] }, + { color: [0, 0, 0, 1], width: 1 }, ), ), ); diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/panels/title.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/panels/title.ts index 4433a730686..cb5dbd4ec17 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/panels/title.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/panels/title.ts @@ -22,7 +22,6 @@ const titleFontDefaults: Omit fontVariant: 'normal', fontStyle: 'normal', // may be overridden (happens if prop on axis style is defined) fontWeight: 'bold', - textOpacity: 1, align: 'center', baseline: 'middle', }; diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/points.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/points.ts index eb70c043d44..a76b3d2508e 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/points.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/points.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { RgbObject } from '../../../../common/color_library_wrappers'; +import { overrideOpacity } from '../../../../common/color_library_wrappers'; import { SeriesKey } from '../../../../common/series_id'; import { Circle, Fill, Rect, Stroke } from '../../../../geoms/types'; import { Rotation } from '../../../../utils/common'; @@ -27,8 +27,11 @@ export function renderPoints(ctx: CanvasRenderingContext2D, points: PointGeometr .sort(({ radius: a }, { radius: b }) => b - a) .forEach(({ x, y, radius, transform, style }) => { const coordinates = { x: x + transform.x, y: y + transform.y, radius }; - const fill = { color: applyOpacity(style.fill.color, opacity) }; - const stroke = { ...style.stroke, color: applyOpacity(style.stroke.color, opacity) }; + const fill = { color: overrideOpacity(style.fill.color, (fillOpacity) => fillOpacity * opacity) }; + const stroke = { + ...style.stroke, + color: overrideOpacity(style.stroke.color, (fillOpacity) => fillOpacity * opacity), + }; renderShape(ctx, style.shape, coordinates, fill, stroke); }); } @@ -52,14 +55,13 @@ export function renderPointGroup( .sort(({ radius: a }, { radius: b }) => b - a) .forEach(({ x, y, radius, transform, style, seriesIdentifier: { key }, panel }) => { const { opacity } = geometryStateStyles[key]; - const fill: Fill = { color: applyOpacity(style.fill.color, opacity) }; - const stroke: Stroke = { ...style.stroke, color: applyOpacity(style.stroke.color, opacity) }; + const fill: Fill = { color: overrideOpacity(style.fill.color, (fillOpacity) => fillOpacity * opacity) }; + const stroke: Stroke = { + ...style.stroke, + color: overrideOpacity(style.stroke.color, (fillOpacity) => fillOpacity * opacity), + }; const coordinates: Circle = { x: x + transform.x, y, radius }; const renderer = () => renderShape(ctx, style.shape, coordinates, fill, stroke); withPanelTransform(ctx, panel, rotation, renderingArea, renderer, { area: clippings, shouldClip }); }); } - -function applyOpacity(color: RgbObject, opacity: number): RgbObject { - return { ...color, opacity: color.opacity * opacity }; -} diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/line.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/line.ts index af7a2a58944..b50d60f88f1 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/line.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/line.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { RGBtoString } from '../../../../../common/color_library_wrappers'; +import { RGBATupleToString } from '../../../../../common/color_library_wrappers'; import { Line, Stroke } from '../../../../../geoms/types'; import { withContext } from '../../../../../renderers/canvas'; @@ -23,7 +23,7 @@ export function renderMultiLine(ctx: CanvasRenderingContext2D, lines: Line[] | s return; } withContext(ctx, () => { - ctx.strokeStyle = RGBtoString(stroke.color); + ctx.strokeStyle = RGBATupleToString(stroke.color); ctx.lineJoin = 'round'; ctx.lineWidth = stroke.width; if (stroke.dash) { diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/path.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/path.ts index 49c23161e13..465c2e206b6 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/path.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/path.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { RGBtoString } from '../../../../../common/color_library_wrappers'; +import { overrideOpacity, RGBATupleToString } from '../../../../../common/color_library_wrappers'; import { Fill, Rect, Stroke } from '../../../../../geoms/types'; import { withClipRanges } from '../../../../../renderers/canvas'; import { ClippedRanges } from '../../../../../utils/geometry'; @@ -48,7 +48,7 @@ export function renderAreaPath( withClipRanges(ctx, clippedRanges, clippings, false, () => renderPathFill(ctx, area, fill, transform)); if (clippedRanges.length > 0 && !hideClippedRanges) { withClipRanges(ctx, clippedRanges, clippings, true, () => - renderPathFill(ctx, area, { ...fill, color: { ...fill.color, opacity: fill.color.opacity / 2 } }, transform), + renderPathFill(ctx, area, { ...fill, color: overrideOpacity(fill.color, fill.color[3] / 2) }, transform), ); } } @@ -56,7 +56,7 @@ export function renderAreaPath( function renderPathFill(ctx: CanvasRenderingContext2D, path: string, fill: Fill, { x, y }: Point) { ctx.translate(x, y); const path2d = new Path2D(path); - ctx.fillStyle = RGBtoString(fill.color); + ctx.fillStyle = RGBATupleToString(fill.color); ctx.fill(path2d); if (fill.texture) { diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/rect.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/rect.ts index 23390867d85..bf1d9d98d5b 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/rect.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/rect.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { RGBtoString } from '../../../../../common/color_library_wrappers'; +import { RGBATupleToString } from '../../../../../common/color_library_wrappers'; import { Fill, Rect, Stroke } from '../../../../../geoms/types'; import { MIN_STROKE_WIDTH } from './line'; @@ -20,7 +20,7 @@ export function renderRect( ) { const borderOffset = !disableBorderOffset && stroke.width >= MIN_STROKE_WIDTH ? stroke.width : 0; if (stroke.width >= MIN_STROKE_WIDTH && height >= borderOffset && width >= borderOffset) { - ctx.strokeStyle = RGBtoString(stroke.color); + ctx.strokeStyle = RGBATupleToString(stroke.color); ctx.lineWidth = stroke.width; ctx.beginPath(); ctx.rect(x + borderOffset / 2, y + borderOffset / 2, width - borderOffset, height - borderOffset); @@ -31,7 +31,7 @@ export function renderRect( ctx.beginPath(); ctx.rect(x + borderOffset, y + borderOffset, width - borderOffset * 2, height - borderOffset * 2); - ctx.fillStyle = RGBtoString(color); + ctx.fillStyle = RGBATupleToString(color); ctx.fill(); if (texture) { diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/shapes.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/shapes.ts index d57776539e5..37aa45ff330 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/shapes.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/primitives/shapes.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { RGBtoString } from '../../../../../common/color_library_wrappers'; +import { RGBATupleToString } from '../../../../../common/color_library_wrappers'; import { Circle, Fill, Stroke } from '../../../../../geoms/types'; import { withContext } from '../../../../../renderers/canvas'; import { degToRad } from '../../../../../utils/common'; @@ -30,11 +30,11 @@ export function renderShape( const path = new Path2D(pathFn(radius)); - ctx.fillStyle = RGBtoString(fillColor); + ctx.fillStyle = RGBATupleToString(fillColor); ctx.fill(path); if (width > MIN_STROKE_WIDTH) { - ctx.strokeStyle = RGBtoString(strokeColor); + ctx.strokeStyle = RGBATupleToString(strokeColor); ctx.lineWidth = width; ctx.setLineDash(dash ?? []); ctx.stroke(path); diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/renderers.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/renderers.ts index e6d0dcb096b..67edc786751 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/renderers.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/renderers.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { stringToRGB } from '../../../../common/color_library_wrappers'; import { Rect } from '../../../../geoms/types'; import { clearCanvas, isCanvasRenderer, renderLayers, withContext } from '../../../../renderers/canvas'; import { renderAnnotations } from './annotations'; @@ -38,7 +37,7 @@ export function renderXYChartCanvas2d( rotation, geometries, geometriesIndex, - theme: { axes: sharedAxesStyle, sharedStyle, barSeriesStyle }, + theme: { axes: sharedAxesStyle, sharedStyle, barSeriesStyle, background }, highlightedLegendItem, annotationDimensions, annotationSpecs, @@ -51,7 +50,7 @@ export function renderXYChartCanvas2d( } = props; const transform = { x: renderingArea.left + chartTransform.x, y: renderingArea.top + chartTransform.y }; renderLayers(ctx, [ - clearCanvas, + () => clearCanvas(ctx, background.color), // render panel grid () => debug && renderGridPanels(ctx, transform, panelGeoms), @@ -149,8 +148,8 @@ export function renderXYChartCanvas2d( ctx, { x: left, y: top, width, height }, 0, - { color: stringToRGB('transparent') }, - { color: stringToRGB('red'), width: 4, dash: [4, 4] }, + { color: [0, 0, 0, 0] }, + { color: [255, 0, 0, 1], width: 4, dash: [4, 4] }, ); const renderer = geometriesIndex.triangulation([0, 0, width, height])?.render; diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/area.test.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/area.test.ts index 279d05b5a64..6198d28e236 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/area.test.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/area.test.ts @@ -6,16 +6,17 @@ * Side Public License, v 1. */ -import { stringToRGB } from '../../../../../common/color_library_wrappers'; +import * as commonColors from '../../../../../common/color_library_wrappers'; +import { colorToRgba } from '../../../../../common/color_library_wrappers'; import { Fill } from '../../../../../geoms/types'; import { getMockCanvas, getMockCanvasContext2D, MockStyles } from '../../../../../mocks'; import * as common from '../../../../../utils/common'; import { getTextureStyles } from '../../../utils/texture'; import { buildAreaStyles } from './area'; -jest.mock('../../../../../common/color_library_wrappers'); jest.mock('../../../utils/texture'); jest.spyOn(common, 'getColorFromVariant'); +jest.spyOn(commonColors, 'colorToRgba'); const COLOR = 'aquamarine'; @@ -56,12 +57,12 @@ describe('Area styles', () => { (common.getColorFromVariant as jest.Mock).mockReturnValue(fillColor); }); - it('should call stringToRGB with values from getColorFromVariant', () => { - expect(stringToRGB).nthCalledWith(1, fillColor, expect.any(Function)); + it('should call colorToRgba with values from getColorFromVariant', () => { + expect(colorToRgba).nthCalledWith(1, fillColor); }); it('should return fill with color', () => { - expect(result.color).toEqual(stringToRGB(fillColor)); + expect(result.color).toEqual(colorToRgba(fillColor)); }); }); @@ -80,7 +81,7 @@ describe('Area styles', () => { it('should return correct fill opacity', () => { const expected = fillColorOpacity * fillOpacity * geoOpacity; - expect(result.color.opacity).toEqual(expected); + expect(result.color[3]).toEqual(expected); }); }); diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/area.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/area.ts index aa24f179814..53ef446b90d 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/area.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/area.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { OpacityFn, stringToRGB } from '../../../../../common/color_library_wrappers'; +import { colorToRgba, overrideOpacity } from '../../../../../common/color_library_wrappers'; import { Fill } from '../../../../../geoms/types'; import { Color, ColorVariant, getColorFromVariant } from '../../../../../utils/common'; import { GeometryStateStyle, AreaStyle } from '../../../../../utils/themes/theme'; @@ -15,9 +15,6 @@ import { getTextureStyles } from '../../../utils/texture'; /** * Return the rendering props for an area. The color of the area will be overwritten * by the fill color of the themeAreaStyle parameter if present - * @param baseColor the assigned color of the area for this series - * @param themeAreaStyle the theme style for the area series - * @param geometryStateStyle the highlight geometry style * @internal */ export function buildAreaStyles( @@ -27,10 +24,11 @@ export function buildAreaStyles( themeAreaStyle: AreaStyle, geometryStateStyle: GeometryStateStyle, ): Fill { - const fillOpacity: OpacityFn = (opacity, seriesOpacity = themeAreaStyle.opacity) => - opacity * seriesOpacity * geometryStateStyle.opacity; - const texture = getTextureStyles(ctx, imgCanvas, baseColor, fillOpacity, themeAreaStyle.texture); - const color = stringToRGB(getColorFromVariant(baseColor, themeAreaStyle.fill), fillOpacity); + const texture = getTextureStyles(ctx, imgCanvas, baseColor, geometryStateStyle.opacity, themeAreaStyle.texture); + const color = overrideOpacity( + colorToRgba(getColorFromVariant(baseColor, themeAreaStyle.fill)), + (opacity) => opacity * geometryStateStyle.opacity * themeAreaStyle.opacity, + ); return { color, diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/bar.test.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/bar.test.ts index 974f884cf79..8d5707eb02e 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/bar.test.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/bar.test.ts @@ -6,16 +6,17 @@ * Side Public License, v 1. */ -import { stringToRGB } from '../../../../../common/color_library_wrappers'; +import * as commonColors from '../../../../../common/color_library_wrappers'; +import { colorToRgba } from '../../../../../common/color_library_wrappers'; import { Fill, Rect, Stroke } from '../../../../../geoms/types'; import { getMockCanvas, getMockCanvasContext2D, MockStyles } from '../../../../../mocks'; import * as common from '../../../../../utils/common'; import { getTextureStyles } from '../../../utils/texture'; import { buildBarStyle } from './bar'; -jest.mock('../../../../../common/color_library_wrappers'); jest.mock('../../../utils/texture'); jest.spyOn(common, 'getColorFromVariant'); +jest.spyOn(commonColors, 'colorToRgba'); const COLOR = 'aquamarine'; @@ -72,17 +73,17 @@ describe('Bar styles', () => { }); }); - it('should call stringToRGB with values from getColorFromVariant', () => { - expect(stringToRGB).nthCalledWith(1, fillColor, expect.any(Function)); - expect(stringToRGB).nthCalledWith(2, strokeColor, expect.any(Function)); + it('should call colorToRgba with values from getColorFromVariant', () => { + expect(colorToRgba).nthCalledWith(1, fillColor); + expect(colorToRgba).nthCalledWith(2, strokeColor); }); it('should return fill with color', () => { - expect(result.fill.color).toEqual(stringToRGB(fillColor)); + expect(result.fill.color).toEqual(colorToRgba(fillColor)); }); it('should return stroke with color', () => { - expect(result.stroke.color).toEqual(stringToRGB(strokeColor)); + expect(result.stroke.color).toEqual(colorToRgba(strokeColor)); }); }); @@ -108,12 +109,12 @@ describe('Bar styles', () => { it('should return correct fill opacity', () => { const expected = fillColorOpacity * fillOpacity * geoOpacity; - expect(result.fill.color.opacity).toEqual(expected); + expect(result.fill.color[3]).toEqual(expected); }); it('should return correct stroke opacity', () => { const expected = strokeColorOpacity * strokeOpacity * geoOpacity; - expect(result.stroke.color.opacity).toEqual(expected); + expect(result.stroke.color[3]).toEqual(expected); }); describe('themeRectBorderStyle opacity is undefined', () => { @@ -126,7 +127,7 @@ describe('Bar styles', () => { it('should use themeRectStyle opacity', () => { const expected = strokeColorOpacity * fillOpacity * geoOpacity; - expect(result.stroke.color.opacity).toEqual(expected); + expect(result.stroke.color[3]).toEqual(expected); }); }); }); diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/bar.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/bar.ts index 5f918365458..c7b46d77ade 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/bar.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/bar.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { stringToRGB, OpacityFn } from '../../../../../common/color_library_wrappers'; +import { colorToRgba, overrideOpacity } from '../../../../../common/color_library_wrappers'; import { Stroke, Fill, Rect } from '../../../../../geoms/types'; import { getColorFromVariant } from '../../../../../utils/common'; import { GeometryStateStyle, RectStyle, RectBorderStyle } from '../../../../../utils/themes/theme'; @@ -29,19 +29,20 @@ export function buildBarStyle( geometryStateStyle: GeometryStateStyle, rect: Rect, ): { fill: Fill; stroke: Stroke } { - const fillOpacity: OpacityFn = (opacity, seriesOpacity = themeRectStyle.opacity) => - opacity * seriesOpacity * geometryStateStyle.opacity; - const texture = getTextureStyles(ctx, imgCanvas, baseColor, fillOpacity, themeRectStyle.texture); - const fillColor = stringToRGB(getColorFromVariant(baseColor, themeRectStyle.fill), fillOpacity); + const texture = getTextureStyles(ctx, imgCanvas, baseColor, geometryStateStyle.opacity, themeRectStyle.texture); + const fillColor = overrideOpacity( + colorToRgba(getColorFromVariant(baseColor, themeRectStyle.fill)), + (opacity) => opacity * themeRectStyle.opacity * geometryStateStyle.opacity, + ); const fill: Fill = { color: fillColor, texture, }; - const defaultStrokeOpacity = - themeRectBorderStyle.strokeOpacity === undefined ? themeRectStyle.opacity : themeRectBorderStyle.strokeOpacity; - const borderStrokeOpacity = defaultStrokeOpacity * geometryStateStyle.opacity; - const strokeOpacity: OpacityFn = (opacity) => opacity * borderStrokeOpacity; - const strokeColor = stringToRGB(getColorFromVariant(baseColor, themeRectBorderStyle.stroke), strokeOpacity); + + const strokeColor = overrideOpacity( + colorToRgba(getColorFromVariant(baseColor, themeRectBorderStyle.stroke)), + (opacity) => opacity * geometryStateStyle.opacity * (themeRectBorderStyle.strokeOpacity ?? themeRectStyle.opacity), + ); const stroke: Stroke = { color: strokeColor, width: diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/line.test.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/line.test.ts index 042432d1c8f..09c7e71edcd 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/line.test.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/line.test.ts @@ -6,14 +6,15 @@ * Side Public License, v 1. */ -import { stringToRGB } from '../../../../../common/color_library_wrappers'; +import * as commonColors from '../../../../../common/color_library_wrappers'; +import { colorToRgba } from '../../../../../common/color_library_wrappers'; import { Stroke } from '../../../../../geoms/types'; import { MockStyles } from '../../../../../mocks'; import * as common from '../../../../../utils/common'; import { buildLineStyles } from './line'; -jest.mock('../../../../../common/color_library_wrappers'); jest.spyOn(common, 'getColorFromVariant'); +jest.spyOn(commonColors, 'colorToRgba'); const COLOR = 'aquamarine'; @@ -54,12 +55,12 @@ describe('Line styles', () => { (common.getColorFromVariant as jest.Mock).mockReturnValue(strokeColor); }); - it('should call stringToRGB with values from getColorFromVariant', () => { - expect(stringToRGB).nthCalledWith(1, strokeColor, expect.any(Function)); + it('should call colorToRgba with values from getColorFromVariant', () => { + expect(colorToRgba).nthCalledWith(1, strokeColor); }); it('should return stroke with color', () => { - expect(result.color).toEqual(stringToRGB(strokeColor)); + expect(result.color).toEqual(colorToRgba(strokeColor)); }); }); @@ -78,7 +79,7 @@ describe('Line styles', () => { it('should return correct stroke opacity', () => { const expected = strokeColorOpacity * strokeOpacity * geoOpacity; - expect(result.color.opacity).toEqual(expected); + expect(result.color[3]).toEqual(expected); }); }); }); diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/line.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/line.ts index d3bd0c92d43..68c0b3697c8 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/line.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/styles/line.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { stringToRGB, OpacityFn } from '../../../../../common/color_library_wrappers'; +import { colorToRgba, overrideOpacity } from '../../../../../common/color_library_wrappers'; import { Stroke } from '../../../../../geoms/types'; import { getColorFromVariant } from '../../../../../utils/common'; import { GeometryStateStyle, LineStyle } from '../../../../../utils/themes/theme'; @@ -24,8 +24,10 @@ export function buildLineStyles( themeLineStyle: LineStyle, geometryStateStyle: GeometryStateStyle, ): Stroke { - const strokeOpacity: OpacityFn = (opacity) => opacity * themeLineStyle.opacity * geometryStateStyle.opacity; - const strokeColor = stringToRGB(getColorFromVariant(baseColor, themeLineStyle.stroke), strokeOpacity); + const strokeColor = overrideOpacity( + colorToRgba(getColorFromVariant(baseColor, themeLineStyle.stroke)), + (opacity) => opacity * themeLineStyle.opacity * geometryStateStyle.opacity, + ); return { color: strokeColor, width: themeLineStyle.strokeWidth, diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/utils/debug.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/utils/debug.ts index 9533d74932b..7997018c6f2 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/utils/debug.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/utils/debug.ts @@ -13,10 +13,10 @@ import { Point } from '../../../../../utils/point'; import { renderRect } from '../primitives/rect'; const DEFAULT_DEBUG_FILL: Fill = { - color: { r: 238, g: 130, b: 238, opacity: 0.2 }, + color: [238, 130, 238, 0.2], // violet }; const DEFAULT_DEBUG_STROKE: Stroke = { - color: { r: 0, g: 0, b: 0, opacity: 0.2 }, + color: [0, 0, 0, 0.2], width: 1, }; @@ -40,7 +40,7 @@ export function renderDebugRectCenterRotated( ctx: CanvasRenderingContext2D, center: Point, rect: Rect, - fill = DEFAULT_DEBUG_FILL, // violet + fill = DEFAULT_DEBUG_FILL, stroke = DEFAULT_DEBUG_STROKE, rotation: number = 0, ) { diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/values/bar.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/values/bar.ts index 233557c272b..7faba54cd11 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/values/bar.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/values/bar.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { colorIsDark, getTextColorIfTextInvertible } from '../../../../../common/color_calcs'; import { fillTextColor } from '../../../../../common/fill_text_color'; import { Font, TextAlign, TextBaseline } from '../../../../../common/text_utils'; import { Rect } from '../../../../../geoms/types'; @@ -45,14 +44,13 @@ export function renderBarValues(ctx: CanvasRenderingContext2D, props: BarValuesP } const { text, fontSize, fontScale, overflowConstraints, isValueContainedInElement } = bar.displayValue; const shadowSize = getTextBorderSize(fill); - const { fillColor, shadowColor } = getTextColors(fill, bar.color, shadowSize); + const { fillColor, shadowColor } = getTextColors(fill, bar.color); const font: Font = { fontFamily, fontStyle: fontStyle ?? 'normal', fontVariant: 'normal', fontWeight: 'normal', textColor: fillColor, - textOpacity: 1, }; const { x, y, align, baseline, rect, overflow } = positionText( @@ -189,17 +187,12 @@ function isOverflow(rect: Rect, chartDimensions: Dimensions, chartRotation: Rota return rect.x < 0 || rect.x + rect.width > cWidth || rect.y < 0 || rect.y + rect.height > cHeight; } -const DEFAULT_VALUE_COLOR = 'black'; -// a little bit of alpha makes black font more readable -const DEFAULT_VALUE_BORDER_COLOR = 'rgba(255, 255, 255, 0.8)'; -const DEFAULT_VALUE_BORDER_SOLID_COLOR = 'rgb(255, 255, 255)'; const TRANSPARENT_COLOR = 'rgba(0,0,0,0)'; type ValueFillDefinition = Theme['barSeriesStyle']['displayValue']['fill']; function getTextColors( fillDefinition: ValueFillDefinition, geometryColor: string, - borderSize: number, ): { fillColor: string; shadowColor: string } { if (typeof fillDefinition === 'string') { return { fillColor: fillDefinition, shadowColor: TRANSPARENT_COLOR }; @@ -210,28 +203,9 @@ function getTextColors( shadowColor: fillDefinition.borderColor || TRANSPARENT_COLOR, }; } - const fillColor = - fillTextColor( - DEFAULT_VALUE_COLOR, - fillDefinition.textInvertible, - fillDefinition.textContrast || false, - geometryColor, - 'white', - ) || DEFAULT_VALUE_COLOR; - - // If the border is too wide it can overlap between a letter or another - // therefore use a solid color for thinker borders - const defaultBorderColor = borderSize < 2 ? DEFAULT_VALUE_BORDER_COLOR : DEFAULT_VALUE_BORDER_SOLID_COLOR; - const shadowColor = - 'textBorder' in fillDefinition - ? getTextColorIfTextInvertible( - colorIsDark(fillColor), - colorIsDark(defaultBorderColor), - defaultBorderColor, - false, - geometryColor, - ) || TRANSPARENT_COLOR - : TRANSPARENT_COLOR; + const fillColor = fillTextColor(geometryColor); + + const shadowColor = fillTextColor(fillColor); return { fillColor, diff --git a/packages/charts/src/chart_types/xy_chart/renderer/dom/highlighter.tsx b/packages/charts/src/chart_types/xy_chart/renderer/dom/highlighter.tsx index 917e5645bff..7d10482c99b 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/dom/highlighter.tsx +++ b/packages/charts/src/chart_types/xy_chart/renderer/dom/highlighter.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { RGBtoString } from '../../../../common/color_library_wrappers'; +import { RGBATupleToString } from '../../../../common/color_library_wrappers'; import { GlobalChartState } from '../../../../state/chart_state'; import { getChartRotationSelector } from '../../../../state/selectors/get_chart_rotation'; import { InitStatus, getInternalIsInitializedSelector } from '../../../../state/selectors/get_internal_is_intialized'; @@ -83,7 +83,7 @@ class HighlighterComponent extends React.Component { > + gridLineStyles.opacity !== undefined ? strokeColorOpacity * gridLineStyles.opacity : strokeColorOpacity, + ); const stroke: Stroke = { color: strokeColor, width: gridLineStyles.strokeWidth, diff --git a/packages/charts/src/chart_types/xy_chart/utils/texture.ts b/packages/charts/src/chart_types/xy_chart/utils/texture.ts index 11eaf14dd51..19d6371869d 100644 --- a/packages/charts/src/chart_types/xy_chart/utils/texture.ts +++ b/packages/charts/src/chart_types/xy_chart/utils/texture.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { OpacityFn } from '../../../common/color_library_wrappers'; +import { Ratio } from '../../../common/geometry'; import { Texture } from '../../../geoms/types'; import { Color, ColorVariant, degToRad, getColorFromVariant } from '../../../utils/common'; import { Point } from '../../../utils/point'; @@ -39,7 +39,7 @@ function createPattern( dpi: number, patternCanvas: HTMLCanvasElement, baseColor: Color | ColorVariant, - fillOpacity: OpacityFn, + sharedGeometryOpacity: Ratio, textureStyle?: TexturedStyles, ): CanvasPattern | null { const pCtx = patternCanvas.getContext('2d'); @@ -53,7 +53,7 @@ function createPattern( patternCanvas.width = dpi * cssWidth; patternCanvas.height = dpi * cssHeight; - pCtx.globalAlpha = opacity ? fillOpacity(opacity, 1) : fillOpacity(1); + pCtx.globalAlpha = sharedGeometryOpacity * (opacity ?? 1); pCtx.lineWidth = strokeWidth; pCtx.strokeStyle = getColorFromVariant(baseColor, stroke ?? ColorVariant.Series); @@ -93,11 +93,11 @@ export const getTextureStyles = ( ctx: CanvasRenderingContext2D, patternCanvas: HTMLCanvasElement, baseColor: Color | ColorVariant, - fillOpacity: OpacityFn, + sharedGeometryOpacity: Ratio, texture?: TexturedStyles, ): Texture | undefined => { const dpi = window.devicePixelRatio; - const pattern = createPattern(ctx, dpi, patternCanvas, baseColor, fillOpacity, texture); + const pattern = createPattern(ctx, dpi, patternCanvas, baseColor, sharedGeometryOpacity, texture); if (!pattern || !texture) return; diff --git a/packages/charts/src/common/__mocks__/color_library_wrappers.ts b/packages/charts/src/common/__mocks__/color_library_wrappers.ts deleted file mode 100644 index 4eb08947d5c..00000000000 --- a/packages/charts/src/common/__mocks__/color_library_wrappers.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -const module = jest.requireActual('../color_library_wrappers.ts'); - -export const { defaultColor, transparentColor, defaultD3Color } = module; - -export const stringToRGB = jest.fn(module.stringToRGB); -export const validateColor = jest.fn(module.validateColor); -export const argsToRGB = jest.fn(module.argsToRGB); -export const argsToRGBString = jest.fn(module.argsToRGBString); -export const RGBtoString = jest.fn(module.RGBtoString); diff --git a/packages/charts/src/common/__mocks__/fill_text_layout.ts b/packages/charts/src/common/__mocks__/fill_text_layout.ts deleted file mode 100644 index fc9fdd4db9e..00000000000 --- a/packages/charts/src/common/__mocks__/fill_text_layout.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -const module = jest.requireActual('../../viewmodel/fill_text_layout.ts'); - -export const getTextColorIfTextInvertible = jest.fn(module.getTextColorIfTextInvertible); diff --git a/packages/charts/src/common/apca_color_contrast.ts b/packages/charts/src/common/apca_color_contrast.ts new file mode 100644 index 00000000000..158db6de852 --- /dev/null +++ b/packages/charts/src/common/apca_color_contrast.ts @@ -0,0 +1,167 @@ +/* eslint-disable header/header */ + +/** + * @notice + * This product includes code that is adapted from https://github.com/Myndex/SAPC-APCA + * which is available under a "W3C SOFTWARE NOTICE AND LICENSE" license. + */ + +/// ///////////////////////////////////////////////////////////////////////////// +/// // +/// // ***** SAPC BLOCK ***** +/// // +/// // For Evaluations, this is referred to as: SAPC-8, D-series constants +/// // S-LUV Advanced Perceptual Contrast +/// // Copyright © 2019-2021 by Andrew Somers. All Rights Reserved. +/// // +/// // +/// // INCLUDED Extensions or Model Features: +/// // • SAPC-8 Core Contrast +/// // • SmoothScale™ scaling technique +/// // • SoftToe black level soft clamp +/// // +/// // NOT INCLUDED — This Version Does NOT Have These Extensions: +/// // • Color Vision Module +/// // • Spatial Frequency Module +/// // • Light Adaptation Module +/// // • Dynamics Module +/// // • Alpha Module +/// // • Personalization Module +/// // • Multiway Module +/// // • DynaFont™ font display +/// // • ResearchMode middle contrast explorer +/// // • ResearchMode static target +/// // • CIE function suite +/// // • SAPColor listings and sorting suite +/// // • RGBcolor() colorString parsing +/// // +/// // +/// ///////////////////////////////////////////////////////////////////////////// + +/// ///////////////////////////////////////////////////////////////////////////// +/// //////////////////////////////////////////////////////////////////////////// +/// // BEGIN SAPC/APCA CONTRAST BLOCK \////////////////////////////////////// +/// / \//////////////////////////////////// + +/// ///////////////////////////////////////////////////////////////////////// +/// // SAPC Function with SmoothScale \//////////////////////////////////// +/// / \////////////////////////////////// +/// + +/// // *** Polarity is Important: do not mix up background and text *** ///// + +/// // Input value must be integer in RGB order (RRGGBB for 0xFFFFFF) ///// + +import { RgbTuple } from './color_library_wrappers'; + +/** + * / // DO NOT use a Y from any other method ///// + * @internal + */ +export function APCAContrast([Rbg, Gbg, Bbg]: RgbTuple, [Rtxt, Gtxt, Btxt]: RgbTuple) { + /// // sRGB Conversion to Relative Luminance (Y) ///// + + const mainTRC = 2.4; // Transfer Curve (aka "Gamma") for sRGB linearization + // Simple power curve vs piecewise described in docs + // Essentially, 2.4 best models actual display + // characteristics in combination with the total method + + const Rco = 0.2126729; // sRGB Red Coefficient (from matrix) + const Gco = 0.7151522; // sRGB Green Coefficient (from matrix) + const Bco = 0.072175; // sRGB Blue Coefficient (from matrix) + + /// // For Finding Raw SAPC Contrast from Relative Luminance (Y) ///// + + const normBG = 0.56; // Constants for SAPC Power Curve Exponents + const normTXT = 0.57; // One pair for normal text, and one for reverse + const revTXT = 0.62; // These are the "beating heart" of SAPC + const revBG = 0.65; + + /// // For Clamping and Scaling Values ///// + // constant updated to https://github.com/Myndex/SAPC-APCA#apca-math-new-098g-4g-constants + // new 0.98G 4g constants + + const blkThrs = 0.022; // Level that triggers the soft black clamp + const blkClmp = 1.414; // Exponent for the soft black clamp curve + const deltaYmin = 0.0005; // Lint trap + const scaleBoW = 1.14; // Scaling for dark text on light + const scaleWoB = 1.14; // Scaling for light text on dark + const loConThresh = 0.035991; // Threshold for new simple offset scale + const loConFactor = 27.7847239587675; + const loConOffset = 0.027; // The simple offset + const loClip = 0.001; // Output clip (lint trap #2) + + // We are only concerned with Y at this point + // Ybg and Ytxt: divide sRGB to 0.0-1.0 range, linearize, + // and then apply the standard coefficients and sum to Y. + // Note that the Y we create here is unique and designed + // exclusively for SAPC. Do not use Y from other methods. + + let Ybg = + Math.pow(Rbg / 255.0, mainTRC) * Rco + Math.pow(Gbg / 255.0, mainTRC) * Gco + Math.pow(Bbg / 255.0, mainTRC) * Bco; + + let Ytxt = + Math.pow(Rtxt / 255.0, mainTRC) * Rco + + Math.pow(Gtxt / 255.0, mainTRC) * Gco + + Math.pow(Btxt / 255.0, mainTRC) * Bco; + + let SAPC = 0.0; // For holding raw SAPC values + let outputContrast = 0.0; // For weighted final values + + /// // TUTORIAL ///// + + // Take Y and soft clamp black, return 0 for very close luminances + // determine polarity, and calculate SAPC raw contrast + // Then apply the output scaling + + // Note that reverse contrast (white text on black) + // intentionally returns a negative number + // Proper polarity is important! + + /// /////// BLACK SOFT CLAMP & INPUT CLIP //////////////////////////////// + + // Soft clamp Y when near black. + // Now clamping all colors to prevent crossover errors + Ytxt = Ytxt > blkThrs ? Ytxt : Ytxt + Math.pow(blkThrs - Ytxt, blkClmp); + + Ybg = Ybg > blkThrs ? Ybg : Ybg + Math.pow(blkThrs - Ybg, blkClmp); + + /// // Return 0 Early for extremely low ∆Y (lint trap #1) ///// + if (Math.abs(Ybg - Ytxt) < deltaYmin) { + return 0.0; + } + + /// /////// SAPC CONTRAST /////////////////////////////////////////////// + + if (Ybg > Ytxt) { + // For normal polarity, black text on white + + /// // Calculate the SAPC contrast value and scale + + SAPC = (Math.pow(Ybg, normBG) - Math.pow(Ytxt, normTXT)) * scaleBoW; + + /// // NEW! SAPC SmoothScale™ + // Low Contrast Smooth Scale Rollout to prevent polarity reversal + // and also a low clip for very low contrasts (lint trap #2) + // much of this is for very low contrasts, less than 10 + // therefore for most reversing needs, only loConOffset is important + outputContrast = + SAPC < loClip ? 0.0 : SAPC < loConThresh ? SAPC - SAPC * loConFactor * loConOffset : SAPC - loConOffset; + } else { + // For reverse polarity, light text on dark + // WoB should always return negative value. + + SAPC = (Math.pow(Ybg, revBG) - Math.pow(Ytxt, revTXT)) * scaleWoB; + + outputContrast = + SAPC > -loClip ? 0.0 : SAPC > -loConThresh ? SAPC - SAPC * loConFactor * loConOffset : SAPC + loConOffset; + } + + return outputContrast * 100; +} // Close APCAcontrast() + +/// /\ ///////////////////////////////////////////\ +/// //\ END OF SAPC/APCA BLOCK /////////////////////////////////////////////\ +/// ///////////////////////////////////////////////////////////////////////////\ +/// ////////////////////////////////////////////////////////////////////////////\ +/* eslint-enable */ diff --git a/packages/charts/src/common/color_calcs.test.ts b/packages/charts/src/common/color_calcs.test.ts index a4253af754a..0d690210718 100644 --- a/packages/charts/src/common/color_calcs.test.ts +++ b/packages/charts/src/common/color_calcs.test.ts @@ -7,73 +7,52 @@ */ import { integerSnap, monotonicHillClimb } from '../solvers/monotonic_hill_climb'; -import { makeHighContrastColor, combineColors } from './color_calcs'; - -describe('calcs', () => { - describe('test makeHighContrastColor', () => { - it('hex input - should change white text to black when background is white', () => { - const expected = '#000'; - const result = makeHighContrastColor('#fff', '#fff'); - expect(result).toBe(expected); - }); - it('rgb input - should change white text to black when background is white', () => { - const expected = '#000'; - const result = makeHighContrastColor('rgb(255, 255, 255)', 'rgb(255, 255, 255)'); - expect(result).toBe(expected); - }); - it('rgba input - should change white text to black when background is white', () => { - const expected = '#000'; - const result = makeHighContrastColor('rgba(255, 255, 255, 1)', 'rgba(255, 255, 255, 1)'); - expect(result).toBe(expected); - }); - it('word input - should change white text to black when background is white', () => { - const expected = '#000'; - const result = makeHighContrastColor('white', 'white'); - expect(result).toBe(expected); +import { highContrastColor, combineColors } from './color_calcs'; +import { RgbaTuple } from './color_library_wrappers'; +import { fillTextColor } from './fill_text_color'; + +describe('Color calcs', () => { + describe('test highContrastColor', () => { + it('should return black when background is white', () => { + expect(fillTextColor('white')).toEqual('rgba(0, 0, 0, 1)'); }); // test contrast computation - it('should provide at least 4.5 contrast', () => { - const foreground = '#fff'; // white - const background = 'rgba(255, 255, 51, 0.3)'; // light yellow - const result = '#000'; // black - expect(result).toBe(makeHighContrastColor(foreground, background)); - }); - it('should use black text for hex value', () => { - const foreground = '#fff'; // white - const background = '#7874B2'; // Thailand color - const result = '#000'; // black - expect(result).toBe(makeHighContrastColor(foreground, background)); + it('should return black with yellow/semi-transparent background', () => { + expect(fillTextColor(`rgba(255,255,51,0.3)`)).toEqual('rgba(0, 0, 0, 1)'); + }); + it('should use white text for Thailand color', () => { + // black for WCAG2, white for WCAG3 + expect(fillTextColor(`rgba(120, 116, 178, 1)`)).toEqual('rgba(0, 0, 0, 1)'); }); it('should switch to black text if background color is in rgba() format - Thailand', () => { - const containerBackground = 'white'; - const background = 'rgba(120, 116, 178, 0.7)'; - const resultForCombined = 'rgba(161, 158, 201, 1)'; // 0.3 'rgba(215, 213, 232, 1)'; // 0.5 - 'rgba(188, 186, 217, 1)'; //0.7 - ; - expect(combineColors(background, containerBackground)).toBe(resultForCombined); - const foreground = 'white'; - const resultForContrastedText = '#000'; // switches to black text - expect(makeHighContrastColor(foreground, resultForCombined)).toBe(resultForContrastedText); + const containerBackground: RgbaTuple = [255, 255, 255, 1]; // white + const background: RgbaTuple = [120, 116, 178, 0.7]; + const blendedBackground: RgbaTuple = [161, 158, 201, 1]; + expect(combineColors(background, containerBackground)).toEqual(blendedBackground); + expect(highContrastColor(blendedBackground, 'WCAG2')).toEqual([0, 0, 0, 1]); + expect(highContrastColor(blendedBackground, 'WCAG3')).toEqual([255, 255, 255, 1]); }); }); describe('test the combineColors function', () => { it('should return correct RGBA with opacity greater than 0.7', () => { - const expected = 'rgba(102, 43, 206, 1)'; - const result = combineColors('rgba(121, 47, 249, 0.8)', '#1c1c24'); - expect(result).toBe(expected); + const expected = [102, 43, 206, 1]; + const result = combineColors([121, 47, 249, 0.8], [28, 28, 36, 1]); + expect(result).toEqual(expected); }); it('should return correct RGBA with opacity less than 0.7', () => { - const expected = 'rgba(226, 186, 187, 1)'; - const result = combineColors('rgba(228, 26, 28, 0.3)', 'rgba(225, 255, 255, 1)'); - expect(result).toBe(expected); + const expected = [226, 186, 187, 1]; + const result = combineColors([228, 26, 28, 0.3], [225, 255, 255, 1]); + expect(result).toEqual(expected); }); it('should return correct RGBA with the input color as a word vs rgba or hex value', () => { - const expected = 'rgba(0, 0, 255, 1)'; - const result = combineColors('blue', 'black'); - expect(result).toBe(expected); + const expected = [0, 0, 255, 1]; + const result = combineColors([0, 0, 255, 1], [0, 0, 0, 1]); + expect(result).toEqual(expected); }); it('should return the correct RGBA with hex input', () => { - const expected = 'rgba(212, 242, 210, 1)'; - const result = combineColors('#D4F2D2', '#BEB7DF'); - expect(result).toBe(expected); + const expected = [212, 242, 210, 1]; + const result = combineColors([212, 242, 210, 1], [190, 183, 223, 1]); + expect(result).toEqual(expected); }); }); }); diff --git a/packages/charts/src/common/color_calcs.ts b/packages/charts/src/common/color_calcs.ts index e37a952715d..a30322e85ad 100644 --- a/packages/charts/src/common/color_calcs.ts +++ b/packages/charts/src/common/color_calcs.ts @@ -6,32 +6,13 @@ * Side Public License, v 1. */ -import chroma from 'chroma-js'; - -import { Color } from '../utils/common'; -import { RgbaTuple, RGBATupleToString, RgbTuple, stringToRGB } from './color_library_wrappers'; -import { Ratio } from './geometry'; -import { TextContrast } from './text_utils'; +import { APCAContrast } from './apca_color_contrast'; +import { RgbaTuple, RGBATupleToString, RgbTuple } from './color_library_wrappers'; +import { getWCAG2ContrastRatio } from './wcag2_color_contrast'; /** @internal */ export function hueInterpolator(colors: RgbTuple[]) { - return (d: number) => { - const index = Math.round(d * 255); - const [r, g, b, a] = colors[index]; - return colors[index].length === 3 ? `rgb(${r},${g},${b})` : `rgba(${r},${g},${b},${a ?? 1})`; - }; -} - -/** @internal */ -export function addOpacity(hexColorString: string, opacity: Ratio) { - // this is a super imperfect multiplicative alpha blender that assumes a "#rrggbb" or "#rrggbbaa" hexColorString - // todo roll some proper utility that can handle "rgb(...)", "rgba(...)", "red", {r, g, b} etc. - return opacity === 1 - ? hexColorString - : hexColorString.slice(0, 7) + - (hexColorString.slice(7).length === 0 || parseInt(hexColorString.slice(7, 2), 16) === 255 - ? `00${Math.round(opacity * 255).toString(16)}`.slice(-2) // color was of full opacity - : `00${Math.round((parseInt(hexColorString.slice(7, 2), 16) / 255) * opacity * 255).toString(16)}`.slice(-2)); + return (d: number) => RGBATupleToString(colors[Math.round(d * 255)]); } /** @internal */ @@ -39,165 +20,54 @@ export function arrayToLookup(keyFun: (v: any) => any, array: Array) { return Object.assign({}, ...array.map((d) => ({ [keyFun(d)]: d }))); } -const rgbaCache: Map = new Map(); - -function colorToRgba(color: Color): RgbaTuple { - const cachedValue = rgbaCache.get(color); - if (cachedValue === undefined) { - const newValue = chroma(color).rgba(); - rgbaCache.set(color, newValue); - return newValue; - } - return cachedValue; -} - -/** If the user specifies the background of the container in which the chart will be on, we can use that color - * and make sure to provide optimal contrast +/** + * Blend a foreground (fg) color with a background (bg) color * @internal */ -export function combineColors(foregroundColor: Color, backgroundColor: Color): Color { - const [red1, green1, blue1, alpha1] = colorToRgba(foregroundColor); - const [red2, green2, blue2, alpha2] = colorToRgba(backgroundColor); +export function combineColors([fgR, fgG, fgB, fgA]: RgbaTuple, [bgR, bgG, bgB, bgA]: RgbaTuple): RgbaTuple { + // combine colors only if foreground has transparency + if (fgA === 1) { + return [fgR, fgG, fgB, fgA]; + } // For reference on alpha calculations: // https://en.wikipedia.org/wiki/Alpha_compositing - const combinedAlpha = alpha1 + alpha2 * (1 - alpha1); + const alpha = fgA + bgA * (1 - fgA); - if (combinedAlpha === 0) { - return 'rgba(0,0,0,0)'; + if (alpha === 0) { + return [0, 0, 0, 0]; } - const combinedRed = Math.round((red1 * alpha1 + red2 * alpha2 * (1 - alpha1)) / combinedAlpha); - const combinedGreen = Math.round((green1 * alpha1 + green2 * alpha2 * (1 - alpha1)) / combinedAlpha); - const combinedBlue = Math.round((blue1 * alpha1 + blue2 * alpha2 * (1 - alpha1)) / combinedAlpha); - const rgba: RgbTuple = [combinedRed, combinedGreen, combinedBlue, combinedAlpha]; - - return RGBATupleToString(rgba); -} - -const validCache: Map = new Map(); - -/** - * Return true if the color is a valid CSS color, false otherwise - * @param color a color written in string - * @internal - */ -export function isColorValid(color?: string): color is Color { - const cachedValue = validCache.get(color); - if (cachedValue === undefined) { - const newValue = Boolean(color) && chroma.valid(color); - validCache.set(color, newValue); - return newValue; - } - return cachedValue; + const r = Math.round((fgR * fgA + bgR * bgA * (1 - fgA)) / alpha); + const g = Math.round((fgG * fgA + bgG * bgA * (1 - fgA)) / alpha); + const b = Math.round((fgB * fgA + bgB * bgA * (1 - fgA)) / alpha); + return [r, g, b, alpha]; } -/** - * Adjust the text color in cases black and white can't reach ideal 4.5 ratio - * @internal - */ -export function makeHighContrastColor(foreground: Color, background: Color, ratio = 4.5): Color { - // determine the lightness factor of the background color to determine whether to lighten or darken the foreground - const lightness = chroma(background).get('hsl.l'); - let highContrastTextColor = foreground; - const originalhighContrastTextColor = foreground; - const isBackgroundDark = colorIsDark(background); - // determine whether white or black text is ideal contrast vs a grey that just passes the ratio - if (isBackgroundDark && chroma.deltaE('black', foreground) === 0) { - highContrastTextColor = '#fff'; - } else if (lightness > 0.5 && chroma.deltaE('white', foreground) === 0) { - highContrastTextColor = '#000'; - } - const precision = 1e8; - let contrast = getContrast(highContrastTextColor, background); - // brighten and darken the text color if not meeting the ratio - while (contrast < ratio) { - highContrastTextColor = isBackgroundDark - ? chroma(highContrastTextColor).brighten().toString() - : chroma(highContrastTextColor).darken().toString(); - const scaledOldContrast = Math.round(contrast * precision) / precision; - contrast = getContrast(highContrastTextColor, background); - const scaledContrast = Math.round(contrast * precision) / precision; - // catch if the ideal contrast may not be possible, switch to the other extreme color contrast - if (scaledOldContrast === scaledContrast) { - const contrastColor = - originalhighContrastTextColor === 'rgba(255, 255, 255, 1)' ? 'rgba(0, 0 , 0, 1)' : 'rgba(255, 255, 255, 1)'; - // make sure the new text color hits the ratio, if not, then return the scaledContrast since we tried earlier - return getContrast(contrastColor, background) > ratio ? contrastColor : scaledContrast.toString(); - } - } - return highContrastTextColor.toString(); -} +const COLOR_WHITE: RgbaTuple = [255, 255, 255, 1]; +const COLOR_BLACK: RgbaTuple = [0, 0, 0, 1]; -/** - * show contrast amount - * @internal - */ -export function getContrast(foregroundColor: string | chroma.Color, backgroundColor: string | chroma.Color): number { - return chroma.contrast(foregroundColor, backgroundColor); +function getHighContrastColorWCAG2(background: RgbTuple): RgbaTuple { + const wWhite = getWCAG2ContrastRatio(COLOR_WHITE, background); + const wBlack = getWCAG2ContrastRatio(COLOR_BLACK, background); + return wWhite >= wBlack ? COLOR_WHITE : COLOR_BLACK; } -/** - * determines if the color is dark based on the luminance - * @internal - */ -export function colorIsDark(color: Color): boolean { - const luminance = chroma(color).luminance(); - return luminance < 0.2; +function getHighContrastColorAPCA(background: RgbTuple): RgbaTuple { + const wWhiteText = Math.abs(APCAContrast(background, COLOR_WHITE)); + const wBlackText = Math.abs(APCAContrast(background, COLOR_BLACK)); + return wWhiteText > wBlackText ? COLOR_WHITE : COLOR_BLACK; } -/** - * inverse color for text - * @internal - */ -export function getTextColorIfTextInvertible( - specifiedTextColorIsDark: boolean, - backgroundIsDark: boolean, - textColor: Color, - textContrast: TextContrast, - backgroundColor: Color, -): Color { - const inverseForContrast = specifiedTextColorIsDark === backgroundIsDark; - const { r: tr, g: tg, b: tb, opacity: to } = stringToRGB(textColor); - if (!textContrast) { - return inverseForContrast - ? to === undefined - ? `rgb(${255 - tr}, ${255 - tg}, ${255 - tb})` - : `rgba(${255 - tr}, ${255 - tg}, ${255 - tb}, ${to})` - : textColor; - } - if (textContrast === true) { - return inverseForContrast - ? to === undefined - ? makeHighContrastColor(`rgb(${255 - tr}, ${255 - tg}, ${255 - tb})`, backgroundColor) - : makeHighContrastColor(`rgba(${255 - tr}, ${255 - tg}, ${255 - tb}, ${to})`, backgroundColor) - : makeHighContrastColor(textColor, backgroundColor); - } else if (typeof textContrast === 'number') { - return inverseForContrast - ? to === undefined - ? makeHighContrastColor(`rgb(${255 - tr}, ${255 - tg}, ${255 - tb})`, backgroundColor, textContrast) - : makeHighContrastColor(`rgba(${255 - tr}, ${255 - tg}, ${255 - tb}, ${to})`, backgroundColor, textContrast) - : makeHighContrastColor(textColor, backgroundColor, textContrast); - } - return 'black'; // this should never happen; added it as previously function return type included undefined; todo -} +const HIGH_CONTRAST_FN = { + WCAG2: getHighContrastColorWCAG2, + WCAG3: getHighContrastColorAPCA, +}; /** - * This function generates color for non-occluded text rendering directly on the - * paper, with possible background color, ie. not on some data ink - * + * Use white or black text depending on the high contrast mode used * @internal */ -export function getOnPaperColorSet(textColor: Color, sectorLineStroke: Color, containerBackgroundColor?: Color) { - // determine the ideal contrast color for the link labels - const validBackgroundColor = isColorValid(containerBackgroundColor) - ? containerBackgroundColor - : 'rgba(255, 255, 255, 0)'; - const contrastTextColor = containerBackgroundColor - ? makeHighContrastColor(textColor, validBackgroundColor) - : textColor; - const strokeColor = containerBackgroundColor - ? makeHighContrastColor(sectorLineStroke, validBackgroundColor) - : undefined; - return { contrastTextColor, strokeColor }; +export function highContrastColor(background: RgbTuple, mode: keyof typeof HIGH_CONTRAST_FN = 'WCAG2'): RgbaTuple { + return HIGH_CONTRAST_FN[mode](background); } diff --git a/packages/charts/src/common/color_library_wrappers.test.ts b/packages/charts/src/common/color_library_wrappers.test.ts index d135a239821..d31457e87da 100644 --- a/packages/charts/src/common/color_library_wrappers.test.ts +++ b/packages/charts/src/common/color_library_wrappers.test.ts @@ -6,226 +6,127 @@ * Side Public License, v 1. */ -import { - stringToRGB, - validateColor, - defaultD3Color, - argsToRGB, - RgbObject, - argsToRGBString, - RGBtoString, -} from './color_library_wrappers'; - -describe('d3 Utils', () => { - describe('stringToRGB', () => { - describe('bad colors or undefined', () => { - it('should return default color for undefined color string', () => { - expect(stringToRGB()).toMatchObject({ - r: 255, - g: 0, - b: 0, - opacity: 1, - }); - }); +import { Logger } from '../utils/logger'; +import { colorToRgba, overrideOpacity } from './color_library_wrappers'; + +jest.mock('../utils/logger', () => ({ + Logger: { + warn: jest.fn(), + }, +})); - it('should return default RgbObject', () => { - expect(stringToRGB('not a color')).toMatchObject({ - r: 255, - g: 0, - b: 0, - opacity: 1, - }); +describe('color library wrappers utils', () => { + describe('colorToRgba', () => { + describe('bad colors or undefined', () => { + it('should return default RgbaTuple', () => { + expect(colorToRgba('not a color')).toMatchObject([255, 0, 0, 1]); }); it('should return default color if bad opacity', () => { - expect(stringToRGB('rgba(50,50,50,x)')).toMatchObject({ - r: 255, - g: 0, - b: 0, - opacity: 1, - }); + expect(colorToRgba('rgba(50,50,50,x)')).toMatchObject([255, 0, 0, 1]); }); }); describe('hex colors', () => { - it('should return RgbObject', () => { - expect(stringToRGB('#ef713d')).toMatchObject({ - r: 239, - g: 113, - b: 61, - }); + it('should return RgbaTuple', () => { + expect(colorToRgba('#ef713d')).toMatchObject([239, 113, 61, 1]); }); - it('should return RgbObject from shorthand', () => { - expect(stringToRGB('#ccc')).toMatchObject({ - r: 204, - g: 204, - b: 204, - }); + it('should return RgbaTuple from shorthand', () => { + expect(colorToRgba('#ccc')).toMatchObject([204, 204, 204, 1]); }); - it('should return RgbObject with correct opacity', () => { + it('should return RgbaTuple with correct opacity', () => { // https://gist.github.com/lopspower/03fb1cc0ac9f32ef38f4 - expect(stringToRGB('#ef713d80').opacity).toBeCloseTo(0.5, 1); + expect(colorToRgba('#ef713d80')[3]).toBeCloseTo(0.5, 1); }); - it('should return correct RgbObject for alpha value of 0', () => { - expect(stringToRGB('#00000000')).toMatchObject({ - r: 0, - g: 0, - b: 0, - opacity: 0, - }); + it('should return correct RgbaTuple for alpha value of 0', () => { + expect(colorToRgba('#00000000')).toMatchObject([0, 0, 0, 0]); }); }); describe('rgb colors', () => { - it('should return RgbObject', () => { - expect(stringToRGB('rgb(50,50,50)')).toMatchObject({ - r: 50, - g: 50, - b: 50, - }); + it('should return RgbaTuple', () => { + expect(colorToRgba('rgb(50,50,50)')).toMatchObject([50, 50, 50, 1]); }); - it('should return RgbObject with correct opacity', () => { - expect(stringToRGB('rgba(50,50,50,0.25)').opacity).toBe(0.25); + it('should return RgbaTuple with correct opacity', () => { + expect(colorToRgba('rgba(50,50,50,0.25)')[3]).toBe(0.25); }); - it('should return correct RgbObject for alpha value of 0', () => { - expect(stringToRGB('rgba(50,50,50,0)')).toMatchObject({ - r: 50, - g: 50, - b: 50, - opacity: 0, - }); + it('should return correct RgbaTuple for alpha value of 0', () => { + expect(colorToRgba('rgba(50,50,50,0)')).toMatchObject([50, 50, 50, 0]); }); }); describe('hsl colors', () => { - it('should return RgbObject', () => { - expect(stringToRGB('hsl(0,0%,50%)')).toMatchObject({ - r: 128, - g: 128, - b: 128, - opacity: 1, - }); + it('should return RgbaTuple', () => { + expect(colorToRgba('hsl(0,0%,50%)')).toMatchObject([128, 128, 128, 1]); }); - it('should return RgbObject with correct opacity', () => { - expect(stringToRGB('hsla(0,0%,50%,0.25)').opacity).toBe(0.25); + it('should return RgbaTuple with correct opacity', () => { + expect(colorToRgba('hsla(0,0%,50%,0.25)')[3]).toBe(0.25); }); - it('should return correct RgbObject for alpha value of 0', () => { - expect(stringToRGB('hsla(0,0%,50%,0)')).toEqual({ - r: 128, - g: 128, - b: 128, - opacity: 0, - }); + it('should return correct RgbaTuple for alpha value of 0', () => { + expect(colorToRgba('hsla(0,0%,50%,0)')).toEqual([128, 128, 128, 0]); }); }); describe('named colors', () => { - it('should return RgbObject', () => { - expect(stringToRGB('aquamarine')).toMatchObject({ - r: 127, - g: 255, - b: 212, - }); - }); - - it('should return default RgbObject with 0 opacity', () => { - expect(stringToRGB('transparent')).toMatchObject({ - r: 0, - g: 0, - b: 0, - opacity: 0, - }); - }); - - it('should return default RgbObject with 0 opacity even with override', () => { - expect(stringToRGB('transparent', 0.5)).toMatchObject({ - r: 0, - g: 0, - b: 0, - opacity: 0, - }); + it('should return RgbaTuple', () => { + expect(colorToRgba('aquamarine')).toMatchObject([127, 255, 212, 1]); + }); + + it('should return default RgbaTuple with 0 opacity', () => { + expect(colorToRgba('transparent')).toMatchObject([0, 0, 0, 0]); + }); + + it('should return default RgbaTuple with 0 opacity even with override', () => { + expect(overrideOpacity(colorToRgba('transparent'), 0.5)).toMatchObject([0, 0, 0, 0]); }); }); - describe('Optional opactiy override', () => { + describe('Optional opacity override', () => { it('should override opacity from color', () => { - expect(stringToRGB('rgba(50,50,50,0.25)', 0.75).opacity).toBe(0.75); + expect(overrideOpacity(colorToRgba('rgba(50,50,50,0.25)'), 0.75)[3]).toBe(0.75); }); it('should use OpacityFn to compute opacity override', () => { - expect(stringToRGB('rgba(50,50,50,0.25)', (o) => o * 2).opacity).toBe(0.5); + expect(overrideOpacity(colorToRgba('rgba(50,50,50,0.25)'), (o) => o * 2)[3]).toBe(0.5); }); }); describe('Edge Cases', () => { it.each([ - [undefined, { r: 255, g: 0, b: 0, opacity: 1 }], - ['', { r: 255, g: 0, b: 0, opacity: 1 }], - ['bad', { r: 187, g: 170, b: 221, opacity: 1 }], - ['#00000000', { r: 0, g: 0, b: 0, opacity: 0 }], - ['#000000', { r: 0, g: 0, b: 0, opacity: 1 }], - ['#6092c000', { r: 96, g: 146, b: 192, opacity: 0 }], - ['#6092c06b', { r: 96, g: 146, b: 192, opacity: 0.42 }], - ['blue', { r: 0, g: 0, b: 255, opacity: 1 }], - ['hsl(20, 100%, 40%)', { r: 204, g: 68, b: 0, opacity: 1 }], - ['hsla(20, 100%, 40%, 0.5)', { r: 204, g: 68, b: 0, opacity: 0.5 }], - ['hsla(20, 100%, 40%, 0)', { r: 204, g: 68, b: 0, opacity: 0 }], - ['rgb(0,128,128)', { r: 0, g: 128, b: 128, opacity: 1 }], - ['rgba(0,128,128,0.5)', { r: 0, g: 128, b: 128, opacity: 0.5 }], - ['rgba(0,128,128,0)', { r: 0, g: 128, b: 128, opacity: 0 }], + // [undefined, [255,0,0, 1 ], + ['', [255, 0, 0, 1]], + ['bad', [187, 170, 221, 1]], + ['#00000000', [0, 0, 0, 0]], + ['#000000', [0, 0, 0, 1]], + ['#6092c000', [96, 146, 192, 0]], + ['#6092c06b', [96, 146, 192, 0.42]], + ['blue', [0, 0, 255, 1]], + ['hsl(20, 100%, 40%)', [204, 68, 0, 1]], + ['hsla(20, 100%, 40%, 0.5)', [204, 68, 0, 0.5]], + ['hsla(20, 100%, 40%, 0)', [204, 68, 0, 0]], + ['rgb(0,128,128)', [0, 128, 128, 1]], + ['rgba(0,128,128,0.5)', [0, 128, 128, 0.5]], + ['rgba(0,128,128,0)', [0, 128, 128, 0]], ])('input: $1', (input, output) => { - expect(stringToRGB(input)).toEqual(output); + expect(colorToRgba(input)).toEqual(output); }); }); }); - describe('validateColor', () => { - it.each(['r', 'g', 'b', 'opacity'])('should return null if %s is NaN', (value) => { - expect( - validateColor({ - ...defaultD3Color, - [value]: NaN, - }), - ).toBeNull(); - }); - - it('should return valid colors', () => { - expect(validateColor(defaultD3Color)).toBe(defaultD3Color); - }); - }); - - describe('argsToRGB', () => { - it.each(['r', 'g', 'b', 'opacity'])('should return defaultD3Color if %s is NaN', (value) => { - const { r, g, b, opacity }: RgbObject = { - ...defaultD3Color, - [value]: NaN, - }; - expect(argsToRGB(r, g, b, opacity)).toEqual(defaultD3Color); - }); - - it('should return valid colors', () => { - const { r, g, b, opacity } = defaultD3Color; - expect(argsToRGB(r, g, b, opacity)).toEqual(defaultD3Color); - }); - }); - - describe('argsToRGBString', () => { - it('should return valid colors', () => { - const { r, g, b, opacity } = defaultD3Color; - expect(argsToRGBString(r, g, b, opacity)).toBe('rgb(255, 0, 0)'); - }); - }); - - describe('RGBtoString', () => { - it('should return valid colors', () => { - expect(RGBtoString(defaultD3Color)).toBe('rgb(255, 0, 0)'); - }); + describe('colorToRGB always return a color', () => { + it.each(['rgba(NaN, 0, 0, 0)', 'rgba(0, NaN, 0, 0)', 'rgba(0, 0, NaN, 0)', 'rgba(0, 0, 0, NaN)'])( + 'should return null if %s is NaN', + (color) => { + expect(colorToRgba(color)).toEqual([255, 0, 0, 1]); + expect(Logger.warn).toBeCalledTimes(1); + }, + ); }); }); diff --git a/packages/charts/src/common/color_library_wrappers.ts b/packages/charts/src/common/color_library_wrappers.ts index b8eefbd00f0..6af1efbfdb9 100644 --- a/packages/charts/src/common/color_library_wrappers.ts +++ b/packages/charts/src/common/color_library_wrappers.ts @@ -7,117 +7,73 @@ */ import chroma from 'chroma-js'; -import { rgb as d3Rgb, RGBColor as D3RGBColor } from 'd3-color'; -import { Color } from '../utils/common'; +import { clamp, Color } from '../utils/common'; +import { Logger } from '../utils/logger'; +import { LRUCache } from './data_structures'; type RGB = number; type A = number; /** @internal */ -export type RgbTuple = [RGB, RGB, RGB, RGB?]; -/** @public */ -export type RgbObject = { r: RGB; g: RGB; b: RGB; opacity: A }; - -/** @internal */ -export type RgbaTuple = [RGB, RGB, RGB, RGB]; +export type RgbTuple = [RGB, RGB, RGB, A?]; -/** @internal */ -export const defaultColor: RgbObject = { r: 255, g: 0, b: 0, opacity: 1 }; -/** @internal */ -export const transparentColor: RgbObject = { r: 0, g: 0, b: 0, opacity: 0 }; -/** @internal */ -export const defaultD3Color: D3RGBColor = d3Rgb(defaultColor.r, defaultColor.g, defaultColor.b, defaultColor.opacity); +/** @public */ +export type RgbaTuple = [r: RGB, g: RGB, b: RGB, alpha: A]; /** @internal */ -export type OpacityFn = (opacity: number, seriesOpacity?: number) => number; +export type OpacityFn = (opacity: number) => number; /** @internal */ -export function stringToRGB(cssColorSpecifier?: string, opacity?: number | OpacityFn): RgbObject { - if (cssColorSpecifier === 'transparent') { - return transparentColor; - } - const color = getColor(cssColorSpecifier); - - if (opacity === undefined) { - return color; - } - - const opacityOverride = typeof opacity === 'number' ? opacity : opacity(color.opacity); +export function overrideOpacity([r, g, b, o]: RgbaTuple, opacity?: number | OpacityFn): RgbaTuple { + const opacityOverride = opacity === undefined ? o : typeof opacity === 'number' ? opacity : opacity(o); - if (isNaN(opacityOverride)) { - return color; + // don't apply override on transparent color to avoid unwanted behaviours + // todo check if we can apply to every transparent colors + if (r === 0 && b === 0 && g === 0 && o === 0) { + return [0, 0, 0, 0]; } - - return { - ...color, - opacity: opacityOverride, - }; + return [r, g, b, clamp(Number.isFinite(opacityOverride) ? opacityOverride : o, 0, 1)]; } -/** - * Returns color as RgbObject or default fallback. - * - * Handles issue in d3-color for hsla and rgba colors with alpha value of `0` - * - * @param cssColorSpecifier - */ -function getColor(cssColorSpecifier: string = ''): RgbObject { - if (!chroma.valid(cssColorSpecifier)) return defaultColor; - - const chromaColor = chroma(cssColorSpecifier); - const color: D3RGBColor = { - ...d3Rgb(chromaColor.alpha(1).css()), - opacity: chromaColor.alpha(), - }; - - return validateColor(color) ?? defaultColor; +/** @internal */ +export function RGBATupleToString(rgba: RgbTuple): Color { + return `rgba(${rgba[0]}, ${rgba[1]}, ${rgba[2]}, ${rgba[3] ?? 1})`; } /** @internal */ -export function validateColor(color: D3RGBColor): D3RGBColor | null { - const { r, g, b, opacity } = color; - - if (isNaN(r) || isNaN(g) || isNaN(b) || isNaN(opacity)) { - return null; +export function isValid(color: Color): chroma.Color | false { + try { + // ref https://github.com/gka/chroma.js/issues/280 + return chroma(color === 'transparent' ? 'rgba(0,0,0,0)' : color); + } catch { + return false; } - - return color; } /** @internal */ -export function argsToRGB(r: number, g: number, b: number, opacity: number): D3RGBColor { - return validateColor(d3Rgb(r, g, b, opacity)) ?? defaultD3Color; +export function getChromaColor(color: RgbaTuple): chroma.Color { + // chroma mutates the input + return chroma(...color); } /** @internal */ -export function argsToRGBString(r: number, g: number, b: number, opacity: number): string { - // d3.rgb returns an Rgb instance, which has a specialized `toString` method - return argsToRGB(r, g, b, opacity).toString(); +export function getGreensColorScale(gamma: number, domain: [number, number]): (value: number) => Color { + const scale = chroma.scale(chroma.brewer.Greens).gamma(gamma).domain(domain); + return (value: number) => scale(value).css(); } -/** @internal */ -export function RGBtoString(rgb: RgbObject): string { - const { r, g, b, opacity } = rgb; - return argsToRGBString(r, g, b, opacity); -} +const rgbaCache = new LRUCache(200); /** @internal */ -export function RGBATupleToString(rgba: RgbTuple): string { - if (rgba.length === 4) { - return `rgba(${rgba[0]}, ${rgba[1]}, ${rgba[2]}, ${rgba[3]})`; +export function colorToRgba(color: Color): RgbaTuple { + const cachedValue = rgbaCache.get(color); + if (cachedValue === undefined) { + const chromaColor = isValid(color); + if (chromaColor === false) Logger.warn(`The provided color is not a valid CSS color, using RED as fallback`, color); + const newValue: RgbaTuple = chromaColor ? chromaColor.rgba() : [255, 0, 0, 1]; + rgbaCache.set(color, newValue); + return newValue; } - return `rgb(${rgba[0]}, ${rgba[1]}, ${rgba[2]})`; -} - -/** convert rgb to hex - * @internal */ -export function RGBAToHex(rgba: Color) { - return chroma(rgba).hex(); -} - -/** convert hex to rgb - * @internal */ -export function HexToRGB(hex: string) { - return chroma(hex).rgba(); + return cachedValue; } diff --git a/packages/charts/src/common/data_structures.ts b/packages/charts/src/common/data_structures.ts new file mode 100644 index 00000000000..b3fc7a9b994 --- /dev/null +++ b/packages/charts/src/common/data_structures.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { clamp } from '../utils/common'; + +/** @internal */ +export class LRUCache { + private readonly max: number; + private readonly cache: Map; + constructor(max = 10) { + this.max = clamp(max, 1, Infinity); + this.cache = new Map(); + } + + get(key: K) { + const item = this.cache.get(key); + if (item) { + this.cache.delete(key); + this.cache.set(key, item); + } + return item; + } + + set(key: K, val: V) { + if (this.cache.has(key)) this.cache.delete(key); + else if (this.cache.size === this.max) this.cache.delete(this.first()); + this.cache.set(key, val); + } + + first() { + return this.cache.keys().next().value; + } +} diff --git a/packages/charts/src/common/fill_text_color.ts b/packages/charts/src/common/fill_text_color.ts index 1adc1f5f156..25fa37b211f 100644 --- a/packages/charts/src/common/fill_text_color.ts +++ b/packages/charts/src/common/fill_text_color.ts @@ -6,80 +6,17 @@ * Side Public License, v 1. */ -import chroma from 'chroma-js'; - import { Color } from '../utils/common'; -import { Logger } from '../utils/logger'; -import { - colorIsDark, - combineColors, - getTextColorIfTextInvertible, - isColorValid, - makeHighContrastColor, -} from './color_calcs'; -import { TextContrast } from './text_utils'; - -function isBackgroundColorValid(color: string | undefined, logWarning: boolean): color is string { - const bgColorAlpha = isColorValid(color) ? chroma(color).alpha() : 1; - if (isColorValid(color) && bgColorAlpha >= 1) { - return true; - } - if (logWarning && bgColorAlpha < 1) { - Logger.expected('Text contrast requires a background color with an alpha value of 1', 1, bgColorAlpha); - } - if (logWarning && color !== 'transparent') { - Logger.warn(`Invalid background color "${String(color)}"`); - } - return false; -} +import { combineColors, highContrastColor } from './color_calcs'; +import { colorToRgba, RGBATupleToString } from './color_library_wrappers'; /** - * Determine the color for the text hinging on the parameters of textInvertible and textContrast + * Determine the color for the text hinging on the parameters of maximizeColorContrast, foreground and containerBackground * @internal */ -export function fillTextColor( - textColor: Color, - textInvertible: boolean, - textContrast: TextContrast, - shapeFillColor: Color, - backgroundColor?: Color, -): string { - if (!isBackgroundColorValid(backgroundColor, true)) { - return getTextColorIfTextInvertible( - colorIsDark(shapeFillColor), - colorIsDark(textColor), - textColor, - false, - 'white', // never used - ); - } - - const adjustedTextColor: string | undefined = textColor; - const containerBackground = combineColors( - shapeFillColor, - backgroundColor === 'transparent' ? 'rgba(255, 255, 255, 1)' : backgroundColor, - ); - const textShouldBeInvertedAndTextContrastIsFalse = textInvertible && !textContrast; - const textShouldBeInvertedAndTextContrastIsSetToTrue = textInvertible && typeof textContrast !== 'number'; - const textContrastIsSetToANumberValue = typeof textContrast === 'number'; - const textShouldNotBeInvertedButTextContrastIsDefined = textContrast && !textInvertible; - - // change the contrast for the inverted slices - if (textShouldBeInvertedAndTextContrastIsFalse || textShouldBeInvertedAndTextContrastIsSetToTrue) { - const backgroundIsDark = colorIsDark(combineColors(shapeFillColor, backgroundColor)); - const specifiedTextColorIsDark = colorIsDark(textColor); - return getTextColorIfTextInvertible( - backgroundIsDark, - specifiedTextColorIsDark, - textColor, - textContrast, - containerBackground, - ); - // if textContrast is a number then take that into account or if textInvertible is set to false - } - if (textContrastIsSetToANumberValue || textShouldNotBeInvertedButTextContrastIsDefined) { - return makeHighContrastColor(adjustedTextColor, containerBackground); - } - - return adjustedTextColor; +export function fillTextColor(background: Color, containerBg: Color = 'white'): Color { + const backgroundRGBA = colorToRgba(background); + const containerBgRGBA = combineColors(colorToRgba(containerBg), [255, 255, 255, 1]); // combine it with white if semi-transparent + const blendedFbBg = combineColors(backgroundRGBA, containerBgRGBA); + return RGBATupleToString(highContrastColor(blendedFbBg)); } diff --git a/packages/charts/src/common/text_utils.ts b/packages/charts/src/common/text_utils.ts index a9443237b9e..b9fef361308 100644 --- a/packages/charts/src/common/text_utils.ts +++ b/packages/charts/src/common/text_utils.ts @@ -16,8 +16,6 @@ import { Pixels, Rectangle } from './geometry'; const FONT_WEIGHTS_NUMERIC = [100, 200, 300, 400, 500, 600, 700, 800, 900]; const FONT_WEIGHTS_ALPHA = ['normal', 'bold', 'lighter', 'bolder', 'inherit', 'initial', 'unset']; -/** @public */ -export type TextContrast = boolean | number; /** * todo consider doing tighter control for permissible font families, eg. as in Kibana Canvas - expression language * - though the same applies for permissible (eg. known available or loaded) font weights, styles, variants... @@ -62,7 +60,6 @@ export interface Font { fontWeight: FontWeight; fontFamily: FontFamily; textColor: string; - textOpacity: number; } /** @public */ diff --git a/packages/charts/src/common/wcag2_color_contrast.ts b/packages/charts/src/common/wcag2_color_contrast.ts new file mode 100644 index 00000000000..1ec989470ca --- /dev/null +++ b/packages/charts/src/common/wcag2_color_contrast.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { RgbTuple } from './color_library_wrappers'; + +function sRGBtoLin(colorChannel: number) { + // Send this function a decimal sRGB gamma encoded color value + // between 0.0 and 1.0, and it returns a linearized value. + return colorChannel <= 0.03928 ? colorChannel / 12.92 : Math.pow((colorChannel + 0.055) / 1.055, 2.4); +} + +function getLuminance([r, g, b]: RgbTuple) { + const vR = r / 255; + const vG = g / 255; + const vB = b / 255; + return 0.2126 * sRGBtoLin(vR) + 0.7152 * sRGBtoLin(vG) + 0.0722 * sRGBtoLin(vB); +} + +/** @internal */ +export function getWCAG2ContrastRatio(foreground: RgbTuple, background: RgbTuple) { + const lumA = getLuminance(foreground); + const lumB = getLuminance(background); + + return lumA >= lumB ? (lumA + 0.05) / (lumB + 0.05) : (lumB + 0.05) / (lumA + 0.05); +} diff --git a/packages/charts/src/components/brush/brush.tsx b/packages/charts/src/components/brush/brush.tsx index 95cba3d326f..795dcf398b9 100644 --- a/packages/charts/src/components/brush/brush.tsx +++ b/packages/charts/src/components/brush/brush.tsx @@ -10,7 +10,7 @@ import React, { RefObject } from 'react'; import { connect } from 'react-redux'; import { renderRect } from '../../chart_types/xy_chart/renderer/canvas/primitives/rect'; -import { RgbObject, stringToRGB } from '../../common/color_library_wrappers'; +import { colorToRgba, RgbaTuple } from '../../common/color_library_wrappers'; import { clearCanvas, withContext, withClip } from '../../renderers/canvas'; import { GlobalChartState } from '../../state/chart_state'; import { getInternalBrushAreaSelector } from '../../state/selectors/get_internal_brush_area'; @@ -21,10 +21,6 @@ import { getInternalMainProjectionAreaSelector } from '../../state/selectors/get import { getInternalProjectionContainerAreaSelector } from '../../state/selectors/get_internal_projection_container_area'; import { Dimensions } from '../../utils/dimensions'; -interface OwnProps { - fillColor?: RgbObject; -} - interface StateProps { initialized: boolean; mainProjectionArea: Dimensions; @@ -35,16 +31,10 @@ interface StateProps { zIndex: number; } -const DEFAULT_FILL_COLOR: RgbObject = { - r: 128, - g: 128, - b: 128, - opacity: 0.6, -}; +// todo move this to theme +const DEFAULT_FILL_COLOR: RgbaTuple = [128, 128, 128, 0.6]; -type Props = OwnProps & StateProps; - -class BrushToolComponent extends React.Component { +class BrushToolComponent extends React.Component { static displayName = 'BrushTool'; private readonly devicePixelRatio: number; @@ -53,7 +43,7 @@ class BrushToolComponent extends React.Component { private canvasRef: RefObject; - constructor(props: Readonly) { + constructor(props: Readonly) { super(props); this.ctx = null; this.devicePixelRatio = window.devicePixelRatio; @@ -101,7 +91,8 @@ class BrushToolComponent extends React.Component { } private drawCanvas = () => { - const { brushEvent, mainProjectionArea, fillColor } = this.props; + const { brushEvent, mainProjectionArea } = this.props; + const { ctx } = this; if (!ctx || !brushEvent) { return; @@ -118,13 +109,13 @@ class BrushToolComponent extends React.Component { height: mainProjectionArea.height, }, () => { - clearCanvas(ctx); + clearCanvas(ctx, 'transparent'); ctx.translate(mainProjectionArea.left, mainProjectionArea.top); renderRect( ctx, { x: left, y: top, width, height }, - { color: fillColor ?? DEFAULT_FILL_COLOR }, - { width: 0, color: stringToRGB('transparent') }, + { color: DEFAULT_FILL_COLOR }, + { width: 0, color: colorToRgba('transparent') }, ); }, ); diff --git a/packages/charts/src/components/tooltip/tooltip.tsx b/packages/charts/src/components/tooltip/tooltip.tsx index 30234a52221..ff5ea77f02f 100644 --- a/packages/charts/src/components/tooltip/tooltip.tsx +++ b/packages/charts/src/components/tooltip/tooltip.tsx @@ -6,13 +6,12 @@ * Side Public License, v 1. */ -import chroma from 'chroma-js'; import classNames from 'classnames'; import React, { memo, useCallback, useMemo, useEffect } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators, Dispatch } from 'redux'; -import { isColorValid } from '../../common/color_calcs'; +import { colorToRgba } from '../../common/color_library_wrappers'; import { TooltipValueFormatter, TooltipSettings, TooltipValue } from '../../specs'; import { onPointerMove as onPointerMoveAction } from '../../state/actions/mouse'; import { GlobalChartState, BackwardRef } from '../../state/chart_state'; @@ -113,7 +112,8 @@ const TooltipComponent = ({ const classes = classNames('echTooltip__item', { echTooltip__rowHighlighted: isHighlighted, }); - const adjustedBGColor = isColorValid(color) && chroma(color).alpha() === 0 ? 'transparent' : backgroundColor; + + const adjustedBGColor = colorToRgba(color)[3] === 0 ? 'transparent' : backgroundColor; return (
{ /** - * patern to apply to canvas fill + * pattern to apply to canvas fill */ pattern: CanvasPattern; } @@ -67,7 +57,7 @@ export interface Fill { /** * fill color in rgba */ - color: RgbObject; + color: RgbaTuple; texture?: Texture; } @@ -79,7 +69,7 @@ export interface Stroke { /** * stroke rgba */ - color: RgbObject; + color: RgbaTuple; /** * stroke width */ diff --git a/packages/charts/src/mocks/geometries.ts b/packages/charts/src/mocks/geometries.ts index abb49d8bd0f..0da02327cdd 100644 --- a/packages/charts/src/mocks/geometries.ts +++ b/packages/charts/src/mocks/geometries.ts @@ -29,20 +29,10 @@ export class MockPointGeometry { style: { shape: PointShape.Circle, fill: { - color: { - r: 255, - g: 255, - b: 255, - opacity: 1, - }, + color: [255, 255, 255, 1], }, stroke: { - color: { - r: 255, - g: 0, - b: 0, - opacity: 1, - }, + color: [255, 0, 0, 1], width: 1, }, }, diff --git a/packages/charts/src/renderers/canvas/index.ts b/packages/charts/src/renderers/canvas/index.ts index c984eb89a7c..b78ef6fee66 100644 --- a/packages/charts/src/renderers/canvas/index.ts +++ b/packages/charts/src/renderers/canvas/index.ts @@ -7,6 +7,7 @@ */ import { Rect } from '../../geoms/types'; +import { Color } from '../../utils/common'; import { ClippedRanges } from '../../utils/geometry'; /** @internal */ @@ -33,10 +34,14 @@ export function withContext(ctx: CanvasRenderingContext2D, fun: CanvasRenderer) } /** @internal */ -export function clearCanvas(ctx: CanvasRenderingContext2D) { +export function clearCanvas(ctx: CanvasRenderingContext2D, bgColor: Color) { withContext(ctx, () => { ctx.setTransform(1, 0, 0, 1, 0, 0); + // with transparent background, clearRect is required ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + ctx.fillStyle = bgColor; + // filling with the background color is required to have a precise text color contrast calculation + ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); }); } diff --git a/packages/charts/src/utils/themes/theme.ts b/packages/charts/src/utils/themes/theme.ts index f1224c08d4b..448d07e612d 100644 --- a/packages/charts/src/utils/themes/theme.ts +++ b/packages/charts/src/utils/themes/theme.ts @@ -341,8 +341,6 @@ export type DisplayValueStyle = Omit & { | Color | { color: Color; borderColor?: Color; borderWidth?: number } | { - textInvertible: boolean; - textContrast?: number | boolean; textBorder?: number; }; alignment?: { diff --git a/storybook/stories/bar/51_label_value_advanced.story.tsx b/storybook/stories/bar/51_label_value_advanced.story.tsx index f7c36a0f467..306f90d0326 100644 --- a/storybook/stories/bar/51_label_value_advanced.story.tsx +++ b/storybook/stories/bar/51_label_value_advanced.story.tsx @@ -60,7 +60,7 @@ export const Example = () => { }; const debug = boolean('debug', false); - const useInverted = boolean('textInverted', false); + const useBorder = boolean('useBorder', false); const valueColor = color('value color', '#fff'); const borderColor = color('value border color', 'rgba(0,0,0,1)'); const borderSize = number('value border width', 1.5); @@ -78,9 +78,7 @@ export const Example = () => { fontFamily: "'Open Sans', Helvetica, Arial, sans-serif", fontStyle: 'normal', padding: 0, - fill: useInverted - ? { textInvertible: useInverted, textContrast: true, textBorder: borderSize } - : { color: valueColor, borderColor, borderWidth: borderSize }, + fill: useBorder ? { textBorder: borderSize } : { color: valueColor, borderColor, borderWidth: borderSize }, offsetX: number('offsetX', 0), offsetY: number('offsetY', 0), alignment: { diff --git a/storybook/stories/interactions/17_png_export.story.tsx b/storybook/stories/interactions/17_png_export.story.tsx index faebb36e604..b720dac9a41 100644 --- a/storybook/stories/interactions/17_png_export.story.tsx +++ b/storybook/stories/interactions/17_png_export.story.tsx @@ -93,7 +93,6 @@ function renderPartitionChart() { { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/mosaic/10_mosaic_simple.story.tsx b/storybook/stories/mosaic/10_mosaic_simple.story.tsx index 31cf82964c4..452a7443144 100644 --- a/storybook/stories/mosaic/10_mosaic_simple.story.tsx +++ b/storybook/stories/mosaic/10_mosaic_simple.story.tsx @@ -116,3 +116,7 @@ export const Example = () => { ); }; + +Example.parameters = { + background: { default: 'white' }, +}; diff --git a/storybook/stories/mosaic/20_mosaic_with_other.story.tsx b/storybook/stories/mosaic/20_mosaic_with_other.story.tsx index a790ca9795a..1e26452a111 100644 --- a/storybook/stories/mosaic/20_mosaic_with_other.story.tsx +++ b/storybook/stories/mosaic/20_mosaic_with_other.story.tsx @@ -91,3 +91,7 @@ export const Example = () => { ); }; + +Example.parameters = { + background: { default: 'white' }, +}; diff --git a/storybook/stories/small_multiples/7_sunbursts.story.tsx b/storybook/stories/small_multiples/7_sunbursts.story.tsx index 92c7b0faba1..b3f6257ede5 100644 --- a/storybook/stories/small_multiples/7_sunbursts.story.tsx +++ b/storybook/stories/small_multiples/7_sunbursts.story.tsx @@ -152,7 +152,6 @@ export const Example = () => { fillLabel: { valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, fontStyle: 'italic', - textInvertible: true, fontWeight: 900, valueFont: { fontFamily: 'Menlo', diff --git a/storybook/stories/stylings/20_partition_background.story.tsx b/storybook/stories/stylings/20_partition_background.story.tsx index f5a3820d50a..b9e82fcd597 100644 --- a/storybook/stories/stylings/20_partition_background.story.tsx +++ b/storybook/stories/stylings/20_partition_background.story.tsx @@ -30,9 +30,6 @@ export const Example = () => { color: color('Background color', 'rgba(255, 255, 255, 1)'), }, }; - const invertTextColors = boolean('invert colors for lightness/darkness', true); - const toggleTextContrast = boolean('text contrast', true); - return ( @@ -75,8 +72,6 @@ export const Example = () => { fillLabel: { valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, fontStyle: 'italic', - textInvertible: invertTextColors, - textContrast: toggleTextContrast, fontWeight: 900, valueFont: { fontFamily: 'Menlo', diff --git a/storybook/stories/stylings/21_partition_labels.story.tsx b/storybook/stories/stylings/21_partition_labels.story.tsx index 5e66a5f268e..616b1ec9e4c 100644 --- a/storybook/stories/stylings/21_partition_labels.story.tsx +++ b/storybook/stories/stylings/21_partition_labels.story.tsx @@ -34,7 +34,6 @@ export const Example = () => { { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true, textContrast: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/10_2_slice.story.tsx b/storybook/stories/sunburst/10_2_slice.story.tsx index 12e7cb38658..d238c5f0132 100644 --- a/storybook/stories/sunburst/10_2_slice.story.tsx +++ b/storybook/stories/sunburst/10_2_slice.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/11_small_large.story.tsx b/storybook/stories/sunburst/11_small_large.story.tsx index d156e36f564..5e25f947f90 100644 --- a/storybook/stories/sunburst/11_small_large.story.tsx +++ b/storybook/stories/sunburst/11_small_large.story.tsx @@ -29,7 +29,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => d, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/12_very_small.story.tsx b/storybook/stories/sunburst/12_very_small.story.tsx index abc2742ed72..4a13f6d4963 100644 --- a/storybook/stories/sunburst/12_very_small.story.tsx +++ b/storybook/stories/sunburst/12_very_small.story.tsx @@ -29,7 +29,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => d, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/13_empty.story.tsx b/storybook/stories/sunburst/13_empty.story.tsx index edc487f16ca..c82b9dcd45b 100644 --- a/storybook/stories/sunburst/13_empty.story.tsx +++ b/storybook/stories/sunburst/13_empty.story.tsx @@ -29,7 +29,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/14_full_zero.story.tsx b/storybook/stories/sunburst/14_full_zero.story.tsx index 45a35038d10..90571d951f1 100644 --- a/storybook/stories/sunburst/14_full_zero.story.tsx +++ b/storybook/stories/sunburst/14_full_zero.story.tsx @@ -29,7 +29,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/15_single.story.tsx b/storybook/stories/sunburst/15_single.story.tsx index adc561461ec..4831eaf699a 100644 --- a/storybook/stories/sunburst/15_single.story.tsx +++ b/storybook/stories/sunburst/15_single.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/15_single_sunburst.story.tsx b/storybook/stories/sunburst/15_single_sunburst.story.tsx index cfc56166939..9d79a2f0d6a 100644 --- a/storybook/stories/sunburst/15_single_sunburst.story.tsx +++ b/storybook/stories/sunburst/15_single_sunburst.story.tsx @@ -64,7 +64,6 @@ export const Example = () => ( fillLabel: { valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, fontStyle: 'italic', - textInvertible: true, fontWeight: 900, valueFont: { fontFamily: 'Menlo', diff --git a/storybook/stories/sunburst/16_single_small.story.tsx b/storybook/stories/sunburst/16_single_small.story.tsx index 1a98068cdb1..ce852dd92cf 100644 --- a/storybook/stories/sunburst/16_single_small.story.tsx +++ b/storybook/stories/sunburst/16_single_small.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/17_single_very_small.story.tsx b/storybook/stories/sunburst/17_single_very_small.story.tsx index b8d26af83df..63071aca21b 100644 --- a/storybook/stories/sunburst/17_single_very_small.story.tsx +++ b/storybook/stories/sunburst/17_single_very_small.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/18_no_sliced.story.tsx b/storybook/stories/sunburst/18_no_sliced.story.tsx index cf9f2ba24e1..93671691891 100644 --- a/storybook/stories/sunburst/18_no_sliced.story.tsx +++ b/storybook/stories/sunburst/18_no_sliced.story.tsx @@ -26,7 +26,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/19_negative.story.tsx b/storybook/stories/sunburst/19_negative.story.tsx index e79d365a50a..60ad2cf33ce 100644 --- a/storybook/stories/sunburst/19_negative.story.tsx +++ b/storybook/stories/sunburst/19_negative.story.tsx @@ -30,7 +30,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/1_simple.story.tsx b/storybook/stories/sunburst/1_simple.story.tsx index 08829f81954..cd519282a43 100644 --- a/storybook/stories/sunburst/1_simple.story.tsx +++ b/storybook/stories/sunburst/1_simple.story.tsx @@ -30,7 +30,6 @@ export const Example = () => { { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/20_total_zero.story.tsx b/storybook/stories/sunburst/20_total_zero.story.tsx index b9c40020151..665359a6547 100644 --- a/storybook/stories/sunburst/20_total_zero.story.tsx +++ b/storybook/stories/sunburst/20_total_zero.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/21_high_pie.story.tsx b/storybook/stories/sunburst/21_high_pie.story.tsx index 7c11b797c03..2e314f45d16 100644 --- a/storybook/stories/sunburst/21_high_pie.story.tsx +++ b/storybook/stories/sunburst/21_high_pie.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.origin, nodeLabel: (d: Datum) => countryLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/22_counter_clockwise.story.tsx b/storybook/stories/sunburst/22_counter_clockwise.story.tsx index a3f183a10ba..57e5a54b8d3 100644 --- a/storybook/stories/sunburst/22_counter_clockwise.story.tsx +++ b/storybook/stories/sunburst/22_counter_clockwise.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/23_clockwise.story.tsx b/storybook/stories/sunburst/23_clockwise.story.tsx index d39dc0d53ff..43675b0ae7f 100644 --- a/storybook/stories/sunburst/23_clockwise.story.tsx +++ b/storybook/stories/sunburst/23_clockwise.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/24_linked_label.story.tsx b/storybook/stories/sunburst/24_linked_label.story.tsx index 815a8c024ea..7036e099241 100644 --- a/storybook/stories/sunburst/24_linked_label.story.tsx +++ b/storybook/stories/sunburst/24_linked_label.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/25_no_labels.story.tsx b/storybook/stories/sunburst/25_no_labels.story.tsx index f2c049c33ff..aa704fb14e0 100644 --- a/storybook/stories/sunburst/25_no_labels.story.tsx +++ b/storybook/stories/sunburst/25_no_labels.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/26_percentage.story.tsx b/storybook/stories/sunburst/26_percentage.story.tsx index 0e3d8a6874a..263b94f009a 100644 --- a/storybook/stories/sunburst/26_percentage.story.tsx +++ b/storybook/stories/sunburst/26_percentage.story.tsx @@ -65,7 +65,6 @@ export const Example = () => ( fontFamily: 'Arial', fillLabel: { fontStyle: 'italic', - textInvertible: true, fontWeight: 900, valueFont: { fontFamily: 'Menlo', diff --git a/storybook/stories/sunburst/27_heterogeneous_depth.story.tsx b/storybook/stories/sunburst/27_heterogeneous_depth.story.tsx index 637de29f266..3619a489cbe 100644 --- a/storybook/stories/sunburst/27_heterogeneous_depth.story.tsx +++ b/storybook/stories/sunburst/27_heterogeneous_depth.story.tsx @@ -66,7 +66,6 @@ export const Example = () => ( fillLabel: { valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, fontStyle: 'italic', - textInvertible: true, fontWeight: 900, valueFont: { fontFamily: 'Menlo', diff --git a/storybook/stories/sunburst/28_not_a_number.story.tsx b/storybook/stories/sunburst/28_not_a_number.story.tsx index ef5793c34e4..8de3a4af9bd 100644 --- a/storybook/stories/sunburst/28_not_a_number.story.tsx +++ b/storybook/stories/sunburst/28_not_a_number.story.tsx @@ -30,7 +30,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/29_custom_stroke.story.tsx b/storybook/stories/sunburst/29_custom_stroke.story.tsx index 2cf6e8b9bac..8d7626a8ce7 100644 --- a/storybook/stories/sunburst/29_custom_stroke.story.tsx +++ b/storybook/stories/sunburst/29_custom_stroke.story.tsx @@ -34,7 +34,6 @@ export const Example = () => { { groupByRollup: (d: Datum) => d.origin, nodeLabel: (d: Datum) => countryLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/2_value_formatted.story.tsx b/storybook/stories/sunburst/2_value_formatted.story.tsx index 35dc305ad51..cd05fd0a029 100644 --- a/storybook/stories/sunburst/2_value_formatted.story.tsx +++ b/storybook/stories/sunburst/2_value_formatted.story.tsx @@ -29,7 +29,6 @@ export const Example = () => ( groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, fillLabel: { - textInvertible: true, fontWeight: 100, fontStyle: 'italic', valueFont: { diff --git a/storybook/stories/sunburst/30_largest_circle.story.tsx b/storybook/stories/sunburst/30_largest_circle.story.tsx index c67509e5946..bbd41849291 100644 --- a/storybook/stories/sunburst/30_largest_circle.story.tsx +++ b/storybook/stories/sunburst/30_largest_circle.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/31_bold_link_value.story.tsx b/storybook/stories/sunburst/31_bold_link_value.story.tsx index 5a64444a035..6dea62809ee 100644 --- a/storybook/stories/sunburst/31_bold_link_value.story.tsx +++ b/storybook/stories/sunburst/31_bold_link_value.story.tsx @@ -27,7 +27,7 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true, valueFont: { fontWeight: 900, fontStyle: 'italic' } }, + fillLabel: { valueFont: { fontWeight: 900, fontStyle: 'italic' } }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/32_custom_tooltip.story.tsx b/storybook/stories/sunburst/32_custom_tooltip.story.tsx index fe1d28c766b..296e557794b 100644 --- a/storybook/stories/sunburst/32_custom_tooltip.story.tsx +++ b/storybook/stories/sunburst/32_custom_tooltip.story.tsx @@ -80,7 +80,6 @@ export const Example = () => { }, fontFamily: 'Arial', fillLabel: { - textInvertible: true, valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, fontStyle: 'italic', }, diff --git a/storybook/stories/sunburst/33_ordered_slices.story.tsx b/storybook/stories/sunburst/33_ordered_slices.story.tsx index 347a78aa584..b4894a48ede 100644 --- a/storybook/stories/sunburst/33_ordered_slices.story.tsx +++ b/storybook/stories/sunburst/33_ordered_slices.story.tsx @@ -68,7 +68,6 @@ export const Example = () => { nodeLabel: (d: any) => d, fillLabel: { valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}`, - textInvertible: true, fontWeight: 600, fontStyle: 'italic', valueFont: { @@ -86,7 +85,6 @@ export const Example = () => { nodeLabel: (d: any) => countryLookup[d]?.name ?? d, sortPredicate: boolean('Move "Other" to end', true) ? sortPredicate : null, fillLabel: { - textInvertible: true, fontWeight: 600, fontStyle: 'italic', maxFontSize: 16, diff --git a/storybook/stories/sunburst/3_value_formatted_2.story.tsx b/storybook/stories/sunburst/3_value_formatted_2.story.tsx index d1a506d92c7..bc099a076d3 100644 --- a/storybook/stories/sunburst/3_value_formatted_2.story.tsx +++ b/storybook/stories/sunburst/3_value_formatted_2.story.tsx @@ -29,7 +29,6 @@ export const Example = () => ( groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, fillLabel: { - textInvertible: true, fontWeight: 100, fontStyle: 'italic', valueFont: { diff --git a/storybook/stories/sunburst/4_fill_labels.story.tsx b/storybook/stories/sunburst/4_fill_labels.story.tsx index acf6b551685..f23c43531dc 100644 --- a/storybook/stories/sunburst/4_fill_labels.story.tsx +++ b/storybook/stories/sunburst/4_fill_labels.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/5_donut.story.tsx b/storybook/stories/sunburst/5_donut.story.tsx index 2e2604ad7c4..5eb417574c3 100644 --- a/storybook/stories/sunburst/5_donut.story.tsx +++ b/storybook/stories/sunburst/5_donut.story.tsx @@ -27,7 +27,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/6_pie_chart_labels.story.tsx b/storybook/stories/sunburst/6_pie_chart_labels.story.tsx index c0d75c8370c..54124f3ca19 100644 --- a/storybook/stories/sunburst/6_pie_chart_labels.story.tsx +++ b/storybook/stories/sunburst/6_pie_chart_labels.story.tsx @@ -29,7 +29,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, // nodeLabel: (d: Datum) => d, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/6_pie_chart_linked_labels.story.tsx b/storybook/stories/sunburst/6_pie_chart_linked_labels.story.tsx index ab768cae0e1..cf1aade9c15 100644 --- a/storybook/stories/sunburst/6_pie_chart_linked_labels.story.tsx +++ b/storybook/stories/sunburst/6_pie_chart_linked_labels.story.tsx @@ -29,7 +29,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, // nodeLabel: (d: Datum) => d, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/7_zero_slice.story.tsx b/storybook/stories/sunburst/7_zero_slice.story.tsx index c277d1a3ee2..1dcccdc1d02 100644 --- a/storybook/stories/sunburst/7_zero_slice.story.tsx +++ b/storybook/stories/sunburst/7_zero_slice.story.tsx @@ -30,7 +30,6 @@ export const Example = () => ( { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: indexInterpolatedFillColor(interpolatorCET2s), }, diff --git a/storybook/stories/sunburst/8_sunburst_two_layers.story.tsx b/storybook/stories/sunburst/8_sunburst_two_layers.story.tsx index 888fa2bb65c..cf2701d08ae 100644 --- a/storybook/stories/sunburst/8_sunburst_two_layers.story.tsx +++ b/storybook/stories/sunburst/8_sunburst_two_layers.story.tsx @@ -59,7 +59,6 @@ export const Example = () => { }, fontFamily: 'Arial', fillLabel: { - textInvertible: true, valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, fontStyle: 'italic', }, diff --git a/storybook/stories/sunburst/9_sunburst_three_layers.story.tsx b/storybook/stories/sunburst/9_sunburst_three_layers.story.tsx index 6d14694707a..ef6dee79f71 100644 --- a/storybook/stories/sunburst/9_sunburst_three_layers.story.tsx +++ b/storybook/stories/sunburst/9_sunburst_three_layers.story.tsx @@ -68,7 +68,6 @@ export const Example = () => ( fillLabel: { valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, fontStyle: 'italic', - textInvertible: true, fontWeight: 900, valueFont: { fontFamily: 'Menlo', diff --git a/storybook/stories/treemap/10_three_layers.story.tsx b/storybook/stories/treemap/10_three_layers.story.tsx index 603bf0e3d5b..90a519a806f 100644 --- a/storybook/stories/treemap/10_three_layers.story.tsx +++ b/storybook/stories/treemap/10_three_layers.story.tsx @@ -46,7 +46,6 @@ export const Example = () => ( valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, fontFamily: 'Helvetica', textColor: 'black', - textInvertible: false, fontWeight: 900, minFontSize: 2, maxFontSize: 20, @@ -61,7 +60,6 @@ export const Example = () => ( fillLabel: { valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, textColor: 'black', - textInvertible: false, fontWeight: 200, fontStyle: 'normal', fontFamily: 'Helvetica', diff --git a/storybook/stories/treemap/1_one_layer.story.tsx b/storybook/stories/treemap/1_one_layer.story.tsx index ac3422645b1..2bd9a8f9fdf 100644 --- a/storybook/stories/treemap/1_one_layer.story.tsx +++ b/storybook/stories/treemap/1_one_layer.story.tsx @@ -37,7 +37,6 @@ export const Example = () => ( groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, fillLabel: { - textInvertible: true, valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, }, shape: { diff --git a/storybook/stories/treemap/2_one_layer_2.story.tsx b/storybook/stories/treemap/2_one_layer_2.story.tsx index 923f79a9d3e..5a646ef4a9e 100644 --- a/storybook/stories/treemap/2_one_layer_2.story.tsx +++ b/storybook/stories/treemap/2_one_layer_2.story.tsx @@ -33,7 +33,6 @@ export const Example = () => ( groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, fillLabel: { - textInvertible: true, valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, valueFont: { fontWeight: 100, diff --git a/storybook/stories/treemap/3_mid_two.story.tsx b/storybook/stories/treemap/3_mid_two.story.tsx index 8942cc34c8f..d0714234a49 100644 --- a/storybook/stories/treemap/3_mid_two.story.tsx +++ b/storybook/stories/treemap/3_mid_two.story.tsx @@ -39,7 +39,6 @@ export const Example = () => ( valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, fontFamily: 'Helvetica', textColor: 'grey', - textInvertible: false, }, shape: { fillColor: 'rgba(0,0,0,0)' }, }, @@ -49,7 +48,6 @@ export const Example = () => ( fillLabel: { valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, textColor: 'black', - textInvertible: false, fontWeight: 200, fontStyle: 'normal', fontFamily: 'Helvetica', diff --git a/storybook/stories/treemap/4_two_layer_stress.story.tsx b/storybook/stories/treemap/4_two_layer_stress.story.tsx index d50c1aaa95b..01c3eea80b4 100644 --- a/storybook/stories/treemap/4_two_layer_stress.story.tsx +++ b/storybook/stories/treemap/4_two_layer_stress.story.tsx @@ -39,7 +39,6 @@ export const Example = () => ( valueFormatter: () => '', fontFamily: 'Helvetica', textColor: 'grey', - textInvertible: true, }, shape: { fillColor: 'rgba(0, 0, 0, 0)', @@ -51,7 +50,6 @@ export const Example = () => ( fillLabel: { valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, textColor: 'black', - textInvertible: true, fontWeight: 100, fontStyle: 'normal', fontFamily: 'Helvetica', diff --git a/storybook/stories/treemap/5_multicolor.story.tsx b/storybook/stories/treemap/5_multicolor.story.tsx index 7d319b16b97..bc4d269dad9 100644 --- a/storybook/stories/treemap/5_multicolor.story.tsx +++ b/storybook/stories/treemap/5_multicolor.story.tsx @@ -62,7 +62,6 @@ export const Example = () => ( fillLabel: { valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, textColor: 'rgba(60,60,60,1)', - textInvertible: false, fontWeight: 100, fontStyle: 'normal', fontFamily: 'Din Condensed', diff --git a/storybook/stories/treemap/6_custom_style.story.tsx b/storybook/stories/treemap/6_custom_style.story.tsx index 6401adfb71d..9b22b26b707 100644 --- a/storybook/stories/treemap/6_custom_style.story.tsx +++ b/storybook/stories/treemap/6_custom_style.story.tsx @@ -53,7 +53,6 @@ export const Example = () => ( fillLabel: { valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, textColor: 'rgba(60,60,60,1)', - textInvertible: false, fontWeight: 600, fontStyle: 'normal', fontFamily: 'Courier New', diff --git a/storybook/stories/treemap/7_percentage.story.tsx b/storybook/stories/treemap/7_percentage.story.tsx index 89679adab1b..6cee1cdfacd 100644 --- a/storybook/stories/treemap/7_percentage.story.tsx +++ b/storybook/stories/treemap/7_percentage.story.tsx @@ -46,7 +46,6 @@ export const Example = () => ( fontFamily: 'Helvetica', textColor: 'black', fontWeight: 100, - textInvertible: false, }, shape: { fillColor: 'rgba(0,0,0,0)' }, }, @@ -55,7 +54,6 @@ export const Example = () => ( nodeLabel: (d: any) => countryLookup[d].name, fillLabel: { textColor: 'black', - textInvertible: false, fontWeight: 200, fontStyle: 'normal', fontFamily: 'Helvetica', diff --git a/storybook/stories/treemap/8_groove_text.story.tsx b/storybook/stories/treemap/8_groove_text.story.tsx index f83af12bf6f..508f19e9493 100644 --- a/storybook/stories/treemap/8_groove_text.story.tsx +++ b/storybook/stories/treemap/8_groove_text.story.tsx @@ -40,7 +40,6 @@ export const Example = () => ( valueFormatter: () => '', fontFamily: 'Helvetica', textColor: '#555', - textInvertible: false, fontWeight: 100, padding: { top: number('group padding top', 0, { range: true, min: 0, max: 20 }), @@ -60,7 +59,6 @@ export const Example = () => ( fillLabel: { valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, textColor: 'black', - textInvertible: true, fontWeight: 200, fontStyle: 'normal', fontFamily: 'Helvetica', diff --git a/storybook/stories/treemap/9_zero_values.story.tsx b/storybook/stories/treemap/9_zero_values.story.tsx index e6dc93c59b7..cea218cf830 100644 --- a/storybook/stories/treemap/9_zero_values.story.tsx +++ b/storybook/stories/treemap/9_zero_values.story.tsx @@ -37,7 +37,6 @@ export const Example = () => ( groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, fillLabel: { - textInvertible: true, valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`, }, shape: { diff --git a/storybook/stories/utils/hierarchical_input_utils.tsx b/storybook/stories/utils/hierarchical_input_utils.tsx index 693b23b017b..95eda9904ba 100644 --- a/storybook/stories/utils/hierarchical_input_utils.tsx +++ b/storybook/stories/utils/hierarchical_input_utils.tsx @@ -63,7 +63,6 @@ export const config: RecursivePartial = { fontFamily: 'Arial', fillLabel: { valueFormatter: (d: number) => d, - textInvertible: true, fontWeight: 500, }, margin: { top: 0, bottom: 0, left: 0, right: 0 }, diff --git a/storybook/stories/waffle/1_simple.story.tsx b/storybook/stories/waffle/1_simple.story.tsx index c35b209a739..c8e01a98a6a 100644 --- a/storybook/stories/waffle/1_simple.story.tsx +++ b/storybook/stories/waffle/1_simple.story.tsx @@ -30,7 +30,6 @@ export const Example = () => { { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9.slice(1))(d.sortIndex), }, @@ -38,7 +37,6 @@ export const Example = () => { { groupByRollup: (d: Datum) => d.sitc1, nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, shape: { fillColor: (d: ShapeTreeNode) => discreteColor(colorBrewerCategoricalStark9.slice(1))(d.sortIndex), }, diff --git a/yarn.lock b/yarn.lock index 9d9e7a55c3e..45eceb37b91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5548,7 +5548,7 @@ resolved "https://registry.yarnpkg.com/@types/d3-collection/-/d3-collection-1.0.8.tgz#aa9552c570a96e33c132e0fd20e331f64baa9dd5" integrity sha512-y5lGlazdc0HNO0F3UUX2DPE7OmYvd9Kcym4hXwrJcNUkDaypR5pX+apuMikl9LfTxKItJsY9KYvzBulpCKyvuQ== -"@types/d3-color@*", "@types/d3-color@^1.2.2": +"@types/d3-color@*": version "1.2.2" resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-1.2.2.tgz#80cf7cfff7401587b8f89307ba36fe4a576bc7cf" integrity sha512-6pBxzJ8ZP3dYEQ4YjQ+NVbQaOflfgXq/JbDiS99oLobM2o72uAST4q6yPxHv6FOTCRC/n35ktuo8pvw/S4M7sw==