woocommerce/plugins/woocommerce-blocks/packages/checkout/components/store-notices-container/store-notices.tsx

134 lines
3.4 KiB
TypeScript
Raw Normal View History

New contexts for `StoreNoticesContainer` and notice grouping (https://github.com/woocommerce/woocommerce-blocks/pull/7711) * Refactor Store Notices Move snackbar hiding filter before notice creation Implements showApplyCouponNotice Refactor context providers Use STORE_NOTICE_CONTEXTS use refs to track notice containers Refactor ref usage Use existing noticeContexts * Move new notice code to checkout package * Combine store and snackbars * Update noticeContexts imports * Remove context provider * Update data store * Fix 502 * Add new error contexts * Force types * Unnecessary reorder of imports * Fix global handling * Document forceType * Optional props are undefined * Remove function name * Missing condition * Remove context prop * Define ACTION_TYPES * Remove controls * Update assets/js/base/context/event-emit/utils.ts Co-authored-by: Thomas Roberts <5656702+opr@users.noreply.github.com> * CONTACT_INFORMATION * Remove ref from registerContainer * Abstract container locating methods * pass context correctly when displaying notices * Remove debugging buttons * Update filter usage - remove useMemo so filter can work inline * Refactor existing error notices from the API (https://github.com/woocommerce/woocommerce-blocks/pull/7728) * Update API type defs * Move create notice utils * Replace useCheckoutNotices with new contexts * processCheckoutResponseHeaders should check headers are defined * Scroll to error notices only if we're not editing a field * Error handling utils * processErrorResponse when pushing changes * processErrorResponse when processing checkout * remove formatStoreApiErrorMessage * Add todo for cart errors * Remove unused deps * unused imports * Fix linting warnings * Unused dep * Update assets/js/types/type-defs/api-response.ts Co-authored-by: Thomas Roberts <5656702+opr@users.noreply.github.com> * Add todo * Use generic * remove const * Update array types * Phone should be in address blocks Co-authored-by: Thomas Roberts <5656702+opr@users.noreply.github.com> * Update store name to wc/store/store-notices * Fix assertResponseIsValid * Funnel woocommerce_rest_invalid_email_address to the correct place * woocommerce_rest_missing_email_address * Move comments around * Move data back into const * Spacing * Remove spacing * Remove forced snack bar and styling * Move notices within wrapper * Remove type * hasStoreNoticesContainer rename * Group by status/context * Remove global context * Remove white space * remove changes to simplify diff * white space * Move comment to typescript * List style * showApplyCouponNotice docs * See if scrollIntoView exists * fix notice tests Co-authored-by: Thomas Roberts <5656702+opr@users.noreply.github.com>
2022-12-19 15:30:13 +00:00
/**
* External dependencies
*/
import classnames from 'classnames';
import { useRef, useEffect } from '@wordpress/element';
import { Notice } from 'wordpress-components';
import { sanitizeHTML } from '@woocommerce/utils';
import { useDispatch } from '@wordpress/data';
import { usePrevious } from '@woocommerce/base-hooks';
import { decodeEntities } from '@wordpress/html-entities';
import { STORE_NOTICES_STORE_KEY } from '@woocommerce/block-data';
/**
* Internal dependencies
*/
import { getClassNameFromStatus } from './utils';
import type { StoreNotice } from './types';
const StoreNotices = ( {
context,
className,
notices,
}: {
context: string;
className: string;
notices: StoreNotice[];
} ): JSX.Element => {
const ref = useRef< HTMLDivElement >( null );
const { removeNotice } = useDispatch( 'core/notices' );
const { registerContainer, unregisterContainer } = useDispatch(
STORE_NOTICES_STORE_KEY
);
const noticeIds = notices.map( ( notice ) => notice.id );
const previousNoticeIds = usePrevious( noticeIds );
useEffect( () => {
// Scroll to container when an error is added here.
const containerRef = ref.current;
if ( ! containerRef ) {
return;
}
// Do not scroll if input has focus.
const activeElement = containerRef.ownerDocument.activeElement;
const inputs = [ 'input', 'select', 'button', 'textarea' ];
if (
activeElement &&
inputs.indexOf( activeElement.tagName.toLowerCase() ) !== -1
) {
return;
}
const newNoticeIds = noticeIds.filter(
( value ) =>
! previousNoticeIds || ! previousNoticeIds.includes( value )
);
if ( newNoticeIds.length && containerRef?.scrollIntoView ) {
containerRef.scrollIntoView( {
behavior: 'smooth',
} );
}
}, [ noticeIds, previousNoticeIds, ref ] );
// Register the container context with the parent.
useEffect( () => {
registerContainer( context );
return () => {
unregisterContainer( context );
};
}, [ context, registerContainer, unregisterContainer ] );
// Group notices by status. Do not group notices that are not dismissable.
const noticesByStatus = {
error: notices.filter( ( { status } ) => status === 'error' ),
success: notices.filter( ( { status } ) => status === 'success' ),
warning: notices.filter( ( { status } ) => status === 'warning' ),
info: notices.filter( ( { status } ) => status === 'info' ),
};
return (
<div
ref={ ref }
className={ classnames( className, 'wc-block-components-notices' ) }
>
{ Object.entries( noticesByStatus ).map(
( [ status, noticeGroup ] ) => {
if ( ! noticeGroup.length ) {
return null;
}
return (
<Notice
key={ `store-notice-${ status }` }
className={ classnames(
'wc-block-components-notices__notice',
getClassNameFromStatus( status )
) }
onRemove={ () => {
noticeGroup.forEach( ( notice ) => {
removeNotice( notice.id, notice.context );
} );
} }
>
{ noticeGroup.length === 1 ? (
<>
{ sanitizeHTML(
decodeEntities(
noticeGroup[ 0 ].content
)
) }
</>
) : (
<ul>
{ noticeGroup.map( ( notice ) => (
<li key={ notice.id }>
{ sanitizeHTML(
decodeEntities( notice.content )
) }
</li>
) ) }
</ul>
) }
</Notice>
);
}
) }
</div>
);
};
export default StoreNotices;