Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Introduced schema#setAttributeProperties() and schema#getAttributeProperties() methods #1711

Merged
merged 8 commits into from
Mar 29, 2019
70 changes: 70 additions & 0 deletions src/model/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ export default class Schema {
constructor() {
this._sourceDefinitions = {};

/**
* A dictionary containing attribute properties.
*
* @private
* @member {Object.<String,String>}
*/
this._attributeProperties = {};

this.decorate( 'checkChild' );
this.decorate( 'checkAttribute' );

Expand Down Expand Up @@ -475,6 +483,58 @@ export default class Schema {
}, { priority: 'high' } );
}

/**
* This method allows assigning additional metadata to the model attributes. For example,
* {@link module:engine/model/schema~AttributeProperties `AttributeProperties#isFormatting` property} is
* used to mark formatting attributes (like `bold` or `italic`).
*
* // Mark bold as a formatting attribute.
* schema.setAttributeProperties( 'bold', {
* isFormatting: true
* } );
*
* // Override code not to be considered a formatting markup.
* schema.setAttributeProperties( 'code', {
* isFormatting: false
* } );
*
* Properties are not limited to members defined in the
* {@link module:engine/model/schema~AttributeProperties `AttributeProperties` type} and you can also use custom properties:
*
* schema.setAttributeProperties( 'blockQuote', {
* customProperty: 'value'
* } );
*
* Subsequent calls with the same attribute will extend its custom properties:
*
* schema.setAttributeProperties( 'blockQuote', {
* one: 1
* } );
*
* schema.setAttributeProperties( 'blockQuote', {
* two: 2
* } );
*
* console.log( schema.getAttributeProperties( 'blockQuote' ) );
* // Logs: { one: 1, two: 2 }
*
* @param {String} attributeName A name of the attribute to receive the properties.
* @param {module:engine/model/schema~AttributeProperties} properties A dictionary of properties.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is confusing. First, it is stated that

You can also use custom attributes:

Then we enforce a type, which clearly declares which properties are allowed.

*/
setAttributeProperties( attributeName, properties ) {
this._attributeProperties[ attributeName ] = Object.assign( this.getAttributeProperties( attributeName ) || {}, properties );
}

/**
* Returns properties associated with a given model attribute. See {@link #setAttributeProperties `setAttributeProperties()`}.
*
* @param {String} attributeName A name of the attribute.
* @returns {module:engine/model/schema~AttributeProperties}
*/
getAttributeProperties( attributeName ) {
return this._attributeProperties[ attributeName ];
}

/**
* Returns the lowest {@link module:engine/model/schema~Schema#isLimit limit element} containing the entire
* selection/range/position or the root otherwise.
Expand Down Expand Up @@ -1285,6 +1345,16 @@ export class SchemaContext {
* @typedef {Object} module:engine/model/schema~SchemaContextItem
*/

/**
* A structure containing additional metadata describing the attribute.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I wrote earlier, setAttributeProperties() allows any property names while this type clearly narrows possible options to isFormatting only. We should figure that out.

*
* See {@link module:engine/model/schema~Schema#setAttributeProperties `Schema#setAttributeProperties()`} for usage examples.
*
* @typedef {Object} module:engine/model/schema~AttributeProperties
* @property {Boolean} [isFormatting] Indicates that the attribute should be considered as a visual formatting, like `bold`, `italic` or
* `fontSize` rather than semantic attribute (such as `src`, `listType`, etc.). For example, it is used by the "Remove format" feature.
*/

function compileBaseItemRule( sourceItemRules, itemName ) {
const itemRule = {
name: itemName,
Expand Down
40 changes: 40 additions & 0 deletions tests/model/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,46 @@ describe( 'Schema', () => {
} );
} );

describe( 'setAttributeProperties()', () => {
beforeEach( () => {
schema.register( '$root' );
schema.register( 'paragraph', {
allowIn: '$root'
} );
schema.register( '$text', {
allowIn: 'paragraph'
} );
schema.extend( '$text', { allowAttributes: 'testAttribute' } );
} );

it( 'allows registering new properties', () => {
schema.setAttributeProperties( 'testAttribute', {
foo: 'bar',
baz: 'bom'
} );

expect( schema.getAttributeProperties( 'testAttribute' ) ).to.deep.equal( {
foo: 'bar',
baz: 'bom'
} );
} );

it( 'support adding properties in subsequent calls', () => {
schema.setAttributeProperties( 'testAttribute', {
first: 'foo'
} );

schema.setAttributeProperties( 'testAttribute', {
second: 'bar'
} );

expect( schema.getAttributeProperties( 'testAttribute' ) ).to.deep.equal( {
first: 'foo',
second: 'bar'
} );
} );
} );

describe( 'getDefinitions()', () => {
it( 'returns compiled definitions', () => {
schema.register( '$root' );
Expand Down