forked from mirrored/github-readme-stats
feat: implement number precision parameter for stats card (#4514)
* Update short number format Update short number format by adding 2 decimal places instead of 1 for numbers between 1,000 and 9,999 * review * Update src/common/utils.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * dev * dev --------- Co-authored-by: Alexandr <qwerty541zxc@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot
Alexandr
parent
7caaa21e73
commit
4143c6dec3
@@ -43,6 +43,7 @@ export default async (req, res) => {
|
||||
disable_animations,
|
||||
border_radius,
|
||||
number_format,
|
||||
number_precision,
|
||||
border_color,
|
||||
rank_icon,
|
||||
show,
|
||||
@@ -124,6 +125,7 @@ export default async (req, res) => {
|
||||
border_radius,
|
||||
border_color,
|
||||
number_format,
|
||||
number_precision: parseInt(number_precision, 10),
|
||||
locale: locale ? locale.toLowerCase() : null,
|
||||
disable_animations: parseBoolean(disable_animations),
|
||||
rank_icon,
|
||||
|
||||
@@ -385,6 +385,7 @@ If we don't support your language, please consider contributing! You can find mo
|
||||
| `disable_animations` | Disables all animations in the card. | boolean | `false` |
|
||||
| `ring_color` | Color of the rank circle. | string (hex color) | `2f80ed` |
|
||||
| `number_format` | Switches between two available formats for displaying the card values `short` (i.e. `6.6k`) and `long` (i.e. `6626`). | enum | `short` |
|
||||
| `number_precision` | Enforce the number of digits after the decimal point for `short` number format. Must be an integer between 0 and 2. Will be ignored for `long` number format. | integer (0, 1 or 2) | `null` |
|
||||
| `show` | Shows [additional items](#showing-additional-individual-stats) on stats card (i.e. `reviews`, `discussions_started`, `discussions_answered`, `prs_merged` or `prs_merged_percentage`). | string (comma-separated values) | `null` |
|
||||
| `commits_year` | Filters and counts only commits made in the specified year. | integer _(YYYY)_ | `<current year> (one year to date)` |
|
||||
|
||||
|
||||
+19
-5
@@ -33,7 +33,8 @@ const RANK_ONLY_CARD_DEFAULT_WIDTH = 290;
|
||||
* @param {boolean} params.showIcons Whether to show icons.
|
||||
* @param {number} params.shiftValuePos Number of pixels the value has to be shifted to the right.
|
||||
* @param {boolean} params.bold Whether to bold the label.
|
||||
* @param {string} params.number_format The format of numbers on card.
|
||||
* @param {string} params.numberFormat The format of numbers on card.
|
||||
* @param {number=} params.numberPrecision The precision of numbers on card.
|
||||
* @returns {string} The stats card text item SVG object.
|
||||
*/
|
||||
const createTextNode = ({
|
||||
@@ -46,10 +47,17 @@ const createTextNode = ({
|
||||
showIcons,
|
||||
shiftValuePos,
|
||||
bold,
|
||||
number_format,
|
||||
numberFormat,
|
||||
numberPrecision,
|
||||
}) => {
|
||||
const precision =
|
||||
typeof numberPrecision === "number" && !isNaN(numberPrecision)
|
||||
? clampValue(numberPrecision, 0, 2)
|
||||
: undefined;
|
||||
const kValue =
|
||||
number_format.toLowerCase() === "long" ? value : kFormatter(value);
|
||||
numberFormat.toLowerCase() === "long" || id === "prs_merged_percentage"
|
||||
? value
|
||||
: kFormatter(value, precision);
|
||||
const staggerDelay = (index + 3) * 150;
|
||||
|
||||
const labelOffset = showIcons ? `x="25"` : "";
|
||||
@@ -251,6 +259,7 @@ const renderStatsCard = (stats, options = {}) => {
|
||||
border_radius,
|
||||
border_color,
|
||||
number_format = "short",
|
||||
number_precision,
|
||||
locale,
|
||||
disable_animations = false,
|
||||
rank_icon = "default",
|
||||
@@ -319,7 +328,11 @@ const renderStatsCard = (stats, options = {}) => {
|
||||
STATS.prs_merged_percentage = {
|
||||
icon: icons.prs_merged_percentage,
|
||||
label: i18n.t("statcard.prs-merged-percentage"),
|
||||
value: mergedPRsPercentage.toFixed(2),
|
||||
value: mergedPRsPercentage.toFixed(
|
||||
typeof number_precision === "number" && !isNaN(number_precision)
|
||||
? clampValue(number_precision, 0, 2)
|
||||
: 2,
|
||||
),
|
||||
id: "prs_merged_percentage",
|
||||
unitSymbol: "%",
|
||||
};
|
||||
@@ -408,7 +421,8 @@ const renderStatsCard = (stats, options = {}) => {
|
||||
showIcons: show_icons,
|
||||
shiftValuePos: 79.01 + (isLongLocale ? 50 : 0),
|
||||
bold: text_bold,
|
||||
number_format,
|
||||
numberFormat: number_format,
|
||||
numberPrecision: number_precision,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Vendored
+1
@@ -25,6 +25,7 @@ export type StatCardOptions = CommonOptions & {
|
||||
custom_title: string;
|
||||
disable_animations: boolean;
|
||||
number_format: string;
|
||||
number_precision: number;
|
||||
ring_color: string;
|
||||
text_bold: boolean;
|
||||
rank_icon: RankIcon;
|
||||
|
||||
+15
-5
@@ -77,15 +77,25 @@ const iconWithLabel = (icon, label, testid, iconSize) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves num with suffix k(thousands) precise to 1 decimal if greater than 999.
|
||||
* Retrieves num with suffix k(thousands) precise to given decimal places.
|
||||
*
|
||||
* @param {number} num The number to format.
|
||||
* @param {number=} precision The number of decimal places to include.
|
||||
* @returns {string|number} The formatted number.
|
||||
*/
|
||||
const kFormatter = (num) => {
|
||||
return Math.abs(num) > 999
|
||||
? Math.sign(num) * parseFloat((Math.abs(num) / 1000).toFixed(1)) + "k"
|
||||
: Math.sign(num) * Math.abs(num);
|
||||
const kFormatter = (num, precision) => {
|
||||
const abs = Math.abs(num);
|
||||
const sign = Math.sign(num);
|
||||
|
||||
if (typeof precision === "number" && !isNaN(precision)) {
|
||||
return (sign * (abs / 1000)).toFixed(precision) + "k";
|
||||
}
|
||||
|
||||
if (abs < 1000) {
|
||||
return sign * abs;
|
||||
}
|
||||
|
||||
return sign * parseFloat((abs / 1000).toFixed(1)) + "k";
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -416,6 +416,13 @@ describe("Test renderStatsCard", () => {
|
||||
expect(getByTestId(document.body, "commits").textContent).toBe("2k");
|
||||
document.body.innerHTML = renderStatsCard(stats, { number_format: "long" });
|
||||
expect(getByTestId(document.body, "commits").textContent).toBe("1999");
|
||||
document.body.innerHTML = renderStatsCard(stats, { number_precision: 2 });
|
||||
expect(getByTestId(document.body, "commits").textContent).toBe("2.00k");
|
||||
document.body.innerHTML = renderStatsCard(stats, {
|
||||
number_format: "long",
|
||||
number_precision: 2,
|
||||
});
|
||||
expect(getByTestId(document.body, "commits").textContent).toBe("1999");
|
||||
});
|
||||
|
||||
it("should render default rank icon with level A+", () => {
|
||||
|
||||
+40
-1
@@ -12,16 +12,55 @@ import {
|
||||
import { getCardColors } from "../src/common/color.js";
|
||||
|
||||
describe("Test utils.js", () => {
|
||||
it("should test kFormatter", () => {
|
||||
it("should test kFormatter default behavior", () => {
|
||||
expect(kFormatter(1)).toBe(1);
|
||||
expect(kFormatter(-1)).toBe(-1);
|
||||
expect(kFormatter(500)).toBe(500);
|
||||
expect(kFormatter(1000)).toBe("1k");
|
||||
expect(kFormatter(1200)).toBe("1.2k");
|
||||
expect(kFormatter(10000)).toBe("10k");
|
||||
expect(kFormatter(12345)).toBe("12.3k");
|
||||
expect(kFormatter(99900)).toBe("99.9k");
|
||||
expect(kFormatter(9900000)).toBe("9900k");
|
||||
});
|
||||
|
||||
it("should test kFormatter with 0 decimal precision", () => {
|
||||
expect(kFormatter(1, 0)).toBe("0k");
|
||||
expect(kFormatter(-1, 0)).toBe("-0k");
|
||||
expect(kFormatter(500, 0)).toBe("1k");
|
||||
expect(kFormatter(1000, 0)).toBe("1k");
|
||||
expect(kFormatter(1200, 0)).toBe("1k");
|
||||
expect(kFormatter(10000, 0)).toBe("10k");
|
||||
expect(kFormatter(12345, 0)).toBe("12k");
|
||||
expect(kFormatter(99000, 0)).toBe("99k");
|
||||
expect(kFormatter(99900, 0)).toBe("100k");
|
||||
expect(kFormatter(9900000, 0)).toBe("9900k");
|
||||
});
|
||||
|
||||
it("should test kFormatter with 1 decimal precision", () => {
|
||||
expect(kFormatter(1, 1)).toBe("0.0k");
|
||||
expect(kFormatter(-1, 1)).toBe("-0.0k");
|
||||
expect(kFormatter(500, 1)).toBe("0.5k");
|
||||
expect(kFormatter(1000, 1)).toBe("1.0k");
|
||||
expect(kFormatter(1200, 1)).toBe("1.2k");
|
||||
expect(kFormatter(10000, 1)).toBe("10.0k");
|
||||
expect(kFormatter(12345, 1)).toBe("12.3k");
|
||||
expect(kFormatter(99900, 1)).toBe("99.9k");
|
||||
expect(kFormatter(9900000, 1)).toBe("9900.0k");
|
||||
});
|
||||
|
||||
it("should test kFormatter with 2 decimal precision", () => {
|
||||
expect(kFormatter(1, 2)).toBe("0.00k");
|
||||
expect(kFormatter(-1, 2)).toBe("-0.00k");
|
||||
expect(kFormatter(500, 2)).toBe("0.50k");
|
||||
expect(kFormatter(1000, 2)).toBe("1.00k");
|
||||
expect(kFormatter(1200, 2)).toBe("1.20k");
|
||||
expect(kFormatter(10000, 2)).toBe("10.00k");
|
||||
expect(kFormatter(12345, 2)).toBe("12.35k");
|
||||
expect(kFormatter(99900, 2)).toBe("99.90k");
|
||||
expect(kFormatter(9900000, 2)).toBe("9900.00k");
|
||||
});
|
||||
|
||||
it("should test parseBoolean", () => {
|
||||
expect(parseBoolean(true)).toBe(true);
|
||||
expect(parseBoolean(false)).toBe(false);
|
||||
|
||||
Reference in New Issue
Block a user