@remit/ui 0.0.52 → 0.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -120,6 +120,36 @@ describe("SelectionSheet", () => {
|
|
|
120
120
|
assert.doesNotMatch(html, /Select similar messages/);
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
+
/**
|
|
124
|
+
* jsdom lays nothing out, so these assert the constraint itself rather than a
|
|
125
|
+
* measured height: the sheet must not carry a fixed pixel ceiling (which sat
|
|
126
|
+
* below its own content at 411×759 and clipped the last action), and its body
|
|
127
|
+
* must scroll rather than clip whatever the ceiling does cut off (#405).
|
|
128
|
+
*/
|
|
129
|
+
it("caps the expanded height against the viewport, not a fixed pixel ceiling", () => {
|
|
130
|
+
const html = renderToString(
|
|
131
|
+
createElement(SelectionSheet, { ...base, startExpanded: true }),
|
|
132
|
+
);
|
|
133
|
+
assert.match(html, /max-height:70dvh/);
|
|
134
|
+
assert.doesNotMatch(html, /max-height:min\(/);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("scrolls the expanded actions instead of clipping them", () => {
|
|
138
|
+
const html = renderToString(
|
|
139
|
+
createElement(SelectionSheet, {
|
|
140
|
+
...base,
|
|
141
|
+
startExpanded: true,
|
|
142
|
+
onSelectSimilar: noop,
|
|
143
|
+
onSomethingElse: noop,
|
|
144
|
+
}),
|
|
145
|
+
);
|
|
146
|
+
assert.match(html, /class="flex min-h-0 flex-1 flex-col overflow-y-auto /);
|
|
147
|
+
assert.doesNotMatch(
|
|
148
|
+
html,
|
|
149
|
+
/class="flex min-h-0 flex-1 flex-col overflow-hidden /,
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
123
153
|
it("renders a partial-failure notice with a Retry action", () => {
|
|
124
154
|
const html = renderToString(
|
|
125
155
|
createElement(SelectionSheet, {
|
|
@@ -66,6 +66,27 @@ export const Expanded: Story = {
|
|
|
66
66
|
args: { startExpanded: true, moveSlot: <MoveSlot /> },
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* The same expanded sheet on the shortest phone viewport a real device reports
|
|
71
|
+
* (411×759 — Android Chrome with its address bar and system nav showing). This
|
|
72
|
+
* is the tallest the content ever gets: select-all, all three quick actions and
|
|
73
|
+
* both smart-flow rows. A fixed height ceiling cut "Something else" off here
|
|
74
|
+
* with no way to reach it (#405); the sheet now takes the height its content
|
|
75
|
+
* needs, and scrolls only when the viewport genuinely can't hold it.
|
|
76
|
+
*/
|
|
77
|
+
export const ExpandedShortViewport: Story = {
|
|
78
|
+
globals: { viewport: { value: "mobileShort" } },
|
|
79
|
+
args: {
|
|
80
|
+
startExpanded: true,
|
|
81
|
+
moveSlot: <MoveSlot />,
|
|
82
|
+
selectAll: {
|
|
83
|
+
checked: false,
|
|
84
|
+
indeterminate: true,
|
|
85
|
+
onChange: () => undefined,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
69
90
|
/** While a search result set pages to its total: the count isn't known, the
|
|
70
91
|
* quick actions are replaced by the running total and an explicit Stop. */
|
|
71
92
|
export const Counting: Story = {
|
|
@@ -24,9 +24,13 @@ const formatCount = (n: number): string => n.toLocaleString();
|
|
|
24
24
|
|
|
25
25
|
/** The peek height of the collapsed teaser row (px). */
|
|
26
26
|
export const SELECTION_SHEET_TEASER_HEIGHT = 56;
|
|
27
|
-
/** Ceiling on the expanded sheet height
|
|
28
|
-
*
|
|
29
|
-
|
|
27
|
+
/** Ceiling on the expanded sheet height, as a share of the dynamic viewport.
|
|
28
|
+
* The sheet is otherwise sized by its own content, so the ceiling binds only on
|
|
29
|
+
* a viewport too short to hold the actions — and there they scroll. */
|
|
30
|
+
const EXPANDED_MAX_DVH = 70;
|
|
31
|
+
/** Assumed expanded height until the sheet has been measured; only sets the
|
|
32
|
+
* collapsed translate for the first frame. */
|
|
33
|
+
const EXPANDED_HEIGHT_FALLBACK = 320;
|
|
30
34
|
|
|
31
35
|
const SNAP_MS = 320;
|
|
32
36
|
const SNAP_EASE = "cubic-bezier(0.32, 0.9, 0.3, 1)";
|
|
@@ -131,11 +135,16 @@ export interface SelectionSheetProps {
|
|
|
131
135
|
|
|
132
136
|
/**
|
|
133
137
|
* The mobile multi-select surface: a peeking bottom sheet that teases at ~56px
|
|
134
|
-
* with the selection count, and expands by drag or tap to
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
138
|
+
* with the selection count, and expands by drag or tap to carry the bulk verbs
|
|
139
|
+
* (Delete / Move / Junk), the select-similar → organize entries, and every
|
|
140
|
+
* escalation state (counting, running progress, partial-failure) the selection
|
|
141
|
+
* can be in. Drag or tap the grabber to collapse back to the teaser; the
|
|
142
|
+
* selection is untouched.
|
|
143
|
+
*
|
|
144
|
+
* Expanded, it takes the height its own content needs, up to
|
|
145
|
+
* {@link EXPANDED_MAX_DVH}% of the dynamic viewport, past which the actions
|
|
146
|
+
* scroll. A fixed ceiling sat below the content on a short phone viewport and
|
|
147
|
+
* cut the last action off with no way to reach it (#405).
|
|
139
148
|
*
|
|
140
149
|
* Sits absolutely against the bottom of the nearest positioned ancestor, so the
|
|
141
150
|
* list it belongs to must be a `relative` container and pad its own bottom by
|
|
@@ -160,7 +169,9 @@ export function SelectionSheet({
|
|
|
160
169
|
}: SelectionSheetProps) {
|
|
161
170
|
const [expanded, setExpanded] = useState(startExpanded);
|
|
162
171
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
163
|
-
const [expandedHeight, setExpandedHeight] = useState(
|
|
172
|
+
const [expandedHeight, setExpandedHeight] = useState(
|
|
173
|
+
EXPANDED_HEIGHT_FALLBACK,
|
|
174
|
+
);
|
|
164
175
|
|
|
165
176
|
// A run or a live count owns the sheet: it stays open so the progress and
|
|
166
177
|
// status can't be dragged out of sight mid-operation.
|
|
@@ -265,7 +276,7 @@ export function SelectionSheet({
|
|
|
265
276
|
data-selection-sheet=""
|
|
266
277
|
className="absolute inset-x-0 bottom-0 z-30 flex select-none flex-col rounded-t-2xl border-t border-line bg-surface shadow-2xl shadow-black/40"
|
|
267
278
|
style={{
|
|
268
|
-
maxHeight:
|
|
279
|
+
maxHeight: `${EXPANDED_MAX_DVH}dvh`,
|
|
269
280
|
minHeight: `${SELECTION_SHEET_TEASER_HEIGHT}px`,
|
|
270
281
|
transform: `translateY(${translate}px)`,
|
|
271
282
|
transition,
|
|
@@ -355,10 +366,12 @@ export function SelectionSheet({
|
|
|
355
366
|
|
|
356
367
|
{/* Expanded content — clipped by the translate when collapsed. It stays
|
|
357
368
|
in the DOM at the teaser, so `inert` when collapsed keeps its offscreen
|
|
358
|
-
verbs out of the tab order and the a11y tree until the sheet opens.
|
|
369
|
+
verbs out of the tab order and the a11y tree until the sheet opens.
|
|
370
|
+
Scrolls rather than clips, so every verb stays reachable on a viewport
|
|
371
|
+
the actions don't fit in (#405). */}
|
|
359
372
|
<div
|
|
360
373
|
inert={!expanded ? true : undefined}
|
|
361
|
-
className="flex min-h-0 flex-1 flex-col overflow-
|
|
374
|
+
className="flex min-h-0 flex-1 flex-col overflow-y-auto px-4 pb-4"
|
|
362
375
|
>
|
|
363
376
|
{progress && (
|
|
364
377
|
<div className="mb-3">
|