Skip to content

Commit

Permalink
[Mobile] Fix regressions with wrapper props and font size customizati…
Browse files Browse the repository at this point in the history
…on (#56985)

* Mobile - Hooks - Use createBlockListBlockFilter

* Mobile - Typography - Refactor the code to incorporate the latest changes from its web counterpart

* Mobile - BlockList: Apply editor.BlockListBlock filter to fix issue with missing block props, as well as refactoring the getEditWrapperProps logic to use the same approach as its web counterpart

* Mobile - Test helpers - Add more global styles data: font sizes and line height

* Mobile - Font Size Picker - Improvde the accessibility label for the Font Size selector

* Mobile - Paragraph tests - Add test for font size and line height customization

* Mobile - Safe guard from an undefined wrapperProps value

* Mobile - Fix having the default font sizes when there are theme font sizes available

* Mobile - Global styles context test - Remove default font sizes

* Mobile - Paragraph tests - Update tests to use modal helpers

* Mobile - Paragraph tests - Adds test to check if the available font sizes are the ones expected with no duplicates

* Update Changelog
  • Loading branch information
Gerardo Pacheco authored Dec 13, 2023
1 parent a51534b commit 94ede3f
Show file tree
Hide file tree
Showing 10 changed files with 363 additions and 95 deletions.
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;
}

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"}}} -->
<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
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

1 comment on commit 94ede3f

@github-actions
Copy link

Choose a reason for hiding this comment

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

Flaky tests detected in 94ede3f.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/7198759264
📝 Reported issues:

Please sign in to comment.