diff --git a/src/components/propertylist.css b/src/components/propertylist.css index c71c304..f1b2f32 100644 --- a/src/components/propertylist.css +++ b/src/components/propertylist.css @@ -7,7 +7,7 @@ --ck-inspector-color-property-list-property-name: #D0363F; --ck-inspector-color-property-list-property-value-true: green; --ck-inspector-color-property-list-property-value-false: red; - --ck-inspector-color-property-list-property-value-undefined: #888; + --ck-inspector-color-property-list-property-value-unknown: #888; --ck-inspector-color-property-list-background: #F5F5F5; } @@ -43,8 +43,13 @@ color: var(--ck-inspector-color-property-list-property-value-true); } - &[value="undefined"] { - color: var(--ck-inspector-color-property-list-property-value-undefined); + &[value="undefined"], + &[value="function() {…}"] { + color: var(--ck-inspector-color-property-list-property-value-unknown); + } + + &[value="function() {…}"] { + font-style: italic; } } } diff --git a/src/components/utils.js b/src/components/utils.js index c949ebd..9162b14 100644 --- a/src/components/utils.js +++ b/src/components/utils.js @@ -8,6 +8,10 @@ export function stringify( value, quotesAroundText = true ) { return 'undefined'; } + if ( typeof value === 'function' ) { + return 'function() {…}'; + } + const stringified = JSON.stringify( value ); // Note: Remove leading and trailing quotes (") from the output. By default it is: @@ -37,6 +41,10 @@ export function uid() { export function stringifyPropertyList( list ) { return list.map( ( [ name, value ] ) => { + if ( typeof name === 'symbol' ) { + name = name.toString(); + } + return [ name, stringify( value ) ]; } ); } diff --git a/src/components/view/nodeinspector.js b/src/components/view/nodeinspector.js index 89e9957..729d72c 100644 --- a/src/components/view/nodeinspector.js +++ b/src/components/view/nodeinspector.js @@ -18,6 +18,8 @@ import { import { stringifyPropertyList } from '../utils'; import ObjectInspector from '../objectinspector'; +const DOCS_URL_PREFIX = 'https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_view'; + class NodeInspector extends Component { editorEventObserverConfig( props ) { return { @@ -61,6 +63,11 @@ class NodeInspector extends Component { url: info.url, items: info.properties }, + { + name: 'Custom Properties', + url: `${ DOCS_URL_PREFIX }_element-Element.html#function-getCustomProperty`, + items: info.customProperties + } ]} />; } @@ -84,29 +91,30 @@ class NodeInspector extends Component { const info = { editorNode: node, properties: [], - attributes: [] + attributes: [], + customProperties: [] }; if ( isViewElement( node ) ) { if ( isViewRoot( node ) ) { info.type = 'RootEditableElement'; info.name = node.rootName; - info.url = 'https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_view_rooteditableelement-RootEditableElement.html'; + info.url = `${ DOCS_URL_PREFIX }_rooteditableelement-RootEditableElement.html`; } else { info.name = node.name; if ( isViewAttributeElement( node ) ) { info.type = 'AttributeElement'; - info.url = 'https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_view_attributeelement-AttributeElement.html'; + info.url = `${ DOCS_URL_PREFIX }_attributeelement-AttributeElement.html`; } else if ( isViewEmptyElement( node ) ) { info.type = 'EmptyElement'; - info.url = 'https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_view_emptyelement-EmptyElement.html'; + info.url = `${ DOCS_URL_PREFIX }_emptyelement-EmptyElement.html`; } else if ( isViewUiElement( node ) ) { info.type = 'UIElement'; - info.url = 'https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_view_uielement-UIElement.html'; + info.url = `${ DOCS_URL_PREFIX }_uielement-UIElement.html`; } else { info.type = 'ContainerElement'; - info.url = 'https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_view_containerelement-ContainerElement.html'; + info.url = `${ DOCS_URL_PREFIX }_containerelement-ContainerElement.html`; } } @@ -116,10 +124,11 @@ class NodeInspector extends Component { [ 'isEmpty', node.isEmpty ], [ 'childCount', node.childCount ], ); + info.customProperties.push( ...node.getCustomProperties() ); } else { info.name = node.data; info.type = 'Text'; - info.url = 'https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_view_text-Text.html'; + info.url = `${ DOCS_URL_PREFIX }_text-Text.html`; info.properties.push( [ 'index', node.index ] @@ -127,6 +136,7 @@ class NodeInspector extends Component { } info.properties = stringifyPropertyList( info.properties ); + info.customProperties = stringifyPropertyList( info.customProperties ); info.attributes = stringifyPropertyList( info.attributes ); return info; diff --git a/tests/inspector/components/utils.js b/tests/inspector/components/utils.js index 4feee9c..b8dbf70 100644 --- a/tests/inspector/components/utils.js +++ b/tests/inspector/components/utils.js @@ -17,6 +17,7 @@ describe( 'Utils', () => { expect( stringify( 'foo' ) ).to.equal( '"foo"' ); expect( stringify( [ 'a' ] ) ).to.equal( '["a"]' ); expect( stringify( { a: false } ) ).to.equal( '{"a":false}' ); + expect( stringify( () => 'foo' ) ).to.equal( 'function() {…}' ); } ); it( 'stringifies values (no quotes around text)', () => { @@ -25,6 +26,7 @@ describe( 'Utils', () => { expect( stringify( 'foo', false ) ).to.equal( 'foo' ); expect( stringify( [ 'a' ], false ) ).to.equal( '["a"]' ); expect( stringify( { a: false }, false ) ).to.equal( '{"a":false}' ); + expect( stringify( () => 'foo' ), false ).to.equal( 'function() {…}' ); } ); } ); @@ -39,9 +41,11 @@ describe( 'Utils', () => { expect( stringifyPropertyList( [ [ 'foo', 'bar' ], [ 'baz', 'qux' ], + [ Symbol( '42' ), 'abc' ] ] ) ).to.have.deep.members( [ [ 'foo', '"bar"' ], [ 'baz', '"qux"' ], + [ 'Symbol(42)', '"abc"' ] ] ); } ); } ); diff --git a/tests/inspector/components/view/nodeinspector.js b/tests/inspector/components/view/nodeinspector.js index 731e338..83b14c0 100644 --- a/tests/inspector/components/view/nodeinspector.js +++ b/tests/inspector/components/view/nodeinspector.js @@ -109,6 +109,10 @@ describe( '', () => { it( 'renders for a RootElement', () => { editor.setData( '

