Files
supabase/apps/studio/components/ui/BannerStack/BannerStackProvider.tsx
T
Danny White ba82106697 chore(www + studio): remove expired Select 2026 promo banners (#51245)
## Problem

Supabase Select 2026 has finished. The promo banners already hide via
the scheduled expiry from #51006, but the campaign code, assets, and
wiring are still in the tree.

## Solution

Remove the Select 2026 sitewide promotion across www and Studio:

- Delete shared `Select26*` banner code, font, and tests from
`ui-patterns`
- Delete Studio `BannerSelect2026*` and its Banner Stack registration
- Unmount the www announcement banner and revert the State of Startups
spacing that only existed for it
- Drop the Select-only session-replay `data-band` allowlist entry and
lint ratchet baseline

Event go pages, blog posts, and other Select content are left alone. The
`Announcement` shell stays for the next campaign.

## Review instructions

1. Open the www homepage on the deploy preview. Confirm there is no
Select announcement bar above the nav.
2. Open `/state-of-startups` on the deploy preview. Confirm the hero
still looks correct with no extra top gap from the removed banner.
3. Open a hosted Studio dashboard page on the deploy preview. Confirm
the Banner Stack no longer shows a Select card.

## Checklist

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [ ] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)
2026-10-05 18:36:13 +11:00

69 lines
2.3 KiB
TypeScript

import { createContext, useCallback, useContext, useState } from 'react'
export const BANNER_ID = {
INDEX_ADVISOR: 'index-advisor-banner',
TABLE_EDITOR_QUEUE_OPERATIONS: 'table-editor-queue-operations-banner',
RLS_EVENT_TRIGGER: 'rls-event-trigger-banner',
FREE_MICRO_UPGRADE: 'free-micro-upgrade-banner',
PRIVACY_POLICY_UPDATE: 'privacy-policy-update-banner',
LOGS_ALL_DEPRECATION: 'logs-all-deprecation-banner',
EXPLORER: 'explorer-banner',
} as const
export type BannerId = (typeof BANNER_ID)[keyof typeof BANNER_ID]
export interface Banner {
id: BannerId
content: React.ReactNode
isDismissed: boolean
priority?: number
onDismiss?: () => void
}
interface BannerStackContextType {
banners: Banner[]
addBanner: (banner: Banner) => void
dismissBanner: (id: BannerId) => void
}
const BannerStackContext = createContext<BannerStackContextType | undefined>(undefined)
export const BannerStackProvider = ({ children }: { children: React.ReactNode }) => {
const [banners, setBanners] = useState<Banner[]>([])
const addBanner = useCallback((banner: Banner) => {
setBanners((prev) => {
const existingIndex = prev.findIndex((b) => b.id === banner.id)
if (existingIndex !== -1) {
if (!prev[existingIndex].isDismissed) return prev
const revived = [...prev]
revived[existingIndex] = banner
return revived.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))
}
const newBanners = [...prev, banner]
return newBanners.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))
})
}, [])
const dismissBanner = useCallback((id: string) => {
setBanners((prev) => prev.map((b) => (b.id === id ? { ...b, isDismissed: true } : b)))
setTimeout(() => {
// A later addBanner can revive this id before the exit animation
// finishes. Drop it only if it is still dismissed.
setBanners((prev) => prev.filter((b) => b.id !== id || !b.isDismissed))
}, 300)
}, [])
return (
<BannerStackContext.Provider value={{ banners, addBanner, dismissBanner }}>
{children}
</BannerStackContext.Provider>
)
}
export const useBannerStack = () => {
const context = useContext(BannerStackContext)
if (!context) throw new Error('useBannerStack must be used within BannerStackProvider')
return context
}