/** * External dependencies */ import { TourKitTypes } from '@woocommerce/components'; /** * Internal dependencies */ import { scrollPopperToVisibleAreaIfNeeded } from './utils'; export const getTourConfig = ( { closeHandler, onNextStepHandler, autoScrollBlock, steps, }: { closeHandler: TourKitTypes.CloseHandler; onNextStepHandler: ( currentStepIndex: number ) => void; autoScrollBlock: ScrollLogicalPosition; steps: TourKitTypes.WooStep[]; } ): TourKitTypes.WooConfig => { let previousPopperTopPosition: number | null = null; let perviousPopperRef: unknown = null; const defaultPlacement = 'top-start'; return { placement: defaultPlacement, options: { effects: { spotlight: { interactivity: { enabled: true, rootElementSelector: '.woocommerce.wc-addons-wrap', }, }, autoScroll: { behavior: 'auto', block: autoScrollBlock, }, }, popperModifiers: [ { name: 'arrow', options: { padding: ( { popper, }: { popper: { width: number }; } ) => { return { // Align the arrow to the left of the popper. right: popper.width - 34, }; }, }, }, { name: 'offset', options: { offset: [ 20, 20 ], }, }, { name: 'flip', options: { allowedAutoPlacements: [ 'right', 'bottom', 'top' ], fallbackPlacements: [ 'bottom-start', 'right' ], flipVariations: false, boundry: 'clippingParents', }, }, { name: 'inAppTourPopperModifications', enabled: true, phase: 'read', fn( { state, instance } ) { // 1. First modification - force `right` placement for items in admin menu. if ( perviousPopperRef !== state.elements.reference ) { const isAdminMenuItem = ( state.elements.reference as HTMLElement ).closest( '#adminmenu' ); const desiredPlacement = isAdminMenuItem ? 'right' : defaultPlacement; if ( state.placement !== desiredPlacement ) { instance.setOptions( { placement: desiredPlacement, } ); } } // 2. Second modification - Try to make sure that the popper is visible once when // the next step is displayed. const popperBoundingRect = state.elements.popper.getBoundingClientRect(); const arrowBoundingRect = state.elements.arrow?.getBoundingClientRect(); const arrowHeight = arrowBoundingRect?.height || 0; // Try to make sure that the popper is visible if poppers' reference (step) changed and // if arrowHeight is not 0 (it means that popper's position hasn't been updated yet). // Also, change if popper's top position changed - the modifier can be called // multiple times for the same position. if ( perviousPopperRef !== state.elements.reference && arrowHeight !== 0 && previousPopperTopPosition !== popperBoundingRect.top ) { scrollPopperToVisibleAreaIfNeeded( popperBoundingRect ); previousPopperTopPosition = popperBoundingRect.top; perviousPopperRef = state.elements.reference; } }, }, ], callbacks: { onNextStep: onNextStepHandler, }, }, steps, closeHandler, }; };