mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-04 00:08:55 +00:00
fix(traces): open the service side panel as a non-modal drawer to avoid flicker (v1.1.0) (#15342) (#15440)
This commit is contained in:
@@ -275,6 +275,24 @@ describe("ODrawer", () => {
|
||||
expect(document.activeElement).toBe(panel.element);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it("focuses without scrolling so a contained portal target is not dragged sideways", async () => {
|
||||
const focusSpy = vi.spyOn(HTMLElement.prototype, "focus");
|
||||
const wrapper = mount(ODrawer, {
|
||||
attachTo: document.body,
|
||||
props: { open: true, title: "Contained" },
|
||||
slots: { default: '<button autofocus data-testid="autofocus-close">Close</button>' },
|
||||
});
|
||||
const panel = findDrawerPanel(wrapper);
|
||||
focusSpy.mockClear();
|
||||
|
||||
await panel.vm.$emit("openAutoFocus", new Event("openAutoFocus", { cancelable: true }));
|
||||
await new Promise((resolve) => setTimeout(resolve));
|
||||
|
||||
expect(focusSpy).toHaveBeenCalledWith({ preventScroll: true });
|
||||
focusSpy.mockRestore();
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Escape key behaviour", () => {
|
||||
|
||||
@@ -305,24 +305,25 @@ function findAutoFocusTarget(root: Element): HTMLElement | null {
|
||||
|
||||
function handleOpenAutoFocus(event: Event) {
|
||||
event.preventDefault();
|
||||
// The panel is still translated off-screen by the slide-in, so a scrolling focus would drag a contained portal target sideways.
|
||||
nextTick(() => {
|
||||
const body = bodyRef.value;
|
||||
const field = body ? findAutoFocusTarget(body) : null;
|
||||
if (field) {
|
||||
field.focus();
|
||||
field.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
const autofocus = body?.querySelector<HTMLElement>("[autofocus]");
|
||||
if (autofocus) {
|
||||
autofocus.focus();
|
||||
autofocus.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
const btnEl = (primaryBtnRef.value as any)?.$el as HTMLElement | undefined;
|
||||
if (btnEl) {
|
||||
btnEl.focus();
|
||||
btnEl.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
body?.closest<HTMLElement>("[data-o2-drawer]")?.focus();
|
||||
body?.closest<HTMLElement>("[data-o2-drawer]")?.focus({ preventScroll: true });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +164,7 @@ const ODrawerStub = {
|
||||
"open",
|
||||
"width",
|
||||
"seamless",
|
||||
"modal",
|
||||
"portalTarget",
|
||||
"title",
|
||||
"subTitle",
|
||||
@@ -311,6 +312,14 @@ describe("ServiceGraphNodeSidePanel", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("drawer behaviour", () => {
|
||||
it("renders a non-modal drawer so the page behind stays interactive", () => {
|
||||
wrapper = mountPanel({ selectedNode: baseNode });
|
||||
const drawer = wrapper.findComponent(ODrawerStub);
|
||||
expect(drawer.props("modal")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Service name display
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
:open="visible"
|
||||
:width="60"
|
||||
:seamless="true"
|
||||
:modal="false"
|
||||
:portal-target="containerEl"
|
||||
:title="selectedNode?.name || selectedNode?.label || selectedNode?.id"
|
||||
data-test="service-graph-side-panel"
|
||||
|
||||
@@ -181,6 +181,7 @@ const ODrawerStub = {
|
||||
"open",
|
||||
"width",
|
||||
"seamless",
|
||||
"modal",
|
||||
"portalTarget",
|
||||
"title",
|
||||
"subTitle",
|
||||
|
||||
@@ -1408,6 +1408,48 @@ describe("ServicesCatalog", () => {
|
||||
expect(wrapper.vm.showSidePanel).toBe(false);
|
||||
expect(wrapper.vm.selectedServiceRow).toBeNull();
|
||||
});
|
||||
|
||||
it("should switch the open side panel to another row in place", async () => {
|
||||
mockFetchQueryDataWithHttpStream.mockImplementation((_req: any, callbacks: any) => {
|
||||
const hits = mockServices.slice(0, 2).map((s) => ({
|
||||
service_name: s.service_name,
|
||||
total_requests: s.total_requests,
|
||||
error_count: s.error_count,
|
||||
error_rate: s.error_rate,
|
||||
avg_duration_ns: s.avg_duration_ns,
|
||||
max_duration_ns: s.max_duration_ns,
|
||||
p50_latency_ns: s.p50_latency_ns,
|
||||
p95_latency_ns: s.p95_latency_ns,
|
||||
p99_latency_ns: s.p99_latency_ns,
|
||||
}));
|
||||
|
||||
if (callbacks?.data) {
|
||||
callbacks.data(null, {
|
||||
type: "search_response_hits",
|
||||
content: { results: { hits } },
|
||||
});
|
||||
}
|
||||
if (callbacks?.complete) {
|
||||
callbacks.complete(null, {});
|
||||
}
|
||||
});
|
||||
|
||||
wrapper = mountServicesCatalog();
|
||||
await flushPromises();
|
||||
|
||||
await wrapper.find('[data-test="services-catalog-row-name-api-gateway"]').trigger("click");
|
||||
expect(wrapper.vm.showSidePanel).toBe(true);
|
||||
expect(wrapper.vm.selectedServiceRow?.service_name).toBe("api-gateway");
|
||||
const panelBefore = wrapper.findComponent('[data-test="services-catalog-node-side-panel"]');
|
||||
expect(panelBefore.exists()).toBe(true);
|
||||
|
||||
await wrapper.find('[data-test="services-catalog-row-name-auth-service"]').trigger("click");
|
||||
expect(wrapper.vm.showSidePanel).toBe(true);
|
||||
expect(wrapper.vm.selectedServiceRow?.service_name).toBe("auth-service");
|
||||
const panelAfter = wrapper.findComponent('[data-test="services-catalog-node-side-panel"]');
|
||||
expect(panelAfter.vm).toBe(panelBefore.vm);
|
||||
expect(panelAfter.props("selectedNode")?.name).toBe("auth-service");
|
||||
});
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user