Merge branch 'master' into fix/749
This commit is contained in:
commit
16d5265aed
|
@ -5,6 +5,7 @@
|
|||
"at-rule-no-unknown": null,
|
||||
"comment-empty-line-before": null,
|
||||
"declaration-block-no-duplicate-properties": null,
|
||||
"declaration-colon-newline-after": null,
|
||||
"declaration-property-unit-whitelist": null,
|
||||
"font-weight-notation": null,
|
||||
"max-line-length": null,
|
||||
|
@ -12,6 +13,8 @@
|
|||
"no-duplicate-selectors": null,
|
||||
"rule-empty-line-before": null,
|
||||
"selector-class-pattern": null,
|
||||
"value-keyword-case": null
|
||||
"string-quotes": "single",
|
||||
"value-keyword-case": null,
|
||||
"value-list-comma-newline-after": null
|
||||
}
|
||||
}
|
|
@ -1,33 +1,36 @@
|
|||
sudo: required
|
||||
|
||||
language: php
|
||||
php:
|
||||
- 7.1
|
||||
|
||||
env:
|
||||
- WP_VERSION=latest WP_MULTISITE=0 RUN_PHPCS=1 WP_CORE_DIR=/tmp/wordpress NODE_RELEASE=8.x
|
||||
matrix:
|
||||
include:
|
||||
- name: "PHP 7.2 unit tests, PHP Coding standards check and JS tests"
|
||||
php: 7.2
|
||||
env: WP_VERSION=latest WP_MULTISITE=0 WP_CORE_DIR=/tmp/wordpress NODE_RELEASE=8.x RUN_PHPCS=1 RUN_JS=1
|
||||
- name: "PHP 7.1 unit tests"
|
||||
php: 7.1
|
||||
env: WP_VERSION=latest WP_MULTISITE=0 WP_CORE_DIR=/tmp/wordpress NODE_RELEASE=8.x
|
||||
- name: "PHP 7.0 unit tests"
|
||||
php: 7.0
|
||||
env: WP_VERSION=latest WP_MULTISITE=0 WP_CORE_DIR=/tmp/wordpress NODE_RELEASE=8.x
|
||||
- name: "PHP 5.6 unit tests"
|
||||
php: 5.6
|
||||
env: WP_VERSION=latest WP_MULTISITE=0 WP_CORE_DIR=/tmp/wordpress NODE_RELEASE=8.x
|
||||
|
||||
before_script:
|
||||
- phpenv config-rm xdebug.ini
|
||||
- export PATH="$HOME/.composer/vendor/bin:$PATH"
|
||||
- bash bin/install-wp-tests.sh wc_admin_test root '' localhost $WP_VERSION
|
||||
- bash bin/travis.sh before
|
||||
- sudo rm -rf ~/.nvm
|
||||
- curl -sL "https://deb.nodesource.com/setup_${NODE_RELEASE}" | sudo -E bash -
|
||||
- sudo apt-get install -y nodejs
|
||||
- node --version
|
||||
- npm --version
|
||||
|
||||
|
||||
install:
|
||||
- npm install
|
||||
|
||||
node_js:
|
||||
- "8"
|
||||
|
||||
before_script:
|
||||
- phpenv config-rm xdebug.ini
|
||||
- export PATH="$WP_CORE_DIR/wp-content/plugins/wc-admin/vendor/bin:$PATH"
|
||||
- bash bin/install-wp-tests.sh wc_admin_test root '' localhost $WP_VERSION
|
||||
- bash bin/travis.sh before
|
||||
- node --version
|
||||
- npm --version
|
||||
- timedatectl
|
||||
|
||||
script:
|
||||
- npm run lint
|
||||
- npm run build
|
||||
- npm test
|
||||
- bash bin/js_lint_test.sh
|
||||
- bash bin/phpunit.sh
|
||||
- bash bin/phpcs.sh
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# Contributing to WooCommerce Admin
|
||||
|
||||
Hi! Thank you for your interest in contributing to WooCommerce Admin. We appreciate it.
|
||||
|
||||
There are many ways to contribute – reporting bugs, feature suggestions, and fixing bugs.
|
||||
|
||||
## Reporting Bugs, Asking Questions, Sending Suggestions
|
||||
|
||||
Open [a GitHub issue](https://github.com/woocommerce/wc-admin/issues/new/choose), that's all. If you have write access, add any appropriate labels.
|
||||
|
||||
If you're filing a bug, specific steps to reproduce are always helpful. Please include what you expected to see and what happened instead.
|
||||
|
||||
## We're Here To Help
|
||||
|
||||
We encourage you to ask for help. We want your first experience with WooCommerce Admin to be a good one, so don't be shy. If you're wondering why something is the way it is, or how a decision was made, you can tag issues with [Type] Question or prefix them with “Question:”
|
||||
|
||||
## License
|
||||
|
||||
WooCommerce Admin is licensed under [GNU General Public License v2 (or later)](/LICENSE.md).
|
||||
|
||||
All materials contributed should be compatible with the GPLv2. This means that if you own the material, you agree to license it under the GPLv2 license. If you are contributing code that is not your own, such as adding a component from another Open Source project, or adding an `npm` package, you need to make sure you follow these steps:
|
||||
|
||||
1. Check that the code has a license. If you can't find one, you can try to contact the original author and get permission to use, or ask them to release under a compatible Open Source license.
|
||||
2. Check the license is compatible with [GPLv2](http://www.gnu.org/licenses/license-list.en.html#GPLCompatibleLicenses), note that the Apache 2.0 license is *not* compatible.
|
|
@ -20,6 +20,7 @@ After cloning the repo, install dependencies with `npm install`. Now you can bui
|
|||
|
||||
- `npm run build` : Build a production version
|
||||
- `npm start` : Build a development version, watch files for changes
|
||||
- `npm run build:release` : Build a WordPress plugin ZIP file (`wc-admin.zip` will be created in the repository root)
|
||||
|
||||
There are also some helper scripts:
|
||||
|
||||
|
@ -29,3 +30,7 @@ There are also some helper scripts:
|
|||
## Privacy
|
||||
|
||||
If you have enabled WooCommerce usage tracking ( option `woocommerce_allow_tracking` ) then, in addition to the tracking described in https://woocommerce.com/usage-tracking/, this plugin also sends information about the actions that site administrators perform to Automattic - see https://automattic.com/privacy/#information-we-collect-automatically for more information.
|
||||
|
||||
## Contributing
|
||||
|
||||
There are many ways to contribute – reporting bugs, feature suggestions and fixing bugs. For full details, please see [CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Exit if any command fails.
|
||||
set -e
|
||||
|
||||
# Change to the expected directory.
|
||||
cd "$(dirname "$0")"
|
||||
cd ..
|
||||
|
||||
# Enable nicer messaging for build status.
|
||||
BLUE_BOLD='\033[1;34m';
|
||||
GREEN_BOLD='\033[1;32m';
|
||||
RED_BOLD='\033[1;31m';
|
||||
YELLOW_BOLD='\033[1;33m';
|
||||
COLOR_RESET='\033[0m';
|
||||
error () {
|
||||
echo -e "\n${RED_BOLD}$1${COLOR_RESET}\n"
|
||||
}
|
||||
status () {
|
||||
echo -e "\n${BLUE_BOLD}$1${COLOR_RESET}\n"
|
||||
}
|
||||
success () {
|
||||
echo -e "\n${GREEN_BOLD}$1${COLOR_RESET}\n"
|
||||
}
|
||||
warning () {
|
||||
echo -e "\n${YELLOW_BOLD}$1${COLOR_RESET}\n"
|
||||
}
|
||||
|
||||
status "💃 Time to release WooCommerce Admin 🕺"
|
||||
|
||||
# Make sure there are no changes in the working tree. Release builds should be
|
||||
# traceable to a particular commit and reliably reproducible. (This is not
|
||||
# totally true at the moment because we download nightly vendor scripts).
|
||||
changed=
|
||||
if ! git diff --exit-code > /dev/null; then
|
||||
changed="file(s) modified"
|
||||
elif ! git diff --cached --exit-code > /dev/null; then
|
||||
changed="file(s) staged"
|
||||
fi
|
||||
if [ ! -z "$changed" ]; then
|
||||
git status
|
||||
error "ERROR: Cannot build plugin zip with dirty working tree. ☝️
|
||||
Commit your changes and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Do a dry run of the repository reset. Prompting the user for a list of all
|
||||
# files that will be removed should prevent them from losing important files!
|
||||
status "Resetting the repository to pristine condition. ✨"
|
||||
git clean -xdf --dry-run
|
||||
warning "🚨 About to delete everything above! Is this okay? 🚨"
|
||||
echo -n "[y]es/[N]o: "
|
||||
read answer
|
||||
if [ "$answer" != "${answer#[Yy]}" ]; then
|
||||
# Remove ignored files to reset repository to pristine condition. Previous
|
||||
# test ensures that changed files abort the plugin build.
|
||||
status "Cleaning working directory... 🛀"
|
||||
git clean -xdf
|
||||
else
|
||||
error "Fair enough; aborting. Tidy up your repo and try again. 🙂"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run the build.
|
||||
status "Generating build... 👷♀️"
|
||||
npm run build
|
||||
npm run docs
|
||||
|
||||
build_files=$(ls dist/*/*.{js,css})
|
||||
|
||||
# Generate the plugin zip file.
|
||||
status "Creating archive... 🎁"
|
||||
zip -r wc-admin.zip \
|
||||
wc-admin.php \
|
||||
lib/*.php \
|
||||
includes/*.php \
|
||||
includes/**/*.php \
|
||||
images/* \
|
||||
$build_files \
|
||||
languages/wc-admin.pot \
|
||||
languages/wc-admin.php \
|
||||
README.md
|
||||
|
||||
success "Done. You've built WooCommerce Admin! 🎉 "
|
|
@ -13,6 +13,7 @@ const { parse, resolver } = require( 'react-docgen' );
|
|||
const { getDescription, getProps, getTitle } = require( './lib/formatting' );
|
||||
const {
|
||||
COMPONENTS_FOLDER,
|
||||
PACKAGES_FOLDER,
|
||||
deleteExistingDocs,
|
||||
getExportedFileList,
|
||||
getMdFileName,
|
||||
|
@ -20,13 +21,14 @@ const {
|
|||
writeTableOfContents,
|
||||
} = require( './lib/file-system' );
|
||||
|
||||
const filePath = path.resolve( COMPONENTS_FOLDER, 'index.js' );
|
||||
|
||||
// Start by wiping the existing docs. **Change this if we end up manually editing docs**
|
||||
deleteExistingDocs();
|
||||
|
||||
// Read components file to get a list of exported files, convert that to a list of absolute paths to public components.
|
||||
const files = getRealFilePaths( getExportedFileList( filePath ) );
|
||||
const files = [
|
||||
...getRealFilePaths( getExportedFileList( path.resolve( COMPONENTS_FOLDER, 'index.js' ) ) ),
|
||||
...getRealFilePaths( getExportedFileList( path.resolve( PACKAGES_FOLDER, 'index.js' ) ), PACKAGES_FOLDER ),
|
||||
];
|
||||
|
||||
// Build the documentation by reading each file.
|
||||
files.forEach( file => {
|
||||
|
|
|
@ -15,6 +15,7 @@ const { namedTypes } = types;
|
|||
const { camelCaseDash } = require( './formatting' );
|
||||
|
||||
const COMPONENTS_FOLDER = path.resolve( __dirname, '../../../client/components/' );
|
||||
const PACKAGES_FOLDER = path.resolve( __dirname, '../../../packages/components/src/' );
|
||||
const DOCS_FOLDER = path.resolve( __dirname, '../../../docs/components/' );
|
||||
|
||||
/**
|
||||
|
@ -78,7 +79,7 @@ function getMdFileName( filepath, absolute = true ) {
|
|||
if ( ! fileParts || ! fileParts[ 1 ] ) {
|
||||
return;
|
||||
}
|
||||
const name = fileParts[ 1 ].split( '/' )[ 0 ];
|
||||
const name = fileParts[ 1 ].replace( 'src/', '' ).split( '/' )[ 0 ];
|
||||
if ( ! absolute ) {
|
||||
return name + '.md';
|
||||
}
|
||||
|
@ -148,7 +149,7 @@ function isFile( file ) {
|
|||
* @param { array } files A list of readme files.
|
||||
*/
|
||||
function writeTableOfContents( files ) {
|
||||
const mdFiles = files.map( f => getMdFileName( f, false ) );
|
||||
const mdFiles = files.map( f => getMdFileName( f, false ) ).sort();
|
||||
const TOC = uniq( mdFiles ).map( doc => {
|
||||
const name = camelCaseDash( doc.replace( '.md', '' ) );
|
||||
return ` * [${ name }](components/${ doc })`;
|
||||
|
@ -161,6 +162,7 @@ function writeTableOfContents( files ) {
|
|||
module.exports = {
|
||||
COMPONENTS_FOLDER,
|
||||
DOCS_FOLDER,
|
||||
PACKAGES_FOLDER,
|
||||
deleteExistingDocs,
|
||||
getExportedFileList,
|
||||
getMdFileName,
|
||||
|
|
|
@ -174,12 +174,14 @@ install_deps() {
|
|||
# Install WooCommerce
|
||||
cd "wp-content/plugins/"
|
||||
# As zip file does not include tests, we have to get it from git repo.
|
||||
git clone https://github.com/woocommerce/woocommerce.git
|
||||
git clone --depth 1 https://github.com/woocommerce/woocommerce.git
|
||||
cd "$WP_CORE_DIR"
|
||||
php wp-cli.phar plugin activate woocommerce
|
||||
|
||||
# Install wc-admin, the correct branch
|
||||
if [ "$TRAVIS_PULL_REQUEST_BRANCH" != "" ]; then
|
||||
# Install wc-admin, the correct branch, if running from Travis CI.
|
||||
php wp-cli.phar plugin install https://github.com/$REPO/archive/$BRANCH.zip --activate
|
||||
fi
|
||||
|
||||
# Back to original dir
|
||||
cd "$WORKING_DIR"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if [[ ${RUN_JS} == 1 ]]; then
|
||||
npm run lint
|
||||
npm run build
|
||||
npm test
|
||||
fi
|
|
@ -121,16 +121,14 @@ function buildScssFile( styleFile ) {
|
|||
mkdirp.sync( path.dirname( outputFile ) );
|
||||
const builtSass = sass.renderSync( {
|
||||
file: styleFile,
|
||||
includePaths: [ path.resolve( __dirname, '../../assets/stylesheets' ) ],
|
||||
includePaths: [ path.resolve( __dirname, '../../client/stylesheets/abstracts' ) ],
|
||||
data: (
|
||||
[
|
||||
'colors',
|
||||
'breakpoints',
|
||||
'variables',
|
||||
'breakpoints',
|
||||
'mixins',
|
||||
'animations',
|
||||
'z-index',
|
||||
].map( ( imported ) => `@import "${ imported }";` ).join( ' ' ) +
|
||||
].map( ( imported ) => `@import "_${ imported }";` ).join( ' ' ) +
|
||||
fs.readFileSync( styleFile, 'utf8' )
|
||||
),
|
||||
} );
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = [
|
||||
require( './node_modules/@wordpress/postcss-themes' )( {
|
||||
require( '@wordpress/postcss-themes' )( {
|
||||
defaults: {
|
||||
primary: '#0085ba',
|
||||
secondary: '#11a0d2',
|
||||
|
|
|
@ -5,6 +5,7 @@ if [[ ${RUN_PHPCS} == 1 ]]; then
|
|||
|
||||
if [ "$CHANGED_FILES" != "" ]; then
|
||||
echo "Running Code Sniffer."
|
||||
cd "$WP_CORE_DIR/wp-content/plugins/wc-admin/"
|
||||
./vendor/bin/phpcs --encoding=utf-8 -n -p $CHANGED_FILES
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
WORKING_DIR="$PWD"
|
||||
cd "/tmp/wordpress/wp-content/plugins/wc-admin/"
|
||||
cd "$WP_CORE_DIR/wp-content/plugins/wc-admin/"
|
||||
phpunit --version
|
||||
phpunit -c phpunit.xml.dist
|
||||
TEST_RESULT=$?
|
||||
cd "$WORKING_DIR"
|
||||
exit $TEST_RESULT
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
if [ $1 == 'before' ]; then
|
||||
|
||||
composer global require "phpunit/phpunit=6.*"
|
||||
|
||||
if [[ ${RUN_PHPCS} == 1 ]]; then
|
||||
cd "$WP_CORE_DIR/wp-content/plugins/wc-admin/"
|
||||
# This can (currently) only run for PHP 7.1+
|
||||
composer install
|
||||
fi
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import PropTypes from 'prop-types';
|
|||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { Chart, ChartPlaceholder } from '@woocommerce/components';
|
||||
import {
|
||||
getAllowedIntervalsForQuery,
|
||||
getCurrentDates,
|
||||
|
@ -24,12 +23,21 @@ import {
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Chart, ChartPlaceholder } from 'components';
|
||||
import { getReportChartData } from 'store/reports/utils';
|
||||
import ReportError from 'analytics/components/report-error';
|
||||
|
||||
class ReportChart extends Component {
|
||||
render() {
|
||||
const { path, primaryData, secondaryData, selectedChart, query } = this.props;
|
||||
const {
|
||||
comparisonChart,
|
||||
query,
|
||||
itemsLabel,
|
||||
path,
|
||||
primaryData,
|
||||
secondaryData,
|
||||
selectedChart,
|
||||
} = this.props;
|
||||
|
||||
if ( primaryData.isError || secondaryData.isError ) {
|
||||
return <ReportError isError />;
|
||||
|
@ -84,7 +92,9 @@ class ReportChart extends Component {
|
|||
title={ selectedChart.label }
|
||||
interval={ currentInterval }
|
||||
allowedIntervals={ allowedIntervals }
|
||||
mode="time-comparison"
|
||||
itemsLabel={ itemsLabel }
|
||||
layout={ comparisonChart ? 'comparison' : 'standard' }
|
||||
mode={ comparisonChart ? 'item-comparison' : 'time-comparison' }
|
||||
pointLabelFormat={ formats.pointLabelFormat }
|
||||
tooltipTitle={ selectedChart.label }
|
||||
xFormat={ formats.xFormat }
|
||||
|
@ -97,6 +107,8 @@ class ReportChart extends Component {
|
|||
}
|
||||
|
||||
ReportChart.propTypes = {
|
||||
comparisonChart: PropTypes.bool,
|
||||
itemsLabel: PropTypes.string,
|
||||
path: PropTypes.string.isRequired,
|
||||
primaryData: PropTypes.object.isRequired,
|
||||
query: PropTypes.object.isRequired,
|
||||
|
|
|
@ -7,14 +7,14 @@ import { Component } from '@wordpress/element';
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { EmptyContent } from '@woocommerce/components';
|
||||
import { getAdminLink } from 'lib/nav-utils';
|
||||
import { getAdminLink } from '@woocommerce/navigation';
|
||||
|
||||
class ReportError extends Component {
|
||||
render() {
|
||||
const { isError, isEmpty } = this.props;
|
||||
const { className, isError, isEmpty } = this.props;
|
||||
let title, actionLabel, actionURL, actionCallback;
|
||||
|
||||
if ( isError ) {
|
||||
|
@ -31,6 +31,7 @@ class ReportError extends Component {
|
|||
}
|
||||
return (
|
||||
<EmptyContent
|
||||
className={ className }
|
||||
title={ title }
|
||||
actionLabel={ actionLabel }
|
||||
actionURL={ actionURL }
|
||||
|
@ -41,8 +42,13 @@ class ReportError extends Component {
|
|||
}
|
||||
|
||||
ReportError.propTypes = {
|
||||
className: PropTypes.string,
|
||||
isError: PropTypes.bool,
|
||||
isEmpty: PropTypes.bool,
|
||||
};
|
||||
|
||||
ReportError.defaultProps = {
|
||||
className: '',
|
||||
};
|
||||
|
||||
export default ReportError;
|
||||
|
|
|
@ -14,12 +14,12 @@ import PropTypes from 'prop-types';
|
|||
*/
|
||||
import { formatCurrency } from '@woocommerce/currency';
|
||||
import { getDateParamsFromQuery } from '@woocommerce/date';
|
||||
import { getNewPath } from '@woocommerce/navigation';
|
||||
import { SummaryList, SummaryListPlaceholder, SummaryNumber } from '@woocommerce/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getNewPath } from 'lib/nav-utils';
|
||||
import { getSummaryNumbers } from 'store/reports/utils';
|
||||
import { numberFormat } from 'lib/number';
|
||||
import ReportError from 'analytics/components/report-error';
|
||||
|
|
|
@ -8,6 +8,7 @@ import { Component, Fragment } from '@wordpress/element';
|
|||
* Internal dependencies
|
||||
*/
|
||||
import { filters } from './config';
|
||||
import CouponsReportTable from './table';
|
||||
import { ReportFilters } from '@woocommerce/components';
|
||||
|
||||
export default class extends Component {
|
||||
|
@ -17,6 +18,7 @@ export default class extends Component {
|
|||
return (
|
||||
<Fragment>
|
||||
<ReportFilters query={ query } path={ path } filters={ filters } />
|
||||
<CouponsReportTable query={ query } />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, _n } from '@wordpress/i18n';
|
||||
import { Component } from '@wordpress/element';
|
||||
import { format as formatDate } from '@wordpress/date';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { withSelect } from '@wordpress/data';
|
||||
import { get, map, orderBy } from 'lodash';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import {
|
||||
appendTimestamp,
|
||||
getCurrentDates,
|
||||
getIntervalForQuery,
|
||||
getDateFormatsForInterval,
|
||||
} from '@woocommerce/date';
|
||||
import { Link, TableCard } from '@woocommerce/components';
|
||||
import { formatCurrency, getCurrencyFormatDecimal } from '@woocommerce/currency';
|
||||
import { onQueryChange } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ReportError from 'analytics/components/report-error';
|
||||
import { QUERY_DEFAULTS } from 'store/constants';
|
||||
import { getReportChartData, getFilterQuery } from 'store/reports/utils';
|
||||
|
||||
class CouponsReportTable extends Component {
|
||||
getHeadersContent() {
|
||||
return [
|
||||
{
|
||||
label: __( 'Coupon Code', 'wc-admin' ),
|
||||
// @TODO it should be the coupon code, not the coupon ID
|
||||
key: 'coupon_id',
|
||||
required: true,
|
||||
isLeftAligned: true,
|
||||
isSortable: true,
|
||||
},
|
||||
{
|
||||
label: __( 'Orders', 'wc-admin' ),
|
||||
key: 'orders_count',
|
||||
required: true,
|
||||
defaultSort: true,
|
||||
isSortable: true,
|
||||
isNumeric: true,
|
||||
},
|
||||
{
|
||||
label: __( 'G. Discounted', 'wc-admin' ),
|
||||
key: 'gross_discount',
|
||||
isSortable: true,
|
||||
isNumeric: true,
|
||||
},
|
||||
{
|
||||
label: __( 'Created', 'wc-admin' ),
|
||||
key: 'created',
|
||||
isSortable: true,
|
||||
},
|
||||
{
|
||||
label: __( 'Expires', 'wc-admin' ),
|
||||
key: 'expires',
|
||||
isSortable: true,
|
||||
},
|
||||
{
|
||||
label: __( 'Type', 'wc-admin' ),
|
||||
key: 'type',
|
||||
isSortable: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
getRowsContent( coupons ) {
|
||||
const { query } = this.props;
|
||||
const currentInterval = getIntervalForQuery( query );
|
||||
const { tableFormat } = getDateFormatsForInterval( currentInterval );
|
||||
|
||||
return map( coupons, coupon => {
|
||||
const { coupon_id, gross_discount, orders_count } = coupon;
|
||||
|
||||
// @TODO must link to the coupon detail report
|
||||
const couponLink = (
|
||||
<Link href="" type="wc-admin">
|
||||
{ coupon_id }
|
||||
</Link>
|
||||
);
|
||||
|
||||
const ordersLink = (
|
||||
<Link
|
||||
href={ '/analytics/orders?filter=advanced&code_includes=' + coupon_id }
|
||||
type="wc-admin"
|
||||
>
|
||||
{ orders_count }
|
||||
</Link>
|
||||
);
|
||||
|
||||
return [
|
||||
// @TODO it should be the coupon code, not the coupon ID
|
||||
{
|
||||
display: couponLink,
|
||||
value: coupon_id,
|
||||
},
|
||||
{
|
||||
display: ordersLink,
|
||||
value: orders_count,
|
||||
},
|
||||
{
|
||||
display: formatCurrency( gross_discount ),
|
||||
value: getCurrencyFormatDecimal( gross_discount ),
|
||||
},
|
||||
{
|
||||
// @TODO
|
||||
display: formatDate( tableFormat, '' ),
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
// @TODO
|
||||
display: formatDate( tableFormat, '' ),
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
// @TODO
|
||||
display: '',
|
||||
value: '',
|
||||
},
|
||||
];
|
||||
} );
|
||||
}
|
||||
|
||||
getSummary( totals ) {
|
||||
if ( ! totals ) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
{
|
||||
label: _n( 'coupon', 'coupons', totals.coupons_count, 'wc-admin' ),
|
||||
value: totals.coupons_count,
|
||||
},
|
||||
{
|
||||
label: _n( 'order', 'orders', totals.orders_count, 'wc-admin' ),
|
||||
value: totals.orders_count,
|
||||
},
|
||||
{
|
||||
label: __( 'gross discounted', 'wc-admin' ),
|
||||
value: formatCurrency( totals.gross_discount ),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
const { coupons, isTableDataError, isTableDataRequesting, primaryData, query } = this.props;
|
||||
|
||||
const isError = isTableDataError || primaryData.isError;
|
||||
|
||||
if ( isError ) {
|
||||
return <ReportError isError />;
|
||||
}
|
||||
|
||||
const isRequesting = isTableDataRequesting || primaryData.isRequesting;
|
||||
|
||||
const tableQuery = {
|
||||
...query,
|
||||
orderby: query.orderby || 'date',
|
||||
order: query.order || 'asc',
|
||||
};
|
||||
|
||||
const headers = this.getHeadersContent();
|
||||
const orderedCoupons = orderBy( coupons, tableQuery.orderby, tableQuery.order );
|
||||
const rows = this.getRowsContent( orderedCoupons );
|
||||
const rowsPerPage = parseInt( tableQuery.per_page ) || QUERY_DEFAULTS.pageSize;
|
||||
const totalRows = get( primaryData, [ 'data', 'totals', 'coupons_count' ], coupons.length );
|
||||
const summary = primaryData.data.totals ? this.getSummary( primaryData.data.totals ) : null;
|
||||
|
||||
return (
|
||||
<TableCard
|
||||
title={ __( 'Coupons', 'wc-admin' ) }
|
||||
compareBy={ 'coupons' }
|
||||
ids={ orderedCoupons.map( coupon => coupon.coupon_id ) }
|
||||
rows={ rows }
|
||||
totalRows={ totalRows }
|
||||
rowsPerPage={ rowsPerPage }
|
||||
headers={ headers }
|
||||
isLoading={ isRequesting }
|
||||
onQueryChange={ onQueryChange }
|
||||
query={ tableQuery }
|
||||
summary={ summary }
|
||||
downloadable
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSelect( ( select, props ) => {
|
||||
const { query } = props;
|
||||
const datesFromQuery = getCurrentDates( query );
|
||||
const primaryData = getReportChartData( 'coupons', 'primary', query, select );
|
||||
const filterQuery = getFilterQuery( 'coupons', query );
|
||||
|
||||
const { getCoupons, isGetCouponsError, isGetCouponsRequesting } = select( 'wc-admin' );
|
||||
const tableQuery = {
|
||||
orderby: query.orderby || 'date',
|
||||
order: query.order || 'asc',
|
||||
page: query.page || 1,
|
||||
per_page: query.per_page || QUERY_DEFAULTS.pageSize,
|
||||
after: appendTimestamp( datesFromQuery.primary.after, 'start' ),
|
||||
before: appendTimestamp( datesFromQuery.primary.before, 'end' ),
|
||||
...filterQuery,
|
||||
};
|
||||
const coupons = getCoupons( tableQuery );
|
||||
const isTableDataError = isGetCouponsError( tableQuery );
|
||||
const isTableDataRequesting = isGetCouponsRequesting( tableQuery );
|
||||
|
||||
return {
|
||||
isTableDataError,
|
||||
isTableDataRequesting,
|
||||
coupons,
|
||||
primaryData,
|
||||
};
|
||||
} )
|
||||
)( CouponsReportTable );
|
|
@ -8,6 +8,11 @@ import { Component, Fragment } from '@wordpress/element';
|
|||
import PropTypes from 'prop-types';
|
||||
import { find } from 'lodash';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { useFilters } from '@woocommerce/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
@ -17,7 +22,7 @@ import OrdersReport from './orders';
|
|||
import ProductsReport from './products';
|
||||
import RevenueReport from './revenue';
|
||||
import CouponsReport from './coupons';
|
||||
import useFilters from 'components/higher-order/use-filters';
|
||||
import TaxesReport from './taxes';
|
||||
|
||||
const REPORTS_FILTER = 'woocommerce-reports-list';
|
||||
|
||||
|
@ -43,6 +48,11 @@ const getReports = () => {
|
|||
title: __( 'Coupons', 'wc-admin' ),
|
||||
component: CouponsReport,
|
||||
},
|
||||
{
|
||||
report: 'taxes',
|
||||
title: __( 'Taxes', 'wc-admin' ),
|
||||
component: TaxesReport,
|
||||
},
|
||||
] );
|
||||
|
||||
return reports;
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Component, Fragment } from '@wordpress/element';
|
||||
import { find } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ReportChart from 'analytics/components/report-chart';
|
||||
import ReportSummary from 'analytics/components/report-summary';
|
||||
|
||||
class OrdersReportChart extends Component {
|
||||
getCharts() {
|
||||
return [
|
||||
{
|
||||
key: 'orders_count',
|
||||
label: __( 'Orders Count', 'wc-admin' ),
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
key: 'net_revenue',
|
||||
label: __( 'Net Revenue', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'avg_order_value',
|
||||
label: __( 'Average Order Value', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'avg_items_per_order',
|
||||
label: __( 'Average Items Per Order', 'wc-admin' ),
|
||||
type: 'average',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
getSelectedChart() {
|
||||
const { query } = this.props;
|
||||
const charts = this.getCharts();
|
||||
const chart = find( charts, { key: query.chart } );
|
||||
if ( chart ) {
|
||||
return chart;
|
||||
}
|
||||
|
||||
return charts[ 0 ];
|
||||
}
|
||||
|
||||
render() {
|
||||
const { path, query } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
<ReportSummary
|
||||
charts={ this.getCharts() }
|
||||
endpoint="orders"
|
||||
query={ query }
|
||||
selectedChart={ this.getSelectedChart() }
|
||||
/>
|
||||
<ReportChart
|
||||
charts={ this.getCharts() }
|
||||
endpoint="orders"
|
||||
path={ path }
|
||||
query={ query }
|
||||
selectedChart={ this.getSelectedChart() }
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
OrdersReportChart.propTypes = {
|
||||
path: PropTypes.string.isRequired,
|
||||
query: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default OrdersReportChart;
|
|
@ -12,6 +12,29 @@ import { NAMESPACE } from 'store/constants';
|
|||
|
||||
const { orderStatuses } = wcSettings;
|
||||
|
||||
export const charts = [
|
||||
{
|
||||
key: 'orders_count',
|
||||
label: __( 'Orders Count', 'wc-admin' ),
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
key: 'net_revenue',
|
||||
label: __( 'Net Revenue', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'avg_order_value',
|
||||
label: __( 'Average Order Value', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'avg_items_per_order',
|
||||
label: __( 'Average Items Per Order', 'wc-admin' ),
|
||||
type: 'average',
|
||||
},
|
||||
];
|
||||
|
||||
export const filters = [
|
||||
{
|
||||
label: __( 'Show', 'wc-admin' ),
|
||||
|
|
|
@ -13,9 +13,11 @@ import { ReportFilters } from '@woocommerce/components';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { advancedFilters, filters } from './config';
|
||||
import OrdersReportChart from './chart';
|
||||
import { advancedFilters, charts, filters } from './config';
|
||||
import getSelectedChart from 'lib/get-selected-chart';
|
||||
import OrdersReportTable from './table';
|
||||
import ReportChart from 'analytics/components/report-chart';
|
||||
import ReportSummary from 'analytics/components/report-summary';
|
||||
|
||||
export default class OrdersReport extends Component {
|
||||
render() {
|
||||
|
@ -29,7 +31,19 @@ export default class OrdersReport extends Component {
|
|||
filters={ filters }
|
||||
advancedFilters={ advancedFilters }
|
||||
/>
|
||||
<OrdersReportChart query={ query } path={ path } />
|
||||
<ReportSummary
|
||||
charts={ charts }
|
||||
endpoint="orders"
|
||||
query={ query }
|
||||
selectedChart={ getSelectedChart( query.chart, charts ) }
|
||||
/>
|
||||
<ReportChart
|
||||
charts={ charts }
|
||||
endpoint="orders"
|
||||
path={ path }
|
||||
query={ query }
|
||||
selectedChart={ getSelectedChart( query.chart, charts ) }
|
||||
/>
|
||||
<OrdersReportTable query={ query } />
|
||||
</Fragment>
|
||||
);
|
||||
|
|
|
@ -12,29 +12,25 @@ import { get, map, orderBy } from 'lodash';
|
|||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { Link, OrderStatus, TableCard, ViewMoreList } from '@woocommerce/components';
|
||||
import { formatCurrency, getCurrencyFormatDecimal } from '@woocommerce/currency';
|
||||
import {
|
||||
appendTimestamp,
|
||||
getCurrentDates,
|
||||
getIntervalForQuery,
|
||||
getDateFormatsForInterval,
|
||||
} from '@woocommerce/date';
|
||||
import { Link, OrderStatus, TableCard, ViewMoreList } from '@woocommerce/components';
|
||||
import { formatCurrency, getCurrencyFormatDecimal } from '@woocommerce/currency';
|
||||
import { getAdminLink, onQueryChange } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getAdminLink, onQueryChange } from 'lib/nav-utils';
|
||||
import ReportError from 'analytics/components/report-error';
|
||||
import { QUERY_DEFAULTS } from 'store/constants';
|
||||
import { getReportChartData, getFilterQuery } from 'store/reports/utils';
|
||||
import './style.scss';
|
||||
|
||||
class OrdersReportTable extends Component {
|
||||
constructor( props ) {
|
||||
super( props );
|
||||
}
|
||||
|
||||
getHeadersContent() {
|
||||
return [
|
||||
{
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Component, Fragment } from '@wordpress/element';
|
||||
import { find } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ReportChart from 'analytics/components/report-chart';
|
||||
import ReportSummary from 'analytics/components/report-summary';
|
||||
|
||||
class ProductsReportChart extends Component {
|
||||
getCharts() {
|
||||
return [
|
||||
{
|
||||
key: 'items_sold',
|
||||
label: __( 'Items Sold', 'wc-admin' ),
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
key: 'gross_revenue',
|
||||
label: __( 'Gross Revenue', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'orders_count',
|
||||
label: __( 'Orders Count', 'wc-admin' ),
|
||||
type: 'number',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
getSelectedChart() {
|
||||
const { query } = this.props;
|
||||
const charts = this.getCharts();
|
||||
const chart = find( charts, { key: query.chart } );
|
||||
if ( chart ) {
|
||||
return chart;
|
||||
}
|
||||
|
||||
return charts[ 0 ];
|
||||
}
|
||||
|
||||
render() {
|
||||
const { path, query } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
<ReportSummary
|
||||
charts={ this.getCharts() }
|
||||
endpoint="products"
|
||||
query={ query }
|
||||
selectedChart={ this.getSelectedChart() }
|
||||
/>
|
||||
<ReportChart
|
||||
charts={ this.getCharts() }
|
||||
endpoint="products"
|
||||
path={ path }
|
||||
query={ query }
|
||||
selectedChart={ this.getSelectedChart() }
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ProductsReportChart.propTypes = {
|
||||
path: PropTypes.string.isRequired,
|
||||
query: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default ProductsReportChart;
|
|
@ -10,6 +10,24 @@ import { __ } from '@wordpress/i18n';
|
|||
import { getRequestByIdString } from 'lib/async-requests';
|
||||
import { NAMESPACE } from 'store/constants';
|
||||
|
||||
export const charts = [
|
||||
{
|
||||
key: 'items_sold',
|
||||
label: __( 'Items Sold', 'wc-admin' ),
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
key: 'gross_revenue',
|
||||
label: __( 'Gross Revenue', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'orders_count',
|
||||
label: __( 'Orders Count', 'wc-admin' ),
|
||||
type: 'number',
|
||||
},
|
||||
];
|
||||
|
||||
const filterConfig = {
|
||||
label: __( 'Show', 'wc-admin' ),
|
||||
staticParams: [ 'chart' ],
|
||||
|
@ -42,7 +60,7 @@ const filterConfig = {
|
|||
},
|
||||
{
|
||||
label: __( 'Product Comparison', 'wc-admin' ),
|
||||
value: 'compare-product',
|
||||
value: 'compare-products',
|
||||
settings: {
|
||||
type: 'products',
|
||||
param: 'products',
|
||||
|
@ -60,7 +78,7 @@ const filterConfig = {
|
|||
},
|
||||
{
|
||||
label: __( 'Product Category Comparison', 'wc-admin' ),
|
||||
value: 'compare-product_cat',
|
||||
value: 'compare-product_cats',
|
||||
settings: {
|
||||
type: 'product_cats',
|
||||
param: 'categories',
|
||||
|
@ -99,13 +117,21 @@ const variationsConfig = {
|
|||
label: __( 'Comparison', 'wc-admin' ),
|
||||
value: 'compare',
|
||||
settings: {
|
||||
// @TODO create a variations autocompleter
|
||||
type: 'products',
|
||||
type: 'variations',
|
||||
param: 'variations',
|
||||
getLabels: getRequestByIdString( NAMESPACE + 'products', product => ( {
|
||||
id: product.id,
|
||||
label: product.name,
|
||||
} ) ),
|
||||
getLabels: getRequestByIdString(
|
||||
query => NAMESPACE + `products/${ query.products }/variations`,
|
||||
variation => {
|
||||
return {
|
||||
id: variation.id,
|
||||
label: variation.attributes.reduce(
|
||||
( desc, attribute, index, arr ) =>
|
||||
desc + `${ attribute.option }${ arr.length === index + 1 ? '' : ', ' }`,
|
||||
''
|
||||
),
|
||||
};
|
||||
}
|
||||
),
|
||||
labels: {
|
||||
helpText: __( 'Select at least two variations to compare', 'wc-admin' ),
|
||||
placeholder: __( 'Search for variations to compare', 'wc-admin' ),
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Component, Fragment } from '@wordpress/element';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
|
@ -13,18 +14,39 @@ import { ReportFilters } from '@woocommerce/components';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { filters } from './config';
|
||||
import ProductsReportChart from './chart';
|
||||
import { charts, filters } from './config';
|
||||
import getSelectedChart from 'lib/get-selected-chart';
|
||||
import ProductsReportTable from './table';
|
||||
import ReportChart from 'analytics/components/report-chart';
|
||||
import ReportSummary from 'analytics/components/report-summary';
|
||||
|
||||
export default class ProductsReport extends Component {
|
||||
render() {
|
||||
const { path, query } = this.props;
|
||||
|
||||
const itemsLabel =
|
||||
'single_product' === query.filter && !! query.products
|
||||
? __( '%s variations', 'wc-admin' )
|
||||
: __( '%s products', 'wc-admin' );
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<ReportFilters query={ query } path={ path } filters={ filters } />
|
||||
<ProductsReportChart query={ query } path={ path } />
|
||||
<ReportSummary
|
||||
charts={ charts }
|
||||
endpoint="products"
|
||||
query={ query }
|
||||
selectedChart={ getSelectedChart( query.chart, charts ) }
|
||||
/>
|
||||
<ReportChart
|
||||
comparisonChart
|
||||
charts={ charts }
|
||||
endpoint="products"
|
||||
itemsLabel={ itemsLabel }
|
||||
path={ path }
|
||||
query={ query }
|
||||
selectedChart={ getSelectedChart( query.chart, charts ) }
|
||||
/>
|
||||
<ProductsReportTable query={ query } />
|
||||
</Fragment>
|
||||
);
|
||||
|
|
|
@ -11,14 +11,14 @@ import { get, map, orderBy } from 'lodash';
|
|||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { appendTimestamp, getCurrentDates } from '@woocommerce/date';
|
||||
import { Link, TableCard } from '@woocommerce/components';
|
||||
import { formatCurrency, getCurrencyFormatDecimal } from '@woocommerce/currency';
|
||||
import { appendTimestamp, getCurrentDates } from '@woocommerce/date';
|
||||
import { getNewPath, getTimeRelatedQuery, onQueryChange } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getNewPath, getTimeRelatedQuery, onQueryChange } from 'lib/nav-utils';
|
||||
import ReportError from 'analytics/components/report-error';
|
||||
import { getFilterQuery, getReportChartData } from 'store/reports/utils';
|
||||
import { QUERY_DEFAULTS } from 'store/constants';
|
||||
|
@ -183,7 +183,7 @@ class ProductsReportTable extends Component {
|
|||
labels={ labels }
|
||||
ids={ orderedProducts.map( p => p.product_id ) }
|
||||
isLoading={ isRequesting }
|
||||
compareBy={ 'product' }
|
||||
compareBy={ 'products' }
|
||||
onQueryChange={ onQueryChange }
|
||||
query={ tableQuery }
|
||||
summary={ null } // @TODO
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Component, Fragment } from '@wordpress/element';
|
||||
import { find } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ReportChart from 'analytics/components/report-chart';
|
||||
import ReportSummary from 'analytics/components/report-summary';
|
||||
|
||||
class RevenueReportChart extends Component {
|
||||
getCharts() {
|
||||
return [
|
||||
{
|
||||
key: 'gross_revenue',
|
||||
label: __( 'Gross Revenue', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'refunds',
|
||||
label: __( 'Refunds', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'coupons',
|
||||
label: __( 'Coupons', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'taxes',
|
||||
label: __( 'Taxes', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'shipping',
|
||||
label: __( 'Shipping', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'net_revenue',
|
||||
label: __( 'Net Revenue', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
getSelectedChart() {
|
||||
const { query } = this.props;
|
||||
const charts = this.getCharts();
|
||||
const chart = find( charts, { key: query.chart } );
|
||||
if ( chart ) {
|
||||
return chart;
|
||||
}
|
||||
|
||||
return charts[ 0 ];
|
||||
}
|
||||
|
||||
render() {
|
||||
const { path, query } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
<ReportSummary
|
||||
charts={ this.getCharts() }
|
||||
endpoint="revenue"
|
||||
query={ query }
|
||||
selectedChart={ this.getSelectedChart() }
|
||||
/>
|
||||
<ReportChart
|
||||
charts={ this.getCharts() }
|
||||
endpoint="revenue"
|
||||
path={ path }
|
||||
query={ query }
|
||||
selectedChart={ this.getSelectedChart() }
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RevenueReportChart.propTypes = {
|
||||
path: PropTypes.string.isRequired,
|
||||
query: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default RevenueReportChart;
|
|
@ -0,0 +1,38 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
export const charts = [
|
||||
{
|
||||
key: 'gross_revenue',
|
||||
label: __( 'Gross Revenue', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'refunds',
|
||||
label: __( 'Refunds', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'coupons',
|
||||
label: __( 'Coupons', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'taxes',
|
||||
label: __( 'Taxes', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'shipping',
|
||||
label: __( 'Shipping', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'net_revenue',
|
||||
label: __( 'Net Revenue', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
];
|
|
@ -13,7 +13,10 @@ import { ReportFilters } from '@woocommerce/components';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import RevenueReportChart from './chart';
|
||||
import { charts } from './config';
|
||||
import getSelectedChart from 'lib/get-selected-chart';
|
||||
import ReportChart from 'analytics/components/report-chart';
|
||||
import ReportSummary from 'analytics/components/report-summary';
|
||||
import RevenueReportTable from './table';
|
||||
|
||||
export default class RevenueReport extends Component {
|
||||
|
@ -23,7 +26,19 @@ export default class RevenueReport extends Component {
|
|||
return (
|
||||
<Fragment>
|
||||
<ReportFilters query={ query } path={ path } />
|
||||
<RevenueReportChart query={ query } path={ path } />
|
||||
<ReportSummary
|
||||
charts={ charts }
|
||||
endpoint="revenue"
|
||||
query={ query }
|
||||
selectedChart={ getSelectedChart( query.chart, charts ) }
|
||||
/>
|
||||
<ReportChart
|
||||
charts={ charts }
|
||||
endpoint="revenue"
|
||||
path={ path }
|
||||
query={ query }
|
||||
selectedChart={ getSelectedChart( query.chart, charts ) }
|
||||
/>
|
||||
<RevenueReportTable query={ query } />
|
||||
</Fragment>
|
||||
);
|
||||
|
|
|
@ -12,19 +12,19 @@ import { get, map } from 'lodash';
|
|||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { Link, TableCard } from '@woocommerce/components';
|
||||
import { formatCurrency, getCurrencyFormatDecimal } from '@woocommerce/currency';
|
||||
import {
|
||||
appendTimestamp,
|
||||
getCurrentDates,
|
||||
getDateFormatsForInterval,
|
||||
getIntervalForQuery,
|
||||
} from '@woocommerce/date';
|
||||
import { Link, TableCard } from '@woocommerce/components';
|
||||
import { formatCurrency, getCurrencyFormatDecimal } from '@woocommerce/currency';
|
||||
import { onQueryChange } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { onQueryChange } from 'lib/nav-utils';
|
||||
import ReportError from 'analytics/components/report-error';
|
||||
import { QUERY_DEFAULTS } from 'store/constants';
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
||||
export const charts = [
|
||||
{
|
||||
key: 'order_tax',
|
||||
label: __( 'Order Tax', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'total_tax',
|
||||
label: __( 'Total Tax', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'shipping_tax',
|
||||
label: __( 'Shipping Tax', 'wc-admin' ),
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: 'orders_count',
|
||||
label: __( 'Orders Count', 'wc-admin' ),
|
||||
type: 'number',
|
||||
},
|
||||
];
|
||||
|
||||
export const filters = [
|
||||
{
|
||||
label: __( 'Show', 'wc-admin' ),
|
||||
staticParams: [ 'chart' ],
|
||||
param: 'filter',
|
||||
showFilters: () => true,
|
||||
filters: [ { label: __( 'All Taxes', 'wc-admin' ), value: 'all' } ],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,45 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Component, Fragment } from '@wordpress/element';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { ReportFilters } from '@woocommerce/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { charts, filters } from './config';
|
||||
import getSelectedChart from 'lib/get-selected-chart';
|
||||
import ReportChart from 'analytics/components/report-chart';
|
||||
import ReportSummary from 'analytics/components/report-summary';
|
||||
export default class TaxesReport extends Component {
|
||||
render() {
|
||||
const { query, path } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
<ReportFilters query={ query } path={ path } filters={ filters } />
|
||||
<ReportSummary
|
||||
charts={ charts }
|
||||
endpoint="taxes"
|
||||
query={ query }
|
||||
selectedChart={ getSelectedChart( query.chart, charts ) }
|
||||
/>
|
||||
<ReportChart
|
||||
charts={ charts }
|
||||
endpoint="taxes"
|
||||
path={ path }
|
||||
query={ query }
|
||||
selectedChart={ getSelectedChart( query.chart, charts ) }
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
TaxesReport.propTypes = {
|
||||
query: PropTypes.object.isRequired,
|
||||
};
|
|
@ -105,6 +105,7 @@ class D3Chart extends Component {
|
|||
mode,
|
||||
orderedKeys,
|
||||
pointLabelFormat,
|
||||
tooltipPosition,
|
||||
tooltipFormat,
|
||||
tooltipTitle,
|
||||
type,
|
||||
|
@ -116,7 +117,6 @@ class D3Chart extends Component {
|
|||
const { width } = this.state;
|
||||
const calculatedWidth = width || node.offsetWidth;
|
||||
const calculatedHeight = height || node.offsetHeight;
|
||||
const scale = width / node.offsetWidth;
|
||||
const adjHeight = calculatedHeight - margin.top - margin.bottom;
|
||||
const adjWidth = calculatedWidth - margin.left - margin.right;
|
||||
const uniqueKeys = getUniqueKeys( data );
|
||||
|
@ -140,7 +140,7 @@ class D3Chart extends Component {
|
|||
orderedKeys: newOrderedKeys,
|
||||
pointLabelFormat,
|
||||
parseDate,
|
||||
scale,
|
||||
tooltipPosition,
|
||||
tooltipFormat: d3TimeFormat( tooltipFormat ),
|
||||
tooltipTitle,
|
||||
type,
|
||||
|
@ -155,7 +155,7 @@ class D3Chart extends Component {
|
|||
xScale,
|
||||
yMax,
|
||||
yScale,
|
||||
yTickOffset: getYTickOffset( adjHeight, scale, yMax ),
|
||||
yTickOffset: getYTickOffset( adjHeight, yMax ),
|
||||
yFormat,
|
||||
valueType,
|
||||
};
|
||||
|
@ -241,6 +241,10 @@ D3Chart.propTypes = {
|
|||
* if `tooltipTitle` is missing, passed to d3TimeFormat.
|
||||
*/
|
||||
tooltipFormat: PropTypes.string,
|
||||
/**
|
||||
* The position where to render the tooltip can be `over` the chart or `below` the chart.
|
||||
*/
|
||||
tooltipPosition: PropTypes.oneOf( [ 'below', 'over' ] ),
|
||||
/**
|
||||
* A string to use as a title for the tooltip. Takes preference over `tooltipFormat`.
|
||||
*/
|
||||
|
@ -280,6 +284,7 @@ D3Chart.defaultProps = {
|
|||
layout: 'standard',
|
||||
mode: 'item-comparison',
|
||||
tooltipFormat: '%B %d, %Y',
|
||||
tooltipPosition: 'over',
|
||||
type: 'line',
|
||||
width: 600,
|
||||
xFormat: '%Y-%m-%d',
|
||||
|
|
|
@ -32,7 +32,6 @@ export default class D3Base extends Component {
|
|||
drawChart: PropTypes.func.isRequired,
|
||||
getParams: PropTypes.func.isRequired,
|
||||
type: PropTypes.string,
|
||||
width: PropTypes.number,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -41,7 +40,6 @@ export default class D3Base extends Component {
|
|||
drawChart: null,
|
||||
getParams: null,
|
||||
type: null,
|
||||
width: null,
|
||||
};
|
||||
|
||||
chartRef = createRef();
|
||||
|
@ -61,10 +59,6 @@ export default class D3Base extends Component {
|
|||
state = { ...state, getParams: nextProps.getParams };
|
||||
}
|
||||
|
||||
if ( nextProps.width !== prevState.width ) {
|
||||
state = { ...state, width: nextProps.width };
|
||||
}
|
||||
|
||||
if ( nextProps.type !== prevState.type ) {
|
||||
state = { ...state, type: nextProps.type };
|
||||
}
|
||||
|
@ -86,7 +80,6 @@ export default class D3Base extends Component {
|
|||
return (
|
||||
( nextState.params !== null && ! isEqual( this.state.params, nextState.params ) ) ||
|
||||
! isEqual( this.state.data, nextState.data ) ||
|
||||
this.state.width !== nextState.width ||
|
||||
this.state.type !== nextState.type
|
||||
);
|
||||
}
|
||||
|
@ -129,6 +122,8 @@ export default class D3Base extends Component {
|
|||
const svg = d3Select( this.chartRef.current )
|
||||
.append( 'svg' )
|
||||
.attr( 'viewBox', `0 0 ${ width } ${ height }` )
|
||||
.attr( 'height', height )
|
||||
.attr( 'width', width )
|
||||
.attr( 'preserveAspectRatio', 'xMidYMid meet' );
|
||||
|
||||
if ( className ) {
|
||||
|
|
|
@ -5,10 +5,14 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import { Component } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { Card } from '@woocommerce/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Card from 'components/card';
|
||||
import Chart from './index';
|
||||
import dummyOrders from './test/fixtures/dummy-hour';
|
||||
|
||||
|
|
|
@ -5,10 +5,14 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import { Component, Fragment } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { Card } from '@woocommerce/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Card from 'components/card';
|
||||
import Chart from './index';
|
||||
import dummyOrders from './test/fixtures/dummy';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
```jsx
|
||||
import { D3Chart, Legend } from '@woocommerce/components';
|
||||
import { D3Chart, Legend } from 'components';
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
|
|
|
@ -2,27 +2,29 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import classNames from 'classnames';
|
||||
import { get, isEqual, partial } from 'lodash';
|
||||
import { Component, createRef } from '@wordpress/element';
|
||||
import { IconButton, NavigableMenu, SelectControl } from '@wordpress/components';
|
||||
import PropTypes from 'prop-types';
|
||||
import { interpolateViridis as d3InterpolateViridis } from 'd3-scale-chromatic';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
import { formatDefaultLocale as d3FormatDefaultLocale } from 'd3-format';
|
||||
import { get, isEqual, partial } from 'lodash';
|
||||
import Gridicon from 'gridicons';
|
||||
import { IconButton, NavigableMenu, SelectControl } from '@wordpress/components';
|
||||
import { interpolateViridis as d3InterpolateViridis } from 'd3-scale-chromatic';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withViewportMatch } from '@wordpress/viewport';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { updateQueryString } from '@woocommerce/navigation';
|
||||
import { H, Section } from '@woocommerce/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import D3Chart from './charts';
|
||||
import Legend from './legend';
|
||||
import { WIDE_BREAKPOINT } from './utils';
|
||||
import { H, Section } from 'components/section';
|
||||
import { gap, gaplarge } from 'stylesheets/abstracts/_variables.scss';
|
||||
import { updateQueryString } from 'lib/nav-utils';
|
||||
|
||||
d3FormatDefaultLocale( {
|
||||
decimal: '.',
|
||||
|
@ -57,16 +59,13 @@ function getOrderedKeys( props ) {
|
|||
class Chart extends Component {
|
||||
constructor( props ) {
|
||||
super( props );
|
||||
this.chartRef = createRef();
|
||||
const wpBody = document.getElementById( 'wpbody' ).getBoundingClientRect().width;
|
||||
const wpWrap = document.getElementById( 'wpwrap' ).getBoundingClientRect().width;
|
||||
const calcGap = wpWrap > 782 ? gaplarge.match( /\d+/ )[ 0 ] : gap.match( /\d+/ )[ 0 ];
|
||||
this.chartBodyRef = createRef();
|
||||
this.state = {
|
||||
data: props.data,
|
||||
orderedKeys: getOrderedKeys( props ),
|
||||
type: props.type,
|
||||
visibleData: [ ...props.data ],
|
||||
width: wpBody - 2 * calcGap,
|
||||
width: 0,
|
||||
};
|
||||
this.handleTypeToggle = this.handleTypeToggle.bind( this );
|
||||
this.handleLegendToggle = this.handleLegendToggle.bind( this );
|
||||
|
@ -90,6 +89,7 @@ class Chart extends Component {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateDimensions();
|
||||
window.addEventListener( 'resize', this.updateDimensions );
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ class Chart extends Component {
|
|||
|
||||
updateDimensions() {
|
||||
this.setState( {
|
||||
width: this.chartRef.current.offsetWidth,
|
||||
width: this.chartBodyRef.current.offsetWidth,
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -185,13 +185,30 @@ class Chart extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
getChartHeight() {
|
||||
const { isViewportLarge, isViewportMobile } = this.props;
|
||||
|
||||
if ( isViewportMobile ) {
|
||||
return 180;
|
||||
}
|
||||
|
||||
if ( isViewportLarge ) {
|
||||
return 300;
|
||||
}
|
||||
|
||||
return 220;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { orderedKeys, type, visibleData, width } = this.state;
|
||||
const {
|
||||
dateParser,
|
||||
itemsLabel,
|
||||
layout,
|
||||
mode,
|
||||
pointLabelFormat,
|
||||
isViewportLarge,
|
||||
isViewportWide,
|
||||
title,
|
||||
tooltipFormat,
|
||||
tooltipTitle,
|
||||
|
@ -201,19 +218,17 @@ class Chart extends Component {
|
|||
valueType,
|
||||
} = this.props;
|
||||
let { yFormat } = this.props;
|
||||
const legendDirection = layout === 'standard' && width >= WIDE_BREAKPOINT ? 'row' : 'column';
|
||||
const chartDirection = layout === 'comparison' && width >= WIDE_BREAKPOINT ? 'row' : 'column';
|
||||
let chartHeight = width > 1329 ? 300 : 220;
|
||||
chartHeight = width <= 1329 && width > 783 ? 220 : chartHeight;
|
||||
chartHeight = width <= 783 ? 180 : chartHeight;
|
||||
const legendDirection = layout === 'standard' && isViewportWide ? 'row' : 'column';
|
||||
const chartDirection = layout === 'comparison' && isViewportWide ? 'row' : 'column';
|
||||
const chartHeight = this.getChartHeight();
|
||||
const legend = (
|
||||
<Legend
|
||||
className={ 'woocommerce-chart__legend' }
|
||||
colorScheme={ d3InterpolateViridis }
|
||||
data={ orderedKeys }
|
||||
handleLegendHover={ this.handleLegendHover }
|
||||
handleLegendToggle={ this.handleLegendToggle }
|
||||
legendDirection={ legendDirection }
|
||||
itemsLabel={ itemsLabel }
|
||||
valueType={ valueType }
|
||||
/>
|
||||
);
|
||||
|
@ -236,10 +251,10 @@ class Chart extends Component {
|
|||
break;
|
||||
}
|
||||
return (
|
||||
<div className="woocommerce-chart" ref={ this.chartRef }>
|
||||
<div className="woocommerce-chart">
|
||||
<div className="woocommerce-chart__header">
|
||||
<H className="woocommerce-chart__title">{ title }</H>
|
||||
{ width >= WIDE_BREAKPOINT && legendDirection === 'row' && legend }
|
||||
{ isViewportWide && legendDirection === 'row' && legend }
|
||||
{ this.renderIntervalSelector() }
|
||||
<NavigableMenu
|
||||
className="woocommerce-chart__types"
|
||||
|
@ -276,8 +291,10 @@ class Chart extends Component {
|
|||
'woocommerce-chart__body',
|
||||
`woocommerce-chart__body-${ chartDirection }`
|
||||
) }
|
||||
ref={ this.chartBodyRef }
|
||||
>
|
||||
{ width >= WIDE_BREAKPOINT && legendDirection === 'column' && legend }
|
||||
{ isViewportWide && legendDirection === 'column' && legend }
|
||||
{ width > 0 && (
|
||||
<D3Chart
|
||||
colorScheme={ d3InterpolateViridis }
|
||||
data={ visibleData }
|
||||
|
@ -288,6 +305,7 @@ class Chart extends Component {
|
|||
orderedKeys={ orderedKeys }
|
||||
pointLabelFormat={ pointLabelFormat }
|
||||
tooltipFormat={ tooltipFormat }
|
||||
tooltipPosition={ isViewportLarge ? 'over' : 'below' }
|
||||
tooltipTitle={ tooltipTitle }
|
||||
type={ type }
|
||||
interval={ interval }
|
||||
|
@ -297,8 +315,9 @@ class Chart extends Component {
|
|||
yFormat={ yFormat }
|
||||
valueType={ valueType }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
{ width < WIDE_BREAKPOINT && <div className="woocommerce-chart__footer">{ legend }</div> }
|
||||
{ ! isViewportWide && <div className="woocommerce-chart__footer">{ legend }</div> }
|
||||
</Section>
|
||||
</div>
|
||||
);
|
||||
|
@ -396,4 +415,8 @@ Chart.defaultProps = {
|
|||
interval: 'day',
|
||||
};
|
||||
|
||||
export default Chart;
|
||||
export default withViewportMatch( {
|
||||
isViewportMobile: '< medium',
|
||||
isViewportLarge: '>= large',
|
||||
isViewportWide: '>= wide',
|
||||
} )( Chart );
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import classNames from 'classnames';
|
||||
import { Component } from '@wordpress/element';
|
||||
import { Component, createRef } from '@wordpress/element';
|
||||
import PropTypes from 'prop-types';
|
||||
import { sprintf } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
|
@ -14,7 +15,7 @@ import { formatCurrency } from '@woocommerce/currency';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
import './legend.scss';
|
||||
import { getColor } from './utils';
|
||||
|
||||
function getFormatedTotal( total, valueType ) {
|
||||
|
@ -36,28 +37,67 @@ function getFormatedTotal( total, valueType ) {
|
|||
* A legend specifically designed for the WooCommerce admin charts.
|
||||
*/
|
||||
class Legend extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.listRef = createRef();
|
||||
|
||||
this.state = {
|
||||
isScrollable: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateListScroll();
|
||||
window.addEventListener( 'resize', this.updateListScroll );
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener( 'resize', this.updateListScroll );
|
||||
}
|
||||
|
||||
updateListScroll = () => {
|
||||
const list = this.listRef.current;
|
||||
const scrolledToEnd = list.scrollHeight - list.scrollTop <= list.offsetHeight;
|
||||
this.setState( {
|
||||
isScrollable: ! scrolledToEnd,
|
||||
} );
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
colorScheme,
|
||||
data,
|
||||
handleLegendHover,
|
||||
handleLegendToggle,
|
||||
itemsLabel,
|
||||
legendDirection,
|
||||
valueType,
|
||||
} = this.props;
|
||||
const { isScrollable } = this.state;
|
||||
const colorParams = {
|
||||
orderedKeys: data,
|
||||
colorScheme,
|
||||
};
|
||||
const numberOfRowsVisible = data.filter( row => row.visible ).length;
|
||||
const showTotalLabel = legendDirection === 'column' && data.length > 5 && itemsLabel;
|
||||
|
||||
return (
|
||||
<ul
|
||||
<div
|
||||
className={ classNames(
|
||||
'woocommerce-legend',
|
||||
`woocommerce-legend__direction-${ legendDirection }`,
|
||||
{
|
||||
'has-total': showTotalLabel,
|
||||
'is-scrollable': isScrollable,
|
||||
},
|
||||
this.props.className
|
||||
) }
|
||||
>
|
||||
<ul
|
||||
className="woocommerce-legend__list"
|
||||
ref={ this.listRef }
|
||||
onScroll={ showTotalLabel ? this.updateListScroll : null }
|
||||
>
|
||||
{ data.map( row => (
|
||||
<li
|
||||
|
@ -95,6 +135,10 @@ class Legend extends Component {
|
|||
</li>
|
||||
) ) }
|
||||
</ul>
|
||||
{ showTotalLabel && (
|
||||
<div className="woocommerce-legend__total">{ sprintf( itemsLabel, data.length ) }</div>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -124,6 +168,11 @@ Legend.propTypes = {
|
|||
* Display legend items as a `row` or `column` inside a flex-box.
|
||||
*/
|
||||
legendDirection: PropTypes.oneOf( [ 'row', 'column' ] ),
|
||||
/**
|
||||
* Label to describe the legend items. It will be displayed in the legend of
|
||||
* comparison charts when there are many.
|
||||
*/
|
||||
itemsLabel: PropTypes.string,
|
||||
/**
|
||||
* What type of data is to be displayed? Number, Average, String?
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
/** @format */
|
||||
|
||||
.woocommerce-legend {
|
||||
&.has-total {
|
||||
padding-bottom: 50px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.woocommerce-legend__direction-column {
|
||||
border-right: 1px solid $core-grey-light-700;
|
||||
min-width: 320px;
|
||||
|
||||
.woocommerce-chart__footer & {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-legend__list {
|
||||
color: $black;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
|
||||
.woocommerce-legend__direction-column & {
|
||||
flex-direction: column;
|
||||
height: 300px;
|
||||
overflow: auto;
|
||||
|
||||
.woocommerce-chart__footer & {
|
||||
border-top: 1px solid $core-grey-light-700;
|
||||
height: 100%;
|
||||
max-height: none;
|
||||
min-height: none;
|
||||
}
|
||||
}
|
||||
|
||||
.has-total.woocommerce-legend__direction-column & {
|
||||
height: 250px;
|
||||
|
||||
.woocommerce-chart__footer & {
|
||||
height: auto;
|
||||
max-height: 220px;
|
||||
min-height: none;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-legend__direction-row & {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-legend__item {
|
||||
& > button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: $white;
|
||||
color: $core-grey-dark-500;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
border: none;
|
||||
padding: 0;
|
||||
|
||||
.woocommerce-legend__item-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
padding: 3px 0 3px 24px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
input {
|
||||
~ .woocommerce-legend__item-checkmark {
|
||||
background-color: $core-grey-light-200;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-legend__item-checkmark {
|
||||
border: 1px solid $core-grey-light-900;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 0;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background-color: $white;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.woocommerce-legend__item-checkmark-checked {
|
||||
background-color: currentColor;
|
||||
border-color: currentColor;
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
left: 5px;
|
||||
top: 2px;
|
||||
width: 3px;
|
||||
height: 6px;
|
||||
border: solid $white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-legend__item-total {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
||||
.woocommerce-legend__item-container {
|
||||
.woocommerce-legend__item-checkmark {
|
||||
outline: 2px solid $core-grey-light-900;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $core-grey-light-100;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-legend__direction-column & {
|
||||
margin: 2px 0;
|
||||
padding: 0;
|
||||
|
||||
& > button {
|
||||
height: 32px;
|
||||
padding: 0 17px;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-top: $gap-small;
|
||||
}
|
||||
|
||||
&:last-child::after {
|
||||
content: '';
|
||||
display: block;
|
||||
height: $gap-small;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-legend__direction-row & {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
& > button {
|
||||
padding: 0 17px;
|
||||
|
||||
.woocommerce-legend__item-container {
|
||||
height: 50px;
|
||||
align-items: center;
|
||||
|
||||
.woocommerce-legend__item-checkmark {
|
||||
top: 17px;
|
||||
}
|
||||
|
||||
.woocommerce-legend__item-title {
|
||||
margin-right: 17px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-legend__total {
|
||||
align-items: center;
|
||||
background: $white;
|
||||
border-top: 1px solid $core-grey-light-700;
|
||||
bottom: 0;
|
||||
color: $core-grey-dark-500;
|
||||
display: flex;
|
||||
height: 50px;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
text-transform: uppercase;
|
||||
|
||||
&::before {
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.2));
|
||||
bottom: 100%;
|
||||
content: '';
|
||||
height: 20px;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.is-scrollable &::before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
.woocommerce-chart {
|
||||
margin-top: -$gap;
|
||||
margin-bottom: $gap-large;
|
||||
background: white;
|
||||
background: $white;
|
||||
border: 1px solid $core-grey-light-700;
|
||||
border-top: 0;
|
||||
|
||||
|
@ -61,15 +61,6 @@
|
|||
|
||||
.woocommerce-chart__footer {
|
||||
width: 100%;
|
||||
|
||||
.woocommerce-legend {
|
||||
&.woocommerce-legend__direction-column {
|
||||
height: 100%;
|
||||
min-height: none;
|
||||
border-right: none;
|
||||
margin-bottom: $gap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
|
@ -185,6 +176,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.y-axis,
|
||||
.axis-month {
|
||||
.tick text {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.focus-grid {
|
||||
line {
|
||||
|
@ -252,146 +249,3 @@
|
|||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-legend {
|
||||
color: $black;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
|
||||
&.woocommerce-legend__direction-column {
|
||||
flex-direction: column;
|
||||
border-right: 1px solid $core-grey-light-700;
|
||||
height: 300px;
|
||||
min-width: 320px;
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
button {
|
||||
height: 32px;
|
||||
padding: 0 17px;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-top: 17px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.woocommerce-legend__direction-row {
|
||||
flex-direction: row;
|
||||
|
||||
li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
button {
|
||||
padding: 0 17px;
|
||||
|
||||
.woocommerce-legend__item-container {
|
||||
height: 50px;
|
||||
align-items: center;
|
||||
|
||||
.woocommerce-legend__item-checkmark {
|
||||
top: 17px;
|
||||
}
|
||||
|
||||
.woocommerce-legend__item-title {
|
||||
margin-right: 17px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
&.woocommerce-legend__item {
|
||||
button {
|
||||
&:hover {
|
||||
background-color: $core-grey-light-100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: $white;
|
||||
color: $core-grey-dark-500;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
border: none;
|
||||
padding: 0;
|
||||
|
||||
.woocommerce-legend__item-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
padding: 3px 0 3px 24px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
input {
|
||||
~ .woocommerce-legend__item-checkmark {
|
||||
background-color: $core-grey-light-200;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-legend__item-checkmark {
|
||||
border: 1px solid $core-grey-light-900;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 0;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background-color: $white;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.woocommerce-legend__item-checkmark-checked {
|
||||
background-color: currentColor;
|
||||
border-color: currentColor;
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
left: 5px;
|
||||
top: 2px;
|
||||
width: 3px;
|
||||
height: 6px;
|
||||
border: solid $white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-legend__item-total {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
||||
.woocommerce-legend__item-container {
|
||||
.woocommerce-legend__item-checkmark {
|
||||
outline: 2px solid $core-grey-light-900;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* @format
|
||||
*/
|
||||
import { shallow } from 'enzyme';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -26,7 +26,7 @@ const data = [
|
|||
|
||||
describe( 'Legend', () => {
|
||||
test( 'should not disable any button if more than one is active', () => {
|
||||
const topSellingProducts = shallow( <Legend colorScheme={ colorScheme } data={ data } /> );
|
||||
const topSellingProducts = mount( <Legend colorScheme={ colorScheme } data={ data } /> );
|
||||
|
||||
expect( topSellingProducts.find( 'button' ).get( 0 ).props.disabled ).toBeFalsy();
|
||||
expect( topSellingProducts.find( 'button' ).get( 1 ).props.disabled ).toBeFalsy();
|
||||
|
@ -35,7 +35,7 @@ describe( 'Legend', () => {
|
|||
test( 'should disable the last active button', () => {
|
||||
data[ 1 ].visible = false;
|
||||
|
||||
const topSellingProducts = shallow( <Legend colorScheme={ colorScheme } data={ data } /> );
|
||||
const topSellingProducts = mount( <Legend colorScheme={ colorScheme } data={ data } /> );
|
||||
|
||||
expect( topSellingProducts.find( 'button' ).get( 0 ).props.disabled ).toBeTruthy();
|
||||
expect( topSellingProducts.find( 'button' ).get( 1 ).props.disabled ).toBeFalsy();
|
||||
|
|
|
@ -172,12 +172,9 @@ describe( 'getYScale', () => {
|
|||
|
||||
describe( 'getYTickOffset', () => {
|
||||
it( 'properly scale the y values for the y-axis ticks given the height and maximum y value', () => {
|
||||
const testYTickOffset1 = getYTickOffset( 100, 1, testYMax );
|
||||
const testYTickOffset1 = getYTickOffset( 100, testYMax );
|
||||
expect( testYTickOffset1( 0 ) ).toEqual( 112 );
|
||||
expect( testYTickOffset1( testYMax ) ).toEqual( 12 );
|
||||
const testYTickOffset2 = getYTickOffset( 100, 2, testYMax );
|
||||
expect( testYTickOffset2( 0 ) ).toEqual( 124 );
|
||||
expect( testYTickOffset2( testYMax ) ).toEqual( 24 );
|
||||
} );
|
||||
} );
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ import { format as formatDate } from '@wordpress/date';
|
|||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { dayTicksThreshold } from '@woocommerce/date';
|
||||
import { dayTicksThreshold, weekTicksThreshold } from '@woocommerce/date';
|
||||
import { formatCurrency } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
|
@ -188,14 +188,13 @@ export const getYScale = ( height, yMax ) =>
|
|||
/**
|
||||
* Describes getyTickOffset
|
||||
* @param {number} height - calculated height of the charting space
|
||||
* @param {number} scale - ratio of the expected width to calculated width (given the viewbox)
|
||||
* @param {number} yMax - from `getYMax`
|
||||
* @returns {function} the D3 linear scale from 0 to the value from `getYMax`, offset by 12 pixels down
|
||||
*/
|
||||
export const getYTickOffset = ( height, scale, yMax ) =>
|
||||
export const getYTickOffset = ( height, yMax ) =>
|
||||
d3ScaleLinear()
|
||||
.domain( [ 0, yMax ] )
|
||||
.rangeRound( [ height + scale * 12, scale * 12 ] );
|
||||
.rangeRound( [ height + 12, 12 ] );
|
||||
|
||||
/**
|
||||
* Describes getyTickOffset
|
||||
|
@ -303,7 +302,10 @@ const calculateXTicksIncrementFactor = ( uniqueDates, maxTicks ) => {
|
|||
export const getXTicks = ( uniqueDates, width, layout, interval ) => {
|
||||
const maxTicks = calculateMaxXTicks( width, layout );
|
||||
|
||||
if ( uniqueDates.length >= dayTicksThreshold && interval === 'day' ) {
|
||||
if (
|
||||
( uniqueDates.length >= dayTicksThreshold && interval === 'day' ) ||
|
||||
( uniqueDates.length >= weekTicksThreshold && interval === 'week' )
|
||||
) {
|
||||
uniqueDates = getFirstDatePerMonth( uniqueDates );
|
||||
}
|
||||
if ( uniqueDates.length <= maxTicks ) {
|
||||
|
@ -374,6 +376,14 @@ export const compareStrings = ( s1, s2, splitChar = ' ' ) => {
|
|||
|
||||
export const drawAxis = ( node, params ) => {
|
||||
const xScale = params.type === 'line' ? params.xLineScale : params.xScale;
|
||||
const removeDuplicateDates = ( d, i, ticks, formatter ) => {
|
||||
const monthDate = d instanceof Date ? d : new Date( d );
|
||||
let prevMonth = i !== 0 ? ticks[ i - 1 ] : ticks[ i ];
|
||||
prevMonth = prevMonth instanceof Date ? prevMonth : new Date( prevMonth );
|
||||
return i === 0
|
||||
? formatter( monthDate )
|
||||
: compareStrings( formatter( prevMonth ), formatter( monthDate ) ).join( ' ' );
|
||||
};
|
||||
|
||||
const yGrids = [];
|
||||
for ( let i = 0; i < 4; i++ ) {
|
||||
|
@ -390,7 +400,7 @@ export const drawAxis = ( node, params ) => {
|
|||
.call(
|
||||
d3AxisBottom( xScale )
|
||||
.tickValues( ticks )
|
||||
.tickFormat( d => params.xFormat( d instanceof Date ? d : new Date( d ) ) )
|
||||
.tickFormat( ( d, i ) => removeDuplicateDates( d, i, ticks, params.xFormat ) )
|
||||
);
|
||||
|
||||
node
|
||||
|
@ -401,23 +411,10 @@ export const drawAxis = ( node, params ) => {
|
|||
.call(
|
||||
d3AxisBottom( xScale )
|
||||
.tickValues( ticks )
|
||||
.tickFormat( ( d, i ) => {
|
||||
const monthDate = d instanceof Date ? d : new Date( d );
|
||||
let prevMonth = i !== 0 ? ticks[ i - 1 ] : ticks[ i ];
|
||||
prevMonth = prevMonth instanceof Date ? prevMonth : new Date( prevMonth );
|
||||
return i === 0
|
||||
? params.x2Format( monthDate )
|
||||
: compareStrings( params.x2Format( prevMonth ), params.x2Format( monthDate ) ).join(
|
||||
' '
|
||||
);
|
||||
} )
|
||||
.tickFormat( ( d, i ) => removeDuplicateDates( d, i, ticks, params.x2Format ) )
|
||||
)
|
||||
.call( g => g.select( '.domain' ).remove() );
|
||||
|
||||
node
|
||||
.selectAll( '.axis-month .tick text' )
|
||||
.style( 'font-size', `${ Math.round( params.scale * 10 ) }px` );
|
||||
|
||||
node
|
||||
.append( 'g' )
|
||||
.attr( 'class', 'pipes' )
|
||||
|
@ -453,10 +450,6 @@ export const drawAxis = ( node, params ) => {
|
|||
.tickFormat( d => d3Format( params.yFormat )( d !== 0 ? d : 0 ) )
|
||||
);
|
||||
|
||||
node
|
||||
.selectAll( '.y-axis .tick text' )
|
||||
.style( 'font-size', `${ Math.round( params.scale * 10 ) }px` );
|
||||
|
||||
node.selectAll( '.domain' ).remove();
|
||||
node
|
||||
.selectAll( '.axis' )
|
||||
|
@ -541,19 +534,18 @@ const handleMouseOutLineChart = ( parentNode, params ) => {
|
|||
params.tooltip.style( 'visibility', 'hidden' );
|
||||
};
|
||||
|
||||
export const WIDE_BREAKPOINT = 1100;
|
||||
|
||||
const calculateTooltipXPosition = (
|
||||
elementCoords,
|
||||
chartCoords,
|
||||
tooltipSize,
|
||||
tooltipMargin,
|
||||
elementWidthRatio
|
||||
elementWidthRatio,
|
||||
tooltipPosition
|
||||
) => {
|
||||
const xPosition =
|
||||
elementCoords.left + elementCoords.width * elementWidthRatio + tooltipMargin - chartCoords.left;
|
||||
|
||||
if ( chartCoords.width < WIDE_BREAKPOINT ) {
|
||||
if ( tooltipPosition === 'below' ) {
|
||||
return Math.max(
|
||||
tooltipMargin,
|
||||
Math.min(
|
||||
|
@ -577,8 +569,14 @@ const calculateTooltipXPosition = (
|
|||
return xPosition;
|
||||
};
|
||||
|
||||
const calculateTooltipYPosition = ( elementCoords, chartCoords, tooltipSize, tooltipMargin ) => {
|
||||
if ( chartCoords.width < WIDE_BREAKPOINT ) {
|
||||
const calculateTooltipYPosition = (
|
||||
elementCoords,
|
||||
chartCoords,
|
||||
tooltipSize,
|
||||
tooltipMargin,
|
||||
tooltipPosition
|
||||
) => {
|
||||
if ( tooltipPosition === 'below' ) {
|
||||
return chartCoords.height;
|
||||
}
|
||||
|
||||
|
@ -590,7 +588,7 @@ const calculateTooltipYPosition = ( elementCoords, chartCoords, tooltipSize, too
|
|||
return yPosition;
|
||||
};
|
||||
|
||||
const calculateTooltipPosition = ( element, chart, elementWidthRatio = 1 ) => {
|
||||
const calculateTooltipPosition = ( element, chart, tooltipPosition, elementWidthRatio = 1 ) => {
|
||||
const elementCoords = element.getBoundingClientRect();
|
||||
const chartCoords = chart.getBoundingClientRect();
|
||||
const tooltipSize = d3Select( '.tooltip' )
|
||||
|
@ -598,7 +596,7 @@ const calculateTooltipPosition = ( element, chart, elementWidthRatio = 1 ) => {
|
|||
.getBoundingClientRect();
|
||||
const tooltipMargin = 24;
|
||||
|
||||
if ( chartCoords.width < WIDE_BREAKPOINT ) {
|
||||
if ( tooltipPosition === 'below' ) {
|
||||
elementWidthRatio = 0;
|
||||
}
|
||||
|
||||
|
@ -608,9 +606,16 @@ const calculateTooltipPosition = ( element, chart, elementWidthRatio = 1 ) => {
|
|||
chartCoords,
|
||||
tooltipSize,
|
||||
tooltipMargin,
|
||||
elementWidthRatio
|
||||
elementWidthRatio,
|
||||
tooltipPosition
|
||||
),
|
||||
y: calculateTooltipYPosition(
|
||||
elementCoords,
|
||||
chartCoords,
|
||||
tooltipSize,
|
||||
tooltipMargin,
|
||||
tooltipPosition
|
||||
),
|
||||
y: calculateTooltipYPosition( elementCoords, chartCoords, tooltipSize, tooltipMargin ),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -667,7 +672,11 @@ export const drawLines = ( node, data, params ) => {
|
|||
return `${ label } ${ formatCurrency( d.value ) }`;
|
||||
} )
|
||||
.on( 'focus', ( d, i, nodes ) => {
|
||||
const position = calculateTooltipPosition( d3Event.target, node.node() );
|
||||
const position = calculateTooltipPosition(
|
||||
d3Event.target,
|
||||
node.node(),
|
||||
params.tooltipPosition
|
||||
);
|
||||
handleMouseOverLineChart( d.date, nodes[ i ].parentNode, node, data, params, position );
|
||||
} )
|
||||
.on( 'blur', ( d, i, nodes ) => handleMouseOutLineChart( nodes[ i ].parentNode, params ) );
|
||||
|
@ -715,7 +724,12 @@ export const drawLines = ( node, data, params ) => {
|
|||
.attr( 'opacity', 0 )
|
||||
.on( 'mouseover', ( d, i, nodes ) => {
|
||||
const elementWidthRatio = i === 0 || i === params.dateSpaces.length - 1 ? 0 : 0.5;
|
||||
const position = calculateTooltipPosition( d3Event.target, node.node(), elementWidthRatio );
|
||||
const position = calculateTooltipPosition(
|
||||
d3Event.target,
|
||||
node.node(),
|
||||
params.tooltipPosition,
|
||||
elementWidthRatio
|
||||
);
|
||||
handleMouseOverLineChart( d.date, nodes[ i ].parentNode, node, data, params, position );
|
||||
} )
|
||||
.on( 'mouseout', ( d, i, nodes ) => handleMouseOutLineChart( nodes[ i ].parentNode, params ) );
|
||||
|
@ -780,7 +794,7 @@ export const drawBars = ( node, data, params ) => {
|
|||
} )
|
||||
.on( 'focus', ( d, i, nodes ) => {
|
||||
const targetNode = d.value > 0 ? d3Event.target : d3Event.target.parentNode;
|
||||
const position = calculateTooltipPosition( targetNode, node.node() );
|
||||
const position = calculateTooltipPosition( targetNode, node.node(), params.tooltipPosition );
|
||||
handleMouseOverBarChart( d.date, nodes[ i ].parentNode, node, data, params, position );
|
||||
} )
|
||||
.on( 'blur', ( d, i, nodes ) => handleMouseOutBarChart( nodes[ i ].parentNode, params ) );
|
||||
|
@ -794,7 +808,11 @@ export const drawBars = ( node, data, params ) => {
|
|||
.attr( 'height', params.height )
|
||||
.attr( 'opacity', '0' )
|
||||
.on( 'mouseover', ( d, i, nodes ) => {
|
||||
const position = calculateTooltipPosition( d3Event.target, node.node() );
|
||||
const position = calculateTooltipPosition(
|
||||
d3Event.target,
|
||||
node.node(),
|
||||
params.tooltipPosition
|
||||
);
|
||||
handleMouseOverBarChart( d.date, nodes[ i ].parentNode, node, data, params, position );
|
||||
} )
|
||||
.on( 'mouseout', ( d, i, nodes ) => handleMouseOutBarChart( nodes[ i ].parentNode, params ) );
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { omit } from 'lodash';
|
||||
|
||||
export function flatenFilters( filters ) {
|
||||
const allFilters = [];
|
||||
filters.forEach( f => {
|
||||
if ( ! f.subFilters ) {
|
||||
allFilters.push( f );
|
||||
} else {
|
||||
allFilters.push( omit( f, 'subFilters' ) );
|
||||
const subFilters = flatenFilters( f.subFilters );
|
||||
allFilters.push( ...subFilters );
|
||||
}
|
||||
} );
|
||||
return allFilters;
|
||||
}
|
|
@ -1,52 +1,6 @@
|
|||
/** @format */
|
||||
|
||||
/**
|
||||
* External Dependencies
|
||||
*/
|
||||
// Turn on react-dates classes/styles, see https://github.com/airbnb/react-dates#initialize
|
||||
import 'react-dates/initialize';
|
||||
|
||||
export { default as AdvancedFilters } from './filters/advanced';
|
||||
export { default as AnimationSlider } from './animation-slider';
|
||||
export { default as Card } from './card';
|
||||
export { default as Chart } from './chart';
|
||||
export { default as ChartLegend } from './chart/legend';
|
||||
export { default as ChartPlaceholder } from './chart/placeholder';
|
||||
export { default as Count } from './count';
|
||||
export { default as CompareFilter } from './filters/compare';
|
||||
export { default as D3Chart } from './chart/charts';
|
||||
export { default as DatePicker } from './filters/date';
|
||||
export { default as DateRange } from './calendar';
|
||||
export { default as DropdownButton } from './dropdown-button';
|
||||
export { default as EllipsisMenu } from './ellipsis-menu';
|
||||
export { default as EmptyContent } from './empty-content';
|
||||
export { default as Flag } from './flag';
|
||||
export { default as FilterPicker } from './filters/filter';
|
||||
export { default as Gravatar } from './gravatar';
|
||||
export { H, Section } from './section';
|
||||
export { default as ImageAsset } from './image-asset';
|
||||
export { default as Link } from './link';
|
||||
export { default as MenuItem } from './ellipsis-menu/menu-item';
|
||||
export { default as MenuTitle } from './ellipsis-menu/menu-title';
|
||||
export { default as OrderStatus } from './order-status';
|
||||
export { default as Pagination } from './pagination';
|
||||
export { default as ProductImage } from './product-image';
|
||||
export { default as ProductRating } from './rating/product';
|
||||
export { default as Rating } from './rating';
|
||||
export { default as ReportFilters } from './filters';
|
||||
export { default as ReviewRating } from './rating/review';
|
||||
export { default as Search } from './search';
|
||||
export { default as SectionHeader } from './section-header';
|
||||
export { default as SegmentedSelection } from './segmented-selection';
|
||||
export { default as SplitButton } from './split-button';
|
||||
export { default as SummaryList } from './summary';
|
||||
export { default as SummaryListPlaceholder } from './summary/placeholder';
|
||||
export { default as SummaryNumber } from './summary/item';
|
||||
export { default as Table } from './table/table';
|
||||
export { default as TableCard } from './table';
|
||||
export { default as EmptyTable } from './table/empty';
|
||||
export { default as TablePlaceholder } from './table/placeholder';
|
||||
export { default as TableSummary } from './table/summary';
|
||||
export { default as Tag } from './tag';
|
||||
export { default as useFilters } from './higher-order/use-filters';
|
||||
export { default as ViewMoreList } from './view-more-list';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
grid-template-columns: 1fr 1fr;
|
||||
grid-column-gap: $gap-large;
|
||||
|
||||
@include breakpoint( '<1100px' ) {
|
||||
@include breakpoint( '<960px' ) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,12 +13,13 @@ import { withSelect } from '@wordpress/data';
|
|||
*/
|
||||
import { Card, EmptyTable, TableCard } from '@woocommerce/components';
|
||||
import { formatCurrency, getCurrencyFormatDecimal } from '@woocommerce/currency';
|
||||
import { getAdminLink } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getAdminLink } from 'lib/nav-utils';
|
||||
import { numberFormat } from 'lib/number';
|
||||
import ReportError from 'analytics/components/report-error';
|
||||
import { NAMESPACE } from 'store/constants';
|
||||
import './style.scss';
|
||||
|
||||
|
@ -91,7 +92,7 @@ export class TopSellingProducts extends Component {
|
|||
const title = __( 'Top Selling Products', 'wc-admin' );
|
||||
|
||||
if ( isError ) {
|
||||
// @TODO An error notice should be displayed when there is an error
|
||||
return <ReportError className="woocommerce-top-selling-products" isError />;
|
||||
}
|
||||
|
||||
if ( ! isRequesting && rows.length === 0 ) {
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
/** @format */
|
||||
.woocommerce-top-selling-products {
|
||||
&.woocommerce-empty-content {
|
||||
margin-bottom: $gap-large;
|
||||
}
|
||||
|
||||
.woocommerce-card__body {
|
||||
padding: 0;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ import PropTypes from 'prop-types';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import * as components from '@woocommerce/components';
|
||||
import * as components from 'components';
|
||||
import * as pkgComponents from '@woocommerce/components';
|
||||
|
||||
class Example extends Component {
|
||||
state = {
|
||||
|
@ -28,7 +29,15 @@ class Example extends Component {
|
|||
}
|
||||
|
||||
async getCode() {
|
||||
const readme = require( `components/${ this.props.filePath }/example.md` );
|
||||
let readme;
|
||||
try {
|
||||
readme = require( `components/src/${ this.props.filePath }/example.md` );
|
||||
} catch ( e ) {
|
||||
readme = require( `components/${ this.props.filePath }/example.md` );
|
||||
}
|
||||
if ( ! readme ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Example to render is the first jsx code block that appears in the readme
|
||||
let code = codeBlocks( readme ).find( block => 'jsx' === block.lang ).code;
|
||||
|
@ -47,6 +56,7 @@ class Example extends Component {
|
|||
const scope = {
|
||||
...wpComponents,
|
||||
...components,
|
||||
...pkgComponents,
|
||||
Component,
|
||||
withState,
|
||||
getSettings,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/** @format */
|
||||
|
||||
.woocommerce_devdocs {
|
||||
@include breakpoint( '>1100px' ) {
|
||||
@include breakpoint( '>1280px' ) {
|
||||
&.is-list {
|
||||
column-gap: $gap-large;
|
||||
columns: 2;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
@include breakpoint( '<782px' ) {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
background: $white;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: -3px;
|
||||
|
@ -20,12 +20,12 @@
|
|||
flex: 1 100%;
|
||||
}
|
||||
|
||||
@include breakpoint( '782px-1100px' ) {
|
||||
@include breakpoint( '782px-960px' ) {
|
||||
max-width: 280px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
@include breakpoint( '>1100px' ) {
|
||||
@include breakpoint( '>960px' ) {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@
|
|||
height: 60px;
|
||||
justify-content: flex-end;
|
||||
|
||||
@include breakpoint( '>1100px' ) {
|
||||
@include breakpoint( '>960px' ) {
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@
|
|||
width: 18px;
|
||||
height: 18px;
|
||||
|
||||
@include breakpoint( '>1100px' ) {
|
||||
@include breakpoint( '>960px' ) {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
@ -80,7 +80,7 @@
|
|||
height: 60px;
|
||||
border-bottom: 3px solid $white;
|
||||
|
||||
@include breakpoint( '>1100px' ) {
|
||||
@include breakpoint( '>960px' ) {
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@
|
|||
}
|
||||
|
||||
font-size: 11px;
|
||||
@include breakpoint( '>1100px' ) {
|
||||
@include breakpoint( '>960px' ) {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
|
@ -109,14 +109,14 @@
|
|||
top: 10px;
|
||||
left: 50%;
|
||||
|
||||
@include breakpoint( '782px-1100px' ) {
|
||||
@include breakpoint( '782px-960px' ) {
|
||||
top: 8px;
|
||||
right: 18px;
|
||||
left: initial;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
@include breakpoint( '>1100px' ) {
|
||||
@include breakpoint( '>960px' ) {
|
||||
top: 16px;
|
||||
right: 28px;
|
||||
left: initial;
|
||||
|
@ -167,7 +167,7 @@
|
|||
position: absolute;
|
||||
padding: 1px;
|
||||
background: $core-orange;
|
||||
border: 2px solid white;
|
||||
border: 2px solid $white;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
display: inline-block;
|
||||
|
@ -209,10 +209,10 @@
|
|||
|
||||
// Extra padding is needed at the bottom of the wrapper because of our positioning, height, and overflow rules. Otherwise, some content can get cut off.
|
||||
padding-bottom: $gap-small * 6;
|
||||
@include breakpoint( '782px-1100px' ) {
|
||||
@include breakpoint( '782px-960px' ) {
|
||||
padding-bottom: $gap-small;
|
||||
}
|
||||
@include breakpoint( '>1100px' ) {
|
||||
@include breakpoint( '>960px' ) {
|
||||
top: 112px;
|
||||
padding-bottom: $gap * 2;
|
||||
}
|
||||
|
|
|
@ -3,19 +3,23 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { Component } from '@wordpress/element';
|
||||
import { Component, findDOMNode } from '@wordpress/element';
|
||||
import classnames from 'classnames';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
import { Fill } from 'react-slot-fill';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactDom from 'react-dom';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { getNewPath, getTimeRelatedQuery } from '@woocommerce/navigation';
|
||||
import { Link } from '@woocommerce/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
import ActivityPanel from './activity-panel';
|
||||
import { Link } from '@woocommerce/components';
|
||||
|
||||
class Header extends Component {
|
||||
constructor() {
|
||||
|
@ -29,7 +33,7 @@ class Header extends Component {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.threshold = ReactDom.findDOMNode( this ).offsetTop;
|
||||
this.threshold = findDOMNode( this ).offsetTop;
|
||||
window.addEventListener( 'scroll', this.onWindowScroll );
|
||||
this.updateIsScrolled();
|
||||
}
|
||||
|
@ -84,7 +88,10 @@ class Header extends Component {
|
|||
</span>
|
||||
{ _sections.map( ( section, i ) => {
|
||||
const sectionPiece = Array.isArray( section ) ? (
|
||||
<Link href={ section[ 0 ] } type={ isEmbedded ? 'wp-admin' : 'wc-admin' }>
|
||||
<Link
|
||||
href={ getNewPath( getTimeRelatedQuery(), section[ 0 ], {} ) }
|
||||
type={ isEmbedded ? 'wp-admin' : 'wc-admin' }
|
||||
>
|
||||
{ section[ 1 ] }
|
||||
</Link>
|
||||
) : (
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
flex-flow: row wrap;
|
||||
}
|
||||
|
||||
@include breakpoint( '782px-1100px' ) {
|
||||
@include breakpoint( '782px-960px' ) {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
|
@ -38,12 +38,12 @@
|
|||
line-height: 50px;
|
||||
background: $white;
|
||||
|
||||
@include breakpoint( '782px-1100px' ) {
|
||||
@include breakpoint( '782px-960px' ) {
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
}
|
||||
|
||||
@include breakpoint( '>1100px' ) {
|
||||
@include breakpoint( '>960px' ) {
|
||||
height: 80px;
|
||||
line-height: 80px;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,11 @@ import { Component, createElement } from '@wordpress/element';
|
|||
import { parse } from 'qs';
|
||||
import { find, last } from 'lodash';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { getTimeRelatedQuery, stringifyQuery } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
@ -13,7 +18,6 @@ import Analytics from 'analytics';
|
|||
import AnalyticsReport from 'analytics/report';
|
||||
import Dashboard from 'dashboard';
|
||||
import DevDocs from 'devdocs';
|
||||
import { getTimeRelatedQuery, stringifyQuery } from 'lib/nav-utils';
|
||||
|
||||
const getPages = () => {
|
||||
const pages = [
|
||||
|
|
|
@ -8,13 +8,17 @@ import { Slot } from 'react-slot-fill';
|
|||
import PropTypes from 'prop-types';
|
||||
import { get } from 'lodash';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { history } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
import { Controller, getPages } from './controller';
|
||||
import Header from 'header';
|
||||
import history from 'lib/history';
|
||||
import Notices from './notices';
|
||||
import { recordPageView } from 'lib/tracks';
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
margin: 80px 0 0 $fallback-gutter-large;
|
||||
margin: 80px 0 0 $gutter-large;
|
||||
|
||||
@include breakpoint( '>1100px' ) {
|
||||
@include breakpoint( '>960px' ) {
|
||||
margin-top: 100px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,20 +6,21 @@ import apiFetch from '@wordpress/api-fetch';
|
|||
import { identity } from 'lodash';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { getIdsFromQuery, stringifyQuery } from 'lib/nav-utils';
|
||||
import { getIdsFromQuery, stringifyQuery } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Get a function that accepts ids as they are found in url parameter and
|
||||
* returns a promise with an optional method applied to results
|
||||
*
|
||||
* @param {string} path - api path
|
||||
* @param {string|function} path - api path string or a function of the query returning api path string
|
||||
* @param {Function} [handleData] - function applied to each iteration of data
|
||||
* @returns {Function} - a function of ids returning a promise
|
||||
*/
|
||||
export function getRequestByIdString( path, handleData = identity ) {
|
||||
return function( queryString = '' ) {
|
||||
return function( queryString = '', query ) {
|
||||
const pathString = 'function' === typeof path ? path( query ) : path;
|
||||
const idList = getIdsFromQuery( queryString );
|
||||
if ( idList.length < 1 ) {
|
||||
return Promise.resolve( [] );
|
||||
|
@ -28,6 +29,6 @@ export function getRequestByIdString( path, handleData = identity ) {
|
|||
include: idList.join( ',' ),
|
||||
per_page: idList.length,
|
||||
} );
|
||||
return apiFetch( { path: path + payload } ).then( data => data.map( handleData ) );
|
||||
return apiFetch( { path: pathString + payload } ).then( data => data.map( handleData ) );
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/** @format */
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { find } from 'lodash';
|
||||
|
||||
/**
|
||||
* Takes a chart name returns the configuration for that chart from and array
|
||||
* of charts. If the chart is not found it will return the first chart.
|
||||
*
|
||||
* @param {string} chartName - the name of the chart to get configuration for
|
||||
* @param {array} charts - list of charts for a particular report
|
||||
* @returns {object} - chart configuration object
|
||||
*/
|
||||
export default function getSelectedChart( chartName, charts = [] ) {
|
||||
const chart = find( charts, { key: chartName } );
|
||||
if ( chart ) {
|
||||
return chart;
|
||||
}
|
||||
return charts[ 0 ];
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
Nav Utils
|
||||
=========
|
||||
|
||||
This is a library of functions used in navigation.
|
||||
|
||||
## `getPath()`
|
||||
|
||||
Get the current path from history.
|
||||
|
||||
## `getQuery()`
|
||||
|
||||
Get the current query string, parsed into an object, from history.
|
||||
|
||||
## `getAdminLink( string: path )`
|
||||
|
||||
JS version of `admin_url`. Returns the full URL for a page in wp-admin.
|
||||
|
||||
## `getNewPath( object: query, string: path, object: currentQuery )`
|
||||
|
||||
Return a URL with set query parameters. Optional `path`, `currentQuery`, both will default to the current value fetched from `history` if not provided.
|
||||
|
||||
## `updateQueryString( object: query )`
|
||||
|
||||
Updates the query parameters of the current page.
|
|
@ -1,12 +0,0 @@
|
|||
/** @format */
|
||||
|
||||
// 782px is the width designated by Gutenberg's `</ Popover>` component.
|
||||
// * https://github.com/WordPress/gutenberg/blob/c8f8806d4465a83c1a0bc62d5c61377b56fa7214/components/popover/utils.js#L6
|
||||
export function isMobileViewport() {
|
||||
return window.innerWidth <= 782;
|
||||
}
|
||||
|
||||
// Most screens at 1100px or lower are tablets
|
||||
export function isTabletViewport() {
|
||||
return window.innerWidth > 782 && window.innerWidth <= 1100;
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
|
||||
export const NAMESPACE = '/wc/v3/';
|
||||
export const SWAGGERNAMESPACE = 'https://virtserver.swaggerhub.com/peterfabian/wc-v3-api/1.0.0/';
|
||||
export const ERROR = 'ERROR';
|
||||
|
||||
// WordPress & WooCommerce both set a hard limit of 100 for the per_page parameter
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/** @format */
|
||||
|
||||
export default {
|
||||
setCoupons( coupons, query ) {
|
||||
return {
|
||||
type: 'SET_COUPONS',
|
||||
coupons,
|
||||
query: query || {},
|
||||
};
|
||||
},
|
||||
setCouponsError( query ) {
|
||||
return {
|
||||
type: 'SET_COUPONS_ERROR',
|
||||
query: query || {},
|
||||
};
|
||||
},
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
/** @format */
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import actions from './actions';
|
||||
import reducer from './reducer';
|
||||
import resolvers from './resolvers';
|
||||
import selectors from './selectors';
|
||||
|
||||
export default {
|
||||
actions,
|
||||
reducer,
|
||||
resolvers,
|
||||
selectors,
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
/** @format */
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { merge } from 'lodash';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ERROR } from 'store/constants';
|
||||
import { getJsonString } from 'store/utils';
|
||||
|
||||
const DEFAULT_STATE = {};
|
||||
|
||||
export default function couponsReducer( state = DEFAULT_STATE, action ) {
|
||||
const queryKey = getJsonString( action.query );
|
||||
|
||||
switch ( action.type ) {
|
||||
case 'SET_COUPONS':
|
||||
return merge( {}, state, {
|
||||
[ queryKey ]: action.coupons,
|
||||
} );
|
||||
|
||||
case 'SET_COUPONS_ERROR':
|
||||
return merge( {}, state, {
|
||||
[ queryKey ]: ERROR,
|
||||
} );
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { dispatch } from '@wordpress/data';
|
||||
// import apiFetch from '@wordpress/api-fetch';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { stringifyQuery } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
// import { NAMESPACE } from 'store/constants';
|
||||
import { SWAGGERNAMESPACE } from 'store/constants';
|
||||
|
||||
export default {
|
||||
async getCoupons( ...args ) {
|
||||
const query = args.length === 1 ? args[ 0 ] : args[ 1 ];
|
||||
|
||||
try {
|
||||
// @TODO update the API endpoint once it's ready
|
||||
// const coupons = await apiFetch( { path: NAMESPACE + 'reports/coupons' + stringifyQuery( query ) } );
|
||||
const response = await fetch(
|
||||
SWAGGERNAMESPACE + 'reports/coupons' + stringifyQuery( query )
|
||||
);
|
||||
const coupons = await response.json();
|
||||
dispatch( 'wc-admin' ).setCoupons( coupons, query );
|
||||
} catch ( error ) {
|
||||
dispatch( 'wc-admin' ).setCouponsError( query );
|
||||
}
|
||||
},
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
/** @format */
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { get } from 'lodash';
|
||||
import { select } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getJsonString } from 'store/utils';
|
||||
import { ERROR } from 'store/constants';
|
||||
|
||||
/**
|
||||
* Returns coupons for a specific query.
|
||||
*
|
||||
* @param {Object} state Current state
|
||||
* @param {Object} query Report query parameters
|
||||
* @return {Array} Report details
|
||||
*/
|
||||
function getCoupons( state, query = {} ) {
|
||||
return get( state, [ 'coupons', getJsonString( query ) ], [] );
|
||||
}
|
||||
|
||||
export default {
|
||||
getCoupons,
|
||||
|
||||
/**
|
||||
* Returns true if a getCoupons request is pending.
|
||||
*
|
||||
* @param {Object} state Current state
|
||||
* @return {Boolean} True if the `getCoupons` request is pending, false otherwise
|
||||
*/
|
||||
isGetCouponsRequesting( state, ...args ) {
|
||||
return select( 'core/data' ).isResolving( 'wc-admin', 'getCoupons', args );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if a getCoupons request has returned an error.
|
||||
*
|
||||
* @param {Object} state Current state
|
||||
* @param {Object} query Query parameters
|
||||
* @return {Boolean} True if the `getCoupons` request has failed, false otherwise
|
||||
*/
|
||||
isGetCouponsError( state, query ) {
|
||||
return ERROR === getCoupons( state, query );
|
||||
},
|
||||
};
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* @format
|
||||
*/
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import deepFreeze from 'deep-freeze';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ERROR } from 'store/constants';
|
||||
import couponsReducer from '../reducer';
|
||||
import { getJsonString } from 'store/utils';
|
||||
|
||||
describe( 'couponsReducer()', () => {
|
||||
it( 'returns an empty data object by default', () => {
|
||||
const state = couponsReducer( undefined, {} );
|
||||
expect( state ).toEqual( {} );
|
||||
} );
|
||||
|
||||
it( 'returns with received coupons data', () => {
|
||||
const originalState = deepFreeze( {} );
|
||||
const query = {
|
||||
orderby: 'orders_count',
|
||||
};
|
||||
const coupons = [ { coupon_id: 1214 }, { coupon_id: 1215 }, { coupon_id: 1216 } ];
|
||||
|
||||
const state = couponsReducer( originalState, {
|
||||
type: 'SET_COUPONS',
|
||||
query,
|
||||
coupons,
|
||||
} );
|
||||
|
||||
const queryKey = getJsonString( query );
|
||||
expect( state[ queryKey ] ).toEqual( coupons );
|
||||
} );
|
||||
|
||||
it( 'tracks multiple queries in coupons data', () => {
|
||||
const otherQuery = {
|
||||
orderby: 'coupon_id',
|
||||
};
|
||||
const otherQueryKey = getJsonString( otherQuery );
|
||||
const otherCoupons = [ { coupon_id: 1 }, { coupon_id: 2 }, { coupon_id: 3 } ];
|
||||
const otherQueryState = {
|
||||
[ otherQueryKey ]: otherCoupons,
|
||||
};
|
||||
const originalState = deepFreeze( otherQueryState );
|
||||
const query = {
|
||||
orderby: 'orders_count',
|
||||
};
|
||||
const coupons = [ { coupon_id: 1214 }, { coupon_id: 1215 }, { coupon_id: 1216 } ];
|
||||
|
||||
const state = couponsReducer( originalState, {
|
||||
type: 'SET_COUPONS',
|
||||
query,
|
||||
coupons,
|
||||
} );
|
||||
|
||||
const queryKey = getJsonString( query );
|
||||
expect( state[ queryKey ] ).toEqual( coupons );
|
||||
expect( state[ otherQueryKey ] ).toEqual( otherCoupons );
|
||||
} );
|
||||
|
||||
it( 'returns with received error data', () => {
|
||||
const originalState = deepFreeze( {} );
|
||||
const query = {
|
||||
orderby: 'orders_count',
|
||||
};
|
||||
|
||||
const state = couponsReducer( originalState, {
|
||||
type: 'SET_COUPONS_ERROR',
|
||||
query,
|
||||
} );
|
||||
|
||||
const queryKey = getJsonString( query );
|
||||
expect( state[ queryKey ] ).toEqual( ERROR );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* @format
|
||||
*/
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
import { dispatch } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import resolvers from '../resolvers';
|
||||
|
||||
const { getCoupons } = resolvers;
|
||||
|
||||
jest.mock( '@wordpress/data', () => ( {
|
||||
dispatch: jest.fn().mockReturnValue( {
|
||||
setCoupons: jest.fn(),
|
||||
} ),
|
||||
} ) );
|
||||
jest.mock( '@wordpress/api-fetch', () => jest.fn() );
|
||||
|
||||
// @TODO reactivate tests when we use the correct API routes instead of swaggerhub
|
||||
xdescribe( 'getCoupons', () => {
|
||||
const COUPONS_1 = [ { coupon_id: 1214 }, { coupon_id: 1215 }, { coupon_id: 1216 } ];
|
||||
|
||||
const COUPONS_2 = [ { coupon_id: 1 }, { coupon_id: 2 }, { coupon_id: 3 } ];
|
||||
|
||||
beforeAll( () => {
|
||||
apiFetch.mockImplementation( options => {
|
||||
if ( options.path === '/wc/v3/reports/coupons' ) {
|
||||
return Promise.resolve( COUPONS_1 );
|
||||
}
|
||||
if ( options.path === '/wc/v3/reports/coupons?orderby=coupon_id' ) {
|
||||
return Promise.resolve( COUPONS_2 );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'returns requested report data', async () => {
|
||||
expect.assertions( 1 );
|
||||
await getCoupons();
|
||||
expect( dispatch().setCoupons ).toHaveBeenCalledWith( COUPONS_1, undefined );
|
||||
} );
|
||||
|
||||
it( 'returns requested report data for a specific query', async () => {
|
||||
expect.assertions( 1 );
|
||||
await getCoupons( { orderby: 'coupon_id' } );
|
||||
expect( dispatch().setCoupons ).toHaveBeenCalledWith( COUPONS_2, { orderby: 'coupon_id' } );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* @format
|
||||
*/
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import deepFreeze from 'deep-freeze';
|
||||
import { select } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ERROR } from 'store/constants';
|
||||
import { getJsonString } from 'store/utils';
|
||||
import selectors from '../selectors';
|
||||
|
||||
const { getCoupons, isGetCouponsRequesting, isGetCouponsError } = selectors;
|
||||
jest.mock( '@wordpress/data', () => ( {
|
||||
...require.requireActual( '@wordpress/data' ),
|
||||
select: jest.fn().mockReturnValue( {} ),
|
||||
} ) );
|
||||
|
||||
const query = { orderby: 'date' };
|
||||
const queryKey = getJsonString( query );
|
||||
|
||||
describe( 'getCoupons()', () => {
|
||||
it( 'returns an empty array when no coupons are available', () => {
|
||||
const state = deepFreeze( {} );
|
||||
expect( getCoupons( state, query ) ).toEqual( [] );
|
||||
} );
|
||||
|
||||
it( 'returns stored coupons for current query', () => {
|
||||
const coupons = [ { coupon_id: 1214 }, { coupon_id: 1215 }, { coupon_id: 1216 } ];
|
||||
const state = deepFreeze( {
|
||||
coupons: {
|
||||
[ queryKey ]: coupons,
|
||||
},
|
||||
} );
|
||||
expect( getCoupons( state, query ) ).toEqual( coupons );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'isGetCouponsRequesting()', () => {
|
||||
beforeAll( () => {
|
||||
select( 'core/data' ).isResolving = jest.fn().mockReturnValue( false );
|
||||
} );
|
||||
|
||||
afterAll( () => {
|
||||
select( 'core/data' ).isResolving.mockRestore();
|
||||
} );
|
||||
|
||||
function setIsResolving( isResolving ) {
|
||||
select( 'core/data' ).isResolving.mockImplementation(
|
||||
( reducerKey, selectorName ) =>
|
||||
isResolving && reducerKey === 'wc-admin' && selectorName === 'getCoupons'
|
||||
);
|
||||
}
|
||||
|
||||
it( 'returns false if never requested', () => {
|
||||
const result = isGetCouponsRequesting( query );
|
||||
expect( result ).toBe( false );
|
||||
} );
|
||||
|
||||
it( 'returns false if request finished', () => {
|
||||
setIsResolving( false );
|
||||
const result = isGetCouponsRequesting( query );
|
||||
expect( result ).toBe( false );
|
||||
} );
|
||||
|
||||
it( 'returns true if requesting', () => {
|
||||
setIsResolving( true );
|
||||
const result = isGetCouponsRequesting( query );
|
||||
expect( result ).toBe( true );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'isGetCouponsError()', () => {
|
||||
it( 'returns false by default', () => {
|
||||
const state = deepFreeze( {} );
|
||||
expect( isGetCouponsError( state, query ) ).toEqual( false );
|
||||
} );
|
||||
|
||||
it( 'returns true if ERROR constant is found', () => {
|
||||
const state = deepFreeze( {
|
||||
coupons: {
|
||||
[ queryKey ]: ERROR,
|
||||
},
|
||||
} );
|
||||
expect( isGetCouponsError( state, query ) ).toEqual( true );
|
||||
} );
|
||||
} );
|
|
@ -2,13 +2,13 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { registerStore } from '@wordpress/data';
|
||||
import { combineReducers } from 'redux';
|
||||
import { combineReducers, registerStore } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { applyMiddleware, addThunks } from './middleware';
|
||||
import coupons from 'store/coupons';
|
||||
import orders from 'store/orders';
|
||||
import products from 'store/products';
|
||||
import reports from 'store/reports';
|
||||
|
@ -16,6 +16,7 @@ import notes from 'store/notes';
|
|||
|
||||
const store = registerStore( 'wc-admin', {
|
||||
reducer: combineReducers( {
|
||||
coupons: coupons.reducer,
|
||||
orders: orders.reducer,
|
||||
products: products.reducer,
|
||||
reports: reports.reducer,
|
||||
|
@ -23,6 +24,7 @@ const store = registerStore( 'wc-admin', {
|
|||
} ),
|
||||
|
||||
actions: {
|
||||
...coupons.actions,
|
||||
...orders.actions,
|
||||
...products.actions,
|
||||
...reports.actions,
|
||||
|
@ -30,6 +32,7 @@ const store = registerStore( 'wc-admin', {
|
|||
},
|
||||
|
||||
selectors: {
|
||||
...coupons.selectors,
|
||||
...orders.selectors,
|
||||
...products.selectors,
|
||||
...reports.selectors,
|
||||
|
@ -37,6 +40,7 @@ const store = registerStore( 'wc-admin', {
|
|||
},
|
||||
|
||||
resolvers: {
|
||||
...coupons.resolvers,
|
||||
...orders.resolvers,
|
||||
...products.resolvers,
|
||||
...reports.resolvers,
|
||||
|
|
|
@ -1,19 +1,27 @@
|
|||
/** @format */
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { dispatch } from '@wordpress/data';
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { stringifyQuery } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { stringifyQuery } from 'lib/nav-utils';
|
||||
import { NAMESPACE } from 'store/constants';
|
||||
|
||||
export default {
|
||||
async getNotes( state, query ) {
|
||||
// TODO: Use controls data plugin or fresh-data instead of async
|
||||
async getNotes( ...args ) {
|
||||
// This is interim code to work with either 2.x or 3.x version of @wordpress/data
|
||||
// TODO: Change to just `getNotes( query )` after Gutenberg plugin uses @wordpress/data 3+
|
||||
const query = args.length === 1 ? args[ 0 ] : args[ 1 ];
|
||||
|
||||
try {
|
||||
const notes = await apiFetch( { path: NAMESPACE + 'admin/notes' + stringifyQuery( query ) } );
|
||||
dispatch( 'wc-admin' ).setNotes( notes, query );
|
||||
|
|
|
@ -40,13 +40,13 @@ describe( 'getNotes', () => {
|
|||
|
||||
it( 'returns requested data', async () => {
|
||||
expect.assertions( 1 );
|
||||
await getNotes( {} );
|
||||
await getNotes();
|
||||
expect( dispatch().setNotes ).toHaveBeenCalledWith( NOTES_1, undefined );
|
||||
} );
|
||||
|
||||
it( 'returns requested data for a specific query', async () => {
|
||||
expect.assertions( 1 );
|
||||
await getNotes( {}, { page: 2 } );
|
||||
await getNotes( { page: 2 } );
|
||||
expect( dispatch().setNotes ).toHaveBeenCalledWith( NOTES_2, { page: 2 } );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -2,15 +2,14 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
||||
import reducer from './reducer';
|
||||
import actions from './actions';
|
||||
import selectors from './selectors';
|
||||
import reducer from './reducer';
|
||||
import resolvers from './resolvers';
|
||||
import selectors from './selectors';
|
||||
|
||||
export default {
|
||||
reducer,
|
||||
actions,
|
||||
selectors,
|
||||
reducer,
|
||||
resolvers,
|
||||
selectors,
|
||||
};
|
||||
|
|
|
@ -5,14 +5,23 @@
|
|||
import { dispatch } from '@wordpress/data';
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { stringifyQuery } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { stringifyQuery } from 'lib/nav-utils';
|
||||
import { NAMESPACE } from 'store/constants';
|
||||
|
||||
export default {
|
||||
async getOrders( state, query ) {
|
||||
// TODO: Use controls data plugin or fresh-data instead of async
|
||||
async getOrders( ...args ) {
|
||||
// This is interim code to work with either 2.x or 3.x version of @wordpress/data
|
||||
// TODO: Change to just `getNotes( query )` after Gutenberg plugin uses @wordpress/data 3+
|
||||
const query = args.length === 1 ? args[ 0 ] : args[ 1 ];
|
||||
|
||||
try {
|
||||
const orders = await apiFetch( { path: NAMESPACE + 'orders' + stringifyQuery( query ) } );
|
||||
dispatch( 'wc-admin' ).setOrders( orders, query );
|
||||
|
|
|
@ -16,7 +16,7 @@ import { ERROR } from 'store/constants';
|
|||
* Returns orders for a specific query.
|
||||
*
|
||||
* @param {Object} state Current state
|
||||
* @param {Object} query Report query paremters
|
||||
* @param {Object} query Report query parameters
|
||||
* @return {Array} Report details
|
||||
*/
|
||||
function getOrders( state, query = {} ) {
|
||||
|
@ -27,7 +27,7 @@ export default {
|
|||
getOrders,
|
||||
|
||||
/**
|
||||
* Returns true if a query is pending.
|
||||
* Returns true if a getOrders request is pending.
|
||||
*
|
||||
* @param {Object} state Current state
|
||||
* @return {Boolean} True if the `getOrders` request is pending, false otherwise
|
||||
|
@ -37,7 +37,7 @@ export default {
|
|||
},
|
||||
|
||||
/**
|
||||
* Returns true if a get orders request has returned an error.
|
||||
* Returns true if a getOrders request has returned an error.
|
||||
*
|
||||
* @param {Object} state Current state
|
||||
* @param {Object} query Query parameters
|
||||
|
|
|
@ -40,13 +40,13 @@ describe( 'getOrders', () => {
|
|||
|
||||
it( 'returns requested report data', async () => {
|
||||
expect.assertions( 1 );
|
||||
await getOrders( {} );
|
||||
await getOrders();
|
||||
expect( dispatch().setOrders ).toHaveBeenCalledWith( ORDERS_1, undefined );
|
||||
} );
|
||||
|
||||
it( 'returns requested report data for a specific query', async () => {
|
||||
expect.assertions( 1 );
|
||||
await getOrders( {}, { orderby: 'id' } );
|
||||
await getOrders( { orderby: 'id' } );
|
||||
expect( dispatch().setOrders ).toHaveBeenCalledWith( ORDERS_2, { orderby: 'id' } );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import deepFreeze from 'deep-freeze';
|
||||
import { select } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ERROR } from 'store/constants';
|
||||
import selectors from '../selectors';
|
||||
import { select } from '@wordpress/data';
|
||||
import { getJsonString } from 'store/utils';
|
||||
|
||||
const { getOrders, isGetOrdersRequesting, isGetOrdersError } = selectors;
|
||||
|
|
|
@ -6,11 +6,21 @@ import apiFetch from '@wordpress/api-fetch';
|
|||
import { dispatch } from '@wordpress/data';
|
||||
import { stringify } from 'qs';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { NAMESPACE } from 'store/constants';
|
||||
|
||||
export default {
|
||||
async getProducts( state, query ) {
|
||||
// TODO: Use controls data plugin or fresh-data instead of async
|
||||
async getProducts( ...args ) {
|
||||
// This is interim code to work with either 2.x or 3.x version of @wordpress/data
|
||||
// TODO: Change to just `getNotes( query )` after Gutenberg plugin uses @wordpress/data 3+
|
||||
const query = args.length === 1 ? args[ 0 ] : args[ 1 ];
|
||||
|
||||
try {
|
||||
const params = query ? '?' + stringify( query ) : '';
|
||||
const products = await apiFetch( { path: '/wc/v3/reports/products' + params } );
|
||||
const products = await apiFetch( { path: NAMESPACE + 'reports/products' + params } );
|
||||
dispatch( 'wc-admin' ).setProducts( products, query );
|
||||
} catch ( error ) {
|
||||
dispatch( 'wc-admin' ).setProductsError( query );
|
||||
|
|
|
@ -16,7 +16,7 @@ import { getJsonString } from 'store/utils';
|
|||
* Returns products report details for a specific report query.
|
||||
*
|
||||
* @param {Object} state Current state
|
||||
* @param {Object} query Report query paremters
|
||||
* @param {Object} query Report query parameters
|
||||
* @return {Object} Report details
|
||||
*/
|
||||
function getProducts( state, query = {} ) {
|
||||
|
@ -27,21 +27,21 @@ export default {
|
|||
getProducts,
|
||||
|
||||
/**
|
||||
* Returns true if a products request is pending.
|
||||
* Returns true if a getProducts request is pending.
|
||||
*
|
||||
* @param {Object} state Current state
|
||||
* @return {Object} True if the `getProducts` request is pending, false otherwise
|
||||
* @return {Boolean} True if the `getProducts` request is pending, false otherwise
|
||||
*/
|
||||
isGetProductsRequesting( state, ...args ) {
|
||||
return select( 'core/data' ).isResolving( 'wc-admin', 'getProducts', args );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if a products request has returned an error.
|
||||
* Returns true if a getProducts request has returned an error.
|
||||
*
|
||||
* @param {Object} state Current state
|
||||
* @param {Object} query Report query paremters
|
||||
* @return {Object} True if the `getProducts` request has failed, false otherwise
|
||||
* @param {Object} query Report query parameters
|
||||
* @return {Boolean} True if the `getProducts` request has failed, false otherwise
|
||||
*/
|
||||
isGetProductsError( state, query ) {
|
||||
return ERROR === getProducts( state, query );
|
||||
|
|
|
@ -57,13 +57,13 @@ describe( 'getProducts', () => {
|
|||
|
||||
it( 'returns requested products', async () => {
|
||||
expect.assertions( 1 );
|
||||
await getProducts( {} );
|
||||
await getProducts();
|
||||
expect( dispatch().setProducts ).toHaveBeenCalledWith( PRODUCTS_1, undefined );
|
||||
} );
|
||||
|
||||
it( 'returns requested products for a specific query', async () => {
|
||||
expect.assertions( 1 );
|
||||
await getProducts( {}, { orderby: 'date' } );
|
||||
await getProducts( { orderby: 'date' } );
|
||||
expect( dispatch().setProducts ).toHaveBeenCalledWith( PRODUCTS_2, { orderby: 'date' } );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import deepFreeze from 'deep-freeze';
|
||||
import { select } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ERROR } from 'store/constants';
|
||||
import selectors from '../selectors';
|
||||
import { select } from '@wordpress/data';
|
||||
import { getJsonString } from 'store/utils';
|
||||
|
||||
const { getProducts, isGetProductsRequesting, isGetProductsError } = selectors;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { combineReducers } from 'redux';
|
||||
import { combineReducers } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
* Internal dependencies
|
||||
*/
|
||||
import actions from './actions';
|
||||
import selectors from './selectors';
|
||||
import reducer from './reducer';
|
||||
import resolvers from './resolvers';
|
||||
import selectors from './selectors';
|
||||
|
||||
export default {
|
||||
actions,
|
||||
selectors,
|
||||
reducer,
|
||||
resolvers,
|
||||
selectors,
|
||||
};
|
||||
|
|
|
@ -6,17 +6,42 @@
|
|||
import apiFetch from '@wordpress/api-fetch';
|
||||
import { dispatch } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { stringifyQuery } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { stringifyQuery } from 'lib/nav-utils';
|
||||
import { NAMESPACE } from 'store/constants';
|
||||
import { NAMESPACE, SWAGGERNAMESPACE } from 'store/constants';
|
||||
|
||||
export default {
|
||||
async getReportStats( state, endpoint, query ) {
|
||||
// TODO: Use controls data plugin or fresh-data instead of async
|
||||
async getReportStats( ...args ) {
|
||||
// This is interim code to work with either 2.x or 3.x version of @wordpress/data
|
||||
// TODO: Change to just `getNotes( endpoint, query )`
|
||||
// after Gutenberg plugin uses @wordpress/data 3+
|
||||
const [ endpoint, query ] = args.length === 2 ? args : args.slice( 1, 3 );
|
||||
const statEndpoints = [ 'orders', 'revenue', 'products' ];
|
||||
|
||||
let apiPath = endpoint + stringifyQuery( query );
|
||||
|
||||
// TODO: Remove once swagger endpoints are phased out.
|
||||
const swaggerEndpoints = [ 'coupons', 'taxes' ];
|
||||
if ( swaggerEndpoints.indexOf( endpoint ) >= 0 ) {
|
||||
apiPath = SWAGGERNAMESPACE + 'reports/' + endpoint + '/stats' + stringifyQuery( query );
|
||||
try {
|
||||
const response = await fetch( apiPath );
|
||||
|
||||
const report = await response.json();
|
||||
dispatch( 'wc-admin' ).setReportStats( endpoint, report, query );
|
||||
} catch ( error ) {
|
||||
dispatch( 'wc-admin' ).setReportStatsError( endpoint, query );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( statEndpoints.indexOf( endpoint ) >= 0 ) {
|
||||
apiPath = NAMESPACE + 'reports/' + endpoint + '/stats' + stringifyQuery( query );
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import { getJsonString } from 'store/utils';
|
|||
*
|
||||
* @param {Object} state Current state
|
||||
* @param {String} endpoint Stats endpoint
|
||||
* @param {Object} query Report query paremters
|
||||
* @param {Object} query Report query parameters
|
||||
* @return {Object} Report details
|
||||
*/
|
||||
function getReportStats( state, endpoint, query = {} ) {
|
||||
|
@ -29,7 +29,7 @@ export default {
|
|||
getReportStats,
|
||||
|
||||
/**
|
||||
* Returns true if a stat query is pending.
|
||||
* Returns true if a stats query is pending.
|
||||
*
|
||||
* @param {Object} state Current state
|
||||
* @return {Boolean} True if the `getReportRevenueStats` request is pending, false otherwise
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import deepFreeze from 'deep-freeze';
|
||||
import { select } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ERROR } from 'store/constants';
|
||||
import selectors from '../selectors';
|
||||
import { select } from '@wordpress/data';
|
||||
|
||||
const { getReportStats, isReportStatsRequesting, isReportStatsError } = selectors;
|
||||
jest.mock( '@wordpress/data', () => ( {
|
||||
|
|
|
@ -9,21 +9,22 @@ import { find, forEach, isNull } from 'lodash';
|
|||
* WooCommerce dependencies
|
||||
*/
|
||||
import { appendTimestamp, getCurrentDates, getIntervalForQuery } from '@woocommerce/date';
|
||||
import { flattenFilters, getActiveFiltersFromQuery, getUrlKey } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { MAX_PER_PAGE } from 'store/constants';
|
||||
import { getActiveFiltersFromQuery, getUrlKey } from 'components/filters/advanced/utils';
|
||||
import { flatenFilters } from 'components/filters/filter/utils';
|
||||
import * as couponsConfig from 'analytics/report/coupons/config';
|
||||
import * as ordersConfig from 'analytics/report/orders/config';
|
||||
import * as productsConfig from 'analytics/report/products/config';
|
||||
import * as taxesConfig from 'analytics/report/taxes/config';
|
||||
|
||||
const reportConfigs = {
|
||||
coupons: couponsConfig,
|
||||
orders: ordersConfig,
|
||||
products: productsConfig,
|
||||
taxes: taxesConfig,
|
||||
};
|
||||
|
||||
export function getFilterQuery( endpoint, query ) {
|
||||
|
@ -60,7 +61,7 @@ export function getQueryFromConfig( config, advancedFilters, query ) {
|
|||
);
|
||||
}
|
||||
|
||||
const filter = find( flatenFilters( config.filters ), { value: queryValue } );
|
||||
const filter = find( flattenFilters( config.filters ), { value: queryValue } );
|
||||
|
||||
if ( ! filter ) {
|
||||
return {};
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
/** @format */
|
||||
|
||||
/* stylelint-disable block-closing-brace-newline-after */
|
||||
|
||||
// Breakpoints
|
||||
// Forked from https://github.com/Automattic/wp-calypso/blob/46ae24d8800fb85da6acf057a640e60dac988a38/assets/stylesheets/shared/mixins/_breakpoints.scss
|
||||
|
||||
// Think very carefully before adding a new breakpoint.
|
||||
// The list below is based on wp-admin's main breakpoints
|
||||
$breakpoints: 320px, 400px, 600px, 782px, 960px, 1100px, 1365px;
|
||||
$breakpoints: 320px, 400px, 600px, 782px, 960px, 1280px, 1440px;
|
||||
|
||||
@mixin breakpoint( $sizes... ) {
|
||||
@each $size in $sizes {
|
||||
|
@ -44,14 +46,16 @@ $breakpoints: 320px, 400px, 600px, 782px, 960px, 1100px, 1365px;
|
|||
@each $breakpoint in $breakpoints {
|
||||
$sizes: $sizes + ' ' + $breakpoint;
|
||||
}
|
||||
@warn "ERROR in breakpoint( #{ $size } ) : You can only use these sizes[ #{$sizes} ] using the following syntax [ <#{ nth( $breakpoints, 1 ) } >#{ nth( $breakpoints, 1 ) } #{ nth( $breakpoints, 1 ) }-#{ nth( $breakpoints, 2 ) } ]";
|
||||
@warn 'ERROR in breakpoint( #{ $size } ) : You can only use these sizes[ #{$sizes} ] using the following syntax [ <#{ nth( $breakpoints, 1 ) } >#{ nth( $breakpoints, 1 ) } #{ nth( $breakpoints, 1 ) }-#{ nth( $breakpoints, 2 ) } ]';
|
||||
}
|
||||
} @else {
|
||||
$sizes: '';
|
||||
@each $breakpoint in $breakpoints {
|
||||
$sizes: $sizes + ' ' + $breakpoint;
|
||||
}
|
||||
@error "ERROR in breakpoint( #{ $size } ) : Please wrap the breakpoint $size in parenthesis. You can use these sizes[ #{$sizes} ] using the following syntax [ <#{ nth( $breakpoints, 1 ) } >#{ nth( $breakpoints, 1 ) } #{ nth( $breakpoints, 1 ) }-#{ nth( $breakpoints, 2 ) } ]";
|
||||
@error 'ERROR in breakpoint( #{ $size } ) : Please wrap the breakpoint $size in parenthesis. You can use these sizes[ #{$sizes} ] using the following syntax [ <#{ nth( $breakpoints, 1 ) } >#{ nth( $breakpoints, 1 ) } #{ nth( $breakpoints, 1 ) }-#{ nth( $breakpoints, 2 ) } ]';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* stylelint-enable */
|
||||
|
|
|
@ -23,8 +23,3 @@ $light-gray-500: $core-grey-light-500;
|
|||
$dark-gray-300: $core-grey-dark-300;
|
||||
$dark-gray-900: $core-grey-dark-900;
|
||||
$alert-red: $error-red;
|
||||
|
||||
:export {
|
||||
gaplarge: $gap-large;
|
||||
gap: $gap;
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
margin: 0;
|
||||
padding-top: 80px;
|
||||
|
||||
@include breakpoint( '782px-1100px' ) {
|
||||
@include breakpoint( '782px-960px' ) {
|
||||
padding-top: 60px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
--large-gap: 40px;
|
||||
--main-gap: 24px;
|
||||
}
|
||||
@media (max-width: 1100px) {
|
||||
@media (max-width: 960px) {
|
||||
:root {
|
||||
--large-gap: 24px;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
},
|
||||
"require-dev": {
|
||||
"squizlabs/php_codesniffer": "*",
|
||||
"wp-coding-standards/wpcs": "1.1.0",
|
||||
"phpunit/phpunit": "7.4.3",
|
||||
"wp-coding-standards/wpcs": "1.2.0",
|
||||
"phpunit/phpunit": "6.5.13",
|
||||
"woocommerce/woocommerce-sniffs": "*",
|
||||
"wimg/php-compatibility": "9.0.0",
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "0.5.0"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "97ef081df4492fe71d877ad105e1709b",
|
||||
"content-hash": "8c25f2e044c24bd6ee6900e7bcbe3c0c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/installers",
|
||||
|
@ -298,22 +298,22 @@
|
|||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phar-io/manifest.git",
|
||||
"reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4"
|
||||
"reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
|
||||
"reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
|
||||
"url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0",
|
||||
"reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-phar": "*",
|
||||
"phar-io/version": "^2.0",
|
||||
"phar-io/version": "^1.0.1",
|
||||
"php": "^5.6 || ^7.0"
|
||||
},
|
||||
"type": "library",
|
||||
|
@ -349,20 +349,20 @@
|
|||
}
|
||||
],
|
||||
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
|
||||
"time": "2018-07-08T19:23:20+00:00"
|
||||
"time": "2017-03-05T18:14:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/version",
|
||||
"version": "2.0.1",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phar-io/version.git",
|
||||
"reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6"
|
||||
"reference": "a70c0ced4be299a63d32fa96d9281d03e94041df"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6",
|
||||
"reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6",
|
||||
"url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df",
|
||||
"reference": "a70c0ced4be299a63d32fa96d9281d03e94041df",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -396,7 +396,7 @@
|
|||
}
|
||||
],
|
||||
"description": "Library for handling version information and constraints",
|
||||
"time": "2018-07-08T19:19:57+00:00"
|
||||
"time": "2017-03-05T17:38:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
|
@ -615,40 +615,40 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "6.1.3",
|
||||
"version": "5.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "4d3ae9b21a7d7e440bd0cf65565533117976859f"
|
||||
"reference": "c89677919c5dd6d3b3852f230a663118762218ac"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4d3ae9b21a7d7e440bd0cf65565533117976859f",
|
||||
"reference": "4d3ae9b21a7d7e440bd0cf65565533117976859f",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac",
|
||||
"reference": "c89677919c5dd6d3b3852f230a663118762218ac",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"php": "^7.1",
|
||||
"phpunit/php-file-iterator": "^2.0",
|
||||
"php": "^7.0",
|
||||
"phpunit/php-file-iterator": "^1.4.2",
|
||||
"phpunit/php-text-template": "^1.2.1",
|
||||
"phpunit/php-token-stream": "^3.0",
|
||||
"phpunit/php-token-stream": "^2.0.1",
|
||||
"sebastian/code-unit-reverse-lookup": "^1.0.1",
|
||||
"sebastian/environment": "^3.1 || ^4.0",
|
||||
"sebastian/environment": "^3.0",
|
||||
"sebastian/version": "^2.0.1",
|
||||
"theseer/tokenizer": "^1.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"phpunit/phpunit": "^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-xdebug": "^2.6.0"
|
||||
"ext-xdebug": "^2.5.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "6.1-dev"
|
||||
"dev-master": "5.3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -674,32 +674,29 @@
|
|||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2018-10-23T05:59:32+00:00"
|
||||
"time": "2018-04-06T15:36:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
"version": "2.0.2",
|
||||
"version": "1.4.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
|
||||
"reference": "050bedf145a257b1ff02746c31894800e5122946"
|
||||
"reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946",
|
||||
"reference": "050bedf145a257b1ff02746c31894800e5122946",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
|
||||
"reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.1"
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
"dev-master": "1.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -714,7 +711,7 @@
|
|||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"email": "sebastian@phpunit.de",
|
||||
"email": "sb@sebastian-bergmann.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
|
@ -724,7 +721,7 @@
|
|||
"filesystem",
|
||||
"iterator"
|
||||
],
|
||||
"time": "2018-09-13T20:33:42+00:00"
|
||||
"time": "2017-11-27T13:52:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-text-template",
|
||||
|
@ -769,28 +766,28 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/php-timer",
|
||||
"version": "2.0.0",
|
||||
"version": "1.0.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-timer.git",
|
||||
"reference": "8b8454ea6958c3dee38453d3bd571e023108c91f"
|
||||
"reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f",
|
||||
"reference": "8b8454ea6958c3dee38453d3bd571e023108c91f",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
|
||||
"reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1"
|
||||
"php": "^5.3.3 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0-dev"
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -805,7 +802,7 @@
|
|||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"email": "sebastian@phpunit.de",
|
||||
"email": "sb@sebastian-bergmann.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
|
@ -814,33 +811,33 @@
|
|||
"keywords": [
|
||||
"timer"
|
||||
],
|
||||
"time": "2018-02-01T13:07:23+00:00"
|
||||
"time": "2017-02-26T11:10:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-token-stream",
|
||||
"version": "3.0.0",
|
||||
"version": "2.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
|
||||
"reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace"
|
||||
"reference": "791198a2c6254db10131eecfe8c06670700904db"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/21ad88bbba7c3d93530d93994e0a33cd45f02ace",
|
||||
"reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db",
|
||||
"reference": "791198a2c6254db10131eecfe8c06670700904db",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-tokenizer": "*",
|
||||
"php": "^7.1"
|
||||
"php": "^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"phpunit/phpunit": "^6.2.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
"dev-master": "2.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -863,57 +860,57 @@
|
|||
"keywords": [
|
||||
"tokenizer"
|
||||
],
|
||||
"time": "2018-02-01T13:16:43+00:00"
|
||||
"time": "2017-11-27T05:48:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "7.4.3",
|
||||
"version": "6.5.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "c151651fb6ed264038d486ea262e243af72e5e64"
|
||||
"reference": "0973426fb012359b2f18d3bd1e90ef1172839693"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c151651fb6ed264038d486ea262e243af72e5e64",
|
||||
"reference": "c151651fb6ed264038d486ea262e243af72e5e64",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0973426fb012359b2f18d3bd1e90ef1172839693",
|
||||
"reference": "0973426fb012359b2f18d3bd1e90ef1172839693",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.1",
|
||||
"ext-dom": "*",
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-xml": "*",
|
||||
"myclabs/deep-copy": "^1.7",
|
||||
"phar-io/manifest": "^1.0.2",
|
||||
"phar-io/version": "^2.0",
|
||||
"php": "^7.1",
|
||||
"myclabs/deep-copy": "^1.6.1",
|
||||
"phar-io/manifest": "^1.0.1",
|
||||
"phar-io/version": "^1.0",
|
||||
"php": "^7.0",
|
||||
"phpspec/prophecy": "^1.7",
|
||||
"phpunit/php-code-coverage": "^6.0.7",
|
||||
"phpunit/php-file-iterator": "^2.0.1",
|
||||
"phpunit/php-code-coverage": "^5.3",
|
||||
"phpunit/php-file-iterator": "^1.4.3",
|
||||
"phpunit/php-text-template": "^1.2.1",
|
||||
"phpunit/php-timer": "^2.0",
|
||||
"sebastian/comparator": "^3.0",
|
||||
"sebastian/diff": "^3.0",
|
||||
"sebastian/environment": "^3.1 || ^4.0",
|
||||
"phpunit/php-timer": "^1.0.9",
|
||||
"phpunit/phpunit-mock-objects": "^5.0.9",
|
||||
"sebastian/comparator": "^2.1",
|
||||
"sebastian/diff": "^2.0",
|
||||
"sebastian/environment": "^3.1",
|
||||
"sebastian/exporter": "^3.1",
|
||||
"sebastian/global-state": "^2.0",
|
||||
"sebastian/object-enumerator": "^3.0.3",
|
||||
"sebastian/resource-operations": "^2.0",
|
||||
"sebastian/resource-operations": "^1.0",
|
||||
"sebastian/version": "^2.0.1"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit-mock-objects": "*"
|
||||
"phpdocumentor/reflection-docblock": "3.0.2",
|
||||
"phpunit/dbunit": "<3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-pdo": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-soap": "*",
|
||||
"ext-xdebug": "*",
|
||||
"phpunit/php-invoker": "^2.0"
|
||||
"phpunit/php-invoker": "^1.1"
|
||||
},
|
||||
"bin": [
|
||||
"phpunit"
|
||||
|
@ -921,7 +918,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "7.4-dev"
|
||||
"dev-master": "6.5.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -947,7 +944,66 @@
|
|||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2018-10-23T05:57:41+00:00"
|
||||
"time": "2018-09-08T15:10:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit-mock-objects",
|
||||
"version": "5.0.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
|
||||
"reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f",
|
||||
"reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.0.5",
|
||||
"php": "^7.0",
|
||||
"phpunit/php-text-template": "^1.2.1",
|
||||
"sebastian/exporter": "^3.1"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": "<6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.5.11"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-soap": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"email": "sebastian@phpunit.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
"description": "Mock Object library for PHPUnit",
|
||||
"homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
|
||||
"keywords": [
|
||||
"mock",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2018-08-09T05:50:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/code-unit-reverse-lookup",
|
||||
|
@ -996,30 +1052,30 @@
|
|||
},
|
||||
{
|
||||
"name": "sebastian/comparator",
|
||||
"version": "3.0.2",
|
||||
"version": "2.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/comparator.git",
|
||||
"reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da"
|
||||
"reference": "34369daee48eafb2651bea869b4b15d75ccc35f9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
|
||||
"reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9",
|
||||
"reference": "34369daee48eafb2651bea869b4b15d75ccc35f9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"sebastian/diff": "^3.0",
|
||||
"php": "^7.0",
|
||||
"sebastian/diff": "^2.0 || ^3.0",
|
||||
"sebastian/exporter": "^3.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.1"
|
||||
"phpunit/phpunit": "^6.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
"dev-master": "2.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -1056,33 +1112,32 @@
|
|||
"compare",
|
||||
"equality"
|
||||
],
|
||||
"time": "2018-07-12T15:12:46+00:00"
|
||||
"time": "2018-02-01T13:46:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/diff",
|
||||
"version": "3.0.1",
|
||||
"version": "2.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/diff.git",
|
||||
"reference": "366541b989927187c4ca70490a35615d3fef2dce"
|
||||
"reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce",
|
||||
"reference": "366541b989927187c4ca70490a35615d3fef2dce",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd",
|
||||
"reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1"
|
||||
"php": "^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"symfony/process": "^2 || ^3.3 || ^4"
|
||||
"phpunit/phpunit": "^6.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
"dev-master": "2.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -1107,12 +1162,9 @@
|
|||
"description": "Diff implementation",
|
||||
"homepage": "https://github.com/sebastianbergmann/diff",
|
||||
"keywords": [
|
||||
"diff",
|
||||
"udiff",
|
||||
"unidiff",
|
||||
"unified diff"
|
||||
"diff"
|
||||
],
|
||||
"time": "2018-06-10T07:54:39+00:00"
|
||||
"time": "2017-08-03T08:09:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/environment",
|
||||
|
@ -1429,25 +1481,25 @@
|
|||
},
|
||||
{
|
||||
"name": "sebastian/resource-operations",
|
||||
"version": "2.0.1",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/resource-operations.git",
|
||||
"reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9"
|
||||
"reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
|
||||
"reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
|
||||
"reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1"
|
||||
"php": ">=5.6.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0-dev"
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -1467,7 +1519,7 @@
|
|||
],
|
||||
"description": "Provides a list of PHP built-in functions that operate on resources",
|
||||
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
|
||||
"time": "2018-10-04T04:07:39+00:00"
|
||||
"time": "2015-07-28T20:34:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/version",
|
||||
|
@ -1713,24 +1765,23 @@
|
|||
},
|
||||
{
|
||||
"name": "woocommerce/woocommerce-sniffs",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce-sniffs.git",
|
||||
"reference": "2890fd5d98b318f62acb42f2b5cd6d02627cfd82"
|
||||
"reference": "c7e6e641e47b397ee64eb52a762c265cf476495a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-sniffs/zipball/2890fd5d98b318f62acb42f2b5cd6d02627cfd82",
|
||||
"reference": "2890fd5d98b318f62acb42f2b5cd6d02627cfd82",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-sniffs/zipball/c7e6e641e47b397ee64eb52a762c265cf476495a",
|
||||
"reference": "c7e6e641e47b397ee64eb52a762c265cf476495a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^0.5.0",
|
||||
"php": ">=7.0",
|
||||
"squizlabs/php_codesniffer": "^3.0.2"
|
||||
},
|
||||
"suggest": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.3"
|
||||
"wimg/php-compatibility": "^9.0",
|
||||
"wp-coding-standards/wpcs": "^1.1"
|
||||
},
|
||||
"type": "phpcodesniffer-standard",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
|
@ -1750,20 +1801,20 @@
|
|||
"woocommerce",
|
||||
"wordpress"
|
||||
],
|
||||
"time": "2018-03-22T18:39:19+00:00"
|
||||
"time": "2018-11-06T22:51:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "wp-coding-standards/wpcs",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards.git",
|
||||
"reference": "46d42828ce7355d8b3776e3171f2bda892d179b4"
|
||||
"reference": "7aa217ab38156c5cb4eae0f04ae376027c407a9b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/WordPress-Coding-Standards/WordPress-Coding-Standards/zipball/46d42828ce7355d8b3776e3171f2bda892d179b4",
|
||||
"reference": "46d42828ce7355d8b3776e3171f2bda892d179b4",
|
||||
"url": "https://api.github.com/repos/WordPress-Coding-Standards/WordPress-Coding-Standards/zipball/7aa217ab38156c5cb4eae0f04ae376027c407a9b",
|
||||
"reference": "7aa217ab38156c5cb4eae0f04ae376027c407a9b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1771,7 +1822,7 @@
|
|||
"squizlabs/php_codesniffer": "^2.9.0 || ^3.0.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpcompatibility/php-compatibility": "*"
|
||||
"phpcompatibility/php-compatibility": "^9.0"
|
||||
},
|
||||
"suggest": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.3 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically."
|
||||
|
@ -1793,7 +1844,7 @@
|
|||
"standards",
|
||||
"wordpress"
|
||||
],
|
||||
"time": "2018-09-10T17:04:05+00:00"
|
||||
"time": "2018-11-12T10:13:12+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
* [Data](data)
|
||||
* [Layout](layout)
|
||||
* [CSS Structure](stylesheets)
|
||||
* [Examples](examples/)
|
||||
|
|
|
@ -20,9 +20,11 @@
|
|||
* [ProductImage](components/product-image.md)
|
||||
* [Rating](components/rating.md)
|
||||
* [Search](components/search.md)
|
||||
* [SectionHeader](components/section-header.md)
|
||||
* [Section](components/section.md)
|
||||
* [SegmentedSelection](components/segmented-selection.md)
|
||||
* [SplitButton](components/split-button.md)
|
||||
* [Summary](components/summary.md)
|
||||
* [Table](components/table.md)
|
||||
* [Tag](components/tag.md)
|
||||
* [ViewMoreList](components/view-more-list.md)
|
||||
|
|
|
@ -29,7 +29,6 @@ An `EllipsisMenu`, with filters used to control the content visible in this card
|
|||
|
||||
### `title`
|
||||
|
||||
- **Required**
|
||||
- Type: One of type: string, node
|
||||
- Default: null
|
||||
|
||||
|
|
|
@ -20,6 +20,13 @@ An array of data.
|
|||
|
||||
Format to parse dates into d3 time format
|
||||
|
||||
### `path`
|
||||
|
||||
- Type: String
|
||||
- Default: null
|
||||
|
||||
Current path
|
||||
|
||||
### `pointLabelFormat`
|
||||
|
||||
- Type: String
|
||||
|
@ -27,6 +34,13 @@ Format to parse dates into d3 time format
|
|||
|
||||
Date format of the point labels (might be used in tooltips and ARIA properties).
|
||||
|
||||
### `query`
|
||||
|
||||
- Type: Object
|
||||
- Default: null
|
||||
|
||||
The query string represented in object form
|
||||
|
||||
### `tooltipFormat`
|
||||
|
||||
- Type: String
|
||||
|
@ -114,6 +128,13 @@ Interval specification (hourly, daily, weekly etc).
|
|||
|
||||
Allowed intervals to show in a dropdown.
|
||||
|
||||
### `valueType`
|
||||
|
||||
- Type: String
|
||||
- Default: null
|
||||
|
||||
What type of data is to be displayed? Number, Average, String?
|
||||
|
||||
`D3Chart` (component)
|
||||
=====================
|
||||
|
||||
|
@ -218,6 +239,13 @@ The list of labels for this chart.
|
|||
A datetime formatting string to format the date displayed as the title of the toolip
|
||||
if `tooltipTitle` is missing, passed to d3TimeFormat.
|
||||
|
||||
### `tooltipPosition`
|
||||
|
||||
- Type: String
|
||||
- Default: `'over'`
|
||||
|
||||
The position where to render the tooltip can be `over` the chart or `below` the chart.
|
||||
|
||||
### `tooltipTitle`
|
||||
|
||||
- Type: String
|
||||
|
@ -311,6 +339,21 @@ Handles `onMouseEnter`/`onMouseLeave` events.
|
|||
|
||||
Display legend items as a `row` or `column` inside a flex-box.
|
||||
|
||||
### `itemsLabel`
|
||||
|
||||
- Type: String
|
||||
- Default: null
|
||||
|
||||
Label to describe the legend items. It will be displayed in the legend of
|
||||
comparison charts when there are many.
|
||||
|
||||
### `valueType`
|
||||
|
||||
- Type: String
|
||||
- Default: null
|
||||
|
||||
What type of data is to be displayed? Number, Average, String?
|
||||
|
||||
`ChartPlaceholder` (component)
|
||||
==============================
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue