From 6227e69e6ec00cc75b53902ee28e4f76fa8a565f Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Thu, 15 Feb 2018 12:55:40 -0500 Subject: [PATCH 01/16] Remove SidebarNavigation from screens with ActionHeader, causes duplicate component --- client/extensions/woocommerce/app/products/index.js | 2 -- client/extensions/woocommerce/app/products/product-update.js | 2 -- client/extensions/woocommerce/app/promotions/index.js | 2 -- client/extensions/woocommerce/app/reviews/index.js | 2 -- 4 files changed, 8 deletions(-) diff --git a/client/extensions/woocommerce/app/products/index.js b/client/extensions/woocommerce/app/products/index.js index beb50dd1ca5b8..27b9d2ec21dff 100644 --- a/client/extensions/woocommerce/app/products/index.js +++ b/client/extensions/woocommerce/app/products/index.js @@ -30,7 +30,6 @@ import NavTabs from 'components/section-nav/tabs'; import NavItem from 'components/section-nav/item'; import ProductsList from './products-list'; import ProductsListSearchResults from './products-list-search-results'; -import SidebarNavigation from 'my-sites/sidebar-navigation'; import SectionNav from 'components/section-nav'; import Search from 'components/search'; @@ -124,7 +123,6 @@ class Products extends Component { return (
- { translate( 'Products' ) } }> - - - ); + return [ + , + , + ]; }; render() { From 2b60cf8f411b8f8ffad902dfb758de42ce2c16a0 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Mon, 19 Feb 2018 16:18:07 -0500 Subject: [PATCH 07/16] =?UTF-8?q?Merge=20our=20ActionHeader=20&=20calypso?= =?UTF-8?q?=20core=E2=80=99s=20SidebarNavigation=20to=20create=20a=20singl?= =?UTF-8?q?e=20nav=20header=20for=20large=20and=20small=20screens?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/action-header/index.js | 111 +++++++++++------- .../components/action-header/style.scss | 108 ++++++++--------- client/extensions/woocommerce/style.scss | 2 +- 3 files changed, 123 insertions(+), 98 deletions(-) diff --git a/client/extensions/woocommerce/components/action-header/index.js b/client/extensions/woocommerce/components/action-header/index.js index 87bec86036aa2..426546ce2bc82 100644 --- a/client/extensions/woocommerce/components/action-header/index.js +++ b/client/extensions/woocommerce/components/action-header/index.js @@ -1,60 +1,85 @@ /** @format */ - /** * External dependencies */ - -import React from 'react'; import PropTypes from 'prop-types'; +import React from 'react'; import classNames from 'classnames'; -import { filter, isArray } from 'lodash'; +import { connect } from 'react-redux'; +import Gridicon from 'gridicons'; +import { isArray } from 'lodash'; /** - * Internal dependencies + * Internal Dependencies */ -import Card from 'components/card'; -import SidebarNavigation from 'my-sites/sidebar-navigation'; -import StickyPanel from 'components/sticky-panel'; - -const ActionHeader = ( { children, breadcrumbs, isLoading } ) => { - // TODO: Implement proper breadcrumbs component. - // For v1, we will just pass in a prop from each page. - let breadcrumbsOutput = breadcrumbs; - if ( isArray( breadcrumbs ) ) { - breadcrumbsOutput = breadcrumbs.map( function( crumb, i ) { - return ( - - { crumb } - { breadcrumbs.length - 1 === i ? ( - '' - ) : ( - / - ) } - - ); +import Button from 'components/button'; +import { getSelectedSiteWithFallback } from 'woocommerce/state/sites/selectors'; +import { setLayoutFocus } from 'state/ui/layout-focus/actions'; +import SiteIcon from 'blocks/site-icon'; + +class ActionHeader extends React.Component { + toggleSidebar = event => { + event.preventDefault(); + this.props.setLayoutFocus( 'sidebar' ); + }; + + renderBreadcrumbs = () => { + const { breadcrumbs, isLoading = false } = this.props; + let breadcrumbsOutput = breadcrumbs; + if ( isArray( breadcrumbs ) ) { + breadcrumbsOutput = breadcrumbs.map( function( crumb, i ) { + return ( + + { crumb } + { breadcrumbs.length - 1 === i ? ( + '' + ) : ( + / + ) } + + ); + } ); + } + const breadcrumbClasses = classNames( 'action-header__breadcrumbs', { + 'is-loading': isLoading, } ); - } - const breadcrumbClasses = classNames( 'action-header__breadcrumbs', { 'is-loading': isLoading } ); - const actions = isArray( children ) ? filter( children, Boolean ) : children; - const containerClasses = classNames( 'action-header__header', { - 'has-no-action': ! actions, - 'has-multiple-actions': actions && actions.length > 1, - } ); - - return ( - - - - { breadcrumbsOutput } + return
{ breadcrumbsOutput }
; + }; + + render() { + const { children, site } = this.props; + + return ( +
+ +
+ +
+ { site &&

{ site.title }

} + { this.renderBreadcrumbs() } +
+
{ children }
- - - ); -}; +
+ ); + } +} ActionHeader.propTypes = { breadcrumbs: PropTypes.node, + setLayoutFocus: PropTypes.func.isRequired, }; -export default ActionHeader; +export default connect( + state => ( { + site: getSelectedSiteWithFallback( state ), + } ), + { setLayoutFocus } +)( ActionHeader ); diff --git a/client/extensions/woocommerce/components/action-header/style.scss b/client/extensions/woocommerce/components/action-header/style.scss index 7404632b621dc..ffccae05e5895 100644 --- a/client/extensions/woocommerce/components/action-header/style.scss +++ b/client/extensions/woocommerce/components/action-header/style.scss @@ -1,45 +1,73 @@ -.action-header__header { +.action-header { width: 100%; min-height: 46px; - padding: 2px 16px 2px; + margin: 0 0 8px; + padding: 8px 16px 8px 0; display: flex; + justify-content: flex-start; flex-direction: row; align-items: center; + box-sizing: border-box; + color: $gray-dark; + background: $white; + border-bottom: 1px solid $gray-lighten-20; @include breakpoint( ">480px" ) { - padding-left: 24px; - padding-right: 24px; + padding: 8px 24px 8px 0; } - @include breakpoint( "<480px" ) { - &.has-multiple-actions { - .action-header__breadcrumbs { - display: none; - } - - .action-header__actions { - width: 100%; - } - } + @include breakpoint( ">660px" ) { + padding: 1px 16px 2px 0; + margin: 0; + } - &.has-no-action { - display: none; - } + @include breakpoint( ">960px" ) { + position: fixed; + z-index: z-index( 'root', '.sticky-panel.is-sticky .sticky-panel__content' ); + width: calc(100% - 273px); + top: 47px; + left: 273px; + } + @include breakpoint( "660px-960px" ) { + position: fixed; + z-index: z-index( 'root', '.sticky-panel.is-sticky .sticky-panel__content' ); + width: calc(100% - 229px); + top: 47px; + left: 229px; } } -.action-header__breadcrumbs { - flex: 1 0 60%; - font-size: 13px; +.action-header__back-to-sidebar { + flex: 1 0 10%; +} - &.is-loading { - @include placeholder(); +.action-header__content { + flex: 1 0 50%; + display: flex; + justify-content: flex-start; + + .site-icon { + margin-right: 8px; } -} -.action-header__breadcrumbs-separator { - color: $gray; - margin: 0 2px; + .action-header__site-title { + font-size: 10px; + color: $gray-darken-10; + margin: 2px 0 -3px; + } + + .action-header__breadcrumbs { + font-size: 13px; + + &.is-loading { + @include placeholder(); + } + } + + .action-header__breadcrumbs-separator { + color: $gray; + margin: 0 2px; + } } .action-header__actions { @@ -70,31 +98,3 @@ } } } -.action-header__notice { - margin: 0; - width: 100%; -} - -.sticky-panel { - @include breakpoint( ">960px" ) { - position: fixed; - z-index: z-index( 'root', '.sticky-panel.is-sticky .sticky-panel__content' ); - width: calc(100% - 273px); - top: 47px; - left: 273px; - } - @include breakpoint( "660px-960px" ) { - position: fixed; - z-index: z-index( 'root', '.sticky-panel.is-sticky .sticky-panel__content' ); - width: calc(100% - 229px); - top: 47px; - left: 229px; - } - @include breakpoint( ">660px" ) { - margin-bottom: 58px; - } -} - -& .sticky-panel.is-sticky .sticky-panel__content { - margin-top: 0; -} diff --git a/client/extensions/woocommerce/style.scss b/client/extensions/woocommerce/style.scss index 021babeb3dac9..2c59a51f4332b 100644 --- a/client/extensions/woocommerce/style.scss +++ b/client/extensions/woocommerce/style.scss @@ -57,7 +57,7 @@ .section-nav { @include breakpoint( ">660px" ) { - margin-top: 58px; + margin-top: 35px; } } From c26994efeeb48b5d942bf79cec05f567b0c620c9 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Mon, 19 Feb 2018 16:31:23 -0500 Subject: [PATCH 08/16] =?UTF-8?q?Set=20the=20header=E2=80=99s=20offset=20o?= =?UTF-8?q?n=20the=20parent=20component,=20to=20avoid=20needing=20to=20red?= =?UTF-8?q?o=20it=20in=20each=20screen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/extensions/woocommerce/app/dashboard/style.scss | 9 --------- client/extensions/woocommerce/app/order/style.scss | 4 ---- client/extensions/woocommerce/app/orders/style.scss | 4 ---- .../woocommerce/app/product-categories/style.scss | 5 ----- .../woocommerce/app/products/product-form.scss | 4 ---- .../woocommerce/app/products/products-list.scss | 8 +------- .../woocommerce/app/promotions/promotion-form.scss | 4 ---- .../woocommerce/app/promotions/promotions-list.scss | 7 ------- client/extensions/woocommerce/app/reviews/style.scss | 4 ---- .../app/settings/shipping/shipping-zone/style.scss | 7 ------- client/extensions/woocommerce/style.scss | 8 +++----- 11 files changed, 4 insertions(+), 60 deletions(-) diff --git a/client/extensions/woocommerce/app/dashboard/style.scss b/client/extensions/woocommerce/app/dashboard/style.scss index b8ec8d0cbb9ae..e80efc07732f2 100644 --- a/client/extensions/woocommerce/app/dashboard/style.scss +++ b/client/extensions/woocommerce/app/dashboard/style.scss @@ -1,12 +1,3 @@ -.dashboard__manage-has-orders, -.dashboard__manage-no-orders, -.dashboard__placeholder, -.setup__wrapper { - @include breakpoint( ">660px" ) { - margin-top: 58px; - } -} - .dashboard__placeholder .card { @include placeholder(); } diff --git a/client/extensions/woocommerce/app/order/style.scss b/client/extensions/woocommerce/app/order/style.scss index 152a227033550..2cb5b1a3676e2 100644 --- a/client/extensions/woocommerce/app/order/style.scss +++ b/client/extensions/woocommerce/app/order/style.scss @@ -7,10 +7,6 @@ box-sizing: border-box; } - @include breakpoint( '>660px' ) { - margin-top: 58px; - } - @include breakpoint( '>960px' ) { display: flex; flex-wrap: wrap; diff --git a/client/extensions/woocommerce/app/orders/style.scss b/client/extensions/woocommerce/app/orders/style.scss index 357b2b0d94638..926cf939f119f 100644 --- a/client/extensions/woocommerce/app/orders/style.scss +++ b/client/extensions/woocommerce/app/orders/style.scss @@ -1,8 +1,4 @@ .orders__container { - @include breakpoint( ">660px" ) { - margin-top: 58px; - } - thead { .table-row { &:hover { diff --git a/client/extensions/woocommerce/app/product-categories/style.scss b/client/extensions/woocommerce/app/product-categories/style.scss index 676542961deb3..0d6ded95a9b62 100644 --- a/client/extensions/woocommerce/app/product-categories/style.scss +++ b/client/extensions/woocommerce/app/product-categories/style.scss @@ -59,10 +59,6 @@ } .product-categories__form { - @include breakpoint( ">660px" ) { - margin-top: 58px; - } - &.is-placeholder div { @include placeholder(); background: $white; @@ -198,4 +194,3 @@ } } } - diff --git a/client/extensions/woocommerce/app/products/product-form.scss b/client/extensions/woocommerce/app/products/product-form.scss index 5abd63063baeb..6e33d6f49dab4 100644 --- a/client/extensions/woocommerce/app/products/product-form.scss +++ b/client/extensions/woocommerce/app/products/product-form.scss @@ -3,10 +3,6 @@ */ .products__form { - @include breakpoint( ">660px" ) { - margin-top: 58px; - } - &.is-placeholder div { @include placeholder(); background: $white; diff --git a/client/extensions/woocommerce/app/products/products-list.scss b/client/extensions/woocommerce/app/products/products-list.scss index 69faa8ca4b0c9..430d0cf730d7b 100644 --- a/client/extensions/woocommerce/app/products/products-list.scss +++ b/client/extensions/woocommerce/app/products/products-list.scss @@ -13,12 +13,6 @@ margin-top: 16px; } - .search-card { - @include breakpoint( ">660px" ) { - margin-top: 58px; - } - } - .products__list-placeholder { @include placeholder(); background: $white; @@ -45,7 +39,7 @@ .is-requesting td div { @include placeholder(); background: transparent; - + .image-thumb::after { display: none; } diff --git a/client/extensions/woocommerce/app/promotions/promotion-form.scss b/client/extensions/woocommerce/app/promotions/promotion-form.scss index b7ade9872d7cd..55b37285513c0 100644 --- a/client/extensions/woocommerce/app/promotions/promotion-form.scss +++ b/client/extensions/woocommerce/app/promotions/promotion-form.scss @@ -1,9 +1,5 @@ .promotions__form { - @include breakpoint( ">660px" ) { - margin-top: 58px; - } - &.is-placeholder div { @include placeholder(); background: $white; diff --git a/client/extensions/woocommerce/app/promotions/promotions-list.scss b/client/extensions/woocommerce/app/promotions/promotions-list.scss index c27d0f935634d..033fa4d1e1fcb 100644 --- a/client/extensions/woocommerce/app/promotions/promotions-list.scss +++ b/client/extensions/woocommerce/app/promotions/promotions-list.scss @@ -13,12 +13,6 @@ margin-top: 16px; } - .search { - @include breakpoint( ">660px" ) { - margin-top: 58px; - } - } - .promotions__list-placeholder { @include placeholder(); background: $white; @@ -105,4 +99,3 @@ } } - diff --git a/client/extensions/woocommerce/app/reviews/style.scss b/client/extensions/woocommerce/app/reviews/style.scss index 9697ad494df58..fa9cbbd2f659b 100644 --- a/client/extensions/woocommerce/app/reviews/style.scss +++ b/client/extensions/woocommerce/app/reviews/style.scss @@ -6,10 +6,6 @@ } .reviews__filter-nav { - @include breakpoint( ">660px" ) { - margin-top: 58px; - } - .section-nav { margin-top: 8px; } diff --git a/client/extensions/woocommerce/app/settings/shipping/shipping-zone/style.scss b/client/extensions/woocommerce/app/settings/shipping/shipping-zone/style.scss index 5cdd796d8003a..f3b91bdeb2b94 100644 --- a/client/extensions/woocommerce/app/settings/shipping/shipping-zone/style.scss +++ b/client/extensions/woocommerce/app/settings/shipping/shipping-zone/style.scss @@ -1,10 +1,3 @@ -.sticky-panel + .shipping-zone__locations-container, -.sticky-panel + .shipping-zone__methods-container { - @include breakpoint( ">660px" ) { - margin-top: 58px; - } -} - .shipping-zone__name { display: flex; flex-direction: row; diff --git a/client/extensions/woocommerce/style.scss b/client/extensions/woocommerce/style.scss index 2c59a51f4332b..564b02e16f750 100644 --- a/client/extensions/woocommerce/style.scss +++ b/client/extensions/woocommerce/style.scss @@ -55,9 +55,9 @@ @import 'woocommerce-services/style'; - .section-nav { + .main { @include breakpoint( ">660px" ) { - margin-top: 35px; + padding-top: 35px; } } @@ -151,9 +151,7 @@ .is-section-woocommerce.focus-sidebar { @include breakpoint( "<660px" ) { - .sticky-panel, - .products__form, - .current-section { + .products__form { display: none; } } From 2d8cce4ed94abb062e5e8c943b68d7755851e0b9 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Mon, 19 Feb 2018 16:36:22 -0500 Subject: [PATCH 09/16] Hide the sidebar toggle when the sidebar is visible (on larger screens) --- .../woocommerce/components/action-header/style.scss | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/extensions/woocommerce/components/action-header/style.scss b/client/extensions/woocommerce/components/action-header/style.scss index ffccae05e5895..7ddccae940b29 100644 --- a/client/extensions/woocommerce/components/action-header/style.scss +++ b/client/extensions/woocommerce/components/action-header/style.scss @@ -1,6 +1,6 @@ .action-header { width: 100%; - min-height: 46px; + min-height: 47px; margin: 0 0 8px; padding: 8px 16px 8px 0; display: flex; @@ -17,7 +17,7 @@ } @include breakpoint( ">660px" ) { - padding: 1px 16px 2px 0; + padding: 5px 16px; margin: 0; } @@ -39,6 +39,10 @@ .action-header__back-to-sidebar { flex: 1 0 10%; + + @include breakpoint( ">660px" ) { + display: none; + } } .action-header__content { From eeb44de56d03daad6e0aba5dee0e02cc6d84ea5a Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Mon, 19 Feb 2018 20:08:38 -0500 Subject: [PATCH 10/16] Clean up breadcrumb code --- .../woocommerce/components/action-header/index.js | 13 +------------ .../woocommerce/components/action-header/style.scss | 9 +++++---- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/client/extensions/woocommerce/components/action-header/index.js b/client/extensions/woocommerce/components/action-header/index.js index 426546ce2bc82..134bcfc231e15 100644 --- a/client/extensions/woocommerce/components/action-header/index.js +++ b/client/extensions/woocommerce/components/action-header/index.js @@ -27,18 +27,7 @@ class ActionHeader extends React.Component { const { breadcrumbs, isLoading = false } = this.props; let breadcrumbsOutput = breadcrumbs; if ( isArray( breadcrumbs ) ) { - breadcrumbsOutput = breadcrumbs.map( function( crumb, i ) { - return ( - - { crumb } - { breadcrumbs.length - 1 === i ? ( - '' - ) : ( - / - ) } - - ); - } ); + breadcrumbsOutput = breadcrumbs.map( ( crumb, i ) => { crumb } ); } const breadcrumbClasses = classNames( 'action-header__breadcrumbs', { 'is-loading': isLoading, diff --git a/client/extensions/woocommerce/components/action-header/style.scss b/client/extensions/woocommerce/components/action-header/style.scss index 7ddccae940b29..66307049bb682 100644 --- a/client/extensions/woocommerce/components/action-header/style.scss +++ b/client/extensions/woocommerce/components/action-header/style.scss @@ -66,11 +66,12 @@ &.is-loading { @include placeholder(); } - } - .action-header__breadcrumbs-separator { - color: $gray; - margin: 0 2px; + span + span::before { + content: ' / '; + color: $gray; + margin: 0 2px; + } } } From dce78ffcafb8e01c793c6a01de16114af4e53d41 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Tue, 20 Feb 2018 15:58:44 -0500 Subject: [PATCH 11/16] Switch to a split button when the action-buttons run out of room --- .../components/action-header/actions.js | 145 ++++++++++++++++++ .../components/action-header/index.js | 4 +- .../components/action-header/style.scss | 42 ++++- 3 files changed, 184 insertions(+), 7 deletions(-) create mode 100644 client/extensions/woocommerce/components/action-header/actions.js diff --git a/client/extensions/woocommerce/components/action-header/actions.js b/client/extensions/woocommerce/components/action-header/actions.js new file mode 100644 index 0000000000000..9ca5dee921e60 --- /dev/null +++ b/client/extensions/woocommerce/components/action-header/actions.js @@ -0,0 +1,145 @@ +/** @format */ + +/** + * External dependencies + */ + +import React, { Component } from 'react'; +import ReactDom from 'react-dom'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { debounce } from 'lodash'; + +/** + * Internal Dependencies + */ +import afterLayoutFlush from 'lib/after-layout-flush'; +import PopoverMenuItem from 'components/popover/menu-item'; +import SplitButton from 'components/split-button'; + +class ActionButtons extends Component { + static propTypes = { + selectedText: PropTypes.string, + selectedCount: PropTypes.number, + label: PropTypes.string, + hasSiblingControls: PropTypes.bool, + }; + + static defaultProps = { + hasSiblingControls: false, + }; + + state = { + isDropdown: false, + }; + + componentDidMount() { + this.setDropdownAfterLayoutFlush(); + window.addEventListener( 'resize', this.setDropdownDebounced ); + } + + componentDidUpdate() { + this.setDropdownAfterLayoutFlush(); + } + + componentWillUnmount() { + window.removeEventListener( 'resize', this.setDropdownDebounced ); + // cancel the debounced `setDropdown` calls that might be already scheduled. + // see https://lodash.com/docs/4.17.4#debounce to learn about the `cancel` method. + this.setDropdownDebounced.cancel(); + this.setDropdownAfterLayoutFlush.cancel(); + } + + setGroupRef = group => { + this.navGroup = group; + }; + + render() { + const buttons = React.Children.map( this.props.children, function( child, index ) { + return child && React.cloneElement( child, { ref: 'button-' + index } ); + } ); + + const buttonsClassName = classNames( { + 'action-header__actions': true, + 'is-dropdown': this.state.isDropdown, + 'is-open': this.state.isDropdownOpen, + } ); + + return ( +
+
{ buttons }
+ + { this.state.isDropdown && this.getDropdown() } +
+ ); + } + + getButtonWidths = () => { + let totalWidth = 0; + + React.Children.forEach( + this.props.children, + function( child, index ) { + if ( ! child ) { + return; + } + /* eslint-disable react/no-string-refs */ + const buttonWidth = ReactDom.findDOMNode( this.refs[ 'button-' + index ] ).offsetWidth; + /* eslint-enable react/no-string-refs */ + totalWidth += buttonWidth; + }.bind( this ) + ); + + this.buttonsWidth = Math.max( totalWidth, this.buttonsWidth || 0 ); + }; + + getDropdown = () => { + const buttons = React.Children.toArray( this.props.children ); + const first = buttons.shift(); + const dropdownOptions = buttons.map( function( child, index ) { + if ( ! child ) { + return null; + } + return { child.props.children }; + } ); + + return ( + + { dropdownOptions } + + ); + }; + + setDropdown = () => { + if ( ! this.navGroup ) { + return; + } + + const navGroupWidth = this.navGroup.offsetWidth; + + this.getButtonWidths(); + this.setState( { + isDropdown: navGroupWidth <= this.buttonsWidth, + } ); + }; + + setDropdownDebounced = debounce( this.setDropdown, 300 ); + + // setDropdown reads element sizes from DOM. If called synchronously in the middle of a React + // update, it causes a synchronous layout reflow, doing the layout two or more times instead + // of just once after all the DOM writes are finished. Prevent that by scheduling the call + // just *after* the next layout flush. + setDropdownAfterLayoutFlush = afterLayoutFlush( this.setDropdown ); + + keyHandler = event => { + switch ( event.keyCode ) { + case 32: // space + case 13: // enter + event.preventDefault(); + document.activeElement.click(); + break; + } + }; +} + +export default ActionButtons; diff --git a/client/extensions/woocommerce/components/action-header/index.js b/client/extensions/woocommerce/components/action-header/index.js index 134bcfc231e15..db38cfc487281 100644 --- a/client/extensions/woocommerce/components/action-header/index.js +++ b/client/extensions/woocommerce/components/action-header/index.js @@ -12,6 +12,7 @@ import { isArray } from 'lodash'; /** * Internal Dependencies */ +import ActionButtons from './actions'; import Button from 'components/button'; import { getSelectedSiteWithFallback } from 'woocommerce/state/sites/selectors'; import { setLayoutFocus } from 'state/ui/layout-focus/actions'; @@ -55,7 +56,7 @@ class ActionHeader extends React.Component { { this.renderBreadcrumbs() } -
{ children }
+ { children } ); } @@ -63,6 +64,7 @@ class ActionHeader extends React.Component { ActionHeader.propTypes = { breadcrumbs: PropTypes.node, + actions: PropTypes.node, setLayoutFocus: PropTypes.func.isRequired, }; diff --git a/client/extensions/woocommerce/components/action-header/style.scss b/client/extensions/woocommerce/components/action-header/style.scss index 66307049bb682..3421024b125ed 100644 --- a/client/extensions/woocommerce/components/action-header/style.scss +++ b/client/extensions/woocommerce/components/action-header/style.scss @@ -38,7 +38,9 @@ } .action-header__back-to-sidebar { - flex: 1 0 10%; + flex-grow: 0; + flex-shrink: 0; + min-width: 40px; @include breakpoint( ">660px" ) { display: none; @@ -46,7 +48,8 @@ } .action-header__content { - flex: 1 0 50%; + flex-grow: 2; + flex-shrink: 0; display: flex; justify-content: flex-start; @@ -67,6 +70,10 @@ @include placeholder(); } + span { + display: inline-block; + } + span + span::before { content: ' / '; color: $gray; @@ -76,13 +83,36 @@ } .action-header__actions { - display: flex; - flex: 1 0 40%; - justify-content: flex-end; - align-items: center; + flex-grow: 1; + flex-shrink: 2; + // Triggers the width of the list, so ActionButtons correctly measures the space. + overflow: hidden; + text-align: right; + + .action-header__actions-list { + display: flex; + justify-content: flex-end; + align-items: center; + flex-wrap: nowrap; + + .button { + flex-basis: auto; + } + } + + &.is-dropdown .action-header__actions-list { + display: none; + } + + .action-header__split-button { + .button + .button { + margin-left: 0; + } + } .button { white-space: nowrap; + overflow: visible; padding: 5px 14px 7px; & + .button { From 514c684c8144c19b9efe29ea38859fbe83cd6357 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Wed, 21 Feb 2018 11:55:34 -0700 Subject: [PATCH 12/16] Remove unused code --- .../components/action-header/actions.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/client/extensions/woocommerce/components/action-header/actions.js b/client/extensions/woocommerce/components/action-header/actions.js index 9ca5dee921e60..af3c48215112b 100644 --- a/client/extensions/woocommerce/components/action-header/actions.js +++ b/client/extensions/woocommerce/components/action-header/actions.js @@ -22,11 +22,6 @@ class ActionButtons extends Component { selectedText: PropTypes.string, selectedCount: PropTypes.number, label: PropTypes.string, - hasSiblingControls: PropTypes.bool, - }; - - static defaultProps = { - hasSiblingControls: false, }; state = { @@ -130,16 +125,6 @@ class ActionButtons extends Component { // of just once after all the DOM writes are finished. Prevent that by scheduling the call // just *after* the next layout flush. setDropdownAfterLayoutFlush = afterLayoutFlush( this.setDropdown ); - - keyHandler = event => { - switch ( event.keyCode ) { - case 32: // space - case 13: // enter - event.preventDefault(); - document.activeElement.click(); - break; - } - }; } export default ActionButtons; From 6bef598a04e0da4ea911bf6ef67397e263e81adf Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Wed, 21 Feb 2018 11:55:59 -0700 Subject: [PATCH 13/16] Style the dropdown, use a real Button --- .../components/action-header/actions.js | 16 +++++++++--- .../components/action-header/style.scss | 26 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/client/extensions/woocommerce/components/action-header/actions.js b/client/extensions/woocommerce/components/action-header/actions.js index af3c48215112b..5bf4ba83d7eaa 100644 --- a/client/extensions/woocommerce/components/action-header/actions.js +++ b/client/extensions/woocommerce/components/action-header/actions.js @@ -14,6 +14,7 @@ import { debounce } from 'lodash'; * Internal Dependencies */ import afterLayoutFlush from 'lib/after-layout-flush'; +import Button from 'components/button'; import PopoverMenuItem from 'components/popover/menu-item'; import SplitButton from 'components/split-button'; @@ -90,16 +91,25 @@ class ActionButtons extends Component { getDropdown = () => { const buttons = React.Children.toArray( this.props.children ); - const first = buttons.shift(); + const first = buttons.pop(); const dropdownOptions = buttons.map( function( child, index ) { if ( ! child ) { return null; } - return { child.props.children }; + return ( + + { child.props.children } + + ); } ); return ( - + { dropdownOptions } ); diff --git a/client/extensions/woocommerce/components/action-header/style.scss b/client/extensions/woocommerce/components/action-header/style.scss index 3421024b125ed..f947971702b0f 100644 --- a/client/extensions/woocommerce/components/action-header/style.scss +++ b/client/extensions/woocommerce/components/action-header/style.scss @@ -133,3 +133,29 @@ } } } + +// & refers to .woocommerce base class +&.popover .popover__menu-item { + padding: 8px 16px; + + .gridicon { + top: 4px; + } + + &.is-scary { + .gridicon { + color: $alert-red; + } + + &.is-selected, + &:hover, + &:focus { + background: $alert-red; + color: $white; + + .gridicon { + color: $white; + } + } + } +} From ad54360761057a50cf88beee4028590f3c10ba13 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Wed, 21 Feb 2018 12:28:16 -0700 Subject: [PATCH 14/16] Add `primaryLabel` when ActionHeader has multiple buttons --- .../woocommerce/app/order/header.js | 4 ++- .../app/product-categories/header.js | 19 ++++++-------- .../app/products/product-header.js | 25 ++++++++---------- .../app/promotions/promotion-header.js | 26 +++++++------------ .../shipping-zone/shipping-zone-header.js | 2 +- .../components/action-header/actions.js | 13 +++++----- .../components/action-header/index.js | 18 +++++++------ 7 files changed, 50 insertions(+), 57 deletions(-) diff --git a/client/extensions/woocommerce/app/order/header.js b/client/extensions/woocommerce/app/order/header.js index 841cf8298df62..76c1026733396 100644 --- a/client/extensions/woocommerce/app/order/header.js +++ b/client/extensions/woocommerce/app/order/header.js @@ -179,8 +179,10 @@ class OrderActionHeader extends Component { , ]; + const primaryLabel = isEditing ? translate( 'Update' ) : translate( 'Edit Order' ); + return ( - + { isEditing ? this.renderEditingButtons() : this.renderViewButtons() } ); diff --git a/client/extensions/woocommerce/app/product-categories/header.js b/client/extensions/woocommerce/app/product-categories/header.js index f87d12b6f1ca4..1685038a49490 100644 --- a/client/extensions/woocommerce/app/product-categories/header.js +++ b/client/extensions/woocommerce/app/product-categories/header.js @@ -17,24 +17,21 @@ import ActionHeader from 'woocommerce/components/action-header'; import Button from 'components/button'; import { getLink } from 'woocommerce/lib/nav-utils'; -function renderDeleteButton( onDelete, category, translate ) { +function renderDeleteButton( onDelete, label ) { return ( onDelete && ( ) ); } -function renderSaveButton( onSave, isBusy, category, translate ) { +function renderSaveButton( onSave, isBusy, label ) { const saveExists = 'undefined' !== typeof onSave; const saveDisabled = false === onSave; - const saveLabel = - category && ! isObject( category.id ) ? translate( 'Update' ) : translate( 'Save' ); - return ( saveExists && ( ) ); @@ -51,9 +48,9 @@ function renderSaveButton( onSave, isBusy, category, translate ) { const ProductCategoryHeader = ( { onDelete, onSave, translate, site, category, isBusy } ) => { const existing = category && ! isObject( category.id ); - - const deleteButton = renderDeleteButton( onDelete, category, translate ); - const saveButton = renderSaveButton( onSave, isBusy, category, translate ); + const deleteButton = renderDeleteButton( onDelete, translate( 'Delete' ) ); + const saveLabel = existing ? translate( 'Update' ) : translate( 'Save' ); + const saveButton = renderSaveButton( onSave, isBusy, saveLabel ); const currentCrumb = category && existing ? ( @@ -72,7 +69,7 @@ const ProductCategoryHeader = ( { onDelete, onSave, translate, site, category, i ]; return ( - + { deleteButton } { saveButton } diff --git a/client/extensions/woocommerce/app/products/product-header.js b/client/extensions/woocommerce/app/products/product-header.js index e22d99c4e5645..75742c8ee8e6e 100644 --- a/client/extensions/woocommerce/app/products/product-header.js +++ b/client/extensions/woocommerce/app/products/product-header.js @@ -17,7 +17,7 @@ import ActionHeader from 'woocommerce/components/action-header'; import Button from 'components/button'; import { getLink } from 'woocommerce/lib/nav-utils'; -function renderViewButton( product, translate ) { +function renderViewButton( product, label ) { const url = product && product.permalink; return ( // TODO: Do more to validate this URL? @@ -29,29 +29,26 @@ function renderViewButton( product, translate ) { rel="noopener noreferrer" > - { translate( 'View' ) } + { label } ); } -function renderTrashButton( onTrash, product, isBusy, translate ) { +function renderTrashButton( onTrash, isBusy, label ) { return ( onTrash && ( ) ); } -function renderSaveButton( onSave, product, isBusy, translate ) { +function renderSaveButton( onSave, isBusy, label ) { const saveExists = 'undefined' !== typeof onSave; const saveDisabled = false === onSave; - const saveLabel = - product && ! isObject( product.id ) ? translate( 'Update' ) : translate( 'Save & Publish' ); - return ( saveExists && ( ) ); @@ -68,10 +65,10 @@ function renderSaveButton( onSave, product, isBusy, translate ) { const ProductHeader = ( { viewEnabled, onTrash, onSave, isBusy, translate, site, product } ) => { const existing = product && ! isObject( product.id ); - - const viewButton = viewEnabled && renderViewButton( product, translate ); - const trashButton = renderTrashButton( onTrash, product, isBusy, translate ); - const saveButton = renderSaveButton( onSave, product, isBusy, translate ); + const viewButton = viewEnabled && renderViewButton( product, translate( 'View' ) ); + const trashButton = renderTrashButton( onTrash, isBusy, translate( 'Delete' ) ); + const saveLabel = existing ? translate( 'Update' ) : translate( 'Save & Publish' ); + const saveButton = renderSaveButton( onSave, isBusy, saveLabel ); const currentCrumb = product && existing ? ( @@ -86,7 +83,7 @@ const ProductHeader = ( { viewEnabled, onTrash, onSave, isBusy, translate, site, ]; return ( - + { trashButton } { viewButton } { saveButton } diff --git a/client/extensions/woocommerce/app/promotions/promotion-header.js b/client/extensions/woocommerce/app/promotions/promotion-header.js index 4510e097bb75e..307d45bbdb4b1 100644 --- a/client/extensions/woocommerce/app/promotions/promotion-header.js +++ b/client/extensions/woocommerce/app/promotions/promotion-header.js @@ -14,16 +14,16 @@ import ActionHeader from 'woocommerce/components/action-header'; import Button from 'components/button'; import { getLink } from 'woocommerce/lib/nav-utils'; -function renderTrashButton( onTrash, promotion, isBusy, translate ) { +function renderTrashButton( onTrash, isBusy, label ) { return onTrash && ( ); } -function renderSaveButton( onSave, promotion, isBusy, translate ) { +function renderSaveButton( onSave, isBusy, label ) { if ( 'undefined' === typeof onSave ) { // 'Save' not allowed here. return null; @@ -31,25 +31,20 @@ function renderSaveButton( onSave, promotion, isBusy, translate ) { const saveDisabled = false === onSave; - const saveLabel = ( promotion && ! isObject( promotion.id ) - ? translate( 'Update' ) - : translate( 'Save & Publish' ) - ); - return ( ); } const PromotionHeader = ( { promotion, onSave, onTrash, isBusy, translate, site } ) => { - const trashButton = renderTrashButton( onTrash, promotion, isBusy, translate ); - const saveButton = renderSaveButton( onSave, promotion, isBusy, translate ); + const existing = ( promotion && ! isObject( promotion.id ) ); + const trashButton = renderTrashButton( onTrash, isBusy, translate( 'Delete' ) ); + const saveLabel = existing ? translate( 'Update' ) : translate( 'Save & Publish' ); + const saveButton = renderSaveButton( onSave, isBusy, saveLabel ); - const currentCrumb = promotion && ! isObject( promotion.id ) - ? ( { translate( 'Edit Promotion' ) } ) - : ( { translate( 'Add Promotion' ) } ); + const currentCrumb = existing ? ( { translate( 'Edit Promotion' ) } ) : ( { translate( 'Add Promotion' ) } ); const breadcrumbs = [ ( { translate( 'Promotions' ) } ), @@ -57,7 +52,7 @@ const PromotionHeader = ( { promotion, onSave, onTrash, isBusy, translate, site ]; return ( - + { trashButton } { saveButton } @@ -82,4 +77,3 @@ PromotionHeader.propTypes = { }; export default localize( PromotionHeader ); - diff --git a/client/extensions/woocommerce/app/settings/shipping/shipping-zone/shipping-zone-header.js b/client/extensions/woocommerce/app/settings/shipping/shipping-zone/shipping-zone-header.js index c6082fd57d7dd..892fd8fe0a629 100644 --- a/client/extensions/woocommerce/app/settings/shipping/shipping-zone/shipping-zone-header.js +++ b/client/extensions/woocommerce/app/settings/shipping/shipping-zone/shipping-zone-header.js @@ -47,7 +47,7 @@ const ShippingZoneHeader = ( { ]; return ( - + { showDelete && (