Add/36075 render fields sections from php config (#36414)
* Add initial component to auto load fills from API config * Add changelog * Update logic to make use of new store and re-usable components * Add changelog * Add loading state for product form data to add/edit product pages
This commit is contained in:
parent
025c6aab17
commit
fc1745b03b
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: tweak
|
||||
|
||||
Tweak the product form types and exports.
|
|
@ -76,7 +76,11 @@ export {
|
|||
// Export types
|
||||
export * from './types';
|
||||
export * from './countries/types';
|
||||
export { ProductForm } from './product-form/types';
|
||||
export {
|
||||
ProductForm,
|
||||
ProductFormField,
|
||||
ProductFormSection,
|
||||
} from './product-form/types';
|
||||
export * from './onboarding/types';
|
||||
export * from './plugins/types';
|
||||
export * from './products/types';
|
||||
|
@ -172,6 +176,7 @@ import { ProductCategorySelectors } from './product-categories/types';
|
|||
import { ProductAttributeTermsSelectors } from './product-attribute-terms/types';
|
||||
import { ProductVariationSelectors } from './product-variations/types';
|
||||
import { TaxClassSelectors } from './tax-classes/types';
|
||||
import { ProductFormSelectors } from './product-form/selectors';
|
||||
|
||||
// As we add types to all the package selectors we can fill out these unknown types with real ones. See one
|
||||
// of the already typed selectors for an example of how you can do this.
|
||||
|
@ -219,6 +224,8 @@ export type WCSelectorType< T > = T extends typeof REVIEWS_STORE_NAME
|
|||
? ShippingZonesSelectors
|
||||
: T extends typeof EXPERIMENTAL_TAX_CLASSES_STORE_NAME
|
||||
? TaxClassSelectors
|
||||
: T extends typeof EXPERIMENTAL_PRODUCT_FORM_STORE_NAME
|
||||
? ProductFormSelectors
|
||||
: never;
|
||||
|
||||
export interface WCDataSelector {
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
import { Field, ProductForm } from './types';
|
||||
import { ProductFormField, ProductForm } from './types';
|
||||
|
||||
export function getFieldsSuccess( fields: Field[] ) {
|
||||
export function getFieldsSuccess( fields: ProductFormField[] ) {
|
||||
return {
|
||||
type: TYPES.GET_FIELDS_SUCCESS as const,
|
||||
fields,
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
getProductFormError,
|
||||
} from './actions';
|
||||
import { WC_ADMIN_NAMESPACE } from '../constants';
|
||||
import { Field, ProductForm } from './types';
|
||||
import { ProductFormField, ProductForm } from './types';
|
||||
import { STORE_NAME } from './constants';
|
||||
|
||||
const resolveSelect =
|
||||
|
@ -23,7 +23,7 @@ const resolveSelect =
|
|||
export function* getFields() {
|
||||
try {
|
||||
const url = WC_ADMIN_NAMESPACE + '/product-form/fields';
|
||||
const results: Field[] = yield apiFetch( {
|
||||
const results: ProductFormField[] = yield apiFetch( {
|
||||
path: url,
|
||||
method: 'GET',
|
||||
} );
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { WPDataSelector, WPDataSelectors } from '../types';
|
||||
import { ProductFormState } from './types';
|
||||
|
||||
export const getFields = ( state: ProductFormState ) => {
|
||||
|
@ -15,3 +16,9 @@ export const getProductForm = ( state: ProductFormState ) => {
|
|||
const { errors, ...form } = state;
|
||||
return form;
|
||||
};
|
||||
|
||||
export type ProductFormSelectors = {
|
||||
getFields: WPDataSelector< typeof getFields >;
|
||||
getField: WPDataSelector< typeof getField >;
|
||||
getProductForm: WPDataSelector< typeof getProductForm >;
|
||||
} & WPDataSelectors;
|
||||
|
|
|
@ -9,22 +9,23 @@ type FieldProperties = {
|
|||
label: string;
|
||||
};
|
||||
|
||||
export type Field = BaseComponent & {
|
||||
export type ProductFormField = BaseComponent & {
|
||||
type: string;
|
||||
section: string;
|
||||
properties: FieldProperties;
|
||||
};
|
||||
|
||||
export type Section = BaseComponent & {
|
||||
export type ProductFormSection = BaseComponent & {
|
||||
title: string;
|
||||
description: string;
|
||||
location: string;
|
||||
};
|
||||
|
||||
export type Subsection = BaseComponent;
|
||||
|
||||
export type ProductForm = {
|
||||
fields: Field[];
|
||||
sections: Section[];
|
||||
fields: ProductFormField[];
|
||||
sections: ProductFormSection[];
|
||||
subsections: Subsection[];
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,13 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { useEffect } from '@wordpress/element';
|
||||
import { Spinner } from '@wordpress/components';
|
||||
import {
|
||||
EXPERIMENTAL_PRODUCT_FORM_STORE_NAME,
|
||||
WCDataSelector,
|
||||
} from '@woocommerce/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -10,16 +16,32 @@ import { useEffect } from '@wordpress/element';
|
|||
import { ProductForm } from './product-form';
|
||||
import { ProductTourContainer } from './tour';
|
||||
import './product-page.scss';
|
||||
import './fills';
|
||||
|
||||
const AddProductPage: React.FC = () => {
|
||||
const { isLoading } = useSelect( ( select: WCDataSelector ) => {
|
||||
const { hasFinishedResolution: hasProductFormFinishedResolution } =
|
||||
select( EXPERIMENTAL_PRODUCT_FORM_STORE_NAME );
|
||||
return {
|
||||
isLoading: ! hasProductFormFinishedResolution( 'getProductForm' ),
|
||||
};
|
||||
} );
|
||||
useEffect( () => {
|
||||
recordEvent( 'view_new_product_management_experience' );
|
||||
}, [] );
|
||||
|
||||
return (
|
||||
<div className="woocommerce-add-product">
|
||||
<ProductForm />
|
||||
<ProductTourContainer />
|
||||
{ isLoading ? (
|
||||
<div className="woocommerce-edit-product__spinner">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<ProductForm />
|
||||
<ProductTourContainer />
|
||||
</>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import {
|
||||
EXPERIMENTAL_PRODUCT_FORM_STORE_NAME,
|
||||
EXPERIMENTAL_PRODUCT_VARIATIONS_STORE_NAME,
|
||||
PartialProduct,
|
||||
Product,
|
||||
|
@ -21,6 +22,7 @@ import { ProductForm } from './product-form';
|
|||
import { ProductFormLayout } from './layout/product-form-layout';
|
||||
import { ProductVariationForm } from './product-variation-form';
|
||||
import './product-page.scss';
|
||||
import './fills';
|
||||
|
||||
const EditProductPage: React.FC = () => {
|
||||
const { productId, variationId } = useParams();
|
||||
|
@ -35,6 +37,8 @@ const EditProductPage: React.FC = () => {
|
|||
isPending,
|
||||
getPermalinkParts,
|
||||
} = select( PRODUCTS_STORE_NAME );
|
||||
const { hasFinishedResolution: hasProductFormFinishedResolution } =
|
||||
select( EXPERIMENTAL_PRODUCT_FORM_STORE_NAME );
|
||||
const {
|
||||
getProductVariation,
|
||||
hasFinishedResolution: hasProductVariationFinishedResolution,
|
||||
|
@ -71,7 +75,8 @@ const EditProductPage: React.FC = () => {
|
|||
'getProductVariation',
|
||||
[ parseInt( variationId, 10 ) ]
|
||||
)
|
||||
),
|
||||
) ||
|
||||
! hasProductFormFinishedResolution( 'getProductForm' ),
|
||||
isPendingAction:
|
||||
isPending( 'createProduct' ) ||
|
||||
isPending(
|
||||
|
|
|
@ -28,6 +28,7 @@ const DetailsSection = () => (
|
|||
id={ DETAILS_SECTION_ID }
|
||||
location="tab/general"
|
||||
pluginId="core"
|
||||
order={ 1 }
|
||||
>
|
||||
<ProductFieldSection
|
||||
id="general/details"
|
||||
|
|
|
@ -1 +1,6 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './product-form-fills';
|
||||
|
||||
export * from './details-section/details-section-fills';
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import {
|
||||
__experimentalWooProductFieldItem as WooProductFieldItem,
|
||||
renderField,
|
||||
useFormContext,
|
||||
} from '@woocommerce/components';
|
||||
import { Product, ProductFormField } from '@woocommerce/data';
|
||||
|
||||
export const Fields: React.FC< { fields: ProductFormField[] } > = ( {
|
||||
fields,
|
||||
} ) => {
|
||||
const { getInputProps } = useFormContext< Product >();
|
||||
|
||||
return (
|
||||
<>
|
||||
{ fields.map( ( field ) => (
|
||||
<WooProductFieldItem
|
||||
key={ field.properties.name }
|
||||
id={ field.id }
|
||||
section={ field.section }
|
||||
pluginId={ field.plugin_id }
|
||||
order={ field.order }
|
||||
>
|
||||
<>
|
||||
{ renderField( field.type, {
|
||||
...getInputProps( field.properties.name ),
|
||||
...field.properties,
|
||||
} ) }
|
||||
</>
|
||||
</WooProductFieldItem>
|
||||
) ) }{ ' ' }
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { registerPlugin } from '@wordpress/plugins';
|
||||
import { useSelect, resolveSelect } from '@wordpress/data';
|
||||
import {
|
||||
EXPERIMENTAL_PRODUCT_FORM_STORE_NAME,
|
||||
WCDataSelector,
|
||||
} from '@woocommerce/data';
|
||||
import { registerCoreProductFields } from '@woocommerce/components';
|
||||
|
||||
registerCoreProductFields();
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Fields } from './product-form-field-fills';
|
||||
import { Sections } from './product-form-section-fills';
|
||||
|
||||
const Form = () => {
|
||||
const { formData } = useSelect( ( select: WCDataSelector ) => {
|
||||
return {
|
||||
formData: select(
|
||||
EXPERIMENTAL_PRODUCT_FORM_STORE_NAME
|
||||
).getProductForm(),
|
||||
};
|
||||
} );
|
||||
|
||||
return (
|
||||
<>
|
||||
{ formData && (
|
||||
<>
|
||||
<Sections sections={ formData.sections } />
|
||||
<Fields fields={ formData.fields } />
|
||||
</>
|
||||
) }
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Preloading product form data, as product pages are waiting on this to be resolved.
|
||||
* The above Form component won't get rendered until the getProductForm is resolved.
|
||||
*/
|
||||
resolveSelect( EXPERIMENTAL_PRODUCT_FORM_STORE_NAME ).getProductForm();
|
||||
registerPlugin( 'wc-admin-product-editor-form-fills', {
|
||||
// @ts-expect-error 'scope' does exist. @types/wordpress__plugins is outdated.
|
||||
scope: 'woocommerce-product-editor',
|
||||
render: () => {
|
||||
return <Form />;
|
||||
},
|
||||
} );
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Card, CardBody } from '@wordpress/components';
|
||||
import {
|
||||
__experimentalWooProductSectionItem as WooProductSectionItem,
|
||||
__experimentalProductFieldSection as ProductFieldSection,
|
||||
} from '@woocommerce/components';
|
||||
import { ProductFormSection } from '@woocommerce/data';
|
||||
|
||||
export const Sections: React.FC< { sections: ProductFormSection[] } > = ( {
|
||||
sections,
|
||||
} ) => {
|
||||
return (
|
||||
<>
|
||||
{ sections.map( ( section ) => (
|
||||
<WooProductSectionItem
|
||||
key={ section.id }
|
||||
id={ section.id }
|
||||
location={ section.location }
|
||||
pluginId={ section.plugin_id }
|
||||
order={ section.order }
|
||||
>
|
||||
<ProductFieldSection
|
||||
id={ section.id }
|
||||
title={ section.title }
|
||||
description={ section.description }
|
||||
/>
|
||||
</WooProductSectionItem>
|
||||
) ) }
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -7,8 +7,8 @@ import {
|
|||
__experimentalWooProductSectionItem as WooProductSectionItem,
|
||||
} from '@woocommerce/components';
|
||||
import { PartialProduct, Product } from '@woocommerce/data';
|
||||
import { Ref } from 'react';
|
||||
import { PluginArea } from '@wordpress/plugins';
|
||||
import { Ref } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -26,8 +26,6 @@ import { OptionsSection } from './sections/options-section';
|
|||
import { ProductFormFooter } from './layout/product-form-footer';
|
||||
import { ProductFormTab } from './product-form-tab';
|
||||
|
||||
import './fills';
|
||||
|
||||
export const ProductForm: React.FC< {
|
||||
product?: PartialProduct;
|
||||
formRef?: Ref< FormRef< Partial< Product > > >;
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: tweak
|
||||
|
||||
Minor adjustments to the ProductForm API
|
Loading…
Reference in New Issue