From eab14c89320027d8256b2dddecdc18ee609df475 Mon Sep 17 00:00:00 2001 From: Martin <2026226+martin-mfg@users.noreply.github.com> Date: Sat, 25 Apr 2026 20:29:08 +0200 Subject: [PATCH] top-langs: add hide_values option (#181) based on https://github.com/anuraghazra/github-readme-stats/pull/4850 by @claw-explorer This PR adds a new hide_values query parameter for the Top Languages card that hides the percentage/byte display values while keeping progress bars and chart visualizations intact. top-langs5 top-langs top-langs2 top-langs3 top-langs4 --- apps/frontend/src/pages/Home/Home.tsx | 4 + .../src/pages/Home/getFullSuffix.test.ts | 1 + apps/frontend/src/pages/Home/getFullSuffix.ts | 6 ++ .../src/pages/Home/stages/Customize.tsx | 13 +++ docs/advanced_documentation.md | 1 + packages/core/src/api/top-langs.js | 2 + packages/core/src/cards/top-languages.js | 64 ++++++++++++-- packages/core/src/cards/types.d.ts | 1 + .../core/tests/renderTopLanguagesCard.test.js | 85 +++++++++++++++++++ 9 files changed, 168 insertions(+), 9 deletions(-) diff --git a/apps/frontend/src/pages/Home/Home.tsx b/apps/frontend/src/pages/Home/Home.tsx index 0372aac3..62a92bbb 100644 --- a/apps/frontend/src/pages/Home/Home.tsx +++ b/apps/frontend/src/pages/Home/Home.tsx @@ -76,6 +76,7 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element { >(); const [customTitle, setCustomTitle] = useState(""); const [langsCount, setLangsCount] = useState(); + const [hideValues, setHideValues] = useState(false); const [showAllStats, setShowAllStats] = useState(false); const [showIcons, setShowIcons] = useState(false); const [includeAllCommits, setIncludeAllCommits] = useState(true); @@ -121,6 +122,7 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element { descriptionLines, customTitle, langsCount, + hideValues, showAllStats, showIcons, includeAllCommits, @@ -306,6 +308,8 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element { setCustomTitle={setCustomTitle} langsCount={langsCount} setLangsCount={setLangsCount} + hideValues={hideValues} + setHideValues={setHideValues} showAllStats={showAllStats} setShowAllStats={setShowAllStats} showIcons={showIcons} diff --git a/apps/frontend/src/pages/Home/getFullSuffix.test.ts b/apps/frontend/src/pages/Home/getFullSuffix.test.ts index aa22224f..5351cb9f 100644 --- a/apps/frontend/src/pages/Home/getFullSuffix.test.ts +++ b/apps/frontend/src/pages/Home/getFullSuffix.test.ts @@ -22,6 +22,7 @@ const baseOptions = { descriptionLines: undefined, customTitle: "", langsCount: undefined, + hideValues: false, showAllStats: false, showIcons: false, includeAllCommits: false, diff --git a/apps/frontend/src/pages/Home/getFullSuffix.ts b/apps/frontend/src/pages/Home/getFullSuffix.ts index b333b429..e6dc776d 100644 --- a/apps/frontend/src/pages/Home/getFullSuffix.ts +++ b/apps/frontend/src/pages/Home/getFullSuffix.ts @@ -19,6 +19,7 @@ interface Options { descriptionLines: number | undefined; customTitle: string; langsCount: number | undefined; + hideValues: boolean; showAllStats: boolean; showIcons: boolean; includeAllCommits: boolean; @@ -41,6 +42,7 @@ export function getFullSuffix({ descriptionLines, customTitle, langsCount, + hideValues, showAllStats, showIcons, includeAllCommits, @@ -138,6 +140,10 @@ export function getFullSuffix({ fullSuffix += `&langs_count=${langsCount}`; } + if (hideValues && selectedCard === CardType.TOP_LANGS) { + fullSuffix += `&hide_values=true`; + } + if (showAllStats && selectedCard === CardType.STATS) { fullSuffix += `&show=reviews,discussions_started,discussions_answered,prs_merged,prs_merged_percentage,prs_commented,prs_reviewed,issues_commented`; } diff --git a/apps/frontend/src/pages/Home/stages/Customize.tsx b/apps/frontend/src/pages/Home/stages/Customize.tsx index 43b65f18..9443c9f0 100644 --- a/apps/frontend/src/pages/Home/stages/Customize.tsx +++ b/apps/frontend/src/pages/Home/stages/Customize.tsx @@ -47,6 +47,8 @@ interface CustomizeStageProps { setCustomTitle: Updater; langsCount: number | undefined; setLangsCount: Updater; + hideValues: boolean; + setHideValues: Updater; showIcons: boolean; setShowIcons: Updater; showAllStats: boolean; @@ -87,6 +89,8 @@ export function CustomizeStage({ setCustomTitle, langsCount, setLangsCount, + hideValues, + setHideValues, showIcons, setShowIcons, showAllStats, @@ -321,6 +325,15 @@ export function CustomizeStage({ max={20} /> )} + {cardType === CardType.TOP_LANGS && ( + + )} {cardType === CardType.WAKATIME && ( { * @param {number} props.size Size of the programming language. * @param {number} props.totalSize Total size of all languages. * @param {string} props.statsFormat Stats format. + * @param {boolean=} props.hideValues Whether to hide stats values. * @param {number} props.index Index of the programming language. * @returns {string} Programming language SVG node. */ @@ -232,10 +233,11 @@ const createProgressTextNode = ({ size, totalSize, statsFormat, + hideValues, index, }) => { const staggerDelay = (index + 3) * 150; - const paddingRight = 95; + const paddingRight = hideValues ? CARD_PADDING * 2 : 95; const progressTextX = width - paddingRight + 10; const progressWidth = width - paddingRight; @@ -245,7 +247,7 @@ const createProgressTextNode = ({ return ` ${name} - ${displayValue} + ${hideValues ? "" : `${displayValue}`} ${createProgressNode({ x: 0, y: 25, @@ -266,6 +268,7 @@ const createProgressTextNode = ({ * @param {Lang} props.lang Programming language object. * @param {number} props.totalSize Total size of all languages. * @param {boolean=} props.hideProgress Whether to hide percentage. + * @param {boolean=} props.hideValues Whether to hide stats values (percentages/bytes). * @param {string=} props.statsFormat Stats format * @param {number} props.index Index of the programming language. * @returns {string} Compact layout programming language SVG node. @@ -274,6 +277,7 @@ const createCompactLangNode = ({ lang, totalSize, hideProgress, + hideValues, statsFormat = "percentages", index, }) => { @@ -287,7 +291,7 @@ const createCompactLangNode = ({ - ${lang.name} ${hideProgress ? "" : displayValue} + ${lang.name} ${hideProgress || hideValues ? "" : displayValue} `; @@ -300,6 +304,7 @@ const createCompactLangNode = ({ * @param {Lang[]} props.langs Array of programming languages. * @param {number} props.totalSize Total size of all languages. * @param {boolean=} props.hideProgress Whether to hide percentage. + * @param {boolean=} props.hideValues Whether to hide stats values. * @param {string=} props.statsFormat Stats format * @returns {string} Programming languages SVG node. */ @@ -307,6 +312,7 @@ const createLanguageTextNode = ({ langs, totalSize, hideProgress, + hideValues, statsFormat, }) => { const longestLang = getLongestLang(langs); @@ -318,6 +324,7 @@ const createLanguageTextNode = ({ lang, totalSize, hideProgress, + hideValues, statsFormat, index, }), @@ -344,16 +351,23 @@ const createLanguageTextNode = ({ * @param {object} props Function properties. * @param {Lang[]} props.langs Array of programming languages. * @param {number} props.totalSize Total size of all languages. + * @param {boolean=} props.hideValues Whether to hide stats values. * @param {string} props.statsFormat Stats format * @returns {string} Donut layout programming language SVG node. */ -const createDonutLanguagesNode = ({ langs, totalSize, statsFormat }) => { +const createDonutLanguagesNode = ({ + langs, + totalSize, + hideValues, + statsFormat, +}) => { return flexLayout({ items: langs.map((lang, index) => { return createCompactLangNode({ lang, totalSize, hideProgress: false, + hideValues, statsFormat, index, }); @@ -371,6 +385,7 @@ const createDonutLanguagesNode = ({ langs, totalSize, statsFormat }) => { * @param {number} totalLanguageSize Total size of all languages. * @param progBarBgColor Color of the background of progress bar. * @param {string} statsFormat Stats format. + * @param {boolean=} hideValues Whether to hide stats values. * @returns {string} Normal layout card SVG object. */ const renderNormalLayout = ( @@ -379,6 +394,7 @@ const renderNormalLayout = ( totalLanguageSize, progBarBgColor, statsFormat, + hideValues, ) => { return flexLayout({ items: langs.map((lang, index) => { @@ -389,6 +405,7 @@ const renderNormalLayout = ( size: lang.size, totalSize: totalLanguageSize, statsFormat, + hideValues, index, progBarBgColor, }); @@ -406,6 +423,7 @@ const renderNormalLayout = ( * @param {number} totalLanguageSize Total size of all languages. * @param {boolean=} hideProgress Whether to hide progress bar. * @param {string} statsFormat Stats format. + * @param {boolean=} hideValues Whether to hide stats values. * @returns {string} Compact layout card SVG object. */ const renderCompactLayout = ( @@ -414,6 +432,7 @@ const renderCompactLayout = ( totalLanguageSize, hideProgress, statsFormat = "percentages", + hideValues, ) => { const paddingRight = 50; const offsetWidth = width - paddingRight; @@ -461,6 +480,7 @@ const renderCompactLayout = ( totalSize: totalLanguageSize, hideProgress, statsFormat, + hideValues, })} `; @@ -472,9 +492,15 @@ const renderCompactLayout = ( * @param {Lang[]} langs Array of programming languages. * @param {number} totalLanguageSize Total size of all languages. * @param {string} statsFormat Stats format. + * @param {boolean=} hideValues Whether to hide stats values. * @returns {string} Compact layout card SVG object. */ -const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat) => { +const renderDonutVerticalLayout = ( + langs, + totalLanguageSize, + statsFormat, + hideValues, +) => { // Donut vertical chart radius and total length const radius = 80; const totalCircleLength = getCircleLength(radius); @@ -531,6 +557,7 @@ const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat) => { totalSize: totalLanguageSize, hideProgress: false, statsFormat, + hideValues, })} @@ -544,9 +571,10 @@ const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat) => { * @param {Lang[]} langs Array of programming languages. * @param {number} totalLanguageSize Total size of all languages. * @param {string} statsFormat Stats format. + * @param {boolean=} hideValues Whether to hide stats values. * @returns {string} Compact layout card SVG object. */ -const renderPieLayout = (langs, totalLanguageSize, statsFormat) => { +const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => { // Pie chart radius and center coordinates const radius = 90; const centerX = 150; @@ -628,6 +656,7 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat) => { totalSize: totalLanguageSize, hideProgress: false, statsFormat, + hideValues, })} @@ -679,9 +708,16 @@ const createDonutPaths = (cx, cy, radius, percentages) => { * @param {number} width Card width. * @param {number} totalLanguageSize Total size of all languages. * @param {string} statsFormat Stats format. + * @param {boolean=} hideValues Whether to hide stats values. * @returns {string} Donut layout card SVG object. */ -const renderDonutLayout = (langs, width, totalLanguageSize, statsFormat) => { +const renderDonutLayout = ( + langs, + width, + totalLanguageSize, + statsFormat, + hideValues, +) => { const centerX = width / 3; const centerY = width / 3; const radius = centerX - 60; @@ -724,7 +760,7 @@ const renderDonutLayout = (langs, width, totalLanguageSize, statsFormat) => { return ` - ${createDonutLanguagesNode({ langs, totalSize: totalLanguageSize, statsFormat })} + ${createDonutLanguagesNode({ langs, totalSize: totalLanguageSize, hideValues, statsFormat })} @@ -800,6 +836,7 @@ const renderTopLanguages = (topLangs, options = {}) => { prog_bar_bg_color, hide, hide_progress, + hide_values, theme, layout, custom_title, @@ -850,13 +887,19 @@ const renderTopLanguages = (topLangs, options = {}) => { }); } else if (layout === "pie") { height = calculatePieLayoutHeight(langs.length); - finalLayout = renderPieLayout(langs, totalLanguageSize, stats_format); + finalLayout = renderPieLayout( + langs, + totalLanguageSize, + stats_format, + hide_values, + ); } else if (layout === "donut-vertical") { height = calculateDonutVerticalLayoutHeight(langs.length); finalLayout = renderDonutVerticalLayout( langs, totalLanguageSize, stats_format, + hide_values, ); } else if (layout === "compact" || hide_progress == true) { height = @@ -868,6 +911,7 @@ const renderTopLanguages = (topLangs, options = {}) => { totalLanguageSize, hide_progress, stats_format, + hide_values, ); } else if (layout === "donut") { height = calculateDonutLayoutHeight(langs.length); @@ -877,6 +921,7 @@ const renderTopLanguages = (topLangs, options = {}) => { width, totalLanguageSize, stats_format, + hide_values, ); } else { finalLayout = renderNormalLayout( @@ -885,6 +930,7 @@ const renderTopLanguages = (topLangs, options = {}) => { totalLanguageSize, fallbackColor(prog_bar_bg_color, "#ddd"), stats_format, + hide_values, ); } diff --git a/packages/core/src/cards/types.d.ts b/packages/core/src/cards/types.d.ts index 1d72262e..08b9f933 100644 --- a/packages/core/src/cards/types.d.ts +++ b/packages/core/src/cards/types.d.ts @@ -53,6 +53,7 @@ export type TopLangOptions = CommonOptions & { langs_count: number; disable_animations: boolean; hide_progress: boolean; + hide_values: boolean; prog_bar_bg_color: string; stats_format: "percentages" | "bytes"; }; diff --git a/packages/core/tests/renderTopLanguagesCard.test.js b/packages/core/tests/renderTopLanguagesCard.test.js index 3cd8c63e..d80e2860 100644 --- a/packages/core/tests/renderTopLanguagesCard.test.js +++ b/packages/core/tests/renderTopLanguagesCard.test.js @@ -909,4 +909,89 @@ describe("Test renderTopLanguages", () => { "css 100.0 B", ); }); + + it("should hide stats values when hide_values is true (compact layout)", () => { + document.body.innerHTML = renderTopLanguages(langs, { + layout: "compact", + hide_values: true, + }); + + expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent( + "HTML", + ); + + expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent( + "javascript", + ); + }); + + it("should hide stats values when hide_values is true (normal layout)", () => { + document.body.innerHTML = renderTopLanguages(langs, { + hide_values: true, + }); + + expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent( + "HTML", + ); + + expect(queryAllByTestId(document.body, "lang-name").length).toBe(3); + }); + + it("should hide stats values when hide_values is true (donut layout)", () => { + document.body.innerHTML = renderTopLanguages(langs, { + layout: "donut", + hide_values: true, + }); + + expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent( + "HTML", + ); + expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent( + "javascript", + ); + expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent( + "css", + ); + }); + + it("should hide stats values when hide_values is true (pie layout)", () => { + document.body.innerHTML = renderTopLanguages(langs, { + layout: "pie", + hide_values: true, + }); + + expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent( + "HTML", + ); + expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent( + "javascript", + ); + expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent( + "css", + ); + expect(queryAllByTestId(document.body, "lang-pie")[0]).toHaveAttribute( + "size", + "40", + ); + + document.body.innerHTML = renderTopLanguages( + { HTML: langs.HTML }, + { + layout: "pie", + hide_values: true, + }, + ); + + expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent( + "HTML", + ); + expect(queryAllByTestId(document.body, "lang-pie")).toHaveLength(1); + expect(queryAllByTestId(document.body, "lang-pie")[0]).toHaveAttribute( + "size", + "100", + ); + expect(queryAllByTestId(document.body, "lang-pie")[0].tagName).toBe( + "circle", + ); + }); });