From 573e45691f2d744b9d237ef3c7235039cff0d90f Mon Sep 17 00:00:00 2001 From: Nicolas Le Cam Date: Thu, 26 Oct 2023 21:48:35 +0200 Subject: [PATCH] MM-52280: Generate higher contrast for some usernames (#25073) * MM-52280: Generate higher contrast for some usernames Keep 3 tries but return the best value instead of the last one Signed-off-by: Nicolas Le Cam * Try ten times to find the best contrast before giving up --------- Signed-off-by: Nicolas Le Cam --- .../src/components/user_profile/user.test.ts | 30 ++++++++++++++++++ .../src/components/user_profile/utils.ts | 31 ++++++++++++++----- 2 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 webapp/channels/src/components/user_profile/user.test.ts diff --git a/webapp/channels/src/components/user_profile/user.test.ts b/webapp/channels/src/components/user_profile/user.test.ts new file mode 100644 index 0000000000..2e518df640 --- /dev/null +++ b/webapp/channels/src/components/user_profile/user.test.ts @@ -0,0 +1,30 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import ColorContrastChecker from 'color-contrast-checker'; + +import {cachedUserNameColors, generateColor} from './utils'; + +const CONSTRAST_CHECKER = new ColorContrastChecker(); +const BACKGROUND_COLOR = '#ACC8E5'; + +describe('components/user_profile/utils', () => { + test.each([ + ['Ross_Bednar', '#432dd2', 4.5], + ['Geovany95', '#2d3086', 4.5], + ['Madisen25', '#52783a', 2.9], + ['Gerard17', '#783a54', 4.5], + ['Alia30', '#392d86', 4.5], + ['Darien.Prosacco97', '#862d6d', 4.5], + ['Alf48', '#4053bf', 3.7], + ['Darron_Orn-Walsh49', '#742d86', 4.5], + ])('should generate best color contrast', (userName, expected, ratio) => { + cachedUserNameColors.clear(); + + const actual = generateColor(userName, BACKGROUND_COLOR); + expect(actual).toBe(expected); + expect( + CONSTRAST_CHECKER.isLevelCustom(actual, BACKGROUND_COLOR, ratio), + ).toBeTruthy(); + }); +}); diff --git a/webapp/channels/src/components/user_profile/utils.ts b/webapp/channels/src/components/user_profile/utils.ts index 406b50d225..2b3d2da8cc 100644 --- a/webapp/channels/src/components/user_profile/utils.ts +++ b/webapp/channels/src/components/user_profile/utils.ts @@ -4,27 +4,44 @@ import ColorContrastChecker from 'color-contrast-checker'; import ColorHash from 'color-hash'; -const cachedColors = new Map(); +const REQUIRED_COLOR_RATIO = 4.5; + +export const cachedUserNameColors = new Map(); export function generateColor(username: string, background: string): string { const cacheKey = `${username}-${background}`; - const cachedColor = cachedColors.get(cacheKey); + const cachedColor = cachedUserNameColors.get(cacheKey); if (cachedColor) { return cachedColor; } let userColor = background; + let contrastRatio = 1; let userAndSalt = username; const checker = new ColorContrastChecker(); const colorHash = new ColorHash(); - let tries = 3; - while (!checker.isLevelCustom(userColor, background, 4.5) && tries > 0) { - userColor = colorHash.hex(userAndSalt); + const backgroundLuminance = checker.hexToLuminance(background); + for (let tries = 10; tries > 0; tries--) { + const textColor = colorHash.hex(userAndSalt); + + const cr = checker.getContrastRatio( + checker.hexToLuminance(textColor), + backgroundLuminance, + ); + + if (cr > contrastRatio) { + userColor = textColor; + contrastRatio = cr; + } + + if (cr >= REQUIRED_COLOR_RATIO) { + break; + } + userAndSalt += 'salt'; - tries--; } - cachedColors.set(cacheKey, userColor); + cachedUserNameColors.set(cacheKey, userColor); return userColor; }