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

Convert AnnotationShareControl to TW and use shared Card component

Previously, this component was _styled_ as a "panel", via SASS mixins,
but did not have any behavioral "panel-ness."

For now, use the shared Card (presentational) component for this
 component, and remove unused SASS and mixins.
parent e9cdc38f
import { import {
Card,
Icon, Icon,
IconButton, IconButton,
TextInput, TextInput,
TextInputWithButton, TextInputWithButton,
useElementShouldClose, useElementShouldClose,
} from '@hypothesis/frontend-shared'; } from '@hypothesis/frontend-shared';
import classnames from 'classnames';
import { useEffect, useRef, useState } from 'preact/hooks'; import { useEffect, useRef, useState } from 'preact/hooks';
import { isShareableURI } from '../../helpers/annotation-sharing'; import { isShareableURI } from '../../helpers/annotation-sharing';
...@@ -37,6 +39,23 @@ function selectionOverflowsInputElement() { ...@@ -37,6 +39,23 @@ function selectionOverflowsInputElement() {
return isIOS(); return isIOS();
} }
/**
*
* @param {object} props
* @param {string} [props.classes] - Optional additional CSS classes
*/
function MenuArrowDown({ classes }) {
return (
<Icon
name="pointer"
classes={classnames(
'absolute inline z-2 text-grey-3 fill-white rotate-180',
classes
)}
/>
);
}
/** /**
* "Popup"-style component for sharing a single annotation. * "Popup"-style component for sharing a single annotation.
* *
...@@ -123,7 +142,7 @@ function AnnotationShareControl({ ...@@ -123,7 +142,7 @@ function AnnotationShareControl({
); );
return ( return (
<div className="AnnotationShareControl" ref={shareRef}> <div className="relative" ref={shareRef}>
<IconButton <IconButton
icon="share" icon="share"
title="Share" title="Share"
...@@ -131,45 +150,61 @@ function AnnotationShareControl({ ...@@ -131,45 +150,61 @@ function AnnotationShareControl({
expanded={isOpen} expanded={isOpen}
/> />
{isOpen && ( {isOpen && (
<div className="annotation-share-panel"> <Card
<div className="annotation-share-panel__header"> classes={classnames(
<div className="annotation-share-panel__title"> // Prefer width 96 (24rem) but ensure that component isn't wider
// than 85vw
'w-96 max-w-[85vw]',
// Position this Card above its IconButton. Account for larger
// IconButtons in touch interfaces
'absolute bottom-8 right-1 touch:bottom-touch-minimum',
'space-y-2 p-2',
// Cards do not have a border in the clean theme. Turn it back on.
'theme-clean:border'
)}
>
<div className="flex items-center pb-2 border-b">
<h2 className="text-brand text-lg font-medium">
Share this annotation Share this annotation
</div> </h2>
</div>
<div
className={classnames(
// Slightly larger font size for touch devices to correspond with
// larger button and input sizes
'flex w-full text-sm touch:text-base'
)}
>
<TextInputWithButton>
<TextInput
aria-label="Use this URL to share this annotation"
type="text"
value={shareUri}
readOnly
inputRef={inputRef}
/>
<IconButton
icon="copy"
title="Copy share link to clipboard"
onClick={copyShareLink}
variant="dark"
/>
</TextInputWithButton>
</div> </div>
<div className="annotation-share-panel__content"> <div className="text-base font-normal" data-testid="share-details">
<div className="hyp-u-layout-row annotation-share-panel__inputs">
<TextInputWithButton>
<TextInput
aria-label="Use this URL to share this annotation"
type="text"
value={shareUri}
readOnly
inputRef={inputRef}
/>
<IconButton
icon="copy"
title="Copy share link to clipboard"
onClick={copyShareLink}
variant="dark"
/>
</TextInputWithButton>
</div>
{inContextAvailable ? ( {inContextAvailable ? (
<div className="annotation-share-panel__details"> <>{annotationSharingInfo}</>
{annotationSharingInfo}
</div>
) : ( ) : (
<div className="annotation-share-panel__details"> <>
This annotation cannot be shared in its original context because This annotation cannot be shared in its original context because
it was made on a document that is not available on the web. This it was made on a document that is not available on the web. This
link shares the annotation by itself. link shares the annotation by itself.
</div> </>
)} )}
{showShareLinks && <ShareLinks shareURI={shareUri} />}
</div> </div>
<Icon name="pointer" classes="annotation-share-panel__arrow" /> {showShareLinks && <ShareLinks shareURI={shareUri} />}
</div> <MenuArrowDown classes="bottom-[-12px] right-1 touch:right-[9px]" />
</Card>
)} )}
</div> </div>
); );
......
...@@ -106,7 +106,7 @@ describe('AnnotationShareControl', () => { ...@@ -106,7 +106,7 @@ describe('AnnotationShareControl', () => {
const wrapper = createComponent(); const wrapper = createComponent();
// Component is not `open` initially // Component is not `open` initially
assert.isFalse(wrapper.find('.annotation-share-panel').exists()); assert.isFalse(wrapper.find('Card').exists());
}); });
it('toggles the share control element when the button is clicked', () => { it('toggles the share control element when the button is clicked', () => {
...@@ -118,7 +118,7 @@ describe('AnnotationShareControl', () => { ...@@ -118,7 +118,7 @@ describe('AnnotationShareControl', () => {
}); });
wrapper.update(); wrapper.update();
assert.isTrue(wrapper.find('.annotation-share-panel').exists()); assert.isTrue(wrapper.find('Card').exists());
}); });
it('renders the share URI in a readonly input field', () => { it('renders the share URI in a readonly input field', () => {
...@@ -194,7 +194,7 @@ describe('AnnotationShareControl', () => { ...@@ -194,7 +194,7 @@ describe('AnnotationShareControl', () => {
const wrapper = createComponent({ isPrivate: testcase.isPrivate }); const wrapper = createComponent({ isPrivate: testcase.isPrivate });
openElement(wrapper); openElement(wrapper);
const permissionsEl = wrapper.find('.annotation-share-panel__details'); const permissionsEl = wrapper.find('[data-testid="share-details"]');
assert.equal(permissionsEl.text(), testcase.expected); assert.equal(permissionsEl.text(), testcase.expected);
}); });
}); });
...@@ -204,7 +204,7 @@ describe('AnnotationShareControl', () => { ...@@ -204,7 +204,7 @@ describe('AnnotationShareControl', () => {
const wrapper = createComponent(); const wrapper = createComponent();
openElement(wrapper); openElement(wrapper);
const detailsEl = wrapper.find('.annotation-share-panel__details'); const detailsEl = wrapper.find('[data-testid="share-details"]');
assert.include( assert.include(
detailsEl.text(), detailsEl.text(),
'This annotation cannot be shared in its original context' 'This annotation cannot be shared in its original context'
......
...@@ -105,22 +105,6 @@ ...@@ -105,22 +105,6 @@
} }
} }
/**
* `panel` with tighter margins and padding, for use in more confined spaces
*/
@mixin panel--compact {
@include panel($rhythm: var.$layout-space--xsmall);
width: 24em;
// Keep panel constrained within annotation card boundaries and not cut off
// on left side when sidebar is extremely narrow
max-width: 85vw;
padding: var.$layout-space--small;
&__header {
padding-bottom: var.$layout-space--xsmall;
}
}
/** /**
* A full-width banner with optional "type" and styled icon at left * A full-width banner with optional "type" and styled icon at left
*/ */
......
@use '../../mixins/buttons';
@use '../../mixins/forms';
@use '../../mixins/layout';
@use '../../mixins/molecules';
@use '../../mixins/utils';
@use '../../variables' as var;
.AnnotationShareControl {
// Allow pointer arrow to be positioned absolutely relative to this container
position: relative;
}
// A compact panel that appears/disappears by tapping the "share" icon on a
// single annotation.
.annotation-share-panel {
@include molecules.panel--compact;
// Position panel to align with share-annotation icon and alignment arrow
position: absolute;
right: 5px;
bottom: 32px;
@media (pointer: coarse) {
// Adjust arrow/panel positioning to account for larger icon target
right: 13px;
bottom: 40px;
}
// Override the pointer cursor that applies to the entire card
cursor: default;
// Hide the bottom border on the panel's header if displaying
// input (with sharing link) directly below
&__header {
border-bottom: none;
}
&__inputs {
@include utils.font--small;
width: 100%;
}
&__details {
@include utils.font--small;
padding: var.$layout-space--xsmall 0;
}
// Position the pointer icon absolutely and flip it to make it point at the
// share icon
&__arrow {
display: inline;
@include molecules.menu-arrow($direction: 'down');
right: 0px;
bottom: -12px;
}
}
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
// UI (Preact) Components // UI (Preact) Components
// ---------- // ----------
@use './AnnotationShareControl';
@use './AutocompleteList'; @use './AutocompleteList';
@use './FilterSelect'; @use './FilterSelect';
@use './FilterStatus'; @use './FilterStatus';
......
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