Skip to content

Commit

Permalink
Fix #1170: Allow customizable width for GitHub Stats Card (#1334)
Browse files Browse the repository at this point in the history
* Change default stats card width with hide rank

* Add tests for stats card with card_width

* Add card_width Stats Card description to readme

* fix: add icon width to stats-card min width calculation

* fix: fixes rank circle padding problem

This commit fixes a padding problem that was introduced in
f9c0e0b. In the new code, the padding
around the rank circle will be 50 when the stats card is bigger than
450. When it is smaller than 450 the left and right padding will shrink
equally.

* style: run prettier

* tests: add extra stats 'card_width' tests

This commit makes sure we also test the stats card width for the case that the 'show_icons'
option is enabled.

* style: run prettier

Co-authored-by: rickstaa <[email protected]>
  • Loading branch information
postatum and rickstaa authored Sep 16, 2022
1 parent 192170c commit b0bb994
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 32 deletions.
2 changes: 2 additions & 0 deletions api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module.exports = async (req, res) => {
hide,
hide_title,
hide_border,
card_width,
hide_rank,
show_icons,
count_private,
Expand Down Expand Up @@ -65,6 +66,7 @@ module.exports = async (req, res) => {
show_icons: parseBoolean(show_icons),
hide_title: parseBoolean(hide_title),
hide_border: parseBoolean(hide_border),
card_width: parseInt(card_width, 10),
hide_rank: parseBoolean(hide_rank),
include_all_commits: parseBoolean(include_all_commits),
line_height,
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ You can provide multiple comma-separated values in the bg_color option to render

- `hide` - Hides the [specified items](#hiding-individual-stats) from stats _(Comma-separated values)_
- `hide_title` - _(boolean)_
- `card_width` - Set the card's width manually _(number)_
- `hide_rank` - _(boolean)_ hides the rank and automatically resizes the card width
- `show_icons` - _(boolean)_
- `include_all_commits` - Count total commits instead of just the current year commits _(boolean)_
Expand Down
91 changes: 59 additions & 32 deletions src/cards/stats-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ const createTextNode = ({
<g class="stagger" style="animation-delay: ${staggerDelay}ms" transform="translate(25, 0)">
${iconSvg}
<text class="stat bold" ${labelOffset} y="12.5">${label}:</text>
<text
class="stat"
x="${(showIcons ? 140 : 120) + shiftValuePos}"
y="12.5"
<text
class="stat"
x="${(showIcons ? 140 : 120) + shiftValuePos}"
y="12.5"
data-testid="${id}"
>${kValue}</text>
</g>
Expand All @@ -66,6 +66,7 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
show_icons = false,
hide_title = false,
hide_border = false,
card_width,
hide_rank = false,
include_all_commits = false,
line_height = 25,
Expand Down Expand Up @@ -174,26 +175,6 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
hide_rank ? 0 : 150,
);

// Conditionally rendered elements
const rankCircle = hide_rank
? ""
: `<g data-testid="rank-circle"
transform="translate(400, ${height / 2 - 50})">
<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">
<text
x="-5"
y="3"
alignment-baseline="central"
dominant-baseline="central"
text-anchor="middle"
>
${rank.level}
</text>
</g>
</g>`;

// the better user's score the the rank will be closer to zero so
// subtracting 100 to get the progress in 100%
const progress = 100 - rank.score;
Expand All @@ -209,13 +190,20 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
return measureText(custom_title ? custom_title : i18n.t("statcard.title"));
};

const width = hide_rank
? clampValue(
50 /* padding */ + calculateTextWidth() * 2,
270 /* min */,
Infinity,
)
: 495;
/*
When hide_rank=true, the minimum card width is 270 px + the title length and padding.
When hide_rank=false, the minimum card_width is 340 px + the icon width (if show_icons=true).
Numbers are picked by looking at existing dimensions on production.
*/
const iconWidth = show_icons ? 16 : 0;
const minCardWidth = hide_rank
? clampValue(50 /* padding */ + calculateTextWidth() * 2, 270, Infinity)
: 340 + iconWidth;
const defaultCardWidth = hide_rank ? 270 : 495;
let width = isNaN(card_width) ? defaultCardWidth : card_width;
if (width < minCardWidth) {
width = minCardWidth;
}

const card = new Card({
customTitle: custom_title,
Expand All @@ -238,6 +226,45 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {

if (disable_animations) card.disableAnimations();

/**
* Calculates the right rank circle translation values such that the rank circle
* keeps respecting the padding.
*
* width > 450: The default left padding of 50 px will be used.
* width < 450: The left and right padding will shrink equally.
*
* @returns {number} - Rank circle translation value.
*/
const calculateRankXTranslation = () => {
if (width < 450) {
return width - 95 + (45 * (450 - 340)) / 110;
} else {
return width - 95;
}
};

// Conditionally rendered elements
const rankCircle = hide_rank
? ""
: `<g data-testid="rank-circle"
transform="translate(${calculateRankXTranslation()}, ${
height / 2 - 50
})">
<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">
<text
x="-5"
y="3"
alignment-baseline="central"
dominant-baseline="central"
text-anchor="middle"
>
${rank.level}
</text>
</g>
</g>`;

// Accessibility Labels
const labels = Object.keys(STATS)
.filter((key) => !hide.includes(key))
Expand All @@ -264,7 +291,7 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
gap: lheight,
direction: "column",
}).join("")}
</svg>
</svg>
`);
};

Expand Down
46 changes: 46 additions & 0 deletions tests/renderStatsCard.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,52 @@ describe("Test renderStatsCard", () => {
expect(queryByTestId(document.body, "rank-circle")).not.toBeInTheDocument();
});

it("should render with custom width set", () => {
document.body.innerHTML = renderStatsCard(stats);
expect(document.querySelector("svg")).toHaveAttribute("width", "495");

document.body.innerHTML = renderStatsCard(stats, { card_width: 400 });
expect(document.querySelector("svg")).toHaveAttribute("width", "400");
});

it("should render with custom width set and limit minimum width", () => {
document.body.innerHTML = renderStatsCard(stats, { card_width: 1 });
expect(document.querySelector("svg")).toHaveAttribute("width", "340");

document.body.innerHTML = renderStatsCard(stats, {
card_width: 1,
hide_rank: true,
});
expect(document.querySelector("svg")).toHaveAttribute(
"width",
"305.81250000000006",
);

document.body.innerHTML = renderStatsCard(stats, {
card_width: 1,
hide_rank: true,
show_icons: true,
});
expect(document.querySelector("svg")).toHaveAttribute(
"width",
"305.81250000000006",
);

document.body.innerHTML = renderStatsCard(stats, {
card_width: 1,
hide_rank: false,
show_icons: true,
});
expect(document.querySelector("svg")).toHaveAttribute("width", "356");

document.body.innerHTML = renderStatsCard(stats, {
card_width: 1,
hide_rank: false,
show_icons: false,
});
expect(document.querySelector("svg")).toHaveAttribute("width", "340");
});

it("should render default colors properly", () => {
document.body.innerHTML = renderStatsCard(stats);

Expand Down

1 comment on commit b0bb994

@vercel
Copy link

@vercel vercel bot commented on b0bb994 Sep 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.