From e9c3e9e135cadadfdb6fa004f2e6114dfbbd8c3e Mon Sep 17 00:00:00 2001 From: Yi Cai Date: Mon, 27 Jul 2026 23:16:30 -0400 Subject: [PATCH] fix(quickstart): make Quick start help menu item clickable Fixes: https://redhat.atlassian.net/browse/RHDHBUGS-3489 Signed-off-by: Yi Cai Co-authored-by: Cursor --- .../.changeset/clickable-menu-refix.md | 5 + .../quickstart/packages/app/package.json | 2 +- .../plugins/quickstart/package.json | 2 +- .../src/QuickstartHelpMenuItem.test.tsx | 88 +++++++++++++++++ .../quickstart/src/QuickstartHelpMenuItem.tsx | 60 ++++++++---- workspaces/quickstart/yarn.lock | 94 +++++++++---------- 6 files changed, 183 insertions(+), 68 deletions(-) create mode 100644 workspaces/quickstart/.changeset/clickable-menu-refix.md create mode 100644 workspaces/quickstart/plugins/quickstart/src/QuickstartHelpMenuItem.test.tsx diff --git a/workspaces/quickstart/.changeset/clickable-menu-refix.md b/workspaces/quickstart/.changeset/clickable-menu-refix.md new file mode 100644 index 00000000000..e7512caf6b4 --- /dev/null +++ b/workspaces/quickstart/.changeset/clickable-menu-refix.md @@ -0,0 +1,5 @@ +--- +'@red-hat-developer-hub/backstage-plugin-quickstart': patch +--- + +Fix Quick start help menu item not responding to clicks in the global-header dropdown diff --git a/workspaces/quickstart/packages/app/package.json b/workspaces/quickstart/packages/app/package.json index 88e24614089..22462aca5c9 100644 --- a/workspaces/quickstart/packages/app/package.json +++ b/workspaces/quickstart/packages/app/package.json @@ -44,7 +44,7 @@ "@mui/icons-material": "5.18.0", "@mui/material": "5.18.0", "@red-hat-developer-hub/backstage-plugin-app-react": "^0.1.0", - "@red-hat-developer-hub/backstage-plugin-global-header": "^1.21.0", + "@red-hat-developer-hub/backstage-plugin-global-header": "^1.21.2", "@red-hat-developer-hub/backstage-plugin-quickstart": "workspace:^", "@red-hat-developer-hub/backstage-plugin-theme": "^0.14.11", "material-icons": "^1.13.14", diff --git a/workspaces/quickstart/plugins/quickstart/package.json b/workspaces/quickstart/plugins/quickstart/package.json index e5ac7f62ffe..ebe9c51e0e2 100644 --- a/workspaces/quickstart/plugins/quickstart/package.json +++ b/workspaces/quickstart/plugins/quickstart/package.json @@ -46,7 +46,7 @@ "@mui/material": "5.18.0", "@patternfly/react-icons": "^6.5.0", "@red-hat-developer-hub/backstage-plugin-app-react": "^0.1.0", - "@red-hat-developer-hub/backstage-plugin-global-header": "^1.21.0", + "@red-hat-developer-hub/backstage-plugin-global-header": "^1.21.2", "@red-hat-developer-hub/backstage-plugin-theme": "^0.14.11", "react-use": "^17.6.0" }, diff --git a/workspaces/quickstart/plugins/quickstart/src/QuickstartHelpMenuItem.test.tsx b/workspaces/quickstart/plugins/quickstart/src/QuickstartHelpMenuItem.test.tsx new file mode 100644 index 00000000000..03dcca24174 --- /dev/null +++ b/workspaces/quickstart/plugins/quickstart/src/QuickstartHelpMenuItem.test.tsx @@ -0,0 +1,88 @@ +/* + * Copyright Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { renderInTestApp } from '@backstage/test-utils'; + +import { QuickstartHelpMenuItem } from './QuickstartHelpMenuItem'; +import { QUICKSTART_DRAWER_ID } from './const'; + +const mockToggleDrawer = jest.fn(); + +jest.mock('@red-hat-developer-hub/backstage-plugin-app-react', () => ({ + useAppDrawer: () => ({ + toggleDrawer: mockToggleDrawer, + isOpen: jest.fn(), + openDrawer: jest.fn(), + closeDrawer: jest.fn(), + }), +})); + +jest.mock('@red-hat-developer-hub/backstage-plugin-global-header', () => ({ + MenuItemLink: ({ title, icon }: { title?: string; icon?: string }) => ( + + {title} + + ), +})); + +describe('QuickstartHelpMenuItem', () => { + beforeEach(() => { + mockToggleDrawer.mockClear(); + }); + + it('renders as a menuitem with Quick start label', async () => { + await renderInTestApp( +
    + +
