This repository has been archived by the owner on Feb 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 219
Convert all products edit to TypeScript #9782
Open
Sidsector9
wants to merge
13
commits into
woocommerce:trunk
Choose a base branch
from
Sidsector9:upkeep/9524
base: trunk
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
0696a64
migrate to typescript
Sidsector9 ed48fc3
fix typescript errors
Sidsector9 0c99974
Merge branch 'trunk' into upkeep/9524
Sidsector9 4b48aca
Merge branch 'upkeep/9524' of github.com:Sidsector9/woocommerce-block…
Sidsector9 40c5340
Merge branch 'trunk' into upkeep/9524
kmanijak 7da0883
Merge branch 'trunk' into upkeep/9524
Sidsector9 dca174b
Merge branch 'upkeep/9524' of github.com:Sidsector9/woocommerce-block…
Sidsector9 742ec90
move from class to functional component
Sidsector9 8f30bd0
Merge branch 'trunk' into upkeep/9524
Sidsector9 df9d3f9
Merge branch 'trunk' into upkeep/9524
nielslange c1aae08
Merge branch 'trunk' into upkeep/9524
Sidsector9 2ecc032
PR review changes
Sidsector9 6261b4e
Merge branch 'upkeep/9524' of github.com:Sidsector9/woocommerce-block…
Sidsector9 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -2,25 +2,23 @@ | |||||
* External dependencies | ||||||
*/ | ||||||
import { __ } from '@wordpress/i18n'; | ||||||
import { createBlock } from '@wordpress/blocks'; | ||||||
import { createBlock, BlockInstance } from '@wordpress/blocks'; | ||||||
import { | ||||||
BlockControls, | ||||||
InnerBlocks, | ||||||
InspectorControls, | ||||||
} from '@wordpress/block-editor'; | ||||||
import { withDispatch, withSelect } from '@wordpress/data'; | ||||||
import { useDispatch } from '@wordpress/data'; | ||||||
import { | ||||||
PanelBody, | ||||||
withSpokenMessages, | ||||||
Placeholder, | ||||||
Button, | ||||||
ToolbarGroup, | ||||||
Disabled, | ||||||
Tip, | ||||||
} from '@wordpress/components'; | ||||||
import { Component } from '@wordpress/element'; | ||||||
import { compose } from '@wordpress/compose'; | ||||||
import PropTypes from 'prop-types'; | ||||||
import { useState, useEffect } from '@wordpress/element'; | ||||||
import { useDebounce } from '@wordpress/compose'; | ||||||
import { Icon, grid } from '@wordpress/icons'; | ||||||
import GridLayoutControl from '@woocommerce/editor-components/grid-layout-control'; | ||||||
import { | ||||||
|
@@ -31,6 +29,7 @@ import { getBlockMap } from '@woocommerce/atomic-utils'; | |||||
import { previewProducts } from '@woocommerce/resource-previews'; | ||||||
import { getSetting } from '@woocommerce/settings'; | ||||||
import { blocksConfig } from '@woocommerce/block-settings'; | ||||||
import { speak } from '@wordpress/a11y'; | ||||||
|
||||||
/** | ||||||
* Internal dependencies | ||||||
|
@@ -48,51 +47,50 @@ import { getSharedContentControls, getSharedListControls } from '../edit'; | |||||
import Block from './block'; | ||||||
import './editor.scss'; | ||||||
|
||||||
/** | ||||||
* Component to handle edit mode of "All Products". | ||||||
*/ | ||||||
class Editor extends Component { | ||||||
static propTypes = { | ||||||
/** | ||||||
* The attributes for this block. | ||||||
*/ | ||||||
attributes: PropTypes.object.isRequired, | ||||||
/** | ||||||
* A callback to update attributes. | ||||||
*/ | ||||||
setAttributes: PropTypes.func.isRequired, | ||||||
/** | ||||||
* From withSpokenMessages. | ||||||
*/ | ||||||
debouncedSpeak: PropTypes.func.isRequired, | ||||||
}; | ||||||
type Attributes = { | ||||||
columns: number; | ||||||
rows: number; | ||||||
alignButtons: boolean; | ||||||
layoutConfig: [ string, object? ][]; | ||||||
}; | ||||||
|
||||||
state = { | ||||||
isEditing: false, | ||||||
innerBlocks: [], | ||||||
}; | ||||||
type Props = { | ||||||
contentVisibility: number; | ||||||
orderby: number; | ||||||
isPreview: number; | ||||||
clientId: string; | ||||||
attributes: Attributes; | ||||||
setAttributes: ( attributes: Record< string, unknown > ) => void; | ||||||
}; | ||||||
|
||||||
blockMap = getBlockMap( 'woocommerce/all-products' ); | ||||||
export default function Edit( props: Props ): JSX.Element { | ||||||
const [ isEditing, setIsEditing ] = useState< boolean >( false ); | ||||||
const [ innerBlocks, setInnerBlocks ] = useState< BlockInstance[] >( [] ); | ||||||
const blockMap = getBlockMap( 'woocommerce/all-products' ); | ||||||
|
||||||
componentDidMount = () => { | ||||||
const { block } = this.props; | ||||||
this.setState( { innerBlocks: block.innerBlocks } ); | ||||||
}; | ||||||
const { clientId, attributes, setAttributes } = props; | ||||||
|
||||||
getTitle = () => { | ||||||
const { columns, rows, alignButtons, layoutConfig } = attributes; | ||||||
|
||||||
useEffect( () => { | ||||||
setInnerBlocks( innerBlocks ); | ||||||
}, [] ); | ||||||
|
||||||
const { replaceInnerBlocks } = useDispatch( 'core/block-editor' ); | ||||||
const debouncedSpeak = useDebounce( speak ); | ||||||
|
||||||
const getTitle = (): string => { | ||||||
return __( 'All Products', 'woo-gutenberg-products-block' ); | ||||||
}; | ||||||
|
||||||
getIcon = () => { | ||||||
const getIcon = (): JSX.Element => { | ||||||
return <Icon icon={ grid } />; | ||||||
}; | ||||||
|
||||||
togglePreview = () => { | ||||||
const { debouncedSpeak } = this.props; | ||||||
const togglePreview = (): void => { | ||||||
setIsEditing( ! isEditing ); | ||||||
|
||||||
this.setState( { isEditing: ! this.state.isEditing } ); | ||||||
|
||||||
if ( ! this.state.isEditing ) { | ||||||
if ( ! isEditing ) { | ||||||
debouncedSpeak( | ||||||
__( | ||||||
'Showing All Products block preview.', | ||||||
|
@@ -102,10 +100,7 @@ class Editor extends Component { | |||||
} | ||||||
}; | ||||||
|
||||||
getInspectorControls = () => { | ||||||
const { attributes, setAttributes } = this.props; | ||||||
const { columns, rows, alignButtons } = attributes; | ||||||
|
||||||
const getInspectorControls = (): JSX.Element => { | ||||||
return ( | ||||||
<InspectorControls key="inspector"> | ||||||
<PanelBody | ||||||
|
@@ -120,10 +115,10 @@ class Editor extends Component { | |||||
rows={ rows } | ||||||
alignButtons={ alignButtons } | ||||||
setAttributes={ setAttributes } | ||||||
minColumns={ getSetting( 'min_columns', 1 ) } | ||||||
maxColumns={ getSetting( 'max_columns', 6 ) } | ||||||
minRows={ getSetting( 'min_rows', 1 ) } | ||||||
maxRows={ getSetting( 'max_rows', 6 ) } | ||||||
minColumns={ getSetting( 'min_columns', 1 ) as number } | ||||||
maxColumns={ getSetting( 'max_columns', 6 ) as number } | ||||||
minRows={ getSetting( 'min_rows', 1 ) as number } | ||||||
maxRows={ getSetting( 'max_rows', 6 ) as number } | ||||||
/> | ||||||
</PanelBody> | ||||||
<PanelBody | ||||||
|
@@ -139,9 +134,7 @@ class Editor extends Component { | |||||
); | ||||||
}; | ||||||
|
||||||
getBlockControls = () => { | ||||||
const { isEditing } = this.state; | ||||||
|
||||||
const getBlockControls = (): JSX.Element => { | ||||||
return ( | ||||||
<BlockControls> | ||||||
<ToolbarGroup | ||||||
|
@@ -152,7 +145,7 @@ class Editor extends Component { | |||||
'Edit the layout of each product', | ||||||
'woo-gutenberg-products-block' | ||||||
), | ||||||
onClick: () => this.togglePreview(), | ||||||
onClick: () => togglePreview(), | ||||||
isActive: isEditing, | ||||||
}, | ||||||
] } | ||||||
|
@@ -161,46 +154,47 @@ class Editor extends Component { | |||||
); | ||||||
}; | ||||||
|
||||||
renderEditMode = () => { | ||||||
const renderEditMode = () => { | ||||||
const onDone = () => { | ||||||
const { block, setAttributes } = this.props; | ||||||
setAttributes( { | ||||||
layoutConfig: getProductLayoutConfig( block.innerBlocks ), | ||||||
layoutConfig: getProductLayoutConfig( innerBlocks ), | ||||||
} ); | ||||||
this.setState( { innerBlocks: block.innerBlocks } ); | ||||||
this.togglePreview(); | ||||||
setInnerBlocks( innerBlocks ); | ||||||
togglePreview(); | ||||||
}; | ||||||
|
||||||
const onCancel = () => { | ||||||
const { block, replaceInnerBlocks } = this.props; | ||||||
const { innerBlocks } = this.state; | ||||||
replaceInnerBlocks( block.clientId, innerBlocks, false ); | ||||||
this.togglePreview(); | ||||||
replaceInnerBlocks( clientId, innerBlocks, false ); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When you use Steps to reproduce:
|
||||||
togglePreview(); | ||||||
}; | ||||||
|
||||||
const onReset = () => { | ||||||
const { block, replaceInnerBlocks } = this.props; | ||||||
const newBlocks = []; | ||||||
DEFAULT_PRODUCT_LIST_LAYOUT.map( ( [ name, attributes ] ) => { | ||||||
newBlocks.push( createBlock( name, attributes ) ); | ||||||
const newBlocks: BlockInstance[] = []; | ||||||
DEFAULT_PRODUCT_LIST_LAYOUT.map( ( [ name, blockAttributes ] ) => { | ||||||
newBlocks.push( createBlock( name, blockAttributes ) ); | ||||||
return true; | ||||||
} ); | ||||||
replaceInnerBlocks( block.clientId, newBlocks, false ); | ||||||
this.setState( { innerBlocks: block.innerBlocks } ); | ||||||
replaceInnerBlocks( clientId, newBlocks, false ); | ||||||
setInnerBlocks( innerBlocks ); | ||||||
}; | ||||||
|
||||||
const InnerBlockProps = { | ||||||
template: this.props.attributes.layoutConfig, | ||||||
const InnerBlockProps: { | ||||||
template: [ string, object? ][]; | ||||||
templateLock: boolean; | ||||||
allowedBlocks: Array< string >; | ||||||
renderAppender?: undefined | boolean; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Considering the property is optional, it can be implicitly
Suggested change
|
||||||
} = { | ||||||
template: layoutConfig, | ||||||
templateLock: false, | ||||||
allowedBlocks: Object.keys( this.blockMap ), | ||||||
allowedBlocks: Object.keys( blockMap ), | ||||||
}; | ||||||
|
||||||
if ( this.props.attributes.layoutConfig.length !== 0 ) { | ||||||
if ( layoutConfig.length !== 0 ) { | ||||||
InnerBlockProps.renderAppender = false; | ||||||
} | ||||||
|
||||||
return ( | ||||||
<Placeholder icon={ this.getIcon() } label={ this.getTitle() }> | ||||||
<Placeholder icon={ getIcon() } label={ getTitle() }> | ||||||
{ __( | ||||||
'Display all products from your store as a grid.', | ||||||
'woo-gutenberg-products-block' | ||||||
|
@@ -221,7 +215,9 @@ class Editor extends Component { | |||||
<li className="wc-block-grid__product"> | ||||||
<ProductDataContextProvider | ||||||
product={ previewProducts[ 0 ] } | ||||||
isLoading={ false } | ||||||
> | ||||||
{ /* @ts-expect-error: `InnerBlocks` is a component that is typed in WordPress core*/ } | ||||||
<InnerBlocks { ...InnerBlockProps } /> | ||||||
</ProductDataContextProvider> | ||||||
</li> | ||||||
|
@@ -263,61 +259,40 @@ class Editor extends Component { | |||||
); | ||||||
}; | ||||||
|
||||||
renderViewMode = () => { | ||||||
const { attributes } = this.props; | ||||||
const { layoutConfig } = attributes; | ||||||
const renderViewMode = () => { | ||||||
const hasContent = layoutConfig && layoutConfig.length !== 0; | ||||||
const blockTitle = this.getTitle(); | ||||||
const blockIcon = this.getIcon(); | ||||||
const blockTitle = getTitle(); | ||||||
const blockIcon = getIcon(); | ||||||
|
||||||
if ( ! hasContent ) { | ||||||
return renderHiddenContentPlaceholder( blockTitle, blockIcon ); | ||||||
} | ||||||
|
||||||
return ( | ||||||
<Disabled> | ||||||
{ /* @ts-expect-error: `Block` is a component that is typed in WordPress core*/ } | ||||||
<Block attributes={ attributes } /> | ||||||
</Disabled> | ||||||
); | ||||||
}; | ||||||
|
||||||
render = () => { | ||||||
const { attributes } = this.props; | ||||||
const { isEditing } = this.state; | ||||||
const blockTitle = this.getTitle(); | ||||||
const blockIcon = this.getIcon(); | ||||||
const blockTitle = getTitle(); | ||||||
const blockIcon = getIcon(); | ||||||
|
||||||
if ( blocksConfig.productCount === 0 ) { | ||||||
return renderNoProductsPlaceholder( blockTitle, blockIcon ); | ||||||
} | ||||||
if ( blocksConfig.productCount === 0 ) { | ||||||
return renderNoProductsPlaceholder( blockTitle, blockIcon ); | ||||||
} | ||||||
|
||||||
return ( | ||||||
<div | ||||||
className={ getBlockClassName( | ||||||
'wc-block-all-products', | ||||||
attributes | ||||||
) } | ||||||
> | ||||||
{ this.getBlockControls() } | ||||||
{ this.getInspectorControls() } | ||||||
{ isEditing ? this.renderEditMode() : this.renderViewMode() } | ||||||
</div> | ||||||
); | ||||||
}; | ||||||
return ( | ||||||
<div | ||||||
className={ getBlockClassName( | ||||||
'wc-block-all-products', | ||||||
attributes | ||||||
) } | ||||||
> | ||||||
{ getBlockControls() } | ||||||
{ getInspectorControls() } | ||||||
{ isEditing ? renderEditMode() : renderViewMode() } | ||||||
</div> | ||||||
); | ||||||
} | ||||||
|
||||||
export default compose( | ||||||
withSpokenMessages, | ||||||
withSelect( ( select, { clientId } ) => { | ||||||
const { getBlock } = select( 'core/block-editor' ); | ||||||
return { | ||||||
block: getBlock( clientId ), | ||||||
}; | ||||||
} ), | ||||||
withDispatch( ( dispatch ) => { | ||||||
const { replaceInnerBlocks } = dispatch( 'core/block-editor' ); | ||||||
return { | ||||||
replaceInnerBlocks, | ||||||
}; | ||||||
} ) | ||||||
)( Editor ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason we cannot use the
debouncedSpeak
provided through the props as it was before, but rather create a new one?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kmanijak Let me know if I've understood it incorrectly. Previously,
debouncedSpeak
was the props added by thewithSpokenMessages
HOC and I've replaced that with theuseDebounce
hook. Is that not correct?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I didn't spot that. But I think we could stick to keep using
withSpokenMessages
which is used across multiple blocks. Is there a reason to replace it with a custom implementation?