Selaa lähdekoodia

Merge pull request #6739 from ckeditor/i/6587

Docs (mention): Add notes about using `AttributeElement`'s priority and id attribute in mentions. Closes #6587.

Other (mention): Renamed `MentionAttribute._uid` to `MentionAttribute.uid` as it needs to be used by integrators when implementing custom converters. Closes #6587.
Piotrek Koszuliński 5 vuotta sitten
vanhempi
commit
94a6952a6a

+ 5 - 0
packages/ckeditor5-mention/docs/_snippets/examples/chat-with-mentions.js

@@ -146,6 +146,11 @@ function MentionLinks( editor ) {
 				class: 'mention',
 				'data-mention': modelAttributeValue.id,
 				href
+			}, {
+				// Make mention attribute to be wrapped by other attribute elements.
+				priority: 20,
+				// Prevent merging mentions together.
+				id: modelAttributeValue.uid
 			} );
 		},
 		converterPriority: 'high'

+ 6 - 1
packages/ckeditor5-mention/docs/_snippets/features/mention-customization.js

@@ -52,7 +52,7 @@ function MentionCustomization( editor ) {
 			value: viewItem => {
 				// The mention feature expects that the mention attribute value
 				// in the model is a plain object with a set of additional attributes.
-				// In order to create a proper object, use the toMentionAttribute() helper method:
+				// In order to create a proper object use the toMentionAttribute() helper method:
 				const mentionAttribute = editor.plugins.get( 'Mention' ).toMentionAttribute( viewItem, {
 					// Add any other properties that you need.
 					link: viewItem.getAttribute( 'href' ),
@@ -79,6 +79,11 @@ function MentionCustomization( editor ) {
 				'data-mention': modelAttributeValue.id,
 				'data-user-id': modelAttributeValue.userId,
 				'href': modelAttributeValue.link
+			}, {
+				// Make mention attribute to be wrapped by other attribute elements.
+				priority: 20,
+				// Prevent merging mentions together.
+				id: modelAttributeValue.uid
 			} );
 		},
 		converterPriority: 'high'

+ 5 - 0
packages/ckeditor5-mention/docs/examples/chat-with-mentions.md

@@ -327,6 +327,11 @@ function MentionLinks( editor ) {
 				class: 'mention',
 				'data-mention': modelAttributeValue.id,
 				href
+			}, {
+				// Make mention attribute to be wrapped by other attribute elements.
+				priority: 20,
+				// Prevent merging mentions together.
+				id: modelAttributeValue.uid
 			} );
 		},
 		converterPriority: 'high'

+ 15 - 1
packages/ckeditor5-mention/docs/features/mentions.md

@@ -180,7 +180,7 @@ A full, working demo with all possible customizations and its source code is ava
 
 ### Customizing the output
 
-In order to change the markup generated by the editor for mentions, you can overwrite the default converter of the mention feature. To do that, you must specify both {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher upcast} and {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher downcast} converters.
+In order to change the markup generated by the editor for mentions, you can overwrite the default converter of the mention feature. To do that, you must specify both {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher upcast} and {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher downcast} converters using {@link module:engine/view/attributeelement~AttributeElement}.
 
 The example below defines a plugin that overrides the default output:
 
@@ -196,6 +196,10 @@ To a link:
 
 The converters must be defined with a `'high'` priority to be executed before the {@link features/link link} feature's converter and before the default converter of the mention feature. A mention is stored in the model as a {@link framework/guides/architecture/editing-engine#text-attributes text attribute} that stores an object (see {@link module:mention/mention~MentionFeedItem}).
 
