tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit caa6561733cbe52ffbd49f234eea52e6adfc86b4
parent c56b4740e9eb9d676874cec2551c67681b5a0b56
Author: Reem H <42309026+reemhamz@users.noreply.github.com>
Date:   Mon, 13 Oct 2025 20:00:30 +0000

Bug 1993862 - FIX for cancelling new list creation when pressing escape. r=home-newtab-reviewers,npypchenko

Differential Revision: https://phabricator.services.mozilla.com/D268331

Diffstat:
Mbrowser/extensions/newtab/content-src/components/Widgets/Lists/Lists.jsx | 25+++++++++++++++++++++++++
Mbrowser/extensions/newtab/data/content/activity-stream.bundle.js | 21+++++++++++++++++++++
Mbrowser/extensions/newtab/test/unit/content-src/components/Widgets/Lists.test.jsx | 66+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
3 files changed, 105 insertions(+), 7 deletions(-)

diff --git a/browser/extensions/newtab/content-src/components/Widgets/Lists/Lists.jsx b/browser/extensions/newtab/content-src/components/Widgets/Lists/Lists.jsx @@ -387,7 +387,32 @@ function Lists({ dispatch, handleUserInteraction }) { if (!selectedList?.label && selectedList?.tasks?.length === 0) { const updatedLists = { ...lists }; delete updatedLists[selected]; + + const listKeys = Object.keys(updatedLists); + const key = listKeys[listKeys.length - 1]; + batch(() => { + dispatch( + ac.AlsoToMain({ + type: at.WIDGETS_LISTS_UPDATE, + data: { lists: updatedLists }, + }) + ); + dispatch( + ac.AlsoToMain({ + type: at.WIDGETS_LISTS_CHANGE_SELECTED, + data: key, + }) + ); + dispatch( + ac.OnlyToMain({ + type: at.WIDGETS_LISTS_USER_EVENT, + data: { userAction: USER_ACTION_TYPES.LIST_DELETE }, + }) + ); + }); } + + handleListInteraction(); } function handleDeleteList() { diff --git a/browser/extensions/newtab/data/content/activity-stream.bundle.js b/browser/extensions/newtab/data/content/activity-stream.bundle.js @@ -12642,7 +12642,28 @@ function Lists({ ...lists }; delete updatedLists[selected]; + const listKeys = Object.keys(updatedLists); + const key = listKeys[listKeys.length - 1]; + (0,external_ReactRedux_namespaceObject.batch)(() => { + dispatch(actionCreators.AlsoToMain({ + type: actionTypes.WIDGETS_LISTS_UPDATE, + data: { + lists: updatedLists + } + })); + dispatch(actionCreators.AlsoToMain({ + type: actionTypes.WIDGETS_LISTS_CHANGE_SELECTED, + data: key + })); + dispatch(actionCreators.OnlyToMain({ + type: actionTypes.WIDGETS_LISTS_USER_EVENT, + data: { + userAction: USER_ACTION_TYPES.LIST_DELETE + } + })); + }); } + handleListInteraction(); } function handleDeleteList() { let updatedLists = { diff --git a/browser/extensions/newtab/test/unit/content-src/components/Widgets/Lists.test.jsx b/browser/extensions/newtab/test/unit/content-src/components/Widgets/Lists.test.jsx @@ -496,18 +496,70 @@ describe("<Lists>", () => { }); it("should cancel creating a new list when Escape key is pressed", () => { - const editList = wrapper.find("panel-item").at(0); - editList.props().onClick(); - wrapper.update(); + const newListId = "new-list-id"; + + // Provide a fallback list so CHANGE_SELECTED has somewhere to go after delete + const stateWithEmptyAndFallback = { + ...mockState, + ListsWidget: { + selected: newListId, + lists: { + [newListId]: { label: "", tasks: [], completed: [] }, // empty "new" list + "test-list": { label: "test", tasks: [], completed: [] }, // fallback + }, + }, + }; - let editableInput = wrapper.find("input.edit-list"); + const localWrapper = mount( + <WrapWithProvider state={stateWithEmptyAndFallback}> + <Lists + dispatch={dispatch} + handleUserInteraction={handleUserInteraction} + /> + </WrapWithProvider> + ); + + const editableText = localWrapper.find("EditableText").at(0); + + assert.ok(editableText.exists()); + editableText.props().setIsEditing(true); + localWrapper.update(); + + let editableInput = localWrapper.find("input.edit-list"); assert.ok(editableInput.exists()); + // Press Escape to cancel new-list creation editableInput.simulate("keyDown", { key: "Escape" }); - wrapper.update(); + localWrapper.update(); + + // Test dispatches from handleCancelNewList + const types = dispatch.getCalls().map(call => call.args[0].type); + assert.include( + types, + at.WIDGETS_LISTS_UPDATE, + "Expected update dispatch on cancel" + ); + assert.include( + types, + at.WIDGETS_LISTS_CHANGE_SELECTED, + "Expected selected list to change after cancel" + ); + assert.include( + types, + at.WIDGETS_LISTS_USER_EVENT, + "Expected telemetry event on cancel" + ); + + const listsState = localWrapper.find("Provider").prop("store").getState() + .ListsWidget.lists; + assert.strictEqual( + Object.keys(listsState).length, + 2, + "expected total lists count to remain as 2 (no new list created on cancel)" + ); - // after cancelling, the input should be gone (list creation cancelled) - editableInput = wrapper.find("input.edit-list"); + // After cancelling, the input should be gone (editing ended / list removed) + editableInput = localWrapper.find("input.edit-list"); assert.strictEqual(editableInput.exists(), false); }); });