From fd177358eac86de8b1d40a82c23899fb1a8176cc Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Thu, 4 Apr 2024 22:48:34 +0000 Subject: [PATCH] feat: comment collapse event (#7949) * feat: define comment collapse event * feat: add firing collapse events * chore: add round-trip test * chore: add tests for firing collapse events * chore: format --- core/comments/workspace_comment.ts | 10 ++ core/events/events.ts | 6 ++ core/events/events_comment_collapse.ts | 103 +++++++++++++++++++++ core/events/utils.ts | 3 + tests/mocha/event_comment_collapse_test.js | 33 +++++++ tests/mocha/index.html | 1 + tests/mocha/workspace_comment_test.js | 39 ++++++++ 7 files changed, 195 insertions(+) create mode 100644 core/events/events_comment_collapse.ts create mode 100644 tests/mocha/event_comment_collapse_test.js diff --git a/core/comments/workspace_comment.ts b/core/comments/workspace_comment.ts index 6226e0176..faa167a3e 100644 --- a/core/comments/workspace_comment.ts +++ b/core/comments/workspace_comment.ts @@ -82,6 +82,15 @@ export class WorkspaceComment { } } + /** Fires a comment collapse event. */ + private fireCollapseEvent(newCollapsed: boolean) { + if (eventUtils.isEnabled()) { + eventUtils.fire( + new (eventUtils.get(eventUtils.COMMENT_COLLAPSE))(this, newCollapsed), + ); + } + } + /** Sets the text of the comment. */ setText(text: string) { const oldText = this.text; @@ -107,6 +116,7 @@ export class WorkspaceComment { /** Sets whether the comment is collapsed or not. */ setCollapsed(collapsed: boolean) { this.collapsed = collapsed; + this.fireCollapseEvent(collapsed); } /** Returns whether the comment is collapsed or not. */ diff --git a/core/events/events.ts b/core/events/events.ts index 6f16387fd..bb8011755 100644 --- a/core/events/events.ts +++ b/core/events/events.ts @@ -24,6 +24,10 @@ import {CommentChange, CommentChangeJson} from './events_comment_change.js'; import {CommentCreate, CommentCreateJson} from './events_comment_create.js'; import {CommentDelete} from './events_comment_delete.js'; import {CommentMove, CommentMoveJson} from './events_comment_move.js'; +import { + CommentCollapse, + CommentCollapseJson, +} from './events_comment_collapse.js'; import {MarkerMove, MarkerMoveJson} from './events_marker_move.js'; import {Selected, SelectedJson} from './events_selected.js'; import {ThemeChange, ThemeChangeJson} from './events_theme_change.js'; @@ -73,6 +77,8 @@ export {CommentCreateJson}; export {CommentDelete}; export {CommentMove}; export {CommentMoveJson}; +export {CommentCollapse}; +export {CommentCollapseJson}; export {FinishedLoading}; export {MarkerMove}; export {MarkerMoveJson}; diff --git a/core/events/events_comment_collapse.ts b/core/events/events_comment_collapse.ts new file mode 100644 index 000000000..6646b1df2 --- /dev/null +++ b/core/events/events_comment_collapse.ts @@ -0,0 +1,103 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as registry from '../registry.js'; +import {WorkspaceComment} from '../comments/workspace_comment.js'; +import {CommentBase, CommentBaseJson} from './events_comment_base.js'; +import * as eventUtils from './utils.js'; +import type {Workspace} from '../workspace.js'; + +export class CommentCollapse extends CommentBase { + override type = eventUtils.COMMENT_COLLAPSE; + + constructor( + comment?: WorkspaceComment, + public newCollapsed?: boolean, + ) { + super(comment); + + if (!comment) { + return; // Blank event to be populated by fromJson. + } + } + + /** + * Encode the event as JSON. + * + * @returns JSON representation. + */ + override toJson(): CommentCollapseJson { + const json = super.toJson() as CommentCollapseJson; + if (this.newCollapsed === undefined) { + throw new Error( + 'The new collapse value undefined. Either call recordNew, or ' + + 'call fromJson', + ); + } + json['newCollapsed'] = this.newCollapsed; + return json; + } + + /** + * Deserializes the JSON event. + * + * @param event The event to append new properties to. Should be a subclass + * of CommentCollapse, but we can't specify that due to the fact that + * parameters to static methods in subclasses must be supertypes of + * parameters to static methods in superclasses. + * @internal + */ + static fromJson( + json: CommentCollapseJson, + workspace: Workspace, + event?: any, + ): CommentCollapse { + const newEvent = super.fromJson( + json, + workspace, + event ?? new CommentCollapse(), + ) as CommentCollapse; + newEvent.newCollapsed = json.newCollapsed; + return newEvent; + } + + /** + * Run a collapse event. + * + * @param forward True if run forward, false if run backward (undo). + */ + override run(forward: boolean) { + const workspace = this.getEventWorkspace_(); + if (!this.commentId) { + throw new Error( + 'The comment ID is undefined. Either pass a comment to ' + + 'the constructor, or call fromJson', + ); + } + // TODO: Remove cast when we update getCommentById. + const comment = workspace.getCommentById( + this.commentId, + ) as unknown as WorkspaceComment; + if (!comment) { + console.warn( + "Can't collapse or uncollapse non-existent comment: " + this.commentId, + ); + return; + } + + comment.setCollapsed(forward ? !!this.newCollapsed : !this.newCollapsed); + } +} + +export interface CommentCollapseJson extends CommentBaseJson { + newCollapsed: boolean; +} + +registry.register( + registry.Type.EVENT, + eventUtils.COMMENT_COLLAPSE, + CommentCollapse, +); diff --git a/core/events/utils.ts b/core/events/utils.ts index 469f10582..ae66f4a51 100644 --- a/core/events/utils.ts +++ b/core/events/utils.ts @@ -180,6 +180,9 @@ export const COMMENT_CHANGE = 'comment_change'; */ export const COMMENT_MOVE = 'comment_move'; +/** Type of event that moves a comment. */ +export const COMMENT_COLLAPSE = 'comment_collapse'; + /** * Name of event that records a workspace load. */ diff --git a/tests/mocha/event_comment_collapse_test.js b/tests/mocha/event_comment_collapse_test.js new file mode 100644 index 000000000..86b36b075 --- /dev/null +++ b/tests/mocha/event_comment_collapse_test.js @@ -0,0 +1,33 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + sharedTestSetup, + sharedTestTeardown, +} from './test_helpers/setup_teardown.js'; + +suite('Comment Collapse Event', function () { + setup(function () { + sharedTestSetup.call(this); + this.workspace = new Blockly.Workspace(); + }); + + teardown(function () { + sharedTestTeardown.call(this); + }); + + suite('Serialization', function () { + test('events round-trip through JSON', function () { + const comment = new Blockly.comments.WorkspaceComment(this.workspace); + const origEvent = new Blockly.Events.CommentCollapse(comment, true); + + const json = origEvent.toJson(); + const newEvent = new Blockly.Events.fromJson(json, this.workspace); + + chai.assert.deepEqual(newEvent, origEvent); + }); + }); +}); diff --git a/tests/mocha/index.html b/tests/mocha/index.html index ca2771880..7b43146f5 100644 --- a/tests/mocha/index.html +++ b/tests/mocha/index.html @@ -63,6 +63,7 @@ import './event_bubble_open_test.js'; import './event_click_test.js'; import './event_comment_change_test.js'; + import './event_comment_collapse_test.js'; import './event_comment_create_test.js'; import './event_comment_delete_test.js'; import './event_comment_move_test.js'; diff --git a/tests/mocha/workspace_comment_test.js b/tests/mocha/workspace_comment_test.js index b5658b265..977d82aa2 100644 --- a/tests/mocha/workspace_comment_test.js +++ b/tests/mocha/workspace_comment_test.js @@ -94,5 +94,44 @@ suite('Workspace comment', function () { this.workspace.id, ); }); + + test('collapse events are fired when a comment is collapsed', function () { + this.renderedComment = new Blockly.comments.RenderedWorkspaceComment( + this.workspace, + ); + const spy = createChangeListenerSpy(this.workspace); + + this.renderedComment.setCollapsed(true); + + assertEventFired( + spy, + Blockly.Events.CommentCollapse, + { + commentId: this.renderedComment.id, + newCollapsed: true, + }, + this.workspace.id, + ); + }); + + test('collapse events are fired when a comment is uncollapsed', function () { + this.renderedComment = new Blockly.comments.RenderedWorkspaceComment( + this.workspace, + ); + this.renderedComment.setCollapsed(true); + const spy = createChangeListenerSpy(this.workspace); + + this.renderedComment.setCollapsed(false); + + assertEventFired( + spy, + Blockly.Events.CommentCollapse, + { + commentId: this.renderedComment.id, + newCollapsed: false, + }, + this.workspace.id, + ); + }); }); });