Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quote v2: add migration #39844

Merged
merged 9 commits into from
Mar 29, 2022
Merged
3 changes: 1 addition & 2 deletions packages/block-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
"src/**/*.scss",
"src/navigation-link/index.js",
"src/template-part/index.js",
"src/query/index.js",
"src/quote/v2/index.js"
"src/query/index.js"
],
"dependencies": {
"@babel/runtime": "^7.16.0",
Expand Down
107 changes: 107 additions & 0 deletions packages/block-library/src/quote/v2/deprecated.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { omit } from 'lodash';

/**
* WordPress dependencies
*/
import { createBlock, parseWithAttributeSchema } from '@wordpress/blocks';
import { RichText, useBlockProps } from '@wordpress/block-editor';

/**
* Internal dependencies
*/
import deprecationsForV1Block from '../deprecated';

export const migrateToQuoteV2 = ( attributes ) => {
oandregal marked this conversation as resolved.
Show resolved Hide resolved
const { value } = attributes;

return [
{
...omit( attributes, [ 'value' ] ),
},
value
? parseWithAttributeSchema( value, {
youknowriad marked this conversation as resolved.
Show resolved Hide resolved
type: 'array',
source: 'query',
selector: 'p',
query: {
content: {
type: 'string',
source: 'html',
},
},
} ).map( ( { content } ) =>
createBlock( 'core/paragraph', { content } )
)
: createBlock( 'core/paragraph' ),
];
};

const v3 = {
attributes: {
value: {
type: 'string',
source: 'html',
selector: 'blockquote',
multiline: 'p',
default: '',
__experimentalRole: 'content',
},
citation: {
type: 'string',
source: 'html',
selector: 'cite',
default: '',
__experimentalRole: 'content',
},
align: {
type: 'string',
},
},
supports: {
anchor: true,
__experimentalSlashInserter: true,
typography: {
fontSize: true,
lineHeight: true,
__experimentalFontStyle: true,
__experimentalFontWeight: true,
__experimentalLetterSpacing: true,
__experimentalTextTransform: true,
__experimentalDefaultControls: {
fontSize: true,
fontAppearance: true,
},
},
},
save( { attributes } ) {
const { align, value, citation } = attributes;

const className = classnames( {
[ `has-text-align-${ align }` ]: align,
} );

return (
<blockquote { ...useBlockProps.save( { className } ) }>
<RichText.Content multiline value={ value } />
{ ! RichText.isEmpty( citation ) && (
<RichText.Content tagName="cite" value={ citation } />
) }
</blockquote>
);
},
migrate: migrateToQuoteV2,
};

/**
* New deprecations need to be placed first
* for them to have higher priority.
*
* Old deprecations may need to be updated as well.
*
* See block-deprecation.md
*/
export default [ v3, ...deprecationsForV1Block ];
48 changes: 45 additions & 3 deletions packages/block-library/src/quote/v2/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,66 @@ import {
store as blockEditorStore,
} from '@wordpress/block-editor';
import { BlockQuotation } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { useDispatch, useSelect, useRegistry } from '@wordpress/data';
import { createBlock } from '@wordpress/blocks';
import { Platform } from '@wordpress/element';
import { Platform, useEffect } from '@wordpress/element';

/**
* Internal dependencies
*/
import { migrateToQuoteV2 } from './deprecated';

const isWebPlatform = Platform.OS === 'web';
const TEMPLATE = [ [ 'core/paragraph', {} ] ];

/**
* At the moment, deprecations don't handle create blocks from attributes
* (like when using CPT templates). For this reason, this hook is necessary
* to avoid breaking templates using the old quote block format.
*
* @param {Object} attributes Block attributes.
* @param {string} clientId Block client ID.
*/
const useMigrateOnLoad = ( attributes, clientId ) => {
const registry = useRegistry();
const { updateBlockAttributes, replaceInnerBlocks } = useDispatch(
blockEditorStore
);
useEffect( () => {
// As soon as the block is loaded, migrate it to the new version.

if ( ! attributes.value ) {
// No need to migrate if it doesn't have the value attribute.
return;
}

const [ newAttributes, newInnerBlocks ] = migrateToQuoteV2(
attributes
);
oandregal marked this conversation as resolved.
Show resolved Hide resolved

registry.batch( () => {
updateBlockAttributes( clientId, newAttributes );
replaceInnerBlocks( clientId, newInnerBlocks );
} );
}, [ attributes.value ] );
};

export default function QuoteEdit( {
attributes: { citation },
attributes,
setAttributes,
isSelected,
insertBlocksAfter,
clientId,
} ) {
const { citation } = attributes;

useMigrateOnLoad( attributes, clientId );
Copy link
Member Author

Choose a reason for hiding this comment

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


const isAncestorOfSelectedBlock = useSelect( ( select ) =>
select( blockEditorStore ).hasSelectedInnerBlock( clientId )
);
const hasSelection = isSelected || isAncestorOfSelectedBlock;

const blockProps = useBlockProps();
const innerBlocksProps = useInnerBlocksProps( blockProps, {
template: TEMPLATE,
Expand Down
34 changes: 2 additions & 32 deletions packages/block-library/src/quote/v2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
*/
import { __ } from '@wordpress/i18n';
import { quote as icon } from '@wordpress/icons';
import { addFilter } from '@wordpress/hooks';

/**
* Internal dependencies
*/
import edit from './edit';
import save from './save';
import transforms from './transforms';
import deprecated from './deprecated';

const settings = {
icon,
Expand All @@ -30,37 +30,7 @@ const settings = {
transforms,
edit,
save,
deprecated,
};

export default settings;

/**
* This function updates the attributes for the quote v2.
* This should be moved to block.json when v2 becomes the default.
*
* @param {Array} blockSettings The settings of the block to be registered.
* @param {string} blockName The name of the block to be registered.
* @return {Array} New settings.
*/
function registerQuoteV2Attributes( blockSettings, blockName ) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Had to remove the filter because it was affecting the deprecation, preventing it from having the value attribute to migrate. The result of this change is that when v2 is enabled, the value attribute will still be present.

We need to remove the attribute from the block.json when v2 becomes the default (see).

Copy link
Contributor

Choose a reason for hiding this comment

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

We need to remove the attribute from the block.json when v2 becomes the default

I don't think we can remove it until the useMigrateOnLoad is removed.

if ( ! window?.__experimentalEnableQuoteBlockV2 ) {
return blockSettings;
}

if ( blockName !== 'core/quote' ) {
return blockSettings;
}

// Deregister the old ones.
delete blockSettings.attributes.value;

return blockSettings;
}

// Importing this file includes side effects.
// This has been declared in block-library/package.json under sideEffects.
addFilter(
'blocks.registerBlockType',
'core/quote/v2-attributes',
registerQuoteV2Attributes
);
92 changes: 92 additions & 0 deletions packages/block-library/src/quote/v2/test/migrate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* WordPress dependencies
*/
import {
registerBlockType,
serialize,
unregisterBlockType,
} from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { migrateToQuoteV2 } from '../deprecated';
import * as paragraph from '../../../paragraph';
import * as quote from '../../../quote';
import * as quoteV2 from '../../../quote/v2';

describe( 'Migrate quote block', () => {
beforeAll( () => {
registerBlockType(
{ name: paragraph.name, ...paragraph.metadata },
paragraph.settings
);
registerBlockType(
{ name: quote.name, ...quote.metadata },
quoteV2.settings
);
} );

afterAll( () => {
unregisterBlockType( paragraph.name );
unregisterBlockType( quote.name );
} );

it( 'should migrate the value attribute to inner blocks', () => {
const [ attributes, innerBlocks ] = migrateToQuoteV2( {
value: '<p>First paragraph</p><p>Second paragraph</p>',
citation: 'Author',
} );
expect( attributes ).toEqual( {
citation: 'Author',
} );
expect( serialize( innerBlocks ) ).toEqual(
`<!-- wp:paragraph -->
<p>First paragraph</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Second paragraph</p>
<!-- /wp:paragraph -->`
);
} );

it( 'should create a paragraph if value is empty', () => {
const [ attributes, innerBlocks ] = migrateToQuoteV2( {
value: undefined,
citation: 'Author',
} );
expect( attributes ).toEqual( {
citation: 'Author',
} );
expect( serialize( innerBlocks ) ).toEqual(
`<!-- wp:paragraph -->
<p></p>
<!-- /wp:paragraph -->`
);
} );

it( 'should keep the formats of the value', () => {
const [ attributes, innerBlocks ] = migrateToQuoteV2( {
value:
'<p><strong>Bold</strong></p><p> and </p><p><em>italic</em></p>',
citation: 'Author',
} );
expect( attributes ).toEqual( {
citation: 'Author',
} );
expect( serialize( innerBlocks ) ).toEqual(
`<!-- wp:paragraph -->
<p><strong>Bold</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p> and </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em>italic</em></p>
<!-- /wp:paragraph -->`
);
} );
} );
2 changes: 1 addition & 1 deletion packages/block-library/src/quote/v2/transforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const transforms = {
query: {
content: {
type: 'string',
source: 'text',
Copy link
Member Author

Choose a reason for hiding this comment

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

I've commited this here because it's the same fix I did for the migration. Thought it doesn't deserve a separate PR.

source: 'html',
},
},
} ).map( ( { content } ) =>
Expand Down