From b22741dd9739d32b0042feed8e1c1c878794bcca Mon Sep 17 00:00:00 2001 From: Tim Fischbach Date: Thu, 16 Jan 2025 13:12:32 +0100 Subject: [PATCH] Highlight and select teasers from sidebar REDMINE-20852, REDMINE-20896 --- .../models/ExternalLinkCollection-spec.js | 36 ++++++++++++ .../frontend/ExternalLinkList-spec.js | 56 +++++++++++++++++++ .../editor/SidebarEditLinkView.js | 3 + .../editor/SidebarListView.js | 5 ++ .../editor/models/ExternalLinkModel.js | 13 +++++ .../externalLinkList/frontend/ExternalLink.js | 1 + .../frontend/ExternalLink.module.css | 1 + .../frontend/ExternalLinkList.js | 15 +++++ 8 files changed, 130 insertions(+) diff --git a/entry_types/scrolled/package/spec/contentElements/externalLinkList/editor/models/ExternalLinkCollection-spec.js b/entry_types/scrolled/package/spec/contentElements/externalLinkList/editor/models/ExternalLinkCollection-spec.js index 93bb9ed44..a01b9376d 100644 --- a/entry_types/scrolled/package/spec/contentElements/externalLinkList/editor/models/ExternalLinkCollection-spec.js +++ b/entry_types/scrolled/package/spec/contentElements/externalLinkList/editor/models/ExternalLinkCollection-spec.js @@ -116,6 +116,42 @@ describe('ExternalLinkCollection', () => { expect(listener).toHaveBeenCalledTimes(1); }); + it('posts content element command on highlight', () => { + const contentElement = factories.contentElement({ + id: 10, + configuration: { + links: [ + {id: 1}, + ] + } + }); + const itemsCollection = ExternalLinkCollection.forContentElement(contentElement); + const listener = jest.fn(); + + contentElement.on('postCommand', listener); + itemsCollection.get(1).highlight(); + + expect(listener).toHaveBeenCalledWith(10, {type: 'HIGHLIGHT_ITEM', index: 0}); + }); + + it('posts content element command on resetHighlight', () => { + const contentElement = factories.contentElement({ + id: 10, + configuration: { + links: [ + {id: 1}, + ] + } + }); + const itemsCollection = ExternalLinkCollection.forContentElement(contentElement); + const listener = jest.fn(); + + contentElement.on('postCommand', listener); + itemsCollection.get(1).resetHighlight(); + + expect(listener).toHaveBeenCalledWith(10, {type: 'RESET_ITEM_HIGHLIGHT'}); + }); + it('return empty title by default', () => { const contentElement = factories.contentElement({ id: 10, diff --git a/entry_types/scrolled/package/spec/contentElements/externalLinkList/frontend/ExternalLinkList-spec.js b/entry_types/scrolled/package/spec/contentElements/externalLinkList/frontend/ExternalLinkList-spec.js index acae388c4..1f09bb4ea 100644 --- a/entry_types/scrolled/package/spec/contentElements/externalLinkList/frontend/ExternalLinkList-spec.js +++ b/entry_types/scrolled/package/spec/contentElements/externalLinkList/frontend/ExternalLinkList-spec.js @@ -128,6 +128,62 @@ describe('ExternalLinkList', () => { expect(container.querySelector(`.${linkStyles.selected}`)).toBeNull(); expect(setTransientState).toHaveBeenCalledWith({selectedItemId: null}) }); + + it('supports highlighting item via command', () => { + const configuration = { + links: [ + {id: 1} + ] + }; + + const {container, triggerEditorCommand} = renderInContentElement( + , + { + editorState: {isSelected: true, isEditable: true} + } + ); + triggerEditorCommand({type: 'HIGHLIGHT_ITEM', index: 0}); + + expect(container.querySelector(`.${linkStyles.highlighted}`)).not.toBeNull(); + }); + + it('supports resetting item highlight via command', () => { + const configuration = { + links: [ + {id: 1} + ] + }; + + const {container, triggerEditorCommand} = renderInContentElement( + , + { + editorState: {isSelected: true, isEditable: true} + } + ); + + triggerEditorCommand({type: 'HIGHLIGHT_ITEM', index: 0}); + triggerEditorCommand({type: 'RESET_ITEM_HIGHLIGHT'}); + + expect(container.querySelector(`.${linkStyles.highlighted}`)).toBeNull(); + }); + + it('supports setting selected item via command', () => { + const configuration = { + links: [ + {id: 1} + ] + }; + + const {container, triggerEditorCommand} = renderInContentElement( + , + { + editorState: {isSelected: true, isEditable: true} + } + ); + triggerEditorCommand({type: 'SET_SELECTED_ITEM', index: 0}); + + expect(container.querySelector(`.${linkStyles.selected}`)).not.toBeNull(); + }); }); function value(text) { diff --git a/entry_types/scrolled/package/src/contentElements/externalLinkList/editor/SidebarEditLinkView.js b/entry_types/scrolled/package/src/contentElements/externalLinkList/editor/SidebarEditLinkView.js index 2ba786384..08bc308ae 100644 --- a/entry_types/scrolled/package/src/contentElements/externalLinkList/editor/SidebarEditLinkView.js +++ b/entry_types/scrolled/package/src/contentElements/externalLinkList/editor/SidebarEditLinkView.js @@ -61,6 +61,9 @@ export const SidebarEditLinkView = Marionette.Layout.extend({ this.formContainer.show(configurationEditor); }, goBack: function() { + this.options.contentElement.postCommand({type: 'SET_SELECTED_ITEM', + index: -1}); + editor.navigate(`/scrolled/content_elements/${this.options.contentElement.get('id')}`, {trigger: true}); }, destroyLink: function () { diff --git a/entry_types/scrolled/package/src/contentElements/externalLinkList/editor/SidebarListView.js b/entry_types/scrolled/package/src/contentElements/externalLinkList/editor/SidebarListView.js index bea01b691..b778c04bd 100644 --- a/entry_types/scrolled/package/src/contentElements/externalLinkList/editor/SidebarListView.js +++ b/entry_types/scrolled/package/src/contentElements/externalLinkList/editor/SidebarListView.js @@ -30,6 +30,8 @@ export const SidebarListView = Marionette.Layout.extend({ this.linksContainer.show(new ListView({ collection: this.collection, sortable: true, + highlight: true, + onEdit: _.bind(this.onEdit, this), onRemove: _.bind(this.onRemove, this) })); @@ -40,6 +42,9 @@ export const SidebarListView = Marionette.Layout.extend({ }, onEdit: function (linkModel) { + this.options.contentElement.postCommand({type: 'SET_SELECTED_ITEM', + index: this.collection.indexOf(linkModel)}); + editor.navigate(`/scrolled/external_links/${this.options.contentElement.id}/${linkModel.id}`, {trigger: true}); }, diff --git a/entry_types/scrolled/package/src/contentElements/externalLinkList/editor/models/ExternalLinkModel.js b/entry_types/scrolled/package/src/contentElements/externalLinkList/editor/models/ExternalLinkModel.js index 37234fb3d..81aae1443 100644 --- a/entry_types/scrolled/package/src/contentElements/externalLinkList/editor/models/ExternalLinkModel.js +++ b/entry_types/scrolled/package/src/contentElements/externalLinkList/editor/models/ExternalLinkModel.js @@ -44,4 +44,17 @@ export const ExternalLinkModel = Backbone.Model.extend({ setFilePositions: function(attribute, x, y) { this.set('thumbnailCropPosition', {x, y}); }, + + highlight() { + this.collection.contentElement.postCommand({ + type: 'HIGHLIGHT_ITEM', + index: this.collection.indexOf(this) + }); + }, + + resetHighlight() { + this.collection.contentElement.postCommand({ + type: 'RESET_ITEM_HIGHLIGHT' + }); + } }); diff --git a/entry_types/scrolled/package/src/contentElements/externalLinkList/frontend/ExternalLink.js b/entry_types/scrolled/package/src/contentElements/externalLinkList/frontend/ExternalLink.js index fb71c472c..c5ffcf21e 100644 --- a/entry_types/scrolled/package/src/contentElements/externalLinkList/frontend/ExternalLink.js +++ b/entry_types/scrolled/package/src/contentElements/externalLinkList/frontend/ExternalLink.js @@ -82,6 +82,7 @@ export function ExternalLink({id, configuration, ...props}) { styles[`textPosition-${props.textPosition}`], {[styles.link]: !!href}, {[styles.outlined]: props.outlined}, + {[styles.highlighted]: props.highlighted}, {[styles.selected]: props.selected})} onClick={props.onClick}> { + if (command.type === 'HIGHLIGHT_ITEM') { + setHighlightedIndex(command.index); + } + else if (command.type === 'RESET_ITEM_HIGHLIGHT') { + setHighlightedIndex(-1); + } + else if (command.type === 'SET_SELECTED_ITEM') { + setSelectedItemId(linkList[command.index]?.id); + } + }); function handleItemClick(event, id) { if (isSelected) { @@ -86,6 +100,7 @@ export function ExternalLinkList(props) { invert={!darkBackground} loadImages={shouldLoad} outlined={isSelected} + highlighted={highlightedIndex === index} selected={link.id === selectedItemId && isSelected} onClick={event => handleItemClick(event, link.id)} /> )}