diff --git a/.syncpackrc b/.syncpackrc index 7a4664d5842..ce95ab63970 100644 --- a/.syncpackrc +++ b/.syncpackrc @@ -201,7 +201,9 @@ "@wordpress/interface", "@wordpress/router", "@wordpress/edit-site", - "@wordpress/private-apis" + "@wordpress/private-apis", + "@wordpress/dataviews", + "@wordpress/icons" ], "packages": [ "@woocommerce/block-templates", diff --git a/packages/js/product-editor/changelog/add-product_data_views_list b/packages/js/product-editor/changelog/add-product_data_views_list new file mode 100644 index 00000000000..548ca3eb055 --- /dev/null +++ b/packages/js/product-editor/changelog/add-product_data_views_list @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add sidebar and dataviews list to the experimental dataviews products page. diff --git a/packages/js/product-editor/package.json b/packages/js/product-editor/package.json index ba2ec86f116..52ec6eacb88 100644 --- a/packages/js/product-editor/package.json +++ b/packages/js/product-editor/package.json @@ -56,6 +56,7 @@ "@wordpress/compose": "wp-6.0", "@wordpress/core-data": "wp-6.0", "@wordpress/data": "wp-6.0", + "@wordpress/dataviews": "^4.2.0", "@wordpress/date": "wp-6.0", "@wordpress/deprecated": "wp-6.0", "@wordpress/edit-post": "wp-6.0", @@ -64,7 +65,7 @@ "@wordpress/hooks": "wp-6.0", "@wordpress/html-entities": "wp-6.0", "@wordpress/i18n": "wp-6.0", - "@wordpress/icons": "wp-6.0", + "@wordpress/icons": "10.6.0", "@wordpress/interface": "wp-6.0", "@wordpress/keyboard-shortcuts": "wp-6.0", "@wordpress/keycodes": "wp-6.0", diff --git a/packages/js/product-editor/src/products-app/constants.ts b/packages/js/product-editor/src/products-app/constants.ts new file mode 100644 index 00000000000..8c2b1b338f9 --- /dev/null +++ b/packages/js/product-editor/src/products-app/constants.ts @@ -0,0 +1,8 @@ +export const LAYOUT_GRID = 'grid'; +export const LAYOUT_TABLE = 'table'; +export const LAYOUT_LIST = 'list'; + +export const OPERATOR_IS = 'is'; +export const OPERATOR_IS_NOT = 'isNot'; +export const OPERATOR_IS_ANY = 'isAny'; +export const OPERATOR_IS_NONE = 'isNone'; diff --git a/packages/js/product-editor/src/products-app/index.tsx b/packages/js/product-editor/src/products-app/index.tsx index 69bef7fb2af..44c15e41dfa 100644 --- a/packages/js/product-editor/src/products-app/index.tsx +++ b/packages/js/product-editor/src/products-app/index.tsx @@ -13,12 +13,16 @@ import { * Internal dependencies */ import { unlock } from '../lock-unlock'; +import useLayoutAreas from './router'; +import { Layout } from './layout'; const { RouterProvider } = unlock( routerPrivateApis ); const { GlobalStylesProvider } = unlock( editorPrivateApis ); function ProductsLayout() { - return
Initial Products Layout
; + // This ensures the edited entity id and type are initialized properly. + const route = useLayoutAreas(); + return ; } export function ProductsApp() { diff --git a/packages/js/product-editor/src/products-app/layout.tsx b/packages/js/product-editor/src/products-app/layout.tsx new file mode 100644 index 00000000000..fe084ebf646 --- /dev/null +++ b/packages/js/product-editor/src/products-app/layout.tsx @@ -0,0 +1,118 @@ +/** + * External dependencies + */ +import { createElement, Fragment, useRef } from '@wordpress/element'; +import { + useViewportMatch, + useResizeObserver, + useReducedMotion, +} from '@wordpress/compose'; +import { __ } from '@wordpress/i18n'; +import { + // @ts-expect-error missing type. + EditorSnackbars, + // @ts-expect-error missing type. + privateApis as editorPrivateApis, +} from '@wordpress/editor'; +// eslint-disable-next-line @woocommerce/dependency-group +import { + // @ts-expect-error missing type. + __unstableMotion as motion, + // @ts-expect-error missing type. + __unstableAnimatePresence as AnimatePresence, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import SidebarContent from './sidebar'; +import SiteHub from './site-hub'; +import { Route } from './router'; +import { unlock } from '../lock-unlock'; + +const { NavigableRegion } = unlock( editorPrivateApis ); + +const ANIMATION_DURATION = 0.3; + +type LayoutProps = { + route: Route; +}; + +export function Layout( { route }: LayoutProps ) { + const [ fullResizer ] = useResizeObserver(); + const toggleRef = useRef< HTMLAnchorElement >( null ); + const isMobileViewport = useViewportMatch( 'medium', '<' ); + const disableMotion = useReducedMotion(); + + const { key: routeKey, areas, widths } = route; + + return ( + <> + { fullResizer } +
+
+ { /* + The NavigableRegion must always be rendered and not use + `inert` otherwise `useNavigateRegions` will fail. + */ } + { ( ! isMobileViewport || ! areas.mobile ) && ( + + + + + + { areas.sidebar } + + + + + ) } + + + + { ! isMobileViewport && areas.content && ( +
+ { areas.content } +
+ ) } + + { ! isMobileViewport && areas.edit && ( +
+ { areas.edit } +
+ ) } +
+
+ + ); +} diff --git a/packages/js/product-editor/src/products-app/product-list/index.tsx b/packages/js/product-editor/src/products-app/product-list/index.tsx new file mode 100644 index 00000000000..6a0988b2448 --- /dev/null +++ b/packages/js/product-editor/src/products-app/product-list/index.tsx @@ -0,0 +1,343 @@ +/** + * External dependencies + */ +import { Action, DataViews, View } from '@wordpress/dataviews'; +import { + createElement, + useState, + useMemo, + useCallback, + useEffect, +} from '@wordpress/element'; +import { Product, ProductQuery } from '@woocommerce/data'; +import { drawerRight } from '@wordpress/icons'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { __ } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; +import classNames from 'classnames'; +import { + // @ts-expect-error missing types. + __experimentalHeading as Heading, + // @ts-expect-error missing types. + __experimentalText as Text, + // @ts-expect-error missing types. + __experimentalHStack as HStack, + // @ts-expect-error missing types. + __experimentalVStack as VStack, + FlexItem, + Button, +} from '@wordpress/components'; +// @ts-expect-error missing type. +// eslint-disable-next-line @woocommerce/dependency-group +import { privateApis as editorPrivateApis } from '@wordpress/editor'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; +import { + useDefaultViews, + defaultLayouts, +} from '../sidebar-dataviews/default-views'; +import { LAYOUT_LIST, OPERATOR_IS } from '../constants'; + +const { NavigableRegion } = unlock( editorPrivateApis ); +const { useHistory, useLocation } = unlock( routerPrivateApis ); + +const STATUSES = [ + { value: 'draft', label: __( 'Draft', 'woocommerce' ) }, + { value: 'future', label: __( 'Scheduled', 'woocommerce' ) }, + { value: 'private', label: __( 'Private', 'woocommerce' ) }, + { value: 'publish', label: __( 'Published', 'woocommerce' ) }, + { value: 'trash', label: __( 'Trash', 'woocommerce' ) }, +]; + +/** + * TODO: auto convert some of the product editor blocks ( from the blocks directory ) to this format. + * The edit function should work relatively well with the edit from the blocks, the only difference is that the blocks rely on getEntityProp to get the value + */ +const fields = [ + { + id: 'name', + label: __( 'Name', 'woocommerce' ), + enableHiding: false, + type: 'text', + render: function nameRender( { item }: { item: Product } ) { + return item.name; + }, + }, + { + id: 'sku', + label: __( 'SKU', 'woocommerce' ), + enableHiding: false, + enableSorting: false, + render: ( { item }: { item: Product } ) => { + return item.sku; + }, + }, + { + id: 'date', + label: __( 'Date', 'woocommerce' ), + render: ( { item }: { item: Product } ) => { + return ; + }, + }, + { + label: __( 'Status', 'woocommerce' ), + id: 'status', + getValue: ( { item }: { item: Product } ) => + STATUSES.find( ( { value } ) => value === item.status )?.label ?? + item.status, + elements: STATUSES, + filterBy: { + operators: [ OPERATOR_IS ], + }, + enableSorting: false, + }, +]; + +export type ProductListProps = { + subTitle?: string; + className?: string; + hideTitleFromUI?: boolean; + postType?: string; +}; + +const PAGE_SIZE = 25; +const EMPTY_ARRAY: Product[] = []; +const EMPTY_ACTIONS_ARRAY: Action< Product >[] = []; + +const getDefaultView = ( + defaultViews: Array< { slug: string; view: View } >, + activeView: string +) => { + return defaultViews.find( ( { slug } ) => slug === activeView )?.view; +}; + +/** + * This function abstracts working with default & custom views by + * providing a [ state, setState ] tuple based on the URL parameters. + * + * Consumers use the provided tuple to work with state + * and don't have to deal with the specifics of default & custom views. + * + * @param {string} postType Post type to retrieve default views for. + * @return {Array} The [ state, setState ] tuple. + */ +function useView( + postType: string +): [ View, ( view: View ) => void, ( view: View ) => void ] { + const { + params: { activeView = 'all', isCustom = 'false', layout }, + } = useLocation(); + const history = useHistory(); + + const defaultViews = useDefaultViews( { postType } ); + const [ view, setView ] = useState< View >( () => { + const initialView = getDefaultView( defaultViews, activeView ) ?? { + type: layout ?? LAYOUT_LIST, + }; + + const type = layout ?? initialView.type; + return { + ...initialView, + type, + }; + } ); + + const setViewWithUrlUpdate = useCallback( + ( newView: View ) => { + const { params } = history.getLocationWithParams(); + + if ( newView.type === LAYOUT_LIST && ! params?.layout ) { + // Skip updating the layout URL param if + // it is not present and the newView.type is LAYOUT_LIST. + } else if ( newView.type !== params?.layout ) { + history.push( { + ...params, + layout: newView.type, + } ); + } + + setView( newView ); + }, + [ history, isCustom ] + ); + + // When layout URL param changes, update the view type + // without affecting any other config. + useEffect( () => { + setView( ( prevView ) => ( { + ...prevView, + type: layout ?? LAYOUT_LIST, + } ) ); + }, [ layout ] ); + + // When activeView or isCustom URL parameters change, reset the view. + useEffect( () => { + const newView = getDefaultView( defaultViews, activeView ); + + if ( newView ) { + const type = layout ?? newView.type; + setView( { + ...newView, + type, + } ); + } + }, [ activeView, isCustom, layout, defaultViews ] ); + + return [ view, setViewWithUrlUpdate, setViewWithUrlUpdate ]; +} + +function getItemId( item: Product ) { + return item.id.toString(); +} + +export default function ProductList( { + subTitle, + className, + hideTitleFromUI = false, +}: ProductListProps ) { + const history = useHistory(); + const location = useLocation(); + const { + postId, + quickEdit = false, + postType = 'product', + isCustom, + activeView = 'all', + } = location.params; + const [ selection, setSelection ] = useState( [ postId ] ); + const [ view, setView ] = useView( postType ); + + const queryParams = useMemo( () => { + const filters: Partial< ProductQuery > = {}; + view.filters?.forEach( ( filter ) => { + if ( filter.field === 'status' ) { + filters.status = Array.isArray( filter.value ) + ? filter.value.join( ',' ) + : filter.value; + } + } ); + const orderby = + view.sort?.field === 'name' ? 'title' : view.sort?.field; + + return { + per_page: view.perPage, + page: view.page, + order: view.sort?.direction, + orderby, + search: view.search, + ...filters, + }; + }, [ location.params, view ] ); + + const onChangeSelection = useCallback( + ( items ) => { + setSelection( items ); + history.push( { + ...location.params, + postId: items.join( ',' ), + } ); + }, + [ history, location.params, view?.type ] + ); + + // TODO: Use the Woo data store to get all the products, as this doesn't contain all the product data. + const { records, totalCount, isLoading } = useSelect( + ( select ) => { + const { getProducts, getProductsTotalCount, isResolving } = + select( 'wc/admin/products' ); + return { + records: getProducts( queryParams ) as Product[], + totalCount: getProductsTotalCount( queryParams ) as number, + isLoading: isResolving( 'getProducts', [ queryParams ] ), + }; + }, + [ queryParams ] + ); + + const paginationInfo = useMemo( + () => ( { + totalItems: totalCount, + totalPages: Math.ceil( totalCount / ( view.perPage || PAGE_SIZE ) ), + } ), + [ totalCount, view.perPage ] + ); + + const classes = classNames( 'edit-site-page', className ); + + return ( + +
+ { ! hideTitleFromUI && ( + + + + { __( 'Products', 'woocommerce' ) } + + + { /* { actions } */ } + + + { subTitle && ( + + { subTitle } + + ) } + + ) } + { + history.push( { + ...location.params, + quickEdit: quickEdit ? undefined : true, + } ); + } } + /> + } + /> +
+
+ ); +} diff --git a/packages/js/product-editor/src/products-app/router.tsx b/packages/js/product-editor/src/products-app/router.tsx new file mode 100644 index 00000000000..3acbc510aba --- /dev/null +++ b/packages/js/product-editor/src/products-app/router.tsx @@ -0,0 +1,68 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; + +/** + * Internal dependencies + */ +import { unlock } from '../lock-unlock'; +import ProductList from './product-list'; +import DataViewsSidebarContent from './sidebar-dataviews'; +import SidebarNavigationScreen from './sidebar-navigation-screen'; + +const { useLocation } = unlock( routerPrivateApis ); + +export type Route = { + key: string; + areas: { + sidebar: React.JSX.Element | React.FunctionComponent; + content?: React.JSX.Element | React.FunctionComponent; + edit?: React.JSX.Element | React.FunctionComponent; + mobile?: React.JSX.Element | React.FunctionComponent | boolean; + preview?: boolean; + }; + widths?: { + content?: number; + edit?: number; + sidebar?: number; + }; +}; + +export default function useLayoutAreas() { + const { params = {} } = useLocation(); + const { postType = 'product', layout = 'table', canvas } = params; + // Products list. + if ( [ 'product' ].includes( postType ) ) { + const isListLayout = layout === 'list' || ! layout; + return { + key: 'products-list', + areas: { + sidebar: ( + } + /> + ), + content: , + preview: false, + mobile: , + }, + widths: { + content: isListLayout ? 380 : undefined, + }, + }; + } + + // Fallback shows the home page preview + return { + key: 'default', + areas: { + sidebar: () => null, + preview: false, + mobile: canvas === 'edit', + }, + }; +} diff --git a/packages/js/product-editor/src/products-app/sidebar-dataviews/dataview-item.tsx b/packages/js/product-editor/src/products-app/sidebar-dataviews/dataview-item.tsx new file mode 100644 index 00000000000..03a4f8cee85 --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-dataviews/dataview-item.tsx @@ -0,0 +1,110 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import classNames from 'classnames'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { addQueryArgs, getQueryArgs, removeQueryArgs } from '@wordpress/url'; +import { VIEW_LAYOUTS } from '@wordpress/dataviews'; +// @ts-expect-error missing type. +// eslint-disable-next-line @woocommerce/dependency-group +import { __experimentalHStack as HStack } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import SidebarNavigationItem from '../sidebar-navigation-item'; +import { unlock } from '../../lock-unlock'; + +const { useHistory, useLocation } = unlock( routerPrivateApis ); + +type DataViewItemProps = { + title: string; + slug: string; + customViewId?: string; + type: string; + icon: React.JSX.Element; + isActive: boolean; + isCustom: boolean; + suffix?: string; +}; + +function useLink( + params: Record< string, string | undefined >, + state?: Record< string, string | undefined >, + shouldReplace = false +) { + const history = useHistory(); + function onClick( event: Event ) { + event?.preventDefault(); + + if ( shouldReplace ) { + history.replace( params, state ); + } else { + history.push( params, state ); + } + } + + const currentArgs = getQueryArgs( window.location.href ); + const currentUrlWithoutArgs = removeQueryArgs( + window.location.href, + ...Object.keys( currentArgs ) + ); + + const newUrl = addQueryArgs( currentUrlWithoutArgs, params ); + + return { + href: newUrl, + onClick, + }; +} + +export default function DataViewItem( { + title, + slug, + customViewId, + type, + icon, + isActive, + isCustom, + suffix, +}: DataViewItemProps ) { + const { + params: { postType, page }, + } = useLocation(); + + const iconToUse = + icon || VIEW_LAYOUTS.find( ( v ) => v.type === type )?.icon; + + let activeView: undefined | string = isCustom ? customViewId : slug; + if ( activeView === 'all' ) { + activeView = undefined; + } + const linkInfo = useLink( { + page, + postType, + layout: type, + activeView, + isCustom: isCustom ? 'true' : undefined, + } ); + return ( + + + { title } + + { suffix } + + ); +} diff --git a/packages/js/product-editor/src/products-app/sidebar-dataviews/default-views.ts b/packages/js/product-editor/src/products-app/sidebar-dataviews/default-views.ts new file mode 100644 index 00000000000..816477363f3 --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-dataviews/default-views.ts @@ -0,0 +1,179 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { useMemo } from '@wordpress/element'; +import { + trash, + pages, + drafts, + published, + scheduled, + notAllowed, +} from '@wordpress/icons'; +import type { ColumnStyle, ViewTable } from '@wordpress/dataviews'; + +/** + * Internal dependencies + */ +import { + LAYOUT_LIST, + LAYOUT_TABLE, + LAYOUT_GRID, + OPERATOR_IS, +} from '../constants'; + +export const defaultLayouts: Record< + string, + { + layout: { + primaryField: string; + mediaField?: string; + styles?: Record< string, ColumnStyle >; + }; + } +> = { + [ LAYOUT_TABLE ]: { + layout: { + primaryField: 'name', + styles: { + name: { + maxWidth: 300, + }, + }, + }, + }, + [ LAYOUT_GRID ]: { + layout: { + mediaField: 'featured-image', + primaryField: 'name', + }, + }, + [ LAYOUT_LIST ]: { + layout: { + primaryField: 'name', + mediaField: 'featured-image', + }, + }, +}; + +const DEFAULT_POST_BASE: Omit< ViewTable, 'view' | 'title' | 'slug' | 'icon' > = + { + type: LAYOUT_TABLE, + search: '', + filters: [], + page: 1, + perPage: 20, + sort: { + field: 'date', + direction: 'desc', + }, + fields: [ 'name', 'sku', 'status', 'date' ], + layout: defaultLayouts[ LAYOUT_LIST ].layout, + }; + +export function useDefaultViews( { postType }: { postType: string } ): Array< { + title: string; + slug: string; + icon: React.JSX.Element; + view: ViewTable; +} > { + const labels = useSelect( + ( select ) => { + const { getPostType } = select( coreStore ); + const postTypeData: { labels?: Record< string, string > } = + getPostType( postType ); + return postTypeData?.labels; + }, + [ postType ] + ); + return useMemo( () => { + return [ + { + title: labels?.all_items || __( 'All items', 'woocommerce' ), + slug: 'all', + icon: pages, + view: { ...DEFAULT_POST_BASE }, + }, + { + title: __( 'Published', 'woocommerce' ), + slug: 'published', + icon: published, + view: { + ...DEFAULT_POST_BASE, + filters: [ + { + field: 'status', + operator: OPERATOR_IS, + value: 'publish', + }, + ], + }, + }, + { + title: __( 'Scheduled', 'woocommerce' ), + slug: 'future', + icon: scheduled, + view: { + ...DEFAULT_POST_BASE, + filters: [ + { + field: 'status', + operator: OPERATOR_IS, + value: 'future', + }, + ], + }, + }, + { + title: __( 'Drafts', 'woocommerce' ), + slug: 'drafts', + icon: drafts, + view: { + ...DEFAULT_POST_BASE, + filters: [ + { + field: 'status', + operator: OPERATOR_IS, + value: 'draft', + }, + ], + }, + }, + { + title: __( 'Private', 'woocommerce' ), + slug: 'private', + icon: notAllowed, + view: { + ...DEFAULT_POST_BASE, + filters: [ + { + field: 'status', + operator: OPERATOR_IS, + value: 'private', + }, + ], + }, + }, + { + title: __( 'Trash', 'woocommerce' ), + slug: 'trash', + icon: trash, + view: { + ...DEFAULT_POST_BASE, + type: LAYOUT_TABLE, + layout: defaultLayouts[ LAYOUT_TABLE ].layout, + filters: [ + { + field: 'status', + operator: OPERATOR_IS, + value: 'trash', + }, + ], + }, + }, + ]; + }, [ labels ] ); +} diff --git a/packages/js/product-editor/src/products-app/sidebar-dataviews/index.tsx b/packages/js/product-editor/src/products-app/sidebar-dataviews/index.tsx new file mode 100644 index 00000000000..245675bfcf3 --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-dataviews/index.tsx @@ -0,0 +1,55 @@ +/** + * External dependencies + */ +import { createElement, Fragment } from '@wordpress/element'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; +// @ts-expect-error missing type. +// eslint-disable-next-line @wordpress/no-unsafe-wp-apis, @woocommerce/dependency-group +import { __experimentalItemGroup as ItemGroup } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; +import DataViewItem from './dataview-item'; +import { useDefaultViews } from './default-views'; + +const { useLocation } = unlock( routerPrivateApis ); + +export default function DataViewsSidebarContent() { + const { + params: { + postType = 'product', + activeView = 'all', + isCustom = 'false', + }, + } = useLocation(); + const defaultViews = useDefaultViews( { postType } ); + if ( ! postType ) { + return null; + } + const isCustomBoolean = isCustom === 'true'; + + return ( + <> + + { defaultViews.map( ( dataview ) => { + return ( + + ); + } ) } + + + ); +} diff --git a/packages/js/product-editor/src/products-app/sidebar-dataviews/style.scss b/packages/js/product-editor/src/products-app/sidebar-dataviews/style.scss new file mode 100644 index 00000000000..d3744634f6a --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-dataviews/style.scss @@ -0,0 +1,19 @@ +.edit-site-sidebar-navigation-screen__title-icon { + position: sticky; + top: 0; + background: $gray-900; + padding-top: $grid-unit-60; + margin-bottom: $grid-unit-10; + padding-bottom: $grid-unit-10; +} + +.edit-site-sidebar-button { + color: #e0e0e0; + flex-shrink: 0; +} + +.edit-site-sidebar-navigation-screen__title { + flex-grow: 1; + overflow-wrap: break-word; + padding: 2px 0 0 +} diff --git a/packages/js/product-editor/src/products-app/sidebar-navigation-item/index.tsx b/packages/js/product-editor/src/products-app/sidebar-navigation-item/index.tsx new file mode 100644 index 00000000000..57f8c23657c --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-navigation-item/index.tsx @@ -0,0 +1,88 @@ +/** + * External dependencies + */ +import { isRTL } from '@wordpress/i18n'; +import { chevronRightSmall, chevronLeftSmall, Icon } from '@wordpress/icons'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; +import classNames from 'classnames'; +import { createElement } from '@wordpress/element'; +import { + // @ts-expect-error missing type. + __experimentalItem as Item, + // @ts-expect-error missing type. + __experimentalHStack as HStack, + FlexBlock, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; + +const { useHistory } = unlock( routerPrivateApis ); + +type SidebarNavigationItemProps = { + className?: string; + icon?: React.JSX.Element; + suffix?: string; + withChevron?: boolean; + uid?: string; + params?: Record< string, string >; + onClick?: ( e: Event ) => void; + children: React.ReactNode; +}; + +export default function SidebarNavigationItem( { + className, + icon, + withChevron = false, + suffix, + uid, + params, + onClick, + children, + ...props +}: SidebarNavigationItemProps ) { + const history = useHistory(); + // If there is no custom click handler, create one that navigates to `params`. + function handleClick( e: Event ) { + if ( onClick ) { + onClick( e ); + } else if ( params ) { + e.preventDefault(); + history.push( params ); + } + } + + return ( + + + { icon && ( + + ) } + { children } + { withChevron && ( + + ) } + { ! withChevron && suffix } + + + ); +} diff --git a/packages/js/product-editor/src/products-app/sidebar-navigation-screen/index.tsx b/packages/js/product-editor/src/products-app/sidebar-navigation-screen/index.tsx new file mode 100644 index 00000000000..03a669c3ea2 --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-navigation-screen/index.tsx @@ -0,0 +1,136 @@ +/** + * External dependencies + */ + +import { isRTL, __ } from '@wordpress/i18n'; +import classNames from 'classnames'; +import { chevronRight, chevronLeft } from '@wordpress/icons'; +import { useSelect } from '@wordpress/data'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { createElement, Fragment } from '@wordpress/element'; +import { + // @ts-expect-error missing type. + __experimentalHStack as HStack, + // @ts-expect-error missing type. + __experimentalHeading as Heading, + // @ts-expect-error missing type. + __experimentalVStack as VStack, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; +import SidebarButton from './sidebar-button'; + +const { useHistory, useLocation } = unlock( routerPrivateApis ); + +type SidebarNavigationScreenProps = { + isRoot?: boolean; + title: string; + actions?: React.JSX.Element; + meta?: string; + content: React.JSX.Element; + footer?: string; + description?: string; + backPath?: string; +}; + +export default function SidebarNavigationScreen( { + isRoot, + title, + actions, + meta, + content, + footer, + description, + backPath: backPathProp, +}: SidebarNavigationScreenProps ) { + const { dashboardLink, dashboardLinkText } = useSelect( ( select ) => { + const { getSettings } = unlock( select( 'core/edit-site' ) ); + return { + dashboardLink: getSettings().__experimentalDashboardLink, + dashboardLinkText: getSettings().__experimentalDashboardLinkText, + }; + }, [] ); + const location = useLocation(); + const history = useHistory(); + const backPath = backPathProp ?? location.state?.backPath; + const icon = isRTL() ? chevronRight : chevronLeft; + + return ( + <> + + + { ! isRoot && ( + { + history.push( backPath ); + } } + icon={ icon } + label={ __( 'Back', 'woocommerce' ) } + showTooltip={ false } + /> + ) } + { isRoot && ( + + ) } + + { title } + + { actions && ( +
+ { actions } +
+ ) } +
+ { meta && ( + <> +
+ { meta } +
+ + ) } + +
+ { description && ( +

+ { description } +

+ ) } + { content } +
+
+ { footer && ( +
+ { footer } +
+ ) } + + ); +} diff --git a/packages/js/product-editor/src/products-app/sidebar-navigation-screen/sidebar-button.tsx b/packages/js/product-editor/src/products-app/sidebar-navigation-screen/sidebar-button.tsx new file mode 100644 index 00000000000..6033e202bf6 --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-navigation-screen/sidebar-button.tsx @@ -0,0 +1,18 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { Button } from '@wordpress/components'; +import classNames from 'classnames'; + +export default function SidebarButton( props: Button.Props ) { + return ( + + + + +
+ +
+
+ + + ); + } + ) +); + +export default SiteHub; diff --git a/packages/js/product-editor/src/products-app/site-hub/site-icon.tsx b/packages/js/product-editor/src/products-app/site-hub/site-icon.tsx new file mode 100644 index 00000000000..754dd6e39fc --- /dev/null +++ b/packages/js/product-editor/src/products-app/site-hub/site-icon.tsx @@ -0,0 +1,56 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; +import { Icon } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { wordpress } from '@wordpress/icons'; +import { store as coreDataStore } from '@wordpress/core-data'; +import classNames from 'classnames'; + +type SiteIconProps = { + className: string; +}; + +function SiteIcon( { className }: SiteIconProps ) { + const { isRequestingSite, siteIconUrl } = useSelect( ( select ) => { + const { getEntityRecord } = select( coreDataStore ); + const siteData: { site_icon_url?: string } = getEntityRecord( + 'root', + '__unstableBase', + undefined + ); + + return { + isRequestingSite: ! siteData, + siteIconUrl: siteData?.site_icon_url, + }; + }, [] ); + + if ( isRequestingSite && ! siteIconUrl ) { + return
; + } + + const icon = siteIconUrl ? ( + { + ) : ( + + ); + + return ( +
+ { icon } +
+ ); +} + +export default SiteIcon; diff --git a/packages/js/product-editor/src/products.scss b/packages/js/product-editor/src/products.scss index 9a1199c1c33..7aaa6b290a4 100644 --- a/packages/js/product-editor/src/products.scss +++ b/packages/js/product-editor/src/products.scss @@ -4,11 +4,12 @@ .woocommerce_page_woocommerce-products-dashboard #adminmenumain { display: none; } + .woocommerce_page_woocommerce-products-dashboard #wpcontent { margin-left: 0; } -body.woocommerce_page_woocommerce-products-dashboard - #woocommerce-products-dashboard { + +body.woocommerce_page_woocommerce-products-dashboard #woocommerce-products-dashboard { @include wp-admin-reset("#woocommerce-products-dashboard"); @include reset; display: block !important; @@ -38,3 +39,5 @@ body.js.is-fullscreen-mode { } } } + +@import "products-app/sidebar-dataviews/style.scss"; diff --git a/packages/js/product-editor/typings/index.d.ts b/packages/js/product-editor/typings/index.d.ts index 92aeddf3d87..1fb7d394b5b 100644 --- a/packages/js/product-editor/typings/index.d.ts +++ b/packages/js/product-editor/typings/index.d.ts @@ -31,6 +31,7 @@ declare module '@wordpress/core-data' { isResolving: boolean; hasResolved: boolean; }; + const store: string; } declare module '@wordpress/keyboard-shortcuts' { function useShortcut( @@ -43,3 +44,24 @@ declare module '@wordpress/keyboard-shortcuts' { declare module '@wordpress/router' { const privateApis; } + +declare module '@wordpress/edit-site/build-module/components/sync-state-with-url/use-init-edited-entity-from-url' { + export default function useInitEditedEntityFromURL(): void; +} + +declare module '@wordpress/edit-site/build-module/components/sidebar-navigation-screen' { + const SidebarNavigationScreen: React.FunctionComponent< { + title: string; + isRoot: boolean; + content: JSX.Element; + } >; + export default SidebarNavigationScreen; +} + +declare module '@wordpress/edit-site/build-module/components/site-hub' { + const SiteHub: React.FunctionComponent< { + ref: React.Ref; + isTransparent: boolean; + } >; + export default SiteHub; +} diff --git a/plugins/woocommerce-admin/webpack.config.js b/plugins/woocommerce-admin/webpack.config.js index cadd1251831..469d48e3f3a 100644 --- a/plugins/woocommerce-admin/webpack.config.js +++ b/plugins/woocommerce-admin/webpack.config.js @@ -186,6 +186,8 @@ const webpackConfig = { extensions: [ '.json', '.js', '.jsx', '.ts', '.tsx' ], alias: { '~': path.resolve( __dirname + '/client' ), + 'react/jsx-dev-runtime': require.resolve( 'react/jsx-dev-runtime' ), + 'react/jsx-runtime': require.resolve( 'react/jsx-runtime' ), }, }, plugins: [ @@ -240,6 +242,10 @@ const webpackConfig = { return null; } + if ( request.startsWith( '@wordpress/dataviews' ) ) { + return null; + } + if ( request.startsWith( '@wordpress/edit-site' ) ) { // The external wp.editSite does not include edit-site components, so we need to skip requesting to external here. We can remove this once the edit-site components are exported in the external wp.editSite. // We use the edit-site components in the customize store. diff --git a/plugins/woocommerce-beta-tester/api/remote-logging/remote-logging.php b/plugins/woocommerce-beta-tester/api/remote-logging/remote-logging.php index 6e7d9b1487b..80d518e03f2 100644 --- a/plugins/woocommerce-beta-tester/api/remote-logging/remote-logging.php +++ b/plugins/woocommerce-beta-tester/api/remote-logging/remote-logging.php @@ -74,6 +74,7 @@ function toggle_remote_logging( $request ) { update_option( 'woocommerce_feature_remote_logging_enabled', 'yes' ); update_option( 'woocommerce_allow_tracking', 'yes' ); update_option( 'woocommerce_remote_variant_assignment', 1 ); + set_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT, WC()->version ); } else { update_option( 'woocommerce_feature_remote_logging_enabled', 'no' ); } diff --git a/plugins/woocommerce-beta-tester/changelog/update-move-woo-version-check b/plugins/woocommerce-beta-tester/changelog/update-move-woo-version-check new file mode 100644 index 00000000000..6807f1d962f --- /dev/null +++ b/plugins/woocommerce-beta-tester/changelog/update-move-woo-version-check @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update remote logger tool to toggle remote logging feature properly diff --git a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/attributes.ts b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/attributes.ts index d70b39f1a99..0e0ae0bfdfe 100644 --- a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/attributes.ts +++ b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/attributes.ts @@ -24,6 +24,14 @@ export const blockAttributes: BlockAttributes = { type: 'boolean', default: false, }, + prefix: { + type: 'string', + default: 'SKU:', + }, + suffix: { + type: 'string', + default: '', + }, }; export default blockAttributes; diff --git a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/block.tsx b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/block.tsx index 329475b0e32..d85ddbab0ac 100644 --- a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/block.tsx +++ b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/block.tsx @@ -1,7 +1,6 @@ /** * External dependencies */ -import { __ } from '@wordpress/i18n'; import clsx from 'clsx'; import { useInnerBlockLayoutContext, @@ -10,6 +9,8 @@ import { import { withProductDataContext } from '@woocommerce/shared-hocs'; import type { HTMLAttributes } from 'react'; import { useStyleProps } from '@woocommerce/base-hooks'; +import { RichText } from '@wordpress/block-editor'; +import type { BlockEditProps } from '@wordpress/blocks'; /** * Internal dependencies @@ -17,18 +18,24 @@ import { useStyleProps } from '@woocommerce/base-hooks'; import './style.scss'; import type { Attributes } from './types'; -type Props = Attributes & HTMLAttributes< HTMLDivElement >; +type Props = BlockEditProps< Attributes > & HTMLAttributes< HTMLDivElement >; const Preview = ( { + setAttributes, parentClassName, sku, className, style, + prefix, + suffix, }: { + setAttributes: ( attributes: Record< string, unknown > ) => void; parentClassName: string; sku: string; className?: string | undefined; style?: React.CSSProperties | undefined; + prefix?: string; + suffix?: string; } ) => (
- { __( 'SKU:', 'woocommerce' ) } { sku } + setAttributes( { prefix: value } ) } + /> + { sku } + setAttributes( { suffix: value } ) } + />
); @@ -50,9 +69,12 @@ const Block = ( props: Props ): JSX.Element | null => { if ( props.isDescendentOfSingleProductTemplate ) { return ( ); } @@ -63,9 +85,12 @@ const Block = ( props: Props ): JSX.Element | null => { return ( - +
); diff --git a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/editor.scss b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/editor.scss new file mode 100644 index 00000000000..4dbdbfa5f37 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/editor.scss @@ -0,0 +1,4 @@ +.wc-block-components-product-sku strong { + margin-left: $gap-smallest; + margin-right: $gap-smallest; +} diff --git a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/index.tsx b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/index.tsx index 2b631c74dcd..3b461c0f052 100644 --- a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/index.tsx +++ b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/index.tsx @@ -29,6 +29,9 @@ const blockConfig: BlockConfiguration = { 'woocommerce/product-meta', ], edit, + save() { + return null; + }, supports, }; diff --git a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/style.scss b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/style.scss index d32bc9ca521..8aa99c856b3 100644 --- a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/style.scss +++ b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/style.scss @@ -1,6 +1,9 @@ .wc-block-components-product-sku { display: block; - text-transform: uppercase; @include font-size(small); overflow-wrap: break-word; + + strong { + text-transform: uppercase; + } } diff --git a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/types.ts b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/types.ts index a43531d141f..ec603507389 100644 --- a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/types.ts +++ b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/sku/types.ts @@ -5,4 +5,6 @@ export interface Attributes { isDescendentOfSingleProductBlock: boolean; showProductSelector: boolean; isDescendantOfAllProducts: boolean; + prefix: string; + suffix: string; } diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/ProductPicker.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/ProductPicker.tsx new file mode 100644 index 00000000000..3d80edf035c --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/ProductPicker.tsx @@ -0,0 +1,81 @@ +/** + * External dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { useBlockProps } from '@wordpress/block-editor'; +import { Icon, info } from '@wordpress/icons'; +import ProductControl from '@woocommerce/editor-components/product-control'; +import type { SelectedOption } from '@woocommerce/block-hocs'; +import { createInterpolateElement } from '@wordpress/element'; +import { + Placeholder, + // @ts-expect-error Using experimental features + __experimentalHStack as HStack, + // @ts-expect-error Using experimental features + __experimentalText as Text, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import type { ProductCollectionEditComponentProps } from '../types'; +import { getCollectionByName } from '../collections'; + +const ProductPicker = ( props: ProductCollectionEditComponentProps ) => { + const blockProps = useBlockProps(); + const attributes = props.attributes; + + const collection = getCollectionByName( attributes.collection ); + if ( ! collection ) { + return; + } + + return ( +
+ + + + + { createInterpolateElement( + sprintf( + /* translators: %s: collection title */ + __( + '%s requires a product to be selected in order to display associated items.', + 'woocommerce' + ), + collection.title + ), + { + strong: , + } + ) } + + + { + const isValidId = ( value[ 0 ]?.id ?? null ) !== null; + if ( isValidId ) { + props.setAttributes( { + query: { + ...attributes.query, + productReference: value[ 0 ].id, + }, + } ); + } + } } + messages={ { + search: __( 'Select a product', 'woocommerce' ), + } } + /> + +
+ ); +}; + +export default ProductPicker; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/editor.scss b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/editor.scss index 63ecbc2f692..4dac0fe0dc5 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/editor.scss +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/editor.scss @@ -168,3 +168,49 @@ $max-button-width: calc(100% / #{$max-button-columns}); color: var(--wp-components-color-accent-inverted, #fff); } } + +// Editor Product Picker +.wc-blocks-product-collection__editor-product-picker { + .wc-blocks-product-collection__info-icon { + fill: var(--wp--preset--color--luminous-vivid-orange, #e26f56); + } +} + +// Linked Product Control +.wc-block-product-collection-linked-product-control { + width: 100%; + text-align: left; + + &__button { + width: 100%; + height: 100%; + padding: 10px; + border: 1px solid $gray-300; + } + + &__image-container { + flex-shrink: 0; + width: 45px; + height: 45px; + + img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + } + } + + &__content { + text-align: left; + } +} + +.wc-block-product-collection-linked-product__popover-content .components-popover__content { + width: 100%; + + .woocommerce-search-list__search { + border: 0; + padding: 0; + } +} diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/index.tsx index de9dcb3c03d..4206a024714 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/index.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/index.tsx @@ -4,18 +4,25 @@ import { store as blockEditorStore } from '@wordpress/block-editor'; import { useState } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; +import { useGetLocation } from '@woocommerce/blocks/product-template/utils'; /** * Internal dependencies */ -import type { ProductCollectionEditComponentProps } from '../types'; +import { + ProductCollectionEditComponentProps, + ProductCollectionUIStatesInEditor, +} from '../types'; import ProductCollectionPlaceholder from './product-collection-placeholder'; import ProductCollectionContent from './product-collection-content'; import CollectionSelectionModal from './collection-selection-modal'; import './editor.scss'; +import { getProductCollectionUIStateInEditor } from '../utils'; +import ProductPicker from './ProductPicker'; const Edit = ( props: ProductCollectionEditComponentProps ) => { const { clientId, attributes } = props; + const location = useGetLocation( props.context, props.clientId ); const [ isSelectionModalOpen, setIsSelectionModalOpen ] = useState( false ); const hasInnerBlocks = useSelect( @@ -24,9 +31,37 @@ const Edit = ( props: ProductCollectionEditComponentProps ) => { [ clientId ] ); - const Component = hasInnerBlocks - ? ProductCollectionContent - : ProductCollectionPlaceholder; + const productCollectionUIStateInEditor = + getProductCollectionUIStateInEditor( { + hasInnerBlocks, + location, + attributes: props.attributes, + usesReference: props.usesReference, + } ); + + /** + * Component to render based on the UI state. + */ + let Component, + isUsingReferencePreviewMode = false; + switch ( productCollectionUIStateInEditor ) { + case ProductCollectionUIStatesInEditor.COLLECTION_PICKER: + Component = ProductCollectionPlaceholder; + break; + case ProductCollectionUIStatesInEditor.PRODUCT_REFERENCE_PICKER: + Component = ProductPicker; + break; + case ProductCollectionUIStatesInEditor.VALID: + Component = ProductCollectionContent; + break; + case ProductCollectionUIStatesInEditor.VALID_WITH_PREVIEW: + Component = ProductCollectionContent; + isUsingReferencePreviewMode = true; + break; + default: + // By default showing collection chooser. + Component = ProductCollectionPlaceholder; + } return ( <> @@ -35,6 +70,9 @@ const Edit = ( props: ProductCollectionEditComponentProps ) => { openCollectionSelectionModal={ () => setIsSelectionModalOpen( true ) } + isUsingReferencePreviewMode={ isUsingReferencePreviewMode } + location={ location } + usesReference={ props.usesReference } /> { isSelectionModalOpen && ( ( filter: FilterName ) => { @@ -121,6 +122,13 @@ const ProductCollectionInspectorControls = ( return ( + + { diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/linked-product-control.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/linked-product-control.tsx new file mode 100644 index 00000000000..35606aff0a9 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/linked-product-control.tsx @@ -0,0 +1,166 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import ProductControl from '@woocommerce/editor-components/product-control'; +import { SelectedOption } from '@woocommerce/block-hocs'; +import { useState, useMemo } from '@wordpress/element'; +import type { WooCommerceBlockLocation } from '@woocommerce/blocks/product-template/utils'; +import type { ProductResponseItem } from '@woocommerce/types'; +import { decodeEntities } from '@wordpress/html-entities'; +import { + PanelBody, + PanelRow, + Button, + Flex, + FlexItem, + Dropdown, + // @ts-expect-error Using experimental features + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __experimentalText as Text, + Spinner, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { useGetProduct } from '../../utils'; +import type { + ProductCollectionQuery, + ProductCollectionSetAttributes, +} from '../../types'; + +const ProductButton: React.FC< { + isOpen: boolean; + onToggle: () => void; + product: ProductResponseItem | null; + isLoading: boolean; +} > = ( { isOpen, onToggle, product, isLoading } ) => { + if ( isLoading && ! product ) { + return ; + } + + return ( + + ); +}; + +const LinkedProductPopoverContent: React.FC< { + query: ProductCollectionQuery; + setAttributes: ProductCollectionSetAttributes; + setIsDropdownOpen: React.Dispatch< React.SetStateAction< boolean > >; +} > = ( { query, setAttributes, setIsDropdownOpen } ) => ( + { + const productId = value[ 0 ]?.id ?? null; + if ( productId !== null ) { + setAttributes( { + query: { + ...query, + productReference: productId, + }, + } ); + setIsDropdownOpen( false ); + } + } } + messages={ { + search: __( 'Select a product', 'woocommerce' ), + } } + /> +); + +const LinkedProductControl = ( { + query, + setAttributes, + location, + usesReference, +}: { + query: ProductCollectionQuery; + setAttributes: ProductCollectionSetAttributes; + location: WooCommerceBlockLocation; + usesReference: string[] | undefined; +} ) => { + const [ isDropdownOpen, setIsDropdownOpen ] = useState< boolean >( false ); + const { product, isLoading } = useGetProduct( query.productReference ); + + const showLinkedProductControl = useMemo( () => { + const isInRequiredLocation = usesReference?.includes( location.type ); + const isProductContextRequired = usesReference?.includes( 'product' ); + const isProductContextSelected = + ( query?.productReference ?? null ) !== null; + + return ( + isProductContextRequired && + ! isInRequiredLocation && + isProductContextSelected + ); + }, [ location.type, query?.productReference, usesReference ] ); + + if ( ! showLinkedProductControl ) return null; + + return ( + + + ( + + ) } + renderContent={ () => ( + + ) } + open={ isDropdownOpen } + onToggle={ () => setIsDropdownOpen( ! isDropdownOpen ) } + /> + + + ); +}; + +export default LinkedProductControl; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/product-collection-content.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/product-collection-content.tsx index dadbddb7751..35714946c42 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/product-collection-content.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/product-collection-content.tsx @@ -10,7 +10,6 @@ import { useInstanceId } from '@wordpress/compose'; import { useEffect, useRef, useMemo } from '@wordpress/element'; import { Button } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; -import { useGetLocation } from '@woocommerce/blocks/product-template/utils'; import fastDeepEqual from 'fast-deep-equal/es6'; /** @@ -68,19 +67,23 @@ const useQueryId = ( const ProductCollectionContent = ( { preview: { setPreviewState, initialPreviewState } = {}, - usesReference, ...props }: ProductCollectionEditComponentProps ) => { const isInitialAttributesSet = useRef( false ); - const { clientId, attributes, setAttributes } = props; - const location = useGetLocation( props.context, props.clientId ); + const { + clientId, + attributes, + setAttributes, + location, + isUsingReferencePreviewMode, + } = props; useSetPreviewState( { setPreviewState, setAttributes, location, attributes, - usesReference, + isUsingReferencePreviewMode, } ); const blockProps = useBlockProps(); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts index 4407c682abe..55a8ee9b460 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts @@ -9,6 +9,16 @@ import { type AttributeMetadata } from '@woocommerce/types'; */ import { WooCommerceBlockLocation } from '../product-template/utils'; +export enum ProductCollectionUIStatesInEditor { + COLLECTION_PICKER = 'collection_chooser', + PRODUCT_REFERENCE_PICKER = 'product_context_picker', + VALID_WITH_PREVIEW = 'uses_reference_preview_mode', + VALID = 'valid', + // Future states + // INVALID = 'invalid', + // DELETED_PRODUCT_REFERENCE = 'deleted_product_reference', +} + export interface ProductCollectionAttributes { query: ProductCollectionQuery; queryId: number; @@ -95,6 +105,7 @@ export interface ProductCollectionQuery { woocommerceHandPickedProducts: string[]; priceRange: undefined | PriceRange; filterable: boolean; + productReference?: number; } export type ProductCollectionEditComponentProps = @@ -108,6 +119,8 @@ export type ProductCollectionEditComponentProps = context: { templateSlug: string; }; + isUsingReferencePreviewMode: boolean; + location: WooCommerceBlockLocation; }; export type TProductCollectionOrder = 'asc' | 'desc'; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/utils.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/utils.tsx index bdbd882e88a..0565027bfe1 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/utils.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/utils.tsx @@ -6,8 +6,10 @@ import { addFilter } from '@wordpress/hooks'; import { select } from '@wordpress/data'; import { isWpVersion } from '@woocommerce/settings'; import type { BlockEditProps, Block } from '@wordpress/blocks'; -import { useLayoutEffect } from '@wordpress/element'; +import { useEffect, useLayoutEffect, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; +import type { ProductResponseItem } from '@woocommerce/types'; +import { getProduct } from '@woocommerce/editor-components/utils'; import { createBlock, // @ts-expect-error Type definitions for this function are missing in Guteberg @@ -18,13 +20,14 @@ import { * Internal dependencies */ import { - type ProductCollectionAttributes, - type TProductCollectionOrder, - type TProductCollectionOrderBy, - type ProductCollectionQuery, - type ProductCollectionDisplayLayout, - type PreviewState, - type SetPreviewState, + ProductCollectionAttributes, + TProductCollectionOrder, + TProductCollectionOrderBy, + ProductCollectionQuery, + ProductCollectionDisplayLayout, + PreviewState, + SetPreviewState, + ProductCollectionUIStatesInEditor, } from './types'; import { coreQueryPaginationBlockName, @@ -166,41 +169,14 @@ export const addProductCollectionToQueryPaginationParentOrAncestor = () => { }; /** - * Get the preview message for the Product Collection block based on the usesReference. - * There are two scenarios: - * 1. When usesReference is product, the preview message will be: - * "Actual products will vary depending on the product being viewed." - * 2. For all other usesReference, the preview message will be: - * "Actual products will vary depending on the page being viewed." - * - * This message will be shown when the usesReference isn't available on the Editor side, but is available on the Frontend. + * Get the message to show in the preview label when the block is in preview mode based + * on the `usesReference` value. */ export const getUsesReferencePreviewMessage = ( location: WooCommerceBlockLocation, - usesReference?: string[] + isUsingReferencePreviewMode: boolean ) => { - if ( ! ( Array.isArray( usesReference ) && usesReference.length > 0 ) ) { - return ''; - } - - if ( usesReference.includes( location.type ) ) { - /** - * Block shouldn't be in preview mode when: - * 1. Current location is archive and termId is available. - * 2. Current location is product and productId is available. - * - * Because in these cases, we have required context on the editor side. - */ - const isArchiveLocationWithTermId = - location.type === LocationType.Archive && - ( location.sourceData?.termId ?? null ) !== null; - const isProductLocationWithProductId = - location.type === LocationType.Product && - ( location.sourceData?.productId ?? null ) !== null; - if ( isArchiveLocationWithTermId || isProductLocationWithProductId ) { - return ''; - } - + if ( isUsingReferencePreviewMode ) { if ( location.type === LocationType.Product ) { return __( 'Actual products will vary depending on the product being viewed.', @@ -217,12 +193,77 @@ export const getUsesReferencePreviewMessage = ( return ''; }; +export const getProductCollectionUIStateInEditor = ( { + location, + usesReference, + attributes, + hasInnerBlocks, +}: { + location: WooCommerceBlockLocation; + usesReference?: string[] | undefined; + attributes: ProductCollectionAttributes; + hasInnerBlocks: boolean; +} ): ProductCollectionUIStatesInEditor => { + const isInRequiredLocation = usesReference?.includes( location.type ); + const isCollectionSelected = !! attributes.collection; + + /** + * Case 1: Product context picker + */ + const isProductContextRequired = usesReference?.includes( 'product' ); + const isProductContextSelected = + ( attributes.query?.productReference ?? null ) !== null; + if ( + isCollectionSelected && + isProductContextRequired && + ! isInRequiredLocation && + ! isProductContextSelected + ) { + return ProductCollectionUIStatesInEditor.PRODUCT_REFERENCE_PICKER; + } + + /** + * Case 2: Preview mode - based on `usesReference` value + */ + if ( isInRequiredLocation ) { + /** + * Block shouldn't be in preview mode when: + * 1. Current location is archive and termId is available. + * 2. Current location is product and productId is available. + * + * Because in these cases, we have required context on the editor side. + */ + const isArchiveLocationWithTermId = + location.type === LocationType.Archive && + ( location.sourceData?.termId ?? null ) !== null; + const isProductLocationWithProductId = + location.type === LocationType.Product && + ( location.sourceData?.productId ?? null ) !== null; + + if ( + ! isArchiveLocationWithTermId && + ! isProductLocationWithProductId + ) { + return ProductCollectionUIStatesInEditor.VALID_WITH_PREVIEW; + } + } + + /** + * Case 3: Collection chooser + */ + if ( ! hasInnerBlocks && ! isCollectionSelected ) { + return ProductCollectionUIStatesInEditor.COLLECTION_PICKER; + } + + return ProductCollectionUIStatesInEditor.VALID; +}; + export const useSetPreviewState = ( { setPreviewState, location, attributes, setAttributes, - usesReference, + isUsingReferencePreviewMode, }: { setPreviewState?: SetPreviewState | undefined; location: WooCommerceBlockLocation; @@ -231,6 +272,7 @@ export const useSetPreviewState = ( { attributes: Partial< ProductCollectionAttributes > ) => void; usesReference?: string[] | undefined; + isUsingReferencePreviewMode: boolean; } ) => { const setState = ( newPreviewState: PreviewState ) => { setAttributes( { @@ -240,8 +282,6 @@ export const useSetPreviewState = ( { }, } ); }; - const isCollectionUsesReference = - usesReference && usesReference?.length > 0; /** * When usesReference is available on Frontend but not on Editor side, @@ -249,10 +289,10 @@ export const useSetPreviewState = ( { */ const usesReferencePreviewMessage = getUsesReferencePreviewMessage( location, - usesReference + isUsingReferencePreviewMode ); useLayoutEffect( () => { - if ( isCollectionUsesReference ) { + if ( isUsingReferencePreviewMode ) { setAttributes( { __privatePreviewState: { isPreview: usesReferencePreviewMessage.length > 0, @@ -263,12 +303,12 @@ export const useSetPreviewState = ( { }, [ setAttributes, usesReferencePreviewMessage, - isCollectionUsesReference, + isUsingReferencePreviewMode, ] ); // Running setPreviewState function provided by Collection, if it exists. useLayoutEffect( () => { - if ( ! setPreviewState && ! isCollectionUsesReference ) { + if ( ! setPreviewState && ! isUsingReferencePreviewMode ) { return; } @@ -294,11 +334,14 @@ export const useSetPreviewState = ( { * - Products by tag * - Products by attribute */ + const termId = + location.type === LocationType.Archive + ? location.sourceData?.termId + : null; useLayoutEffect( () => { - if ( ! setPreviewState && ! isCollectionUsesReference ) { + if ( ! setPreviewState && ! isUsingReferencePreviewMode ) { const isGenericArchiveTemplate = - location.type === LocationType.Archive && - location.sourceData?.termId === null; + location.type === LocationType.Archive && termId === null; setAttributes( { __privatePreviewState: { @@ -315,11 +358,11 @@ export const useSetPreviewState = ( { }, [ attributes?.query?.inherit, usesReferencePreviewMessage, - location.sourceData?.termId, + termId, location.type, setAttributes, setPreviewState, - isCollectionUsesReference, + isUsingReferencePreviewMode, ] ); }; @@ -356,3 +399,35 @@ export const getDefaultProductCollection = () => }, createBlocksFromInnerBlocksTemplate( INNER_BLOCKS_TEMPLATE ) ); + +export const useGetProduct = ( productId: number | undefined ) => { + const [ product, setProduct ] = useState< ProductResponseItem | null >( + null + ); + const [ isLoading, setIsLoading ] = useState< boolean >( false ); + + useEffect( () => { + const fetchProduct = async () => { + if ( productId ) { + setIsLoading( true ); + try { + const fetchedProduct = ( await getProduct( + productId + ) ) as ProductResponseItem; + setProduct( fetchedProduct ); + } catch ( error ) { + setProduct( null ); + } finally { + setIsLoading( false ); + } + } else { + setProduct( null ); + setIsLoading( false ); + } + }; + + fetchProduct(); + }, [ productId ] ); + + return { product, isLoading }; +}; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-template/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-template/edit.tsx index 5c469725163..1c09035f46e 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-template/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-template/edit.tsx @@ -266,7 +266,7 @@ const ProductTemplateEdit = ( products: getEntityRecords( 'postType', postType, { ...query, ...restQueryArgs, - location, + productCollectionLocation: location, productCollectionQueryContext, previewState: __privateProductCollectionPreviewState, /** diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-template/utils.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-template/utils.tsx index 5f5344a7296..9106d24ba3c 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-template/utils.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-template/utils.tsx @@ -63,17 +63,65 @@ const prepareIsInGenericTemplate = ( entitySlug: string ): boolean => templateSlug === entitySlug; -export type WooCommerceBlockLocation = ReturnType< - typeof createLocationObject ->; +interface WooCommerceBaseLocation { + type: LocationType; + sourceData?: object | undefined; +} -const createLocationObject = ( - type: LocationType, - sourceData: Record< string, unknown > = {} -) => ( { - type, - sourceData, -} ); +interface ProductLocation extends WooCommerceBaseLocation { + type: LocationType.Product; + sourceData?: + | { + productId: number; + } + | undefined; +} + +interface ArchiveLocation extends WooCommerceBaseLocation { + type: LocationType.Archive; + sourceData?: + | { + taxonomy: string; + termId: number; + } + | undefined; +} + +interface CartLocation extends WooCommerceBaseLocation { + type: LocationType.Cart; + sourceData?: + | { + productIds: number[]; + } + | undefined; +} + +interface OrderLocation extends WooCommerceBaseLocation { + type: LocationType.Order; + sourceData?: + | { + orderId: number; + } + | undefined; +} + +interface SiteLocation extends WooCommerceBaseLocation { + type: LocationType.Site; + sourceData?: object | undefined; +} + +export type WooCommerceBlockLocation = + | ProductLocation + | ArchiveLocation + | CartLocation + | OrderLocation + | SiteLocation; + +const createLocationObject = ( type: LocationType, sourceData: object = {} ) => + ( { + type, + sourceData, + } as WooCommerceBlockLocation ); type ContextProperties = { templateSlug: string; @@ -83,7 +131,7 @@ type ContextProperties = { export const useGetLocation = < T, >( context: Context< T & ContextProperties >, clientId: string -) => { +): WooCommerceBlockLocation => { const templateSlug = context.templateSlug || ''; const postId = context.postId || null; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/single-product/save.tsx b/plugins/woocommerce-blocks/assets/js/blocks/single-product/save.tsx index 0feb6d8f950..4c8f4ef2973 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/single-product/save.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/single-product/save.tsx @@ -4,7 +4,9 @@ import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; const Save = () => { - const blockProps = useBlockProps.save(); + const blockProps = useBlockProps.save( { + className: 'woocommerce', + } ); return (
diff --git a/plugins/woocommerce-blocks/assets/js/editor-components/product-control/index.tsx b/plugins/woocommerce-blocks/assets/js/editor-components/product-control/index.tsx index bfa1e5f2a85..f86f2878dc7 100644 --- a/plugins/woocommerce-blocks/assets/js/editor-components/product-control/index.tsx +++ b/plugins/woocommerce-blocks/assets/js/editor-components/product-control/index.tsx @@ -62,6 +62,16 @@ interface ProductControlProps { * Whether to show variations in the list of items available. */ showVariations?: boolean; + /** + * Different messages to display in the component. + * If any of the messages are not provided, the default message will be used. + */ + messages?: { + list?: string; + noItems?: string; + search?: string; + updated?: string; + }; } const messages = { @@ -188,7 +198,7 @@ const ProductControl = ( } else if ( showVariations ) { return renderItemWithVariations; } - return () => null; + return undefined; }; if ( error ) { @@ -216,7 +226,10 @@ const ProductControl = ( onChange={ onChange } renderItem={ getRenderItemFunc() } onSearch={ onSearch } - messages={ messages } + messages={ { + ...messages, + ...props.messages, + } } isHierarchical /> ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts index 788f27b123a..e38db1b526a 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts @@ -9,6 +9,7 @@ import { test as base, expect } from '@woocommerce/e2e-utils'; */ import ProductCollectionPage, { BLOCK_LABELS, + Collections, SELECTORS, } from './product-collection.page'; @@ -81,6 +82,87 @@ test.describe( 'Product Collection', () => { ).toBeVisible(); } ); + test.describe( 'when no results are found', () => { + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'does not render', async ( { page, editor, pageObject } ) => { + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( 'featured' ); + await pageObject.addFilter( 'Price Range' ); + await pageObject.setPriceRange( { + max: '1', + } ); + + const featuredBlock = editor.canvas.getByLabel( 'Block: Featured' ); + + await expect( + featuredBlock.getByText( 'Featured products' ) + ).toBeVisible(); + // The "No results found" info is rendered in editor for all collections. + await expect( + featuredBlock.getByText( 'No results found' ) + ).toBeVisible(); + + await pageObject.publishAndGoToFrontend(); + + const content = page.locator( 'main' ); + + await expect( content ).not.toContainText( 'Featured products' ); + await expect( content ).not.toContainText( 'No results found' ); + } ); + + // This test ensures the runtime render state is correctly reset for + // each block. + test( 'does not prevent subsequent blocks from render', async ( { + page, + pageObject, + } ) => { + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( 'featured' ); + await pageObject.addFilter( 'Price Range' ); + await pageObject.setPriceRange( { + max: '1', + } ); + + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( 'topRated' ); + + await pageObject.refreshLocators( 'editor' ); + await expect( pageObject.products ).toHaveCount( 5 ); + + await pageObject.publishAndGoToFrontend(); + + await pageObject.refreshLocators( 'frontend' ); + await expect( pageObject.products ).toHaveCount( 5 ); + await expect( page.locator( 'main' ) ).not.toContainText( + 'Featured products' + ); + } ); + + test( 'renders if No Results block is present', async ( { + page, + editor, + pageObject, + } ) => { + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( 'productCatalog' ); + await pageObject.addFilter( 'Price Range' ); + await pageObject.setPriceRange( { + max: '1', + } ); + + await expect( + editor.canvas.getByText( 'No results found' ) + ).toBeVisible(); + + await pageObject.publishAndGoToFrontend(); + + await expect( page.getByText( 'No results found' ) ).toBeVisible(); + } ); + } ); + test.describe( 'Renders correctly with all Product Elements', () => { const expectedProductContent = [ 'Beanie', // core/post-title @@ -321,7 +403,7 @@ test.describe( 'Product Collection', () => { } ); } ); - test.describe( 'Location is recognised', () => { + test.describe( 'Location is recognized', () => { const filterRequest = ( request: Request ) => { const url = request.url(); return ( @@ -337,7 +419,9 @@ test.describe( 'Product Collection', () => { return ( url.includes( 'wp/v2/product' ) && searchParams.get( 'isProductCollectionBlock' ) === 'true' && - !! searchParams.get( `location[sourceData][productId]` ) + !! searchParams.get( + `productCollectionLocation[sourceData][productId]` + ) ); }; @@ -349,26 +433,30 @@ test.describe( 'Product Collection', () => { if ( locationType === 'product' ) { return { - type: searchParams.get( 'location[type]' ), + type: searchParams.get( 'productCollectionLocation[type]' ), productId: searchParams.get( - `location[sourceData][productId]` + `productCollectionLocation[sourceData][productId]` ), }; } if ( locationType === 'archive' ) { return { - type: searchParams.get( 'location[type]' ), + type: searchParams.get( 'productCollectionLocation[type]' ), taxonomy: searchParams.get( - `location[sourceData][taxonomy]` + `productCollectionLocation[sourceData][taxonomy]` + ), + termId: searchParams.get( + `productCollectionLocation[sourceData][termId]` ), - termId: searchParams.get( `location[sourceData][termId]` ), }; } return { - type: searchParams.get( 'location[type]' ), - sourceData: searchParams.get( `location[sourceData]` ), + type: searchParams.get( 'productCollectionLocation[type]' ), + sourceData: searchParams.get( + `productCollectionLocation[sourceData]` + ), }; }; @@ -401,10 +489,10 @@ test.describe( 'Product Collection', () => { pageObject.BLOCK_NAME ); - const locationReuqestPromise = + const locationRequestPromise = page.waitForRequest( filterProductRequest ); await pageObject.chooseCollectionInTemplate( 'featured' ); - const locationRequest = await locationReuqestPromise; + const locationRequest = await locationRequestPromise; const { type, productId } = getLocationDetailsFromRequest( locationRequest, @@ -880,3 +968,309 @@ test.describe( 'Product Collection', () => { } ); } ); } ); + +test.describe( 'Testing "usesReference" argument in "registerProductCollection"', () => { + const MY_REGISTERED_COLLECTIONS = { + myCustomCollectionWithProductContext: { + name: 'My Custom Collection - Product Context', + label: 'Block: My Custom Collection - Product Context', + previewLabelTemplate: [ 'woocommerce/woocommerce//single-product' ], + shouldShowProductPicker: true, + }, + myCustomCollectionWithCartContext: { + name: 'My Custom Collection - Cart Context', + label: 'Block: My Custom Collection - Cart Context', + previewLabelTemplate: [ 'woocommerce/woocommerce//page-cart' ], + shouldShowProductPicker: false, + }, + myCustomCollectionWithOrderContext: { + name: 'My Custom Collection - Order Context', + label: 'Block: My Custom Collection - Order Context', + previewLabelTemplate: [ + 'woocommerce/woocommerce//order-confirmation', + ], + shouldShowProductPicker: false, + }, + myCustomCollectionWithArchiveContext: { + name: 'My Custom Collection - Archive Context', + label: 'Block: My Custom Collection - Archive Context', + previewLabelTemplate: [ + 'woocommerce/woocommerce//taxonomy-product_cat', + ], + shouldShowProductPicker: false, + }, + myCustomCollectionMultipleContexts: { + name: 'My Custom Collection - Multiple Contexts', + label: 'Block: My Custom Collection - Multiple Contexts', + previewLabelTemplate: [ + 'woocommerce/woocommerce//single-product', + 'woocommerce/woocommerce//order-confirmation', + ], + shouldShowProductPicker: true, + }, + }; + + // Activate plugin which registers custom product collections + test.beforeEach( async ( { requestUtils } ) => { + await requestUtils.activatePlugin( + 'register-product-collection-tester' + ); + } ); + + Object.entries( MY_REGISTERED_COLLECTIONS ).forEach( + ( [ key, collection ] ) => { + for ( const template of collection.previewLabelTemplate ) { + test( `Collection "${ collection.name }" should show preview label in "${ template }"`, async ( { + pageObject, + editor, + } ) => { + await pageObject.goToEditorTemplate( template ); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInTemplate( + key as Collections + ); + + const block = editor.canvas.getByLabel( collection.label ); + const previewButtonLocator = block.getByTestId( + SELECTORS.previewButtonTestID + ); + + await expect( previewButtonLocator ).toBeVisible(); + } ); + } + + test( `Collection "${ collection.name }" should not show preview label in a post`, async ( { + pageObject, + editor, + admin, + } ) => { + await admin.createNewPost(); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( key as Collections ); + + // Check visibility of product picker + const editorProductPicker = editor.canvas.locator( + SELECTORS.productPicker + ); + const expectedVisibility = collection.shouldShowProductPicker + ? 'toBeVisible' + : 'toBeHidden'; + await expect( editorProductPicker )[ expectedVisibility ](); + + if ( collection.shouldShowProductPicker ) { + await pageObject.chooseProductInEditorProductPickerIfAvailable( + editor.canvas + ); + } + + // At this point, the product picker should be hidden + await expect( editorProductPicker ).toBeHidden(); + + // Check visibility of preview label + const block = editor.canvas.getByLabel( collection.label ); + const previewButtonLocator = block.getByTestId( + SELECTORS.previewButtonTestID + ); + + await expect( previewButtonLocator ).toBeHidden(); + } ); + + test( `Collection "${ collection.name }" should not show preview label in Product Catalog template`, async ( { + pageObject, + editor, + } ) => { + await pageObject.goToProductCatalogAndInsertCollection( + key as Collections + ); + + const block = editor.canvas.getByLabel( collection.label ); + const previewButtonLocator = block.getByTestId( + SELECTORS.previewButtonTestID + ); + + await expect( previewButtonLocator ).toBeHidden(); + } ); + } + ); +} ); + +test.describe( 'Product picker', () => { + const MY_REGISTERED_COLLECTIONS_THAT_NEEDS_PRODUCT = { + myCustomCollectionWithProductContext: { + name: 'My Custom Collection - Product Context', + label: 'Block: My Custom Collection - Product Context', + collection: + 'woocommerce/product-collection/my-custom-collection-product-context', + }, + myCustomCollectionMultipleContexts: { + name: 'My Custom Collection - Multiple Contexts', + label: 'Block: My Custom Collection - Multiple Contexts', + collection: + 'woocommerce/product-collection/my-custom-collection-multiple-contexts', + }, + }; + + // Activate plugin which registers custom product collections + test.beforeEach( async ( { requestUtils } ) => { + await requestUtils.activatePlugin( + 'register-product-collection-tester' + ); + } ); + + Object.entries( MY_REGISTERED_COLLECTIONS_THAT_NEEDS_PRODUCT ).forEach( + ( [ key, collection ] ) => { + test( `For collection "${ collection.name }" - manually selected product reference should be available on Frontend in a post`, async ( { + pageObject, + admin, + page, + editor, + } ) => { + await admin.createNewPost(); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( key as Collections ); + + // Verify that product picker is shown in Editor + const editorProductPicker = editor.canvas.locator( + SELECTORS.productPicker + ); + await expect( editorProductPicker ).toBeVisible(); + + // Once a product is selected, the product picker should be hidden + await pageObject.chooseProductInEditorProductPickerIfAvailable( + editor.canvas + ); + await expect( editorProductPicker ).toBeHidden(); + + // On Frontend, verify that product reference is a number + await pageObject.publishAndGoToFrontend(); + const collectionWithProductContext = page.locator( + `[data-collection="${ collection.collection }"]` + ); + const queryAttribute = JSON.parse( + ( await collectionWithProductContext.getAttribute( + 'data-query' + ) ) || '{}' + ); + expect( typeof queryAttribute?.productReference ).toBe( + 'number' + ); + } ); + + test( `For collection "${ collection.name }" - changing product using inspector control`, async ( { + pageObject, + admin, + page, + editor, + } ) => { + await admin.createNewPost(); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( key as Collections ); + + // Verify that product picker is shown in Editor + const editorProductPicker = editor.canvas.locator( + SELECTORS.productPicker + ); + await expect( editorProductPicker ).toBeVisible(); + + // Once a product is selected, the product picker should be hidden + await pageObject.chooseProductInEditorProductPickerIfAvailable( + editor.canvas + ); + await expect( editorProductPicker ).toBeHidden(); + + // Verify that Album is selected + await expect( + admin.page.locator( SELECTORS.linkedProductControl.button ) + ).toContainText( 'Album' ); + + // Change product using inspector control to Beanie + await admin.page + .locator( SELECTORS.linkedProductControl.button ) + .click(); + await admin.page + .locator( SELECTORS.linkedProductControl.popoverContent ) + .getByLabel( 'Beanie', { exact: true } ) + .click(); + await expect( + admin.page.locator( SELECTORS.linkedProductControl.button ) + ).toContainText( 'Beanie' ); + + // On Frontend, verify that product reference is a number + await pageObject.publishAndGoToFrontend(); + const collectionWithProductContext = page.locator( + `[data-collection="${ collection.collection }"]` + ); + const queryAttribute = JSON.parse( + ( await collectionWithProductContext.getAttribute( + 'data-query' + ) ) || '{}' + ); + expect( typeof queryAttribute?.productReference ).toBe( + 'number' + ); + } ); + + test( `For collection "${ collection.name }" - product picker shouldn't be shown in Single Product template`, async ( { + pageObject, + admin, + editor, + } ) => { + await admin.visitSiteEditor( { + postId: `woocommerce/woocommerce//single-product`, + postType: 'wp_template', + canvas: 'edit', + } ); + await editor.canvas.locator( 'body' ).click(); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInTemplate( + key as Collections + ); + + const editorProductPicker = editor.canvas.locator( + SELECTORS.productPicker + ); + await expect( editorProductPicker ).toBeHidden(); + } ); + } + ); + + test( 'Product picker should work as expected while changing collection using "Choose collection" button from Toolbar', async ( { + pageObject, + admin, + editor, + } ) => { + await admin.createNewPost(); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( + 'myCustomCollectionWithProductContext' + ); + + // Verify that product picker is shown in Editor + const editorProductPicker = editor.canvas.locator( + SELECTORS.productPicker + ); + await expect( editorProductPicker ).toBeVisible(); + + // Once a product is selected, the product picker should be hidden + await pageObject.chooseProductInEditorProductPickerIfAvailable( + editor.canvas + ); + await expect( editorProductPicker ).toBeHidden(); + + // Change collection using Toolbar + await pageObject.changeCollectionUsingToolbar( + 'myCustomCollectionMultipleContexts' + ); + await expect( editorProductPicker ).toBeVisible(); + + // Once a product is selected, the product picker should be hidden + await pageObject.chooseProductInEditorProductPickerIfAvailable( + editor.canvas + ); + await expect( editorProductPicker ).toBeHidden(); + + // Product picker should be hidden for collections that don't need product + await pageObject.changeCollectionUsingToolbar( 'featured' ); + await expect( editorProductPicker ).toBeHidden(); + } ); +} ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts index 22a0fb3e3e1..1a87ebeb605 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts @@ -1,7 +1,7 @@ /** * External dependencies */ -import { Locator, Page } from '@playwright/test'; +import { FrameLocator, Locator, Page } from '@playwright/test'; import { Editor, Admin } from '@woocommerce/e2e-utils'; import { BlockRepresentation } from '@wordpress/e2e-test-utils-playwright/build-types/editor/insert-block'; @@ -62,6 +62,12 @@ export const SELECTORS = { previewButtonTestID: 'product-collection-preview-button', collectionPlaceholder: '[data-type="woocommerce/product-collection"] .components-placeholder', + productPicker: '.wc-blocks-product-collection__editor-product-picker', + linkedProductControl: { + button: '.wc-block-product-collection-linked-product-control__button', + popoverContent: + '.wc-block-product-collection-linked-product__popover-content', + }, }; export type Collections = @@ -200,10 +206,31 @@ class ProductCollectionPage { } } + async chooseProductInEditorProductPickerIfAvailable( + pageReference: Page | FrameLocator + ) { + const editorProductPicker = pageReference.locator( + SELECTORS.productPicker + ); + + if ( await editorProductPicker.isVisible() ) { + await editorProductPicker + .locator( 'label' ) + .filter( { + hasText: 'Album', + } ) + .click(); + } + } + async createNewPostAndInsertBlock( collection?: Collections ) { await this.admin.createNewPost(); await this.insertProductCollection(); await this.chooseCollectionInPost( collection ); + // If product picker is available, choose a product. + await this.chooseProductInEditorProductPickerIfAvailable( + this.admin.page + ); await this.refreshLocators( 'editor' ); await this.editor.openDocumentSettingsSidebar(); } @@ -345,6 +372,10 @@ class ProductCollectionPage { await this.editor.canvas.locator( 'body' ).click(); await this.insertProductCollection(); await this.chooseCollectionInTemplate( collection ); + // If product picker is available, choose a product. + await this.chooseProductInEditorProductPickerIfAvailable( + this.editor.canvas + ); await this.refreshLocators( 'editor' ); } @@ -571,6 +602,30 @@ class ProductCollectionPage { .click(); } + async changeCollectionUsingToolbar( collection: Collections ) { + // Click "Choose collection" button in the toolbar. + await this.admin.page + .getByRole( 'toolbar', { name: 'Block Tools' } ) + .getByRole( 'button', { name: 'Choose collection' } ) + .click(); + + // Select the collection from the modal. + const collectionChooserModal = this.admin.page.locator( + '.wc-blocks-product-collection__modal' + ); + await collectionChooserModal + .getByRole( 'button', { + name: collectionToButtonNameMap[ collection ], + } ) + .click(); + + await collectionChooserModal + .getByRole( 'button', { + name: 'Continue', + } ) + .click(); + } + async setDisplaySettings( { itemsPerPage, offset, diff --git a/plugins/woocommerce/changelog/50164-add-44877-context-linking-a-product-with-collection b/plugins/woocommerce/changelog/50164-add-44877-context-linking-a-product-with-collection new file mode 100644 index 00000000000..1308c1e61e7 --- /dev/null +++ b/plugins/woocommerce/changelog/50164-add-44877-context-linking-a-product-with-collection @@ -0,0 +1,4 @@ +Significance: major +Type: add + +Product Collection - Show product picker in Editor when collection requires a product but not available
A collection can define if it requires a product context. This can be done using `usesReference` argument i.e. ```tsx __experimentalRegisterProductCollection({ ..., usesReference: ['product'], ) ``` When product context doesn't exist in current template/page/post etc. then we show product picker in Editor. This way, merchant can manually provide a product context to the collection. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50590-add-44877-context-linking-a-product-with-collection-inspector-control b/plugins/woocommerce/changelog/50590-add-44877-context-linking-a-product-with-collection-inspector-control new file mode 100644 index 00000000000..2db092dad41 --- /dev/null +++ b/plugins/woocommerce/changelog/50590-add-44877-context-linking-a-product-with-collection-inspector-control @@ -0,0 +1,4 @@ +Significance: major +Type: add + +Product Collection - Implement Inspector control to change selected product \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50842-43149-fix-bulk-product-price-in-de-crease b/plugins/woocommerce/changelog/50842-43149-fix-bulk-product-price-in-de-crease new file mode 100644 index 00000000000..affaea6bdf9 --- /dev/null +++ b/plugins/woocommerce/changelog/50842-43149-fix-bulk-product-price-in-de-crease @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Product bulk edit: fix increasing & decreasing sale price when there was no previous sale. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/51033-47922-product-sku-improvements b/plugins/woocommerce/changelog/51033-47922-product-sku-improvements new file mode 100644 index 00000000000..1849a43e716 --- /dev/null +++ b/plugins/woocommerce/changelog/51033-47922-product-sku-improvements @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Product SKU block: add editable prefixes and suffixes. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/51067-revert-50531 b/plugins/woocommerce/changelog/51067-revert-50531 new file mode 100644 index 00000000000..e65258f8afe --- /dev/null +++ b/plugins/woocommerce/changelog/51067-revert-50531 @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak +Comment: This reverts an existing PR. + diff --git a/plugins/woocommerce/changelog/add-docblocks-to-products-shortcode-hooks b/plugins/woocommerce/changelog/add-docblocks-to-products-shortcode-hooks new file mode 100644 index 00000000000..0c79db17c86 --- /dev/null +++ b/plugins/woocommerce/changelog/add-docblocks-to-products-shortcode-hooks @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Added doc blocks to conditionally fired hooks to better explain nuanced behavior diff --git a/plugins/woocommerce/changelog/add-product_data_views_list b/plugins/woocommerce/changelog/add-product_data_views_list new file mode 100644 index 00000000000..7669d680be8 --- /dev/null +++ b/plugins/woocommerce/changelog/add-product_data_views_list @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Update webpack config to bundle in @wordpress/dataviews package. diff --git a/plugins/woocommerce/changelog/fix-43605 b/plugins/woocommerce/changelog/fix-43605 new file mode 100644 index 00000000000..ad1d926bd91 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-43605 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Trap focus inside the product gallery modal. diff --git a/plugins/woocommerce/changelog/fix-47794_variation_selector_display b/plugins/woocommerce/changelog/fix-47794_variation_selector_display new file mode 100644 index 00000000000..84ebab4b1c5 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-47794_variation_selector_display @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Fix variation selector display issues on the front end #51023 diff --git a/plugins/woocommerce/changelog/fix-50667-my-account-hooked-size b/plugins/woocommerce/changelog/fix-50667-my-account-hooked-size new file mode 100644 index 00000000000..4fcd396ddc4 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-50667-my-account-hooked-size @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix My Account block icon being too small when inserted via block hooks diff --git a/plugins/woocommerce/changelog/fix-50891 b/plugins/woocommerce/changelog/fix-50891 new file mode 100644 index 00000000000..33cc44e00b0 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-50891 @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Add filter for response of `wc_rest_should_load_namespace` function to allow loading namespaces. diff --git a/plugins/woocommerce/changelog/fix-cys-internal-classes b/plugins/woocommerce/changelog/fix-cys-internal-classes new file mode 100644 index 00000000000..0bbe2f28b7d --- /dev/null +++ b/plugins/woocommerce/changelog/fix-cys-internal-classes @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Mark several Customize Your Store PHP classes as internal diff --git a/plugins/woocommerce/changelog/fix-pc-prevent-rendering-on-empty-query b/plugins/woocommerce/changelog/fix-pc-prevent-rendering-on-empty-query new file mode 100644 index 00000000000..bbdaf94ece0 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-pc-prevent-rendering-on-empty-query @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Product Collection: Don't render when empty unless the no results block is present. diff --git a/plugins/woocommerce/changelog/fix-remote-logger-sanitise-query-params b/plugins/woocommerce/changelog/fix-remote-logger-sanitise-query-params new file mode 100644 index 00000000000..bf2342b18e0 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-remote-logger-sanitise-query-params @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Add query params masking to remote logger diff --git a/plugins/woocommerce/changelog/update-move-woo-version-check b/plugins/woocommerce/changelog/update-move-woo-version-check new file mode 100644 index 00000000000..af5e8c0070e --- /dev/null +++ b/plugins/woocommerce/changelog/update-move-woo-version-check @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Enhance WooCommerce version checking for remote logging reliability diff --git a/plugins/woocommerce/client/legacy/css/woocommerce.scss b/plugins/woocommerce/client/legacy/css/woocommerce.scss index efd786f4d58..8c97759bf97 100644 --- a/plugins/woocommerce/client/legacy/css/woocommerce.scss +++ b/plugins/woocommerce/client/legacy/css/woocommerce.scss @@ -445,6 +445,18 @@ p.demo_store, min-width: 75%; display: inline-block; margin-right: 1em; + + /* We hide the default chevron because it cannot be directly modified. Instead, we add a custom chevron using a background image. */ + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + padding-right: 2em; + background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJmZWF0aGVyIGZlYXRoZXItY2hldnJvbi1kb3duIj48cG9seWxpbmUgcG9pbnRzPSI2IDkgMTIgMTUgMTggOSI+PC9wb2x5bGluZT48L3N2Zz4=) + no-repeat; + background-size: 16px; + -webkit-background-size: 16px; + background-position: calc(100% - 12px) 50%; + -webkit-background-position: calc(100% - 12px) 50%; } td.label { diff --git a/plugins/woocommerce/client/legacy/js/frontend/single-product.js b/plugins/woocommerce/client/legacy/js/frontend/single-product.js index 6b5c99cd8e8..e8adcc662fc 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/single-product.js +++ b/plugins/woocommerce/client/legacy/js/frontend/single-product.js @@ -127,6 +127,8 @@ jQuery( function( $ ) { this.onResetSlidePosition = this.onResetSlidePosition.bind( this ); this.getGalleryItems = this.getGalleryItems.bind( this ); this.openPhotoswipe = this.openPhotoswipe.bind( this ); + this.trapFocusPhotoswipe = this.trapFocusPhotoswipe.bind( this ); + this.handlePswpTrapFocus = this.handlePswpTrapFocus.bind( this ); if ( this.flexslider_enabled ) { this.initFlexslider( args.flexslider ); @@ -307,8 +309,10 @@ jQuery( function( $ ) { e.preventDefault(); var pswpElement = $( '.pswp' )[0], - items = this.getGalleryItems(), - eventTarget = $( e.target ), + items = this.getGalleryItems(), + eventTarget = $( e.target ), + currentTarget = e.currentTarget, + self = this, clicked; if ( 0 < eventTarget.closest( '.woocommerce-product-gallery__trigger' ).length ) { @@ -326,14 +330,73 @@ jQuery( function( $ ) { } captionEl.children[0].textContent = item.title; return true; - } + }, + timeToIdle: 0, // Ensure the gallery controls are always visible to avoid keyboard navigation issues. }, wc_single_product_params.photoswipe_options ); // Initializes and opens PhotoSwipe. var photoswipe = new PhotoSwipe( pswpElement, PhotoSwipeUI_Default, items, options ); + + photoswipe.listen( 'afterInit', function() { + self.trapFocusPhotoswipe( true ); + }); + + photoswipe.listen( 'close', function() { + self.trapFocusPhotoswipe( false ); + currentTarget.focus(); + }); + photoswipe.init(); }; + /** + * Control focus in photoswipe modal. + * + * @param {boolean} trapFocus - Whether to trap focus or not. + */ + ProductGallery.prototype.trapFocusPhotoswipe = function( trapFocus ) { + var pswp = document.querySelector( '.pswp' ); + + if ( ! pswp ) { + return; + } + + if ( trapFocus ) { + pswp.addEventListener( 'keydown', this.handlePswpTrapFocus ); + } else { + pswp.removeEventListener( 'keydown', this.handlePswpTrapFocus ); + } + }; + + /** + * Handle keydown event in photoswipe modal. + */ + ProductGallery.prototype.handlePswpTrapFocus = function( e ) { + var allFocusablesEls = e.currentTarget.querySelectorAll( 'button:not([disabled])' ); + var filteredFocusablesEls = Array.from( allFocusablesEls ).filter( function( btn ) { + return btn.style.display !== 'none' && window.getComputedStyle( btn ).display !== 'none'; + } ); + + if ( 1 >= filteredFocusablesEls.length ) { + return; + } + + var firstTabStop = filteredFocusablesEls[0]; + var lastTabStop = filteredFocusablesEls[filteredFocusablesEls.length - 1]; + + if ( e.key === 'Tab' ) { + if ( e.shiftKey ) { + if ( document.activeElement === firstTabStop ) { + e.preventDefault(); + lastTabStop.focus(); + } + } else if ( document.activeElement === lastTabStop ) { + e.preventDefault(); + firstTabStop.focus(); + } + } + }; + /** * Function to call wc_product_gallery on jquery selector. */ diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-post-types.php b/plugins/woocommerce/includes/admin/class-wc-admin-post-types.php index 741fda165cd..095893653ba 100644 --- a/plugins/woocommerce/includes/admin/class-wc-admin-post-types.php +++ b/plugins/woocommerce/includes/admin/class-wc-admin-post-types.php @@ -896,7 +896,8 @@ class WC_Admin_Post_Types { return false; } - $old_price = (float) $product->{"get_{$price_type}_price"}(); + $old_price = $product->{"get_{$price_type}_price"}(); + $old_price = '' === $old_price ? (float) $product->get_regular_price() : (float) $old_price; $price_changed = false; $change_price = absint( $request_data[ "change_{$price_type}_price" ] ); diff --git a/plugins/woocommerce/includes/shortcodes/class-wc-shortcode-products.php b/plugins/woocommerce/includes/shortcodes/class-wc-shortcode-products.php index 9b551b3f2e0..51eed9c2753 100644 --- a/plugins/woocommerce/includes/shortcodes/class-wc-shortcode-products.php +++ b/plugins/woocommerce/includes/shortcodes/class-wc-shortcode-products.php @@ -641,8 +641,13 @@ class WC_Shortcode_Products { do_action( "woocommerce_shortcode_before_{$this->type}_loop", $this->attributes ); - // Fire standard shop loop hooks when paginating results so we can show result counts and so on. if ( wc_string_to_bool( $this->attributes['paginate'] ) ) { + /** + * Fire the standard shop hooks when paginating so we can display result counts etc. + * If the pagination is not enabled, this hook will not be fired. + * + * @since 3.3.1 + */ do_action( 'woocommerce_before_shop_loop' ); } @@ -667,8 +672,13 @@ class WC_Shortcode_Products { $GLOBALS['post'] = $original_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited woocommerce_product_loop_end(); - // Fire standard shop loop hooks when paginating results so we can show result counts and so on. if ( wc_string_to_bool( $this->attributes['paginate'] ) ) { + /** + * Fire the standard shop hooks when paginating so we can display the pagination. + * If the pagination is not enabled, this hook will not be fired. + * + * @since 3.3.1 + */ do_action( 'woocommerce_after_shop_loop' ); } diff --git a/plugins/woocommerce/includes/wc-rest-functions.php b/plugins/woocommerce/includes/wc-rest-functions.php index 3eeec672a27..e9b3055cc9f 100644 --- a/plugins/woocommerce/includes/wc-rest-functions.php +++ b/plugins/woocommerce/includes/wc-rest-functions.php @@ -410,8 +410,6 @@ function wc_rest_should_load_namespace( string $ns, string $rest_route = '' ): b 'wc/private', ); - // We can consider allowing filtering this list in the future. - $known_namespace_request = false; foreach ( $known_namespaces as $known_namespace ) { if ( str_starts_with( $rest_route, $known_namespace ) ) { @@ -424,5 +422,15 @@ function wc_rest_should_load_namespace( string $ns, string $rest_route = '' ): b return true; } - return str_starts_with( $rest_route, $ns ); + /** + * Filters whether a namespace should be loaded. + * + * @param bool $should_load True if the namespace should be loaded, false otherwise. + * @param string $ns The namespace to check. + * @param string $rest_route The REST route being checked. + * @param array $known_namespaces Known namespaces that we know are safe to not load if the request is not for them. + * + * @since 9.4 + */ + return apply_filters( 'wc_rest_should_load_namespace', str_starts_with( $rest_route, $ns ), $ns, $rest_route, $known_namespaces ); } diff --git a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/CustomizeStore.php b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/CustomizeStore.php index fe6a2f28b7a..91fc334318f 100644 --- a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/CustomizeStore.php +++ b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/CustomizeStore.php @@ -7,6 +7,8 @@ use WP_Post; /** * Customize Your Store Task + * + * @internal */ class CustomizeStore extends Task { /** diff --git a/plugins/woocommerce/src/Blocks/AI/Configuration.php b/plugins/woocommerce/src/Blocks/AI/Configuration.php index cd60cdd7f25..426a672db06 100644 --- a/plugins/woocommerce/src/Blocks/AI/Configuration.php +++ b/plugins/woocommerce/src/Blocks/AI/Configuration.php @@ -8,6 +8,8 @@ use Automattic\Jetpack\Connection\Utils; /** * Class Configuration + * + * @internal */ class Configuration { diff --git a/plugins/woocommerce/src/Blocks/AI/Connection.php b/plugins/woocommerce/src/Blocks/AI/Connection.php index cedd08b8cd2..0a20ddf10f8 100644 --- a/plugins/woocommerce/src/Blocks/AI/Connection.php +++ b/plugins/woocommerce/src/Blocks/AI/Connection.php @@ -10,6 +10,8 @@ use WpOrg\Requests\Requests; /** * Class Connection + * + * @internal */ class Connection { const TEXT_COMPLETION_API_URL = 'https://public-api.wordpress.com/wpcom/v2/text-completion'; diff --git a/plugins/woocommerce/src/Blocks/AIContent/ContentProcessor.php b/plugins/woocommerce/src/Blocks/AIContent/ContentProcessor.php index 55b202c0e3c..1f34077fb26 100644 --- a/plugins/woocommerce/src/Blocks/AIContent/ContentProcessor.php +++ b/plugins/woocommerce/src/Blocks/AIContent/ContentProcessor.php @@ -10,6 +10,8 @@ use WP_Error; * ContentProcessor class. * * Process images for content + * + * @internal */ class ContentProcessor { diff --git a/plugins/woocommerce/src/Blocks/AIContent/PatternsDictionary.php b/plugins/woocommerce/src/Blocks/AIContent/PatternsDictionary.php index cb87c48b8ea..9a881e47787 100644 --- a/plugins/woocommerce/src/Blocks/AIContent/PatternsDictionary.php +++ b/plugins/woocommerce/src/Blocks/AIContent/PatternsDictionary.php @@ -5,6 +5,8 @@ namespace Automattic\WooCommerce\Blocks\AIContent; /** * Patterns Dictionary class. + * + * @internal */ class PatternsDictionary { /** diff --git a/plugins/woocommerce/src/Blocks/AIContent/PatternsHelper.php b/plugins/woocommerce/src/Blocks/AIContent/PatternsHelper.php index d6eceadb97e..560d7ec831b 100644 --- a/plugins/woocommerce/src/Blocks/AIContent/PatternsHelper.php +++ b/plugins/woocommerce/src/Blocks/AIContent/PatternsHelper.php @@ -6,6 +6,8 @@ use WP_Error; /** * Patterns Helper class. + * + * @internal */ class PatternsHelper { /** diff --git a/plugins/woocommerce/src/Blocks/AIContent/UpdatePatterns.php b/plugins/woocommerce/src/Blocks/AIContent/UpdatePatterns.php index d9e792e85eb..d2e0228ca5f 100644 --- a/plugins/woocommerce/src/Blocks/AIContent/UpdatePatterns.php +++ b/plugins/woocommerce/src/Blocks/AIContent/UpdatePatterns.php @@ -7,6 +7,8 @@ use WP_Error; /** * Pattern Images class. + * + * @internal */ class UpdatePatterns { diff --git a/plugins/woocommerce/src/Blocks/AIContent/UpdateProducts.php b/plugins/woocommerce/src/Blocks/AIContent/UpdateProducts.php index 20e0549f8e3..571cb08029e 100644 --- a/plugins/woocommerce/src/Blocks/AIContent/UpdateProducts.php +++ b/plugins/woocommerce/src/Blocks/AIContent/UpdateProducts.php @@ -6,6 +6,8 @@ use Automattic\WooCommerce\Blocks\AI\Connection; use WP_Error; /** * Pattern Images class. + * + * @internal */ class UpdateProducts { diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/CustomerAccount.php b/plugins/woocommerce/src/Blocks/BlockTypes/CustomerAccount.php index 80bd799b224..8d201c1051f 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/CustomerAccount.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/CustomerAccount.php @@ -68,6 +68,7 @@ class CustomerAccount extends AbstractBlock { public function modify_hooked_block_attributes( $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context ) { $parsed_hooked_block['attrs']['displayStyle'] = 'icon_only'; $parsed_hooked_block['attrs']['iconStyle'] = 'line'; + $parsed_hooked_block['attrs']['iconClass'] = 'wc-block-customer-account__account-icon'; /* * The Mini Cart block (which is hooked into the header) has a margin of 0.5em on the left side. diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php index bf42d344a52..f2f9f72328a 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php @@ -47,6 +47,18 @@ class ProductCollection extends AbstractBlock { protected $custom_order_opts = array( 'popularity', 'rating' ); + /** + * The render state of the product collection block. + * + * These props are runtime-based and reinitialize for every block on a page. + * + * @var array + */ + private $render_state = array( + 'has_results' => false, + 'has_no_results_block' => false, + ); + /** * Initialize this block type. * @@ -80,8 +92,30 @@ class ProductCollection extends AbstractBlock { // Provide location context into block's context. add_filter( 'render_block_context', array( $this, 'provide_location_context_for_inner_blocks' ), 11, 1 ); + // Disable block render if the ProductTemplate block is empty. + add_filter( + 'render_block_woocommerce/product-template', + function ( $html ) { + $this->render_state['has_results'] = ! empty( $html ); + return $html; + }, + 100, + 1 + ); + + // Enable block render if the ProductCollectionNoResults block is rendered. + add_filter( + 'render_block_woocommerce/product-collection-no-results', + function ( $html ) { + $this->render_state['has_no_results_block'] = ! empty( $html ); + return $html; + }, + 100, + 1 + ); + // Interactivity API: Add navigation directives to the product collection block. - add_filter( 'render_block_woocommerce/product-collection', array( $this, 'enhance_product_collection_with_interactivity' ), 10, 2 ); + add_filter( 'render_block_woocommerce/product-collection', array( $this, 'handle_rendering' ), 10, 2 ); add_filter( 'render_block_core/query-pagination', array( $this, 'add_navigation_link_directives' ), 10, 3 ); add_filter( 'posts_clauses', array( $this, 'add_price_range_filter_posts_clauses' ), 10, 2 ); @@ -90,6 +124,46 @@ class ProductCollection extends AbstractBlock { add_filter( 'render_block_data', array( $this, 'disable_enhanced_pagination' ), 10, 1 ); } + /** + * Handle the rendering of the block. + * + * @param string $block_content The block content about to be rendered. + * @param array $block The block being rendered. + * + * @return string + */ + public function handle_rendering( $block_content, $block ) { + if ( $this->should_prevent_render() ) { + return ''; // Prevent rendering. + } + + // Reset the render state for the next render. + $this->reset_render_state(); + + return $this->enhance_product_collection_with_interactivity( $block_content, $block ); + } + + /** + * Check if the block should be prevented from rendering. + * + * @return bool + */ + private function should_prevent_render() { + return ! $this->render_state['has_results'] && ! $this->render_state['has_no_results_block']; + } + + /** + * Reset the render state. + */ + private function reset_render_state() { + $this->render_state = array( + 'has_results' => false, + 'has_no_results_block' => false, + ); + } + + + /** * Provides the location context to each inner block of the product collection block. * Hint: Only blocks using the 'query' context will be affected. diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductSKU.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductSKU.php index 1475a005217..50e748f646e 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductSKU.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductSKU.php @@ -69,14 +69,27 @@ class ProductSKU extends AbstractBlock { $styles_and_classes = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); + $prefix = isset( $attributes['prefix'] ) ? wp_kses_post( ( $attributes['prefix'] ) ) : __( 'SKU: ', 'woocommerce' ); + if ( ! empty( $prefix ) ) { + $prefix = sprintf( '%s', $prefix ); + } + + $suffix = isset( $attributes['suffix'] ) ? wp_kses_post( ( $attributes['suffix'] ) ) : ''; + if ( ! empty( $suffix ) ) { + $suffix = sprintf( '%s', $suffix ); + } + return sprintf( '
- SKU: - %3$s + %3$s + %4$s + %5$s
', esc_attr( $styles_and_classes['classes'] ), esc_attr( $styles_and_classes['styles'] ?? '' ), - $product_sku + $prefix, + $product_sku, + $suffix ); } } diff --git a/plugins/woocommerce/src/Blocks/Patterns/AIPatterns.php b/plugins/woocommerce/src/Blocks/Patterns/AIPatterns.php index 8bcf093fd1f..6982fad05a7 100644 --- a/plugins/woocommerce/src/Blocks/Patterns/AIPatterns.php +++ b/plugins/woocommerce/src/Blocks/Patterns/AIPatterns.php @@ -8,6 +8,8 @@ use Automattic\WooCommerce\Blocks\Images\Pexels; /** * AIPatterns class. + * + * @internal */ class AIPatterns { const PATTERNS_AI_DATA_POST_TYPE = 'patterns_ai_data'; diff --git a/plugins/woocommerce/src/Blocks/Patterns/PTKClient.php b/plugins/woocommerce/src/Blocks/Patterns/PTKClient.php index e319c22ffed..2b86b3c857c 100644 --- a/plugins/woocommerce/src/Blocks/Patterns/PTKClient.php +++ b/plugins/woocommerce/src/Blocks/Patterns/PTKClient.php @@ -5,6 +5,8 @@ use WP_Error; /** * PatternsToolkit class. + * + * @internal */ class PTKClient { /** diff --git a/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php b/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php index 2b24e1a9657..1ce873b39f8 100644 --- a/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php +++ b/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php @@ -7,6 +7,8 @@ use WP_Upgrader; /** * PTKPatterns class. + * + * @internal */ class PTKPatternsStore { const TRANSIENT_NAME = 'ptk_patterns'; diff --git a/plugins/woocommerce/src/Blocks/Patterns/PatternRegistry.php b/plugins/woocommerce/src/Blocks/Patterns/PatternRegistry.php index 9a79a95cc5b..3af03be5200 100644 --- a/plugins/woocommerce/src/Blocks/Patterns/PatternRegistry.php +++ b/plugins/woocommerce/src/Blocks/Patterns/PatternRegistry.php @@ -5,6 +5,8 @@ use Automattic\WooCommerce\Admin\Features\Features; /** * PatternRegistry class. + * + * @internal */ class PatternRegistry { const SLUG_REGEX = '/^[A-z0-9\/_-]+$/'; diff --git a/plugins/woocommerce/src/Internal/Logging/RemoteLogger.php b/plugins/woocommerce/src/Internal/Logging/RemoteLogger.php index 01e71d40e62..6daa928b46e 100644 --- a/plugins/woocommerce/src/Internal/Logging/RemoteLogger.php +++ b/plugins/woocommerce/src/Internal/Logging/RemoteLogger.php @@ -20,11 +20,10 @@ use WC_Log_Levels; * @package WooCommerce\Classes */ class RemoteLogger extends \WC_Log_Handler { - const LOG_ENDPOINT = 'https://public-api.wordpress.com/rest/v1.1/logstash'; - const RATE_LIMIT_ID = 'woocommerce_remote_logging'; - const RATE_LIMIT_DELAY = 60; // 1 minute. - const WC_LATEST_VERSION_TRANSIENT = 'latest_woocommerce_version'; - const FETCH_LATEST_VERSION_RETRY = 'fetch_latest_woocommerce_version_retry'; + const LOG_ENDPOINT = 'https://public-api.wordpress.com/rest/v1.1/logstash'; + const RATE_LIMIT_ID = 'woocommerce_remote_logging'; + const RATE_LIMIT_DELAY = 60; // 1 minute. + const WC_NEW_VERSION_TRANSIENT = 'woocommerce_new_version'; /** * Handle a log entry. @@ -71,7 +70,7 @@ class RemoteLogger extends \WC_Log_Handler { 'wc_version' => WC()->version, 'php_version' => phpversion(), 'wp_version' => get_bloginfo( 'version' ), - 'request_uri' => filter_input( INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_URL ), + 'request_uri' => $this->sanitize_request_uri( filter_input( INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_URL ) ), ), ); @@ -150,7 +149,7 @@ class RemoteLogger extends \WC_Log_Handler { return false; } - if ( ! $this->is_latest_woocommerce_version() ) { + if ( ! $this->should_current_version_be_logged() ) { return false; } @@ -221,7 +220,7 @@ class RemoteLogger extends \WC_Log_Handler { self::LOG_ENDPOINT, array( 'body' => wp_json_encode( $body ), - 'timeout' => 2, + 'timeout' => 3, 'headers' => array( 'Content-Type' => 'application/json', ), @@ -256,14 +255,22 @@ class RemoteLogger extends \WC_Log_Handler { * * @return bool */ - private function is_latest_woocommerce_version() { - $latest_wc_version = $this->fetch_latest_woocommerce_version(); + private function should_current_version_be_logged() { + $new_version = get_site_transient( self::WC_NEW_VERSION_TRANSIENT ); - if ( is_null( $latest_wc_version ) ) { - return false; + if ( false === $new_version ) { + $new_version = $this->fetch_new_woocommerce_version(); + // Cache the new version for a week since we want to keep logging in with the same version for a while even if the new version is available. + set_site_transient( self::WC_NEW_VERSION_TRANSIENT, $new_version, WEEK_IN_SECONDS ); } - return version_compare( WC()->version, $latest_wc_version, '>=' ); + if ( ! is_string( $new_version ) || '' === $new_version ) { + // If the new version is not available, we consider the current version to be the latest. + return true; + } + + // If the current version is the latest, we don't want to log errors. + return version_compare( WC()->version, $new_version, '>=' ); } /** @@ -316,45 +323,34 @@ class RemoteLogger extends \WC_Log_Handler { } /** - * Fetch the latest WooCommerce version using the WordPress API and cache it. + * Fetch the new version of WooCommerce from the WordPress API. * - * @return string|null + * @return string|null New version if an update is available, null otherwise. */ - private function fetch_latest_woocommerce_version() { - $cached_version = get_transient( self::WC_LATEST_VERSION_TRANSIENT ); - if ( $cached_version ) { - return $cached_version; + private function fetch_new_woocommerce_version() { + if ( ! function_exists( 'get_plugins' ) ) { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + } + if ( ! function_exists( 'get_plugin_updates' ) ) { + require_once ABSPATH . 'wp-admin/includes/update.php'; } - $retry_count = get_transient( self::FETCH_LATEST_VERSION_RETRY ); - if ( false === $retry_count || ! is_numeric( $retry_count ) ) { - $retry_count = 0; - } + $plugin_updates = get_plugin_updates(); - if ( $retry_count >= 3 ) { + // Check if WooCommerce plugin update information is available. + if ( ! is_array( $plugin_updates ) || ! isset( $plugin_updates[ WC_PLUGIN_BASENAME ] ) ) { return null; } - if ( ! function_exists( 'plugins_api' ) ) { - require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; - } - // Fetch the latest version from the WordPress API. - $plugin_info = plugins_api( 'plugin_information', array( 'slug' => 'woocommerce' ) ); + $wc_plugin_update = $plugin_updates[ WC_PLUGIN_BASENAME ]; - if ( is_wp_error( $plugin_info ) ) { - ++$retry_count; - set_transient( self::FETCH_LATEST_VERSION_RETRY, $retry_count, HOUR_IN_SECONDS ); + // Ensure the update object exists and has the required information. + if ( ! $wc_plugin_update || ! isset( $wc_plugin_update->update->new_version ) ) { return null; } - if ( ! empty( $plugin_info->version ) ) { - $latest_version = $plugin_info->version; - set_transient( self::WC_LATEST_VERSION_TRANSIENT, $latest_version, WEEK_IN_SECONDS ); - delete_transient( self::FETCH_LATEST_VERSION_RETRY ); - return $latest_version; - } - - return null; + $new_version = $wc_plugin_update->update->new_version; + return is_string( $new_version ) ? $new_version : null; } /** @@ -435,4 +431,52 @@ class RemoteLogger extends \WC_Log_Handler { protected function is_dev_or_local_environment() { return in_array( wp_get_environment_type(), array( 'development', 'local' ), true ); } + /** + * Sanitize the request URI to only allow certain query parameters. + * + * @param string $request_uri The request URI to sanitize. + * @return string The sanitized request URI. + */ + private function sanitize_request_uri( $request_uri ) { + $default_whitelist = array( 'path', 'page', 'step', 'task', 'tab' ); + + /** + * Filter to allow other plugins to whitelist request_uri query parameter values for unmasked remote logging. + * + * @since 9.4.0 + * + * @param string $default_whitelist The default whitelist of query parameters. + */ + $whitelist = apply_filters( 'woocommerce_remote_logger_request_uri_whitelist', $default_whitelist ); + + $parsed_url = wp_parse_url( $request_uri ); + if ( ! isset( $parsed_url['query'] ) ) { + return $request_uri; + } + + parse_str( $parsed_url['query'], $query_params ); + + foreach ( $query_params as $key => &$value ) { + if ( ! in_array( $key, $whitelist, true ) ) { + $value = 'xxxxxx'; + } + } + + $parsed_url['query'] = http_build_query( $query_params ); + return $this->build_url( $parsed_url ); + } + + /** + * Build a URL from its parsed components. + * + * @param array $parsed_url The parsed URL components. + * @return string The built URL. + */ + private function build_url( $parsed_url ) { + $path = $parsed_url['path'] ?? ''; + $query = isset( $parsed_url['query'] ) ? "?{$parsed_url['query']}" : ''; + $fragment = isset( $parsed_url['fragment'] ) ? "#{$parsed_url['fragment']}" : ''; + + return "$path$query$fragment"; + } } diff --git a/plugins/woocommerce/src/StoreApi/Utilities/DraftOrderTrait.php b/plugins/woocommerce/src/StoreApi/Utilities/DraftOrderTrait.php index 4e80f2695f8..dedc60ae418 100644 --- a/plugins/woocommerce/src/StoreApi/Utilities/DraftOrderTrait.php +++ b/plugins/woocommerce/src/StoreApi/Utilities/DraftOrderTrait.php @@ -60,9 +60,8 @@ trait DraftOrderTrait { return true; } - // Failed orders and those needing payment can be retried if the cart hasn't changed. - // Pending orders are excluded from this check since they may be awaiting an update from the payment processor. - if ( $order_object->needs_payment() && ! $order_object->has_status( 'pending' ) && $order_object->has_cart_hash( wc()->cart->get_cart_hash() ) ) { + // Pending and failed orders can be retried if the cart hasn't changed. + if ( $order_object->needs_payment() && $order_object->has_cart_hash( wc()->cart->get_cart_hash() ) ) { return true; } diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-edit.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-edit.spec.js index 89d18f069f5..e94595088b6 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-edit.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-edit.spec.js @@ -282,3 +282,119 @@ test( } ); } ); + +test( + 'can decrease the sale price if the product was not previously in sale when bulk editing products', + { tag: [ '@gutenberg', '@services' ] }, + async ( { page, products } ) => { + await page.goto( `wp-admin/edit.php?post_type=product` ); + + const salePriceDecrease = 10; + + await test.step( 'Update products with the "Sale > Decrease existing sale price" option', async () => { + await page.goto( `wp-admin/edit.php?post_type=product` ); + + for ( const product of products ) { + await page.getByLabel( `Select ${ product.name }` ).click(); + } + + await page + .locator( '#bulk-action-selector-top' ) + .selectOption( 'Edit' ); + await page.locator( '#doaction' ).click(); + + await page + .locator( 'select[name="change_sale_price"]' ) + .selectOption( + 'Decrease existing sale price by (fixed amount or %):' + ); + await page + .getByPlaceholder( 'Enter sale price ($)' ) + .fill( `${ salePriceDecrease }%` ); + + await page.getByRole( 'button', { name: 'Update' } ).click(); + } ); + + await test.step( 'Verify products have a sale price', async () => { + for ( const product of products ) { + await page.goto( `product/${ product.slug }` ); + + const expectedSalePrice = ( + product.regular_price * + ( 1 - salePriceDecrease / 100 ) + ).toFixed( 2 ); + + await expect + .soft( + await page + .locator( 'ins' ) + .getByText( `$${ expectedSalePrice }` ) + .count() + ) + .toBeGreaterThan( 0 ); + } + } ); + } +); + +test( + 'increasing the sale price from 0 does not change the sale price when bulk editing products', + { tag: [ '@gutenberg', '@services' ] }, + async ( { page, api } ) => { + let product; + await api + .post( 'products', { + id: 0, + name: `Product _${ Date.now() }`, + type: 'simple', + regular_price: '100', + sale_price: '0', + manage_stock: true, + stock_quantity: 10, + stock_status: 'instock', + } ) + .then( ( response ) => { + product = response.data; + } ); + + const salePriceIncrease = 10; + + await test.step( 'Update products with the "Sale > Increase existing sale price" option', async () => { + await page.goto( `wp-admin/edit.php?post_type=product` ); + + await page.getByLabel( `Select ${ product.name }` ).click(); + + await page + .locator( '#bulk-action-selector-top' ) + .selectOption( 'Edit' ); + await page.locator( '#doaction' ).click(); + + await page + .locator( 'select[name="change_sale_price"]' ) + .selectOption( + 'Increase existing sale price by (fixed amount or %):' + ); + + await page + .getByPlaceholder( 'Enter sale price ($)' ) + .fill( `${ salePriceIncrease }%` ); + + await page.getByRole( 'button', { name: 'Update' } ).click(); + } ); + + await test.step( 'Verify products have a sale price', async () => { + await page.goto( `product/${ product.slug }` ); + + const expectedSalePrice = '$0.00'; + + await expect + .soft( + await page + .locator( 'ins' ) + .getByText( expectedSalePrice ) + .count() + ) + .toBeGreaterThan( 0 ); + } ); + } +); diff --git a/plugins/woocommerce/tests/php/includes/wc-rest-functions-test.php b/plugins/woocommerce/tests/php/includes/wc-rest-functions-test.php index d5938d7fbcb..41d2dd8dfc4 100644 --- a/plugins/woocommerce/tests/php/includes/wc-rest-functions-test.php +++ b/plugins/woocommerce/tests/php/includes/wc-rest-functions-test.php @@ -27,4 +27,22 @@ class WCRestFunctionsTest extends WC_Unit_Test_Case { $this->assertFalse( wc_rest_should_load_namespace( 'wc-analytics', 'wc/v2' ) ); $this->assertTrue( wc_rest_should_load_namespace( 'wc/v2', 'wc/v2' ) ); } + + /** + * @testDox Test wc_rest_should_load_namespace known works with preload. + */ + public function test_wc_rest_should_load_namespace_known_works_with_preload() { + $memo = rest_preload_api_request( array(), '/wc/store/v1/cart' ); + $this->assertArrayHasKey( '/wc/store/v1/cart', $memo ); + } + + /** + * @testDox Test wc_rest_should_load_namespace filter. + */ + public function test_wc_rest_should_load_namespace_filter() { + $this->assertFalse( wc_rest_should_load_namespace( 'wc/v1', 'wc/v2' ) ); + add_filter( 'wc_rest_should_load_namespace', '__return_true' ); + $this->assertTrue( wc_rest_should_load_namespace( 'wc/v1', 'wc/v2' ) ); + remove_filter( 'wc_rest_should_load_namespace', '__return_true' ); + } } diff --git a/plugins/woocommerce/tests/php/src/Internal/Logging/RemoteLoggerTest.php b/plugins/woocommerce/tests/php/src/Internal/Logging/RemoteLoggerTest.php index 7133f38f5ef..1133880bad2 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Logging/RemoteLoggerTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Logging/RemoteLoggerTest.php @@ -1,482 +1,646 @@ sut = wc_get_container()->get( RemoteLogger::class ); - } + class RemoteLoggerTest extends \WC_Unit_Test_Case { + /** + * System under test. + * + * @var RemoteLogger + */ + private $sut; - /** - * Tear down. - * - * @return void - */ - public function tearDown(): void { - $this->cleanup_filters(); - delete_option( 'woocommerce_feature_remote_logging_enabled' ); - delete_transient( RemoteLogger::WC_LATEST_VERSION_TRANSIENT ); - delete_transient( RemoteLogger::FETCH_LATEST_VERSION_RETRY ); - global $wpdb; - $wpdb->query( "DELETE FROM {$wpdb->prefix}wc_rate_limits" ); - WC_Cache_Helper::invalidate_cache_group( WC_Rate_Limiter::CACHE_GROUP ); - } - - /** - * Cleanup filters used in tests. - * - * @return void - */ - private function cleanup_filters() { - $filters = array( - 'option_woocommerce_admin_remote_feature_enabled', - 'option_woocommerce_allow_tracking', - 'option_woocommerce_version', - 'option_woocommerce_remote_variant_assignment', - 'plugins_api', - 'pre_http_request', - 'woocommerce_remote_logger_formatted_log_data', - ); - foreach ( $filters as $filter ) { - remove_all_filters( $filter ); + /** + * Set up test + * + * @return void + */ + public function setUp(): void { + parent::setUp(); + $this->sut = wc_get_container()->get( RemoteLogger::class ); } - } - /** - * @testdox Remote logging is allowed when all conditions are met - */ - public function test_remote_logging_allowed() { - $this->setup_remote_logging_conditions( true ); - $this->assertTrue( $this->sut->is_remote_logging_allowed() ); - } + /** + * Tear down. + * + * @return void + */ + public function tearDown(): void { + $this->cleanup_filters(); + delete_option( 'woocommerce_feature_remote_logging_enabled' ); + delete_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT ); + global $wpdb; + $wpdb->query( "DELETE FROM {$wpdb->prefix}wc_rate_limits" ); + WC_Cache_Helper::invalidate_cache_group( WC_Rate_Limiter::CACHE_GROUP ); + } - /** - * @testdox Remote logging is not allowed under various conditions - * @dataProvider remote_logging_disallowed_provider - * - * @param string $condition The condition being tested. - * @param callable $setup_callback Callback to set up the test condition. - */ - public function test_remote_logging_not_allowed( $condition, $setup_callback ) { - $this->setup_remote_logging_conditions( true ); - $setup_callback( $this ); - $this->assertFalse( $this->sut->is_remote_logging_allowed() ); - } + /** + * Cleanup filters used in tests. + * + * @return void + */ + private function cleanup_filters() { + $filters = array( + 'option_woocommerce_admin_remote_feature_enabled', + 'option_woocommerce_allow_tracking', + 'option_woocommerce_version', + 'option_woocommerce_remote_variant_assignment', + 'plugins_api', + 'pre_http_request', + 'woocommerce_remote_logger_formatted_log_data', + 'pre_site_transient_update_plugins', + 'woocommerce_remote_logger_request_uri_whitelist', + ); + foreach ( $filters as $filter ) { + remove_all_filters( $filter ); + } + } - /** - * Data provider for test_remote_logging_not_allowed. - * - * @return array[] Test cases with conditions and setup callbacks. - */ - public function remote_logging_disallowed_provider() { - return array( - 'feature flag disabled' => array( - 'condition' => 'feature flag disabled', - 'setup' => fn() => update_option( 'woocommerce_feature_remote_logging_enabled', 'no' ), - ), - 'tracking opted out' => array( - 'condition' => 'tracking opted out', - 'setup' => fn() => add_filter( 'option_woocommerce_allow_tracking', fn() => 'no' ), - ), - 'outdated version' => array( - 'condition' => 'outdated version', - 'setup' => function () { - $version = WC()->version; - $next_version = implode( - '.', - array_map( - function ( $n, $i ) { - return 0 === $i ? $n + 1 : 0; - }, - explode( '.', $version ), - array_keys( explode( '.', $version ) ) - ) - ); - set_transient( RemoteLogger::WC_LATEST_VERSION_TRANSIENT, $next_version ); - }, + /** + * @testdox Remote logging is allowed when all conditions are met + */ + public function test_remote_logging_allowed() { + $this->setup_remote_logging_conditions( true ); + $this->assertTrue( $this->sut->is_remote_logging_allowed() ); + } + + /** + * @testdox Remote logging is not allowed under various conditions + * @dataProvider remote_logging_disallowed_provider + * + * @param string $condition The condition being tested. + * @param callable $setup_callback Callback to set up the test condition. + */ + public function test_remote_logging_not_allowed( $condition, $setup_callback ) { + $this->setup_remote_logging_conditions( true ); + $setup_callback( $this ); + $this->assertFalse( $this->sut->is_remote_logging_allowed() ); + } + + /** + * Data provider for test_remote_logging_not_allowed. + * + * @return array[] Test cases with conditions and setup callbacks. + */ + public function remote_logging_disallowed_provider() { + return array( + 'feature flag disabled' => array( + 'condition' => 'feature flag disabled', + 'setup' => fn() => update_option( 'woocommerce_feature_remote_logging_enabled', 'no' ), + ), + 'tracking opted out' => array( + 'condition' => 'tracking opted out', + 'setup' => fn() => add_filter( 'option_woocommerce_allow_tracking', fn() => 'no' ), + ), 'high variant assignment' => array( 'condition' => 'high variant assignment', 'setup' => fn() => add_filter( 'option_woocommerce_remote_variant_assignment', fn() => 15 ), ), - ), - ); - } + 'outdated version' => array( + 'condition' => 'outdated version', + 'setup' => function () { + $version = WC()->version; + // Next major version. (e.g. 9.0.1 -> 10.0.0). + $next_version = implode( + '.', + array_map( + function ( $n, $i ) { + return 0 === $i ? $n + 1 : 0; + }, + explode( '.', $version ), + array_keys( explode( '.', $version ) ) + ) + ); - /** - * @testdox Fetch latest WooCommerce version retries on API failure - */ - public function test_fetch_latest_woocommerce_version_retry() { - $this->setup_remote_logging_conditions( true ); - add_filter( 'plugins_api', fn() => new \WP_Error(), 10, 3 ); - - for ( $i = 1; $i <= 4; $i++ ) { - $this->sut->is_remote_logging_allowed(); - $retry_count = get_transient( RemoteLogger::FETCH_LATEST_VERSION_RETRY ); - $this->assertEquals( min( $i, 3 ), $retry_count ); - } - } - - /** - * @testdox get_formatted_log method returns expected array structure - * @dataProvider get_formatted_log_provider - * - * @param string $level The log level. - * @param string $message The log message. - * @param array $context The log context. - * @param array $expected The expected formatted log array. - */ - public function test_get_formatted_log( $level, $message, $context, $expected ) { - $formatted_log = $this->sut->get_formatted_log( $level, $message, $context ); - foreach ( $expected as $key => $value ) { - $this->assertArrayHasKey( $key, $formatted_log ); - $this->assertEquals( $value, $formatted_log[ $key ] ); - } - } - - /** - * Data provider for test_get_formatted_log. - * - * @return array[] Test cases with log data and expected formatted output. - */ - public function get_formatted_log_provider() { - return array( - 'basic log data' => array( - 'error', - 'Fatal error occurred at line 123 in ' . ABSPATH . 'wp-content/file.php', - array( 'tags' => array( 'tag1', 'tag2' ) ), - array( - 'feature' => 'woocommerce_core', - 'severity' => 'error', - 'message' => 'Fatal error occurred at line 123 in **/wp-content/file.php', - 'tags' => array( 'woocommerce', 'php', 'tag1', 'tag2' ), + set_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT, $next_version, WEEK_IN_SECONDS ); + }, ), - ), - 'log with backtrace' => array( - 'error', - 'Test error message', - array( 'backtrace' => ABSPATH . 'wp-content/plugins/woocommerce/file.php' ), - array( 'trace' => '**/woocommerce/file.php' ), - ), - 'log with extra attributes' => array( - 'error', - 'Test error message', - array( - 'extra' => array( - 'key1' => 'value1', - 'key2' => 'value2', + ); + } + + /** + * @testdox should_current_version_be_logged method behaves correctly + * @dataProvider should_current_version_be_logged_provider + * + * @param string $current_version The current WooCommerce version. + * @param string $new_version The new WooCommerce version. + * @param string $transient_value The value of the transient. + * @param bool $expected The expected result. + */ + public function test_should_current_version_be_logged( $current_version, $new_version, $transient_value, $expected ) { + $wc_version = WC()->version; + WC()->version = $current_version; + + // Set up the transient. + if ( null !== $transient_value ) { + set_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT, $transient_value, WEEK_IN_SECONDS ); + } else { + delete_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT ); + + $this->setup_mock_plugin_updates( $new_version ); + } + + $result = $this->invoke_private_method( $this->sut, 'should_current_version_be_logged', array() ); + $this->assertEquals( $expected, $result ); + + // Clean up. + delete_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT ); + + WC()->version = $wc_version; + } + + /** + * Data provider for test_should_current_version_be_logged. + */ + public function should_current_version_be_logged_provider() { + return array( + 'current version is latest (transient set)' => array( '9.2.0', '9.2.0', '9.2.0', true ), + 'current version is newer (transient set)' => array( '9.3.0', '9.2.0', '9.2.0', true ), + 'current version is older (transient set)' => array( '9.1.0', '9.2.0', '9.2.0', false ), + 'new version is null (transient set)' => array( '9.2.0', null, null, true ), + 'transient not set, current version is latest' => array( '9.2.0', '9.2.0', null, true ), + 'transient not set, current version is newer' => array( '9.3.0', '9.2.0', null, true ), + 'transient not set, current version is older' => array( '9.1.0', '9.2.0', null, false ), + 'transient not set, new version is null' => array( '9.2.0', null, null, true ), + ); + } + + /** + * @testdox fetch_new_woocommerce_version method returns correct version + */ + public function test_fetch_new_woocommerce_version() { + $this->setup_mock_plugin_updates( '9.3.0' ); + + $result = $this->invoke_private_method( $this->sut, 'fetch_new_woocommerce_version', array() ); + $this->assertEquals( '9.3.0', $result, 'The result should be the latest version when an update is available.' ); + } + + /** + * @testdox fetch_new_woocommerce_version method returns null when no update is available + */ + public function test_fetch_new_woocommerce_version_no_update() { + add_filter( 'pre_site_transient_update_plugins', fn() => array() ); + + $result = $this->invoke_private_method( $this->sut, 'fetch_new_woocommerce_version', array() ); + $this->assertNull( $result, 'The result should be null when no update is available.' ); + } + + + /** + * @testdox get_formatted_log method returns expected array structure + * @dataProvider get_formatted_log_provider + * + * @param string $level The log level. + * @param string $message The log message. + * @param array $context The log context. + * @param array $expected The expected formatted log array. + */ + public function test_get_formatted_log( $level, $message, $context, $expected ) { + $formatted_log = $this->sut->get_formatted_log( $level, $message, $context ); + foreach ( $expected as $key => $value ) { + $this->assertArrayHasKey( $key, $formatted_log ); + $this->assertEquals( $value, $formatted_log[ $key ] ); + } + } + + /** + * Data provider for test_get_formatted_log. + * + * @return array[] Test cases with log data and expected formatted output. + */ + public function get_formatted_log_provider() { + return array( + 'basic log data' => array( + 'error', + 'Fatal error occurred at line 123 in ' . ABSPATH . 'wp-content/file.php', + array( 'tags' => array( 'tag1', 'tag2' ) ), + array( + 'feature' => 'woocommerce_core', + 'severity' => 'error', + 'message' => 'Fatal error occurred at line 123 in **/wp-content/file.php', + 'tags' => array( 'woocommerce', 'php', 'tag1', 'tag2' ), ), ), - array( - 'extra' => array( - 'key1' => 'value1', - 'key2' => 'value2', + 'log with backtrace' => array( + 'error', + 'Test error message', + array( 'backtrace' => ABSPATH . 'wp-content/plugins/woocommerce/file.php' ), + array( 'trace' => '**/woocommerce/file.php' ), + ), + 'log with extra attributes' => array( + 'error', + 'Test error message', + array( + 'extra' => array( + 'key1' => 'value1', + 'key2' => 'value2', + ), + ), + array( + 'extra' => array( + 'key1' => 'value1', + 'key2' => 'value2', + ), ), ), - ), - ); - } + ); + } - /** - * @testdox should_handle method behaves correctly under different conditions - * @dataProvider should_handle_provider - * - * @param callable $setup Function to set up the test environment. - * @param string $level Log level to test. - * @param bool $expected Expected result of should_handle method. - */ - public function test_should_handle( $setup, $level, $expected ) { - $this->sut = $this->getMockBuilder( RemoteLogger::class ) + + /** + * @testdox get_formatted_log method correctly sanitizes request URI + */ + public function test_get_formatted_log_sanitizes_request_uri() { + global $mock_filter_input, $mock_return; + $mock_filter_input = true; + $mock_return = '/shop?path=home&user=admin&token=abc123'; + + $formatted_log = $this->sut->get_formatted_log( 'error', 'Test message', array() ); + + $mock_filter_input = false; + + $this->assertArrayHasKey( 'properties', $formatted_log ); + $this->assertArrayHasKey( 'request_uri', $formatted_log['properties'] ); + $this->assertNotNull( $formatted_log['properties']['request_uri'], 'Request URI should not be null' ); + $this->assertStringContainsString( 'path=home', $formatted_log['properties']['request_uri'] ); + $this->assertStringContainsString( 'user=xxxxxx', $formatted_log['properties']['request_uri'] ); + $this->assertStringContainsString( 'token=xxxxxx', $formatted_log['properties']['request_uri'] ); + } + + /** + * @testdox sanitize_request_uri method respects whitelist filter + */ + public function test_sanitize_request_uri_respects_whitelist_filter() { + add_filter( + 'woocommerce_remote_logger_request_uri_whitelist', + function ( $whitelist ) { + $whitelist[] = 'custom_param'; + return $whitelist; + } + ); + + $request_uri = '/shop?path=home&custom_param=value&token=abc123'; + $sanitized_uri = $this->invoke_private_method( $this->sut, 'sanitize_request_uri', array( $request_uri ) ); + $this->assertStringContainsString( 'path=home', $sanitized_uri ); + $this->assertStringContainsString( 'custom_param=value', $sanitized_uri ); + $this->assertStringContainsString( 'token=xxxxxx', $sanitized_uri ); + } + + /** + * @testdox sanitize_request_uri method correctly sanitizes request URIs + */ + public function test_sanitize_request_uri() { + $reflection = new \ReflectionClass( $this->sut ); + $method = $reflection->getMethod( 'sanitize_request_uri' ); + $method->setAccessible( true ); + + // Test with whitelisted parameters. + $request_uri = '/shop?path=home&page=2&step=1&task=checkout'; + $sanitized_uri = $method->invokeArgs( $this->sut, array( $request_uri ) ); + $this->assertStringContainsString( 'path=home', $sanitized_uri ); + $this->assertStringContainsString( 'page=2', $sanitized_uri ); + $this->assertStringContainsString( 'step=1', $sanitized_uri ); + $this->assertStringContainsString( 'task=checkout', $sanitized_uri ); + + // Test with non-whitelisted parameters. + $request_uri = '/shop?path=home&user=admin&token=abc123'; + $sanitized_uri = $method->invokeArgs( $this->sut, array( $request_uri ) ); + $this->assertStringContainsString( 'path=home', $sanitized_uri ); + $this->assertStringContainsString( 'user=xxxxxx', $sanitized_uri ); + $this->assertStringContainsString( 'token=xxxxxx', $sanitized_uri ); + + // Test with mixed parameters. + $request_uri = '/shop?path=home&page=2&user=admin&step=1&token=abc123'; + $sanitized_uri = $method->invokeArgs( $this->sut, array( $request_uri ) ); + $this->assertStringContainsString( 'path=home', $sanitized_uri ); + $this->assertStringContainsString( 'page=2', $sanitized_uri ); + $this->assertStringContainsString( 'step=1', $sanitized_uri ); + $this->assertStringContainsString( 'user=xxxxxx', $sanitized_uri ); + $this->assertStringContainsString( 'token=xxxxxx', $sanitized_uri ); + } + + /** + * @testdox should_handle method behaves correctly under different conditions + * @dataProvider should_handle_provider + * + * @param callable $setup Function to set up the test environment. + * @param string $level Log level to test. + * @param bool $expected Expected result of should_handle method. + */ + public function test_should_handle( $setup, $level, $expected ) { + $this->sut = $this->getMockBuilder( RemoteLogger::class ) ->onlyMethods( array( 'is_remote_logging_allowed', 'is_third_party_error' ) ) ->getMock(); - $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); - $this->sut->method( 'is_third_party_error' )->willReturn( false ); + $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); + $this->sut->method( 'is_third_party_error' )->willReturn( false ); - $setup( $this ); + $setup( $this ); - $result = $this->invoke_private_method( $this->sut, 'should_handle', array( $level, 'Test message', array() ) ); - $this->assertEquals( $expected, $result ); - } + $result = $this->invoke_private_method( $this->sut, 'should_handle', array( $level, 'Test message', array() ) ); + $this->assertEquals( $expected, $result ); + } - /** - * Data provider for test_should_handle method. - * - * @return array Test cases for should_handle method. - */ - public function should_handle_provider() { - return array( - 'throttled' => array( - fn() => WC_Rate_Limiter::set_rate_limit( RemoteLogger::RATE_LIMIT_ID, 10 ), - 'critical', - false, - ), - 'less severe than critical' => array( - fn() => null, - 'error', - false, - ), - 'critical level' => array( - fn() => null, - 'critical', - true, - ), - ); - } + /** + * Data provider for test_should_handle method. + * + * @return array Test cases for should_handle method. + */ + public function should_handle_provider() { + return array( + 'throttled' => array( + fn() => WC_Rate_Limiter::set_rate_limit( RemoteLogger::RATE_LIMIT_ID, 10 ), + 'critical', + false, + ), + 'less severe than critical' => array( + fn() => null, + 'error', + false, + ), + 'critical level' => array( + fn() => null, + 'critical', + true, + ), + ); + } - /** - * @testdox handle method applies filter and doesn't send logs when filtered to null - */ - public function test_handle_filtered_log_null() { - $this->sut = $this->getMockBuilder( RemoteLogger::class ) + /** + * @testdox handle method applies filter and doesn't send logs when filtered to null + */ + public function test_handle_filtered_log_null() { + $this->sut = $this->getMockBuilder( RemoteLogger::class ) ->onlyMethods( array( 'is_remote_logging_allowed' ) ) ->getMock(); - $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); + $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); - add_filter( 'woocommerce_remote_logger_formatted_log_data', fn() => null, 10, 4 ); - add_filter( 'pre_http_request', fn() => $this->fail( 'wp_safe_remote_post should not be called' ), 10, 3 ); + add_filter( 'woocommerce_remote_logger_formatted_log_data', fn() => null, 10, 4 ); + add_filter( 'pre_http_request', fn() => $this->fail( 'wp_safe_remote_post should not be called' ), 10, 3 ); - $this->assertFalse( $this->sut->handle( time(), 'error', 'Test message', array() ) ); - } + $this->assertFalse( $this->sut->handle( time(), 'error', 'Test message', array() ) ); + } - /** - * @testdox handle method does not send logs in dev environment - */ - public function test_handle_does_not_send_logs_in_dev_environment() { - $this->sut = $this->getMockBuilder( RemoteLoggerWithEnvironmentOverride::class ) + /** + * @testdox handle method does not send logs in dev environment + */ + public function test_handle_does_not_send_logs_in_dev_environment() { + $this->sut = $this->getMockBuilder( RemoteLoggerWithEnvironmentOverride::class ) ->onlyMethods( array( 'is_remote_logging_allowed' ) ) ->getMock(); - $this->sut->set_is_dev_or_local( true ); - $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); + $this->sut->set_is_dev_or_local( true ); + $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); - $this->assertFalse( $this->sut->handle( time(), 'error', 'Test message', array() ) ); - } + $this->assertFalse( $this->sut->handle( time(), 'error', 'Test message', array() ) ); + } - /** - * @testdox handle method successfully sends log - */ - public function test_handle_successful() { - $this->sut = $this->getMockBuilder( RemoteLoggerWithEnvironmentOverride::class ) + /** + * @testdox handle method successfully sends log + */ + public function test_handle_successful() { + $this->sut = $this->getMockBuilder( RemoteLoggerWithEnvironmentOverride::class ) ->onlyMethods( array( 'is_remote_logging_allowed' ) ) ->getMock(); - $this->sut->set_is_dev_or_local( false ); - $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); + $this->sut->set_is_dev_or_local( false ); + $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); - add_filter( - 'pre_http_request', - function ( $preempt, $args ) { - $this->assertArrayHasKey( 'body', $args ); - $this->assertArrayHasKey( 'headers', $args ); - return array( - 'response' => array( - 'code' => 200, - 'message' => 'OK', + add_filter( + 'pre_http_request', + function ( $preempt, $args ) { + $this->assertArrayHasKey( 'body', $args ); + $this->assertArrayHasKey( 'headers', $args ); + return array( + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + 'body' => wp_json_encode( array( 'success' => true ) ), + ); + }, + 10, + 3 + ); + + $this->assertTrue( $this->sut->handle( time(), 'critical', 'Test message', array() ) ); + $this->assertTrue( WC_Rate_Limiter::retried_too_soon( RemoteLogger::RATE_LIMIT_ID ) ); + } + + /** + * @testdox handle method handles remote logging failure + */ + public function test_handle_remote_logging_failure() { + $this->sut = $this->getMockBuilder( RemoteLoggerWithEnvironmentOverride::class ) + ->onlyMethods( array( 'is_remote_logging_allowed' ) ) + ->getMock(); + + $this->sut->set_is_dev_or_local( false ); + $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); + + add_filter( + 'pre_http_request', + function ( $preempt, $args, $url ) { + if ( 'https://public-api.wordpress.com/rest/v1.1/logstash' === $url ) { + throw new \Exception( 'Remote logging failed: A valid URL was not provided.' ); + } + return $preempt; + }, + 10, + 3 + ); + + $this->assertFalse( $this->sut->handle( time(), 'critical', 'Test message', array() ) ); + $this->assertTrue( WC_Rate_Limiter::retried_too_soon( RemoteLogger::RATE_LIMIT_ID ) ); + } + + /** + * @testdox is_third_party_error method correctly identifies third-party errors + * @dataProvider is_third_party_error_provider + * @param string $message The error message to check. + * @param array $context The context of the error. + * @param bool $expected_result The expected result of the check. + */ + public function test_is_third_party_error( $message, $context, $expected_result ) { + $result = $this->invoke_private_method( $this->sut, 'is_third_party_error', array( $message, $context ) ); + $this->assertEquals( $expected_result, $result ); + } + + /** + * Data provider for test_is_third_party_error. + * + * @return array[] Test cases. + */ + public function is_third_party_error_provider() { + return array( + array( 'Fatal error in ' . WC_ABSPATH . 'file.php', array(), false ), + array( 'Fatal error in /wp-content/file.php', array(), false ), + array( 'Fatal error in /wp-content/file.php', array( 'source' => 'fatal-errors' ), false ), + array( + 'Fatal error in /wp-content/plugins/3rd-plugin/file.php', + array( + 'source' => 'fatal-errors', + 'backtrace' => array( '/wp-content/plugins/3rd-plugin/file.php', WC_ABSPATH . 'file.php' ), ), - 'body' => wp_json_encode( array( 'success' => true ) ), - ); - }, - 10, - 3 - ); - - $this->assertTrue( $this->sut->handle( time(), 'critical', 'Test message', array() ) ); - $this->assertTrue( WC_Rate_Limiter::retried_too_soon( RemoteLogger::RATE_LIMIT_ID ) ); - } - - /** - * @testdox handle method handles remote logging failure - */ - public function test_handle_remote_logging_failure() { - $this->sut = $this->getMockBuilder( RemoteLoggerWithEnvironmentOverride::class ) - ->onlyMethods( array( 'is_remote_logging_allowed' ) ) - ->getMock(); - - $this->sut->set_is_dev_or_local( false ); - $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); - - add_filter( - 'pre_http_request', - function ( $preempt, $args, $url ) { - if ( 'https://public-api.wordpress.com/rest/v1.1/logstash' === $url ) { - throw new \Exception( 'Remote logging failed: A valid URL was not provided.' ); - } - return $preempt; - }, - 10, - 3 - ); - - $this->assertFalse( $this->sut->handle( time(), 'critical', 'Test message', array() ) ); - $this->assertTrue( WC_Rate_Limiter::retried_too_soon( RemoteLogger::RATE_LIMIT_ID ) ); - } - - /** - * @testdox is_third_party_error method correctly identifies third-party errors - * @dataProvider is_third_party_error_provider - * @param string $message The error message to check. - * @param array $context The context of the error. - * @param bool $expected_result The expected result of the check. - */ - public function test_is_third_party_error( $message, $context, $expected_result ) { - $result = $this->invoke_private_method( $this->sut, 'is_third_party_error', array( $message, $context ) ); - $this->assertEquals( $expected_result, $result ); - } - - /** - * Data provider for test_is_third_party_error. - * - * @return array[] Test cases. - */ - public function is_third_party_error_provider() { - return array( - array( 'Fatal error in ' . WC_ABSPATH . 'file.php', array(), false ), - array( 'Fatal error in /wp-content/file.php', array(), false ), - array( 'Fatal error in /wp-content/file.php', array( 'source' => 'fatal-errors' ), false ), - array( - 'Fatal error in /wp-content/plugins/3rd-plugin/file.php', - array( - 'source' => 'fatal-errors', - 'backtrace' => array( '/wp-content/plugins/3rd-plugin/file.php', WC_ABSPATH . 'file.php' ), + false, ), - false, - ), - array( - 'Fatal error in /wp-content/plugins/woocommerce-3rd-plugin/file.php', array( - 'source' => 'fatal-errors', - 'backtrace' => array( WP_PLUGIN_DIR . 'woocommerce-3rd-plugin/file.php' ), + 'Fatal error in /wp-content/plugins/woocommerce-3rd-plugin/file.php', + array( + 'source' => 'fatal-errors', + 'backtrace' => array( WP_PLUGIN_DIR . 'woocommerce-3rd-plugin/file.php' ), + ), + true, ), - true, - ), - array( - 'Fatal error in /wp-content/plugins/3rd-plugin/file.php', array( - 'source' => 'fatal-errors', - 'backtrace' => array( WP_PLUGIN_DIR . '3rd-plugin/file.php' ), + 'Fatal error in /wp-content/plugins/3rd-plugin/file.php', + array( + 'source' => 'fatal-errors', + 'backtrace' => array( WP_PLUGIN_DIR . '3rd-plugin/file.php' ), + ), + true, ), - true, - ), - array( - 'Fatal error in /wp-content/plugins/3rd-plugin/file.php', array( - 'source' => 'fatal-errors', - 'backtrace' => array( array( 'file' => WP_PLUGIN_DIR . '3rd-plugin/file.php' ) ), + 'Fatal error in /wp-content/plugins/3rd-plugin/file.php', + array( + 'source' => 'fatal-errors', + 'backtrace' => array( array( 'file' => WP_PLUGIN_DIR . '3rd-plugin/file.php' ) ), + ), + true, ), - true, - ), - ); - } + ); + } - /** - * @testdox sanitize method correctly sanitizes paths - */ - public function test_sanitize() { - $message = WC_ABSPATH . 'includes/class-wc-test.php on line 123'; - $expected = '**/woocommerce/includes/class-wc-test.php on line 123'; - $result = $this->invoke_private_method( $this->sut, 'sanitize', array( $message ) ); - $this->assertEquals( $expected, $result ); - } + /** + * @testdox sanitize method correctly sanitizes paths + */ + public function test_sanitize() { + $message = WC_ABSPATH . 'includes/class-wc-test.php on line 123'; + $expected = '**/woocommerce/includes/class-wc-test.php on line 123'; + $result = $this->invoke_private_method( $this->sut, 'sanitize', array( $message ) ); + $this->assertEquals( $expected, $result ); + } - /** - * @testdox sanitize_trace method correctly sanitizes stack traces - */ - public function test_sanitize_trace() { - $trace = array( - WC_ABSPATH . 'includes/class-wc-test.php:123', - ABSPATH . 'wp-includes/plugin.php:456', - ); - $expected = "**/woocommerce/includes/class-wc-test.php:123\n**/wp-includes/plugin.php:456"; - $result = $this->invoke_private_method( $this->sut, 'sanitize_trace', array( $trace ) ); - $this->assertEquals( $expected, $result ); - } + /** + * @testdox sanitize_trace method correctly sanitizes stack traces + */ + public function test_sanitize_trace() { + $trace = array( + WC_ABSPATH . 'includes/class-wc-test.php:123', + ABSPATH . 'wp-includes/plugin.php:456', + ); + $expected = "**/woocommerce/includes/class-wc-test.php:123\n**/wp-includes/plugin.php:456"; + $result = $this->invoke_private_method( $this->sut, 'sanitize_trace', array( $trace ) ); + $this->assertEquals( $expected, $result ); + } - /** - * Setup common conditions for remote logging tests. - * - * @param bool $enabled Whether remote logging is enabled. - */ - private function setup_remote_logging_conditions( $enabled = true ) { - update_option( 'woocommerce_feature_remote_logging_enabled', $enabled ? 'yes' : 'no' ); - add_filter( 'option_woocommerce_allow_tracking', fn() => 'yes' ); - add_filter( 'option_woocommerce_remote_variant_assignment', fn() => 5 ); - add_filter( - 'plugins_api', - function ( $result, $action, $args ) use ( $enabled ) { - if ( 'plugin_information' === $action && 'woocommerce' === $args->slug ) { - return (object) array( 'version' => $enabled ? WC()->version : '9.0.0' ); - } - return $result; - }, - 10, - 3 - ); - } + /** + * Setup common conditions for remote logging tests. + * + * @param bool $enabled Whether remote logging is enabled. + */ + private function setup_remote_logging_conditions( $enabled = true ) { + update_option( 'woocommerce_feature_remote_logging_enabled', $enabled ? 'yes' : 'no' ); + add_filter( 'option_woocommerce_allow_tracking', fn() => 'yes' ); + add_filter( 'option_woocommerce_remote_variant_assignment', fn() => 5 ); + $this->setup_mock_plugin_updates( $enabled ? WC()->version : '9.0.0' ); + } - /** - * Helper method to invoke private methods. - * - * @param object $obj Object instance. - * @param string $method_name Name of the private method. - * @param array $parameters Parameters to pass to the method. - * @return mixed - */ - private function invoke_private_method( $obj, $method_name, $parameters = array() ) { - $reflection = new \ReflectionClass( get_class( $obj ) ); - $method = $reflection->getMethod( $method_name ); - $method->setAccessible( true ); - return $method->invokeArgs( $obj, $parameters ); + /** + * Set up mock plugin updates. + * + * @param string $new_version The new version of WooCommerce to simulate. + */ + private function setup_mock_plugin_updates( $new_version ) { + $update_plugins = (object) array( + 'response' => array( + WC_PLUGIN_BASENAME => (object) array( + 'new_version' => $new_version, + 'package' => 'https://downloads.wordpress.org/plugin/woocommerce.zip', + 'slug' => 'woocommerce', + ), + ), + ); + add_filter( 'pre_site_transient_update_plugins', fn() => $update_plugins ); + } + + /** + * Helper method to invoke private methods. + * + * @param object $obj Object instance. + * @param string $method_name Name of the private method. + * @param array $parameters Parameters to pass to the method. + * @return mixed + */ + private function invoke_private_method( $obj, $method_name, $parameters = array() ) { + $reflection = new \ReflectionClass( get_class( $obj ) ); + $method = $reflection->getMethod( $method_name ); + $method->setAccessible( true ); + return $method->invokeArgs( $obj, $parameters ); + } } -} //phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound, Squiz.Classes.ClassFileName.NoMatch, Suin.Classes.PSR4.IncorrectClassName -/** - * Mock class that extends RemoteLogger to allow overriding is_dev_or_local_environment. - */ -class RemoteLoggerWithEnvironmentOverride extends RemoteLogger { /** - * The is_dev_or_local value. - * - * @var bool + * Mock class that extends RemoteLogger to allow overriding is_dev_or_local_environment. */ - private $is_dev_or_local = false; + class RemoteLoggerWithEnvironmentOverride extends RemoteLogger { + /** + * The is_dev_or_local value. + * + * @var bool + */ + private $is_dev_or_local = false; - /** - * Set the is_dev_or_local value. - * - * @param bool $value The value to set. - */ - public function set_is_dev_or_local( $value ) { - $this->is_dev_or_local = $value; + /** + * Set the is_dev_or_local value. + * + * @param bool $value The value to set. + */ + public function set_is_dev_or_local( $value ) { + $this->is_dev_or_local = $value; + } + + /** + * @inheritDoc + */ + protected function is_dev_or_local_environment() { + return $this->is_dev_or_local; + } } +//phpcs:enable Generic.Files.OneObjectStructurePerFile.MultipleFound, Squiz.Classes.ClassFileName.NoMatch, Suin.Classes.PSR4.IncorrectClassName +} +/** + * Mocks for global functions used in RemoteLogger.php + */ +namespace Automattic\WooCommerce\Internal\Logging { /** - * @inheritDoc + * The filter_input function will return NULL if we change the $_SERVER variables at runtime, so we + * need to override it in RemoteLogger's namespace when we want it to return a specific value for testing. + * + * @return mixed */ - protected function is_dev_or_local_environment() { - return $this->is_dev_or_local; + function filter_input() { + global $mock_filter_input, $mock_return; + + if ( true === $mock_filter_input ) { + return $mock_return; + } else { + return call_user_func_array( '\filter_input', func_get_args() ); + } } } -//phpcs:enable Generic.Files.OneObjectStructurePerFile.MultipleFound, Squiz.Classes.ClassFileName.NoMatch, Suin.Classes.PSR4.IncorrectClassName diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db28d78d96a..ec49d327648 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -256,7 +256,7 @@ importers: version: 10.5.0(sass@1.69.5)(webpack@5.89.0(webpack-cli@3.3.12)) ts-jest: specifier: ~29.1.1 - version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) + version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) typescript: specifier: ^5.3.3 version: 5.3.3 @@ -292,7 +292,7 @@ importers: version: 2.3.2 debug: specifier: ^4.3.4 - version: 4.3.4(supports-color@9.4.0) + version: 4.3.4(supports-color@8.1.1) dompurify: specifier: ^2.4.7 version: 2.4.7 @@ -453,7 +453,7 @@ importers: version: 27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)) ts-jest: specifier: ~29.1.1 - version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) + version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) typescript: specifier: ^5.3.3 version: 5.3.3 @@ -2616,6 +2616,9 @@ importers: '@wordpress/data': specifier: wp-6.0 version: 6.6.1(react@17.0.2) + '@wordpress/dataviews': + specifier: ^4.2.0 + version: 4.2.0(@emotion/is-prop-valid@1.2.1)(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/date': specifier: wp-6.0 version: 4.6.1 @@ -2641,8 +2644,8 @@ importers: specifier: wp-6.0 version: 4.6.1 '@wordpress/icons': - specifier: wp-6.0 - version: 8.2.3 + specifier: 10.6.0 + version: 10.6.0(react@17.0.2) '@wordpress/interface': specifier: wp-6.0 version: 4.5.6(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2) @@ -2878,7 +2881,7 @@ importers: version: 5.0.5 ts-jest: specifier: ~29.1.1 - version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) + version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) typescript: specifier: ^5.3.3 version: 5.3.3 @@ -2890,7 +2893,7 @@ importers: dependencies: debug: specifier: ^4.3.4 - version: 4.3.4(supports-color@9.4.0) + version: 4.3.4(supports-color@8.1.1) devDependencies: '@babel/core': specifier: ^7.23.5 @@ -2999,7 +3002,7 @@ importers: version: 1.2.5(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2) debug: specifier: ^4.3.4 - version: 4.3.4(supports-color@9.4.0) + version: 4.3.4(supports-color@8.1.1) prop-types: specifier: ^15.8.1 version: 15.8.1 @@ -3376,7 +3379,7 @@ importers: version: 3.34.0 debug: specifier: ^4.3.4 - version: 4.3.4(supports-color@9.4.0) + version: 4.3.4(supports-color@8.1.1) dompurify: specifier: ^2.4.7 version: 2.4.7 @@ -4666,7 +4669,7 @@ importers: version: 27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)) ts-jest: specifier: ~29.1.1 - version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) + version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) ts-node: specifier: ^10.9.2 version: 10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3) @@ -4851,7 +4854,7 @@ importers: version: 1.2.2 ts-jest: specifier: ~29.1.1 - version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) + version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) ts-loader: specifier: ^9.5.1 version: 9.5.1(typescript@5.3.3)(webpack@5.89.0(webpack-cli@3.3.12)) @@ -5105,23 +5108,23 @@ packages: '@ariakit/core@0.3.11': resolution: {integrity: sha512-+MnOeqnA4FLI/7vqsZLbZQHHN4ofd9kvkNjz44fNi0gqmD+ZbMWiDkFAvZII75dYnxYw5ZPpWjA4waK22VBWig==} - '@ariakit/core@0.3.8': - resolution: {integrity: sha512-LlSCwbyyozMX4ZEobpYGcv1LFqOdBTdTYPZw3lAVgLcFSNivsazi3NkKM9qNWNIu00MS+xTa0+RuIcuWAjlB2Q==} - '@ariakit/core@0.4.5': resolution: {integrity: sha512-e294+bEcyzt/H/kO4fS5/czLAlkF7PY+Kul3q2z54VY+GGay8NlVs9UezAB7L4jUBlYRAXwp7/1Sq3R7b+MZ7w==} + '@ariakit/core@0.4.9': + resolution: {integrity: sha512-nV0B/OTK/0iB+P9RC7fudznYZ8eR6rR1F912Zc54e3+wSW5RrRvNOiRxyMrgENidd4R7cCMDw77XJLSBLKgEPQ==} + '@ariakit/react-core@0.3.14': resolution: {integrity: sha512-16Qj6kDPglpdWtU5roY9q+G66naOjauTY5HvUIaL2aLY0187ATaRrABIKoMMzTtJyhvsud4jFlzivz+/zCQ8yw==} peerDependencies: react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - '@ariakit/react-core@0.3.9': - resolution: {integrity: sha512-K1Rcxr6FpF0n3L7Uvo+e5hm+zqoZmXLRcYF/skI+/j+ole+uNbnsnfGhG1avqJlklqH4bmkFkjZzmMdOnUC0Ig==} + '@ariakit/react-core@0.4.10': + resolution: {integrity: sha512-r6DZmtHBmSoOj848+RpBwdZy/55YxPhMhfH14JIO2OLn1F6iSFkQwR7AAGpIrlYycWJFSF7KrQu50O+SSfFJdQ==} peerDependencies: - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 '@ariakit/react-core@0.4.5': resolution: {integrity: sha512-ciTYPwpj/+mdA+EstveEnoygbx5e4PXQJxfkLKy4lkTkDJJUS9GcbYhdnIFJVUta6P1YFvzkIKo+/y9mcbMKJg==} @@ -5135,11 +5138,11 @@ packages: react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - '@ariakit/react@0.3.9': - resolution: {integrity: sha512-gC+gibh2go8wvBqzYXavlHKwAfmee5GUMrPSQ9WBBLIfm9nQElujxcHJydaRx+ULR5FbOnbZVC3vU2ic8hSrNw==} + '@ariakit/react@0.4.10': + resolution: {integrity: sha512-c1+6sNLj57aAXrBZMCVGG+OXeFrPAG0TV1jT7oPJcN/KLRs3aCuO3CCJVep/eKepFzzK01kNRGYX3wPT1TXPNw==} peerDependencies: - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 '@ariakit/react@0.4.5': resolution: {integrity: sha512-GUHxaOY1JZrJUHkuV20IY4NWcgknhqTQM0qCQcVZDCi+pJiWchUjTG+UyIr/Of02hU569qnQ7yovskCf+V3tNg==} @@ -6918,18 +6921,12 @@ packages: '@floating-ui/core@0.6.2': resolution: {integrity: sha512-jktYRmZwmau63adUG3GKOAVCofBXkk55S/zQ94XOorAHhwqFIOFAy1rSp2N0Wp6/tGbe9V3u/ExlGZypyY17rg==} - '@floating-ui/core@0.7.3': - resolution: {integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==} - '@floating-ui/core@1.5.2': resolution: {integrity: sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==} '@floating-ui/dom@0.4.5': resolution: {integrity: sha512-b+prvQgJt8pieaKYMSJBXHxX/DYwdLsAWxKYqnO5dO2V4oo/TYBZJAUQCVNjTWWsrs6o4VDrNcP9+E70HAhJdw==} - '@floating-ui/dom@0.5.4': - resolution: {integrity: sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==} - '@floating-ui/dom@1.5.3': resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==} @@ -6939,12 +6936,6 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/react-dom@0.7.2': - resolution: {integrity: sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - '@floating-ui/react-dom@1.3.0': resolution: {integrity: sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==} peerDependencies: @@ -7800,12 +7791,6 @@ packages: '@radix-ui/primitive@1.0.1': resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} - '@radix-ui/react-arrow@1.0.2': - resolution: {integrity: sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-arrow@1.0.3': resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} peerDependencies: @@ -7819,12 +7804,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.0.2': - resolution: {integrity: sha512-s8WdQQ6wNXpaxdZ308KSr8fEWGrg4un8i4r/w7fhiS4ElRNjk5rRcl0/C6TANG2LvLOGIxtzo/jAg6Qf73TEBw==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-collection@1.0.3': resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: @@ -7872,11 +7851,6 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-direction@1.0.0': - resolution: {integrity: sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-direction@1.0.1': resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} peerDependencies: @@ -7892,12 +7866,6 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-dismissable-layer@1.0.3': - resolution: {integrity: sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-dismissable-layer@1.0.4': resolution: {integrity: sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==} peerDependencies: @@ -7911,12 +7879,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.0.4': - resolution: {integrity: sha512-y6AT9+MydyXcByivdK1+QpjWoKaC7MLjkS/cH1Q3keEyMvDkiY85m8o2Bi6+Z1PPUlCsMULopxagQOSfN0wahg==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-focus-guards@1.0.0': resolution: {integrity: sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==} peerDependencies: @@ -7937,12 +7899,6 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-focus-scope@1.0.2': - resolution: {integrity: sha512-spwXlNTfeIprt+kaEWE/qYuYT3ZAqJiAGjN/JgdvgVDTu8yc+HuX+WOWXrKliKnLnwck0F6JDkqIERncnih+4A==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-focus-scope@1.0.3': resolution: {integrity: sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==} peerDependencies: @@ -7970,18 +7926,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-menu@2.0.4': - resolution: {integrity: sha512-mzKR47tZ1t193trEqlQoJvzY4u9vYfVH16ryBrVrCAGZzkgyWnMQYEZdUkM7y8ak9mrkKtJiqB47TlEnubeOFQ==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - - '@radix-ui/react-popper@1.1.1': - resolution: {integrity: sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-popper@1.1.2': resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==} peerDependencies: @@ -8001,12 +7945,6 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-portal@1.0.2': - resolution: {integrity: sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-portal@1.0.3': resolution: {integrity: sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==} peerDependencies: @@ -8032,12 +7970,6 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-primitive@1.0.2': - resolution: {integrity: sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-primitive@1.0.3': resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} peerDependencies: @@ -8051,12 +7983,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.0.3': - resolution: {integrity: sha512-stjCkIoMe6h+1fWtXlA6cRfikdBzCLp3SnVk7c48cv/uy3DTGoXhN76YaOYUJuy3aEDvDIKwKR5KSmvrtPvQPQ==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-roving-focus@1.0.4': resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} peerDependencies: @@ -8101,11 +8027,6 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-slot@1.0.1': - resolution: {integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-slot@1.0.2': resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: @@ -8187,11 +8108,6 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-use-escape-keydown@1.0.2': - resolution: {integrity: sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-use-escape-keydown@1.0.3': resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} peerDependencies: @@ -8224,11 +8140,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-rect@1.0.0': - resolution: {integrity: sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-use-rect@1.0.1': resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} peerDependencies: @@ -8238,11 +8149,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-size@1.0.0': - resolution: {integrity: sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-use-size@1.0.1': resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} peerDependencies: @@ -8265,9 +8171,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/rect@1.0.0': - resolution: {integrity: sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg==} - '@radix-ui/rect@1.0.1': resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} @@ -10432,11 +10335,19 @@ packages: '@use-gesture/core@10.3.0': resolution: {integrity: sha512-rh+6MND31zfHcy9VU3dOZCqGY511lvGcfyJenN4cWZe0u1BH6brBpBddLVXhF2r4BMqWbvxfsbL7D287thJU2A==} + '@use-gesture/core@10.3.1': + resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} + '@use-gesture/react@10.3.0': resolution: {integrity: sha512-3zc+Ve99z4usVP6l9knYVbVnZgfqhKah7sIG+PS2w+vpig2v2OLct05vs+ZXMzwxdNCMka8B+8WlOo0z6Pn6DA==} peerDependencies: react: '>= 16.8.0' + '@use-gesture/react@10.3.1': + resolution: {integrity: sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==} + peerDependencies: + react: '>= 16.8.0' + '@webassemblyjs/ast@1.11.1': resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==} @@ -10693,6 +10604,10 @@ packages: resolution: {integrity: sha512-mOQtwpY5hUt4vMLyshZPPV1x9MBRF2FimUjIImfYJb1x8o6jY4npikzWplAfWYQUJJjWfw/1NmfqD7vUOh9+ww==} engines: {node: '>=12'} + '@wordpress/a11y@4.6.0': + resolution: {integrity: sha512-dSYGLgntqQCAiHBnNxttLOUZnH26m/BrIQdCXtb9JVJy5p68JAdFHbr6qFoOfOoTCvwUqE8cNS7K4GWfAJwT0w==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/api-fetch@3.23.1': resolution: {integrity: sha512-dmeigLuvqYAzpQ2hWUQT1P5VQAjkj9hS1z7PgNi1CcULFPbY8BWW+KiBETUu6Wm+rlSbUL2dC8qrA4JDv9ja5A==} @@ -10927,13 +10842,6 @@ packages: react: ^17.0.0 react-dom: ^17.0.0 - '@wordpress/components@25.13.0': - resolution: {integrity: sha512-Ym/5Xv7NnkJu40jCSmt/t6B8vT2ue2vobwDEz1FKlB0xGm5bzzh5589m2nZqqY459/Qm9dl5R4BKSdvKqKB2MQ==} - engines: {node: '>=12'} - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - '@wordpress/components@25.16.0': resolution: {integrity: sha512-voQuMsO5JbH+JW33TnWurwwvpSb8IQ4XU5wyVMubX4TUwadt+/2ToNJbZIDXoaJPei7vbM81Ft+pH+zGlN8CyA==} engines: {node: '>=12'} @@ -10955,6 +10863,13 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 + '@wordpress/components@28.6.0': + resolution: {integrity: sha512-9YmA+7Tmz19oOfKifOF/VxcwJwyyLK8Y2LupK7ge6Oue0P1bMLs/9LBgZUBizoKMWmXYdzBm8pXf9Eyqq3PG0Q==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + '@wordpress/compose@3.25.3': resolution: {integrity: sha512-tCO2EnJCkCH548OqA0uU8V1k/1skz2QwBlHs8ZQSpimqUS4OWWsAlndCEFe4U4vDTqFt2ow7tzAir+05Cw8MAg==} @@ -10992,6 +10907,12 @@ packages: peerDependencies: react: ^18.0.0 + '@wordpress/compose@7.6.0': + resolution: {integrity: sha512-4ukiLfCOUkb0zmdFpPSVOnQkpNHTWqQUOCgpMykjKO0gRfa/rZ6dxcZUQ/KEYT5EKZkGCo9bR4lBhxjNVrgfug==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + peerDependencies: + react: ^18.0.0 + '@wordpress/core-commands@0.7.0': resolution: {integrity: sha512-kMfyANcDUmA2+4EfEZuDVNFOWKEOJe7oEaZtC6tFRR1wYAlPYOzaQJxbtQMBzqhvHlQMORaxDQNhaoJ8+ac8MQ==} engines: {node: '>=12'} @@ -11042,6 +10963,12 @@ packages: peerDependencies: react: ^17.0.0 + '@wordpress/data@10.6.0': + resolution: {integrity: sha512-u6g1IeK3Vv0Ulr/0jPWU5wpde+flWH1SDvqgc50GjG2v03NWqzie8zTGGeHo8Fque7s/UNbGYKlzrbM3+dPl5g==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + peerDependencies: + react: ^18.0.0 + '@wordpress/data@4.27.3': resolution: {integrity: sha512-5763NgNV9IIa1CC3Q80dAvrH6108tJtj3IrHfUCZmUk1atSNsOMBCkLdQ7tGTTi2JFejeGEMg1LJI22JD5zM6Q==} @@ -11081,14 +11008,16 @@ packages: peerDependencies: react: ^18.0.0 + '@wordpress/dataviews@4.2.0': + resolution: {integrity: sha512-rCnMbEVXKZYgQmJO7S448KPVh78DTHgfJ+B5H937l/HX8+Gd0OlkpbKi4C4UZUj0k/xwY7ccKERYurq3W8/NFg==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + peerDependencies: + react: ^18.0.0 + '@wordpress/date@4.44.0': resolution: {integrity: sha512-WrSAg+gbRN5YB/YZhQnJMNKj80efc+6taVYq3VjSzp27CPxh75qTE5N56TJWGKZbB8mqCIEWy6eOXhIoBW19mQ==} engines: {node: '>=12'} - '@wordpress/date@4.47.0': - resolution: {integrity: sha512-HIruX+wMaQWKYLCFIu6JeEEoqRYkhpL4cWfZ1lJG78wNsgq3vRiHzXQaXHcbmJQCq0PZOxtmeSzldPiUMFVNpg==} - engines: {node: '>=12'} - '@wordpress/date@4.57.0': resolution: {integrity: sha512-azUXRQDhxoCkME7c+0Cw/aCZmyoQeTXhWJYtZBFyPU5wsIXSv/Ucp3WggJR7OSKFnE5rSp5qpCt/nihfLLfZWQ==} engines: {node: '>=12'} @@ -11097,6 +11026,10 @@ packages: resolution: {integrity: sha512-7/w2pzCDvzbidqAl2Rhd/FeA6QZhZmb03Y7rPIO0eJR33L8QWnLiyw+r4Et2DLji8A7N8/gcc+hsRL6lcEsGMA==} engines: {node: '>=12'} + '@wordpress/date@5.6.0': + resolution: {integrity: sha512-uB/FaNHudbs4DgaPGld+Ckvoo8kYvxcDhVyJ6Io3MgONMcsDr4KR3lOc50MprbNZPbXG2KB0CTgHA+PHNxP9iQ==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/dependency-extraction-webpack-plugin@2.9.0': resolution: {integrity: sha512-Eo8ByPd3iZ6az4UmdLD2xYLp1/7os/H80l28Y5OlS4DozkD3vcWCBReynWoBax74u3oJ9wWN5b/8oSxGwIKXYQ==} peerDependencies: @@ -11133,6 +11066,10 @@ packages: resolution: {integrity: sha512-ilOkjXejcnJMxnq1gTVkBnDPP9W+XjlEe1TIfaMKcCwKsfsNy6bgURxWl1qIM2dPjH+5KK65bPjW0XELTMJy4w==} engines: {node: '>=12'} + '@wordpress/deprecated@4.6.0': + resolution: {integrity: sha512-XQbF7SIb43I4Ey7nEDqowm7YJgzoUpdmZfNBN01/UXKUZ0FNaKzf2LCNjOCwfEfRE7AroyUgMR40qWVBBs+GKQ==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/dom-ready@3.27.0': resolution: {integrity: sha512-X7yVAm/JL5UKNfttAN2Ak3suEyOag/MPfr/aX8L2k/od71a6zJBkpMcdKaVPVfIPj9HcrW6ROrfINySPtoGCLA==} engines: {node: '>=12'} @@ -11149,6 +11086,10 @@ packages: resolution: {integrity: sha512-G6OnfLLC0MIWi9efTW6DMNEtEoy7tCoV0MxD19gUuG3/rDOi8RgHYwuNCvt6ieQMISiLiHnsg4tZc4D45zdfZA==} engines: {node: '>=12'} + '@wordpress/dom-ready@4.6.0': + resolution: {integrity: sha512-3fX1O1abmp3++FpZMPnDQygeygUggqfEvWQQQ80di/ksMEo6DXvIdtXolwDQt9WIC1WetLdI7Mf3KKVJnruyxg==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/dom@2.18.0': resolution: {integrity: sha512-tM2WeQuSObl3nzWjUTF0/dyLnA7sdl/MXaSe32D64OF89bjSyJvjUipI7gjKzI3kJ7ddGhwcTggGvSB06MOoCQ==} @@ -11156,10 +11097,6 @@ packages: resolution: {integrity: sha512-ympP0cK4ErQSFCRyrhjg8wAK7Wb5NqTUyiw1kV+2TQ35PKNG+TCXjYkk19Wc0kxiYZPFtbxk8OPp40e8Up7y7g==} engines: {node: '>=12'} - '@wordpress/dom@3.47.0': - resolution: {integrity: sha512-SY6wfAc4yrXYil8fm/uyeKQnPjGuc0G9Q1/5pUKO6dssst8fClsrxy+hXNl0FYFGWnAZBqg5ccrwYydrFt5k/g==} - engines: {node: '>=12'} - '@wordpress/dom@3.57.0': resolution: {integrity: sha512-3vJ1Z5Lzb7kfMoB8ni275vFGIRrljWFQ2XsVfO6oA/HeoIfHAGVcR58GmbjyxwEgClrizMGIkbs9ubrRpontLQ==} engines: {node: '>=12'} @@ -11168,6 +11105,10 @@ packages: resolution: {integrity: sha512-wdWBzfxU8iUPpxxTACkFpYbEoC0f+Hqs24IYOkhn/8ERp2LFpUdFcwF7/DmY6agSpUs8iWT/2hSGdUz9Lw2f0w==} engines: {node: '>=12'} + '@wordpress/dom@4.6.0': + resolution: {integrity: sha512-ZCjMOya5dTkzgp/vTq7w1qpvVQDPoF7sJpalARUUQjeMUkUw/PTLYvvXJ3gARBCgaEdD85QjLorpxnJVz1XNng==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/e2e-test-utils-playwright@1.0.1': resolution: {integrity: sha512-DNR45Q0px6p3XLnJzRXANIXSQ1OKLdWCwQLQctuSmhVyqSyKS0VZApiYVoaPTKLEdxl+WeJ7jN153q1vUa5Lcg==} engines: {node: '>=18.12.0', npm: '>=8.19.2'} @@ -11273,6 +11214,10 @@ packages: resolution: {integrity: sha512-/d/lWBDYYgzE2yeXYvPnjMSDG1EdQs5TSLdjM/drQVJMxWayFqAPaF/pVczLHCPYfjgyJN4Zc+bneAKj6dEiLw==} engines: {node: '>=12'} + '@wordpress/element@6.6.0': + resolution: {integrity: sha512-IvSocvmd0fNus/XZo7K1EU4UD7aOKUdi3Y7pFUW2ljBbL3vuXk3E+6bwYahCjUIlBhpgGuCjemWTdg2Awzfmiw==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/env@10.5.0': resolution: {integrity: sha512-Hx+fi6qTEAuycznulkuMi4d5RDPZ6lPPAxaylpCwXNX2hgx5jrrpgnY4Zn0chBgZMpShO7BbA+zNDq2E6evvTw==} engines: {node: '>=18.12.0', npm: '>=8.19.2'} @@ -11289,6 +11234,10 @@ packages: resolution: {integrity: sha512-DkTDo1Qhvs9rfobBpg5vXAOKaev3Jox8R5ryvYIhql5chrkj/V5k2ZzwUChFXxYmivVkWacCwDGmDmwe2ex/ag==} engines: {node: '>=12'} + '@wordpress/escape-html@3.6.0': + resolution: {integrity: sha512-NY9As0uJ81TPTogBzD6G/m7L4+sjvkjTEKkNsHLD5aEYxRX+RHlPYPyyd6y4CmlOkttwymbV9eKNP+LrfX5zZQ==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/eslint-plugin@12.9.0': resolution: {integrity: sha512-R6dTvD4uFYeoUJFZNUhm1CSwthC0Pl0RIY057Y9oUvGSqjjm7RqRIwKrMlw3dO0P9KoBGGHUox8NUj6EciRXww==} engines: {node: '>=12', npm: '>=6.9'} @@ -11358,8 +11307,8 @@ packages: resolution: {integrity: sha512-4sIngmH64M1jzcprfkffo1GHsQbd/QNbTweq6cSPIJNorKfE63Inf59NQ6r0pq6+Nz+cuq64eMz5v4eyngjZ/A==} engines: {node: '>=12'} - '@wordpress/hooks@4.4.0': - resolution: {integrity: sha512-KO0gUx0KLhH3XCatg9ZOU1TH0fgyQUccAEIM8liErfgmrabHl8JhDoR2Uk5k0jNKZNPog7XxvKgPFVtCzvzQig==} + '@wordpress/hooks@4.6.0': + resolution: {integrity: sha512-FWJhubBXeyRhx12YUmxT9pNoV9Azvx8nkynhduV+RNgA+F2SXoOf15pr+USPV//m3Bx031GN/wPHjgUCbC6+XA==} engines: {node: '>=18.12.0', npm: '>=8.19.2'} '@wordpress/html-entities@3.24.0': @@ -11378,6 +11327,10 @@ packages: resolution: {integrity: sha512-Nb0nCYIdTEehWJ6HoA76bxpseKDY/12rYZ10eqf5OSr6oMvtyJ5j4fkNMKuHFQ00Mhppl9fkYWp2c8ZzBcp5Vw==} engines: {node: '>=12'} + '@wordpress/html-entities@4.6.0': + resolution: {integrity: sha512-ypTlGwDKw7jpmu9rneErkkq9dFHXzju8SGdEWkVAeqhRS9Ifri9DvmrovASB2c5IPY+Ijwh4YlVkx1yNBRHr5w==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/i18n@3.20.0': resolution: {integrity: sha512-SIoOJFB4UrrYAScS4H91CYCLW9dX3Ghv8pBKc/yHGculb1AdGr6gRMlmJxZV62Cn3CZ4Ga86c+FfR+GiBu0JPg==} hasBin: true @@ -11392,11 +11345,6 @@ packages: engines: {node: '>=12'} hasBin: true - '@wordpress/i18n@4.54.0': - resolution: {integrity: sha512-gSKBopBN9rY9GhNy3CXLK3n4D5viuBTObvcu3blu4SFqkHl+Ws1Gx0tHbpypfV80ESrOyMXHJIAqWgBD8d4Hew==} - engines: {node: '>=12'} - hasBin: true - '@wordpress/i18n@4.57.0': resolution: {integrity: sha512-VYWYHE+7NxnZvE9Swhhe4leQcn0jHNkzRAEV36TkfAL/MvrQYCRh71KLTvKhsilG96HUQdBwjH0VPLmYEmR3sg==} engines: {node: '>=12'} @@ -11412,6 +11360,15 @@ packages: engines: {node: '>=18.12.0', npm: '>=8.19.2'} hasBin: true + '@wordpress/i18n@5.6.0': + resolution: {integrity: sha512-xTpwuRh0owYFlgRHUbUAQIWr8ye3FC0ZsjDIOskJaNkrheAU9ZWKJDcmQmPvi01Udml4g9LUIaffkcRd2kyW2g==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + hasBin: true + + '@wordpress/icons@10.6.0': + resolution: {integrity: sha512-dy58bQFVee2izXA65Ptar1f8mVhL1hilOJI3BWbLWmxHr9H4VjI0ohjW4ZkAhahBG2yIvKZja/HaFMTs5O/7Xg==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/icons@4.1.0': resolution: {integrity: sha512-1FpEjT9kJbr0cWbgdgIwd2DoeerWijcVx3qCZ/WMFKNElBH9lfZLuWPI1hpX102HGWFcEi3VlbVpdBGeCeYQWg==} engines: {node: '>=12'} @@ -11432,10 +11389,6 @@ packages: resolution: {integrity: sha512-QkJRDNgSJzfU3OCVr5X9P3Au3MIag2yT4dzM3Ej6VfrF0SPfFgMwroXKSdNEHmCCG7AwtzGOjaqjpQ3y9vRMkA==} engines: {node: '>=12'} - '@wordpress/icons@9.38.0': - resolution: {integrity: sha512-K+rSZM1eKuWh+rXeMWNLj4dT0a3RJSzoUUh9UDQZCSdnThyAyZECGEKfHSCfd28/yabxLKaziXrb5/MVBrPjZw==} - engines: {node: '>=12'} - '@wordpress/icons@9.48.0': resolution: {integrity: sha512-47efXMuqX8Qbf7sFyYeUJ0TPjs3tNqnjHUn3WGc7Gq1IIYD6EGYFmCzPAfciUIXwRBhez2oC4y6IAXl5GP3KBw==} engines: {node: '>=12'} @@ -11473,6 +11426,10 @@ packages: resolution: {integrity: sha512-qUMqZMLlunwY2J31HG6NZwD2kBIqcwvIDBmdQYvVuQ2aDGeB2Z6sVPXyHCqGfh2ynFfaIL8bDtjW5UtYGPUI4A==} engines: {node: '>=12'} + '@wordpress/is-shallow-equal@5.6.0': + resolution: {integrity: sha512-WjxXleJePz9scpTXMTl//mn3AgEBqdHd56pWtaDgz9Ub7O5H8AMNa2BU4VDK8OOQ3iwpAUgqGhaTRK5GjbaeSA==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/jest-console@3.10.0': resolution: {integrity: sha512-iS1GSO+o7+p2PhvScOquD+IK7WqmVxa2s9uTUQyNEo06f9EUv6KNw0B1iZ00DpbgLqDCiczfdCNapC816UXIIA==} engines: {node: '>=8'} @@ -11572,6 +11529,10 @@ packages: resolution: {integrity: sha512-GLKho4gAFbqgmP3GxEPP5iSS2WwOtqX0xL0zVjElNC/uHKCULyZ2UlyDAc2clN5wiVNf3hC4A1BsxzKeKIMNFQ==} engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/keycodes@4.6.0': + resolution: {integrity: sha512-7jmKM1BLyoQPLXFl+3FPaKBrLEe7kUIkBMGS88083SQtXXFcW8sYQt5jd6E1yY6EAnniGveUNrv0C9Lbaipx3w==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/lazy-import@1.34.0': resolution: {integrity: sha512-ZF4YhWDJtvlev1GqZ7FRr2CPg5Vssw6lb4gn2OH56/KWuHf/LrBPVdshXR6ujDPvgUMnNFRf39ofHIENoj7JPA==} engines: {npm: '>=6.9.0'} @@ -11723,6 +11684,12 @@ packages: resolution: {integrity: sha512-4vMhlu40+qxkt6lyCv2KWCx9bP7hcpPC9GXj9Kq3gwKIzSSHoqbYs3V8HYeGWrG9g7JWMFN9Pkdy8Bm61ZsKuQ==} engines: {node: '>=12'} + '@wordpress/primitives@4.6.0': + resolution: {integrity: sha512-uu4ANmgwslB2YOyIBQDSwKTQXXqGDL9Gz5INe+UeJZBMt2uU/TGEjKcZ63dqbuM8mqlPAcdVGL52RCt7mIKEhQ==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + peerDependencies: + react: ^18.0.0 + '@wordpress/priority-queue@1.11.2': resolution: {integrity: sha512-ulwmUOklY3orn1xXpcPnTyGWV5B/oycxI+cHZ6EevBVgM5sq+BW3xo0PKLR/MMm6UNBtFTu/71QAJrNZcD6V1g==} @@ -11734,6 +11701,10 @@ packages: resolution: {integrity: sha512-g4Oka9aQFVPQUhXkKhHT6BoyTEdCG6S0TUvP4SP16PbkhbvIFwZ25GRQb2ERCVTdseCuDIM5YP0kwZd3NqTlGg==} engines: {node: '>=12'} + '@wordpress/priority-queue@3.6.0': + resolution: {integrity: sha512-r2cyisWaqDLesIqC8BqWoXyNIxt1lwjvevw5Kijl9zxzxfYBsNQlu7RI1JNYgnjbDQQirWukFgprt7tdzhwssQ==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/private-apis@0.20.0': resolution: {integrity: sha512-byyPRUNAD8/ca9N8gP2rUr8DHuMbzSoXO03nP8g3cecTN6iCOWqFDm6adkrbiAX527N9Ip+GOrRJd7Tta4kRIg==} engines: {node: '>=12'} @@ -11777,6 +11748,12 @@ packages: peerDependencies: redux: '>=4' + '@wordpress/redux-routine@5.6.0': + resolution: {integrity: sha512-CQkO+JZefPJLNBh5iBup2DRCXfUoPfEZeo2mhO91tSbBmrP08v1Pdk6YLsa8gNDXp4qJbFhNHMGCqRzEioMOhA==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + peerDependencies: + redux: '>=4' + '@wordpress/reusable-blocks@3.20.0': resolution: {integrity: sha512-2Wp1W704eYfTdCrYx+EKr5VbW/Z0AX24M8+FxWmhFlGjWpdzGl9shuMKv6cLfXeLDitU8fyHILXAVAXsvRvK3A==} engines: {node: '>=12'} @@ -11807,15 +11784,15 @@ packages: peerDependencies: react: ^17.0.0 - '@wordpress/rich-text@6.24.0': - resolution: {integrity: sha512-RkvzK8zvLgpd7i5dlL6zs+Dig1lZNSZf/3sYyjX6RalISXNuxF6Zn8Or7kBcq7EcYmey0LMlVIl5FTZ2l7HSIA==} + '@wordpress/rich-text@6.34.0': + resolution: {integrity: sha512-qeHPgSaI6UolAA9s8ShlbqjWtlh1kTIOMKATDZD6GOACZurXh9ZVJxxsE95FSmLEu4SDmYJ0b2sZlh92yJuaPw==} engines: {node: '>=12'} peerDependencies: react: ^18.0.0 - '@wordpress/rich-text@6.34.0': - resolution: {integrity: sha512-qeHPgSaI6UolAA9s8ShlbqjWtlh1kTIOMKATDZD6GOACZurXh9ZVJxxsE95FSmLEu4SDmYJ0b2sZlh92yJuaPw==} - engines: {node: '>=12'} + '@wordpress/rich-text@7.6.0': + resolution: {integrity: sha512-XxlfrlwfCPX7f3u9DMinouYNM9PDBMeGZb4MlK2Fbrc8ympaTZOdH4U74VR3jgv0Eusx6vxFEA5JVVXpW/xS2w==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} peerDependencies: react: ^18.0.0 @@ -11926,6 +11903,10 @@ packages: resolution: {integrity: sha512-WhMKX/ETGUJr2GkaPgGwFF8gTU/PgikfvE2b2ZDjUglxIPYnujBa9S6w+kQPzwGniGJutHL1DFK+TmAaxoci9A==} engines: {node: '>=12'} + '@wordpress/undo-manager@1.6.0': + resolution: {integrity: sha512-Sl2rG/7t5zTQOgp+jOPn5m27sKd1DJIX/EGhM6LtRcjXZqa0rLDJXal1xWfkZk5oghaqW1TAwXJsg9UdAlh7Nw==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/url@2.22.2': resolution: {integrity: sha512-aqpYKQXzyzkCOm+GzZRYlLb+wh58g0cwR1PaKAl0UXaBS4mdS+X6biMriylb4P8CVC/RR7CSw5XI20JC24KDwQ==} @@ -11978,6 +11959,10 @@ packages: resolution: {integrity: sha512-Xs37x0IkvNewPNKs1A8cnw5xLb+AqwUqqCsH4+5Sjat5GDqP86mHgLfRIlE4d6fBYg+q6tO7DVPG49TT3/wzgA==} engines: {node: '>=12'} + '@wordpress/warning@3.6.0': + resolution: {integrity: sha512-pm57z1LZkzfQsXsji6yxcP0XSymKbvP087vJLlMkmLf+MoNVyTD6UvFpXl8hRSH6C6pySoJSgGFXaH81CRuO2Q==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/widgets@3.24.0': resolution: {integrity: sha512-bgjUoBjHKhyM2u7QrTScll7hCFDrHw0OxZWGbPXOGfE0VUgaej/d8QV5re7I+sOIi0g8+XLYQE0fwEyANt1iUg==} peerDependencies: @@ -16078,6 +16063,20 @@ packages: react-dom: optional: true + framer-motion@11.3.30: + resolution: {integrity: sha512-9VmqGe9OIjfMoCcs+ZsKXlv6JaG5QagKX2F1uSbkG3Z33wgjnz60Kw+CngC1M49rDYau+Y9aL+8jGagAwrbVyw==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 + react-dom: ^18.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + framer-motion@6.5.1: resolution: {integrity: sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==} peerDependencies: @@ -25466,10 +25465,10 @@ snapshots: '@ariakit/core@0.3.11': {} - '@ariakit/core@0.3.8': {} - '@ariakit/core@0.4.5': {} + '@ariakit/core@0.4.9': {} + '@ariakit/react-core@0.3.14(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@ariakit/core': 0.3.11 @@ -25486,9 +25485,9 @@ snapshots: react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.2.0(react@18.3.1) - '@ariakit/react-core@0.3.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@ariakit/react-core@0.4.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@ariakit/core': 0.3.8 + '@ariakit/core': 0.4.9 '@floating-ui/dom': 1.5.3 react: 17.0.2 react-dom: 17.0.2(react@17.0.2) @@ -25514,9 +25513,9 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@ariakit/react@0.3.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@ariakit/react@0.4.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@ariakit/react-core': 0.3.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@ariakit/react-core': 0.4.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2) react: 17.0.2 react-dom: 17.0.2(react@17.0.2) @@ -25703,7 +25702,7 @@ snapshots: '@wordpress/primitives': 3.55.0 '@wordpress/react-i18n': 3.55.0 classnames: 2.3.2 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) react: 17.0.2 react-dom: 17.0.2(react@17.0.2) react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) @@ -25807,7 +25806,7 @@ snapshots: '@babel/traverse': 7.23.5 '@babel/types': 7.23.5 convert-source-map: 1.9.0 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 lodash: 4.17.21 @@ -25830,7 +25829,7 @@ snapshots: '@babel/traverse': 7.23.5 '@babel/types': 7.23.5 convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -25850,7 +25849,7 @@ snapshots: '@babel/traverse': 7.23.5 '@babel/types': 7.23.5 convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -29949,7 +29948,7 @@ snapshots: '@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@17.0.2))(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/babel-plugin': 11.11.0 '@emotion/is-prop-valid': 1.2.1 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) @@ -29964,7 +29963,7 @@ snapshots: '@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@18.3.1))(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/babel-plugin': 11.11.0 '@emotion/is-prop-valid': 1.2.1 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@18.3.1) @@ -30132,8 +30131,6 @@ snapshots: '@floating-ui/core@0.6.2': {} - '@floating-ui/core@0.7.3': {} - '@floating-ui/core@1.5.2': dependencies: '@floating-ui/utils': 0.1.6 @@ -30142,10 +30139,6 @@ snapshots: dependencies: '@floating-ui/core': 0.6.2 - '@floating-ui/dom@0.5.4': - dependencies: - '@floating-ui/core': 0.7.3 - '@floating-ui/dom@1.5.3': dependencies: '@floating-ui/core': 1.5.2 @@ -30169,27 +30162,12 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@floating-ui/react-dom@0.7.2(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@floating-ui/dom': 0.5.4 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - use-isomorphic-layout-effect: 1.1.2(@types/react@17.0.71)(react@17.0.2) - transitivePeerDependencies: - - '@types/react' - '@floating-ui/react-dom@1.3.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@floating-ui/dom': 1.5.3 react: 17.0.2 react-dom: 17.0.2(react@17.0.2) - '@floating-ui/react-dom@2.0.4(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@floating-ui/dom': 1.5.3 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - '@floating-ui/react-dom@2.0.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: '@floating-ui/dom': 1.5.3 @@ -31560,7 +31538,7 @@ snapshots: '@oclif/color': 1.0.13 '@oclif/core': 2.15.0(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3) chalk: 4.1.2 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) fs-extra: 9.1.0 http-call: 5.3.0 load-json-file: 5.3.0 @@ -32030,7 +32008,7 @@ snapshots: '@puppeteer/browsers@1.4.6(typescript@5.3.2)': dependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.3.0 @@ -32044,7 +32022,7 @@ snapshots: '@puppeteer/browsers@1.4.6(typescript@5.3.3)': dependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.3.0 @@ -32058,7 +32036,7 @@ snapshots: '@puppeteer/browsers@1.9.0': dependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.3.1 @@ -32080,13 +32058,6 @@ snapshots: dependencies: '@babel/runtime': 7.25.0 - '@radix-ui/react-arrow@1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - '@radix-ui/react-arrow@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 @@ -32107,16 +32078,6 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.3.0 - '@radix-ui/react-collection@1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-context': 1.0.0(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-slot': 1.0.1(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - '@radix-ui/react-collection@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 @@ -32235,11 +32196,6 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@radix-ui/react-direction@1.0.0(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - react: 17.0.2 - '@radix-ui/react-direction@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 @@ -32276,17 +32232,6 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-dismissable-layer@1.0.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) - '@radix-ui/react-use-escape-keydown': 1.0.2(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - '@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 @@ -32315,21 +32260,6 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.3.0 - '@radix-ui/react-dropdown-menu@2.0.4(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-context': 1.0.0(react@17.0.2) - '@radix-ui/react-id': 1.0.0(react@17.0.2) - '@radix-ui/react-menu': 2.0.4(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-controllable-state': 1.0.0(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - transitivePeerDependencies: - - '@types/react' - '@radix-ui/react-focus-guards@1.0.0(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 @@ -32372,15 +32302,6 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-focus-scope@1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - '@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 @@ -32433,50 +32354,6 @@ snapshots: optionalDependencies: '@types/react': 17.0.71 - '@radix-ui/react-menu@2.0.4(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-collection': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-context': 1.0.0(react@17.0.2) - '@radix-ui/react-direction': 1.0.0(react@17.0.2) - '@radix-ui/react-dismissable-layer': 1.0.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-focus-guards': 1.0.0(react@17.0.2) - '@radix-ui/react-focus-scope': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-id': 1.0.0(react@17.0.2) - '@radix-ui/react-popper': 1.1.1(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-portal': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-presence': 1.0.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-roving-focus': 1.0.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-slot': 1.0.1(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) - aria-hidden: 1.2.3 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - react-remove-scroll: 2.5.5(@types/react@17.0.71)(react@17.0.2) - transitivePeerDependencies: - - '@types/react' - - '@radix-ui/react-popper@1.1.1(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@floating-ui/react-dom': 0.7.2(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-arrow': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-context': 1.0.0(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) - '@radix-ui/react-use-layout-effect': 1.0.0(react@17.0.2) - '@radix-ui/react-use-rect': 1.0.0(react@17.0.2) - '@radix-ui/react-use-size': 1.0.0(react@17.0.2) - '@radix-ui/rect': 1.0.0 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - transitivePeerDependencies: - - '@types/react' - '@radix-ui/react-popper@1.1.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 @@ -32529,13 +32406,6 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-portal@1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - '@radix-ui/react-portal@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 @@ -32586,13 +32456,6 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-primitive@1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/react-slot': 1.0.1(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 @@ -32613,21 +32476,6 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.3.0 - '@radix-ui/react-roving-focus@1.0.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-collection': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-context': 1.0.0(react@17.0.2) - '@radix-ui/react-direction': 1.0.0(react@17.0.2) - '@radix-ui/react-id': 1.0.0(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) - '@radix-ui/react-use-controllable-state': 1.0.0(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - '@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 @@ -32756,12 +32604,6 @@ snapshots: '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) react: 18.3.1 - '@radix-ui/react-slot@1.0.1(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - react: 17.0.2 - '@radix-ui/react-slot@1.0.2(@types/react@17.0.71)(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 @@ -32930,12 +32772,6 @@ snapshots: '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) react: 18.3.1 - '@radix-ui/react-use-escape-keydown@1.0.2(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) - react: 17.0.2 - '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@17.0.71)(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 @@ -32990,12 +32826,6 @@ snapshots: optionalDependencies: '@types/react': 17.0.71 - '@radix-ui/react-use-rect@1.0.0(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/rect': 1.0.0 - react: 17.0.2 - '@radix-ui/react-use-rect@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 @@ -33012,12 +32842,6 @@ snapshots: optionalDependencies: '@types/react': 17.0.71 - '@radix-ui/react-use-size@1.0.0(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/react-use-layout-effect': 1.0.0(react@17.0.2) - react: 17.0.2 - '@radix-ui/react-use-size@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 @@ -33054,10 +32878,6 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.3.0 - '@radix-ui/rect@1.0.0': - dependencies: - '@babel/runtime': 7.25.0 - '@radix-ui/rect@1.0.1': dependencies: '@babel/runtime': 7.25.0 @@ -34593,7 +34413,7 @@ snapshots: style-loader: 1.3.0(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) terser-webpack-plugin: 4.2.3(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) ts-dedent: 2.2.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))))(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.89.0(webpack-cli@3.3.12)))(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) util-deprecate: 1.0.2 webpack: 4.47.0(webpack-cli@3.3.12(webpack@5.89.0)) webpack-dev-middleware: 3.7.3(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) @@ -34654,7 +34474,7 @@ snapshots: style-loader: 1.3.0(webpack@4.47.0) terser-webpack-plugin: 4.2.3(webpack@4.47.0) ts-dedent: 2.2.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.89.0))(webpack@4.47.0) + url-loader: 4.1.1(file-loader@6.2.0(webpack@4.47.0))(webpack@4.47.0) util-deprecate: 1.0.2 webpack: 4.47.0 webpack-dev-middleware: 3.7.3(webpack@4.47.0) @@ -35828,7 +35648,7 @@ snapshots: telejson: 6.0.8 terser-webpack-plugin: 4.2.3(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) ts-dedent: 2.2.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))))(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.89.0(webpack-cli@3.3.12)))(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) util-deprecate: 1.0.2 webpack: 4.47.0(webpack-cli@3.3.12(webpack@5.89.0)) webpack-dev-middleware: 3.7.3(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) @@ -35878,7 +35698,7 @@ snapshots: telejson: 6.0.8 terser-webpack-plugin: 4.2.3(webpack@4.47.0) ts-dedent: 2.2.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.89.0))(webpack@4.47.0) + url-loader: 4.1.1(file-loader@6.2.0(webpack@4.47.0))(webpack@4.47.0) util-deprecate: 1.0.2 webpack: 4.47.0 webpack-dev-middleware: 3.7.3(webpack@4.47.0) @@ -37549,7 +37369,6 @@ snapshots: '@types/react-dom@18.3.0': dependencies: '@types/react': 17.0.71 - optional: true '@types/react-outside-click-handler@1.3.3': dependencies: @@ -38009,7 +37828,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.56.0 '@typescript-eslint/type-utils': 5.56.0(eslint@8.55.0)(typescript@5.3.2) '@typescript-eslint/utils': 5.56.0(eslint@8.55.0)(typescript@5.3.2) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.55.0 grapheme-splitter: 1.0.4 ignore: 5.3.0 @@ -38028,7 +37847,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/type-utils': 5.62.0(eslint@8.55.0)(typescript@5.3.2) '@typescript-eslint/utils': 5.62.0(eslint@8.55.0)(typescript@5.3.2) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.55.0 graphemer: 1.4.0 ignore: 5.3.0 @@ -38047,7 +37866,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/type-utils': 5.62.0(eslint@8.55.0)(typescript@5.3.3) '@typescript-eslint/utils': 5.62.0(eslint@8.55.0)(typescript@5.3.3) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.55.0 graphemer: 1.4.0 ignore: 5.3.0 @@ -38127,7 +37946,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.56.0 '@typescript-eslint/types': 5.56.0 '@typescript-eslint/typescript-estree': 5.56.0(typescript@5.3.2) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.55.0 optionalDependencies: typescript: 5.3.2 @@ -38139,7 +37958,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.2) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.55.0 optionalDependencies: typescript: 5.3.2 @@ -38151,7 +37970,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.3) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.55.0 optionalDependencies: typescript: 5.3.3 @@ -38349,6 +38168,8 @@ snapshots: '@use-gesture/core@10.3.0': {} + '@use-gesture/core@10.3.1': {} + '@use-gesture/react@10.3.0(react@17.0.2)': dependencies: '@use-gesture/core': 10.3.0 @@ -38359,6 +38180,16 @@ snapshots: '@use-gesture/core': 10.3.0 react: 18.3.1 + '@use-gesture/react@10.3.1(react@17.0.2)': + dependencies: + '@use-gesture/core': 10.3.1 + react: 17.0.2 + + '@use-gesture/react@10.3.1(react@18.3.1)': + dependencies: + '@use-gesture/core': 10.3.1 + react: 18.3.1 + '@webassemblyjs/ast@1.11.1': dependencies: '@webassemblyjs/helper-numbers': 1.11.1 @@ -38828,7 +38659,7 @@ snapshots: '@wordpress/a11y@3.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/dom-ready': 3.47.0 '@wordpress/i18n': 4.47.0 @@ -38844,6 +38675,12 @@ snapshots: '@wordpress/dom-ready': 3.27.0 '@wordpress/i18n': 4.6.1 + '@wordpress/a11y@4.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/dom-ready': 4.6.0 + '@wordpress/i18n': 5.6.0 + '@wordpress/api-fetch@3.23.1(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@17.0.2))': dependencies: '@babel/runtime': 7.25.0 @@ -38881,7 +38718,7 @@ snapshots: '@wordpress/api-fetch@6.44.0': dependencies: '@babel/runtime': 7.25.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/i18n': 4.57.0 '@wordpress/url': 3.48.0 '@wordpress/api-fetch@7.0.1': @@ -38896,7 +38733,7 @@ snapshots: '@wordpress/autop@3.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/babel-plugin-import-jsx-pragma@1.1.3(@babel/core@7.12.9)': dependencies: @@ -38993,7 +38830,7 @@ snapshots: '@babel/plugin-transform-runtime': 7.23.4(@babel/core@7.24.7) '@babel/preset-env': 7.23.5(@babel/core@7.24.7) '@babel/preset-typescript': 7.23.3(@babel/core@7.24.7) - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/babel-plugin-import-jsx-pragma': 4.30.0(@babel/core@7.24.7) '@wordpress/browserslist-config': 5.30.0 '@wordpress/warning': 2.47.0 @@ -39015,7 +38852,7 @@ snapshots: '@wordpress/blob@3.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/blob@3.6.1': dependencies: @@ -39075,34 +38912,34 @@ snapshots: '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) '@emotion/styled': 11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@17.0.2))(@types/react@17.0.71)(react@17.0.2) '@react-spring/web': 9.7.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/a11y': 3.47.0 + '@wordpress/a11y': 3.57.0 '@wordpress/api-fetch': 6.44.0 '@wordpress/blob': 3.47.0 '@wordpress/blocks': 12.24.0(react@17.0.2) '@wordpress/commands': 0.18.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/date': 4.47.0 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/compose': 6.34.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/date': 4.57.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 '@wordpress/element': 5.34.0 - '@wordpress/escape-html': 2.47.0 + '@wordpress/escape-html': 2.57.0 '@wordpress/hooks': 3.57.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 - '@wordpress/is-shallow-equal': 4.47.0 + '@wordpress/is-shallow-equal': 4.57.0 '@wordpress/keyboard-shortcuts': 4.24.0(react@17.0.2) '@wordpress/keycodes': 3.57.0 '@wordpress/notices': 4.15.0(react@17.0.2) '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/private-apis': 0.29.0 - '@wordpress/rich-text': 6.24.0(react@17.0.2) + '@wordpress/rich-text': 6.34.0(react@17.0.2) '@wordpress/style-engine': 1.30.0 '@wordpress/token-list': 2.47.0 '@wordpress/url': 3.48.0 - '@wordpress/warning': 2.47.0 + '@wordpress/warning': 2.57.0 '@wordpress/wordcount': 3.47.0 change-case: 4.1.2 classnames: 2.3.2 @@ -39136,34 +38973,34 @@ snapshots: '@emotion/react': 11.11.1(@types/react@17.0.71)(react@18.3.1) '@emotion/styled': 11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@18.3.1))(@types/react@17.0.71)(react@18.3.1) '@react-spring/web': 9.7.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@wordpress/a11y': 3.47.0 + '@wordpress/a11y': 3.57.0 '@wordpress/api-fetch': 6.44.0 '@wordpress/blob': 3.47.0 '@wordpress/blocks': 12.24.0(react@18.3.1) '@wordpress/commands': 0.18.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@wordpress/compose': 6.24.0(react@18.3.1) - '@wordpress/data': 9.17.0(react@18.3.1) - '@wordpress/date': 4.47.0 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/compose': 6.34.0(react@18.3.1) + '@wordpress/data': 9.27.0(react@18.3.1) + '@wordpress/date': 4.57.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 '@wordpress/element': 5.34.0 - '@wordpress/escape-html': 2.47.0 + '@wordpress/escape-html': 2.57.0 '@wordpress/hooks': 3.57.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 - '@wordpress/is-shallow-equal': 4.47.0 + '@wordpress/is-shallow-equal': 4.57.0 '@wordpress/keyboard-shortcuts': 4.24.0(react@18.3.1) '@wordpress/keycodes': 3.57.0 '@wordpress/notices': 4.15.0(react@18.3.1) '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@wordpress/private-apis': 0.29.0 - '@wordpress/rich-text': 6.24.0(react@18.3.1) + '@wordpress/rich-text': 6.34.0(react@18.3.1) '@wordpress/style-engine': 1.30.0 '@wordpress/token-list': 2.47.0 '@wordpress/url': 3.48.0 - '@wordpress/warning': 2.47.0 + '@wordpress/warning': 2.57.0 '@wordpress/wordcount': 3.47.0 change-case: 4.1.2 classnames: 2.3.2 @@ -39203,7 +39040,7 @@ snapshots: '@wordpress/compose': 5.5.0(react@18.3.1) '@wordpress/data': 6.15.0(react@18.3.1) '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 4.20.0 '@wordpress/hooks': 3.6.1 '@wordpress/html-entities': 3.24.0 @@ -39239,7 +39076,7 @@ snapshots: '@wordpress/block-editor@8.6.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@react-spring/web': 9.7.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/a11y': 3.47.0 '@wordpress/api-fetch': 6.21.0 @@ -39379,32 +39216,32 @@ snapshots: '@wordpress/block-library@8.24.1(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@preact/signals-core@1.5.1)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 - '@wordpress/a11y': 3.47.0 + '@wordpress/a11y': 3.57.0 '@wordpress/api-fetch': 6.44.0 '@wordpress/autop': 3.47.0 '@wordpress/blob': 3.47.0 '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/blocks': 12.24.0(react@17.0.2) '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/date': 4.47.0 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/date': 4.57.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 '@wordpress/element': 5.34.0 - '@wordpress/escape-html': 2.47.0 + '@wordpress/escape-html': 2.57.0 '@wordpress/hooks': 3.57.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 '@wordpress/interactivity': 3.0.1(@preact/signals-core@1.5.1) '@wordpress/keycodes': 3.57.0 '@wordpress/notices': 4.15.0(react@17.0.2) - '@wordpress/primitives': 3.45.0 + '@wordpress/primitives': 3.55.0 '@wordpress/private-apis': 0.29.0 '@wordpress/reusable-blocks': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/rich-text': 6.24.0(react@17.0.2) + '@wordpress/rich-text': 6.34.0(react@17.0.2) '@wordpress/server-side-render': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/url': 3.48.0 '@wordpress/viewport': 5.24.0(react@17.0.2) @@ -39435,7 +39272,7 @@ snapshots: '@wordpress/block-serialization-default-parser@4.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/blocks@11.1.5(react@18.3.1)': dependencies: @@ -39446,7 +39283,7 @@ snapshots: '@wordpress/compose': 5.5.0(react@18.3.1) '@wordpress/data': 6.15.0(react@18.3.1) '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 4.20.0 '@wordpress/hooks': 3.6.1 '@wordpress/html-entities': 3.24.0 @@ -39530,7 +39367,7 @@ snapshots: '@wordpress/compose': 6.24.0(react@17.0.2) '@wordpress/data': 9.17.0(react@17.0.2) '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/dom': 3.57.0 '@wordpress/element': 5.24.0 '@wordpress/hooks': 3.57.0 '@wordpress/html-entities': 3.47.0 @@ -39560,7 +39397,7 @@ snapshots: '@wordpress/compose': 6.24.0(react@18.3.1) '@wordpress/data': 9.17.0(react@18.3.1) '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/dom': 3.57.0 '@wordpress/element': 5.24.0 '@wordpress/hooks': 3.57.0 '@wordpress/html-entities': 3.47.0 @@ -39641,9 +39478,9 @@ snapshots: dependencies: '@babel/runtime': 7.25.0 '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.22.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.36.0 '@wordpress/keyboard-shortcuts': 4.24.0(react@17.0.2) '@wordpress/private-apis': 0.20.0 @@ -39672,7 +39509,7 @@ snapshots: '@wordpress/compose': 4.2.0(react@18.3.1) '@wordpress/date': 4.44.0 '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 3.2.0 '@wordpress/hooks': 3.6.1 '@wordpress/i18n': 4.47.0 @@ -39820,7 +39657,7 @@ snapshots: '@wordpress/compose': 5.4.1(react@17.0.2) '@wordpress/date': 4.44.0 '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/escape-html': 2.47.0 '@wordpress/hooks': 3.6.1 @@ -39868,7 +39705,7 @@ snapshots: '@wordpress/compose': 5.4.1(react@17.0.2) '@wordpress/date': 4.44.0 '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/escape-html': 2.47.0 '@wordpress/hooks': 3.6.1 @@ -39904,7 +39741,7 @@ snapshots: '@wordpress/components@20.0.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/cache': 11.11.0 '@emotion/css': 11.11.2 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) @@ -39925,7 +39762,7 @@ snapshots: '@wordpress/icons': 9.36.0 '@wordpress/is-shallow-equal': 4.24.0 '@wordpress/keycodes': 3.47.0 - '@wordpress/primitives': 3.45.0 + '@wordpress/primitives': 3.55.0 '@wordpress/rich-text': 5.20.0(react@17.0.2) '@wordpress/warning': 2.47.0 change-case: 4.1.2 @@ -39974,7 +39811,7 @@ snapshots: '@wordpress/icons': 9.36.0 '@wordpress/is-shallow-equal': 4.24.0 '@wordpress/keycodes': 3.47.0 - '@wordpress/primitives': 3.45.0 + '@wordpress/primitives': 3.55.0 '@wordpress/rich-text': 5.20.0(react@17.0.2) '@wordpress/warning': 2.47.0 change-case: 4.1.2 @@ -40001,70 +39838,6 @@ snapshots: - '@types/react' - supports-color - '@wordpress/components@25.13.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@ariakit/react': 0.3.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@babel/runtime': 7.25.0 - '@emotion/cache': 11.11.0 - '@emotion/css': 11.11.2 - '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) - '@emotion/serialize': 1.1.2 - '@emotion/styled': 11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@17.0.2))(@types/react@17.0.71)(react@17.0.2) - '@emotion/utils': 1.2.1 - '@floating-ui/react-dom': 2.0.4(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-dropdown-menu': 2.0.4(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@types/gradient-parser': 0.1.3 - '@types/highlight-words-core': 1.2.1 - '@use-gesture/react': 10.3.0(react@17.0.2) - '@wordpress/a11y': 3.47.0 - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/date': 4.47.0 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 - '@wordpress/element': 5.34.0 - '@wordpress/escape-html': 2.47.0 - '@wordpress/hooks': 3.57.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.54.0 - '@wordpress/icons': 9.48.0 - '@wordpress/is-shallow-equal': 4.47.0 - '@wordpress/keycodes': 3.57.0 - '@wordpress/primitives': 3.45.0 - '@wordpress/private-apis': 0.29.0 - '@wordpress/rich-text': 6.24.0(react@17.0.2) - '@wordpress/warning': 2.47.0 - change-case: 4.1.2 - classnames: 2.3.2 - colord: 2.9.3 - date-fns: 2.30.0 - deepmerge: 4.3.1 - dom-scroll-into-view: 1.2.1 - downshift: 6.1.12(react@17.0.2) - fast-deep-equal: 3.1.3 - framer-motion: 10.16.16(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - gradient-parser: 0.1.5 - highlight-words-core: 1.2.2 - is-plain-object: 5.0.0 - memize: 2.1.0 - path-to-regexp: 6.2.1 - re-resizable: 6.9.11(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - react: 17.0.2 - react-colorful: 5.6.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - react-dom: 17.0.2(react@17.0.2) - reakit: 1.3.11(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - remove-accents: 0.5.0 - use-lilius: 2.0.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - uuid: 9.0.1 - valtio: 1.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(babel-plugin-macros@3.1.0)(react@17.0.2) - transitivePeerDependencies: - - '@babel/helper-module-imports' - - '@babel/types' - - '@types/react' - - aslemammad-vite-plugin-macro - - babel-plugin-macros - - supports-color - - vite - '@wordpress/components@25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@ariakit/react': 0.3.14(react-dom@17.0.2(react@17.0.2))(react@17.0.2) @@ -40078,7 +39851,7 @@ snapshots: '@floating-ui/react-dom': 2.0.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@types/gradient-parser': 0.1.3 '@types/highlight-words-core': 1.2.1 - '@use-gesture/react': 10.3.0(react@17.0.2) + '@use-gesture/react': 10.3.1(react@17.0.2) '@wordpress/a11y': 3.57.0 '@wordpress/compose': 6.34.0(react@17.0.2) '@wordpress/date': 4.57.0 @@ -40141,7 +39914,7 @@ snapshots: '@floating-ui/react-dom': 2.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/gradient-parser': 0.1.3 '@types/highlight-words-core': 1.2.1 - '@use-gesture/react': 10.3.0(react@18.3.1) + '@use-gesture/react': 10.3.1(react@18.3.1) '@wordpress/a11y': 3.57.0 '@wordpress/compose': 6.34.0(react@18.3.1) '@wordpress/date': 4.57.0 @@ -40266,7 +40039,7 @@ snapshots: '@floating-ui/react-dom': 2.0.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@types/gradient-parser': 0.1.3 '@types/highlight-words-core': 1.2.1 - '@use-gesture/react': 10.3.0(react@17.0.2) + '@use-gesture/react': 10.3.1(react@17.0.2) '@wordpress/a11y': 3.57.0 '@wordpress/compose': 6.34.0(react@17.0.2) '@wordpress/date': 4.57.0 @@ -40308,6 +40081,61 @@ snapshots: - '@types/react' - supports-color + '@wordpress/components@28.6.0(@emotion/is-prop-valid@1.2.1)(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@ariakit/react': 0.4.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@babel/runtime': 7.25.0 + '@emotion/cache': 11.11.0 + '@emotion/css': 11.11.2 + '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) + '@emotion/serialize': 1.1.2 + '@emotion/styled': 11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@17.0.2))(@types/react@17.0.71)(react@17.0.2) + '@emotion/utils': 1.2.1 + '@floating-ui/react-dom': 2.0.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/gradient-parser': 0.1.3 + '@types/highlight-words-core': 1.2.1 + '@use-gesture/react': 10.3.1(react@17.0.2) + '@wordpress/a11y': 4.6.0 + '@wordpress/compose': 7.6.0(react@17.0.2) + '@wordpress/date': 5.6.0 + '@wordpress/deprecated': 4.6.0 + '@wordpress/dom': 4.6.0 + '@wordpress/element': 6.6.0 + '@wordpress/escape-html': 3.6.0 + '@wordpress/hooks': 4.6.0 + '@wordpress/html-entities': 4.6.0 + '@wordpress/i18n': 5.6.0 + '@wordpress/icons': 10.6.0(react@17.0.2) + '@wordpress/is-shallow-equal': 5.6.0 + '@wordpress/keycodes': 4.6.0 + '@wordpress/primitives': 4.6.0(react@17.0.2) + '@wordpress/private-apis': 1.6.0 + '@wordpress/rich-text': 7.6.0(react@17.0.2) + '@wordpress/warning': 3.6.0 + change-case: 4.1.2 + clsx: 2.1.1 + colord: 2.9.3 + date-fns: 3.6.0 + deepmerge: 4.3.1 + fast-deep-equal: 3.1.3 + framer-motion: 11.3.30(@emotion/is-prop-valid@1.2.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + gradient-parser: 0.1.5 + highlight-words-core: 1.2.2 + is-plain-object: 5.0.0 + memize: 2.1.0 + path-to-regexp: 6.2.1 + re-resizable: 6.9.11(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + react: 17.0.2 + react-colorful: 5.6.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + react-dom: 17.0.2(react@17.0.2) + remove-accents: 0.5.0 + use-lilius: 2.0.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + uuid: 9.0.1 + transitivePeerDependencies: + - '@emotion/is-prop-valid' + - '@types/react' + - supports-color + '@wordpress/compose@3.25.3(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 @@ -40331,8 +40159,8 @@ snapshots: '@babel/runtime': 7.25.0 '@types/lodash': 4.14.149 '@types/mousetrap': 1.6.15 - '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.27.0 + '@wordpress/deprecated': 3.6.1 + '@wordpress/dom': 3.6.1 '@wordpress/element': 3.2.0 '@wordpress/is-shallow-equal': 4.24.0 '@wordpress/keycodes': 3.47.0 @@ -40350,7 +40178,7 @@ snapshots: '@babel/runtime': 7.25.0 '@types/mousetrap': 1.6.15 '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/dom': 3.27.0 '@wordpress/element': 4.20.0 '@wordpress/is-shallow-equal': 4.47.0 '@wordpress/keycodes': 3.47.0 @@ -40366,7 +40194,7 @@ snapshots: '@babel/runtime': 7.25.0 '@types/mousetrap': 1.6.15 '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/dom': 3.27.0 '@wordpress/element': 4.20.0 '@wordpress/is-shallow-equal': 4.47.0 '@wordpress/keycodes': 3.47.0 @@ -40383,7 +40211,7 @@ snapshots: '@types/lodash': 4.14.202 '@types/mousetrap': 1.6.15 '@wordpress/deprecated': 3.6.1 - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/is-shallow-equal': 4.24.0 '@wordpress/keycodes': 3.6.1 @@ -40397,7 +40225,7 @@ snapshots: '@wordpress/compose@5.5.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.23.6 + '@babel/runtime': 7.25.0 '@types/lodash': 4.14.202 '@types/mousetrap': 1.6.15 '@wordpress/deprecated': 3.41.0 @@ -40415,7 +40243,7 @@ snapshots: '@wordpress/compose@5.5.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.23.6 + '@babel/runtime': 7.25.0 '@types/lodash': 4.14.202 '@types/mousetrap': 1.6.15 '@wordpress/deprecated': 3.41.0 @@ -40435,8 +40263,8 @@ snapshots: dependencies: '@babel/runtime': 7.25.0 '@types/mousetrap': 1.6.15 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 '@wordpress/element': 5.34.0 '@wordpress/is-shallow-equal': 4.47.0 '@wordpress/keycodes': 3.47.0 @@ -40452,8 +40280,8 @@ snapshots: dependencies: '@babel/runtime': 7.25.0 '@types/mousetrap': 1.6.15 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 '@wordpress/element': 5.34.0 '@wordpress/is-shallow-equal': 4.47.0 '@wordpress/keycodes': 3.47.0 @@ -40499,12 +40327,29 @@ snapshots: react: 18.3.1 use-memo-one: 1.1.3(react@18.3.1) + '@wordpress/compose@7.6.0(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@types/mousetrap': 1.6.15 + '@wordpress/deprecated': 4.6.0 + '@wordpress/dom': 4.6.0 + '@wordpress/element': 6.6.0 + '@wordpress/is-shallow-equal': 5.6.0 + '@wordpress/keycodes': 4.6.0 + '@wordpress/priority-queue': 3.6.0 + '@wordpress/undo-manager': 1.6.0 + change-case: 4.1.2 + clipboard: 2.0.11 + mousetrap: 1.6.5 + react: 17.0.2 + use-memo-one: 1.1.3(react@17.0.2) + '@wordpress/core-commands@0.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 '@wordpress/commands': 0.9.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.22.0 '@wordpress/i18n': 4.47.0 '@wordpress/icons': 9.36.0 @@ -40707,6 +40552,25 @@ snapshots: '@wordpress/deprecated': 3.41.0 react: 17.0.2 + '@wordpress/data@10.6.0(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/compose': 7.6.0(react@17.0.2) + '@wordpress/deprecated': 4.6.0 + '@wordpress/element': 6.6.0 + '@wordpress/is-shallow-equal': 5.6.0 + '@wordpress/priority-queue': 3.6.0 + '@wordpress/private-apis': 1.6.0 + '@wordpress/redux-routine': 5.6.0(redux@4.2.1) + deepmerge: 4.3.1 + equivalent-key-map: 0.2.2 + is-plain-object: 5.0.0 + is-promise: 4.0.0 + react: 17.0.2 + redux: 4.2.1 + rememo: 4.0.2 + use-memo-one: 1.1.3(react@17.0.2) + '@wordpress/data@4.27.3(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 @@ -40733,8 +40597,8 @@ snapshots: '@wordpress/deprecated': 3.6.1 '@wordpress/element': 3.2.0 '@wordpress/is-shallow-equal': 4.24.0 - '@wordpress/priority-queue': 2.47.0 - '@wordpress/redux-routine': 4.47.0(redux@4.2.1) + '@wordpress/priority-queue': 2.57.0 + '@wordpress/redux-routine': 4.57.0(redux@4.2.1) equivalent-key-map: 0.2.2 is-promise: 4.0.0 lodash: 4.17.21 @@ -40747,7 +40611,7 @@ snapshots: '@wordpress/data@6.15.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 5.20.0(react@17.0.2) '@wordpress/deprecated': 3.41.0 '@wordpress/element': 4.20.0 @@ -40817,7 +40681,7 @@ snapshots: '@wordpress/data@7.6.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 5.20.0(react@17.0.2) '@wordpress/deprecated': 3.41.0 '@wordpress/element': 4.20.0 @@ -40835,7 +40699,7 @@ snapshots: '@wordpress/data@7.6.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 5.20.0(react@18.3.1) '@wordpress/deprecated': 3.41.0 '@wordpress/element': 4.20.0 @@ -40927,6 +40791,28 @@ snapshots: rememo: 4.0.2 use-memo-one: 1.1.3(react@18.3.1) + '@wordpress/dataviews@4.2.0(@emotion/is-prop-valid@1.2.1)(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@ariakit/react': 0.4.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@babel/runtime': 7.25.0 + '@wordpress/components': 28.6.0(@emotion/is-prop-valid@1.2.1)(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/compose': 7.6.0(react@17.0.2) + '@wordpress/data': 10.6.0(react@17.0.2) + '@wordpress/element': 6.6.0 + '@wordpress/i18n': 5.6.0 + '@wordpress/icons': 10.6.0(react@17.0.2) + '@wordpress/primitives': 4.6.0(react@17.0.2) + '@wordpress/private-apis': 1.6.0 + '@wordpress/warning': 3.6.0 + clsx: 2.1.1 + react: 17.0.2 + remove-accents: 0.5.0 + transitivePeerDependencies: + - '@emotion/is-prop-valid' + - '@types/react' + - react-dom + - supports-color + '@wordpress/date@4.44.0': dependencies: '@babel/runtime': 7.23.5 @@ -40934,13 +40820,6 @@ snapshots: moment: 2.29.4 moment-timezone: 0.5.43 - '@wordpress/date@4.47.0': - dependencies: - '@babel/runtime': 7.25.0 - '@wordpress/deprecated': 3.47.0 - moment: 2.29.4 - moment-timezone: 0.5.43 - '@wordpress/date@4.57.0': dependencies: '@babel/runtime': 7.25.0 @@ -40954,6 +40833,13 @@ snapshots: moment: 2.29.4 moment-timezone: 0.5.43 + '@wordpress/date@5.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/deprecated': 4.6.0 + moment: 2.29.4 + moment-timezone: 0.5.43 + '@wordpress/dependency-extraction-webpack-plugin@2.9.0(webpack@4.47.0(webpack-cli@3.3.12))': dependencies: json2php: 0.0.4 @@ -41015,6 +40901,11 @@ snapshots: '@babel/runtime': 7.23.5 '@wordpress/hooks': 3.6.1 + '@wordpress/deprecated@4.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/hooks': 4.6.0 + '@wordpress/dom-ready@3.27.0': dependencies: '@babel/runtime': 7.23.5 @@ -41031,6 +40922,10 @@ snapshots: dependencies: '@babel/runtime': 7.23.5 + '@wordpress/dom-ready@4.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/dom@2.18.0': dependencies: '@babel/runtime': 7.25.0 @@ -41041,11 +40936,6 @@ snapshots: '@babel/runtime': 7.23.5 '@wordpress/deprecated': 3.41.0 - '@wordpress/dom@3.47.0': - dependencies: - '@babel/runtime': 7.25.0 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom@3.57.0': dependencies: '@babel/runtime': 7.25.0 @@ -41056,6 +40946,11 @@ snapshots: '@babel/runtime': 7.23.5 lodash: 4.17.21 + '@wordpress/dom@4.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/deprecated': 4.6.0 + '@wordpress/e2e-test-utils-playwright@1.0.1(@playwright/test@1.46.1)(encoding@0.1.13)(typescript@5.3.2)': dependencies: '@playwright/test': 1.46.1 @@ -41134,7 +41029,7 @@ snapshots: '@wordpress/e2e-test-utils@4.16.1(encoding@0.1.13)(jest@25.5.4)(puppeteer@2.1.1)(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@18.3.1))': dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.23.5 '@wordpress/keycodes': 2.19.3 '@wordpress/url': 2.22.2(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@18.3.1)) jest: 25.5.4 @@ -41160,7 +41055,7 @@ snapshots: '@wordpress/e2e-test-utils@4.16.1(encoding@0.1.13)(jest@29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)))(puppeteer@17.1.3(encoding@0.1.13))(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1))': dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.23.5 '@wordpress/keycodes': 2.19.3 '@wordpress/url': 2.22.2(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1)) jest: 29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)) @@ -41264,7 +41159,7 @@ snapshots: '@wordpress/element': 4.4.1 '@wordpress/hooks': 3.6.1 '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/icons': 8.4.0 '@wordpress/interface': 4.5.6(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2) '@wordpress/keyboard-shortcuts': 3.4.1(react@17.0.2) '@wordpress/keycodes': 3.6.1 @@ -41288,36 +41183,36 @@ snapshots: '@wordpress/edit-site@5.15.0(patch_hash=6y3l6gxu33zybfmvbjd23dtqda)(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@preact/signals-core@1.5.1)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.23.5 - '@wordpress/a11y': 3.47.0 + '@babel/runtime': 7.25.0 + '@wordpress/a11y': 3.57.0 '@wordpress/api-fetch': 6.44.0 '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/block-library': 8.24.1(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@preact/signals-core@1.5.1)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/blocks': 12.24.0(react@17.0.2) '@wordpress/commands': 0.9.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/components': 25.13.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) '@wordpress/core-commands': 0.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/date': 4.44.0 '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.47.0 + '@wordpress/dom': 3.57.0 '@wordpress/editor': 13.24.1(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/element': 5.22.0 - '@wordpress/escape-html': 2.47.0 + '@wordpress/escape-html': 2.57.0 '@wordpress/hooks': 3.57.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.36.0 '@wordpress/interface': 5.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/keyboard-shortcuts': 4.24.0(react@17.0.2) - '@wordpress/keycodes': 3.47.0 + '@wordpress/keycodes': 3.57.0 '@wordpress/media-utils': 4.38.0 '@wordpress/notices': 4.15.0(react@17.0.2) '@wordpress/plugins': 6.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/primitives': 3.45.0 + '@wordpress/primitives': 3.55.0 '@wordpress/private-apis': 0.20.0 '@wordpress/reusable-blocks': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/router': 0.7.0(react@17.0.2) @@ -41369,7 +41264,7 @@ snapshots: '@wordpress/hooks': 3.6.1 '@wordpress/html-entities': 3.6.1 '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/icons': 8.4.0 '@wordpress/keyboard-shortcuts': 3.4.1(react@17.0.2) '@wordpress/keycodes': 3.6.1 '@wordpress/media-utils': 3.4.1 @@ -41395,22 +41290,22 @@ snapshots: '@wordpress/editor@13.24.1(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 - '@wordpress/a11y': 3.47.0 + '@wordpress/a11y': 3.57.0 '@wordpress/api-fetch': 6.44.0 '@wordpress/blob': 3.47.0 '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/blocks': 12.24.0(react@17.0.2) '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/date': 4.47.0 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/date': 4.57.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 '@wordpress/element': 5.34.0 '@wordpress/hooks': 3.57.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 '@wordpress/keyboard-shortcuts': 4.24.0(react@17.0.2) '@wordpress/keycodes': 3.57.0 @@ -41420,7 +41315,7 @@ snapshots: '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/private-apis': 0.29.0 '@wordpress/reusable-blocks': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/rich-text': 6.24.0(react@17.0.2) + '@wordpress/rich-text': 6.34.0(react@17.0.2) '@wordpress/server-side-render': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/url': 3.48.0 '@wordpress/wordcount': 3.47.0 @@ -41517,6 +41412,17 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + '@wordpress/element@6.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@types/react': 17.0.71 + '@types/react-dom': 18.3.0 + '@wordpress/escape-html': 3.6.0 + change-case: 4.1.2 + is-plain-object: 5.0.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@wordpress/env@10.5.0': dependencies: chalk: 4.1.2 @@ -41546,6 +41452,10 @@ snapshots: dependencies: '@babel/runtime': 7.25.0 + '@wordpress/escape-html@3.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/eslint-plugin@12.9.0(@babel/core@7.24.7)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.56.0(eslint@8.55.0)(typescript@5.3.2))(eslint-import-resolver-webpack@0.13.2)(eslint-plugin-import@2.28.1)(eslint@8.55.0))(eslint-import-resolver-webpack@0.13.2(eslint-plugin-import@2.28.1)(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@5.1.4)))(eslint@8.55.0)(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.2)))(typescript@5.3.2)(wp-prettier@2.6.2)': dependencies: '@babel/core': 7.24.7 @@ -41715,13 +41625,13 @@ snapshots: '@wordpress/hooks@3.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/hooks@3.6.1': dependencies: '@babel/runtime': 7.24.7 - '@wordpress/hooks@4.4.0': + '@wordpress/hooks@4.6.0': dependencies: '@babel/runtime': 7.25.0 @@ -41741,6 +41651,10 @@ snapshots: dependencies: '@babel/runtime': 7.23.5 + '@wordpress/html-entities@4.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/i18n@3.20.0': dependencies: '@babel/runtime': 7.25.0 @@ -41769,15 +41683,6 @@ snapshots: sprintf-js: 1.1.3 tannin: 1.2.0 - '@wordpress/i18n@4.54.0': - dependencies: - '@babel/runtime': 7.25.0 - '@wordpress/hooks': 3.57.0 - gettext-parser: 1.4.0 - memize: 2.1.0 - sprintf-js: 1.1.3 - tannin: 1.2.0 - '@wordpress/i18n@4.57.0': dependencies: '@babel/runtime': 7.25.0 @@ -41800,12 +41705,29 @@ snapshots: '@wordpress/i18n@5.0.1': dependencies: '@babel/runtime': 7.25.0 - '@wordpress/hooks': 4.4.0 + '@wordpress/hooks': 4.6.0 gettext-parser: 1.4.0 memize: 2.1.0 sprintf-js: 1.1.3 tannin: 1.2.0 + '@wordpress/i18n@5.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/hooks': 4.6.0 + gettext-parser: 1.4.0 + memize: 2.1.0 + sprintf-js: 1.1.3 + tannin: 1.2.0 + + '@wordpress/icons@10.6.0(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/element': 6.6.0 + '@wordpress/primitives': 4.6.0(react@17.0.2) + transitivePeerDependencies: + - react + '@wordpress/icons@4.1.0': dependencies: '@babel/runtime': 7.25.0 @@ -41836,12 +41758,6 @@ snapshots: '@wordpress/element': 5.22.0 '@wordpress/primitives': 3.45.0 - '@wordpress/icons@9.38.0': - dependencies: - '@babel/runtime': 7.25.0 - '@wordpress/element': 5.34.0 - '@wordpress/primitives': 3.45.0 - '@wordpress/icons@9.48.0': dependencies: '@babel/runtime': 7.25.0 @@ -41867,7 +41783,7 @@ snapshots: '@wordpress/deprecated': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/icons': 8.4.0 '@wordpress/plugins': 4.4.3(react@17.0.2) '@wordpress/preferences': 1.2.5(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2) '@wordpress/viewport': 4.20.0(react@17.0.2) @@ -41882,15 +41798,15 @@ snapshots: '@wordpress/interface@5.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.23.5 - '@wordpress/a11y': 3.47.0 - '@wordpress/components': 25.13.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/deprecated': 3.47.0 - '@wordpress/element': 5.24.0 - '@wordpress/i18n': 4.47.0 - '@wordpress/icons': 9.38.0 + '@babel/runtime': 7.25.0 + '@wordpress/a11y': 3.57.0 + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/deprecated': 3.57.0 + '@wordpress/element': 5.34.0 + '@wordpress/i18n': 4.57.0 + '@wordpress/icons': 9.48.0 '@wordpress/plugins': 6.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/viewport': 5.24.0(react@17.0.2) @@ -41922,6 +41838,10 @@ snapshots: dependencies: '@babel/runtime': 7.25.0 + '@wordpress/is-shallow-equal@5.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/jest-console@3.10.0(jest@25.5.4)': dependencies: '@babel/runtime': 7.25.0 @@ -42078,7 +41998,7 @@ snapshots: '@wordpress/keyboard-shortcuts@3.20.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/data': 7.6.0(react@17.0.2) '@wordpress/element': 4.20.0 '@wordpress/keycodes': 3.47.0 @@ -42108,7 +42028,7 @@ snapshots: '@wordpress/keyboard-shortcuts@4.24.0(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 - '@wordpress/data': 9.17.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.34.0 '@wordpress/keycodes': 3.57.0 react: 17.0.2 @@ -42117,7 +42037,7 @@ snapshots: '@wordpress/keyboard-shortcuts@4.24.0(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 - '@wordpress/data': 9.17.0(react@18.3.1) + '@wordpress/data': 9.27.0(react@18.3.1) '@wordpress/element': 5.34.0 '@wordpress/keycodes': 3.57.0 react: 18.3.1 @@ -42151,6 +42071,11 @@ snapshots: '@babel/runtime': 7.25.0 '@wordpress/i18n': 5.0.1 + '@wordpress/keycodes@4.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/i18n': 5.6.0 + '@wordpress/lazy-import@1.34.0': dependencies: execa: 4.1.0 @@ -42176,21 +42101,21 @@ snapshots: '@wordpress/notices@3.12.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/a11y': 3.47.0 '@wordpress/data': 6.15.0(react@17.0.2) react: 17.0.2 '@wordpress/notices@3.12.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/a11y': 3.47.0 '@wordpress/data': 6.15.0(react@18.3.1) react: 18.3.1 '@wordpress/notices@3.31.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/a11y': 3.47.0 '@wordpress/data': 9.17.0(react@17.0.2) transitivePeerDependencies: @@ -42207,15 +42132,15 @@ snapshots: '@wordpress/notices@4.15.0(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 - '@wordpress/a11y': 3.47.0 - '@wordpress/data': 9.17.0(react@17.0.2) + '@wordpress/a11y': 3.57.0 + '@wordpress/data': 9.27.0(react@17.0.2) react: 17.0.2 '@wordpress/notices@4.15.0(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 - '@wordpress/a11y': 3.47.0 - '@wordpress/data': 9.17.0(react@18.3.1) + '@wordpress/a11y': 3.57.0 + '@wordpress/data': 9.27.0(react@18.3.1) react: 18.3.1 '@wordpress/npm-package-json-lint-config@3.1.0(npm-package-json-lint@5.4.2)': @@ -42237,7 +42162,7 @@ snapshots: '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.34.0 - '@wordpress/html-entities': 3.47.0 + '@wordpress/html-entities': 3.57.0 '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 '@wordpress/notices': 4.15.0(react@17.0.2) @@ -42281,11 +42206,11 @@ snapshots: dependencies: '@babel/runtime': 7.25.0 '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) '@wordpress/element': 5.34.0 '@wordpress/hooks': 3.57.0 '@wordpress/icons': 9.48.0 - '@wordpress/is-shallow-equal': 4.47.0 + '@wordpress/is-shallow-equal': 4.57.0 memize: 2.1.0 react: 17.0.2 react-dom: 17.0.2(react@17.0.2) @@ -42355,7 +42280,7 @@ snapshots: '@wordpress/preferences@3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 - '@wordpress/a11y': 3.47.0 + '@wordpress/a11y': 3.57.0 '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.34.0 @@ -42376,7 +42301,7 @@ snapshots: '@wordpress/preferences@3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 - '@wordpress/a11y': 3.47.0 + '@wordpress/a11y': 3.57.0 '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@wordpress/data': 9.27.0(react@18.3.1) '@wordpress/element': 5.34.0 @@ -42446,6 +42371,13 @@ snapshots: '@wordpress/element': 5.34.0 classnames: 2.3.2 + '@wordpress/primitives@4.6.0(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/element': 6.6.0 + clsx: 2.1.1 + react: 17.0.2 + '@wordpress/priority-queue@1.11.2': dependencies: '@babel/runtime': 7.25.0 @@ -42460,13 +42392,18 @@ snapshots: '@babel/runtime': 7.25.0 requestidlecallback: 0.3.0 + '@wordpress/priority-queue@3.6.0': + dependencies: + '@babel/runtime': 7.25.0 + requestidlecallback: 0.3.0 + '@wordpress/private-apis@0.20.0': dependencies: '@babel/runtime': 7.25.0 '@wordpress/private-apis@0.29.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/private-apis@0.32.0': dependencies: @@ -42514,6 +42451,14 @@ snapshots: redux: 4.2.1 rungen: 0.3.2 + '@wordpress/redux-routine@5.6.0(redux@4.2.1)': + dependencies: + '@babel/runtime': 7.25.0 + is-plain-object: 5.0.0 + is-promise: 4.0.0 + redux: 4.2.1 + rungen: 0.3.2 + '@wordpress/reusable-blocks@3.20.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@wordpress/block-editor': 10.5.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) @@ -42539,9 +42484,9 @@ snapshots: '@wordpress/blocks': 12.24.0(react@17.0.2) '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.34.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 '@wordpress/notices': 4.15.0(react@17.0.2) '@wordpress/private-apis': 0.29.0 @@ -42564,7 +42509,7 @@ snapshots: '@babel/runtime': 7.25.0 '@wordpress/compose': 4.2.0(react@18.3.1) '@wordpress/data': 5.2.0(react@18.3.1)(redux@4.2.1) - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 3.2.0 '@wordpress/escape-html': 2.57.0 '@wordpress/is-shallow-equal': 4.24.0 @@ -42637,36 +42582,6 @@ snapshots: react: 18.3.1 rememo: 3.0.0 - '@wordpress/rich-text@6.24.0(react@17.0.2)': - dependencies: - '@babel/runtime': 7.25.0 - '@wordpress/a11y': 3.47.0 - '@wordpress/compose': 6.34.0(react@17.0.2) - '@wordpress/data': 9.27.0(react@17.0.2) - '@wordpress/deprecated': 3.47.0 - '@wordpress/element': 5.34.0 - '@wordpress/escape-html': 2.57.0 - '@wordpress/i18n': 4.57.0 - '@wordpress/keycodes': 3.57.0 - memize: 2.1.0 - react: 17.0.2 - rememo: 4.0.2 - - '@wordpress/rich-text@6.24.0(react@18.3.1)': - dependencies: - '@babel/runtime': 7.25.0 - '@wordpress/a11y': 3.47.0 - '@wordpress/compose': 6.34.0(react@18.3.1) - '@wordpress/data': 9.27.0(react@18.3.1) - '@wordpress/deprecated': 3.47.0 - '@wordpress/element': 5.34.0 - '@wordpress/escape-html': 2.57.0 - '@wordpress/i18n': 4.57.0 - '@wordpress/keycodes': 3.57.0 - memize: 2.1.0 - react: 18.3.1 - rememo: 4.0.2 - '@wordpress/rich-text@6.34.0(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 @@ -42695,9 +42610,23 @@ snapshots: memize: 2.1.0 react: 18.3.1 + '@wordpress/rich-text@7.6.0(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/a11y': 4.6.0 + '@wordpress/compose': 7.6.0(react@17.0.2) + '@wordpress/data': 10.6.0(react@17.0.2) + '@wordpress/deprecated': 4.6.0 + '@wordpress/element': 6.6.0 + '@wordpress/escape-html': 3.6.0 + '@wordpress/i18n': 5.6.0 + '@wordpress/keycodes': 4.6.0 + memize: 2.1.0 + react: 17.0.2 + '@wordpress/router@0.7.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.23.5 + '@babel/runtime': 7.25.0 '@wordpress/element': 5.22.0 '@wordpress/private-apis': 0.20.0 '@wordpress/url': 3.48.0 @@ -43019,7 +42948,7 @@ snapshots: '@wordpress/server-side-render@3.10.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.23.5 + '@babel/runtime': 7.25.0 '@wordpress/api-fetch': 6.21.0 '@wordpress/blocks': 11.21.0(react@17.0.2) '@wordpress/components': 19.17.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2) @@ -43084,7 +43013,7 @@ snapshots: '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/compose': 6.34.0(react@17.0.2) '@wordpress/data': 9.27.0(react@17.0.2) - '@wordpress/deprecated': 3.47.0 + '@wordpress/deprecated': 3.57.0 '@wordpress/element': 5.34.0 '@wordpress/i18n': 4.57.0 '@wordpress/url': 3.48.0 @@ -43107,7 +43036,7 @@ snapshots: '@wordpress/style-engine@0.15.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 lodash: 4.17.21 '@wordpress/style-engine@0.2.0': @@ -43177,7 +43106,12 @@ snapshots: '@wordpress/undo-manager@0.7.0': dependencies: '@babel/runtime': 7.25.0 - '@wordpress/is-shallow-equal': 4.47.0 + '@wordpress/is-shallow-equal': 4.57.0 + + '@wordpress/undo-manager@1.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/is-shallow-equal': 5.6.0 '@wordpress/url@2.22.2(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1))': dependencies: @@ -43225,7 +43159,7 @@ snapshots: '@wordpress/viewport@4.20.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 5.20.0(react@17.0.2) '@wordpress/data': 7.6.0(react@17.0.2) react: 17.0.2 @@ -43241,8 +43175,8 @@ snapshots: '@wordpress/viewport@5.24.0(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.34.0 react: 17.0.2 @@ -43254,6 +43188,8 @@ snapshots: '@wordpress/warning@2.6.1': {} + '@wordpress/warning@3.6.0': {} + '@wordpress/widgets@3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@babel/runtime': 7.25.0 @@ -43261,11 +43197,11 @@ snapshots: '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/blocks': 12.24.0(react@17.0.2) '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.34.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 '@wordpress/notices': 4.15.0(react@17.0.2) classnames: 2.3.2 @@ -46817,7 +46753,7 @@ snapshots: date-fns@2.30.0: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 date-fns@3.6.0: {} @@ -47739,7 +47675,7 @@ snapshots: eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.56.0(eslint@8.55.0)(typescript@5.3.2))(eslint-import-resolver-webpack@0.13.2)(eslint-plugin-import@2.28.1)(eslint@8.55.0): dependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) enhanced-resolve: 5.15.0 eslint: 8.55.0 eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.56.0(eslint@8.55.0)(typescript@5.3.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.56.0(eslint@8.55.0)(typescript@5.3.2))(eslint-import-resolver-webpack@0.13.2)(eslint-plugin-import@2.28.1)(eslint@8.55.0))(eslint-import-resolver-webpack@0.13.2(eslint-plugin-import@2.28.1)(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@5.1.4)))(eslint@8.55.0) @@ -47756,7 +47692,7 @@ snapshots: eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.55.0)(typescript@5.3.3))(eslint-import-resolver-webpack@0.13.8)(eslint-plugin-import@2.29.0)(eslint@8.55.0): dependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) enhanced-resolve: 5.15.0 eslint: 8.55.0 eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0(eslint@8.55.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.55.0)(typescript@5.3.3))(eslint-import-resolver-webpack@0.13.8)(eslint-plugin-import@2.29.0)(eslint@8.55.0))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.0)(webpack@5.89.0(webpack-cli@4.10.0)))(eslint@8.55.0) @@ -48109,7 +48045,7 @@ snapshots: eslint-plugin-jsx-a11y@6.8.0(eslint@8.55.0): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 aria-query: 5.3.0 array-includes: 3.1.7 array.prototype.flatmap: 1.3.2 @@ -48442,7 +48378,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -49170,7 +49106,7 @@ snapshots: follow-redirects@1.15.6(debug@4.3.4): optionalDependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) follow-redirects@1.5.10: dependencies: @@ -49391,6 +49327,14 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + framer-motion@11.3.30(@emotion/is-prop-valid@1.2.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + dependencies: + tslib: 2.6.3 + optionalDependencies: + '@emotion/is-prop-valid': 1.2.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + framer-motion@6.5.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2): dependencies: '@motionone/dom': 10.12.0 @@ -52393,7 +52337,9 @@ snapshots: pretty-format: 24.9.0 throat: 4.1.0 transitivePeerDependencies: + - bufferutil - supports-color + - utf-8-validate jest-jasmine2@25.5.4: dependencies: @@ -54065,7 +54011,7 @@ snapshots: chalk: 5.2.0 cli-truncate: 3.1.0 commander: 10.0.1 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) execa: 7.2.0 lilconfig: 2.1.0 listr2: 5.0.8(enquirer@2.4.1) @@ -55369,7 +55315,7 @@ snapshots: dependencies: carlo: 0.9.46 chokidar: 3.5.3 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) isbinaryfile: 3.0.3 mime: 2.6.0 opn: 5.5.0 @@ -55897,7 +55843,7 @@ snapshots: '@oclif/plugin-warn-if-update-available': 2.1.1(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3) aws-sdk: 2.1515.0 concurrently: 7.6.0 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) find-yarn-workspace-root: 2.0.0 fs-extra: 8.1.0 github-slugger: 1.5.0 @@ -56525,7 +56471,7 @@ snapshots: polished@4.2.2: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 popmotion@11.0.3: dependencies: @@ -57409,7 +57355,7 @@ snapshots: puppeteer-core@13.7.0(encoding@0.1.13): dependencies: cross-fetch: 3.1.5(encoding@0.1.13) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) devtools-protocol: 0.0.981744 extract-zip: 2.0.1 https-proxy-agent: 5.0.1 @@ -57448,7 +57394,7 @@ snapshots: '@puppeteer/browsers': 1.4.6(typescript@5.3.2) chromium-bidi: 0.4.16(devtools-protocol@0.0.1147663) cross-fetch: 4.0.0(encoding@0.1.13) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) devtools-protocol: 0.0.1147663 ws: 8.13.0 optionalDependencies: @@ -57464,7 +57410,7 @@ snapshots: '@puppeteer/browsers': 1.4.6(typescript@5.3.3) chromium-bidi: 0.4.16(devtools-protocol@0.0.1147663) cross-fetch: 4.0.0(encoding@0.1.13) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) devtools-protocol: 0.0.1147663 ws: 8.13.0 optionalDependencies: @@ -57480,7 +57426,7 @@ snapshots: '@puppeteer/browsers': 1.9.0 chromium-bidi: 0.5.1(devtools-protocol@0.0.1203626) cross-fetch: 4.0.0(encoding@0.1.13) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) devtools-protocol: 0.0.1203626 ws: 8.14.2 transitivePeerDependencies: @@ -57516,7 +57462,7 @@ snapshots: puppeteer@17.1.3(encoding@0.1.13): dependencies: cross-fetch: 3.1.5(encoding@0.1.13) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) devtools-protocol: 0.0.1036444 extract-zip: 2.0.1 https-proxy-agent: 5.0.1 @@ -57850,7 +57796,7 @@ snapshots: react-docgen-typescript-plugin@1.0.5(typescript@5.3.2)(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@5.1.4)): dependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) endent: 2.1.0 find-cache-dir: 3.3.2 flat-cache: 3.2.0 @@ -58011,7 +57957,7 @@ snapshots: react-inspector@5.1.1(react@17.0.2): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 is-dom: 1.1.0 prop-types: 15.8.1 react: 17.0.2 @@ -58399,7 +58345,7 @@ snapshots: react-select@3.2.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/cache': 10.0.29 '@emotion/core': 10.3.1(react@17.0.2) '@emotion/css': 10.0.27 @@ -59806,7 +59752,7 @@ snapshots: dependencies: '@kwsites/file-exists': 1.1.1 '@kwsites/promise-deferred': 1.1.1 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -60554,7 +60500,7 @@ snapshots: colord: 2.9.3 cosmiconfig: 7.1.0 css-functions-list: 3.2.1 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) fast-glob: 3.3.2 fastest-levenshtein: 1.0.16 file-entry-cache: 6.0.1 @@ -61327,7 +61273,7 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.23.5) - ts-jest@29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3): + ts-jest@29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -61859,14 +61805,14 @@ snapshots: optionalDependencies: file-loader: 6.2.0(webpack@5.89.0(webpack-cli@4.10.0)) - url-loader@4.1.1(file-loader@6.2.0(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))))(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))): + url-loader@4.1.1(file-loader@6.2.0(webpack@4.47.0))(webpack@4.47.0): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 4.47.0(webpack-cli@3.3.12(webpack@5.89.0)) + webpack: 4.47.0 optionalDependencies: - file-loader: 6.2.0(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) + file-loader: 6.2.0(webpack@4.47.0) url-loader@4.1.1(file-loader@6.2.0(webpack@5.89.0(uglify-js@3.17.4)(webpack-cli@4.10.0)))(webpack@5.89.0(uglify-js@3.17.4)(webpack-cli@4.10.0)): dependencies: @@ -61877,14 +61823,14 @@ snapshots: optionalDependencies: file-loader: 6.2.0(webpack@5.89.0(uglify-js@3.17.4)(webpack-cli@4.10.0)) - url-loader@4.1.1(file-loader@6.2.0(webpack@5.89.0))(webpack@4.47.0): + url-loader@4.1.1(file-loader@6.2.0(webpack@5.89.0(webpack-cli@3.3.12)))(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 4.47.0 + webpack: 4.47.0(webpack-cli@3.3.12(webpack@5.89.0)) optionalDependencies: - file-loader: 6.2.0(webpack@4.47.0) + file-loader: 6.2.0(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) url-loader@4.1.1(file-loader@6.2.0(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@5.1.4)))(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@4.10.0)): dependencies: