jenkins-bot has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/392570 )
Change subject: Update: capture wiki link clicks and navigation
......................................................................
Update: capture wiki link clicks and navigation
Extract Link.onClick to link/click#onClick and use in ContentSection
so that internal clicks on content are routed internally instead of
triggering a full page reload.
Additional changes:
* Moved Link to link/
* Added the feature so that the onClick handler works when set on
containers
Bug: T177367
Change-Id: I5175d9473c128fce87820e9d11788db7b4dd6df1
---
M src/common/components/content-section/content-section.tsx
M src/common/components/header/header.tsx
D src/common/components/link.tsx
A src/common/components/link/link.tsx
A src/common/components/link/on-click.ts
M src/common/components/thumbnail/thumbnail.tsx
M src/common/pages/about.tsx
M src/common/pages/home.tsx
8 files changed, 90 insertions(+), 65 deletions(-)
Approvals:
Jhernandez: Looks good to me, approved
jenkins-bot: Verified
diff --git a/src/common/components/content-section/content-section.tsx
b/src/common/components/content-section/content-section.tsx
index a87ad83..e68912e 100644
--- a/src/common/components/content-section/content-section.tsx
+++ b/src/common/components/content-section/content-section.tsx
@@ -1,16 +1,25 @@
+import { History } from "history";
import { h } from "preact";
import { PageSection } from "../../models/page/page";
import Content from "../content/content";
import DynamicHeader from "../dynamic-header/dynamic-header";
+import { onClick } from "../link/on-click";
import "./content-section.css";
export interface Props {
section: PageSection;
}
-export default function ContentSection({ section }: Props): JSX.Element {
+export default function ContentSection(
+ { section }: Props,
+ context: { history?: History }
+): JSX.Element {
return (
- <section class="ContentSection" id={section.fragment}>
+ <section
+ onClick={event => onClick(context, event)}
+ class="ContentSection"
+ id={section.fragment}
+ >
{section.titleHTML && ( // Omit empty headers such as the lead.
<DynamicHeader class="ContentSection-header" level={section.level + 1}>
<Content dangerouslySetInnerHTML={{ __html: section.titleHTML }} />
diff --git a/src/common/components/header/header.tsx
b/src/common/components/header/header.tsx
index 199d143..979eb96 100644
--- a/src/common/components/header/header.tsx
+++ b/src/common/components/header/header.tsx
@@ -1,6 +1,6 @@
import { h } from "preact";
import Wordmark from "../wordmark/wordmark";
-import Link from "../link";
+import Link from "../link/link";
import { home } from "../../routers/api";
import "./header.css";
diff --git a/src/common/components/link.tsx b/src/common/components/link.tsx
deleted file mode 100644
index 75ec31c..0000000
--- a/src/common/components/link.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { h } from "preact";
-import { History } from "history";
-import { ChildrenProps, classOf, ClassProps } from "./preact-utils";
-
-export interface Props extends ClassProps, ChildrenProps {
- href: string;
-}
-
-function isModifiedEvent(event: MouseEvent): boolean {
- return event.metaKey || event.altKey || event.ctrlKey || event.shiftKey;
-}
-
-/**
- * A single page app link that intercepts navigation (click) events and passes
- * control to History. All hyperlinks should use a Link component.
- */
-export default function Link(
- { href, children, ...props }: Props,
- context: { history?: History }
-): JSX.Element {
- return (
- <a
- class={classOf("Link", props.class)}
- href={href}
- onClick={event => {
- const origin = window.location.origin;
- // Use currentTarget as target may be other DOM elements inside the
- // anchor element
- const link = event.currentTarget as HTMLAnchorElement;
- // TODO: Move all the logic to check if an event should be captured to
a
- // DOM utilities module and add unit tests
- if (
- // Check if the href is internal
- //
- // N.B. link.href is used because browsers will transform relative
- // paths to the full path when set on the HTML anchor, which is what
- // is used to check against the current origin
- link.href &&
- link.href.indexOf(origin) === 0 &&
- // onClick not prevented default
- !event.defaultPrevented &&
- // Ignore everything but left clicks
- event.button === 0 &&
- // Let browser handle "target=_blank" etc.
- !link.target &&
- // Ignore clicks with modifier keys
- !isModifiedEvent(event) &&
- // We have a context history
- context.history
- ) {
- event.preventDefault();
- context.history.push(href);
- }
- }}
- >
- {children}
- </a>
- );
-}
diff --git a/src/common/components/link/link.tsx
b/src/common/components/link/link.tsx
new file mode 100644
index 0000000..ea064f3
--- /dev/null
+++ b/src/common/components/link/link.tsx
@@ -0,0 +1,27 @@
+import { History } from "history";
+import { h } from "preact";
+import { ChildrenProps, classOf, ClassProps } from "../preact-utils";
+import { onClick } from "./on-click";
+
+export interface Props extends ClassProps, ChildrenProps {
+ href: string;
+}
+
+/**
+ * A single page app link that intercepts navigation (click) events and passes
+ * control to History. All hyperlinks should use a Link component.
+ */
+export default function Link(
+ { href, children, ...props }: Props,
+ context: { history?: History }
+): JSX.Element {
+ return (
+ <a
+ class={classOf("Link", props.class)}
+ href={href}
+ onClick={event => onClick(context, event)}
+ >
+ {children}
+ </a>
+ );
+}
diff --git a/src/common/components/link/on-click.ts
b/src/common/components/link/on-click.ts
new file mode 100644
index 0000000..21f827b
--- /dev/null
+++ b/src/common/components/link/on-click.ts
@@ -0,0 +1,48 @@
+import { History } from "history";
+
+function isModifiedEvent(event: MouseEvent): boolean {
+ return event.metaKey || event.altKey || event.ctrlKey || event.shiftKey;
+}
+
+function isInternalLink(link: HTMLAnchorElement) {
+ // N.B. link.href is used because browsers will transform relative paths to
+ // the full path when set on the HTML anchor, which is what is used to check
+ // against the current origin.
+ return link.href && link.href.indexOf(window.location.origin) === 0;
+}
+
+// TODO: add unit tests
+export function onClick(
+ context: { history?: History },
+ event: MouseEvent
+): void {
+ let link = null;
+ if (event.currentTarget instanceof HTMLAnchorElement) {
+ // When set on an anchor, currentTarget is the anchor
+ link = event.currentTarget;
+ } else if (event.target instanceof HTMLAnchorElement) {
+ // When set on a parent to capture all clicks, currentTarget may be
+ // something else. Use target if it is a link, as the handler triggers on
+ // the bubbling phase
+ link = event.target;
+ }
+
+ if (
+ link &&
+ isInternalLink(link) &&
+ // onClick not prevented default
+ !event.defaultPrevented &&
+ // Ignore everything but left clicks
+ event.button === 0 &&
+ // Let browser handle "target=_blank" etc.
+ !link.target &&
+ // Ignore clicks with modifier keys
+ !isModifiedEvent(event) &&
+ // We have a context history
+ context.history
+ ) {
+ event.preventDefault();
+ const path = link.href.replace(window.location.origin, "");
+ context.history.push(path);
+ }
+}
diff --git a/src/common/components/thumbnail/thumbnail.tsx
b/src/common/components/thumbnail/thumbnail.tsx
index 64dd7a2..8b29c93 100644
--- a/src/common/components/thumbnail/thumbnail.tsx
+++ b/src/common/components/thumbnail/thumbnail.tsx
@@ -1,6 +1,6 @@
import { h } from "preact";
import { ClassProps, classOf } from "../preact-utils";
-import Link from "../link";
+import Link from "../link/link";
import { PageImage } from "../../models/page/image";
import "./thumbnail.css";
diff --git a/src/common/pages/about.tsx b/src/common/pages/about.tsx
index 33a3ba9..24b7440 100644
--- a/src/common/pages/about.tsx
+++ b/src/common/pages/about.tsx
@@ -1,6 +1,6 @@
import { h, Component as PreactComponent } from "preact";
import App from "../components/app/app";
-import Link from "../components/link";
+import Link from "../components/link/link";
import Page from "../components/page/page";
// Fake a type declaration for the global variable VERSION that will be
replaced
diff --git a/src/common/pages/home.tsx b/src/common/pages/home.tsx
index 20b1a64..3a18729 100644
--- a/src/common/pages/home.tsx
+++ b/src/common/pages/home.tsx
@@ -11,7 +11,7 @@
} from "../../common/routers/api";
import Page from "../components/page/page";
import { PageTitleID } from "../models/page/title";
-import Link from "../components/link";
+import Link from "../components/link/link";
const testSummaries = [
{
--
To view, visit https://gerrit.wikimedia.org/r/392570
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I5175d9473c128fce87820e9d11788db7b4dd6df1
Gerrit-PatchSet: 3
Gerrit-Project: marvin
Gerrit-Branch: master
Gerrit-Owner: Niedzielski <[email protected]>
Gerrit-Reviewer: Jhernandez <[email protected]>
Gerrit-Reviewer: Sniedzielski <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits