Skip to content

Commit

Permalink
feature(banner): add blog announcement banner (#8801)
Browse files Browse the repository at this point in the history
* feature(banner): add blog announcement banner

* copy changes

* more vars

* ssr

* cleanup
  • Loading branch information
fiji-flo authored May 8, 2023
1 parent 80f51f4 commit 2e730ae
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 32 deletions.
13 changes: 7 additions & 6 deletions client/src/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@
--top-navigation-height: 4rem;
--top-navigation-offset: -4rem;
--top-banner-inner-height: 3.125rem;
// top-banner-inner-height + 2 * padding + 2 * border;
--top-banner-height: calc(
var(--top-banner-inner-height) + 2 * 0.125rem + 2 * 1px
);
// top-banner-inner-height + 2 * padding;
--top-banner-height: calc(var(--top-banner-inner-height) + 2 * 0.125rem);
// + border
--top-banner-outer-height: calc(var(--top-banner-height) + 2 * 1px);

--z-index-back: -1;
--z-index-top: 9999;
Expand All @@ -101,10 +101,11 @@
);
}

.top-banner.visible ~ * {
.top-banner.visible ~ *,
.top-banner.loading ~ * {
--sticky-header-height: calc(
var(--top-nav-height) + var(--article-actions-container-height) +
var(--top-banner-height) + 2px
var(--top-banner-outer-height) + 2px
);
}

Expand Down
3 changes: 1 addition & 2 deletions client/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ const Sitemap = React.lazy(() => import("./sitemap"));

function Layout({ pageType, children }) {
const { pathname } = useLocation();
const isServer = useIsServer();
const [category, setCategory] = React.useState<string | null>(
getCategoryByPathname(pathname)
);
Expand All @@ -52,7 +51,7 @@ function Layout({ pageType, children }) {
category ? `category-${category}` : ""
} ${pageType}`}
>
{!isServer && <TopPlacement />}
<TopPlacement />
{pageType !== "document-page" && (
<TopNavigation extraClasses="main-document-header-container" />
)}
Expand Down
3 changes: 2 additions & 1 deletion client/src/document/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
top: 0;
z-index: var(--z-index-top);
@media screen and (min-width: $screen-md) {
.top-banner.visible ~ & {
.top-banner.visible ~ &,
.top-banner.loading ~ & {
top: var(--top-banner-height);
}
}
Expand Down
20 changes: 13 additions & 7 deletions client/src/placement-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export enum Status {
success = "success",
geoUnsupported = "geo_unsupported",
capReached = "cap_reached",
loading = "loading",
}

export interface Fallback {
Expand All @@ -19,7 +20,7 @@ export interface Fallback {
by: string;
}

export interface PlacementStatus {
export interface PlacementData {
status: Status;
click: string;
view: string;
Expand All @@ -36,7 +37,10 @@ export interface PlacementStatus {
}

type PlacementType = "side" | "top";
type PlacementData = Record<PlacementType, PlacementStatus>;
export interface PlacementContextData
extends Partial<Record<PlacementType, PlacementData>> {
status: Status;
}

const PLACEMENT_MAP: Record<PlacementType, RegExp> = {
side: /\/[^/]+\/(docs\/|blog\/|search$|_homepage)/i,
Expand All @@ -50,7 +54,7 @@ function placementTypes(pathname: string): string[] {
}

export const PlacementContext = React.createContext<
PlacementData | null | undefined
PlacementContextData | null | undefined
>(undefined);

export function PlacementProvider(props: { children: React.ReactNode }) {
Expand All @@ -63,7 +67,7 @@ export function PlacementProvider(props: { children: React.ReactNode }) {
isLoading,
isValidating,
mutate,
} = useSWR<PlacementData>(
} = useSWR<PlacementContextData>(
!PLACEMENT_ENABLED ||
user?.settings?.noAds ||
!placementTypes(location.pathname)
Expand All @@ -88,8 +92,8 @@ export function PlacementProvider(props: { children: React.ReactNode }) {
}

try {
const placementResponse: PlacementData = await response.json();
gleanClick(`pong: pong->status ${placementResponse.side.status}`);
const placementResponse: PlacementContextData = await response.json();
gleanClick(`pong: pong->status ${placementResponse.side?.status}`);
return placementResponse;
} catch (e) {
throw Error(response.statusText);
Expand All @@ -110,7 +114,9 @@ export function PlacementProvider(props: { children: React.ReactNode }) {
}, [location.pathname, mutate]);

return (
<PlacementContext.Provider value={isLoading || isValidating ? null : pong}>
<PlacementContext.Provider
value={isLoading || isValidating ? { status: Status.loading } : pong}
>
{props.children}
</PlacementContext.Provider>
);
Expand Down
1 change: 1 addition & 0 deletions client/src/telemetry/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const TOGGLE_PLUS_OFFLINE_DISABLED = "toggle_plus_offline_disabled";
export const TOGGLE_PLUS_OFFLINE_ENABLED = "toggle_plus_offline_enabled";
export const TOGGLE_PLUS_ADS_FREE_DISABLED = "toggle_plus_ads_free_disabled";
export const TOGGLE_PLUS_ADS_FREE_ENABLED = "toggle_plus_ads_free_enabled";
export const BANNER_BLOG_LAUNCH_CLICK = "banner_blog_launch_click";

export const PLUS_UPDATES = Object.freeze({
EVENT_COLLAPSE: "plus_updates_event_collapse",
Expand Down
29 changes: 22 additions & 7 deletions client/src/ui/organisms/placement/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ section.place {
font-size: 0.625rem;

grid-template-areas: "pong pong pong" "no space note";
height: var(--top-banner-height);
margin: 0 auto;
width: 100%;

Expand Down Expand Up @@ -139,20 +140,34 @@ section.place {
--place-top-cta-color: var(--background-secondary);

background-color: var(--place-top-background);

border-bottom: 1px solid var(--border-primary);
height: var(--top-banner-height);
position: sticky;
top: 0;
z-index: var(--z-index-top);

&.empty {
height: 0;
}

&.visible {
border: 1px solid var(--border-primary);
height: fit-content;
&.fallback {
position: initial;
}

@media screen and (max-width: #{$screen-md - 1}) {
display: none;
}

.fallback-copy {
font-size: 1rem;
grid-column: 1/4;
line-height: var(--top-banner-height);
margin: 0 auto;

a:not(.button) {
color: var(--apis-accent-color);

&:hover,
&:focus {
text-decoration: underline;
}
}
}
}
50 changes: 41 additions & 9 deletions client/src/ui/organisms/placement/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { useUserData } from "../../../user-context";

import "./index.scss";
import { useGleanClick } from "../../../telemetry/glean-context";
import { PlacementStatus, usePlacement } from "../../../placement-context";
import {
PlacementData,
Status,
usePlacement,
} from "../../../placement-context";
import { BANNER_BLOG_LAUNCH_CLICK } from "../../../telemetry/constants";

interface Timer {
timeout: number | null;
Expand All @@ -13,7 +18,7 @@ interface Timer {
}

function viewed(
pong: PlacementStatus,
pong: PlacementData,
observer: IntersectionObserver | null = null
) {
navigator?.sendBeacon?.(
Expand Down Expand Up @@ -41,7 +46,26 @@ export function SidePlacement() {
);
}

function Fallback() {
const gleanClick = useGleanClick();

return (
<p className="fallback-copy">
Discover the latest web development insights on our new{" "}
<a
href="/en-US/blog/"
onClick={() => {
gleanClick(BANNER_BLOG_LAUNCH_CLICK);
}}
>
MDN Blog
</a>
</p>
);
}

export function TopPlacement() {
const isServer = useIsServer();
const placementData = usePlacement();
const { textColor, backgroundColor, ctaTextColor, ctaBackgroundColor } =
placementData?.top?.colors || {};
Expand All @@ -54,13 +78,21 @@ export function TopPlacement() {
].filter(([_, v]) => Boolean(v))
);

const status =
isServer || placementData?.status === Status.loading
? "loading"
: placementData?.top
? "visible"
: "fallback";

return (
<div
className={`top-banner ${placementData?.top ? "visible" : "empty"}`}
style={css}
>
{!placementData?.top ? (
<section className="place top container"></section>
<div className={`top-banner ${status}`} style={css}>
{isServer || !placementData?.top ? (
<section className="place top container">
{!isServer && placementData?.status !== Status.loading && (
<Fallback />
)}
</section>
) : (
<PlacementInner
pong={placementData.top}
Expand All @@ -80,7 +112,7 @@ export function PlacementInner({
imageWidth,
imageHeight,
}: {
pong: PlacementStatus;
pong: PlacementData;
extraClassNames?: string[];
cta?: string;
imageWidth?: number;
Expand Down

0 comments on commit 2e730ae

Please sign in to comment.