@qoretechnologies/qlip 0.1.3-pr.28.g211f2a4 → 0.1.3-pr.29.g497a7aa
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/dist/runtime/setup.d.ts +4 -1
- package/dist/runtime/setup.d.ts.map +1 -1
- package/dist/runtime/setup.js +13 -3
- package/dist/stories/BodyPortal.d.ts +12 -0
- package/dist/stories/BodyPortal.d.ts.map +1 -0
- package/dist/stories/BodyPortal.js +18 -0
- package/dist/stories/BodyPortal.stories.d.ts +17 -0
- package/dist/stories/BodyPortal.stories.d.ts.map +1 -0
- package/dist/stories/BodyPortal.stories.js +33 -0
- package/package.json +2 -1
package/dist/runtime/setup.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
export
|
|
1
|
+
export declare const VIEWPORT_RESET_STYLE_ID = "__qlip-viewport-reset";
|
|
2
|
+
export declare const VIEWPORT_RESET_CSS = "html, body, #storybook-root, body > div:not([class]):not([id]) { width: 100%; height: 100%; margin: 0; padding: 0; }";
|
|
3
|
+
/** Exported for unit testing. */
|
|
4
|
+
export declare const injectViewportStyles: () => void;
|
|
2
5
|
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/runtime/setup.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/runtime/setup.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAS/D,eAAO,MAAM,kBAAkB,yHACyF,CAAC;AAEzH,iCAAiC;AACjC,eAAO,MAAM,oBAAoB,YAOhC,CAAC"}
|
package/dist/runtime/setup.js
CHANGED
|
@@ -3,13 +3,23 @@ import { captureAutoScreenshot, captureErrorScreenshot } from './screenshot.js';
|
|
|
3
3
|
import { flushConsoleLogs, getRuntimeState, initRuntimeState, pushConsoleLog } from './context.js';
|
|
4
4
|
import { realDateNow } from './clock.js';
|
|
5
5
|
const isBrowser = () => typeof globalThis.__vitest_browser__ !== 'undefined';
|
|
6
|
-
const
|
|
6
|
+
export const VIEWPORT_RESET_STYLE_ID = '__qlip-viewport-reset';
|
|
7
|
+
// Storybook's portable stories mount every story into an anonymous <div>
|
|
8
|
+
// (no id, no class) appended to <body>; it needs the full viewport for
|
|
9
|
+
// height-filling stories. A bare `body > div` also matched everything React
|
|
10
|
+
// portals into <body> (reqore's fixed-position floating-actions bar, popovers)
|
|
11
|
+
// and stretched it to 100% x 100%, which captured as opaque black rectangles.
|
|
12
|
+
// `:first-of-type` is no alternative: Storybook's hidden `sb-wrapper` divs
|
|
13
|
+
// precede the canvas.
|
|
14
|
+
export const VIEWPORT_RESET_CSS = 'html, body, #storybook-root, body > div:not([class]):not([id]) { width: 100%; height: 100%; margin: 0; padding: 0; }';
|
|
15
|
+
/** Exported for unit testing. */
|
|
16
|
+
export const injectViewportStyles = () => {
|
|
7
17
|
const doc = globalThis.document;
|
|
8
18
|
if (!doc)
|
|
9
19
|
return;
|
|
10
20
|
const style = doc.createElement('style');
|
|
11
|
-
style.id =
|
|
12
|
-
style.textContent =
|
|
21
|
+
style.id = VIEWPORT_RESET_STYLE_ID;
|
|
22
|
+
style.textContent = VIEWPORT_RESET_CSS;
|
|
13
23
|
doc.head.appendChild(style);
|
|
14
24
|
};
|
|
15
25
|
const serializeArg = (arg) => {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface IBodyPortalProps {
|
|
2
|
+
/** Label of the portalled toolbar; the toolbar sizes itself to it. */
|
|
3
|
+
label?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Renders a fixed-position toolbar straight into <body>, the way UI
|
|
7
|
+
* libraries portal floating actions, popovers and dropdowns. Like those, it
|
|
8
|
+
* carries no explicit width/height and sizes itself to its content, so a
|
|
9
|
+
* capture-time rule that stretches <body> children would balloon it.
|
|
10
|
+
*/
|
|
11
|
+
export declare const BodyPortal: ({ label }: IBodyPortalProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=BodyPortal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BodyPortal.d.ts","sourceRoot":"","sources":["../../src/stories/BodyPortal.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,WAAgC,gBAAgB,4CAuB1E,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
3
|
+
/**
|
|
4
|
+
* Renders a fixed-position toolbar straight into <body>, the way UI
|
|
5
|
+
* libraries portal floating actions, popovers and dropdowns. Like those, it
|
|
6
|
+
* carries no explicit width/height and sizes itself to its content, so a
|
|
7
|
+
* capture-time rule that stretches <body> children would balloon it.
|
|
8
|
+
*/
|
|
9
|
+
export const BodyPortal = ({ label = 'floating actions' }) => (_jsxs("div", { style: { height: '100%', padding: 16, boxSizing: 'border-box' }, children: [_jsx("h1", { children: "A toolbar portalled into body sits at the bottom right" }), createPortal(_jsx("div", { className: "body-portal-toolbar", "data-testid": "body-portal", style: {
|
|
10
|
+
position: 'fixed',
|
|
11
|
+
right: 16,
|
|
12
|
+
bottom: 16,
|
|
13
|
+
padding: '6px 12px',
|
|
14
|
+
background: '#1d4ed8',
|
|
15
|
+
color: '#fff',
|
|
16
|
+
borderRadius: 4,
|
|
17
|
+
whiteSpace: 'nowrap',
|
|
18
|
+
}, children: label }), document.body)] }));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { StoryObj } from '@storybook/react-vite';
|
|
2
|
+
declare const meta: {
|
|
3
|
+
title: string;
|
|
4
|
+
component: ({ label }: import("./BodyPortal.js").IBodyPortalProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
parameters: {
|
|
6
|
+
layout: string;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export default meta;
|
|
10
|
+
type Story = StoryObj<typeof meta>;
|
|
11
|
+
/**
|
|
12
|
+
* The capture-time viewport reset sizes the story canvas to the viewport but
|
|
13
|
+
* must leave elements portalled into <body> alone. Before the fix the
|
|
14
|
+
* content-sized portal was stretched to 100% x 100% of the viewport.
|
|
15
|
+
*/
|
|
16
|
+
export declare const KeepsItsOwnSize: Story;
|
|
17
|
+
//# sourceMappingURL=BodyPortal.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BodyPortal.stories.d.ts","sourceRoot":"","sources":["../../src/stories/BodyPortal.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAK5D,QAAA,MAAM,IAAI;;;;;;CAMyB,CAAC;AAEpC,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAGnC;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,KAkB7B,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { expect, within } from 'storybook/test';
|
|
2
|
+
import { BodyPortal } from './BodyPortal.js';
|
|
3
|
+
const meta = {
|
|
4
|
+
title: 'Example/BodyPortal',
|
|
5
|
+
component: BodyPortal,
|
|
6
|
+
parameters: {
|
|
7
|
+
layout: 'fullscreen',
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
export default meta;
|
|
11
|
+
/**
|
|
12
|
+
* The capture-time viewport reset sizes the story canvas to the viewport but
|
|
13
|
+
* must leave elements portalled into <body> alone. Before the fix the
|
|
14
|
+
* content-sized portal was stretched to 100% x 100% of the viewport.
|
|
15
|
+
*/
|
|
16
|
+
export const KeepsItsOwnSize = {
|
|
17
|
+
args: { label: 'floating actions' },
|
|
18
|
+
play: async ({ canvasElement }) => {
|
|
19
|
+
const canvas = within(canvasElement);
|
|
20
|
+
await expect(canvas.getByText(/portalled into body/i)).toBeInTheDocument();
|
|
21
|
+
const portal = document.querySelector('[data-testid="body-portal"]');
|
|
22
|
+
if (!portal)
|
|
23
|
+
throw new Error('portal not rendered');
|
|
24
|
+
const rect = portal.getBoundingClientRect();
|
|
25
|
+
// Content-sized: a short label, one line. Stretched: the whole viewport.
|
|
26
|
+
await expect(rect.width).toBeLessThan(window.innerWidth / 2);
|
|
27
|
+
await expect(rect.height).toBeLessThan(60);
|
|
28
|
+
await expect(rect.right).toBeLessThanOrEqual(window.innerWidth);
|
|
29
|
+
await expect(rect.bottom).toBeLessThanOrEqual(window.innerHeight);
|
|
30
|
+
// The anonymous story canvas itself still fills the viewport.
|
|
31
|
+
await expect(canvasElement.getBoundingClientRect().height).toBe(window.innerHeight);
|
|
32
|
+
},
|
|
33
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qoretechnologies/qlip",
|
|
3
|
-
"version": "0.1.3-pr.
|
|
3
|
+
"version": "0.1.3-pr.29.g497a7aa",
|
|
4
4
|
"description": "Storybook screenshot capture for the Vitest addon",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"@storybook/addon-vitest": "^10.2.0",
|
|
77
77
|
"@storybook/react-vite": "^10.2.0",
|
|
78
78
|
"@types/node": "^20.10.0",
|
|
79
|
+
"@types/react-dom": "^19.2.7",
|
|
79
80
|
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
|
80
81
|
"@typescript-eslint/parser": "^6.10.0",
|
|
81
82
|
"@vitest/browser-playwright": "^4.0.17",
|