Repository: nodejs/nodejs.dev (ts)

Issue:
Releases page
## Summary
Create a /release page that has the same content as https://github.com/nodejs/nodejs.org/edit/master/locale/en/about/releases.md

The table data should load from https://github.com/nodejs/Release/blob/main/schedule.json

## Related issue
https://github.com/nodejs/nodejs.dev/issues/1339 

Pull request:
Add releases page
## Description

~This PR is WIP until #1339 will be finished with release data retrieving~

**Update 2 June:**
PR is ready for review:
![image](https://user-images.githubusercontent.com/5843270/120538363-17461100-c3ef-11eb-9d90-e64d5e3a0279.png)


## Related Issues

Closes #1350

Patch:
diff --git a/content/about/releases.md b/content/about/releases.md
new file mode 100644
index 00000000..9e19f774
--- /dev/null
+++ b/content/about/releases.md
@@ -0,0 +1,13 @@
+---
+title: Releases
+description: "Releases | Node.js"
+authors: ZYSzys, nstanard,  mikeal, fhemberger, garybernhardt, piepmatz, boneskull, bjb568
+category: releases
+---
+
+Major Node.js versions enter _Current_ release status for six months, which gives library authors time to add support for them.
+After six months, odd-numbered releases (9, 11, etc.) become unsupported, and even-numbered releases (10, 12, etc.) move to _Active LTS_ status and are ready for general use.
+_LTS_ release status is "long-term support", which typically guarantees that critical bugs will be fixed for a total of 30 months.
+Production applications should only use _Active LTS_ or _Maintenance LTS_ releases.
+
+![Releases](https://raw.githubusercontent.com/nodejs/Release/master/schedule.svg?sanitize=true)
diff --git a/src/components/AnimatedPlaceholder/index.tsx b/src/components/AnimatedPlaceholder/index.tsx
index 65949f3c..882906a4 100644
--- a/src/components/AnimatedPlaceholder/index.tsx
+++ b/src/components/AnimatedPlaceholder/index.tsx
@@ -2,7 +2,7 @@ import React from 'react';
 import './AnimatedPlaceholder.scss';
 
 interface Props {
-  children?: JSX.Element;
+  children?: React.ReactNode;
 }
 
 const AnimatedPlaceholder = ({ children }: Props): JSX.Element => {
diff --git a/src/components/Article/index.tsx b/src/components/Article/index.tsx
index c5ada40a..dd81ee6f 100644
--- a/src/components/Article/index.tsx
+++ b/src/components/Article/index.tsx
@@ -18,6 +18,7 @@ interface Props {
   previous?: PaginationInfo;
   blog?: boolean;
   date?: string;
+  children?: React.ReactNode;
 }
 
 const NAV_HEIGHT = 72;
@@ -33,6 +34,7 @@ const Article = ({
   authors,
   blog,
   date,
+  children,
 }: Props): JSX.Element => {
   const element = React.useRef<HTMLDivElement>(null);
 
@@ -85,18 +87,16 @@ const Article = ({
     );
 
     if (currentElementRef && currentElementRef.current) {
-      Array.from(currentElementRef.current.children).forEach(
-        (children): void => {
-          observer.observe(children);
-        }
-      );
+      Array.from(currentElementRef.current.children).forEach((child): void => {
+        observer.observe(child);
+      });
     }
 
     return (): void => {
       if (currentElementRef && currentElementRef.current) {
         Array.from(currentElementRef.current.children).forEach(
-          (children): void => {
-            observer.unobserve(children);
+          (child): void => {
+            observer.unobserve(child);
           }
         );
       }
@@ -114,6 +114,9 @@ const Article = ({
       )}
       {/* eslint-disable-next-line react/no-danger */}
       <div ref={element} dangerouslySetInnerHTML={{ __html: html }} />
+      {children && (
+        <div className="article-reader__additional-content">{children}</div>
+      )}
       {!blog && <AuthorsList authors={authors as string[]} />}
       {!blog && <EditLink relativePath={relativePath} editPath={editPath} />}
       {!blog && <Pagination previous={previous} next={next} />}
diff --git a/src/pages/releases.tsx b/src/pages/releases.tsx
new file mode 100644
index 00000000..7c57e876
--- /dev/null
+++ b/src/pages/releases.tsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import { graphql } from 'gatsby';
+import { Page } from '../types';
+import Layout from '../components/Layout';
+import Article from '../components/Article';
+import Footer from '../components/Footer';
+import '../styles/article-reader.scss';
+import DownloadTable from '../components/DownloadReleases/DownloadTable';
+import { useReleaseData } from '../hooks/useReleaseHistory';
+
+export default function ReleasesPage({ data }: Page): JSX.Element {
+  const { title, description } = data.page.frontmatter;
+  const { html, tableOfContents } = data.page;
+  const { authors } = data.page.fields;
+  const { releaseData } = useReleaseData();
+
+  return (
+    <>
+      <Layout title={title} description={description} showFooter={false}>
+        <main className="centered-container">
+          <Article
+            title={title}
+            html={html}
+            tableOfContents={tableOfContents}
+            authors={authors}
+            editPath="content/about/releases.md"
+          >
+            <DownloadTable releases={releaseData} />
+          </Article>
+        </main>
+      </Layout>
+      <Footer />
+    </>
+  );
+}
+
+export const query = graphql`
+  query {
+    page: markdownRemark(fields: { slug: { eq: "releases" } }) {
+      html
+      tableOfContents(absolute: false, pathToSlugField: "frontmatter.path")
+      frontmatter {
+        title
+        description
+      }
+      fields {
+        authors
+      }
+    }
+  }
+`;
diff --git a/src/styles/layout.scss b/src/styles/layout.scss
index 7ed743e9..97e374f2 100644
--- a/src/styles/layout.scss
+++ b/src/styles/layout.scss
@@ -345,6 +345,10 @@ select {
     border: none;
     border-bottom: 0.4rem solid var(--black2);
   }
+
+  img {
+    max-width: 100%;
+  }
 }
 
 /* sr-only for desktop */


Test patch:
diff --git a/src/components/Article/__tests__/__snapshots__/article.test.tsx.snap b/src/components/Article/__tests__/__snapshots__/article.test.tsx.snap
index 18669c8e..ac36084c 100644
--- a/src/components/Article/__tests__/__snapshots__/article.test.tsx.snap
+++ b/src/components/Article/__tests__/__snapshots__/article.test.tsx.snap
@@ -259,3 +259,125 @@ exports[`Article component renders correctly in case html ref is null 1`] = `
   </article>
 </div>
 `;
+
+exports[`Article component should accept and render child components 1`] = `
+<div>
+  <article
+    class="article-reader"
+  >
+    <h1
+      class="article-reader__headline"
+    >
+      test-title
+    </h1>
+    <details
+      class="toc"
+    >
+      <summary>
+        <h6>
+          TABLE OF CONTENTS
+        </h6>
+      </summary>
+      <div>
+        test-description
+      </div>
+    </details>
+    <div>
+      <span>
+        Test html
+      </span>
+    </div>
+    <div
+      class="article-reader__additional-content"
+    >
+      <div>
+        test child JSX
+      </div>
+    </div>
+    <ul
+      class="list"
+    >
+      <h5>
+        Contributors
+      </h5>
+      <li>
+        <a
+          class="link"
+          href="https://github.com/test-user1"
+          rel="noopener noreferrer"
+          style="margin-left: 0px;"
+          target="_blank"
+          title="test-user1"
+        >
+          <img
+            alt="test-user1"
+            class="img"
+            src="https://github.com/test-user1.png?size=60"
+          />
+        </a>
+      </li>
+      <li>
+        <a
+          class="link"
+          href="https://github.com/test-user2"
+          rel="noopener noreferrer"
+          target="_blank"
+          title="test-user2"
+        >
+          <img
+            alt="test-user2"
+            class="img"
+            src="https://github.com/test-user2.png?size=60"
+          />
+        </a>
+      </li>
+    </ul>
+    <div
+      class="edit"
+    >
+      <a
+        class="link"
+        href="https://github.com/nodejs/nodejs.dev/edit/main/src/documentation/test-path"
+      >
+        <span>
+          Edit this page on GitHub
+        </span>
+         
+        <svg
+          class="icon"
+          fill="currentColor"
+          height="1em"
+          viewBox="0 0 40 40"
+          width="1em"
+        >
+          <path
+            d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"
+          />
+        </svg>
+      </a>
+    </div>
+    <ul
+      class="pagination"
+    >
+      <li>
+        <a
+          class="link"
+          href="/learn/test-slug"
+          rel="prev"
+        >
+          ←   Prev
+        </a>
+      </li>
+      <li>
+        <a
+          class="link"
+          href="/learn/test-slug"
+          rel="next"
+        >
+          Next   →
+        </a>
+      </li>
+    </ul>
+  </article>
+</div>
+`;
diff --git a/src/components/Article/__tests__/article.test.tsx b/src/components/Article/__tests__/article.test.tsx
index 23a445e9..9838ba5e 100644
--- a/src/components/Article/__tests__/article.test.tsx
+++ b/src/components/Article/__tests__/article.test.tsx
@@ -42,6 +42,9 @@ describe('Article component', () => {
     window.IntersectionObserver = intersectionObserverOriginal;
     window.scrollTo = scrollToOriginal;
     window.history.replaceState = replaceStateOriginal;
+
+    // clean-up history state
+    window.history.replaceState({}, '', null);
   });
 
   it('renders correctly', () => {
@@ -302,4 +305,25 @@ describe('Article component', () => {
 
     expect(window.scrollTo).toHaveBeenCalledWith({ top: 1 });
   });
+
+  it('should accept and render child components', () => {
+    const { title, description, html, next, previous, authors, relativePath } =
+      getArticleProps();
+
+    const { container } = render(
+      <Article
+        title={title}
+        tableOfContents={description}
+        html={html}
+        next={next}
+        previous={previous}
+        authors={authors}
+        relativePath={relativePath}
+      >
+        <div>test child JSX</div>
+      </Article>
+    );
+
+    expect(container).toMatchSnapshot();
+  });
 });
diff --git a/test/__fixtures__/page.tsx b/test/__fixtures__/page.tsx
index 685a9d28..49ba78b0 100644
--- a/test/__fixtures__/page.tsx
+++ b/test/__fixtures__/page.tsx
@@ -9,7 +9,10 @@ import {
   Page,
 } from '../../src/types';
 
-import { ReleaseData } from '../../src/hooks/useReleaseHistory';
+import {
+  NodeReleaseData,
+  ReleaseData,
+} from '../../src/hooks/useReleaseHistory';
 
 export const createPaginationInfo = (): PaginationInfo =>
   ({
@@ -100,6 +103,28 @@ export const createReleaseData = (): ReleaseData[] =>
     },
   ] as ReleaseData[];
 
+export const createNodeReleaseData = (): NodeReleaseData[] =>
+  [
+    {
+      endOfLife: '2022-04-30',
+      maintenanceLTSStart: '2020-11-30',
+      activeLTSStart: '2019-10-21',
+      codename: 'Erbium',
+      initialRelease: '2019-04-23',
+      release: 'v12',
+      status: 'Maintenance LTS',
+    },
+    {
+      endOfLife: '2023-04-30',
+      maintenanceLTSStart: '2021-10-19',
+      activeLTSStart: '2020-10-27',
+      codename: 'Fermium',
+      initialRelease: '2020-04-21',
+      release: 'v14',
+      status: 'Active LTS',
+    },
+  ] as NodeReleaseData[];
+
 export const createLearnPageContext = (): LearnPageContext =>
   ({
     slug: 'test-slug',
@@ -176,11 +201,11 @@ export const createBlogPageData = (): BlogPageData => ({
   },
 });
 
-export const createTrademarkData = (): Page => ({
+export const createGeneralPageData = (): Page => ({
   data: {
     page: {
       fields: {
-        authors: ['palak'],
+        authors: ['author-mock'],
       },
       frontmatter: {
         description: 'Mock Description',
diff --git a/test/pages/__snapshots__/releases.test.tsx.snap b/test/pages/__snapshots__/releases.test.tsx.snap
new file mode 100644
index 00000000..a900e995
--- /dev/null
+++ b/test/pages/__snapshots__/releases.test.tsx.snap
@@ -0,0 +1,467 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Releases page renders correctly 1`] = `
+<div>
+  <div
+    class="layout-container"
+  >
+    <nav
+      aria-label="Primary"
+      class="nav"
+    >
+      <div
+        class="nav__container"
+      >
+        <div
+          class="nav__startwrapper"
+        >
+          <a
+            href="/"
+          >
+            <div
+              class="logo"
+            >
+              <img
+                alt="Node.js"
+                class="nav__logo light-mode-only"
+                src="test-file-stub"
+              />
+              <img
+                alt="Node.js"
+                class="nav__logo dark-mode-only"
+                src="test-file-stub"
+              />
+            </div>
+          </a>
+        </div>
+        <ul
+          class="nav__tabs__container"
+        >
+          <li
+            class="nav__tabs"
+          >
+            <a
+              class="activeStyleTab"
+              href="/learn"
+            >
+              Learn
+            </a>
+          </li>
+          <li
+            class="nav__tabs"
+          >
+            <a
+              class="activeStyleTab"
+              href="https://nodejs.org/en/docs/"
+              rel="noopener noreferrer"
+              target="_blank"
+            >
+              Documentation
+            </a>
+          </li>
+          <li
+            class="nav__tabs"
+          >
+            <a
+              class="activeStyleTab"
+              href="/download"
+            >
+              Download
+            </a>
+          </li>
+          <li
+            class="nav__tabs"
+          >
+            <a
+              class="activeStyleTab"
+              href="https://nodejs.org/en/get-involved/"
+              rel="noopener noreferrer"
+              target="_blank"
+            >
+              Community
+            </a>
+          </li>
+        </ul>
+        <div
+          class="nav__endwrapper"
+        >
+          <ul
+            class="right-container"
+          >
+            <li
+              class="nav__tabs nav__tabs--right"
+            >
+              <button
+                class="dark-mode-toggle"
+                type="button"
+              >
+                <span
+                  class="sr-only"
+                >
+                  Toggle Dark Mode
+                </span>
+                <i
+                  class="material-icons light-mode-only"
+                >
+                  nights_stay
+                </i>
+                <i
+                  class="material-icons dark-mode-only"
+                >
+                  wb_sunny
+                </i>
+              </button>
+            </li>
+            <li
+              class="nav__tabs"
+            >
+              <a
+                href="https://github.com/nodejs/nodejs.dev"
+                rel="noopener noreferrer"
+                target="_blank"
+              >
+                <span
+                  class="sr-only"
+                >
+                  GitHub
+                </span>
+                <svg
+                  height="2.2rem"
+                  style="fill: var(--color-text-accent);"
+                  viewBox="0 0 438.549 438.549"
+                  width="2.2rem"
+                >
+                  <path
+                    d="M409.132,114.573c-19.608-33.596-46.205-60.194-79.798-79.8C295.736,15.166,259.057,5.365,219.271,5.365   c-39.781,0-76.472,9.804-110.063,29.408c-33.596,19.605-60.192,46.204-79.8,79.8C9.803,148.168,0,184.854,0,224.63   c0,47.78,13.94,90.745,41.827,128.906c27.884,38.164,63.906,64.572,108.063,79.227c5.14,0.954,8.945,0.283,11.419-1.996   c2.475-2.282,3.711-5.14,3.711-8.562c0-0.571-0.049-5.708-0.144-15.417c-0.098-9.709-0.144-18.179-0.144-25.406l-6.567,1.136   c-4.187,0.767-9.469,1.092-15.846,1c-6.374-0.089-12.991-0.757-19.842-1.999c-6.854-1.231-13.229-4.086-19.13-8.559   c-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559   c-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-0.951-2.568-2.098-3.711-3.429c-1.142-1.331-1.997-2.663-2.568-3.997   c-0.572-1.335-0.098-2.43,1.427-3.289c1.525-0.859,4.281-1.276,8.28-1.276l5.708,0.853c3.807,0.763,8.516,3.042,14.133,6.851   c5.614,3.806,10.229,8.754,13.846,14.842c4.38,7.806,9.657,13.754,15.846,17.847c6.184,4.093,12.419,6.136,18.699,6.136   c6.28,0,11.704-0.476,16.274-1.423c4.565-0.952,8.848-2.383,12.847-4.285c1.713-12.758,6.377-22.559,13.988-29.41   c-10.848-1.14-20.601-2.857-29.264-5.14c-8.658-2.286-17.605-5.996-26.835-11.14c-9.235-5.137-16.896-11.516-22.985-19.126   c-6.09-7.614-11.088-17.61-14.987-29.979c-3.901-12.374-5.852-26.648-5.852-42.826c0-23.035,7.52-42.637,22.557-58.817   c-7.044-17.318-6.379-36.732,1.997-58.24c5.52-1.715,13.706-0.428,24.554,3.853c10.85,4.283,18.794,7.952,23.84,10.994   c5.046,3.041,9.089,5.618,12.135,7.708c17.705-4.947,35.976-7.421,54.818-7.421s37.117,2.474,54.823,7.421l10.849-6.849   c7.419-4.57,16.18-8.758,26.262-12.565c10.088-3.805,17.802-4.853,23.134-3.138c8.562,21.509,9.325,40.922,2.279,58.24   c15.036,16.18,22.559,35.787,22.559,58.817c0,16.178-1.958,30.497-5.853,42.966c-3.9,12.471-8.941,22.457-15.125,29.979   c-6.191,7.521-13.901,13.85-23.131,18.986c-9.232,5.14-18.182,8.85-26.84,11.136c-8.662,2.286-18.415,4.004-29.263,5.146   c9.894,8.562,14.842,22.077,14.842,40.539v60.237c0,3.422,1.19,6.279,3.572,8.562c2.379,2.279,6.136,2.95,11.276,1.995   c44.163-14.653,80.185-41.062,108.068-79.226c27.88-38.161,41.825-81.126,41.825-128.906   C438.536,184.851,428.728,148.168,409.132,114.573z"
+                  />
+                </svg>
+              </a>
+            </li>
+          </ul>
+        </div>
+      </div>
+    </nav>
+    <main
+      class="centered-container"
+    >
+      <article
+        class="article-reader"
+      >
+        <h1
+          class="article-reader__headline"
+        >
+          Mock Title
+        </h1>
+        <details
+          class="toc"
+        >
+          <summary>
+            <h6>
+              TABLE OF CONTENTS
+            </h6>
+          </summary>
+          <div>
+            Table of Content
+          </div>
+        </details>
+        <div>
+          <div>
+            Sample
+          </div>
+        </div>
+        <div
+          class="article-reader__additional-content"
+        >
+          <div
+            class="downloads-table-container"
+          >
+            <table
+              class="downloads-table"
+            >
+              <thead>
+                <tr>
+                  <th>
+                    Release
+                  </th>
+                  <th>
+                    Status
+                  </th>
+                  <th>
+                    Codename
+                  </th>
+                  <th>
+                    Initial Release
+                  </th>
+                  <th>
+                    Active LTS Start
+                  </th>
+                  <th>
+                    Maintainance LTS Start
+                  </th>
+                  <th>
+                    End of Life
+                  </th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr>
+                  <td>
+                    <a
+                      href="https://nodejs.org/download/release/latest-v12.x/"
+                    >
+                      v12
+                    </a>
+                  </td>
+                  <td>
+                    Maintenance LTS
+                  </td>
+                  <td>
+                    <a
+                      href="https://nodejs.org/download/release/latest-erbium/"
+                    >
+                      Erbium
+                    </a>
+                  </td>
+                  <td>
+                    2019-04-23
+                  </td>
+                  <td>
+                    2019-10-21
+                  </td>
+                  <td>
+                    2020-11-30
+                  </td>
+                  <td>
+                    2022-04-30
+                  </td>
+                </tr>
+                <tr>
+                  <td>
+                    <a
+                      href="https://nodejs.org/download/release/latest-v14.x/"
+                    >
+                      v14
+                    </a>
+                  </td>
+                  <td>
+                    Active LTS
+                  </td>
+                  <td>
+                    <a
+                      href="https://nodejs.org/download/release/latest-fermium/"
+                    >
+                      Fermium
+                    </a>
+                  </td>
+                  <td>
+                    2020-04-21
+                  </td>
+                  <td>
+                    2020-10-27
+                  </td>
+                  <td>
+                    2021-10-19
+                  </td>
+                  <td>
+                    2023-04-30
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
+        <ul
+          class="list"
+        >
+          <h5>
+            Contributors
+          </h5>
+          <li>
+            <a
+              class="link"
+              href="https://github.com/author-mock"
+              rel="noopener noreferrer"
+              style="margin-left: 0px;"
+              target="_blank"
+              title="author-mock"
+            >
+              <img
+                alt="author-mock"
+                class="img"
+                src="https://github.com/author-mock.png?size=60"
+              />
+            </a>
+          </li>
+        </ul>
+        <div
+          class="edit"
+        >
+          <a
+            class="link"
+            href="https://github.com/nodejs/nodejs.dev/edit/main/content/about/releases.md"
+          >
+            <span>
+              Edit this page on GitHub
+            </span>
+             
+            <svg
+              class="icon"
+              fill="currentColor"
+              height="1em"
+              viewBox="0 0 40 40"
+              width="1em"
+            >
+              <path
+                d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"
+              />
+            </svg>
+          </a>
+        </div>
+      </article>
+    </main>
+  </div>
+  <section
+    class="bottom-info"
+  >
+    <div
+      class="random-contributor"
+    />
+  </section>
+  <footer
+    class="footer"
+  >
+    <ul
+      class="footer__left"
+    >
+      <li>
+        <a
+          class="footer__link"
+          href="/trademark"
+        >
+          Trademark Policy
+        </a>
+      </li>
+      <li>
+        <a
+          class="footer__link"
+          href="/privacy"
+        >
+          Privacy Policy
+        </a>
+      </li>
+      <li>
+        <a
+          class="footer__link"
+          href="https://github.com/openjs-foundation/cross-project-council/blob/main/CODE_OF_CONDUCT.md#contributor-covenant-code-of-conduct"
+        >
+          Code of Conduct
+        </a>
+      </li>
+      <li>
+        <a
+          class="footer__link"
+          href="https://nodejs.org/en/security/"
+        >
+          Security Reporting
+        </a>
+      </li>
+      <li>
+        <a
+          class="footer__link"
+          href="https://nodejs.org/en/about/"
+        >
+          About
+        </a>
+      </li>
+      <li>
+        <a
+          class="footer__link"
+          href="https://nodejs.org/en/blog/"
+        >
+          Blog
+        </a>
+      </li>
+    </ul>
+    <ul
+      class="footer__right"
+    >
+      <li>
+        © OpenJS Foundation
+      </li>
+      <li>
+        <a
+          aria-label="Node.js Github Page Link"
+          href="https://github.com/nodejs/node"
+          rel="noopener noreferrer"
+          target="_blank"
+        >
+          <span
+            class="sr-only"
+          >
+            GitHub
+          </span>
+          <svg
+            height="1.8rem"
+            style="fill: var(--color-text-secondary);"
+            viewBox="0 0 438.549 438.549"
+            width="1.8rem"
+          >
+            <path
+              d="M409.132,114.573c-19.608-33.596-46.205-60.194-79.798-79.8C295.736,15.166,259.057,5.365,219.271,5.365   c-39.781,0-76.472,9.804-110.063,29.408c-33.596,19.605-60.192,46.204-79.8,79.8C9.803,148.168,0,184.854,0,224.63   c0,47.78,13.94,90.745,41.827,128.906c27.884,38.164,63.906,64.572,108.063,79.227c5.14,0.954,8.945,0.283,11.419-1.996   c2.475-2.282,3.711-5.14,3.711-8.562c0-0.571-0.049-5.708-0.144-15.417c-0.098-9.709-0.144-18.179-0.144-25.406l-6.567,1.136   c-4.187,0.767-9.469,1.092-15.846,1c-6.374-0.089-12.991-0.757-19.842-1.999c-6.854-1.231-13.229-4.086-19.13-8.559   c-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559   c-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-0.951-2.568-2.098-3.711-3.429c-1.142-1.331-1.997-2.663-2.568-3.997   c-0.572-1.335-0.098-2.43,1.427-3.289c1.525-0.859,4.281-1.276,8.28-1.276l5.708,0.853c3.807,0.763,8.516,3.042,14.133,6.851   c5.614,3.806,10.229,8.754,13.846,14.842c4.38,7.806,9.657,13.754,15.846,17.847c6.184,4.093,12.419,6.136,18.699,6.136   c6.28,0,11.704-0.476,16.274-1.423c4.565-0.952,8.848-2.383,12.847-4.285c1.713-12.758,6.377-22.559,13.988-29.41   c-10.848-1.14-20.601-2.857-29.264-5.14c-8.658-2.286-17.605-5.996-26.835-11.14c-9.235-5.137-16.896-11.516-22.985-19.126   c-6.09-7.614-11.088-17.61-14.987-29.979c-3.901-12.374-5.852-26.648-5.852-42.826c0-23.035,7.52-42.637,22.557-58.817   c-7.044-17.318-6.379-36.732,1.997-58.24c5.52-1.715,13.706-0.428,24.554,3.853c10.85,4.283,18.794,7.952,23.84,10.994   c5.046,3.041,9.089,5.618,12.135,7.708c17.705-4.947,35.976-7.421,54.818-7.421s37.117,2.474,54.823,7.421l10.849-6.849   c7.419-4.57,16.18-8.758,26.262-12.565c10.088-3.805,17.802-4.853,23.134-3.138c8.562,21.509,9.325,40.922,2.279,58.24   c15.036,16.18,22.559,35.787,22.559,58.817c0,16.178-1.958,30.497-5.853,42.966c-3.9,12.471-8.941,22.457-15.125,29.979   c-6.191,7.521-13.901,13.85-23.131,18.986c-9.232,5.14-18.182,8.85-26.84,11.136c-8.662,2.286-18.415,4.004-29.263,5.146   c9.894,8.562,14.842,22.077,14.842,40.539v60.237c0,3.422,1.19,6.279,3.572,8.562c2.379,2.279,6.136,2.95,11.276,1.995   c44.163-14.653,80.185-41.062,108.068-79.226c27.88-38.161,41.825-81.126,41.825-128.906   C438.536,184.851,428.728,148.168,409.132,114.573z"
+            />
+          </svg>
+        </a>
+      </li>
+      <li>
+        <a
+          aria-label="Node.js Twitter Link"
+          href="https://twitter.com/nodejs"
+          rel="noopener noreferrer"
+          target="_blank"
+        >
+          <svg
+            height="1.8rem"
+            style="fill: var(--color-text-secondary);"
+            viewBox="328 355 335 276"
+            width="1.8rem"
+            xmlns="http://www.w3.org/2000/svg"
+          >
+            <path
+              d=" M 630, 425 A 195, 195 0 0 1 331, 600 A 142, 142 0 0 0 428, 570 A  70,  70 0 0 1 370, 523 A  70,  70 0 0 0 401, 521 A  70,  70 0 0 1 344, 455 A  70,  70 0 0 0 372, 460 A  70,  70 0 0 1 354, 370 A 195, 195 0 0 0 495, 442 A  67,  67 0 0 1 611, 380 A 117, 117 0 0 0 654, 363 A  65,  65 0 0 1 623, 401 A 117, 117 0 0 0 662, 390 A  65,  65 0 0 1 630, 425 Z"
+            />
+          </svg>
+        </a>
+      </li>
+      <li>
+        <a
+          aria-label="Node.js Slack Link"
+          href="https://slack.openjsf.org"
+          rel="noopener noreferrer"
+          target="_blank"
+        >
+          <svg
+            height="1.8rem"
+            style="fill: var(--color-text-secondary);"
+            viewBox="0 0 54 54"
+            width="1.8rem"
+            xmlns="http://www.w3.org/2000/svg"
+          >
+            <path
+              d="M19.712.133a5.381 5.381 0 0 0-5.376 5.387 5.381 5.381 0 0 0 5.376 5.386h5.376V5.52A5.381 5.381 0 0 0 19.712.133m0 14.365H5.376A5.381 5.381 0 0 0 0 19.884a5.381 5.381 0 0 0 5.376 5.387h14.336a5.381 5.381 0 0 0 5.376-5.387 5.381 5.381 0 0 0-5.376-5.386"
+            />
+            <path
+              d="M53.76 19.884a5.381 5.381 0 0 0-5.376-5.386 5.381 5.381 0 0 0-5.376 5.386v5.387h5.376a5.381 5.381 0 0 0 5.376-5.387m-14.336 0V5.52A5.381 5.381 0 0 0 34.048.133a5.381 5.381 0 0 0-5.376 5.387v14.364a5.381 5.381 0 0 0 5.376 5.387 5.381 5.381 0 0 0 5.376-5.387"
+            />
+            <path
+              d="M34.048 54a5.381 5.381 0 0 0 5.376-5.387 5.381 5.381 0 0 0-5.376-5.386h-5.376v5.386A5.381 5.381 0 0 0 34.048 54m0-14.365h14.336a5.381 5.381 0 0 0 5.376-5.386 5.381 5.381 0 0 0-5.376-5.387H34.048a5.381 5.381 0 0 0-5.376 5.387 5.381 5.381 0 0 0 5.376 5.386"
+            />
+            <path
+              d="M0 34.249a5.381 5.381 0 0 0 5.376 5.386 5.381 5.381 0 0 0 5.376-5.386v-5.387H5.376A5.381 5.381 0 0 0 0 34.25m14.336-.001v14.364A5.381 5.381 0 0 0 19.712 54a5.381 5.381 0 0 0 5.376-5.387V34.25a5.381 5.381 0 0 0-5.376-5.387 5.381 5.381 0 0 0-5.376 5.387"
+            />
+          </svg>
+        </a>
+      </li>
+    </ul>
+  </footer>
+</div>
+`;
diff --git a/test/pages/__snapshots__/trademark.test.tsx.snap b/test/pages/__snapshots__/trademark.test.tsx.snap
index 4f7ebe99..b75c7e90 100644
--- a/test/pages/__snapshots__/trademark.test.tsx.snap
+++ b/test/pages/__snapshots__/trademark.test.tsx.snap
@@ -249,16 +249,16 @@ exports[`Trademark page renders correctly 1`] = `
           <li>
             <a
               class="link"
-              href="https://github.com/palak"
+              href="https://github.com/author-mock"
               rel="noopener noreferrer"
               style="margin-left: 0px;"
               target="_blank"
-              title="palak"
+              title="author-mock"
             >
               <img
-                alt="palak"
+                alt="author-mock"
                 class="img"
-                src="https://github.com/palak.png?size=60"
+                src="https://github.com/author-mock.png?size=60"
               />
             </a>
           </li>
diff --git a/test/pages/releases.test.tsx b/test/pages/releases.test.tsx
new file mode 100644
index 00000000..df45d3e6
--- /dev/null
+++ b/test/pages/releases.test.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import '../__mocks__/intersectionObserverMock';
+import {
+  createGeneralPageData,
+  createNodeReleaseData,
+} from '../__fixtures__/page';
+import ReleasesPage from '../../src/pages/releases';
+import { useReleaseData } from '../../src/hooks/useReleaseHistory';
+
+const mockData = createGeneralPageData();
+const mockReleaseData = createNodeReleaseData();
+jest.mock('../../src/hooks/useReleaseHistory');
+
+describe('Releases page', () => {
+  it('renders correctly', () => {
+    (useReleaseData as jest.Mock).mockReturnValue({
+      releaseData: mockReleaseData,
+    });
+
+    const { container } = render(<ReleasesPage data={mockData.data} />);
+    expect(container).toMatchSnapshot();
+  });
+});
diff --git a/test/pages/trademark.test.tsx b/test/pages/trademark.test.tsx
index a08f1702..5c06f27f 100644
--- a/test/pages/trademark.test.tsx
+++ b/test/pages/trademark.test.tsx
@@ -3,9 +3,9 @@ import { render } from '@testing-library/react';
 import TrademarkPage from '../../src/pages/trademark';
 import '../__mocks__/intersectionObserverMock';
 
-import { createTrademarkData } from '../__fixtures__/page';
+import { createGeneralPageData } from '../__fixtures__/page';
 
-const mockData = createTrademarkData();
+const mockData = createGeneralPageData();
 
 describe('Trademark page', () => {
   it('renders correctly', () => {