Package entry point and lint fixes

Adds packages/core/src/index.ts as the convenience re-export barrel for
all Dialog parts, Presence, Portal, VisuallyHidden, createFocusTrap,
createScrollLock, and createDismiss. Also resolves pre-existing biome
formatter and noNonNullAssertion violations across five files so CI passes.
This commit is contained in:
Mats Bosson 2026-03-29 06:01:41 +07:00
parent a25244840b
commit 7dd8615757
7 changed files with 53 additions and 17 deletions

View File

@ -0,0 +1,31 @@
// packages/core/src/index.ts
// Main entry — re-exports everything for convenience.
// Prefer sub-path imports (e.g. "pettyui/dialog") for tree-shaking.
export { Dialog } from "./components/dialog/index";
export type { DialogRootProps } from "./components/dialog/dialog-root";
export type { DialogContentProps } from "./components/dialog/dialog-content";
export type { DialogTitleProps } from "./components/dialog/dialog-title";
export type { DialogDescriptionProps } from "./components/dialog/dialog-description";
export type { DialogTriggerProps } from "./components/dialog/dialog-trigger";
export type { DialogCloseProps } from "./components/dialog/dialog-close";
export type { DialogPortalProps } from "./components/dialog/dialog-portal";
export type { DialogOverlayProps } from "./components/dialog/dialog-overlay";
export { Presence } from "./utilities/presence/index";
export type { PresenceProps, PresenceChildProps } from "./utilities/presence/index";
export { Portal } from "./utilities/portal/index";
export type { PortalProps } from "./utilities/portal/index";
export { VisuallyHidden } from "./utilities/visually-hidden/index";
export type { VisuallyHiddenProps } from "./utilities/visually-hidden/index";
export { createFocusTrap } from "./utilities/focus-trap/index";
export type { FocusTrap } from "./utilities/focus-trap/index";
export { createScrollLock } from "./utilities/scroll-lock/index";
export type { ScrollLock } from "./utilities/scroll-lock/index";
export { createDismiss } from "./utilities/dismiss/index";
export type { CreateDismissOptions, Dismiss } from "./utilities/dismiss/index";

View File

@ -19,9 +19,7 @@ export interface DisclosureState {
* Tooltip, Collapsible, etc.). Wraps createControllableSignal with
* convenience open/close/toggle methods.
*/
export function createDisclosureState(
options: CreateDisclosureStateOptions,
): DisclosureState {
export function createDisclosureState(options: CreateDisclosureStateOptions): DisclosureState {
const [isOpen, setIsOpen] = createControllableSignal<boolean>({
value: () => options.open,
defaultValue: () => options.defaultOpen ?? false,

View File

@ -36,8 +36,8 @@ export function createFocusTrap(getContainer: Accessor<HTMLElement | null>): Foc
return;
}
const first = focusable[0]!;
const last = focusable[focusable.length - 1]!;
const first = focusable[0] as HTMLElement;
const last = focusable[focusable.length - 1] as HTMLElement;
if (e.shiftKey) {
if (document.activeElement === first) {

View File

@ -17,9 +17,5 @@ export function Portal(props: PortalProps): JSX.Element {
return <>{props.children}</>;
}
return (
<SolidPortal mount={props.target ?? document.body}>
{props.children}
</SolidPortal>
);
return <SolidPortal mount={props.target ?? document.body}>{props.children}</SolidPortal>;
}

View File

@ -75,12 +75,12 @@ export function Presence(props: PresenceProps): JSX.Element {
// Wrap in a function so Show evaluates children lazily (only when mounted).
return (
<Show when={shouldMount()}>
{(
() =>
{
(() =>
typeof props.children === "function"
? (props.children as (p: PresenceChildProps) => JSX.Element)(childProps)
: props.children
) as unknown as JSX.Element}
: props.children) as unknown as JSX.Element
}
</Show>
);
}

View File

@ -96,7 +96,12 @@ describe("createFocusTrap", () => {
const trap = createFocusTrap(() => container);
trap.activate();
// button1 is already focused after activate
const event = new KeyboardEvent("keydown", { key: "Tab", shiftKey: true, bubbles: true, cancelable: true });
const event = new KeyboardEvent("keydown", {
key: "Tab",
shiftKey: true,
bubbles: true,
cancelable: true,
});
document.dispatchEvent(event);
expect(document.activeElement).toBe(button2);
trap.deactivate();
@ -138,7 +143,9 @@ describe("createFocusTrap", () => {
// Tab from the only element — Tab wraps back to itself (first === last)
// What matters is it doesn't fire twice causing erratic behaviour
expect(() => {
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Tab", bubbles: true, cancelable: true }));
document.dispatchEvent(
new KeyboardEvent("keydown", { key: "Tab", bubbles: true, cancelable: true }),
);
}).not.toThrow();
trap.deactivate();
dispose();

View File

@ -4,7 +4,11 @@ import { Portal } from "../../src/utilities/portal/portal";
describe("Portal", () => {
it("renders children into document.body by default", () => {
render(() => <Portal><div data-testid="portal-content">hello</div></Portal>);
render(() => (
<Portal>
<div data-testid="portal-content">hello</div>
</Portal>
));
// Content should be in document.body, not the render container
expect(document.body.querySelector("[data-testid='portal-content']")).toBeTruthy();
});