Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX Always include unsaved anchors from the current WYSIWYG field #2742

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/dist/js/TinyMCE_sslink-anchor.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/dist/js/bundle.js

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions client/src/components/AnchorSelectorField/AnchorSelectorField.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ class AnchorSelectorField extends SilverStripeComponent {
return Promise.resolve();
}

// Get anchors that belong to the current field
let fieldAnchors = [];
if (props.loadingState === anchorSelectorStates.FIELD_ONLY) {
fieldAnchors = this.props.anchors;
}

// Mark page updating
props.actions.anchorSelector.beginUpdating(props.pageId);

Expand All @@ -57,9 +63,11 @@ class AnchorSelectorField extends SilverStripeComponent {
return fetch(fetchURL, { credentials: 'same-origin' })
.then(response => response.json())
.then((anchors) => {
// Fold in field anchors and ensure array has only unique values
const allAnchors = [...new Set([...anchors, ...fieldAnchors])];
// Update anchors
props.actions.anchorSelector.updated(props.pageId, anchors);
return anchors;
props.actions.anchorSelector.updated(props.pageId, allAnchors);
return allAnchors;
})
.catch((error) => {
props.actions.anchorSelector.updateFailed(props.pageId);
Expand Down Expand Up @@ -174,6 +182,7 @@ function mapStateToProps(state, ownProps) {
&& (
page.loadingState === anchorSelectorStates.SUCCESS
|| page.loadingState === anchorSelectorStates.DIRTY
|| page.loadingState === anchorSelectorStates.FIELD_ONLY
)
) {
// eslint-disable-next-line prefer-destructuring
Expand Down
10 changes: 9 additions & 1 deletion client/src/legacy/TinyMCE_sslink-anchor.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import jQuery from 'jquery';
import ShortcodeSerialiser from 'lib/ShortcodeSerialiser';
import { createInsertLinkModal } from 'containers/InsertLinkModal/InsertLinkModal';
import { provideInjector } from 'lib/Injector';
import { updated } from '../state/anchorSelector/AnchorSelectorActions';
import { updatedCurrentField } from '../state/anchorSelector/AnchorSelectorActions';

const commandName = 'sslinkanchor';

Expand All @@ -30,6 +30,14 @@ const plugin = {
init(editor) {
editor.addCommand(commandName, () => {
const field = jQuery(`#${editor.id}`).entwine('ss');
// Get the anchors in the current field and save them as props for AnchorSelectorField
const currentPageID = Number(jQuery('#Form_EditForm_ID').val() || 0);
const validTargets = editor
.$('[id],[name]', editor.getBody())
.toArray()
.map((element) => element.id || element.name);
ss.store.dispatch(updatedCurrentField(currentPageID, validTargets, editor.id));
// Open the anchor link form
field.openLinkAnchorDialog();
});
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export default {
ANCHORSELECTOR_CURRENT_FIELD: 'ANCHORSELECTOR_CURRENT_FIELD',
ANCHORSELECTOR_UPDATED: 'ANCHORSELECTOR_UPDATED',
ANCHORSELECTOR_UPDATING: 'ANCHORSELECTOR_UPDATING',
ANCHORSELECTOR_UPDATE_FAILED: 'ANCHORSELECTOR_UPDATE_FAILED',
Expand Down
17 changes: 17 additions & 0 deletions client/src/state/anchorSelector/AnchorSelectorActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,23 @@ export function updated(pageId, anchors, cacheResult = false) {
};
}

/**
* Get the anchors that belong in a specific field.
* The server doesn't know about anchors that haven't been saved yet, so this allows
* a WYSIWYG field to register its own anchors.
*
* @param {Number} pageId - ID of page to query for
* @param {Array} anchors - List of anchor strings
* @param {String} fieldID - ID of the field these anchors belong to
* @returns {Object}
*/
export function updatedCurrentField(pageId, anchors, fieldID) {
return {
type: ACTION_TYPES.ANCHORSELECTOR_CURRENT_FIELD,
payload: { pageId, anchors, fieldID },
};
}

/**
* Mark a tree as failed
*
Expand Down
5 changes: 5 additions & 0 deletions client/src/state/anchorSelector/AnchorSelectorReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ export default function anchorSelectorReducer(state = initialState, action = nul
return updatePage(newSelectorLoadingState, anchors);
}

case ACTION_TYPES.ANCHORSELECTOR_CURRENT_FIELD: {
const { anchors } = action.payload;
return updatePage(anchorSelectorStates.FIELD_ONLY, anchors);
}

case ACTION_TYPES.ANCHORSELECTOR_UPDATE_FAILED: {
return updatePage(anchorSelectorStates.FAILED, []);
}
Expand Down
1 change: 1 addition & 0 deletions client/src/state/anchorSelector/AnchorSelectorStates.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export default {
SUCCESS: 'SUCCESS', // Done, or nothing selected
DIRTY: 'DIRTY', // Needs updating
FIELD_ONLY: 'FIELD_ONLY', // Has anchors from the field but needs to fetch the rest from server
UPDATING: 'UPDATING',
FAILED: 'FAILED',
};
18 changes: 0 additions & 18 deletions tests/behat/features/insert-a-link.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ So that I can link to a external website or a page on my site
Background:
Given a "page" "Home"
And a "page" "About Us" has the "Content" "<p>My awesome content</p>"
And a "page" "Details" has the "Content" "<p>My sub-par content<a name="youranchor"></a></p>"
And a "file" "file1.jpg"
And I am logged in with "ADMIN" permissions
And I go to "/admin/pages"
Expand All @@ -26,23 +25,6 @@ So that I can link to a external website or a page on my site
# Required to avoid "unsaved changes" browser dialog
Then I press the "Save" button

Scenario: I can link to an anchor in an internal page
When I select "awesome" in the "Content" HTML field
And I press the "Insert link" HTML field button
And I click "Anchor on a page" in the ".mce-menu" element
Then I should see an "form#Form_editorAnchorLink" element
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
When I click "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
And I click "Details" in the "#Form_editorAnchorLink_PageID_Holder .Select-menu-outer" element
And I click "Select or enter anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-multi-value-wrapper" element
And I click "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
Then I should see "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-value" element
When I fill in "my desc" for "Link description"
And I press the "Insert" button
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link,id=3]#youranchor">awesome</a>"
# Required to avoid "unsaved changes" browser dialog
Then I press the "Save" button

Scenario: I can edit a link to an internal page
Given I fill in the "Content" HTML field with "<a title='my desc' href='[sitetree_link,id=2]'>awesome</a>"
And I select "awesome" in the "Content" HTML field
Expand Down
65 changes: 65 additions & 0 deletions tests/behat/features/insert-anchor-link.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
@retry
Feature: Insert links into a page
As a cms author
I want to insert a link into my content
So that I can link to a external website or a page on my site

Background:
Given I add an extension "SilverStripe\CMS\Tests\Behaviour\AdditionalAnchorPageExtension" to the "Page" class
And a "page" "Home"
And a "page" "About Us" has the "Content" "<p>My awesome content</p>"
And a "page" "Details" has the "Content" "<p>My sub-par content<a name="youranchor"></a></p>"
And I am logged in with "ADMIN" permissions
And I go to "/admin/pages"
And I click on "About Us" in the tree

Scenario: I can link to an anchor in an internal page
When I select "awesome" in the "Content" HTML field
And I press the "Insert link" HTML field button
And I click "Anchor on a page" in the ".mce-menu" element
Then I should see an "form#Form_editorAnchorLink" element
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
When I click "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
And I click "Details" in the "#Form_editorAnchorLink_PageID_Holder .Select-menu-outer" element
And I click "Select or enter anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-multi-value-wrapper" element
And I click "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
Then I should see "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-value" element
When I fill in "my desc" for "Link description"
And I press the "Insert" button
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link,id=3]#youranchor">awesome</a>"
# Required to avoid "unsaved changes" browser dialog
Then I press the "Save" button

Scenario: I can link to an anchor from a dataobject on the current page
When I select "awesome" in the "Content" HTML field
And I press the "Insert link" HTML field button
And I click "Anchor on a page" in the ".mce-menu" element
Then I should see an "form#Form_editorAnchorLink" element
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
When I click "Select or enter anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-multi-value-wrapper" element
Then I should see "dataobject-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
When I click "dataobject-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
Then I should see "dataobject-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-value" element
When I fill in "my desc" for "Link description"
And I press the "Insert" button
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link,id=2]#dataobject-anchor">awesome</a>"
# Required to avoid "unsaved changes" browser dialog
Then I press the "Save" button

Scenario: I can link to an unsaved anchor in the current page
Given I fill in the "Content" HTML field with "<p>My awesome content</p><p><a id='unsaved-anchor'></a>unsaved content</p>"
When I select "awesome" in the "Content" HTML field
And I press the "Insert link" HTML field button
And I click "Anchor on a page" in the ".mce-menu" element
Then I should see an "form#Form_editorAnchorLink" element
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
When I click "Select or enter anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-multi-value-wrapper" element
Then I should see "unsaved-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
And I should see "dataobject-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
When I click "unsaved-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
Then I should see "unsaved-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-value" element
When I fill in "my desc" for "Link description"
And I press the "Insert" button
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link,id=2]#unsaved-anchor">awesome</a>"
# Required to avoid "unsaved changes" browser dialog
Then I press the "Save" button
Loading