Skip to content

Commit

Permalink
chore: Fix funnel support for modals
Browse files Browse the repository at this point in the history
  • Loading branch information
connorlanigan committed Aug 29, 2023
1 parent da29978 commit d10f710
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 18 deletions.
32 changes: 26 additions & 6 deletions src/container/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getExternalProps } from '../internal/utils/external-props';
import { applyDisplayName } from '../internal/utils/apply-display-name';
import useBaseComponent from '../internal/hooks/use-base-component';
import { AnalyticsFunnelSubStep } from '../internal/analytics/components/analytics-funnel';
import { useFunnelSubStep } from '../internal/analytics/hooks/use-funnel';

export { ContainerProps };

Expand All @@ -16,19 +17,38 @@ export default function Container({
disableContentPaddings = false,
...props
}: ContainerProps) {
const baseComponentProps = useBaseComponent('Container');
const externalProps = getExternalProps(props);
return (
<AnalyticsFunnelSubStep>
<InternalContainer
<WrappedInternalContainer
variant={variant}
disableHeaderPaddings={disableHeaderPaddings}
disableContentPaddings={disableContentPaddings}
{...externalProps}
{...baseComponentProps}
disableHeaderPaddings={disableHeaderPaddings}
{...props}
/>
</AnalyticsFunnelSubStep>
);
}

function WrappedInternalContainer({
variant,
disableContentPaddings,
disableHeaderPaddings,
...props
}: ContainerProps) {
const baseComponentProps = useBaseComponent('Container');
const externalProps = getExternalProps(props);
const { subStepRef, funnelSubStepProps } = useFunnelSubStep();
return (
<InternalContainer
variant={variant}
disableHeaderPaddings={disableHeaderPaddings}
disableContentPaddings={disableContentPaddings}
{...externalProps}
{...baseComponentProps}
__subStepRef={subStepRef}
__funnelSubStepProps={funnelSubStepProps}
/>
);
}

applyDisplayName(Container, 'Container');
12 changes: 8 additions & 4 deletions src/container/internal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useMergeRefs } from '../internal/hooks/use-merge-refs';
import { useMobile } from '../internal/hooks/use-mobile';
import { useVisualRefresh } from '../internal/hooks/use-visual-mode';
import styles from './styles.css.js';
import { useFunnelSubStep } from '../internal/analytics/hooks/use-funnel';
import type { useFunnelSubStep } from '../internal/analytics/hooks/use-funnel';

export interface InternalContainerProps extends Omit<ContainerProps, 'variant'>, InternalBaseComponentProps {
__stickyHeader?: boolean;
Expand All @@ -31,6 +31,9 @@ export interface InternalContainerProps extends Omit<ContainerProps, 'variant'>,
* * `full-page` – Only for internal use in table, cards and other components
*/
variant?: ContainerProps['variant'] | 'embedded' | 'full-page' | 'cards';

__funnelSubStepProps?: ReturnType<typeof useFunnelSubStep>['funnelSubStepProps'];
__subStepRef?: ReturnType<typeof useFunnelSubStep>['subStepRef'];
}

