diff --git a/packages/block-editor/src/components/editor-skeleton/index.js b/packages/block-editor/src/components/editor-skeleton/index.js index f95ad556fe5256..5b566f87221ffd 100644 --- a/packages/block-editor/src/components/editor-skeleton/index.js +++ b/packages/block-editor/src/components/editor-skeleton/index.js @@ -6,9 +6,24 @@ import classnames from 'classnames'; /** * WordPress dependencies */ +import { useEffect } from '@wordpress/element'; import { navigateRegions } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +function useHTMLClass( className ) { + useEffect( () => { + const element = + document && document.querySelector( `html:not(.${ className }` ); + if ( ! element ) { + return; + } + element.classList.toggle( className ); + return () => { + element.classList.toggle( className ); + }; + }, [ className ] ); +} + function EditorSkeleton( { footer, header, @@ -17,6 +32,7 @@ function EditorSkeleton( { publish, className, } ) { + useHTMLClass( 'block-editor-editor-skeleton__html-container' ); return ( "`; +exports[`Basic rendering should render 1`] = `""`; diff --git a/packages/block-editor/src/components/link-control/test/index.js b/packages/block-editor/src/components/link-control/test/index.js index 6e003a77e6dc45..d783a07c2f98d2 100644 --- a/packages/block-editor/src/components/link-control/test/index.js +++ b/packages/block-editor/src/components/link-control/test/index.js @@ -836,7 +836,7 @@ describe( 'Selecting links', () => { describe( 'Addition Settings UI', () => { it( 'should display "New Tab" setting (in "off" mode) by default when a link is selected', async () => { const selectedLink = first( fauxEntitySuggestions ); - const expectedSettingText = 'Open in New Tab'; + const expectedSettingText = 'Open in new tab'; const LinkControlConsumer = () => { const [ link ] = useState( selectedLink ); @@ -873,7 +873,7 @@ describe( 'Addition Settings UI', () => { const customSettings = [ { id: 'newTab', - title: 'Open in New Tab', + title: 'Open in new tab', }, { id: 'noFollow', diff --git a/packages/block-editor/src/components/url-popover/README.md b/packages/block-editor/src/components/url-popover/README.md index 13810e9e3cc8e9..1af8eeb670ad2f 100644 --- a/packages/block-editor/src/components/url-popover/README.md +++ b/packages/block-editor/src/components/url-popover/README.md @@ -64,7 +64,7 @@ class MyURLPopover extends Component { onClose={ this.closeURLPopover } renderSettings={ () => ( diff --git a/packages/block-editor/src/components/url-popover/image-url-input-ui.js b/packages/block-editor/src/components/url-popover/image-url-input-ui.js index b6b30af373868c..dfd45c71fc5b0e 100644 --- a/packages/block-editor/src/components/url-popover/image-url-input-ui.js +++ b/packages/block-editor/src/components/url-popover/image-url-input-ui.js @@ -228,7 +228,7 @@ const ImageURLInputUI = ( { const advancedOptions = ( <> diff --git a/packages/block-library/src/columns/editor.scss b/packages/block-library/src/columns/editor.scss index b62b3f62215143..df272d55d360f1 100644 --- a/packages/block-library/src/columns/editor.scss +++ b/packages/block-library/src/columns/editor.scss @@ -66,20 +66,32 @@ // Responsiveness: Show at most one columns on mobile. flex-basis: 100%; + // Between mobile and large viewports, allow 2 columns. @include break-small() { + flex-basis: calc(50% - (#{$grid-size-large})); + flex-grow: 0; + margin-left: 0; + margin-right: 0; + } + + // At large viewports, show all columns horizontally. + @include break-medium() { + // Available space should be divided equally amongst columns + // without an assigned width. This is achieved by assigning a + // flex basis that is consistent (equal), would not cause the + // sum total of column widths to exceed 100%, and which would + // cede to a column with an assigned width. The `flex-grow` + // allows columns to maximally and equally occupy space + // remaining after subtracting the space occupied by columns + // with explicit widths (if any exist). + flex-basis: 0; flex-grow: 1; - flex-basis: auto; - // Beyond mobile, allow columns. Columns with an explicitly- - // assigned width should maintain their `flex-basis` width and - // not grow. All other blocks should automatically inherit the - // `flex-grow` to occupy the available space. + // Columns with an explicitly-assigned width should maintain + // their `flex-basis` width and not grow. &[data-has-explicit-width] { flex-grow: 0; } - - margin-left: 0; - margin-right: 0; } // Add space between columns. Themes can customize this if they wish to work differently. diff --git a/packages/block-library/src/columns/style.scss b/packages/block-library/src/columns/style.scss index b32377cc3522eb..4eae6afb078554 100644 --- a/packages/block-library/src/columns/style.scss +++ b/packages/block-library/src/columns/style.scss @@ -30,15 +30,10 @@ word-break: break-word; // For back-compat. overflow-wrap: break-word; // New standard. + // Between mobile and large viewports, allow 2 columns. @include break-small() { - - // Beyond mobile, allow columns. Columns with an explicitly-assigned - // width should maintain their `flex-basis` width and not grow. All - // other blocks should automatically inherit the `flex-grow` to occupy - // the available space. - &[style] { - flex-grow: 0; - } + flex-basis: calc(50% - #{$grid-size-large}); + flex-grow: 0; // Add space between the multiple columns. Themes can customize this if they wish to work differently. // Only apply this beyond the mobile breakpoint, as there's only a single column on mobile. @@ -47,7 +42,23 @@ } } + // At large viewports, show all columns horizontally. @include break-medium() { + // Available space should be divided equally amongst columns without an + // assigned width. This is achieved by assigning a flex basis that is + // consistent (equal), would not cause the sum total of column widths to + // exceed 100%, and which would cede to a column with an assigned width. + // The `flex-grow` allows columns to maximally and equally occupy space + // remaining after subtracting the space occupied by columns with + // explicit widths (if any exist). + flex-basis: 0; + flex-grow: 1; + + // Columns with an explicitly-assigned width should maintain their + // `flex-basis` width and not grow. + &[style] { + flex-grow: 0; + } // When columns are in a single row, add space before all except the first. &:not(:first-child) { diff --git a/packages/block-library/src/group/edit.js b/packages/block-library/src/group/edit.js index ecb99b96cb4deb..80f496967f599f 100644 --- a/packages/block-library/src/group/edit.js +++ b/packages/block-library/src/group/edit.js @@ -6,7 +6,7 @@ import { compose } from '@wordpress/compose'; import { InnerBlocks, __experimentalUseColors } from '@wordpress/block-editor'; import { useRef } from '@wordpress/element'; -function GroupEdit( { hasInnerBlocks } ) { +function GroupEdit( { hasInnerBlocks, className } ) { const ref = useRef(); const { TextColor, @@ -28,7 +28,7 @@ function GroupEdit( { hasInnerBlocks } ) { { InspectorControlsColorPanel } -
+
{ controls={ [ { icon: , - title: 'Linear Gradient', + title: __( 'Linear Gradient' ), isActive: hasGradient && type === 'linear-gradient', onClick: onSetLinearGradient, }, { icon: , - title: 'Radial Gradient', + title: __( 'Radial Gradient' ), isActive: hasGradient && type === 'radial-gradient', onClick: onSetRadialGradient, }, diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/links.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/links.test.js.snap index cf6bb746c56033..330dfbbe142b08 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/links.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/links.test.js.snap @@ -20,7 +20,7 @@ exports[`Links can be created by selecting text and using keyboard shortcuts 1`] exports[`Links can be created by selecting text and using keyboard shortcuts 2`] = ` " -

This is Gutenberg

+

This is Gutenberg

" `; @@ -62,7 +62,7 @@ exports[`Links can be removed 1`] = ` exports[`Links should contain a label when it should open in a new tab 1`] = ` " -

This is WordPress

+

This is WordPress

" `; diff --git a/packages/e2e-tests/specs/editor/various/links.test.js b/packages/e2e-tests/specs/editor/various/links.test.js index 797de506e7f5e7..8d6c27869cd72e 100644 --- a/packages/e2e-tests/specs/editor/various/links.test.js +++ b/packages/e2e-tests/specs/editor/various/links.test.js @@ -67,7 +67,7 @@ describe( 'Links', () => { // Type a URL await page.keyboard.type( 'https://wordpress.org/gutenberg' ); - // Navigate to and toggle the "Open in New Tab" checkbox. + // Navigate to and toggle the "Open in new tab" checkbox. await page.keyboard.press( 'Tab' ); await page.keyboard.press( 'Tab' ); await page.keyboard.press( 'Space' ); @@ -371,7 +371,7 @@ describe( 'Links', () => { ) ).not.toBeNull(); - // Tab to the "Open in New Tab" toggle. + // Tab to the "Open in new tab" toggle. await page.keyboard.press( 'Tab' ); await page.keyboard.press( 'Tab' ); @@ -467,7 +467,7 @@ describe( 'Links', () => { await pressKeyWithModifier( 'primary', 'k' ); await waitForAutoFocus(); - // Navigate to and toggle the "Open in New Tab" checkbox. + // Navigate to and toggle the "Open in new tab" checkbox. await page.keyboard.press( 'Tab' ); await page.keyboard.press( 'Tab' ); await page.keyboard.press( 'Space' ); @@ -476,7 +476,7 @@ describe( 'Links', () => { // a changing value of the setting. await page.waitForSelector( ':focus.components-form-toggle__input' ); - // Close dialog. Expect that "Open in New Tab" would have been applied + // Close dialog. Expect that "Open in new tab" would have been applied // immediately. await page.keyboard.press( 'Escape' ); @@ -505,7 +505,7 @@ describe( 'Links', () => { await pressKeyWithModifier( 'primary', 'k' ); await waitForAutoFocus(); - // Navigate to the "Open in New Tab" checkbox. + // Navigate to the "Open in new tab" checkbox. await page.keyboard.press( 'Tab' ); await page.keyboard.press( 'Tab' ); // Uncheck the checkbox. diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index b6b1a33544ff81..8d25b17abbaa3c 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -1,7 +1,6 @@ { "name": "@wordpress/edit-site", - "version": "1.2.0", - "private": true, + "version": "1.3.0-alpha.0", "description": "Edit Site Page module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/format-library/src/image/index.js b/packages/format-library/src/image/index.js index 273352d9a01bf5..273ee1093c650c 100644 --- a/packages/format-library/src/image/index.js +++ b/packages/format-library/src/image/index.js @@ -45,6 +45,7 @@ export const image = { this.onKeyDown = this.onKeyDown.bind( this ); this.openModal = this.openModal.bind( this ); this.closeModal = this.closeModal.bind( this ); + this.anchorRef = null; this.state = { modal: false, }; @@ -95,6 +96,22 @@ export const image = { this.setState( { modal: false } ); } + componentDidMount() { + this.anchorRef = getRange(); + } + + componentDidUpdate( prevProps ) { + // When the popover is open or when the selected image changes, + // update the anchorRef. + if ( + ( ! prevProps.isObjectActive && this.props.isObjectActive ) || + prevProps.activeObjectAttributes.url !== + this.props.activeObjectAttributes.url + ) { + this.anchorRef = getRange(); + } + } + render() { const { value, @@ -151,7 +168,7 @@ export const image = { { // Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar diff --git a/packages/format-library/src/link/inline.js b/packages/format-library/src/link/inline.js index 2d9eb18df15783..827e6652e4025a 100644 --- a/packages/format-library/src/link/inline.js +++ b/packages/format-library/src/link/inline.js @@ -10,14 +10,7 @@ import { useMemo, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { withSpokenMessages, Popover } from '@wordpress/components'; import { prependHTTP } from '@wordpress/url'; -import { - create, - insert, - isCollapsed, - applyFormat, - getTextContent, - slice, -} from '@wordpress/rich-text'; +import { create, insert, isCollapsed, applyFormat } from '@wordpress/rich-text'; import { __experimentalLinkControl as LinkControl } from '@wordpress/block-editor'; /** @@ -120,11 +113,9 @@ function InlineLinkUI( { } const newUrl = prependHTTP( nextValue.url ); - const selectedText = getTextContent( slice( value ) ); const format = createLinkFormat( { url: newUrl, opensInNewWindow: nextValue.opensInNewTab, - text: selectedText, } ); if ( isCollapsed( value ) && ! isActive ) { diff --git a/packages/format-library/src/link/utils.js b/packages/format-library/src/link/utils.js index ed22d57b24a022..d57cb03b2f6d1c 100644 --- a/packages/format-library/src/link/utils.js +++ b/packages/format-library/src/link/utils.js @@ -18,7 +18,6 @@ import { getFragment, isValidFragment, } from '@wordpress/url'; -import { __, sprintf } from '@wordpress/i18n'; /** * Check for issues with the provided href. @@ -93,7 +92,7 @@ export function isValidHref( href ) { * * @return {Object} The final format object. */ -export function createLinkFormat( { url, opensInNewWindow, text } ) { +export function createLinkFormat( { url, opensInNewWindow } ) { const format = { type: 'core/link', attributes: { @@ -102,12 +101,8 @@ export function createLinkFormat( { url, opensInNewWindow, text } ) { }; if ( opensInNewWindow ) { - // translators: accessibility label for external links, where the argument is the link text - const label = sprintf( __( '%s (opens in a new tab)' ), text ); - format.attributes.target = '_blank'; format.attributes.rel = 'noreferrer noopener'; - format.attributes[ 'aria-label' ] = label; } return format; diff --git a/packages/format-library/src/text-color/index.js b/packages/format-library/src/text-color/index.js index 467467553f05cd..62ba3e910e85f2 100644 --- a/packages/format-library/src/text-color/index.js +++ b/packages/format-library/src/text-color/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { get } from 'lodash'; +import { get, isEmpty } from 'lodash'; /** * WordPress dependencies @@ -11,6 +11,7 @@ import { useSelect } from '@wordpress/data'; import { useCallback, useMemo, useState } from '@wordpress/element'; import { RichTextToolbarButton } from '@wordpress/block-editor'; import { Dashicon } from '@wordpress/components'; +import { removeFormat } from '@wordpress/rich-text'; /** * Internal dependencies @@ -23,12 +24,18 @@ const title = __( 'Text Color' ); const EMPTY_ARRAY = []; function TextColorEdit( { value, onChange, isActive, activeAttributes } ) { - const colors = useSelect( ( select ) => { - const { getSettings } = select( 'core/block-editor' ); - if ( getSettings ) { - return get( getSettings(), [ 'colors' ], EMPTY_ARRAY ); + const { colors, disableCustomColors } = useSelect( ( select ) => { + const blockEditorSelect = select( 'core/block-editor' ); + let settings; + if ( blockEditorSelect && blockEditorSelect.getSettings ) { + settings = blockEditorSelect.getSettings(); + } else { + settings = {}; } - return EMPTY_ARRAY; + return { + colors: get( settings, [ 'colors' ], EMPTY_ARRAY ), + disableCustomColors: settings.disableCustomColors, + }; } ); const [ isAddingColor, setIsAddingColor ] = useState( false ); const enableIsAddingColor = useCallback( () => setIsAddingColor( true ), [ @@ -46,6 +53,13 @@ function TextColorEdit( { value, onChange, isActive, activeAttributes } ) { backgroundColor: activeColor, }; }, [ value, colors ] ); + + const hasColorsToChoose = + ! isEmpty( colors ) || disableCustomColors !== true; + if ( ! hasColorsToChoose && ! isActive ) { + return null; + } + return ( <> } title={ title } - onClick={ enableIsAddingColor } + // If has no colors to choose but a color is active remove the color onClick + onClick={ + hasColorsToChoose + ? enableIsAddingColor + : () => onChange( removeFormat( value, name ) ) + } /> { isAddingColor && ( { return wp.media.view.MediaFrame.Select.extend( { /** @@ -26,6 +31,25 @@ const getFeaturedImageMediaFrame = () => { } ); }, + /** + * Handle the edit state requirements of selected media item. + * + * @return {void} + */ + editState() { + const selection = this.state( 'featured-image' ).get( 'selection' ); + const view = new wp.media.view.EditImage( { + model: selection.single(), + controller: this, + } ).render(); + + // Set the view to the EditImage frame using the selected image. + this.content.set( view ); + + // After bringing in the frame, load the actual editor via an ajax call. + view.loadEditor(); + }, + /** * Create the default states. * @@ -37,12 +61,23 @@ const getFeaturedImageMediaFrame = () => { this.featuredImageToolbar, this ); - this.states.add( [ new wp.media.controller.FeaturedImage() ] ); + this.on( 'content:render:edit-image', this.editState, this ); + + this.states.add( [ + new wp.media.controller.FeaturedImage(), + new wp.media.controller.EditImage( { + model: this.options.editImage, + } ), + ] ); }, } ); }; -// Getter for the sake of unit tests. +/** + * Prepares the Gallery toolbars and frames. + * + * @return {wp.media.view.MediaFrame.Select} The default media workflow. + */ const getGalleryDetailsMediaFrame = () => { /** * Custom gallery details frame. @@ -51,13 +86,77 @@ const getGalleryDetailsMediaFrame = () => { * @class GalleryDetailsMediaFrame * @class */ - return wp.media.view.MediaFrame.Post.extend( { + return wp.media.view.MediaFrame.Select.extend( { + /** + * Set up gallery toolbar. + * + * @return {void} + */ + galleryToolbar() { + const editing = this.state().get( 'editing' ); + this.toolbar.set( + new wp.media.view.Toolbar( { + controller: this, + items: { + insert: { + style: 'primary', + text: editing + ? wp.media.view.l10n.updateGallery + : wp.media.view.l10n.insertGallery, + priority: 80, + requires: { library: true }, + + /** + * @fires wp.media.controller.State#update + */ + click() { + const controller = this.controller, + state = controller.state(); + + controller.close(); + state.trigger( + 'update', + state.get( 'library' ) + ); + + // Restore and reset the default state. + controller.setState( controller.options.state ); + controller.reset(); + }, + }, + }, + } ) + ); + }, + + /** + * Handle the edit state requirements of selected media item. + * + * @return {void} + */ + editState() { + const selection = this.state( 'gallery' ).get( 'selection' ); + const view = new wp.media.view.EditImage( { + model: selection.single(), + controller: this, + } ).render(); + + // Set the view to the EditImage frame using the selected image. + this.content.set( view ); + + // After bringing in the frame, load the actual editor via an ajax call. + view.loadEditor(); + }, + /** * Create the default states. * * @return {void} */ createStates: function createStates() { + this.on( 'toolbar:create:main-gallery', this.galleryToolbar, this ); + this.on( 'content:render:edit-image', this.editState, this ); + this.states.add( [ new wp.media.controller.Library( { id: 'gallery', @@ -77,6 +176,9 @@ const getGalleryDetailsMediaFrame = () => { ) ), } ), + new wp.media.controller.EditImage( { + model: this.options.editImage, + } ), new wp.media.controller.GalleryEdit( { library: this.options.selection, @@ -157,7 +259,6 @@ class MediaUpload extends Component { if ( unstableFeaturedImageFlow ) { this.buildAndSetFeatureImageFrame(); } - this.initializeListeners(); } @@ -169,6 +270,11 @@ class MediaUpload extends Component { this.frame.on( 'close', this.onClose ); } + /** + * Sets the Gallery frame and initializes listeners. + * + * @return {void} + */ buildAndSetGalleryFrame() { const { addToGallery = false, @@ -197,7 +303,6 @@ class MediaUpload extends Component { if ( ! this.GalleryDetailsMediaFrame ) { this.GalleryDetailsMediaFrame = getGalleryDetailsMediaFrame(); } - const attachments = getAttachmentsCollection( value ); const selection = new wp.media.model.Selection( attachments.models, { props: attachments.props.toJSON(), @@ -214,6 +319,11 @@ class MediaUpload extends Component { this.initializeListeners(); } + /** + * Initializes the Media Library requirements for the featured image flow. + * + * @return {void} + */ buildAndSetFeatureImageFrame() { const featuredImageFrame = getFeaturedImageMediaFrame(); const attachments = getAttachmentsCollection( this.props.value ); @@ -266,7 +376,6 @@ class MediaUpload extends Component { onOpen() { this.updateCollection(); - if ( ! this.props.value ) { return; }