Merge branch 'trunk' into fix/tax_lookup_and_order_stat_deletion

This commit is contained in:
Nestor Soriano 2023-02-03 10:19:59 +01:00
commit ad7d209e56
No known key found for this signature in database
GPG Key ID: 08110F3518C12CAD
534 changed files with 6922 additions and 4857 deletions

View File

@ -27,26 +27,26 @@ runs:
echo "BUILD_FILTERS=$(node ./.github/actions/setup-woocommerce-monorepo/scripts/parse-input-filter.js '${{ inputs.build-filters }}')" >> $GITHUB_OUTPUT
- name: Setup PNPM
uses: pnpm/action-setup@10693b3829bf86eb2572aef5f3571dcf5ca9287d
uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd
with:
version: '^7.13.3'
version: '^7.22.0'
- name: Setup Node
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c
with:
node-version-file: .nvmrc
cache: pnpm
registry-url: 'https://registry.npmjs.org'
- name: Setup PHP
uses: shivammathur/setup-php@e04e1d97f0c0481c6e1ba40f8a538454fe5d7709
uses: shivammathur/setup-php@8e2ac35f639d3e794c1da1f28999385ab6fdf0fc
with:
php-version: ${{ inputs.php-version }}
coverage: none
tools: phpcs, sirbrillig/phpcs-changed
- name: Cache Composer Dependencies
uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77
uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12
with:
path: ~/.cache/composer/files
key: ${{ runner.os }}-php-${{ inputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
@ -59,7 +59,7 @@ runs:
pnpm install ${{ steps.parse-input.outputs.INSTALL_FILTERS }}
- name: Cache Build Output
uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77
uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12
with:
path: node_modules/.cache/turbo
key: ${{ runner.os }}-build-output-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}

View File

@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c
- name: Install Octokit
run: npm --prefix .github/workflows/scripts install @octokit/action

View File

@ -1,4 +1,4 @@
name: "Pull request post-merge processing"
name: 'Pull request post-merge processing'
on:
pull_request_target:
types: [closed]
@ -13,7 +13,7 @@ jobs:
permissions:
pull-requests: write
steps:
- name: "Get the action scripts"
- name: 'Get the action scripts'
run: |
scripts="assign-milestone-to-merged-pr.php add-post-merge-comment.php post-request-shared.php"
for script in $scripts
@ -29,11 +29,11 @@ jobs:
done
env:
GITHUB_API_URL: ${{ env.GITHUB_API_URL }}
- name: "Install PHP"
uses: shivammathur/setup-php@v2
- name: 'Install PHP'
uses: shivammathur/setup-php@8e2ac35f639d3e794c1da1f28999385ab6fdf0fc
with:
php-version: '7.4'
- name: "Run the script to assign a milestone"
- name: 'Run the script to assign a milestone'
if: |
!github.event.pull_request.milestone &&
github.event.pull_request.base.ref == 'trunk'

View File

@ -30,7 +30,7 @@ jobs:
freeze: ${{ steps.check-freeze.outputs.freeze }}
steps:
- name: 'Install PHP'
uses: shivammathur/setup-php@v2
uses: shivammathur/setup-php@8e2ac35f639d3e794c1da1f28999385ab6fdf0fc
with:
php-version: '7.4'

View File

@ -17,11 +17,56 @@ concurrency:
permissions: {}
jobs:
api-tests:
name: API tests on nightly build
runs-on: ubuntu-20.04
permissions:
contents: read
env:
ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-results
ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-report
steps:
- uses: actions/checkout@v3
with:
ref: ${{ env.BRANCH_NAME }}
- name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo
with:
install-filters: woocommerce
build: false
- name: Run API tests.
working-directory: plugins/woocommerce
env:
BASE_URL: ${{ secrets.SMOKE_TEST_URL }}
USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
DEFAULT_TIMEOUT_OVERRIDE: 120000
run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello.test.js
- name: Generate API Test report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
- name: Archive API test report
if: success() || failure()
uses: actions/upload-artifact@v3
with:
name: ${{ env.API_ARTIFACT }}
path: |
${{ env.ALLURE_RESULTS_DIR }}
${{ env.ALLURE_REPORT_DIR }}
if-no-files-found: ignore
retention-days: 5
e2e-tests:
name: E2E tests on nightly build
runs-on: ubuntu-20.04
permissions:
contents: read
needs: [api-tests]
env:
ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
@ -78,52 +123,6 @@ jobs:
if-no-files-found: ignore
retention-days: 5
api-tests:
name: API tests on nightly build
runs-on: ubuntu-20.04
permissions:
contents: read
needs: [e2e-tests]
if: success() || failure()
env:
ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-results
ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-report
steps:
- uses: actions/checkout@v3
with:
ref: ${{ env.BRANCH_NAME }}
- name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo
with:
install-filters: woocommerce
build: false
- name: Run API tests.
working-directory: plugins/woocommerce
env:
BASE_URL: ${{ secrets.SMOKE_TEST_URL }}
USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
DEFAULT_TIMEOUT_OVERRIDE: 120000
run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello.test.js
- name: Generate API Test report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
- name: Archive API test report
if: success() || failure()
uses: actions/upload-artifact@v3
with:
name: ${{ env.API_ARTIFACT }}
path: |
${{ env.ALLURE_RESULTS_DIR }}
${{ env.ALLURE_REPORT_DIR }}
if-no-files-found: ignore
retention-days: 5
k6-tests:
name: k6 tests on nightly build
runs-on: ubuntu-20.04
@ -181,6 +180,7 @@ jobs:
runs-on: ubuntu-20.04
permissions:
contents: read
needs: [api-tests]
env:
USE_WP_ENV: 1
ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/allure-results
@ -256,7 +256,7 @@ jobs:
runs-on: ubuntu-20.04
permissions:
contents: read
needs: [test-plugins, k6-tests]
needs: [e2e-tests, test-plugins, k6-tests]
steps:
- name: Create dirs
run: |
@ -312,7 +312,7 @@ jobs:
( success() || failure() ) &&
! github.event.pull_request.head.repo.fork
runs-on: ubuntu-20.04
needs: [test-plugins, k6-tests]
needs: [e2e-tests, test-plugins, k6-tests]
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
RUN_ID: ${{ github.run_id }}

View File

@ -20,7 +20,7 @@ jobs:
uses: actions/checkout@v3
- name: 'Setup node'
uses: actions/setup-node@v3
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c
with:
node-version: 16

View File

@ -1,6 +1,6 @@
{
"dev": true,
"filter": "^(?:react|react-dom|typescript|@typescript-eslint|@types/react).*$",
"filter": "^(?:react|react-dom|eslint|typescript|@typescript-eslint|@types/react).*$",
"indent": "\t",
"overrides": true,
"peer": true,
@ -51,6 +51,18 @@
"**"
],
"pinVersion": "^4.8.3"
},
{
"dependencies": [
"eslint"
],
"dependencyTypes": [
"devDependencies"
],
"packages": [
"**"
],
"pinVersion": "^8.32.0"
}
]
}

