Commit d674e11b authored by Lyza Danger Gardner's avatar Lyza Danger Gardner Committed by Lyza Gardner

Convert `Buckets` to TS

Perform an internal refactor to reduce the number of internal components
parent 0facdc30
import { LabeledButton } from '@hypothesis/frontend-shared';
import classnames from 'classnames';
/**
* @typedef {import('../util/buckets').Bucket} Bucket
* @typedef {import("preact").ComponentChildren} Children
*/
import type { Bucket } from '../util/buckets';
/**
* Render a set of buckets in a vertical channel positioned along the edge of
* the sidebar.
*
* @param {object} props
* @param {Children} props.children
*/
function BucketList({ children }) {
return (
<ul
className={classnames(
// 2020-11-20: Making bucket bar one pixel wider (23px vs 22px) is an
// interim and pragmatic solution for an apparent glitch on
// Safari and Chrome. Adding one pixel resolves this issue:
// https://github.com/hypothesis/client/pull/2750
'absolute w-[23px] left-[-22px] h-full',
// The background is set to low opacity when the sidebar is collapsed.
'bg-grey-2 sidebar-collapsed:bg-black/[.08]',
// Disable pointer events along the sidebar itself; re-enable them in
// bucket indicator buttons
'pointer-events-none'
)}
>
{children}
</ul>
);
}
export type BucketsProps = {
/**
* Bucket containing the $tags of any annotations that are offscreen above
* the current viewport. If the set of $tags is non-empty, up navigation
* will be rendered.
*/
above: Bucket;
/**
* Render a vertically-positioned bucket-list item.
*
* @param {object} props
* @param {Children} props.children
* @param {number} props.topPosition - The vertical top position, in pixels,
* for this bucket item relative to the top of the containing BucketList
*/
function BucketItem({ children, topPosition }) {
return (
<li
className={classnames(
'absolute right-0',
// Re-enable pointer events, which are disabled on the containing list
'pointer-events-auto'
)}
style={{ top: topPosition }}
>
{children}
</li>
);
}
/**
* Bucket containing the $tags of any annotations that are offscreen below
* the current viewport. If the set of $tags is non-empty, down navigation
* will be rendered.
*/
below: Bucket;
/**
* A list of buckets visible on-screen. A left-pointing arrow will be
* rendered for each bucket.
*/
buckets: Bucket[];
onFocusAnnotations: ($tags: string[]) => void;
onScrollToClosestOffScreenAnchor: (
$tags: string[],
direction: 'down' | 'up'
) => void;
onSelectAnnotations: ($tags: string[], toggle: boolean) => void;
};
/**
* A list of buckets, including up and down navigation (when applicable) and
......@@ -64,14 +38,6 @@ function BucketItem({ children, topPosition }) {
* This component and its buttons are sized with absolute units such that they
* don't scale with changes to the host page's root font size. They will still
* properly scale with user/browser zooming.
*
* @param {object} props
* @param {Bucket} props.above
* @param {Bucket} props.below
* @param {Bucket[]} props.buckets
* @param {(tags: string[]) => void} props.onFocusAnnotations
* @param {(tags: string[], direction: 'down'|'up') => void} props.onScrollToClosestOffScreenAnchor
* @param {(tags: string[], toggle: boolean) => void} props.onSelectAnnotations
*/
export default function Buckets({
above,
......@@ -80,14 +46,30 @@ export default function Buckets({
onFocusAnnotations,
onScrollToClosestOffScreenAnchor,
onSelectAnnotations,
}) {
}: BucketsProps) {
const showUpNavigation = above.tags.size > 0;
const showDownNavigation = below.tags.size > 0;
return (
<BucketList>
<ul
className={classnames(
// 2020-11-20: Making bucket bar one pixel wider (23px vs 22px) is an
// interim and pragmatic solution for an apparent glitch on
// Safari and Chrome. Adding one pixel resolves this issue:
// https://github.com/hypothesis/client/pull/2750
'absolute w-[23px] left-[-22px] h-full',
// The background is set to low opacity when the sidebar is collapsed.
'bg-grey-2 sidebar-collapsed:bg-black/[.08]',
// Disable pointer events along the sidebar itself; re-enable them in
// bucket indicator buttons
'pointer-events-none'
)}
>
{showUpNavigation && (
<BucketItem topPosition={above.position}>
<li
className="absolute right-0 pointer-events-auto"
style={{ top: above.position }}
>
<LabeledButton
className={classnames(
'BucketButton UpBucketButton',
......@@ -109,10 +91,14 @@ export default function Buckets({
>
{above.tags.size}
</LabeledButton>
</BucketItem>
</li>
)}
{buckets.map((bucket, index) => (
<BucketItem topPosition={bucket.position} key={index}>
<li
className="absolute right-0 pointer-events-auto"
key={index}
style={{ top: bucket.position }}
>
<LabeledButton
className={classnames(
'BucketButton LeftBucketButton',
......@@ -134,10 +120,13 @@ export default function Buckets({
>
{bucket.tags.size}
</LabeledButton>
</BucketItem>
</li>
))}
{showDownNavigation && (
<BucketItem topPosition={below.position}>
<li
className="absolute right-0 pointer-events-auto"
style={{ top: below.position }}
>
<LabeledButton
className="BucketButton DownBucketButton right-0"
data-testid="down-navigation-button"
......@@ -152,8 +141,8 @@ export default function Buckets({
>
{below.tags.size}
</LabeledButton>
</BucketItem>
</li>
)}
</BucketList>
</ul>
);
}
......@@ -124,7 +124,7 @@ describe('Buckets', () => {
const wrapper = createComponent();
const upButton = wrapper.find(upButtonSelector);
// The list item element wrapping the button
const bucketItem = wrapper.find('BucketItem').first();
const bucketItem = wrapper.find('li').first();
assert.isTrue(upButton.exists());
assert.equal(
......@@ -144,7 +144,7 @@ describe('Buckets', () => {
const downButton = wrapper.find(downButtonSelector);
// The list item element wrapping the button
const bucketItem = wrapper.find('BucketItem').last();
const bucketItem = wrapper.find('li').last();
assert.isTrue(downButton.exists());
assert.equal(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment