Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@featherds/dock

Package Overview
Dependencies
Maintainers
6
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@featherds/dock - npm Package Compare versions

Comparing version
0.12.41
to
0.12.42
+140
src/components/FeatherDock.spec.ts
import { describe, it, beforeEach, afterEach, expect, vi } from "vitest";
import { mount } from "@vue/test-utils";
import { nextTick } from "vue";
import FeatherDock from "./FeatherDock.vue";
describe("FeatherDock", () => {
let wrapper: any = null;
beforeEach(() => {
wrapper = mount(FeatherDock as unknown as any, {
props: {
id: "test-dock",
modelValue: false,
},
});
});
afterEach(() => {
vi.restoreAllMocks();
vi.clearAllMocks();
if (wrapper) wrapper.unmount();
wrapper = null;
});
it('reflects the open property to the "open" attribute (via aria-expanded/class)', async () => {
if (!wrapper) throw new Error("wrapper not mounted");
// set open -> toggle button aria-expanded should be "true" and root should have dock-open class
await wrapper.setProps({ modelValue: true });
await nextTick();
const toggle = wrapper.find('[data-ref-id="feather-dock-toggle"]');
expect(toggle.exists()).toBe(true);
expect(toggle.attributes("aria-expanded")).toBe("true");
expect(wrapper.classes()).toContain("dock-open");
// unset open -> aria-expanded should be "false" and class removed
await wrapper.setProps({ modelValue: false });
await nextTick();
expect(toggle.attributes("aria-expanded")).toBe("false");
expect(wrapper.classes()).not.toContain("dock-open");
});
it("emits update events with the new state when toggled", async () => {
if (!wrapper) throw new Error("wrapper not mounted");
const toggle = wrapper.find('[data-ref-id="feather-dock-toggle"]');
expect(toggle.exists()).toBe(true);
// ensure starting state
await wrapper.setProps({ modelValue: false });
await nextTick();
// click the toggle button
await toggle.trigger("click");
await nextTick();
// component should emit update:modelValue with true and update:dock-expanded
expect(wrapper.emitted("update:modelValue")?.[0]?.[0]).toBe(true);
expect(wrapper.emitted("update:dock-expanded")).toBeTruthy();
// aria and classes should reflect new state
expect(toggle.attributes("aria-expanded")).toBe("true");
expect(wrapper.classes()).toContain("dock-open");
});
it("updates internal state when external prop changes without emitting update events", async () => {
if (!wrapper) throw new Error("wrapper not mounted");
// Ensure no emits initially
expect(wrapper.emitted("update:modelValue")).toBeUndefined();
// Change the prop externally and expect the component to reflect the change
await wrapper.setProps({ modelValue: true });
await nextTick();
const toggle = wrapper.find('[data-ref-id="feather-dock-toggle"]');
expect(toggle.attributes("aria-expanded")).toBe("true");
// Changing the prop should not cause the component to emit an update back to the parent
expect(wrapper.emitted("update:modelValue")).toBeUndefined();
});
it("computes and exposes expanded/collapsed widths and dockWidth reacts to modelValue", async () => {
// mount with explicit width props
const w = mount(FeatherDock as unknown as any, {
props: {
id: "width-dock",
modelValue: true,
expandedWidth: "420px",
collapsedWidth: "48px",
},
});
// expandedWidthPx / collapsedWidthPx should reflect the px values
expect(w.vm.expandedWidthPx).toBe("420px");
expect(w.vm.collapsedWidthPx).toBe("48px");
// dockWidth should reflect expandedWidth when modelValue=true
expect(w.vm.dockWidth).toBe("420px");
// collapse and assert dockWidth changes
await w.setProps({ modelValue: false });
await nextTick();
expect(w.vm.dockWidth).toBe("48px");
w.unmount();
});
it("closes on Escape key and focuses the toggle button", async () => {
// mount open
const w = mount(FeatherDock as unknown as any, {
attachTo: document.body,
props: { id: "kbd-dock", modelValue: true },
});
// Wait for DOM to be updated and for the component to mount into document
await nextTick();
// The component's handler queries the document by id, so spy the actual DOM element
const docToggleEl = document.querySelector(
`#kbd-dock .feather-dock-toggle`
) as HTMLElement | null;
expect(docToggleEl).toBeTruthy();
const focusSpy = vi.fn();
if (docToggleEl) docToggleEl.focus = focusSpy as unknown as () => void;
// dispatch Escape keydown on document
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
await nextTick();
// component should emit closing events
expect(w.emitted("update:modelValue")?.[0]?.[0]).toBe(false);
expect(w.emitted("update:dock-collapsed")).toBeTruthy();
// focus should have been called on the toggle button
expect(focusSpy).toHaveBeenCalled();
w.unmount();
});
});
+8
-4

