feat(FN-1291): add board/list toggle sub-buttons to MobileNavBar
- Add board and list view toggle sub-buttons to MobileNavBar component - Add CSS styles for toggle sub-buttons with active/hover states - Update MobileNavBar tests to cover new toggle sub-buttons - Update mobile feature access regression tests for new nav items
This commit is contained in:
@@ -91,26 +91,23 @@ describe("Mobile Feature Access Regression Guard", () => {
|
||||
|
||||
it("list view is accessible via mobile nav bar", () => {
|
||||
const props = createDefaultMobileNavProps();
|
||||
const { rerender } = render(<MobileNavBar {...props} view="board" />);
|
||||
render(<MobileNavBar {...props} view="board" />);
|
||||
|
||||
const firstTab = screen.getByTestId("mobile-nav-tab-board");
|
||||
expect(firstTab.textContent).toMatch(/Board|List/);
|
||||
const listTab = screen.getByTestId("mobile-nav-tab-list");
|
||||
expect(listTab.textContent).toContain("List");
|
||||
|
||||
fireEvent.click(firstTab);
|
||||
expect(props.onChangeView).toHaveBeenCalledWith("board");
|
||||
|
||||
rerender(<MobileNavBar {...props} view="list" />);
|
||||
expect(screen.getByTestId("mobile-nav-tab-board").textContent).toContain("List");
|
||||
fireEvent.click(listTab);
|
||||
expect(props.onChangeView).toHaveBeenCalledWith("list");
|
||||
});
|
||||
|
||||
it("board view is accessible via mobile nav bar", () => {
|
||||
const props = createDefaultMobileNavProps();
|
||||
render(<MobileNavBar {...props} view="board" />);
|
||||
|
||||
const firstTab = screen.getByTestId("mobile-nav-tab-board");
|
||||
expect(firstTab.textContent).toContain("Board");
|
||||
const boardTab = screen.getByTestId("mobile-nav-tab-board");
|
||||
expect(boardTab.textContent).toContain("Board");
|
||||
|
||||
fireEvent.click(firstTab);
|
||||
fireEvent.click(boardTab);
|
||||
expect(props.onChangeView).toHaveBeenCalledWith("board");
|
||||
});
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
GitBranch,
|
||||
Grid3X3,
|
||||
LayoutGrid,
|
||||
List,
|
||||
Lightbulb,
|
||||
Mail,
|
||||
MoreHorizontal,
|
||||
@@ -123,8 +124,6 @@ export function MobileNavBar({
|
||||
return null;
|
||||
}
|
||||
|
||||
const boardTabLabel = view === "list" ? "List" : "Board";
|
||||
const isBoardOrList = view === "board" || view === "list";
|
||||
const planningHandler = activePlanningSessionCount > 0 && onResumePlanning ? onResumePlanning : onOpenPlanning;
|
||||
|
||||
return (
|
||||
@@ -134,17 +133,30 @@ export function MobileNavBar({
|
||||
role="tablist"
|
||||
aria-label="Primary navigation"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={`mobile-nav-tab${isBoardOrList ? " mobile-nav-tab--active" : ""}`}
|
||||
data-testid="mobile-nav-tab-board"
|
||||
role="tab"
|
||||
aria-selected={isBoardOrList}
|
||||
onClick={() => onChangeView("board")}
|
||||
>
|
||||
<LayoutGrid />
|
||||
<span className="mobile-nav-tab-label">{boardTabLabel}</span>
|
||||
</button>
|
||||
<div className="mobile-nav-view-toggle">
|
||||
<button
|
||||
type="button"
|
||||
className={`mobile-nav-view-toggle-btn${view === "board" ? " mobile-nav-view-toggle-btn--active" : ""}`}
|
||||
data-testid="mobile-nav-tab-board"
|
||||
role="tab"
|
||||
aria-selected={view === "board"}
|
||||
onClick={() => onChangeView("board")}
|
||||
>
|
||||
<LayoutGrid />
|
||||
<span>Board</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`mobile-nav-view-toggle-btn${view === "list" ? " mobile-nav-view-toggle-btn--active" : ""}`}
|
||||
data-testid="mobile-nav-tab-list"
|
||||
role="tab"
|
||||
aria-selected={view === "list"}
|
||||
onClick={() => onChangeView("list")}
|
||||
>
|
||||
<List />
|
||||
<span>List</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -50,29 +50,54 @@ describe("MobileNavBar", () => {
|
||||
mockViewport("mobile");
|
||||
});
|
||||
|
||||
it("renders four tab buttons", () => {
|
||||
it("renders five tab buttons (board + list + agents + activity + more)", () => {
|
||||
render(<MobileNavBar {...createDefaultProps()} />);
|
||||
|
||||
expect(screen.getByTestId("mobile-nav-tab-board")).toBeDefined();
|
||||
expect(screen.getByTestId("mobile-nav-tab-list")).toBeDefined();
|
||||
expect(screen.getByTestId("mobile-nav-tab-agents")).toBeDefined();
|
||||
expect(screen.getByTestId("mobile-nav-tab-activity")).toBeDefined();
|
||||
expect(screen.getByTestId("mobile-nav-tab-more")).toBeDefined();
|
||||
});
|
||||
|
||||
it("active tab is highlighted", () => {
|
||||
it("active tab is highlighted for agents", () => {
|
||||
render(<MobileNavBar {...createDefaultProps()} view="agents" />);
|
||||
|
||||
expect(screen.getByTestId("mobile-nav-tab-agents").className).toContain("mobile-nav-tab--active");
|
||||
expect(screen.getByTestId("mobile-nav-tab-board").className).not.toContain("mobile-nav-tab--active");
|
||||
});
|
||||
|
||||
it("board/list label changes based on current view", () => {
|
||||
it("board sub-button calls onChangeView with 'board'", () => {
|
||||
const props = createDefaultProps();
|
||||
const { rerender } = render(<MobileNavBar {...props} view="board" />);
|
||||
expect(screen.getByText("Board")).toBeDefined();
|
||||
render(<MobileNavBar {...props} view="list" />);
|
||||
|
||||
rerender(<MobileNavBar {...props} view="list" />);
|
||||
expect(screen.getByText("List")).toBeDefined();
|
||||
fireEvent.click(screen.getByTestId("mobile-nav-tab-board"));
|
||||
expect(props.onChangeView).toHaveBeenCalledWith("board");
|
||||
});
|
||||
|
||||
it("list sub-button calls onChangeView with 'list'", () => {
|
||||
const props = createDefaultProps();
|
||||
render(<MobileNavBar {...props} view="board" />);
|
||||
|
||||
fireEvent.click(screen.getByTestId("mobile-nav-tab-list"));
|
||||
expect(props.onChangeView).toHaveBeenCalledWith("list");
|
||||
});
|
||||
|
||||
it("board sub-button is active when view is 'board'", () => {
|
||||
render(<MobileNavBar {...createDefaultProps()} view="board" />);
|
||||
expect(screen.getByTestId("mobile-nav-tab-board").className).toContain("mobile-nav-view-toggle-btn--active");
|
||||
expect(screen.getByTestId("mobile-nav-tab-list").className).not.toContain("mobile-nav-view-toggle-btn--active");
|
||||
});
|
||||
|
||||
it("list sub-button is active when view is 'list'", () => {
|
||||
render(<MobileNavBar {...createDefaultProps()} view="list" />);
|
||||
expect(screen.getByTestId("mobile-nav-tab-list").className).toContain("mobile-nav-view-toggle-btn--active");
|
||||
expect(screen.getByTestId("mobile-nav-tab-board").className).not.toContain("mobile-nav-view-toggle-btn--active");
|
||||
});
|
||||
|
||||
it("board sub-button is not active when view is 'agents'", () => {
|
||||
render(<MobileNavBar {...createDefaultProps()} view="agents" />);
|
||||
expect(screen.getByTestId("mobile-nav-tab-board").className).not.toContain("mobile-nav-view-toggle-btn--active");
|
||||
expect(screen.getByTestId("mobile-nav-tab-list").className).not.toContain("mobile-nav-view-toggle-btn--active");
|
||||
});
|
||||
|
||||
it("shows activity badge with combined planning + mailbox count", () => {
|
||||
|
||||
@@ -25863,6 +25863,52 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
/* Board/List view toggle — two sub-buttons sharing first tab slot */
|
||||
.mobile-nav-view-toggle {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
min-height: 56px;
|
||||
}
|
||||
|
||||
.mobile-nav-view-toggle-btn {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
padding: 6px 0;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 10px;
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
transition: color 0.15s ease;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.mobile-nav-view-toggle-btn:active {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.mobile-nav-view-toggle-btn--active {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.mobile-nav-view-toggle-btn svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.mobile-nav-view-toggle-btn span {
|
||||
font-size: 10px;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mobile-nav-tab:active {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user