Skip to content

Commit 9a7c3d7

Browse files
authored
Merge pull request #1558 from easyops-cn/steve/link-in-app
fix(link): support inApp url
2 parents fe286fd + d8955b3 commit 9a7c3d7

File tree

2 files changed

+99
-6
lines changed

2 files changed

+99
-6
lines changed

bricks/basic/src/link/index.spec.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ jest.mock("@next-core/runtime", () => ({
2323
listen: jest.fn(),
2424
}),
2525
}));
26+
jest.mock("@next-core/react-runtime", () => ({
27+
useCurrentApp: () => ({
28+
homepage: "/demo",
29+
}),
30+
}));
2631

2732
beforeEach(() => {
2833
jest.clearAllMocks();
@@ -160,4 +165,56 @@ describe("eo-button", () => {
160165
expect(element6.shadowRoot?.childNodes.length).toBe(0);
161166
expect(element7.shadowRoot?.childNodes.length).toBe(0);
162167
});
168+
169+
test("inApp link", async () => {
170+
const element = document.createElement("eo-link") as Link;
171+
element.url = "/next-page";
172+
element.inApp = true;
173+
element.textContent = "inApp link";
174+
const onClick = jest.fn();
175+
element.addEventListener("click", onClick);
176+
177+
act(() => {
178+
document.body.appendChild(element);
179+
});
180+
181+
expect(element.shadowRoot?.querySelector("a")?.href).toBe(
182+
"http://localhost/demo/next-page"
183+
);
184+
185+
act(() => {
186+
element.shadowRoot?.querySelector("a")?.click();
187+
});
188+
expect(mockHistoryPush).toHaveBeenCalledWith("/demo/next-page");
189+
190+
act(() => {
191+
document.body.removeChild(element);
192+
});
193+
});
194+
195+
test("inApp link to home", async () => {
196+
const element = document.createElement("eo-link") as Link;
197+
element.url = "/";
198+
element.inApp = true;
199+
element.textContent = "inApp link";
200+
const onClick = jest.fn();
201+
element.addEventListener("click", onClick);
202+
203+
act(() => {
204+
document.body.appendChild(element);
205+
});
206+
207+
expect(element.shadowRoot?.querySelector("a")?.href).toBe(
208+
"http://localhost/demo"
209+
);
210+
211+
act(() => {
212+
element.shadowRoot?.querySelector("a")?.click();
213+
});
214+
expect(mockHistoryPush).toHaveBeenCalledWith("/demo");
215+
216+
act(() => {
217+
document.body.removeChild(element);
218+
});
219+
});
163220
});

bricks/basic/src/link/index.tsx

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import React, {
77
} from "react";
88
import { createDecorators } from "@next-core/element";
99
import { ReactNextElement, wrapBrick } from "@next-core/react-element";
10+
import { useCurrentApp } from "@next-core/react-runtime";
1011
import type { EoTooltip, ToolTipProps } from "../tooltip";
1112
import type {
1213
GeneralIcon,
@@ -35,6 +36,7 @@ export interface LinkProps {
3536
disabled?: boolean;
3637
url?: ExtendedLocationDescriptor;
3738
href?: string;
39+
inApp?: boolean;
3840
icon?: GeneralIconProps;
3941
target?: Target;
4042
showExternalIcon?: boolean;
@@ -90,6 +92,11 @@ class Link extends ReactNextElement implements LinkProps {
9092
})
9193
accessor url: ExtendedLocationDescriptor | undefined;
9294

95+
/**
96+
* 标识 `url` 是否为微应用内链接(即使用 APP.homepage 作为前缀)
97+
*/
98+
@property({ type: Boolean }) accessor inApp: boolean | undefined;
99+
93100
/**
94101
* 链接跳转目标
95102
*/
@@ -156,6 +163,7 @@ class Link extends ReactNextElement implements LinkProps {
156163
disabled={this.disabled}
157164
url={this.url}
158165
href={this.href}
166+
inApp={this.inApp}
159167
target={this.target}
160168
showExternalIcon={this.showExternalIcon}
161169
icon={this.icon}
@@ -178,6 +186,7 @@ export function LinkComponent({
178186
disabled,
179187
url,
180188
href,
189+
inApp,
181190
target,
182191
showExternalIcon,
183192
icon,
@@ -190,19 +199,28 @@ export function LinkComponent({
190199
const history = getHistory();
191200
const linkRef = useRef<HTMLAnchorElement>(null);
192201
const [currentLocation, setCurrentLocation] = useState(history.location);
202+
const app = useCurrentApp();
203+
const prefixWithHomepage = inApp ? app?.homepage : undefined;
193204

194205
const computedHref = useMemo(() => {
195206
if (disabled) return;
196207
if (href) return href;
197208
if (!url) return "";
198209
const loc =
199210
typeof url === "string"
200-
? createLocation(url, null, undefined, currentLocation)
211+
? createLocation(
212+
prefixWithHomepage
213+
? `${prefixWithHomepage}${url === "/" ? "" : url}`
214+
: url,
215+
null,
216+
undefined,
217+
currentLocation
218+
)
201219
: getExtendedLocationDescriptor(url, currentLocation);
202220
return loc
203221
? history.createHref(loc as LocationDescriptorObject<NextHistoryState>)
204222
: "";
205-
}, [disabled, href, url, currentLocation, history]);
223+
}, [disabled, href, url, currentLocation, history, prefixWithHomepage]);
206224

207225
const handleClick = useCallback(
208226
(e: MouseEvent) => {
@@ -225,15 +243,33 @@ export function LinkComponent({
225243
if (!url) return;
226244

227245
const method = replace ? history.replace : history.push;
228-
229246
method(
230247
typeof url === "string"
231-
? url
232-
: getExtendedLocationDescriptor(url, currentLocation)
248+
? prefixWithHomepage
249+
? `${prefixWithHomepage}${url === "/" ? "" : url}`
250+
: url
251+
: getExtendedLocationDescriptor(
252+
prefixWithHomepage
253+
? {
254+
...url,
255+
pathname: `${prefixWithHomepage}${url.pathname === "/" ? "" : url.pathname}`,
256+
}
257+
: url,
258+
currentLocation
259+
)
233260
);
234261
}
235262
},
236-
[currentLocation, disabled, history, href, replace, target, url]
263+
[
264+
currentLocation,
265+
disabled,
266+
history,
267+
href,
268+
replace,
269+
target,
270+
url,
271+
prefixWithHomepage,
272+
]
237273
);
238274

239275
useEffect(() => {

0 commit comments

Comments
 (0)