mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge pull request #22920 from dannon/gbutton-disabled-title-fix
[26.1] Fix GButton disabled-title not displaying
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import { getLocalVue } from "@tests/vitest/helpers";
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import VueRouter from "vue-router";
|
||||
|
||||
import GButton from "./GButton.vue";
|
||||
|
||||
const localVue = getLocalVue(true);
|
||||
localVue.use(VueRouter);
|
||||
|
||||
function mountGButton(props: object) {
|
||||
return mount(GButton as object, { propsData: props, localVue });
|
||||
}
|
||||
|
||||
describe("GButton.vue", () => {
|
||||
it("uses the regular title when enabled", () => {
|
||||
const wrapper = mountGButton({ title: "Click me" });
|
||||
const button = wrapper.get("button");
|
||||
|
||||
expect(button.attributes("title")).toBe("Click me");
|
||||
expect(button.attributes("data-title")).toBe("Click me");
|
||||
});
|
||||
|
||||
it("uses the disabled title when disabled", () => {
|
||||
const wrapper = mountGButton({
|
||||
disabled: true,
|
||||
title: "Click me",
|
||||
disabledTitle: "Cannot click right now",
|
||||
});
|
||||
const button = wrapper.get("button");
|
||||
|
||||
expect(button.attributes("title")).toBe("Cannot click right now");
|
||||
expect(button.attributes("data-title")).toBe("Cannot click right now");
|
||||
});
|
||||
|
||||
it("falls back to the regular title when disabled without a disabled title", () => {
|
||||
const wrapper = mountGButton({ disabled: true, title: "Click me" });
|
||||
const button = wrapper.get("button");
|
||||
|
||||
expect(button.attributes("title")).toBe("Click me");
|
||||
});
|
||||
|
||||
// A disabled button must stay hoverable so the (disabled) title can surface its
|
||||
// tooltip. We mark it disabled via aria-disabled and a JS click guard rather than
|
||||
// the native `disabled` attribute (which would suppress hover events).
|
||||
it("remains hoverable when disabled", () => {
|
||||
const wrapper = mountGButton({ disabled: true, disabledTitle: "Nope" });
|
||||
const button = wrapper.get("button");
|
||||
|
||||
expect(button.attributes("aria-disabled")).toBe("true");
|
||||
expect(button.attributes("disabled")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not emit click when disabled", async () => {
|
||||
const wrapper = mountGButton({ disabled: true, disabledTitle: "Nope" });
|
||||
|
||||
await wrapper.get("button").trigger("click");
|
||||
|
||||
expect(wrapper.emitted("click")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("emits click when enabled", async () => {
|
||||
const wrapper = mountGButton({ title: "Click me" });
|
||||
|
||||
await wrapper.get("button").trigger("click");
|
||||
|
||||
expect(wrapper.emitted("click")).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("GButton.vue disabled navigation", () => {
|
||||
// A disabled button with a `to` prop must not navigate. The component-level @click
|
||||
// guard does not run for a RouterLink (Vue 2 treats @click on a component as a
|
||||
// component listener, not a native one), and an empty `to` is not a reliable no-op
|
||||
// in vue-router -- so a disabled GButton renders as a plain button instead.
|
||||
it("renders an enabled router-link button as an anchor", () => {
|
||||
const router = new VueRouter({ mode: "abstract", routes: [{ path: "/" }, { path: "/pages/create" }] });
|
||||
const wrapper = mount(GButton as object, {
|
||||
propsData: { to: "/pages/create" },
|
||||
localVue,
|
||||
router,
|
||||
});
|
||||
|
||||
expect(wrapper.element.tagName).toBe("A");
|
||||
});
|
||||
|
||||
it("renders a disabled router-link button as a plain button", () => {
|
||||
const router = new VueRouter({ mode: "abstract", routes: [{ path: "/" }, { path: "/pages/create" }] });
|
||||
const wrapper = mount(GButton as object, {
|
||||
propsData: { to: "/pages/create", disabled: true, disabledTitle: "Nope" },
|
||||
localVue,
|
||||
router,
|
||||
});
|
||||
|
||||
expect(wrapper.element.tagName).toBe("BUTTON");
|
||||
});
|
||||
|
||||
it("does not navigate when a disabled router-link button is clicked", async () => {
|
||||
const router = new VueRouter({ mode: "abstract", routes: [{ path: "/start" }, { path: "/pages/create" }] });
|
||||
await router.push("/start?keep=me");
|
||||
const routeBeforeClick = router.currentRoute.fullPath;
|
||||
const wrapper = mount(GButton as object, {
|
||||
propsData: { to: "/pages/create", disabled: true },
|
||||
localVue,
|
||||
router,
|
||||
});
|
||||
|
||||
await wrapper.trigger("click");
|
||||
|
||||
expect(router.currentRoute.fullPath).toBe(routeBeforeClick);
|
||||
});
|
||||
});
|
||||
@@ -241,8 +241,9 @@ const buttonElementRef = useResolveElement(buttonRef);
|
||||
background-color: var(--color-grey-100) !important;
|
||||
border-color: var(--color-grey-200) !important;
|
||||
color: var(--color-grey-500) !important;
|
||||
// No `pointer-events: none` here: clicks/routing are already blocked in JS, and
|
||||
// suppressing pointer events would also kill hover, hiding the `disabledTitle` tooltip.
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
|
||||
&:focus-visible {
|
||||
border-color: var(--color-grey-500) !important;
|
||||
|
||||
@@ -1,19 +1,30 @@
|
||||
import { computed } from "vue";
|
||||
import { RouterLink } from "vue-router";
|
||||
|
||||
export interface ClickableProps {
|
||||
to?: string;
|
||||
href?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the correct type of clickable root element based on a components props.
|
||||
* Returns the correct type of clickable root element based on a component's props.
|
||||
*
|
||||
* A disabled component always renders as a plain `button`. Rendering it as a
|
||||
* RouterLink (or anchor) would let clicks fall through to navigation: a
|
||||
* component-level `@click` guard does not run for a RouterLink, and an empty
|
||||
* `to`/`href` is not a reliable no-op in vue-router.
|
||||
*/
|
||||
export function useClickableElement(props: ClickableProps) {
|
||||
if (props.to) {
|
||||
return RouterLink;
|
||||
} else if (props.href) {
|
||||
return "a" as const;
|
||||
} else {
|
||||
return "button" as const;
|
||||
}
|
||||
return computed(() => {
|
||||
if (props.disabled) {
|
||||
return "button" as const;
|
||||
} else if (props.to) {
|
||||
return RouterLink;
|
||||
} else if (props.href) {
|
||||
return "a" as const;
|
||||
} else {
|
||||
return "button" as const;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user