Skip to content
Merged
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 @@ -41,7 +41,15 @@ const ActionGrid = ({
</div>
);

export const GivebackActionCatalog = (): ReactElement => {
interface GivebackActionCatalogProps {
// Scrolls the tab strip back to the top so a filtered list always starts in
// view (no jump from the previous scroll position).
onFilter?: () => void;
}

export const GivebackActionCatalog = ({
onFilter,
}: GivebackActionCatalogProps): ReactElement => {
const { logEvent } = useLogContext();
const { actions, categories, isPending } = useContributionActions(true);
const [selectedCategory, setSelectedCategory] = useState<string>(ALL_FILTER);
Expand Down Expand Up @@ -73,6 +81,7 @@ export const GivebackActionCatalog = (): ReactElement => {
extra: JSON.stringify({ category_id: categoryId }),
});
setSelectedCategory(categoryId);
onFilter?.();
};

const toggleShowAll = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ import React from 'react';

// daily.dev brand canvas: the signature pink → purple → blue gradient (cabbage →
// onion → blueCheese) glowing softly from the top of a dark surface, the way the
// marketing site and Plus pages feel smooth and pastel, no hard shapes or
// marketing site and Plus pages feel - smooth and pastel, no hard shapes or
// grids. Every tint is a theme token via color-mix so it tracks the design
// system, and a whisper of grain keeps the gradient from banding.
// The sweep fades only vertically (a top-anchored linear mask) so it fills the
// full width edge to edge - including the top corners. A radial mask centered at
// the top looks prettier but starves the corners, and once the page sits inside
// the app's rounded, clipped content card those starved corners read as dark
// gaps where the gradient gets "cut". A straight downward fade has no such
// horizontal falloff, so the brand color reaches every corner cleanly.
const brandSweep: CSSProperties = {
backgroundImage:
'linear-gradient(125deg, ' +
Expand All @@ -14,12 +20,12 @@ const brandSweep: CSSProperties = {
'color-mix(in srgb, var(--theme-accent-blueCheese-default) 30%, transparent) 62%, ' +
'color-mix(in srgb, var(--theme-accent-onion-default) 34%, transparent) 82%, ' +
'color-mix(in srgb, var(--theme-accent-cabbage-default) 34%, transparent))',
maskImage: 'radial-gradient(125% 75% at 50% -10%, black, transparent 70%)',
maskImage: 'linear-gradient(to bottom, black 0%, black 32%, transparent 92%)',
WebkitMaskImage:
'radial-gradient(125% 75% at 50% -10%, black, transparent 70%)',
'linear-gradient(to bottom, black 0%, black 32%, transparent 92%)',
};

// A soft horizon glow anchored to the bottom edge for depth a wide, flat
// A soft horizon glow anchored to the bottom edge for depth - a wide, flat
// ellipse, so it's ambient light rather than a circle.
const horizonGlow: CSSProperties = {
background:
Expand All @@ -38,12 +44,18 @@ const vignette: CSSProperties = {
};

export const GivebackBackground = (): ReactElement => (
// The app's content card (MainLayout) wraps the page in a rounded, clipped
// border with a 2px inner padding on `laptop:`. Filling only `inset-0` leaves
// that padding (the dark card bg) showing as a crescent where our square
// corners meet the card's rounded corner. Bleed a few px past the padding so
// the gradient reaches the inner border edge; the card's `overflow-clip` +
// `rounded-24` then clips it to a clean rounded corner with no dark gap.
<div
aria-hidden
className="pointer-events-none absolute inset-0 overflow-hidden"
className="pointer-events-none absolute inset-0 overflow-hidden laptop:-inset-1"
>
{/* The brand glow is a fixed-height hero band anchored to the top. Sizing it
in px (not inset-0) keeps it consistent otherwise its mask scales with
in px (not inset-0) keeps it consistent - otherwise its mask scales with
the page height and the glow spreads down on longer pages. */}
<div className="absolute inset-x-0 top-0 h-[42rem]" style={brandSweep} />
<div className="absolute inset-0" style={horizonGlow} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FlexCol } from '../../../components/utilities';
import { PlayIcon } from '../../../components/icons';
import { IconSize } from '../../../components/Icon';

// The campaign clip behind a lightweight click-to-play facade the heavy embed
// The campaign clip behind a lightweight click-to-play facade - the heavy embed
// only mounts on click, so the hero never autoplays or loads the iframe up
// front. The placeholder poster shows the Giveback charm on the brand backdrop;
// swap VIDEO_ID for the final film when it's ready.
Expand All @@ -15,7 +15,7 @@ const CHARM_IMAGE_SRC =
'https://media.daily.dev/image/upload/s--d1dldAty--/f_auto,q_auto/v1780848838/public/daily.dev%20Charm%20-%20Giveback%20(1)';

// A dark, on-brand backdrop (page background + a soft cabbage glow from the top)
// so the charm rendered with `mix-blend-screen` reads as floating, exactly
// so the charm - rendered with `mix-blend-screen` - reads as floating, exactly
// like it does elsewhere on the dark page.
const posterBackdrop: CSSProperties = {
background:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { FlexCol, FlexRow } from '../../../components/utilities';
import {
Typography,
TypographyColor,
TypographyTag,
TypographyType,
} from '../../../components/typography/Typography';
import { GivebackFilterChip } from './GivebackFilterChip';
Expand Down Expand Up @@ -52,7 +51,7 @@ export const GivebackCauseSelection = ({

if (isLoading) {
return (
<div className="grid grid-cols-2 gap-3 tablet:grid-cols-3">
<div className="grid grid-cols-1 gap-3 tablet:grid-cols-2">
{Array.from({ length: 6 }).map((_, index) => (
<div
// eslint-disable-next-line react/no-array-index-key
Expand All @@ -77,16 +76,6 @@ export const GivebackCauseSelection = ({

return (
<FlexCol className="gap-6">
<Typography
tag={TypographyTag.P}
type={TypographyType.Callout}
color={TypographyColor.Secondary}
className="max-w-2xl"
>
Pick as many as you like. daily.dev funds every donation, and you can
change them anytime.
</Typography>

{categories.length > 0 && (
<FlexRow className="flex-wrap gap-2">
<GivebackFilterChip
Expand All @@ -105,7 +94,7 @@ export const GivebackCauseSelection = ({
</FlexRow>
)}

<div className="grid grid-cols-2 gap-3 tablet:grid-cols-3">
<div className="grid grid-cols-1 gap-3 tablet:grid-cols-2">
{visibleCauses.map(({ cause, index }) => (
<GivebackCauseCard
key={cause.id}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -36,34 +36,34 @@ const renderSummary = (status: Partial<ContributionStatus>) => {
return render(<GivebackFundingSummary />);
};

it('renders campaign points as dollars against the cycle goal with backers', () => {
it('renders campaign points as dollars against the cycle goal with contributors', () => {
renderSummary({
currentCyclePoints: 5000,
currentCycleTargetPoints: 10000,
contributorsCount: 12480,
});

expect(screen.getByText('$5,000')).toBeInTheDocument();
expect(screen.getByText('pledged of $10,000 goal')).toBeInTheDocument();
expect(screen.getByText('unlocked of $10,000 goal')).toBeInTheDocument();
expect(screen.getByText('50%')).toBeInTheDocument();
expect(screen.getByText(/12,480 backers/)).toBeInTheDocument();
expect(screen.getByText(/12,480 contributors/)).toBeInTheDocument();
});

it('shows a goal-forward empty state when nothing is pledged yet', () => {
it('shows a goal-forward empty state when nothing is unlocked yet', () => {
renderSummary({
currentCyclePoints: 0,
currentCycleTargetPoints: 10000,
contributorsCount: 0,
});

expect(screen.getByText('$10,000')).toBeInTheDocument();
expect(screen.getByText("goal we'll fund together")).toBeInTheDocument();
expect(
screen.getByText('goal to unlock for good causes'),
screen.getByText('Be the first to move the meter.'),
).toBeInTheDocument();
expect(screen.getByText('Be the first to back this.')).toBeInTheDocument();
// None of the "$0 / 0% / 0 backers" zeros leak through.
// None of the "$0 / 0% / 0 contributors" zeros leak through.
expect(screen.queryByText('$0')).not.toBeInTheDocument();
expect(screen.queryByText(/0 backers/)).not.toBeInTheDocument();
expect(screen.queryByText(/0 contributors/)).not.toBeInTheDocument();
});

it('renders a skeleton without zeros before data arrives', () => {
Expand All @@ -75,5 +75,5 @@ it('renders a skeleton without zeros before data arrives', () => {
render(<GivebackFundingSummary />);

expect(screen.queryByText('$0')).not.toBeInTheDocument();
expect(screen.queryByText(/pledged of/)).not.toBeInTheDocument();
expect(screen.queryByText(/unlocked of/)).not.toBeInTheDocument();
});
Loading
Loading