From f53c9397b493baf10afd9b48e8d8729b3b7e8773 Mon Sep 17 00:00:00 2001 From: Anurag Date: Sat, 18 Sep 2021 22:22:02 +0530 Subject: [PATCH 1/2] feat(layout): improve flexLayout & fixed layout overlaps --- src/cards/repo-card.js | 55 ++++++++++++++-------------- src/cards/top-languages-card.js | 63 +++++++++++++++++++++------------ src/common/utils.js | 36 ++++++++++++++++--- tests/flexLayout.test.js | 46 ++++++++++++++++++++++++ tests/renderRepoCard.test.js | 38 +++----------------- tests/utils.test.js | 21 ----------- 6 files changed, 148 insertions(+), 111 deletions(-) create mode 100644 tests/flexLayout.test.js diff --git a/src/cards/repo-card.js b/src/cards/repo-card.js index 9a21fcbdeb358..b1112d64c8f34 100644 --- a/src/cards/repo-card.js +++ b/src/cards/repo-card.js @@ -5,6 +5,7 @@ const { getCardColors, flexLayout, wrapTextMultiline, + measureText, } = require("../common/utils"); const I18n = require("../common/I18n"); const Card = require("../common/Card"); @@ -61,20 +62,15 @@ const renderRepoCard = (repo, options = {}) => { }); // returns theme based colors with proper overrides and defaults - const { - titleColor, - textColor, - iconColor, - bgColor, - borderColor, - } = getCardColors({ - title_color, - icon_color, - text_color, - bg_color, - border_color, - theme, - }); + const { titleColor, textColor, iconColor, bgColor, borderColor } = + getCardColors({ + title_color, + icon_color, + text_color, + bg_color, + border_color, + theme, + }); const totalStars = kFormatter(stargazers.totalCount); const totalForks = kFormatter(forkCount); @@ -96,21 +92,24 @@ const renderRepoCard = (repo, options = {}) => { const svgLanguage = primaryLanguage ? ` - + ${langName} ` : ""; + const iconSize = 16; const iconWithLabel = (icon, label, testid) => { - return ` - + const iconSvg = ` + ${icon} - ${label} `; + const text = `${label}`; + return flexLayout({ items: [iconSvg, text], gap: 20 }).join(""); }; + const svgStars = stargazers.totalCount > 0 && iconWithLabel(icons.star, totalStars, "stargazers"); @@ -118,8 +117,13 @@ const renderRepoCard = (repo, options = {}) => { forkCount > 0 && iconWithLabel(icons.fork, totalForks, "forkcount"); const starAndForkCount = flexLayout({ - items: [svgStars, svgForks], - gap: 65, + items: [svgLanguage, svgStars, svgForks], + sizes: [ + measureText(langName, 12), + iconSize + measureText(`${totalStars}`, 12), + iconSize + measureText(`${totalForks}`, 12), + ], + gap: 25, }).join(""); const card = new Card({ @@ -163,15 +167,8 @@ const renderRepoCard = (repo, options = {}) => { .join("")} - - ${svgLanguage} - - - ${starAndForkCount} - + + ${starAndForkCount} `); }; diff --git a/src/cards/top-languages-card.js b/src/cards/top-languages-card.js index 701c8c0d46bb9..c8109f36d6955 100644 --- a/src/cards/top-languages-card.js +++ b/src/cards/top-languages-card.js @@ -7,6 +7,8 @@ const { getCardColors, flexLayout, lowercaseTrim, + measureText, + chunkArray, } = require("../common/utils"); const DEFAULT_CARD_WIDTH = 300; @@ -33,12 +35,12 @@ const createProgressTextNode = ({ width, color, name, progress }) => { `; }; -const createCompactLangNode = ({ lang, totalSize, x, y }) => { +const createCompactLangNode = ({ lang, totalSize }) => { const percentage = ((lang.size / totalSize) * 100).toFixed(2); const color = lang.color || "#858585"; return ` - + ${lang.name} ${percentage}% @@ -47,25 +49,38 @@ const createCompactLangNode = ({ lang, totalSize, x, y }) => { `; }; -const createLanguageTextNode = ({ langs, totalSize, x, y }) => { - return langs.map((lang, index) => { - if (index % 2 === 0) { - return createCompactLangNode({ +const getLongestLang = (arr) => + arr.reduce( + (savedLang, lang) => + lang.name.length > savedLang.name.length ? lang : savedLang, + { name: "" }, + ); + +const createLanguageTextNode = ({ langs, totalSize }) => { + const longestLang = getLongestLang(langs); + const chunked = chunkArray(langs, langs.length / 2); + const layouts = chunked.map((array) => { + const items = array.map((lang, index) => + createCompactLangNode({ lang, - x, - y: 12.5 * index + y, totalSize, index, - }); - } - return createCompactLangNode({ - lang, - x: 150, - y: 12.5 + 12.5 * index, - totalSize, - index, - }); + }), + ); + return flexLayout({ + items, + gap: 25, + direction: "column", + }).join(""); }); + + const percent = ((longestLang.size / totalSize) * 100).toFixed(2); + const minGap = 150; + const maxGap = 20 + measureText(`${longestLang.name} ${percent}%`, 11); + return flexLayout({ + items: layouts, + gap: maxGap < minGap ? minGap : maxGap, + }).join(""); }; /** @@ -132,12 +147,14 @@ const renderCompactLayout = (langs, width, totalLanguageSize) => { ${compactProgressBar} - ${createLanguageTextNode({ - x: 0, - y: 25, - langs, - totalSize: totalLanguageSize, - }).join("")} + + + ${createLanguageTextNode({ + langs, + totalSize: totalLanguageSize, + width, + })} + `; }; diff --git a/src/common/utils.js b/src/common/utils.js index e9beb54b36c82..b8a33404e0936 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -89,21 +89,26 @@ function request(data, headers) { /** * - * @param {String[]} items + * @param {string[]} items * @param {Number} gap - * @param {string} direction + * @param {"column" | "row"} direction + * + * @returns {string[]} * * @description * Auto layout utility, allows us to layout things * vertically or horizontally with proper gaping */ -function flexLayout({ items, gap, direction }) { +function flexLayout({ items, gap, direction, sizes = [] }) { + let lastSize = 0; // filter() for filtering out empty strings return items.filter(Boolean).map((item, i) => { - let transform = `translate(${gap * i}, 0)`; + const size = sizes?.[i] || 0; + let transform = `translate(${lastSize}, 0)`; if (direction === "column") { - transform = `translate(0, ${gap * i})`; + transform = `translate(0, ${lastSize})`; } + lastSize += size + gap; return `${item}`; }); } @@ -232,6 +237,26 @@ function measureText(str, fontSize = 10) { } const lowercaseTrim = (name) => name.toLowerCase().trim(); +/** + * @template T + * @param {Array} arr + * @param {number} perChunk + * @returns {Array} + */ +function chunkArray(arr, perChunk) { + return arr.reduce((resultArray, item, index) => { + const chunkIndex = Math.floor(index / perChunk); + + if (!resultArray[chunkIndex]) { + resultArray[chunkIndex] = []; // start a new chunk + } + + resultArray[chunkIndex].push(item); + + return resultArray; + }, []); +} + module.exports = { renderError, kFormatter, @@ -250,4 +275,5 @@ module.exports = { CONSTANTS, CustomError, lowercaseTrim, + chunkArray, }; diff --git a/tests/flexLayout.test.js b/tests/flexLayout.test.js new file mode 100644 index 0000000000000..5f2defd6ea805 --- /dev/null +++ b/tests/flexLayout.test.js @@ -0,0 +1,46 @@ +const { flexLayout } = require("../src/common/utils"); + +describe("flexLayout", () => { + it("should work with row & col layouts", () => { + const layout = flexLayout({ + items: ["1", "2"], + gap: 60, + }); + + expect(layout).toStrictEqual([ + `1`, + `2`, + ]); + + const columns = flexLayout({ + items: ["1", "2"], + gap: 60, + direction: "column", + }); + + expect(columns).toStrictEqual([ + `1`, + `2`, + ]); + }); + + it("should work with sizes", () => { + const layout = flexLayout({ + items: [ + "1", + "2", + "3", + "4", + ], + gap: 20, + sizes: [200, 100, 55, 25], + }); + + expect(layout).toStrictEqual([ + `1`, + `2`, + `3`, + `4`, + ]); + }); +}); diff --git a/tests/renderRepoCard.test.js b/tests/renderRepoCard.test.js index e375d35383ca5..a6d249821dee1 100644 --- a/tests/renderRepoCard.test.js +++ b/tests/renderRepoCard.test.js @@ -89,36 +89,6 @@ describe("Test renderRepoCard", () => { ); }); - it("should shift the text position depending on language length", () => { - document.body.innerHTML = renderRepoCard({ - ...data_repo.repository, - primaryLanguage: { - ...data_repo.repository.primaryLanguage, - name: "Jupyter Notebook", - }, - }); - - expect(queryByTestId(document.body, "primary-lang")).toBeInTheDocument(); - expect(queryByTestId(document.body, "star-fork-group")).toHaveAttribute( - "transform", - "translate(155, 0)", - ); - - // Small lang - document.body.innerHTML = renderRepoCard({ - ...data_repo.repository, - primaryLanguage: { - ...data_repo.repository.primaryLanguage, - name: "Ruby", - }, - }); - - expect(queryByTestId(document.body, "star-fork-group")).toHaveAttribute( - "transform", - "translate(125, 0)", - ); - }); - it("should hide language if primaryLanguage is null & fallback to correct values", () => { document.body.innerHTML = renderRepoCard({ ...data_repo.repository, @@ -332,11 +302,13 @@ describe("Test renderRepoCard", () => { ); expect(queryByTestId(document.body, "badge")).toHaveTextContent("模板"); }); - + it("should render without rounding", () => { - document.body.innerHTML = renderRepoCard(data_repo.repository, { border_radius: "0" }); + document.body.innerHTML = renderRepoCard(data_repo.repository, { + border_radius: "0", + }); expect(document.querySelector("rect")).toHaveAttribute("rx", "0"); - document.body.innerHTML = renderRepoCard(data_repo.repository, { }); + document.body.innerHTML = renderRepoCard(data_repo.repository, {}); expect(document.querySelector("rect")).toHaveAttribute("rx", "4.5"); }); }); diff --git a/tests/utils.test.js b/tests/utils.test.js index 66f55d5d25129..15c4d97481590 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -44,27 +44,6 @@ describe("Test utils.js", () => { ).toHaveTextContent(/Secondary Message/gim); }); - it("should test flexLayout", () => { - const layout = flexLayout({ - items: ["1", "2"], - gap: 60, - }).join(""); - - expect(layout).toBe( - `12`, - ); - - const columns = flexLayout({ - items: ["1", "2"], - gap: 60, - direction: "column", - }).join(""); - - expect(columns).toBe( - `12`, - ); - }); - it("getCardColors: should return expected values", () => { let colors = getCardColors({ title_color: "f00", From 5ee7e98f6400349e4dbdb2e2bbe9993f6e4e6596 Mon Sep 17 00:00:00 2001 From: Anurag Date: Sat, 18 Sep 2021 22:27:13 +0530 Subject: [PATCH 2/2] chore: fix vercel build --- src/common/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/utils.js b/src/common/utils.js index b8a33404e0936..f14e8cc65f4f0 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -103,7 +103,7 @@ function flexLayout({ items, gap, direction, sizes = [] }) { let lastSize = 0; // filter() for filtering out empty strings return items.filter(Boolean).map((item, i) => { - const size = sizes?.[i] || 0; + const size = sizes[i] || 0; let transform = `translate(${lastSize}, 0)`; if (direction === "column") { transform = `translate(0, ${lastSize})`;