feat(FN-1956): merge fusion/fn-1956

This commit is contained in:
gsxdsm
2026-04-17 15:24:37 -07:00
parent 0433ce5dd8
commit 6fbf2871e9
5 changed files with 31 additions and 96 deletions

View File

@@ -320,6 +320,15 @@ export function MailboxModal({
)}
</div>
<div className="mailbox-header-actions">
<button
className="btn-sm btn-primary"
onClick={handleOpenCompose}
title="Compose message"
data-testid="mailbox-header-compose"
>
<MessageSquare size={14} />
<span>Compose</span>
</button>
{activeTab === "inbox" && unreadCount > 0 && (
<button
className="btn-sm btn-secondary"
@@ -631,17 +640,6 @@ export function MailboxModal({
)}
</div>
{/* Compose FAB (only when viewing inbox/outbox, not in detail view or agents tab) */}
{!selectedMessage && !showComposer && activeTab !== "agents" && (
<button
className="mailbox-compose-fab"
onClick={handleOpenCompose}
title="Compose message"
data-testid="mailbox-compose-fab"
>
<MessageSquare size={20} />
</button>
)}
</div>
</div>
);

View File

@@ -379,6 +379,15 @@ export function MailboxView({
)}
</div>
<div className="mailbox-header-actions">
<button
className="btn-sm btn-primary"
onClick={handleOpenCompose}
title="Compose message"
data-testid="mailbox-header-compose"
>
<MessageSquare size={14} />
<span>Compose</span>
</button>
{activeTab === "inbox" && unreadCount > 0 && (
<button
className="btn-sm btn-secondary"
@@ -694,17 +703,6 @@ export function MailboxView({
)}
</div>
{/* Compose FAB (only when viewing inbox/outbox, not in detail view or agents tab) */}
{!selectedMessage && !showComposer && activeTab !== "agents" && (
<button
className="mailbox-compose-fab"
onClick={handleOpenCompose}
title="Compose message"
data-testid="mailbox-compose-fab"
>
<MessageSquare size={20} />
</button>
)}
</div>
);
}

View File

@@ -329,18 +329,18 @@ describe("MailboxModal", () => {
});
});
it("shows compose FAB in inbox tab", async () => {
it("shows compose button in header on inbox tab", async () => {
render(<MailboxModal {...defaultProps} />);
await waitFor(() => {
expect(screen.getByTestId("mailbox-compose-fab")).toBeDefined();
expect(screen.getByTestId("mailbox-header-compose")).toBeDefined();
});
});
it("does not show compose FAB in agents tab", async () => {
it("shows compose button in header on agents tab", async () => {
render(<MailboxModal {...defaultProps} />);
fireEvent.click(screen.getByTestId("mailbox-tab-agents"));
await waitFor(() => {
expect(screen.queryByTestId("mailbox-compose-fab")).toBeNull();
expect(screen.getByTestId("mailbox-header-compose")).toBeDefined();
});
});
@@ -516,9 +516,6 @@ describe("MailboxModal", () => {
expect(mailboxMobileSection).toContain("max-width: 100%;");
expect(mailboxMobileSection).toContain(".mailbox-modal .mailbox-agents");
expect(mailboxMobileSection).toContain("min-height: 200px;");
expect(mailboxMobileSection).toContain(".mailbox-modal .mailbox-compose-fab");
expect(mailboxMobileSection).toContain("bottom: 16px;");
expect(mailboxMobileSection).toContain("right: 16px;");
expect(mailboxMobileSection).toContain(".mailbox-modal .mailbox-empty");
expect(mailboxMobileSection).toContain("padding: 32px 12px;");
});
@@ -564,21 +561,6 @@ describe("MailboxModal", () => {
expect(blockMatch![1]).not.toContain("color: white");
});
it("mailbox compose FAB uses theme-aware tokens", () => {
const blockMatch = css.match(/\.mailbox-compose-fab\s*\{([^}]*)\}/);
expect(blockMatch).toBeTruthy();
expect(blockMatch![1]).toContain("var(--fab-bg)");
expect(blockMatch![1]).toContain("var(--fab-text)");
expect(blockMatch![1]).not.toContain("background: var(--todo)");
expect(blockMatch![1]).not.toContain("color: white");
// Check hover state
const hoverMatch = css.match(/\.mailbox-compose-fab:hover\s*\{([^}]*)\}/);
expect(hoverMatch).toBeTruthy();
expect(hoverMatch![1]).toContain("var(--fab-bg)");
expect(hoverMatch![1]).toContain("var(--fab-text)");
});
it("mission event type error uses CSS custom properties", () => {
const blockMatch = css.match(/\.mission-event__type--error\s*\{([^}]*)\}/);
expect(blockMatch).toBeTruthy();

View File

@@ -361,7 +361,7 @@ describe("MailboxView", () => {
});
});
it("shows compose FAB in inbox tab", async () => {
it("shows compose button in header on inbox tab", async () => {
mockFetchInbox.mockResolvedValue({
messages: [],
unreadCount: 0,
@@ -370,11 +370,11 @@ describe("MailboxView", () => {
render(<MailboxView {...defaultProps} />);
await waitFor(() => {
expect(screen.getByTestId("mailbox-compose-fab")).toBeDefined();
expect(screen.getByTestId("mailbox-header-compose")).toBeDefined();
});
});
it("does not show compose FAB in agents tab", async () => {
it("shows compose button in header on agents tab", async () => {
mockFetchInbox.mockResolvedValue({
messages: [],
unreadCount: 0,
@@ -388,7 +388,7 @@ describe("MailboxView", () => {
});
await waitFor(() => {
expect(screen.queryByTestId("mailbox-compose-fab")).toBeNull();
expect(screen.getByTestId("mailbox-header-compose")).toBeDefined();
});
});
@@ -446,7 +446,7 @@ describe("MailboxView", () => {
});
});
it("shows MessageComposer with agents when clicking compose FAB from inbox tab", async () => {
it("shows MessageComposer with agents when clicking compose button from header", async () => {
mockFetchInbox.mockResolvedValue({
messages: [],
unreadCount: 0,
@@ -454,14 +454,14 @@ describe("MailboxView", () => {
render(<MailboxView {...defaultProps} />);
// Verify compose FAB is visible in inbox tab
// Verify compose button is visible in header
await waitFor(() => {
expect(screen.getByTestId("mailbox-compose-fab")).toBeDefined();
expect(screen.getByTestId("mailbox-header-compose")).toBeDefined();
});
// Click compose FAB
// Click compose button
await act(async () => {
fireEvent.click(screen.getByTestId("mailbox-compose-fab"));
fireEvent.click(screen.getByTestId("mailbox-header-compose"));
});
// Verify MessageComposer is shown
@@ -516,7 +516,6 @@ describe("MailboxView", () => {
expect(mailboxMobileSection).toContain(".mailbox-view .mailbox-header");
expect(mailboxMobileSection).toContain(".mailbox-view .mailbox-tabs");
expect(mailboxMobileSection).toContain(".mailbox-view .mailbox-content");
expect(mailboxMobileSection).toContain(".mailbox-view .mailbox-compose-fab");
expect(mailboxMobileSection).toContain(".mailbox-view .mailbox-empty");
});
@@ -542,13 +541,6 @@ describe("MailboxView", () => {
// Content should have padding-bottom accounting for mobile nav
expect(contentRuleMatch![0]).toContain("padding-bottom");
// FAB should account for mobile nav height
expect(mailboxMobileSection).toContain(".mailbox-view .mailbox-compose-fab");
const fabRuleMatch = mailboxMobileSection.match(/\.mailbox-view\s+\.mailbox-compose-fab\s*\{[^}]*\}/);
expect(fabRuleMatch).toBeTruthy();
expect(fabRuleMatch![0]).toContain("--mobile-nav-height");
expect(fabRuleMatch![0]).toContain("safe-area-inset-bottom");
expect(fabRuleMatch![0]).toContain("--standalone-bottom-gap");
});
it("renders structural elements that mobile CSS targets", async () => {