one
Version:
One is a new React Framework that makes Vite serve both native and web.
516 lines (513 loc) • 16.7 kB
JavaScript
"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