Wire quick access commands
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { AppShell } from "./AppShell";
|
||||
|
||||
describe("AppShell", () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it("не показывает общую ленту навигации между приложениями", () => {
|
||||
const view = render(
|
||||
<AppShell activeApp="word" status={{ savedLabel: "Сохранено локально" }}>
|
||||
@@ -37,4 +41,29 @@ describe("AppShell", () => {
|
||||
expect(screen.getByText("Число слов: 42")).toBeInTheDocument();
|
||||
expect(screen.getByText("125%")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("отправляет команды панели быстрого доступа активному приложению", () => {
|
||||
const commands: string[] = [];
|
||||
function onCommand(event: WindowEventMap["qoffice-command"]) {
|
||||
commands.push(event.detail.command);
|
||||
}
|
||||
window.addEventListener("qoffice-command", onCommand);
|
||||
|
||||
try {
|
||||
const view = render(
|
||||
<AppShell activeApp="word" status={{ savedLabel: "Сохранено локально" }}>
|
||||
<div>Рабочая область QWord</div>
|
||||
</AppShell>
|
||||
);
|
||||
const quickAccessButtons = view.container.querySelectorAll(".quick-access button");
|
||||
|
||||
fireEvent.click(quickAccessButtons[0]);
|
||||
fireEvent.click(quickAccessButtons[1]);
|
||||
fireEvent.click(quickAccessButtons[2]);
|
||||
|
||||
expect(commands).toEqual(["save", "undo", "redo"]);
|
||||
} finally {
|
||||
window.removeEventListener("qoffice-command", onCommand);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
Share2,
|
||||
Undo2
|
||||
} from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
import type { MouseEvent, ReactNode } from "react";
|
||||
import type { QOfficeCommand } from "../hooks/useQOfficeCommand";
|
||||
import type { AppId } from "../types";
|
||||
|
||||
interface AppShellProps {
|
||||
@@ -28,6 +29,14 @@ const appTitles: Record<AppId, string> = {
|
||||
mail: "Почта - QOutlook"
|
||||
};
|
||||
|
||||
function dispatchQOfficeCommand(command: QOfficeCommand) {
|
||||
window.dispatchEvent(new CustomEvent("qoffice-command", { detail: { command } }));
|
||||
}
|
||||
|
||||
function keepActiveEditorFocus(event: MouseEvent<HTMLButtonElement>) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
export function AppShell({ activeApp, status, children }: AppShellProps) {
|
||||
const statusClassName = ["statusbar", status.className].filter(Boolean).join(" ");
|
||||
|
||||
@@ -36,13 +45,13 @@ export function AppShell({ activeApp, status, children }: AppShellProps) {
|
||||
<main className="workspace">
|
||||
<header className="titlebar">
|
||||
<div className="quick-access" aria-label="Панель быстрого доступа">
|
||||
<button type="button" title="Сохранить" aria-label="Сохранить">
|
||||
<button type="button" title="Сохранить" aria-label="Сохранить" onMouseDown={keepActiveEditorFocus} onClick={() => dispatchQOfficeCommand("save")}>
|
||||
<Save size={16} />
|
||||
</button>
|
||||
<button type="button" title="Отменить" aria-label="Отменить">
|
||||
<button type="button" title="Отменить" aria-label="Отменить" onMouseDown={keepActiveEditorFocus} onClick={() => dispatchQOfficeCommand("undo")}>
|
||||
<Undo2 size={16} />
|
||||
</button>
|
||||
<button type="button" title="Повторить" aria-label="Повторить">
|
||||
<button type="button" title="Повторить" aria-label="Повторить" onMouseDown={keepActiveEditorFocus} onClick={() => dispatchQOfficeCommand("redo")}>
|
||||
<Redo2 size={16} />
|
||||
</button>
|
||||
<button type="button" title="Настроить панель" aria-label="Настроить панель">
|
||||
|
||||
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
export {};
|
||||
|
||||
type DesktopOfficeFileKind = "docx" | "xlsx" | "pptx" | "eml" | "mail";
|
||||
type QOfficeDesktopCommand = "open" | "save" | "export-pdf" | "print";
|
||||
type QOfficeDesktopCommand = "open" | "save" | "export-pdf" | "print" | "undo" | "redo";
|
||||
|
||||
interface DesktopOpenOfficeFileResult {
|
||||
canceled: boolean;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { render, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { cleanup, render, waitFor } from "@testing-library/react";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { useQOfficeCommand } from "./useQOfficeCommand";
|
||||
|
||||
function CommandProbe({ onSave, onExportPdf }: { onSave: () => void; onExportPdf?: () => void }) {
|
||||
@@ -8,6 +8,10 @@ function CommandProbe({ onSave, onExportPdf }: { onSave: () => void; onExportPdf
|
||||
}
|
||||
|
||||
describe("useQOfficeCommand", () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it("вызывает обработчик команды QOffice", async () => {
|
||||
const onSave = vi.fn();
|
||||
render(<CommandProbe onSave={onSave} />);
|
||||
@@ -39,4 +43,23 @@ describe("useQOfficeCommand", () => {
|
||||
await waitFor(() => expect(onExportPdf).toHaveBeenCalledTimes(1));
|
||||
expect(onSave).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("использует команду документа для undo и redo без обработчика приложения", async () => {
|
||||
const originalExecCommand = document.execCommand;
|
||||
const execCommand = vi.fn();
|
||||
Object.defineProperty(document, "execCommand", { configurable: true, value: execCommand });
|
||||
|
||||
try {
|
||||
render(<CommandProbe onSave={vi.fn()} />);
|
||||
|
||||
window.dispatchEvent(new CustomEvent("qoffice-command", { detail: { command: "undo" } }));
|
||||
window.dispatchEvent(new CustomEvent("qoffice-command", { detail: { command: "redo" } }));
|
||||
|
||||
await waitFor(() => expect(execCommand).toHaveBeenCalledWith("redo"));
|
||||
expect(execCommand).toHaveBeenNthCalledWith(1, "undo");
|
||||
expect(execCommand).toHaveBeenNthCalledWith(2, "redo");
|
||||
} finally {
|
||||
Object.defineProperty(document, "execCommand", { configurable: true, value: originalExecCommand });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
export type QOfficeCommand = "open" | "save" | "export-pdf" | "print";
|
||||
export type QOfficeCommand = "open" | "save" | "export-pdf" | "print" | "undo" | "redo";
|
||||
export type QOfficeCommandHandlers = Partial<Record<QOfficeCommand, () => void | Promise<void>>>;
|
||||
|
||||
export function useQOfficeCommand(handlers: QOfficeCommandHandlers) {
|
||||
@@ -16,6 +16,11 @@ export function useQOfficeCommand(handlers: QOfficeCommandHandlers) {
|
||||
const handler = handlersRef.current[command];
|
||||
if (handler) {
|
||||
void handler();
|
||||
return;
|
||||
}
|
||||
|
||||
if (command === "undo" || command === "redo") {
|
||||
globalThis.document.execCommand?.(command);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user