Skip to content

Commit

Permalink
Edit Custom Fields for New Product Editor (#45396)
Browse files Browse the repository at this point in the history
* Create edit modal

* Add update function to the useCustomFields hook

* Integrate the EditModal in the CustomFields component

* Add edition and validation logic to the custom field EditModal component

* Fix text control validation error styles

* Focus the name field when its invalid

* Fix linter error

* Fix edit modal min width and controls width

* Add changelog file

* Fix text overflow in custom fields table

* Remove non needed block style file
  • Loading branch information
mdperez86 authored and Konamiman committed Mar 13, 2024
1 parent fc3bf99 commit 33976b3
Show file tree
Hide file tree
Showing 14 changed files with 254 additions and 43 deletions.
4 changes: 4 additions & 0 deletions packages/js/product-editor/changelog/add-44169-edit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Edit Custom Fields for New Product Editor
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,5 @@
"inserter": false,
"lock": false,
"__experimentalToolbar": false
},
"usesContext": [ "postType" ],
"editorStyle": "file:./editor.css"
}
}

This file was deleted.

1 change: 0 additions & 1 deletion packages/js/product-editor/src/blocks/style.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
@import "product-fields/attributes/editor.scss";
@import "product-fields/description/editor.scss";
@import "product-fields/catalog-visibility/editor.scss";
@import "product-fields/custom-fields/editor.scss";
@import "product-fields/custom-fields-toggle/editor.scss";
@import "product-fields/downloads/editor.scss";
@import "product-fields/images/editor.scss";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,55 +1,96 @@
/**
* External dependencies
*/
import { createElement } from '@wordpress/element';
import { Button } from '@wordpress/components';
import { createElement, Fragment, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import classNames from 'classnames';

/**
* Internal dependencies
*/
import { useCustomFields } from '../../hooks/use-custom-fields';
import { EditModal } from './edit-modal';
import { EmptyState } from './empty-state';
import type { Metadata } from '../../types';
import type { CustomFieldsProps } from './types';

export function CustomFields( { className, ...props }: CustomFieldsProps ) {
const { customFields } = useCustomFields();
const { customFields, updateCustomField } = useCustomFields();
const [ selectedCustomField, setSelectedCustomField ] =
useState< Metadata< string > >();

if ( customFields.length === 0 ) {
return <EmptyState />;
}

function customFieldEditButtonClickHandler(
customField: Metadata< string >
) {
return function handleCustomFieldEditButtonClick() {
setSelectedCustomField( customField );
};
}

function handleEditModalUpdate( customField: Metadata< string > ) {
updateCustomField( customField );
setSelectedCustomField( undefined );
}

function handleEditModalCancel() {
setSelectedCustomField( undefined );
}

return (
<table
{ ...props }
className={ classNames(
'woocommerce-product-custom-fields__table',
className
) }
>
<thead>
<tr className="woocommerce-product-custom-fields__table-row">
<th>{ __( 'Name', 'woocommerce' ) }</th>
<th>{ __( 'Value', 'woocommerce' ) }</th>
<th>{ __( 'Actions', 'woocommerce' ) }</th>
</tr>
</thead>
<tbody>
{ customFields.map( ( customField ) => (
<tr
className="woocommerce-product-custom-fields__table-row"
key={ customField.id ?? customField.key }
>
<td className="woocommerce-product-custom-fields__table-datacell">
{ customField.key }
</td>
<td className="woocommerce-product-custom-fields__table-datacell">
{ customField.value }
</td>
<td className="woocommerce-product-custom-fields__table-datacell"></td>
<>
<table
{ ...props }
className={ classNames(
'woocommerce-product-custom-fields__table',
className
) }
>
<thead>
<tr className="woocommerce-product-custom-fields__table-row">
<th>{ __( 'Name', 'woocommerce' ) }</th>
<th>{ __( 'Value', 'woocommerce' ) }</th>
<th>{ __( 'Actions', 'woocommerce' ) }</th>
</tr>
) ) }
</tbody>
</table>
</thead>
<tbody>
{ customFields.map( ( customField ) => (
<tr
className="woocommerce-product-custom-fields__table-row"
key={ customField.id ?? customField.key }
>
<td className="woocommerce-product-custom-fields__table-datacell">
{ customField.key }
</td>
<td className="woocommerce-product-custom-fields__table-datacell">
{ customField.value }
</td>
<td className="woocommerce-product-custom-fields__table-datacell">
<Button
variant="tertiary"
onClick={ customFieldEditButtonClickHandler(
customField
) }
>
{ __( 'Edit', 'woocommerce' ) }
</Button>
</td>
</tr>
) ) }
</tbody>
</table>

{ selectedCustomField && (
<EditModal
initialValue={ selectedCustomField }
onUpdate={ handleEditModalUpdate }
onCancel={ handleEditModalCancel }
/>
) }
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* External dependencies
*/
import { Button, Modal } from '@wordpress/components';
import { createElement, useState, useRef } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import classNames from 'classnames';
import type { FocusEvent } from 'react';

/**
* Internal dependencies
*/
import { TextControl } from '../../text-control';
import type { Metadata } from '../../../types';
import type { EditModalProps } from './types';

function validateName( value: string ) {
if ( value.startsWith( '_' ) ) {
return __(
'The name cannot begin with the underscore (_) character.',
'woocommerce'
);
}
}

export function EditModal( {
initialValue,
onUpdate,
onCancel,
...props
}: EditModalProps ) {
const [ customField, setCustomField ] =
useState< Metadata< string > >( initialValue );
const [ validationError, setValidationError ] = useState< string >();
const nameTextRef = useRef< HTMLInputElement >( null );

function renderTitle() {
return sprintf(
/* translators: %s: the name of the custom field */
__( 'Edit %s', 'woocommerce' ),
customField.key
);
}

function handleNameChange( key: string ) {
setCustomField( ( current ) => ( { ...current, key } ) );
}

function handleNameBlur( event: FocusEvent< HTMLInputElement > ) {
const error = validateName( event.target.value );
setValidationError( error );
}

function handleValueChange( value: string ) {
setCustomField( ( current ) => ( { ...current, value } ) );
}

function handleUpdateButtonClick() {
const error = validateName( customField.key );
if ( error ) {
setValidationError( error );
nameTextRef.current?.focus();
return;
}

onUpdate( customField );
}

return (
<Modal
shouldCloseOnClickOutside={ false }
{ ...props }
title={ renderTitle() }
onRequestClose={ onCancel }
className={ classNames(
'woocommerce-product-custom-fields__edit-modal',
props.className
) }
>
<TextControl
ref={ nameTextRef }
label={ __( 'Name', 'woocommerce' ) }
error={ validationError }
value={ customField.key }
onChange={ handleNameChange }
onBlur={ handleNameBlur }
/>

<TextControl
label={ __( 'Value', 'woocommerce' ) }
value={ customField.value }
onChange={ handleValueChange }
/>

<div className="woocommerce-product-custom-fields__edit-modal-actions">
<Button variant="secondary" onClick={ onCancel }>
{ __( 'Cancel', 'woocommerce' ) }
</Button>

<Button variant="primary" onClick={ handleUpdateButtonClick }>
{ __( 'Update', 'woocommerce' ) }
</Button>
</div>
</Modal>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './edit-modal';
export * from './types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.woocommerce-product-custom-fields__edit-modal {
min-width: 75%;

&-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
margin-top: $grid-unit-40;
}

.components-base-control {
width: 100%;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* External dependencies
*/
import { Modal } from '@wordpress/components';

/**
* Internal dependencies
*/
import { Metadata } from '../../../types';

export type EditModalProps = Omit<
Modal.Props,
'title' | 'onRequestClose' | 'children'
> & {
initialValue: Metadata< string >;
onUpdate( value: Metadata< string > ): void;
onCancel(): void;
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import "./empty-state/style.scss";
@import "./edit-modal/style.scss";

.woocommerce-product-custom-fields {
&__table {
Expand All @@ -22,12 +23,13 @@
&-datacell {
padding-top: $grid-unit-30;
padding-bottom: $grid-unit-30;
overflow: hidden;

&:last-child {
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0;
padding: 0 2px 0 0;
gap: $grid-unit;
}
}
Expand Down
12 changes: 12 additions & 0 deletions packages/js/product-editor/src/components/text-control/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.woocommerce-product-text-control {
&.has-error {
.components-input-control__container
.components-input-control__backdrop {
border-color: $studio-red-50;
}

.components-base-control__help {
color: $studio-red-50;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ export const TextControl = forwardRef( function ForwardedTextControl(
<InputControl
{ ...props }
ref={ ref }
className={ classNames( className, {
'has-error': error,
} ) }
className={ classNames(
'woocommerce-product-text-control',
className,
{
'has-error': error,
}
) }
label={
<Label
label={ label }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,16 @@ export function useCustomFields<
setMetas( [ ...internalMetas, ...newValue ] );
}

return { customFields, setCustomFields };
function updateCustomField( customField: T ) {
setCustomFields( ( current ) =>
current.map( ( field ) => {
if ( customField.id && field.id === customField.id ) {
return customField;
}
return field;
} )
);
}

return { customFields, setCustomFields, updateCustomField };
}
1 change: 1 addition & 0 deletions packages/js/product-editor/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
@import "components/require-password/styles.scss";
@import "components/schedule-publish-modal/style.scss";
@import "components/custom-fields/style.scss";
@import "components/text-control/style.scss";

/* Field Blocks */

Expand Down

0 comments on commit 33976b3

Please sign in to comment.