@celestia-island/hikari 0.55.115 → 0.55.117
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 +1 -1
- package/src/components/HkAdminTablePage.tsx +14 -2
- package/src/components/HkAuthMethodList.test.tsx +7 -2
- package/src/components/HkCopyBadge.test.tsx +53 -5
- package/src/components/HkCopyBadge.tsx +15 -6
- package/src/components/HkTable.drag.test.tsx +499 -0
- package/src/components/HkTable.scss +86 -0
- package/src/components/HkTable.tsx +203 -7
- package/src/components/HkToast.test.tsx +28 -0
- package/src/components/HkToast.tsx +12 -1
- package/src/components/HkTooltip.test.tsx +75 -13
- package/src/components/HkTooltip.tsx +39 -17
- package/src/i18n/locales/ar/components.json +2 -0
- package/src/i18n/locales/de/components.json +2 -0
- package/src/i18n/locales/en/components.json +2 -0
- package/src/i18n/locales/es/components.json +2 -0
- package/src/i18n/locales/fr/components.json +2 -0
- package/src/i18n/locales/ja/components.json +2 -0
- package/src/i18n/locales/ko/components.json +2 -0
- package/src/i18n/locales/pt/components.json +2 -0
- package/src/i18n/locales/ru/components.json +2 -0
- package/src/i18n/locales/zh-Hans/components.json +2 -0
- package/src/i18n/locales/zh-Hant/components.json +2 -0
package/package.json
CHANGED
|
@@ -50,8 +50,14 @@ export const HkAdminTablePage = defineComponent({
|
|
|
50
50
|
rowKey: { type: String, default: "id" },
|
|
51
51
|
emptyTitle: { type: String, default: "" },
|
|
52
52
|
emptyDescription: { type: String as PropType<string | undefined>, default: undefined },
|
|
53
|
+
/** Left grip column with drag-to-reorder (forwarded to HTable; the
|
|
54
|
+
* `reorder` event is forwarded verbatim — indices into `rows`). */
|
|
55
|
+
draggable: { type: Boolean, default: false },
|
|
53
56
|
},
|
|
54
|
-
|
|
57
|
+
emits: {
|
|
58
|
+
reorder: (_fromIndex: number, _toIndex: number) => true,
|
|
59
|
+
},
|
|
60
|
+
setup(props, { emit, slots }) {
|
|
55
61
|
|
|
56
62
|
return () => {
|
|
57
63
|
const { t } = useI18n();
|
|
@@ -96,7 +102,13 @@ export const HkAdminTablePage = defineComponent({
|
|
|
96
102
|
<div class="hk-admin-table-page-body">{slots.body()}</div>
|
|
97
103
|
) : (
|
|
98
104
|
<HCard padded={false}>
|
|
99
|
-
<HTable
|
|
105
|
+
<HTable
|
|
106
|
+
columns={props.columns}
|
|
107
|
+
rows={props.rows}
|
|
108
|
+
rowKey={props.rowKey}
|
|
109
|
+
draggable={props.draggable}
|
|
110
|
+
onReorder={(from: number, to: number) => emit("reorder", from, to)}
|
|
111
|
+
>
|
|
100
112
|
{slots}
|
|
101
113
|
</HTable>
|
|
102
114
|
</HCard>
|
|
@@ -60,13 +60,18 @@ describe("HkAuthMethodList", () => {
|
|
|
60
60
|
expect(c.querySelector(".s-auth-methods [role='radiogroup']")).toBeNull();
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
it("wraps every tile in a tooltip carrying the provider label", () => {
|
|
63
|
+
it("wraps every tile in a tooltip carrying the provider label", async () => {
|
|
64
64
|
const c = mount(
|
|
65
65
|
h("div", { class: "s-auth-methods" }, h(HkAuthMethodList, { methods })),
|
|
66
66
|
);
|
|
67
67
|
const wrappers = c.querySelectorAll(".hk-tooltip-wrapper");
|
|
68
68
|
expect(wrappers.length).toBe(2);
|
|
69
|
-
//
|
|
69
|
+
// Popups mount lazily on first engage — a touch tap mounts each one
|
|
70
|
+
// immediately — and then carry the label text.
|
|
71
|
+
for (const w of wrappers) {
|
|
72
|
+
w.dispatchEvent(new PointerEvent("pointerdown", { pointerType: "touch", bubbles: true }));
|
|
73
|
+
}
|
|
74
|
+
await Promise.resolve();
|
|
70
75
|
const popups = [...document.querySelectorAll(".hk-tooltip-popup .hk-tooltip-content")]
|
|
71
76
|
.map((el) => el.textContent);
|
|
72
77
|
expect(popups).toContain("GitHub");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { createApp, h, defineComponent } from "vue";
|
|
2
|
+
import { createApp, h, defineComponent, nextTick } from "vue";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -8,6 +8,7 @@ import HkCopyBadge from "./HkCopyBadge";
|
|
|
8
8
|
import { useToast } from "../runtime/useToast";
|
|
9
9
|
|
|
10
10
|
const mounts: Array<{ app: ReturnType<typeof createApp>; container: HTMLElement }> = [];
|
|
11
|
+
const appUnmounts: Array<{ app: ReturnType<typeof createApp> }> = [];
|
|
11
12
|
|
|
12
13
|
function mount(renderNode: () => ReturnType<typeof h>) {
|
|
13
14
|
const container = document.createElement("div");
|
|
@@ -15,6 +16,7 @@ function mount(renderNode: () => ReturnType<typeof h>) {
|
|
|
15
16
|
const app = createApp({ render: renderNode });
|
|
16
17
|
app.mount(container);
|
|
17
18
|
mounts.push({ app, container });
|
|
19
|
+
appUnmounts.push({ app });
|
|
18
20
|
return container;
|
|
19
21
|
}
|
|
20
22
|
|
|
@@ -126,18 +128,64 @@ describe("HkCopyBadge", () => {
|
|
|
126
128
|
expect(writeText).not.toHaveBeenCalled();
|
|
127
129
|
});
|
|
128
130
|
|
|
129
|
-
|
|
131
|
+
/** Engage the lazy tooltip without hovering: a touch tap mounts the
|
|
132
|
+
* popup immediately (showNow), no fake timers needed. */
|
|
133
|
+
function engageTooltip(c: HTMLElement) {
|
|
134
|
+
c.querySelector(".hk-tooltip-wrapper")!.dispatchEvent(
|
|
135
|
+
new PointerEvent("pointerdown", { pointerType: "touch", bubbles: true }),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
it("renders the custom tooltip text", async () => {
|
|
130
140
|
const c = mount(() => h(HkCopyBadge, { tooltip: "Open details" }, () => "#d.132"));
|
|
131
|
-
|
|
141
|
+
// The popup mounts lazily on first engage (dense-list contract).
|
|
142
|
+
expect(document.querySelector(".hk-tooltip-popup")).toBeNull();
|
|
143
|
+
engageTooltip(c);
|
|
144
|
+
await nextTick();
|
|
145
|
+
const content = document.body.querySelector(".hk-tooltip-popup .hk-tooltip-content");
|
|
132
146
|
expect(content?.textContent).toBe("Open details");
|
|
133
147
|
});
|
|
134
148
|
|
|
135
|
-
it("shows the localized click-to-copy tooltip by default", () => {
|
|
149
|
+
it("shows the localized click-to-copy tooltip by default", async () => {
|
|
136
150
|
const c = mount(() => h(HkCopyBadge, null, () => "#d.132"));
|
|
137
|
-
|
|
151
|
+
engageTooltip(c);
|
|
152
|
+
await nextTick();
|
|
153
|
+
const content = document.body.querySelector(".hk-tooltip-popup .hk-tooltip-content");
|
|
138
154
|
expect(content?.textContent).toBe("Click to copy");
|
|
139
155
|
});
|
|
140
156
|
|
|
157
|
+
it("links the badge to the popup via aria-describedby across the whole lifecycle", async () => {
|
|
158
|
+
const c = mount(() => h(HkCopyBadge, { tooltip: "Open details" }, () => "#d.132"));
|
|
159
|
+
const badge = c.querySelector(".hk-badge")!;
|
|
160
|
+
|
|
161
|
+
// Dormant: no popup, no describedby.
|
|
162
|
+
expect(badge.getAttribute("aria-describedby")).toBeNull();
|
|
163
|
+
expect(document.body.querySelector(".hk-tooltip-popup")).toBeNull();
|
|
164
|
+
|
|
165
|
+
// Engage: describedby == the popup id.
|
|
166
|
+
engageTooltip(c);
|
|
167
|
+
await nextTick();
|
|
168
|
+
const popup = document.body.querySelector(".hk-tooltip-popup")!;
|
|
169
|
+
expect(badge.getAttribute("aria-describedby")).toBe(popup.getAttribute("id"));
|
|
170
|
+
expect(popup.querySelector(".hk-tooltip-content")!.textContent).toBe("Open details");
|
|
171
|
+
|
|
172
|
+
// Leave: the attribute flips BACK (slot re-invocation must not stick).
|
|
173
|
+
c.querySelector(".hk-tooltip-wrapper")!.dispatchEvent(new MouseEvent("mouseleave"));
|
|
174
|
+
await nextTick();
|
|
175
|
+
expect(badge.getAttribute("aria-describedby")).toBeNull();
|
|
176
|
+
|
|
177
|
+
// Re-engage: returns, reusing the same popup element.
|
|
178
|
+
engageTooltip(c);
|
|
179
|
+
await nextTick();
|
|
180
|
+
expect(badge.getAttribute("aria-describedby")).toBe(
|
|
181
|
+
document.body.querySelector(".hk-tooltip-popup")!.getAttribute("id"),
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
// Unmount while visible: the teleported popup goes away.
|
|
185
|
+
appUnmounts.splice(0).forEach(({ app }) => app.unmount());
|
|
186
|
+
expect(document.body.querySelector(".hk-tooltip-popup")).toBeNull();
|
|
187
|
+
});
|
|
188
|
+
|
|
141
189
|
it("renders the inert badge face when disabled", () => {
|
|
142
190
|
const c = mount(() => h(HkCopyBadge, { disabled: true }, () => "#d.132"));
|
|
143
191
|
const badge = c.querySelector(".hk-badge")!;
|
|
@@ -27,9 +27,12 @@ import "./HkCopyBadge.scss";
|
|
|
27
27
|
*
|
|
28
28
|
* A11y: the tag is a real button (role, tab order, Enter/Space), and the
|
|
29
29
|
* click is stopped from reaching clickable ancestors — a tag inside a
|
|
30
|
-
* selectable row copies without selecting the row.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
30
|
+
* selectable row copies without selecting the row. The tooltip popup is
|
|
31
|
+
* linked to the badge via aria-describedby (HkTooltip hands the popup id
|
|
32
|
+
* down as a slot prop, so the describedby lands on the actual focus
|
|
33
|
+
* target). Consumer attrs (class/style/data-*) land on the badge element
|
|
34
|
+
* itself, so a call site skinning the pill keeps working; the tooltip
|
|
35
|
+
* wrapper stays clean.
|
|
33
36
|
*/
|
|
34
37
|
export default defineComponent({
|
|
35
38
|
name: "HkCopyBadge",
|
|
@@ -95,7 +98,7 @@ export default defineComponent({
|
|
|
95
98
|
// itself; only the interactive contract below is ours to add.
|
|
96
99
|
const { class: attrClass, style: attrStyle, ...restAttrs } = attrs;
|
|
97
100
|
|
|
98
|
-
const
|
|
101
|
+
const buildBadge = (describedBy?: string) => (
|
|
99
102
|
<HBadge
|
|
100
103
|
ref={badgeRef}
|
|
101
104
|
{...restAttrs}
|
|
@@ -110,6 +113,7 @@ export default defineComponent({
|
|
|
110
113
|
color={props.color}
|
|
111
114
|
bgColor={props.bgColor}
|
|
112
115
|
borderColor={props.borderColor}
|
|
116
|
+
aria-describedby={describedBy}
|
|
113
117
|
{...(props.disabled
|
|
114
118
|
? {}
|
|
115
119
|
: {
|
|
@@ -133,8 +137,13 @@ export default defineComponent({
|
|
|
133
137
|
</HBadge>
|
|
134
138
|
);
|
|
135
139
|
|
|
136
|
-
if (props.disabled) return
|
|
137
|
-
return
|
|
140
|
+
if (props.disabled) return buildBadge();
|
|
141
|
+
return (
|
|
142
|
+
<HTooltip text={tooltipText.value}>
|
|
143
|
+
{(tip: { popupId: string; visible: boolean }) =>
|
|
144
|
+
buildBadge(tip?.visible ? tip.popupId : undefined)}
|
|
145
|
+
</HTooltip>
|
|
146
|
+
);
|
|
138
147
|
};
|
|
139
148
|
},
|
|
140
149
|
});
|