Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

And/migrate set custom colors #2

Merged
merged 3 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ let config = {
MFE_CONFIG_API_URL: process.env.MFE_CONFIG_API_URL,
APP_ID: process.env.APP_ID,
SUPPORT_URL: process.env.SUPPORT_URL,
CUSTOM_PRIMARY_COLORS: process.env.CUSTOM_PRIMARY_COLORS || {},
};

/**
Expand Down Expand Up @@ -325,4 +326,5 @@ export function ensureConfig(keys, requester = 'unspecified application code') {
* @property {string} APP_ID
* @property {string} SUPPORT_URL
* @property {string} PARAGON_THEME_URLS
* @property {Object} CUSTOM_PRIMARY_COLORS
*/
14 changes: 14 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,17 @@ export const APP_INIT_ERROR = `${APP_TOPIC}.INIT_ERROR`;
export const CONFIG_TOPIC = 'CONFIG';

export const CONFIG_CHANGED = `${CONFIG_TOPIC}.CHANGED`;

export const PRIMARY_COLOR_DEFINITIONS = {
'pgn-color-primary-100': { '#FFFFFF': 94 },
'pgn-color-primary-200': { '#FFFFFF': 75 },
'pgn-color-primary-300': { '#FFFFFF': 50 },
'pgn-color-primary-400': { '#FFFFFF': 25 },
'pgn-color-primary-500': { '#FFFFFF': 0 },
'pgn-color-primary-600': { '#000000': 10 },
'pgn-color-primary-700': { '#000000': 20 },
'pgn-color-primary-800': { '#000000': 25 },
'pgn-color-primary-900': { '#000000': 30 },
'pgn-color-link-base': { '#FFFFFF': 35 },
'pgn-color-link-hover': { '#FFFFFF': 0 },
};
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export {
convertKeyNames,
getQueryParameters,
ensureDefinedConfig,
mix,
} from './utils';
export {
APP_TOPIC,
Expand Down
39 changes: 39 additions & 0 deletions src/initialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ import {
APP_LOGGING_INITIALIZED,
APP_ANALYTICS_INITIALIZED,
APP_READY, APP_INIT_ERROR,
PRIMARY_COLOR_DEFINITIONS,
} from './constants';
import configureCache from './auth/LocalForageCache';
import { mix } from './utils';

/**
* A browser history or memory history object created by the [history](https://github.com/ReactTraining/history)
Expand Down Expand Up @@ -193,6 +195,42 @@ export function loadExternalScripts(externalScripts, data) {
});
}

/*
* Set custom colors based on the config content.
* This method allows to change primary colors and its levels on runtime,
* if a specific level is already in the configuration that level will have
* priority otherwise the level will be calculated based on primary color by
* using the mix function.
*/
export function setCustomPrimaryColors() {
const { CUSTOM_PRIMARY_COLORS } = getConfig();
const { PARAGON_THEME_URLS } = getConfig();
const primary = CUSTOM_PRIMARY_COLORS['pgn-color-primary-base'];

if (!primary || PARAGON_THEME_URLS != null) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This make me thing... whatever you define with the var PARAGON_THEME_URLS in the mfe_config would discard the CUSTOM_PRIMARY_COLORS feature. But for the moment seems functional.

return;
}
document.documentElement.style.setProperty('--pgn-color-primary-base', primary);

Object.keys(PRIMARY_COLOR_DEFINITIONS).forEach((key) => {
let color;

if (key in CUSTOM_PRIMARY_COLORS) {
color = CUSTOM_PRIMARY_COLORS[key];
} else {
try {
const [base, weight] = Object.entries(PRIMARY_COLOR_DEFINITIONS[key])[0];

color = mix(base, primary, weight);
} catch (error) {
// eslint-disable-next-line no-console
console.error('Error setting custom colors', error.message);
}
}
document.documentElement.style.setProperty('--'.concat(key), color);
});
}

