From 4b7d2c50e3706d5f501d191cecc81c90652ea85e Mon Sep 17 00:00:00 2001 From: Baluduvamsi2006 Date: Mon, 2 Mar 2026 02:00:20 +0530 Subject: [PATCH 1/6] feat_add_global_spotlight --- e2e/tests/consumer_groups.list.spec.ts | 1 + e2e/tests/consumers.list.spec.ts | 1 + .../hot-path.upstream-service-route.spec.ts | 3 ++ e2e/tests/plugin_configs.list.spec.ts | 1 + e2e/tests/protos.list.spec.ts | 1 + e2e/tests/routes.list.spec.ts | 1 + .../services.crud-required-fields.spec.ts | 1 + e2e/tests/services.list.spec.ts | 1 + e2e/tests/stream_routes.list.spec.ts | 1 + e2e/tests/upstreams.list.spec.ts | 1 + package.json | 6 ++- pnpm-lock.yaml | 28 +++++++++++ src/components/GlobalSpotlight.tsx | 48 +++++++++++++++++++ src/main.tsx | 1 + src/routes/__root.tsx | 2 + 15 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/components/GlobalSpotlight.tsx diff --git a/e2e/tests/consumer_groups.list.spec.ts b/e2e/tests/consumer_groups.list.spec.ts index a9dd217b83..b2ff734453 100644 --- a/e2e/tests/consumer_groups.list.spec.ts +++ b/e2e/tests/consumer_groups.list.spec.ts @@ -47,6 +47,7 @@ const consumerGroups: APISIXType['ConsumerGroupPut'][] = Array.from( test.describe('page and page_size should work correctly', () => { test.describe.configure({ mode: 'serial' }); + test.beforeAll(async () => { await deleteAllConsumerGroups(e2eReq); await Promise.all( diff --git a/e2e/tests/consumers.list.spec.ts b/e2e/tests/consumers.list.spec.ts index 43ff5ce9b5..685a770861 100644 --- a/e2e/tests/consumers.list.spec.ts +++ b/e2e/tests/consumers.list.spec.ts @@ -49,6 +49,7 @@ const consumers: APISIXType['ConsumerPut'][] = Array.from({ length: 11 }, (_, i) test.describe('page and page_size should work correctly', () => { test.describe.configure({ mode: 'serial' }); + test.beforeAll(async () => { await deleteAllConsumers(e2eReq); await Promise.all(consumers.map((d) => putConsumerReq(e2eReq, d))); diff --git a/e2e/tests/hot-path.upstream-service-route.spec.ts b/e2e/tests/hot-path.upstream-service-route.spec.ts index 60521f3bcd..811c870cf4 100644 --- a/e2e/tests/hot-path.upstream-service-route.spec.ts +++ b/e2e/tests/hot-path.upstream-service-route.spec.ts @@ -61,6 +61,7 @@ test('can create upstream -> service -> route', async ({ page }) => { scheme: 'https', nodes: [{ host: 'httpbin.org', port: 443 }], }; + await test.step('create upstream', async () => { // Navigate to the upstream list page await upstreamsPom.toIndex(page); @@ -158,6 +159,7 @@ test('can create upstream -> service -> route', async ({ page }) => { }, }, } satisfies Partial; + await test.step('create service', async () => { // upstream id should be set expect(service.upstream_id).not.toBeUndefined(); @@ -275,6 +277,7 @@ test('can create upstream -> service -> route', async ({ page }) => { }, }, }; + await test.step('create route', async () => { // service id should be set expect(route.service_id).not.toBeUndefined(); diff --git a/e2e/tests/plugin_configs.list.spec.ts b/e2e/tests/plugin_configs.list.spec.ts index f4a67a396b..2806b042d4 100644 --- a/e2e/tests/plugin_configs.list.spec.ts +++ b/e2e/tests/plugin_configs.list.spec.ts @@ -67,6 +67,7 @@ const pluginConfigs: APISIXType['PluginConfigPut'][] = Array.from( test.describe('page and page_size should work correctly', () => { test.describe.configure({ mode: 'serial' }); + test.beforeAll(async () => { await deleteAllPluginConfigs(e2eReq); await Promise.all(pluginConfigs.map((d) => putPluginConfigReq(e2eReq, d))); diff --git a/e2e/tests/protos.list.spec.ts b/e2e/tests/protos.list.spec.ts index 2f17897e08..cf9222e34b 100644 --- a/e2e/tests/protos.list.spec.ts +++ b/e2e/tests/protos.list.spec.ts @@ -55,6 +55,7 @@ message TestMessage${i + 1} { test.describe('page and page_size should work correctly', () => { test.describe.configure({ mode: 'serial' }); + test.beforeAll(async () => { // Delete all existing protos const existingProtos = await e2eReq diff --git a/e2e/tests/routes.list.spec.ts b/e2e/tests/routes.list.spec.ts index b6f171d920..7a999cd12d 100644 --- a/e2e/tests/routes.list.spec.ts +++ b/e2e/tests/routes.list.spec.ts @@ -63,6 +63,7 @@ const routes: APISIXType['Route'][] = Array.from({ length: 11 }, (_, i) => ({ test.describe('page and page_size should work correctly', () => { test.describe.configure({ mode: 'serial' }); + test.beforeAll(async () => { await deleteAllRoutes(e2eReq); await Promise.all(routes.map((d) => putRouteReq(e2eReq, d))); diff --git a/e2e/tests/services.crud-required-fields.spec.ts b/e2e/tests/services.crud-required-fields.spec.ts index f0f17b3eec..ede2ac2c6b 100644 --- a/e2e/tests/services.crud-required-fields.spec.ts +++ b/e2e/tests/services.crud-required-fields.spec.ts @@ -41,6 +41,7 @@ test('should CRUD service with required fields', async ({ page }) => { await servicesPom.getAddServiceBtn(page).click(); await servicesPom.isAddPage(page); + await test.step('submit with required fields', async () => { await uiFillServiceRequiredFields(page, { name: serviceName, diff --git a/e2e/tests/services.list.spec.ts b/e2e/tests/services.list.spec.ts index ceb3972951..ef45cba464 100644 --- a/e2e/tests/services.list.spec.ts +++ b/e2e/tests/services.list.spec.ts @@ -55,6 +55,7 @@ const services: APISIXType['Service'][] = Array.from({ length: 11 }, (_, i) => ( test.describe('page and page_size should work correctly', () => { test.describe.configure({ mode: 'serial' }); + test.beforeAll(async () => { await deleteAllServices(e2eReq); await Promise.all( diff --git a/e2e/tests/stream_routes.list.spec.ts b/e2e/tests/stream_routes.list.spec.ts index 5363b6fe53..28f9c5cc28 100644 --- a/e2e/tests/stream_routes.list.spec.ts +++ b/e2e/tests/stream_routes.list.spec.ts @@ -58,6 +58,7 @@ const streamRoutes: APISIXType['StreamRoute'][] = Array.from( test.describe('page and page_size should work correctly', () => { test.describe.configure({ mode: 'serial' }); + test.beforeAll(async () => { await deleteAllStreamRoutes(e2eReq); await Promise.all( diff --git a/e2e/tests/upstreams.list.spec.ts b/e2e/tests/upstreams.list.spec.ts index 4119da66d1..99a252ef0c 100644 --- a/e2e/tests/upstreams.list.spec.ts +++ b/e2e/tests/upstreams.list.spec.ts @@ -60,6 +60,7 @@ const upstreams: APISIXType['Upstream'][] = Array.from( test.describe('page and page_size should work correctly', () => { test.describe.configure({ mode: 'serial' }); + test.beforeAll(async () => { await deleteAllUpstreams(e2eReq); await Promise.all(upstreams.map((d) => putUpstreamReq(e2eReq, d))); diff --git a/package.json b/package.json index ff6720917a..21451942d5 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@mantine/hooks": "^8.0.0", "@mantine/modals": "^8.0.0", "@mantine/notifications": "^8.0.0", + "@mantine/spotlight": "^8.3.15", "@monaco-editor/react": "^4.7.0", "@tanstack/react-query": "^5.74.4", "@tanstack/react-router": "^1.116.0", @@ -102,11 +103,12 @@ "overrides": { "lodash": ">=4.17.21", "minimatch": ">=3.0.5", - "@swc/core": "1.10.0" + "@swc/core": "1.10.0", + "@iconify/utils": "3.0.1" }, "onlyBuiltDependencies": [ "@swc/core", "esbuild" ] } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 518d990b5b..155d1fbb00 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,6 +34,9 @@ importers: '@mantine/notifications': specifier: ^8.0.0 version: 8.3.12(@mantine/core@8.3.12(@mantine/hooks@8.3.12(react@19.1.0))(@types/react@19.2.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@8.3.12(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/spotlight': + specifier: ^8.3.15 + version: 8.3.15(@mantine/core@8.3.12(@mantine/hooks@8.3.12(react@19.1.0))(@types/react@19.2.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@8.3.12(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@monaco-editor/react': specifier: ^4.7.0 version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -1042,11 +1045,24 @@ packages: react: ^18.x || ^19.x react-dom: ^18.x || ^19.x + '@mantine/spotlight@8.3.15': + resolution: {integrity: sha512-zKssw/6eBmkY+1sGAgD8Vpy7dU5MXcY/cpvfr65SfIknRljKM9D4Z9TflzgIpxEdhvozls06MPcxj/pZkGpELQ==} + peerDependencies: + '@mantine/core': 8.3.15 + '@mantine/hooks': 8.3.15 + react: ^18.x || ^19.x + react-dom: ^18.x || ^19.x + '@mantine/store@8.3.12': resolution: {integrity: sha512-EC4eIKpm5s7neMbBrWsP6jGKLqrzHf63Ao3penYr7fn25dFXdbXZYw+IG8GYzxOC4yG61b2zTS+bpy5+vwzXpw==} peerDependencies: react: ^18.x || ^19.x + '@mantine/store@8.3.15': + resolution: {integrity: sha512-wdx91a73dM2G02YPIZ9i5UXPWfvjdf3qPAwSGnSsBFQg5uM/5CcPAOOQwlYIkvX1edUA5BFOk/4IjpEXSYUDeQ==} + peerDependencies: + react: ^18.x || ^19.x + '@monaco-editor/loader@1.7.0': resolution: {integrity: sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==} @@ -5041,10 +5057,22 @@ snapshots: react-dom: 19.1.0(react@19.1.0) react-transition-group: 4.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/spotlight@8.3.15(@mantine/core@8.3.12(@mantine/hooks@8.3.12(react@19.1.0))(@types/react@19.2.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@8.3.12(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@mantine/core': 8.3.12(@mantine/hooks@8.3.12(react@19.1.0))(@types/react@19.2.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/hooks': 8.3.12(react@19.1.0) + '@mantine/store': 8.3.15(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + '@mantine/store@8.3.12(react@19.1.0)': dependencies: react: 19.1.0 + '@mantine/store@8.3.15(react@19.1.0)': + dependencies: + react: 19.1.0 + '@monaco-editor/loader@1.7.0': dependencies: state-local: 1.0.7 diff --git a/src/components/GlobalSpotlight.tsx b/src/components/GlobalSpotlight.tsx new file mode 100644 index 0000000000..dcdb6416c3 --- /dev/null +++ b/src/components/GlobalSpotlight.tsx @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 { Spotlight } from '@mantine/spotlight'; +import { useNavigate } from '@tanstack/react-router'; +import { useTranslation } from 'react-i18next'; + +import { navRoutes } from '@/config/navRoutes'; +import IconSearch from '~icons/material-symbols/search'; + +export const GlobalSpotlight = () => { + const { t } = useTranslation(); + const navigate = useNavigate(); + + const actions = navRoutes.map((route) => ({ + id: route.to, + label: t(`sources.${route.label}`), + description: `Jump to ${t(`sources.${route.label}`)} dashboard`, + onClick: () => { + navigate({ to: route.to }); + }, + })); + + return ( + , + placeholder: 'Search resources... (Ctrl + K)', + }} + /> + ); +}; diff --git a/src/main.tsx b/src/main.tsx index 7dacffa7e6..c1c5033996 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -16,6 +16,7 @@ */ import '@mantine/core/styles.css'; import '@mantine/notifications/styles.css'; +import '@mantine/spotlight/styles.css'; import './styles/global.css'; import { createTheme, MantineProvider } from '@mantine/core'; diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index bcce5948eb..1b2ee49ab3 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -21,6 +21,7 @@ import { createRootRoute, HeadContent, Outlet } from '@tanstack/react-router'; import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'; import { I18nextProvider } from 'react-i18next'; +import { GlobalSpotlight } from '@/components/GlobalSpotlight'; import { Header } from '@/components/Header'; import { Navbar } from '@/components/Navbar'; import { SettingsModal } from '@/components/page/SettingsModal'; @@ -55,6 +56,7 @@ const Root = () => { + ); }; From 09154f2630dfb33c403ca5baf71b0bfad92fc0b7 Mon Sep 17 00:00:00 2001 From: Baluduvamsi2006 Date: Mon, 2 Mar 2026 02:12:57 +0530 Subject: [PATCH 2/6] "fix_spotlight_ui_refinements" --- src/components/GlobalSpotlight.tsx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/components/GlobalSpotlight.tsx b/src/components/GlobalSpotlight.tsx index dcdb6416c3..16758b21cb 100644 --- a/src/components/GlobalSpotlight.tsx +++ b/src/components/GlobalSpotlight.tsx @@ -14,11 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Spotlight } from '@mantine/spotlight'; +import { ActionIcon } from '@mantine/core'; +import { Spotlight, spotlight } from '@mantine/spotlight'; import { useNavigate } from '@tanstack/react-router'; import { useTranslation } from 'react-i18next'; import { navRoutes } from '@/config/navRoutes'; +import IconClose from '~icons/material-symbols/close'; +import IconDashboard from '~icons/material-symbols/dashboard-outline'; import IconSearch from '~icons/material-symbols/search'; export const GlobalSpotlight = () => { @@ -29,6 +32,7 @@ export const GlobalSpotlight = () => { id: route.to, label: t(`sources.${route.label}`), description: `Jump to ${t(`sources.${route.label}`)} dashboard`, + leftSection: , onClick: () => { navigate({ to: route.to }); }, @@ -39,9 +43,21 @@ export const GlobalSpotlight = () => { actions={actions} nothingFound={t('noData')} highlightQuery + scrollAreaProps={{ type: 'scroll', offsetScrollbars: true, mah: 400 }} searchProps={{ - leftSection: , + leftSection: , placeholder: 'Search resources... (Ctrl + K)', + rightSection: ( + spotlight.close()} + > + + + ), }} /> ); From 952282a95108e2a53fedd88fa4b4c6b386cff056 Mon Sep 17 00:00:00 2001 From: Baluduvamsi2006 Date: Mon, 2 Mar 2026 02:16:10 +0530 Subject: [PATCH 3/6] "fix_close_button_ux" --- src/components/GlobalSpotlight.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/GlobalSpotlight.tsx b/src/components/GlobalSpotlight.tsx index 16758b21cb..83fbf7c2ec 100644 --- a/src/components/GlobalSpotlight.tsx +++ b/src/components/GlobalSpotlight.tsx @@ -43,19 +43,20 @@ export const GlobalSpotlight = () => { actions={actions} nothingFound={t('noData')} highlightQuery - scrollAreaProps={{ type: 'scroll', offsetScrollbars: true, mah: 400 }} + scrollAreaProps={{ type: 'always', offsetScrollbars: true, mah: 400 }} searchProps={{ leftSection: , placeholder: 'Search resources... (Ctrl + K)', rightSection: ( spotlight.close()} + variant="subtle" + color="gray" + onMouseDown={(e) => { + e.preventDefault(); + spotlight.close(); + }} > - + ), }} From 05317696c9ce344c141ec2583398894f60536160 Mon Sep 17 00:00:00 2001 From: Baluduvamsi2006 Date: Mon, 2 Mar 2026 02:20:08 +0530 Subject: [PATCH 4/6] added close button --- src/components/GlobalSpotlight.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/GlobalSpotlight.tsx b/src/components/GlobalSpotlight.tsx index 83fbf7c2ec..53b8e42ac7 100644 --- a/src/components/GlobalSpotlight.tsx +++ b/src/components/GlobalSpotlight.tsx @@ -47,11 +47,12 @@ export const GlobalSpotlight = () => { searchProps={{ leftSection: , placeholder: 'Search resources... (Ctrl + K)', + rightSectionPointerEvents: 'all', rightSection: ( { + onClick={(e) => { e.preventDefault(); spotlight.close(); }} From 2d094f36ee0ddcfc02d618dcd00d3c73d78f5c96 Mon Sep 17 00:00:00 2001 From: BALUDU VAMSI Date: Fri, 6 Mar 2026 06:58:59 +0530 Subject: [PATCH 5/6] Update src/components/GlobalSpotlight.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/components/GlobalSpotlight.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/GlobalSpotlight.tsx b/src/components/GlobalSpotlight.tsx index 53b8e42ac7..07431562f3 100644 --- a/src/components/GlobalSpotlight.tsx +++ b/src/components/GlobalSpotlight.tsx @@ -31,7 +31,7 @@ export const GlobalSpotlight = () => { const actions = navRoutes.map((route) => ({ id: route.to, label: t(`sources.${route.label}`), - description: `Jump to ${t(`sources.${route.label}`)} dashboard`, + description: t('spotlight.jumpToDashboard', { resource: t(`sources.${route.label}`) }), leftSection: , onClick: () => { navigate({ to: route.to }); From a1d533bc1f04397b6daaced7b0b7c40804ee4c32 Mon Sep 17 00:00:00 2001 From: BALUDU VAMSI Date: Fri, 6 Mar 2026 06:59:16 +0530 Subject: [PATCH 6/6] Update pnpm-lock.yaml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pnpm-lock.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 155d1fbb00..85ee9912a3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1045,11 +1045,11 @@ packages: react: ^18.x || ^19.x react-dom: ^18.x || ^19.x - '@mantine/spotlight@8.3.15': + '@mantine/spotlight@8.3.12': resolution: {integrity: sha512-zKssw/6eBmkY+1sGAgD8Vpy7dU5MXcY/cpvfr65SfIknRljKM9D4Z9TflzgIpxEdhvozls06MPcxj/pZkGpELQ==} peerDependencies: - '@mantine/core': 8.3.15 - '@mantine/hooks': 8.3.15 + '@mantine/core': 8.3.12 + '@mantine/hooks': 8.3.12 react: ^18.x || ^19.x react-dom: ^18.x || ^19.x