fix(redis): keep key browser actions usable in narrow panels
This commit is contained in:
1 parent
be251d596e
commit
df8a659cc1
2 files changed
+93
-28
No files matched your search
@@ -3215,7 +3215,9 @@ defineExpose({ focusSearch, insertCommand, executeCommand: executeAiCommand });
|
||||
>{{ customGrouping.enabled ? t("redisGrouping.selectLoaded") : t("redis.selectAllLoaded") }}</Button
|
||||
>
|
||||
<Button v-if="checkedKeys.size > 0" variant="ghost" size="sm" class="h-6 shrink-0 px-1.5 text-xs" :disabled="selectionBusy" data-redis-deselect-all @click="clearAllCheckedKeys">{{ t("redis.deselectAll") }}</Button>
|
||||
<Button v-if="checkedKeys.size > 0" variant="ghost" size="sm" class="h-6 shrink-0 px-1.5 text-xs" :disabled="selectionBusy" :title="t('redis.batchExpiry')" data-redis-batch-expiry @click="openBatchExpiryDialog"><Clock class="w-3 h-3 mr-1" />{{ t("redis.batchExpiry") }}</Button>
|
||||
<Button v-if="checkedKeys.size > 0" variant="ghost" size="sm" class="h-6 shrink-0 px-1.5 text-xs" :disabled="selectionBusy" :title="t('redis.batchExpiry')" :aria-label="t('redis.batchExpiry')" data-redis-batch-expiry @click="openBatchExpiryDialog"
|
||||
><Clock class="redis-expiry-icon w-3 h-3 mr-1" /><span class="redis-expiry-label">{{ t("redis.batchExpiry") }}</span></Button
|
||||
>
|
||||
<Button v-if="checkedKeys.size > 0" variant="ghost" size="sm" class="h-6 shrink-0 text-xs text-destructive" :disabled="selectionBusy" data-redis-batch-delete @click="requestBatchDelete"><Trash2 class="w-3 h-3 mr-1" />{{ checkedKeys.size }}</Button>
|
||||
<Button variant="ghost" size="icon" class="h-6 w-6 shrink-0" :disabled="mutatingKeys || loading || loadingMore || isFetchingAll" @click="loadKeys">
|
||||
<Loader2 v-if="loading" class="h-3 w-3 animate-spin" />
|
||||
@@ -3828,14 +3830,17 @@ defineExpose({ focusSearch, insertCommand, executeCommand: executeAiCommand });
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.redis-search-mode-group,
|
||||
.redis-key-toolbar-actions {
|
||||
.redis-search-mode-group {
|
||||
flex-wrap: nowrap;
|
||||
min-width: 0;
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.redis-search-mode-group {
|
||||
justify-self: start;
|
||||
/* Wrapping can never overlap; with nowrap + justify-end an overflowing row
|
||||
spills left and paints over the search-mode segmented control (#9356). */
|
||||
.redis-key-toolbar-actions {
|
||||
flex-wrap: wrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.redis-search-mode-button {
|
||||
@@ -3856,39 +3861,43 @@ defineExpose({ focusSearch, insertCommand, executeCommand: executeAiCommand });
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
@container (max-width: 320px) {
|
||||
/* Toolbar max-content is ~730px; collapse the widest label to icon-only before
|
||||
the row reaches the overflow band (covers ~320px..740px, see #9356). */
|
||||
@container (max-width: 740px) {
|
||||
.redis-expiry-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.redis-expiry-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@container (max-width: 340px) {
|
||||
/* Small-window fallback below the 360px pane floor: two rows —
|
||||
[mode | count] and the action buttons (#9356). */
|
||||
.redis-key-toolbar-header {
|
||||
grid-template-columns: auto auto minmax(0, 1fr);
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.redis-search-mode-group {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.redis-key-count {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 2;
|
||||
text-align: left;
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.redis-key-toolbar-actions {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 2;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@container (max-width: 240px) {
|
||||
.redis-search-mode-group {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.redis-key-count {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.redis-key-toolbar-actions {
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.redis-fuzzy-label {
|
||||
display: none;
|
||||
}
|
||||
@@ -3908,7 +3917,7 @@ defineExpose({ focusSearch, insertCommand, executeCommand: executeAiCommand });
|
||||
}
|
||||
|
||||
.redis-workspace-splitpanes > :deep(.splitpanes__pane:first-child) {
|
||||
min-width: min(256px, 64%);
|
||||
min-width: min(360px, 64%);
|
||||
}
|
||||
|
||||
.redis-workspace-splitpanes :deep(.splitpanes--vertical > .splitpanes__splitter) {
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
// jsdom cannot lay out flex/grid or evaluate container queries, so the #9356
|
||||
// overlap fix is guarded at the source level: the toolbar actions group must
|
||||
// wrap (a nowrap + justify-end row spills LEFT onto the search-mode segmented
|
||||
// control when the key pane is ~320px..730px wide), and the batch-expiry label
|
||||
// must collapse to an icon before the row reaches that overflow band.
|
||||
const source = readFileSync(new URL("../RedisKeyBrowser.vue", import.meta.url), "utf8");
|
||||
|
||||
function styleRule(selector: string): string {
|
||||
// Anchor on a line-start selector so template `class="..."` occurrences never match.
|
||||
const start = source.indexOf(`\n${selector} {`);
|
||||
expect(start).toBeGreaterThan(-1);
|
||||
const open = source.indexOf("{", start);
|
||||
const close = source.indexOf("}", open);
|
||||
return source.slice(open + 1, close);
|
||||
}
|
||||
|
||||
describe("redis key browser toolbar layout (#9356)", () => {
|
||||
it("lets the toolbar actions wrap instead of overflowing onto the search-mode group", () => {
|
||||
expect(styleRule(".redis-key-toolbar-actions")).toContain("flex-wrap: wrap");
|
||||
});
|
||||
|
||||
it("collapses the batch-expiry label to an icon in a 740px container tier", () => {
|
||||
const tierStart = source.indexOf("@container (max-width: 740px)");
|
||||
const tierEnd = source.indexOf("@container (max-width: 340px)");
|
||||
expect(tierStart).toBeGreaterThan(-1);
|
||||
expect(tierEnd).toBeGreaterThan(tierStart);
|
||||
const tierBody = source.slice(tierStart, tierEnd);
|
||||
expect(tierBody).toContain(".redis-expiry-label");
|
||||
expect(tierBody).toContain(".redis-expiry-icon");
|
||||
});
|
||||
|
||||
it("switches to two toolbar rows in the 340px container tier", () => {
|
||||
const tierStart = source.indexOf("@container (max-width: 340px)");
|
||||
const tierEnd = source.indexOf("@container (max-width: 240px)");
|
||||
expect(tierStart).toBeGreaterThan(-1);
|
||||
expect(tierEnd).toBeGreaterThan(tierStart);
|
||||
const tierBody = source.slice(tierStart, tierEnd);
|
||||
expect(tierBody).toContain("grid-template-columns: auto minmax(0, 1fr)");
|
||||
const actionsRule = tierBody.slice(tierBody.indexOf(".redis-key-toolbar-actions"));
|
||||
expect(actionsRule).toContain("grid-row: 2");
|
||||
expect(actionsRule).toContain("justify-content: flex-start");
|
||||
});
|
||||
|
||||
it("marks the batch-expiry icon and label in the template", () => {
|
||||
expect(source).toMatch(/class="[^"]*redis-expiry-icon[^"]*"/);
|
||||
expect(source).toMatch(/<span class="redis-expiry-label">/);
|
||||
expect(source).toContain(`:aria-label="t('redis.batchExpiry')"`);
|
||||
});
|
||||
|
||||
it("keeps the key pane at a 360px minimum width", () => {
|
||||
expect(source).toContain("min-width: min(360px, 64%)");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user