From cb99414d3dc6027139bed09006deee0615847239 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Mon, 1 May 2023 09:21:29 +0300 Subject: [PATCH 01/25] Show no activity error in compact layout of wakatime card (#2662) --- src/cards/wakatime-card.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/cards/wakatime-card.js b/src/cards/wakatime-card.js index 2c329558..77521966 100644 --- a/src/cards/wakatime-card.js +++ b/src/cards/wakatime-card.js @@ -267,12 +267,20 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => { ${compactProgressBar} - ${createLanguageTextNode({ - x: 0, - y: 25, - langs: filteredLanguages, - totalSize: 100, - }).join("")} + ${ + filteredLanguages.length + ? createLanguageTextNode({ + x: 0, + y: 25, + langs: filteredLanguages, + totalSize: 100, + }).join("") + : noCodingActivityNode({ + // @ts-ignore + color: textColor, + text: i18n.t("wakatimecard.nocodingactivity"), + }) + } `; } else { finalLayout = flexLayout({ From a4d6fe104f8c7df2c3f0fc6619aa68306e458c5d Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Wed, 3 May 2023 08:04:33 +0300 Subject: [PATCH 02/25] Fixed card constructor docstring to resolve vscode type errors (#2674) --- src/common/Card.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/common/Card.js b/src/common/Card.js index 2f1d9c29..c61b17db 100644 --- a/src/common/Card.js +++ b/src/common/Card.js @@ -12,6 +12,12 @@ class Card { * @param {string?=} args.customTitle Card custom title. * @param {string?=} args.defaultTitle Card default title. * @param {string?=} args.titlePrefixIcon Card title prefix icon. + * @param {object?=} args.colors Card colors arguments. + * @param {string} args.colors.titleColor Card title color. + * @param {string} args.colors.textColor Card text color. + * @param {string} args.colors.iconColor Card icon color. + * @param {string|Array} args.colors.bgColor Card background color. + * @param {string} args.colors.borderColor Card border color. * @returns {Card} Card instance. */ constructor({ From 0dc35316669fffef34cf3a5339fd7f7d985a04f5 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Thu, 4 May 2023 10:13:47 +0300 Subject: [PATCH 03/25] Fixed wakatime create text node func docstring to resolve vscode type errors (#2678) --- src/cards/wakatime-card.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/cards/wakatime-card.js b/src/cards/wakatime-card.js index 77521966..4cf06f86 100644 --- a/src/cards/wakatime-card.js +++ b/src/cards/wakatime-card.js @@ -86,14 +86,15 @@ const createLanguageTextNode = ({ langs, totalSize, x, y }) => { /** * Create WakaTime text item. * - * @param {Object[]} args The function arguments. - * @param {string} id The id of the text node item. - * @param {string} label The label of the text node item. - * @param {string} value The value of the text node item. - * @param {number} index The index of the text node item. - * @param {percent} percent Percentage of the text node item. - * @param {boolean} hideProgress Whether to hide the progress bar. - * @param {string} progressBarBackgroundColor The color of the progress bar background. + * @param {Object} args The function arguments. + * @param {string} args.id The id of the text node item. + * @param {string} args.label The label of the text node item. + * @param {string} args.value The value of the text node item. + * @param {number} args.index The index of the text node item. + * @param {string} args.percent Percentage of the text node item. + * @param {boolean} args.hideProgress Whether to hide the progress bar. + * @param {string} args.progressBarColor The color of the progress bar. + * @param {string} args.progressBarBackgroundColor The color of the progress bar background. */ const createTextNode = ({ id, From 311204f4f9712a5ab6fb94c616a1c4310dd28006 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Fri, 5 May 2023 17:06:52 +0300 Subject: [PATCH 04/25] Fixed docstring for several wakatime card gen functions to resolve multiple type errors (#2690) --- src/cards/wakatime-card.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cards/wakatime-card.js b/src/cards/wakatime-card.js index 4cf06f86..0b236676 100644 --- a/src/cards/wakatime-card.js +++ b/src/cards/wakatime-card.js @@ -36,11 +36,11 @@ const noCodingActivityNode = ({ color, text }) => { /** * Create compact WakaTime layout. * - * @param {Object[]} args The function arguments. - * @param {import("../fetchers/types").WakaTimeLang[]} languages The languages array. - * @param {number} totalSize The total size of the languages. - * @param {number} x The x position of the language node. - * @param {number} y The y position of the language node. + * @param {Object} args The function arguments. + * @param {import("../fetchers/types").WakaTimeLang} args.lang The languages array. + * @param {number} args.totalSize The total size of the languages. + * @param {number} args.x The x position of the language node. + * @param {number} args.y The y position of the language node. */ const createCompactLangNode = ({ lang, totalSize, x, y }) => { const color = languageColors[lang.name] || "#858585"; @@ -58,11 +58,11 @@ const createCompactLangNode = ({ lang, totalSize, x, y }) => { /** * Create WakaTime language text node item. * - * @param {Object[]} args The function arguments. - * @param {import("../fetchers/types").WakaTimeLang} lang The language object. - * @param {number} totalSize The total size of the languages. - * @param {number} x The x position of the language node. - * @param {number} y The y position of the language node. + * @param {Object} args The function arguments. + * @param {import("../fetchers/types").WakaTimeLang[]} args.langs The language objects. + * @param {number} args.totalSize The total size of the languages. + * @param {number} args.x The x position of the language node. + * @param {number} args.y The y position of the language node. */ const createLanguageTextNode = ({ langs, totalSize, x, y }) => { return langs.map((lang, index) => { From c6dab8b0f2a0ecf66b1d7e797579a31c950897e5 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Fri, 5 May 2023 17:07:15 +0300 Subject: [PATCH 05/25] Fixed docstring for stats card create text node function to resolve type errors (#2689) --- src/cards/stats-card.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cards/stats-card.js b/src/cards/stats-card.js index 92701d81..4761d023 100644 --- a/src/cards/stats-card.js +++ b/src/cards/stats-card.js @@ -20,14 +20,16 @@ const RANK_CARD_DEFAULT_WIDTH = 450; /** * Create a stats card text item. * - * @param {object[]} createTextNodeParams Object that contains the createTextNode parameters. + * @param {object} createTextNodeParams Object that contains the createTextNode parameters. + * @param {string} createTextNodeParams.icon The icon to display. * @param {string} createTextNodeParams.label The label to display. - * @param {string} createTextNodeParams.value The value to display. + * @param {number} createTextNodeParams.value The value to display. * @param {string} createTextNodeParams.id The id of the stat. * @param {number} createTextNodeParams.index The index of the stat. * @param {boolean} createTextNodeParams.showIcons Whether to show icons. * @param {number} createTextNodeParams.shiftValuePos Number of pixels the value has to be shifted to the right. * @param {boolean} createTextNodeParams.bold Whether to bold the label. + * @param {string} createTextNodeParams.number_format The format of numbers on card. * @returns */ const createTextNode = ({ From c656f3df6bd26ebe9e244616618617099a84bfb0 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Fri, 5 May 2023 17:07:37 +0300 Subject: [PATCH 06/25] Update codecov action version to resolve github action deprecation warning (#2677) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fe34668d..e8fc84a7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,4 +34,4 @@ jobs: npm run format:check - name: Code Coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 From d306bec73e5325869b41592dfc1758119c2a4036 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Fri, 5 May 2023 17:09:23 +0300 Subject: [PATCH 07/25] Add PRs cache cleaning workflow (#2661) --- .github/workflows/prs-cache-clean.yml | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/prs-cache-clean.yml diff --git a/.github/workflows/prs-cache-clean.yml b/.github/workflows/prs-cache-clean.yml new file mode 100644 index 00000000..8ee4670e --- /dev/null +++ b/.github/workflows/prs-cache-clean.yml @@ -0,0 +1,33 @@ +name: prs cache clean +on: + pull_request: + types: + - closed + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Cleanup + run: | + gh extension install actions/gh-actions-cache + + REPO=${{ github.repository }} + BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge" + + echo "Fetching list of cache key" + cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) + + ## Setting this to not fail the workflow while deleting cache keys. + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR + do + gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm + done + echo "Done" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 2619c1263876b9ac6eef5fb11737efa7ad2603b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 May 2023 14:10:17 +0000 Subject: [PATCH 08/25] build(deps): bump yaml from 2.1.3 to 2.2.2 (#2644) Bumps [yaml](https://github.com/eemeli/yaml) from 2.1.3 to 2.2.2. - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](https://github.com/eemeli/yaml/compare/v2.1.3...v2.2.2) --- updated-dependencies: - dependency-name: yaml dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index ebc7570a..a378d89e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5581,9 +5581,9 @@ "dev": true }, "node_modules/yaml": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", - "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", + "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", "dev": true, "engines": { "node": ">= 14" @@ -9855,9 +9855,9 @@ "dev": true }, "yaml": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", - "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", + "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", "dev": true }, "yargs": { From a340900e8a1fac282d953fd4c7ac1ae9a459726e Mon Sep 17 00:00:00 2001 From: Fabiano Couto Date: Fri, 5 May 2023 18:12:28 -0300 Subject: [PATCH 09/25] fix(wakatime-fetcher): add default range to prevent user not found error (#2670) * feat(theme): add github_dark_dimmed theme * feat(theme): change github_dark_dimmed icon color * contrast ratio adjustment contrast ratio adjustment on github_dark_dimmed theme * feat(theme): readme preview * feat(theme): github themes next to each other * github themes next to each other * feat(RankIcon): add rank icon option * feat(RankIcon): extract rankIcon to icons file * feat(RankIcon): update readme * feat(RankIcon): test coverage * Update readme.md Co-authored-by: Rick Staa * add wakatime default range * update wakatime demos in readme with most active user * update wakatime demo in readme * remove &range=all_time from wakatime demos in readme --------- Co-authored-by: Rick Staa --- readme.md | 8 ++++---- src/fetchers/wakatime-fetcher.js | 4 +++- tests/fetchWakatime.test.js | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index ae18c9e6..a95022be 100644 --- a/readme.md +++ b/readme.md @@ -456,13 +456,13 @@ Change the `?username=` value to your [Wakatime](https://wakatime.com) username. ### Demo -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) - Compact layout -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&layout=compact)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&layout=compact)](https://github.com/anuraghazra/github-readme-stats) * * * @@ -520,7 +520,7 @@ Choose from any of the [default themes](#themes) - WakaTime card -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) * * * diff --git a/src/fetchers/wakatime-fetcher.js b/src/fetchers/wakatime-fetcher.js index fa1f3d89..2af8fa99 100644 --- a/src/fetchers/wakatime-fetcher.js +++ b/src/fetchers/wakatime-fetcher.js @@ -14,7 +14,9 @@ const fetchWakatimeStats = async ({ username, api_domain, range }) => { const { data } = await axios.get( `https://${ api_domain ? api_domain.replace(/\/$/gi, "") : "wakatime.com" - }/api/v1/users/${username}/stats/${range || ""}?is_including_today=true`, + }/api/v1/users/${username}/stats/${ + range || "all_time" + }?is_including_today=true`, ); return data.data; diff --git a/tests/fetchWakatime.test.js b/tests/fetchWakatime.test.js index 47ca25b7..04c01ba4 100644 --- a/tests/fetchWakatime.test.js +++ b/tests/fetchWakatime.test.js @@ -105,7 +105,7 @@ describe("Wakatime fetcher", () => { const username = "anuraghazra"; mock .onGet( - `https://wakatime.com/api/v1/users/${username}/stats/?is_including_today=true`, + `https://wakatime.com/api/v1/users/${username}/stats/all_time?is_including_today=true`, ) .reply(200, wakaTimeData); From 0c2fe4e07bab9c8fd8ec2477766cbf2b1fb11cdc Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Sat, 6 May 2023 10:43:37 +0300 Subject: [PATCH 10/25] Cover with test changes in #2662 pull request (#2673) --- tests/renderWakatimeCard.test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/renderWakatimeCard.test.js b/tests/renderWakatimeCard.test.js index 25e7ab4d..630f2596 100644 --- a/tests/renderWakatimeCard.test.js +++ b/tests/renderWakatimeCard.test.js @@ -67,4 +67,19 @@ describe("Test Render Wakatime Card", () => { "No coding activity this week", ); }); + + it('should show "no coding activity this week" message when using compact layout and there has not been activity', () => { + document.body.innerHTML = renderWakatimeCard( + { + ...wakaTimeData.data, + languages: undefined, + }, + { + layout: "compact", + }, + ); + expect(document.querySelector(".stat").textContent).toBe( + "No coding activity this week", + ); + }); }); From 0caa4c5fd8788bb77406d298dc74fecc7bdc9eed Mon Sep 17 00:00:00 2001 From: Rongrong Date: Sat, 6 May 2023 16:07:03 +0800 Subject: [PATCH 11/25] test: fix mistaken pageInfo.endCursor keys (#2657) The previous mock logic was too simplistic and has been fixed in the commit. If the mock logic had been properly implemented, then the mistaken pageInfo.endCursor keys should have made the test case "should fetch two pages of stars if 'FETCH_MULTI_PAGE_STARS' env variable is set to `true`" stuck. --- tests/api.test.js | 2 +- tests/fetchStats.test.js | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/api.test.js b/tests/api.test.js index 461f3e18..f11832ef 100644 --- a/tests/api.test.js +++ b/tests/api.test.js @@ -43,7 +43,7 @@ const data_stats = { nodes: [{ stargazers: { totalCount: 100 } }], pageInfo: { hasNextPage: false, - cursor: "cursor", + endCursor: "cursor", }, }, }, diff --git a/tests/fetchStats.test.js b/tests/fetchStats.test.js index 04e943a7..08523f33 100644 --- a/tests/fetchStats.test.js +++ b/tests/fetchStats.test.js @@ -27,7 +27,7 @@ const data_stats = { ], pageInfo: { hasNextPage: true, - cursor: "cursor", + endCursor: "cursor", }, }, }, @@ -44,7 +44,7 @@ const data_repo = { ], pageInfo: { hasNextPage: false, - cursor: "cursor", + endCursor: "cursor", }, }, }, @@ -64,7 +64,7 @@ const data_repo_zero_stars = { ], pageInfo: { hasNextPage: true, - cursor: "cursor", + endCursor: "cursor", }, }, }, @@ -86,11 +86,12 @@ const mock = new MockAdapter(axios); beforeEach(() => { process.env.FETCH_MULTI_PAGE_STARS = "false"; // Set to `false` to fetch only one page of stars. - mock - .onPost("https://api.github.com/graphql") - .replyOnce(200, data_stats) - .onPost("https://api.github.com/graphql") - .replyOnce(200, data_repo); + mock.onPost("https://api.github.com/graphql").reply((cfg) => { + return [ + 200, + cfg.data.includes("contributionsCollection") ? data_stats : data_repo, + ]; + }); }); afterEach(() => { From 688f4e497593d22d813423248bd96aaf0f0e495f Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 6 May 2023 11:59:11 +0200 Subject: [PATCH 12/25] docs: update wakatime user (#2697) The old example user doesn't have a WakaTime account anymore. --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index a95022be..58af2835 100644 --- a/readme.md +++ b/readme.md @@ -448,7 +448,7 @@ You can use the `&hide_progress=true` option to hide the percentages and the pro Change the `?username=` value to your [Wakatime](https://wakatime.com) username. ```md -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) ``` > **Note**: From 6f449a1f0b4cca5d19c16a5822f162921f5cf238 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Sun, 7 May 2023 23:03:00 +0300 Subject: [PATCH 13/25] Fixed docstring for get styles function to resolve vscode type errors (#2700) --- src/getStyles.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/getStyles.js b/src/getStyles.js index f7b90f4a..7e611392 100644 --- a/src/getStyles.js +++ b/src/getStyles.js @@ -65,10 +65,11 @@ const getAnimations = () => { /** * Retrieves CSS styles for a card. * - * @param {Object[]} colors The colors to use for the card. + * @param {Object} colors The colors to use for the card. * @param {string} colors.titleColor The title color. * @param {string} colors.textColor The text color. * @param {string} colors.iconColor The icon color. + * @param {string} colors.ringColor The ring color. * @param {boolean} colors.show_icons Whether to show icons. * @param {number} colors.progress The progress value to animate to. * @returns {string} Card CSS styles. From 15436e2aa19de52177409735f67da56f243e4431 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Sun, 7 May 2023 23:03:26 +0300 Subject: [PATCH 14/25] Fixed axios imports inside docstrings to resolve vscode type errors (#2699) --- src/fetchers/repo-fetcher.js | 4 ++-- src/fetchers/top-languages-fetcher.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fetchers/repo-fetcher.js b/src/fetchers/repo-fetcher.js index ff7a2be8..412d1d4c 100644 --- a/src/fetchers/repo-fetcher.js +++ b/src/fetchers/repo-fetcher.js @@ -5,9 +5,9 @@ import { MissingParamError, request } from "../common/utils.js"; /** * Repo data fetcher. * - * @param {import('Axios').AxiosRequestHeaders} variables Fetcher variables. + * @param {import('axios').AxiosRequestHeaders} variables Fetcher variables. * @param {string} token GitHub token. - * @returns {Promise} The response. + * @returns {Promise} The response. */ const fetcher = (variables, token) => { return request( diff --git a/src/fetchers/top-languages-fetcher.js b/src/fetchers/top-languages-fetcher.js index 45b2ba7d..b57d901a 100644 --- a/src/fetchers/top-languages-fetcher.js +++ b/src/fetchers/top-languages-fetcher.js @@ -11,7 +11,7 @@ import { /** * Top languages fetcher object. * - * @param {import('Axios').AxiosRequestHeaders} variables Fetcher variables. + * @param {import('axios').AxiosRequestHeaders} variables Fetcher variables. * @param {string} token GitHub token. * @returns {Promise} Languages fetcher response. */ From ce116e2d2f42aad54f9fc8e54f208c6c9b1ff558 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Mon, 8 May 2023 10:31:47 +0300 Subject: [PATCH 15/25] Fixed typos inside bug report issue template (#2706) --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 198bc80e..367ae4f3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -15,7 +15,7 @@ body: required: true - type: textarea attributes: - label: Expected behaviour + label: Expected behavior description: A clear and concise description of what you expected to happen. - type: textarea From 550b88ce878ea19d433b8167731db0a3c306d8a2 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Mon, 8 May 2023 10:32:50 +0300 Subject: [PATCH 16/25] Update wakatime user inside docs translations (#2705) --- docs/readme_de.md | 8 ++++---- docs/readme_es.md | 10 +++++----- docs/readme_kr.md | 10 +++++----- docs/readme_nl.md | 8 ++++---- docs/readme_np.md | 8 ++++---- docs/readme_pt-BR.md | 6 +++--- docs/readme_tr.md | 10 +++++----- 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/readme_de.md b/docs/readme_de.md index 8756670e..1ad0d26a 100644 --- a/docs/readme_de.md +++ b/docs/readme_de.md @@ -253,18 +253,18 @@ Du kannst die `&layout=compact` Option nutzen, um das Kartendesign zu ändern. Ändere `?username=` in den eigenen [Wakatime](https://wakatime.com)-Benutzernamen. ```md -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) ``` ### Beispiel -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) - Kompaktes Layout -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&layout=compact)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&layout=compact)](https://github.com/anuraghazra/github-readme-stats) --- diff --git a/docs/readme_es.md b/docs/readme_es.md index b477e5c7..5672027a 100644 --- a/docs/readme_es.md +++ b/docs/readme_es.md @@ -282,18 +282,18 @@ Puedes usar la opción `& layout = compact` para cambiar el diseño de la tarjet cambia el valor del parámetro `?username=` a tu username en [Wakatime](https://wakatime.com). ```md -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) ``` ### Ejemplo -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) - Diseño compacto -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&layout=compact)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&layout=compact)](https://github.com/anuraghazra/github-readme-stats) --- @@ -343,7 +343,7 @@ Escoja cualquiera de los [temas por defecto](#themes) - Tarjeta de Wakatime -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) --- diff --git a/docs/readme_kr.md b/docs/readme_kr.md index 4a1c57cc..0d7bced3 100644 --- a/docs/readme_kr.md +++ b/docs/readme_kr.md @@ -298,18 +298,18 @@ _참고: `?username=` 속성의 값을 [Wakatime](https://wakatime.com) 계정의 사용자 명(닉네임)으로 바꿔주세요. ```md -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) ``` ### 미리보기 -[![willianrod 님의 wakatime 통계](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok 님의 wakatime 통계](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) -[![willianrod 님의 wakatime 통계](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok 님의 wakatime 통계](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) - 컴팩트한 레이아웃 -[![willianrod 님의 wakatime 통계](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&layout=compact)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok 님의 wakatime 통계](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&layout=compact)](https://github.com/anuraghazra/github-readme-stats) --- @@ -359,7 +359,7 @@ _참고: - Wakatime 카드 -[![willianrod 님의 Wakatime 카드](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok 님의 Wakatime 카드](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) --- diff --git a/docs/readme_nl.md b/docs/readme_nl.md index b279c4f7..f70828f1 100644 --- a/docs/readme_nl.md +++ b/docs/readme_nl.md @@ -283,14 +283,14 @@ Je kan de `&layout=compact` optie gebruiken om het kaart ontwerp aan te passen. Verander de `?username=` waarde naar je [Wakatime](https://wakatime.com) gebruikersnaam. ```md -[![willianrod's Wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's Wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) ``` ### Demo -[![willianrod's Wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's Wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) -[![willianrod's Wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's Wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) --- @@ -340,7 +340,7 @@ Kies uit de [standaard thema\'s](#themes) - Wakatime kaart -[![willianrod's Wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's Wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) --- diff --git a/docs/readme_np.md b/docs/readme_np.md index e90ee57b..b322545c 100644 --- a/docs/readme_np.md +++ b/docs/readme_np.md @@ -279,14 +279,14 @@ You can use the `&langs_count=` option to increase or decrease the number of lan Change the `?username=` value to your [Wakatime](https://wakatime.com) username. ```md -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) ``` ### डेमो -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) --- @@ -336,7 +336,7 @@ Change the `?username=` value to your [Wakatime](https://wakatime.com) username. - वक समय कार्ड -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) --- diff --git a/docs/readme_pt-BR.md b/docs/readme_pt-BR.md index 1ac57716..cf6eeedc 100644 --- a/docs/readme_pt-BR.md +++ b/docs/readme_pt-BR.md @@ -242,14 +242,14 @@ Utilize a opção `&layout=compact` para mudar o layout do cartão. Altere o valor de `?username=` para o seu username do Wakatime. ```md -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) ``` ### Demonstração -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) --- diff --git a/docs/readme_tr.md b/docs/readme_tr.md index d8ae9778..fe5aff73 100644 --- a/docs/readme_tr.md +++ b/docs/readme_tr.md @@ -282,18 +282,18 @@ Endpoint: `api/top-langs?username=mustafacagri` `?username=` değerini [Wakatime](https://wakatime.com)'daki kullanıcı adınızla değiştirin. ```md -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) ``` ### Demo -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) - Kompakt Düzen -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod&layout=compact)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok&layout=compact)](https://github.com/anuraghazra/github-readme-stats) --- @@ -343,7 +343,7 @@ Endpoint: `api/top-langs?username=mustafacagri` - Wakatime kart -[![willianrod's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=willianrod)](https://github.com/anuraghazra/github-readme-stats) +[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) --- From 8ff5a3ba45cb493f61444d2963e969b2b54d91e4 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Mon, 8 May 2023 10:33:08 +0300 Subject: [PATCH 17/25] Fixed docstring for create language text node function (#2704) --- src/cards/top-languages-card.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cards/top-languages-card.js b/src/cards/top-languages-card.js index ce8e12a8..816b651e 100644 --- a/src/cards/top-languages-card.js +++ b/src/cards/top-languages-card.js @@ -98,7 +98,7 @@ const createCompactLangNode = ({ lang, totalSize, hideProgress, index }) => { /** * Creates compact layout of text only language nodes. * - * @param {object[]} props Function properties. + * @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.hideProgress Whether to hide percentage. From daa1977ba310f5dcb44f7945e7ecb5537e708c05 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Tue, 9 May 2023 10:04:02 +0300 Subject: [PATCH 18/25] Fixed docstring for wakatime card recalculate percentages function (#2710) --- src/cards/wakatime-card.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cards/wakatime-card.js b/src/cards/wakatime-card.js index 0b236676..dacc4227 100644 --- a/src/cards/wakatime-card.js +++ b/src/cards/wakatime-card.js @@ -140,7 +140,7 @@ const createTextNode = ({ * hiding languages. * * @param {import("../fetchers/types").WakaTimeLang[]} languages The languages array. - * @return {import("../fetchers/types").WakaTimeLang[]} The recalculated languages array. + * @return {void} The recalculated languages array. */ const recalculatePercentages = (languages) => { const totalSum = languages.reduce( From c5e7f7b490f3c36857396e8c888d21d9e102c3ae Mon Sep 17 00:00:00 2001 From: Nabil Alamin Date: Tue, 9 May 2023 19:54:34 +0100 Subject: [PATCH 19/25] add pie chart layout to language card (#2099) * add pie chart layout to language card * resolve failing top-lang card tests * scale down pie chart * update readme.md * Update readme.md Co-authored-by: Rick Staa * style: format code * update donut layout to be created without dependencies * minor update * style: format readme * resolve failing tests * refactor: clean up code and add extra tests This commit cleans up the pie chart generation code and adds additional tests. * feat: improve pie chart positioning * rename layout pie to donut * add animation to donut layout * refactor: rename pie and doughnut to donut * feat: decrease donus animation delay --------- Co-authored-by: rickstaa --- package-lock.json | 145 ++++---------- readme.md | 12 ++ src/cards/top-languages-card.js | 311 +++++++++++++++++++++++++------ src/cards/types.d.ts | 2 +- tests/renderTopLanguages.test.js | 287 +++++++++++++++++++++++++++- 5 files changed, 593 insertions(+), 164 deletions(-) diff --git a/package-lock.json b/package-lock.json index a378d89e..a2156910 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1495,31 +1495,19 @@ } }, "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", "dev": true, "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" } }, "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, "engines": { "node": ">=0.4.0" @@ -1778,12 +1766,6 @@ "node": ">=8" } }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, "node_modules/browserslist": { "version": "4.21.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", @@ -2509,20 +2491,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3865,18 +3833,18 @@ } }, "node_modules/jsdom": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.0.tgz", - "integrity": "sha512-x4a6CKCgx00uCmP+QakBDFXwjAJ69IkkIWHmtmjd3wvXPcdOS44hfX2vqkOQrVrq8l9DhNNADZRXaCEWvgXtVA==", + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-pksjj7Rqoa+wdpkKcLzQRHhJCEE42qQhl/xLMUKHgoSejaKOdaXEAnqs6uDNwMl/fciHTzKeR8Wm8cw7N+g98A==", "dev": true, "dependencies": { "abab": "^2.0.6", - "acorn": "^8.7.1", - "acorn-globals": "^6.0.0", + "acorn": "^8.8.0", + "acorn-globals": "^7.0.0", "cssom": "^0.5.0", "cssstyle": "^2.3.0", "data-urls": "^3.0.2", - "decimal.js": "^10.3.1", + "decimal.js": "^10.4.1", "domexception": "^4.0.0", "escodegen": "^2.0.0", "form-data": "^4.0.0", @@ -3884,18 +3852,17 @@ "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.1", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "^7.0.0", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", + "tough-cookie": "^4.1.2", "w3c-xmlserializer": "^3.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^2.0.0", "whatwg-mimetype": "^3.0.0", "whatwg-url": "^11.0.0", - "ws": "^8.8.0", + "ws": "^8.9.0", "xml-name-validator": "^4.0.0" }, "engines": { @@ -5397,15 +5364,6 @@ "node": ">=10.12.0" } }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, "node_modules/w3c-xmlserializer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", @@ -6830,27 +6788,19 @@ "dev": true }, "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", "dev": true, "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" } }, "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true }, "agent-base": { @@ -7049,12 +6999,6 @@ "fill-range": "^7.0.1" } }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, "browserslist": { "version": "4.21.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", @@ -7590,13 +7534,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -8609,18 +8546,18 @@ } }, "jsdom": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.0.tgz", - "integrity": "sha512-x4a6CKCgx00uCmP+QakBDFXwjAJ69IkkIWHmtmjd3wvXPcdOS44hfX2vqkOQrVrq8l9DhNNADZRXaCEWvgXtVA==", + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-pksjj7Rqoa+wdpkKcLzQRHhJCEE42qQhl/xLMUKHgoSejaKOdaXEAnqs6uDNwMl/fciHTzKeR8Wm8cw7N+g98A==", "dev": true, "requires": { "abab": "^2.0.6", - "acorn": "^8.7.1", - "acorn-globals": "^6.0.0", + "acorn": "^8.8.0", + "acorn-globals": "^7.0.0", "cssom": "^0.5.0", "cssstyle": "^2.3.0", "data-urls": "^3.0.2", - "decimal.js": "^10.3.1", + "decimal.js": "^10.4.1", "domexception": "^4.0.0", "escodegen": "^2.0.0", "form-data": "^4.0.0", @@ -8628,18 +8565,17 @@ "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.1", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "^7.0.0", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", + "tough-cookie": "^4.1.2", "w3c-xmlserializer": "^3.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^2.0.0", "whatwg-mimetype": "^3.0.0", "whatwg-url": "^11.0.0", - "ws": "^8.8.0", + "ws": "^8.9.0", "xml-name-validator": "^4.0.0" } }, @@ -9724,15 +9660,6 @@ "convert-source-map": "^1.6.0" } }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, "w3c-xmlserializer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", diff --git a/readme.md b/readme.md index 58af2835..5f3c1254 100644 --- a/readme.md +++ b/readme.md @@ -423,6 +423,14 @@ You can use the `&layout=compact` option to change the card design. [![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=compact)](https://github.com/anuraghazra/github-readme-stats) ``` +### Donut Chart Language Card Layout + +You can use the `&layout=donut` option to change the card design. + +```md +[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=donut)](https://github.com/anuraghazra/github-readme-stats) +``` + ### Hide Progress Bars You can use the `&hide_progress=true` option to hide the percentages and the progress bars (layout will be automatically set to `compact`). @@ -439,6 +447,10 @@ You can use the `&hide_progress=true` option to hide the percentages and the pro [![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=compact)](https://github.com/anuraghazra/github-readme-stats) +- Donut Chart layout + +[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=donut)](https://github.com/anuraghazra/github-readme-stats) + - Hidden progress bars [![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) diff --git a/src/cards/top-languages-card.js b/src/cards/top-languages-card.js index 816b651e..262ae972 100644 --- a/src/cards/top-languages-card.js +++ b/src/cards/top-languages-card.js @@ -36,13 +36,134 @@ const getLongestLang = (arr) => ); /** - * Creates a node to display usage of a programming language in percentage - * using text and a horizontal progress bar. + * Convert degrees to radians. + * + * @param {number} angleInDegrees Angle in degrees. + * @returns Angle in radians. + */ +const degreesToRadians = (angleInDegrees) => angleInDegrees * (Math.PI / 180.0); + +/** + * Convert radians to degrees. + * + * @param {number} angleInRadians Angle in radians. + * @returns Angle in degrees. + */ +const radiansToDegrees = (angleInRadians) => angleInRadians / (Math.PI / 180.0); + +/** + * Convert polar coordinates to cartesian coordinates. + * + * @param {number} centerX Center x coordinate. + * @param {number} centerY Center y coordinate. + * @param {number} radius Radius of the circle. + * @param {number} angleInDegrees Angle in degrees. + * @returns {{x: number, y: number}} Cartesian coordinates. + */ +const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => { + const rads = degreesToRadians(angleInDegrees); + return { + x: centerX + radius * Math.cos(rads), + y: centerY + radius * Math.sin(rads), + }; +}; + +/** + * Convert cartesian coordinates to polar coordinates. + * + * @param {number} centerX Center x coordinate. + * @param {number} centerY Center y coordinate. + * @param {number} x Point x coordinate. + * @param {number} y Point y coordinate. + * @returns {{radius: number, angleInDegrees: number}} Polar coordinates. + */ +const cartesianToPolar = (centerX, centerY, x, y) => { + const radius = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2)); + let angleInDegrees = radiansToDegrees(Math.atan2(y - centerY, x - centerX)); + if (angleInDegrees < 0) angleInDegrees += 360; + return { radius, angleInDegrees }; +}; + +/** + * Calculates height for the compact layout. + * + * @param {number} totalLangs Total number of languages. + * @returns {number} Card height. + */ +const calculateCompactLayoutHeight = (totalLangs) => { + return 90 + Math.round(totalLangs / 2) * 25; +}; + +/** + * Calculates height for the normal layout. + * + * @param {number} totalLangs Total number of languages. + * @returns {number} Card height. + */ +const calculateNormalLayoutHeight = (totalLangs) => { + return 45 + (totalLangs + 1) * 40; +}; + +/** + * Calculates height for the donut layout. + * + * @param {number} totalLangs Total number of languages. + * @returns {number} Card height. + */ +const calculateDonutLayoutHeight = (totalLangs) => { + return 215 + Math.max(totalLangs - 5, 0) * 32; +}; + +/** + * Calculates the center translation needed to keep the donut chart centred. + * @param {number} totalLangs Total number of languages. + * @returns {number} Donut center translation. + */ +const donutCenterTranslation = (totalLangs) => { + return -45 + Math.max(totalLangs - 5, 0) * 16; +}; + +/** + * Trim top languages to lang_count while also hiding certain languages. + * + * @param {Record} topLangs Top languages. + * @param {string[]} hide Languages to hide. + * @param {string} langs_count Number of languages to show. + * @returns {{topLangs: Record, totalSize: number}} Trimmed top languages and total size. + */ +const trimTopLanguages = (topLangs, hide, langs_count) => { + let langs = Object.values(topLangs); + let langsToHide = {}; + let langsCount = clampValue(parseInt(langs_count), 1, 10); + + // populate langsToHide map for quick lookup + // while filtering out + if (hide) { + hide.forEach((langName) => { + langsToHide[lowercaseTrim(langName)] = true; + }); + } + + // filter out languages to be hidden + langs = langs + .sort((a, b) => b.size - a.size) + .filter((lang) => { + return !langsToHide[lowercaseTrim(lang.name)]; + }) + .slice(0, langsCount); + + const totalLanguageSize = langs.reduce((acc, curr) => acc + curr.size, 0); + + return { langs, totalLanguageSize }; +}; + +/** + * Create progress bar text item for a programming language. * * @param {object} props Function properties. * @param {number} props.width The card width - * @param {string} props.name Name of the programming language. * @param {string} props.color Color of the programming language. + * @param {string} props.name Name of the programming language. * @param {string} props.progress Usage of the programming language in percentage. * @param {number} props.index Index of the programming language. * @returns {string} Programming language SVG node. @@ -71,7 +192,7 @@ const createProgressTextNode = ({ width, color, name, progress, index }) => { }; /** - * Creates a text only node to display usage of a programming language in percentage. + * Creates compact text item for a programming language. * * @param {object} props Function properties. * @param {Lang} props.lang Programming language object. @@ -96,7 +217,7 @@ const createCompactLangNode = ({ lang, totalSize, hideProgress, index }) => { }; /** - * Creates compact layout of text only language nodes. + * Create compact languages text items for all programming languages. * * @param {object} props Function properties. * @param {Lang[]} props.langs Array of programming languages. @@ -134,7 +255,29 @@ const createLanguageTextNode = ({ langs, totalSize, hideProgress }) => { }; /** - * Renders layout to display user's most frequently used programming languages. + * Create donut languages text items for all programming languages. + * + * @param {object[]} props Function properties. + * @param {Lang[]} props.langs Array of programming languages. + * @param {number} props.totalSize Total size of all languages. + * @returns {string} Donut layout programming language SVG node. + */ +const createDonutLanguagesNode = ({ langs, totalSize }) => { + return flexLayout({ + items: langs.map((lang, index) => { + return createCompactLangNode({ + lang, + totalSize, + index, + }); + }), + gap: 32, + direction: "column", + }).join(""); +}; + +/** + * Renders the default language card layout. * * @param {Lang[]} langs Array of programming languages. * @param {number} width Card width. @@ -158,7 +301,7 @@ const renderNormalLayout = (langs, width, totalLanguageSize) => { }; /** - * Renders compact layout to display user's most frequently used programming languages. + * Renders the compact language card layout. * * @param {Lang[]} langs Array of programming languages. * @param {number} width Card width. @@ -218,60 +361,105 @@ const renderCompactLayout = (langs, width, totalLanguageSize, hideProgress) => { }; /** - * Calculates height for the compact layout. + * Creates the SVG paths for the language donut chart. * - * @param {number} totalLangs Total number of languages. - * @returns {number} Card height. + * @param {number} cx Donut center x-position. + * @param {number} cy Donut center y-position. + * @param {number} radius Donut arc Radius. + * @param {number[]} percentages Array with donut section percentages. + * @returns {{d: string, percent: number}[]} Array of svg path elements */ -const calculateCompactLayoutHeight = (totalLangs) => { - return 90 + Math.round(totalLangs / 2) * 25; -}; +const createDonutPaths = (cx, cy, radius, percentages) => { + const paths = []; + let startAngle = 0; + let endAngle = 0; -/** - * Calculates height for the normal layout. - * - * @param {number} totalLangs Total number of languages. - * @returns {number} Card height. - */ -const calculateNormalLayoutHeight = (totalLangs) => { - return 45 + (totalLangs + 1) * 40; -}; + const totalPercent = percentages.reduce((acc, curr) => acc + curr, 0); + for (let i = 0; i < percentages.length; i++) { + const tmpPath = {}; -/** - * Hides languages and trims the list to show only the top N languages. - * - * @param {Record} topLangs Top languages. - * @param {string[]} hide Languages to hide. - * @param {string} langs_count Number of languages to show. - */ -const useLanguages = (topLangs, hide, langs_count) => { - let langs = Object.values(topLangs); - let langsToHide = {}; - let langsCount = clampValue(parseInt(langs_count), 1, 10); + let percent = parseFloat( + ((percentages[i] / totalPercent) * 100).toFixed(2), + ); - // populate langsToHide map for quick lookup - // while filtering out - if (hide) { - hide.forEach((langName) => { - langsToHide[lowercaseTrim(langName)] = true; - }); + endAngle = 3.6 * percent + startAngle; + const startPoint = polarToCartesian(cx, cy, radius, endAngle - 90); // rotate donut 90 degrees counter-clockwise. + const endPoint = polarToCartesian(cx, cy, radius, startAngle - 90); // rotate donut 90 degrees counter-clockwise. + const largeArc = endAngle - startAngle <= 180 ? 0 : 1; + + tmpPath.percent = percent; + tmpPath.d = `M ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArc} 0 ${endPoint.x} ${endPoint.y}`; + + paths.push(tmpPath); + startAngle = endAngle; } - // filter out languages to be hidden - langs = langs - .sort((a, b) => b.size - a.size) - .filter((lang) => { - return !langsToHide[lowercaseTrim(lang.name)]; - }) - .slice(0, langsCount); - - const totalLanguageSize = langs.reduce((acc, curr) => acc + curr.size, 0); - - return { langs, totalLanguageSize }; + return paths; }; /** - * Renders card to display user's most frequently used programming languages. + * Renders the donut language card layout. + * + * @param {Lang[]} langs Array of programming languages. + * @param {number} width Card width. + * @param {number} totalLanguageSize Total size of all languages. + * @returns {string} Donut layout card SVG object. + */ +const renderDonutLayout = (langs, width, totalLanguageSize) => { + const centerX = width / 3; + const centerY = width / 3; + const radius = centerX - 60; + const strokeWidth = 12; + + const colors = langs.map((lang) => lang.color); + const langsPercents = langs.map((lang) => + parseFloat(((lang.size / totalLanguageSize) * 100).toFixed(2)), + ); + + const langPaths = createDonutPaths(centerX, centerY, radius, langsPercents); + + const donutPaths = + langs.length === 1 + ? `` + : langPaths + .map((section, index) => { + const staggerDelay = (index + 3) * 100; + const delay = staggerDelay + 300; + + const output = ` + + + + + `; + + return output; + }) + .join(""); + + const donut = `${donutPaths}`; + + return ` + + + ${createDonutLanguagesNode({ langs, totalSize: totalLanguageSize })} + + + + ${donut} + + + `; +}; + +/** + * Renders card that display user's most frequently used programming languages. * * @param {import('../fetchers/types').TopLangData} topLangs User's most frequently used programming languages. * @param {Partial} options Card options. @@ -302,7 +490,7 @@ const renderTopLanguages = (topLangs, options = {}) => { translations: langCardLocales, }); - const { langs, totalLanguageSize } = useLanguages( + const { langs, totalLanguageSize } = trimTopLanguages( topLangs, hide, String(langs_count), @@ -326,6 +514,10 @@ const renderTopLanguages = (topLangs, options = {}) => { totalLanguageSize, hide_progress, ); + } else if (layout?.toLowerCase() === "donut") { + height = calculateDonutLayoutHeight(langs.length); + width = width + 50; // padding + finalLayout = renderDonutLayout(langs, width, totalLanguageSize); } else { finalLayout = renderNormalLayout(langs, width, totalLanguageSize); } @@ -394,4 +586,17 @@ const renderTopLanguages = (topLangs, options = {}) => { `); }; -export { renderTopLanguages, MIN_CARD_WIDTH }; +export { + getLongestLang, + degreesToRadians, + radiansToDegrees, + polarToCartesian, + cartesianToPolar, + calculateCompactLayoutHeight, + calculateNormalLayoutHeight, + calculateDonutLayoutHeight, + donutCenterTranslation, + trimTopLanguages, + renderTopLanguages, + MIN_CARD_WIDTH, +}; diff --git a/src/cards/types.d.ts b/src/cards/types.d.ts index 02a41b57..fea5aa95 100644 --- a/src/cards/types.d.ts +++ b/src/cards/types.d.ts @@ -39,7 +39,7 @@ export type TopLangOptions = CommonOptions & { hide_border: boolean; card_width: number; hide: string[]; - layout: "compact" | "normal"; + layout: "compact" | "normal" | "donut"; custom_title: string; langs_count: number; disable_animations: boolean; diff --git a/tests/renderTopLanguages.test.js b/tests/renderTopLanguages.test.js index de9e21f1..e4f47c39 100644 --- a/tests/renderTopLanguages.test.js +++ b/tests/renderTopLanguages.test.js @@ -1,9 +1,20 @@ import { queryAllByTestId, queryByTestId } from "@testing-library/dom"; import { cssToObject } from "@uppercod/css-to-object"; import { - MIN_CARD_WIDTH, + getLongestLang, + degreesToRadians, + radiansToDegrees, + polarToCartesian, + cartesianToPolar, + calculateCompactLayoutHeight, + calculateNormalLayoutHeight, + calculateDonutLayoutHeight, + donutCenterTranslation, + trimTopLanguages, renderTopLanguages, + MIN_CARD_WIDTH, } from "../src/cards/top-languages-card.js"; + // adds special assertions like toHaveTextContent import "@testing-library/jest-dom"; @@ -27,6 +38,205 @@ const langs = { }, }; +/** + * Retrieve the language percentage from the donut chart SVG. + * @param {string} d The SVG path element. + * @param {number} centerX The center X coordinate of the donut chart. + * @param {number} centerY The center Y coordinate of the donut chart. + * @returns {number} The percentage of the language. + */ +const langPercentFromSvg = (d, centerX, centerY) => { + const dTmp = d + .split(" ") + .filter((x) => !isNaN(x)) + .map((x) => parseFloat(x)); + const endAngle = + cartesianToPolar(centerX, centerY, dTmp[0], dTmp[1]).angleInDegrees + 90; + let startAngle = + cartesianToPolar(centerX, centerY, dTmp[7], dTmp[8]).angleInDegrees + 90; + if (startAngle > endAngle) startAngle -= 360; + return (endAngle - startAngle) / 3.6; +}; + +describe("Test renderTopLanguages helper functions", () => { + it("getLongestLang", () => { + const langArray = Object.values(langs); + expect(getLongestLang(langArray)).toBe(langs.javascript); + }); + + it("degreesToRadians", () => { + expect(degreesToRadians(0)).toBe(0); + expect(degreesToRadians(90)).toBe(Math.PI / 2); + expect(degreesToRadians(180)).toBe(Math.PI); + expect(degreesToRadians(270)).toBe((3 * Math.PI) / 2); + expect(degreesToRadians(360)).toBe(2 * Math.PI); + }); + + it("radiansToDegrees", () => { + expect(radiansToDegrees(0)).toBe(0); + expect(radiansToDegrees(Math.PI / 2)).toBe(90); + expect(radiansToDegrees(Math.PI)).toBe(180); + expect(radiansToDegrees((3 * Math.PI) / 2)).toBe(270); + expect(radiansToDegrees(2 * Math.PI)).toBe(360); + }); + + it("polarToCartesian", () => { + expect(polarToCartesian(100, 100, 60, 0)).toStrictEqual({ x: 160, y: 100 }); + expect(polarToCartesian(100, 100, 60, 45)).toStrictEqual({ + x: 142.42640687119285, + y: 142.42640687119285, + }); + expect(polarToCartesian(100, 100, 60, 90)).toStrictEqual({ + x: 100, + y: 160, + }); + expect(polarToCartesian(100, 100, 60, 135)).toStrictEqual({ + x: 57.573593128807154, + y: 142.42640687119285, + }); + expect(polarToCartesian(100, 100, 60, 180)).toStrictEqual({ + x: 40, + y: 100.00000000000001, + }); + expect(polarToCartesian(100, 100, 60, 225)).toStrictEqual({ + x: 57.57359312880714, + y: 57.573593128807154, + }); + expect(polarToCartesian(100, 100, 60, 270)).toStrictEqual({ + x: 99.99999999999999, + y: 40, + }); + expect(polarToCartesian(100, 100, 60, 315)).toStrictEqual({ + x: 142.42640687119285, + y: 57.57359312880714, + }); + expect(polarToCartesian(100, 100, 60, 360)).toStrictEqual({ + x: 160, + y: 99.99999999999999, + }); + }); + + it("cartesianToPolar", () => { + expect(cartesianToPolar(100, 100, 160, 100)).toStrictEqual({ + radius: 60, + angleInDegrees: 0, + }); + expect( + cartesianToPolar(100, 100, 142.42640687119285, 142.42640687119285), + ).toStrictEqual({ radius: 60.00000000000001, angleInDegrees: 45 }); + expect(cartesianToPolar(100, 100, 100, 160)).toStrictEqual({ + radius: 60, + angleInDegrees: 90, + }); + expect( + cartesianToPolar(100, 100, 57.573593128807154, 142.42640687119285), + ).toStrictEqual({ radius: 60, angleInDegrees: 135 }); + expect(cartesianToPolar(100, 100, 40, 100.00000000000001)).toStrictEqual({ + radius: 60, + angleInDegrees: 180, + }); + expect( + cartesianToPolar(100, 100, 57.57359312880714, 57.573593128807154), + ).toStrictEqual({ radius: 60, angleInDegrees: 225 }); + expect(cartesianToPolar(100, 100, 99.99999999999999, 40)).toStrictEqual({ + radius: 60, + angleInDegrees: 270, + }); + expect( + cartesianToPolar(100, 100, 142.42640687119285, 57.57359312880714), + ).toStrictEqual({ radius: 60.00000000000001, angleInDegrees: 315 }); + expect(cartesianToPolar(100, 100, 160, 99.99999999999999)).toStrictEqual({ + radius: 60, + angleInDegrees: 360, + }); + }); + + it("calculateCompactLayoutHeight", () => { + expect(calculateCompactLayoutHeight(0)).toBe(90); + expect(calculateCompactLayoutHeight(1)).toBe(115); + expect(calculateCompactLayoutHeight(2)).toBe(115); + expect(calculateCompactLayoutHeight(3)).toBe(140); + expect(calculateCompactLayoutHeight(4)).toBe(140); + expect(calculateCompactLayoutHeight(5)).toBe(165); + expect(calculateCompactLayoutHeight(6)).toBe(165); + expect(calculateCompactLayoutHeight(7)).toBe(190); + expect(calculateCompactLayoutHeight(8)).toBe(190); + expect(calculateCompactLayoutHeight(9)).toBe(215); + expect(calculateCompactLayoutHeight(10)).toBe(215); + }); + + it("calculateNormalLayoutHeight", () => { + expect(calculateNormalLayoutHeight(0)).toBe(85); + expect(calculateNormalLayoutHeight(1)).toBe(125); + expect(calculateNormalLayoutHeight(2)).toBe(165); + expect(calculateNormalLayoutHeight(3)).toBe(205); + expect(calculateNormalLayoutHeight(4)).toBe(245); + expect(calculateNormalLayoutHeight(5)).toBe(285); + expect(calculateNormalLayoutHeight(6)).toBe(325); + expect(calculateNormalLayoutHeight(7)).toBe(365); + expect(calculateNormalLayoutHeight(8)).toBe(405); + expect(calculateNormalLayoutHeight(9)).toBe(445); + expect(calculateNormalLayoutHeight(10)).toBe(485); + }); + + it("calculateDonutLayoutHeight", () => { + expect(calculateDonutLayoutHeight(0)).toBe(215); + expect(calculateDonutLayoutHeight(1)).toBe(215); + expect(calculateDonutLayoutHeight(2)).toBe(215); + expect(calculateDonutLayoutHeight(3)).toBe(215); + expect(calculateDonutLayoutHeight(4)).toBe(215); + expect(calculateDonutLayoutHeight(5)).toBe(215); + expect(calculateDonutLayoutHeight(6)).toBe(247); + expect(calculateDonutLayoutHeight(7)).toBe(279); + expect(calculateDonutLayoutHeight(8)).toBe(311); + expect(calculateDonutLayoutHeight(9)).toBe(343); + expect(calculateDonutLayoutHeight(10)).toBe(375); + }); + + it("donutCenterTranslation", () => { + expect(donutCenterTranslation(0)).toBe(-45); + expect(donutCenterTranslation(1)).toBe(-45); + expect(donutCenterTranslation(2)).toBe(-45); + expect(donutCenterTranslation(3)).toBe(-45); + expect(donutCenterTranslation(4)).toBe(-45); + expect(donutCenterTranslation(5)).toBe(-45); + expect(donutCenterTranslation(6)).toBe(-29); + expect(donutCenterTranslation(7)).toBe(-13); + expect(donutCenterTranslation(8)).toBe(3); + expect(donutCenterTranslation(9)).toBe(19); + expect(donutCenterTranslation(10)).toBe(35); + }); + + it("trimTopLanguages", () => { + expect(trimTopLanguages([])).toStrictEqual({ + langs: [], + totalLanguageSize: 0, + }); + expect(trimTopLanguages([langs.javascript])).toStrictEqual({ + langs: [langs.javascript], + totalLanguageSize: 200, + }); + expect( + trimTopLanguages([langs.javascript, langs.HTML], [], 5), + ).toStrictEqual({ + langs: [langs.javascript, langs.HTML], + totalLanguageSize: 400, + }); + expect(trimTopLanguages(langs, [], 5)).toStrictEqual({ + langs: Object.values(langs), + totalLanguageSize: 500, + }); + expect(trimTopLanguages(langs, [], 2)).toStrictEqual({ + langs: Object.values(langs).slice(0, 2), + totalLanguageSize: 400, + }); + expect(trimTopLanguages(langs, ["javascript"], 5)).toStrictEqual({ + langs: [langs.HTML, langs.css], + totalLanguageSize: 300, + }); + }); +}); + describe("Test renderTopLanguages", () => { it("should render correctly", () => { document.body.innerHTML = renderTopLanguages(langs); @@ -236,6 +446,81 @@ describe("Test renderTopLanguages", () => { ); }); + it("should render with layout donut", () => { + document.body.innerHTML = renderTopLanguages(langs, { layout: "donut" }); + + expect(queryByTestId(document.body, "header")).toHaveTextContent( + "Most Used Languages", + ); + + expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent( + "HTML 40.00%", + ); + expect(queryAllByTestId(document.body, "lang-donut")[0]).toHaveAttribute( + "size", + "40", + ); + const d = queryAllByTestId(document.body, "lang-donut")[0] + .getAttribute("d") + .split(" ") + .filter((x) => !isNaN(x)) + .map((x) => parseFloat(x)); + const center = { x: d[7], y: d[7] }; + const HTMLLangPercent = langPercentFromSvg( + queryAllByTestId(document.body, "lang-donut")[0].getAttribute("d"), + center.x, + center.y, + ); + expect(HTMLLangPercent).toBeCloseTo(40); + + expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent( + "javascript 40.00%", + ); + expect(queryAllByTestId(document.body, "lang-donut")[1]).toHaveAttribute( + "size", + "40", + ); + const javascriptLangPercent = langPercentFromSvg( + queryAllByTestId(document.body, "lang-donut")[1].getAttribute("d"), + center.x, + center.y, + ); + expect(javascriptLangPercent).toBeCloseTo(40); + + expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent( + "css 20.00%", + ); + expect(queryAllByTestId(document.body, "lang-donut")[2]).toHaveAttribute( + "size", + "20", + ); + const cssLangPercent = langPercentFromSvg( + queryAllByTestId(document.body, "lang-donut")[2].getAttribute("d"), + center.x, + center.y, + ); + expect(cssLangPercent).toBeCloseTo(20); + + expect(HTMLLangPercent + javascriptLangPercent + cssLangPercent).toBe(100); + + // Should render full donut (circle) if one language is 100%. + document.body.innerHTML = renderTopLanguages( + { HTML: langs.HTML }, + { layout: "donut" }, + ); + expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent( + "HTML 100.00%", + ); + expect(queryAllByTestId(document.body, "lang-donut")[0]).toHaveAttribute( + "size", + "100", + ); + expect(queryAllByTestId(document.body, "lang-donut")).toHaveLength(1); + expect(queryAllByTestId(document.body, "lang-donut")[0].tagName).toBe( + "circle", + ); + }); + it("should render a translated title", () => { document.body.innerHTML = renderTopLanguages(langs, { locale: "cn" }); expect(document.getElementsByClassName("header")[0].textContent).toBe( From 4b19453c1ae1ab0ff7292206fae8db85b427369f Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Wed, 10 May 2023 23:59:00 +0300 Subject: [PATCH 20/25] Fixed docstring for create donut language node function (#2713) --- src/cards/top-languages-card.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cards/top-languages-card.js b/src/cards/top-languages-card.js index 262ae972..5523c930 100644 --- a/src/cards/top-languages-card.js +++ b/src/cards/top-languages-card.js @@ -257,7 +257,7 @@ const createLanguageTextNode = ({ langs, totalSize, hideProgress }) => { /** * Create donut languages text items for all programming languages. * - * @param {object[]} props Function properties. + * @param {object} props Function properties. * @param {Lang[]} props.langs Array of programming languages. * @param {number} props.totalSize Total size of all languages. * @returns {string} Donut layout programming language SVG node. From 1f4a2c4d827533813faedf2b40172fe1c221037d Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Thu, 11 May 2023 09:36:21 +0300 Subject: [PATCH 21/25] Add missing argument to create compact lang node function to resolve vscode type error (#2714) --- src/cards/top-languages-card.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cards/top-languages-card.js b/src/cards/top-languages-card.js index 5523c930..ed51f1c9 100644 --- a/src/cards/top-languages-card.js +++ b/src/cards/top-languages-card.js @@ -268,6 +268,7 @@ const createDonutLanguagesNode = ({ langs, totalSize }) => { return createCompactLangNode({ lang, totalSize, + hideProgress: false, index, }); }), From ff9839b73caefcfb0185e61f529c7def7398fc1c Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <53787217+qwerty541@users.noreply.github.com> Date: Thu, 11 May 2023 09:49:57 +0300 Subject: [PATCH 22/25] Top languages card pie layout (#2709) * Top languages card donut layout * Top languages card pie layout * renames * dev * docs * dev * dev * animations * dev * handle one language --- readme.md | 12 +++ src/cards/top-languages-card.js | 115 +++++++++++++++++++++++++++- src/cards/types.d.ts | 2 +- tests/renderTopLanguages.test.js | 127 ++++++++++++++++++++++++++++++- 4 files changed, 250 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 5f3c1254..d1e56775 100644 --- a/readme.md +++ b/readme.md @@ -431,6 +431,14 @@ You can use the `&layout=donut` option to change the card design. [![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=donut)](https://github.com/anuraghazra/github-readme-stats) ``` +### Pie Chart Language Card Layout + +You can use the `&layout=pie` option to change the card design. + +```md +[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=pie)](https://github.com/anuraghazra/github-readme-stats) +``` + ### Hide Progress Bars You can use the `&hide_progress=true` option to hide the percentages and the progress bars (layout will be automatically set to `compact`). @@ -451,6 +459,10 @@ You can use the `&hide_progress=true` option to hide the percentages and the pro [![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=donut)](https://github.com/anuraghazra/github-readme-stats) +- Pie Chart layout + +[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=pie)](https://github.com/anuraghazra/github-readme-stats) + - Hidden progress bars [![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&hide_progress=true)](https://github.com/anuraghazra/github-readme-stats) diff --git a/src/cards/top-languages-card.js b/src/cards/top-languages-card.js index ed51f1c9..e03e8bcb 100644 --- a/src/cards/top-languages-card.js +++ b/src/cards/top-languages-card.js @@ -114,6 +114,16 @@ const calculateDonutLayoutHeight = (totalLangs) => { return 215 + Math.max(totalLangs - 5, 0) * 32; }; +/** + * Calculates height for the pie layout. + * + * @param {number} totalLangs Total number of languages. + * @returns {number} Card height. + */ +const calculatePieLayoutHeight = (totalLangs) => { + return 300 + Math.round(totalLangs / 2) * 25; +}; + /** * Calculates the center translation needed to keep the donut chart centred. * @param {number} totalLangs Total number of languages. @@ -361,6 +371,101 @@ const renderCompactLayout = (langs, width, totalLanguageSize, hideProgress) => { `; }; +/** + * Renders pie layout to display user's most frequently used programming languages. + * + * @param {Lang[]} langs Array of programming languages. + * @param {number} totalLanguageSize Total size of all languages. + * @returns {string} Compact layout card SVG object. + */ +const renderPieLayout = (langs, totalLanguageSize) => { + // Pie chart radius and center coordinates + const radius = 90; + const centerX = 150; + const centerY = 100; + + // Start angle for the pie chart parts + let startAngle = 0; + + // Start delay coefficient for the pie chart parts + let startDelayCoefficient = 1; + + // SVG paths + const paths = []; + + // Generate each pie chart part + for (const lang of langs) { + if (langs.length === 1) { + paths.push(` + + `); + break; + } + + const langSizePart = lang.size / totalLanguageSize; + const percentage = langSizePart * 100; + // Calculate the angle for the current part + const angle = langSizePart * 360; + + // Calculate the end angle + const endAngle = startAngle + angle; + + // Calculate the coordinates of the start and end points of the arc + const startPoint = polarToCartesian(centerX, centerY, radius, startAngle); + const endPoint = polarToCartesian(centerX, centerY, radius, endAngle); + + // Determine the large arc flag based on the angle + const largeArcFlag = angle > 180 ? 1 : 0; + + // Calculate delay + const delay = startDelayCoefficient * 100; + + // SVG arc markup + paths.push(` + + + + `); + + // Update the start angle for the next part + startAngle = endAngle; + // Update the start delay coefficient for the next part + startDelayCoefficient += 1; + } + + return ` + + + + ${paths.join("")} + + + + + ${createLanguageTextNode({ + langs, + totalSize: totalLanguageSize, + hideProgress: false, + })} + + + + `; +}; + /** * Creates the SVG paths for the language donut chart. * @@ -505,7 +610,10 @@ const renderTopLanguages = (topLangs, options = {}) => { let height = calculateNormalLayoutHeight(langs.length); let finalLayout = ""; - if (layout === "compact" || hide_progress == true) { + if (layout === "pie") { + height = calculatePieLayoutHeight(langs.length); + finalLayout = renderPieLayout(langs, totalLanguageSize); + } else if (layout === "compact" || hide_progress == true) { height = calculateCompactLayoutHeight(langs.length) + (hide_progress ? -25 : 0); @@ -580,6 +688,10 @@ const renderTopLanguages = (topLangs, options = {}) => { `, ); + if (layout === "pie") { + return card.render(finalLayout); + } + return card.render(` ${finalLayout} @@ -596,6 +708,7 @@ export { calculateCompactLayoutHeight, calculateNormalLayoutHeight, calculateDonutLayoutHeight, + calculatePieLayoutHeight, donutCenterTranslation, trimTopLanguages, renderTopLanguages, diff --git a/src/cards/types.d.ts b/src/cards/types.d.ts index fea5aa95..7945118c 100644 --- a/src/cards/types.d.ts +++ b/src/cards/types.d.ts @@ -39,7 +39,7 @@ export type TopLangOptions = CommonOptions & { hide_border: boolean; card_width: number; hide: string[]; - layout: "compact" | "normal" | "donut"; + layout: "compact" | "normal" | "donut" | "pie"; custom_title: string; langs_count: number; disable_animations: boolean; diff --git a/tests/renderTopLanguages.test.js b/tests/renderTopLanguages.test.js index e4f47c39..e4bc56de 100644 --- a/tests/renderTopLanguages.test.js +++ b/tests/renderTopLanguages.test.js @@ -9,6 +9,7 @@ import { calculateCompactLayoutHeight, calculateNormalLayoutHeight, calculateDonutLayoutHeight, + calculatePieLayoutHeight, donutCenterTranslation, trimTopLanguages, renderTopLanguages, @@ -40,12 +41,13 @@ const langs = { /** * Retrieve the language percentage from the donut chart SVG. + * * @param {string} d The SVG path element. * @param {number} centerX The center X coordinate of the donut chart. * @param {number} centerY The center Y coordinate of the donut chart. * @returns {number} The percentage of the language. */ -const langPercentFromSvg = (d, centerX, centerY) => { +const langPercentFromDonutLayoutSvg = (d, centerX, centerY) => { const dTmp = d .split(" ") .filter((x) => !isNaN(x)) @@ -58,6 +60,34 @@ const langPercentFromSvg = (d, centerX, centerY) => { return (endAngle - startAngle) / 3.6; }; +/** + * Retrieve the language percentage from the pie chart SVG. + * + * @param {string} d The SVG path element. + * @param {number} centerX The center X coordinate of the pie chart. + * @param {number} centerY The center Y coordinate of the pie chart. + * @returns {number} The percentage of the language. + */ +const langPercentFromPieLayoutSvg = (d, centerX, centerY) => { + const dTmp = d + .split(" ") + .filter((x) => !isNaN(x)) + .map((x) => parseFloat(x)); + const startAngle = cartesianToPolar( + centerX, + centerY, + dTmp[2], + dTmp[3], + ).angleInDegrees; + let endAngle = cartesianToPolar( + centerX, + centerY, + dTmp[9], + dTmp[10], + ).angleInDegrees; + return ((endAngle - startAngle) / 360) * 100; +}; + describe("Test renderTopLanguages helper functions", () => { it("getLongestLang", () => { const langArray = Object.values(langs); @@ -193,6 +223,20 @@ describe("Test renderTopLanguages helper functions", () => { expect(calculateDonutLayoutHeight(10)).toBe(375); }); + it("calculatePieLayoutHeight", () => { + expect(calculatePieLayoutHeight(0)).toBe(300); + expect(calculatePieLayoutHeight(1)).toBe(325); + expect(calculatePieLayoutHeight(2)).toBe(325); + expect(calculatePieLayoutHeight(3)).toBe(350); + expect(calculatePieLayoutHeight(4)).toBe(350); + expect(calculatePieLayoutHeight(5)).toBe(375); + expect(calculatePieLayoutHeight(6)).toBe(375); + expect(calculatePieLayoutHeight(7)).toBe(400); + expect(calculatePieLayoutHeight(8)).toBe(400); + expect(calculatePieLayoutHeight(9)).toBe(425); + expect(calculatePieLayoutHeight(10)).toBe(425); + }); + it("donutCenterTranslation", () => { expect(donutCenterTranslation(0)).toBe(-45); expect(donutCenterTranslation(1)).toBe(-45); @@ -466,7 +510,7 @@ describe("Test renderTopLanguages", () => { .filter((x) => !isNaN(x)) .map((x) => parseFloat(x)); const center = { x: d[7], y: d[7] }; - const HTMLLangPercent = langPercentFromSvg( + const HTMLLangPercent = langPercentFromDonutLayoutSvg( queryAllByTestId(document.body, "lang-donut")[0].getAttribute("d"), center.x, center.y, @@ -480,7 +524,7 @@ describe("Test renderTopLanguages", () => { "size", "40", ); - const javascriptLangPercent = langPercentFromSvg( + const javascriptLangPercent = langPercentFromDonutLayoutSvg( queryAllByTestId(document.body, "lang-donut")[1].getAttribute("d"), center.x, center.y, @@ -494,7 +538,7 @@ describe("Test renderTopLanguages", () => { "size", "20", ); - const cssLangPercent = langPercentFromSvg( + const cssLangPercent = langPercentFromDonutLayoutSvg( queryAllByTestId(document.body, "lang-donut")[2].getAttribute("d"), center.x, center.y, @@ -520,6 +564,81 @@ describe("Test renderTopLanguages", () => { "circle", ); }); + it("should render with layout pie", () => { + document.body.innerHTML = renderTopLanguages(langs, { layout: "pie" }); + + expect(queryByTestId(document.body, "header")).toHaveTextContent( + "Most Used Languages", + ); + + expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent( + "HTML 40.00%", + ); + expect(queryAllByTestId(document.body, "lang-pie")[0]).toHaveAttribute( + "size", + "40", + ); + + const d = queryAllByTestId(document.body, "lang-pie")[0] + .getAttribute("d") + .split(" ") + .filter((x) => !isNaN(x)) + .map((x) => parseFloat(x)); + const center = { x: d[0], y: d[1] }; + const HTMLLangPercent = langPercentFromPieLayoutSvg( + queryAllByTestId(document.body, "lang-pie")[0].getAttribute("d"), + center.x, + center.y, + ); + expect(HTMLLangPercent).toBeCloseTo(40); + + expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent( + "javascript 40.00%", + ); + expect(queryAllByTestId(document.body, "lang-pie")[1]).toHaveAttribute( + "size", + "40", + ); + const javascriptLangPercent = langPercentFromPieLayoutSvg( + queryAllByTestId(document.body, "lang-pie")[1].getAttribute("d"), + center.x, + center.y, + ); + expect(javascriptLangPercent).toBeCloseTo(40); + + expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent( + "css 20.00%", + ); + expect(queryAllByTestId(document.body, "lang-pie")[2]).toHaveAttribute( + "size", + "20", + ); + const cssLangPercent = langPercentFromPieLayoutSvg( + queryAllByTestId(document.body, "lang-pie")[2].getAttribute("d"), + center.x, + center.y, + ); + expect(cssLangPercent).toBeCloseTo(20); + + expect(HTMLLangPercent + javascriptLangPercent + cssLangPercent).toBe(100); + + // Should render full pie (circle) if one language is 100%. + document.body.innerHTML = renderTopLanguages( + { HTML: langs.HTML }, + { layout: "pie" }, + ); + expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent( + "HTML 100.00%", + ); + expect(queryAllByTestId(document.body, "lang-pie")[0]).toHaveAttribute( + "size", + "100", + ); + expect(queryAllByTestId(document.body, "lang-pie")).toHaveLength(1); + expect(queryAllByTestId(document.body, "lang-pie")[0].tagName).toBe( + "circle", + ); + }); it("should render a translated title", () => { document.body.innerHTML = renderTopLanguages(langs, { locale: "cn" }); From d59a80599f781fd9af4731236519a2e551e97590 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov Date: Sat, 13 May 2023 14:20:18 +0300 Subject: [PATCH 23/25] Docs: add missing top languages card layouts. (#2717) --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index d1e56775..f59342f3 100644 --- a/readme.md +++ b/readme.md @@ -303,7 +303,7 @@ You can provide multiple comma-separated values in the bg_color option to render - `hide` - Hide the languages specified from the card _(Comma-separated values)_. Default: `[] (blank array)`. - `hide_title` - _(boolean)_. Default: `false`. -- `layout` - Switch between two available layouts `default` & `compact`. Default: `default`. +- `layout` - Switch between four available layouts `normal` & `compact` & `donut` & `pie`. Default: `normal`. - `card_width` - Set the card's width manually _(number)_. Default `300`. - `langs_count` - Show more languages on the card, between 1-10 _(number)_. Default `5`. - `exclude_repo` - Exclude specified repositories _(Comma-separated values)_. Default: `[] (blank array)`. From 6d45f89c9efcccc995a1912695cbe269f0e5c289 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov Date: Sat, 13 May 2023 14:21:06 +0300 Subject: [PATCH 24/25] Top languages card render test: move repeated code into helper function (#2718) --- tests/renderTopLanguages.test.js | 39 +++++++++++++++++--------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/tests/renderTopLanguages.test.js b/tests/renderTopLanguages.test.js index e4bc56de..a8bc873a 100644 --- a/tests/renderTopLanguages.test.js +++ b/tests/renderTopLanguages.test.js @@ -39,6 +39,19 @@ const langs = { }, }; +/** + * Retrieve number array from SVG path definition string. + * + * @param {string} d SVG path definition string. + * @return {number[]} Resulting numbers array. + */ +const getNumbersFromSvgPathDefinitionAttribute = (d) => { + return d + .split(" ") + .filter((x) => !isNaN(x)) + .map((x) => parseFloat(x)); +}; + /** * Retrieve the language percentage from the donut chart SVG. * @@ -48,10 +61,7 @@ const langs = { * @returns {number} The percentage of the language. */ const langPercentFromDonutLayoutSvg = (d, centerX, centerY) => { - const dTmp = d - .split(" ") - .filter((x) => !isNaN(x)) - .map((x) => parseFloat(x)); + const dTmp = getNumbersFromSvgPathDefinitionAttribute(d); const endAngle = cartesianToPolar(centerX, centerY, dTmp[0], dTmp[1]).angleInDegrees + 90; let startAngle = @@ -69,10 +79,7 @@ const langPercentFromDonutLayoutSvg = (d, centerX, centerY) => { * @returns {number} The percentage of the language. */ const langPercentFromPieLayoutSvg = (d, centerX, centerY) => { - const dTmp = d - .split(" ") - .filter((x) => !isNaN(x)) - .map((x) => parseFloat(x)); + const dTmp = getNumbersFromSvgPathDefinitionAttribute(d); const startAngle = cartesianToPolar( centerX, centerY, @@ -504,11 +511,9 @@ describe("Test renderTopLanguages", () => { "size", "40", ); - const d = queryAllByTestId(document.body, "lang-donut")[0] - .getAttribute("d") - .split(" ") - .filter((x) => !isNaN(x)) - .map((x) => parseFloat(x)); + const d = getNumbersFromSvgPathDefinitionAttribute( + queryAllByTestId(document.body, "lang-donut")[0].getAttribute("d"), + ); const center = { x: d[7], y: d[7] }; const HTMLLangPercent = langPercentFromDonutLayoutSvg( queryAllByTestId(document.body, "lang-donut")[0].getAttribute("d"), @@ -579,11 +584,9 @@ describe("Test renderTopLanguages", () => { "40", ); - const d = queryAllByTestId(document.body, "lang-pie")[0] - .getAttribute("d") - .split(" ") - .filter((x) => !isNaN(x)) - .map((x) => parseFloat(x)); + const d = getNumbersFromSvgPathDefinitionAttribute( + queryAllByTestId(document.body, "lang-pie")[0].getAttribute("d"), + ); const center = { x: d[0], y: d[1] }; const HTMLLangPercent = langPercentFromPieLayoutSvg( queryAllByTestId(document.body, "lang-pie")[0].getAttribute("d"), From 7ec1a76c6590a8c9682aa71d81ca6bef0405a078 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov Date: Wed, 17 May 2023 09:37:25 +0300 Subject: [PATCH 25/25] Docs (translations): add missing top languages card layouts. (#2728) --- docs/readme_cn.md | 2 +- docs/readme_de.md | 2 +- docs/readme_es.md | 2 +- docs/readme_fr.md | 2 +- docs/readme_it.md | 2 +- docs/readme_ja.md | 2 +- docs/readme_kr.md | 2 +- docs/readme_nl.md | 2 +- docs/readme_np.md | 2 +- docs/readme_pt-BR.md | 2 +- docs/readme_tr.md | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/readme_cn.md b/docs/readme_cn.md index d6487b66..3bd2c149 100644 --- a/docs/readme_cn.md +++ b/docs/readme_cn.md @@ -171,7 +171,7 @@ dark, radical, merko, gruvbox, tokyonight, onedark, cobalt, synthwave, highcontr - `hide` - 从卡片中隐藏指定语言 _(Comma seperated values)_ - `hide_title` - _(boolean)_ -- `layout` - 在两个可用布局 `default` & `compact` 间切换 +- `layout` - 提供四種佈局 `normal` & `compact` & `donut` & `pie` 间切换 - `card_width` - 手动设置卡片的宽度 _(number)_ > :warning: **重要:** diff --git a/docs/readme_de.md b/docs/readme_de.md index 1ad0d26a..a94e8c2c 100644 --- a/docs/readme_de.md +++ b/docs/readme_de.md @@ -161,7 +161,7 @@ Du kannst mehrere, mit Kommas separierte, Werte in der bg_color Option angeben, - `hide` - Verbirgt die angegebenen Sprachen von der Karte _(Komma separierte Werte)_ - `hide_title` - _(Boolean)_ -- `layout` - Wechsel zwischen den zwei verfügbaren Layouts `default` & `compact` +- `layout` - Wechseln Sie zwischen den vier verfügbaren Layouts `normal` & `compact` & `donut` & `pie` - `card_width` - Lege die Breite der Karte manuell fest _(Zahl)_ > :warning: **Wichtig:** diff --git a/docs/readme_es.md b/docs/readme_es.md index 5672027a..a353502c 100644 --- a/docs/readme_es.md +++ b/docs/readme_es.md @@ -175,7 +175,7 @@ Puedes pasar mútliples valores separados por coma en la opción `bg_color` para - `hide` - Oculta de la tarjeta los lenguajes especificados _(valores separados por comas)_ - `hide_title` - _(booleano)_ -- `layout` - Cambia entre los dos diseños disponibles `default` & `compact` +- `layout` - Cambiar entre los cuatro diseños disponibles `normal` & `compact` & `donut` & `pie` - `card_width` - Establece el ancho de la tarjeta manualmente _(número)_ - `langs_count` - Muestra más lenguajes en la tarjeta, entre 1-10, por defecto 5 _(número)_ - `exclude_repo` - Excluye los repositorios especificados _(valores separados por comas)_ diff --git a/docs/readme_fr.md b/docs/readme_fr.md index ce0d3d49..427f2971 100644 --- a/docs/readme_fr.md +++ b/docs/readme_fr.md @@ -171,7 +171,7 @@ Vous pouvez fournir plusieurs valeurs (suivie d'une virgule) dans l'option bg_co - `hide` - Masquer les langages spécifiés sur la carte _(Comma seperated values)_ - `hide_title` - Masquer le titre _(boolean)_ -- `layout` - Alterner entre 2 mise en page `default` & `compact` +- `layout` - Alterner entre 4 mise en page `normal` & `compact` & `donut` & `pie` - `card_width` - Fixer la largeur de la carte manuellement _(number)_ > :warning: **Important:** diff --git a/docs/readme_it.md b/docs/readme_it.md index e54af7dc..4418c333 100644 --- a/docs/readme_it.md +++ b/docs/readme_it.md @@ -171,7 +171,7 @@ Puoi fornire valori separati da virgola nel parametro bg_color per creare un gra - `hide` - Nasconde un linguaggio specifico _(valori separati da virgola)_ - `hide_title` - Nasconde il titolo _(booleano)_ -- `layout` - Specifica il tipo di layout, `default` (esteso) o `compact` (compatto) +- `layout` - Specificare il tipo di layout, `normal` (esteso), `compact` (compatto), `donut` (ciambella) e `pie` (torta) - `card_width` - Specifica il valore della larghezza _(numero)_ > :warning: **Importante:** diff --git a/docs/readme_ja.md b/docs/readme_ja.md index 2c2def7f..e6629ae2 100644 --- a/docs/readme_ja.md +++ b/docs/readme_ja.md @@ -174,7 +174,7 @@ bg_color オプションで複数のカンマ区切りの値を指定してグ - `hide` - 特定の言語を隠す _(カンマ区切りで指定)_ - `hide_title` - _(boolean)_ -- `layout` - `default` か `compact` のいずれかのレイアウトに切り替える +- `layout` - `normal` & `compact` & `donut` & `pie` のいずれかのレイアウトに切り替える - `card_width` - カードの横幅 _(number)_ - `langs_count` - 表示される言語の数 _(1 ~ 10, 初期値 5)_ - `exclude_repo` - 指定されたリポジトリを除外する _(カンマ区切りで指定)_ diff --git a/docs/readme_kr.md b/docs/readme_kr.md index 0d7bced3..28207af5 100644 --- a/docs/readme_kr.md +++ b/docs/readme_kr.md @@ -189,7 +189,7 @@ dark, radical, merko, gruvbox, tokyonight, onedark, cobalt, synthwave, highcontr - `hide` - 카드에서 특정 언어 제외 _(Comma-separated values)_ - `hide_title` - 타이틀 제외 _(boolean)_ -- `layout` - 사용 가능한 두 가지 값, `default` & `compact` 중 표시 형태 선택 +- `layout` - 4가지 값 사용 가능, `normal` & `compact` & `donut` & `pie` 중 표시 형태 선택 - `card_width` - 카드 너비 직접 설정 _(number)_ - `langs_count` - 카드에 표시할 언어의 수 (1-10 사이, 기본 값 : 5) _(number)_ - `exclude_repo` - 통계에 제외할 저장소 지정 _(Comma-separated values)_ diff --git a/docs/readme_nl.md b/docs/readme_nl.md index f70828f1..04ba9cf4 100644 --- a/docs/readme_nl.md +++ b/docs/readme_nl.md @@ -176,7 +176,7 @@ Je kan meerdere komma verdeelde waarden in de bg_color optie geven om een kleure - `hide` - Verbergt specifieke talen van de kaart _(komma gescheiden waardes)_ - `hide_title` - _(boolean)_ -- `layout` - Keuze voor de twee beschikbare layouts `default` & `compact` +- `layout` - Keuze uit de vier beschikbare indelingen `normal` & `compact` & `donut` & `pie` - `card_width` - Stelt de breedte van de kaart handmatig in. _(nummer)_ - `langs_count` - Laat meer talen op de kaart zien, waarde tussen 1-10, staat standaard op to 5 _(nummer)_ - `exclude_repo` - Verbergt specifieke repositories _(komma gescheiden waardes)_ diff --git a/docs/readme_np.md b/docs/readme_np.md index b322545c..088039dd 100644 --- a/docs/readme_np.md +++ b/docs/readme_np.md @@ -174,7 +174,7 @@ You can provide multiple comma-separated values in bg_color option to render a g - `hide` - Hide the languages specified from the card _(Comma-separated values)_ - `hide_title` - _(boolean)_ -- `layout` - Switch between two available layouts `default` & `compact` +- `layout` - Switch between four available layouts `normal` & `compact` & `donut` & `pie`. Default: `normal`. - `card_width` - Set the card's width manually _(number)_ - `langs_count` - Show more languages on the card, between 1-10, defaults to 5 _(number)_ - `exclude_repo` - Exclude specified repositories _(Comma-separated values)_ diff --git a/docs/readme_pt-BR.md b/docs/readme_pt-BR.md index cf6eeedc..bcb58f2b 100644 --- a/docs/readme_pt-BR.md +++ b/docs/readme_pt-BR.md @@ -164,7 +164,7 @@ Personalize a aparência do seu `Stats Card` ou `Repo Card` da maneira que desej - `hide` - Oculta linguagens específicas _(Valores separados por vírgulas)_ - `hide_title` - Oculta o título _(boolean)_ -- `layout` - Alterna entre os dois layouts disponíveis `default` & `compact` +- `layout` - Alterna entre os quatro layouts disponíveis `normal` & `compact` & `donut` & `pie` - `card_width` - Define a largura do cartão manualmente _(number)_ > :warning: **Importante:** diff --git a/docs/readme_tr.md b/docs/readme_tr.md index fe5aff73..d08a00cb 100644 --- a/docs/readme_tr.md +++ b/docs/readme_tr.md @@ -176,7 +176,7 @@ bg_color içerisinde birden fazla rengi gradient olarak göstermek için virgül - `hide` - Belirli bir dili listede gizler _(Virgül ile ayırılmış değerlerle)_ - `hide_title` - _(boolean)_ -- `layout` - Uygun olan iki tasarım / layout arasında değişiklik yapar `default` & `compact` +- `layout` - Dört uygun tasarım / düzen arasında geçiş `normal` & `compact` & `donut` & `pie` - `card_width` - Kartın genişliğini manuel olarak belirler _(number)_ - `langs_count` - 1-10 arasında istediğiniz kadar dil gösterebilirsiniz. Varsayılan: 5 _(number)_ - `exclude_repo` - Belirli repoları listeden çıkartır _(Virgül ile ayırılmış değerlerle)_