VisuallyHidden and Portal utilities
This commit is contained in:
parent
b291ceab50
commit
697e80ef72
2
packages/core/src/utilities/portal/index.ts
Normal file
2
packages/core/src/utilities/portal/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { Portal } from "./portal";
|
||||||
|
export type { PortalProps } from "./portal";
|
||||||
25
packages/core/src/utilities/portal/portal.tsx
Normal file
25
packages/core/src/utilities/portal/portal.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import type { JSX } from "solid-js";
|
||||||
|
import { isServer } from "solid-js/web";
|
||||||
|
import { Portal as SolidPortal } from "solid-js/web";
|
||||||
|
|
||||||
|
export interface PortalProps {
|
||||||
|
/** Target container. Defaults to document.body. */
|
||||||
|
target?: Element | null;
|
||||||
|
children: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSR-safe portal. During SSR, renders content inline.
|
||||||
|
* On the client, moves content to the target container.
|
||||||
|
*/
|
||||||
|
export function Portal(props: PortalProps): JSX.Element {
|
||||||
|
if (isServer) {
|
||||||
|
return <>{props.children}</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SolidPortal mount={props.target ?? document.body}>
|
||||||
|
{props.children}
|
||||||
|
</SolidPortal>
|
||||||
|
);
|
||||||
|
}
|
||||||
2
packages/core/src/utilities/visually-hidden/index.ts
Normal file
2
packages/core/src/utilities/visually-hidden/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { VisuallyHidden } from "./visually-hidden";
|
||||||
|
export type { VisuallyHiddenProps } from "./visually-hidden";
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
import type { JSX } from "solid-js";
|
||||||
|
import { splitProps } from "solid-js";
|
||||||
|
|
||||||
|
export interface VisuallyHiddenProps extends JSX.HTMLAttributes<HTMLSpanElement> {
|
||||||
|
children?: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
const visuallyHiddenStyle: JSX.CSSProperties = {
|
||||||
|
position: "absolute",
|
||||||
|
border: "0",
|
||||||
|
width: "1px",
|
||||||
|
height: "1px",
|
||||||
|
padding: "0",
|
||||||
|
margin: "-1px",
|
||||||
|
overflow: "hidden",
|
||||||
|
clip: "rect(0, 0, 0, 0)",
|
||||||
|
"white-space": "nowrap",
|
||||||
|
"word-wrap": "normal",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders content visually hidden but accessible to screen readers.
|
||||||
|
*/
|
||||||
|
export function VisuallyHidden(props: VisuallyHiddenProps): JSX.Element {
|
||||||
|
const [local, rest] = splitProps(props, ["children", "style"]);
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
style={{ ...visuallyHiddenStyle, ...(local.style as JSX.CSSProperties | undefined) }}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{local.children}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
packages/core/tests/utilities/portal.test.tsx
Normal file
23
packages/core/tests/utilities/portal.test.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { render } from "@solidjs/testing-library";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
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>);
|
||||||
|
// Content should be in document.body, not the render container
|
||||||
|
expect(document.body.querySelector("[data-testid='portal-content']")).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders children into a custom target", () => {
|
||||||
|
const target = document.createElement("div");
|
||||||
|
document.body.appendChild(target);
|
||||||
|
render(() => (
|
||||||
|
<Portal target={target}>
|
||||||
|
<div data-testid="custom-portal">hello</div>
|
||||||
|
</Portal>
|
||||||
|
));
|
||||||
|
expect(target.querySelector("[data-testid='custom-portal']")).toBeTruthy();
|
||||||
|
document.body.removeChild(target);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user