From 81e92392d54810a3c200d833d155fa5bb9cb0cfa Mon Sep 17 00:00:00 2001 From: Joshua T Flowers Date: Thu, 18 May 2023 01:25:36 -0700 Subject: [PATCH] Fix double scrollbars on product editor page (#38281) * Refactor Layout component to functional component * Add class to pages based on page path * Add styling for interface skeleton on product pages * Add changelog entries * Move product page styles out of product editor package and into client * Fix linting issues * Check for location before checking path in page tracking * Dont add body classes when no page path exists * Record page view without router location for embed pages --- .../js/product-editor/changelog/fix-38149 | 4 + .../src/components/editor/style.scss | 17 -- .../client/layout/hooks/use-page-classes.ts | 56 ++++++ .../woocommerce-admin/client/layout/index.js | 167 ++++++++---------- .../products/add-edit-product-page.scss | 136 ++++++++++++++ .../client/products/add-product-page.tsx | 2 +- .../client/products/edit-product-page.tsx | 2 +- .../client/products/product-page.scss | 156 ++++------------ .../client/products/product-page.tsx | 2 +- plugins/woocommerce/changelog/fix-38149 | 4 + 10 files changed, 313 insertions(+), 233 deletions(-) create mode 100644 packages/js/product-editor/changelog/fix-38149 create mode 100644 plugins/woocommerce-admin/client/layout/hooks/use-page-classes.ts create mode 100644 plugins/woocommerce-admin/client/products/add-edit-product-page.scss create mode 100644 plugins/woocommerce/changelog/fix-38149 diff --git a/packages/js/product-editor/changelog/fix-38149 b/packages/js/product-editor/changelog/fix-38149 new file mode 100644 index 0000000000000..f793db4eeff15 --- /dev/null +++ b/packages/js/product-editor/changelog/fix-38149 @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Fix double scrollbars on product editor page diff --git a/packages/js/product-editor/src/components/editor/style.scss b/packages/js/product-editor/src/components/editor/style.scss index b03b8cea707dc..d21e479f8318d 100644 --- a/packages/js/product-editor/src/components/editor/style.scss +++ b/packages/js/product-editor/src/components/editor/style.scss @@ -1,18 +1 @@ @import '@wordpress/interface/src/style.scss'; - -.interface-interface-skeleton { - @include breakpoint( '<782px' ) { - top: $adminbar-height-mobile; - } - top: $adminbar-height; - background-color: $white; -} - -.interface-interface-skeleton__sidebar { - width: 280px; -} - -.interface-interface-skeleton__header { - // Higher than the sidebar which has a z-index of 90. - z-index: 100; -} diff --git a/plugins/woocommerce-admin/client/layout/hooks/use-page-classes.ts b/plugins/woocommerce-admin/client/layout/hooks/use-page-classes.ts new file mode 100644 index 0000000000000..c1b3aadc0a95a --- /dev/null +++ b/plugins/woocommerce-admin/client/layout/hooks/use-page-classes.ts @@ -0,0 +1,56 @@ +/** + * External dependencies + */ +import { useEffect } from '@wordpress/element'; +import { RouteMatch } from 'react-router-dom'; + +type Page = { + container: JSX.Element; + path: string; + breadcrumbs: + | string[] + | ( ( { match }: { match: RouteMatch } ) => string[] ); + wpOpenMenu: string; + navArgs: { + id: string; + }; + capability: string; +}; + +export function usePageClasses( page: Page ) { + function convertCamelCaseToKebabCase( str: string ) { + return str.replace( + /[A-Z]/g, + ( letter ) => `-${ letter.toLowerCase() }` + ); + } + + function getPathClassName( path: string ) { + const suffix = + path === '/' + ? '_home' + : path + .replace( /:[a-zA-Z?]+/g, function ( match ) { + return convertCamelCaseToKebabCase( match ).replace( + ':', + '' + ); + } ) + .replace( /\//g, '_' ); + + return `woocommerce-admin-page_${ suffix }`; + } + + useEffect( () => { + if ( ! page.path ) { + return; + } + + const classes = getPathClassName( page.path ); + + document.body.classList.add( classes ); + return () => { + document.body.classList.remove( classes ); + }; + }, [ page.path ] ); +} diff --git a/plugins/woocommerce-admin/client/layout/index.js b/plugins/woocommerce-admin/client/layout/index.js index 32517462a08e2..9c06bfd0e10d5 100644 --- a/plugins/woocommerce-admin/client/layout/index.js +++ b/plugins/woocommerce-admin/client/layout/index.js @@ -4,7 +4,7 @@ import { SlotFillProvider } from '@wordpress/components'; import { compose } from '@wordpress/compose'; import { withSelect } from '@wordpress/data'; -import { Component, lazy, Suspense } from '@wordpress/element'; +import { Component, lazy, Suspense, useEffect } from '@wordpress/element'; import { unstable_HistoryRouter as HistoryRouter, Route, @@ -15,7 +15,7 @@ import { } from 'react-router-dom'; import { Children, cloneElement } from 'react'; import PropTypes from 'prop-types'; -import { get, isFunction, identity, memoize } from 'lodash'; +import { isFunction, identity } from 'lodash'; import { CustomerEffortScoreModalContainer, triggerExitPageCesSurvey, @@ -45,6 +45,7 @@ import { Footer } from './footer'; import Notices from './notices'; import TransientNotices from './transient-notices'; import { getAdminSetting } from '~/utils/admin-settings'; +import { usePageClasses } from './hooks/use-page-classes'; import '~/activity-panel'; import '~/mobile-banner'; import './navigation'; @@ -118,39 +119,18 @@ const LayoutSwitchWrapper = ( props ) => { ); }; -class _Layout extends Component { - memoizedLayoutContext = memoize( - ( page ) => page?.navArgs?.id?.toLowerCase() || 'page' - ); - componentDidMount() { - this.recordPageViewTrack(); - triggerExitPageCesSurvey(); - } - - componentDidUpdate( prevProps ) { - const previousPath = get( prevProps, 'location.pathname' ); - const currentPath = get( this.props, 'location.pathname' ); - - if ( ! previousPath || ! currentPath ) { - return; - } - - if ( previousPath !== currentPath ) { - this.recordPageViewTrack(); - setTimeout( () => { - triggerExitPageCesSurvey(); - }, 0 ); - } - } - - recordPageViewTrack() { - const { - activePlugins, - installedPlugins, - isEmbedded, - isJetpackConnected, - } = this.props; +function _Layout( { + activePlugins, + installedPlugins, + isEmbedded, + isJetpackConnected, + location, + match, + page, +} ) { + usePageClasses( page ); + function recordPageViewTrack() { const navigationFlag = { has_navigation: !! window.wcNavigation, }; @@ -164,7 +144,7 @@ class _Layout extends Component { return; } - const pathname = get( this.props, 'location.pathname' ); + const { pathname } = location; if ( ! pathname ) { return; } @@ -185,69 +165,78 @@ class _Layout extends Component { } ); } - isWCPaySettingsPage() { - const { page, section, tab } = getQuery(); + useEffect( () => { + triggerExitPageCesSurvey(); + }, [] ); + + useEffect( () => { + recordPageViewTrack(); + setTimeout( () => { + triggerExitPageCesSurvey(); + }, 0 ); + }, [ location?.pathname ] ); + + function isWCPaySettingsPage() { + const { page: queryPage, section, tab } = getQuery(); return ( - page === 'wc-settings' && + queryPage === 'wc-settings' && tab === 'checkout' && section === 'woocommerce_payments' ); } - render() { - const { isEmbedded, ...restProps } = this.props; - const { location, page } = this.props; - const { breadcrumbs } = page; - const query = Object.fromEntries( - new URLSearchParams( location && location.search ) - ); + const { breadcrumbs } = page; - return ( - - -
-
- - { ! isEmbedded && ( - -
- -
-
- ) } + const query = Object.fromEntries( + new URLSearchParams( location && location.search ) + ); - { isEmbedded && this.isWCPaySettingsPage() && ( - - - - ) } -
- -
- - { window.wcAdminFeatures.navigation && ( - + return ( + + +
+
+ + { ! isEmbedded && ( + +
+ +
+
) } - - - - ); - } + + { isEmbedded && isWCPaySettingsPage() && ( + + + + ) } +
+ +
+ + { window.wcAdminFeatures.navigation && ( + + ) } + +
+
+ ); } _Layout.propTypes = { diff --git a/plugins/woocommerce-admin/client/products/add-edit-product-page.scss b/plugins/woocommerce-admin/client/products/add-edit-product-page.scss new file mode 100644 index 0000000000000..864dfdb0ecb4c --- /dev/null +++ b/plugins/woocommerce-admin/client/products/add-edit-product-page.scss @@ -0,0 +1,136 @@ +.woocommerce-add-product, +.woocommerce-edit-product { + .woocommerce-product-form-actions { + margin-top: $gap-largest + $gap-smaller; + } + .components-checkbox-control, + .components-toggle-control { + & > * { + margin-bottom: 0; + } + } + .components-input-control { + &__prefix { + margin-left: $gap-smaller; + } + &__suffix { + margin-right: $gap-smaller; + } + } + .components-currency-control { + .components-input-control__prefix { + color: $gray-700; + } + .components-input-control__input { + text-align: right; + } + } + .components-checkbox-control { + &__label { + display: flex; + align-items: center; + } + + &__input-container { + align-self: center; + } + + .components-base-control__field { + display: flex; + } + } + .components-toggle-control + .components-base-control__field + .components-toggle-control__label { + display: flex; + align-items: center; + } + .woocommerce-tooltip { + margin-left: $gap-smaller; + &__button { + padding: 0; + } + } + .woocommerce-product-form { + &__custom-label-input { + display: flex; + flex-direction: column; + label { + display: block; + margin-bottom: $gap-smaller; + } + } + &__optional-input { + color: $gray-700; + } + &__secondary-text { + font-size: 12px; + color: $gray-700; + margin-top: $gap-smaller; + } + } + .has-error { + .components-base-control__help { + color: $studio-red-50; + margin-bottom: 0; + } + } + + // This is needed because Gutenberg disables tooltip events on disabled elements. + // We are explicitly using this on a disabled item so this overlay prevents + // the tooltip from seeing the disabled property and allows mouse events to occur. + // See https://github.com/WordPress/gutenberg/blob/411b6eee8376e31bf9db4c15c92a80524ae38e9b/packages/components/src/tooltip/index.js#L99-L102 + .woocommerce-product-form__tooltip-disabled-overlay { + position: relative; + display: inline-block; + &::after { + content: ''; + display: inline-block; + position: absolute; + width: 100%; + height: 100%; + background: transparent; + left: 0; + top: 0; + cursor: default; + } + + .components-base-control { + margin-bottom: 0; + } + } + .half-width-field { + @include breakpoint( '<960px' ) { + width: 100%; + } + width: 50%; + } + + .components-base-control__label { + .woocommerce-product-form__secondary-text { + display: block; + font-weight: 400; + } + } +} + +.woocommerce-edit-product { + position: relative; + + &__error { + background: #fff; + text-align: center; + border: 1px solid $gray-400; + border-radius: 2px; + padding: 1em 2em; + } + + &__spinner { + display: flex; + align-items: center; + justify-content: center; + position: absolute; + width: 100%; + height: 100%; + } +} diff --git a/plugins/woocommerce-admin/client/products/add-product-page.tsx b/plugins/woocommerce-admin/client/products/add-product-page.tsx index 34c0126b64f34..4df69e4cc6142 100644 --- a/plugins/woocommerce-admin/client/products/add-product-page.tsx +++ b/plugins/woocommerce-admin/client/products/add-product-page.tsx @@ -15,7 +15,7 @@ import { */ import { ProductForm } from './product-form'; import { ProductTourContainer } from './tour'; -import './product-page.scss'; +import './add-edit-product-page.scss'; import './fills'; const AddProductPage: React.FC = () => { diff --git a/plugins/woocommerce-admin/client/products/edit-product-page.tsx b/plugins/woocommerce-admin/client/products/edit-product-page.tsx index 3fbfffd31d28e..0c2d1273f1111 100644 --- a/plugins/woocommerce-admin/client/products/edit-product-page.tsx +++ b/plugins/woocommerce-admin/client/products/edit-product-page.tsx @@ -21,7 +21,7 @@ import { useParams } from 'react-router-dom'; import { ProductForm } from './product-form'; import { ProductFormLayout } from './layout/product-form-layout'; import { ProductVariationForm } from './product-variation-form'; -import './product-page.scss'; +import './add-edit-product-page.scss'; import './fills'; const EditProductPage: React.FC = () => { diff --git a/plugins/woocommerce-admin/client/products/product-page.scss b/plugins/woocommerce-admin/client/products/product-page.scss index 864dfdb0ecb4c..9c80168e26478 100644 --- a/plugins/woocommerce-admin/client/products/product-page.scss +++ b/plugins/woocommerce-admin/client/products/product-page.scss @@ -1,136 +1,44 @@ -.woocommerce-add-product, -.woocommerce-edit-product { - .woocommerce-product-form-actions { - margin-top: $gap-largest + $gap-smaller; +.woocommerce-admin-page__add-product, +.woocommerce-admin-page__product_product-id { + .woocommerce-store-alerts { + display: none; } - .components-checkbox-control, - .components-toggle-control { - & > * { - margin-bottom: 0; - } - } - .components-input-control { - &__prefix { - margin-left: $gap-smaller; - } - &__suffix { - margin-right: $gap-smaller; - } - } - .components-currency-control { - .components-input-control__prefix { - color: $gray-700; - } - .components-input-control__input { - text-align: right; - } - } - .components-checkbox-control { - &__label { - display: flex; - align-items: center; - } - &__input-container { - align-self: center; - } - - .components-base-control__field { - display: flex; - } - } - .components-toggle-control - .components-base-control__field - .components-toggle-control__label { - display: flex; - align-items: center; - } - .woocommerce-tooltip { - margin-left: $gap-smaller; - &__button { - padding: 0; - } - } - .woocommerce-product-form { - &__custom-label-input { - display: flex; - flex-direction: column; - label { - display: block; - margin-bottom: $gap-smaller; - } - } - &__optional-input { - color: $gray-700; - } - &__secondary-text { - font-size: 12px; - color: $gray-700; - margin-top: $gap-smaller; - } + .woocommerce-layout__primary { + margin-left: 0; + margin-right: 0; + margin-bottom: 0; } - .has-error { - .components-base-control__help { - color: $studio-red-50; - margin-bottom: 0; - } - } - - // This is needed because Gutenberg disables tooltip events on disabled elements. - // We are explicitly using this on a disabled item so this overlay prevents - // the tooltip from seeing the disabled property and allows mouse events to occur. - // See https://github.com/WordPress/gutenberg/blob/411b6eee8376e31bf9db4c15c92a80524ae38e9b/packages/components/src/tooltip/index.js#L99-L102 - .woocommerce-product-form__tooltip-disabled-overlay { - position: relative; - display: inline-block; - &::after { - content: ''; - display: inline-block; - position: absolute; - width: 100%; - height: 100%; - background: transparent; - left: 0; - top: 0; - cursor: default; - } - .components-base-control { - margin-bottom: 0; - } - } - .half-width-field { - @include breakpoint( '<960px' ) { - width: 100%; - } - width: 50%; + .woocommerce-layout .woocommerce-layout__main { + padding: 0; } - .components-base-control__label { - .woocommerce-product-form__secondary-text { - display: block; - font-weight: 400; - } + .interface-interface-skeleton { + background-color: $white; + position: static; } -} -.woocommerce-edit-product { - position: relative; - - &__error { - background: #fff; - text-align: center; - border: 1px solid $gray-400; - border-radius: 2px; - padding: 1em 2em; + .interface-interface-skeleton__sidebar { + width: 280px; } - &__spinner { - display: flex; - align-items: center; - justify-content: center; - position: absolute; - width: 100%; - height: 100%; + .interface-interface-skeleton__header { + background-color: $white; + width: calc(100% - $admin-menu-width); + left: $admin-menu-width; + top: $adminbar-height; + position: fixed; + @include breakpoint( '<960px' ) { + left: $admin-menu-width-collapsed; + width: calc(100% - $admin-menu-width-collapsed); + } + @include breakpoint( '<782px' ) { + top: $adminbar-height-mobile; + left: 0; + width: 100%; + } + // Higher than the sidebar which has a z-index of 90. + z-index: 100; } } diff --git a/plugins/woocommerce-admin/client/products/product-page.tsx b/plugins/woocommerce-admin/client/products/product-page.tsx index 5ce95939b0280..f97b04d05c379 100644 --- a/plugins/woocommerce-admin/client/products/product-page.tsx +++ b/plugins/woocommerce-admin/client/products/product-page.tsx @@ -15,8 +15,8 @@ import { useParams } from 'react-router-dom'; * Internal dependencies */ import { useProductEntityRecord } from './hooks/use-product-entity-record'; - import './fills/product-block-editor-fills'; +import './product-page.scss'; declare const productBlockEditorSettings: ProductEditorSettings; diff --git a/plugins/woocommerce/changelog/fix-38149 b/plugins/woocommerce/changelog/fix-38149 new file mode 100644 index 0000000000000..8f69c391f13e2 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-38149 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add WooCommerce Admin page class to body of every page