initial commit

This commit is contained in:
2021-12-10 12:03:04 +00:00
commit c46c7ddbf0
3643 changed files with 582794 additions and 0 deletions

View File

@ -0,0 +1,87 @@
/**
* External dependencies
*/
import { useRef } from '@wordpress/element';
/**
* Internal dependencies
*/
import './style.scss';
interface ScrollToTopProps {
focusableSelector?: string;
}
const maybeScrollToTop = ( scrollPoint: HTMLElement ): void => {
if ( ! scrollPoint ) {
return;
}
const yPos = scrollPoint.getBoundingClientRect().bottom;
const isScrollPointVisible = yPos >= 0 && yPos <= window.innerHeight;
if ( ! isScrollPointVisible ) {
scrollPoint.scrollIntoView();
}
};
const moveFocusToElement = (
scrollPoint: HTMLElement,
focusableSelector: string
): void => {
const focusableElements =
scrollPoint.parentElement?.querySelectorAll( focusableSelector ) || [];
if ( focusableElements.length ) {
const targetElement = focusableElements[ 0 ] as HTMLElement;
maybeScrollToTop( targetElement );
targetElement?.focus();
} else {
maybeScrollToTop( scrollPoint );
}
};
const scrollToHTMLElement = (
scrollPoint: HTMLElement,
options: ScrollToTopProps
): void => {
const { focusableSelector } = options || {};
if ( ! window || ! Number.isFinite( window.innerHeight ) ) {
return;
}
if ( focusableSelector ) {
moveFocusToElement( scrollPoint, focusableSelector );
} else {
maybeScrollToTop( scrollPoint );
}
};
/**
* HOC that provides a function to scroll to the top of the component.
*/
const withScrollToTop = (
OriginalComponent: React.FunctionComponent< Record< string, unknown > >
) => {
return ( props: Record< string, unknown > ): JSX.Element => {
const scrollPointRef = useRef< HTMLDivElement >( null );
const scrollToTop = ( args: ScrollToTopProps ) => {
if ( scrollPointRef.current !== null ) {
scrollToHTMLElement( scrollPointRef.current, args );
}
};
return (
<>
<div
className="with-scroll-to-top__scroll-point"
ref={ scrollPointRef }
aria-hidden
/>
<OriginalComponent { ...props } scrollToTop={ scrollToTop } />
</>
);
};
};
export default withScrollToTop;

View File

@ -0,0 +1,4 @@
.with-scroll-to-top__scroll-point {
position: relative;
top: -$gap-larger;
}

View File

@ -0,0 +1,89 @@
/**
* External dependencies
*/
import TestRenderer from 'react-test-renderer';
/**
* Internal dependencies
*/
import withScrollToTop from '../index';
const TestComponent = withScrollToTop( ( props ) => (
<span { ...props }>
<button />
</span>
) );
const focusedMock = jest.fn();
const scrollIntoViewMock = jest.fn();
const mockedButton = {
focus: focusedMock,
scrollIntoView: scrollIntoViewMock,
};
const render = ( { inView } ) => {
const getBoundingClientRect = () => ( {
bottom: inView ? 0 : -10,
} );
return TestRenderer.create( <TestComponent />, {
createNodeMock: ( element ) => {
if ( element.type === 'button' ) {
return {
...mockedButton,
getBoundingClientRect,
};
}
if ( element.type === 'div' ) {
return {
getBoundingClientRect,
parentElement: {
querySelectorAll: () => [
{ ...mockedButton, getBoundingClientRect },
],
},
scrollIntoView: scrollIntoViewMock,
};
}
return null;
},
} );
};
describe( 'withScrollToTop Component', () => {
afterEach( () => {
focusedMock.mockReset();
scrollIntoViewMock.mockReset();
} );
describe( 'if component is not in view', () => {
beforeEach( () => {
const renderer = render( { inView: false } );
const props = renderer.root.findByType( 'span' ).props;
props.scrollToTop( { focusableSelector: 'button' } );
} );
it( 'scrolls to top of the component when scrollToTop is called', () => {
expect( scrollIntoViewMock ).toHaveBeenCalledTimes( 1 );
} );
it( 'moves focus to top of the component when scrollToTop is called', () => {
expect( focusedMock ).toHaveBeenCalledTimes( 1 );
} );
} );
describe( 'if component is in view', () => {
beforeEach( () => {
const renderer = render( { inView: true } );
const props = renderer.root.findByType( 'span' ).props;
props.scrollToTop( { focusableSelector: 'button' } );
} );
it( "doesn't scroll to top of the component when scrollToTop is called", () => {
expect( scrollIntoViewMock ).toHaveBeenCalledTimes( 0 );
} );
it( 'moves focus to top of the component when scrollToTop is called', () => {
expect( focusedMock ).toHaveBeenCalledTimes( 1 );
} );
} );
} );