export default function InternalContainer({
Expand All @@ -52,6 +55,8 @@ export default function InternalContainer({
__headerRef,
__darkHeader = false,
__disableStickyMobile = true,
__funnelSubStepProps,
__subStepRef,
...restProps
}: InternalContainerProps) {
const isMobile = useMobile();
Expand All @@ -68,12 +73,11 @@ export default function InternalContainer({
);
const { setHasStickyBackground } = useAppLayoutContext();
const isRefresh = useVisualRefresh();
const { subStepRef, funnelSubStepProps } = useFunnelSubStep();

const hasDynamicHeight = isRefresh && variant === 'full-page';
const overlapElement = useDynamicOverlap({ disabled: !hasDynamicHeight || !__darkHeader });

const mergedRef = useMergeRefs(rootRef, subStepRef, __internalRootRef);
const mergedRef = useMergeRefs(rootRef, __subStepRef, __internalRootRef);
const headerMergedRef = useMergeRefs(headerRef, overlapElement, __headerRef);

/**
Expand Down Expand Up @@ -104,7 +108,7 @@ export default function InternalContainer({
return (
<div
{...baseProps}
{...funnelSubStepProps}
{...__funnelSubStepProps}
className={clsx(
baseProps.className,
styles.root,
Expand Down
17 changes: 14 additions & 3 deletions src/internal/analytics/components/analytics-funnel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
getSubStepNameSelector,
getSubStepSelector,
} from '../selectors';
import { nodeBelongs } from '../../utils/node-belongs';

export const FUNNEL_VERSION = '1.0';

Expand Down Expand Up @@ -288,7 +289,7 @@ const InnerAnalyticsFunnelStep = ({ children, stepNumber, stepNameSelector }: An
);
};
interface AnalyticsFunnelSubStepProps {
children?: React.ReactNode;
children?: React.ReactNode | ((props: FunnelSubStepContextValue) => React.ReactNode);
}

export const AnalyticsFunnelSubStep = ({ children }: AnalyticsFunnelSubStepProps) => {
Expand Down Expand Up @@ -330,6 +331,10 @@ export const AnalyticsFunnelSubStep = ({ children }: AnalyticsFunnelSubStepProps
const context = isNested ? inheritedContext : newContext;

useEffect(() => {
if (isNested || !subStepRef.current) {
return;
}

const onMouseDown = () => (mousePressed.current = true);

const onMouseUp = async () => {
Expand All @@ -347,7 +352,7 @@ export const AnalyticsFunnelSubStep = ({ children }: AnalyticsFunnelSubStepProps
*/
await new Promise(r => setTimeout(r, 1));

if (!subStepRef.current || !subStepRef.current.contains(document.activeElement)) {
if (!subStepRef.current || !document.activeElement || !nodeBelongs(subStepRef.current, document.activeElement)) {
isFocusedSubStep.current = false;

/*
Expand All @@ -371,7 +376,13 @@ export const AnalyticsFunnelSubStep = ({ children }: AnalyticsFunnelSubStepProps
subStepNameSelector,
subStepSelector,
focusCleanupFunction,
isNested,
subStepRef,
]);

return <FunnelSubStepContext.Provider value={context}>{children}</FunnelSubStepContext.Provider>;
return (
<FunnelSubStepContext.Provider value={context}>
{typeof children === 'function' ? children(context) : children}
</FunnelSubStepContext.Provider>
);
};
3 changes: 2 additions & 1 deletion src/internal/analytics/hooks/use-funnel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
getSubStepAllSelector,
} from '../selectors';
import { FunnelMetrics } from '../';
import { nodeBelongs } from '../../utils/node-belongs';

/**
* Custom React Hook to manage and interact with FunnelSubStep.
Expand Down Expand Up @@ -116,7 +117,7 @@ export const useFunnelSubStep = () => {
return;
}

if (!subStepRef.current || !subStepRef.current.contains(event.relatedTarget) || !event.relatedTarget) {
if (!subStepRef.current || !event.relatedTarget || !nodeBelongs(subStepRef.current, event.relatedTarget)) {
isFocusedSubStep.current = false;

if (funnelInteractionId && subStepId && funnelState.current !== 'cancelled') {
Expand Down
14 changes: 10 additions & 4 deletions src/modal/internal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ import { ButtonContext } from '../internal/context/button-context';
type InternalModalProps = SomeRequired<ModalProps, 'size'> & InternalBaseComponentProps;

export default function InternalModal({ modalRoot, ...rest }: InternalModalProps) {
const referrerId = useUniqueId('modal');

return (
<Portal container={modalRoot}>
<InnerModal {...rest} />
</Portal>
<div id={referrerId}>
<Portal container={modalRoot}>
<InnerModal {...rest} referrerId={referrerId} />
</Portal>
</div>
);
}

Expand All @@ -47,8 +51,9 @@ function InnerModal({
disableContentPaddings,
onDismiss,
__internalRootRef = null,
referrerId,
...rest
}: InternalModalProps) {
}: InternalModalProps & { referrerId: string }) {
const instanceUniqueId = useUniqueId();
const headerId = `${rest.id || instanceUniqueId}-header`;
const lastMouseDownElementRef = useRef<HTMLElement | null>(null);
Expand Down Expand Up @@ -128,6 +133,7 @@ function InnerModal({
onClick={onOverlayClick}
ref={mergedRef}
style={footerHeight ? { scrollPaddingBottom: footerHeight } : undefined}
data-awsui-referrer-id={referrerId}
>
<FocusLock disabled={!visible} autoFocus={true} restoreFocus={true} className={styles['focus-lock']}>
<div
Expand Down

0 comments on commit d10f710

Please sign in to comment.