diff --git a/dotcom-rendering/src/components/Weather.stories.tsx b/dotcom-rendering/src/components/Weather.stories.tsx deleted file mode 100644 index 030a0f5f39..0000000000 --- a/dotcom-rendering/src/components/Weather.stories.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { splitTheme } from '../../.storybook/decorators/splitThemeDecorator'; -import { Weather as WeatherComponent } from './Weather'; - -const meta: Meta = { - title: 'Components/Weather', - component: WeatherComponent, -}; - -type Story = StoryObj; -export const Weather: Story = { - args: { - edition: 'UK', - location: { - id: '712993', - city: 'Ickleford', - country: 'United Kingdom', - }, - now: { - description: 'Sunny', - icon: 1, - link: 'http://www.accuweather.com/en/gb/ickleford/sg5-3/current-weather/712993?lang=en-us', - dateTime: null, - temperature: { metric: 27, imperial: 81 }, - }, - forecast: { - '3h': { - description: 'Mostly sunny', - icon: 2, - link: '', - dateTime: '2024-07-31T20:00:00+01:00', - temperature: { metric: 23, imperial: 74 }, - }, - '6h': { - description: 'Partly cloudy', - icon: 35, - link: '', - dateTime: '2024-07-31T23:00:00+01:00', - temperature: { metric: 18, imperial: 65 }, - }, - '9h': { - description: 'Cloudy', - icon: 7, - link: '', - dateTime: '2024-08-01T02:00:00+01:00', - temperature: { metric: 15, imperial: 59 }, - }, - '12h': { - description: 'Intermittent clouds', - icon: 36, - link: '', - dateTime: '2024-08-01T05:00:00+01:00', - temperature: { metric: 16, imperial: 61 }, - }, - }, - }, - decorators: [splitTheme()], -}; - -export const WeatherUS: Story = { - ...Weather, - args: { - ...Weather.args, - edition: 'US', - }, -}; - -export default meta; diff --git a/dotcom-rendering/src/components/Weather.tsx b/dotcom-rendering/src/components/Weather.tsx deleted file mode 100644 index b1ca8b5aaa..0000000000 --- a/dotcom-rendering/src/components/Weather.tsx +++ /dev/null @@ -1,293 +0,0 @@ -/** - * "WEATHER" - * - * Whether the weather be fine, - * Or whether the weather be not, - * Whether the weather be cold, - * Or whether the weather be hot, - * We'll weather the weather - * Whatever the weather, - * Whether we like it or not! - * - * Author: Anonymous British - */ - -import { css, keyframes } from '@emotion/react'; -import { - between, - from, - space, - textSans15, - textSans17, - until, - visuallyHidden, -} from '@guardian/source/foundations'; -import { - SvgChevronDownSingle, - SvgChevronUpSingle, - SvgExternal, -} from '@guardian/source/react-components'; -import { useId } from 'react'; -import type { EditionId } from '../lib/edition'; -import { palette } from '../palette'; -import type { WeatherApiData, WeatherData } from '../types/weather'; -import { WeatherSlot } from './WeatherSlot'; - -const visuallyHiddenCSS = css` - ${visuallyHidden} -`; - -const weatherCSS = css` - animation: ${keyframes`from { opacity: 0; } to { opacity: 1; }`} 250ms; - --border: 1px solid ${palette('--article-border')}; - width: 100%; - display: flex; - flex-direction: row; - flex-wrap: wrap; - align-items: center; - letter-spacing: -0.56px; - - ${between.tablet.and.leftCol} { - padding-top: 6px; - height: 52px; - } -`; - -const locationCSS = css` - flex: 1; - ${textSans17}; - padding: 12px 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - - ${until.tablet} { - flex-basis: 100%; - border-bottom: var(--border); - } - - ${between.tablet.and.leftCol} { - padding-right: 1ch; - } - - ${from.leftCol} { - flex-basis: 100%; - border-bottom: var(--border); - } -`; - -const nowCSS = css` - ${until.tablet} { - justify-content: flex-end; - flex-basis: 100%; - border-bottom: var(--border); - order: -1; - padding-bottom: 4px; - margin-top: -${space[9]}px; - } - - ${from.leftCol} { - flex-basis: 100%; - border-bottom: var(--border); - padding-top: 8px; - padding-bottom: 24px; - } -`; - -const slotCSS = css` - display: flex; - flex: 1; - padding-top: 6px; - - border-left: var(--border); - padding-left: 6px; - - ${until.mobileLandscape} { - &.forecast-4 { - display: none; - } - } - - ${until.tablet} { - &.now, - &.forecast-1 { - border-left: none; - } - } - - ${between.tablet.and.desktop} { - padding-left: 6px; - padding-right: 6px; - &.forecast-3, - &.forecast-4 { - display: none; - } - } - - ${between.desktop.and.wide} { - padding-left: 4px; - padding-right: 4px; - &.forecast-4 { - display: none; - } - } - - ${from.leftCol} { - &.now, - &.forecast-1 { - border-left: none; - } - } -`; - -const linkCSS = css` - a { - ${textSans15}; - color: inherit; - text-decoration: none; - display: block; - display: flex; - flex-direction: row; - align-items: center; - - &:hover { - text-decoration: underline; - } - } - - ${until.tablet} { - flex-basis: 100%; - padding-top: 12px; - } - - ${from.leftCol} { - padding-top: 24px; - flex-basis: 100%; - } -`; - -const ExternalLinkIcon = () => ( -
- -
-); - -export interface WeatherProps - extends Pick { - now: WeatherData; - edition: EditionId; - link?: string; -} - -const collapsibleStyles = css` - .checkbox-label { - display: none; - } - - .checkbox { - display: none; - } - - ${until.tablet} { - .checkbox-label { - display: inherit; - } - - .checkbox:not(:checked) ~ .now { - border-bottom: none; - } - - .checkbox:not(:checked) ~ .now > .checkbox-label { - svg:first-child { - display: none; - } - } - - .checkbox:checked ~ .now > .checkbox-label { - svg:last-child { - display: none; - } - } - - .checkbox:not(:checked) ~ .collapsible { - display: none; - } - } -`; - -export const WeatherPlaceholder = () => ( - -); - -export const Weather = ({ - location, - now, - forecast, - edition, - link, -}: WeatherProps) => { - const checkboxId = useId(); - - return ( - - ); -}; diff --git a/dotcom-rendering/src/components/WeatherSlot.tsx b/dotcom-rendering/src/components/WeatherSlot.tsx deleted file mode 100644 index 581e8ad3b3..0000000000 --- a/dotcom-rendering/src/components/WeatherSlot.tsx +++ /dev/null @@ -1,229 +0,0 @@ -import type { SerializedStyles } from '@emotion/react'; -import { css, keyframes } from '@emotion/react'; -import { isString } from '@guardian/libs'; -import { - from, - palette, - textSans12, - textSans17, - textSans24, - until, - visuallyHidden, -} from '@guardian/source/foundations'; -import { lazy, Suspense } from 'react'; -import { type EditionId, getEditionFromId } from '../lib/edition'; -import type { WeatherData } from '../types/weather'; - -interface IconProps { - size?: number; -} - -const formatTemperature = (value: number, unit: string) => - `${value}°${unit.toLocaleUpperCase()}`; - -const formatTime = (dateTime: string, edition: EditionId) => - new Date(dateTime).toLocaleTimeString( - getEditionFromId(edition).dateLocale, - { - hour: 'numeric', - // US and AU dates include AM/PM markers that cause the timestamp to - // wrap onto the next line, which we don't want for the design. - // Given that the times are always on the hour, i.e. the minutes are - // always "00", we can choose to show the hour only without losing - // information. This shortens the timestamp and keeps it on one line. - minute: - edition === 'US' || edition === 'AU' ? undefined : 'numeric', - }, - ); - -const visuallyHiddenCSS = css` - ${visuallyHidden} -`; - -const slotCSS = css` - animation: ${keyframes`from { opacity: 0; } to { opacity: 1; }`} 250ms; - position: relative; - display: inline-flex; - flex-direction: column; - align-items: flex-start; - justify-content: center; - padding-left: 40px; - min-height: 40px; /* icon height (32) + 4 + 4 */ - - ${from.leftCol} { - padding-left: 0; - } -`; - -const timeCSS = css` - ${textSans12}; - display: block; -`; - -const nowCSS = [ - timeCSS, - css` - ${until.tablet} { - display: none; - } - - ${from.leftCol} { - padding-bottom: 4px; - } - `, -]; - -const tempCSS = (isNow: boolean) => [ - css` - display: block; - ${textSans17}; - - ${from.leftCol} { - order: 1; - padding-top: 4px; - } - `, - isNow && - css` - ${from.leftCol} { - ${textSans24}; - line-height: 1; - } - `, -]; - -const iconCSS = (size: number) => css` - position: absolute; - top: 50%; - left: 0px; - margin-top: -16px; - - ${from.leftCol} { - position: static; - margin-top: 0; - height: ${size}px; - width: ${size}px; - margin-right: ${size === 50 ? 8 : 0}px; - } -`; - -const flexRow = css` - display: flex; - flex-direction: row; -`; - -const flexColumn = css` - display: flex; - flex-direction: column; -`; - -const flexRowBelowLeftCol = css` - ${until.leftCol} { - ${flexRow} - } -`; - -const flexColumnBelowLeftCol = css` - ${until.leftCol} { - ${flexColumn} - } -`; - -const LoadingIcon = () => ( - -); - -export type WeatherSlotProps = WeatherData & { - edition: EditionId; - css?: SerializedStyles; -}; - -export const WeatherSlot = ({ - icon, - temperature, - dateTime, - description, - edition, - ...props -}: WeatherSlotProps) => { - const isNow = !isString(dateTime); - - const Icon = lazy(() => - import(`../static/icons/weather/weather-${icon}.svg`).then( - ({ default: Component }) => { - return { - default: ({ size = 32 }: IconProps) => ( - - ), - }; - }, - ), - ); - - return ( -

- - {isNow ? 'The current weather' : 'The forecast for'} - - {isNow ? ( -

-
- }> - - -
-
- - Now - - is - - {formatTemperature( - edition === 'US' - ? temperature.imperial - : temperature.metric, - edition === 'US' ? 'F' : 'C', - )} - - - , {description.toLowerCase()}. - -
-
- ) : ( -
-
- - is - - {formatTemperature( - edition === 'US' - ? temperature.imperial - : temperature.metric, - edition === 'US' ? 'F' : 'C', - )} - - - , {description.toLowerCase()}. - -
- }> - - -
- )} -

