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

Editor: Display Site Icon (if one is set) in Gutenberg Fullscreen Mode #22952

Merged
merged 22 commits into from
Jul 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
20 changes: 20 additions & 0 deletions gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,23 @@ function gutenberg_rest_nonce() {
exit( wp_create_nonce( 'wp_rest' ) );
}
add_action( 'wp_ajax_gutenberg_rest_nonce', 'gutenberg_rest_nonce' );


/**
* Exposes the site icon url to the Gutenberg editor through the WordPress REST
* API. The site icon url should instead be fetched from the wp/v2/settings
* endpoint when https://github.com/WordPress/gutenberg/pull/19967 is complete.
*
* @since 8.2.1
*
* @param WP_REST_Response $response Response data served by the WordPress REST index endpoint.
* @return WP_REST_Response
*/
function register_site_icon_url( $response ) {
$data = $response->data;
$data['site_icon_url'] = get_site_icon_url();
$response->set_data( $data );
return $response;
}

add_filter( 'rest_index', 'register_site_icon_url' );
17 changes: 17 additions & 0 deletions lib/compat.php
Original file line number Diff line number Diff line change
Expand Up @@ -568,3 +568,20 @@ function gutenberg_output_html_nav_menu_item( $item_output, $item, $depth, $args
return $item_output;
}
add_filter( 'walker_nav_menu_start_el', 'gutenberg_output_html_nav_menu_item', 10, 4 );

/**
* Amends the paths to preload when initializing edit post.
*
* @see https://core.trac.wordpress.org/ticket/50606
*
* @since 8.4.0
*
* @param array $preload_paths Default path list that will be preloaded.
* @return array Modified path list to preload.
*/
function gutenberg_preload_edit_post( $preload_paths ) {
$additional_paths = array( '/?context=edit' );
Copy link
Contributor

Choose a reason for hiding this comment

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

This ticket has been rejected on Core, should we remove that code from here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the heads up Riad 👍 This is on my radar -- will respond when I get the chance tomorrow.

Copy link
Contributor Author

@jeyip jeyip Mar 18, 2021

Choose a reason for hiding this comment

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

It's been quite a while since I've looked at this code, but yeah removing it makes sense to me, especially if, according to Timothy, the index endpoint doesn't even support context query params.

I'll spin up a draft to remove this code soon.

Copy link
Contributor

Choose a reason for hiding this comment

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

Here's an attempt at making the entity request urls more specific to each entity, and prevents the need for preloading /?context=edit: #30482

return array_merge( $preload_paths, $additional_paths );
}

