UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

464 lines 14.8 kB
var import_vitest = require("vitest"); var import_react = require("react"); var import_server = require("react-dom/server"); var import_WebStackView = require("../WebStackView.cjs"); var import_ScreenRenderContext = require("../ScreenRenderContext.cjs"); function makeRoute(name) { return { key: `${name}-key`, name, params: void 0 }; } function makeState(names, index) { return { key: "stack-1", index, routeNames: names, routes: names.map(makeRoute), type: "stack", stale: false, preloadedRoutes: [] }; } function makeDescriptors(perRoute) { const out = {}; for (const name of Object.keys(perRoute)) { out[`${name}-key`] = { options: perRoute[name].options, render: () => perRoute[name].content ?? null, navigation: {} }; } return out; } (0, import_vitest.describe)("resolveOverlayRender", () => { const A = () => null; const B = () => null; (0, import_vitest.it)("prefers per-route render over context", () => { const result = (0, import_WebStackView.resolveOverlayRender)({ presentation: "formSheet", render: { web: A } }, { web: B }); (0, import_vitest.expect)(result).toBe(A); }); (0, import_vitest.it)("falls back to context render when route has none", () => { const result = (0, import_WebStackView.resolveOverlayRender)({ presentation: "formSheet" }, { web: B }); (0, import_vitest.expect)(result).toBe(B); }); (0, import_vitest.it)("returns undefined when nothing is configured", () => { (0, import_vitest.expect)((0, import_WebStackView.resolveOverlayRender)({ presentation: "formSheet" }, void 0)).toBeUndefined(); (0, import_vitest.expect)((0, import_WebStackView.resolveOverlayRender)({ presentation: "formSheet" }, { ios: A })).toBeUndefined(); }); (0, import_vitest.it)("does not pick from ios/android slots on web", () => { (0, import_vitest.expect)((0, import_WebStackView.resolveOverlayRender)({ presentation: "formSheet", render: { ios: A } }, { web: B })).toBe(B); }); }); (0, import_vitest.describe)("OverlayHost", () => { (0, import_vitest.it)("invokes the render component with route props for overlay presentations", () => { const captured = []; const Render = import_vitest.vi.fn(props => { captured.push(props); return (0, import_react.createElement)("div", { "data-modal": props.routeKey }, props.children); }); const descriptors = makeDescriptors({ filter: { options: { presentation: "formSheet", sheetAllowedDetents: [0.5, 1], sheetGrabberVisible: true }, content: (0, import_react.createElement)("span", null, "filter-body") } }); const markup = (0, import_server.renderToStaticMarkup)((0, import_react.createElement)(import_WebStackView.OverlayHost, { route: makeRoute("filter"), descriptor: descriptors["filter-key"], contextRender: { web: Render }, onDismiss: () => {} })); (0, import_vitest.expect)(Render).toHaveBeenCalledTimes(1); (0, import_vitest.expect)(captured[0]).toMatchObject({ routeKey: "filter-key", presentation: "formSheet", sheetAllowedDetents: [0.5, 1], sheetGrabberVisible: true, dismissible: true }); (0, import_vitest.expect)(typeof captured[0].dismiss).toBe("function"); (0, import_vitest.expect)(markup).toContain('data-modal="filter-key"'); (0, import_vitest.expect)(markup).toContain("filter-body"); }); (0, import_vitest.it)("falls back to inline rendering when no render is configured", () => { const descriptors = makeDescriptors({ filter: { options: { presentation: "formSheet" }, content: (0, import_react.createElement)("span", null, "inline-content") } }); const markup = (0, import_server.renderToStaticMarkup)((0, import_react.createElement)(import_WebStackView.OverlayHost, { route: makeRoute("filter"), descriptor: descriptors["filter-key"], contextRender: void 0, onDismiss: () => {} })); (0, import_vitest.expect)(markup).toContain("inline-content"); }); (0, import_vitest.it)("skips render when presentation is not an overlay", () => { const Render = import_vitest.vi.fn(() => null); const descriptors = makeDescriptors({ home: { options: { presentation: "card" }, content: (0, import_react.createElement)("span", null, "card-content") } }); const markup = (0, import_server.renderToStaticMarkup)((0, import_react.createElement)(import_WebStackView.OverlayHost, { route: makeRoute("home"), descriptor: descriptors["home-key"], contextRender: { web: Render }, onDismiss: () => {} })); (0, import_vitest.expect)(Render).not.toHaveBeenCalled(); (0, import_vitest.expect)(markup).toContain("card-content"); }); (0, import_vitest.it)("per-route render overrides context render", () => { const ContextRender = import_vitest.vi.fn(() => null); const PerRoute = import_vitest.vi.fn(() => (0, import_react.createElement)("em", null, "per-route")); const descriptors = makeDescriptors({ sheet: { options: { presentation: "formSheet", render: { web: PerRoute } }, content: (0, import_react.createElement)("span", null, "body") } }); const markup = (0, import_server.renderToStaticMarkup)((0, import_react.createElement)(import_WebStackView.OverlayHost, { route: makeRoute("sheet"), descriptor: descriptors["sheet-key"], contextRender: { web: ContextRender }, onDismiss: () => {} })); (0, import_vitest.expect)(PerRoute).toHaveBeenCalledTimes(1); (0, import_vitest.expect)(ContextRender).not.toHaveBeenCalled(); (0, import_vitest.expect)(markup).toContain("per-route"); }); (0, import_vitest.it)("dismiss callback wraps the supplied onDismiss", () => { const onDismiss = import_vitest.vi.fn(); let capturedDismiss; const Render = props => { capturedDismiss = props.dismiss; return null; }; const descriptors = makeDescriptors({ sheet: { options: { presentation: "formSheet" } } }); (0, import_server.renderToStaticMarkup)((0, import_react.createElement)(import_WebStackView.OverlayHost, { route: makeRoute("sheet"), descriptor: descriptors["sheet-key"], contextRender: { web: Render }, onDismiss })); (0, import_vitest.expect)(typeof capturedDismiss).toBe("function"); capturedDismiss(); (0, import_vitest.expect)(onDismiss).toHaveBeenCalledTimes(1); }); (0, import_vitest.it)("respects gestureEnabled: false as dismissible: false", () => { let captured; const Render = props => { captured = props; return null; }; const descriptors = makeDescriptors({ sheet: { options: { presentation: "formSheet", gestureEnabled: false } } }); (0, import_server.renderToStaticMarkup)((0, import_react.createElement)(import_WebStackView.OverlayHost, { route: makeRoute("sheet"), descriptor: descriptors["sheet-key"], contextRender: { web: Render }, onDismiss: () => {} })); (0, import_vitest.expect)(captured.dismissible).toBe(false); }); }); (0, import_vitest.describe)("WebStackView overlay dispatch", () => { (0, import_vitest.it)("renders each overlay route via the configured render", () => { const calls = []; const Render = props => { calls.push(props.routeKey); return (0, import_react.createElement)("div", { "data-route": props.routeKey }); }; const state = makeState(["home", "filter", "help"], 2); const descriptors = makeDescriptors({ home: { options: { presentation: "card" } }, filter: { options: { presentation: "formSheet" } }, help: { options: { presentation: "modal" } } }); const navigation = { dispatch: import_vitest.vi.fn() }; const markup = (0, import_server.renderToStaticMarkup)((0, import_react.createElement)(import_ScreenRenderContext.StackRenderProvider, { value: { web: Render } }, (0, import_react.createElement)(import_WebStackView.WebStackView, { state, navigation, descriptors }))); (0, import_vitest.expect)(calls).toEqual(["filter-key", "help-key"]); (0, import_vitest.expect)(markup).toContain('data-route="filter-key"'); (0, import_vitest.expect)(markup).toContain('data-route="help-key"'); }); (0, import_vitest.it)("leaves overlay routes in the underlying NativeStackView when no render is configured", () => { const state = makeState(["home", "filter"], 1); const descriptors = makeDescriptors({ home: { options: { presentation: "card" } }, filter: { options: { presentation: "formSheet" }, content: (0, import_react.createElement)("span", null, "should-not-render-as-overlay") } }); const navigation = { dispatch: import_vitest.vi.fn() }; const markup = (0, import_server.renderToStaticMarkup)((0, import_react.createElement)(import_WebStackView.WebStackView, { state, navigation, descriptors })); (0, import_vitest.expect)(markup).not.toContain("should-not-render-as-overlay"); }); (0, import_vitest.it)("passes open: true to the regular overlay render", () => { let captured; const Render = props => { captured = props; return null; }; const state = makeState(["home", "sheet"], 1); const descriptors = makeDescriptors({ home: { options: { presentation: "card" } }, sheet: { options: { presentation: "formSheet" } } }); const navigation = { dispatch: import_vitest.vi.fn() }; (0, import_server.renderToStaticMarkup)((0, import_react.createElement)(import_ScreenRenderContext.StackRenderProvider, { value: { web: Render } }, (0, import_react.createElement)(import_WebStackView.WebStackView, { state, navigation, descriptors }))); (0, import_vitest.expect)(captured.open).toBe(true); (0, import_vitest.expect)(captured.routeName).toBe("sheet"); }); (0, import_vitest.it)("keepMounted: keeps rendering the route via the persistent slot after the route is popped", () => { const calls = []; let mountTrackerCalls = 0; const Render = props => { calls.push({ name: props.routeName, open: props.open }); return (0, import_react.createElement)("div", { "data-route": props.routeName, "data-open": String(props.open) }, props.children); }; const MountTracker = () => { mountTrackerCalls++; return (0, import_react.createElement)("span", null, `mounted-${mountTrackerCalls}`); }; const initialState = makeState(["home", "settings"], 1); const settingsDescriptor = { options: { presentation: "formSheet", keepMounted: true }, render: () => (0, import_react.createElement)(MountTracker), navigation: {} }; const descriptors = { "home-key": { options: { presentation: "card" }, render: () => null, navigation: {} }, "settings-key": settingsDescriptor }; const navigation = { dispatch: import_vitest.vi.fn() }; const tree = (0, import_react.createElement)(import_ScreenRenderContext.StackRenderProvider, { value: { web: Render } }, (0, import_react.createElement)(import_WebStackView.WebStackView, { state: initialState, navigation, descriptors })); const out1 = (0, import_server.renderToStaticMarkup)(tree); (0, import_vitest.expect)(calls.some(c => c.name === "settings" && c.open === true)).toBe(true); (0, import_vitest.expect)(out1).toContain('data-route="settings"'); (0, import_vitest.expect)(out1).toContain('data-open="true"'); (0, import_vitest.expect)(calls).toHaveLength(1); }); (0, import_vitest.it)("peels off only overlay routes with a render configured, leaving render-less overlays in the underlying view", () => { const PerRoute = import_vitest.vi.fn(() => (0, import_react.createElement)("div", { "data-route": "filter" })); const state = makeState(["home", "help", "filter"], 2); const descriptors = makeDescriptors({ home: { options: { presentation: "card" } }, help: { options: { presentation: "modal" } }, // overlay-presented, no render filter: { options: { presentation: "formSheet", render: { web: PerRoute } } } }); const navigation = { dispatch: import_vitest.vi.fn() }; (0, import_server.renderToStaticMarkup)((0, import_react.createElement)(import_WebStackView.WebStackView, { state, navigation, descriptors })); (0, import_vitest.expect)(PerRoute).toHaveBeenCalledTimes(1); }); (0, import_vitest.it)("dispatches a pop action when an overlay calls dismiss", () => { let captured; const Render = props => { captured = props; return null; }; const state = makeState(["home", "filter"], 1); const descriptors = makeDescriptors({ home: { options: { presentation: "card" } }, filter: { options: { presentation: "formSheet" } } }); const dispatch = import_vitest.vi.fn(); const navigation = { dispatch, isFocused: () => true }; (0, import_server.renderToStaticMarkup)((0, import_react.createElement)(import_ScreenRenderContext.StackRenderProvider, { value: { web: Render } }, (0, import_react.createElement)(import_WebStackView.WebStackView, { state, navigation, descriptors }))); captured.dismiss(); (0, import_vitest.expect)(dispatch).toHaveBeenCalledTimes(1); const action = dispatch.mock.calls[0][0]; (0, import_vitest.expect)(action.type).toBe("POP"); (0, import_vitest.expect)(action.source).toBe("filter-key"); (0, import_vitest.expect)(action.target).toBe("stack-1"); }); });