diff --git a/src/model/schema.js b/src/model/schema.js index d967eb320..584f661d3 100644 --- a/src/model/schema.js +++ b/src/model/schema.js @@ -14,6 +14,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix'; import Range from './range'; import Position from './position'; import Element from './element'; +import Text from './text'; import TreeWalker from './treewalker'; /** @@ -488,8 +489,14 @@ export default class Schema { */ checkAttributeInSelection( selection, attribute ) { if ( selection.isCollapsed ) { + const firstPosition = selection.getFirstPosition(); + const context = [ + ...firstPosition.getAncestors(), + new Text( '', selection.getAttributes() ) + ]; + // Check whether schema allows for a text with the attribute in the selection. - return this.checkAttribute( [ ...selection.getFirstPosition().getAncestors(), '$text' ], attribute ); + return this.checkAttribute( context, attribute ); } else { const ranges = selection.getRanges(); diff --git a/tests/model/schema.js b/tests/model/schema.js index b02a606c8..b79a16de7 100644 --- a/tests/model/schema.js +++ b/tests/model/schema.js @@ -1035,6 +1035,9 @@ describe( 'Schema', () => { allowIn: '$root', allowAttributes: [ 'name', 'title' ] } ); + schema.extend( '$text', { + allowAttributes: [ 'italic' ] + } ); schema.addAttributeCheck( ( ctx, attributeName ) => { // Allow 'bold' on p>$text. @@ -1046,6 +1049,15 @@ describe( 'Schema', () => { if ( ctx.endsWith( '$root p' ) && attributeName == 'bold' ) { return true; } + + // Disallow 'italic' on $text that has 'bold' already. + if ( inTextWithBold( ctx ) && attributeName == 'italic' ) { + return false; + } + + function inTextWithBold( context ) { + return context.endsWith( '$text' ) && context.last.getAttribute( 'bold' ); + } } ); } ); @@ -1062,6 +1074,51 @@ describe( 'Schema', () => { setData( model, '[]' ); expect( schema.checkAttributeInSelection( doc.selection, attribute ) ).to.be.false; } ); + + it( 'should check attributes of the selection (selection inside the $text[bold])', () => { + setData( model, '

<$text bold="true">f[]oo

' ); + + expect( schema.checkAttributeInSelection( doc.selection, 'italic' ) ).to.be.false; + + model.change( writer => { + writer.removeSelectionAttribute( 'bold' ); + } ); + + expect( schema.checkAttributeInSelection( doc.selection, 'italic' ) ).to.be.true; + } ); + + it( 'should check attributes of the selection (attribute set manually on selection)', () => { + setData( model, '

foo[]bar

' ); + + expect( schema.checkAttributeInSelection( doc.selection, 'italic' ) ).to.be.true; + + model.change( writer => { + writer.setSelectionAttribute( 'bold', true ); + } ); + + expect( schema.checkAttributeInSelection( doc.selection, 'italic' ) ).to.be.false; + } ); + + it( 'should pass all selection\'s attributes to checkAttribute()', done => { + schema.on( 'checkAttribute', ( evt, args ) => { + const context = args[ 0 ]; + const attributeName = args[ 1 ]; + + expect( attributeName ).to.equal( 'italic' ); + expect( Array.from( context.last.getAttributeKeys() ) ).to.deep.equal( [ 'bold', 'underline' ] ); + + done(); + }, { priority: 'highest' } ); + + setData( model, '

foo[]bar

' ); + + model.change( writer => { + writer.setSelectionAttribute( 'bold', true ); + writer.setSelectionAttribute( 'underline', true ); + } ); + + expect( schema.checkAttributeInSelection( doc.selection, 'italic' ) ).to.be.false; + } ); } ); describe( 'when selection is not collapsed', () => { @@ -1102,6 +1159,11 @@ describe( 'Schema', () => { setData( model, '[
]' ); expect( schema.checkAttributeInSelection( doc.selection, 'title' ) ).to.be.true; } ); + + it( 'should check attributes of text', () => { + setData( model, '

<$text bold="true">f[o]o

' ); + expect( schema.checkAttributeInSelection( doc.selection, 'italic' ) ).to.be.false; + } ); } ); } );