foo

' ); + editor.editing.view.change( writer => { + writer.setCustomProperty( 'foo', 'bar', editor.editing.view.document.getRoot() ); + } ); + wrapper.setProps( { inspectedNode: root } ); @@ -133,11 +137,23 @@ describe( '', () => { [ 'isEmpty', 'false' ], [ 'childCount', '1' ], ] ); + + expect( lists[ 2 ].name ).to.equal( 'Custom Properties' ); + + const items = lists[ 2 ].items; + expect( items[ 0 ] ).to.have.members( [ 'Symbol(rootName)', '"main"' ] ); + expect( items[ 1 ][ 0 ] ).to.equal( 'Symbol(document)' ); + expect( items[ 1 ][ 1 ] ).to.match( /^{/ ); + expect( items[ 2 ] ).to.have.members( [ 'foo', '"bar"' ] ); } ); it( 'renders for a ContainerElement', () => { editor.setData( '

foo

' ); + editor.editing.view.change( writer => { + writer.setCustomProperty( 'foo', 'bar', editor.editing.view.document.getRoot().getChild( 0 ) ); + } ); + wrapper.setProps( { inspectedNode: root.getChild( 0 ) } ); @@ -157,11 +173,20 @@ describe( '', () => { [ 'isEmpty', 'false' ], [ 'childCount', '1' ], ] ); + + expect( lists[ 2 ].name ).to.equal( 'Custom Properties' ); + expect( lists[ 2 ].items ).to.deep.equal( [ + [ 'foo', '"bar"' ] + ] ); } ); it( 'renders for an AttributeElement', () => { editor.setData( '

foo

' ); + editor.editing.view.change( writer => { + writer.setCustomProperty( 'foo', 'bar', editor.editing.view.document.getRoot().getChild( 0 ).getChild( 0 ) ); + } ); + wrapper.setProps( { inspectedNode: root.getChild( 0 ).getChild( 0 ) } ); @@ -181,6 +206,11 @@ describe( '', () => { [ 'isEmpty', 'false' ], [ 'childCount', '1' ], ] ); + + expect( lists[ 2 ].name ).to.equal( 'Custom Properties' ); + expect( lists[ 2 ].items ).to.deep.equal( [ + [ 'foo', '"bar"' ] + ] ); } ); it( 'renders for an EmptyElement', () => { @@ -189,6 +219,7 @@ describe( '', () => { editor.editing.view.change( writer => { const foo = writer.createEmptyElement( 'foo' ); writer.insert( editor.editing.view.document.selection.getFirstPosition(), foo ); + writer.setCustomProperty( 'foo', 'bar', foo ); } ); wrapper.setProps( { @@ -210,6 +241,11 @@ describe( '', () => { [ 'isEmpty', 'true' ], [ 'childCount', '0' ], ] ); + + expect( lists[ 2 ].name ).to.equal( 'Custom Properties' ); + expect( lists[ 2 ].items ).to.deep.equal( [ + [ 'foo', '"bar"' ] + ] ); } ); it( 'renders for an UIElement', () => { @@ -218,6 +254,7 @@ describe( '', () => { editor.editing.view.change( writer => { const foo = writer.createUIElement( 'foo' ); writer.insert( editor.editing.view.document.selection.getFirstPosition(), foo ); + writer.setCustomProperty( 'foo', 'bar', foo ); } ); wrapper.setProps( { @@ -239,6 +276,11 @@ describe( '', () => { [ 'isEmpty', 'true' ], [ 'childCount', '0' ], ] ); + + expect( lists[ 2 ].name ).to.equal( 'Custom Properties' ); + expect( lists[ 2 ].items ).to.deep.equal( [ + [ 'foo', '"bar"' ] + ] ); } ); it( 'renders for a Text', () => {