Skip to content

Commit 75b2204

Browse files
committed
💥 !Define searchParamIntegration! 💥
1 parent e564606 commit 75b2204

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

src/integration.ts

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

163+
/**
164+
* If your SolidStart "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+
187+
const createLocationChangeNotifier: CreateLocationChangeNotifier = notify => {
188+
return bindEvent(window, "popstate", () => notify());
189+
};
190+
191+
return createIntegration(
192+
() => ({
193+
value: getWidgetsUrl(new URLSearchParams(window.location.search)),
194+
state: history.state
195+
}),
196+
({ value, replace, state }) => {
197+
if (replace) {
198+
window.history.replaceState(state, "", value);
199+
} else {
200+
window.history.pushState(state, "", value);
201+
}
202+
// Because the above `pushState/replaceState` call should never change `window.location.hash`,
203+
// this behavior from `pathIntegration` doesn't make sense in the context of `searchParamIntegration`:
204+
// scrollToHash(window.location.hash.slice(1), scroll);
205+
// Perhaps when a widget loads, it should run the `el.scrollIntoView` function itself.
206+
},
207+
/* init */
208+
createLocationChangeNotifier,
209+
{
210+
go: delta => window.history.go(delta),
211+
renderPath: path => {
212+
const params = new URLSearchParams(window.location.search);
213+
params.set(widgetUrlsSearchParamName, encodeURIComponent(path));
214+
// Starts with `?` because we don't want to change the path at all
215+
// This degrades gracefully if javascript is disabled
216+
return `?${params.toString()}`;
217+
},
218+
parsePath: str => {
219+
const url = new URL(str, 'https://github.com/');
220+
return getWidgetsUrl(url.searchParams);
221+
}
222+
}
223+
);
224+
}
225+
163226
export function hashIntegration() {
164227
return createIntegration(
165228
() => 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)