View File

@ -51,7 +51,7 @@
"sass": "^1.49.9",
"sass-loader": "^10.2.1",
"syncpack": "^8.3.9",
"turbo": "^1.4.5",
"turbo": "^1.7.0",
"typescript": "^4.8.3",
"url-loader": "^1.1.2",
"webpack": "^5.70.0"

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -44,7 +44,7 @@
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@woocommerce/api": "^0.2.0",
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"jest-mock-extended": "^1.0.18",

View File

@ -36,7 +36,7 @@
},
"devDependencies": {
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0"
"eslint": "^8.32.0"
},
"publishConfig": {
"access": "public"

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -55,7 +55,7 @@
"@typescript-eslint/parser": "^5.43.0",
"@woocommerce/eslint-plugin": "workspace:*",
"axios-mock-adapter": "^1.20.0",
"eslint": "^8.2.0",
"eslint": "^8.32.0",
"jest": "^27",
"ts-jest": "^27",
"typescript": "^4.8.3"

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Add TreeControl expand/collapse functionality.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Move experimental product section components to @woocommerce/product-editor package.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Altering styles to correctly target fields within slot fills on product editor.

View File

@ -1,4 +0,0 @@
Significance: minor
Type: tweak
Update spelling of Cancelled to Canceled for US English.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Include CSS for experimental tree control so it renders properly in Storybook.

View File

@ -0,0 +1,5 @@
Significance: patch
Type: fix
Comment: Move registerFill call to inside an useEffect since it was updating a component while rendering another component

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add new WooProductTabItem component for slot filling tab items.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Updating the product editor fill components to support multiple targets.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Updating WooProductFieldItem to uniquely generate IDs with different sections.

View File

@ -44,6 +44,7 @@
"@woocommerce/navigation": "workspace:*",
"@wordpress/a11y": "3.5.0",
"@wordpress/api-fetch": "^6.0.1",
"@wordpress/base-styles": "^4.3.0",
"@wordpress/block-editor": "^9.8.0",
"@wordpress/block-library": "^7.16.0",
"@wordpress/blocks": "^11.18.0",
@ -129,7 +130,7 @@
"@wordpress/scripts": "^12.6.1",
"concurrently": "^7.0.0",
"css-loader": "^3.6.0",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"postcss-loader": "^3.0.0",
@ -164,5 +165,11 @@
"pnpm lint:fix",
"pnpm test-staged"
]
},
"pnpm": {
"overrides": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
}
}
}

View File

@ -0,0 +1,31 @@
/**
* External dependencies
*/
import { useEffect, useState } from 'react';
/**
* Internal dependencies
*/
import { TreeItemProps } from '../types';
export function useExpander( {
shouldItemBeExpanded,
item,
}: Pick< TreeItemProps, 'shouldItemBeExpanded' | 'item' > ) {
const [ isExpanded, setExpanded ] = useState( false );
useEffect( () => {
if (
item.children?.length &&
typeof shouldItemBeExpanded === 'function'
) {
setExpanded( shouldItemBeExpanded( item ) );
}
}, [ item, shouldItemBeExpanded ] );
function onToggleExpand() {
setExpanded( ( prev ) => ! prev );
}
return { isExpanded, onToggleExpand };
}

View File

@ -1,30 +1,43 @@
/**
* External dependencies
*/
import React from 'react';
/**
* Internal dependencies
*/
import { TreeItemProps } from '../types';
import { useExpander } from './use-expander';
export function useTreeItem( { item, level, ...props }: TreeItemProps ) {
export function useTreeItem( {
item,
level,
shouldItemBeExpanded,
...props
}: TreeItemProps ) {
const nextLevel = level + 1;
const nextHeadingPaddingLeft = ( level - 1 ) * 28 + 12;
const expander = useExpander( {
item,
shouldItemBeExpanded,
} );
return {
item,
level: nextLevel,
expander,
treeItemProps: {
...props,
},
headingProps: {
style: {
paddingLeft: nextHeadingPaddingLeft,
},
'--level': level,
} as React.CSSProperties,
},
treeProps: {
items: item.children,
level: nextLevel,
shouldItemBeExpanded,
},
};
}

View File

@ -7,7 +7,13 @@
*/
import { TreeProps } from '../types';
export function useTree( { ref, items, level = 1, ...props }: TreeProps ) {
export function useTree( {
ref,
items,
level = 1,
shouldItemBeExpanded,
...props
}: TreeProps ) {
return {
level,
items,
@ -16,6 +22,7 @@ export function useTree( { ref, items, level = 1, ...props }: TreeProps ) {
},
treeItemProps: {
level,
shouldItemBeExpanded,
},
};
}

View File

@ -1,14 +1,14 @@
/**
* External dependencies
*/
import { BaseControl } from '@wordpress/components';
import React, { createElement } from 'react';
import { BaseControl, TextControl } from '@wordpress/components';
import React, { createElement, useCallback, useState } from 'react';
/**
* Internal dependencies
*/
import { TreeControl } from '../tree-control';
import { Item } from '../types';
import { Item, LinkedTree } from '../types';
const listItems: Item[] = [
{ value: '1', label: 'Technology' },
@ -36,6 +36,35 @@ export const SimpleTree: React.FC = () => {
);
};
function shouldItemBeExpanded( item: LinkedTree, filter: string ) {
if ( ! filter || ! item.children?.length ) return false;
return item.children.some( ( child ) => {
if ( new RegExp( filter, 'ig' ).test( child.data.label ) ) {
return true;
}
return shouldItemBeExpanded( child, filter );
} );
}
export const ExpandOnFilter: React.FC = () => {
const [ filter, setFilter ] = useState( '' );
return (
<>
<TextControl value={ filter } onChange={ setFilter } />
<BaseControl label="Expand on filter" id="expand-on-filter">
<TreeControl
id="expand-on-filter"
items={ listItems }
shouldItemBeExpanded={ ( item ) =>
shouldItemBeExpanded( item, filter )
}
/>
</BaseControl>
</>
);
};
export default {
title: 'WooCommerce Admin/experimental/TreeControl',
component: TreeControl,

View File

@ -6,7 +6,7 @@
flex-grow: 1;
gap: $gap-smaller;
min-height: $gap-largest;
padding: 0 $gap-small;
padding: 0 $gap-small 0 calc( ( var( --level ) - 1 ) * ( $gap + $gap-small ) + $gap-small );
border-radius: 2px;
&:hover,
@ -31,4 +31,12 @@
display: block;
}
}
&__expander {
display: flex;
align-items: center;
.components-button {
padding: 0;
}
}
}

View File

