This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 40a8f6ae7 feat(cluster): clarify supported NameServer address formats 
(#4383)
40a8f6ae7 is described below

commit 40a8f6ae78534039845a7297684e9b74407bc1e3
Author: Yanghanrui666 <[email protected]>
AuthorDate: Mon Sep 21 11:45:46 2026 +0800

    feat(cluster): clarify supported NameServer address formats (#4383)
    
    Add a neutral InfoBanner to the NameServer create/edit and connection-test
    dialogs explaining that Studio connects from the server side, so the address
    must be reachable from the Studio server, and listing the supported forms
    (SLB/VIP, headless Service DNS, comma-separated list).
    
    Adds both zh and en i18n entries and asserts the guidance renders and
    localizes in ClusterPage tests.
---
 web/src/i18n/translations.ts                         |  8 ++++++++
 web/src/pages/cluster/__tests__/ClusterPage.test.tsx | 20 ++++++++++++++++++++
 web/src/pages/cluster/index.tsx                      | 11 +++++++++++
 3 files changed, 39 insertions(+)

diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts
index 3f5c1105a..9a7bbe368 100644
--- a/web/src/i18n/translations.ts
+++ b/web/src/i18n/translations.ts
@@ -193,6 +193,14 @@ const translations: Record<string, Record<Lang, string>> = 
{
   'cluster.createCluster': { zh: '新建集群', en: 'New Cluster' },
   'cluster.nsAddr': { zh: 'NameServer 地址', en: 'NameServer Address' },
   'cluster.nsAddrPlaceholder': { zh: '例:10.0.1.1:9876', en: 'e.g. 
10.0.1.1:9876' },
+  'cluster.nsAddrGuidanceTitle': {
+    zh: 'NameServer 地址填写说明',
+    en: 'NameServer address guidance',
+  },
+  'cluster.nsAddrGuidance': {
+    zh: 'Studio 会从服务器直接连接该地址,因此地址必须能被 Studio 服务器访问。支持 SLB/VIP、Headless Service 
DNS(例如 namesrv.mq.svc.cluster.local:9876),也支持用英文逗号分隔的多个 NameServer 地址(例如 
ns1:9876,ns2:9876)。',
+    en: 'Studio connects to this address directly from the server, so it must 
be reachable from the Studio server. Supported forms include an SLB/VIP, a 
headless Service DNS name (for example namesrv.mq.svc.cluster.local:9876), and 
a comma-separated NameServer list (for example ns1:9876,ns2:9876).',
+  },
   'cluster.k8sNamespace': { zh: 'K8s Namespace', en: 'K8s Namespace' },
   'cluster.k8sId': { zh: 'k8s ID', en: 'k8s ID' },
   'clients.selectClusterPlaceholder': { zh: '选择 Broker 集群', en: 'Select Broker 
cluster' },
diff --git a/web/src/pages/cluster/__tests__/ClusterPage.test.tsx 
b/web/src/pages/cluster/__tests__/ClusterPage.test.tsx
index 4be12b4f0..d5871f3c4 100644
--- a/web/src/pages/cluster/__tests__/ClusterPage.test.tsx
+++ b/web/src/pages/cluster/__tests__/ClusterPage.test.tsx
@@ -29,6 +29,7 @@ import type {
   NameServerConfigDiffResult,
 } from '../../../api/cluster';
 import { LangProvider } from '../../../i18n/LangContext';
+import { LANGUAGE_STORAGE_KEY } from '../../../i18n/languagePreference';
 
 const clusterServiceMocks = vi.hoisted(() => ({
   createNameserverRegistry: vi.fn(),
@@ -527,6 +528,9 @@ describe('Cluster page', () => {
 
     await user.click(screen.getByRole('button', { name: /新建 NameServer/ }));
     const dialog = await screen.findByRole('dialog', { name: /新建 NameServer/ 
});
+    
expect(within(dialog).getByTestId('nameserver-address-guidance')).toHaveTextContent(
+      'Studio 会从服务器直接连接该地址',
+    );
     await user.type(within(dialog).getByLabelText('名称'), 'rocketmq3');
     await user.type(within(dialog).getByLabelText('NameServer 地址'), 
'rocketmq3-nameserver:9876');
     await user.click(within(dialog).getByRole('button', { name: /确\s*认/ }));
@@ -569,6 +573,22 @@ describe('Cluster page', () => {
     confirmSpy.mockRestore();
   });
 
+  it('localizes the NameServer address guidance', async () => {
+    localStorage.setItem(LANGUAGE_STORAGE_KEY, 'en');
+    const user = userEvent.setup();
+    renderWithProviders(<ClusterPage />);
+    await user.click(screen.getByRole('tab', { name: /NameServer/ }));
+    await user.click(screen.getByRole('button', { name: /New NameServer/ }));
+    const dialog = await screen.findByRole('dialog', { name: /New NameServer/ 
});
+
+    
expect(within(dialog).getByTestId('nameserver-address-guidance')).toHaveTextContent(
+      'Studio connects to this address directly from the server',
+    );
+    
expect(within(dialog).getByTestId('nameserver-address-guidance')).toHaveTextContent(
+      'ns1:9876,ns2:9876',
+    );
+  });
+
   it('opens NameServer config drift details from a registry row', async () => {
     const user = userEvent.setup();
     instanceServiceMocks.listInstances.mockResolvedValue([
diff --git a/web/src/pages/cluster/index.tsx b/web/src/pages/cluster/index.tsx
index 5b83391dc..85b892d57 100644
--- a/web/src/pages/cluster/index.tsx
+++ b/web/src/pages/cluster/index.tsx
@@ -50,6 +50,7 @@ import {
 } from '@ant-design/icons';
 import { Cpu, HardDrives, Globe } from '@phosphor-icons/react';
 import PageHeader from '../../components/PageHeader';
+import InfoBanner from '../../components/InfoBanner';
 import { useLang } from '../../i18n/LangContext';
 import { countClusterComponents } from './clusterStats';
 import type {
@@ -1820,6 +1821,11 @@ const ClusterPage = () => {
         cancelText={t('common.cancel')}
         destroyOnHidden
       >
+        <InfoBanner
+          title={t('cluster.nsAddrGuidanceTitle')}
+          description={t('cluster.nsAddrGuidance')}
+          data-testid="nameserver-address-guidance"
+        />
         <Form form={nsCreateForm} layout="vertical" style={{ marginTop: 16 }}>
           <Form.Item
             name="name"
@@ -1915,6 +1921,11 @@ const ClusterPage = () => {
         destroyOnHidden
       >
         <Text type="secondary">{t('cluster.testConnectionDesc')}</Text>
+        <InfoBanner
+          title={t('cluster.nsAddrGuidanceTitle')}
+          description={t('cluster.nsAddrGuidance')}
+          data-testid="nameserver-address-guidance"
+        />
         <Form form={connectForm} layout="vertical" style={{ marginTop: 16 }}>
           <Form.Item
             name="namesrvAddr"

Reply via email to