feat(files): a press anywhere on a file's row plays it
A table row (and its narrow card) can carry its object's default action: a click or tap on the row runs it, a press on a control inside the row, the click that ends a text selection and a drag do not.
This commit is contained in:
@@ -1390,3 +1390,61 @@ describe("Files · a file watched part way", () => {
|
||||
expect(await screen.findByText("playing 3")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
// The row is the click (and tap) surface for its file's default action, as the name always was: a
|
||||
// press anywhere on it plays. A press on one of its own controls is that control's, and a click that
|
||||
// ends a selection or a drag across the row opens nothing (DESIGN "Text stays selectable").
|
||||
describe("Files · a press on the row plays it", () => {
|
||||
const sizeCellOf = (text: string) =>
|
||||
[...rowOf(text).querySelectorAll<HTMLElement>("td")].find((td) => !td.querySelector("button"))!;
|
||||
|
||||
it("plays the file from a click on the row, not only on its name", async () => {
|
||||
mocks.files = [file(1)];
|
||||
mocks.folders = [YT];
|
||||
mount();
|
||||
fireEvent.click(sizeCellOf("Clip 1"));
|
||||
expect(await screen.findByText("playing 1")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("leaves a press on the row's own control to that control", async () => {
|
||||
mocks.files = [file(1)];
|
||||
mocks.folders = [YT];
|
||||
mount();
|
||||
// Share opens its own dialog; the row must not start the player under it.
|
||||
const share = rowOf("Clip 1").querySelector<HTMLElement>(
|
||||
'button[aria-label="files.actions.share"]',
|
||||
)!;
|
||||
fireEvent.click(share);
|
||||
expect(await screen.findByRole("dialog")).toBeTruthy();
|
||||
expect(screen.queryByText("playing 1")).toBeNull();
|
||||
});
|
||||
|
||||
it("opens nothing for a press that travelled across the row", async () => {
|
||||
mocks.files = [file(1)];
|
||||
mocks.folders = [YT];
|
||||
mount();
|
||||
const cell = sizeCellOf("Clip 1");
|
||||
// jsdom has no PointerEvent: the press's coordinates are set on the event by hand.
|
||||
const down = createEvent.pointerDown(cell);
|
||||
Object.defineProperties(down, { clientX: { value: 10 }, clientY: { value: 10 } });
|
||||
fireEvent(cell, down);
|
||||
fireEvent.click(cell, { clientX: 60, clientY: 10, detail: 1 });
|
||||
await act(async () => {});
|
||||
expect(screen.queryByText("playing 1")).toBeNull();
|
||||
});
|
||||
|
||||
it("opens nothing for the click that ends selecting the row's words", async () => {
|
||||
mocks.files = [file(1)];
|
||||
mocks.folders = [YT];
|
||||
mount();
|
||||
const cell = sizeCellOf("Clip 1");
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(rowOf("Clip 1"));
|
||||
window.getSelection()!.removeAllRanges();
|
||||
window.getSelection()!.addRange(range);
|
||||
fireEvent.click(cell, { detail: 1 });
|
||||
await act(async () => {});
|
||||
window.getSelection()!.removeAllRanges();
|
||||
expect(screen.queryByText("playing 1")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -690,6 +690,8 @@ export default function Files() {
|
||||
narrowedBy={q}
|
||||
pageScope={pageScope}
|
||||
rowProps={(f) => ({ ...rowMenu.triggerProps(f), "data-file-id": f.id })}
|
||||
// A press anywhere on the row does what its name does: plays, or opens the viewer.
|
||||
rowOpen={openOf}
|
||||
// `group`: the row's quick icons show while it is hovered or holds the focus.
|
||||
rowClassName={(f) =>
|
||||
ringed === f.id ? "group bg-accent/8 ring-2 ring-inset ring-accent/60" : "group"
|
||||
|
||||
Reference in New Issue
Block a user