diff --git a/.ci/vrts.sh b/.ci/vrts.sh index 1c3252f0ec..541f1b8ed3 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 9dd38f5505..694b28038d 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 f038002ddf..153ba3ca72 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 5965aea4d5..9bbf59f84e 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 8eaf1f71b3..96511a5965 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 34707f6b7f..e97a85d0ba 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 53688116e0..02dd72ec66 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 5eb537bc16..c2590dbbda 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 2cd9188fa7..ec4323f8e0 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 35d8a6d816..334d912c78 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 b36ea0553a..cf19520c15 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 16e311feed..70b8e9e987 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 964a522cbd..ab20933833 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 8cb05eb328..7dd0ed1d91 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 046a394514..e7a529fc5a 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 1701853b5a..147a5864ca 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 f74fb52489..e318efecc0 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 71b28fc598..aa2b4005fb 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 22271e0cfc..0cf9b9cdb5 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 ca8bfae312..60eccc4172 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 28235f8eee..40a1e9740b 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 dc5ca3973e..52f6c0a6ed 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 0858553d29..db94ab7b1d 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 cfe7bd83fd..4582995729 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 ec9cc29c84..acf0ca6fc3 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 0221467820..5ad6270450 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 a5b2b157fe..e291cf69f0 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 3074e5e877..21ad88da93 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 3074e5e877..21ad88da93 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 6f38bf3e28..09037c4525 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 a1b92ec05b..48fe53d128 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 6fdbe060d2..679e647bfb 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 cfe7bd83fd..4582995729 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 73d5a2bc58..f73f342a6c 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 2e8fa5f711..1ed2e2d8ee 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 b811100d29..74422d6d66 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 8009ff2132..63815bcfde 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 f5ccaed941..d37dd2ee37 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 356be7cf98..191b7897e8 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 83033e9706..ebe3e933cb 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 ba3cd4ab88..99e3ad68d2 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 33654ba0a8..551a58eb42 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 8f39c2fdb1..ebc580aef4 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 bf531cc4ef..9ec4d8908f 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 cbde9d60ca..33f3812c9a 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 70ff0f2b63..a525d41109 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 597b5660c9..ed5b086574 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 e75a99f71c..b040d6a26b 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 bbab206a54..08cfbd11dc 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 8c0e770bf8..7963e08e66 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 ea83b37b4c..3d4b16b213 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 a8b357ea1a..036dd50839 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 174f51b4e8..4db277a7ee 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 15c8ae5ac4..10b8d01973 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 8d38e3f2d1..afd88c6e13 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 39aa2fef0c..2aa8799121 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 ac4db31da3..54af0e87da 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 18305732c2..437576577c 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 b1e3d73aed..e63be27d34 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 82a2193efd..8c02af8406 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 a22ca9d066..2de754d5b9 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 46078413b8..4bb3c9c78c 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 1ea546cde3..594f3daf02 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 966c8e5e9d..4699e2b444 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 48c61da915..9b81c40029 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 aa0f96b739..a1b133eec2 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 80306ec9b8..a79d9f95cb 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 de51d264f2..8372780e42 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 4022c3963f..455af53328 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 1b2bda57d9..c4640c6bf9 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 baa570a22d..7af736738d 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 c9a7693134..b1259288cf 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 15218ae5b5..216a3a2396 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 7d7ff9b120..ef9365a487 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 92360860a5..e6d0a2ac8d 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 dd90b8b9db..aba665ad93 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 156fe7bc9f..a01c055aa1 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 a355030014..9a0fd3ab06 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 ca53cf609d..fb08f60dfa 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 1ac9b509d8..71cc124525 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 418aff27bd..dfaaef13fc 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 3e8d89e10b..2af01408c6 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 a12286e243..22a083f68d 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 cd58e23847..e98fdbdfb0 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 51b420575f..6b8d8e7fb0 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 64bededbcc..fc131f44ef 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 6cf6a8da97..3cde7b4fb8 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 553bc26520..7baa2b4d1e 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 4433a73068..cb5dbd4ec1 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 eb70c043d4..a76b3d2508 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 af7a2a5894..b50d60f88f 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 49c23161e1..465c2e206b 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 23390867d8..bf1d9d98d5 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 d57776539e..37aa45ff33 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 e6d0dcb096..67edc78675 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 279d05b5a6..6198d28e23 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 aa24f17981..53ef446b90 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 974f884cf7..8d5707eb02 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 5f91836545..c7b46d77ad 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 042432d1c8..09c7e71edc 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 d3bd0c92d4..68c0b3697c 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 9533d74932..7997018c6f 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 233557c272..7faba54cd1 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 917e5645bf..7d10482c99 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 11eaf14dd5..19d6371869 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 4eb08947d5..0000000000 --- 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 fc9fdd4db9..0000000000 --- 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 0000000000..158db6de85 --- /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 a4253af754..0d69021071 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 e37a952715..a30322e85a 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 d135a23982..d31457e87d 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 b8eefbd00f..6af1efbfdb 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 0000000000..b3fc7a9b99 --- /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 1adc1f5f15..25fa37b211 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 a9443237b9..b9fef36130 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 0000000000..1ec989470c --- /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 95cba3d326..795dcf398b 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 30234a5222..ff5ea77f02 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 abb49d8bd0..0da02327cd 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 c984eb89a7..b78ef6fee6 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 f1224c08d4..448d07e612 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 f7c36a0f46..306f90d032 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 faebb36e60..b720dac9a4 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 31cf82964c..452a744314 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 a790ca9795..1e26452a11 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 92c7b0faba..b3f6257ede 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 f5a3820d50..b9e82fcd59 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 5e66a5f268..616b1ec9e4 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 12e7cb3865..d238c5f013 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 d156e36f56..5e25f947f9 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 abc2742ed7..4a13f6d496 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 edc487f16c..c82b9dcd45 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 45a35038d1..90571d951f 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 adc561461e..4831eaf699 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 cfc5616693..9d79a2f0d6 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 1a98068cdb..ce852dd92c 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 b8d26af83d..63071aca21 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 cf9f2ba24e..9367169189 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 e79d365a50..60ad2cf33c 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 08829f8195..cd519282a4 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 b9c4002015..665359a654 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 7c11b797c0..2e314f45d1 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 a3f183a10b..57e5a54b8d 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 d39dc0d53f..43675b0ae7 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 815a8c024e..7036e09924 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 f2c049c33f..aa704fb14e 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 0e3d8a6874..263b94f009 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 637de29f26..3619a489cb 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 ef5793c34e..8de3a4af9b 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 2cf6e8b9ba..8d7626a8ce 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 35dc305ad5..cd05fd0a02 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 c67509e594..bbd4184929 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 5a64444a03..6dea62809e 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 fe1d28c766..296e557794 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 347a78aa58..b4894a48ed 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 d1a506d92c..bc099a076d 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 acf6b55168..f23c43531d 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 2e2604ad7c..5eb417574c 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 c0d75c8370..54124f3ca1 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 ab768cae0e..cf1aade9c1 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 c277d1a3ee..1dcccdc1d0 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 888fa2bb65..cf2701d08a 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 6d14694707..ef6dee79f7 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 603bf0e3d5..90a519a806 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 ac3422645b..2bd9a8f9fd 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 923f79a9d3..5a646ef4a9 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 8942cc34c8..d0714234a4 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 d50c1aaa95..01c3eea80b 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 7d319b16b9..bc4d269dad 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 6401adfb71..9b22b26b70 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 89679adab1..6cee1cdfac 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 f83af12bf6..508f19e949 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 e6dc93c59b..cea218cf83 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 693b23b017..95eda9904b 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 c35b209a73..c8e01a98a6 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 9d9e7a55c3..45eceb37b9 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==