Skip to content
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
20 changes: 13 additions & 7 deletions packages/format-library/src/link/inline.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ import { createLinkFormat, isValidHref, getFormatBoundary } from './utils';
import { link as settings } from './index';
import CSSClassesSettingComponent from './css-classes-setting';

// CSSClassesSettingComponent moved to its own file and imported above.

const LINK_SETTINGS = [
...LinkControl.DEFAULT_LINK_SETTINGS,
{
Expand Down Expand Up @@ -167,7 +165,18 @@ function InlineLinkUI( {

return;
} else if ( newText === richTextText ) {
newValue = applyFormat( value, linkFormat );
// Use explicit format boundaries rather than relying on
// the current selection which may be collapsed or
// misaligned after external value changes.
const boundary = getFormatBoundary( value, {
type: 'core/link',
} );
newValue = applyFormat(
value,
linkFormat,
boundary.start,
boundary.end
);
} else {
// Scenario: Editing an existing link.

Expand Down Expand Up @@ -318,10 +327,7 @@ function getRichTextValueFromSelection( value, isActive ) {
} );

textStart = boundary.start;

// Text *selection* always extends +1 beyond the edge of the format.
// We account for that here.
textEnd = boundary.end + 1;
textEnd = boundary.end;
}

// Get a RichTextValue containing the selected text content.
Expand Down
24 changes: 12 additions & 12 deletions packages/format-library/src/link/test/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ describe( 'getFormatBoundary', () => {
expect(
getFormatBoundary( record, { type: 'core/link' } )
).toEqual( {
start: null,
end: null,
start: undefined,
end: undefined,
} );
} );

Expand Down Expand Up @@ -168,8 +168,8 @@ describe( 'getFormatBoundary', () => {
expect(
getFormatBoundary( record, { type: 'core/link' } )
).toEqual( {
start: null,
end: null,
start: undefined,
end: undefined,
} );
}
);
Expand Down Expand Up @@ -213,8 +213,8 @@ describe( 'getFormatBoundary', () => {
expect(
getFormatBoundary( record, { type: 'core/link' } )
).toEqual( {
start: null,
end: null,
start: undefined,
end: undefined,
} );
}
);
Expand Down Expand Up @@ -254,7 +254,7 @@ describe( 'getFormatBoundary', () => {
getFormatBoundary( record, { type: 'core/link' } )
).toEqual( {
start: 6,
end: 10,
end: 11,
} );
}
);
Expand Down Expand Up @@ -289,7 +289,7 @@ describe( 'getFormatBoundary', () => {
getFormatBoundary( record, { type: 'core/link' } )
).toEqual( {
start: 6,
end: 10,
end: 11,
} );
} );
} );
Expand Down Expand Up @@ -332,7 +332,7 @@ describe( 'getFormatBoundary', () => {
getFormatBoundary( record, { type: 'core/link' } )
).toEqual( {
start: 6,
end: 10,
end: 11,
} );
}
);
Expand Down Expand Up @@ -381,7 +381,7 @@ describe( 'getFormatBoundary', () => {

expect( getFormatBoundary( record, { type: 'core/link' } ) ).toEqual( {
start: 0,
end: 5,
end: 6,
} );
} );

Expand Down Expand Up @@ -417,7 +417,7 @@ describe( 'getFormatBoundary', () => {
getFormatBoundary( record, { type: 'core/bold' } )
).toEqual( {
start: 4,
end: 6,
end: 7,
} );
} );
} );
Expand Down Expand Up @@ -453,7 +453,7 @@ describe( 'getFormatBoundary', () => {
)
).toEqual( {
start: 0,
end: 4,
end: 5,
} );
} );
} );
12 changes: 8 additions & 4 deletions packages/format-library/src/link/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ export function getFormatBoundary(
endIndex = value.end
) {
const EMPTY_BOUNDARIES = {
start: null,
end: null,
start: undefined,
end: undefined,
};

const { formats } = value;
Expand Down Expand Up @@ -210,10 +210,14 @@ export function getFormatBoundary(
// Safe guard: start index cannot be less than 0.
startIndex = startIndex < 0 ? 0 : startIndex;

// // Return the indices of the "edges" as the boundaries.
// Return the indices of the "edges" as the boundaries.
// walkToEnd returns the last index that has the format (e.g. 10),
// but rich-text APIs like applyFormat and slice expect the end to be
// one position past the last character (e.g. 11), just like
// String.prototype.slice. Adding 1 here so consumers don't have to.
return {
start: startIndex,
end: endIndex,
end: endIndex + 1,
};
}

Expand Down
41 changes: 41 additions & 0 deletions test/e2e/specs/editor/blocks/links.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,47 @@ test.describe( 'Links', () => {
] );
} );

test( 'correctly updates the link when caret at outer edge of format boundary', async ( {
page,
editor,
LinkUtils,
} ) => {
await editor.insertBlock( {
name: 'core/paragraph',
attributes: {
content:
'<a href="https://wordpress.org/gutenberg">Gutenberg</a> is awesome',
},
} );

// Change the link text by typing to trigger a RichText value change.
await editor.canvas
.getByRole( 'link', { name: 'Gutenberg' } )
.dblclick();
await page.keyboard.type( 'Block Editor' );

const linkPopover = LinkUtils.getLinkPopover();
await expect( linkPopover ).toBeVisible();

// Edit only the URL.
await linkPopover.getByRole( 'button', { name: 'Edit' } ).click();
await linkPopover
.getByPlaceholder( 'Search or type URL' )
.fill( 'https://wordpress.org' );
await linkPopover.getByRole( 'button', { name: 'Apply' } ).click();

// The link should have the updated URL.
await expect.poll( editor.getBlocks ).toMatchObject( [
{
name: 'core/paragraph',
attributes: {
content:
'<a href="https://wordpress.org">Block Editor</a> is awesome',
},
},
] );
} );

test( 'toggle state of advanced link settings is preserved across editing links', async ( {
page,
editor,
Expand Down
Loading