fix: histogram no data availavble fix (#13020)

This commit is contained in:
Bhargav
2026-07-03 09:23:42 +00:00
committed by GitHub
parent b3df84b978
commit cffab89a57
3 changed files with 75 additions and 41 deletions
+28 -5
View File
@@ -16,6 +16,7 @@ import { computed, ref } from "vue";
// Canonical keys for every pinnable item in the logs More menu.
export type ToolbarPinKey =
| "histogram"
| "sqlMode"
| "quickMode"
| "functionEditor"
@@ -25,6 +26,7 @@ export type ToolbarPinKey =
// Fixed left-to-right render order for pinned controls. Pinning never changes an
// item's position — it only toggles whether the item is shown outside the menu.
export const TOOLBAR_PIN_ORDER: ToolbarPinKey[] = [
"histogram",
"sqlMode",
"quickMode",
"functionEditor",
@@ -34,18 +36,32 @@ export const TOOLBAR_PIN_ORDER: ToolbarPinKey[] = [
const STORAGE_KEY = "logs_toolbar_pinned_items";
// Histogram was a fixed toolbar control before it became pinnable, so it is
// pinned by default. Its absence from STORAGE_KEY (including arrays persisted
// before histogram existed as a pin key) means "never decided", not "unpinned" —
// this flag records the user's first explicit pin/unpin of histogram, after
// which STORAGE_KEY alone is authoritative.
const HISTOGRAM_PIN_DECIDED_KEY = "logs_toolbar_histogram_pin_decided";
const isValidKey = (key: string): key is ToolbarPinKey =>
(TOOLBAR_PIN_ORDER as string[]).includes(key);
const readInitial = (): ToolbarPinKey[] => {
try {
const raw = window.localStorage.getItem(STORAGE_KEY);
if (!raw) return [];
const parsed = JSON.parse(raw);
if (!Array.isArray(parsed)) return [];
return parsed.filter((k): k is ToolbarPinKey => typeof k === "string" && isValidKey(k));
const parsed = raw ? JSON.parse(raw) : [];
const pins: ToolbarPinKey[] = Array.isArray(parsed)
? parsed.filter(
(k): k is ToolbarPinKey => typeof k === "string" && isValidKey(k),
)
: [];
const histogramDecided =
window.localStorage.getItem(HISTOGRAM_PIN_DECIDED_KEY) === "true";
if (!histogramDecided && !pins.includes("histogram"))
pins.push("histogram");
return pins;
} catch {
return [];
return ["histogram"];
}
};
@@ -72,6 +88,13 @@ export function useToolbarPins() {
if (next.has(key)) next.delete(key);
else next.add(key);
pinnedSet.value = next;
if (key === "histogram") {
try {
window.localStorage.setItem(HISTOGRAM_PIN_DECIDED_KEY, "true");
} catch {
// localStorage may be unavailable — the default-pinned fallback reapplies next session.
}
}
persist();
};
+35 -25
View File
@@ -126,9 +126,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<OTooltip :content="t('search.resetFilters')" />
</OButton>
<!-- Histogram toggle — moves into More menu below 328px available width -->
<!-- ── Pinned toolbar controls ──────────────────────────────────
Items pinned out of the More menu render here in fixed order.
They collapse back into the menu on narrow widths (see
showPinned*/pin* computeds). -->
<!-- Histogram (pinned by default — see useToolbarPins) -->
<OButton
v-if="!shouldMoveButtonsToMenu"
v-if="showPinnedHistogram"
data-test="logs-search-bar-histogram-btn"
size="xs"
variant="outline"
@@ -144,11 +149,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<OTooltip :content="searchObj.meta.showHistogram ? t('search.hideHistogram') : t('search.showHistogramLabel')" shortcut-id="logsToggleHistogram" />
</OButton>
<!-- ── Pinned toolbar controls ──────────────────────────────────
Items pinned out of the More menu render here in fixed order.
They collapse back into the menu on narrow widths (see
showPinned*/pin* computeds). -->
<!-- SQL Mode (pinned) -->
<OButton
v-if="showPinnedSqlMode"
@@ -291,9 +291,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
{{ t("search.resetFilters") }}
</ODropdownItem>
<!-- Histogram (shown here only when toolbar is too narrow for inline button) -->
<!-- Histogram — pinned copy renders on the toolbar -->
<ODropdownItem
v-if="shouldMoveButtonsToMenu"
data-test="logs-search-bar-menu-histogram-btn"
@select.prevent="searchObj.meta.showHistogram = !searchObj.meta.showHistogram"
>
@@ -304,13 +303,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
{{ t("search.showHistogramLabel") }}
<template #icon-right>
<OSwitch
v-model="searchObj.meta.showHistogram"
size="md"
data-test="logs-search-bar-show-histogram-toggle-btn"
class="tw:ml-auto"
@click.stop
/>
<span class="tw:ml-auto tw:flex tw:items-center tw:gap-1">
<OSwitch
v-model="searchObj.meta.showHistogram"
size="md"
data-test="logs-search-bar-show-histogram-toggle-btn"
@click.stop
/>
<OButton
data-test="logs-search-bar-menu-pin-histogram-btn"
variant="ghost-neutral"
size="icon-sm"
:title="isPinned('histogram') ? t('search.unpinFromToolbar') : t('search.pinToToolbar')"
@click.stop="togglePin('histogram')"
>
<OIcon :name="isPinned('histogram') ? 'keep' : 'keep-outline'" size="sm" />
</OButton>
</span>
</template>
</ODropdownItem>
@@ -2404,35 +2413,33 @@ export default defineComponent({
// Each threshold has a small buffer (+16px) so collapse fires before clipping.
const shouldHideToolbarButtonText = computed(() => availableLeftWidth.value < 720);
const toolbarToggleIconOnly = computed(() => availableLeftWidth.value < 568);
const shouldMoveButtonsToMenu = computed(() => availableLeftWidth.value < 328);
const toolbarMoveResetToMenu = computed(() => availableLeftWidth.value < 248);
const toolbarToggleAsDropdown = computed(() => availableLeftWidth.value < 176);
// ── Pinned toolbar items ──────────────────────────────────────────────
// Items pinned out of the "More" menu render as fixed-position toolbar
// controls. They share the left section with the toggle group / reset /
// histogram, so we allocate the leftover width to pinned items with a running
// budget: the More button is always reserved, then pinned items are kept in
// priority order (sql mode kept longest, syntax guide dropped first).
// controls. They share the left section with the toggle group / reset, so we
// allocate the leftover width to pinned items with a running budget: the More
// button is always reserved, then pinned items are kept in priority order
// (histogram kept longest, syntax guide dropped first).
const { isPinned, togglePin } = useToolbarPins();
// Approximate rendered widths (px) of each pinned control and of the fixed
// left-section content, used only to decide how many pinned items fit before
// they would clip. Hidden pinned items stay reachable inside the More menu.
const PIN_ITEM_WIDTH = { sqlMode: 46, quickMode: 46, savedViews: 62 };
const PIN_ITEM_WIDTH = { histogram: 46, sqlMode: 46, quickMode: 46, savedViews: 62 };
const SYNTAX_GUIDE_LABEL_WIDTH = 108;
const SYNTAX_GUIDE_ICON_WIDTH = 40;
const PIN_ITEM_GAP = 4;
// Width consumed by the always-present left content (toggle group in its
// current collapse state, reset, histogram) plus the reserved More button.
// current collapse state, reset) plus the reserved More button.
const baseReservedWidth = computed(() => {
let w = 0;
if (toolbarToggleAsDropdown.value) w += 120;
else if (toolbarToggleIconOnly.value) w += 190;
else w += 350;
if (!toolbarMoveResetToMenu.value) w += shouldHideToolbarButtonText.value ? 40 : 88;
if (!shouldMoveButtonsToMenu.value) w += 64; // histogram button
w += 92; // More button (always visible)
w += 24; // inter-item gaps / padding buffer
return w;
@@ -2448,6 +2455,7 @@ export default defineComponent({
const budget = pinBudget.value;
let used = 0;
const res = {
histogram: false,
sqlMode: false,
quickMode: false,
savedViews: false,
@@ -2462,6 +2470,7 @@ export default defineComponent({
}
return false;
};
if (isPinned("histogram")) res.histogram = tryFit(PIN_ITEM_WIDTH.histogram);
if (isPinned("sqlMode")) res.sqlMode = tryFit(PIN_ITEM_WIDTH.sqlMode);
if (isPinned("quickMode")) res.quickMode = tryFit(PIN_ITEM_WIDTH.quickMode);
if (isPinned("savedViews")) res.savedViews = tryFit(PIN_ITEM_WIDTH.savedViews);
@@ -2478,6 +2487,7 @@ export default defineComponent({
// Function editor lives on the right toolbar (next to the date picker), so it
// is not part of the left-section budget.
const showPinnedHistogram = computed(() => pinnedVisibility.value.histogram);
const showPinnedSqlMode = computed(() => pinnedVisibility.value.sqlMode);
const showPinnedQuickMode = computed(() => pinnedVisibility.value.quickMode);
const showPinnedFunctionEditor = computed(() => isPinned("functionEditor"));
@@ -5217,12 +5227,12 @@ export default defineComponent({
toolbarRightRef,
shouldHideToolbarButtonText,
toolbarToggleIconOnly,
shouldMoveButtonsToMenu,
toolbarMoveResetToMenu,
toolbarToggleAsDropdown,
isPinned,
togglePin,
pinSyntaxGuideIconOnly,
showPinnedHistogram,
showPinnedSqlMode,
showPinnedQuickMode,
showPinnedFunctionEditor,
+12 -11
View File
@@ -349,22 +349,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
<div
class="histogram-empty"
data-test="logs-search-no-data-histogram"
<!-- Same no-data treatment as dashboard panels (PanelSchemaRenderer);
inline min-height/padding overridden to fit the 6.25rem strip. -->
<OEmptyState
v-else-if="
searchObj.meta.showHistogram &&
!searchObj.loadingHistogram &&
!searchObj.loading
"
>
<h6 class="tw:text-center">
<span class="histogram-empty__message">
<OIcon name="warning" size="xs"></OIcon> No
data found for histogram.</span
>
</h6>
</div>
size="inline"
icon="bar-chart"
title="No Data"
:backdrop="false"
data-test="logs-search-no-data-histogram"
class="histogram-empty tw:!min-h-0 tw:!p-2"
/>
<div
class="histogram-empty"
@@ -710,6 +709,7 @@ import OPagination from "@/lib/navigation/Pagination/OPagination.vue";
import ODropdown from "@/lib/overlay/Dropdown/ODropdown.vue";
import ODropdownItem from "@/lib/overlay/Dropdown/ODropdownItem.vue";
import OTag from "@/lib/core/Badge/OTag.vue";
import OEmptyState from "@/lib/core/EmptyState/OEmptyState.vue";
import LoadingProgress from "@/components/common/LoadingProgress.vue";
import { toast } from "@/lib/feedback/Toast/useToast";
@@ -723,6 +723,7 @@ export default defineComponent({
OTooltip,
OSelect,
OPagination,
OEmptyState,
LoadingProgress,
DetailTable: defineAsyncComponent(() => import("./DetailTable.vue")),
ChartRenderer: defineAsyncComponent(