[Product Editor] Fix blank editor flash when loading product (#43840)
* EditorLoadingContext * Use EditorLoadingContext * Remove fallbacks * Make sure metadata exists before using it * Add header loading state * Do not return skeleton * Use EditorLoadingContext * Update editor loading state * Remove ProductPageSkeleton * Remove unused import * Remove unused import * Handle undefined variationId and parentId in VariationSwitcherFooter * Remove ProductPageSkeleton * Include productId in determination of whether editor is loading * Handle variation loading * Fix rebase merge conflict mistakes * Fix layout margins * Show welcome tour and feedback bar after editor has loaded * Changelogs * Make loading context experimental
This commit is contained in:
parent
2432b3b22e
commit
d279466eb3
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
|
||||
Product Editor loading state now shows until form is displayed. No more blank flash of white.
|
|
@ -46,6 +46,7 @@ import { ModalEditor } from '../modal-editor';
|
|||
import { ProductEditorSettings } from '../editor';
|
||||
import { BlockEditorProps } from './types';
|
||||
import { ProductTemplate } from '../../types';
|
||||
import { LoadingState } from './loading-state';
|
||||
|
||||
function getLayoutTemplateId(
|
||||
productTemplate: ProductTemplate | undefined,
|
||||
|
@ -62,11 +63,13 @@ function getLayoutTemplateId(
|
|||
// Fallback to simple product if no layout template is set.
|
||||
return 'simple-product';
|
||||
}
|
||||
|
||||
export function BlockEditor( {
|
||||
context,
|
||||
settings: _settings,
|
||||
postType,
|
||||
productId,
|
||||
setIsEditorLoading,
|
||||
}: BlockEditorProps ) {
|
||||
useConfirmUnsavedProductChanges( postType );
|
||||
|
||||
|
@ -142,8 +145,14 @@ export function BlockEditor( {
|
|||
|
||||
const { updateEditorSettings } = useDispatch( 'core/editor' );
|
||||
|
||||
const isEditorLoading =
|
||||
! layoutTemplate ||
|
||||
// variations don't have a product template
|
||||
( postType !== 'product_variation' && ! productTemplate ) ||
|
||||
productId === -1;
|
||||
|
||||
useLayoutEffect( () => {
|
||||
if ( ! layoutTemplate ) {
|
||||
if ( isEditorLoading ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -159,6 +168,8 @@ export function BlockEditor( {
|
|||
productTemplate,
|
||||
} as Partial< ProductEditorSettings > );
|
||||
|
||||
setIsEditorLoading( isEditorLoading );
|
||||
|
||||
// We don't need to include onChange or updateEditorSettings in the dependencies,
|
||||
// since we get new instances of them on every render, which would cause an infinite loop.
|
||||
//
|
||||
|
@ -176,10 +187,6 @@ export function BlockEditor( {
|
|||
|
||||
const { closeModalEditor } = useDispatch( productEditorUiStore );
|
||||
|
||||
if ( ! blocks ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( isModalEditorOpen ) {
|
||||
return (
|
||||
<ModalEditor
|
||||
|
@ -204,7 +211,11 @@ export function BlockEditor( {
|
|||
<BlockEditorKeyboardShortcuts.Register />
|
||||
<BlockTools>
|
||||
<ObserveTyping>
|
||||
<BlockList className="woocommerce-product-block-editor__block-list" />
|
||||
{ isEditorLoading ? (
|
||||
<LoadingState />
|
||||
) : (
|
||||
<BlockList className="woocommerce-product-block-editor__block-list" />
|
||||
) }
|
||||
</ObserveTyping>
|
||||
</BlockTools>
|
||||
{ /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ }
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from './loading-state';
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement } from '@wordpress/element';
|
||||
|
||||
export function LoadingState() {
|
||||
return (
|
||||
<div
|
||||
className="woocommerce-product-block-editor__block-list block-editor-block-list__layout is-root-container is-loading"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="wp-block-woocommerce-product-tab">
|
||||
<div className="wp-block-woocommerce-product-section">
|
||||
<div className="wp-block-woocommerce-product-section__heading-title-wrapper">
|
||||
<div className="wp-block-woocommerce-product-section__heading-title" />
|
||||
</div>
|
||||
|
||||
<div className="wp-block-woocommerce-product-section__content wp-block-woocommerce-product-section-header__content--block-gap-unit-30">
|
||||
<div className="block-editor-block-list__block">
|
||||
<div className="woocommerce-product-form-label__label" />
|
||||
<div className="woocommerce-product-form-input" />
|
||||
</div>
|
||||
|
||||
<div className="block-editor-block-list__block">
|
||||
<div className="woocommerce-product-form-label__label" />
|
||||
<div className="woocommerce-product-form-textarea" />
|
||||
</div>
|
||||
|
||||
<div className="block-editor-block-list__block">
|
||||
<div className="woocommerce-product-form-label__label" />
|
||||
<div className="woocommerce-product-form-textarea" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="wp-block-woocommerce-product-section">
|
||||
<div className="wp-block-woocommerce-product-section__heading-title-wrapper">
|
||||
<div className="wp-block-woocommerce-product-section__heading-title" />
|
||||
</div>
|
||||
|
||||
<div className="wp-block-woocommerce-product-section__content wp-block-woocommerce-product-section__content--block-gap-unit-30">
|
||||
<div className="block-editor-block-list__block">
|
||||
<div className="woocommerce-product-form-label__label" />
|
||||
<div className="woocommerce-product-form-input" />
|
||||
</div>
|
||||
|
||||
<div className="block-editor-block-list__block">
|
||||
<div className="woocommerce-product-form-label__label" />
|
||||
<div className="woocommerce-product-form-textarea" />
|
||||
</div>
|
||||
|
||||
<div className="block-editor-block-list__block">
|
||||
<div className="woocommerce-product-form-label__label" />
|
||||
<div className="woocommerce-product-form-textarea" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -160,6 +160,44 @@
|
|||
}
|
||||
}
|
||||
|
||||
.woocommerce-product-block-editor {
|
||||
.block-editor-block-list__layout.is-root-container.is-loading {
|
||||
.wp-block-woocommerce-product-section__heading-title {
|
||||
@include placeholder();
|
||||
background-color: $gray-200;
|
||||
border-radius: $grid-unit-05;
|
||||
width: 300px;
|
||||
height: 28px;
|
||||
margin-bottom: $gap-smaller;
|
||||
}
|
||||
|
||||
.woocommerce-product-form-label__label {
|
||||
@include placeholder();
|
||||
background-color: $gray-200;
|
||||
border-radius: $grid-unit-05;
|
||||
width: $grid-unit-80;
|
||||
height: 28px;
|
||||
margin-bottom: $gap-smaller;
|
||||
}
|
||||
|
||||
.woocommerce-product-form-input {
|
||||
@include placeholder();
|
||||
background-color: $gray-200;
|
||||
border-radius: $grid-unit-05;
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.woocommerce-product-form-textarea {
|
||||
@include placeholder();
|
||||
background-color: $gray-200;
|
||||
border-radius: $grid-unit-05;
|
||||
width: 100%;
|
||||
height: 108px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wp-admin.woocommerce-feature-enabled-product-block-editor {
|
||||
.components-modal {
|
||||
&__frame {
|
||||
|
|
|
@ -13,4 +13,5 @@ export type BlockEditorProps = {
|
|||
postType: string;
|
||||
productId: number;
|
||||
settings?: ProductEditorSettings;
|
||||
setIsEditorLoading: ( isEditorLoading: boolean ) => void;
|
||||
};
|
||||
|
|
|
@ -31,6 +31,7 @@ import { InterfaceSkeleton } from '@wordpress/interface';
|
|||
*/
|
||||
import { Header } from '../header';
|
||||
import { BlockEditor } from '../block-editor';
|
||||
import { EditorLoadingContext } from '../../contexts/editor-loading-context';
|
||||
import { ValidationProvider } from '../../contexts/validation-context';
|
||||
import { EditorProps } from './types';
|
||||
import { store as productEditorUiStore } from '../../store/product-editor-ui';
|
||||
|
@ -41,10 +42,13 @@ export function Editor( {
|
|||
productType = 'product',
|
||||
settings,
|
||||
}: EditorProps ) {
|
||||
const [ isEditorLoading, setIsEditorLoading ] = useState( true );
|
||||
const [ selectedTab, setSelectedTab ] = useState< string | null >( null );
|
||||
|
||||
const updatedLayoutContext = useExtendLayout( 'product-block-editor' );
|
||||
|
||||
const productId = product?.id || -1;
|
||||
|
||||
// Check if the prepublish sidebar is open from the store.
|
||||
const isPrepublishPanelOpen = useSelect( ( select ) => {
|
||||
return select( productEditorUiStore ).isPrepublishPanelOpen();
|
||||
|
@ -56,40 +60,47 @@ export function Editor( {
|
|||
<EntityProvider
|
||||
kind="postType"
|
||||
type={ productType }
|
||||
id={ product.id }
|
||||
id={ productId }
|
||||
>
|
||||
<ShortcutProvider>
|
||||
<ValidationProvider initialValue={ product }>
|
||||
<InterfaceSkeleton
|
||||
header={
|
||||
<Header
|
||||
onTabSelect={ setSelectedTab }
|
||||
productType={ productType }
|
||||
/>
|
||||
}
|
||||
content={
|
||||
<>
|
||||
<BlockEditor
|
||||
settings={ settings }
|
||||
postType={ productType }
|
||||
productId={ product.id }
|
||||
context={ {
|
||||
selectedTab,
|
||||
postType: productType,
|
||||
postId: product.id,
|
||||
} }
|
||||
/>
|
||||
</>
|
||||
}
|
||||
actions={
|
||||
isPrepublishPanelOpen && (
|
||||
<PrepublishPanel
|
||||
<EditorLoadingContext.Provider
|
||||
value={ isEditorLoading }
|
||||
>
|
||||
<InterfaceSkeleton
|
||||
header={
|
||||
<Header
|
||||
onTabSelect={ setSelectedTab }
|
||||
productType={ productType }
|
||||
productId={ product.id }
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
}
|
||||
content={
|
||||
<>
|
||||
<BlockEditor
|
||||
settings={ settings }
|
||||
postType={ productType }
|
||||
productId={ productId }
|
||||
context={ {
|
||||
selectedTab,
|
||||
postType: productType,
|
||||
postId: productId,
|
||||
} }
|
||||
setIsEditorLoading={
|
||||
setIsEditorLoading
|
||||
}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
actions={
|
||||
isPrepublishPanelOpen && (
|
||||
<PrepublishPanel
|
||||
productType={ productType }
|
||||
productId={ productId }
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</EditorLoadingContext.Provider>
|
||||
<Popover.Slot />
|
||||
</ValidationProvider>
|
||||
</ShortcutProvider>
|
||||
|
|
|
@ -29,7 +29,7 @@ export type ProductEditorSettings = Partial<
|
|||
};
|
||||
|
||||
export type EditorProps = {
|
||||
product: Pick< Product, 'id' | 'type' >;
|
||||
product?: Pick< Product, 'id' | 'type' > | null;
|
||||
productType?: string;
|
||||
settings?: ProductEditorSettings;
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import { WooHeaderItem, useAdminSidebarWidth } from '@woocommerce/admin-layout';
|
||||
import { useEntityProp } from '@wordpress/core-data';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { createElement, useEffect } from '@wordpress/element';
|
||||
import { createElement, useContext, useEffect } from '@wordpress/element';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Button, Tooltip } from '@wordpress/components';
|
||||
import { chevronLeft, group, Icon } from '@wordpress/icons';
|
||||
|
@ -18,11 +18,13 @@ import { PinnedItems } from '@wordpress/interface';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { EditorLoadingContext } from '../../contexts/editor-loading-context';
|
||||
import { getHeaderTitle } from '../../utils';
|
||||
import { MoreMenu } from './more-menu';
|
||||
import { PreviewButton } from './preview-button';
|
||||
import { SaveDraftButton } from './save-draft-button';
|
||||
import { PublishButton } from './publish-button';
|
||||
import { LoadingState } from './loading-state';
|
||||
import { PrepublishButton } from '../prepublish-panel';
|
||||
import { Tabs } from '../tabs';
|
||||
import { HEADER_PINNED_ITEMS_SCOPE, TRACKS_SOURCE } from '../../constants';
|
||||
|
@ -41,6 +43,8 @@ export function Header( {
|
|||
onTabSelect,
|
||||
productType = 'product',
|
||||
}: HeaderProps ) {
|
||||
const isEditorLoading = useContext( EditorLoadingContext );
|
||||
|
||||
const [ productId ] = useEntityProp< number >(
|
||||
'postType',
|
||||
productType,
|
||||
|
@ -75,8 +79,8 @@ export function Header( {
|
|||
} );
|
||||
}, [ sidebarWidth ] );
|
||||
|
||||
if ( ! productId ) {
|
||||
return null;
|
||||
if ( isEditorLoading ) {
|
||||
return <LoadingState />;
|
||||
}
|
||||
|
||||
const isVariation = lastPersistedProduct?.parent_id > 0;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from './loading-state';
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement } from '@wordpress/element';
|
||||
|
||||
export function LoadingState() {
|
||||
return (
|
||||
<div
|
||||
className="woocommerce-product-header is-loading"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="woocommerce-product-header__inner">
|
||||
<div />
|
||||
|
||||
<div className="woocommerce-product-header__title" />
|
||||
|
||||
<div className="woocommerce-product-header__actions">
|
||||
<div className="woocommerce-product-header__action" />
|
||||
<div className="woocommerce-product-header__action" />
|
||||
<div className="woocommerce-product-header__action" />
|
||||
<div className="woocommerce-product-header__action" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="woocommerce-product-tabs">
|
||||
{ Array( 7 )
|
||||
.fill( 0 )
|
||||
.map( ( _, index ) => (
|
||||
<div key={ index } className="components-button" />
|
||||
) ) }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -70,6 +70,51 @@
|
|||
}
|
||||
}
|
||||
|
||||
.woocommerce-product-header.is-loading {
|
||||
.woocommerce-product-header__title {
|
||||
@include placeholder();
|
||||
background-color: $gray-200;
|
||||
border-radius: $grid-unit-05;
|
||||
min-width: 300px;
|
||||
min-height: $grid-unit-30;
|
||||
}
|
||||
|
||||
.woocommerce-product-header__actions {
|
||||
.woocommerce-product-header__action {
|
||||
@include placeholder();
|
||||
background-color: $gray-200;
|
||||
border-radius: $grid-unit-05;
|
||||
height: $grid-unit-30;
|
||||
|
||||
&:nth-child(n) {
|
||||
min-width: $grid-unit-80;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
min-width: calc(10 * $grid-unit);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
min-width: $icon-size;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.woocommerce-product-tabs {
|
||||
height: 46px;
|
||||
align-items: flex-start;
|
||||
|
||||
.components-button {
|
||||
@include placeholder();
|
||||
background-color: $gray-200;
|
||||
border-radius: $grid-unit-05;
|
||||
height: $grid-unit-20;
|
||||
min-width: $grid-unit-80;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-product-header__more-menu {
|
||||
.components-popover__content {
|
||||
min-width: auto;
|
||||
|
|
|
@ -48,8 +48,6 @@ export {
|
|||
export { Checkbox as __experimentalCheckboxControl } from './checkbox-control';
|
||||
export { NumberControl as __experimentalNumberControl } from './number-control';
|
||||
|
||||
export * from './product-page-skeleton';
|
||||
|
||||
export * from './modal-editor-welcome-guide';
|
||||
|
||||
export {
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from './product-page-skeleton';
|
|
@ -1,63 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement } from '@wordpress/element';
|
||||
|
||||
export function ProductPageSkeleton() {
|
||||
return (
|
||||
<div className="woocommerce-product-page-skeleton" aria-hidden="true">
|
||||
<div className="woocommerce-product-page-skeleton__header">
|
||||
<div className="woocommerce-product-page-skeleton__header-row">
|
||||
<div />
|
||||
<div className="woocommerce-product-page-skeleton__header-title" />
|
||||
<div className="woocommerce-product-page-skeleton__header-actions">
|
||||
<div className="woocommerce-product-page-skeleton__header-actions-other" />
|
||||
<div className="woocommerce-product-page-skeleton__header-actions-main" />
|
||||
<div className="woocommerce-product-page-skeleton__header-actions-config" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="woocommerce-product-page-skeleton__header-row">
|
||||
<div className="woocommerce-product-page-skeleton__tabs">
|
||||
{ Array( 7 )
|
||||
.fill( 0 )
|
||||
.map( ( _, index ) => (
|
||||
<div
|
||||
key={ index }
|
||||
className="woocommerce-product-page-skeleton__tab-item"
|
||||
/>
|
||||
) ) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="woocommerce-product-page-skeleton__body">
|
||||
<div className="woocommerce-product-page-skeleton__body-tabs-content">
|
||||
<div className="woocommerce-product-page-skeleton__block-title" />
|
||||
|
||||
<div className="woocommerce-product-page-skeleton__block-label" />
|
||||
<div className="woocommerce-product-page-skeleton__block-input" />
|
||||
|
||||
<div className="woocommerce-product-page-skeleton__block-label" />
|
||||
<div className="woocommerce-product-page-skeleton__block-textarea" />
|
||||
|
||||
<div className="woocommerce-product-page-skeleton__block-label" />
|
||||
<div className="woocommerce-product-page-skeleton__block-textarea" />
|
||||
|
||||
<div className="woocommerce-product-page-skeleton__block-separator" />
|
||||
|
||||
<div className="woocommerce-product-page-skeleton__block-title" />
|
||||
|
||||
<div className="woocommerce-product-page-skeleton__block-label" />
|
||||
<div className="woocommerce-product-page-skeleton__block-input" />
|
||||
|
||||
<div className="woocommerce-product-page-skeleton__block-label" />
|
||||
<div className="woocommerce-product-page-skeleton__block-textarea" />
|
||||
|
||||
<div className="woocommerce-product-page-skeleton__block-label" />
|
||||
<div className="woocommerce-product-page-skeleton__block-textarea" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
@mixin skeleton {
|
||||
@include placeholder();
|
||||
background-color: $gray-200;
|
||||
border-radius: $grid-unit-05;
|
||||
min-width: $grid-unit-20;
|
||||
min-height: $grid-unit-20;
|
||||
}
|
||||
|
||||
.woocommerce-product-page-skeleton {
|
||||
height: calc(100vh - 46px);
|
||||
overflow: hidden;
|
||||
|
||||
@include breakpoint(">782px") {
|
||||
height: calc(100vh - $grid-unit-40);
|
||||
}
|
||||
|
||||
&__header {
|
||||
border-bottom: 1px solid $gray-300;
|
||||
}
|
||||
|
||||
&__header-row {
|
||||
&:first-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
padding: 0 $grid-unit-40;
|
||||
|
||||
@include breakpoint(">782px") {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
grid-gap: $grid-unit-20;
|
||||
padding: 0 $grid-unit-20;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
overflow: hidden;
|
||||
|
||||
@include breakpoint(">782px") {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__header-title {
|
||||
@include skeleton();
|
||||
width: 100%;
|
||||
height: $grid-unit-30;
|
||||
|
||||
@include breakpoint(">782px") {
|
||||
width: 450px;
|
||||
}
|
||||
}
|
||||
|
||||
&__header-actions {
|
||||
display: none;
|
||||
|
||||
@include breakpoint(">782px") {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: $grid-unit;
|
||||
}
|
||||
}
|
||||
|
||||
&__header-actions-other,
|
||||
&__header-actions-main,
|
||||
&__header-actions-config {
|
||||
@include skeleton();
|
||||
height: $grid-unit-30;
|
||||
}
|
||||
|
||||
&__header-actions-other {
|
||||
width: $grid-unit-60;
|
||||
}
|
||||
|
||||
&__header-actions-main {
|
||||
width: $grid-unit-80;
|
||||
}
|
||||
|
||||
&__header-actions-config {
|
||||
width: $grid-unit-30;
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
height: 46px;
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
gap: $grid-unit-30;
|
||||
overflow-x: auto;
|
||||
margin: 0 $grid-unit-40;
|
||||
}
|
||||
|
||||
&__tab-item {
|
||||
@include skeleton();
|
||||
width: $grid-unit-80;
|
||||
height: $grid-unit-20;
|
||||
margin-top: $grid-unit;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__body-tabs-content {
|
||||
padding-top: $grid-unit-80;
|
||||
padding-left: $grid-unit-40;
|
||||
padding-right: $grid-unit-40;
|
||||
|
||||
@include breakpoint(">782px") {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
max-width: 650px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__block-title {
|
||||
@include skeleton();
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
margin-bottom: $grid-unit-40;
|
||||
|
||||
@include breakpoint(">782px") {
|
||||
width: 450px;
|
||||
}
|
||||
}
|
||||
|
||||
&__block-label {
|
||||
@include skeleton();
|
||||
width: $grid-unit-80;
|
||||
margin-bottom: $grid-unit;
|
||||
}
|
||||
|
||||
&__block-input {
|
||||
@include skeleton();
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
margin-bottom: $grid-unit-30;
|
||||
}
|
||||
|
||||
&__block-textarea {
|
||||
@include skeleton();
|
||||
width: 100%;
|
||||
height: 126px;
|
||||
margin-bottom: $grid-unit-30;
|
||||
}
|
||||
|
||||
&__block-separator {
|
||||
border-bottom: 1px solid $gray-300;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
margin: $grid-unit-80 0;
|
||||
}
|
||||
}
|
|
@ -17,8 +17,8 @@ import { useVariationSwitcher } from '../../hooks/use-variation-switcher';
|
|||
|
||||
export type VariationSwitcherProps = {
|
||||
parentProductType?: string;
|
||||
variationId: number;
|
||||
parentId: number;
|
||||
variationId?: number;
|
||||
parentId?: number;
|
||||
};
|
||||
|
||||
export function VariationSwitcherFooter( {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createContext } from '@wordpress/element';
|
||||
|
||||
export const EditorLoadingContext = createContext( false );
|
|
@ -34,7 +34,8 @@ function useProductEntityProp< T >(
|
|||
);
|
||||
|
||||
const metadataItem = useMemo(
|
||||
() => metadata.find( ( item ) => item.key === metaKey ),
|
||||
() =>
|
||||
metadata ? metadata.find( ( item ) => item.key === metaKey ) : null,
|
||||
[ metadata, metaKey ]
|
||||
);
|
||||
|
||||
|
|
|
@ -31,10 +31,15 @@ export * from './store/product-editor-ui';
|
|||
* Hooks
|
||||
*/
|
||||
export * from './hooks';
|
||||
export { PostTypeContext } from './contexts/post-type-context';
|
||||
export { useValidation, useValidations } from './contexts/validation-context';
|
||||
export * from './contexts/validation-context/types';
|
||||
|
||||
/**
|
||||
* Contexts
|
||||
*/
|
||||
export { EditorLoadingContext as __experimentalEditorLoadingContext } from './contexts/editor-loading-context';
|
||||
export { PostTypeContext } from './contexts/post-type-context';
|
||||
|
||||
// Init the store
|
||||
registerProductEditorUiStore();
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
@import "components/remove-confirmation-modal/style.scss";
|
||||
@import "components/manage-download-limits-modal/style.scss";
|
||||
@import "components/label/style.scss";
|
||||
@import "components/product-page-skeleton/styles.scss";
|
||||
@import "components/modal-editor-welcome-guide/style.scss";
|
||||
@import "components/attribute-control/attribute-skeleton.scss";
|
||||
@import "components/checkbox-control/style.scss";
|
||||
|
|
|
@ -16,7 +16,6 @@ import {
|
|||
isWCAdmin,
|
||||
} from '@woocommerce/navigation';
|
||||
import { Spinner } from '@woocommerce/components';
|
||||
import { ProductPageSkeleton } from '@woocommerce/product-editor';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -208,7 +207,6 @@ export const getPages = () => {
|
|||
if ( isFeatureEnabled( 'product_block_editor' ) ) {
|
||||
const productPage = {
|
||||
container: ProductPage,
|
||||
fallback: ProductPageSkeleton,
|
||||
layout: {
|
||||
header: false,
|
||||
},
|
||||
|
@ -274,7 +272,6 @@ export const getPages = () => {
|
|||
if ( window.wcAdminFeatures[ 'product-variation-management' ] ) {
|
||||
pages.push( {
|
||||
container: ProductVariationPage,
|
||||
fallback: ProductPageSkeleton,
|
||||
layout: {
|
||||
header: false,
|
||||
},
|
||||
|
|
|
@ -10,10 +10,10 @@ import {
|
|||
TRACKS_SOURCE,
|
||||
__experimentalProductMVPCESFooter as FeedbackBar,
|
||||
__experimentalProductMVPFeedbackModalContainer as ProductMVPFeedbackModalContainer,
|
||||
ProductPageSkeleton,
|
||||
__experimentalEditorLoadingContext as EditorLoadingContext,
|
||||
} from '@woocommerce/product-editor';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import { useEffect } from '@wordpress/element';
|
||||
import { useContext, useEffect } from '@wordpress/element';
|
||||
import { registerPlugin, unregisterPlugin } from '@wordpress/plugins';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { WooFooterItem } from '@woocommerce/admin-layout';
|
||||
|
@ -36,18 +36,42 @@ export default function ProductPage() {
|
|||
const product = useProductEntityRecord( productId );
|
||||
|
||||
useEffect( () => {
|
||||
registerPlugin( 'wc-admin-more-menu', {
|
||||
registerPlugin( 'wc-admin-product-editor', {
|
||||
// @ts-expect-error 'scope' does exist. @types/wordpress__plugins is outdated.
|
||||
scope: 'woocommerce-product-block-editor',
|
||||
render: () => (
|
||||
<>
|
||||
<WooProductMoreMenuItem>
|
||||
{ ( { onClose }: { onClose: () => void } ) => (
|
||||
<MoreMenuFill onClose={ onClose } />
|
||||
) }
|
||||
</WooProductMoreMenuItem>
|
||||
</>
|
||||
),
|
||||
render: () => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const isEditorLoading = useContext( EditorLoadingContext );
|
||||
|
||||
if ( isEditorLoading ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WooProductMoreMenuItem>
|
||||
{ ( { onClose }: { onClose: () => void } ) => (
|
||||
<MoreMenuFill onClose={ onClose } />
|
||||
) }
|
||||
</WooProductMoreMenuItem>
|
||||
|
||||
<WooFooterItem>
|
||||
<>
|
||||
<FeedbackBar productType="product" />
|
||||
<ProductMVPFeedbackModalContainer
|
||||
productId={
|
||||
productId
|
||||
? parseInt( productId, 10 )
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</>
|
||||
</WooFooterItem>
|
||||
|
||||
<BlockEditorTourWrapper />
|
||||
</>
|
||||
);
|
||||
},
|
||||
} );
|
||||
|
||||
const unregisterBlocks = initBlocks();
|
||||
|
@ -56,7 +80,7 @@ export default function ProductPage() {
|
|||
unregisterPlugin( 'wc-admin-more-menu' );
|
||||
unregisterBlocks();
|
||||
};
|
||||
}, [] );
|
||||
}, [ productId ] );
|
||||
|
||||
useEffect(
|
||||
function trackViewEvents() {
|
||||
|
@ -74,25 +98,12 @@ export default function ProductPage() {
|
|||
[ productId ]
|
||||
);
|
||||
|
||||
if ( ! product?.id ) {
|
||||
return <ProductPageSkeleton />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Editor
|
||||
product={ product }
|
||||
settings={ productBlockEditorSettings || {} }
|
||||
/>
|
||||
<WooFooterItem>
|
||||
<>
|
||||
<FeedbackBar productType="product" />
|
||||
<ProductMVPFeedbackModalContainer
|
||||
productId={ product.id }
|
||||
/>
|
||||
</>
|
||||
</WooFooterItem>
|
||||
<BlockEditorTourWrapper />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
TRACKS_SOURCE,
|
||||
__experimentalVariationSwitcherFooter as VariationSwitcherFooter,
|
||||
__experimentalProductMVPFeedbackModalContainer as ProductMVPFeedbackModalContainer,
|
||||
ProductPageSkeleton,
|
||||
} from '@woocommerce/product-editor';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import { useEffect } from '@wordpress/element';
|
||||
|
@ -80,10 +79,6 @@ export default function ProductPage() {
|
|||
[ productId ]
|
||||
);
|
||||
|
||||
if ( ! variation?.id ) {
|
||||
return <ProductPageSkeleton />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Editor
|
||||
|
@ -94,12 +89,12 @@ export default function ProductPage() {
|
|||
<WooFooterItem order={ 0 }>
|
||||
<>
|
||||
<VariationSwitcherFooter
|
||||
parentId={ variation.parent_id }
|
||||
variationId={ variation.id }
|
||||
parentId={ variation?.parent_id }
|
||||
variationId={ variation?.id }
|
||||
/>
|
||||
|
||||
<ProductMVPFeedbackModalContainer
|
||||
productId={ variation.parent_id }
|
||||
productId={ variation?.parent_id }
|
||||
/>
|
||||
</>
|
||||
</WooFooterItem>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
|
||||
Product Editor loading state now shows until form is displayed. No more blank flash of white.
|
Loading…
Reference in New Issue