Skip to content

Commit 3601cd1

Browse files
committed
feat(actions): enhance group selection logic for edit actions and update tests
1 parent 4049fb7 commit 3601cd1

4 files changed

Lines changed: 60 additions & 5 deletions

File tree

src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3683,6 +3683,7 @@ export default function App() {
36833683
hasNotebooks: notebookCount > 0,
36843684
hasMetadataClipboard,
36853685
selectionHasRoi,
3686+
selectedGroupCount: selectedGroupIds.length,
36863687
}),
36873688
[
36883689
status,
@@ -3694,6 +3695,7 @@ export default function App() {
36943695
notebookCount,
36953696
hasMetadataClipboard,
36963697
selectionHasRoi,
3698+
selectedGroupIds,
36973699
],
36983700
);
36993701

src/actions/registry.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,13 @@ export function buildStaticActions(
213213
menuPath: "Edit/Rename",
214214
iconUrl: getEditIconUrl("rename.svg"),
215215
beginGroup: true,
216-
enabled: (s) => ready(s) && s.currentId !== null,
216+
// Mirror DataLab desktop's ``SelectCond.exactly_one_group_or_one_object``:
217+
// a single group, or a single object (with no group) — selecting a group
218+
// populates ``selectedIds`` with its children, so check the group count.
219+
enabled: (s) =>
220+
ready(s) &&
221+
(s.selectedGroupCount === 1 ||
222+
(s.selectedGroupCount === 0 && s.selectedIds.length === 1)),
217223
run: cb.onRenameCurrent,
218224
},
219225
{
@@ -570,11 +576,13 @@ export function buildAIAssistantActions(
570576
* | pattern | meaning | requirement beyond a selection |
571577
* | --------- | ----------------------------- | ------------------------------ |
572578
* | ``1_to_1``| one object → one object | none |
573-
* | ``n_to_1``| n objects → one object | none (operates on the set) |
579+
* | ``n_to_1``| n objects → one object | at least two selected objects |
574580
* | ``2_to_1``| object + operand → one object | a second object must exist |
575581
*
576582
* Every pattern first needs the runtime ready, not busy, and at least one
577-
* selected or current object. Keep this in sync with ``processor.py`` when a
583+
* selected or current object. ``n_to_1`` aggregations (Sum, Average, …) are
584+
* only meaningful on two or more objects, mirroring DataLab desktop's
585+
* ``SelectCond.at_least_two``. Keep this in sync with ``processor.py`` when a
578586
* new pattern is introduced.
579587
*/
580588
export function isFeatureActionEnabled(
@@ -583,6 +591,8 @@ export function isFeatureActionEnabled(
583591
): boolean {
584592
if (s.status !== "ready" || s.busy) return false;
585593
if (s.selectedIds.length === 0 && !s.currentId) return false;
594+
// ``n_to_1`` aggregations need at least two objects to combine.
595+
if (pattern === "n_to_1") return s.selectedIds.length >= 2;
586596
// ``2_to_1`` needs a second operand object to combine with the selection.
587597
if (pattern === "2_to_1") return s.hasObjects;
588598
return true;

src/actions/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ export interface ActionState {
1313
selectedIds: string[];
1414
currentId: string | null;
1515
hasObjects: boolean;
16+
/** Number of currently selected groups. Mirrors DataLab desktop's
17+
* group selection, which is exclusive with object selection. Used to
18+
* reproduce ``SelectCond.exactly_one_group_or_one_object`` (Rename),
19+
* where a single selected group counts as one target even though its
20+
* child object ids populate ``selectedIds``. */
21+
selectedGroupCount: number;
1622
/** ``true`` when at least one macro is loaded in the workspace. */
1723
hasMacros: boolean;
1824
/** ``true`` when at least one notebook is loaded in the workspace. */

tests/ts/actions/registry.test.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ function makeState(over: Partial<ActionState> = {}): ActionState {
2323
hasNotebooks: false,
2424
hasMetadataClipboard: false,
2525
selectionHasRoi: false,
26+
selectedGroupCount: 0,
2627
...over,
2728
};
2829
}
@@ -85,6 +86,29 @@ describe("buildStaticActions", () => {
8586
expect(del.enabled(makeState({ selectedIds: ["x"] }))).toBe(true);
8687
});
8788

89+
it("enables ``edit.rename`` only for a single object or a single group", () => {
90+
const actions = buildStaticActions(makeStaticCallbacks());
91+
const rename = actions.find((a) => a.id === "edit.rename")!;
92+
// Nothing selected — disabled.
93+
expect(rename.enabled(makeState())).toBe(false);
94+
// Exactly one object (no group) — enabled.
95+
expect(rename.enabled(makeState({ selectedIds: ["a"] }))).toBe(true);
96+
// Several objects selected — disabled (mirrors desktop exactly_one).
97+
expect(rename.enabled(makeState({ selectedIds: ["a", "b"] }))).toBe(false);
98+
// A single group (its children populate ``selectedIds``) — enabled.
99+
expect(
100+
rename.enabled(
101+
makeState({ selectedIds: ["a", "b"], selectedGroupCount: 1 }),
102+
),
103+
).toBe(true);
104+
// Two groups selected — disabled.
105+
expect(
106+
rename.enabled(
107+
makeState({ selectedIds: ["a", "b"], selectedGroupCount: 2 }),
108+
),
109+
).toBe(false);
110+
});
111+
88112
it("enables ``file.save_workspace_h5`` for any persistable content", () => {
89113
const actions = buildStaticActions(makeStaticCallbacks());
90114
const save = actions.find((a) => a.id === "file.save_workspace_h5")!;
@@ -240,10 +264,23 @@ describe("isFeatureActionEnabled", () => {
240264
);
241265
});
242266

243-
it("enables 1-to-1 and n-to-1 as soon as something is selected", () => {
267+
it("enables 1-to-1 as soon as something is selected", () => {
244268
const sel = makeState({ currentId: "a" });
245269
expect(isFeatureActionEnabled("1_to_1", sel)).toBe(true);
246-
expect(isFeatureActionEnabled("n_to_1", sel)).toBe(true);
270+
});
271+
272+
it("requires at least two selected objects for n-to-1 features", () => {
273+
// A single selection (current or one selected id) is not enough:
274+
// aggregations like Sum/Average/Product need two or more objects.
275+
expect(
276+
isFeatureActionEnabled("n_to_1", makeState({ currentId: "a" })),
277+
).toBe(false);
278+
expect(
279+
isFeatureActionEnabled("n_to_1", makeState({ selectedIds: ["a"] })),
280+
).toBe(false);
281+
expect(
282+
isFeatureActionEnabled("n_to_1", makeState({ selectedIds: ["a", "b"] })),
283+
).toBe(true);
247284
});
248285
});
249286

0 commit comments

Comments
 (0)