Unverified Commit 11757dba authored by Leo Hanisch's avatar Leo Hanisch Committed by GitHub
Browse files

feat: multiline text in repo description (#206)



* Enable multi line repo card descriptions

* Adjust unit tests to multi line descriptions

* refactor: refactored code & fixed tests

* style: minor height adjustments

* chore: codecov stuck

Co-authored-by: default avataranuraghazra <hazru.anurag@gmail.com>
parent 68198d8b
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -21,7 +21,8 @@
  },
  "dependencies": {
    "dotenv": "^8.2.0",
    "emoji-name-map": "^1.2.8"
    "emoji-name-map": "^1.2.8",
    "word-wrap": "^1.2.3"
  },
  "husky": {
    "hooks": {
+28 −17
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ const {
  encodeHTML,
  getCardColors,
  FlexLayout,
  wrapTextMultiline,
} = require("../src/utils");
const icons = require("./icons");
const toEmoji = require("emoji-name-map");
@@ -31,7 +32,6 @@ const renderRepoCard = (repo, options = {}) => {
  const langName = (primaryLanguage && primaryLanguage.name) || "Unspecified";
  const langColor = (primaryLanguage && primaryLanguage.color) || "#333";

  const height = 120;
  const shiftText = langName.length > 15 ? 0 : 30;

  let desc = description || "No description provided";
@@ -41,9 +41,12 @@ const renderRepoCard = (repo, options = {}) => {
    return toEmoji.get(emoji) || "";
  });

  if (desc.length > 55) {
    desc = `${desc.slice(0, 55)}..`;
  }
  const multiLineDescription = wrapTextMultiline(desc);
  const descriptionLines = multiLineDescription.length;
  const lineHeight = 10;

  const height =
    (descriptionLines > 1 ? 120 : 110) + descriptionLines * lineHeight;

  // returns theme based colors with proper overrides and defaults
  const { titleColor, textColor, iconColor, bgColor } = getCardColors({
@@ -74,7 +77,7 @@ const renderRepoCard = (repo, options = {}) => {

  const svgLanguage = primaryLanguage
    ? `
    <g data-testid="primary-lang" transform="translate(30, 100)">
    <g data-testid="primary-lang" transform="translate(30, 0)">
      <circle data-testid="lang-color" cx="0" cy="-5" r="6" fill="${langColor}" />
      <text data-testid="lang-name" class="gray" x="15">${langName}</text>
    </g>
@@ -125,14 +128,22 @@ const renderRepoCard = (repo, options = {}) => {
          : ""
      }

      <text class="description" x="25" y="70">${encodeHTML(desc)}</text>
      <text class="description" x="25" y="50">
        ${multiLineDescription
          .map((line) => `<tspan dy="1.2em" x="25">${encodeHTML(line)}</tspan>`)
          .join("")}
      </text>

      <g transform="translate(0, ${height - 20})">
        ${svgLanguage}

      <g transform="translate(${primaryLanguage ? 155 - shiftText : 25}, 100)">
        <g 
          data-testid="star-fork-group" 
          transform="translate(${primaryLanguage ? 155 - shiftText : 25}, 0)"
        >
          ${FlexLayout({ items: [svgStars, svgForks], gap: 65 }).join("")}
        </g>

      </g>
    </svg>
  `;
};
+21 −2
Original line number Diff line number Diff line
const axios = require("axios");
const wrap = require("word-wrap");
const themes = require("../themes");

const renderError = (message, secondaryMessage = "") => {
@@ -127,10 +128,27 @@ function getCardColors({
  return { titleColor, iconColor, textColor, bgColor };
}

const fn = () => {};
function wrapTextMultiline(text, width = 60, maxLines = 3) {
  const wrapped = wrap(encodeHTML(text), { width })
    .split("\n") // Split wrapped lines to get an array of lines
    .map((line) => line.trim()); // Remove leading and trailing whitespace of each line

  const lines = wrapped.slice(0, maxLines); // Only consider maxLines lines

  // Add "..." to the last line if the text exceeds maxLines
  if (wrapped.length > maxLines) {
    lines[maxLines - 1] += "...";
  }

  // Remove empty lines if text fits in less than maxLines lines
  const multiLineText = lines.filter(Boolean);
  return multiLineText;
}

const noop = () => {};
// return console instance based on the environment
const logger =
  process.env.NODE_ENV !== "test" ? console : { log: fn, error: fn };
  process.env.NODE_ENV !== "test" ? console : { log: noop, error: noop };

const CONSTANTS = {
  THIRTY_MINUTES: 1800,
@@ -150,6 +168,7 @@ module.exports = {
  FlexLayout,
  getCardColors,
  clampValue,
  wrapTextMultiline,
  logger,
  CONSTANTS,
};
+13 −9
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ describe("Test renderRepoCard", () => {
    expect(header).toHaveTextContent("convoychat");
    expect(header).not.toHaveTextContent("anuraghazra");
    expect(document.getElementsByClassName("description")[0]).toHaveTextContent(
      "Help us take over the world! React + TS + GraphQL Chat .."
      "Help us take over the world! React + TS + GraphQL Chat App"
    );
    expect(queryByTestId(document.body, "stargazers")).toHaveTextContent("38k");
    expect(queryByTestId(document.body, "forkcount")).toHaveTextContent("100");
@@ -55,12 +55,16 @@ describe("Test renderRepoCard", () => {
    document.body.innerHTML = renderRepoCard({
      ...data_repo.repository,
      description:
        "Very long long long long long long long long text it should trim it",
        "The quick brown fox jumps over the lazy dog is an English-language pangram—a sentence that contains all of the letters of the English alphabet",
    });

    expect(document.getElementsByClassName("description")[0]).toHaveTextContent(
      "Very long long long long long long long long text it sh.."
    );
    expect(
      document.getElementsByClassName("description")[0].children[0].textContent
    ).toBe("The quick brown fox jumps over the lazy dog is an");

    expect(
      document.getElementsByClassName("description")[0].children[1].textContent
    ).toBe("English-language pangram—a sentence that contains all");

    // Should not trim
    document.body.innerHTML = renderRepoCard({
@@ -95,9 +99,9 @@ describe("Test renderRepoCard", () => {
    });

    expect(queryByTestId(document.body, "primary-lang")).toBeInTheDocument();
    expect(document.getElementsByTagName("g")[1]).toHaveAttribute(
    expect(queryByTestId(document.body, "star-fork-group")).toHaveAttribute(
      "transform",
      "translate(155, 100)"
      "translate(155, 0)"
    );

    // Small lang
@@ -109,9 +113,9 @@ describe("Test renderRepoCard", () => {
      },
    });

    expect(document.getElementsByTagName("g")[1]).toHaveAttribute(
    expect(queryByTestId(document.body, "star-fork-group")).toHaveAttribute(
      "transform",
      "translate(125, 100)"
      "translate(125, 0)"
    );
  });

+26 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ const {
  renderError,
  FlexLayout,
  getCardColors,
  wrapTextMultiline,
} = require("../src/utils");

const { queryByTestId } = require("@testing-library/dom");
@@ -108,3 +109,28 @@ describe("Test utils.js", () => {
    });
  });
});

describe("wrapTextMultiline", () => {
  it("should not wrap small texts", () => {
    {
      let multiLineText = wrapTextMultiline("Small text should not wrap");
      expect(multiLineText).toEqual(["Small text should not wrap"]);
    }
  });
  it("should wrap large texts", () => {
    let multiLineText = wrapTextMultiline(
      "Hello world long long long text",
      20,
      3
    );
    expect(multiLineText).toEqual(["Hello world long", "long long text"]);
  });
  it("should wrap large texts and limit max lines", () => {
    let multiLineText = wrapTextMultiline(
      "Hello world long long long text",
      10,
      2
    );
    expect(multiLineText).toEqual(["Hello", "world long..."]);
  });
});