Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@
@import 'extends/image-based-cards';
@import 'extends/image-based-card-and-hover';
@import 'extends/ranking-cards';
@import 'extends/highly-ranked';
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
@import "../_unity-bootstrap-theme-variables.scss";

.highly-ranked-section {
padding-top: $uds-size-spacing-12;
padding-bottom: $uds-size-spacing-12;
background-color: $faint;

@media (max-width: map-get($grid-breakpoints, "lg")) {
padding-top: $uds-size-spacing-6;
padding-bottom: $uds-size-spacing-6;
}

.highly-ranked-title {
margin-bottom: $uds-size-spacing-4;

.highlight-gold {
display: inline-block;
padding: $uds-size-spacing-half;
background-color: $gold;
color: $dark;
line-height: 1.1;
}
}

.highly-ranked-description {
font-size: 1.25rem;
line-height: $uds-size-spacing-3 + 0.25rem;
color: $dark;
margin-bottom: $uds-size-spacing-4;

@media (max-width: map-get($grid-breakpoints, "md")) {
font-size: 1rem;
}
}

.highly-ranked-cta {
margin-bottom: $uds-size-spacing-4;
}

.highly-ranked-grid {
margin-top: $uds-size-spacing-4;
}

.highly-ranked-card {
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
gap: $uds-size-spacing-1;

.ranking-value-container {
border-left: 8px solid $gold;
padding-left: $uds-size-spacing-2;
margin-bottom: 0;

@media (max-width: map-get($grid-breakpoints, "md")) {
padding-left: $uds-size-spacing-3;
}
}

.ranking-content {
padding-left: calc(8px + #{$uds-size-spacing-2});

@media (max-width: map-get($grid-breakpoints, "md")) {
padding-left: calc(8px + #{$uds-size-spacing-3});
}
}

.ranking-value {
font-size: $uds-size-font-xxxl;
font-weight: $font-weight-bold;
margin: 0;
line-height: 1.1;
color: $dark;

@media (max-width: map-get($grid-breakpoints, "md")) {
font-size: 3rem; // 48px
}
}

.ranking-title {
font-size: 1.5rem;
font-weight: $font-weight-bold;
margin: 0 0 $uds-size-spacing-1 0;
color: $dark;
}

.ranking-description {
font-size: 1rem;
color: $dark;
margin-bottom: 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// @ts-check
import { sanitizeDangerousMarkup } from "@asu/shared";
import classNames from "classnames";
import PropTypes from "prop-types";
import React from "react";

import { Button } from "../Button/Button";

/**
* @typedef {Object} RankingItem
* @property {string} value - The main ranking value (e.g., "400+", "#2")
* @property {string} title - The title of the ranking (e.g., "‘prestigious faculty’")
* @property {string} description - The description of the ranking
*/

/**
* @typedef {Object} HighlyRankedProps
* @property {string} [title] - The section title
* @property {string} [description] - The section description
* @property {string} [ctaText] - The text for the CTA button
* @property {string} [ctaUrl] - The URL for the CTA button
* @property {"dark" | "gold" | "maroon" | "gray"} [ctaButtonColor] - The color of the CTA button
* @property {RankingItem[]} [rankings] - The list of rankings to display
*/

/**
* @param {HighlyRankedProps} props
* @returns {JSX.Element}
*/
export const HighlyRanked = ({
title = "Highly ranked",
description = "",
ctaText = "",
ctaUrl = "",
ctaButtonColor = "dark",
rankings = [],
}) => {
return (
<section className="highly-ranked-section">
<div className="container">
<div className="row">
<div className="col-12 col-lg-8">
{title && (
<h2 className="highly-ranked-title">
<span className="highlight-gold">{title}</span>
</h2>
)}
{description && (
<p
className="highly-ranked-description"
dangerouslySetInnerHTML={sanitizeDangerousMarkup(description)}
/>
)}
{ctaText && ctaUrl && (
<div className="highly-ranked-cta">
<Button
label={ctaText}
href={ctaUrl}
color={ctaButtonColor}
size={"small"}
classes={[]}
/>
</div>
)}
</div>
</div>

<div className="row highly-ranked-grid">
{rankings.map((ranking, index) => (
<div key={index} className="col-12 col-lg-6 mb-4">
<div className="highly-ranked-card">
<div className="ranking-value-container">
<h3 className="ranking-value">{ranking.value}</h3>
</div>
<div className="ranking-content">
{ranking.title && (
<h4 className="ranking-title">{ranking.title}</h4>
)}
{ranking.description && (
<p
className="ranking-description"
dangerouslySetInnerHTML={sanitizeDangerousMarkup(
ranking.description
)}
/>
)}
</div>
</div>
</div>
))}
</div>
</div>
</section>
);
};

HighlyRanked.propTypes = {
title: PropTypes.string,
description: PropTypes.string,
ctaText: PropTypes.string,
ctaUrl: PropTypes.string,
rankings: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.string.isRequired,
title: PropTypes.string,
description: PropTypes.string,
})
),
ctaButtonColor: PropTypes.oneOf(["dark", "gold", "maroon", "gray"]),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from "react";
import { HighlyRanked } from "./HighlyRanked";

export default {
title: "Components/HighlyRanked",
component: HighlyRanked,
};

const Template = args => <HighlyRanked {...args} />;

export const Default = Template.bind({});
Default.args = {
title: "ASU is highly ranked",
description:
"ASU consistently ranks among the best in the nation, recognized for our commitment to excellence and showcasing our dedication to providing quality education and impactful research.",
ctaText: "See more ASU rankings",
ctaUrl: "https://www.asu.edu/rankings",
rankings: [
{
value: "400+",
title: "‘prestigious faculty’",
description: "National Academies-honored faculty",
},
{
value: "#2",
title: "in the U.S. for employability",
description: "among public universities",
},
{
value: "83",
title: "top-ranked programs",
description: "Ranked in the top 25 in the U.S., including 38 in the top 10",
},
{
value: "$6.1",
title: "billion",
description: "FY24 economic impact on the state’s gross product",
},
{
value: "Top 10",
title: "worldwide among universities granted U.S. patents",
description: "For two years",
},
{
value: "270+",
title: "athletic championships",
description: "National and conference titles",
},
{
value: "620,000+",
title: "alumni",
description: "Leading, shaping, changing our world",
},
{
value: "Association of American Universities (AAU) member",
title: "",
description: "Along with Harvard, Stanford and MIT",
},
{
value: "Top producer of elite scholars",
title: "",
description: "For 10 consecutive years",
},
],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { render, screen } from "@testing-library/react";
import React from "react";
import { expect, describe, it } from "vitest";
import { HighlyRanked } from "./HighlyRanked";

describe("HighlyRanked component", () => {
const mockProps = {
title: "ASU is highly ranked",
description: "Test description",
ctaText: "CTA Rank",
ctaUrl: "#",
rankings: [
{
value: "400+",
title: "faculty",
description: "description 1",
},
],
};

it("should render the title and description", () => {
render(<HighlyRanked {...mockProps} />);
expect(screen.getByText("ASU is highly ranked")).toBeInTheDocument();
expect(screen.getByText("Test description")).toBeInTheDocument();
});

it("should render the CTA button", () => {
render(<HighlyRanked {...mockProps} />);
const cta = screen.getByText("CTA Rank");
expect(cta).toBeInTheDocument();
expect(cta.closest("a")).toHaveAttribute("href", "#");
});

it("should render the ranking cards", () => {
render(<HighlyRanked {...mockProps} />);
expect(screen.getByText("400+")).toBeInTheDocument();
expect(screen.getByText("faculty")).toBeInTheDocument();
expect(screen.getByText("description 1")).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { initHighlyRanked as default } from "../../core/utils";
1 change: 1 addition & 0 deletions packages/unity-react-core/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export * from "./CardArrangement/CardArrangement";
export * from "./Divider/Divider";
export * from "./FeedAnatomy";
export * from "./Hero/Hero";
export * from "./HighlyRanked/HighlyRanked";
export * from "./Image/Image";
export * from "./Pagination/Pagination";
export * from "./RankingCard/RankingCard";
Expand Down
7 changes: 7 additions & 0 deletions packages/unity-react-core/src/core/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
import { Divider } from "../../components/Divider/Divider";
import { GridLinks } from "../../components/GridLinks/GridLinks";
import { Hero } from "../../components/Hero/Hero";
import { HighlyRanked } from "../../components/HighlyRanked/HighlyRanked";
import { Image } from "../../components/Image/Image";
import { List } from "../../components/List/List";
import { Pagination } from "../../components/Pagination/Pagination";
Expand Down Expand Up @@ -169,3 +170,9 @@ export const initTooltip = ({ targetSelector, props }) =>
*/
export const initList = ({ targetSelector, props }) =>
RenderReact(List, props, document.querySelector(targetSelector));

/**
* @param {ComponentProps} props
*/
export const initHighlyRanked = ({ targetSelector, props }) =>
RenderReact(HighlyRanked, props, document.querySelector(targetSelector));