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

[Mobile] Fix regressions with wrapper props and font size customization #56985

Merged
merged 13 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 39 additions & 20 deletions packages/block-editor/src/components/block-list/block.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* External dependencies
*/
import { Pressable, View } from 'react-native';
import classnames from 'classnames';

/**
* WordPress dependencies
Expand All @@ -12,6 +13,7 @@ import {
getMergedGlobalStyles,
useMobileGlobalStylesColors,
useGlobalStyles,
withFilters,
} from '@wordpress/components';
import {
__experimentalGetAccessibleBlockLabel as getAccessibleBlockLabel,
Expand Down Expand Up @@ -42,20 +44,36 @@ import { useSettings } from '../use-settings';

const EMPTY_ARRAY = [];

// Helper function to memoize the wrapperProps since getEditWrapperProps always returns a new reference.
const wrapperPropsCache = new WeakMap();
const emptyObj = {};
function getWrapperProps( value, getWrapperPropsFunction ) {
if ( ! getWrapperPropsFunction ) {
return emptyObj;
/**
* Merges wrapper props with special handling for classNames and styles.
*
* @param {Object} propsA
* @param {Object} propsB
*
* @return {Object} Merged props.
*/
function mergeWrapperProps( propsA, propsB ) {
const newProps = {
...propsA,
...propsB,
};

// May be set to undefined, so check if the property is set!
if (
propsA?.hasOwnProperty( 'className' ) &&
propsB?.hasOwnProperty( 'className' )
) {
newProps.className = classnames( propsA.className, propsB.className );
}
const cachedValue = wrapperPropsCache.get( value );
if ( ! cachedValue ) {
const wrapperProps = getWrapperPropsFunction( value );
wrapperPropsCache.set( value, wrapperProps );
return wrapperProps;

if (
propsA?.hasOwnProperty( 'style' ) &&
propsB?.hasOwnProperty( 'style' )
) {
newProps.style = { ...propsA.style, ...propsB.style };
}
return cachedValue;

return newProps;
}

function BlockWrapper( {
Expand Down Expand Up @@ -136,6 +154,7 @@ function BlockListBlock( {
rootClientId,
setAttributes,
toggleSelection,
wrapperProps,
} ) {
const {
baseGlobalStyles,
Expand Down Expand Up @@ -252,12 +271,11 @@ function BlockListBlock( {
[ blockWidth, setBlockWidth ]
);

// Block level styles.
let wrapperProps = {};
// Determine whether the block has props to apply to the wrapper.
if ( blockType?.getEditWrapperProps ) {
wrapperProps = getWrapperProps(
attributes,
blockType.getEditWrapperProps
wrapperProps = mergeWrapperProps(
wrapperProps,
blockType.getEditWrapperProps( attributes )
);
}

Expand All @@ -266,7 +284,7 @@ function BlockListBlock( {
return getMergedGlobalStyles(
baseGlobalStyles,
globalStyle,
wrapperProps.style,
wrapperProps?.style,
attributes,
defaultColors,
name,
Expand All @@ -284,7 +302,7 @@ function BlockListBlock( {
// eslint-disable-next-line react-hooks/exhaustive-deps
JSON.stringify( globalStyle ),
// eslint-disable-next-line react-hooks/exhaustive-deps
JSON.stringify( wrapperProps.style ),
JSON.stringify( wrapperProps?.style ),
// eslint-disable-next-line react-hooks/exhaustive-deps
JSON.stringify(
Object.fromEntries(
Expand Down Expand Up @@ -651,5 +669,6 @@ export default compose(
// Block is sometimes not mounted at the right time, causing it be undefined
// see issue for more info
// https://github.com/WordPress/gutenberg/issues/17013
ifCondition( ( { block } ) => !! block )
ifCondition( ( { block } ) => !! block ),
withFilters( 'editor.BlockListBlock' )
)( BlockListBlock );
7 changes: 4 additions & 3 deletions packages/block-editor/src/hooks/index.native.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
/**
* Internal dependencies
*/
import { createBlockEditFilter } from './utils';
import { createBlockEditFilter, createBlockListBlockFilter } from './utils';
import './compat';
import align from './align';
import anchor from './anchor';
import './custom-class-name';
import './generated-class-name';
import style from './style';
import './color';
import './font-size';
import color from './color';
import fontSize from './font-size';
import './layout';

createBlockEditFilter( [ align, anchor, style ] );
createBlockListBlockFilter( [ align, style, color, fontSize ] );

export { getBorderClassesAndStyles, useBorderProps } from './use-border-props';
export { getColorClassesAndStyles, useColorProps } from './use-color-props';
Expand Down
64 changes: 31 additions & 33 deletions packages/block-editor/src/hooks/typography.native.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,48 @@
/**
* WordPress dependencies
*/
import { hasBlockSupport } from '@wordpress/blocks';
/**
* External dependencies
*/
import { useSelect } from '@wordpress/data';
import { pure } from '@wordpress/compose';
import { PanelBody } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import InspectorControls from '../components/inspector-controls';
import { useHasTypographyPanel } from '../components/global-styles/typography-panel';

import {
LINE_HEIGHT_SUPPORT_KEY,
LineHeightEdit,
useIsLineHeightDisabled,
} from './line-height';
import {
FONT_SIZE_SUPPORT_KEY,
FontSizeEdit,
useIsFontSizeDisabled,
} from './font-size';
import { store as blockEditorStore } from '../store';

import { LINE_HEIGHT_SUPPORT_KEY, LineHeightEdit } from './line-height';
import { FONT_SIZE_SUPPORT_KEY, FontSizeEdit } from './font-size';

export const TYPOGRAPHY_SUPPORT_KEY = 'typography';
export const TYPOGRAPHY_SUPPORT_KEYS = [
LINE_HEIGHT_SUPPORT_KEY,
FONT_SIZE_SUPPORT_KEY,
];

export function TypographyPanel( props ) {
const isDisabled = useIsTypographyDisabled( props );
const isSupported = hasTypographySupport( props.name );

if ( isDisabled || ! isSupported ) return null;
function TypographyPanelPure( { clientId, setAttributes, settings } ) {
function selector( select ) {
const { style, fontFamily, fontSize } =
select( blockEditorStore ).getBlockAttributes( clientId ) || {};
return { style, fontFamily, fontSize };
}
const { style, fontSize } = useSelect( selector, [ clientId ] );
const isEnabled = useHasTypographyPanel( settings );

if ( ! isEnabled ) {
return null;
}
fluiddot marked this conversation as resolved.
Show resolved Hide resolved

const props = {
attributes: {
fontSize,
style,
},
setAttributes,
};

return (
<InspectorControls>
Expand All @@ -46,17 +54,7 @@ export function TypographyPanel( props ) {
);
}

const hasTypographySupport = ( blockName ) => {
return TYPOGRAPHY_SUPPORT_KEYS.some( ( key ) =>
hasBlockSupport( blockName, key )
);
};

function useIsTypographyDisabled( props = {} ) {
const configs = [
useIsFontSizeDisabled( props ),
useIsLineHeightDisabled( props ),
];

return configs.filter( Boolean ).length === configs.length;
}
// We don't want block controls to re-render when typing inside a block. `pure`
// will prevent re-renders unless props change, so only pass the needed props
// and not the whole attributes object.
export const TypographyPanel = pure( TypographyPanelPure );
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,15 @@ exports[`Paragraph block should render without crashing and match snapshot 1`] =
/>
</View>
`;

exports[`Paragraph block should set a font size value 1`] = `
"<!-- wp:paragraph {"style":{"typography":{"fontSize":"30px"}}} -->
fluiddot marked this conversation as resolved.
Show resolved Hide resolved
<p style="font-size:30px">A quick brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
`;

exports[`Paragraph block should set a line height value 1`] = `
"<!-- wp:paragraph {"style":{"typography":{"lineHeight":1.8}}} -->
<p style="line-height:1.8">A quick brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
`;
114 changes: 114 additions & 0 deletions packages/block-library/src/paragraph/test/edit.native.js
fluiddot marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import {
act,
addBlock,
dismissModal,
getBlock,
typeInRichText,
fireEvent,
Expand All @@ -15,6 +16,7 @@ import {
within,
withFakeTimers,
waitForElementToBeRemoved,
waitForModalVisible,
} from 'test/helpers';
import Clipboard from '@react-native-clipboard/clipboard';
import TextInputState from 'react-native/Libraries/Components/TextInput/TextInputState';
Expand Down Expand Up @@ -687,6 +689,118 @@ describe( 'Paragraph block', () => {
` );
} );

it( 'should show the expected font sizes values', async () => {
// Arrange
const screen = await initializeEditor( { withGlobalStyles: true } );
await addBlock( screen, 'Paragraph' );

// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );

// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitForModalVisible( blockSettingsModal );

// Open Font size settings
fireEvent.press( screen.getByLabelText( 'Font Size, Custom' ) );
await waitFor( () => screen.getByLabelText( 'Selected: Default' ) );

// Assert
const modalContent = within( blockSettingsModal );
expect( modalContent.getByLabelText( 'Small' ) ).toBeVisible();
expect( modalContent.getByText( '14px' ) ).toBeVisible();
expect( modalContent.getByLabelText( 'Medium' ) ).toBeVisible();
expect( modalContent.getByText( '17px' ) ).toBeVisible();
expect( modalContent.getByLabelText( 'Large' ) ).toBeVisible();
expect( modalContent.getByText( '30px' ) ).toBeVisible();
expect( modalContent.getByLabelText( 'Extra Large' ) ).toBeVisible();
expect( modalContent.getByText( '40px' ) ).toBeVisible();
expect(
modalContent.getByLabelText( 'Extra Extra Large' )
).toBeVisible();
expect( modalContent.getByText( '52px' ) ).toBeVisible();
} );

it( 'should set a font size value', async () => {
// Arrange
const screen = await initializeEditor( { withGlobalStyles: true } );
await addBlock( screen, 'Paragraph' );

// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );

// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitForModalVisible( blockSettingsModal );

// Open Font size settings
fireEvent.press( screen.getByLabelText( 'Font Size, Custom' ) );

// Tap one font size
fireEvent.press( screen.getByLabelText( 'Large' ) );

// Dismiss the Block Settings modal.
await dismissModal( blockSettingsModal );

// Assert
expect( getEditorHtml() ).toMatchSnapshot();
} );

it( 'should set a line height value', async () => {
// Arrange
const screen = await initializeEditor( { withGlobalStyles: true } );
await addBlock( screen, 'Paragraph' );

// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );

// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitForModalVisible( blockSettingsModal );

const lineHeightControl = screen.getByLabelText( /Line Height/ );
fireEvent.press(
within( lineHeightControl ).getByText( '1.5', { hidden: true } )
);
const lineHeightTextInput = within(
lineHeightControl
).getByDisplayValue( '1.5', { hidden: true } );
fireEvent.changeText( lineHeightTextInput, '1.8' );

// Dismiss the Block Settings modal.
await dismissModal( blockSettingsModal );

// Assert
expect( getEditorHtml() ).toMatchSnapshot();
} );

it( 'should focus on the previous Paragraph block when backspacing in an empty Paragraph block', async () => {
// Arrange
const screen = await initializeEditor();
Expand Down
Loading
Loading