diff --git a/apps-rendering/package.json b/apps-rendering/package.json index 50d61212527..6f42b8c8fa4 100644 --- a/apps-rendering/package.json +++ b/apps-rendering/package.json @@ -86,7 +86,7 @@ "eslint-plugin-jsx-a11y": "6.7.1", "eslint-plugin-react": "7.33.2", "express": "4.21.0", - "html-webpack-plugin": "5.6.0", + "html-webpack-plugin": "5.6.3", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0", "jsdom": "16.7.0", @@ -103,9 +103,9 @@ "tslib": "2.6.2", "tsx": "4.6.2", "typescript": "5.5.3", - "webpack": "5.94.0", + "webpack": "5.97.1", "webpack-cli": "5.1.4", - "webpack-dev-server": "5.0.4", + "webpack-dev-server": "5.1.0", "webpack-manifest-plugin": "5.0.0", "whatwg-fetch": "3.6.19", "winston": "3.11.0", diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index 100e4cb551a..6ab042c52b6 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -53,7 +53,7 @@ "@guardian/shimport": "1.0.2", "@guardian/source": "8.0.0", "@guardian/source-development-kitchen": "12.0.0", - "@guardian/support-dotcom-components": "3.1.0", + "@guardian/support-dotcom-components": "3.2.0", "@guardian/tsconfig": "0.2.0", "@playwright/test": "1.45.3", "@sentry/browser": "7.75.1", @@ -191,12 +191,12 @@ "unified": "11.0.5", "valibot": "0.28.1", "web-vitals": "4.2.3", - "webpack": "5.94.0", + "webpack": "5.97.1", "webpack-assets-manifest": "5.2.1", "webpack-bundle-analyzer": "4.10.2", "webpack-cli": "5.1.4", "webpack-dev-middleware": "7.4.2", - "webpack-dev-server": "5.0.4", + "webpack-dev-server": "5.1.0", "webpack-hot-middleware": "2.26.1", "webpack-hot-server-middleware": "0.6.1", "webpack-manifest-plugin": "5.0.0", diff --git a/dotcom-rendering/src/client/userFeatures/user-features.test.ts b/dotcom-rendering/src/client/userFeatures/user-features.test.ts index e96a1fc0eec..a720abd3697 100644 --- a/dotcom-rendering/src/client/userFeatures/user-features.test.ts +++ b/dotcom-rendering/src/client/userFeatures/user-features.test.ts @@ -35,18 +35,11 @@ const getAuthStatus = getAuthStatus_ as jest.MockedFunction< const PERSISTENCE_KEYS = { USER_FEATURES_EXPIRY_COOKIE: 'gu_user_features_expiry', - PAYING_MEMBER_COOKIE: 'gu_paying_member', - RECURRING_CONTRIBUTOR_COOKIE: 'gu_recurring_contributor', AD_FREE_USER_COOKIE: 'GU_AF1', ACTION_REQUIRED_FOR_COOKIE: 'gu_action_required_for', DIGITAL_SUBSCRIBER_COOKIE: 'gu_digital_subscriber', SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE: 'gu.contributions.contrib-timestamp', - ONE_OFF_CONTRIBUTION_DATE_COOKIE: 'gu_one_off_contribution_date', HIDE_SUPPORT_MESSAGING_COOKIE: 'gu_hide_support_messaging', - SUPPORT_MONTHLY_CONTRIBUTION_COOKIE: - 'gu.contributions.recurring.contrib-timestamp.Monthly', - SUPPORT_ANNUAL_CONTRIBUTION_COOKIE: - 'gu.contributions.recurring.contrib-timestamp.Annual', }; const setAllFeaturesData = (opts: { isExpired: boolean }) => { @@ -58,11 +51,6 @@ const setAllFeaturesData = (opts: { isExpired: boolean }) => { const adFreeExpiryDate = opts.isExpired ? new Date(currentTime - msInOneDay * 2) : new Date(currentTime + msInOneDay * 2); - setCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE, value: 'true' }); - setCookie({ - name: PERSISTENCE_KEYS.RECURRING_CONTRIBUTOR_COOKIE, - value: 'true', - }); setCookie({ name: PERSISTENCE_KEYS.DIGITAL_SUBSCRIBER_COOKIE, value: 'true', @@ -86,8 +74,6 @@ const setAllFeaturesData = (opts: { isExpired: boolean }) => { }; const deleteAllFeaturesData = () => { - removeCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE }); - removeCookie({ name: PERSISTENCE_KEYS.RECURRING_CONTRIBUTOR_COOKIE }); removeCookie({ name: PERSISTENCE_KEYS.DIGITAL_SUBSCRIBER_COOKIE }); removeCookie({ name: PERSISTENCE_KEYS.USER_FEATURES_EXPIRY_COOKIE }); removeCookie({ name: PERSISTENCE_KEYS.AD_FREE_USER_COOKIE }); @@ -125,14 +111,6 @@ describe('Refreshing the features data', () => { it('Does not delete the data just because it has expired', async () => { setAllFeaturesData({ isExpired: true }); await refresh(); - expect( - getCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE }), - ).toBe('true'); - expect( - getCookie({ - name: PERSISTENCE_KEYS.RECURRING_CONTRIBUTOR_COOKIE, - }), - ).toBe('true'); expect( getCookie({ name: PERSISTENCE_KEYS.USER_FEATURES_EXPIRY_COOKIE, @@ -148,15 +126,6 @@ describe('Refreshing the features data', () => { await refresh(); expect(fetchJsonSpy).not.toHaveBeenCalled(); }); - - it('Performs an update if membership-frontend wipes just the paying-member cookie', async () => { - // Set everything except paying-member cookie - setAllFeaturesData({ isExpired: true }); - removeCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE }); - - await refresh(); - expect(fetchJsonSpy).toHaveBeenCalledTimes(1); - }); }); }); describe('If user signed out', () => { @@ -178,14 +147,6 @@ describe('If user signed out', () => { expect( getCookie({ name: PERSISTENCE_KEYS.AD_FREE_USER_COOKIE }), ).toBeNull(); - expect( - getCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE }), - ).toBeNull(); - expect( - getCookie({ - name: PERSISTENCE_KEYS.RECURRING_CONTRIBUTOR_COOKIE, - }), - ).toBeNull(); expect( getCookie({ name: PERSISTENCE_KEYS.DIGITAL_SUBSCRIBER_COOKIE }), ).toBeNull(); @@ -270,14 +231,6 @@ describe('Storing new feature data', () => { }), ); return refresh().then(() => { - expect( - getCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE }), - ).toBe('false'); - expect( - getCookie({ - name: PERSISTENCE_KEYS.RECURRING_CONTRIBUTOR_COOKIE, - }), - ).toBe('false'); expect( getCookie({ name: PERSISTENCE_KEYS.DIGITAL_SUBSCRIBER_COOKIE }), ).toBe('false'); @@ -300,14 +253,6 @@ describe('Storing new feature data', () => { }), ); return refresh().then(() => { - expect( - getCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE }), - ).toBe('true'); - expect( - getCookie({ - name: PERSISTENCE_KEYS.RECURRING_CONTRIBUTOR_COOKIE, - }), - ).toBe('true'); expect( getCookie({ name: PERSISTENCE_KEYS.DIGITAL_SUBSCRIBER_COOKIE }), ).toBe('true'); diff --git a/dotcom-rendering/src/client/userFeatures/user-features.ts b/dotcom-rendering/src/client/userFeatures/user-features.ts index 6229e1ef83f..333c9a2424f 100644 --- a/dotcom-rendering/src/client/userFeatures/user-features.ts +++ b/dotcom-rendering/src/client/userFeatures/user-features.ts @@ -27,15 +27,11 @@ import { import type { UserFeaturesResponse } from './user-features-lib'; const USER_FEATURES_EXPIRY_COOKIE = 'gu_user_features_expiry'; -const PAYING_MEMBER_COOKIE = 'gu_paying_member'; const ACTION_REQUIRED_FOR_COOKIE = 'gu_action_required_for'; const DIGITAL_SUBSCRIBER_COOKIE = 'gu_digital_subscriber'; const HIDE_SUPPORT_MESSAGING_COOKIE = 'gu_hide_support_messaging'; const AD_FREE_USER_COOKIE = 'GU_AF1'; -const RECURRING_CONTRIBUTOR_COOKIE = 'gu_recurring_contributor'; -const ONE_OFF_CONTRIBUTION_DATE_COOKIE = 'gu_one_off_contribution_date'; - const forcedAdFreeMode = !!/[#&]noadsaf(&.*)?$/.exec(window.location.hash); const userHasData = () => { @@ -43,9 +39,6 @@ const userHasData = () => { getAdFreeCookie() ?? getCookie({ name: ACTION_REQUIRED_FOR_COOKIE }) ?? getCookie({ name: USER_FEATURES_EXPIRY_COOKIE }) ?? - getCookie({ name: PAYING_MEMBER_COOKIE }) ?? - getCookie({ name: RECURRING_CONTRIBUTOR_COOKIE }) ?? - getCookie({ name: ONE_OFF_CONTRIBUTION_DATE_COOKIE }) ?? getCookie({ name: DIGITAL_SUBSCRIBER_COOKIE }) ?? getCookie({ name: HIDE_SUPPORT_MESSAGING_COOKIE }); return !!cookie; @@ -69,14 +62,6 @@ const persistResponse = (JsonResponse: UserFeaturesResponse) => { name: USER_FEATURES_EXPIRY_COOKIE, value: timeInDaysFromNow(1), }); - setCookie({ - name: PAYING_MEMBER_COOKIE, - value: String(JsonResponse.contentAccess.paidMember), - }); - setCookie({ - name: RECURRING_CONTRIBUTOR_COOKIE, - value: String(JsonResponse.contentAccess.recurringContributor), - }); setCookie({ name: DIGITAL_SUBSCRIBER_COOKIE, value: String(JsonResponse.contentAccess.digitalPack), @@ -85,12 +70,6 @@ const persistResponse = (JsonResponse: UserFeaturesResponse) => { name: HIDE_SUPPORT_MESSAGING_COOKIE, value: String(!JsonResponse.showSupportMessaging), }); - if (JsonResponse.oneOffContributionDate) { - setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: JsonResponse.oneOffContributionDate, - }); - } removeCookie({ name: ACTION_REQUIRED_FOR_COOKIE }); if (JsonResponse.alertAvailableFor) { @@ -110,12 +89,9 @@ const persistResponse = (JsonResponse: UserFeaturesResponse) => { const deleteOldData = (): void => { removeCookie({ name: AD_FREE_USER_COOKIE }); removeCookie({ name: USER_FEATURES_EXPIRY_COOKIE }); - removeCookie({ name: PAYING_MEMBER_COOKIE }); - removeCookie({ name: RECURRING_CONTRIBUTOR_COOKIE }); removeCookie({ name: ACTION_REQUIRED_FOR_COOKIE }); removeCookie({ name: DIGITAL_SUBSCRIBER_COOKIE }); removeCookie({ name: HIDE_SUPPORT_MESSAGING_COOKIE }); - removeCookie({ name: ONE_OFF_CONTRIBUTION_DATE_COOKIE }); }; const requestNewData = () => { diff --git a/dotcom-rendering/src/components/BigSixOnwardsContent.tsx b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx new file mode 100644 index 00000000000..59b36a6b0ed --- /dev/null +++ b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx @@ -0,0 +1,211 @@ +import { css } from '@emotion/react'; +import { from, headlineBold24, space } from '@guardian/source/foundations'; +import { decideFormat } from '../lib/articleFormat'; +import { useApi } from '../lib/useApi'; +import { palette } from '../palette'; +import type { DCRFrontCard } from '../types/front'; +import type { FETrailType } from '../types/trails'; +import { Card } from './Card/Card'; +import { LI } from './Card/components/LI'; +import { UL } from './Card/components/UL'; +import { LeftColumn } from './LeftColumn'; +import { Placeholder } from './Placeholder'; + +type OnwardsResponse = { + trails: FETrailType[]; + heading: string; + displayname: string; + description: string; +}; + +const containerStyles = css` + display: flex; + flex-direction: column; + padding-bottom: ${space[6]}px; + + ${from.leftCol} { + flex-direction: row; + padding-right: 80px; + } +`; + +const headerStyles = css` + ${headlineBold24}; + color: ${palette('--carousel-text')}; + padding-bottom: ${space[2]}px; + padding-top: ${space[1]}px; + margin-left: 0; +`; +const mobileHeaderStyles = css` + ${headerStyles}; + ${from.tablet} { + padding-left: 10px; + } + ${from.leftCol} { + display: none; + } +`; + +const convertFETrailToDcrTrail = ( + trails: FETrailType[], + discussionApiUrl: string, +): DCRFrontCard[] => + trails.map((trail) => ({ + dataLinkName: 'onwards-content-card', + discussionId: trail.discussion?.discussionId, + discussionApiUrl, + format: decideFormat(trail.format), + headline: trail.headline, + image: { + src: trail.masterImage ?? '', + altText: trail.linkText ?? '', + }, + isExternalLink: false, + onwardsSource: 'related-content', + showLivePlayable: false, + showQuotedHeadline: false, + url: trail.url, + webPublicationDate: trail.webPublicationDate, + })); + +type Props = { url: string; discussionApiUrl: string }; + +/** + * Big Six refers to the style of the onwards content container. It displays six article + * cards in a gallery-style container, as opposed to a carousel. + */ +export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { + const { data, error } = useApi(url); + + if (error) { + // Send the error to Sentry and then prevent the element from rendering + window.guardian.modules.sentry.reportError(error, 'onwards-lower'); + return null; + } + + if (!data?.trails) { + return ( + + ); + } + + const trails: DCRFrontCard[] = convertFETrailToDcrTrail( + data.trails, + discussionApiUrl, + ); + + const firstSlice75 = trails.slice(0, 1); + const firstSlice25 = trails.slice(1, 2); + const secondSlice25 = trails.slice(2, 6); + + const heading = data.heading || data.displayname; + + return ( +
+ +

+ {heading} +

+
+

+ {heading} +

+
+
    + {firstSlice75.map((trail) => ( +
  • + +
  • + ))} + {firstSlice25.map((trail) => ( +
  • + +
  • + ))} +