, + ); + + expect( + screen.getByRole('menuitem', { name: /Quick start/i }), + ).toBeInTheDocument(); + }); + + it('toggles the quickstart drawer and closes the menu on click', async () => { + const handleClose = jest.fn(); + const user = userEvent.setup(); + + await renderInTestApp( +
    + +
, + ); + + await user.click(screen.getByRole('menuitem', { name: /Quick start/i })); + + expect(mockToggleDrawer).toHaveBeenCalledWith(QUICKSTART_DRAWER_ID); + expect(handleClose).toHaveBeenCalled(); + }); + + it('forwards ref to the underlying MenuItem element', async () => { + const ref = { current: null as HTMLLIElement | null }; + + await renderInTestApp( +
    + +
, + ); + + expect(ref.current).toBeInstanceOf(HTMLLIElement); + expect(ref.current).toHaveAttribute('role', 'menuitem'); + }); +}); diff --git a/workspaces/quickstart/plugins/quickstart/src/QuickstartHelpMenuItem.tsx b/workspaces/quickstart/plugins/quickstart/src/QuickstartHelpMenuItem.tsx index c393a6c2d53..c03d30c3502 100644 --- a/workspaces/quickstart/plugins/quickstart/src/QuickstartHelpMenuItem.tsx +++ b/workspaces/quickstart/plugins/quickstart/src/QuickstartHelpMenuItem.tsx @@ -14,28 +14,50 @@ * limitations under the License. */ +import { forwardRef } from 'react'; +import type { Ref } from 'react'; + import { useAppDrawer } from '@red-hat-developer-hub/backstage-plugin-app-react'; -import { GlobalHeaderMenuItem } from '@red-hat-developer-hub/backstage-plugin-global-header/alpha'; +import { MenuItemLink } from '@red-hat-developer-hub/backstage-plugin-global-header'; + +import MenuItem from '@mui/material/MenuItem'; import { QUICKSTART_DRAWER_ID } from './const'; -export const QuickstartHelpMenuItem = ({ - handleClose, -}: { - handleClose?: () => void; -}) => { - const { toggleDrawer } = useAppDrawer(); +/** + * Help-dropdown menu item that toggles the Quick start drawer. + * + * Uses `forwardRef` so MUI Menu focus management can attach a ref to the + * underlying `MenuItem`. Avoids `GlobalHeaderMenuItem` without a `to` prop, + * which (in global-header 1.21.0) rendered `MenuItem component={Fragment}` and + * dropped click handlers / menuitem role. + */ +export const QuickstartHelpMenuItem = forwardRef( + function QuickstartHelpMenuItem( + { + handleClose, + }: { + handleClose?: () => void; + }, + ref: Ref, + ) { + const { toggleDrawer } = useAppDrawer(); - const handleClick = () => { - toggleDrawer(QUICKSTART_DRAWER_ID); - handleClose?.(); - }; + const handleClick = () => { + toggleDrawer(QUICKSTART_DRAWER_ID); + handleClose?.(); + }; - return ( - - ); -}; + return ( + + + + ); + }, +); diff --git a/workspaces/quickstart/yarn.lock b/workspaces/quickstart/yarn.lock index ffaa247f213..90f47b1797b 100644 --- a/workspaces/quickstart/yarn.lock +++ b/workspaces/quickstart/yarn.lock @@ -2840,7 +2840,7 @@ __metadata: languageName: node linkType: hard -"@backstage/core-components@npm:^0.18.11, @backstage/core-components@npm:^0.18.3, @backstage/core-components@npm:^0.18.8": +"@backstage/core-components@npm:^0.18.11, @backstage/core-components@npm:^0.18.3": version: 0.18.11 resolution: "@backstage/core-components@npm:0.18.11" dependencies: @@ -2898,7 +2898,7 @@ __metadata: languageName: node linkType: hard -"@backstage/core-plugin-api@npm:^1.10.9, @backstage/core-plugin-api@npm:^1.12.0, @backstage/core-plugin-api@npm:^1.12.4, @backstage/core-plugin-api@npm:^1.12.7": +"@backstage/core-plugin-api@npm:^1.10.9, @backstage/core-plugin-api@npm:^1.12.0, @backstage/core-plugin-api@npm:^1.12.7": version: 1.12.7 resolution: "@backstage/core-plugin-api@npm:1.12.7" dependencies: @@ -3951,7 +3951,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-notifications-common@npm:^0.2.1, @backstage/plugin-notifications-common@npm:^0.2.3": +"@backstage/plugin-notifications-common@npm:^0.2.3": version: 0.2.3 resolution: "@backstage/plugin-notifications-common@npm:0.2.3" dependencies: @@ -3961,7 +3961,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-notifications@npm:^0.5.15, @backstage/plugin-notifications@npm:^0.5.18": +"@backstage/plugin-notifications@npm:^0.5.18": version: 0.5.18 resolution: "@backstage/plugin-notifications@npm:0.5.18" dependencies: @@ -4406,7 +4406,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-backend-module-catalog@npm:^0.3.13, @backstage/plugin-search-backend-module-catalog@npm:^0.3.16": +"@backstage/plugin-search-backend-module-catalog@npm:^0.3.16": version: 0.3.16 resolution: "@backstage/plugin-search-backend-module-catalog@npm:0.3.16" dependencies: @@ -4424,7 +4424,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-backend-module-pg@npm:^0.5.53, @backstage/plugin-search-backend-module-pg@npm:^0.5.56": +"@backstage/plugin-search-backend-module-pg@npm:^0.5.56": version: 0.5.56 resolution: "@backstage/plugin-search-backend-module-pg@npm:0.5.56" dependencies: @@ -4438,7 +4438,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-backend-module-techdocs@npm:^0.4.12, @backstage/plugin-search-backend-module-techdocs@npm:^0.4.15": +"@backstage/plugin-search-backend-module-techdocs@npm:^0.4.15": version: 0.4.15 resolution: "@backstage/plugin-search-backend-module-techdocs@npm:0.4.15" dependencies: @@ -4475,7 +4475,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-backend@npm:^2.1.0, @backstage/plugin-search-backend@npm:^2.1.3": +"@backstage/plugin-search-backend@npm:^2.1.3": version: 2.1.3 resolution: "@backstage/plugin-search-backend@npm:2.1.3" dependencies: @@ -4497,7 +4497,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-common@npm:^1.2.21, @backstage/plugin-search-common@npm:^1.2.22, @backstage/plugin-search-common@npm:^1.2.24": +"@backstage/plugin-search-common@npm:^1.2.21, @backstage/plugin-search-common@npm:^1.2.24": version: 1.2.24 resolution: "@backstage/plugin-search-common@npm:1.2.24" dependencies: @@ -4507,7 +4507,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-react@npm:^1.10.0, @backstage/plugin-search-react@npm:^1.11.0, @backstage/plugin-search-react@npm:^1.11.5": +"@backstage/plugin-search-react@npm:^1.10.0, @backstage/plugin-search-react@npm:^1.11.5": version: 1.11.5 resolution: "@backstage/plugin-search-react@npm:1.11.5" dependencies: @@ -4537,7 +4537,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search@npm:^1.7.0, @backstage/plugin-search@npm:^1.7.5": +"@backstage/plugin-search@npm:^1.7.5": version: 1.7.5 resolution: "@backstage/plugin-search@npm:1.7.5" dependencies: @@ -4787,7 +4787,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-user-settings@npm:^0.9.1, @backstage/plugin-user-settings@npm:^0.9.4": +"@backstage/plugin-user-settings@npm:^0.9.4": version: 0.9.4 resolution: "@backstage/plugin-user-settings@npm:0.9.4" dependencies: @@ -4947,7 +4947,7 @@ __metadata: languageName: node linkType: hard -"@backstage/theme@npm:^0.7.2, @backstage/theme@npm:^0.7.3": +"@backstage/theme@npm:^0.7.3": version: 0.7.3 resolution: "@backstage/theme@npm:0.7.3" dependencies: @@ -9879,37 +9879,37 @@ __metadata: languageName: node linkType: hard -"@red-hat-developer-hub/backstage-plugin-global-header@npm:^1.17.1, @red-hat-developer-hub/backstage-plugin-global-header@npm:^1.21.0": - version: 1.21.0 - resolution: "@red-hat-developer-hub/backstage-plugin-global-header@npm:1.21.0" +"@red-hat-developer-hub/backstage-plugin-global-header@npm:^1.17.1, @red-hat-developer-hub/backstage-plugin-global-header@npm:^1.21.2": + version: 1.22.1 + resolution: "@red-hat-developer-hub/backstage-plugin-global-header@npm:1.22.1" dependencies: - "@backstage/catalog-model": "npm:^1.7.7" - "@backstage/core-components": "npm:^0.18.8" - "@backstage/core-plugin-api": "npm:^1.12.4" - "@backstage/frontend-plugin-api": "npm:^0.15.1" - "@backstage/plugin-app-react": "npm:^0.2.1" - "@backstage/plugin-catalog-react": "npm:^2.1.0" - "@backstage/plugin-notifications": "npm:^0.5.15" - "@backstage/plugin-notifications-common": "npm:^0.2.1" - "@backstage/plugin-search": "npm:^1.7.0" - "@backstage/plugin-search-backend": "npm:^2.1.0" - "@backstage/plugin-search-backend-module-catalog": "npm:^0.3.13" - "@backstage/plugin-search-backend-module-pg": "npm:^0.5.53" - "@backstage/plugin-search-backend-module-techdocs": "npm:^0.4.12" - "@backstage/plugin-search-common": "npm:^1.2.22" - "@backstage/plugin-search-react": "npm:^1.11.0" - "@backstage/plugin-signals-react": "npm:^0.0.20" - "@backstage/plugin-user-settings": "npm:^0.9.1" - "@backstage/theme": "npm:^0.7.2" + "@backstage/catalog-model": "npm:^1.9.0" + "@backstage/core-components": "npm:^0.18.11" + "@backstage/core-plugin-api": "npm:^1.12.7" + "@backstage/frontend-plugin-api": "npm:^0.17.2" + "@backstage/plugin-app-react": "npm:^0.2.4" + "@backstage/plugin-catalog-react": "npm:^3.1.0" + "@backstage/plugin-notifications": "npm:^0.5.18" + "@backstage/plugin-notifications-common": "npm:^0.2.3" + "@backstage/plugin-search": "npm:^1.7.5" + "@backstage/plugin-search-backend": "npm:^2.1.3" + "@backstage/plugin-search-backend-module-catalog": "npm:^0.3.16" + "@backstage/plugin-search-backend-module-pg": "npm:^0.5.56" + "@backstage/plugin-search-backend-module-techdocs": "npm:^0.4.15" + "@backstage/plugin-search-common": "npm:^1.2.24" + "@backstage/plugin-search-react": "npm:^1.11.5" + "@backstage/plugin-signals-react": "npm:^0.0.23" + "@backstage/plugin-user-settings": "npm:^0.9.4" + "@backstage/theme": "npm:^0.7.3" "@mui/icons-material": "npm:5.18.0" "@mui/material": "npm:5.18.0" "@mui/styled-engine": "npm:5.18.0" - "@scalprum/react-core": "npm:0.11.1" + "@scalprum/react-core": "npm:0.11.3" react-use: "npm:^17.5.0" peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 react-router-dom: ^6.0.0 - checksum: 10c0/9240736fb33969037b414c0281db6cd3f759643ca1c3f2803516b2d0701fd9b7acf78686277785a05afd395ad909bfc04b9c39baa131ca816f71093837b0aa3d + checksum: 10c0/5b795fa34feb5e9b345278a161f75bd006e4c2e187a5ceb4311875759ff9ad7455a65cd62113c35710b94e9789c587e6129df493c6508610878d93dbcfd11104 languageName: node linkType: hard @@ -9933,7 +9933,7 @@ __metadata: "@mui/material": "npm:5.18.0" "@patternfly/react-icons": "npm:^6.5.0" "@red-hat-developer-hub/backstage-plugin-app-react": "npm:^0.1.0" - "@red-hat-developer-hub/backstage-plugin-global-header": "npm:^1.21.0" + "@red-hat-developer-hub/backstage-plugin-global-header": "npm:^1.21.2" "@red-hat-developer-hub/backstage-plugin-theme": "npm:^0.14.11" "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^14.0.0" @@ -10585,27 +10585,27 @@ __metadata: languageName: node linkType: hard -"@scalprum/core@npm:^0.9.0": - version: 0.9.0 - resolution: "@scalprum/core@npm:0.9.0" +"@scalprum/core@npm:^0.9.1": + version: 0.9.3 + resolution: "@scalprum/core@npm:0.9.3" dependencies: "@openshift/dynamic-plugin-sdk": "npm:^5.0.1" tslib: "npm:^2.6.2" - checksum: 10c0/7eb93a9f32883f737203d12663d17248c57d68c90f23addb58b400eaa78aa015396f2655d16423190c195c6e75b3828b2dd84a7cd19706ebbd3e507fedc85b88 + checksum: 10c0/760c3f8d162163cad453d6f4d8494bf61f6b9ae28a624706887176673fe87147a0ed65d3a2e7fd8654b0e75eaf87914bd1357ce041d7c3c28372b24856027304 languageName: node linkType: hard -"@scalprum/react-core@npm:0.11.1": - version: 0.11.1 - resolution: "@scalprum/react-core@npm:0.11.1" +"@scalprum/react-core@npm:0.11.3": + version: 0.11.3 + resolution: "@scalprum/react-core@npm:0.11.3" dependencies: "@openshift/dynamic-plugin-sdk": "npm:^5.0.1" - "@scalprum/core": "npm:^0.9.0" + "@scalprum/core": "npm:^0.9.1" lodash: "npm:^4.17.0" peerDependencies: react: ">=16.8.0 || >=17.0.0 || ^18.0.0" react-dom: ">=16.8.0 || >=17.0.0 || ^18.0.0" - checksum: 10c0/61094ddfcef1547bb4f43ef794f555ee625e0aa1b5479ad1fa056fa00301fe96c7b96c53ef7c1ece8f8bc0be0859404507887f3109163c8a2025558ec30f364a + checksum: 10c0/6de3f4b624fb3e0cf1a7785154dfd6cd589809b0d44dbf0e065531729320ea918b17b673744fe60f4bdf3ef0602af87e857bf98109e03f6cc7634b4893269ff6 languageName: node linkType: hard @@ -14607,7 +14607,7 @@ __metadata: "@mui/material": "npm:5.18.0" "@playwright/test": "npm:1.61.1" "@red-hat-developer-hub/backstage-plugin-app-react": "npm:^0.1.0" - "@red-hat-developer-hub/backstage-plugin-global-header": "npm:^1.21.0" + "@red-hat-developer-hub/backstage-plugin-global-header": "npm:^1.21.2" "@red-hat-developer-hub/backstage-plugin-quickstart": "workspace:^" "@red-hat-developer-hub/backstage-plugin-theme": "npm:^0.14.11" "@testing-library/dom": "npm:^9.0.0"