/**
* The default handler for the initialization lifecycle's `analytics` phase.
*
Expand Down Expand Up @@ -294,6 +332,7 @@ export async function initialize({
await handlers.config();
await jsFileConfig();
await runtimeConfig();
setCustomPrimaryColors();
publish(APP_CONFIG_INITIALIZED);

loadExternalScripts(externalScripts, {
Expand Down
85 changes: 85 additions & 0 deletions src/initialize.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,4 +350,89 @@ describe('initialize', () => {
expect(hydrateAuthenticatedUser).not.toHaveBeenCalled();
expect(logError).not.toHaveBeenCalled();
});

it('should not set any color', async () => {
const messages = { i_am: 'a message' };
await initialize({
messages,
});

// eslint-disable-next-line no-underscore-dangle
expect(document.documentElement.style._values).toEqual({});
expect(logError).not.toHaveBeenCalled();
});

it('should set primary color and calculate its levels', async () => {
config.CUSTOM_PRIMARY_COLORS = { 'pgn-color-primary-base': '#A000000' };
const messages = { i_am: 'a message' };
const expectedKeys = [
'--pgn-color-primary-base',
'--pgn-color-primary-100',
'--pgn-color-primary-200',
'--pgn-color-primary-300',
'--pgn-color-primary-400',
'--pgn-color-primary-500',
'--pgn-color-primary-600',
'--pgn-color-primary-700',
'--pgn-color-primary-800',
'--pgn-color-primary-900',
'--pgn-color-link-base',
'--pgn-color-link-hover',
];

await initialize({
messages,
});

// eslint-disable-next-line no-underscore-dangle
expect(Object.keys(document.documentElement.style._values)).toEqual(expectedKeys);
expect(logError).not.toHaveBeenCalled();
});

it('should set primary color and its levels from config', async () => {
config.CUSTOM_PRIMARY_COLORS = {
'pgn-color-primary-base': '#A000000',
'pgn-color-primary-100': '#A001000',
'pgn-color-primary-200': '#A000000',
'pgn-color-primary-300': '#A045000',
'pgn-color-primary-400': '#A07AB00',
'pgn-color-primary-500': '#A000B12',
'pgn-color-primary-600': '#A087400',
'pgn-color-primary-700': '#A0abc00',
'pgn-color-primary-800': '#AABCFA0',
'pgn-color-primary-900': '#A014200',
'pgn-color-link-base': '#FF0056',
'pgn-color-link-hover': '#AFFCDA',
};
const messages = { i_am: 'a message' };

await initialize({
messages,
});

// eslint-disable-next-line no-underscore-dangle
expect(Object.values(document.documentElement.style._values)).toEqual(Object.values(config.CUSTOM_PRIMARY_COLORS));
expect(logError).not.toHaveBeenCalled();
});
it('should log error when color is invalid', async () => {
// eslint-disable-next-line no-console
console.error = jest.fn();
configureCache.mockReturnValueOnce(Promise.resolve({
get: (url) => {
const params = new URL(url).search;
const mfe = new URLSearchParams(params).get('mfe');
return ({ data: { ...newConfig.common, ...newConfig[mfe] } });
},
}));
config.CUSTOM_PRIMARY_COLORS = { 'pgn-color-primary-base': '#AB' };
const messages = { i_am: 'a message' };

await initialize({
messages,
});

// eslint-disable-next-line no-console
expect(console.error).toHaveBeenNthCalledWith(9, 'Error setting custom colors', 'Parameter color does not have format #RRGGBB');
expect(logError).not.toHaveBeenCalled();
});
});
1 change: 1 addition & 0 deletions src/react/AppProvider.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jest.mock('../config', () => ({
REFRESH_ACCESS_TOKEN_ENDPOINT: 'localhost:18000/oauth2/access_token',
ACCESS_TOKEN_COOKIE_NAME: 'access_token',
CSRF_TOKEN_API_PATH: 'localhost:18000/csrf',
CUSTOM_PRIMARY_COLORS: {},
}),
}));

Expand Down
34 changes: 34 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,37 @@ export function ensureDefinedConfig(object, requester) {
}
});
}

/**
* This function is the javascript version of SASS mix() function,
* https://sass-lang.com/documentation/modules/color#mix
*
* @param {string} First color in hexadecimal.
* @param {string} Second color in hexadecimal.
* @param {number} Relative opacity of each color.
* @returns {string} Returns a color that’s a mixture of color1 and color2.
*/
export function mix(color1, color2, weight = 50) {
let color = '#';

function d2h(d) { return d.toString(16); } // convert a decimal value to hex
function h2d(h) { return parseInt(h, 16); } // convert a hex value to decimal

if (color1.length < 6 || color2.length < 6) {
throw new Error('Parameter color does not have format #RRGGBB');
}

for (let i = 0; i <= 5; i += 2) { // loop through each of the 3 hex pairs—red, green, and blue
const v1 = h2d(color1.replace('#', '').substr(i, 2));
const v2 = h2d(color2.replace('#', '').substr(i, 2));
let val = d2h(Math.round(v2 + (v1 - v2) * (weight / 100.0)));

while (val.length < 2) {
val = '0'.concat(val);
}

color += val;
}

return color;
}
19 changes: 19 additions & 0 deletions src/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
snakeCaseObject,
convertKeyNames,
getQueryParameters,
mix,
} from '.';

describe('modifyObjectKeys', () => {
Expand Down Expand Up @@ -113,3 +114,21 @@ describe('getQueryParameters', () => {
});
});
});

describe('mix', () => {
it('should return rigth value', () => {
const expected = '#546e88'; // This value was calculated in https://sass.js.org/ by using sass mix function

expect(mix('#FFFFFF', '#0A3055', 30)).toBe(expected);
});

it('should thow error', () => {
expect(() => mix('#FFFFFF', '#0A3')).toThrow('Parameter color does not have format #RRGGBB');
});

it('should return rigth value without hash symbol on parameters', () => {
const expected = '#8598aa'; // This value was calculated in https://sass.js.org/ by using sass mix function

expect(mix('FFFFFF', '0A3055')).toBe(expected);
});
});