From 1f164d378a5ad47025939c87ed6804941c413d42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Fri, 26 Jun 2026 11:09:15 +0200 Subject: [PATCH 1/6] add filterable widget availability layer gate widget types per request before REST list and import map --- .../premium-analytics/src/class-analytics.php | 4 + .../src/widget-availability.php | 77 +++++++++++++++++++ .../premium-analytics/src/widget-modules.php | 17 ++-- .../tests/php/Widget_Availability_Test.php | 69 +++++++++++++++++ 4 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 projects/packages/premium-analytics/src/widget-availability.php create mode 100644 projects/packages/premium-analytics/tests/php/Widget_Availability_Test.php diff --git a/projects/packages/premium-analytics/src/class-analytics.php b/projects/packages/premium-analytics/src/class-analytics.php index daccd953800c..c50101ede430 100644 --- a/projects/packages/premium-analytics/src/class-analytics.php +++ b/projects/packages/premium-analytics/src/class-analytics.php @@ -70,6 +70,10 @@ public static function init( $options = array() ) { // Hydrate the widget type registry from the build manifest at init. require_once __DIR__ . '/widget-types.php'; + // Layer the availability filter over the registry (environment gating + // and any host overrides) before the modules below expose the types. + require_once __DIR__ . '/widget-availability.php'; + // Expose dashboard widget modules over REST and wire them into the // page import map for dynamic import() on the client. require_once __DIR__ . '/widget-modules.php'; diff --git a/projects/packages/premium-analytics/src/widget-availability.php b/projects/packages/premium-analytics/src/widget-availability.php new file mode 100644 index 000000000000..f1b5d386efd4 --- /dev/null +++ b/projects/packages/premium-analytics/src/widget-availability.php @@ -0,0 +1,77 @@ + Widget_Type` map. + */ +const WIDGET_TYPES_FILTER = 'jetpack_premium_analytics_widget_types'; + +/** + * Returns the widget types available for the current request. + * + * Starts from every registered widget type and runs the map through + * WIDGET_TYPES_FILTER, the single extension point for hiding widget types per + * request. Callers that expose widget types to the client should use this + * instead of get_registered_widget_types() so the same policy is applied to the + * REST list and the import map alike. + * + * @return Widget_Type[] Associative array of `$name => $widget_type` pairs. + */ +function get_available_widget_types() { + /** + * Filters the widget types available to the dashboard for this request. + * + * Removing an entry hides that widget type from the client entirely: it + * drops out of the `/jetpack/v4/widget-modules` REST list and out of the + * page import map, so it cannot be rendered or added to a dashboard. + * + * @param Widget_Type[] $widget_types Map of `$name => Widget_Type`. + */ + return apply_filters( WIDGET_TYPES_FILTER, get_registered_widget_types() ); +} + +/** + * Hides developer-only widget types when running in production. + * + * Keyed off the WordPress core environment type, so a site opts in to these + * widgets by declaring a non-production environment (e.g. `WP_ENVIRONMENT_TYPE` + * set to `local`, `development`, or `staging`). + * + * @param Widget_Type[] $widget_types Map of `$name => Widget_Type`. + * @return Widget_Type[] The map without production-restricted widget types. + */ +function filter_widget_types_by_environment( $widget_types ) { + if ( 'production' !== wp_get_environment_type() ) { + return $widget_types; + } + + // Widget types that must never reach a production dashboard. + $non_production_only = array( 'jpa/react-query-dev-tool' ); + + foreach ( $non_production_only as $widget_type_name ) { + unset( $widget_types[ $widget_type_name ] ); + } + + return $widget_types; +} + +add_filter( WIDGET_TYPES_FILTER, __NAMESPACE__ . '\\filter_widget_types_by_environment' ); diff --git a/projects/packages/premium-analytics/src/widget-modules.php b/projects/packages/premium-analytics/src/widget-modules.php index 96046e01a8c2..d5e4927b9449 100644 --- a/projects/packages/premium-analytics/src/widget-modules.php +++ b/projects/packages/premium-analytics/src/widget-modules.php @@ -2,12 +2,12 @@ /** * Dashboard widget modules: REST exposure + import-map wiring. * - * Reads the widget types from Widget_Type_Registry (hydrated from the build - * manifest in widget-types.php) and exposes them to the client through the - * `/jetpack/v4/widget-modules` REST endpoint, plus adds each widget's render - * and metadata modules to the dashboard page's import map so the client can - * dynamically `import()` them on demand. The host feeds the REST records to - * `useWidgetTypes()` in @wordpress/widget-primitives. + * Reads the available widget types (the registry from widget-types.php, run + * through the availability filter in widget-availability.php) and exposes them + * to the client through the `/jetpack/v4/widget-modules` REST endpoint. + * + * Plus, it adds each widget's render and metadata modules to the dashboard page's + * import map so the client can dynamically `import()` them on demand. * * @package automattic/jetpack-premium-analytics */ @@ -42,7 +42,7 @@ function register_widget_modules_rest_route() { function get_widget_modules_response() { $records = array(); - foreach ( get_registered_widget_types() as $widget_type ) { + foreach ( get_available_widget_types() as $widget_type ) { $records[] = array( 'name' => $widget_type->name, 'render_module' => $widget_type->render_module, @@ -62,13 +62,14 @@ function get_widget_modules_response() { * @return array Updated boot dependencies. */ function add_widget_modules_to_boot_deps( $boot_dependencies ) { - foreach ( get_registered_widget_types() as $widget_type ) { + foreach ( get_available_widget_types() as $widget_type ) { if ( ! empty( $widget_type->render_module ) ) { $boot_dependencies[] = array( 'import' => 'dynamic', 'id' => $widget_type->render_module, ); } + if ( ! empty( $widget_type->widget_module ) ) { $boot_dependencies[] = array( 'import' => 'dynamic', diff --git a/projects/packages/premium-analytics/tests/php/Widget_Availability_Test.php b/projects/packages/premium-analytics/tests/php/Widget_Availability_Test.php new file mode 100644 index 000000000000..72e6d1bc55c9 --- /dev/null +++ b/projects/packages/premium-analytics/tests/php/Widget_Availability_Test.php @@ -0,0 +1,69 @@ +assertSame( 'production', wp_get_environment_type() ); + + $widget_types = array( + 'jpa/react-query-dev-tool' => new Widget_Type( 'jpa/react-query-dev-tool' ), + 'jpa/hello-world' => new Widget_Type( 'jpa/hello-world' ), + ); + + $filtered = filter_widget_types_by_environment( $widget_types ); + + $this->assertArrayNotHasKey( 'jpa/react-query-dev-tool', $filtered, 'Developer-only widgets must be hidden in production.' ); + $this->assertArrayHasKey( 'jpa/hello-world', $filtered, 'Regular widgets remain available.' ); + } + + /** + * Verifies get_available_widget_types() runs the registry through the + * WIDGET_TYPES_FILTER so any callback can scope the visible set. + */ + public function test_get_available_widget_types_applies_filter() { + $registry = Widget_Type_Registry::get_instance(); + $registry->register( 'test/sentinel' ); + + $callback = static function ( $widget_types ) { + unset( $widget_types['test/sentinel'] ); + return $widget_types; + }; + add_filter( WIDGET_TYPES_FILTER, $callback ); + + $available = get_available_widget_types(); + + remove_filter( WIDGET_TYPES_FILTER, $callback ); + $registry->unregister( 'test/sentinel' ); + + $this->assertArrayNotHasKey( 'test/sentinel', $available, 'A filter callback can remove a widget type from the available set.' ); + } +} From 01df29634308a9501f234f83ab5d6fd4c54c3bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Fri, 26 Jun 2026 11:09:35 +0200 Subject: [PATCH 2/6] add React Query Devtools dev-only widget replace inline provider devtools with a non-production widget --- .../changelog/add-react-query-devtools-widget | 4 +++ .../packages/data/package.json | 3 -- .../src/providers/query-client-provider.tsx | 26 ++------------- .../widgets/react-query-dev-tool/package.json | 11 +++++++ .../widgets/react-query-dev-tool/render.tsx | 32 +++++++++++++++++++ .../react-query-dev-tool/style.module.css | 5 +++ .../widgets/react-query-dev-tool/widget.json | 7 ++++ .../widgets/react-query-dev-tool/widget.ts | 19 +++++++++++ 8 files changed, 80 insertions(+), 27 deletions(-) create mode 100644 projects/packages/premium-analytics/changelog/add-react-query-devtools-widget create mode 100644 projects/packages/premium-analytics/widgets/react-query-dev-tool/package.json create mode 100644 projects/packages/premium-analytics/widgets/react-query-dev-tool/render.tsx create mode 100644 projects/packages/premium-analytics/widgets/react-query-dev-tool/style.module.css create mode 100644 projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.json create mode 100644 projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.ts diff --git a/projects/packages/premium-analytics/changelog/add-react-query-devtools-widget b/projects/packages/premium-analytics/changelog/add-react-query-devtools-widget new file mode 100644 index 000000000000..78f1b1ac4484 --- /dev/null +++ b/projects/packages/premium-analytics/changelog/add-react-query-devtools-widget @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Dashboard: add a React Query Devtools widget, gated to non-production environments through a new `jetpack_premium_analytics_widget_types` filter for scoping widget types server-side. diff --git a/projects/packages/premium-analytics/packages/data/package.json b/projects/packages/premium-analytics/packages/data/package.json index 46765a1e3e32..79b6e32b2af5 100644 --- a/projects/packages/premium-analytics/packages/data/package.json +++ b/projects/packages/premium-analytics/packages/data/package.json @@ -18,8 +18,5 @@ "@wordpress/i18n": "^6.9.0", "@wordpress/url": "4.48.1", "date-fns": "4.1.0" - }, - "devDependencies": { - "@tanstack/react-query-devtools": "5.90.2" } } diff --git a/projects/packages/premium-analytics/packages/data/src/providers/query-client-provider.tsx b/projects/packages/premium-analytics/packages/data/src/providers/query-client-provider.tsx index dfb504ca2e26..4121d4466454 100644 --- a/projects/packages/premium-analytics/packages/data/src/providers/query-client-provider.tsx +++ b/projects/packages/premium-analytics/packages/data/src/providers/query-client-provider.tsx @@ -2,7 +2,7 @@ * External dependencies */ import { QueryClient, QueryClientProvider, QueryCache } from '@tanstack/react-query'; -import { ReactNode, lazy, Suspense } from 'react'; +import { ReactNode } from 'react'; /** * Internal dependencies */ @@ -11,19 +11,6 @@ import { globalErrorManager } from './global-error-manager'; const DEFAULT_STALE_TIME = 5 * 60 * 1000; const DEFAULT_GC_TIME = 10 * 60 * 1000; -// Upstream gates devtools behind an admin-toolkit experiment flag; that system -// isn't available here, so we show them in dev builds only. Gate the `lazy()` -// creation on NODE_ENV (not just the render) so the dynamic import sits in a -// dead branch that production builds tree-shake out — no orphaned chunk. -const ReactQueryDevtools = - process.env.NODE_ENV !== 'production' - ? lazy( () => - import( '@tanstack/react-query-devtools' ).then( d => ( { - default: d.ReactQueryDevtools, - } ) ) - ) - : null; - /** * Extract HTTP status code from various error formats. * WordPress REST API errors may have different shapes. @@ -133,14 +120,5 @@ export const queryClient = new QueryClient( { } ); export const AnalyticsQueryClientProvider = ( { children }: { children: ReactNode } ) => { - return ( - - <>{ children } - { ReactQueryDevtools && ( - - - - ) } - - ); + return { children }; }; diff --git a/projects/packages/premium-analytics/widgets/react-query-dev-tool/package.json b/projects/packages/premium-analytics/widgets/react-query-dev-tool/package.json new file mode 100644 index 000000000000..de909fddd34d --- /dev/null +++ b/projects/packages/premium-analytics/widgets/react-query-dev-tool/package.json @@ -0,0 +1,11 @@ +{ + "name": "@automattic/jetpack-premium-analytics-widget-react-query-dev-tool", + "version": "0.1.0-alpha", + "private": true, + "type": "module", + "dependencies": { + "@jetpack-premium-analytics/data": "workspace:*", + "@tanstack/react-query-devtools": "5.90.2", + "@wordpress/icons": "^13.0.0" + } +} diff --git a/projects/packages/premium-analytics/widgets/react-query-dev-tool/render.tsx b/projects/packages/premium-analytics/widgets/react-query-dev-tool/render.tsx new file mode 100644 index 000000000000..bb882402993a --- /dev/null +++ b/projects/packages/premium-analytics/widgets/react-query-dev-tool/render.tsx @@ -0,0 +1,32 @@ +/** + * External dependencies + */ +import { queryClient } from '@jetpack-premium-analytics/data'; +import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools'; +/** + * Internal dependencies + */ +import styles from './style.module.css'; + +/** + * Renders the React Query Devtools as a dashboard widget. + * + * The panel is bound to the dashboard's shared `queryClient` singleton through + * the explicit `client` prop rather than React context: this widget bundles its + * own copy of `@tanstack/react-query`, while `queryClient` comes from the shared + * data module. Passing the instance directly keeps the panel inspecting the very + * cache every other widget reads from, sidestepping the duplicate-context issue. + * + * Visibility is gated server-side — the `jpa/react-query-dev-tool` type is + * removed from the widget list in production (see `src/widget-availability.php`), + * so this render module is never requested there. + * + * @return The rendered devtools panel. + */ +export default function ReactQueryDevTool() { + return ( +
+ +
+ ); +} diff --git a/projects/packages/premium-analytics/widgets/react-query-dev-tool/style.module.css b/projects/packages/premium-analytics/widgets/react-query-dev-tool/style.module.css new file mode 100644 index 000000000000..5bd9e47257a5 --- /dev/null +++ b/projects/packages/premium-analytics/widgets/react-query-dev-tool/style.module.css @@ -0,0 +1,5 @@ +.root { + height: 100%; + min-height: 480px; + overflow: auto; +} diff --git a/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.json b/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.json new file mode 100644 index 000000000000..b5699f2d5d30 --- /dev/null +++ b/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.json @@ -0,0 +1,7 @@ +{ + "name": "jpa/react-query-dev-tool", + "title": "React Query Devtools", + "description": "Inspect the dashboard's React Query cache. Available outside production only.", + "category": "developer", + "presentation": "full-bleed" +} diff --git a/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.ts b/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.ts new file mode 100644 index 000000000000..c7fdea1d645b --- /dev/null +++ b/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.ts @@ -0,0 +1,19 @@ +/** + * WordPress dependencies + */ +import { bug } from '@wordpress/icons'; + +/** + * Widget type definition. + * + * Developer tool, exposed only outside production. Availability is enforced + * server-side by the `jetpack_premium_analytics_widget_types` filter (see + * `src/widget-availability.php`); this metadata only describes the type for the + * dashboard's widget picker. + */ +export default { + name: 'jpa/react-query-dev-tool', + title: 'React Query Devtools', + icon: bug, + presentation: 'full-bleed', +}; From f8db9f18b74c292045ac581f36e31fb2acb575a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Fri, 26 Jun 2026 12:50:41 +0200 Subject: [PATCH 3/6] fix Phan errors in widget availability test cover functions with ::func / CoversFunction, not CoversClass --- .../tests/php/Widget_Availability_Test.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/projects/packages/premium-analytics/tests/php/Widget_Availability_Test.php b/projects/packages/premium-analytics/tests/php/Widget_Availability_Test.php index 72e6d1bc55c9..bdc2a9bc6151 100644 --- a/projects/packages/premium-analytics/tests/php/Widget_Availability_Test.php +++ b/projects/packages/premium-analytics/tests/php/Widget_Availability_Test.php @@ -7,7 +7,6 @@ namespace Automattic\Jetpack\PremiumAnalytics; -use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\CoversFunction; use WorDBless\BaseTestCase; @@ -15,15 +14,11 @@ require_once __DIR__ . '/../../src/widget-availability.php'; /** - * @covers \Automattic\Jetpack\PremiumAnalytics\get_available_widget_types - * @covers \Automattic\Jetpack\PremiumAnalytics\filter_widget_types_by_environment - * @covers ::Automattic\Jetpack\PremiumAnalytics\filter_widget_types_by_environment * @covers ::Automattic\Jetpack\PremiumAnalytics\get_available_widget_types + * @covers ::Automattic\Jetpack\PremiumAnalytics\filter_widget_types_by_environment */ #[CoversFunction( 'Automattic\Jetpack\PremiumAnalytics\get_available_widget_types' )] #[CoversFunction( 'Automattic\Jetpack\PremiumAnalytics\filter_widget_types_by_environment' )] -#[CoversClass( filter_widget_types_by_environment::class )] -#[CoversClass( get_available_widget_types::class )] class Widget_Availability_Test extends BaseTestCase { /** From 8c19a7504bbad3d557ac64d9b2399dab86b1de2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Fri, 26 Jun 2026 12:50:52 +0200 Subject: [PATCH 4/6] tighten widget + availability docblocks shorten prose; document the wp_get_environment_type production default --- .../src/widget-availability.php | 51 ++++++++----------- .../premium-analytics/src/widget-modules.php | 16 +++--- .../widgets/react-query-dev-tool/render.tsx | 15 +++--- .../widgets/react-query-dev-tool/widget.ts | 7 ++- 4 files changed, 38 insertions(+), 51 deletions(-) diff --git a/projects/packages/premium-analytics/src/widget-availability.php b/projects/packages/premium-analytics/src/widget-availability.php index f1b5d386efd4..6fc61a4b8a18 100644 --- a/projects/packages/premium-analytics/src/widget-availability.php +++ b/projects/packages/premium-analytics/src/widget-availability.php @@ -1,18 +1,15 @@ Widget_Type` map. + * Filter over the available widget types map (`$name => Widget_Type`). */ const WIDGET_TYPES_FILTER = 'jetpack_premium_analytics_widget_types'; /** * Returns the widget types available for the current request. * - * Starts from every registered widget type and runs the map through - * WIDGET_TYPES_FILTER, the single extension point for hiding widget types per - * request. Callers that expose widget types to the client should use this - * instead of get_registered_widget_types() so the same policy is applied to the - * REST list and the import map alike. + * Every registered widget type, run through WIDGET_TYPES_FILTER. Use this, not + * get_registered_widget_types(), anywhere widget types reach the client, so the + * same policy covers the REST list and the import map. * - * @return Widget_Type[] Associative array of `$name => $widget_type` pairs. + * @return Widget_Type[] Map of `$name => Widget_Type`. */ function get_available_widget_types() { /** - * Filters the widget types available to the dashboard for this request. + * Filters the widget types available to the dashboard this request. * - * Removing an entry hides that widget type from the client entirely: it - * drops out of the `/jetpack/v4/widget-modules` REST list and out of the - * page import map, so it cannot be rendered or added to a dashboard. + * Removing an entry drops it from the `/jetpack/v4/widget-modules` REST list + * and the page import map, so it cannot be rendered or added. * * @param Widget_Type[] $widget_types Map of `$name => Widget_Type`. */ @@ -50,21 +43,21 @@ function get_available_widget_types() { } /** - * Hides developer-only widget types when running in production. + * Hides developer-only widget types in production. * - * Keyed off the WordPress core environment type, so a site opts in to these - * widgets by declaring a non-production environment (e.g. `WP_ENVIRONMENT_TYPE` - * set to `local`, `development`, or `staging`). + * Keyed off wp_get_environment_type(), which defaults to `production`: a site + * opts in by declaring `WP_ENVIRONMENT_TYPE` as `local`, `development`, or + * `staging`. * * @param Widget_Type[] $widget_types Map of `$name => Widget_Type`. - * @return Widget_Type[] The map without production-restricted widget types. + * @return Widget_Type[] The map minus developer-only types in production. */ function filter_widget_types_by_environment( $widget_types ) { if ( 'production' !== wp_get_environment_type() ) { return $widget_types; } - // Widget types that must never reach a production dashboard. + // Types that must never reach a production dashboard. $non_production_only = array( 'jpa/react-query-dev-tool' ); foreach ( $non_production_only as $widget_type_name ) { diff --git a/projects/packages/premium-analytics/src/widget-modules.php b/projects/packages/premium-analytics/src/widget-modules.php index d5e4927b9449..8c591f7e0d43 100644 --- a/projects/packages/premium-analytics/src/widget-modules.php +++ b/projects/packages/premium-analytics/src/widget-modules.php @@ -2,12 +2,10 @@ /** * Dashboard widget modules: REST exposure + import-map wiring. * - * Reads the available widget types (the registry from widget-types.php, run - * through the availability filter in widget-availability.php) and exposes them - * to the client through the `/jetpack/v4/widget-modules` REST endpoint. - * - * Plus, it adds each widget's render and metadata modules to the dashboard page's - * import map so the client can dynamically `import()` them on demand. + * Reads get_available_widget_types() (the registry filtered by + * widget-availability.php) and exposes it two ways: the + * `/jetpack/v4/widget-modules` REST list, and the page import map, where each + * widget's render and metadata modules are registered for dynamic `import()`. * * @package automattic/jetpack-premium-analytics */ @@ -35,7 +33,7 @@ function register_widget_modules_rest_route() { add_action( 'rest_api_init', __NAMESPACE__ . '\\register_widget_modules_rest_route' ); /** - * Build the REST response: one record per registered widget. + * Build the REST response: one record per available widget type. * * @return \WP_REST_Response */ @@ -55,8 +53,8 @@ function get_widget_modules_response() { } /** - * Add registered widget modules to the dashboard page import map as dynamic - * dependencies, so the client can `import()` them on demand. + * Add available widget modules to the page import map as dynamic dependencies, + * so the client can `import()` them on demand. * * @param array $boot_dependencies Boot dependencies for the page. * @return array Updated boot dependencies. diff --git a/projects/packages/premium-analytics/widgets/react-query-dev-tool/render.tsx b/projects/packages/premium-analytics/widgets/react-query-dev-tool/render.tsx index bb882402993a..0ccc9ff9ecc0 100644 --- a/projects/packages/premium-analytics/widgets/react-query-dev-tool/render.tsx +++ b/projects/packages/premium-analytics/widgets/react-query-dev-tool/render.tsx @@ -9,17 +9,14 @@ import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools'; import styles from './style.module.css'; /** - * Renders the React Query Devtools as a dashboard widget. + * React Query Devtools as a dashboard widget. * - * The panel is bound to the dashboard's shared `queryClient` singleton through - * the explicit `client` prop rather than React context: this widget bundles its - * own copy of `@tanstack/react-query`, while `queryClient` comes from the shared - * data module. Passing the instance directly keeps the panel inspecting the very - * cache every other widget reads from, sidestepping the duplicate-context issue. + * Bound to the shared `queryClient` via the explicit `client` prop, not context: + * the widget bundles its own `@tanstack/react-query`, so passing the instance + * directly inspects the real cache and sidesteps the duplicate-context problem. * - * Visibility is gated server-side — the `jpa/react-query-dev-tool` type is - * removed from the widget list in production (see `src/widget-availability.php`), - * so this render module is never requested there. + * Server-gated: widget-availability.php drops `jpa/react-query-dev-tool` in + * production, so this module is never requested there. * * @return The rendered devtools panel. */ diff --git a/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.ts b/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.ts index c7fdea1d645b..9474d5142c6e 100644 --- a/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.ts +++ b/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.ts @@ -6,10 +6,9 @@ import { bug } from '@wordpress/icons'; /** * Widget type definition. * - * Developer tool, exposed only outside production. Availability is enforced - * server-side by the `jetpack_premium_analytics_widget_types` filter (see - * `src/widget-availability.php`); this metadata only describes the type for the - * dashboard's widget picker. + * Developer tool, gated to non-production server-side by the + * `jetpack_premium_analytics_widget_types` filter (widget-availability.php). + * This metadata only describes the type for the dashboard's widget picker. */ export default { name: 'jpa/react-query-dev-tool', From 3b11b65d973cc0c1482bf1b3e8c68d00f2183d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Fri, 26 Jun 2026 17:16:38 +0200 Subject: [PATCH 5/6] fix react-query-devtools widget i18n and deps wrap title in __(), link: data dep, declare react --- .../widgets/react-query-dev-tool/package.json | 6 ++++-- .../widgets/react-query-dev-tool/render.tsx | 12 +++++++----- .../widgets/react-query-dev-tool/widget.ts | 4 ++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/projects/packages/premium-analytics/widgets/react-query-dev-tool/package.json b/projects/packages/premium-analytics/widgets/react-query-dev-tool/package.json index de909fddd34d..a4069b1b65ac 100644 --- a/projects/packages/premium-analytics/widgets/react-query-dev-tool/package.json +++ b/projects/packages/premium-analytics/widgets/react-query-dev-tool/package.json @@ -4,8 +4,10 @@ "private": true, "type": "module", "dependencies": { - "@jetpack-premium-analytics/data": "workspace:*", + "@jetpack-premium-analytics/data": "link:../../packages/data", "@tanstack/react-query-devtools": "5.90.2", - "@wordpress/icons": "^13.0.0" + "@wordpress/i18n": "^6.9.0", + "@wordpress/icons": "^13.0.0", + "react": "18.3.1" } } diff --git a/projects/packages/premium-analytics/widgets/react-query-dev-tool/render.tsx b/projects/packages/premium-analytics/widgets/react-query-dev-tool/render.tsx index 0ccc9ff9ecc0..35d1c5fca3cb 100644 --- a/projects/packages/premium-analytics/widgets/react-query-dev-tool/render.tsx +++ b/projects/packages/premium-analytics/widgets/react-query-dev-tool/render.tsx @@ -11,16 +11,18 @@ import styles from './style.module.css'; /** * React Query Devtools as a dashboard widget. * - * Bound to the shared `queryClient` via the explicit `client` prop, not context: - * the widget bundles its own `@tanstack/react-query`, so passing the instance - * directly inspects the real cache and sidesteps the duplicate-context problem. + * Bound to the shared `queryClient` via the explicit `client` prop rather than + * the React Query context. The panel renders inside this widget's own lazily + * loaded module, so a context lookup is not guaranteed to resolve to the + * dashboard's provider; passing the singleton directly always targets the real + * cache. * * Server-gated: widget-availability.php drops `jpa/react-query-dev-tool` in * production, so this module is never requested there. * - * @return The rendered devtools panel. + * @return {React.ReactNode} The rendered devtools panel. */ -export default function ReactQueryDevTool() { +export default function ReactQueryDevTool(): React.ReactNode { return (
diff --git a/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.ts b/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.ts index 9474d5142c6e..b1286a25a698 100644 --- a/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.ts +++ b/projects/packages/premium-analytics/widgets/react-query-dev-tool/widget.ts @@ -1,6 +1,7 @@ /** * WordPress dependencies */ +import { __ } from '@wordpress/i18n'; import { bug } from '@wordpress/icons'; /** @@ -12,7 +13,6 @@ import { bug } from '@wordpress/icons'; */ export default { name: 'jpa/react-query-dev-tool', - title: 'React Query Devtools', + title: __( 'React Query Devtools', 'jetpack-premium-analytics' ), icon: bug, - presentation: 'full-bleed', }; From b5b9d6847b96d25f404c96938ee6d8503aafe8d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Fri, 26 Jun 2026 17:17:16 +0200 Subject: [PATCH 6/6] drop redundant presentation in locations widget widget.json is the canonical source read by the build --- projects/packages/premium-analytics/widgets/locations/widget.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/projects/packages/premium-analytics/widgets/locations/widget.ts b/projects/packages/premium-analytics/widgets/locations/widget.ts index afa65d07ba8a..49100a79c20c 100644 --- a/projects/packages/premium-analytics/widgets/locations/widget.ts +++ b/projects/packages/premium-analytics/widgets/locations/widget.ts @@ -23,7 +23,6 @@ export default { name: 'jpa/locations', title: __( 'Locations', 'jetpack-premium-analytics' ), icon: mapMarker, - presentation: 'full-bleed', attributes: [ { id: 'max',