Repository: anuraghazra/github-readme-stats
Pull request #2858 (2023-06-19 09:10:03)

feat: add 'progress' rank icon
This pull request allows users to replace the rank level with the rank progress by setting the `rank_icon` query to `progress`.

#### Preview

```md
![Anurag's GitHub stats](https://github-readme-stats-git-addprogressrankicon-rickstaa.vercel.app/api?username=anuraghazra\&rank_icon=progress)
```

![Anurag's GitHub stats](https://github-readme-stats-git-addprogressrankicon-rickstaa.vercel.app/api?username=anuraghazra\&rank_icon=progress)


vercel[bot]: [vc]: #kgiSDOAFX82dR8Lcb0YHXuADz+wKkPGTN48vN2gXwl8=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJnaXRodWItcmVhZG1lLXN0YXRzIiwicm9vdERpcmVjdG9yeSI6bnVsbCwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL2dpdGh1Yi1yZWFkbWUtc3RhdHMtdGVhbS9naXRodWItcmVhZG1lLXN0YXRzLzJwOHJEMzc5blZuNDNoU3FkamFkRkVzY3dHSEwiLCJuZXh0Q29tbWl0U3RhdHVzIjoiUEVORElORyJ9XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Updated (UTC) |
| :--- | :----- | :------ | :------ |
| **github-readme-stats** | 🔄 Building ([Inspect](https://vercel.com/github-readme-stats-team/github-readme-stats/2p8rD379nVn43hSqdjadFEscwGHL)) |  | Jun 19, 2023 9:10am |


rickstaa: @qwerty541, what do you think about the text size and would you put a space between the number and the `%` sign 🤔?

Patch:
diff --git a/package-lock.json b/package-lock.json
index e6689ba..a11ae14 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -33,6 +33,9 @@
         "lodash.snakecase": "^4.1.1",
         "parse-diff": "^0.7.0",
         "prettier": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=18.0.0"
       }
     },
     "node_modules/@actions/core": {
diff --git a/readme.md b/readme.md
index ca8a806..4a1eef3 100644
--- a/readme.md
+++ b/readme.md
@@ -304,7 +304,7 @@ You can provide multiple comma-separated values in the bg\_color option to rende
 *   `hide_title` - *(boolean)*. Default: `false`.
 *   `card_width` - Set the card's width manually *(number)*. Default: `500px  (approx.)`.
 *   `hide_rank` - *(boolean)* hides the rank and automatically resizes the card width. Default: `false`.
-*   `rank_icon` - Shows alternative rank icon (i.e. `github` or `default`). Default: `default`.
+*   `rank_icon` - Shows alternative rank icon (i.e. `github`, `progress` or `default`). Default: `default`.
 *   `show_icons` - *(boolean)*. Default: `false`.
 *   `include_all_commits` - Count total commits instead of just the current year commits *(boolean)*. Default: `false`.
 *   `line_height` - Sets the line height between text *(number)*. Default: `25`.
@@ -553,6 +553,10 @@ Change the `?username=` value to your [Wakatime](https://wakatime.com) username.
 
 ![Anurag's GitHub stats](https://github-readme-stats.vercel.app/api?username=anuraghazra\&rank_icon=github)
 
+*   Shows rank progress instead of rank level
+
+![Anurag's GitHub stats](https://github-readme-stats.vercel.app/api?username=anuraghazra\&rank_icon=progress)
+
 *   Customize Border Color
 
 ![Anurag's GitHub stats](https://github-readme-stats.vercel.app/api?username=anuraghazra\&border_color=2e4058)
diff --git a/src/cards/stats-card.js b/src/cards/stats-card.js
index 79b0076..c6694f2 100644
--- a/src/cards/stats-card.js
+++ b/src/cards/stats-card.js
@@ -328,7 +328,7 @@ const renderStatsCard = (stats = {}, options = {}) => {
         <circle class="rank-circle-rim" cx="-10" cy="8" r="40" />
         <circle class="rank-circle" cx="-10" cy="8" r="40" />
         <g class="rank-text">
-          ${rankIcon(rank_icon, rank?.level)}
+          ${rankIcon(rank_icon, rank?.level, progress)}
         </g>
       </g>`;
 
diff --git a/src/cards/top-languages-card.js b/src/cards/top-languages-card.js
index 4e8ed3c..f20f0e5 100644
--- a/src/cards/top-languages-card.js
+++ b/src/cards/top-languages-card.js
@@ -164,14 +164,14 @@ const donutCenterTranslation = (totalLangs) => {
  * Trim top languages to lang_count while also hiding certain languages.
  *
  * @param {Record<string, Lang>} topLangs Top languages.
- * @param {string} langs_count Number of languages to show.
+ * @param {number} langs_count Number of languages to show.
  * @param {string[]=} hide Languages to hide.
  * @returns {{ langs: Lang[], totalLanguageSize: number }} Trimmed top languages and total size.
  */
 const trimTopLanguages = (topLangs, langs_count, hide) => {
   let langs = Object.values(topLangs);
   let langsToHide = {};
-  let langsCount = clampValue(parseInt(langs_count), 1, MAXIMUM_LANGS_COUNT);
+  let langsCount = clampValue(langs_count, 1, MAXIMUM_LANGS_COUNT);
 
   // populate langsToHide map for quick lookup
   // while filtering out
@@ -734,7 +734,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
 
   const { langs, totalLanguageSize } = trimTopLanguages(
     topLangs,
-    String(langs_count),
+    langs_count,
     hide,
   );
 
diff --git a/src/cards/types.d.ts b/src/cards/types.d.ts
index abd7d20..53091b9 100644
--- a/src/cards/types.d.ts
+++ b/src/cards/types.d.ts
@@ -1,5 +1,5 @@
 type ThemeNames = keyof typeof import("../../themes/index.js");
-type RankIcon = "default" | "github";
+type RankIcon = "default" | "github" | "progress";
 
 export type CommonOptions = {
   title_color: string;
diff --git a/src/cards/wakatime-card.js b/src/cards/wakatime-card.js
index 396ede1..f1df53b 100644
--- a/src/cards/wakatime-card.js
+++ b/src/cards/wakatime-card.js
@@ -204,7 +204,7 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
 
   const lheight = parseInt(String(line_height), 10);
 
-  const langsCount = clampValue(parseInt(String(langs_count)), 1, langs_count);
+  const langsCount = clampValue(langs_count, 1, langs_count);
 
   // returns theme based colors with proper overrides and defaults
   const { titleColor, textColor, iconColor, bgColor, borderColor } =
diff --git a/src/common/icons.js b/src/common/icons.js
index 4368375..fd743ff 100644
--- a/src/common/icons.js
+++ b/src/common/icons.js
@@ -14,9 +14,12 @@ const icons = {
 /**
  * Get rank icon
  *
+ * @param {string} rankIcon - The rank icon type.
+ * @param {number} rankLevel - The rank level.
+ * @param {number} progress - The rank progress.
  * @returns {string} - The SVG code of the rank icon
  */
-const rankIcon = (rankIcon, rankLevel) => {
+const rankIcon = (rankIcon, rankLevel, progress) => {
   switch (rankIcon) {
     case "github":
       return `
@@ -24,6 +27,12 @@ const rankIcon = (rankIcon, rankLevel) => {
           <path d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"></path>
         </svg>
       `;
+    case "progress":
+      return `
+      <text x="-5" y="3" alignment-baseline="central" dominant-baseline="central" text-anchor="middle" data-testid="progress-rank-icon" class="rank-progress-text">
+          ${progress.toFixed(1)}%
+      </text>
+      `;
     case "default":
     default:
       return `
diff --git a/src/getStyles.js b/src/getStyles.js
index 7e61139..088f723 100644
--- a/src/getStyles.js
+++ b/src/getStyles.js
@@ -98,6 +98,9 @@ const getStyles = ({
       font: 800 24px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor};
       animation: scaleInAnimation 0.3s ease-in-out forwards;
     }
+    .rank-progress-text {
+      font-size: 16px;
+    }
     
     .not_bold { font-weight: 400 }
     .bold { font-weight: 700 }


Test patch:
diff --git a/tests/__snapshots__/renderWakatimeCard.test.js.snap b/tests/__snapshots__/renderWakatimeCard.test.js.snap
index 055c0f6..1ad81d2 100644
--- a/tests/__snapshots__/renderWakatimeCard.test.js.snap
+++ b/tests/__snapshots__/renderWakatimeCard.test.js.snap
@@ -42,6 +42,9 @@ exports[`Test Render Wakatime Card should render correctly with compact layout 1
       font: 800 24px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58;
       animation: scaleInAnimation 0.3s ease-in-out forwards;
     }
+    .rank-progress-text {
+      font-size: 16px;
+    }
     
     .not_bold { font-weight: 400 }
     .bold { font-weight: 700 }
@@ -222,6 +225,9 @@ exports[`Test Render Wakatime Card should render correctly with compact layout w
       font: 800 24px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58;
       animation: scaleInAnimation 0.3s ease-in-out forwards;
     }
+    .rank-progress-text {
+      font-size: 16px;
+    }
     
     .not_bold { font-weight: 400 }
     .bold { font-weight: 700 }
diff --git a/tests/renderStatsCard.test.js b/tests/renderStatsCard.test.js
index a2fd5b6..05d7452 100644
--- a/tests/renderStatsCard.test.js
+++ b/tests/renderStatsCard.test.js
@@ -20,7 +20,7 @@ const stats = {
   totalDiscussionsStarted: 10,
   totalDiscussionsAnswered: 50,
   contributedTo: 500,
-  rank: { level: "A+", score: 40 },
+  rank: { level: "A+", percentile: 40 },
 };
 
 describe("Test renderStatsCard", () => {
@@ -417,4 +417,14 @@ describe("Test renderStatsCard", () => {
     });
     expect(queryByTestId(document.body, "github-rank-icon")).toBeDefined();
   });
+
+  it("should show the progress", () => {
+    document.body.innerHTML = renderStatsCard(stats, {
+      rank_icon: "progress",
+    });
+    expect(queryByTestId(document.body, "rank-progress-text")).toBeDefined();
+    expect(
+      queryByTestId(document.body, "progress-rank-icon").textContent.trim(),
+    ).toBe((100 - stats.rank.percentile).toFixed(1) + "%");
+  });
 });