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

Prevent link paste in RichText components in Button and Navigation blocks #28130

Merged
merged 2 commits into from
Jan 13, 2021
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
34 changes: 1 addition & 33 deletions packages/rich-text/src/component/format-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,20 @@
import { getActiveFormat } from '../get-active-format';
import { getActiveObject } from '../get-active-object';

/**
* Set of all interactive content tags.
*
* @see https://html.spec.whatwg.org/multipage/dom.html#interactive-content
*/
const interactiveContentTags = new Set( [
'a',
'audio',
'button',
'details',
'embed',
'iframe',
'input',
'label',
'select',
'textarea',
'video',
] );

export default function FormatEdit( {
formatTypes,
onChange,
onFocus,
value,
allowedFormats,
withoutInteractiveFormatting,
forwardedRef,
} ) {
return formatTypes.map( ( settings ) => {
const { name, edit: Edit, tagName } = settings;
const { name, edit: Edit } = settings;

if ( ! Edit ) {
return null;
}

if ( allowedFormats && allowedFormats.indexOf( name ) === -1 ) {
return null;
}

if (
withoutInteractiveFormatting &&
interactiveContentTags.has( tagName )
) {
return null;
}

const activeFormat = getActiveFormat( value, name );
const isActive = activeFormat !== undefined;
const activeObject = getActiveObject( value );
Expand Down
6 changes: 2 additions & 4 deletions packages/rich-text/src/component/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ function RichText(
} = useFormatTypes( {
clientId,
identifier,
withoutInteractiveFormatting,
allowedFormats,
} );

// For backward compatibility, fall back to tagName if it's a string.
Expand Down Expand Up @@ -1102,10 +1104,6 @@ function RichText(
<>
{ isSelected && (
<FormatEdit
allowedFormats={ allowedFormats }
withoutInteractiveFormatting={
withoutInteractiveFormatting
}
value={ record.current }
onChange={ handleChange }
onFocus={ focus }
Expand Down
18 changes: 12 additions & 6 deletions packages/rich-text/src/component/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { useFormatTypes } from './use-format-types';
import FormatEdit from './format-edit';
import { applyFormat } from '../apply-format';
import { getActiveFormat } from '../get-active-format';
Expand All @@ -46,7 +47,6 @@ import { isCollapsed } from '../is-collapsed';
import { remove } from '../remove';
import styles from './style.scss';
import ToolbarButtonWithOptions from './toolbar-button-with-options';
import { store as richTextStore } from '../store';

const unescapeSpaces = ( text ) => {
return text.replace( /&nbsp;|&#160;/gi, ' ' );
Expand Down Expand Up @@ -799,7 +799,6 @@ export class RichText extends Component {
maxWidth,
formatTypes,
parentBlockStyles,
withoutInteractiveFormatting,
accessibilityLabel,
disableEditingMenu = false,
} = this.props;
Expand Down Expand Up @@ -953,9 +952,6 @@ export class RichText extends Component {
<FormatEdit
formatTypes={ formatTypes }
value={ record }
withoutInteractiveFormatting={
withoutInteractiveFormatting
}
onChange={ this.onFormatChange }
onFocus={ () => {} }
/>
Expand All @@ -977,6 +973,16 @@ RichText.defaultProps = {
tagName: 'div',
};

const withFormatTypes = ( WrappedComponent ) => ( props ) => {
const { formatTypes } = useFormatTypes( {
ellatrix marked this conversation as resolved.
Show resolved Hide resolved
clientId: props.clientId,
identifier: props.identifier,
withoutInteractiveFormatting: props.withoutInteractiveFormatting,
} );

return <WrappedComponent { ...props } formatTypes={ formatTypes } />;
};

export default compose( [
withSelect( ( select, { clientId } ) => {
const { getBlockParents, getBlock, getSettings } = select(
Expand All @@ -988,12 +994,12 @@ export default compose( [
get( parentBlock, [ 'attributes', 'childrenStyles' ] ) || {};

return {
formatTypes: select( richTextStore ).getFormatTypes(),
areMentionsSupported:
getSettings( 'capabilities' ).mentions === true,
areXPostsSupported: getSettings( 'capabilities' ).xposts === true,
...{ parentBlockStyles },
};
} ),
withPreferredColorScheme,
withFormatTypes,
] )( RichText );
53 changes: 48 additions & 5 deletions packages/rich-text/src/component/use-format-types.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* WordPress dependencies
*/
import { useMemo } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
/**
* Internal dependencies
Expand All @@ -11,16 +12,58 @@ function formatTypesSelector( select ) {
return select( richTextStore ).getFormatTypes();
}

/**
* Set of all interactive content tags.
*
* @see https://html.spec.whatwg.org/multipage/dom.html#interactive-content
*/
const interactiveContentTags = new Set( [
'a',
'audio',
'button',
'details',
'embed',
'iframe',
'input',
'label',
'select',
'textarea',
'video',
] );

/**
* This hook provides RichText with the `formatTypes` and its derived props from
* experimental format type settings.
*
* @param {Object} $0 Options
* @param {string} $0.clientId Block client ID.
* @param {string} $0.identifier Block attribute.
* @param {Object} $0 Options
* @param {string} $0.clientId Block client ID.
* @param {string} $0.identifier Block attribute.
* @param {boolean} $0.withoutInteractiveFormatting Whether to clean the interactive formattings or not.
* @param {Array} $0.allowedFormats Allowed formats
*/
export function useFormatTypes( { clientId, identifier } ) {
const formatTypes = useSelect( formatTypesSelector, [] );
export function useFormatTypes( {
clientId,
identifier,
withoutInteractiveFormatting,
allowedFormats,
} ) {
const allFormatTypes = useSelect( formatTypesSelector, [] );
const formatTypes = useMemo( () => {
return allFormatTypes.filter( ( { name, tagName } ) => {
if ( allowedFormats && allowedFormats.includes( name ) ) {
return false;
}

if (
withoutInteractiveFormatting &&
interactiveContentTags.has( tagName )
) {
return false;
}

return true;
} );
}, [ allFormatTypes, allowedFormats, interactiveContentTags ] );
const keyedSelected = useSelect(
( select ) =>
formatTypes.reduce( ( accumulator, type ) => {
Expand Down