- ); -}; diff --git a/dotcom-rendering/src/components/WeatherWrapper.importable.tsx b/dotcom-rendering/src/components/WeatherWrapper.importable.tsx deleted file mode 100644 index 007b2c2294..0000000000 --- a/dotcom-rendering/src/components/WeatherWrapper.importable.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import type { EditionId } from '../lib/edition'; -import { useApi } from '../lib/useApi'; -import { parseWeatherData } from '../types/weather'; -import { Weather, WeatherPlaceholder } from './Weather'; - -const appendPartnerCodeToUrl = ( - url: string | undefined, -): string | undefined => { - if (!url) return undefined; - - try { - const link = new URL(url); - link.searchParams.append('partner', 'web_guardian_adc'); - return link.href; - } catch { - return undefined; - } -}; - -type Props = { - ajaxUrl: string; - edition: EditionId; -}; - -export const WeatherWrapper = ({ ajaxUrl, edition }: Props) => { - const { data, error } = useApi(`${ajaxUrl}/weather.json`); - - if (error) { - window.guardian.modules.sentry.reportError(error, 'weather'); - } - - const result = parseWeatherData(data); - - if (result.kind === 'error' && result.error === 'ParsingError') { - window.guardian.modules.sentry.reportError( - Error('Invalid weather data'), - 'weather', - ); - } - - return result.kind === 'error' ? ( - - ) : ( - - ); -}; diff --git a/dotcom-rendering/src/layouts/FrontLayout.tsx b/dotcom-rendering/src/layouts/FrontLayout.tsx index bf08ad5b3a..21bd5320be 100644 --- a/dotcom-rendering/src/layouts/FrontLayout.tsx +++ b/dotcom-rendering/src/layouts/FrontLayout.tsx @@ -93,7 +93,6 @@ const isToggleable = ( const decideLeftContent = ( front: DCRFrontType, collection: DCRCollectionType, - hasPageSkin: boolean, ) => { // show CPScott? if ( @@ -105,24 +104,6 @@ const decideLeftContent = ( return ; } - // show weather? - if ( - front.config.switches['weather'] && - isNetworkFrontPageId(front.config.pageId) && - // based on https://github.com/guardian/frontend/blob/473aafd168fec7f2a578a52c8e84982e3ec10fea/common/app/views/support/GetClasses.scala#L107 - collection.displayName.toLowerCase() === 'headlines' && - !hasPageSkin - ) { - return ( - - - - ); - } - // show nothing! return null; }; @@ -661,7 +642,6 @@ export const FrontLayout = ({ front, NAV }: Props) => { leftContent={decideLeftContent( front, collection, - hasPageSkin, )} sectionId={ophanName} collectionId={collection.id} diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index 121fae015e..18b7c77d20 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -7246,10 +7246,6 @@ const paletteColours = { light: () => sourcePalette.news[400], dark: () => '#DC2E1C', }, - '--weather-icon': { - light: () => sourcePalette.neutral[97], - dark: () => sourcePalette.neutral[7], - }, '--witness-title-author': { light: witnessTitleAuthor, dark: witnessTitleAuthor, diff --git a/dotcom-rendering/src/types/weather.ts b/dotcom-rendering/src/types/weather.ts deleted file mode 100644 index 6afd6cad43..0000000000 --- a/dotcom-rendering/src/types/weather.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { Output } from 'valibot'; -import { - nullable, - number, - object, - optional, - safeParse, - string, - transform, - tuple, - unknown, -} from 'valibot'; -import type { Result } from '../lib/result'; -import { error, ok } from '../lib/result'; - -const weatherDataSchema = object({ - description: string(), - icon: number(), - link: nullable(string(), ''), - dateTime: nullable(string()), - temperature: object({ - metric: number(), - imperial: number(), - }), -}); -export type WeatherData = Output; - -const weatherApiDataSchema = object({ - location: object({ - id: string(), - city: string(), - country: string(), - }), - weather: weatherDataSchema, - /** - * Our weather API returns 24h forecast. - * Each forecast is 1 hour offset from the previous forecast, and the first forecast is 1 hour offset from now. - */ - forecast: transform( - tuple([ - unknown(), - unknown(), - unknown(), - weatherDataSchema, - unknown(), - unknown(), - weatherDataSchema, - unknown(), - unknown(), - weatherDataSchema, - unknown(), - unknown(), - weatherDataSchema, - ]), - (forecast) => ({ - '3h': forecast[3], - '6h': forecast[6], - '9h': forecast[9], - '12h': forecast[12], - }), - ), -}); -export type WeatherApiData = Output; - -export const parseWeatherData = ( - data: unknown, -): Result<'Loading' | 'ParsingError', WeatherApiData> => { - const result = safeParse(optional(weatherApiDataSchema), data); - if (!result.success) return error('ParsingError'); - return result.output ? ok(result.output) : error('Loading'); -};