From f1a6884b7fed433f175cd1ee82743a89d7adc5d2 Mon Sep 17 00:00:00 2001 From: Liam Sarsfield <43409125+LiamSarsfield@users.noreply.github.com> Date: Thu, 8 Oct 2026 11:51:08 +0100 Subject: [PATCH 1/5] My Jetpack: Wait for modules before showing Available empty state --- .../my-jetpack-tab-panel/features/content.tsx | 12 +++----- .../features/test/content.test.tsx | 28 ++++++++++++++++--- .../my-jetpack/changelog/fm-mj-cycle-loading | 5 ++++ 3 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 projects/packages/my-jetpack/changelog/fm-mj-cycle-loading diff --git a/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/content.tsx b/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/content.tsx index 657e80bfaf86..88898c423ad5 100644 --- a/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/content.tsx +++ b/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/content.tsx @@ -321,16 +321,12 @@ function FeaturesTabContent() { const stepOrder = useStepOrder( visible, openSlug, `${ filter }|${ search }|${ isLoading }` ); const openIndex = stepOrder.findIndex( feature => feature.slug === openSlug ); - // Neither read has anything to say yet: a seed-only catalog is not a failure to load - // one, and features whose modules are still in flight all read inactive, which would - // otherwise announce "nothing is active" on a site with plenty on. - // Only the status filters wait on the modules: a search or a plan filter is already - // a settled answer once the catalog is here, and an unreachable module read would - // otherwise leave those two with no message at all. - const onStatus = filter === 'active' || filter === 'inactive'; + // Module-dependent filters cannot report no matches until their modules arrive. + const onModules = + filter === 'active' || filter === 'inactive' || ( filter === 'available' && ! search.trim() ); const settling = ( mainFeatures.isPlaceholderData && mainFeatures.features.length === 0 ) || - ( onStatus && states.some( state => state.pending ) ); + ( onModules && states.some( state => state.pending ) ); return (
diff --git a/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/test/content.test.tsx b/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/test/content.test.tsx index a5a633c0dbcb..ce078630a6cd 100644 --- a/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/test/content.test.tsx +++ b/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/test/content.test.tsx @@ -141,12 +141,32 @@ describe( 'FeaturesContent', () => { expect( screen.queryByRole( 'heading' ) ).not.toBeInTheDocument(); } ); - it( 'still answers a search that matched nothing while modules are in flight', () => { - mockStates = [ { ...activeStats, pending: true, status: 'inactive' } as FeatureState ]; + it.each( [ 'all', 'available' ] )( + 'still answers a search that matched nothing while modules are in flight (filter: %s)', + filter => { + mockStates = [ { ...activeStats, pending: true, status: 'inactive' } as FeatureState ]; + + renderAt( `/features?filter=${ filter }&search=zzzz` ); + + expect( screen.getByRole( 'heading' ) ).toHaveTextContent( 'No features match “zzzz”.' ); + } + ); + + it( 'waits for modules before reporting an empty Available filter after a reload', () => { + mockIsLoading = true; + mockStates = [ { ...activeStats, pending: true } as FeatureState ]; + const { rerenderAt } = renderAt( '/features?filter=available' ); - renderAt( '/features?search=zzzz' ); + expect( screen.queryByText( 'No features found.' ) ).not.toBeInTheDocument(); - expect( screen.getByRole( 'heading' ) ).toHaveTextContent( 'No features match “zzzz”.' ); + mockIsLoading = false; + mockStates = [ activeStats ]; + rerenderAt(); + expect( screen.getByText( 'grid card' ) ).toBeInTheDocument(); + + mockStates = [ { ...activeStats, unavailableReason: 'Unavailable' } ]; + rerenderAt(); + expect( screen.getByText( 'No features found.' ) ).toBeInTheDocument(); } ); it( 'reports an empty catalog as a failure rather than a site with no features', () => { diff --git a/projects/packages/my-jetpack/changelog/fm-mj-cycle-loading b/projects/packages/my-jetpack/changelog/fm-mj-cycle-loading new file mode 100644 index 000000000000..68dbabdb7665 --- /dev/null +++ b/projects/packages/my-jetpack/changelog/fm-mj-cycle-loading @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Avoid an empty state while the unreleased Available filter loads modules. + + From d3533ab1ecd1da2b7faa7c7c1c3d6ba0717b6d30 Mon Sep 17 00:00:00 2001 From: Liam Sarsfield <43409125+LiamSarsfield@users.noreply.github.com> Date: Thu, 8 Oct 2026 11:52:15 +0100 Subject: [PATCH 2/5] My Jetpack: Exclude the legacy custom content types module --- .../features/test/use-more-features.test.ts | 18 ++++++++++++++++++ .../features/use-more-features.ts | 1 + .../my-jetpack/changelog/fm-mj-cycle-cpt | 4 ++++ .../plugins/jetpack/changelog/fm-mj-cycle-cpt | 4 ++++ 4 files changed, 27 insertions(+) create mode 100644 projects/packages/my-jetpack/changelog/fm-mj-cycle-cpt create mode 100644 projects/plugins/jetpack/changelog/fm-mj-cycle-cpt diff --git a/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/test/use-more-features.test.ts b/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/test/use-more-features.test.ts index 9a99ed72698c..26a4057a7588 100644 --- a/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/test/use-more-features.test.ts +++ b/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/test/use-more-features.test.ts @@ -89,6 +89,24 @@ describe( 'groupMoreFeatures', () => { expect( monitor.isSwitching ).toBe( true ); } ); + it.each( [ false, true ] )( + 'excludes the helper-owned custom content types module (active: %s)', + activated => { + const groupedWithCpt = groupMoreFeatures( + [], + [ { label: 'Writing', modules: [ 'custom-content-types', 'monitor' ] } ], + { + 'custom-content-types': mod( 'custom-content-types', { activated } ), + monitor: mod( 'monitor' ), + }, + {}, + {} + ); + + expect( slugsOf( groupedWithCpt ) ).toEqual( [ [ 'Writing', [ 'monitor' ] ] ] ); + } + ); + it( 'filters by status and hides modules under plan and essential filters', () => { const filtered = ( filter: Parameters< typeof filterMoreFeatures >[ 1 ] ) => filterMoreFeatures( grouped, filter, '' ).flatMap( group => diff --git a/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/use-more-features.ts b/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/use-more-features.ts index b98400940fb7..d6dcebd6796b 100644 --- a/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/use-more-features.ts +++ b/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/use-more-features.ts @@ -100,6 +100,7 @@ export function groupMoreFeatures( for ( const $module of Object.values( modules ).sort( compareModulesByName ) ) { if ( $module.module === 'vaultpress' || + $module.module === 'custom-content-types' || covered.has( $module.module ) || hidden.has( $module.module ) ) { diff --git a/projects/packages/my-jetpack/changelog/fm-mj-cycle-cpt b/projects/packages/my-jetpack/changelog/fm-mj-cycle-cpt new file mode 100644 index 000000000000..4e3c4295d230 --- /dev/null +++ b/projects/packages/my-jetpack/changelog/fm-mj-cycle-cpt @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Features: Hide the legacy Custom Content Types module. diff --git a/projects/plugins/jetpack/changelog/fm-mj-cycle-cpt b/projects/plugins/jetpack/changelog/fm-mj-cycle-cpt new file mode 100644 index 000000000000..d51c45b2bc19 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fm-mj-cycle-cpt @@ -0,0 +1,4 @@ +Significance: patch +Type: bugfix + +My Jetpack: Hide the legacy Custom Content Types module from Features. From dea6b8c871df3e8e4192920424feed9c3d9610ef Mon Sep 17 00:00:00 2001 From: Liam Sarsfield <43409125+LiamSarsfield@users.noreply.github.com> Date: Thu, 8 Oct 2026 11:56:21 +0100 Subject: [PATCH 3/5] My Jetpack: Allow scrolling to the connection screen footer --- .../_inc/components/connection-screen/index.tsx | 2 +- projects/packages/my-jetpack/_inc/test/app.test.jsx | 10 ++++++++++ .../packages/my-jetpack/changelog/fm-mj-cycle-footer | 4 ++++ .../packages/my-jetpack/routes/dashboard/route.scss | 4 ++++ projects/plugins/backup/changelog/fm-mj-cycle-footer | 4 ++++ projects/plugins/boost/changelog/fm-mj-cycle-footer | 4 ++++ projects/plugins/jetpack/changelog/fm-mj-cycle-footer | 4 ++++ projects/plugins/protect/changelog/fm-mj-cycle-footer | 4 ++++ projects/plugins/search/changelog/fm-mj-cycle-footer | 4 ++++ projects/plugins/social/changelog/fm-mj-cycle-footer | 4 ++++ projects/plugins/stats/changelog/fm-mj-cycle-footer | 4 ++++ .../plugins/videopress/changelog/fm-mj-cycle-footer | 4 ++++ 12 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 projects/packages/my-jetpack/changelog/fm-mj-cycle-footer create mode 100644 projects/plugins/backup/changelog/fm-mj-cycle-footer create mode 100644 projects/plugins/boost/changelog/fm-mj-cycle-footer create mode 100644 projects/plugins/jetpack/changelog/fm-mj-cycle-footer create mode 100644 projects/plugins/protect/changelog/fm-mj-cycle-footer create mode 100644 projects/plugins/search/changelog/fm-mj-cycle-footer create mode 100644 projects/plugins/social/changelog/fm-mj-cycle-footer create mode 100644 projects/plugins/stats/changelog/fm-mj-cycle-footer create mode 100644 projects/plugins/videopress/changelog/fm-mj-cycle-footer diff --git a/projects/packages/my-jetpack/_inc/components/connection-screen/index.tsx b/projects/packages/my-jetpack/_inc/components/connection-screen/index.tsx index 9fb6f13fc70d..33a5377deab5 100644 --- a/projects/packages/my-jetpack/_inc/components/connection-screen/index.tsx +++ b/projects/packages/my-jetpack/_inc/components/connection-screen/index.tsx @@ -15,7 +15,7 @@ const ConnectionScreen: FC = () => { const returnToPage = useMyJetpackReturnToPage(); const { apiRoot, apiNonce, registrationNonce } = useMyJetpackConnection(); return ( - + { expect( screen.getByTestId( 'my-jetpack-screen' ) ).toBeInTheDocument(); } ); + it( 'gives the connection route its scoped scrolling layout', () => { + window.location.hash = '#/connection'; + + render( ); + + const connect = screen.getByRole( 'button', { name: 'Connect your user account' } ); + // eslint-disable-next-line testing-library/no-node-access -- The scrolling layout belongs to the AdminPage root. + expect( connect.closest( '.jp-admin-page' ) ).toHaveClass( 'jp-my-jetpack-connection' ); + } ); + it( 'renders the partner coupon screen in place of the routes', () => { window.location.hash = ''; window.myJetpackInitialState.partnerCoupon = { diff --git a/projects/packages/my-jetpack/changelog/fm-mj-cycle-footer b/projects/packages/my-jetpack/changelog/fm-mj-cycle-footer new file mode 100644 index 000000000000..376e417fb702 --- /dev/null +++ b/projects/packages/my-jetpack/changelog/fm-mj-cycle-footer @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Connection: Make the connection screen's Connect button and footer links reachable on shorter screens. diff --git a/projects/packages/my-jetpack/routes/dashboard/route.scss b/projects/packages/my-jetpack/routes/dashboard/route.scss index 92727adef7f3..d69d280d01b3 100644 --- a/projects/packages/my-jetpack/routes/dashboard/route.scss +++ b/projects/packages/my-jetpack/routes/dashboard/route.scss @@ -8,6 +8,10 @@ body:is(.jetpack_page_my-jetpack, .admin_page_my-jetpack):not(.jetpack-admin-ful @include jetpack-admin-page-layout-wp-build; + .jp-admin-page.jp-my-jetpack-connection { + overflow: auto; + } + // The Jetpack font stack onboarding-screen/styles.module.scss sets on `body`. // Inlined, not imported — that file is a component module, not a partial. font-family: diff --git a/projects/plugins/backup/changelog/fm-mj-cycle-footer b/projects/plugins/backup/changelog/fm-mj-cycle-footer new file mode 100644 index 000000000000..5f933b5b606e --- /dev/null +++ b/projects/plugins/backup/changelog/fm-mj-cycle-footer @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +My Jetpack: Make the connection screen's Connect button and footer links reachable on shorter screens. diff --git a/projects/plugins/boost/changelog/fm-mj-cycle-footer b/projects/plugins/boost/changelog/fm-mj-cycle-footer new file mode 100644 index 000000000000..5f933b5b606e --- /dev/null +++ b/projects/plugins/boost/changelog/fm-mj-cycle-footer @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +My Jetpack: Make the connection screen's Connect button and footer links reachable on shorter screens. diff --git a/projects/plugins/jetpack/changelog/fm-mj-cycle-footer b/projects/plugins/jetpack/changelog/fm-mj-cycle-footer new file mode 100644 index 000000000000..5debe5b5bb89 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fm-mj-cycle-footer @@ -0,0 +1,4 @@ +Significance: patch +Type: bugfix + +My Jetpack: Make the connection screen's Connect button and footer links reachable on shorter screens. diff --git a/projects/plugins/protect/changelog/fm-mj-cycle-footer b/projects/plugins/protect/changelog/fm-mj-cycle-footer new file mode 100644 index 000000000000..5f933b5b606e --- /dev/null +++ b/projects/plugins/protect/changelog/fm-mj-cycle-footer @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +My Jetpack: Make the connection screen's Connect button and footer links reachable on shorter screens. diff --git a/projects/plugins/search/changelog/fm-mj-cycle-footer b/projects/plugins/search/changelog/fm-mj-cycle-footer new file mode 100644 index 000000000000..5f933b5b606e --- /dev/null +++ b/projects/plugins/search/changelog/fm-mj-cycle-footer @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +My Jetpack: Make the connection screen's Connect button and footer links reachable on shorter screens. diff --git a/projects/plugins/social/changelog/fm-mj-cycle-footer b/projects/plugins/social/changelog/fm-mj-cycle-footer new file mode 100644 index 000000000000..5f933b5b606e --- /dev/null +++ b/projects/plugins/social/changelog/fm-mj-cycle-footer @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +My Jetpack: Make the connection screen's Connect button and footer links reachable on shorter screens. diff --git a/projects/plugins/stats/changelog/fm-mj-cycle-footer b/projects/plugins/stats/changelog/fm-mj-cycle-footer new file mode 100644 index 000000000000..5f933b5b606e --- /dev/null +++ b/projects/plugins/stats/changelog/fm-mj-cycle-footer @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +My Jetpack: Make the connection screen's Connect button and footer links reachable on shorter screens. diff --git a/projects/plugins/videopress/changelog/fm-mj-cycle-footer b/projects/plugins/videopress/changelog/fm-mj-cycle-footer new file mode 100644 index 000000000000..5f933b5b606e --- /dev/null +++ b/projects/plugins/videopress/changelog/fm-mj-cycle-footer @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +My Jetpack: Make the connection screen's Connect button and footer links reachable on shorter screens. From 9e253967966a8d479357ed6f4951f8f453278d68 Mon Sep 17 00:00:00 2001 From: Liam Sarsfield <43409125+LiamSarsfield@users.noreply.github.com> Date: Fri, 9 Oct 2026 09:42:25 +0100 Subject: [PATCH 4/5] no-mistakes(document): Correct Features documentation and changelog whitespace --- .../my-jetpack-tab-panel/features/use-more-features.ts | 2 +- projects/packages/my-jetpack/changelog/fm-mj-cycle-loading | 2 -- projects/packages/my-jetpack/changelog/fm-mj-offline-payload | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/use-more-features.ts b/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/use-more-features.ts index d6dcebd6796b..9c01308f1e4a 100644 --- a/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/use-more-features.ts +++ b/projects/packages/my-jetpack/_inc/components/my-jetpack-tab-panel/features/use-more-features.ts @@ -77,7 +77,7 @@ function getStateModule( state: FeatureState ): MyJetpackModule | null { * its own card is still that card's, and must not surface here instead. * @param requested - Switch key to the value asked of it. * @param hidden - Modules that do not apply to the site, left out entirely. - * @return Non-empty groups by label, each sorted by name, with every leftover module under Other, last. + * @return Non-empty groups by label, each sorted by name, with eligible ungrouped modules under Other, last. */ export function groupMoreFeatures( features: MainFeature[], diff --git a/projects/packages/my-jetpack/changelog/fm-mj-cycle-loading b/projects/packages/my-jetpack/changelog/fm-mj-cycle-loading index 68dbabdb7665..159bf7b4b95b 100644 --- a/projects/packages/my-jetpack/changelog/fm-mj-cycle-loading +++ b/projects/packages/my-jetpack/changelog/fm-mj-cycle-loading @@ -1,5 +1,3 @@ Significance: patch Type: changed Comment: Avoid an empty state while the unreleased Available filter loads modules. - - diff --git a/projects/packages/my-jetpack/changelog/fm-mj-offline-payload b/projects/packages/my-jetpack/changelog/fm-mj-offline-payload index cad2dffb5fc4..426fc5bf8d19 100644 --- a/projects/packages/my-jetpack/changelog/fm-mj-offline-payload +++ b/projects/packages/my-jetpack/changelog/fm-mj-offline-payload @@ -1,5 +1,3 @@ Significance: patch Type: changed Comment: Prepare a gated offline Features seed; offline entry remains disabled. - - From 5e789b16e4ba67c3b99da94f973606d2a005ee7c Mon Sep 17 00:00:00 2001 From: Liam Sarsfield <43409125+LiamSarsfield@users.noreply.github.com> Date: Fri, 9 Oct 2026 12:09:29 +0100 Subject: [PATCH 5/5] My Jetpack: Restore the base offline payload changelog --- projects/packages/my-jetpack/changelog/fm-mj-offline-payload | 2 ++ 1 file changed, 2 insertions(+) diff --git a/projects/packages/my-jetpack/changelog/fm-mj-offline-payload b/projects/packages/my-jetpack/changelog/fm-mj-offline-payload index 426fc5bf8d19..cad2dffb5fc4 100644 --- a/projects/packages/my-jetpack/changelog/fm-mj-offline-payload +++ b/projects/packages/my-jetpack/changelog/fm-mj-offline-payload @@ -1,3 +1,5 @@ Significance: patch Type: changed Comment: Prepare a gated offline Features seed; offline entry remains disabled. + +