Skip to content

Commit a95447b

Browse files
committed
Convert Excerpt component to Tailwind
1 parent 2e26f16 commit a95447b

File tree

5 files changed

+70
-94
lines changed

5 files changed

+70
-94
lines changed

src/sidebar/components/Excerpt.js

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,32 @@ import { applyTheme } from '../helpers/theme';
2020
* @param {InlineControlsProps} props
2121
*/
2222
function InlineControls({ isCollapsed, setCollapsed, linkStyle = {} }) {
23-
const toggleLabel = isCollapsed ? 'More' : 'Less';
24-
2523
return (
26-
<div className="Excerpt__inline-controls">
27-
<div className="Excerpt__toggle-container">
24+
<div
25+
className={classnames(
26+
// Position these controls at the bottom right of the excerpt
27+
'absolute block right-0 bottom-0',
28+
// Give extra width for larger tap target and gradient fade
29+
// Fade transparent-to-white left-to-right to make the toggle
30+
// control text (More/Less) more readable above other text.
31+
// This gradient is implemented to-left to take advantage of Tailwind's
32+
// automatic to-transparent calculation: this avoids Safari's problem
33+
// with transparents in gradients:
34+
// https://bugs.webkit.org/show_bug.cgi?id=150940
35+
// https://tailwindcss.com/docs/gradient-color-stops#fading-to-transparent
36+
'w-20 bg-gradient-to-l from-white'
37+
)}
38+
>
39+
<div className="flex justify-end">
2840
<LinkButton
29-
classes="InlineLinkButton InlineLinkButton--underlined"
41+
classes="inline underline"
3042
onClick={() => setCollapsed(!isCollapsed)}
3143
expanded={!isCollapsed}
3244
title="Toggle visibility of full excerpt text"
3345
style={linkStyle}
3446
variant="dark"
3547
>
36-
{toggleLabel}
48+
{isCollapsed ? 'More' : 'Less'}
3749
</LinkButton>
3850
</div>
3951
</div>
@@ -86,14 +98,13 @@ function Excerpt({
8698
const [collapsedByInlineControls, setCollapsedByInlineControls] =
8799
useState(true);
88100

89-
// Container for the excerpt's content.
90101
const contentElement = /** @type {{ current: HTMLDivElement }} */ (useRef());
91102

92-
// Measured height of `contentElement` in pixels.
103+
// Measured height of `contentElement` in pixels
93104
const [contentHeight, setContentHeight] = useState(0);
94105

95-
// Update the measured height of the content after the initial render and
96-
// when the size of the content element changes.
106+
// Update the measured height of the content container after initial render,
107+
// and when the size of the content element changes.
97108
const updateContentHeight = useCallback(() => {
98109
const newContentHeight = contentElement.current.clientHeight;
99110
setContentHeight(newContentHeight);
@@ -133,18 +144,48 @@ function Excerpt({
133144
: onToggleCollapsed(collapsed);
134145

135146
return (
136-
<div className="Excerpt" style={contentStyle}>
137-
<div className="Excerpt__content" ref={contentElement}>
147+
<div
148+
data-testid="excerpt-container"
149+
className={classnames(
150+
'relative overflow-hidden',
151+
'transition-[max-height] ease-in duration-150'
152+
)}
153+
style={contentStyle}
154+
>
155+
<div
156+
className={classnames(
157+
// Establish new block-formatting context to prevent margin-collapsing
158+
// in descendent elements from potentially "leaking out" and pushing
159+
// this element down from the top of the container.
160+
// See https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context
161+
// See https://github.com/hypothesis/client/issues/1518
162+
'inline-block w-full'
163+
)}
164+
data-testid="excerpt-content"
165+
ref={contentElement}
166+
>
138167
{children}
139168
</div>
140169
<div
170+
data-testid="excerpt-expand"
141171
role="presentation"
142172
onClick={() => setCollapsed(false)}
143-
className={classnames({
144-
Excerpt__shadow: true,
145-
'Excerpt__shadow--transparent': inlineControls,
146-
'is-hidden': !isExpandable,
147-
})}
173+
className={classnames(
174+
// This element provides a clickable area at the bottom of an
175+
// expandable excerpt to expand it.
176+
'transition-[opacity] duration-150 ease-linear',
177+
'absolute w-full bottom-0 h-touch-minimum',
178+
{
179+
// For expandable excerpts not using inlineControls, style this
180+
// element with a custom shadow-like gradient
181+
'bg-gradient-to-b from-excerptStop1 via-excerptStop2 to-excerptStop3':
182+
!inlineControls && isExpandable,
183+
'bg-none': inlineControls,
184+
// Don't make this shadow visible OR clickable if there's nothing
185+
// to do here (the excerpt isn't expandable)
186+
'opacity-0 pointer-events-none': !isExpandable,
187+
}
188+
)}
148189
title="Show the full excerpt"
149190
/>
150191
{isOverflowing && inlineControls && (

src/sidebar/components/test/Excerpt-test.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,14 @@ describe('Excerpt', () => {
5050
});
5151

5252
function getExcerptHeight(wrapper) {
53-
return wrapper.find('.Excerpt').prop('style')['max-height'];
53+
return wrapper.find('[data-testid="excerpt-container"]').prop('style')[
54+
'max-height'
55+
];
5456
}
5557

5658
it('renders content in container', () => {
5759
const wrapper = createExcerpt();
58-
const contentEl = wrapper.find('.Excerpt__content');
60+
const contentEl = wrapper.find('[data-testid="excerpt-content"]');
5961
assert.include(contentEl.html(), 'default content');
6062
});
6163

@@ -109,7 +111,7 @@ describe('Excerpt', () => {
109111
it('calls `onToggleCollapsed` when user clicks in bottom area to expand excerpt', () => {
110112
const onToggleCollapsed = sinon.stub();
111113
const wrapper = createExcerpt({ onToggleCollapsed }, TALL_DIV);
112-
const control = wrapper.find('.Excerpt__shadow');
114+
const control = wrapper.find('[data-testid="excerpt-expand"]');
113115
assert.equal(getExcerptHeight(wrapper), 40);
114116
control.simulate('click');
115117
assert.called(onToggleCollapsed);

src/styles/sidebar/components/Excerpt.scss

Lines changed: 0 additions & 73 deletions
This file was deleted.

src/styles/sidebar/components/_index.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
// ----------
1515
@use './AnnotationShareControl';
1616
@use './AutocompleteList';
17-
@use './Excerpt';
1817
@use './FilterSelect';
1918
@use './FilterStatus';
2019
@use './GroupList';

tailwind.config.mjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ export default {
7575
'px-tiny': ['10px'],
7676
'px-base': ['16px'],
7777
},
78+
gradientColorStops: {
79+
// These gradient stops define a custom gradient shown at the bottom of
80+
// long annotation body excerpts.
81+
excerptStop1: 'rgba(255, 255, 255, 0) 50%',
82+
excerptStop2: 'rgba(0, 0, 0, 0.08) 95%',
83+
excerptStop3: 'rgba(0, 0, 0, 0.13) 100%',
84+
},
7885
keyframes: {
7986
adderPopDown: {
8087
'0%': {

0 commit comments

Comments
 (0)