fix(accounts): isolate action menu interactions
Stop double-click events on account action buttons before they reach editable table rows. Simplify the account menu heading to a single hoster and access-type label without empty identity separators, and cover both interaction and formatting paths.
This commit is contained in:
@@ -114,6 +114,7 @@ import {
|
|||||||
projectAccountRows,
|
projectAccountRows,
|
||||||
resolveHistoryRetentionSelection,
|
resolveHistoryRetentionSelection,
|
||||||
sortAccountRows,
|
sortAccountRows,
|
||||||
|
formatAccountContextHeading,
|
||||||
type AccountAddOption,
|
type AccountAddOption,
|
||||||
type AccountRowSource,
|
type AccountRowSource,
|
||||||
type SettingsFormViewModel,
|
type SettingsFormViewModel,
|
||||||
@@ -5982,8 +5983,7 @@ export function App(): ReactElement {
|
|||||||
y={accountContextMenu.y}
|
y={accountContextMenu.y}
|
||||||
>
|
>
|
||||||
<div className="account-context-heading">
|
<div className="account-context-heading">
|
||||||
<strong>{activeAccountContextRow.hosterLabel}</strong>
|
<strong>{formatAccountContextHeading(activeAccountContextRow.hosterLabel, activeAccountContextRow.modeLabel)}</strong>
|
||||||
<span>{activeAccountContextRow.modeLabel} · {activeAccountContextRow.username}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="ctx-menu-sep" />
|
<div className="ctx-menu-sep" />
|
||||||
<button className="ctx-menu-item" onClick={() => { setAccountContextMenu(null); openEditAccountDialog(activeAccountContextRow); }}>
|
<button className="ctx-menu-item" onClick={() => { setAccountContextMenu(null); openEditAccountDialog(activeAccountContextRow); }}>
|
||||||
|
|||||||
@@ -356,13 +356,14 @@ function AccountRow({
|
|||||||
aria-label={`${row.hoster} Aktionen`}
|
aria-label={`${row.hoster} Aktionen`}
|
||||||
className="settings-account-action-button"
|
className="settings-account-action-button"
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const rect = event.currentTarget.getBoundingClientRect();
|
const rect = event.currentTarget.getBoundingClientRect();
|
||||||
actions.onContextMenu(row.id, rect.right, rect.bottom);
|
actions.onContextMenu(row.id, rect.right, rect.bottom);
|
||||||
}}
|
}}
|
||||||
type="button"
|
onDoubleClick={(event) => event.stopPropagation()}
|
||||||
>⋯</button>
|
type="button"
|
||||||
|
>⋯</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -72,6 +72,10 @@ export function getAccountTableMinWidth(widths: AccountTableColumnWidths): numbe
|
|||||||
return 42 + 64 + ACCOUNT_TABLE_COLUMN_IDS.reduce((sum, id) => sum + widths[id], 0);
|
return 42 + 64 + ACCOUNT_TABLE_COLUMN_IDS.reduce((sum, id) => sum + widths[id], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatAccountContextHeading(hoster: string, mode: string): string {
|
||||||
|
return `${hoster} | ${mode}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function getSettingsSaveLabel(state: SettingsSaveState): string {
|
export function getSettingsSaveLabel(state: SettingsSaveState): string {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case "dirty":
|
case "dirty":
|
||||||
|
|||||||
@@ -794,6 +794,7 @@ describe("account workspace", () => {
|
|||||||
checkbox.props.onChange();
|
checkbox.props.onChange();
|
||||||
row.props.onDoubleClick();
|
row.props.onDoubleClick();
|
||||||
actionButton.props.onClick({ stopPropagation: () => {}, currentTarget: { getBoundingClientRect: () => ({ right: 20, bottom: 30 }) } });
|
actionButton.props.onClick({ stopPropagation: () => {}, currentTarget: { getBoundingClientRect: () => ({ right: 20, bottom: 30 }) } });
|
||||||
|
actionButton.props.onDoubleClick({ stopPropagation: () => calls.push("action-double-click-stopped") });
|
||||||
|
|
||||||
expect(calls).toEqual([
|
expect(calls).toEqual([
|
||||||
`select:${rowId}:false`,
|
`select:${rowId}:false`,
|
||||||
@@ -801,10 +802,21 @@ describe("account workspace", () => {
|
|||||||
`select:${rowId}:false`,
|
`select:${rowId}:false`,
|
||||||
`toggle:${rowId}`,
|
`toggle:${rowId}`,
|
||||||
`edit:${rowId}`,
|
`edit:${rowId}`,
|
||||||
`context:${rowId}`
|
`context:${rowId}`,
|
||||||
|
"action-double-click-stopped"
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("formats the account action heading as hoster and access type without a dangling marker", () => {
|
||||||
|
const api = settingsModel as typeof settingsModel & {
|
||||||
|
formatAccountContextHeading?: (hoster: string, mode: string) => string;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(api.formatAccountContextHeading).toBeTypeOf("function");
|
||||||
|
expect(api.formatAccountContextHeading?.("Debrid-Link", "API")).toBe("Debrid-Link | API");
|
||||||
|
expect(api.formatAccountContextHeading?.("Mega-Debrid", "Web-Login")).toBe("Mega-Debrid | Web-Login");
|
||||||
|
});
|
||||||
|
|
||||||
it("shows the selected account count", () => {
|
it("shows the selected account count", () => {
|
||||||
const model = workspaceModel();
|
const model = workspaceModel();
|
||||||
const tree = AccountWorkspace({
|
const tree = AccountWorkspace({
|
||||||
|
|||||||
Reference in New Issue
Block a user