Skip to content

Commit

Permalink
Merge pull request #13702 from ckeditor/ck/12588-link-doesnt-show-a-c…
Browse files Browse the repository at this point in the history
…hange-in-the-link

Fix (link): The link text should update along with the URL if they were the same in the first place. Closes #12588.
  • Loading branch information
oleq authored Mar 21, 2023
2 parents 5f90bd3 + b167006 commit 0beb8c6
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 5 deletions.
64 changes: 59 additions & 5 deletions packages/ckeditor5-link/src/linkcommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import { Command } from 'ckeditor5/src/core';
import { findAttributeRange } from 'ckeditor5/src/typing';
import { Collection, first, toMap } from 'ckeditor5/src/utils';
import type { Range } from 'ckeditor5/src/engine';
import type { Range, DocumentSelection, Model, Writer } from 'ckeditor5/src/engine';

import AutomaticDecorators from './utils/automaticdecorators';
import { isLinkableElement } from './utils';
Expand Down Expand Up @@ -161,8 +161,13 @@ export default class LinkCommand extends Command {

// When selection is inside text with `linkHref` attribute.
if ( selection.hasAttribute( 'linkHref' ) ) {
const linkText = extractTextFromSelection( selection );
// Then update `linkHref` value.
const linkRange = findAttributeRange( position, 'linkHref', selection.getAttribute( 'linkHref' ), model );
let linkRange = findAttributeRange( position, 'linkHref', selection.getAttribute( 'linkHref' ), model );

if ( selection.getAttribute( 'linkHref' ) === linkText ) {
linkRange = this._updateLinkContent( model, writer, linkRange, href );
}

writer.setAttribute( 'linkHref', href, linkRange );

Expand Down Expand Up @@ -227,14 +232,26 @@ export default class LinkCommand extends Command {
}

for ( const range of rangesToUpdate ) {
writer.setAttribute( 'linkHref', href, range );
let linkRange = range;

if ( rangesToUpdate.length === 1 ) {
// Current text of the link in the document.
const linkText = extractTextFromSelection( selection );

if ( selection.getAttribute( 'linkHref' ) === linkText ) {
linkRange = this._updateLinkContent( model, writer, range, href );
writer.setSelection( writer.createSelection( linkRange ) );
}
}

writer.setAttribute( 'linkHref', href, linkRange );

truthyManualDecorators.forEach( item => {
writer.setAttribute( item, true, range );
writer.setAttribute( item, true, linkRange );
} );

falsyManualDecorators.forEach( item => {
writer.removeAttribute( item, range );
writer.removeAttribute( item, linkRange );
} );
}
}
Expand Down Expand Up @@ -277,4 +294,41 @@ export default class LinkCommand extends Command {

return true;
}

/**
* Updates selected link with a new value as its content and as its href attribute.
*
* @param model Model is need to insert content.
* @param writer Writer is need to create text element in model.
* @param range A range where should be inserted content.
* @param href A link value which should be in the href attribute and in the content.
*/
private _updateLinkContent( model: Model, writer: Writer, range: Range, href: string ): Range {
const text = writer.createText( href, { linkHref: href } );

return model.insertContent( text, range );
}
}

// Returns a text of a link under the collapsed selection or a selection that contains the entire link.
function extractTextFromSelection( selection: DocumentSelection ): string | null {
if ( selection.isCollapsed ) {
const firstPosition = selection.getFirstPosition();

return firstPosition!.textNode && firstPosition!.textNode.data;
} else {
const rangeItems = Array.from( selection.getFirstRange()!.getItems() );

if ( rangeItems.length > 1 ) {
return null;
}

const firstNode = rangeItems[ 0 ];

if ( firstNode.is( '$text' ) || firstNode.is( '$textProxy' ) ) {
return firstNode.data;
}

return null;
}
}
82 changes: 82 additions & 0 deletions packages/ckeditor5-link/tests/linkcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,24 @@ describe( 'LinkCommand', () => {

expect( getData( model ) ).to.equal( 'foo<$text linkHref="url">url</$text>[]bar' );
} );

it( 'should update content if href is equal to content', () => {
setData( model, '<$text linkHref="url">ur[]l</$text>' );

command.execute( 'url2', { linkIsFoo: true, linkIsBar: true, linkIsSth: true } );

expect( getData( model ) ).to
.equal( '<$text linkHref="url2" linkIsBar="true" linkIsFoo="true" linkIsSth="true">url2</$text>[]' );
} );

it( 'should not add new attributes if there are falsy when href is equal to content', () => {
setData( model, '<$text linkHref="url">ur[]l</$text>' );

command.execute( 'url2', { linkIsFoo: false, linkIsBar: false, linkIsSth: false } );

expect( getData( model ) ).to
.equal( '<$text linkHref="url2">url2</$text>[]' );
} );
} );

describe( 'range selection', () => {
Expand Down Expand Up @@ -752,6 +770,70 @@ describe( 'LinkCommand', () => {
'</paragraph>'
);
} );

it( 'should update content if href is equal to content', () => {
setData( model, '[<$text linkHref="url">url</$text>]' );

command.execute( 'url2', { linkIsFoo: true, linkIsBar: true, linkIsSth: true } );

expect( getData( model ) ).to
.equal( '[<$text linkHref="url2" linkIsBar="true" linkIsFoo="true" linkIsSth="true">url2</$text>]' );
} );

it( 'should not update content if href is equal to content but there is a non-link following in the selection', () => {
setData( model, '<paragraph>[<$text linkHref="url">url</$text>foo]</paragraph>' );

command.execute( 'url2', { linkIsFoo: true, linkIsBar: true, linkIsSth: true } );

expect( getData( model ) ).to
.equal(
'<paragraph>[<$text linkHref="url2" linkIsBar="true" linkIsFoo="true" linkIsSth="true">urlfoo</$text>]</paragraph>'
);
} );

it( 'should not update content if href is equal to content but there is a non-link preceding in the selection', () => {
setData( model, '<paragraph>[foo<$text linkHref="url">url</$text>]</paragraph>' );

command.execute( 'url2', { linkIsFoo: true, linkIsBar: true, linkIsSth: true } );

expect( getData( model ) ).to
.equal(
'<paragraph>[<$text linkHref="url2" linkIsBar="true" linkIsFoo="true" linkIsSth="true">foourl</$text>]</paragraph>'
);
} );

it( 'should not add new attributes if there are falsy when href is equal to content', () => {
setData( model, '[<$text linkHref="url">url</$text>]' );

command.execute( 'url2', { linkIsFoo: false, linkIsBar: false, linkIsSth: false } );

expect( getData( model ) ).to
.equal( '[<$text linkHref="url2">url2</$text>]' );
} );

it( 'should not update link which is equal its href if selection is on more than one element', () => {
setData( model,
'<paragraph>' +
'<$text linkHref="foo">[foo</$text>' +
'</paragraph>' +
'<paragraph>bar</paragraph>' +
'<paragraph>baz]</paragraph>'
);

command.execute( 'foooo' );

expect( getData( model ) ).to
.equal( '<paragraph>' +
'[<$text linkHref="foooo">foo</$text>' +
'</paragraph>' +
'<paragraph>' +
'<$text linkHref="foooo">bar</$text>' +
'</paragraph>' +
'<paragraph>' +
'<$text linkHref="foooo">baz</$text>]' +
'</paragraph>'
);
} );
} );

describe( 'restoreManualDecoratorStates()', () => {
Expand Down

0 comments on commit 0beb8c6

Please sign in to comment.