+To control how the mention element is wrapped by other attribute elements (like bold, italic, etc) set its {@link module:engine/view/attributeelement~AttributeElement#priority}. To replicate default plugin behavior and make mention to be wrapped by other elements set priority to `20`.
+
+By default, attribute elements that are next to each other and have the same value will be rendered as single HTML element. To prevent this the model attribute value object expose a unique id of each inserted mention to the model as `uid`. To prevent merging subsequent mentions set it as {@link module:engine/view/attributeelement~AttributeElement#id}.
+
 **Note:** The feature prevents copying fragments of existing mentions. If only a part of a mention is selected, it will be copied as plain text. The internal converter with the {@link module:engine/conversion/conversion~ConverterDefinition `'highest'` priority} controls this behaviour; thus, we do not recommend adding mention converters with the `'highest'` priority to avoid collisions and quirky results.
 
 ```js
@@ -254,6 +258,11 @@ function MentionCustomization( editor ) {
 				'data-mention': modelAttributeValue.id,
 				'data-user-id': modelAttributeValue.userId,
 				'href': modelAttributeValue.link
+			}, {
+				// Make mention attribute to be wrapped by other attribute elements.
+				priority: 20,
+				// Prevent merging mentions together.
+				id: modelAttributeValue.uid
 			} );
 		},
 		converterPriority: 'high'
@@ -341,6 +350,11 @@ function MentionCustomization( editor ) {
 				'data-mention': modelAttributeValue.id,
 				'data-user-id': modelAttributeValue.userId,
 				'href': modelAttributeValue.link
+			}, {
+				// Make mention attribute to be wrapped by other attribute elements.
+				priority: 20,
+				// Prevent merging mentions together.
+				id: modelAttributeValue.uid
 			} );
 		},
 		converterPriority: 'high'

+ 4 - 3
packages/ckeditor5-mention/src/mention.js

@@ -29,7 +29,7 @@ export default class Mention extends Plugin {
 	 *
 	 *		// For a view element: <span data-mention="@joe">@John Doe</span>
 	 *		// it will return:
-	 *		// { id: '@joe', userId: '1234', _uid: '7a7bc7...', _text: '@John Doe' }
+	 *		// { id: '@joe', userId: '1234', uid: '7a7bc7...', _text: '@John Doe' }
 	 *
 	 * @param {module:engine/view/element~Element} viewElement
 	 * @param {String|Object} [data] Additional data to be stored in the mention attribute.
@@ -225,8 +225,9 @@ export default class Mention extends Plugin {
  * See {@link module:mention/mention~Mention#toMentionAttribute `Mention#toMentionAttribute()`}.
  *
  * @interface module:mention/mention~MentionAttribute
- * @property {String} id The ID of a mention. It identifies the mention item in the mention feed.
- * @property {String} _uid An internal mention view item ID. Should be passed as an `option.id` when using
+ * @property {String} id The ID of a mention. It identifies the mention item in the mention feed. There can be multiple mentions
+ * in the document with the same ID (e.g. the same hashtag being mentioned).
+ * @property {String} uid A unique ID of this mention instance. Should be passed as an `option.id` when using
  * {@link module:engine/view/downcastwriter~DowncastWriter#createAttributeElement writer.createAttributeElement()}.
  * @property {String} _text Helper property that stores the text of the inserted mention. Used for detecting a broken mention
  * in the editing area.

+ 2 - 2
packages/ckeditor5-mention/src/mentionediting.js

@@ -69,7 +69,7 @@ export default class MentionEditing extends Plugin {
 }
 
 export function _addMentionAttributes( baseMentionData, data ) {
-	return Object.assign( { _uid: uid() }, baseMentionData, data || {} );
+	return Object.assign( { uid: uid() }, baseMentionData, data || {} );
 }
 
 /**
@@ -142,7 +142,7 @@ function createViewMentionElement( mention, viewWriter ) {
 	};
 
 	const options = {
-		id: mention._uid,
+		id: mention.uid,
 		priority: 20
 	};
 

+ 4 - 1
packages/ckeditor5-mention/tests/manual/mention-custom-view.js

@@ -50,7 +50,10 @@ class CustomMentionAttributeView extends Plugin {
 					class: 'mention',
 					'data-mention': modelAttributeValue.id,
 					'href': modelAttributeValue.link
-				}, { id: modelAttributeValue._uid } );
+				}, {
+					priority: 20,
+					id: modelAttributeValue.uid
+				} );
 			},
 			converterPriority: 'high'
 		} );

+ 2 - 2
packages/ckeditor5-mention/tests/mention.js

@@ -62,7 +62,7 @@ describe( 'Mention', () => {
 			const mentionAttribute = editor.plugins.get( 'Mention' ).toMentionAttribute( viewElement );
 
 			expect( mentionAttribute ).to.have.property( 'id', '@John' );
-			expect( mentionAttribute ).to.have.property( '_uid' );
+			expect( mentionAttribute ).to.have.property( 'uid' );
 			expect( mentionAttribute ).to.have.property( '_text', 'John Doe' );
 		} );
 
@@ -77,7 +77,7 @@ describe( 'Mention', () => {
 
 			expect( mentionAttribute ).to.have.property( 'id', '@John' );
 			expect( mentionAttribute ).to.have.property( 'foo', 'bar' );
-			expect( mentionAttribute ).to.have.property( '_uid' );
+			expect( mentionAttribute ).to.have.property( 'uid' );
 			expect( mentionAttribute ).to.have.property( '_text', 'John Doe' );
 		} );
 

+ 1 - 1
packages/ckeditor5-mention/tests/mentioncommand.js

@@ -167,7 +167,7 @@ describe( 'MentionCommand', () => {
 
 	function assertMention( textNode, id ) {
 		expect( textNode.hasAttribute( 'mention' ) ).to.be.true;
-		expect( textNode.getAttribute( 'mention' ) ).to.have.property( '_uid' );
+		expect( textNode.getAttribute( 'mention' ) ).to.have.property( 'uid' );
 		expect( textNode.getAttribute( 'mention' ) ).to.have.property( '_text', textNode.data );
 		expect( textNode.getAttribute( 'mention' ) ).to.have.property( 'id', id );
 	}

+ 15 - 9
packages/ckeditor5-mention/tests/mentionediting.js

@@ -77,7 +77,7 @@ describe( 'MentionEditing', () => {
 			expect( textNode.hasAttribute( 'mention' ) ).to.be.true;
 			expect( textNode.getAttribute( 'mention' ) ).to.have.property( 'id', '@John' );
 			expect( textNode.getAttribute( 'mention' ) ).to.have.property( '_text', '@John' );
-			expect( textNode.getAttribute( 'mention' ) ).to.have.property( '_uid' );
+			expect( textNode.getAttribute( 'mention' ) ).to.have.property( 'uid' );
 
 			const expectedView = '<p>foo <span class="mention" data-mention="@John">@John</span> bar</p>';
 
@@ -96,7 +96,7 @@ describe( 'MentionEditing', () => {
 			expect( textNode.hasAttribute( 'mention' ) ).to.be.true;
 			expect( textNode.getAttribute( 'mention' ) ).to.have.property( 'id', '@Ted Mosby' );
 			expect( textNode.getAttribute( 'mention' ) ).to.have.property( '_text', 'Ted Mosby' );
-			expect( textNode.getAttribute( 'mention' ) ).to.have.property( '_uid' );
+			expect( textNode.getAttribute( 'mention' ) ).to.have.property( 'uid' );
 
 			const expectedView = '<p>Hello <b class="mention" data-mention="@Ted Mosby">Ted Mosby</b></p>';
 
@@ -122,8 +122,8 @@ describe( 'MentionEditing', () => {
 			assertTextNode( paragraph.getChild( 0 ) );
 			assertTextNode( paragraph.getChild( 1 ) );
 
-			const firstMentionId = paragraph.getChild( 0 ).getAttribute( 'mention' )._uid;
-			const secondMentionId = paragraph.getChild( 1 ).getAttribute( 'mention' )._uid;
+			const firstMentionId = paragraph.getChild( 0 ).getAttribute( 'mention' ).uid;
+			const secondMentionId = paragraph.getChild( 1 ).getAttribute( 'mention' ).uid;
 
 			expect( firstMentionId ).to.not.equal( secondMentionId );
 
@@ -138,7 +138,7 @@ describe( 'MentionEditing', () => {
 				expect( textNode.hasAttribute( 'mention' ) ).to.be.true;
 				expect( textNode.getAttribute( 'mention' ) ).to.have.property( 'id', '@John' );
 				expect( textNode.getAttribute( 'mention' ) ).to.have.property( '_text', '@John' );
-				expect( textNode.getAttribute( 'mention' ) ).to.have.property( '_uid' );
+				expect( textNode.getAttribute( 'mention' ) ).to.have.property( 'uid' );
 			}
 		} );
 
@@ -151,7 +151,7 @@ describe( 'MentionEditing', () => {
 			expect( textNode.hasAttribute( 'mention' ) ).to.be.true;
 			expect( textNode.getAttribute( 'mention' ) ).to.have.property( 'id', '@John' );
 			expect( textNode.getAttribute( 'mention' ) ).to.have.property( '_text', '@Jo' );
-			expect( textNode.getAttribute( 'mention' ) ).to.have.property( '_uid' );
+			expect( textNode.getAttribute( 'mention' ) ).to.have.property( 'uid' );
 
 			const expectedView = '<p><span class="mention" data-mention="@John">@Jo</span></p>';
 
@@ -197,7 +197,10 @@ describe( 'MentionEditing', () => {
 						class: 'mention',
 						'data-mention': modelAttributeValue.id,
 						'href': modelAttributeValue.link
-					}, { id: modelAttributeValue._uid } );
+					}, {
+						priority: 20,
+						id: modelAttributeValue.uid
+					} );
 				},
 				converterPriority: 'high'
 			} );
@@ -312,7 +315,7 @@ describe( 'MentionEditing', () => {
 			expect( textNode.hasAttribute( 'mention' ) ).to.be.true;
 			expect( textNode.getAttribute( 'mention' ) ).to.have.property( 'id', '@John' );
 			expect( textNode.getAttribute( 'mention' ) ).to.have.property( '_text', '@John' );
-			expect( textNode.getAttribute( 'mention' ) ).to.have.property( '_uid' );
+			expect( textNode.getAttribute( 'mention' ) ).to.have.property( 'uid' );
 
 			model.change( writer => {
 				const paragraph = doc.getRoot().getChild( 0 );
@@ -677,7 +680,10 @@ function addCustomMentionConverters( editor ) {
 			return viewWriter.createAttributeElement( 'b', {
 				class: 'mention',
 				'data-mention': modelAttributeValue.id
-			}, { id: modelAttributeValue._uid } );
+			}, {
+				priority: 20,
+				id: modelAttributeValue.uid
+			} );
 		},
 		converterPriority: 'high'
 	} );

+ 5 - 5
packages/ckeditor5-mention/tests/mentionui.js

@@ -621,7 +621,7 @@ describe( 'MentionUI', () => {
 						writer.createPositionAt( doc.getRoot().getChild( 0 ), 9 )
 					);
 
-					writer.setAttribute( 'mention', { id: '@Lily', _uid: 1234 }, range );
+					writer.setAttribute( 'mention', { id: '@Lily', uid: 1234 }, range );
 				} );
 
 				return waitForDebounce()
@@ -651,7 +651,7 @@ describe( 'MentionUI', () => {
 						writer.createPositionAt( doc.getRoot().getChild( 0 ), 9 )
 					);
 
-					writer.setAttribute( 'mention', { id: '@Lily', _uid: 1234 }, range );
+					writer.setAttribute( 'mention', { id: '@Lily', uid: 1234 }, range );
 				} );
 
 				return waitForDebounce()
@@ -725,7 +725,7 @@ describe( 'MentionUI', () => {
 			it( 'should not show panel when selection is after existing mention', () => {
 				setData( model, '<paragraph>foo [@Lily] bar[]</paragraph>' );
 				model.change( writer => {
-					writer.setAttribute( 'mention', { id: '@Lily', _uid: 1234 }, doc.selection.getFirstRange() );
+					writer.setAttribute( 'mention', { id: '@Lily', uid: 1234 }, doc.selection.getFirstRange() );
 				} );
 
 				return waitForDebounce()
@@ -750,7 +750,7 @@ describe( 'MentionUI', () => {
 						writer.createPositionAt( doc.getRoot().getChild( 0 ), 4 ),
 						writer.createPositionAt( doc.getRoot().getChild( 0 ), 9 )
 					);
-					writer.setAttribute( 'mention', { id: '@Lily', _uid: 1234 }, range );
+					writer.setAttribute( 'mention', { id: '@Lily', uid: 1234 }, range );
 				} );
 
 				return waitForDebounce()
@@ -1295,7 +1295,7 @@ describe( 'MentionUI', () => {
 				setData( model, '<paragraph>foo [#101] bar</paragraph><paragraph></paragraph>' );
 
 				model.change( writer => {
-					writer.setAttribute( 'mention', { id: '#101', _uid: 1234 }, doc.selection.getFirstRange() );
+					writer.setAttribute( 'mention', { id: '#101', uid: 1234 }, doc.selection.getFirstRange() );
 				} );
 
 				// Increase the response time to extend the debounce time out.