FN-6913: localize right dock labels

Route right-dock accessibility labels through the app i18n namespace.\n\n- Replace hard-coded right dock and pop-out aria/title copy with translated keys and English fallbacks.\n- Add rightDock locale entries across app locale catalogs and typed resources.\n- Extend RightDock tests to cover localized dock and expanded modal affordance labels.\n\nFiles changed:\n packages/dashboard/app/components/RightDock.tsx          | 16 +++++++++++-----\n .../dashboard/app/components/RightDockExpandModal.tsx    | 12 +++++++++---\n .../app/components/__tests__/RightDock.test.tsx          | 15 ++++++++++++++-\n packages/i18n/locales/en/app.json                        | 11 +++++++++++\n packages/i18n/locales/es/app.json                        | 11 +++++++++++\n packages/i18n/locales/fr/app.json                        | 11 +++++++++++\n packages/i18n/locales/ko/app.json                        | 11 +++++++++++\n packages/i18n/locales/zh-CN/app.json                     | 11 +++++++++++\n packages/i18n/locales/zh-TW/app.json                     | 11 +++++++++++\n packages/i18n/src/resources.d.ts                         | 11 +++++++++++\n 10 files changed, 111 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-6913

Fusion-Task-Lineage: 1c07da14-d8a3-42ba-8c93-843c3fe1fa63
This commit is contained in:
gsxdsm
2026-06-22 19:35:26 -07:00
parent 584ed02541
commit 7ee36f26e5
10 changed files with 111 additions and 9 deletions

View File

@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useRef, useState, type KeyboardEvent as ReactKeyboardEvent } from "react";
import { Maximize2 } from "lucide-react";
import { useTranslation } from "react-i18next";
import {
findOverflowViewEntry,
getVisibleOverflowViewEntries,
@@ -89,6 +90,9 @@ FN-6882 splits right-dock entries into launcher actions and inline views. Action
FNXC:Navigation 2026-06-22-09:00:
The right dock is visible by default on tablet/desktop project screens. Show/hide is owned solely by the canonical Header right-sidebar toggle (the in-dock collapse toggle was removed); the dock takes only `open` and renders null when closed so the main content reclaims the space.
FNXC:i18n 2026-06-22-00:00:
Right-dock affordance labels are user-facing accessibility copy, so route them through the app namespace and keep English defaults colocated with the component for tests and fallback rendering.
*/
export function RightDock({
open,
@@ -97,6 +101,7 @@ export function RightDock({
onExpand,
footerVisible = false,
}: RightDockProps) {
const { t } = useTranslation("app");
const entries = useMemo(() => getVisibleOverflowViewEntries(visibilityOptions), [visibilityOptions]);
const [selectedKey, setSelectedKey] = useState<OverflowViewKey>(() => readStoredRightDockView(visibilityOptions));
const [width, setWidth] = useState(readStoredRightDockWidth);
@@ -198,12 +203,13 @@ export function RightDock({
const SelectedIcon = selectedEntry.icon;
const dockWidth = `${width}px`;
const expandSelectedViewLabel = t("rightDock.expandView", "Expand {{label}}", { label: selectedEntry.label });
return (
<aside
className={`right-dock${open ? "" : " right-dock--collapsed"}${footerVisible ? " right-dock--with-footer" : ""}`}
style={dockWidth ? { width: dockWidth } : undefined}
aria-label="Right dock"
aria-label={t("rightDock.label", "Right dock")}
data-testid="right-dock"
>
{open ? (
@@ -214,7 +220,7 @@ export function RightDock({
aria-valuemin={RIGHT_DOCK_MIN_WIDTH}
aria-valuemax={RIGHT_DOCK_MAX_WIDTH}
aria-valuenow={width}
aria-label="Resize right dock"
aria-label={t("rightDock.resize", "Resize right dock")}
tabIndex={0}
data-testid="right-dock-resize-handle"
onPointerDown={handleResizeStart}
@@ -222,7 +228,7 @@ export function RightDock({
/>
) : null}
<div className="right-dock__toolbar">
<div className="right-dock__tabs" role="tablist" aria-label="Right dock views">
<div className="right-dock__tabs" role="tablist" aria-label={t("rightDock.views", "Right dock views")}>
{entries.map((entry) => {
const Icon = entry.icon;
const selected = Boolean(entry.render && entry.key === selectedEntry.key);
@@ -248,8 +254,8 @@ export function RightDock({
<button
type="button"
className="btn-icon right-dock__expand"
aria-label={`Expand ${selectedEntry.label}`}
title={`Expand ${selectedEntry.label}`}
aria-label={expandSelectedViewLabel}
title={expandSelectedViewLabel}
data-testid="right-dock-expand"
onClick={() => onExpand?.(selectedEntry.key)}
>

View File

@@ -1,6 +1,7 @@
import { useCallback, useEffect, useRef, useState, type CSSProperties, type PointerEvent as ReactPointerEvent, type RefObject } from "react";
import { createPortal } from "react-dom";
import { Maximize2, X } from "lucide-react";
import { useTranslation } from "react-i18next";
import { findOverflowViewEntry, type OverflowViewEntry, type OverflowViewKey, type OverflowViewRenderProps, type OverflowViewVisibilityOptions } from "./overflowViewRegistry";
import { nextFloatingZ, currentFloatingZ } from "./floatingWindowStack";
import "./RightDock.css";
@@ -113,6 +114,9 @@ Expanded right-dock views reuse the same overflow registry render function as th
FNXC:Navigation 2026-06-21-20:16:
FN-6882 makes most right-dock entries launcher actions. The expand modal is restricted to inline view entries so action-only tools cannot open an empty modal body.
FNXC:i18n 2026-06-22-00:00:
Expanded right-dock modal affordance labels are accessibility copy and must use the app namespace so locale catalogs and fallback tests cover the modal surface with the dock controls.
*/
export function RightDockExpandModal({
viewKey,
@@ -121,6 +125,7 @@ export function RightDockExpandModal({
onClose,
returnFocusRef,
}: RightDockExpandModalProps) {
const { t } = useTranslation("app");
const resolvedEntry = viewKey ? findOverflowViewEntry(viewKey, visibilityOptions) : undefined;
const entry: RenderableOverflowViewEntry | undefined = resolvedEntry?.render ? { ...resolvedEntry, render: resolvedEntry.render } : undefined;
@@ -295,6 +300,7 @@ export function RightDockExpandModal({
}
const Icon = entry.icon;
const expandedViewLabel = t("rightDock.viewExpanded", "{{label}} expanded", { label: entry.label });
const panelStyle = {
left: `${position.x}px`,
@@ -306,7 +312,7 @@ export function RightDockExpandModal({
// FNXC:FloatingWindow 2026-06-22-22:30: Portaled to document.body so this floating modal shares the ONE root stacking context with the other floating modals (FloatingWindow/terminal/New Task) — the shared 10100+ z stack only orders correctly across types when they all live at the document root.
return createPortal(
<div className="modal-overlay open right-dock-expand-modal-overlay" role="dialog" aria-modal="false" aria-label={`${entry.label} expanded`} data-testid="right-dock-expand-modal" style={{ zIndex }}>
<div className="modal-overlay open right-dock-expand-modal-overlay" role="dialog" aria-modal="false" aria-label={expandedViewLabel} data-testid="right-dock-expand-modal" style={{ zIndex }}>
<div
className="modal right-dock-expand-modal right-dock-expand-modal--floating"
style={panelStyle}
@@ -319,7 +325,7 @@ export function RightDockExpandModal({
className={`right-dock-expand-resize-handle right-dock-expand-resize-handle--${direction}`}
data-testid={`right-dock-expand-resize-${direction}`}
role="separator"
aria-label="Resize expanded right dock window"
aria-label={t("rightDock.resizeExpandedView", "Resize expanded right dock window")}
onPointerDown={(event) => handleFloatingResizePointerDown(event, direction)}
/>
))}
@@ -333,7 +339,7 @@ export function RightDockExpandModal({
<Icon size={16} />
<span>{entry.label}</span>
</div>
<button className="modal-close" onClick={closeAndRestoreFocus} aria-label="Close expanded right dock view" data-testid="right-dock-expand-close">
<button className="modal-close" onClick={closeAndRestoreFocus} aria-label={t("rightDock.closeExpandedView", "Close expanded right dock view")} data-testid="right-dock-expand-close">
<X size={20} />
</button>
</div>

View File

@@ -99,6 +99,17 @@ describe("RightDock", () => {
expect(screen.queryByTestId("right-dock-tab-documents")).toBeNull();
});
it("exposes localized right-dock affordance labels without an in-dock collapse shell", () => {
render(<RightDock open={true} renderProps={renderProps} />);
expect(screen.getByTestId("right-dock")).toHaveAttribute("aria-label", "Right dock");
expect(screen.getByTestId("right-dock-resize-handle")).toHaveAttribute("aria-label", "Resize right dock");
expect(screen.getByRole("tablist", { name: "Right dock views" })).toBeInTheDocument();
expect(screen.getByTestId("right-dock-expand")).toHaveAttribute("aria-label", "Expand Files");
expect(screen.getByTestId("right-dock-expand")).toHaveAttribute("title", "Expand Files");
expect(screen.queryByTestId("right-dock-collapse-toggle")).toBeNull();
});
it("renders exactly the current right-dock tool entries and no removed content-view tabs", () => {
render(
<RightDock
@@ -239,6 +250,7 @@ describe("RightDock", () => {
);
expect(screen.getByTestId("right-dock-expand-modal")).toBeInTheDocument();
expect(screen.getByTestId("right-dock-expand-modal")).toHaveAttribute("aria-label", "Files expanded");
expect(screen.getByTestId("right-dock-expand-body")).toBeInTheDocument();
/*
FNXC:RightDock 2026-06-22-17:40:
@@ -248,7 +260,8 @@ describe("RightDock", () => {
expect(screen.getByTestId("right-dock-expand-modal")).toHaveAttribute("aria-modal", "false");
expect(screen.getByTestId("right-dock-expand-drag-handle")).toBeInTheDocument();
expect(screen.getByTestId("right-dock-expand-modal").querySelector(".right-dock-expand-modal--floating")).not.toBeNull();
expect(screen.getByTestId("right-dock-expand-resize-se")).toBeInTheDocument();
expect(screen.getByTestId("right-dock-expand-resize-se")).toHaveAttribute("aria-label", "Resize expanded right dock window");
expect(screen.getByTestId("right-dock-expand-close")).toHaveAttribute("aria-label", "Close expanded right dock view");
fireEvent.click(screen.getByTestId("right-dock-expand-close"));
expect(onClose).toHaveBeenCalledTimes(1);
await new Promise((resolve) => window.setTimeout(resolve, 0));

View File

@@ -5107,6 +5107,17 @@
"unavailable": "Research is unavailable for this project.",
"viewLabel": "Research view"
},
"rightDock": {
"closeExpandedView": "Close expanded right dock view",
"collapse": "Collapse right dock",
"expand": "Expand right dock",
"expandView": "Expand {{label}}",
"label": "Right dock",
"resize": "Resize right dock",
"viewExpanded": "{{label}} expanded",
"views": "Right dock views",
"resizeExpandedView": "Resize expanded right dock window"
},
"routine": {
"andMore_one": "…and {{count}} more",
"andMore_other": "…and {{count}} more",

View File

@@ -5106,6 +5106,17 @@
"unavailable": "La investigación no está disponible para este proyecto.",
"viewLabel": "Vista de investigación"
},
"rightDock": {
"closeExpandedView": "",
"collapse": "",
"expand": "",
"expandView": "",
"label": "",
"resize": "",
"viewExpanded": "",
"views": "",
"resizeExpandedView": ""
},
"routine": {
"andMore_one": "",
"andMore_other": "",

View File

@@ -5106,6 +5106,17 @@
"unavailable": "La recherche est indisponible pour ce projet.",
"viewLabel": "Affichage de la recherche"
},
"rightDock": {
"closeExpandedView": "",
"collapse": "",
"expand": "",
"expandView": "",
"label": "",
"resize": "",
"viewExpanded": "",
"views": "",
"resizeExpandedView": ""
},
"routine": {
"andMore_one": "",
"andMore_other": "",

View File

@@ -5106,6 +5106,17 @@
"unavailable": "이 프로젝트에서는 리서치를 사용할 수 없습니다.",
"viewLabel": "리서치 보기"
},
"rightDock": {
"closeExpandedView": "",
"collapse": "",
"expand": "",
"expandView": "",
"label": "",
"resize": "",
"viewExpanded": "",
"views": "",
"resizeExpandedView": ""
},
"routine": {
"andMore_one": "",
"andMore_other": "",

View File

@@ -5106,6 +5106,17 @@
"unavailable": "此项目不支持研究功能。",
"viewLabel": "研究视图"
},
"rightDock": {
"closeExpandedView": "",
"collapse": "",
"expand": "",
"expandView": "",
"label": "",
"resize": "",
"viewExpanded": "",
"views": "",
"resizeExpandedView": ""
},
"routine": {
"andMore_one": "",
"andMore_other": "",

View File

@@ -5106,6 +5106,17 @@
"unavailable": "此專案不支援研究功能。",
"viewLabel": "研究檢視"
},
"rightDock": {
"closeExpandedView": "",
"collapse": "",
"expand": "",
"expandView": "",
"label": "",
"resize": "",
"viewExpanded": "",
"views": "",
"resizeExpandedView": ""
},
"routine": {
"andMore_one": "",
"andMore_other": "",

View File

@@ -5108,6 +5108,17 @@ export default interface Resources {
"unavailable": "Research is unavailable for this project.",
"viewLabel": "Research view"
},
"rightDock": {
"closeExpandedView": "Close expanded right dock view",
"collapse": "Collapse right dock",
"expand": "Expand right dock",
"expandView": "Expand {{label}}",
"label": "Right dock",
"resize": "Resize right dock",
"resizeExpandedView": "Resize expanded right dock window",
"viewExpanded": "{{label}} expanded",
"views": "Right dock views"
},
"routine": {
"andMore_one": "…and {{count}} more",
"andMore_other": "…and {{count}} more",