Skip to content

Commit

Permalink
Merge pull request #13979 from ckeditor/ck/11709-link-styles
Browse files Browse the repository at this point in the history
Feature (style): The styles dropdown applies link styles only to the whole link. Closes #11709.
  • Loading branch information
niegowski authored May 4, 2023
2 parents 5e7ebf8 + 5583168 commit f23d002
Show file tree
Hide file tree
Showing 15 changed files with 2,008 additions and 79 deletions.
33 changes: 21 additions & 12 deletions packages/ckeditor5-html-support/src/generalhtmlsupport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ import { modifyGhsAttribute } from './utils';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { GeneralHtmlSupportConfig } from './generalhtmlsupportconfig';

type LimitedSelectable = Exclude<Selectable, Iterable<Range> | null>;

/**
* The General HTML Support feature.
*
Expand Down Expand Up @@ -103,7 +101,7 @@ export default class GeneralHtmlSupport extends Plugin {
* @param className The css class to add.
* @param selectable The selection or element to update.
*/
public addModelHtmlClass( viewElementName: string, className: ArrayOrItem<string>, selectable: LimitedSelectable ): void {
public addModelHtmlClass( viewElementName: string, className: ArrayOrItem<string>, selectable: Selectable ): void {
const model = this.editor.model;
const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );

Expand All @@ -126,7 +124,7 @@ export default class GeneralHtmlSupport extends Plugin {
* @param className The css class to remove.
* @param selectable The selection or element to update.
*/
public removeModelHtmlClass( viewElementName: string, className: ArrayOrItem<string>, selectable: LimitedSelectable ): void {
public removeModelHtmlClass( viewElementName: string, className: ArrayOrItem<string>, selectable: Selectable ): void {
const model = this.editor.model;
const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );

Expand All @@ -148,7 +146,7 @@ export default class GeneralHtmlSupport extends Plugin {
* @param attributes The object with attributes to set.
* @param selectable The selection or element to update.
*/
private setModelHtmlAttributes( viewElementName: string, attributes: Record<string, unknown>, selectable: LimitedSelectable ) {
private setModelHtmlAttributes( viewElementName: string, attributes: Record<string, unknown>, selectable: Selectable ) {
const model = this.editor.model;
const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );

Expand All @@ -170,7 +168,7 @@ export default class GeneralHtmlSupport extends Plugin {
* @param attributeName The attribute name (or names) to remove.
* @param selectable The selection or element to update.
*/
private removeModelHtmlAttributes( viewElementName: string, attributeName: ArrayOrItem<string>, selectable: LimitedSelectable ) {
private removeModelHtmlAttributes( viewElementName: string, attributeName: ArrayOrItem<string>, selectable: Selectable ) {
const model = this.editor.model;
const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );

Expand All @@ -192,7 +190,7 @@ export default class GeneralHtmlSupport extends Plugin {
* @param styles The object with styles to set.
* @param selectable The selection or element to update.
*/
private setModelHtmlStyles( viewElementName: string, styles: Record<string, string>, selectable: LimitedSelectable ) {
private setModelHtmlStyles( viewElementName: string, styles: Record<string, string>, selectable: Selectable ) {
const model = this.editor.model;
const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );

Expand All @@ -214,7 +212,7 @@ export default class GeneralHtmlSupport extends Plugin {
* @param properties The style (or styles list) to remove.
* @param selectable The selection or element to update.
*/
private removeModelHtmlStyles( viewElementName: string, properties: ArrayOrItem<string>, selectable: LimitedSelectable ) {
private removeModelHtmlStyles( viewElementName: string, properties: ArrayOrItem<string>, selectable: Selectable ) {
const model = this.editor.model;
const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );

Expand All @@ -235,10 +233,14 @@ export default class GeneralHtmlSupport extends Plugin {
*/
function* getItemsToUpdateGhsAttribute(
model: Model,
selectable: LimitedSelectable,
selectable: Selectable,
ghsAttributeName: string
): IterableIterator<Item | DocumentSelection> {
if ( selectable.is( 'documentSelection' ) && selectable.isCollapsed ) {
if ( !selectable ) {
return;
}

if ( !( Symbol.iterator in selectable ) && selectable.is( 'documentSelection' ) && selectable.isCollapsed ) {
if ( model.schema.checkAttributeInSelection( selectable, ghsAttributeName ) ) {
yield selectable;
}
Expand All @@ -254,10 +256,17 @@ function* getItemsToUpdateGhsAttribute(
*/
function getValidRangesForSelectable(
model: Model,
selectable: LimitedSelectable,
selectable: NonNullable<Selectable>,
ghsAttributeName: string
): Iterable<Range> {
if ( selectable.is( 'node' ) || selectable.is( '$text' ) || selectable.is( '$textProxy' ) ) {
if (
!( Symbol.iterator in selectable ) &&
(
selectable.is( 'node' ) ||
selectable.is( '$text' ) ||
selectable.is( '$textProxy' )
)
) {
if ( model.schema.checkAttribute( selectable, ghsAttributeName ) ) {
return [ model.createRangeOn( selectable ) ];
} else {
Expand Down
103 changes: 101 additions & 2 deletions packages/ckeditor5-html-support/tests/datafilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3329,6 +3329,46 @@ describe( 'DataFilter', () => {
);
} );

it( 'should not add classes if selectable is null', () => {
setModelData( model, '<paragraph>[foobar]</paragraph>' );

htmlSupport.addModelHtmlClass( 'cite', [ 'foo', 'bar' ], null );

expect( getModelDataWithAttributes( model, { withoutSelection: true } ) ).to.deep.equal( {
data: '<paragraph>foobar</paragraph>',
attributes: {}
} );

expect( editor.getData() ).to.equal(
'<p>foobar</p>'
);
} );

it( 'should not remove classes if selectable is null', () => {
editor.setData(
'<p><cite class="foo bar">foobar</cite></p>'
);

model.change( writer => {
writer.setSelection( model.document.getRoot().getChild( 0 ), 'in' );
} );

htmlSupport.removeModelHtmlClass( 'cite', [ 'foo', 'bar' ], null );

expect( getModelDataWithAttributes( model, { withoutSelection: true } ) ).to.deep.equal( {
data: '<paragraph><$text htmlCite="(1)">foobar</$text></paragraph>',
attributes: {
1: {
classes: [ 'foo', 'bar' ]
}
}
} );

expect( editor.getData() ).to.equal(
'<p><cite class="foo bar">foobar</cite></p>'
);
} );

describe( 'on ranges', () => {
beforeEach( () => {
root = model.document.getRoot();
Expand All @@ -3339,7 +3379,7 @@ describe( 'DataFilter', () => {
dataFilter.allowAttributes( { name: 'cite', attributes: true } );
} );

it( 'should add new classes', () => {
it( 'should add new classes (single range)', () => {
editor.setData( '<p>foobar</p>' );

htmlSupport.addModelHtmlClass( 'cite', [ 'foo', 'bar' ], model.createRange(
Expand All @@ -3361,7 +3401,7 @@ describe( 'DataFilter', () => {
);
} );

it( 'should remove classes', () => {
it( 'should remove classes (single range)', () => {
editor.setData( '<p><cite class="foo">foobar</cite></p>' );

htmlSupport.removeModelHtmlClass( 'cite', 'foo', model.createRange(
Expand All @@ -3382,6 +3422,65 @@ describe( 'DataFilter', () => {
'<p><cite class="foo">f</cite>oobar</p>'
);
} );

it( 'should add new classes (array of ranges)', () => {
editor.setData( '<p>foobar</p>' );

htmlSupport.addModelHtmlClass( 'cite', [ 'foo', 'bar' ], [
model.createRange(
model.createPositionAt( root.getChild( 0 ), 1 ),
model.createPositionAt( root.getChild( 0 ), 2 )
),
model.createRange(
model.createPositionAt( root.getChild( 0 ), 3 ),
model.createPositionAt( root.getChild( 0 ), 4 )
)
] );

expect( getModelDataWithAttributes( model, { withoutSelection: true } ) ).to.deep.equal( {
data: '<paragraph>f<$text htmlCite="(1)">o</$text>o<$text htmlCite="(2)">b</$text>ar</paragraph>',
attributes: {
1: {
classes: [ 'foo', 'bar' ]
},
2: {
classes: [ 'foo', 'bar' ]
}
}
} );

expect( editor.getData() ).to.equal(
'<p>f<cite class="foo bar">o</cite>o<cite class="foo bar">b</cite>ar</p>'
);
} );

it( 'should remove classes (array pf ranges)', () => {
editor.setData( '<p><cite class="foo">foobar</cite></p>' );

htmlSupport.removeModelHtmlClass( 'cite', 'foo', [
model.createRange(
model.createPositionAt( root.getChild( 0 ), 0 ),
model.createPositionAt( root.getChild( 0 ), 2 )
),
model.createRange(
model.createPositionAt( root.getChild( 0 ), 3 ),
model.createPositionAt( root.getChild( 0 ), 6 )
)
] );

expect( getModelDataWithAttributes( model, { withoutSelection: true } ) ).to.deep.equal( {
data: '<paragraph>fo<$text htmlCite="(1)">o</$text>bar</paragraph>',
attributes: {
1: {
classes: [ 'foo' ]
}
}
} );

expect( editor.getData() ).to.equal(
'<p>fo<cite class="foo">o</cite>bar</p>'
);
} );
} );
} );
} );
Expand Down
Loading

0 comments on commit f23d002

Please sign in to comment.