Skip to content

Commit

Permalink
feat: Implemented Billboard component (#369)
Browse files Browse the repository at this point in the history
  • Loading branch information
johglove committed Oct 12, 2023
1 parent 5b18dd8 commit 7309627
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 0 deletions.
81 changes: 81 additions & 0 deletions src/components/PageContent/Billboard/Billboard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
Copyright (C) 2018 The Trustees of Indiana University
SPDX-License-Identifier: BSD-3-Clause
*/
import classNames from "classnames";
import * as PropTypes from "prop-types";
import * as React from "react";
import * as Rivet from "../../util/Rivet";
import { TestUtils } from "../../util/TestUtils";

const Billboard = ({
children,
className,
image,
testMode = false,
title,
variant = "standard",
...attrs
}) => {
const classNameArr = [
"rvt-billboard",
variant === "center" ? "rvt-billboard--center" : "",
variant === "reverse" ? "rvt-billboard--reverse" : "",
className
]

return (
<div
{...attrs}
className={classNames(classNameArr)}
{...(testMode && { "data-testid": TestUtils.Billboard.container })}
>
{image && <BillboardImage testMode={testMode}>{image}</BillboardImage>}
<div className="rvt-billboard__body">
<h2
className="rvt-billboard__title"
{...(testMode && { "data-testid": TestUtils.Billboard.title })}
>
{title}
</h2>
{children && <BillboardContent testMode={testMode}>{children}</BillboardContent>}
</div>
</div>
)
}

Billboard.displayName = "Billboard";
Billboard.propTypes = {
/** Optional image to display */
image: PropTypes.oneOfType(PropTypes.element),
/** [Developer] Adds data-testId attributes for component testing */
testMode: PropTypes.bool,
/** The title of the billboard */
title: PropTypes.string.isRequired,
/* The variant determines the style of the billboard */
variant: PropTypes.oneOf(["center", "reverse", "standard"])
};

const BillboardImage = ({ children, testMode = false }) => {
return (
<div
className="rvt-billboard__image"
{...(testMode && { "data-testid": TestUtils.Billboard.image })}
>
{children}
</div>
)
}

const BillboardContent = ({ children, testMode = false }) => {
return (
<div
className="rvt-billboard__content rvt-flow"
{...(testMode && { "data-testid": TestUtils.Billboard.content })}
>
{children}
</div>
)
}

export default Rivet.rivetize(Billboard);
90 changes: 90 additions & 0 deletions src/components/PageContent/Billboard/Billboard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
Use the billboard component to highlight news articles, student or alumni stories, or other important content on your website.

A billboard contains an image, title, summary text, and optional call to action link.

Billboards are often used on website home pages, section index pages, and landing pages for marketing campaigns.

View the [Rivet documentation for Billboard](https://rivet.uits.iu.edu/components/billboard/).

### Billboard Examples

<!-- prettier-ignore-start -->
```jsx
const image = <img src="https://rivet.iu.edu/img/placeholder/billboard-2.webp" alt="Student in vintage-style Indiana University t-shirt" />;
<div className="rvt-container-sm">
<Billboard
image={image}
title="Standard Billboard"
>
<p>
Short paragraph of content for the billboard
</p>
<a className="rvt-cta" href="#">A call to action</a>
</Billboard>
</div>
```
<!-- prettier-ignore-end -->

<!-- prettier-ignore-start -->
```jsx
const image = <img src="https://rivet.iu.edu/img/placeholder/billboard-2.webp" alt="Student in vintage-style Indiana University t-shirt" />;
<div className="rvt-container-sm">
<Billboard
image={image}
title="Reversed Billboard"
variant="reverse"
>
<p>
Short paragraph of content for the billboard
</p>
</Billboard>
</div>
```
<!-- prettier-ignore-end -->

<!-- prettier-ignore-start -->
```jsx
<div className="rvt-container-sm">
<Billboard
title="Centered Billboard"
variant="center"
>
<p>
Note when using the center variant, the billboard should not have an image
</p>
</Billboard>
</div>
```
<!-- prettier-ignore-end -->

<!-- prettier-ignore-start -->
```jsx
<div className="rvt-container-sm">
<Billboard title="No Image Billboard">
<p>
Short paragraph of content for the billboard
</p>
</Billboard>
</div>
```
<!-- prettier-ignore-end -->

<!-- prettier-ignore-start -->
```jsx
const image = <img src="https://rivet.iu.edu/img/placeholder/billboard-2.webp" alt="Student in vintage-style Indiana University t-shirt" />;
<div className="rvt-container-sm">
<Billboard
image={image}
title="No Content Billboard"
/>
</div>
```
<!-- prettier-ignore-end -->

<!-- prettier-ignore-start -->
```jsx
<div className="rvt-container-sm">
<Billboard title="Title Only Billboard" />
</div>
```
<!-- prettier-ignore-end -->
161 changes: 161 additions & 0 deletions src/components/PageContent/Billboard/Billboard.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import React from "react";
import Billboard from "./Billboard";
import { TestUtils } from "../../util/TestUtils";

const testIds = TestUtils.Billboard
const image = <img src="https://rivet.iu.edu/img/placeholder/billboard-2.webp" alt="Student in vintage-style Indiana University t-shirt" />
const imageStr = '<img src="https://rivet.iu.edu/img/placeholder/billboard-2.webp" alt="Student in vintage-style Indiana University t-shirt">'
const title = "test title"
const content = <p>Test content</p>
const contentString = "<p>Test content</p>"
const customClassName = "custom-style"

describe("<Billboard />", () => {
describe("Rendering", () => {
it("standard should render without throwing an error", () => {
render(
<Billboard
className={customClassName}
image={image}
testMode
title={title}
>
{content}
</Billboard>
);
checkRenderContainer()
checkRenderTitle()
checkRenderContent()
checkRenderImage()
});

it("without image should render without throwing an error", () => {
render(
<Billboard
className={customClassName}
testMode
title={title}
variant="reverse"
>
{content}
</Billboard>
);

checkRenderContainer()
checkRenderTitle()
checkRenderContent()
const billboardImage = screen.queryByTestId(testIds.image);
expect(billboardImage).not.toBeInTheDocument();
});

it("without content should render without throwing an error", () => {
render(
<Billboard
className={customClassName}
image={image}
testMode
title={title}
/>
);
checkRenderContainer()
checkRenderTitle()
checkRenderImage()
const billboardContent = screen.queryByTestId(testIds.content);
expect(billboardContent).not.toBeInTheDocument();
});
});
describe("Variants", () => {
it("variant standard should render without reverse or center classes", () => {
render(
<Billboard
className={customClassName}
image={image}
testMode
title={title}
>
{content}
</Billboard>
);
checkRenderContainer()
checkRenderTitle()
checkRenderContent()
checkRenderImage()
const billboardContainer = screen.getByTestId(testIds.container);
expect(billboardContainer).not.toHaveClass("rvt-billboard--reverse");
expect(billboardContainer).not.toHaveClass("rvt-billboard--center");
});
it("variant reverse should render with only reverse class", () => {
render(
<Billboard
className={customClassName}
image={image}
testMode
title={title}
variant="reverse"
>
{content}
</Billboard>
);

checkRenderContainer()
checkRenderTitle()
checkRenderContent()
checkRenderImage()
const billboardContainer = screen.getByTestId(testIds.container);
expect(billboardContainer).toHaveClass("rvt-billboard--reverse");
expect(billboardContainer).not.toHaveClass("rvt-billboard--center");
});

it("variant center should render with only center class", () => {
render(
<Billboard
className={customClassName}
image={image}
testMode
title={title}
variant="center"
>
{content}
</Billboard>
);

checkRenderContainer()
checkRenderTitle()
checkRenderContent()
checkRenderImage()
const billboardContainer = screen.getByTestId(testIds.container);
expect(billboardContainer).not.toHaveClass("rvt-billboard--reverse");
expect(billboardContainer).toHaveClass("rvt-billboard--center");
});
});
});

const checkRenderContainer = () => {
const billboardContainer = screen.getByTestId(testIds.container);
expect(billboardContainer).toBeVisible();
expect(billboardContainer).toHaveClass("rvt-billboard");
expect(billboardContainer).toHaveClass(customClassName);
}

const checkRenderTitle = () => {
const billboardTitle = screen.getByTestId(testIds.title);
expect(billboardTitle).toBeVisible();
expect(billboardTitle).toHaveClass("rvt-billboard__title");
expect(billboardTitle.innerHTML).toBe(title);
}

const checkRenderContent = () => {
const billboardContent = screen.getByTestId(testIds.content);
expect(billboardContent).toBeVisible();
expect(billboardContent).toHaveClass("rvt-billboard__content");
expect(billboardContent.innerHTML).toBe(contentString);
}

const checkRenderImage = () => {
const billboardImage = screen.getByTestId(testIds.image);
expect(billboardImage).toBeVisible();
expect(billboardImage).toHaveClass("rvt-billboard__image");
expect(billboardImage.innerHTML).toBe(imageStr);
}
1 change: 1 addition & 0 deletions src/components/PageContent/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ Copyright (C) 2018 The Trustees of Indiana University
SPDX-License-Identifier: BSD-3-Clause
*/
export { default as Badge } from "./Badge/Badge";
export { default as Billboard } from "./Billboard/Billboard";
export { default as Disclosure } from "./Disclosure/Disclosure";
6 changes: 6 additions & 0 deletions src/components/util/TestUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,10 @@ export const TestUtils = {
Table: { testId: "testId" },
Footer: { testId: "testId" },
Disclosure: { testId: "disclosure__testId" },
Billboard: {
container: "billboard-container",
content: "billboard-content",
image: "billboard-image",
title: "billboard-title"
}
};

0 comments on commit 7309627

Please sign in to comment.