diff --git a/CUSTOM-EVENTS.md b/CUSTOM-EVENTS.md index d70ff5c64..79225a85e 100644 --- a/CUSTOM-EVENTS.md +++ b/CUSTOM-EVENTS.md @@ -23,6 +23,7 @@ If you need to override the editor's bult-in behavior, try overriding the built- - [Toolbar Custom Events](#toolbar-custom-events) - [`hideToolbar`](#hidetoolbar) - [`positionToolbar`](#positiontoolbar) + - [`positionedToolbar`](#positionedtoolbar) - [`showToolbar`](#showtoolbar) - [Proxied Custom Events](#proxied-custom-events) - [`editableClick`](#editableclick) @@ -152,6 +153,9 @@ These events are triggered by the toolbar when the toolbar extension has not bee ### `positionToolbar` `positionToolbar` is triggered each time the current selection is checked and the toolbar's position is about to be updated. This event is triggered after all of the buttons have had their state updated, but before the toolbar is moved to the correct location. This event will be triggered even if nothing will be changed about the toolbar's appearance. +### `positionedToolbar` +`positionedToolbar` is triggered each time the current selection is checked, the toolbar is displayed, and the toolbar's position was updated. This differs from the `positionToolbar` event in that the visibility and location of the toolbar has already been changed (as opposed to the event triggering before those changes occur). This event will be triggered even if nothing was changed about the toolbar's appearance. + ### `showToolbar` `showToolbar` is triggered whenever the toolbar was hidden and has just been displayed. diff --git a/spec/anchor-preview.spec.js b/spec/anchor-preview.spec.js index c621918eb..3126aae57 100644 --- a/spec/anchor-preview.spec.js +++ b/spec/anchor-preview.spec.js @@ -237,16 +237,16 @@ describe('Anchor Preview TestCase', function () { it('should be displayed when the option showWhenToolbarIsVisible is set to true and toolbar is visible', function () { var editor = this.newMediumEditor('.editor', { - delay: 200, - anchorPreview: { - showWhenToolbarIsVisible: true - }, - toolbar: { - static: true - } - }), - anchorPreview = editor.getExtensionByName('anchor-preview'), - toolbar = editor.getExtensionByName('toolbar'); + delay: 200, + anchorPreview: { + showWhenToolbarIsVisible: true + }, + toolbar: { + static: true + } + }), + anchorPreview = editor.getExtensionByName('anchor-preview'), + toolbar = editor.getExtensionByName('toolbar'); selectElementContentsAndFire(editor.elements[0].firstChild); @@ -258,21 +258,21 @@ describe('Anchor Preview TestCase', function () { jasmine.clock().tick(250); expect(anchorPreview.showPreview).toHaveBeenCalled(); expect(toolbar.isDisplayed()).toBe(true); - expect(anchorPreview.getPreviewElement().classList.contains('medium-toolbar-arrow-over')).toBe(true); + expect(anchorPreview.getPreviewElement().classList.contains('medium-editor-anchor-preview-active')).toBe(true); }); - it('should be displayed when the option showWhenToolbarIsVisible is set to true and toolbar is visible', function () { + it('should NOT be displayed when the option showWhenToolbarIsVisible is set to false and toolbar is visible', function () { var editor = this.newMediumEditor('.editor', { - delay: 200, - anchorPreview: { - showWhenToolbarIsVisible: false - }, - toolbar: { - static: true - } - }), - anchorPreview = editor.getExtensionByName('anchor-preview'), - toolbar = editor.getExtensionByName('toolbar'); + delay: 200, + anchorPreview: { + showWhenToolbarIsVisible: false + }, + toolbar: { + static: true + } + }), + anchorPreview = editor.getExtensionByName('anchor-preview'), + toolbar = editor.getExtensionByName('toolbar'); selectElementContentsAndFire(editor.elements[0].firstChild); @@ -282,10 +282,58 @@ describe('Anchor Preview TestCase', function () { // preview shows only after delay jasmine.clock().tick(250); - expect(anchorPreview.showPreview).not.toHaveBeenCalled(); expect(toolbar.isDisplayed()).toBe(true); - expect(anchorPreview.getPreviewElement().classList.contains('medium-toolbar-arrow-over')).toBe(false); + expect(anchorPreview.getPreviewElement().classList.contains('medium-editor-anchor-preview-active')).toBe(false); + }); + + // https://github.com/yabwe/medium-editor/issues/1047 + it('should display the anchor form in the toolbar when clicked when showWhenToolbarIsVisible is set to true adn toolbar is visible', function () { + var editor = this.newMediumEditor('.editor', { + anchorPreview: { + showWhenToolbarIsVisible: true + }, + toolbar: { + static: true + } + }), + anchorPreview = editor.getExtensionByName('anchor-preview'), + anchor = editor.getExtensionByName('anchor'), + toolbar = editor.getExtensionByName('toolbar'); + + // show toolbar + selectElementContentsAndFire(editor.elements[0].firstChild); + jasmine.clock().tick(1); + expect(toolbar.isDisplayed()).toBe(true); + + // show preview + fireEvent(document.getElementById('test-link'), 'mouseover'); + + // load into editor + jasmine.clock().tick(1); + expect(anchorPreview.getPreviewElement().classList.contains('medium-editor-anchor-preview-active')).toBe(true); + + var clickEvent = { + defaultPrevented: false, + preventDefault: function () { + this.defaultPrevented = true; + } + }; + + // trigger all events toolbar is listening to on clicks + fireEvent(anchorPreview.getPreviewElement(), 'mousedown'); + fireEvent(anchorPreview.getPreviewElement(), 'mouseup'); + anchorPreview.handleClick(clickEvent); + jasmine.clock().tick(1); + + // click on the link should have called `preventDefault` to stop from navigating away + expect(clickEvent.defaultPrevented).toBe(true, 'link navigation was not prevented on click of the anchor-preview'); + + // anchor form should be visible in toolbar + expect(toolbar.isDisplayed()).toBe(true); + expect(anchor.isDisplayed()).toBe(true, 'anchor form to edit link is not visible'); + expect(anchorPreview.getPreviewElement().classList.contains('medium-editor-anchor-preview-active')).toBe(false, + 'anchor-preview is still visible after being clicked'); }); it('should NOT be present when anchorPreview option is set to false', function () { diff --git a/spec/toolbar.spec.js b/spec/toolbar.spec.js index 1e159dff6..3dc7b65b9 100644 --- a/spec/toolbar.spec.js +++ b/spec/toolbar.spec.js @@ -96,7 +96,18 @@ describe('MediumEditor.extensions.toolbar TestCase', function () { selectElementContentsAndFire(this.el); expect(callback).toHaveBeenCalledWith({}, this.el); + }); + + it('should trigger positionedToolbar custom event when toolbar is moved', function () { + var editor = this.newMediumEditor('.editor'), + callback = jasmine.createSpy(); + + this.el.innerHTML = 'specOnUpdateToolbarTest'; + editor.subscribe('positionedToolbar', callback); + + selectElementContentsAndFire(this.el); + expect(callback).toHaveBeenCalledWith({}, this.el); }); it('should trigger positionToolbar before setToolbarPosition is called', function () { @@ -131,6 +142,29 @@ describe('MediumEditor.extensions.toolbar TestCase', function () { expect(toolbar.setToolbarPosition).toHaveBeenCalled(); }); + it('should trigger positionedToolbar after setToolbarPosition and showToolbar is called', function () { + this.el.innerHTML = 'position sanity check'; + var editor = this.newMediumEditor('.editor'), + toolbar = editor.getExtensionByName('toolbar'), + temp = { + update: function () { + expect(toolbar.setToolbarPosition).toHaveBeenCalled(); + expect(toolbar.showToolbar).toHaveBeenCalled(); + } + }; + + selectElementContents(this.el); + jasmine.clock().tick(1); + + spyOn(toolbar, 'setToolbarPosition').and.callThrough(); + spyOn(toolbar, 'showToolbar').and.callThrough(); + spyOn(temp, 'update').and.callThrough(); + editor.subscribe('positionedToolbar', temp.update); + selectElementContentsAndFire(this.el); + expect(temp.update).toHaveBeenCalled(); + expect(toolbar.setToolbarPosition).toHaveBeenCalled(); + }); + it('should trigger the hideToolbar custom event when toolbar is hidden', function () { var editor = this.newMediumEditor('.editor'), callback = jasmine.createSpy(); diff --git a/src/js/extensions/anchor-preview.js b/src/js/extensions/anchor-preview.js index 1a020baae..026dd5387 100644 --- a/src/js/extensions/anchor-preview.js +++ b/src/js/extensions/anchor-preview.js @@ -125,6 +125,15 @@ attachToEditables: function () { this.subscribe('editableMouseover', this.handleEditableMouseover.bind(this)); + this.subscribe('positionedToolbar', this.handlePositionedToolbar.bind(this)); + }, + + handlePositionedToolbar: function () { + // If the toolbar is visible and positioned, we don't need to hide the preview + // when showWhenToolbarIsVisible is true + if (!this.showWhenToolbarIsVisible) { + this.hidePreview(); + } }, handleClick: function (event) { diff --git a/src/js/extensions/toolbar.js b/src/js/extensions/toolbar.js index 998169ba1..33d6f13b0 100644 --- a/src/js/extensions/toolbar.js +++ b/src/js/extensions/toolbar.js @@ -514,30 +514,26 @@ setToolbarPosition: function () { var container = this.base.getFocusedElement(), - selection = this.window.getSelection(), - anchorPreview; + selection = this.window.getSelection(); // If there isn't a valid selection, bail if (!container) { return this; } - if (this.static && !this.relativeContainer) { - this.showToolbar(); - this.positionStaticToolbar(container); - } else if (!selection.isCollapsed) { + if (this.static || !selection.isCollapsed) { this.showToolbar(); // we don't need any absolute positioning if relativeContainer is set if (!this.relativeContainer) { - this.positionToolbar(selection); + if (this.static) { + this.positionStaticToolbar(container); + } else { + this.positionToolbar(selection); + } } - } - - anchorPreview = this.base.getExtensionByName('anchor-preview'); - if (anchorPreview && typeof anchorPreview.hidePreview === 'function') { - anchorPreview.hidePreview(); + this.trigger('positionedToolbar', {}, this.base.getFocusedElement()); } },