@ -1,7 +1,9 @@
/**
* External dependencies
*/
import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { chevronDown, chevronUp } from '@wordpress/icons';
import classNames from 'classnames';
import { createElement, forwardRef } from 'react';
@ -16,7 +18,13 @@ export const TreeItem = forwardRef( function ForwardedTreeItem(
props: TreeItemProps,
ref: React.ForwardedRef< HTMLLIElement >
) {
const { item, treeItemProps, headingProps, treeProps } = useTreeItem( {
const {
item,
treeItemProps,
headingProps,
treeProps,
expander: { isExpanded, onToggleExpand },
} = useTreeItem( {
...props,
ref,
} );
@ -36,9 +44,26 @@ export const TreeItem = forwardRef( function ForwardedTreeItem(
<div className="experimental-woocommerce-tree-item__label">
<span>{ item.data.label }</span>
</div>
{ Boolean( item.children?.length ) && (
<div className="experimental-woocommerce-tree-item__expander">
<Button
icon={ isExpanded ? chevronUp : chevronDown }
onClick={ onToggleExpand }
className="experimental-woocommerce-tree-item__expander"
aria-label={
isExpanded
? __( 'Collapse', 'woocommerce' )
: __( 'Expand', 'woocommerce' )
}
/>
</div>
) }
</div>
{ Boolean( item.children.length ) && <Tree { ...treeProps } /> }
{ Boolean( item.children.length ) && isExpanded && (
<Tree { ...treeProps } />
) }
</li>
);
} );

View File

@ -16,6 +16,21 @@ export type TreeProps = React.DetailedHTMLProps<
> & {
level?: number;
items: LinkedTree[];
/**
* Return if the tree item passed in should be expanded.
*
* @example
* <Tree
* shouldItemBeExpanded={
* ( item ) => checkExpanded( item, filter )
* }
* />
*
* @param item The tree item to determine if should be expanded.
*
* @see {@link LinkedTree}
*/
shouldItemBeExpanded?( item: LinkedTree ): boolean;
};
export type TreeItemProps = React.DetailedHTMLProps<
@ -24,6 +39,7 @@ export type TreeItemProps = React.DetailedHTMLProps<
> & {
level: number;
item: LinkedTree;
shouldItemBeExpanded?( item: LinkedTree ): boolean;
};
export type TreeControlProps = Omit< TreeProps, 'items' | 'level' > & {

View File

@ -87,10 +87,7 @@ export { CollapsibleContent } from './collapsible-content';
export { createOrderedChildren, sortFillsByOrder } from './utils';
export { WooProductFieldItem as __experimentalWooProductFieldItem } from './woo-product-field-item';
export { WooProductSectionItem as __experimentalWooProductSectionItem } from './woo-product-section-item';
export {
ProductSectionLayout as __experimentalProductSectionLayout,
ProductFieldSection as __experimentalProductFieldSection,
} from './product-section-layout';
export { WooProductTabItem as __experimentalWooProductTabItem } from './woo-product-tab-item';
export * from './product-fields';
export {
SlotContextProvider,

View File

@ -1,6 +1,7 @@
/**
* External Dependencies
*/
@import 'node_modules/@wordpress/base-styles/colors.native';
@import '@automattic/tour-kit/dist/esm/styles.scss';
/**
@ -55,4 +56,4 @@
@import 'tour-kit/style.scss';
@import 'collapsible-content/style.scss';
@import 'form/style.scss';
@import 'product-section-layout/style.scss';
@import 'experimental-tree-control/tree.scss';

View File

@ -20,7 +20,10 @@ function createOrderedChildren< T = Fill.Props, S = Record< string, unknown > >(
injectProps?: S
) {
if ( typeof children === 'function' ) {
return cloneElement( children( props ), { order, ...injectProps } );
return cloneElement( children( { ...props, order, ...injectProps } ), {
order,
...injectProps,
} );
} else if ( isValidElement( children ) ) {
return cloneElement( children, { ...props, order, ...injectProps } );
}

View File

@ -1,54 +1,94 @@
/**
* External dependencies
*/
import React from 'react';
import React, { useEffect } from 'react';
import { Slot, Fill } from '@wordpress/components';
import { createElement, Children } from '@wordpress/element';
import { createElement, Children, Fragment } from '@wordpress/element';
/**
* Internal dependencies
*/
import { createOrderedChildren, sortFillsByOrder } from '../utils';
import { useSlotContext, SlotContextHelpersType } from '../slot-context';
import { ProductFillLocationType } from '../woo-product-tab-item';
type WooProductFieldItemProps = {
id: string;
section: string;
sections: ProductFillLocationType[];
pluginId: string;
order?: number;
};
type WooProductFieldSlotProps = {
section: string;
};
export const WooProductFieldItem: React.FC< WooProductFieldItemProps > & {
Slot: React.FC< Slot.Props & WooProductFieldSlotProps >;
} = ( { children, order = 20, section, id } ) => {
type WooProductFieldFillProps = {
fieldName: string;
sectionName: string;
order: number;
children?: React.ReactNode;
};
const WooProductFieldFill: React.FC< WooProductFieldFillProps > = ( {
fieldName,
sectionName,
order,
children,
} ) => {
const { registerFill, getFillHelpers } = useSlotContext();
registerFill( id );
const fieldId = `product_field/${ sectionName }/${ fieldName }`;
useEffect( () => {
registerFill( fieldId );
}, [] );
return (
<Fill name={ `woocommerce_product_field_${ section }` }>
{ ( fillProps: Fill.Props ) => {
return createOrderedChildren<
Fill.Props & SlotContextHelpersType,
<Fill
name={ `woocommerce_product_field_${ sectionName }` }
key={ fieldId }
>
{ ( fillProps: Fill.Props ) =>
createOrderedChildren<
Fill.Props &
SlotContextHelpersType & {
sectionName: string;
},
{ _id: string }
>(
children,
order,
{
sectionName,
...fillProps,
...getFillHelpers(),
},
{ _id: id }
);
} }
{ _id: fieldId }
)
}
</Fill>
);
};
export const WooProductFieldItem: React.FC< WooProductFieldItemProps > & {
Slot: React.FC< Slot.Props & WooProductFieldSlotProps >;
} = ( { children, sections, id } ) => {
return (
<>
{ sections.map( ( { name: sectionName, order = 20 } ) => (
<WooProductFieldFill
fieldName={ id }
sectionName={ sectionName }
order={ order }
key={ sectionName }
>
{ children }
</WooProductFieldFill>
) ) }
</>
);
};
WooProductFieldItem.Slot = ( { fillProps, section } ) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const { filterRegisteredFills } = useSlotContext();

View File

@ -3,41 +3,51 @@
*/
import React from 'react';
import { Slot, Fill } from '@wordpress/components';
import { createElement } from '@wordpress/element';
import { createElement, Fragment } from '@wordpress/element';
/**
* Internal dependencies
*/
import { createOrderedChildren, sortFillsByOrder } from '../utils';
import { ProductFillLocationType } from '../woo-product-tab-item';
type WooProductSectionItemProps = {
id: string;
location: string;
tabs: ProductFillLocationType[];
pluginId: string;
order?: number;
};
type WooProductFieldSlotProps = {
location: string;
type WooProductSectionSlotProps = {
tab: string;
};
export const WooProductSectionItem: React.FC< WooProductSectionItemProps > & {
Slot: React.FC< Slot.Props & WooProductFieldSlotProps >;
} = ( { children, order = 20, location } ) => (
<Fill name={ `woocommerce_product_section_${ location }` }>
Slot: React.FC< Slot.Props & WooProductSectionSlotProps >;
} = ( { children, tabs } ) => {
return (
<>
{ tabs.map( ( { name: tabName, order: tabOrder } ) => (
<Fill
name={ `woocommerce_product_section_${ tabName }` }
key={ tabName }
>
{ ( fillProps: Fill.Props ) => {
return createOrderedChildren< Fill.Props >(
children,
order,
fillProps
);
return createOrderedChildren<
Fill.Props & { tabName: string }
>( children, tabOrder || 20, {
tabName,
...fillProps,
} );
} }
</Fill>
);
) ) }
</>
);
};
WooProductSectionItem.Slot = ( { fillProps, location } ) => (
WooProductSectionItem.Slot = ( { fillProps, tab } ) => (
<Slot
name={ `woocommerce_product_section_${ location }` }
name={ `woocommerce_product_section_${ tab }` }
fillProps={ fillProps }
>
{ ( fills ) => {

View File

@ -0,0 +1,35 @@
# WooProductTabItem Slot & Fill
A Slotfill component that will allow you to add a new tab to the product editor.
## Usage
```jsx
<WooProductTabItem id={ key } location="tab/general" order={ 2 } pluginId="test-plugin" tabProps={ { title: 'New tab', name: 'new-tab' } } >
<Card>
<CardBody>{ /* Tab content */ }</CardBody>
</Card>
</WooProductTabItem>
<WooProductTabItem.Slot location="tab/general" />
```
### WooProductTabItem (fill)
This is the fill component. You must provide the `id` prop to identify your section fill with a unique string. This component will accept a series of props:
| Prop | Type | Description |
| ---------- | ------ | -------------------------------------------------------------------------------------------------------------- |
| `id` | String | A unique string to identify your fill. Used for configuiration management. |
| `location` | String | The string used to identify the particular location that you want to render your section. |
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
| `tabProps` | Object | An object containing tab props: name, title, className, disabled (see TabPanel.Tab from @wordpress/components) |
| `order` | Number | (optional) This number will dictate the order that the sections rendered by a Slot will be appear. |
### WooProductTabItem.Slot (slot)
This is the slot component, and will not be used as frequently. It must also receive the required `location` prop that will be identical to the fill `location`.
| Name | Type | Description |
| ---------- | ------ | ---------------------------------------------------------------------------------------------------- |
| `location` | String | Unique to the location that the Slot appears, and must be the same as the one provided to any fills. |

View File

@ -0,0 +1 @@
export * from './woo-product-tab-item';

View File

@ -0,0 +1,107 @@
/**
* External dependencies
*/
import React, { ReactElement, ReactNode } from 'react';
import { Slot, Fill, TabPanel } from '@wordpress/components';
import { createElement, Fragment } from '@wordpress/element';
/**
* Internal dependencies
*/
import { createOrderedChildren } from '../utils';
export type ProductFillLocationType = { name: string; order?: number };
type WooProductTabItemProps = {
id: string;
pluginId: string;
tabProps:
| TabPanel.Tab
// eslint-disable-next-line @typescript-eslint/no-explicit-any
| ( ( fillProps: Record< string, any > | undefined ) => TabPanel.Tab );
templates?: Array< ProductFillLocationType >;
};
type WooProductFieldSlotProps = {
template: string;
children: (
tabs: TabPanel.Tab[],
tabChildren: Record< string, ReactNode >
) => ReactElement | null;
};
export const WooProductTabItem: React.FC< WooProductTabItemProps > & {
Slot: React.VFC<
Omit< Slot.Props, 'children' > & WooProductFieldSlotProps
>;
} = ( { children, tabProps, templates } ) => {
if ( ! templates ) {
// eslint-disable-next-line no-console
console.warn( 'WooProductTabItem fill is missing templates property.' );
return null;
}
return (
<>
{ templates.map( ( templateData ) => (
<Fill
name={ `woocommerce_product_tab_${ templateData.name }` }
key={ templateData.name }
>
{ ( fillProps: Fill.Props ) => {
return createOrderedChildren< Fill.Props >(
children,
templateData.order || 20,
{},
{
tabProps,
templateName: templateData.name,
order: templateData.order || 20,
...fillProps,
}
);
} }
</Fill>
) ) }
</>
);
};
WooProductTabItem.Slot = ( { fillProps, template, children } ) => (
<Slot
name={ `woocommerce_product_tab_${ template }` }
fillProps={ fillProps }
>
{ ( fills ) => {
const tabData = fills.reduce(
( { childrenMap, tabs }, fill ) => {
const props: WooProductTabItemProps & { order: number } =
fill[ 0 ].props;
if ( props && props.tabProps ) {
childrenMap[ props.tabProps.name ] = fill[ 0 ];
const tabProps =
typeof props.tabProps === 'function'
? props.tabProps( fillProps )
: props.tabProps;
tabs.push( {
...tabProps,
order: props.order ?? 20,
} );
}
return {
childrenMap,
tabs,
};
},
{ childrenMap: {}, tabs: [] } as {
childrenMap: Record< string, ReactElement >;
tabs: Array< TabPanel.Tab & { order: number } >;
}
);
const orderedTabs = tabData.tabs.sort( ( a, b ) => {
return a.order - b.order;
} );
return children( orderedTabs, tabData.childrenMap );
} }
</Slot>
);

View File

@ -2,7 +2,7 @@
"phpVersion": null,
"core": null,
"plugins": [
"https://downloads.wordpress.org/plugin/woocommerce.7.1.0.zip",
"https://downloads.wordpress.org/plugin/woocommerce.7.3.0.zip",
"."
],
"config": {

View File

@ -1,4 +1,4 @@
Significance: patch
Type: update
Disable TikTok in the OBW
bump WooCommerce version

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -50,7 +50,7 @@
"@babel/core": "^7.17.5",
"@types/jest": "^27.4.1",
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"rimraf": "^3.0.2",

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -53,7 +53,7 @@
"@babel/core": "^7.17.5",
"@types/jest": "^27.4.1",
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"rimraf": "^3.0.2",

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add FeedbackModal and ProductMVPFeedbackModal components

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add a function to help decide if comments section should be shown

View File

@ -52,7 +52,7 @@
"@wordpress/browserslist-config": "^4.1.1",
"concurrently": "^7.0.0",
"css-loader": "^3.6.0",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"postcss-loader": "^3.0.0",

View File

@ -29,6 +29,10 @@ type CustomerEffortScoreProps = {
onModalShownCallback?: () => void;
onModalDismissedCallback?: () => void;
icon?: React.ReactElement | null;
shouldShowComments?: (
firstQuestionScore: number,
secondQuestionScore: number
) => boolean;
};
/**
@ -48,6 +52,7 @@ type CustomerEffortScoreProps = {
* @param {Function} props.onNoticeDismissedCallback Function to call when the notice is dismissed.
* @param {Function} props.onModalShownCallback Function to call when the modal is shown.
* @param {Function} props.onModalDismissedCallback Function to call when modal is dismissed.
* @param {Function} props.shouldShowComments Callback to determine if comments section should be shown.
* @param {Object} props.icon Icon (React component) to be shown on the notice.
*/
const CustomerEffortScore: React.VFC< CustomerEffortScoreProps > = ( {
@ -62,6 +67,10 @@ const CustomerEffortScore: React.VFC< CustomerEffortScoreProps > = ( {
onModalShownCallback = noop,
onModalDismissedCallback = noop,
icon,
shouldShowComments = ( firstQuestionScore, secondQuestionScore ) =>
[ firstQuestionScore, secondQuestionScore ].some(
( score ) => score === 1 || score === 2
),
} ) => {
const [ shouldCreateNotice, setShouldCreateNotice ] = useState( true );
const [ visible, setVisible ] = useState( false );
@ -108,6 +117,7 @@ const CustomerEffortScore: React.VFC< CustomerEffortScoreProps > = ( {
secondQuestion={ secondQuestion }
recordScoreCallback={ recordScoreCallback }
onCloseModal={ onModalDismissedCallback }
shouldShowComments={ shouldShowComments }
/>
);
};
@ -145,6 +155,10 @@ CustomerEffortScore.propTypes = {
* The second survey question.
*/
secondQuestion: PropTypes.string,
/**
* A function to determine whether or not the comments field shown be shown.
*/
shouldShowComments: PropTypes.func,
};
export { CustomerEffortScore };

View File

@ -32,6 +32,7 @@ import { __ } from '@wordpress/i18n';
* @param {string} props.defaultScore Default score.
* @param {Function} props.onCloseModal Callback for when user closes modal by clicking cancel.
* @param {Function} props.customOptions List of custom score options, contains label and value.
* @param {Function} props.shouldShowComments A function to determine whether or not the comments field shown be shown.
*/
function CustomerFeedbackModal( {
recordScoreCallback,
@ -42,6 +43,10 @@ function CustomerFeedbackModal( {
defaultScore = NaN,
onCloseModal,
customOptions,
shouldShowComments = ( firstQuestionScore, secondQuestionScore ) =>
[ firstQuestionScore, secondQuestionScore ].some(
( score ) => score === 1 || score === 2
),
}: {
recordScoreCallback: (
score: number,
@ -55,6 +60,10 @@ function CustomerFeedbackModal( {
defaultScore?: number;
onCloseModal?: () => void;
customOptions?: { label: string; value: string }[];
shouldShowComments?: (
firstQuestionScore: number,
secondQuestionScore: number
) => boolean;
} ): JSX.Element | null {
const options =
customOptions && customOptions.length > 0
@ -200,8 +209,10 @@ function CustomerFeedbackModal( {
</div>
) }
{ [ firstQuestionScore, secondQuestionScore ].some(
( score ) => score === 1 || score === 2
{ typeof shouldShowComments === 'function' &&
shouldShowComments(
firstQuestionScore,
secondQuestionScore
) && (
<div className="woocommerce-customer-effort-score__comments">
<TextareaControl
@ -218,7 +229,9 @@ function CustomerFeedbackModal( {
'Optional, but much apprecated. We love reading your feedback!',
'woocommerce'
) }
onChange={ ( value: string ) => setComments( value ) }
onChange={ ( value: string ) =>
setComments( value )
}
rows={ 5 }
/>
</div>

View File

@ -0,0 +1,12 @@
.woocommerce-feedback-modal__buttons {
text-align: right;
.components-button {
margin-left: 1em;
}
}
.woocommerce-feedback-modal .woocommerce-feedback-modal__description {
max-width: 550px;
margin: 0 0 1.5em 0;
}

View File

@ -0,0 +1,106 @@
/**
* External dependencies
*/
import { createElement, useState } from '@wordpress/element';
import PropTypes from 'prop-types';
import { Button, Modal } from '@wordpress/components';
import { Text } from '@woocommerce/experimental';
import { __ } from '@wordpress/i18n';
/**
* Provides a modal requesting customer feedback.
*
* Answers and comments are sent to a callback function.
*
* @param {Object} props Component props.
* @param {Function} props.onSubmit Function to call when the results are sent.
* @param {string} props.title Title displayed in the modal.
* @param {string} props.description Description displayed in the modal.
* @param {string} props.isSubmitButtonDisabled Boolean to enable/disable the send button.
* @param {string} props.submitButtonLabel Label for the send button.
* @param {string} props.cancelButtonLabel Label for the cancel button.
* @param {Function} props.onModalClose Callback for when user closes modal by clicking cancel.
* @param {Function} props.children Children to be rendered.
*/
function FeedbackModal( {
onSubmit,
title,
description,
onModalClose,
children,
isSubmitButtonDisabled,
submitButtonLabel,
cancelButtonLabel,
}: {
onSubmit: () => void;
title: string;
description?: string;
onModalClose?: () => void;
children?: JSX.Element;
isSubmitButtonDisabled?: boolean;
submitButtonLabel?: string;
cancelButtonLabel?: string;
} ): JSX.Element | null {
const [ isOpen, setOpen ] = useState( true );
const closeModal = () => {
setOpen( false );
if ( onModalClose ) {
onModalClose();
}
};
if ( ! isOpen ) {
return null;
}
return (
<Modal
className="woocommerce-feedback-modal"
title={ title }
onRequestClose={ closeModal }
shouldCloseOnClickOutside={ false }
>
<Text
variant="body"
as="p"
className="woocommerce-feedback-modal__description"
size={ 14 }
lineHeight="20px"
marginBottom="1.5em"
>
{ description }
</Text>
{ children }
<div className="woocommerce-feedback-modal__buttons">
<Button isTertiary onClick={ closeModal } name="cancel">
{ cancelButtonLabel }
</Button>
<Button
isPrimary={ ! isSubmitButtonDisabled }
isSecondary={ isSubmitButtonDisabled }
onClick={ () => {
onSubmit();
setOpen( false );
} }
name="send"
disabled={ isSubmitButtonDisabled }
>
{ submitButtonLabel }
</Button>
</div>
</Modal>
);
}
FeedbackModal.propTypes = {
onSubmit: PropTypes.func.isRequired,
title: PropTypes.string,
description: PropTypes.string,
onModalClose: PropTypes.func,
isSubmitButtonDisabled: PropTypes.bool,
submitButtonLabel: PropTypes.string,
cancelButtonLabel: PropTypes.string,
};
export { FeedbackModal };

View File

@ -0,0 +1 @@
export * from './feedback-modal';

View File

@ -0,0 +1,54 @@
/**
* External dependencies
*/
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { createElement } from '@wordpress/element';
/**
* Internal dependencies
*/
import { FeedbackModal } from '../index';
const mockRecordScoreCallback = jest.fn();
describe( 'FeedbackModal', () => {
it( 'should render a modal', async () => {
render(
<FeedbackModal
onSubmit={ mockRecordScoreCallback }
title="Testing"
submitButtonLabel="Send"
cancelButtonLabel="Cancel"
/>
);
// Wait for the modal to render.
await screen.findByRole( 'dialog' );
expect(
screen.getByRole( 'button', { name: /Send/i } )
).toBeInTheDocument();
expect(
screen.getByRole( 'button', { name: /Cancel/i } )
).toBeInTheDocument();
} );
it( 'should close modal when cancel button pressed', async () => {
render(
<FeedbackModal
onSubmit={ mockRecordScoreCallback }
title="Testing"
submitButtonLabel="Send"
cancelButtonLabel="Cancel"
/>
);
// Wait for the modal to render.
await screen.findByRole( 'dialog' );
// Press cancel button.
fireEvent.click( screen.getByRole( 'button', { name: /Cancel/i } ) );
expect( screen.queryByRole( 'dialog' ) ).not.toBeInTheDocument();
} );
} );

View File

@ -1,3 +1,5 @@
export * from './customer-effort-score';
export * from './customer-feedback-simple';
export * from './customer-feedback-modal';
export * from './product-mvp-feedback-modal';
export * from './feedback-modal';

View File

@ -0,0 +1 @@
export * from './product-mvp-feedback-modal';

View File

@ -0,0 +1,23 @@
.woocommerce-product-mvp-feedback-modal {
&__subtitle {
margin-top: $gap-smaller !important;
}
&__checkboxes {
margin: $gap-small 0;
}
&__comments {
margin-top: 2em;
margin-bottom: 1.5em;
label {
display: block;
font-weight: bold;
text-transform: none;
font-size: 14px;
}
textarea {
width: 100%;
}
}
}

View File

@ -0,0 +1,156 @@
/**
* External dependencies
*/
import { createElement, Fragment, useState } from '@wordpress/element';
import PropTypes from 'prop-types';
import { CheckboxControl, TextareaControl } from '@wordpress/components';
import { Text } from '@woocommerce/experimental';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { FeedbackModal } from '../feedback-modal';
/**
* Provides a modal requesting customer feedback.
*
*
* @param {Object} props Component props.
* @param {Function} props.recordScoreCallback Function to call when the results are sent.
* @param {Function} props.onCloseModal Callback for when user closes modal by clicking cancel.
*/
function ProductMVPFeedbackModal( {
recordScoreCallback,
onCloseModal,
}: {
recordScoreCallback: ( checked: string[], comments: string ) => void;
onCloseModal?: () => void;
} ): JSX.Element | null {
const [ missingFeatures, setMissingFeatures ] = useState( false );
const [ missingPlugins, setMissingPlugins ] = useState( false );
const [ difficultToUse, setDifficultToUse ] = useState( false );
const [ slowBuggyOrBroken, setSlowBuggyOrBroken ] = useState( false );
const [ other, setOther ] = useState( false );
const checkboxes = [
{
key: 'missing-features',
label: __( 'Missing features', 'woocommerce' ),
checked: missingFeatures,
onChange: setMissingFeatures,
},
{
key: 'missing-plugins',
label: __( 'Missing plugins', 'woocommerce' ),
checked: missingPlugins,
onChange: setMissingPlugins,
},
{
key: 'difficult-to-use',
label: __( 'It is difficult to use', 'woocommerce' ),
checked: difficultToUse,
onChange: setDifficultToUse,
},
{
key: 'slow-buggy-or-broken',
label: __( 'It is slow, buggy, or broken', 'woocommerce' ),
checked: slowBuggyOrBroken,
onChange: setSlowBuggyOrBroken,
},
{
key: 'other',
label: __( 'Other (describe below)', 'woocommerce' ),
checked: other,
onChange: setOther,
},
];
const [ comments, setComments ] = useState( '' );
const onSendFeedback = () => {
const checked = checkboxes
.filter( ( checkbox ) => checkbox.checked )
.map( ( checkbox ) => checkbox.key );
recordScoreCallback( checked, comments );
};
const isSendButtonDisabled =
! comments &&
! missingFeatures &&
! missingPlugins &&
! difficultToUse &&
! slowBuggyOrBroken &&
! other;
return (
<FeedbackModal
title={ __(
'Thanks for trying out the new product editor!',
'woocommerce'
) }
description={ __(
'Were working on making it better, and your feedback will help improve the experience for thousands of merchants like you.',
'woocommerce'
) }
onSubmit={ onSendFeedback }
onModalClose={ onCloseModal }
isSubmitButtonDisabled={ isSendButtonDisabled }
submitButtonLabel={ __( 'Send feedback', 'woocommerce' ) }
cancelButtonLabel={ __( 'Skip', 'woocommerce' ) }
>
<>
<Text
variant="subtitle.small"
as="p"
weight="600"
size="14"
lineHeight="20px"
>
{ __(
'What made you switch back to the classic product editor?',
'woocommerce'
) }
</Text>
<Text
weight="400"
size="12"
as="p"
lineHeight="16px"
color="#757575"
className="woocommerce-product-mvp-feedback-modal__subtitle"
>
{ __( '(Check all that apply)', 'woocommerce' ) }
</Text>
<div className="woocommerce-product-mvp-feedback-modal__checkboxes">
{ checkboxes.map( ( checkbox, index ) => (
<CheckboxControl
key={ index }
label={ checkbox.label }
name={ checkbox.key }
checked={ checkbox.checked }
onChange={ checkbox.onChange }
/>
) ) }
</div>
<div className="woocommerce-product-mvp-feedback-modal__comments">
<TextareaControl
label={ __( 'Additional comments', 'woocommerce' ) }
value={ comments }
placeholder={ __(
'Optional, but much apprecated. We love reading your feedback!',
'woocommerce'
) }
onChange={ ( value: string ) => setComments( value ) }
rows={ 5 }
/>
</div>
</>
</FeedbackModal>
);
}
ProductMVPFeedbackModal.propTypes = {
recordScoreCallback: PropTypes.func.isRequired,
onCloseModal: PropTypes.func,
};
export { ProductMVPFeedbackModal };

View File

@ -0,0 +1,54 @@
/**
* External dependencies
*/
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { createElement } from '@wordpress/element';
/**
* Internal dependencies
*/
import { ProductMVPFeedbackModal } from '../index';
const mockRecordScoreCallback = jest.fn();
describe( 'ProductMVPFeedbackModal', () => {
it( 'should close the ProductMVPFeedback modal when skip button pressed', async () => {
render(
<ProductMVPFeedbackModal
recordScoreCallback={ mockRecordScoreCallback }
/>
);
// Wait for the modal to render.
await screen.findByRole( 'dialog' );
// Press cancel button.
fireEvent.click( screen.getByRole( 'button', { name: /Skip/i } ) );
expect( screen.queryByRole( 'dialog' ) ).not.toBeInTheDocument();
} );
it( 'should enable Send button when an option is checked', async () => {
render(
<ProductMVPFeedbackModal
recordScoreCallback={ mockRecordScoreCallback }
/>
);
// Wait for the modal to render.
await screen.findByRole( 'dialog' );
fireEvent.click( screen.getByRole( 'checkbox', { name: /other/i } ) );
fireEvent.click(
screen.getByRole( 'button', { name: /Send feedback/i } )
);
} );
it( 'should call the function sent as recordScoreCallback with the checked options', async () => {
render(
<ProductMVPFeedbackModal
recordScoreCallback={ mockRecordScoreCallback }
/>
);
// Wait for the modal to render.
await screen.findByRole( 'dialog' );
fireEvent.click( screen.getByRole( 'checkbox', { name: /other/i } ) );
expect( mockRecordScoreCallback ).toHaveBeenCalledWith(
[ 'other' ],
''
);
} );
} );

View File

@ -1,4 +1,6 @@
@import 'customer-feedback-simple/customer-feedback-simple.scss';
@import 'product-mvp-feedback-modal/product-mvp-feedback-modal.scss';
@import 'feedback-modal/feedback-modal.scss';
.woocommerce-customer-effort-score__selection {
margin: 1em 0 1.5em 0;

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Update type definition for ProductForm

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Removing unused code from ProductForm data store.

View File

@ -63,7 +63,7 @@
"@types/wordpress__data": "^6.0.0",
"@types/wordpress__data-controls": "^2.2.0",
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"redux": "^4.1.0",

View File

@ -24,6 +24,7 @@ export function getProductFormSuccess( productForm: ProductForm ) {
fields: productForm.fields,
sections: productForm.sections,
subsections: productForm.subsections,
tabs: productForm.tabs,
};
}

View File

@ -17,6 +17,7 @@ const reducer: Reducer< ProductFormState, Action > = (
fields: [],
sections: [],
subsections: [],
tabs: [],
},
action
) => {
@ -42,6 +43,7 @@ const reducer: Reducer< ProductFormState, Action > = (
fields: action.fields,
sections: action.sections,
subsections: action.subsections,
tabs: action.tabs,
};
break;
case TYPES.GET_PRODUCT_FORM_ERROR:

View File

@ -1,8 +1,7 @@
/**
* External dependencies
*/
import { apiFetch, select } from '@wordpress/data-controls';
import { controls } from '@wordpress/data';
import { apiFetch } from '@wordpress/data-controls';
/**
* Internal dependencies
@ -15,10 +14,6 @@ import {
} from './actions';
import { WC_ADMIN_NAMESPACE } from '../constants';
import { ProductFormField, ProductForm } from './types';
import { STORE_NAME } from './constants';
const resolveSelect =
controls && controls.resolveSelect ? controls.resolveSelect : select;
export function* getFields() {
try {
@ -34,10 +29,6 @@ export function* getFields() {
}
}
export function* getCountry() {
yield resolveSelect( STORE_NAME, 'getProductForm' );
}
export function* getProductForm() {
try {
const url = WC_ADMIN_NAMESPACE + '/product-form';

View File

@ -23,10 +23,16 @@ export type ProductFormSection = BaseComponent & {
export type Subsection = BaseComponent;
export type Tabs = BaseComponent & {
name: string;
title: string;
};
export type ProductForm = {
fields: ProductFormField[];
sections: ProductFormSection[];
subsections: Subsection[];
tabs: Tabs[];
};
export type ProductFormState = ProductForm & {

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -40,7 +40,7 @@
"@types/qs": "^6.9.7",
"@woocommerce/eslint-plugin": "workspace:*",
"d3-time-format": "^2.3.0",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"rimraf": "^3.0.2",

View File

@ -14,6 +14,7 @@ module.exports = [
'@woocommerce/navigation',
'@woocommerce/notices',
'@woocommerce/number',
'@woocommerce/product-editor',
'@woocommerce/tracks',
// wc-blocks packages
'@woocommerce/blocks-checkout',

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add @woocommerce/product-editor package to the packages list.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -30,7 +30,7 @@
"devDependencies": {
"@babel/core": "^7.17.5",
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"rimraf": "^3.0.2",

View File

@ -41,7 +41,7 @@
"@wordpress/babel-plugin-import-jsx-pragma": "1.1.3",
"@wordpress/babel-preset-default": "3.0.2",
"@wordpress/browserslist-config": "^4.1.0",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"eslint-plugin-jest": "23.20.0"
},
"peerDependencies": {

View File

@ -16,7 +16,7 @@ const orderStatus = [
[ 'Processing', 'wc-processing' ],
[ 'On hold', 'wc-on-hold' ],
[ 'Completed', 'wc-completed' ],
[ 'Canceled', 'wc-cancelled' ],
[ 'Cancelled', 'wc-cancelled' ],
[ 'Refunded', 'wc-refunded' ],
[ 'Failed', 'wc-failed' ],
];

View File

@ -37,7 +37,7 @@
"@wordpress/babel-plugin-import-jsx-pragma": "1.1.3",
"@wordpress/babel-preset-default": "3.0.2",
"@wordpress/browserslist-config": "^4.1.0",
"eslint": "^8.1.0",
"eslint": "^8.32.0",
"eslint-plugin-jest": "23.20.0"
},
"peerDependencies": {

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -47,7 +47,7 @@
},
"devDependencies": {
"@babel/core": "^7.17.5",
"eslint": "^8.25.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"rimraf": "^3.0.2",

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -65,7 +65,7 @@
"@wordpress/browserslist-config": "^4.1.1",
"concurrently": "^7.0.0",
"css-loader": "^3.6.0",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"postcss-loader": "^3.0.0",

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -45,7 +45,7 @@
"@types/qs": "^6.9.7",
"@types/react": "^17.0.2",
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"rimraf": "^3.0.2",

View File

@ -28,7 +28,7 @@
"@babel/core": "7.12.9",
"@woocommerce/eslint-plugin": "workspace:*",
"chalk": "^4.1.2",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"glob": "^7.2.0",
"lodash": "^4.17.21",
"mkdirp": "^1.0.4"

View File

@ -40,7 +40,7 @@
"devDependencies": {
"@babel/core": "^7.17.5",
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"rimraf": "^3.0.2",

View File

@ -40,7 +40,7 @@
"devDependencies": {
"@babel/core": "^7.17.5",
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"rimraf": "^3.0.2",

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -63,7 +63,7 @@
"@types/jest": "^27.4.1",
"@types/qs": "^6.9.7",
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"rimraf": "^3.0.2",

View File

@ -56,7 +56,7 @@
"@types/wordpress__data": "^6.0.0",
"@types/wordpress__notices": "^3.5.0",
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"redux": "^4.2.0",

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -50,7 +50,7 @@
"@types/jest": "^27.4.1",
"@types/lodash": "^4.14.184",
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"rimraf": "^3.0.2",

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -48,7 +48,7 @@
"@wordpress/browserslist-config": "^4.1.1",
"@types/wordpress__components": "^19.10.1",
"css-loader": "^3.6.0",
"eslint": "^8.12.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"postcss-loader": "^3.0.0",

View File

@ -0,0 +1 @@
package-lock=false

View File

@ -0,0 +1,11 @@
# Product Editor
A collection of WooCommerce Admin product editor components and utilities.
## Installation
Install the module
```bash
pnpm install @woocommerce/product-editor --save
```

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Adding initial build/package files for brand new package: @woocommere/product-editor.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -0,0 +1,32 @@
{
"name": "woocommerce/product-editor",
"description": "WooCommerce Admin product editor component library",
"type": "library",
"license": "GPL-3.0-or-later",
"minimum-stability": "dev",
"require-dev": {
"automattic/jetpack-changelogger": "3.3.0"
},
"config": {
"platform": {
"php": "7.2"
}
},
"extra": {
"changelogger": {
"formatter": {
"filename": "../../../tools/changelogger/class-package-formatter.php"
},
"types": {
"fix": "Fixes an existing bug",
"add": "Adds functionality",
"update": "Update existing functionality",
"dev": "Development related task",
"tweak": "A minor adjustment to the codebase",
"performance": "Address performance issues",
"enhancement": "Improve existing functionality"
},
"changelog": "CHANGELOG.md"
}
}
}

483
packages/js/product-editor/composer.lock generated Normal file
View File

@ -0,0 +1,483 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "36c31d66fa022c27e5bb2bf5db177a6b",
"packages": [],
"packages-dev": [
{
"name": "automattic/jetpack-changelogger",
"version": "v3.3.0",
"source": {
"type": "git",
"url": "https://github.com/Automattic/jetpack-changelogger.git",
"reference": "8f63c829b8d1b0d7b1d5de93510d78523ed18959"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Automattic/jetpack-changelogger/zipball/8f63c829b8d1b0d7b1d5de93510d78523ed18959",
"reference": "8f63c829b8d1b0d7b1d5de93510d78523ed18959",
"shasum": ""
},
"require": {
"php": ">=5.6",
"symfony/console": "^3.4 || ^5.2 || ^6.0",
"symfony/process": "^3.4 || ^5.2 || ^6.0",
"wikimedia/at-ease": "^1.2 || ^2.0"
},
"require-dev": {
"wikimedia/testing-access-wrapper": "^1.0 || ^2.0",
"yoast/phpunit-polyfills": "1.0.4"
},
"bin": [
"bin/changelogger"
],
"type": "project",
"extra": {
"autotagger": true,
"branch-alias": {
"dev-trunk": "3.3.x-dev"
},
"mirror-repo": "Automattic/jetpack-changelogger",
"version-constants": {
"::VERSION": "src/Application.php"
},
"changelogger": {
"link-template": "https://github.com/Automattic/jetpack-changelogger/compare/${old}...${new}"
}
},
"autoload": {
"psr-4": {
"Automattic\\Jetpack\\Changelog\\": "lib",
"Automattic\\Jetpack\\Changelogger\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0-or-later"
],
"description": "Jetpack Changelogger tool. Allows for managing changelogs by dropping change files into a changelog directory with each PR.",
"support": {
"source": "https://github.com/Automattic/jetpack-changelogger/tree/v3.3.0"
},
"time": "2022-12-26T13:49:01+00:00"
},
{
"name": "psr/log",
"version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "d49695b909c3b7628b6289db5479a1c204601f11"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
"reference": "d49695b909c3b7628b6289db5479a1c204601f11",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/1.1.4"
},
"time": "2021-05-03T11:20:27+00:00"
},
{
"name": "symfony/console",
"version": "3.4.x-dev",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/a10b1da6fc93080c180bba7219b5ff5b7518fe81",
"reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81",
"shasum": ""
},
"require": {
"php": "^5.5.9|>=7.0.8",
"symfony/debug": "~2.8|~3.0|~4.0",
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
"symfony/dependency-injection": "<3.4",
"symfony/process": "<3.3"
},
"provide": {
"psr/log-implementation": "1.0"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "~3.3|~4.0",
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/event-dispatcher": "~2.8|~3.0|~4.0",
"symfony/lock": "~3.4|~4.0",
"symfony/process": "~3.3|~4.0"
},
"suggest": {
"psr/log": "For using the console logger",
"symfony/event-dispatcher": "",
"symfony/lock": "",
"symfony/process": ""
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Console\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/console/tree/3.4"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-10-24T10:57:07+00:00"
},
{
"name": "symfony/debug",
"version": "4.4.x-dev",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
"reference": "1a692492190773c5310bc7877cb590c04c2f05be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/debug/zipball/1a692492190773c5310bc7877cb590c04c2f05be",
"reference": "1a692492190773c5310bc7877cb590c04c2f05be",
"shasum": ""
},
"require": {
"php": ">=7.1.3",
"psr/log": "^1|^2|^3"
},
"conflict": {
"symfony/http-kernel": "<3.4"
},
"require-dev": {
"symfony/http-kernel": "^3.4|^4.0|^5.0"
},
"default-branch": true,
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Debug\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides tools to ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/debug/tree/v4.4.44"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"abandoned": "symfony/error-handler",
"time": "2022-07-28T16:29:46+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "dev-main",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"default-branch": true,
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/process",
"version": "3.4.x-dev",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/b8648cf1d5af12a44a51d07ef9bf980921f15fca",
"reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca",
"shasum": ""
},
"require": {
"php": "^5.5.9|>=7.0.8"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/3.4"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-10-24T10:57:07+00:00"
},
{
"name": "wikimedia/at-ease",
"version": "v2.0.0",
"source": {
"type": "git",
"url": "https://github.com/wikimedia/at-ease.git",
"reference": "013ac61929797839c80a111a3f1a4710d8248e7a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/wikimedia/at-ease/zipball/013ac61929797839c80a111a3f1a4710d8248e7a",
"reference": "013ac61929797839c80a111a3f1a4710d8248e7a",
"shasum": ""
},
"require": {
"php": ">=5.6.99"
},
"require-dev": {
"jakub-onderka/php-console-highlighter": "0.3.2",
"jakub-onderka/php-parallel-lint": "1.0.0",
"mediawiki/mediawiki-codesniffer": "22.0.0",
"mediawiki/minus-x": "0.3.1",
"ockcyp/covers-validator": "0.5.1 || 0.6.1",
"phpunit/phpunit": "4.8.36 || ^6.5"
},
"type": "library",
"autoload": {
"files": [
"src/Wikimedia/Functions.php"
],
"psr-4": {
"Wikimedia\\AtEase\\": "src/Wikimedia/AtEase/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "Tim Starling",
"email": "tstarling@wikimedia.org"
},
{
"name": "MediaWiki developers",
"email": "wikitech-l@lists.wikimedia.org"
}
],
"description": "Safe replacement to @ for suppressing warnings.",
"homepage": "https://www.mediawiki.org/wiki/at-ease",
"support": {
"source": "https://github.com/wikimedia/at-ease/tree/master"
},
"time": "2018-10-10T15:39:06+00:00"
}
],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"platform-overrides": {
"php": "7.2"
},
"plugin-api-version": "2.3.0"
}

Some files were not shown because too many files have changed in this diff Show More