-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Paragraph: Add LineHeightControl + Attribute #20775
Changes from all commits
b9c4827
58d57f7
d7fba5e
9319076
38c4404
7867960
bb61179
0facc59
601a1f2
d455c17
ce678af
499d59e
c329f21
2d6beb4
2e99301
864e8ea
cc18a2b
0838ee0
002e753
3bd0457
bda4f0e
5900583
35794cc
bf7ef83
96d4158
5f43e25
c449aad
5923ffc
777f079
a129ab8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { TextControl } from '@wordpress/components'; | ||
import { ZERO } from '@wordpress/keycodes'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { | ||
BASE_DEFAULT_VALUE, | ||
RESET_VALUE, | ||
STEP, | ||
useIsLineHeightControlsDisabled, | ||
isLineHeightDefined, | ||
} from './utils'; | ||
|
||
export default function LineHeightControl( { value: lineHeight, onChange } ) { | ||
const isDisabled = useIsLineHeightControlsDisabled(); | ||
const isDefined = isLineHeightDefined( lineHeight ); | ||
|
||
// Don't render the controls if disabled by editor settings | ||
if ( isDisabled ) { | ||
return null; | ||
} | ||
|
||
const handleOnKeyDown = ( event ) => { | ||
const { keyCode } = event; | ||
|
||
if ( keyCode === ZERO && ! isDefined ) { | ||
/** | ||
* Prevents the onChange callback from firing, which prevents | ||
* the logic from assuming the change was triggered from | ||
* an input arrow CLICK. | ||
*/ | ||
event.preventDefault(); | ||
onChange( '0' ); | ||
} | ||
}; | ||
|
||
const handleOnChange = ( nextValue ) => { | ||
// Set the next value without modification if lineHeight has been defined | ||
if ( isDefined ) { | ||
onChange( nextValue ); | ||
return; | ||
} | ||
|
||
// Otherwise... | ||
/** | ||
* The following logic handles the initial up/down arrow CLICK of the | ||
* input element. This is so that the next values (from an undefined value state) | ||
* are more better suited for line-height rendering. | ||
*/ | ||
let adjustedNextValue = nextValue; | ||
|
||
switch ( nextValue ) { | ||
case `${ STEP }`: | ||
// Increment by step value | ||
adjustedNextValue = BASE_DEFAULT_VALUE + STEP; | ||
break; | ||
case '0': | ||
// Decrement by step value | ||
adjustedNextValue = BASE_DEFAULT_VALUE - STEP; | ||
break; | ||
} | ||
|
||
onChange( adjustedNextValue ); | ||
}; | ||
|
||
const value = isDefined ? lineHeight : RESET_VALUE; | ||
|
||
return ( | ||
<div className="block-editor-line-height-control"> | ||
<TextControl | ||
autoComplete="off" | ||
onKeyDown={ handleOnKeyDown } | ||
onChange={ handleOnChange } | ||
label={ __( 'Line height' ) } | ||
placeholder={ BASE_DEFAULT_VALUE } | ||
step={ STEP } | ||
type="number" | ||
value={ value } | ||
min={ 0 } | ||
/> | ||
</div> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
.block-editor-line-height-control { | ||
margin-bottom: 24px; | ||
|
||
input { | ||
display: block; | ||
max-width: 60px; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useSelect } from '@wordpress/data'; | ||
|
||
export const BASE_DEFAULT_VALUE = 1.5; | ||
export const STEP = 0.1; | ||
/** | ||
* There are varying value types within LineHeightControl: | ||
* | ||
* {undefined} Initial value. No changes from the user. | ||
* {string} Input value. Value consumed/outputted by the input. Empty would be ''. | ||
* {number} Block attribute type. Input value needs to be converted for attribute setting. | ||
* | ||
* Note: If the value is undefined, the input requires it to be an empty string ('') | ||
* in order to be considered "controlled" by props (rather than internal state). | ||
*/ | ||
export const RESET_VALUE = ''; | ||
|
||
/** | ||
* Retrieves whether custom lineHeight controls should be disabled from editor settings. | ||
* | ||
* @return {boolean} Whether lineHeight controls should be disabled. | ||
*/ | ||
export function useIsLineHeightControlsDisabled() { | ||
const isDisabled = useSelect( ( select ) => { | ||
const { getSettings } = select( 'core/block-editor' ); | ||
|
||
return !! getSettings().__experimentalDisableCustomLineHeight; | ||
}, [] ); | ||
|
||
return isDisabled; | ||
} | ||
|
||
/** | ||
* Determines if the lineHeight attribute has been properly defined. | ||
* | ||
* @param {any} lineHeight The value to check. | ||
* | ||
* @return {boolean} Whether the lineHeight attribute is valid. | ||
*/ | ||
export function isLineHeightDefined( lineHeight ) { | ||
return lineHeight !== undefined && lineHeight !== RESET_VALUE; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { addFilter } from '@wordpress/hooks'; | ||
import { hasBlockSupport } from '@wordpress/blocks'; | ||
import { createHigherOrderComponent } from '@wordpress/compose'; | ||
import { PanelBody } from '@wordpress/components'; | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import LineHeightControl from '../components/line-height-control'; | ||
import InspectorControls from '../components/inspector-controls'; | ||
import { cleanEmptyObject } from './utils'; | ||
|
||
export const LINE_HEIGHT_SUPPRT_KEY = '__experimentalLineHeight'; | ||
|
||
/** | ||
* Override the default edit UI to include new inspector controls for block | ||
* color, if block defines support. | ||
* | ||
* @param {Function} BlockEdit Original component | ||
* @return {Function} Wrapped component | ||
*/ | ||
export const withBlockControls = createHigherOrderComponent( | ||
( BlockEdit ) => ( props ) => { | ||
const { name: blockName } = props; | ||
if ( ! hasBlockSupport( blockName, LINE_HEIGHT_SUPPRT_KEY ) ) { | ||
return <BlockEdit key="edit" { ...props } />; | ||
} | ||
const { style } = props.attributes; | ||
const onChange = ( newLineHeightValue ) => { | ||
const newStyle = { | ||
...style, | ||
typography: { | ||
lineHeight: newLineHeightValue, | ||
}, | ||
}; | ||
props.setAttributes( { | ||
style: cleanEmptyObject( newStyle ), | ||
} ); | ||
}; | ||
|
||
return [ | ||
<InspectorControls key="control"> | ||
<PanelBody title={ __( 'Typography' ) }> | ||
<LineHeightControl | ||
value={ style?.typography?.lineHeight } | ||
onChange={ onChange } | ||
/> | ||
</PanelBody> | ||
</InspectorControls>, | ||
<BlockEdit key="edit" { ...props } />, | ||
]; | ||
}, | ||
'withToolbarControls' | ||
); | ||
|
||
addFilter( | ||
'editor.BlockEdit', | ||
'core/color/with-block-controls', | ||
withBlockControls | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { pickBy, isEqual, isObject, identity, mapValues } from 'lodash'; | ||
|
||
/** | ||
* Removed undefined values from nested object. | ||
* | ||
* @param {*} object | ||
* @return {*} Object cleaned from undefined values | ||
*/ | ||
export const cleanEmptyObject = ( object ) => { | ||
if ( ! isObject( object ) ) { | ||
return object; | ||
} | ||
const cleanedNestedObjects = pickBy( | ||
mapValues( object, cleanEmptyObject ), | ||
identity | ||
); | ||
return isEqual( cleanedNestedObjects, {} ) | ||
? undefined | ||
: cleanedNestedObjects; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,7 +26,10 @@ | |
}, | ||
"direction": { | ||
"type": "string", | ||
"enum": [ "ltr", "rtl" ] | ||
"enum": [ | ||
"ltr", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prettier autofixed these ones |
||
"rtl" | ||
] | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -80,13 +80,19 @@ function ParagraphBlock( { | |
setAttributes, | ||
setFontSize, | ||
} ) { | ||
const { align, content, dropCap, placeholder, direction } = attributes; | ||
const { align, content, direction, dropCap, placeholder } = attributes; | ||
|
||
const ref = useRef(); | ||
const dropCapMinimumHeight = useDropCapMinimumHeight( dropCap, [ | ||
fontSize.size, | ||
] ); | ||
|
||
const styles = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Created a variable from inline implementation |
||
fontSize: fontSize.size ? `${ fontSize.size }px` : undefined, | ||
direction, | ||
minHeight: dropCapMinimumHeight, | ||
}; | ||
|
||
return ( | ||
<> | ||
<BlockControls> | ||
|
@@ -132,11 +138,7 @@ function ParagraphBlock( { | |
[ `has-text-align-${ align }` ]: align, | ||
[ fontSize.class ]: fontSize.class, | ||
} ) } | ||
style={ { | ||
fontSize: fontSize.size ? fontSize.size + 'px' : undefined, | ||
direction, | ||
minHeight: dropCapMinimumHeight, | ||
} } | ||
style={ styles } | ||
value={ content } | ||
onChange={ ( newContent ) => | ||
setAttributes( { content: newContent } ) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -307,7 +307,7 @@ describe( 'Embedding content', () => { | |
await page.keyboard.type( 'Hello there!' ); | ||
await publishPost(); | ||
const postUrl = await page.$eval( | ||
'[id^=inspector-text-control-]', | ||
'.editor-post-publish-panel [id^=inspector-text-control-]', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change does not seem related, what was the reason for it? Should we include a comment or maybe discuss it in another PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's the interesting thing! For the longest time, this test was failing for this PR. The reason for that is because the To resolve this, I made the test more specific to the Publish Panel There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for clarification @ItsJonQ! |
||
( el ) => el.value | ||
); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I write a value of 0 directly we assume right away another value as if the user was decreasing the value.
If I write a value of 0.1 directly we assume right away another value as if the user was increasing the value.
Maybe in the event that is triggered, we can check if the controls up/down are being used instead of using the value as a way to check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, good call. This one is tricky. We can capture
UP
/DOWN
on keyboard, but not so much clickingup
/down
in theinput
control.Investigating...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jorgefilipecosta Haii! I just pushed a change to account for this logic.
Typing in
0
(zero) has now been accounted for.Some good news is, the user can't physically trigger the
0.1
case by typing. This flow can only be triggered by either pressingUP
or clicking theup
arrow in theinput
.