Skip to content

Commit d36f655

Browse files
committed
💥 !Define searchParamIntegration! 💥
1 parent 40aedb2 commit d36f655

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

src/integration.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,68 @@ export function pathIntegration() {
160160
);
161161
}
162162

163+
/**
164+
* If your Solidjs "app" is really just a "widget" or "micro-frontend" that will be embedded into a larger app,
165+
* you can use this router integration to embed your entire url into a *single* search parameter.
166+
*
167+
* This is done by taking your "app"s normal url, and passing it through `encodeURIComponent` and `decodeURIComponent`.
168+
*
169+
* If your widget has search params, they will not leak into the parent/host app, and your solid app should not pickup parents search params, unless your Solid code is reading from `window.location`.
170+
*/
171+
// This implementation was based on pathIntegration, with many ideas and concepts taken from hashIntegration.
172+
export function searchParamIntegration(
173+
/**
174+
* Let's say you are building a chat widget that will be integrated into a larger app.
175+
*
176+
* You want to pass in a globally unique search param key here, perhaps "supportChatWidgetUrl"
177+
*/
178+
widgetUrlsSearchParamName: string
179+
) {
180+
function getWidgetsUrl(searchParams: URLSearchParams): string {
181+
return decodeURIComponent(
182+
searchParams.get(widgetUrlsSearchParamName) || encodeURIComponent("/")
183+
);
184+
}
185+
186+
const createLocationChangeNotifier: CreateLocationChangeNotifier = notify => {
187+
return bindEvent(window, "popstate", () => notify());
188+
};
189+
190+
return createIntegration(
191+
() => ({
192+
value: getWidgetsUrl(new URLSearchParams(window.location.search)),
193+
state: history.state
194+
}),
195+
({ value, replace, state }) => {
196+
if (replace) {
197+
window.history.replaceState(state, "", value);
198+
} else {
199+
window.history.pushState(state, "", value);
200+
}
201+
// Because the above `pushState/replaceState` call should never change `window.location.hash`,
202+
// this behavior from `pathIntegration` doesn't make sense in the context of `searchParamIntegration`:
203+
// scrollToHash(window.location.hash.slice(1), scroll);
204+
// Perhaps when a widget loads, it should run the `el.scrollIntoView` function itself.
205+
},
206+
/* init */
207+
createLocationChangeNotifier,
208+
{
209+
go: delta => window.history.go(delta),
210+
renderPath: path => {
211+
const params = new URLSearchParams(window.location.search);
212+
params.set(widgetUrlsSearchParamName, encodeURIComponent(path));
213+
// Starts with `?` because we don't want to change the path at all
214+
// This degrades gracefully if javascript is disabled
215+
return `?${params.toString()}`;
216+
},
217+
parsePath: str => {
218+
const url = new URL(str, "https://github.com/");
219+
return getWidgetsUrl(url.searchParams);
220+
}
221+
}
222+
);
223+
}
224+
163225
export function hashIntegration() {
164226
return createIntegration(
165227
() => window.location.hash.slice(1),

test/integration.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
import { hashIntegration } from "../src/integration";
2+
import { searchParamIntegration } from "../src/integration";
3+
4+
describe("query integration should", () => {
5+
const widgetUrl = `/add-account?type=dex#/ugh`;
6+
const encodedPath = encodeURIComponent(widgetUrl);
7+
test.each([
8+
["http://localhost/", "/"],
9+
["http://localhost//#/practice", "/"],
10+
["http://localhost/base/#/practice", "/"],
11+
["http://localhost/#/practice#some-id", "/"],
12+
["file:///C:/Users/Foo/index.html#/test", "/"],
13+
[`http://localhost/?carniatomon=${encodedPath}`, widgetUrl],
14+
[`http://localhost/?carniatomon=${encodedPath}#/practice`, widgetUrl],
15+
[`http://localhost/base/?carniatomon=${encodedPath}#/practice`, widgetUrl],
16+
[`http://localhost/?carniatomon=${encodedPath}#/practice#some-id`, widgetUrl],
17+
[`file:///C:/Users/Foo/index.html?carniatomon=${encodedPath}#/test`, widgetUrl]
18+
])(`parse paths (case '%s' as '%s')`, (urlString, expected) => {
19+
const parsed = searchParamIntegration(
20+
'carniatomon'
21+
).utils!.parsePath!(urlString);
22+
expect(parsed).toBe(expected);
23+
});
24+
});
25+
226

327
describe("Hash integration should", () => {
428
test.each([

0 commit comments

Comments
 (0)