Skip to content

Commit

Permalink
RichText: List: Fix outdent with children (#13559)
Browse files Browse the repository at this point in the history
* Fix outdent

* Add unit test

* Add e2e test

* Add unit tests for getLastChildIndex
  • Loading branch information
ellatrix authored Jan 29, 2019
1 parent 6d500c2 commit ccbf07e
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 11 deletions.
12 changes: 12 additions & 0 deletions packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,18 @@ exports[`List should indent and outdent level 2 3`] = `
<!-- /wp:list -->"
`;
exports[`List should outdent with children 1`] = `
"<!-- wp:list -->
<ul><li>a<ul><li>b<ul><li>c</li></ul></li></ul></li></ul>
<!-- /wp:list -->"
`;
exports[`List should outdent with children 2`] = `
"<!-- wp:list -->
<ul><li>a</li><li>b<ul><li>c</li></ul></li></ul>
<!-- /wp:list -->"
`;
exports[`List should split indented list item 1`] = `
"<!-- wp:list -->
<ul><li>one<ul><li>two</li><li>three</li></ul></li></ul>
Expand Down
18 changes: 18 additions & 0 deletions packages/e2e-tests/specs/blocks/list.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,4 +262,22 @@ describe( 'List', () => {

expect( await getEditedPostContent() ).toMatchSnapshot();
} );

it( 'should outdent with children', async () => {
await insertBlock( 'List' );
await page.keyboard.type( 'a' );
await page.keyboard.press( 'Enter' );
await pressKeyWithModifier( 'primary', 'm' );
await page.keyboard.type( 'b' );
await page.keyboard.press( 'Enter' );
await pressKeyWithModifier( 'primary', 'm' );
await page.keyboard.type( 'c' );

expect( await getEditedPostContent() ).toMatchSnapshot();

await page.keyboard.press( 'ArrowUp' );
await pressKeyWithModifier( 'primaryShift', 'm' );

expect( await getEditedPostContent() ).toMatchSnapshot();
} );
} );
40 changes: 40 additions & 0 deletions packages/rich-text/src/get-last-child-index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Internal dependencies
*/

import { LINE_SEPARATOR } from './special-characters';

/**
* Gets the line index of the last child in the list.
*
* @param {Object} value Value to search.
* @param {number} lineIndex Line index of a list item in the list.
*
* @return {Array} The index of the last child.
*/
export function getLastChildIndex( { text, formats }, lineIndex ) {
const lineFormats = formats[ lineIndex ] || [];
// Use the given line index in case there are no next children.
let childIndex = lineIndex;

// `lineIndex` could be `undefined` if it's the first line.
for ( let index = lineIndex || 0; index < text.length; index++ ) {
// We're only interested in line indices.
if ( text[ index ] !== LINE_SEPARATOR ) {
continue;
}

const formatsAtIndex = formats[ index ] || [];

// If the amout of formats is equal or more, store it, then return the
// last one if the amount of formats is less.
if ( formatsAtIndex.length >= lineFormats.length ) {
childIndex = index;
} else {
return childIndex;
}
}

// If the end of the text is reached, return the last child index.
return childIndex;
}
21 changes: 14 additions & 7 deletions packages/rich-text/src/outdent-list-items.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { LINE_SEPARATOR } from './special-characters';
import { normaliseFormats } from './normalise-formats';
import { getLineIndex } from './get-line-index';
import { getParentLineIndex } from './get-parent-line-index';
import { getLastChildIndex } from './get-last-child-index';

/**
* Outdents any selected list items if possible.
Expand All @@ -16,17 +17,23 @@ import { getParentLineIndex } from './get-parent-line-index';
*/
export function outdentListItems( value ) {
const { text, formats, start, end } = value;
const lineIndex = getLineIndex( value );
const lineFormats = formats[ lineIndex ];
const startingLineIndex = getLineIndex( value, start );

if ( lineFormats === undefined ) {
// Return early if the starting line index cannot be further outdented.
if ( formats[ startingLineIndex ] === undefined ) {
return value;
}

const newFormats = formats.slice( 0 );
const parentFormats = formats[ getParentLineIndex( value, lineIndex ) ] || [];

for ( let index = lineIndex; index < end; index++ ) {
const parentFormats = formats[ getParentLineIndex( value, startingLineIndex ) ] || [];
const endingLineIndex = getLineIndex( value, end );
const lastChildIndex = getLastChildIndex( value, endingLineIndex );

// Outdent all list items from the starting line index until the last child
// index of the ending list. All children of the ending list need to be
// outdented, otherwise they'll be orphaned.
for ( let index = startingLineIndex; index <= lastChildIndex; index++ ) {
// Skip indices that are not line separators.
if ( text[ index ] !== LINE_SEPARATOR ) {
continue;
}
Expand All @@ -37,7 +44,7 @@ export function outdentListItems( value ) {
);

if ( newFormats[ index ].length === 0 ) {
delete newFormats[ lineIndex ];
delete newFormats[ index ];
}
}

Expand Down
50 changes: 50 additions & 0 deletions packages/rich-text/src/test/get-last-child-index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* External dependencies
*/
import deepFreeze from 'deep-freeze';

/**
* Internal dependencies
*/

import { getLastChildIndex } from '../get-last-child-index';
import { LINE_SEPARATOR } from '../special-characters';

describe( 'outdentListItems', () => {
const ul = { type: 'ul' };

it( 'should return undefined if there is only one line', () => {
expect( getLastChildIndex( deepFreeze( {
formats: [ , ],
text: '1',
} ), undefined ) ).toBe( undefined );
} );

it( 'should return the last line if no line is indented', () => {
expect( getLastChildIndex( deepFreeze( {
formats: [ , ],
text: `1${ LINE_SEPARATOR }`,
} ), undefined ) ).toBe( 1 );
} );

it( 'should return the last child index', () => {
expect( getLastChildIndex( deepFreeze( {
formats: [ , [ ul ], , [ ul ], , ],
text: `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3`,
} ), undefined ) ).toBe( 3 );
} );

it( 'should return the last child index by sibling', () => {
expect( getLastChildIndex( deepFreeze( {
formats: [ , [ ul ], , [ ul ], , ],
text: `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3`,
} ), 1 ) ).toBe( 3 );
} );

it( 'should return the last child index (with further lower indented items)', () => {
expect( getLastChildIndex( deepFreeze( {
formats: [ , [ ul ], , , , ],
text: `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3`,
} ), 1 ) ).toBe( 1 );
} );
} );
30 changes: 26 additions & 4 deletions packages/rich-text/src/test/outdent-list-items.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe( 'outdentListItems', () => {
start: 1,
end: 1,
};
const result = outdentListItems( deepFreeze( record ), ul );
const result = outdentListItems( deepFreeze( record ) );

expect( result ).toEqual( record );
expect( result ).toBe( record );
Expand All @@ -43,7 +43,7 @@ describe( 'outdentListItems', () => {
start: 2,
end: 2,
};
const result = outdentListItems( deepFreeze( record ), ul );
const result = outdentListItems( deepFreeze( record ) );

expect( result ).toEqual( expected );
expect( result ).not.toBe( record );
Expand All @@ -65,7 +65,7 @@ describe( 'outdentListItems', () => {
start: 5,
end: 5,
};
const result = outdentListItems( deepFreeze( record ), ul );
const result = outdentListItems( deepFreeze( record ) );

expect( result ).toEqual( expected );
expect( result ).not.toBe( record );
Expand All @@ -87,10 +87,32 @@ describe( 'outdentListItems', () => {
start: 2,
end: 5,
};
const result = outdentListItems( deepFreeze( record ), ul );
const result = outdentListItems( deepFreeze( record ) );

expect( result ).toEqual( expected );
expect( result ).not.toBe( record );
expect( getSparseArrayLength( result.formats ) ).toBe( 1 );
} );

it( 'should outdent list item with children', () => {
// As we're testing list formats, the text should remain the same.
const text = `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3${ LINE_SEPARATOR }4`;
const record = {
formats: [ , [ ul ], , [ ul, ul ], , [ ul, ul ], , ],
text,
start: 2,
end: 2,
};
const expected = {
formats: [ , , , [ ul ], , [ ul ], , ],
text,
start: 2,
end: 2,
};
const result = outdentListItems( deepFreeze( record ) );

expect( result ).toEqual( expected );
expect( result ).not.toBe( record );
expect( getSparseArrayLength( result.formats ) ).toBe( 2 );
} );
} );

0 comments on commit ccbf07e

Please sign in to comment.