Skip to content

Commit

Permalink
Update styling of partners sidebar (#2694)
Browse files Browse the repository at this point in the history
* Unify Partner Info Cards

* Remove extraneous "in the press"

* Fix layout

Style like other grids in mobile; as sidebar otherwise

* Fix press test
RoyEJohnson authored Jan 13, 2025

Verified

This commit was signed with the committer’s verified signature. The key has expired.
coreyja Corey Alexander
1 parent 42a4a8b commit 32c9a7d
Showing 11 changed files with 283 additions and 227 deletions.
68 changes: 68 additions & 0 deletions src/app/components/partner-card/partner-card.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
@import 'pattern-library/core/pattern-library/headers';
@import 'mixins/placeholder-selectors';

.partner-card {
align-content: center;
align-items: center;
color: inherit;
display: grid;
grid-column-gap: $normal-margin;
grid-row-gap: 0.5rem;
grid-template-columns: 33% 1fr;
grid-template-rows: 100%;
min-height: 13rem;
padding: 1.2rem 1rem 1.2rem 1.2rem;
position: relative;
text-decoration: none;

&:hover::after,
&:active::after {
background: rgba(ui-color(black), 0.1);
content: '\A';
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}

.logo {
align-items: center;
display: grid;
height: 100%;
max-width: 100%;
position: relative;

> img {
max-width: 100%;
}
}

.badge {
@include verified-badge();

margin-top: 0.5rem;
position: absolute;
right: 0;
top: 0;
z-index: 1;
}

.name {
@include title-font(1.8rem);
}

.tags {
@include body-font(1.2rem);

color: text-color(helper);

.tag:not(:first-child)::before {
content: '';
}
}

.stars-and-count {
margin-top: $normal-margin;
}
}
63 changes: 63 additions & 0 deletions src/app/components/partner-card/partner-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
import './partner-card.scss';

export default function PartnerCard({
type,
href,
title,
logoUrl,
tags,
onClick,
badgeImage,
verifiedFeatures,
analyticsContentType
}: {
type: string | null;
title: string;
href: string;
logoUrl: string | null;
tags: string[];
onClick?: React.MouseEventHandler<HTMLAnchorElement>;
badgeImage?: string;
verifiedFeatures?: string;
analyticsContentType?: string;
}) {
const analyticsInfo = analyticsContentType
? {
'data-analytics-select-content': title,
'data-content-type': analyticsContentType,
'data-content-tags': `,category=${type},`
}
: {};

return (
<a className="partner-card" href={href} onClick={onClick} {...analyticsInfo}>
<div className="logo">
<img src={logoUrl ?? ''} alt="" />
{verifiedFeatures && badgeImage && (
<div className="badge">
<img
className="background"
src={badgeImage}
alt="verified"
/>
<FontAwesomeIcon className="checkmark" icon={faCheck} />
<div className="tooltip right">{verifiedFeatures}</div>
</div>
)}
</div>
<div className="info">
<div className="name">{title}</div>
<div className="tags">
{tags.map((value) => (
<span className="tag" key={value}>
{value}
</span>
))}
</div>
</div>
</a>
);
}
Original file line number Diff line number Diff line change
@@ -1,63 +1,8 @@
import React from 'react';
import {useNavigate} from 'react-router-dom';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
import StarsAndCount from '~/components/stars-and-count/stars-and-count';
import PartnerCard from '~/components/partner-card/partner-card';
import './partners.scss';

function Blurb({blurb, badgeImage, onClick}) {
const tags = [blurb.cost, blurb.type].filter((x) => x);
const {count: ratingCount, average: rating} = blurb;

return (
<li>
<a
className="blurb"
href={blurb.url}
onClick={onClick}
data-analytics-select-content={blurb.name}
data-content-type="Partner Profile"
data-content-tags={`,category=${blurb.type},`}
>
<div className="logo">
<img src={blurb.image} alt="" />
{blurb.verifiedFeatures && (
<div className="badge">
<img
className="background"
src={badgeImage}
alt="verified"
/>
<FontAwesomeIcon
className="checkmark"
icon={faCheck}
/>
<div className="tooltip right">
{blurb.verifiedFeatures}
</div>
</div>
)}
</div>
<div className="info">
<div className="name">{blurb.name}</div>
<div className="tags">
{tags.map((value) => (
<span className="tag" key={value}>
{value}
</span>
))}
</div>
<StarsAndCount
rating={rating}
count={ratingCount}
showNumber
/>
</div>
</a>
</li>
);
}

export default function Partners({bookAbbreviation, model}) {
const {title, seeMoreText, blurbs, badgeImage} = model;
const navigate = useNavigate();
@@ -92,12 +37,19 @@ export default function Partners({bookAbbreviation, model}) {
<div className="blurb-scroller" data-analytics-content-list={title}>
<ul className="blurbs">
{blurbs.map((blurb) => (
<Blurb
blurb={blurb}
onClick={onClick}
badgeImage={badgeImage}
key={blurb.url}
/>
<li key={blurb.url}>
<PartnerCard
type={blurb.type}
title={blurb.name}
href={blurb.url}
logoUrl={blurb.image}
tags={[blurb.cost, blurb.type].filter((x) => x)}
onClick={onClick}
badgeImage={badgeImage}
verifiedFeatures={blurb.verifiedFeatures}
analyticsContentType='Partner Profile'
/>
</li>
))}
</ul>
</div>
Original file line number Diff line number Diff line change
@@ -59,70 +59,4 @@
padding: 0.5rem;
}
}

.blurb {
align-content: center;
align-items: center;
color: inherit;
display: grid;
grid-column-gap: $normal-margin;
grid-row-gap: 0.5rem;
grid-template-columns: 10rem 1fr;
grid-template-rows: 100%;
min-height: 13rem;
padding: 1.2rem 1rem 1.2rem 1.2rem;
position: relative;
text-decoration: none;

&:hover::after,
&:active::after {
background: rgba(ui-color(black), 0.1);
content: '\A';
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}

.logo {
align-items: center;
display: grid;
height: 100%;
max-width: 100%;
position: relative;

> img {
max-width: 100%;
}
}

.badge {
@extend %verified-badge;

margin-top: 0.5rem;
position: absolute;
right: 0;
top: 0;
z-index: 1;
}

.name {
@include title-font(1.8rem);
}

.tags {
@include body-font(1.2rem);

color: text-color(helper);

.tag:not(:first-child)::before {
content: '';
}
}

.stars-and-count {
margin-top: $normal-margin;
}
}
}
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@
}

