Mobile message compose + WidgetConfigDialog SheetForms (Slice 8)
Below md, both the message-compose Dialog and the WidgetConfigDialog
render inside a SheetForm instead of a centered Dialog.
Message compose (UsersPage.impl.tsx): the form body (subject, formatting
toolbar, HTML textarea, preview, attachments) is extracted into a shared
composeBody const consumed by both SheetForm (mobile) and Dialog
(desktop). SheetForm wired with title, onSave=handleSend (which already
closes on success per R4.5), onCancel=closeCompose, isPending,
saveDisabled, saveLabel='Send message'.
WidgetConfigDialog: the draftBody const is shared between branches. The
two-mode flow (list vs draft) maps to dynamic SheetForm props -- list
mode ('Dashboard widgets' / Done / Cancel both close), draft mode
('Add/Edit widget' / Save widget / Cancel=reset back to list). The
inline Back/Save buttons are hidden on mobile (!isMobile) since the
SheetForm footer provides them.
Desktop (md+) is token-identical for both components -- the
isComposeMobile (900px) fullscreen styling on compose is preserved for
the 768-900px band. The large diff (~860 lines) is dominated by
extraction/re-indentation of shared form bodies into consts; the
behavioral delta is ~80 lines.
Tests: 3 new (compose mobile send/subject, WidgetConfigDialog desktop +
mobile titles/Done). 116 tests pass; lint/build green.
Refs openspec/changes/mobile-responsive-parity/ (spec R4, tasks slice 8).
This commit is contained in:
@@ -286,7 +286,7 @@ describe("UsersPage (slice 6b — compose dialog formatting actions)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("UsersPage (mobile card layout — slice 5)", () => {
|
||||
describe("UsersPage (mobile card layout — slice 5)", () => {
|
||||
beforeEach(() => {
|
||||
window.matchMedia = ((query: string) => ({
|
||||
matches: query.includes("768"),
|
||||
@@ -342,4 +342,35 @@ describe("UsersPage (slice 6b — compose dialog formatting actions)", () => {
|
||||
// drawer opens via a card-body tap, not via the checkbox.
|
||||
expect(screen.queryByTestId("session-panel-stub")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders compose in a SheetForm below md with send button", async () => {
|
||||
users = [
|
||||
userFixture({
|
||||
jellyfin_id: "u1",
|
||||
display_name: "Alice",
|
||||
email: "alice@example.com",
|
||||
}),
|
||||
];
|
||||
render(
|
||||
<TooltipProvider>
|
||||
<UsersPage />
|
||||
</TooltipProvider>,
|
||||
);
|
||||
|
||||
// Select the deliverable user via the mobile card checkbox.
|
||||
await userEvent.click(
|
||||
screen.getByRole("checkbox", { name: /Select Alice/i }),
|
||||
);
|
||||
await userEvent.click(
|
||||
screen.getByRole("button", { name: "Message selected" }),
|
||||
);
|
||||
|
||||
// On mobile, compose opens in a SheetForm (not a Dialog). The SheetForm
|
||||
// header carries the title and the footer carries the Send button.
|
||||
expect(screen.getByText("Message selected users")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", { name: "Send message" }),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Subject")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user