Skip to content

Commit

Permalink
Fix format parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix committed Nov 6, 2018
1 parent 9195550 commit 8ff7d19
Show file tree
Hide file tree
Showing 14 changed files with 366 additions and 39 deletions.
5 changes: 2 additions & 3 deletions packages/format-library/src/bold/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ const name = 'core/bold';
export const bold = {
name,
title: __( 'Bold' ),
match: {
tagName: 'strong',
},
tagName: 'strong',
className: null,
edit( { isActive, value, onChange } ) {
const onToggle = () => onChange( toggleFormat( value, { type: name } ) );

Expand Down
5 changes: 2 additions & 3 deletions packages/format-library/src/code/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ const name = 'core/code';
export const code = {
name,
title: __( 'Code' ),
match: {
tagName: 'code',
},
tagName: 'code',
className: null,
edit( { value, onChange } ) {
const onToggle = () => onChange( toggleFormat( value, { type: name } ) );

Expand Down
5 changes: 2 additions & 3 deletions packages/format-library/src/image/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ export const image = {
title: __( 'Image' ),
keywords: [ __( 'photo' ), __( 'media' ) ],
object: true,
match: {
tagName: 'img',
},
tagName: 'img',
className: null,
attributes: {
className: 'class',
style: 'style',
Expand Down
5 changes: 2 additions & 3 deletions packages/format-library/src/italic/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ const name = 'core/italic';
export const italic = {
name,
title: __( 'Italic' ),
match: {
tagName: 'em',
},
tagName: 'em',
className: null,
edit( { isActive, value, onChange } ) {
const onToggle = () => onChange( toggleFormat( value, { type: name } ) );

Expand Down
5 changes: 2 additions & 3 deletions packages/format-library/src/link/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ const name = 'core/link';
export const link = {
name,
title: __( 'Link' ),
match: {
tagName: 'a',
},
tagName: 'a',
className: null,
attributes: {
url: 'href',
target: 'target',
Expand Down
5 changes: 2 additions & 3 deletions packages/format-library/src/strikethrough/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ const name = 'core/strikethrough';
export const strikethrough = {
name,
title: __( 'Strikethrough' ),
match: {
tagName: 'del',
},
tagName: 'del',
className: null,
edit( { isActive, value, onChange } ) {
const onToggle = () => onChange( toggleFormat( value, { type: name } ) );

Expand Down
26 changes: 19 additions & 7 deletions packages/rich-text/src/create.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/**
* External dependencies
* WordPress dependencies
*/

import { find } from 'lodash';
import { select } from '@wordpress/data';

/**
* Internal dependencies
Expand All @@ -11,7 +10,6 @@ import { find } from 'lodash';
import { isEmpty } from './is-empty';
import { isFormatEqual } from './is-format-equal';
import { createElement } from './create-element';
import { getFormatTypes } from './get-format-types';
import {
LINE_SEPARATOR,
OBJECT_REPLACEMENT_CHARACTER,
Expand All @@ -36,9 +34,23 @@ function simpleFindKey( object, value ) {
}

function toFormat( { type, attributes } ) {
const formatType = find( getFormatTypes(), ( { match } ) =>
type === match.tagName
);
let formatType;

if ( attributes && attributes.class ) {
formatType = select( 'core/rich-text' ).getFormatTypeForClassName( attributes.class );

if ( formatType ) {
attributes.class = ` ${ attributes.class } `.replace( ` ${ formatType.className } `, ' ' ).trim();

if ( ! attributes.class ) {
delete attributes.class;
}
}
}

if ( ! formatType ) {
formatType = select( 'core/rich-text' ).getFormatTypeForBareElement( type );
}

if ( ! formatType ) {
return attributes ? { type, attributes } : { type };
Expand Down
49 changes: 49 additions & 0 deletions packages/rich-text/src/register-format-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,55 @@ export function registerFormatType( name, settings ) {
return;
}

if (
typeof settings.tagName !== 'string' ||
settings.tagName === ''
) {
window.console.error(
'Format tag names must be a string.'
);
return;
}

if (
( typeof settings.className !== 'string' || settings.className === '' ) &&
settings.className !== null
) {
window.console.error(
'Format class names must be a string, or null to handle bare elements.'
);
return;
}

if ( ! /^[_a-zA-Z]+[a-zA-Z0-9-]*$/.test( settings.className ) ) {
window.console.error(
'A class name must begin with a letter, followed by any number of hyphens, letters, or numbers.'
);
return;
}

if ( settings.className === null ) {
const formatTypeForBareElement = select( 'core/rich-text' )
.getFormatTypeForBareElement( settings.tagName );

if ( formatTypeForBareElement ) {
window.console.error(
`Format "${ formatTypeForBareElement.name }" is already registered to handle bare tag name "${ settings.tagName }".`
);
return;
}
} else {
const formatTypeForClassName = select( 'core/rich-text' )
.getFormatTypeForClassName( settings.className );

if ( formatTypeForClassName ) {
window.console.error(
`Format "${ formatTypeForClassName.name }" is already registered to handle class name "${ settings.className }".`
);
return;
}
}

if ( ! ( 'title' in settings ) || settings.title === '' ) {
window.console.error(
'The format "' + settings.name + '" must have a title.'
Expand Down
34 changes: 34 additions & 0 deletions packages/rich-text/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* External dependencies
*/
import createSelector from 'rememo';
import { find } from 'lodash';

/**
* Returns all the available format types.
Expand All @@ -28,3 +29,36 @@ export const getFormatTypes = createSelector(
export function getFormatType( state, name ) {
return state.formatTypes[ name ];
}

/**
* Gets the format type, if any, that can handle a bare element (without a
* data-format-type attribute), given the tag name of this element.
*
* @param {Object} state Data state.
* @param {string} bareElementTagName The tag name of the element to find a
* format type for.
* @return {?Object} Format type.
*/
export function getFormatTypeForBareElement( state, bareElementTagName ) {
return find( getFormatTypes( state ), ( { tagName } ) => {
return bareElementTagName === tagName;
} );
}

/**
* Gets the format type, if any, that can handle an element, given its classes.
*
* @param {Object} state Data state.
* @param {string} elementClassName The classes of the element to find a format
* type for.
* @return {?Object} Format type.
*/
export function getFormatTypeForClassName( state, elementClassName ) {
return find( getFormatTypes( state ), ( { className } ) => {
if ( className === null ) {
return false;
}

return ` ${ elementClassName } `.indexOf( ` ${ className } ` ) >= 0;
} );
}
26 changes: 25 additions & 1 deletion packages/rich-text/src/test/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import { JSDOM } from 'jsdom';
*/
import { create } from '../create';
import { createElement } from '../create-element';
import { getSparseArrayLength, spec } from './helpers';
import { registerFormatType } from '../register-format-type';
import { unregisterFormatType } from '../unregister-format-type';
import { getSparseArrayLength, spec, specWithRegistration } from './helpers';

const { window } = new JSDOM();
const { document } = window;
Expand Down Expand Up @@ -54,6 +56,28 @@ describe( 'create', () => {
} );
} );

specWithRegistration.forEach( ( {
description,
formatName,
formatType,
html,
value,
} ) => {
it( description, () => {
if ( formatName ) {
registerFormatType( formatName, formatType );
}

const result = create( { html } );

if ( formatName ) {
unregisterFormatType( formatName );
}

expect( result ).toEqual( value );
} );
} );

it( 'should reference formats', () => {
const value = create( { html: '<em>te<strong>st</strong></em>' } );

Expand Down
77 changes: 77 additions & 0 deletions packages/rich-text/src/test/helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -709,3 +709,80 @@ export const spec = [
},
},
];

export const specWithRegistration = [
{
description: 'should create format by matching the class',
formatName: 'my-plugin/link',
formatType: {
title: 'Custom Link',
tagName: 'a',
className: 'custom-format',
edit() {},
},
html: '<a class="custom-format">a</a>',
value: {
formats: [ [ {
type: 'my-plugin/link',
attributes: {},
unregisteredAttributes: {},
} ] ],
text: 'a',
},
},
{
description: 'should retain class names',
formatName: 'my-plugin/link',
formatType: {
title: 'Custom Link',
tagName: 'a',
className: 'custom-format',
edit() {},
},
html: '<a class="custom-format test">a</a>',
value: {
formats: [ [ {
type: 'my-plugin/link',
attributes: {},
unregisteredAttributes: {
class: 'test',
},
} ] ],
text: 'a',
},
},
{
description: 'should create base format',
formatName: 'core/link',
formatType: {
title: 'Link',
tagName: 'a',
className: null,
edit() {},
},
html: '<a class="custom-format">a</a>',
value: {
formats: [ [ {
type: 'core/link',
attributes: {},
unregisteredAttributes: {
class: 'custom-format',
},
} ] ],
text: 'a',
},
},
{
description: 'should create fallback format',
html: '<a class="custom-format">a</a>',
value: {
formats: [ [ {
type: 'a',
attributes: {
class: 'custom-format',
},
} ] ],
text: 'a',
},
},
];
Loading

0 comments on commit 8ff7d19

Please sign in to comment.