+
    + {secondSlice25.map((trail, index) => { + const dataLinkName = `onwards-content-gallery-style ${ + trail.dataLinkName + }-position-${index + 2}`; + + return ( +
  • 0} + > + +
  • + ); + })} +
+
+
+ ); +}; diff --git a/dotcom-rendering/src/components/CalloutEmbedBlockComponent.importable.tsx b/dotcom-rendering/src/components/CalloutEmbedBlockComponent.importable.tsx index 8ca13f2f796..d6f5d4b14db 100644 --- a/dotcom-rendering/src/components/CalloutEmbedBlockComponent.importable.tsx +++ b/dotcom-rendering/src/components/CalloutEmbedBlockComponent.importable.tsx @@ -6,15 +6,13 @@ import { } from '@guardian/source/foundations'; import { Button } from '@guardian/source/react-components'; import { useEffect, useState } from 'react'; -import type { ArticleFormat } from '../lib/articleFormat'; -import { decidePalette } from '../lib/decidePalette'; +import { palette } from '../palette'; import MinusIcon from '../static/icons/minus.svg'; import PlusIcon from '../static/icons/plus.svg'; import type { CalloutBlockElement, CalloutBlockElementV2, } from '../types/content'; -import type { Palette } from '../types/palette'; import { Form } from './CalloutEmbed/Form'; const wrapperStyles = css` @@ -79,10 +77,10 @@ const summaryContentWrapper = css` flex-direction: row; `; -const speechBubbleStyles = (palette: Palette) => css` +const speechBubbleStyles = css` ${textSansBold17} color: ${srcPalette.neutral[100]}; - background-color: ${palette.background.speechBubble}; + background-color: ${palette('--speech-bubble-background')}; min-width: 88px; padding-bottom: 6px; padding-left: 10px; @@ -93,7 +91,7 @@ const speechBubbleStyles = (palette: Palette) => css` height: 22px; border-bottom-right-radius: 18px; position: absolute; - background-color: ${palette.background.speechBubble}; + background-color: ${palette('--speech-bubble-background')}; } `; @@ -147,10 +145,8 @@ type FormDataType = { [key in string]: unknown }; */ export const CalloutEmbedBlockComponent = ({ callout, - format, }: { callout: CalloutBlockElement | CalloutBlockElementV2; - format: ArticleFormat; }) => { let expandFormButtonRef: HTMLButtonElement | null = null; let firstFieldElementRef: HTMLElement | null = null; @@ -160,8 +156,6 @@ export const CalloutEmbedBlockComponent = ({ const [error, setError] = useState(''); const [submissionSuccess, setSubmissionSuccess] = useState(false); - const palette = decidePalette(format); - const { title, description, formFields } = callout; const onSubmit = async (formData: FormDataType) => { @@ -312,7 +306,7 @@ export const CalloutEmbedBlockComponent = ({
-
+

Share your story

@@ -338,7 +332,7 @@ export const CalloutEmbedBlockComponent = ({
-
+

Share your story

diff --git a/dotcom-rendering/src/components/CalloutEmbedBlockComponent.stories.tsx b/dotcom-rendering/src/components/CalloutEmbedBlockComponent.stories.tsx index 3f6f711ab8e..16e144fe17c 100644 --- a/dotcom-rendering/src/components/CalloutEmbedBlockComponent.stories.tsx +++ b/dotcom-rendering/src/components/CalloutEmbedBlockComponent.stories.tsx @@ -2,7 +2,6 @@ import type { Decorator, Meta, StoryObj } from '@storybook/react'; import { centreColumnDecorator } from '../../.storybook/decorators/gridDecorators'; import { allModes } from '../../.storybook/modes'; import { calloutCampaign } from '../../fixtures/manual/calloutCampaign'; -import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat'; import { customMockFetch } from '../lib/mockRESTCalls'; import { CalloutEmbedBlockComponent as CalloutEmbedBlock } from './CalloutEmbedBlockComponent.importable'; @@ -39,11 +38,6 @@ const goodRequest: Decorator = (Story) => { export const CalloutEmbedBlockComponent = { args: { callout: calloutCampaign, - format: { - display: ArticleDisplay.Standard, - design: ArticleDesign.Standard, - theme: Pillar.News, - }, }, decorators: [goodRequest], } satisfies Story; diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index a2a3a62ccfd..aa3f3c26153 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -141,6 +141,7 @@ export type Props = { trailTextColour?: string; /** The square podcast series image, if it exists for a card */ podcastImage?: PodcastSeriesImage; + galleryCount?: number; }; const starWrapper = (cardHasImage: boolean) => css` @@ -340,6 +341,8 @@ export const Card = ({ trailTextSize, trailTextColour, podcastImage, + // eslint-disable-next-line @typescript-eslint/no-unused-vars -- Added in preparation for UI changes to display gallery count + galleryCount, }: Props) => { const hasSublinks = supportingContent && supportingContent.length > 0; const sublinkPosition = decideSublinkPosition( diff --git a/dotcom-rendering/src/components/Carousel.importable.tsx b/dotcom-rendering/src/components/Carousel.importable.tsx index 3f60618c868..7d3bc6f8dff 100644 --- a/dotcom-rendering/src/components/Carousel.importable.tsx +++ b/dotcom-rendering/src/components/Carousel.importable.tsx @@ -453,6 +453,7 @@ const Title = ({ {title} ); + type CarouselCardProps = { isFirst: boolean; index: number; @@ -500,6 +501,7 @@ const CarouselCard = ({ }: CarouselCardProps) => { const isVideoContainer = containerType === 'fixed/video'; const cardImagePosition = isOnwardContent ? 'bottom' : 'top'; + return (
  • { const card = cards[0]; if (!card) return null; @@ -183,7 +185,11 @@ export const SplashCardLayout = ({ ); return ( -
      +
      • { const card = cards[0]; if (!card) return null; @@ -287,7 +295,11 @@ export const BoostedCardLayout = ({ liveUpdatesPosition, } = decideCardProperties(card.boostLevel); return ( -
          +
          • { if (cards.length === 0) return null; return (
              )} @@ -444,6 +459,7 @@ export const FlexibleGeneral = ({ imageLoading={imageLoading} aspectRatio={aspectRatio} isFirstRow={!splash.length && i === 0} + isLastRow={i === groupedCards.length - 1} /> ); @@ -460,6 +476,7 @@ export const FlexibleGeneral = ({ isFirstRow={!splash.length && i === 0} isFirstStandardRow={i === 0} aspectRatio={aspectRatio} + isLastRow={i === groupedCards.length - 1} /> ); } diff --git a/dotcom-rendering/src/components/FlexibleSpecial.tsx b/dotcom-rendering/src/components/FlexibleSpecial.tsx index fc1fff05590..36de07c0b1d 100644 --- a/dotcom-rendering/src/components/FlexibleSpecial.tsx +++ b/dotcom-rendering/src/components/FlexibleSpecial.tsx @@ -113,6 +113,7 @@ export const OneCardLayout = ({ absoluteServerTimes, imageLoading, aspectRatio, + isLastRow, }: { cards: DCRFrontCard[]; imageLoading: Loading; @@ -120,6 +121,7 @@ export const OneCardLayout = ({ showAge?: boolean; absoluteServerTimes: boolean; aspectRatio: AspectRatio; + isLastRow: boolean; }) => { const card = cards[0]; if (!card) return null; @@ -137,7 +139,7 @@ export const OneCardLayout = ({ card.supportingContent?.length ?? 0, ); return ( -
                +
                • +
                    {cards.map((card, cardIndex) => { return (
                  • { slideshowImages: trail.slideshowImages, showLivePlayable: trail.showLivePlayable, showMainVideo: trail.showMainVideo, + galleryCount: trail.galleryCount, }; return Card({ ...defaultProps, ...cardProps }); diff --git a/dotcom-rendering/src/components/FrontSection.tsx b/dotcom-rendering/src/components/FrontSection.tsx index 67ff92815e8..b7fa9b4cdf8 100644 --- a/dotcom-rendering/src/components/FrontSection.tsx +++ b/dotcom-rendering/src/components/FrontSection.tsx @@ -55,7 +55,8 @@ type Props = { /** Fronts containers can have their styling overridden using a `containerLevel`. * If used, this can be either "Primary" or "Secondary", both of which have different styles */ containerLevel?: DCRContainerLevel; - + /** Fronts containers spacing rules vary depending on the size of their container spacing which is derived from if the next container is a primary or secondary. */ + containerSpacing?: 'large' | 'small'; /** Defaults to `false`. If true a Hide button is show top right allowing this section * to be collapsed */ @@ -296,7 +297,7 @@ const sectionContentBorderFromLeftCol = css` content: ''; position: absolute; top: ${space[2]}px; - bottom: ${space[6]}px; + bottom: 0; border-left: 1px solid ${schemePalette('--section-border')}; transform: translateX(-50%); /** Keeps the vertical divider ontop of carousel item dividers */ @@ -356,46 +357,12 @@ const bottomPadding = css` padding-bottom: ${space[9]}px; `; -/** Adds space above the border of primary level containers without - * causing gaps in the vertical side borders from tablet upwards - */ -const primaryLevelTopSpacer = css` - height: ${space[4]}px; - background-color: ${schemePalette('--front-page-background')}; - ${from.tablet} { - display: grid; - column-gap: 20px; - grid-template-columns: - minmax(0, 1fr) - [decoration-start] - repeat(12, 40px) - [decoration-end] - minmax(0, 1fr); - } - ${from.desktop} { - grid-template-columns: - minmax(0, 1fr) - [decoration-start] - repeat(12, 60px) - [decoration-end] - minmax(0, 1fr); - } - ${from.leftCol} { - grid-template-columns: - minmax(0, 1fr) - [decoration-start] - repeat(14, 60px) - [decoration-end] - minmax(0, 1fr); - } - ${from.wide} { - grid-template-columns: - minmax(0, 1fr) - [decoration-start] - repeat(16, 60px) - [decoration-end] - minmax(0, 1fr); - } +const smallBottomPadding = css` + padding-bottom: ${space[6]}px; +`; + +const largeBottomPadding = css` + padding-bottom: ${space[10]}px; `; const primaryLevelTopBorder = css` @@ -503,6 +470,7 @@ export const FrontSection = ({ containerName, containerPalette, containerLevel, + containerSpacing, description, editionId, leftContent, @@ -536,174 +504,162 @@ export const FrontSection = ({ !!ajaxUrl && !containerLevel; const showVerticalRule = !hasPageSkin; - /** * id is being used to set the containerId in @see {ShowMore.importable.tsx} * this id pre-existed showMore so is probably also being used for something else. */ return ( - <> - {/** Primary level containers have additional spacing above the horizontal rule */} - {containerLevel === 'Primary' && ( -
                    -
                    -
                    - )} - -
                    - {!!containerLevel && showTopBorder && ( -
                    - )} - + +
                    + {!!containerLevel && showTopBorder && (
                    + )} -
                    as the leftContent - title?.toLowerCase() === 'opinion', - ), - showVerticalRule && - !containerLevel && - sectionHeadlineFromLeftCol( - schemePalette('--section-border'), - ), - title?.toLowerCase() === 'headlines' && - sectionHeadlineHeight, - ]} - > - - } - collectionBranding={collectionBranding} - /> +
                    - {leftContent} -
                    +
                    as the leftContent + title?.toLowerCase() === 'opinion', + ), + showVerticalRule && + !containerLevel && + sectionHeadlineFromLeftCol( + schemePalette('--section-border'), + ), + title?.toLowerCase() === 'headlines' && + sectionHeadlineHeight, + ]} + > + + } + collectionBranding={collectionBranding} + /> - {isToggleable && ( -
                    - -
                    - )} + {leftContent} +
                    -
                    - {children} + {isToggleable && ( +
                    +
                    + )} -
                    - {isString(targetedTerritory) && - isAustralianTerritory(targetedTerritory) ? ( - - - - ) : showMore ? ( - - - - ) : null} - {pagination && ( - - )} -
                    +
                    + {children} +
                    - {treats && !hasPageSkin && ( -
                    - + {isString(targetedTerritory) && + isAustralianTerritory(targetedTerritory) ? ( + + -
                    + + ) : showMore ? ( + + + + ) : null} + {pagination && ( + )} -
                    -
                    - +
                    + + {treats && !hasPageSkin && ( +
                    + +
                    + )} +
                    +
                    ); }; diff --git a/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx b/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx index c0b5dcf67da..a834d1f25b6 100644 --- a/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx +++ b/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx @@ -9,8 +9,6 @@ import { createPortal } from 'react-dom'; import { submitComponentEvent } from '../client/ophan/ophan'; import { useArticleCounts } from '../lib/articleCount'; import { - getLastOneOffContributionTimestamp, - isRecurringContributor, shouldHideSupportMessaging, useHasOptedOutOfArticleCount, } from '../lib/contributions'; @@ -125,9 +123,6 @@ const usePayload = ({ isPaidContent, tags, showSupportMessaging: !hideSupportMessagingForUser, - isRecurringContributor: isRecurringContributor(isSignedIn), - lastOneOffContributionDate: - getLastOneOffContributionTimestamp() ?? undefined, mvtId, countryCode, epicViewLog: getEpicViewLog(storage.local), diff --git a/dotcom-rendering/src/components/OnwardsUpper.importable.tsx b/dotcom-rendering/src/components/OnwardsUpper.importable.tsx index d8a59467e87..888f0156ff7 100644 --- a/dotcom-rendering/src/components/OnwardsUpper.importable.tsx +++ b/dotcom-rendering/src/components/OnwardsUpper.importable.tsx @@ -6,10 +6,12 @@ import { Pillar, } from '../lib/articleFormat'; import type { EditionId } from '../lib/edition'; +import { useAB } from '../lib/useAB'; import { useIsAndroid } from '../lib/useIsAndroid'; import { palette } from '../palette'; import type { OnwardsSource } from '../types/onwards'; import type { TagType } from '../types/tag'; +import { BigSixOnwardsContent } from './BigSixOnwardsContent'; import { FetchOnwardsData } from './FetchOnwardsData.importable'; import { Section } from './Section'; @@ -214,6 +216,12 @@ export const OnwardsUpper = ({ discussionApiUrl, absoluteServerTimes, }: Props) => { + const abTestAPI = useAB()?.api; + const isInOnwardsAbTestVariant = abTestAPI?.isUserInVariant( + 'OnwardsContentArticle', + 'variant', + ); + const isAndroid = useIsAndroid(); if (isAndroid) return null; @@ -260,10 +268,12 @@ export const OnwardsUpper = ({ // --- Tag excludes --- // const tagsToExclude = []; + // Exclude ad features from non-ad feature content if (!isPaidContent) { tagsToExclude.push('tone/advertisement-features'); } + // We don't want to show professional network content on videos or interactives if ( contentType.toLowerCase() === 'video' || @@ -296,7 +306,18 @@ export const OnwardsUpper = ({ return (
                    - {!!url && ( + {!!url && isInOnwardsAbTestVariant && ( +
                    + +
                    + )} + {!!url && !isInOnwardsAbTestVariant && (
                    )} - {!!(!isPaidContent && curatedDataUrl) && ( + {!!curatedDataUrl && !isPaidContent && (
                    +
                      {cards.map((card) => { return (
                    • +
                        {cards.map((card, cardIndex) => { return (
                      • + + + Pos + Team + P + GD + Pts + + + + + + 1 + Liverpool + 11 + 15 + 28 + + + + 2 + Man City + 11 + 9 + 23 + + + + 3 + Nottm Forest + 11 + 5 + 19 + + + + 4 + Brighton + 11 + 4 + 19 + + + + 5 + Chelsea + 10 + 8 + 18 + + + + 6 + Arsenal + 10 + 6 + 18 + + + + 7 + Fulham + 11 + 3 + 18 + + + + 8 + Newcastle + 11 + 2 + 18 + + + + 9 + Aston Villa + 11 + 0 + 18 + + + + 10 + Tottenham Hotspur + 11 + 10 + 16 + + + + 11 + Brentford + 11 + 0 + 16 + + + + 12 + AFC Bournemouth + 11 + 0 + 15 + + + + 13 + Man Utd + 11 + 0 + 15 + + + + 14 + West Ham + 11 + -6 + 12 + + + + 15 + Leicester + 11 + -7 + 10 + + + + 16 + Everton + 11 + -7 + 10 + + + + 17 + Ipswich + 11 + -10 + 8 + + + + 18 + Crystal Palace + 11 + -7 + 7 + + + + 19 + Wolverhampton + 11 + -11 + 6 + + + + 20 + Southampton + 11 + -14 + 4 + + + + `; + +const meta = { + title: 'Components/TableBlockComponent', + component: TableBlockComponent, + render: (args) => , + args: { + element: { + isMandatory: true, + elementId: 'table', + _type: 'model.dotcomrendering.pageElements.TableBlockElement', + html: tableHtml, + }, + }, +} satisfies Meta; + +export default meta; + +export const Default = {}; diff --git a/dotcom-rendering/src/components/TableBlockComponent.tsx b/dotcom-rendering/src/components/TableBlockComponent.tsx index 244817016b6..63fcf1c7460 100644 --- a/dotcom-rendering/src/components/TableBlockComponent.tsx +++ b/dotcom-rendering/src/components/TableBlockComponent.tsx @@ -1,54 +1,113 @@ import { css } from '@emotion/react'; -import { palette, text, textSans12 } from '@guardian/source/foundations'; -import { unescapeData } from '../lib/escapeData'; +import { textSans12 } from '@guardian/source/foundations'; +import { isElement, parseHtml } from '../lib/domUtils'; +import { palette } from '../palette'; +import { logger } from '../server/lib/logging'; import type { TableBlockElement } from '../types/content'; +const tableStyles = css` + width: 100%; + background: ${palette('--table-block-background')}; + border-top: 0.0625rem solid ${palette('--table-block-border-top')}; + color: ${palette('--table-block-text')}; + border-collapse: inherit; +`; + +const headStyles = css` + tr { + font-weight: 800; + text-align: left; + } +`; + +const rowStyles = css` + ${textSans12}; + :nth-child(odd) > td { + background-color: ${palette('--table-block-stripe')}; + } +`; + +const cellPadding = css` + padding: 0.5rem; +`; + const tableEmbed = css` - .table--football { - width: 100%; - background: ${palette.neutral[97]}; - border-top: 0.0625rem solid ${palette.focus[400]}; - color: ${palette.neutral[7]}; - border-collapse: inherit; - tr:nth-child(odd) > td { - background-color: ${palette.neutral[93]}; + tr > th:first-child, + td:first-child { + color: ${palette('--table-block-text-first-column')}; + } +`; + +type Props = { + element: TableBlockElement; +}; + +const buildElementTree = (node: Node) => { + const children = Array.from(node.childNodes).map(buildElementTree); + switch (node.nodeName) { + case 'TABLE': { + return ( + + {children} +
                        + ); } - th { - padding: 0.5rem; + case 'THEAD': { + return {children}; } - td { - padding: 0.5rem; + case 'TBODY': { + return {children}; } - tr { - ${textSans12}; + case 'ABBR': { + return ( + + {children} + + ); } - thead { - tr { - font-weight: 800; - text-align: left; - } + case 'TR': { + return {children}; } - tr > th:first-child, - td:first-child { - color: ${text.supporting}; + case 'TH': { + return {children}; } - .table-column--main { - width: 100%; + case 'TD': { + const isMainColumn = (node as HTMLElement).className.includes( + 'table-column--main', + ); + return ( + + {children} + + ); } + case '#text': { + return node.textContent; + } + default: + logger.warn('TableBlockComponent: Unknown element received', { + isDev: process.env.NODE_ENV !== 'production', + element: { + name: node.nodeName, + html: isElement(node) ? node.outerHTML : undefined, + }, + }); + return null; } - margin-bottom: 16px; -`; - -type Props = { - element: TableBlockElement; }; export const TableBlockComponent = ({ element }: Props) => { + const fragment = parseHtml(element.html); return (
                        + > + {Array.from(fragment.childNodes).map(buildElementTree)} +
                        ); }; diff --git a/dotcom-rendering/src/components/TextBlockComponent.tsx b/dotcom-rendering/src/components/TextBlockComponent.tsx index 655757d0069..acea33aec5c 100644 --- a/dotcom-rendering/src/components/TextBlockComponent.tsx +++ b/dotcom-rendering/src/components/TextBlockComponent.tsx @@ -17,7 +17,6 @@ import { type ArticleFormat, ArticleSpecial, } from '../lib/articleFormat'; -import { decidePalette } from '../lib/decidePalette'; import { getAttrs, isElement, parseHtml } from '../lib/domUtils'; import { palette as themePalette } from '../palette'; import { logger } from '../server/lib/logging'; @@ -228,7 +227,7 @@ const styles = (format: ArticleFormat) => css` border-radius: 50%; height: ${remSpace[2]}; width: ${remSpace[2]}; - background-color: ${decidePalette(format).background.bullet}; + background-color: ${themePalette('--textblock-bullet-background')}; } ${until.tablet} { diff --git a/dotcom-rendering/src/components/Titlepiece.importable.tsx b/dotcom-rendering/src/components/Titlepiece.importable.tsx index 497d852cf65..29e11681ac1 100644 --- a/dotcom-rendering/src/components/Titlepiece.importable.tsx +++ b/dotcom-rendering/src/components/Titlepiece.importable.tsx @@ -3,8 +3,6 @@ import { from, headlineBold14, space, - textSansBold14, - textSansBold17, until, visuallyHidden, } from '@guardian/source/foundations'; @@ -62,32 +60,6 @@ const slimNavEditionSwitcherOverrides = css` } `; -const accreditationStyles = css` - ${gridContent} - grid-row: 1; - justify-self: start; - align-self: start; - display: flex; - flex-wrap: wrap; - padding-top: 10px; - color: ${themePalette('--masthead-accreditation-text')}; - - ${textSansBold14} - - ${until.mobileMedium} { - display: none; - } - ${until.mobileLandscape} { - max-width: 100px; - } -`; - -const accreditationStylesFromLeftCol = css` - ${from.leftCol} { - ${textSansBold17} - } -`; - const logoStyles = css` ${gridMainColumn} grid-row: 1; @@ -540,18 +512,6 @@ export const Titlepiece = ({
                    - {/* Accreditation text */} - {!showSlimNav && editionId === 'UK' && ( - - News provider of the year - - )} - {/** Expanded menu checkbox */} {}, + date: new Date('2024-12-26T00:00:00Z'), + }, +} satisfies Story; diff --git a/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx b/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx index a2ea21fbfeb..a34cc676719 100644 --- a/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx +++ b/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx @@ -94,6 +94,10 @@ const styles = { ${textEgyptianBold17}; } `, + highlight: css` + background-color: inherit; + color: inherit; + `, ticker: css` margin-bottom: ${space[4]}px; `, @@ -113,10 +117,10 @@ const styles = { `, }; -const stylesGivingTuesday = { +const stylesSubCampaign = { container: css` /* stylelint-disable-next-line color-no-hex */ - background: #f3afd9; + background: #edb438; color: ${palette.neutral[100]}; `, grid: css` @@ -157,7 +161,7 @@ const stylesGivingTuesday = { } h2 { margin: ${space[2]}px 0 ${space[4]}px; - color: ${'#670055'}; + color: ${'#1A2835'}; ${headlineMedium24} ${from.tablet} { @@ -180,6 +184,11 @@ const stylesGivingTuesday = { } color: ${'#1A2835'}; `, + highlight: css` + background-color: ${'#670055'}; + color: ${'#F6F6F6'}; + padding-left: 2px; + `, ticker: css` margin-bottom: ${space[4]}px; `, @@ -211,7 +220,7 @@ const tickerSettings = { }, }; -const tickerSettingsGivingTuesday = { +const tickerSettingsSubCampaign = { currencySymbol: '$', copy: {}, tickerStylingSettings: { @@ -223,24 +232,24 @@ const tickerSettingsGivingTuesday = { }, }; -const heading = (isGivingTuesday: boolean) => { - return isGivingTuesday - ? 'This Giving Tuesday, give to the Guardian.' +const heading = (isSubCampaign: boolean) => { + return isSubCampaign + ? 'Last chance to support us this year' : 'Can you help us hit our goal?'; }; -const bodyCopy = (isGivingTuesday: boolean) => { - const givingTuesdayCopy = +const bodyCopy = (isSubCampaign: boolean) => { + const SubCampaignCopy = 'We’re funded by readers, not billionaires - which means we can publish factual journalism with no outside influence.'; const normalCopy = 'With no billionaire owner or shareholders pulling our strings, reader support keeps us fiercely independent.'; - return isGivingTuesday ? givingTuesdayCopy : normalCopy; + return isSubCampaign ? SubCampaignCopy : normalCopy; }; -const bodyCopyHighlightedText = (isGivingTuesday: boolean) => { - const givingTuesdayCopy = 'Help us raise $4m to keep going in 2025.'; +const bodyCopyHighlightedText = (isSubCampaign: boolean) => { + const SubCampaignCopy = 'Help us keep going in 2025.'; const normalCopy = 'Help us hit our most important annual fundraising goal so we can keep going.'; - return isGivingTuesday ? givingTuesdayCopy : normalCopy; + return isSubCampaign ? SubCampaignCopy : normalCopy; }; const getTickerData = async (): Promise => { @@ -316,48 +325,38 @@ export const UsEoy2024: ReactComponent = ({ currencySymbol, } = useChoiceCards(choiceCardAmounts, 'US', cta, cta); - const isGivingTuesday = - date >= new Date('2024-11-27T00:00:01') && - date < new Date('2024-12-03T23:59:59'); + const isSubCampaign = + date >= new Date('2024-12-20T00:00:01') && + date < new Date('2024-12-31T23:59:59'); return (
                    -
                    -
                    +
                    +
                    -

                    {heading(isGivingTuesday)}

                    +

                    {heading(isSubCampaign)}

                    @@ -366,24 +365,32 @@ export const UsEoy2024: ReactComponent = ({ copy={{}} tickerData={tickerData} tickerStylingSettings={ - isGivingTuesday - ? tickerSettingsGivingTuesday.tickerStylingSettings + isSubCampaign + ? tickerSettingsSubCampaign.tickerStylingSettings : tickerSettings.tickerStylingSettings } size={'medium'} />
                    - {bodyCopy(isGivingTuesday)} + {bodyCopy(isSubCampaign)} {' '} - {bodyCopyHighlightedText(isGivingTuesday)} + + {bodyCopyHighlightedText(isSubCampaign)} +
                    @@ -399,13 +406,13 @@ export const UsEoy2024: ReactComponent = ({ getCtaUrl={getCtaUrl} cssCtaOverides={buttonStyles({ default: { - backgroundColour: isGivingTuesday + backgroundColour: isSubCampaign ? '#016D67' : '#C41C1C', textColour: '#FFFFFF', }, hover: { - backgroundColour: isGivingTuesday + backgroundColour: isSubCampaign ? '#891414' : '#C41C1C', textColour: '#FFFFFF', diff --git a/dotcom-rendering/src/components/marketing/banners/designableBanner/DesignableBanner.tsx b/dotcom-rendering/src/components/marketing/banners/designableBanner/DesignableBanner.tsx index ecf5289ae11..e41a54bd24f 100644 --- a/dotcom-rendering/src/components/marketing/banners/designableBanner/DesignableBanner.tsx +++ b/dotcom-rendering/src/components/marketing/banners/designableBanner/DesignableBanner.tsx @@ -626,7 +626,7 @@ const styles = { } `, reminderText: css` - ${textSans17} + ${textSans17}; display: none; ${from.tablet} { @@ -634,7 +634,7 @@ const styles = { } `, reminderCta: ({ default: defaultSettings }: CtaSettings) => css` - ${textSansBold17} + ${textSansBold17}; color: ${defaultSettings.backgroundColour}; display: inline; height: auto; diff --git a/dotcom-rendering/src/components/marketing/banners/designableBanner/DesignableBannerV2.tsx b/dotcom-rendering/src/components/marketing/banners/designableBanner/DesignableBannerV2.tsx new file mode 100644 index 00000000000..4aebe43f3e8 --- /dev/null +++ b/dotcom-rendering/src/components/marketing/banners/designableBanner/DesignableBannerV2.tsx @@ -0,0 +1,290 @@ +import { css } from '@emotion/react'; +import { brandAlt, from, neutral, space } from '@guardian/source/foundations'; +import { SvgGuardianLogo } from '@guardian/source/react-components'; +import { useEffect, useState } from 'react'; +import { + removeMediaRulePrefix, + useMatchMedia, +} from '../../../../lib/useMatchMedia'; +import { ThreeTierChoiceCards } from '../../epics/ThreeTierChoiceCards'; +import type { SupportTier } from '../../epics/utils/threeTierChoiceCardAmounts'; +import type { ReactComponent } from '../../lib/ReactComponent'; +import { bannerWrapper, validatedBannerWrapper } from '../common/BannerWrapper'; +import type { BannerRenderProps } from '../common/types'; +import { DesignableBannerArticleCount } from './components/DesignableBannerArticleCount'; +import { DesignableBannerBody } from './components/DesignableBannerBody'; +import { DesignableBannerCloseButton } from './components/DesignableBannerCloseButton'; +import { DesignableBannerCtas } from './components/DesignableBannerCtas'; +import { DesignableBannerHeader } from './components/DesignableBannerHeader'; +import type { BannerTemplateSettings } from './settings'; +import { templateSpacing } from './styles/templateStyles'; + +const DesignableBannerV2: ReactComponent = ({ + content, + countryCode, + onCloseClick, + articleCounts, + separateArticleCount, // legacy field + separateArticleCountSettings, + submitComponentEvent, + design, + onCtaClick, + onSecondaryCtaClick, +}: BannerRenderProps): JSX.Element => { + const isTabletOrAbove = useMatchMedia(removeMediaRulePrefix(from.tablet)); + + const [iosAppBannerPresent, setIosAppBannerPresent] = useState(false); + + const [ + threeTierChoiceCardSelectedProduct, + setThreeTierChoiceCardSelectedProduct, + ] = useState('SupporterPlus'); + + const variantOfChoiceCard = 'TWO_TIER_CHOICE_CARDS'; //this may need changed when US involved + + useEffect(() => { + setIosAppBannerPresent( + window.innerHeight !== window.document.documentElement.clientHeight, + ); + }, []); + + useEffect(() => { + if (iosAppBannerPresent) { + if (submitComponentEvent) { + submitComponentEvent({ + component: { + componentType: 'ACQUISITIONS_OTHER', + id: 'safari-ios-banner-present', + }, + action: 'VIEW', + }); + } + } + }, [iosAppBannerPresent, submitComponentEvent]); + + if (!design) { + return <>; + } + + //All config hard coded for the tests - TODO make this configurable in the future + const templateSettings: BannerTemplateSettings = { + containerSettings: { + backgroundColour: neutral[100], + textColor: neutral[0], + }, + headerSettings: { + textColour: neutral[0], + }, + primaryCtaSettings: { + default: { + backgroundColour: brandAlt[400], + textColour: neutral[0], + }, + hover: { + backgroundColour: brandAlt[400], + textColour: neutral[0], + }, + }, + //not used in this design but is required to be passed in + secondaryCtaSettings: { + default: { + backgroundColour: brandAlt[400], + textColour: neutral[0], + }, + hover: { + backgroundColour: brandAlt[400], + textColour: neutral[0], + }, + }, + closeButtonSettings: { + default: { + backgroundColour: neutral[100], + textColour: neutral[0], + border: `1px solid ${neutral[38]}`, + }, + hover: { + backgroundColour: neutral[100], + textColour: neutral[0], + border: `1px solid ${neutral[100]}`, + }, + }, + highlightedTextSettings: { + textColour: neutral[0], + highlightColour: neutral[100], //set to be white as we may want this in the future? + }, + articleCountTextColour: neutral[0], + bannerId: 'designable-banner', + }; + + const mainOrMobileContent = isTabletOrAbove + ? content.mainContent + : content.mobileContent; + + const showAboveArticleCount = + (separateArticleCountSettings?.type === 'above' || + separateArticleCount) && + articleCounts.forTargetedWeeks >= 5; + + return isTabletOrAbove ? ( + <> +
                    + +
                    + + ) : ( +
                    +
                    + +
                    + +
                    +
                    + {showAboveArticleCount && ( + + )} +
                    + +
                    +
                    +
                    + +
                    +
                    + +
                    +
                    +
                    + ); +}; + +const styles = { + outerContainer: ( + background: string, + textColor: string | undefined, + limitHeight: boolean, + ) => css` + background: ${background}; + color: ${textColor}; + ${limitHeight ? 'max-height: 70vh;' : ''} + overflow: auto; + + * { + box-sizing: border-box; + } + + ${from.tablet} { + border-top: 1px solid ${neutral[0]}; + } + + b, + strong { + font-weight: bold; + } + `, + containerOverrides: css` + display: flex; + flex-direction: column; + position: relative; + padding: 0 10px; + + ${from.tablet} { + display: grid; + grid-template-columns: 1fr 280px; + grid-template-rows: auto 1fr auto; + column-gap: ${space[5]}px; + width: 100%; + max-width: 1300px; + margin: 0 auto; + } + + ${from.desktop} { + column-gap: 60px; + grid-template-columns: 1fr 460px; + } + + ${from.wide} { + column-gap: 100px; + } + + ${templateSpacing.bannerContainer}; + `, + closeButtonOverrides: css` + margin-top: ${space[3]}px; + grid-column: 2; + grid-row: 1; + justify-content: flex-end; + `, + linkButtonOverrides: css` + background-color: ${brandAlt[400]}; + color: ${neutral[0]}; + display: flex; + flex-wrap: wrap; + width: 100%; + `, + linkButtonContainer: css` + padding-top: ${space[3]}px; + `, + guardianLogoContainer: css` + display: none; + ${from.tablet} { + display: block; + width: 100px; + } + grid-column: 1; + grid-row: 1; + justify-self: start; + padding-top: ${space[3]}px; + padding-left: ${space[3]}px; + `, +}; + +const unvalidated = bannerWrapper(DesignableBannerV2, 'designable-banner'); +const validated = validatedBannerWrapper( + DesignableBannerV2, + 'designable-banner', +); + +export { + validated as DesignableBannerV2, + unvalidated as DesignableBannerV2Unvalidated, +}; diff --git a/dotcom-rendering/src/components/marketing/banners/designableBanner/stories/DesignableBannerV2.stories.tsx b/dotcom-rendering/src/components/marketing/banners/designableBanner/stories/DesignableBannerV2.stories.tsx new file mode 100644 index 00000000000..0576a81f126 --- /dev/null +++ b/dotcom-rendering/src/components/marketing/banners/designableBanner/stories/DesignableBannerV2.stories.tsx @@ -0,0 +1,47 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import lzstring from 'lz-string'; +import { design, props, reducedBodyCopy } from '../../utils/storybook'; +import { DesignableBannerV2 } from '../DesignableBannerV2'; + +type WithJsonProps = T & { json?: string }; +type Props = WithJsonProps>; +const meta: Meta = { + component: DesignableBannerV2, + title: 'Components/marketing/DesignableBannerV2', + args: { + ...props, + json: '', + }, + render: ({ json, ...args }) => { + const jsonProps = json + ? JSON.parse(lzstring.decompressFromEncodedURIComponent(json)) + : {}; + + return ( +
                    + +
                    + ); + }, +}; +export default meta; + +type Story = StoryObj; +export const BasicDesignableBannerV2: Story = { + name: 'Basic DesignableBannerV2', + args: { + ...meta.args, + separateArticleCountSettings: { + type: 'above', + }, + content: { + ...reducedBodyCopy, + }, + mobileContent: { + ...reducedBodyCopy, + }, + design: { + ...design, + }, + }, +}; diff --git a/dotcom-rendering/src/components/marketing/banners/utils/storybook.ts b/dotcom-rendering/src/components/marketing/banners/utils/storybook.ts index 1fd9349a16b..a38823c294a 100644 --- a/dotcom-rendering/src/components/marketing/banners/utils/storybook.ts +++ b/dotcom-rendering/src/components/marketing/banners/utils/storybook.ts @@ -71,6 +71,17 @@ export const mobileContentWithHeading = { heading: 'Show your support for reader-funded journalism', }; +export const reducedBodyCopy = { + heading: 'Lorem ipsum dolor sit amet, et lorem.', // Max 40 characters. Example is 37 characters. + paragraphs: [ + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation.', + ], //Max 180 charcters this example is 175ish + cta: { + text: 'Continue', + baseUrl: 'https://support.theguardian.com/contribute/one-off', + }, +}; + const hexColourStringRegex = /^([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i; export const stringToHexColour = (colourString: string): HexColour => { if (hexColourStringRegex.test(colourString)) { diff --git a/dotcom-rendering/src/components/marketing/epics/ThreeTierChoiceCardData.tsx b/dotcom-rendering/src/components/marketing/epics/ThreeTierChoiceCardData.tsx index a3591a0fb3f..0a3613c64e4 100644 --- a/dotcom-rendering/src/components/marketing/epics/ThreeTierChoiceCardData.tsx +++ b/dotcom-rendering/src/components/marketing/epics/ThreeTierChoiceCardData.tsx @@ -90,3 +90,48 @@ export const ChoiceCardTestData_US: ChoiceInfo[] = [ recommended: false, }, ]; + +export const ChoiceCardTestData_TwoTier_REGULAR: ChoiceInfo[] = [ + { + supportTier: 'SupporterPlus', + label: ( + amount: number, + currencySymbol: string, + discount?: number, + ): JSX.Element | string => { + if (!isUndefined(discount)) { + return ( + <> + Support{' '} + + {currencySymbol} + {amount} + {' '} + {currencySymbol} + {amount * discount}/month{' '} + + ); + } else { + return `Support ${currencySymbol}${amount}/month`; + } + }, + benefitsLabel: 'All-access digital', + benefits: () => [ + 'Unlimited access to the Guardian app', + 'Ad-free reading on all your devices', + 'Exclusive newsletter for supporters', + 'And much more!', + ], + recommended: true, + }, + { + supportTier: 'OneOff', + label: (amount: number, currencySymbol: string): string => + `Support us from just ${currencySymbol}1`, + benefitsLabel: undefined, + benefits: (currencySymbol: string) => [ + `We welcome support of any size, any time - whether you choose to give ${currencySymbol}1 or more`, + ], + recommended: false, + }, +]; diff --git a/dotcom-rendering/src/components/marketing/epics/ThreeTierChoiceCards.tsx b/dotcom-rendering/src/components/marketing/epics/ThreeTierChoiceCards.tsx index c41594b6e17..b0842019c85 100644 --- a/dotcom-rendering/src/components/marketing/epics/ThreeTierChoiceCards.tsx +++ b/dotcom-rendering/src/components/marketing/epics/ThreeTierChoiceCards.tsx @@ -22,6 +22,7 @@ import { import type { Dispatch, SetStateAction } from 'react'; import { ChoiceCardTestData_REGULAR, + ChoiceCardTestData_TwoTier_REGULAR, ChoiceCardTestData_US, } from './ThreeTierChoiceCardData'; import type { @@ -185,6 +186,8 @@ const getChoiceCardData = (choiceCardVariant: string): ChoiceInfo[] => { return ChoiceCardTestData_US; case 'US_CHECKOUT_THREE_TIER_CHOICE_CARDS': return ChoiceCardTestData_US; + case 'TWO_TIER_CHOICE_CARDS': + return ChoiceCardTestData_TwoTier_REGULAR; default: return ChoiceCardTestData_REGULAR; } diff --git a/dotcom-rendering/src/experiments/ab-tests.ts b/dotcom-rendering/src/experiments/ab-tests.ts index 89624262880..f00fb644ad0 100644 --- a/dotcom-rendering/src/experiments/ab-tests.ts +++ b/dotcom-rendering/src/experiments/ab-tests.ts @@ -4,6 +4,7 @@ import { adBlockAsk } from './tests/ad-block-ask'; import { consentlessAds } from './tests/consentless-ads'; import { integrateIma } from './tests/integrate-ima'; import { mpuWhenNoEpic } from './tests/mpu-when-no-epic'; +import { onwardsContentArticle } from './tests/onwards-content-article'; import { optimiseSpacefinderInline } from './tests/optimise-spacefinder-inline'; import { signInGateMainControl } from './tests/sign-in-gate-main-control'; import { signInGateMainVariant } from './tests/sign-in-gate-main-variant'; @@ -21,4 +22,5 @@ export const tests: ABTest[] = [ adBlockAsk, optimiseSpacefinderInline, UsaExpandableMarketingCard, + onwardsContentArticle, ]; diff --git a/dotcom-rendering/src/experiments/tests/onwards-content-article.ts b/dotcom-rendering/src/experiments/tests/onwards-content-article.ts new file mode 100644 index 00000000000..6ec9284817c --- /dev/null +++ b/dotcom-rendering/src/experiments/tests/onwards-content-article.ts @@ -0,0 +1,30 @@ +import type { ABTest } from '@guardian/ab-core'; + +export const onwardsContentArticle: ABTest = { + id: 'OnwardsContentArticle', + start: '2024-12-05', + expiry: '2025-01-29', + author: 'dotcom.platform@guardian.co.uk', + description: + 'Test the impact of showing the galleries onwards content component on article pages.', + audience: 50 / 100, + audienceOffset: 50 / 100, + audienceCriteria: 'Article pages', + successMeasure: + 'Users are more likely to click a link in the onward content component.', + canRun: () => true, + variants: [ + { + id: 'control', + test: (): void => { + /* no-op */ + }, + }, + { + id: 'variant', + test: (): void => { + /* no-op */ + }, + }, + ], +}; diff --git a/dotcom-rendering/src/layouts/FrontLayout.tsx b/dotcom-rendering/src/layouts/FrontLayout.tsx index d29dd7c3333..5b262e0c171 100644 --- a/dotcom-rendering/src/layouts/FrontLayout.tsx +++ b/dotcom-rendering/src/layouts/FrontLayout.tsx @@ -745,6 +745,7 @@ export const FrontLayout = ({ front, NAV }: Props) => { containerLevel={ collection.config.containerLevel } + containerSpacing={collection.containerSpacing} > { } }; -describe('getLastOneOffContributionDate', () => { +describe('getLastOneOffContributionTimestamp', () => { beforeEach(clearAllCookies); - it('returns date from attributes cookie if only cookie found', () => { - const somePastDate = '2020-01-28'; - setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: somePastDate, - }); - const lastOneOffContributionDate = getLastOneOffContributionTimestamp(); - - // Our function will convert YYYY-MM-DD into a timestamp - const somePastDateToTimestamp = Date.parse(somePastDate); - expect(lastOneOffContributionDate).toBe(somePastDateToTimestamp); - }); - - it('returns a support cookie date if only cookie found', () => { + it('returns a support cookie date if found', () => { const somePastDate = 1582567969093; setCookie({ name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, @@ -48,31 +34,10 @@ describe('getLastOneOffContributionDate', () => { expect(lastOneOffContributionDate).toBe(somePastDate); }); - it('returns the most recent date if both cookies present', () => { - const muchLongerAgo = '2020-01-28'; - setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: muchLongerAgo, - }); - - const notSoLongAgo = 1582567969093; + it('returns undefined if the date cannot be parsed correctly', () => { setCookie({ name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, - value: String(notSoLongAgo), - }); - - const lastOneOffContributionDate = getLastOneOffContributionTimestamp(); - expect(lastOneOffContributionDate).toBe(notSoLongAgo); - }); - - it('returns an empty string if no dates can be parsed correctly', () => { - setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: 'CANT_TOUCH_THIS', - }); - setCookie({ - name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, - value: 'OR_THIS', + value: 'NOT_A_DATE', }); const lastOneOffContributionDate = getLastOneOffContributionTimestamp(); @@ -97,8 +62,8 @@ describe('isRecentOneOffContributor', () => { it('returns true if there are 5 days between the last contribution date and now', () => { setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: '2018-08-01', + name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, + value: Date.parse('2018-08-01').toString(), }); MockDate.set(Date.parse('2018-08-07T10:50:34')); @@ -106,18 +71,19 @@ describe('isRecentOneOffContributor', () => { }); it('returns true if there are 0 days between the last contribution date and now', () => { + const theDate = Date.parse('2018-08-01T13:00:30'); setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: '2018-08-01', + name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, + value: theDate.toString(), }); - MockDate.set(Date.parse('2018-08-01T13:00:30')); + MockDate.set(theDate); expect(isRecentOneOffContributor()).toBe(true); }); it('returns false if the one-off contribution was more than 3 months ago', () => { setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: '2018-08-01', + name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, + value: Date.parse('2018-08-01').toString(), }); MockDate.set(Date.parse('2019-08-01T13:00:30')); expect(isRecentOneOffContributor()).toBe(false); diff --git a/dotcom-rendering/src/lib/contributions.ts b/dotcom-rendering/src/lib/contributions.ts index de91bc67ee0..59c03659964 100644 --- a/dotcom-rendering/src/lib/contributions.ts +++ b/dotcom-rendering/src/lib/contributions.ts @@ -14,14 +14,9 @@ import type { DCRTagPageType } from '../types/tagPage'; // User Attributes API cookies (created on sign-in) export const HIDE_SUPPORT_MESSAGING_COOKIE = 'gu_hide_support_messaging'; export const RECURRING_CONTRIBUTOR_COOKIE = 'gu_recurring_contributor'; -export const ONE_OFF_CONTRIBUTION_DATE_COOKIE = 'gu_one_off_contribution_date'; export const OPT_OUT_OF_ARTICLE_COUNT_COOKIE = 'gu_article_count_opt_out'; -// Support Frontend cookies (created when a contribution is made) -export const SUPPORT_RECURRING_CONTRIBUTOR_MONTHLY_COOKIE = - 'gu.contributions.recurring.contrib-timestamp.Monthly'; -export const SUPPORT_RECURRING_CONTRIBUTOR_ANNUAL_COOKIE = - 'gu.contributions.recurring.contrib-timestamp.Annual'; +// Support Frontend cookie (created when a contribution is made) export const SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE = 'gu.contributions.contrib-timestamp'; @@ -61,88 +56,26 @@ export const hasSupporterCookie = ( } }; -// Determine if user is a recurring contributor by checking if they are signed in -// AND have at least one of the relevant cookies. -// We need to look at both User Attributes and Frontend Support cookies -// as the former might not reflect the latest contributor status, since it's set upon signing in. -// Frontend Support cookies are set when a contribution is made. -export const isRecurringContributor = (isSignedIn: boolean): boolean => { - // Attributes cookie - we want this to have a specific value - const isRecurringContributorFromAttrs = - getCookie({ name: RECURRING_CONTRIBUTOR_COOKIE }) === 'true'; - - // Support cookies - we only care whether these exist - const hasMonthlyContributionCookie = - getCookie({ - name: SUPPORT_RECURRING_CONTRIBUTOR_MONTHLY_COOKIE, - shouldMemoize: true, - }) !== null; - const hasAnnualContributionCookie = - getCookie({ - name: SUPPORT_RECURRING_CONTRIBUTOR_ANNUAL_COOKIE, - shouldMemoize: true, - }) !== null; - - return ( - isSignedIn && - (isRecurringContributorFromAttrs || - hasMonthlyContributionCookie || - hasAnnualContributionCookie) - ); -}; - -// looks at attribute and support cookies -// ONE_OFF_CONTRIBUTION_DATE_COOKIE (attributes cookie, when loggin in) -// SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE (support cookie, when making one-off contribution) -// Get the date of the latest one-off contribution by looking at the two relevant cookies -// and returning a Unix epoch string of the latest date found. +// looks at the SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE (set by support-frontend when making one-off contribution) +// and returns a Unix epoch int of the date if it exists. export const getLastOneOffContributionTimestamp = (): number | undefined => { - // Attributes cookie - expects YYYY-MM-DD - const contributionDateFromAttributes = getCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - }); - // Support cookies - expects Unix epoch const contributionDateFromSupport = getCookie({ name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, }); - if (!contributionDateFromAttributes && !contributionDateFromSupport) { + if (!contributionDateFromSupport) { return undefined; } - // Parse dates into common format so they can be compared - const parsedDateFromAttributes = contributionDateFromAttributes - ? Date.parse(contributionDateFromAttributes) - : 0; + // Parse dates into common a number const parsedDateFromSupport = contributionDateFromSupport ? parseInt(contributionDateFromSupport, 10) : 0; - // Return most recent date - // Condition only passed if 'parsedDateFromAttributes' is NOT NaN - if (parsedDateFromAttributes > parsedDateFromSupport) { - return parsedDateFromAttributes; - } - return parsedDateFromSupport || undefined; // This guards against 'parsedDateFromSupport' being NaN }; -export const getLastOneOffContributionDate = (): string | undefined => { - const timestamp = getLastOneOffContributionTimestamp(); - - if (isUndefined(timestamp)) { - return undefined; - } - - const date = new Date(timestamp); - const year = date.getFullYear(); - const month = (date.getMonth() + 1).toString().padStart(2, '0'); - const day = date.getDate().toString().padStart(2, '0'); - - return `${year}-${month}-${day}`; -}; - const dateDiffDays = (from: number, to: number): number => { const oneDayMs = 1000 * 60 * 60 * 24; const diffMs = to - from; @@ -168,11 +101,7 @@ export const shouldHideSupportMessaging = ( if (hasCookie === 'Pending') { return 'Pending'; } else { - return ( - hasCookie || - isRecurringContributor(isSignedIn) || - isRecentOneOffContributor() - ); + return hasCookie || isRecentOneOffContributor(); } }; diff --git a/dotcom-rendering/src/lib/decidePalette.ts b/dotcom-rendering/src/lib/decidePalette.ts index ea1e9ff98ea..a7c837d2f0a 100644 --- a/dotcom-rendering/src/lib/decidePalette.ts +++ b/dotcom-rendering/src/lib/decidePalette.ts @@ -86,22 +86,6 @@ const textStandfirstLink = (format: ArticleFormat): string => { } }; -const backgroundBullet = (format: ArticleFormat): string => { - if (format.theme === ArticleSpecial.Labs) return BLACK; - if (format.theme === ArticleSpecial.SpecialReport) { - return specialReport[300]; - } - if (format.design === ArticleDesign.Analysis) { - switch (format.theme) { - case Pillar.News: - return news[300]; - default: - return pillarPalette[format.theme].main; - } - } - return pillarPalette[format.theme].main; -}; - const backgroundBulletStandfirst = (format: ArticleFormat): string => { if ( format.design === ArticleDesign.DeadBlog || @@ -138,18 +122,6 @@ const backgroundBulletStandfirst = (format: ArticleFormat): string => { return neutral[86]; // default previously defined in Standfirst.tsx }; -const backgroundSpeechBubble = (format: ArticleFormat): string => { - if (format.design === ArticleDesign.Analysis) { - switch (format.theme) { - case Pillar.News: - return news[300]; - default: - return pillarPalette[format.theme].main; - } - } - return pillarPalette[format.theme].main; -}; - const backgroundFilterButtonHover = (format: ArticleFormat): string => { switch (format.theme) { case Pillar.News: @@ -437,9 +409,7 @@ export const decidePalette = (format: ArticleFormat): Palette => { background: { analysisContrast: backgroundAnalysisContrastColour(), analysisContrastHover: backgroundAnalysisContrastHoverColour(), - bullet: backgroundBullet(format), bulletStandfirst: backgroundBulletStandfirst(format), - speechBubble: backgroundSpeechBubble(format), filterButton: backgroundFilterButton(), filterButtonHover: backgroundFilterButtonHover(format), filterButtonActive: backgroundFilterButtonActive(format), diff --git a/dotcom-rendering/src/lib/readerRevenueDevUtils.ts b/dotcom-rendering/src/lib/readerRevenueDevUtils.ts index 082407e1177..53f2975ab72 100644 --- a/dotcom-rendering/src/lib/readerRevenueDevUtils.ts +++ b/dotcom-rendering/src/lib/readerRevenueDevUtils.ts @@ -3,16 +3,12 @@ import { HIDE_SUPPORT_MESSAGING_COOKIE, RECURRING_CONTRIBUTOR_COOKIE, SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, - SUPPORT_RECURRING_CONTRIBUTOR_ANNUAL_COOKIE, - SUPPORT_RECURRING_CONTRIBUTOR_MONTHLY_COOKIE, } from './contributions'; import { getLocaleCode } from './getCountryCode'; const readerRevenueCookies = [ HIDE_SUPPORT_MESSAGING_COOKIE, RECURRING_CONTRIBUTOR_COOKIE, - SUPPORT_RECURRING_CONTRIBUTOR_MONTHLY_COOKIE, - SUPPORT_RECURRING_CONTRIBUTOR_ANNUAL_COOKIE, SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, ]; diff --git a/dotcom-rendering/src/lib/renderElement.tsx b/dotcom-rendering/src/lib/renderElement.tsx index a220303bfc8..cd0241d8f26 100644 --- a/dotcom-rendering/src/lib/renderElement.tsx +++ b/dotcom-rendering/src/lib/renderElement.tsx @@ -185,10 +185,7 @@ export const renderElement = ({ case 'model.dotcomrendering.pageElements.CalloutBlockElement': return ( - + ); case 'model.dotcomrendering.pageElements.CalloutBlockElementV2': diff --git a/dotcom-rendering/src/model/enhanceCards.ts b/dotcom-rendering/src/model/enhanceCards.ts index eff5e94b83d..9bf80a23db8 100644 --- a/dotcom-rendering/src/model/enhanceCards.ts +++ b/dotcom-rendering/src/model/enhanceCards.ts @@ -269,7 +269,6 @@ export const enhanceCards = ( const podcastImage = getPodcastSeriesImage(faciaCard); const isContributorTagPage = !!pageId && pageId.startsWith('profile/'); - return { format, dataLinkName, @@ -329,5 +328,6 @@ export const enhanceCards = ( }, }), podcastImage, + galleryCount: faciaCard.card.galleryCount, }; }); diff --git a/dotcom-rendering/src/model/enhanceCollections.ts b/dotcom-rendering/src/model/enhanceCollections.ts index 19b531c19a4..0cd97b44bb5 100644 --- a/dotcom-rendering/src/model/enhanceCollections.ts +++ b/dotcom-rendering/src/model/enhanceCollections.ts @@ -42,6 +42,13 @@ const findCollectionSuitableForFrontBranding = ( return index; }; +/** Depending on the next sibling of the container, we assign either large or small spacing rules during render */ +const getContainerSpacing = (nextSiblingCollection?: FECollectionType) => { + const nextCollectionIsPrimary = + nextSiblingCollection?.config.collectionLevel === 'Primary'; + return nextCollectionIsPrimary ? 'large' : 'small'; +}; + export const enhanceCollections = ({ collections, editionId, @@ -89,6 +96,8 @@ export const enhanceCollections = ({ }, ); + const containerSpacing = getContainerSpacing(collections[index + 1]); + return { id, displayName, @@ -99,6 +108,7 @@ export const enhanceCollections = ({ collectionType, href, containerPalette, + containerSpacing, collectionBranding, grouped: groupCards( collectionType, diff --git a/dotcom-rendering/src/model/front-schema.json b/dotcom-rendering/src/model/front-schema.json index 1b08fa3f4b6..0dcefd774f3 100644 --- a/dotcom-rendering/src/model/front-schema.json +++ b/dotcom-rendering/src/model/front-schema.json @@ -1031,6 +1031,9 @@ }, "isLive": { "type": "boolean" + }, + "galleryCount": { + "type": "number" } }, "required": [ @@ -1779,6 +1782,9 @@ }, "isLive": { "type": "boolean" + }, + "galleryCount": { + "type": "number" } }, "required": [ @@ -2527,6 +2533,9 @@ }, "isLive": { "type": "boolean" + }, + "galleryCount": { + "type": "number" } }, "required": [ diff --git a/dotcom-rendering/src/model/tag-page-schema.json b/dotcom-rendering/src/model/tag-page-schema.json index 610b9414df7..0cee409dec3 100644 --- a/dotcom-rendering/src/model/tag-page-schema.json +++ b/dotcom-rendering/src/model/tag-page-schema.json @@ -552,6 +552,9 @@ }, "isLive": { "type": "boolean" + }, + "galleryCount": { + "type": "number" } }, "required": [ diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index bdcbdeee47a..215d8ba3d71 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -50,8 +50,33 @@ const pillarPalette = ( } }; -const textblockTextLight: PaletteFunction = (format: ArticleFormat) => { - switch (format.design) { +const textblockBulletLight: PaletteFunction = ({ theme, design }) => { + switch (theme) { + case Pillar.News: { + return design === ArticleDesign.Analysis + ? sourcePalette.news[300] + : sourcePalette.news[400]; + } + case Pillar.Opinion: + case Pillar.Sport: + case Pillar.Culture: + case Pillar.Lifestyle: { + return pillarPalette(theme, 400); + } + case ArticleSpecial.Labs: { + return sourcePalette.neutral[7]; + } + case ArticleSpecial.SpecialReport: { + return sourcePalette.specialReport[300]; + } + case ArticleSpecial.SpecialReportAlt: { + return sourcePalette.specialReportAlt[200]; + } + } +}; + +const textblockTextLight: PaletteFunction = ({ design }) => { + switch (design) { case ArticleDesign.Audio: return sourcePalette.neutral[97]; default: @@ -1823,6 +1848,23 @@ const accordionBackgroundDark: PaletteFunction = () => const accordionBackgroundLight: PaletteFunction = () => sourcePalette.neutral[97]; +const tableBlockTextLight: PaletteFunction = () => sourcePalette.neutral[7]; +const tableBlockTextDark: PaletteFunction = () => sourcePalette.neutral[86]; +const tableBlockBackgroundLight: PaletteFunction = () => + sourcePalette.neutral[97]; +const tableBlockBackgroundDark: PaletteFunction = () => + sourcePalette.neutral[38]; +const tableBlockStripeLight: PaletteFunction = () => sourcePalette.neutral[93]; +const tableBlockStripeDark: PaletteFunction = () => sourcePalette.neutral[20]; +const tableBlockTextFirstColumnLight: PaletteFunction = () => + sourcePalette.neutral[46]; +const tableBlockTextFirstColumnDark: PaletteFunction = () => + sourcePalette.neutral[60]; +const tableBlockBorderTopLight: PaletteFunction = () => + sourcePalette.brand[500]; +const tableBlockBorderTopDark: PaletteFunction = () => + sourcePalette.neutral[60]; + const tableOfContentsLight: PaletteFunction = () => sourcePalette.neutral[7]; const tableOfContentsDark: PaletteFunction = () => sourcePalette.neutral[86]; const tableOfContentsBorderLight: PaletteFunction = () => @@ -4892,6 +4934,26 @@ const staffPickBadgeTextLight: PaletteFunction = () => sourcePalette.neutral[100]; const staffPickBadgeTextDark: PaletteFunction = () => sourcePalette.neutral[7]; +const speechBubbleBackgroundLight: PaletteFunction = ({ theme, design }) => { + switch (theme) { + case Pillar.News: + return design === ArticleDesign.Analysis + ? sourcePalette.news[300] + : sourcePalette.news[400]; + case Pillar.Opinion: + case Pillar.Sport: + case Pillar.Culture: + case Pillar.Lifestyle: + return pillarPalette(theme, 400); + case ArticleSpecial.Labs: + return sourcePalette.labs[400]; + case ArticleSpecial.SpecialReport: + return sourcePalette.specialReport[400]; + case ArticleSpecial.SpecialReportAlt: + return sourcePalette.specialReportAlt[200]; + } +}; + const staffBadgeLight: PaletteFunction = () => sourcePalette.brand[400]; const staffBadgeDark: PaletteFunction = () => sourcePalette.neutral[100]; @@ -5443,9 +5505,6 @@ const highlightsCardKickerText: PaletteFunction = (format) => { } }; -const mastheadAccreditationText: PaletteFunction = () => - sourcePalette.brandAlt[400]; - const pinnedPostBorderLight: PaletteFunction = ({ theme }) => { switch (theme) { case Pillar.News: @@ -6610,10 +6669,6 @@ const paletteColours = { light: liveBlockContainerBackgroundLight, dark: liveBlockContainerBackgroundDark, }, - '--masthead-accreditation-text': { - light: mastheadAccreditationText, - dark: mastheadAccreditationText, - }, '--masthead-nav-background': { light: mastheadNavBackground, dark: mastheadNavBackground, @@ -6978,6 +7033,10 @@ const paletteColours = { light: slideshowPaginationDotActiveLight, dark: slideshowPaginationDotActiveDark, }, + '--speech-bubble-background': { + light: speechBubbleBackgroundLight, + dark: speechBubbleBackgroundLight, + }, '--stacked-progress-background': { light: () => sourcePalette.neutral[86], /** @@ -7106,6 +7165,26 @@ const paletteColours = { light: syndicationButtonTextLight, dark: syndicationButtonTextDark, }, + '--table-block-background': { + light: tableBlockBackgroundLight, + dark: tableBlockBackgroundDark, + }, + '--table-block-border-top': { + light: tableBlockBorderTopLight, + dark: tableBlockBorderTopDark, + }, + '--table-block-stripe': { + light: tableBlockStripeLight, + dark: tableBlockStripeDark, + }, + '--table-block-text': { + light: tableBlockTextLight, + dark: tableBlockTextDark, + }, + '--table-block-text-first-column': { + light: tableBlockTextFirstColumnLight, + dark: tableBlockTextFirstColumnDark, + }, '--table-of-contents': { light: tableOfContentsLight, dark: tableOfContentsDark, @@ -7122,6 +7201,10 @@ const paletteColours = { light: () => sourcePalette.neutral[0], dark: () => sourcePalette.neutral[86], }, + '--textblock-bullet-background': { + light: textblockBulletLight, + dark: textblockBulletLight, + }, '--textblock-text': { light: textblockTextLight, dark: textblockTextDark, diff --git a/dotcom-rendering/src/types/front.ts b/dotcom-rendering/src/types/front.ts index 1a06c88350d..fdd565d528f 100644 --- a/dotcom-rendering/src/types/front.ts +++ b/dotcom-rendering/src/types/front.ts @@ -290,6 +290,7 @@ export type FEFrontCard = { shortUrl: string; group: string; isLive: boolean; + galleryCount?: number; }; discussion: { isCommentable: boolean; @@ -323,6 +324,11 @@ export type DCRFrontCard = { url: string; headline: string; showQuotedHeadline: boolean; + /** @see JSX.IntrinsicAttributes["data-link-name"] */ + dataLinkName: string; + discussionApiUrl: string; + isExternalLink: boolean; + showLivePlayable: boolean; trailText?: string; starRating?: StarRating; webPublicationDate?: string; @@ -333,21 +339,17 @@ export type DCRFrontCard = { isBoosted?: boolean; boostLevel?: BoostLevel; isCrossword?: boolean; - /** @see JSX.IntrinsicAttributes["data-link-name"] */ - dataLinkName: string; - discussionApiUrl: string; discussionId?: string; byline?: string; showByline?: boolean; avatarUrl?: string; mainMedia?: MainMedia; - isExternalLink: boolean; embedUri?: string; branding?: Branding; slideshowImages?: DCRSlideshowImage[]; - showLivePlayable: boolean; showMainVideo?: boolean; podcastImageSrc?: string; + galleryCount?: number; }; export type DCRSlideshowImage = { @@ -417,6 +419,7 @@ export type DCRCollectionType = { description?: string; collectionType: DCRContainerType; containerPalette?: DCRContainerPalette; + containerSpacing?: 'large' | 'small'; grouped: DCRGroupedTrails; curated: DCRFrontCard[]; backfill: DCRFrontCard[]; diff --git a/dotcom-rendering/src/types/palette.ts b/dotcom-rendering/src/types/palette.ts index a462360ebd1..ac904af5a03 100644 --- a/dotcom-rendering/src/types/palette.ts +++ b/dotcom-rendering/src/types/palette.ts @@ -16,9 +16,7 @@ export type Palette = { background: { analysisContrast: Colour; analysisContrastHover: Colour; - bullet: Colour; bulletStandfirst: Colour; - speechBubble: Colour; filterButton: Colour; filterButtonHover: Colour; filterButtonActive: Colour; diff --git a/dotcom-rendering/webpack/webpack.config.dev-server.js b/dotcom-rendering/webpack/webpack.config.dev-server.js index c14f72a2f7b..d21805fcda5 100644 --- a/dotcom-rendering/webpack/webpack.config.dev-server.js +++ b/dotcom-rendering/webpack/webpack.config.dev-server.js @@ -34,6 +34,7 @@ module.exports = { devMiddleware: { publicPath: '/assets/', serverSideRender: true, + writeToDisk: true, headers: (req, res) => { // Allow any localhost request from accessing the assets if ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea34b01c925..1a72b4251eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,7 +102,7 @@ importers: version: 8.4.2(@types/react@18.3.1)(storybook@8.4.2) '@storybook/addon-webpack5-compiler-babel': specifier: 3.0.3 - version: 3.0.3(webpack@5.94.0) + version: 3.0.3(webpack@5.97.1) '@storybook/components': specifier: 8.4.2 version: 8.4.2(storybook@8.4.2) @@ -165,7 +165,7 @@ importers: version: 2.100.0(constructs@10.3.0) babel-loader: specifier: 9.2.1 - version: 9.2.1(@babel/core@7.26.0)(webpack@5.94.0) + version: 9.2.1(@babel/core@7.26.0)(webpack@5.97.1) buffer: specifier: 6.0.3 version: 6.0.3 @@ -197,8 +197,8 @@ importers: specifier: 4.21.0 version: 4.21.0 html-webpack-plugin: - specifier: 5.6.0 - version: 5.6.0(webpack@5.94.0) + specifier: 5.6.3 + version: 5.6.3(webpack@5.97.1) jest: specifier: 29.7.0 version: 29.7.0(@types/node@18.18.14) @@ -237,7 +237,7 @@ importers: version: 29.1.2(@babel/core@7.26.0)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.5.3) ts-loader: specifier: 9.5.1 - version: 9.5.1(typescript@5.5.3)(webpack@5.94.0) + version: 9.5.1(typescript@5.5.3)(webpack@5.97.1) tslib: specifier: 2.6.2 version: 2.6.2 @@ -248,17 +248,17 @@ importers: specifier: 5.5.3 version: 5.5.3 webpack: - specifier: 5.94.0 - version: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + specifier: 5.97.1 + version: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-cli: specifier: 5.1.4 - version: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0) + version: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) webpack-dev-server: - specifier: 5.0.4 - version: 5.0.4(webpack-cli@5.1.4)(webpack@5.94.0) + specifier: 5.1.0 + version: 5.1.0(webpack-cli@5.1.4)(webpack@5.97.1) webpack-manifest-plugin: specifier: 5.0.0 - version: 5.0.0(patch_hash=zao44j4xgexeap52664hqknxfu)(webpack@5.94.0) + version: 5.0.0(patch_hash=zao44j4xgexeap52664hqknxfu)(webpack@5.97.1) whatwg-fetch: specifier: 3.6.19 version: 3.6.19 @@ -374,8 +374,8 @@ importers: specifier: 12.0.0 version: 12.0.0(@emotion/react@11.11.3)(@guardian/libs@19.2.1)(@guardian/source@8.0.0)(@types/react@18.3.1)(react@18.3.1)(tslib@2.6.2)(typescript@5.5.3) '@guardian/support-dotcom-components': - specifier: 3.1.0 - version: 3.1.0(@guardian/libs@19.2.1)(zod@3.22.4) + specifier: 3.2.0 + version: 3.2.0(@guardian/libs@19.2.1)(zod@3.22.4) '@guardian/tsconfig': specifier: 0.2.0 version: 0.2.0 @@ -396,7 +396,7 @@ importers: version: 8.4.2(storybook@8.4.2) '@storybook/addon-webpack5-compiler-swc': specifier: 1.0.5 - version: 1.0.5(webpack@5.94.0) + version: 1.0.5(webpack@5.97.1) '@storybook/components': specifier: 8.4.2 version: 8.4.2(storybook@8.4.2) @@ -558,7 +558,7 @@ importers: version: 2.100.0(constructs@10.3.0) babel-loader: specifier: 9.2.1 - version: 9.2.1(@babel/core@7.26.0)(webpack@5.94.0) + version: 9.2.1(@babel/core@7.26.0)(webpack@5.97.1) babel-plugin-polyfill-corejs3: specifier: 0.10.6 version: 0.10.6(@babel/core@7.26.0) @@ -594,7 +594,7 @@ importers: version: 11.0.0 css-loader: specifier: 7.1.2 - version: 7.1.2(webpack@5.94.0) + version: 7.1.2(webpack@5.97.1) curlyquotes: specifier: 1.5.5 version: 1.5.5 @@ -744,7 +744,7 @@ importers: version: 14.0.0(stylelint@16.5.0) swc-loader: specifier: 0.2.6 - version: 0.2.6(@swc/core@1.9.2)(webpack@5.94.0) + version: 0.2.6(@swc/core@1.9.2)(webpack@5.97.1) swr: specifier: 1.3.0 version: 1.3.0(react@18.3.1) @@ -759,7 +759,7 @@ importers: version: 2.0.0 ts-loader: specifier: 9.5.1 - version: 9.5.1(typescript@5.5.3)(webpack@5.94.0) + version: 9.5.1(typescript@5.5.3)(webpack@5.97.1) ts-unused-exports: specifier: 10.1.0 version: 10.1.0(typescript@5.5.3) @@ -788,32 +788,32 @@ importers: specifier: 4.2.3 version: 4.2.3 webpack: - specifier: 5.94.0 - version: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + specifier: 5.97.1 + version: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-assets-manifest: specifier: 5.2.1 - version: 5.2.1(webpack@5.94.0) + version: 5.2.1(webpack@5.97.1) webpack-bundle-analyzer: specifier: 4.10.2 version: 4.10.2 webpack-cli: specifier: 5.1.4 - version: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.0.4)(webpack@5.94.0) + version: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.1.0)(webpack@5.97.1) webpack-dev-middleware: specifier: 7.4.2 - version: 7.4.2(webpack@5.94.0) + version: 7.4.2(webpack@5.97.1) webpack-dev-server: - specifier: 5.0.4 - version: 5.0.4(webpack-cli@5.1.4)(webpack@5.94.0) + specifier: 5.1.0 + version: 5.1.0(webpack-cli@5.1.4)(webpack@5.97.1) webpack-hot-middleware: specifier: 2.26.1 version: 2.26.1 webpack-hot-server-middleware: specifier: 0.6.1 - version: 0.6.1(webpack@5.94.0) + version: 0.6.1(webpack@5.97.1) webpack-manifest-plugin: specifier: 5.0.0 - version: 5.0.0(patch_hash=zao44j4xgexeap52664hqknxfu)(webpack@5.94.0) + version: 5.0.0(patch_hash=zao44j4xgexeap52664hqknxfu)(webpack@5.97.1) webpack-merge: specifier: 6.0.1 version: 6.0.1 @@ -4378,8 +4378,8 @@ packages: - utf-8-validate dev: false - /@guardian/support-dotcom-components@3.1.0(@guardian/libs@19.2.1)(zod@3.22.4): - resolution: {integrity: sha512-JzXo3QGITIyehVFEeM2ZvUsCWbjEkAAxtl+zsTpWniEnmylFuyb9YH7DS+uT7GeXUnSBzkHFle/pkL2WPDa1kA==} + /@guardian/support-dotcom-components@3.2.0(@guardian/libs@19.2.1)(zod@3.22.4): + resolution: {integrity: sha512-jxsOmP+DTGdpy3oitRGFjnRfznvOAW+ojRltZQT3qT6Eoe5nkuNc5ip0ok0y4qSnov/3MKn1UxJpvqTQT6okTw==} peerDependencies: '@guardian/libs': ^17.0.0 zod: ^3.22.4 @@ -4389,7 +4389,7 @@ packages: compression: 1.7.4 cors: 2.8.5 date-fns: 2.30.0 - express: 4.21.0 + express: 4.21.2 jsonschema: 1.4.1 lodash.debounce: 4.0.8 log4js: 6.9.1 @@ -5865,23 +5865,23 @@ packages: storybook: 8.4.2(prettier@3.0.3) dev: false - /@storybook/addon-webpack5-compiler-babel@3.0.3(webpack@5.94.0): + /@storybook/addon-webpack5-compiler-babel@3.0.3(webpack@5.97.1): resolution: {integrity: sha512-rVQTTw+oxJltbVKaejIWSHwVKOBJs3au21f/pYXhV0aiNgNhxEa3vr79t/j0j8ox8uJtzM8XYOb7FlkvGfHlwQ==} engines: {node: '>=18'} dependencies: '@babel/core': 7.26.0 - babel-loader: 9.2.1(@babel/core@7.26.0)(webpack@5.94.0) + babel-loader: 9.2.1(@babel/core@7.26.0)(webpack@5.97.1) transitivePeerDependencies: - supports-color - webpack dev: false - /@storybook/addon-webpack5-compiler-swc@1.0.5(webpack@5.94.0): + /@storybook/addon-webpack5-compiler-swc@1.0.5(webpack@5.97.1): resolution: {integrity: sha512-1NlM3noit2vA22OyWb8Ma2lhcEKCS1Snv2kr+EkaVABUqNDfVc9AD/GgYQhF7F/2CoF5N2JU7uzXDzFHd5TzZg==} engines: {node: '>=18'} dependencies: '@swc/core': 1.9.2 - swc-loader: 0.2.6(@swc/core@1.9.2)(webpack@5.94.0) + swc-loader: 0.2.6(@swc/core@1.9.2)(webpack@5.97.1) transitivePeerDependencies: - '@swc/helpers' - webpack @@ -5923,24 +5923,24 @@ packages: case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.2.3 constants-browserify: 1.0.0 - css-loader: 6.10.0(webpack@5.94.0) + css-loader: 6.10.0(webpack@5.97.1) es-module-lexer: 1.5.3 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.5.3)(webpack@5.94.0) - html-webpack-plugin: 5.6.0(webpack@5.94.0) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.5.3)(webpack@5.97.1) + html-webpack-plugin: 5.6.3(webpack@5.97.1) magic-string: 0.30.5 path-browserify: 1.0.1 process: 0.11.10 semver: 7.5.4 storybook: 8.4.2(prettier@3.0.3) - style-loader: 3.3.3(webpack@5.94.0) - terser-webpack-plugin: 5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.94.0) + style-loader: 3.3.3(webpack@5.97.1) + terser-webpack-plugin: 5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.97.1) ts-dedent: 2.2.0 typescript: 5.5.3 url: 0.11.3 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-dev-middleware: 6.1.3(webpack@5.94.0) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-dev-middleware: 6.1.3(webpack@5.97.1) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.6.1 transitivePeerDependencies: @@ -5967,24 +5967,24 @@ packages: case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.2.3 constants-browserify: 1.0.0 - css-loader: 6.10.0(webpack@5.94.0) + css-loader: 6.10.0(webpack@5.97.1) es-module-lexer: 1.5.3 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.5.3)(webpack@5.94.0) - html-webpack-plugin: 5.6.0(webpack@5.94.0) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.5.3)(webpack@5.97.1) + html-webpack-plugin: 5.6.3(webpack@5.97.1) magic-string: 0.30.5 path-browserify: 1.0.1 process: 0.11.10 semver: 7.5.4 storybook: 8.4.2(prettier@3.0.3) - style-loader: 3.3.3(webpack@5.94.0) - terser-webpack-plugin: 5.3.10(esbuild@0.18.20)(webpack@5.94.0) + style-loader: 3.3.3(webpack@5.97.1) + terser-webpack-plugin: 5.3.10(esbuild@0.18.20)(webpack@5.97.1) ts-dedent: 2.2.0 typescript: 5.5.3 url: 0.11.3 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-dev-middleware: 6.1.3(webpack@5.94.0) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-dev-middleware: 6.1.3(webpack@5.97.1) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.6.1 transitivePeerDependencies: @@ -6109,7 +6109,7 @@ packages: dependencies: '@storybook/core-webpack': 8.4.2(storybook@8.4.2) '@storybook/react': 8.4.2(@storybook/test@8.4.2)(react-dom@18.3.1)(react@18.3.1)(storybook@8.4.2)(typescript@5.5.3) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.5.3)(webpack@5.94.0) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.5.3)(webpack@5.97.1) '@types/node': 22.8.0 '@types/semver': 7.5.6 find-up: 5.0.0 @@ -6122,7 +6122,7 @@ packages: storybook: 8.4.2(prettier@3.0.3) tsconfig-paths: 4.2.0 typescript: 5.5.3 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - '@storybook/test' - '@swc/core' @@ -6146,7 +6146,7 @@ packages: dependencies: '@storybook/core-webpack': 8.4.2(storybook@8.4.2) '@storybook/react': 8.4.2(@storybook/test@8.4.2)(react-dom@18.3.1)(react@18.3.1)(storybook@8.4.2)(typescript@5.5.3) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.5.3)(webpack@5.94.0) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.5.3)(webpack@5.97.1) '@types/node': 22.8.0 '@types/semver': 7.5.6 find-up: 5.0.0 @@ -6159,7 +6159,7 @@ packages: storybook: 8.4.2(prettier@3.0.3) tsconfig-paths: 4.2.0 typescript: 5.5.3 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - '@storybook/test' - '@swc/core' @@ -6177,7 +6177,7 @@ packages: storybook: 8.4.2(prettier@3.0.3) dev: false - /@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.5.3)(webpack@5.94.0): + /@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.5.3)(webpack@5.97.1): resolution: {integrity: sha512-KUqXC3oa9JuQ0kZJLBhVdS4lOneKTOopnNBK4tUAgoxWQ3u/IjzdueZjFr7gyBrXMoU6duutk3RQR9u8ZpYJ4Q==} peerDependencies: typescript: '>= 4.x' @@ -6191,7 +6191,7 @@ packages: react-docgen-typescript: 2.2.2(typescript@5.5.3) tslib: 2.6.2 typescript: 5.5.3 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - supports-color dev: false @@ -6851,10 +6851,28 @@ packages: '@types/trusted-types': 2.0.7 dev: false + /@types/eslint-scope@3.7.7: + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.6 + dev: false + + /@types/eslint@9.6.1: + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + dependencies: + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + dev: false + /@types/estree@1.0.5: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: false + /@types/estree@1.0.6: + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + dev: false + /@types/express-serve-static-core@4.17.41: resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==} dependencies: @@ -7232,7 +7250,7 @@ packages: dependencies: '@types/node': 20.14.10 tapable: 2.2.1 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - '@swc/core' - esbuild @@ -7248,7 +7266,7 @@ packages: resolution: {integrity: sha512-8Z3/edqxE3RRlOJwKSgOFxLZRt/i1qFlv/Bi308ZUKo9jh8oGngd9r8GR0ZNKW5AEJq8QNQE3b17CwghTjQ0Uw==} dependencies: '@types/node': 20.14.10 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - '@swc/core' - esbuild @@ -7618,135 +7636,135 @@ packages: tinyrainbow: 1.2.0 dev: false - /@webassemblyjs/ast@1.12.1: - resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} + /@webassemblyjs/ast@1.14.1: + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} dependencies: - '@webassemblyjs/helper-numbers': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 dev: false - /@webassemblyjs/floating-point-hex-parser@1.11.6: - resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} + /@webassemblyjs/floating-point-hex-parser@1.13.2: + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} dev: false - /@webassemblyjs/helper-api-error@1.11.6: - resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} + /@webassemblyjs/helper-api-error@1.13.2: + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} dev: false - /@webassemblyjs/helper-buffer@1.12.1: - resolution: {integrity: sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==} + /@webassemblyjs/helper-buffer@1.14.1: + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} dev: false - /@webassemblyjs/helper-numbers@1.11.6: - resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} + /@webassemblyjs/helper-numbers@1.13.2: + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} dependencies: - '@webassemblyjs/floating-point-hex-parser': 1.11.6 - '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 '@xtuc/long': 4.2.2 dev: false - /@webassemblyjs/helper-wasm-bytecode@1.11.6: - resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} + /@webassemblyjs/helper-wasm-bytecode@1.13.2: + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} dev: false - /@webassemblyjs/helper-wasm-section@1.12.1: - resolution: {integrity: sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==} + /@webassemblyjs/helper-wasm-section@1.14.1: + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-buffer': 1.12.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 dev: false - /@webassemblyjs/ieee754@1.11.6: - resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} + /@webassemblyjs/ieee754@1.13.2: + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} dependencies: '@xtuc/ieee754': 1.2.0 dev: false - /@webassemblyjs/leb128@1.11.6: - resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==} + /@webassemblyjs/leb128@1.13.2: + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} dependencies: '@xtuc/long': 4.2.2 dev: false - /@webassemblyjs/utf8@1.11.6: - resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} + /@webassemblyjs/utf8@1.13.2: + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} dev: false - /@webassemblyjs/wasm-edit@1.12.1: - resolution: {integrity: sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==} + /@webassemblyjs/wasm-edit@1.14.1: + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-buffer': 1.12.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/helper-wasm-section': 1.12.1 - '@webassemblyjs/wasm-gen': 1.12.1 - '@webassemblyjs/wasm-opt': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 - '@webassemblyjs/wast-printer': 1.12.1 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 dev: false - /@webassemblyjs/wasm-gen@1.12.1: - resolution: {integrity: sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==} + /@webassemblyjs/wasm-gen@1.14.1: + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/ieee754': 1.11.6 - '@webassemblyjs/leb128': 1.11.6 - '@webassemblyjs/utf8': 1.11.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 dev: false - /@webassemblyjs/wasm-opt@1.12.1: - resolution: {integrity: sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==} + /@webassemblyjs/wasm-opt@1.14.1: + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-buffer': 1.12.1 - '@webassemblyjs/wasm-gen': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 dev: false - /@webassemblyjs/wasm-parser@1.12.1: - resolution: {integrity: sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==} + /@webassemblyjs/wasm-parser@1.14.1: + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-api-error': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/ieee754': 1.11.6 - '@webassemblyjs/leb128': 1.11.6 - '@webassemblyjs/utf8': 1.11.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 dev: false - /@webassemblyjs/wast-printer@1.12.1: - resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} + /@webassemblyjs/wast-printer@1.14.1: + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} dependencies: - '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 dev: false - /@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.94.0): + /@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.97.1): resolution: {integrity: sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==} engines: {node: '>=14.15.0'} peerDependencies: webpack: 5.x.x webpack-cli: 5.x.x dependencies: - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) dev: false - /@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.94.0): + /@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.97.1): resolution: {integrity: sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==} engines: {node: '>=14.15.0'} peerDependencies: webpack: 5.x.x webpack-cli: 5.x.x dependencies: - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) dev: false - /@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.0.4)(webpack@5.94.0): + /@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.1.0)(webpack@5.97.1): resolution: {integrity: sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==} engines: {node: '>=14.15.0'} peerDependencies: @@ -7757,9 +7775,9 @@ packages: webpack-dev-server: optional: true dependencies: - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0) - webpack-dev-server: 5.0.4(webpack-cli@5.1.4)(webpack@5.94.0) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) + webpack-dev-server: 5.1.0(webpack-cli@5.1.4)(webpack@5.97.1) dev: false /@xhmikosr/archive-type@7.0.0: @@ -7895,14 +7913,6 @@ packages: acorn-walk: 8.3.1 dev: false - /acorn-import-attributes@1.9.5(acorn@8.11.2): - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 - dependencies: - acorn: 8.11.2 - dev: false - /acorn-jsx@5.3.2(acorn@8.11.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -7933,6 +7943,12 @@ packages: hasBin: true dev: false + /acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: false + /agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} @@ -8358,7 +8374,7 @@ packages: - supports-color dev: false - /babel-loader@9.2.1(@babel/core@7.26.0)(webpack@5.94.0): + /babel-loader@9.2.1(@babel/core@7.26.0)(webpack@5.97.1): resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -8368,7 +8384,7 @@ packages: '@babel/core': 7.26.0 find-cache-dir: 4.0.0 schema-utils: 4.2.0 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /babel-plugin-istanbul@6.1.1: @@ -9344,6 +9360,11 @@ packages: engines: {node: '>= 0.6'} dev: false + /cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + dev: false + /copy-file@11.0.0: resolution: {integrity: sha512-mFsNh/DIANLqFt5VHZoGirdg7bK5+oTWlhnGu6tgRhzBlnEKWaPX2xrFaLltii/6rmhqFMJqffUgknuRdpYlHw==} engines: {node: '>=18'} @@ -9511,7 +9532,7 @@ packages: engines: {node: '>=12 || >=16'} dev: false - /css-loader@6.10.0(webpack@5.94.0): + /css-loader@6.10.0(webpack@5.97.1): resolution: {integrity: sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw==} engines: {node: '>= 12.13.0'} peerDependencies: @@ -9531,10 +9552,10 @@ packages: postcss-modules-values: 4.0.0(postcss@8.4.47) postcss-value-parser: 4.2.0 semver: 7.5.4 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false - /css-loader@7.1.2(webpack@5.94.0): + /css-loader@7.1.2(webpack@5.97.1): resolution: {integrity: sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==} engines: {node: '>= 18.12.0'} peerDependencies: @@ -9554,7 +9575,7 @@ packages: postcss-modules-values: 4.0.0(postcss@8.4.39) postcss-value-parser: 4.2.0 semver: 7.5.4 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /css-select@4.3.0: @@ -9849,13 +9870,6 @@ packages: default-browser-id: 5.0.0 dev: false - /default-gateway@6.0.3: - resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} - engines: {node: '>= 10'} - dependencies: - execa: 5.1.1 - dev: false - /defaults@3.0.0: resolution: {integrity: sha512-RsqXDEAALjfRTro+IFNKpcPCt0/Cy2FqHSIlnomiJp9YGadpQnrtbRpSgN2+np21qHcIKiva4fiOQGjS9/qR/A==} engines: {node: '>=18'} @@ -10235,7 +10249,7 @@ packages: is-string: 1.0.7 is-typed-array: 1.1.13 is-weakref: 1.0.2 - object-inspect: 1.13.2 + object-inspect: 1.13.3 object-keys: 1.1.1 object.assign: 4.1.5 regexp.prototype.flags: 1.5.2 @@ -11149,6 +11163,45 @@ packages: - supports-color dev: false + /express@4.21.2: + resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} + engines: {node: '>= 0.10.0'} + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.3 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.1 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + /ext-list@2.2.2: resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==} engines: {node: '>=0.10.0'} @@ -11463,7 +11516,7 @@ packages: signal-exit: 4.1.0 dev: false - /fork-ts-checker-webpack-plugin@8.0.0(typescript@5.5.3)(webpack@5.94.0): + /fork-ts-checker-webpack-plugin@8.0.0(typescript@5.5.3)(webpack@5.97.1): resolution: {integrity: sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==} engines: {node: '>=12.13.0', yarn: '>=1.0.0'} peerDependencies: @@ -11483,7 +11536,7 @@ packages: semver: 7.5.4 tapable: 2.2.1 typescript: 5.5.3 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /form-data-encoder@2.1.4: @@ -12154,8 +12207,8 @@ packages: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} dev: false - /html-webpack-plugin@5.6.0(webpack@5.94.0): - resolution: {integrity: sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==} + /html-webpack-plugin@5.6.3(webpack@5.97.1): + resolution: {integrity: sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==} engines: {node: '>=10.13.0'} peerDependencies: '@rspack/core': 0.x || 1.x @@ -12171,7 +12224,7 @@ packages: lodash: 4.17.21 pretty-error: 4.0.0 tapable: 2.2.1 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /htmlparser2@6.1.0: @@ -14596,11 +14649,6 @@ packages: engines: {node: '>= 6'} dev: false - /object-inspect@1.13.2: - resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} - engines: {node: '>= 0.4'} - dev: false - /object-inspect@1.13.3: resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} engines: {node: '>= 0.4'} @@ -14996,6 +15044,10 @@ packages: resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} dev: false + /path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + dev: false + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -16229,7 +16281,7 @@ packages: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - object-inspect: 1.13.2 + object-inspect: 1.13.3 dev: false /signal-exit@3.0.7: @@ -16662,13 +16714,13 @@ packages: peek-readable: 5.3.1 dev: false - /style-loader@3.3.3(webpack@5.94.0): + /style-loader@3.3.3(webpack@5.97.1): resolution: {integrity: sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 dependencies: - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /stylelint-config-recommended@14.0.0(stylelint@16.5.0): @@ -16797,7 +16849,7 @@ packages: picocolors: 1.1.1 dev: false - /swc-loader@0.2.6(@swc/core@1.9.2)(webpack@5.94.0): + /swc-loader@0.2.6(@swc/core@1.9.2)(webpack@5.97.1): resolution: {integrity: sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==} peerDependencies: '@swc/core': ^1.2.147 @@ -16805,7 +16857,7 @@ packages: dependencies: '@swc/core': 1.9.2 '@swc/counter': 0.1.3 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /swr@1.3.0(react@18.3.1): @@ -16844,7 +16896,7 @@ packages: streamx: 2.21.0 dev: false - /terser-webpack-plugin@5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.94.0): + /terser-webpack-plugin@5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.97.1): resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -16867,10 +16919,10 @@ packages: schema-utils: 3.3.0 serialize-javascript: 6.0.1 terser: 5.26.0 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false - /terser-webpack-plugin@5.3.10(esbuild@0.18.20)(webpack@5.94.0): + /terser-webpack-plugin@5.3.10(esbuild@0.18.20)(webpack@5.97.1): resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -16892,7 +16944,7 @@ packages: schema-utils: 3.3.0 serialize-javascript: 6.0.1 terser: 5.26.0 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /terser@5.26.0: @@ -17141,7 +17193,7 @@ packages: yargs-parser: 21.1.1 dev: false - /ts-loader@9.5.1(typescript@5.5.3)(webpack@5.94.0): + /ts-loader@9.5.1(typescript@5.5.3)(webpack@5.97.1): resolution: {integrity: sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==} engines: {node: '>=12.0.0'} peerDependencies: @@ -17154,7 +17206,7 @@ packages: semver: 7.5.4 source-map: 0.7.4 typescript: 5.5.3 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /ts-node@10.9.2(@swc/core@1.9.2)(@types/node@16.18.68)(typescript@5.1.6): @@ -17807,7 +17859,7 @@ packages: engines: {node: '>=12'} dev: false - /webpack-assets-manifest@5.2.1(webpack@5.94.0): + /webpack-assets-manifest@5.2.1(webpack@5.97.1): resolution: {integrity: sha512-MsEcXVio1GY6R+b4dVfTHIDMB0RB90KajQG8neRbH92vE2S1ClGw9mNa9NPlratYBvZOhExmN0qqMNFTaCTuIg==} engines: {node: '>=10.13.0'} peerDependencies: @@ -17820,7 +17872,7 @@ packages: lodash.has: 4.5.2 schema-utils: 3.3.0 tapable: 2.2.1 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /webpack-bundle-analyzer@4.10.2: @@ -17845,7 +17897,7 @@ packages: - utf-8-validate dev: false - /webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.0.4)(webpack@5.94.0): + /webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.1.0)(webpack@5.97.1): resolution: {integrity: sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==} engines: {node: '>=14.15.0'} hasBin: true @@ -17863,9 +17915,9 @@ packages: optional: true dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.94.0) - '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.94.0) - '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.0.4)(webpack@5.94.0) + '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.97.1) + '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.97.1) + '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.1.0)(webpack@5.97.1) colorette: 2.0.20 commander: 10.0.1 cross-spawn: 7.0.3 @@ -17874,13 +17926,13 @@ packages: import-local: 3.1.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-bundle-analyzer: 4.10.2 - webpack-dev-server: 5.0.4(webpack-cli@5.1.4)(webpack@5.94.0) + webpack-dev-server: 5.1.0(webpack-cli@5.1.4)(webpack@5.97.1) webpack-merge: 5.10.0 dev: false - /webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0): + /webpack-cli@5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1): resolution: {integrity: sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==} engines: {node: '>=14.15.0'} hasBin: true @@ -17898,9 +17950,9 @@ packages: optional: true dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.94.0) - '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.94.0) - '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.0.4)(webpack@5.94.0) + '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.97.1) + '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.97.1) + '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.1.0)(webpack@5.97.1) colorette: 2.0.20 commander: 10.0.1 cross-spawn: 7.0.3 @@ -17909,12 +17961,12 @@ packages: import-local: 3.1.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-dev-server: 5.0.4(webpack-cli@5.1.4)(webpack@5.94.0) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-dev-server: 5.1.0(webpack-cli@5.1.4)(webpack@5.97.1) webpack-merge: 5.10.0 dev: false - /webpack-dev-middleware@6.1.3(webpack@5.94.0): + /webpack-dev-middleware@6.1.3(webpack@5.97.1): resolution: {integrity: sha512-A4ChP0Qj8oGociTs6UdlRUGANIGrCDL3y+pmQMc+dSsraXHCatFpmMey4mYELA+juqwUqwQsUgJJISXl1KWmiw==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -17928,28 +17980,10 @@ packages: mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.2.0 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false - /webpack-dev-middleware@7.2.1(webpack@5.94.0): - resolution: {integrity: sha512-hRLz+jPQXo999Nx9fXVdKlg/aehsw1ajA9skAneGmT03xwmyuhvF93p6HUKKbWhXdcERtGTzUCtIQr+2IQegrA==} - engines: {node: '>= 18.12.0'} - peerDependencies: - webpack: ^5.0.0 - peerDependenciesMeta: - webpack: - optional: true - dependencies: - colorette: 2.0.20 - memfs: 4.7.7 - mime-types: 2.1.35 - on-finished: 2.4.1 - range-parser: 1.2.1 - schema-utils: 4.2.0 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - dev: false - - /webpack-dev-middleware@7.4.2(webpack@5.94.0): + /webpack-dev-middleware@7.4.2(webpack@5.97.1): resolution: {integrity: sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==} engines: {node: '>= 18.12.0'} peerDependencies: @@ -17964,11 +17998,11 @@ packages: on-finished: 2.4.1 range-parser: 1.2.1 schema-utils: 4.2.0 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false - /webpack-dev-server@5.0.4(webpack-cli@5.1.4)(webpack@5.94.0): - resolution: {integrity: sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA==} + /webpack-dev-server@5.1.0(webpack-cli@5.1.4)(webpack@5.97.1): + resolution: {integrity: sha512-aQpaN81X6tXie1FoOB7xlMfCsN19pSvRAeYUHOdFWOlhpQ/LlbfTqYwwmEDFV0h8GGuqmCmKmT+pxcUV/Nt2gQ==} engines: {node: '>= 18.12.0'} hasBin: true peerDependencies: @@ -17993,7 +18027,6 @@ packages: colorette: 2.0.20 compression: 1.7.4 connect-history-api-fallback: 2.0.0 - default-gateway: 6.0.3 express: 4.21.0 graceful-fs: 4.2.11 html-entities: 2.4.0 @@ -18002,16 +18035,15 @@ packages: launch-editor: 2.6.1 open: 10.1.0 p-retry: 6.2.0 - rimraf: 5.0.5 schema-utils: 4.2.0 selfsigned: 2.4.1 serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0) - webpack-dev-middleware: 7.2.1(webpack@5.94.0) - ws: 8.17.1 + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) + webpack-dev-middleware: 7.4.2(webpack@5.97.1) + ws: 8.18.0 transitivePeerDependencies: - bufferutil - debug @@ -18034,7 +18066,7 @@ packages: strip-ansi: 6.0.1 dev: false - /webpack-hot-server-middleware@0.6.1(webpack@5.94.0): + /webpack-hot-server-middleware@0.6.1(webpack@5.97.1): resolution: {integrity: sha512-YOKwdS0hnmADsNCsReGkMOBkoz2YVrQZvnVcViM2TDXlK9NnaOGXmnrLFjzwsHFa0/iuJy/QJFEoMxzk8R1Mgg==} peerDependencies: webpack: '*' @@ -18042,19 +18074,19 @@ packages: debug: 3.2.7 require-from-string: 2.0.2 source-map-support: 0.5.21 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - supports-color dev: false - /webpack-manifest-plugin@5.0.0(patch_hash=zao44j4xgexeap52664hqknxfu)(webpack@5.94.0): + /webpack-manifest-plugin@5.0.0(patch_hash=zao44j4xgexeap52664hqknxfu)(webpack@5.97.1): resolution: {integrity: sha512-8RQfMAdc5Uw3QbCQ/CBV/AXqOR8mt03B6GJmRbhWopE8GzRfEpn+k0ZuWywxW+5QZsffhmFDY1J6ohqJo+eMuw==} engines: {node: '>=12.22.0'} peerDependencies: webpack: ^5.47.0 dependencies: tapable: 2.2.1 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-sources: 2.3.1 dev: false patched: true @@ -18108,8 +18140,8 @@ packages: resolution: {integrity: sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==} dev: false - /webpack@5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4): - resolution: {integrity: sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==} + /webpack@5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4): + resolution: {integrity: sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -18118,13 +18150,13 @@ packages: webpack-cli: optional: true dependencies: - '@types/estree': 1.0.5 - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/wasm-edit': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.11.2 - acorn-import-attributes: 1.9.5(acorn@8.11.2) - browserslist: 4.23.0 + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.14.0 + browserslist: 4.24.0 chrome-trace-event: 1.0.3 enhanced-resolve: 5.17.1 es-module-lexer: 1.5.3 @@ -18138,9 +18170,9 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.94.0) + terser-webpack-plugin: 5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.97.1) watchpack: 2.4.1 - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.0.4)(webpack@5.94.0) + webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.1.0)(webpack@5.97.1) webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' @@ -18148,8 +18180,8 @@ packages: - uglify-js dev: false - /webpack@5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4): - resolution: {integrity: sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==} + /webpack@5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4): + resolution: {integrity: sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -18158,13 +18190,13 @@ packages: webpack-cli: optional: true dependencies: - '@types/estree': 1.0.5 - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/wasm-edit': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.11.2 - acorn-import-attributes: 1.9.5(acorn@8.11.2) - browserslist: 4.23.0 + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.14.0 + browserslist: 4.24.0 chrome-trace-event: 1.0.3 enhanced-resolve: 5.17.1 es-module-lexer: 1.5.3 @@ -18178,9 +18210,9 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(esbuild@0.18.20)(webpack@5.94.0) + terser-webpack-plugin: 5.3.10(esbuild@0.18.20)(webpack@5.97.1) watchpack: 2.4.1 - webpack-cli: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' @@ -18468,6 +18500,19 @@ packages: optional: true dev: false + /ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + /xml-name-validator@3.0.0: resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} dev: false