@@ -29,4 +29,4 @@ import { defineComponent, useCssVars, computed, ref, watch, provide, inject, readonly, onMounted, onUnmounted, createBlock, openBlock, resolveDynamicComponent, normalizeClass, withCtx, createVNode, createElementVNode, unref, renderSlot, createTextVNode } from "vue";

useCssVars((_ctx) => ({
"12c90f18": dockWidth.value,
"7e7aacd6": dockConfig.value.isOpen ? "cubic-bezier(0, 0.8, 0.4, 1)" : "cubic-beziercubic-bezier(0, 0.8, 0.4, 1)"
"13ea2f12": dockWidth.value,
"f3bd3be2": dockConfig.value.isOpen ? "cubic-bezier(0, 0.8, 0.4, 1)" : "cubic-beziercubic-bezier(0, 0.8, 0.4, 1)"
}));

@@ -36,2 +36,3 @@ const props = __props;

const dockContentRef = ref(void 0);
const toggleHovering = ref(false);
const isDockOpen = ref(props.modelValue);

@@ -165,2 +166,3 @@ const pushedSelectorPadding = ref("");

provide("dockConfig", readonly(dockConfig));
provide("toggleHovering", readonly(toggleHovering));
onMounted(() => {

@@ -218,2 +220,4 @@ if (props.pushedSelector) {

onClick: toggleDock,
onMouseenter: _cache[0] || (_cache[0] = ($event) => toggleHovering.value = true),
onMouseleave: _cache[1] || (_cache[1] = ($event) => toggleHovering.value = false),
"aria-expanded": dockConfig.value.isOpen,

@@ -240,3 +244,3 @@ "aria-label": dockConfig.value.isOpen ? _ctx.labels.collapse : _ctx.labels.expand,

renderSlot(_ctx.$slots, "docked", {}, () => [
_cache[0] || (_cache[0] = createElementVNode("div", { class: "custom-content" }, [
_cache[2] || (_cache[2] = createElementVNode("div", { class: "custom-content" }, [
createElementVNode("h2", null, "Custom Dock Content"),

@@ -268,5 +272,5 @@ createElementVNode("p", null, 'Add your content here using the "docked" slot.'),

};
const FeatherDock = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-c6b62f8b"]]);
const FeatherDock = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-4407d75f"]]);
export {
FeatherDock
};

@@ -31,5 +31,5 @@

}
.feather-dock[data-v-c6b62f8b] {
--feather-dock-width: var(--12c90f18);
--feather-dock-toggle-timing-fn: var(--7e7aacd6);
.feather-dock[data-v-4407d75f] {
--feather-dock-width: var(--13ea2f12);
--feather-dock-toggle-timing-fn: var(--f3bd3be2);
position: fixed;

@@ -52,6 +52,6 @@ inset: var(--feather-dock-header-offset) 0 0 0;

}
.feather-dock.right[data-v-c6b62f8b] {
.feather-dock.right[data-v-4407d75f] {
inset: 0 0 0 auto;
}
.feather-dock.right .feather-dock-toggle[data-v-c6b62f8b] {
.feather-dock.right .feather-dock-toggle[data-v-4407d75f] {
transform: rotate(180deg);

@@ -63,7 +63,7 @@ right: calc(var(--feather-dock-width) - 1rem);

}
.feather-dock.dock-closed[data-v-c6b62f8b] {
.feather-dock.dock-closed[data-v-4407d75f] {
scrollbar-width: none;
scrollbar-color: transparent transparent;
}
.feather-dock.dock-closed > .feather-dock-toggle[data-v-c6b62f8b] {
.feather-dock.dock-closed > .feather-dock-toggle[data-v-4407d75f] {
position: absolute;

@@ -73,6 +73,6 @@ left: calc(var(--feather-dock-width) / 2 - 1.5rem);

}
.feather-dock.dock-closed > .feather-dock-toggle[data-v-c6b62f8b] {
.feather-dock.dock-closed > .feather-dock-toggle[data-v-4407d75f] {
top: var(--feather-dock-toggle-top);
}
.feather-dock > .feather-dock-toggle[data-v-c6b62f8b] {
.feather-dock > .feather-dock-toggle[data-v-4407d75f] {
position: fixed;

@@ -91,29 +91,29 @@ top: calc(var(--feather-dock-toggle-top) + var(--feather-dock-header-offset));

}
.feather-dock > .feather-dock-toggle .ripple[data-v-c6b62f8b] {
.feather-dock > .feather-dock-toggle .ripple[data-v-4407d75f] {
background-color: var(--feather-state-color-on-neutral);
opacity: var(--feather-state-opacity-pressed-on-neutral);
}
.feather-dock > .feather-dock-toggle.selected[data-v-c6b62f8b],
.feather-dock > .feather-dock-toggle .selected[data-v-c6b62f8b] {
.feather-dock > .feather-dock-toggle.selected[data-v-4407d75f],
.feather-dock > .feather-dock-toggle .selected[data-v-4407d75f] {
background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0));
}
.feather-dock > .feather-dock-toggle.hover[data-v-c6b62f8b]:hover, .feather-dock > .feather-dock-toggle:hover .hover[data-v-c6b62f8b] {
.feather-dock > .feather-dock-toggle.hover[data-v-4407d75f]:hover, .feather-dock > .feather-dock-toggle:hover .hover[data-v-4407d75f] {
background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0));
}
.feather-dock > .feather-dock-toggle.hover:hover.selected[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle:hover .hover.selected[data-v-c6b62f8b] {
.feather-dock > .feather-dock-toggle.hover:hover.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover .hover.selected[data-v-4407d75f] {
background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral))), linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0));
}
.feather-dock > .feather-dock-toggle:focus.focus[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle:focus .focus[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle.focused.focus[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle.focused .focus[data-v-c6b62f8b] {
.feather-dock > .feather-dock-toggle:focus.focus[data-v-4407d75f], .feather-dock > .feather-dock-toggle:focus .focus[data-v-4407d75f], .feather-dock > .feather-dock-toggle.focused.focus[data-v-4407d75f], .feather-dock > .feather-dock-toggle.focused .focus[data-v-4407d75f] {
background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0));
}
.feather-dock > .feather-dock-toggle:focus.focus.selected[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle:focus .focus.selected[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle.focused.focus.selected[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle.focused .focus.selected[data-v-c6b62f8b] {
.feather-dock > .feather-dock-toggle:focus.focus.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle:focus .focus.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle.focused.focus.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle.focused .focus.selected[data-v-4407d75f] {
background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral))), linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0));
}
.feather-dock > .feather-dock-toggle:hover:focus .focus.hover[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle:hover:focus.focus.hover[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle:hover.focused .focus.hover[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle:hover.focused.focus.hover[data-v-c6b62f8b] {
.feather-dock > .feather-dock-toggle:hover:focus .focus.hover[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover:focus.focus.hover[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover.focused .focus.hover[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover.focused.focus.hover[data-v-4407d75f] {
background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral))), linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0));
}
.feather-dock > .feather-dock-toggle:hover:focus .focus.hover.selected[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle:hover:focus.focus.hover.selected[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle:hover.focused .focus.hover.selected[data-v-c6b62f8b], .feather-dock > .feather-dock-toggle:hover.focused.focus.hover.selected[data-v-c6b62f8b] {
.feather-dock > .feather-dock-toggle:hover:focus .focus.hover.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover:focus.focus.hover.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover.focused .focus.hover.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover.focused.focus.hover.selected[data-v-4407d75f] {
background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral))), linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral))), linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0));
}
.feather-dock > .feather-dock-content[data-v-c6b62f8b] {
.feather-dock > .feather-dock-content[data-v-4407d75f] {
position: relative;

@@ -129,5 +129,5 @@ height: 100%;

}
.feather-dock > .feather-dock-content .custom-content[data-v-c6b62f8b] {
.feather-dock > .feather-dock-content .custom-content[data-v-4407d75f] {
text-align: center;
padding: 1rem;
}
{
"name": "@featherds/dock",
"version": "0.12.41",
"version": "0.12.42",
"publishConfig": {

@@ -12,4 +12,4 @@ "access": "public"

"dependencies": {
"@featherds/icon": "^0.12.41",
"@featherds/styles": "^0.12.41",
"@featherds/icon": "^0.12.42",
"@featherds/styles": "^0.12.42",
"vue": "^3.5.13"

@@ -22,3 +22,3 @@ },

"types": "./src/index.d.ts",
"gitHead": "339b2892b574bcb01022eb9c7edde9a86895fa4d"
"gitHead": "e807ef35c30f4d9ef4a08ac173dfd50e3f2004d7"
}

@@ -10,2 +10,4 @@ <template>

@click="toggleDock"
@mouseenter="toggleHovering = true"
@mouseleave="toggleHovering = false"
:aria-expanded="dockConfig.isOpen"

@@ -82,2 +84,4 @@ :aria-label="dockConfig.isOpen ? labels.collapse : labels.expand"

const toggleHovering = ref(false);
// use composable's ref as source of truth; its .value is a Ref<boolean>

@@ -284,2 +288,3 @@ const isDockOpen = ref<boolean>(props.modelValue);

provide("dockConfig", readonly(dockConfig)); //readonly
provide("toggleHovering", readonly(toggleHovering));

@@ -286,0 +291,0 @@ onMounted(() => {