From 178135eea5ed6d68baa00ce602c9bc2eaaad71aa Mon Sep 17 00:00:00 2001 From: Paul Sealock Date: Thu, 20 Sep 2018 17:07:37 +1200 Subject: [PATCH] i18n: Advanced Filters strings --- .../client/analytics/report/orders/config.js | 196 +++++++++++------- .../components/filters/advanced/index.js | 121 +++++------ .../filters/advanced/search-filter.js | 65 ++++-- .../filters/advanced/select-filter.js | 78 ++++--- .../components/filters/advanced/style.scss | 77 +++---- .../client/components/filters/index.js | 7 +- .../client/components/filters/style.scss | 4 + .../client/components/search/index.js | 8 +- plugins/woocommerce-admin/package.json | 2 +- 9 files changed, 316 insertions(+), 242 deletions(-) diff --git a/plugins/woocommerce-admin/client/analytics/report/orders/config.js b/plugins/woocommerce-admin/client/analytics/report/orders/config.js index 3b37bb828aa..6e72f689eff 100644 --- a/plugins/woocommerce-admin/client/analytics/report/orders/config.js +++ b/plugins/woocommerce-admin/client/analytics/report/orders/config.js @@ -2,7 +2,7 @@ /** * External dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, _x } from '@wordpress/i18n'; /** * Internal dependencies @@ -30,87 +30,123 @@ export const filters = [ { label: __( 'Advanced Filters', 'wc-admin' ), value: 'advanced' }, ]; +/*eslint-disable max-len*/ export const advancedFilterConfig = { - status: { - labels: { - add: __( 'Order Status', 'wc-admin' ), - remove: __( 'Remove order status filter', 'wc-admin' ), - rule: __( 'Select an order status filter match', 'wc-admin' ), - title: __( 'Order Status', 'wc-admin' ), - }, - rules: [ - { value: 'is', label: __( 'Is', 'wc-admin' ) }, - { value: 'is_not', label: __( 'Is Not', 'wc-admin' ) }, - ], - input: { - component: 'SelectControl', - options: Object.keys( orderStatuses ).map( key => ( { - value: key, - label: orderStatuses[ key ], - } ) ), - defaultOption: 'wc-cancelled', - }, - }, - product_id: { - labels: { - add: __( 'Products', 'wc-admin' ), - placeholder: __( 'Search products', 'wc-admin' ), - remove: __( 'Remove products filter', 'wc-admin' ), - rule: __( 'Select a product filter match', 'wc-admin' ), - title: __( 'Product', 'wc-admin' ), - }, - rules: [ - { value: 'includes', label: __( 'Includes', 'wc-admin' ) }, - { value: 'excludes', label: __( 'Excludes', 'wc-admin' ) }, - { value: 'is', label: __( 'Is', 'wc-admin' ) }, - { value: 'is_not', label: __( 'Is Not', 'wc-admin' ) }, - ], - input: { - component: 'Search', - type: 'products', - getLabels: getRequestByIdString( NAMESPACE + 'products', product => ( { - id: product.id, - label: product.name, - } ) ), - }, - }, - code: { - labels: { - add: __( 'Coupon Codes', 'wc-admin' ), - placeholder: __( 'Search coupons', 'wc-admin' ), - remove: __( 'Remove coupon filter', 'wc-admin' ), - rule: __( 'Select a coupon filter match', 'wc-admin' ), - title: __( 'Coupon Code', 'wc-admin' ), - }, - rules: [ - { value: 'includes', label: __( 'Includes', 'wc-admin' ) }, - { value: 'excludes', label: __( 'Excludes', 'wc-admin' ) }, - { value: 'is', label: __( 'Is', 'wc-admin' ) }, - { value: 'is_not', label: __( 'Is Not', 'wc-admin' ) }, - ], - input: { - component: 'Search', - type: 'coupons', - getLabels: getRequestByIdString( NAMESPACE + 'coupons', coupon => ( { - id: coupon.id, - label: coupon.code, - } ) ), - }, - }, - customer: { - labels: { - add: __( 'Customer Type', 'wc-admin' ), - remove: __( 'Remove customer filter', 'wc-admin' ), - rule: __( 'Select a customer filter match', 'wc-admin' ), - title: __( 'Customer is', 'wc-admin' ), - }, - input: { - component: 'SelectControl', - options: [ - { value: 'new', label: __( 'New', 'wc-admin' ) }, - { value: 'returning', label: __( 'Returning', 'wc-admin' ) }, + title: _x( + 'Orders Match {{select /}} Filters', + 'A sentence describing filters for Orders. See screen shot for context: https://cloudup.com/cSsUY9VeCVJ', + 'wc-admin' + ), + filters: { + status: { + labels: { + add: __( 'Order Status', 'wc-admin' ), + remove: __( 'Remove order status filter', 'wc-admin' ), + rule: __( 'Select an order status filter match', 'wc-admin' ), + /* translators: A sentence describing an Order Status filter. See screen shot for context: https://cloudup.com/cSsUY9VeCVJ */ + title: __( 'Order Status {{rule /}} {{filter /}}', 'wc-admin' ), + filter: __( 'Select an order status', 'wc-admin' ), + }, + rules: [ + { + value: 'is', + /* translators: Sentence fragment, logical, "Is" refers to searching for orders matching a chosen order status. Screenshot for context: https://cloudup.com/cSsUY9VeCVJ */ + label: _x( 'Is', 'order status', 'wc-admin' ), + }, + { + value: 'is_not', + /* translators: Sentence fragment, logical, "Is Not" refers to searching for orders that don\'t match a chosen order status. Screenshot for context: https://cloudup.com/cSsUY9VeCVJ */ + label: _x( 'Is Not', 'order status', 'wc-admin' ), + }, ], - defaultOption: 'new', + input: { + component: 'SelectControl', + options: Object.keys( orderStatuses ).map( key => ( { + value: key, + label: orderStatuses[ key ], + } ) ), + }, + }, + product: { + labels: { + add: __( 'Products', 'wc-admin' ), + placeholder: __( 'Search products', 'wc-admin' ), + remove: __( 'Remove products filter', 'wc-admin' ), + rule: __( 'Select a product filter match', 'wc-admin' ), + /* translators: A sentence describing a Product filter. See screen shot for context: https://cloudup.com/cSsUY9VeCVJ */ + title: __( 'Product {{rule /}} {{filter /}}', 'wc-admin' ), + filter: __( 'Select products', 'wc-admin' ), + }, + rules: [ + { + value: 'includes', + /* translators: Sentence fragment, logical, "Includes" refers to orders including a given product(s). Screenshot for context: https://cloudup.com/cSsUY9VeCVJ */ + label: _x( 'Includes', 'products', 'wc-admin' ), + }, + { + value: 'excludes', + /* translators: Sentence fragment, logical, "Excludes" refers to orders excluding a given product(s). Screenshot for context: https://cloudup.com/cSsUY9VeCVJ */ + label: _x( 'Excludes', 'products', 'wc-admin' ), + }, + ], + input: { + component: 'Search', + type: 'products', + getLabels: getRequestByIdString( NAMESPACE + 'products', product => ( { + id: product.id, + label: product.name, + } ) ), + }, + }, + code: { + labels: { + add: __( 'Coupon Codes', 'wc-admin' ), + placeholder: __( 'Search coupons', 'wc-admin' ), + remove: __( 'Remove coupon filter', 'wc-admin' ), + rule: __( 'Select a coupon filter match', 'wc-admin' ), + /* translators: A sentence describing a Coupon filter. See screen shot for context: https://cloudup.com/cSsUY9VeCVJ */ + title: __( 'Coupon Code {{rule /}} {{filter /}}', 'wc-admin' ), + filter: __( 'Select coupon codes', 'wc-admin' ), + }, + rules: [ + { + value: 'includes', + /* translators: Sentence fragment, logical, "Includes" refers to orders including a given coupon code(s). Screenshot for context: https://cloudup.com/cSsUY9VeCVJ */ + label: _x( 'Includes', 'coupon code', 'wc-admin' ), + }, + { + value: 'excludes', + /* translators: Sentence fragment, logical, "Excludes" refers to orders excluding a given coupon code(s). Screenshot for context: https://cloudup.com/cSsUY9VeCVJ */ + label: _x( 'Excludes', 'coupon code', 'wc-admin' ), + }, + ], + input: { + component: 'Search', + type: 'coupons', + getLabels: getRequestByIdString( NAMESPACE + 'coupons', coupon => ( { + id: coupon.id, + label: coupon.code, + } ) ), + }, + }, + customer: { + labels: { + add: __( 'Customer Type', 'wc-admin' ), + remove: __( 'Remove customer filter', 'wc-admin' ), + rule: __( 'Select a customer filter match', 'wc-admin' ), + /* translators: A sentence describing a Customer filter. See screen shot for context: https://cloudup.com/cSsUY9VeCVJ */ + title: __( 'Customer is {{filter /}}', 'wc-admin' ), + filter: __( 'Select a customer type', 'wc-admin' ), + }, + input: { + component: 'SelectControl', + options: [ + { value: 'new', label: __( 'New', 'wc-admin' ) }, + { value: 'returning', label: __( 'Returning', 'wc-admin' ) }, + ], + defaultOption: 'new', + }, }, }, }; +/*eslint-enable max-len*/ diff --git a/plugins/woocommerce-admin/client/components/filters/advanced/index.js b/plugins/woocommerce-admin/client/components/filters/advanced/index.js index 6f61aa0c9ad..dceb00741fe 100644 --- a/plugins/woocommerce-admin/client/components/filters/advanced/index.js +++ b/plugins/woocommerce-admin/client/components/filters/advanced/index.js @@ -2,12 +2,13 @@ /** * External dependencies */ -import { __, sprintf } from '@wordpress/i18n'; -import { Component, Fragment, createRef } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { Component, createRef } from '@wordpress/element'; import { SelectControl, Button, Dropdown, IconButton } from '@wordpress/components'; import { partial, findIndex, difference } from 'lodash'; import PropTypes from 'prop-types'; import Gridicon from 'gridicons'; +import interpolateComponents from 'interpolate-components'; /** * Internal dependencies @@ -37,7 +38,7 @@ class AdvancedFilters extends Component { super( ...arguments ); this.state = { match: query.match || 'all', - activeFilters: getActiveFiltersFromQuery( query, config ), + activeFilters: getActiveFiltersFromQuery( query, config.filters ), }; this.filterListRef = createRef(); @@ -75,30 +76,31 @@ class AdvancedFilters extends Component { getTitle() { const { match } = this.state; - const { filterTitle } = this.props; - return ( - - { sprintf( __( '%s Match', 'wc-admin' ), filterTitle ) } - - { __( 'Filters', 'wc-admin' ) } - - ); + const { config } = this.props; + return interpolateComponents( { + mixedString: config.title, + components: { + select: ( + + ), + }, + } ); } getAvailableFilterKeys() { const { config } = this.props; const activeFilterKeys = this.state.activeFilters.map( f => f.key ); - return difference( Object.keys( config ), activeFilterKeys ); + return difference( Object.keys( config.filters ), activeFilterKeys ); } addFilter( key, onClose ) { - const filterConfig = this.props.config[ key ]; + const filterConfig = this.props.config.filters[ key ]; const newFilter = { key }; if ( Array.isArray( filterConfig.rules ) && filterConfig.rules.length ) { newFilter.rule = filterConfig.rules[ 0 ].value; @@ -131,46 +133,47 @@ class AdvancedFilters extends Component { getUpdateHref( activeFilters, matchValue ) { const { path, query, config } = this.props; - const updatedQuery = getQueryFromActiveFilters( activeFilters, query, config ); + const updatedQuery = getQueryFromActiveFilters( activeFilters, query, config.filters ); const match = matchValue === 'all' ? undefined : matchValue; return getNewPath( { ...updatedQuery, match }, path, query ); } + isEnglish() { + const { siteLocale } = wcSettings; + return /en-/.test( siteLocale ); + } + render() { const { config } = this.props; const { activeFilters, match } = this.state; const availableFilterKeys = this.getAvailableFilterKeys(); const updateHref = this.getUpdateHref( activeFilters, match ); const updateDisabled = window.location.hash && window.location.hash.substr( 1 ) === updateHref; + const isEnglish = this.isEnglish(); return (
    { activeFilters.map( filter => { const { key } = filter; - const { input, labels } = config[ key ]; + const { input, labels } = config.filters[ key ]; return (
  • - { /*eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex*/ } -
    - { /*eslint-enable-next-line jsx-a11y/no-noninteractive-tabindex*/ } - { labels.title } -
    - { 'SelectControl' === input.component && ( - - ) } - { 'Search' === input.component && ( - - ) } -
    -
    + { 'SelectControl' === input.component && ( + + ) } + { 'Search' === input.component && ( + + ) } (
  • ) ) } @@ -239,25 +242,25 @@ AdvancedFilters.propTypes = { /** * The configuration object required to render filters. */ - config: PropTypes.objectOf( - PropTypes.shape( { - labels: PropTypes.shape( { - add: PropTypes.string, - placeholder: PropTypes.string, - remove: PropTypes.string, - title: PropTypes.string, - } ), - rules: PropTypes.arrayOf( PropTypes.object ), - input: PropTypes.object, - } ) - ).isRequired, + config: PropTypes.shape( { + title: PropTypes.string, + filters: PropTypes.objectOf( + PropTypes.shape( { + labels: PropTypes.shape( { + add: PropTypes.string, + remove: PropTypes.string, + rule: PropTypes.string, + title: PropTypes.string, + filter: PropTypes.string, + } ), + rules: PropTypes.arrayOf( PropTypes.object ), + input: PropTypes.object, + } ) + ), + } ).isRequired, /** * Name of this filter, used in translations. */ - filterTitle: PropTypes.string.isRequired, - /** - * The `path` parameter supplied by React-Router. - */ path: PropTypes.string.isRequired, /** * The query string represented in object form. diff --git a/plugins/woocommerce-admin/client/components/filters/advanced/search-filter.js b/plugins/woocommerce-admin/client/components/filters/advanced/search-filter.js index 0d40e41d9e2..ef69c268fb2 100644 --- a/plugins/woocommerce-admin/client/components/filters/advanced/search-filter.js +++ b/plugins/woocommerce-admin/client/components/filters/advanced/search-filter.js @@ -2,11 +2,12 @@ /** * External dependencies */ -import { Component, Fragment } from '@wordpress/element'; +import { Component } from '@wordpress/element'; import { SelectControl } from '@wordpress/components'; -import { partial } from 'lodash'; +import { find, partial } from 'lodash'; import PropTypes from 'prop-types'; -import { withInstanceId } from '@wordpress/compose'; +import interpolateComponents from 'interpolate-components'; +import classnames from 'classnames'; /** * Internal dependencies @@ -41,40 +42,66 @@ class SearchFilter extends Component { onFilterChange( filter.key, 'value', idList ); } + getLegend( filter, config ) { + const { selected } = this.state; + const rule = find( config.rules, { value: filter.rule } ) || {}; + const filterStr = selected.map( item => item.label ).join( ', ' ); + + return interpolateComponents( { + mixedString: config.labels.title, + components: { + filter: { filterStr }, + rule: { rule.label }, + }, + } ); + } + render() { - const { config, filter, instanceId, onFilterChange } = this.props; + const { config, filter, onFilterChange, isEnglish } = this.props; const { selected } = this.state; const { key, rule } = filter; const { input, labels, rules } = config; - return ( - -
    - { labels.title } -
    - { rule && ( + const children = interpolateComponents( { + mixedString: labels.title, + components: { + rule: ( - ) } -
    + ), + filter: ( + ), + }, + } ); + /*eslint-disable jsx-a11y/no-noninteractive-tabindex*/ + return ( +
    + + { this.getLegend( filter, config, selected ) } + +
    + { children }
    - +
    ); + /*eslint-enable jsx-a11y/no-noninteractive-tabindex*/ } } @@ -105,4 +132,4 @@ SearchFilter.propTypes = { onFilterChange: PropTypes.func.isRequired, }; -export default withInstanceId( SearchFilter ); +export default SearchFilter; diff --git a/plugins/woocommerce-admin/client/components/filters/advanced/select-filter.js b/plugins/woocommerce-admin/client/components/filters/advanced/select-filter.js index a6b83aef79c..f88b971003b 100644 --- a/plugins/woocommerce-admin/client/components/filters/advanced/select-filter.js +++ b/plugins/woocommerce-admin/client/components/filters/advanced/select-filter.js @@ -2,11 +2,12 @@ /** * External dependencies */ -import { Component, Fragment } from '@wordpress/element'; +import { Component } from '@wordpress/element'; import { SelectControl, Spinner } from '@wordpress/components'; -import { partial } from 'lodash'; +import { find, partial } from 'lodash'; import PropTypes from 'prop-types'; -import { withInstanceId } from '@wordpress/compose'; +import interpolateComponents from 'interpolate-components'; +import classnames from 'classnames'; /** * Internal dependencies @@ -40,42 +41,62 @@ class SelectFilter extends Component { return options; } + getLegend( filter, config ) { + const rule = find( config.rules, { value: filter.rule } ) || {}; + const value = find( config.input.options, { value: filter.value } ) || {}; + return interpolateComponents( { + mixedString: config.labels.title, + components: { + filter: { value.label }, + rule: { rule.label }, + }, + } ); + } + render() { - const { config, filter, instanceId, onFilterChange } = this.props; + const { config, filter, onFilterChange, isEnglish } = this.props; const { options } = this.state; const { key, rule, value } = filter; const { labels, rules } = config; - return ( - -
    - { labels.title } -
    - { rule && ( + const children = interpolateComponents( { + mixedString: labels.title, + components: { + rule: ( - ) } -
    - { ! options && } - { options && ( - - ) } + ), + filter: options ? ( + + ) : ( + + ), + }, + } ); + /*eslint-disable jsx-a11y/no-noninteractive-tabindex*/ + return ( +
    + { this.getLegend( filter, config ) } +
    + { children }
    - +
    ); + /*eslint-enable jsx-a11y/no-noninteractive-tabindex*/ } } @@ -87,6 +108,7 @@ SelectFilter.propTypes = { labels: PropTypes.shape( { rule: PropTypes.string, title: PropTypes.string, + filter: PropTypes.string, } ), rules: PropTypes.arrayOf( PropTypes.object ), input: PropTypes.object, @@ -105,4 +127,4 @@ SelectFilter.propTypes = { onFilterChange: PropTypes.func.isRequired, }; -export default withInstanceId( SelectFilter ); +export default SelectFilter; diff --git a/plugins/woocommerce-admin/client/components/filters/advanced/style.scss b/plugins/woocommerce-admin/client/components/filters/advanced/style.scss index 14ad6a56405..842c2cd1613 100644 --- a/plugins/woocommerce-admin/client/components/filters/advanced/style.scss +++ b/plugins/woocommerce-admin/client/components/filters/advanced/style.scss @@ -41,17 +41,13 @@ border-bottom: 1px solid $core-grey-light-700; fieldset { - padding: $gap-smaller $gap; + padding: $gap-smaller $gap-smaller $gap-smaller $gap; } &:hover { background-color: $core-grey-light-200; } - .components-base-control { - margin: 0; - } - .woocommerce-filters-advanced__remove { width: 40px; height: 38px; @@ -85,22 +81,41 @@ } .woocommerce-filters-advanced__fieldset { - display: grid; - grid-template-columns: 100px 150px auto; + & > div { + padding: 0 $gap-smallest; + + @include breakpoint( '<782px' ) { + display: block; + margin: 0; + width: 100%; + padding: $gap-smallest 0; + } + } + + display: flex; + align-items: center; + white-space: nowrap; @include breakpoint( '<782px' ) { - display: flex; - flex-direction: column; + display: block; + } + + &.is-english { + display: grid; + grid-template-columns: 100px 150px auto; + + @include breakpoint( '<782px' ) { + display: block; + } } } -.woocommerce-filters-advanced__fieldset-legend { - align-self: center; +.woocommerce-filters-advanced__rule { + width: 150px; +} - @include breakpoint( '<782px' ) { - align-self: initial; - padding: $gap-smallest 0; - } +.woocommerce-filters-advanced__input { + width: 100%; } .woocommerce-filters-advanced__add-filter-dropdown { @@ -156,35 +171,3 @@ } } } - -.woocommerce-filters-advanced__list-selector { - @include breakpoint( '<782px' ) { - padding: $gap-smallest 0; - } - - .components-spinner { - margin: auto; - display: block; - float: none; - top: 50%; - transform: translateY(-50%); - - @include breakpoint( '<782px' ) { - transform: none; - } - } -} - -.woocommerce-filters-advanced__list-specifier { - @include breakpoint( '<782px' ) { - padding: $gap-smallest 0; - } - - & + .woocommerce-filters-advanced__list-selector { - padding: 0 0 0 $gap-smaller; - - @include breakpoint( '<782px' ) { - padding: $gap-smallest 0; - } - } -} diff --git a/plugins/woocommerce-admin/client/components/filters/index.js b/plugins/woocommerce-admin/client/components/filters/index.js index a196b80ccf8..8f679513d3d 100644 --- a/plugins/woocommerce-admin/client/components/filters/index.js +++ b/plugins/woocommerce-admin/client/components/filters/index.js @@ -45,12 +45,7 @@ class ReportFilters extends Component { if ( 'advanced' === query.filter ) { return (
    - +
    ); } diff --git a/plugins/woocommerce-admin/client/components/filters/style.scss b/plugins/woocommerce-admin/client/components/filters/style.scss index a00f82a032f..2858f405fa4 100644 --- a/plugins/woocommerce-admin/client/components/filters/style.scss +++ b/plugins/woocommerce-admin/client/components/filters/style.scss @@ -17,6 +17,10 @@ } } } + + .components-base-control__field { + margin-bottom: 0; + } } .woocommerce-filters-date, diff --git a/plugins/woocommerce-admin/client/components/search/index.js b/plugins/woocommerce-admin/client/components/search/index.js index dd5d8a06ca7..19dddfed2cf 100644 --- a/plugins/woocommerce-admin/client/components/search/index.js +++ b/plugins/woocommerce-admin/client/components/search/index.js @@ -114,14 +114,14 @@ class Search extends Component { render() { const autocompleter = this.getAutocompleter(); - const { placeholder, inlineTags, selected, instanceId } = this.props; + const { placeholder, inlineTags, selected, instanceId, className } = this.props; const { value = '', isActive } = this.state; const aria = { 'aria-labelledby': this.props[ 'aria-labelledby' ], 'aria-label': this.props[ 'aria-label' ], }; return ( -
    +
    { ( { listBoxId, activeId, onChange } ) => @@ -187,6 +187,10 @@ class Search extends Component { } Search.propTypes = { + /** + * Class name applied to parent div. + */ + className: PropTypes.string, /** * Function called when selected results change, passed result list. */ diff --git a/plugins/woocommerce-admin/package.json b/plugins/woocommerce-admin/package.json index 08ceb984a8a..9f8719e0dec 100755 --- a/plugins/woocommerce-admin/package.json +++ b/plugins/woocommerce-admin/package.json @@ -78,8 +78,8 @@ "history": "^4.7.2", "html-to-react": "^1.3.3", "husky": "^0.14.3", - "interpolate-components": "1.1.1", "marked": "^0.5.0", + "interpolate-components": "^1.1.1", "node-sass": "^4.9.3", "postcss-color-function": "^4.0.1", "postcss-loader": "^3.0.0",