/** * External dependencies */ import deprecated from '@wordpress/deprecated'; import { createSlotFill as baseCreateSlotFill, __experimentalUseSlot, useSlot as __useSlot, } from 'wordpress-components'; import { CURRENT_USER_IS_ADMIN } from '@woocommerce/block-settings'; import { Children, cloneElement } from '@wordpress/element'; /** * Internal dependencies */ import BlockErrorBoundary from '../error-boundary'; /** * This function is used in case __experimentalUseSlot is removed and useSlot is not released, it tries to mock * the return value of that slot. * * @return {Object} The hook mocked return, currently: * fills, a null array of length 2. */ const mockedUseSlot = () => { /** * If we're here, it means useSlot was never graduated and __experimentalUseSlot is removed, so we should change our code. * */ deprecated( '__experimentalUseSlot', { plugin: 'woocommerce-gutenberg-products-block', } ); // We're going to mock its value return { fills: new Array( 2 ), }; }; /** * A hook that is used inside a slotFillProvider to return information on the a slot. * * @param {string} slotName The slot name to be hooked into. * @return {Object} slot data. */ let useSlot; if ( typeof __useSlot === 'function' ) { useSlot = __useSlot; } else if ( typeof __experimentalUseSlot === 'function' ) { useSlot = __experimentalUseSlot; } else { useSlot = mockedUseSlot; } export { useSlot }; /** * Abstracts @wordpress/components createSlotFill, wraps Fill in an error boundary and passes down fillProps. * * @param {string} slotName The generated slotName, based down to createSlotFill. * @param {null|function(Element):Element} [onError] Returns an element to display the error if the current use is an admin. * * @return {Object} Returns a newly wrapped Fill and Slot. */ export const createSlotFill = ( slotName, onError = null ) => { const { Fill: BaseFill, Slot: BaseSlot } = baseCreateSlotFill( slotName ); /** * A Fill that will get rendered inside associate slot. * If the code inside has a error, it would be caught ad removed. * The error is only visible to admins. * * @param {Object} props Items props. * @param {Array} props.children Children to be rendered. */ const Fill = ( { children } ) => ( { ( fillProps ) => Children.map( children, ( fill ) => ( null would render nothing. */ renderError={ CURRENT_USER_IS_ADMIN ? onError : () => null } > { cloneElement( fill, fillProps ) } ) ) } ); /** * A Slot that will get rendered inside our tree. * This forces Slot to use the Portal implementation that allows events to be bubbled to react tree instead of dom tree. * * @param {Object} [props] Slot props. * @param {string} props.className Class name to be used on slot. * @param {Object} props.fillProps Props to be passed to fills. * @param {Element|string} props.as Element used to render the slot, defaults to div. * */ const Slot = ( props ) => ; return { Fill, Slot, }; };