refactor: language card renderer refactor (#1184)

* refactor: language card render refactor

* chore: change error msg
This commit is contained in:
Anurag Hazra 2021-07-11 19:28:06 +05:30 committed by GitHub
parent 99aea7f803
commit 112efb4b92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 162 additions and 118 deletions

View File

@ -40,7 +40,7 @@ module.exports = async (req, res) => {
}
if (locale && !isLocaleAvailable(locale)) {
return res.send(renderError("Something went wrong", "Language not found"));
return res.send(renderError("Something went wrong", "Locale not found"));
}
try {

View File

@ -4,6 +4,13 @@ const { langCardLocales } = require("../translations");
const { createProgressNode } = require("../common/createProgressNode");
const { clampValue, getCardColors, flexLayout } = require("../common/utils");
const DEFAULT_CARD_WIDTH = 300;
const DEFAULT_LANGS_COUNT = 5;
const DEFAULT_LANG_COLOR = "#858585";
const CARD_PADDING = 25;
const lowercaseTrim = (name) => name.toLowerCase().trim();
const createProgressTextNode = ({ width, color, name, progress }) => {
const paddingRight = 95;
const progressTextX = width - paddingRight + 10;
@ -58,35 +65,99 @@ const createLanguageTextNode = ({ langs, totalSize, x, y }) => {
});
};
const lowercaseTrim = (name) => name.toLowerCase().trim();
/**
*
* @param {any[]} langs
* @param {number} width
* @param {number} totalLanguageSize
* @returns {string}
*/
const renderNormalLayout = (langs, width, totalLanguageSize) => {
return flexLayout({
items: langs.map((lang) => {
return createProgressTextNode({
width: width,
name: lang.name,
color: lang.color || DEFAULT_LANG_COLOR,
progress: ((lang.size / totalLanguageSize) * 100).toFixed(2),
});
}),
gap: 40,
direction: "column",
}).join("");
};
const renderTopLanguages = (topLangs, options = {}) => {
const {
hide_title,
hide_border,
card_width,
title_color,
text_color,
bg_color,
hide,
theme,
layout,
custom_title,
locale,
langs_count = 5,
border_radius,
border_color,
} = options;
/**
*
* @param {any[]} langs
* @param {number} width
* @param {number} totalLanguageSize
* @returns {string}
*/
const renderCompactLayout = (langs, width, totalLanguageSize) => {
const paddingRight = 50;
const offsetWidth = width - paddingRight;
// progressOffset holds the previous language's width and used to offset the next language
// so that we can stack them one after another, like this: [--][----][---]
let progressOffset = 0;
const compactProgressBar = langs
.map((lang) => {
const percentage = parseFloat(
((lang.size / totalLanguageSize) * offsetWidth).toFixed(2),
);
const i18n = new I18n({
locale,
translations: langCardLocales,
});
const progress = percentage < 10 ? percentage + 10 : percentage;
const output = `
<rect
mask="url(#rect-mask)"
data-testid="lang-progress"
x="${progressOffset}"
y="0"
width="${progress}"
height="8"
fill="${lang.color || "#858585"}"
/>
`;
progressOffset += percentage;
return output;
})
.join("");
return `
<mask id="rect-mask">
<rect x="0" y="0" width="${offsetWidth}" height="8" fill="white" rx="5" />
</mask>
${compactProgressBar}
${createLanguageTextNode({
x: 0,
y: 25,
langs,
totalSize: totalLanguageSize,
}).join("")}
`;
};
/**
* @param {number} totalLangs
* @returns {number}
*/
const calculateCompactLayoutHeight = (totalLangs) => {
return 90 + Math.round(totalLangs / 2) * 25;
};
/**
* @param {number} totalLangs
* @returns {number}
*/
const calculateNormalLayoutHeight = (totalLangs) => {
return 45 + (totalLangs + 1) * 40;
};
const useLanguages = (topLangs, hide, langs_count) => {
let langs = Object.values(topLangs);
let langsToHide = {};
langsCount = clampValue(parseInt(langs_count), 1, 10);
let langsCount = clampValue(parseInt(langs_count), 1, 10);
// populate langsToHide map for quick lookup
// while filtering out
@ -104,12 +175,55 @@ const renderTopLanguages = (topLangs, options = {}) => {
})
.slice(0, langsCount);
const totalLanguageSize = langs.reduce((acc, curr) => {
return acc + curr.size;
}, 0);
const totalLanguageSize = langs.reduce((acc, curr) => acc + curr.size, 0);
return { langs, totalLanguageSize };
};
const renderTopLanguages = (topLangs, options = {}) => {
const {
hide_title,
hide_border,
card_width,
title_color,
text_color,
bg_color,
hide,
theme,
layout,
custom_title,
locale,
langs_count = DEFAULT_LANGS_COUNT,
border_radius,
border_color,
} = options;
const i18n = new I18n({
locale,
translations: langCardLocales,
});
const { langs, totalLanguageSize } = useLanguages(
topLangs,
hide,
langs_count,
);
let width = isNaN(card_width) ? DEFAULT_CARD_WIDTH : card_width;
let height = calculateNormalLayoutHeight(langs.length);
let finalLayout = "";
if (layout === "compact") {
width = width + 50; // padding
height = calculateCompactLayoutHeight(langs.length);
finalLayout = renderCompactLayout(langs, width, totalLanguageSize);
} else {
finalLayout = renderNormalLayout(langs, width, totalLanguageSize);
}
// returns theme based colors with proper overrides and defaults
const { titleColor, textColor, bgColor, borderColor } = getCardColors({
const colors = getCardColors({
title_color,
text_color,
bg_color,
@ -117,97 +231,24 @@ const renderTopLanguages = (topLangs, options = {}) => {
theme,
});
let width = isNaN(card_width) ? 300 : card_width;
let height = 45 + (langs.length + 1) * 40;
let finalLayout = "";
// RENDER COMPACT LAYOUT
if (layout === "compact") {
width = width + 50;
height = 90 + Math.round(langs.length / 2) * 25;
// progressOffset holds the previous language's width and used to offset the next language
// so that we can stack them one after another, like this: [--][----][---]
let progressOffset = 0;
const compactProgressBar = langs
.map((lang) => {
const percentage = (
(lang.size / totalLanguageSize) *
(width - 50)
).toFixed(2);
const progress =
percentage < 10 ? parseFloat(percentage) + 10 : percentage;
const output = `
<rect
mask="url(#rect-mask)"
data-testid="lang-progress"
x="${progressOffset}"
y="0"
width="${progress}"
height="8"
fill="${lang.color || "#858585"}"
/>
`;
progressOffset += parseFloat(percentage);
return output;
})
.join("");
finalLayout = `
<mask id="rect-mask">
<rect x="0" y="0" width="${
width - 50
}" height="8" fill="white" rx="5" />
</mask>
${compactProgressBar}
${createLanguageTextNode({
x: 0,
y: 25,
langs,
totalSize: totalLanguageSize,
}).join("")}
`;
} else {
finalLayout = flexLayout({
items: langs.map((lang) => {
return createProgressTextNode({
width: width,
name: lang.name,
color: lang.color || "#858585",
progress: ((lang.size / totalLanguageSize) * 100).toFixed(2),
});
}),
gap: 40,
direction: "column",
}).join("");
}
const card = new Card({
customTitle: custom_title,
defaultTitle: i18n.t("langcard.title"),
width,
height,
border_radius,
colors: {
titleColor,
textColor,
bgColor,
borderColor,
},
colors,
});
card.disableAnimations();
card.setHideBorder(hide_border);
card.setHideTitle(hide_title);
card.setCSS(`
.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
`);
card.setCSS(
`.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${colors.textColor} }`,
);
return card.render(`
<svg data-testid="lang-items" x="25">
<svg data-testid="lang-items" x="${CARD_PADDING}">
${finalLayout}
</svg>
`);

View File

@ -198,7 +198,7 @@ describe("Test renderTopLanguages", () => {
);
expect(queryAllByTestId(document.body, "lang-progress")[0]).toHaveAttribute(
"width",
"120.00",
"120",
);
expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent(
@ -206,7 +206,7 @@ describe("Test renderTopLanguages", () => {
);
expect(queryAllByTestId(document.body, "lang-progress")[1]).toHaveAttribute(
"width",
"120.00",
"120",
);
expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent(
@ -214,7 +214,7 @@ describe("Test renderTopLanguages", () => {
);
expect(queryAllByTestId(document.body, "lang-progress")[2]).toHaveAttribute(
"width",
"60.00",
"60",
);
});
@ -228,25 +228,28 @@ describe("Test renderTopLanguages", () => {
it("should render without rounding", () => {
document.body.innerHTML = renderTopLanguages(langs, { border_radius: "0" });
expect(document.querySelector("rect")).toHaveAttribute("rx", "0");
document.body.innerHTML = renderTopLanguages(langs, { });
document.body.innerHTML = renderTopLanguages(langs, {});
expect(document.querySelector("rect")).toHaveAttribute("rx", "4.5");
});
it("should render langs with specified langs_count", async () => {
options = {
langs_count: 1
}
langs_count: 1,
};
document.body.innerHTML = renderTopLanguages(langs, { ...options });
expect(queryAllByTestId(document.body, "lang-name").length).toBe(options.langs_count)
expect(queryAllByTestId(document.body, "lang-name").length).toBe(
options.langs_count,
);
});
it("should render langs with specified langs_count even when hide is set", async () => {
options = {
hide: ["HTML"],
langs_count: 2
}
langs_count: 2,
};
document.body.innerHTML = renderTopLanguages(langs, { ...options });
expect(queryAllByTestId(document.body, "lang-name").length).toBe(options.langs_count)
expect(queryAllByTestId(document.body, "lang-name").length).toBe(
options.langs_count,
);
});
});