UNPKG

one

Version:

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

516 lines (513 loc) 16.7 kB
"use strict"; var import_vitest = require("vitest"); var import_react = require("react"); var import_server = require("react-dom/server"); var import_WebStackView = require("../WebStackView.native.js"); var import_ScreenRenderContext = require("../ScreenRenderContext.native.js"); function _type_of(obj) { "@swc/helpers - typeof"; return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj; } 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) { var out = {}; var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0; try { var _loop = function () { var name = _step.value; out[`${name}-key`] = { options: perRoute[name].options, render: function () { var _perRoute_name_content; return (_perRoute_name_content = perRoute[name].content) !== null && _perRoute_name_content !== void 0 ? _perRoute_name_content : null; }, navigation: {} }; }; for (var _iterator = Object.keys(perRoute)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) _loop(); } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return out; } (0, import_vitest.describe)("resolveOverlayRender", function () { var A = function () { return null; }; var B = function () { return null; }; (0, import_vitest.it)("prefers per-route render over context", function () { var 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", function () { var 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", function () { (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", function () { (0, import_vitest.expect)((0, import_WebStackView.resolveOverlayRender)({ presentation: "formSheet", render: { ios: A } }, { web: B })).toBe(B); }); }); (0, import_vitest.describe)("OverlayHost", function () { (0, import_vitest.it)("invokes the render component with route props for overlay presentations", function () { var captured = []; var Render = import_vitest.vi.fn(function (props) { captured.push(props); return /* @__PURE__ */(0, import_react.createElement)("div", { "data-modal": props.routeKey }, props.children); }); var descriptors = makeDescriptors({ filter: { options: { presentation: "formSheet", sheetAllowedDetents: [0.5, 1], sheetGrabberVisible: true }, content: /* @__PURE__ */(0, import_react.createElement)("span", null, "filter-body") } }); var markup = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */(0, import_react.createElement)(import_WebStackView.OverlayHost, { route: makeRoute("filter"), descriptor: descriptors["filter-key"], contextRender: { web: Render }, onDismiss: function () {} })); (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)(_type_of(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", function () { var descriptors = makeDescriptors({ filter: { options: { presentation: "formSheet" }, content: /* @__PURE__ */(0, import_react.createElement)("span", null, "inline-content") } }); var markup = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */(0, import_react.createElement)(import_WebStackView.OverlayHost, { route: makeRoute("filter"), descriptor: descriptors["filter-key"], contextRender: void 0, onDismiss: function () {} })); (0, import_vitest.expect)(markup).toContain("inline-content"); }); (0, import_vitest.it)("skips render when presentation is not an overlay", function () { var Render = import_vitest.vi.fn(function () { return null; }); var descriptors = makeDescriptors({ home: { options: { presentation: "card" }, content: /* @__PURE__ */(0, import_react.createElement)("span", null, "card-content") } }); var markup = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */(0, import_react.createElement)(import_WebStackView.OverlayHost, { route: makeRoute("home"), descriptor: descriptors["home-key"], contextRender: { web: Render }, onDismiss: function () {} })); (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", function () { var ContextRender = import_vitest.vi.fn(function () { return null; }); var PerRoute = import_vitest.vi.fn(function () { return /* @__PURE__ */(0, import_react.createElement)("em", null, "per-route"); }); var descriptors = makeDescriptors({ sheet: { options: { presentation: "formSheet", render: { web: PerRoute } }, content: /* @__PURE__ */(0, import_react.createElement)("span", null, "body") } }); var markup = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */(0, import_react.createElement)(import_WebStackView.OverlayHost, { route: makeRoute("sheet"), descriptor: descriptors["sheet-key"], contextRender: { web: ContextRender }, onDismiss: function () {} })); (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", function () { var onDismiss = import_vitest.vi.fn(); var capturedDismiss; var Render = function (props) { capturedDismiss = props.dismiss; return null; }; var descriptors = makeDescriptors({ sheet: { options: { presentation: "formSheet" } } }); (0, import_server.renderToStaticMarkup)(/* @__PURE__ */(0, import_react.createElement)(import_WebStackView.OverlayHost, { route: makeRoute("sheet"), descriptor: descriptors["sheet-key"], contextRender: { web: Render }, onDismiss })); (0, import_vitest.expect)(typeof capturedDismiss === "undefined" ? "undefined" : _type_of(capturedDismiss)).toBe("function"); capturedDismiss(); (0, import_vitest.expect)(onDismiss).toHaveBeenCalledTimes(1); }); (0, import_vitest.it)("respects gestureEnabled: false as dismissible: false", function () { var captured; var Render = function (props) { captured = props; return null; }; var descriptors = makeDescriptors({ sheet: { options: { presentation: "formSheet", gestureEnabled: false } } }); (0, import_server.renderToStaticMarkup)(/* @__PURE__ */(0, import_react.createElement)(import_WebStackView.OverlayHost, { route: makeRoute("sheet"), descriptor: descriptors["sheet-key"], contextRender: { web: Render }, onDismiss: function () {} })); (0, import_vitest.expect)(captured.dismissible).toBe(false); }); }); (0, import_vitest.describe)("WebStackView overlay dispatch", function () { (0, import_vitest.it)("renders each overlay route via the configured render", function () { var calls = []; var Render = function (props) { calls.push(props.routeKey); return /* @__PURE__ */(0, import_react.createElement)("div", { "data-route": props.routeKey }); }; var state = makeState(["home", "filter", "help"], 2); var descriptors = makeDescriptors({ home: { options: { presentation: "card" } }, filter: { options: { presentation: "formSheet" } }, help: { options: { presentation: "modal" } } }); var navigation = { dispatch: import_vitest.vi.fn() }; var markup = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */(0, import_react.createElement)(import_ScreenRenderContext.StackRenderProvider, { value: { web: Render } }, /* @__PURE__ */(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", function () { var state = makeState(["home", "filter"], 1); var descriptors = makeDescriptors({ home: { options: { presentation: "card" } }, filter: { options: { presentation: "formSheet" }, content: /* @__PURE__ */(0, import_react.createElement)("span", null, "should-not-render-as-overlay") } }); var navigation = { dispatch: import_vitest.vi.fn() }; var markup = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */(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", function () { var captured; var Render = function (props) { captured = props; return null; }; var state = makeState(["home", "sheet"], 1); var descriptors = makeDescriptors({ home: { options: { presentation: "card" } }, sheet: { options: { presentation: "formSheet" } } }); var navigation = { dispatch: import_vitest.vi.fn() }; (0, import_server.renderToStaticMarkup)(/* @__PURE__ */(0, import_react.createElement)(import_ScreenRenderContext.StackRenderProvider, { value: { web: Render } }, /* @__PURE__ */(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", function () { var calls = []; var mountTrackerCalls = 0; var Render = function (props) { calls.push({ name: props.routeName, open: props.open }); return /* @__PURE__ */(0, import_react.createElement)("div", { "data-route": props.routeName, "data-open": String(props.open) }, props.children); }; var MountTracker = function () { mountTrackerCalls++; return /* @__PURE__ */(0, import_react.createElement)("span", null, `mounted-${mountTrackerCalls}`); }; var initialState = makeState(["home", "settings"], 1); var settingsDescriptor = { options: { presentation: "formSheet", keepMounted: true }, render: function () { return /* @__PURE__ */(0, import_react.createElement)(MountTracker); }, navigation: {} }; var descriptors = { "home-key": { options: { presentation: "card" }, render: function () { return null; }, navigation: {} }, "settings-key": settingsDescriptor }; var navigation = { dispatch: import_vitest.vi.fn() }; var tree = /* @__PURE__ */(0, import_react.createElement)(import_ScreenRenderContext.StackRenderProvider, { value: { web: Render } }, /* @__PURE__ */(0, import_react.createElement)(import_WebStackView.WebStackView, { state: initialState, navigation, descriptors })); var out1 = (0, import_server.renderToStaticMarkup)(tree); (0, import_vitest.expect)(calls.some(function (c) { return 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", function () { var PerRoute = import_vitest.vi.fn(function () { return /* @__PURE__ */(0, import_react.createElement)("div", { "data-route": "filter" }); }); var state = makeState(["home", "help", "filter"], 2); var descriptors = makeDescriptors({ home: { options: { presentation: "card" } }, help: { options: { presentation: "modal" } }, // overlay-presented, no render filter: { options: { presentation: "formSheet", render: { web: PerRoute } } } }); var navigation = { dispatch: import_vitest.vi.fn() }; (0, import_server.renderToStaticMarkup)(/* @__PURE__ */(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", function () { var captured; var Render = function (props) { captured = props; return null; }; var state = makeState(["home", "filter"], 1); var descriptors = makeDescriptors({ home: { options: { presentation: "card" } }, filter: { options: { presentation: "formSheet" } } }); var dispatch = import_vitest.vi.fn(); var navigation = { dispatch, isFocused: function () { return true; } }; (0, import_server.renderToStaticMarkup)(/* @__PURE__ */(0, import_react.createElement)(import_ScreenRenderContext.StackRenderProvider, { value: { web: Render } }, /* @__PURE__ */(0, import_react.createElement)(import_WebStackView.WebStackView, { state, navigation, descriptors }))); captured.dismiss(); (0, import_vitest.expect)(dispatch).toHaveBeenCalledTimes(1); var 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"); }); }); //# sourceMappingURL=WebStackView.test.native.js.map