add_filter( 'block_editor_preload_paths', 'gutenberg_preload_edit_post' );
2 changes: 1 addition & 1 deletion lib/edit-site-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ function gutenberg_edit_site_init( $hook ) {
// Preload block editor paths.
// most of these are copied from edit-forms-blocks.php.
$preload_paths = array(
'/',
'/?context=edit',
'/wp/v2/types?context=edit',
'/wp/v2/taxonomies?per_page=100&context=edit',
'/wp/v2/pages?per_page=100&context=edit',
Expand Down
6 changes: 6 additions & 0 deletions packages/core-data/src/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ import { apiFetch, select } from './controls';
export const DEFAULT_ENTITY_KEY = 'id';

export const defaultEntities = [
{
label: __( 'Base' ),
name: '__unstableBase',
kind: 'root',
baseURL: '',
},
jeyip marked this conversation as resolved.
Show resolved Hide resolved
{
label: __( 'Site' ),
name: 'site',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,64 @@ import { get } from 'lodash';
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { Button } from '@wordpress/components';
import { Button, Icon } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { addQueryArgs } from '@wordpress/url';
import { wordpress } from '@wordpress/icons';

function FullscreenModeClose() {
const { isActive, postType } = useSelect( ( select ) => {
const { getCurrentPostType } = select( 'core/editor' );
const { isFeatureActive } = select( 'core/edit-post' );
const { getPostType } = select( 'core' );
const { isActive, isRequestingSiteIcon, postType, siteIconUrl } = useSelect(
( select ) => {
const { getCurrentPostType } = select( 'core/editor' );
const { isFeatureActive } = select( 'core/edit-post' );
const { isResolving } = select( 'core/data' );
const { getEntityRecord, getPostType } = select( 'core' );
const siteData =
getEntityRecord( 'root', '__unstableBase', undefined ) || {};

return {
isActive: isFeatureActive( 'fullscreenMode' ),
postType: getPostType( getCurrentPostType() ),
};
}, [] );
return {
Copy link
Member

Choose a reason for hiding this comment

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

@mtias @youknowriad do you think we should limit this to just edit-site for now, or do you think it would also be good to start testing it in edit-post?

Copy link
Member

Choose a reason for hiding this comment

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

We should do it in both, but if we are not certain yet, better to keep it in edit-site. We would need to decide if it's ready for 5.5 as well (in edit-post).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We would need to decide if it's ready for 5.5 as well

Sounds good 👍 . I'm newer on the team so I'm not sure what this entails. Could you elaborate?

Also, I wanted to call out that I'm on the gardening rotation so development on this PR is going to slow down until the end of the month.

Copy link
Member

Choose a reason for hiding this comment

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

The second week of July is the beta period for WordPress 5.5. Anything that affects the post-editor needs to be in good shape by then.

Copy link
Contributor Author

@jeyip jeyip Jun 29, 2020

Choose a reason for hiding this comment

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

@mtias I'm back from the gardening rotation, which leaves us a few weeks to test this feature if we want it in 5.5. I see two options here:


1. Test post-editor for 5.5 release

Pros:

  • Site icon feature released sooner
  • No need for a follow-up PR to bring post-editor to feature parity

Cons:

  • The accelerated timeline could cause us to miss issues, especially since code review on this PR isn't finished yet.

2. Test post-editor for 5.6 release

Pros:

  • More time to test the feature

Cons:

  • Have to follow up with a second PR to ensure feature parity between site editor and post editor

Also @sirreal Would you be able to send me documentation about how I'd make sure the post-editor is tested thoroughly with these changes for the 5.5 beta?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mtias @vindl Since it sounds like there is more extensive testing needed before releasing this feature in edit-post, I'm leaning towards separating this feature into two PRs. The current PR would be for edit-site, and the follow-up would be for edit-post.

Since the work here isn't urgent, we wouldn't have to rush testing while we prep for 5.6. Let me know what you think.

isActive: isFeatureActive( 'fullscreenMode' ),
isRequestingSiteIcon: isResolving( 'core', 'getEntityRecord', [
'root',
'__unstableBase',
undefined,
] ),
postType: getPostType( getCurrentPostType() ),
siteIconUrl: siteData.site_icon_url,
};
},
[]
);

if ( ! isActive || ! postType ) {
return null;
}

let buttonIcon = <Icon size="36px" icon={ wordpress } />;

if ( siteIconUrl ) {
buttonIcon = (
<img
alt={ __( 'Site Icon' ) }
className="edit-post-fullscreen-mode-close_site-icon"
src={ siteIconUrl }
/>
);
} else if ( isRequestingSiteIcon ) {
buttonIcon = null;
Copons marked this conversation as resolved.
Show resolved Hide resolved
}

return (
<Button
className="edit-post-fullscreen-mode-close"
icon={ wordpress }
iconSize={ 36 }
className="edit-post-fullscreen-mode-close has-icon"
Copy link
Contributor

Choose a reason for hiding this comment

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

I might be missing some context, but is there a reason why we're adding the .has-icon class?

Copy link
Contributor

Choose a reason for hiding this comment

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

Good question.

Previously, we used to pass the wordpress icon (an SVG component from @wordpress/icons) via the icon prop to Button. The Button component forwards that prop to an Icon element it embeds, and sets the has-icon prop.

Since we're no longer passing the logo to Button via the icon prop (but wrap the Button around the logo), we're losing the .has-icon styling and have to add it back manually.

However, we might explore an alternative...

Copy link
Contributor

Choose a reason for hiding this comment

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

The Icon component seems to accept pretty much any valid component as a fallback. So we might be able to continue to pass in our custom log (or fallback) via Button's icon prop, and remove the manually added has-icon prop (which is arguably an implementation thing rather than part of the API).

Copy link
Contributor

Choose a reason for hiding this comment

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

I was experimenting a bit with this, and it would be possible. However, the Icon component is also passing a size prop to the element it clones, and <img /> elements don't support that. So let's keep things as they are.

href={ addQueryArgs( 'edit.php', {
post_type: postType.slug,
} ) }
label={ get( postType, [ 'labels', 'view_items' ], __( 'Back' ) ) }
/>
jeyip marked this conversation as resolved.
Show resolved Hide resolved
showTooltip
>
{ buttonIcon }
</Button>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,8 @@
}
}
}

.edit-post-fullscreen-mode-close_site-icon {
width: 36px;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* External dependencies
*/
import { render } from '@testing-library/react';

/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import FullscreenModeClose from '../';

jest.mock( '@wordpress/data/src/components/use-select', () => {
// This allows us to tweak the returned value on each test
const mock = jest.fn();
return mock;
} );

jest.mock( '@wordpress/core-data' );

describe( 'FullscreenModeClose', () => {
describe( 'when in full screen mode', () => {
it( 'should display a user uploaded site icon if it exists', () => {
useSelect.mockImplementation( ( cb ) => {
return cb( () => ( {
isResolving: () => false,
isFeatureActive: () => true,
getCurrentPostType: () => {},
getPostType: () => true,
getEntityRecord: () => ( {
site_icon_url: 'https://fakeUrl.com',
} ),
} ) );
} );

const { container } = render( <FullscreenModeClose /> );
const siteIcon = container.querySelector(
'.edit-post-fullscreen-mode-close_site-icon'
);

expect( siteIcon ).toBeTruthy();
} );

it( 'should display a default site icon if no user uploaded site icon exists', () => {
useSelect.mockImplementation( ( cb ) => {
return cb( () => ( {
isResolving: () => false,
isFeatureActive: () => true,
getCurrentPostType: () => {},
getPostType: () => true,
getEntityRecord: () => ( {
site_icon_url: '',
} ),
} ) );
} );

const { container } = render( <FullscreenModeClose /> );
const siteIcon = container.querySelector(
'.edit-post-fullscreen-mode-close_site-icon'
);
const defaultIcon = container.querySelector( 'svg' );

expect( siteIcon ).toBeFalsy();
expect( defaultIcon ).toBeTruthy();
} );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,61 @@
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { Button } from '@wordpress/components';
import { Button, Icon } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { wordpress } from '@wordpress/icons';

function FullscreenModeClose( { icon } ) {
const isActive = useSelect( ( select ) => {
return select( 'core/edit-site' ).isFeatureActive( 'fullscreenMode' );
}, [] );
const { isActive, isRequestingSiteIcon, siteIconUrl } = useSelect(
( select ) => {
const { isFeatureActive } = select( 'core/edit-site' );
const { getEntityRecord } = select( 'core' );
const { isResolving } = select( 'core/data' );
const siteData =
getEntityRecord( 'root', '__unstableBase', undefined ) || {};

return {
isActive: isFeatureActive( 'fullscreenMode' ),
isRequestingSiteIcon: isResolving( 'core', 'getEntityRecord', [
'root',
'__unstableBase',
undefined,
] ),
siteIconUrl: siteData.site_icon_url,
};
},
[]
);

if ( ! isActive ) {
return null;
}

const buttonIcon = icon || wordpress;
let buttonIcon = <Icon size="36px" icon={ wordpress } />;

if ( siteIconUrl ) {
buttonIcon = (
<img
alt={ __( 'Site Icon' ) }
className="edit-site-fullscreen-mode-close_site-icon"
src={ siteIconUrl }
/>
);
} else if ( isRequestingSiteIcon ) {
buttonIcon = null;
} else if ( icon ) {
buttonIcon = <Icon size="36px" icon={ icon } />;
}

return (
<Button
className="edit-site-fullscreen-mode-close"
icon={ buttonIcon }
iconSize={ 36 }
className="edit-site-fullscreen-mode-close has-icon"
href="index.php"
label={ __( 'Back' ) }
/>
showTooltip
>
{ buttonIcon }
</Button>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,22 @@
color: $white;
border-radius: 0;
height: $header-height;
width: $header-height;
min-width: $header-height;

&:hover {
background: #32373d; // WP-admin light-gray.
}

&:active {
color: $white;
}

&:focus {
box-shadow: inset 0 0 0 $border-width-focus var(--wp-admin-theme-color), inset 0 0 0 3px $white;
&.has-icon {
&:hover {
background: #32373d; // WP-admin light-gray.
}
&:active {
color: $white;
}
&:focus {
box-shadow: inset 0 0 0 $border-width-focus var(--wp-admin-theme-color), inset 0 0 0 3px $white;
}
}
}
}

.edit-site-fullscreen-mode-close_site-icon {
width: 36px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* External dependencies
*/
import { render } from '@testing-library/react';

/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import FullscreenModeClose from '../';

jest.mock( '@wordpress/data/src/components/use-select', () => {
// This allows us to tweak the returned value on each test
const mock = jest.fn();
return mock;
} );

jest.mock( '@wordpress/core-data' );

describe( 'FullscreenModeClose', () => {
describe( 'when in full screen mode', () => {
it( 'should display a user uploaded site icon if it exists', () => {
useSelect.mockImplementation( ( cb ) => {
return cb( () => ( {
isResolving: () => false,
isFeatureActive: () => true,
getEntityRecord: () => ( {
site_icon_url: 'https://fakeUrl.com',
} ),
} ) );
} );

const { container } = render( <FullscreenModeClose /> );
const siteIcon = container.querySelector(
'.edit-site-fullscreen-mode-close_site-icon'
);

expect( siteIcon ).toBeTruthy();
} );

it( 'should display a default site icon if no user uploaded site icon exists', () => {
useSelect.mockImplementation( ( cb ) => {
return cb( () => ( {
isResolving: () => false,
isFeatureActive: () => true,
getEntityRecord: () => ( {
site_icon_url: '',
} ),
} ) );
} );

const { container } = render( <FullscreenModeClose /> );
const siteIcon = container.querySelector(
'.edit-site-fullscreen-mode-close_site-icon'
);
const defaultIcon = container.querySelector( 'svg' );

expect( siteIcon ).toBeFalsy();
expect( defaultIcon ).toBeTruthy();
} );
} );
} );