.badge {
@extend %verified-badge;
@include verified-badge();

align-self: start;
grid-area: icon;
50 changes: 18 additions & 32 deletions src/app/pages/partners/results/result-grid.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import React from 'react';
import {useNavigate} from 'react-router-dom';
import type {PartnerEntry} from './results';
import PartnerCard from '~/components/partner-card/partner-card';

function modelFromEntry(entry: PartnerEntry) {
return {
type: entry.type,
title: entry.title,
logoUrl: entry.logoUrl,
description: entry.blurb,
tags: entry.tags,
badgeImage: '/dist/images/partners/verified-badge.svg'
};
}

function ResultCard({entry}: {entry: PartnerEntry}) {
const {type, title, logoUrl, tags} =
modelFromEntry(entry);
export const badgeImage = '/dist/images/partners/verified-badge.svg';
export function useOnSelect() {
const navigate = useNavigate();
const onSelect = React.useCallback(

return React.useCallback(
(event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
const href = event.currentTarget.getAttribute('href');
@@ -26,27 +16,23 @@ function ResultCard({entry}: {entry: PartnerEntry}) {
},
[navigate]
);
}

function ResultCard({entry}: {entry: PartnerEntry}) {
const {type, title, logoUrl, tags} = entry;
const onSelect = useOnSelect();

return (
<a
<PartnerCard
type={type}
href={`?${encodeURIComponent(title)}`}
type="button"
className="card"
title={title}
logoUrl={logoUrl}
tags={tags.map((t) => t.value).filter((v) => v !== null)}
onClick={onSelect}
data-analytics-select-content={title}
data-content-type="Partner Profile"
data-content-tags={`,category=${type},`}
>
<div className="logo">
{logoUrl && <img src={logoUrl} alt="" />}
</div>
<div className="resource-title">{title}</div>
<div className="tags">
{tags.map(({value}) => (
<div key={value}>{value}</div>
))}
</div>
</a>
badgeImage={badgeImage}
analyticsContentType='Partner Profile'
/>
);
}

157 changes: 96 additions & 61 deletions src/app/pages/partners/results/results.scss
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@
margin: 0;
}

.grid {
@mixin partner-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
grid-auto-rows: minmax(min-content, 100%);
@@ -42,77 +42,85 @@
justify-content: left;
}

.card {
background-color: ui-color(white);
border-radius: 0.3rem;
box-shadow: $card-shadow;
color: inherit;
display: grid;
grid-row-gap: 1rem;
grid-template: 'image' 8rem
'title'
'tags' 1fr
'rating';
height: 100%;
padding: $normal-margin;
text-decoration: none;

.logo {
align-self: center;
grid-area: image;
justify-self: center;

> img {
max-height: 8rem;
max-width: 100%;
}
.partner-card {
@include partner-card();
}
}

@mixin partner-card {
background-color: ui-color(white);
border-radius: 0.3rem;
box-shadow: $card-shadow;
color: inherit;
display: grid;
grid-row-gap: 1rem;
grid-template: 'image' 8rem
'title'
'tags' 1fr
'rating';
height: 100%;
padding: $normal-margin;
text-decoration: none;

.logo {
align-self: center;
grid-area: image;
justify-self: center;

> img {
max-height: 8rem;
max-width: 100%;
}
}

.badge {
@extend %verified-badge;
.badge {
@include verified-badge();

align-self: start;
grid-area: image;
justify-self: end;
z-index: auto;
}
align-self: start;
grid-area: image;
justify-self: end;
z-index: auto;
}

.resource-title {
@include set-font(h4);
.resource-title {
@include set-font(h4);

font-weight: bold;
grid-area: title;
line-height: normal;
}
font-weight: bold;
grid-area: title;
line-height: normal;
}

.tags,
.stars-and-count {
@include body-font(1.2rem);
.tags,
.stars-and-count {
@include body-font(1.2rem);

color: text-color(helper);
}
color: text-color(helper);
}

.tags {
grid-area: tags;
}
.tags {
grid-area: tags;
}

.stars-and-count {
align-items: center;
grid-area: rating;
display: grid;
grid-auto-flow: column;
grid-column-gap: 0.7rem;
justify-content: left;

.stars {
color: os-color(yellow);
font-size: 75%;
padding-bottom: 3%;
}
.stars-and-count {
align-items: center;
grid-area: rating;
display: grid;
grid-auto-flow: column;
grid-column-gap: 0.7rem;
justify-content: left;

.stars {
color: os-color(yellow);
font-size: 75%;
padding-bottom: 3%;
}
}
}

.grid {
@include partner-grid;
}

.boxed {
gap: 3rem;

@@ -137,13 +145,21 @@
display: flex;
flex-direction: column;
gap: $normal-margin;

ul {
@include partner-grid();
}

li > a {
@include partner-card();
}
}
}

@include wider-than($phone-max) {
display: flex;
flex-direction: row;
gap: 3rem;
gap: $normal-margin;
max-width: 120rem;
margin: 0 auto;
padding-right: $normal-margin;
@@ -153,12 +169,20 @@
}

> .sidebar {
min-width: 20rem;
border: thin solid black;
height: max-content;

.sidebar-content {
background-color: ui-color(white);

ul {
padding: 0;
margin: 0;

li {
border-bottom: thin solid ui-color(form-border);
}
}
}

h2 {
@@ -171,6 +195,7 @@

.grid {
gap: 0.2rem;
grid-template-columns: 100%;
}

.card {
@@ -180,5 +205,15 @@
}
}
}
@include width-between($phone-max, $tablet-max) {
> .sidebar {
min-width: calc(50% - 3rem);
}
}
@include wider-than($tablet-max) {
> .sidebar {
min-width: calc(25% + 12rem);
}
}
}
}
23 changes: 21 additions & 2 deletions src/app/pages/partners/results/results.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import ResultGrid from './result-grid';
import ResultGrid, {badgeImage, useOnSelect} from './result-grid';
import PartnerCard from '~/components/partner-card/partner-card';
import useSearchContext from '../search-context';
import partnerFeaturePromise from '~/models/salesforce-partners';
import {useDataFromPromise} from '~/helpers/page-data-utils';
@@ -211,11 +212,29 @@ function resultEntry(pd: PartnerData) {
}

function Sidebar({entries}: {entries: PartnerEntry[]}) {
const onSelect = useOnSelect();

return (
<div className="sidebar">
<div className="sidebar-content">
<h2>Startups</h2>
<ResultGrid entries={entries} />
<ul className="no-bullets">
{entries.map(({type, title, logoUrl, tags}) => (
<li key={title}>
<PartnerCard
type={type}
href={`?${encodeURIComponent(title)}`}
title={title}
logoUrl={logoUrl}
tags={tags.map((t) => t.value).filter((v) => v !== null)}
onClick={onSelect}
badgeImage={badgeImage}
analyticsContentType='Partner Profile'
/>
</li>
))}
</ul>

</div>
</div>
);
1 change: 0 additions & 1 deletion src/app/pages/press/press.tsx
Original file line number Diff line number Diff line change
@@ -27,7 +27,6 @@ function MainPage() {

return (
<React.Fragment>
<div>In the press</div>
<section id='in-the-press'>
<Banner />
</section>
2 changes: 1 addition & 1 deletion src/styles/mixins/placeholder-selectors.scss
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
-webkit-font-smoothing: antialiased;
}

%verified-badge {
@mixin verified-badge {
align-items: center;
color: text-color(white);
display: grid;
2 changes: 1 addition & 1 deletion test/src/pages/press/press.test.tsx
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ describe('press page', () => {
<Press />
</MemoryRouter>
);
await screen.findByText('In the press');
await screen.findByRole('heading', {'level': 1, name: 'In the Press'});
await waitFor(() => expect(document.title).toMatch('- OpenStax'));
document.title = '';
});

0 comments on commit 32c9a7d

Please sign in to comment.