mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-08-30 17:56:18 +08:00
fix(config): announce tab validation state
This commit is contained in:
@@ -74,6 +74,15 @@ export function ConfigTabs({
|
||||
const Icon = CONFIG_TAB_ICONS[id];
|
||||
const isActive = active === id;
|
||||
const errorCount = errorCounts[id] ?? 0;
|
||||
const isDirty = dirtyTabs.has(id);
|
||||
const tabLabel = t(`config_management.visual.sections.${id}.title`);
|
||||
const accessibleLabel = [
|
||||
tabLabel,
|
||||
errorCount > 0 ? t('config_management.meta_errors', { count: errorCount }) : null,
|
||||
isDirty ? t('config_management.status_dirty_short') : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
|
||||
return (
|
||||
<button
|
||||
@@ -86,6 +95,7 @@ export function ConfigTabs({
|
||||
id={configTabDomId(id)}
|
||||
aria-selected={isActive}
|
||||
aria-controls={configPanelDomId(id)}
|
||||
aria-label={accessibleLabel}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
className={`${styles.tab} ${isActive ? styles.tabActive : ''}`}
|
||||
disabled={disabled}
|
||||
@@ -93,15 +103,13 @@ export function ConfigTabs({
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
<Icon size={15} className={styles.tabGlyph} />
|
||||
<span className={styles.tabLabel}>
|
||||
{t(`config_management.visual.sections.${id}.title`)}
|
||||
</span>
|
||||
<span className={styles.tabLabel}>{tabLabel}</span>
|
||||
{errorCount > 0 ? (
|
||||
<span className={styles.tabBadge} aria-hidden="true">
|
||||
{errorCount}
|
||||
</span>
|
||||
) : null}
|
||||
{dirtyTabs.has(id) ? <span className={styles.tabDirtyDot} aria-hidden="true" /> : null}
|
||||
{isDirty ? <span className={styles.tabDirtyDot} aria-hidden="true" /> : null}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import { createElement } from 'react';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import i18n from '@/i18n';
|
||||
import { ConfigTabs } from '@/features/config/components/ConfigTabs';
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
describe('ConfigTabs accessibility', () => {
|
||||
test('announces validation errors and unsaved changes in the tab name', () => {
|
||||
const markup = renderToStaticMarkup(
|
||||
createElement(ConfigTabs, {
|
||||
active: 'common',
|
||||
errorCounts: { streaming: 2 },
|
||||
dirtyTabs: new Set(['streaming']),
|
||||
onChange: noop,
|
||||
})
|
||||
);
|
||||
const accessibleLabel = [
|
||||
i18n.t('config_management.visual.sections.streaming.title'),
|
||||
i18n.t('config_management.meta_errors', { count: 2 }),
|
||||
i18n.t('config_management.status_dirty_short'),
|
||||
].join(', ');
|
||||
|
||||
expect(markup).toContain(`aria-label="${accessibleLabel}"`);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user