From d0fa668c68eafe13e6b2cfc9f5d5d89c09b15766 Mon Sep 17 00:00:00 2001 From: Barney Carroll Date: Tue, 10 May 2022 17:23:10 +0100 Subject: [PATCH] Clean up m.route.Link (#2768) * Simpler m.route.Link documentation. Fixes #2767 * Remove redundant HTML encoding from markdown docs * Warn about m.route.Link immunity to event handler API. * Integrate @JAForbes review corrections * Example code typo in docs/route.md Co-authored-by: Matias Kinnunen Co-authored-by: Matias Kinnunen --- docs/route.md | 100 +++++++++++++------------------------------------- 1 file changed, 26 insertions(+), 74 deletions(-) diff --git a/docs/route.md b/docs/route.md index 91c5fa35e..ff0e52f51 100644 --- a/docs/route.md +++ b/docs/route.md @@ -64,7 +64,7 @@ Argument | Type | Required | D ---------------------- | ---------------------------------------- | -------- | --- `root` | `Element` | Yes | A DOM element that will be the parent node to the subtree `defaultRoute` | `String` | Yes | The route to redirect to if the current URL does not match a route. Note, this is not the initial route. Initial route will be your address bar's url. -`routes` | Object<String,Component|RouteResolver> | Yes | An object whose keys are route strings and values are either components or a [RouteResolver](#routeresolver) +`routes` | `Object` | Yes | An object whose keys are route strings and values are either components or a [RouteResolver](#routeresolver) **returns** | | | Returns `undefined` [How to read signatures](signatures.md) @@ -125,94 +125,46 @@ This is a simple property, so you can both read it and write to it. ##### m.route.Link -This component can create a dynamic routable link: +This component creates a dynamic routed link. Its essential function is to produce `a` links with local `href`s transformed to take account of the [route prefix](#mrouteprefix). ```javascript -m(m.route.Link, {href: "/test"}) -``` - -Using `m.route.Link` causes the link to behave as a router link - clicking it navigates to the route specified in `href`, instead of navigating away from the current page to the URL specified in `href`. +m(m.route.Link, {href: "/foo"}, "foo") -You can also set the `options` passed to `m.route.set` when the link is clicked by passing the `options` attribute: - -```javascript -m(m.route.Link, {href: "/test", options: {replace: true}}) +// Unless m.route.prefix has changed from the default strategy, render to: +// foo ``` -You can pass other attributes, too, and you can also specify the tag name used. - -```javascript -m(m.route.Link, { - // Any hyperscript selector is valid here - it's literally passed as the - // first parameter to `m`. - selector: "span", - options: {replace: true}, - params: {key: "value"}, - href: "/test", - disabled: false, - class: "nav-link", - "data-foo": 1, - // and other attributes -}, "link name") -``` - -Magic attributes used by this selector (except `href` and `disabled`) *are* removed while proxying, so you won't have an odd `selector="span"` or `options="[object Object]"` attribute show up in your link's DOM node. The above vnode renders to this hyperscript, assuming the prefix is the default `#!`: - -```javascript -m("span", { - href: "#!/test", - onclick: function(e) { - // ... - }, - disabled: false, // Only if you specify it - class: "nav-link", - "data-foo": 1, - // and other attributes -}) -``` +Links accept a selection of special attributes: +* `selector` is what would be passed as the first argument to [`m`](hyperscript.md): any selector is valid, including non-`a` elements. +* `params` & `options` are the arguments with the same names as defined in [`m.route.set`](#mrouteset). +* `disabled`, if true, disables routing behaviour and any bound `onclick` handler, and attaches a `data-disabled="true"` attribute for accessibility hints; if the element is an `a`, the `href` is removed. -You can also prevent navigation by, in an `onclick` handler, invoking `e.preventDefault()` or returning `false`. This is the same way you block other events, so it's pretty natural. +*Routing behaviour cannot be prevented using the event handling API: use `disabled` instead.* ```javascript m(m.route.Link, { - href: "/test", - onclick: function(e) { - // Do things... - if (notReady()) e.preventDefault() - } + href: "/foo", + selector: "button.large", + disabled: true, + params: {key: "value"}, + options: {replace: true}, }, "link name") -``` - -This supports full accessibility for both `a` and `button`, via a `disabled` attribute. This ensures [no `href` attribute or `onclick` handler is set](https://css-tricks.com/how-to-disable-links/) and that an `"aria-disabled": "true"` attribute *is* set. If you are passing an `onclick` handler already, that's dropped. (You can work around this by adding it directly in a [lifecycle hook](lifecycle-methods.md).) The `disabled` attribute is itself proxied to the element or component, so you can disable routed ` ``` -Do note that this doesn't also disable pointer events for you - you have to do that yourself through CSS - this only does the JS part. Also, the removal of `href` *can* break certain style sheets - if you're relying on this to style disabled links, you may need to update your stylesheets accordingly. Chances are, you're probably just looking it up via `a`, `.some-class`, or `#some-id`, and if you are, you're already good to go. If you're using `[href]` or `:link`, in most cases you can just remove them and it'll still work - it's pretty common to over-specify selectors. If you can't do either, check for both `[href]`/`:link` *and* the non-standard `[disabled]` attribute that was implicitly forwarded to the component. - `vnode = m(m.route.Link, attributes, children)` Argument | Type | Required | Description --------------------- | ------------------------------------ | -------- | --- `attributes.href` | `Object` | Yes | The target route to navigate to. -`attributes.selector` | String|Object|Function | No | This sets the tag name to use. Must be a valid selector for [`m`](hyperscript.md) if given, defaults to `"a"`. -`attributes.options` | `Object` | No | This sets the options passed to [`m.route.set`](#mrouteset). -`attributes.disabled` | `Object` | No | This sets the options passed to [`m.route.set`](#mrouteset). -`attributes.disabled` | `Object` | No | This disables the link, so clicking on it doesn't route anywhere. -`attributes` | `Object` | No | Other attributes to apply to the returned vnode may be passed. -`children` | Array<Vnode>|String|Number|Boolean | No | Child [vnodes](vnodes.md) for this link. +`attributes.disabled` | `Boolean` | No | Disables the element accessibly. +`attributes.selector` | `String|Object|Function` | No | A selector for [`m`](hyperscript.md), defaults to `"a"`. +`attributes.options` | `Object` | No | Sets the `options` passed to [`m.route.set`](#mrouteset). +`attributes.params` | `Object` | No | Sets the `params` passed to [`m.route.set`](#mrouteset). +`attributes` | `Object` | No | Any other attributes to be forwarded to `m`. +`children` | `Array|String|Number|Boolean` | No | Child [vnodes](vnodes.md) for this link. **returns** | `Vnode` | | A [vnode](vnodes.md). ##### m.route.param @@ -228,7 +180,7 @@ Retrieves a route parameter from the last fully resolved route. A route paramete Argument | Type | Required | Description ----------------- | --------------- | -------- | --- `key` | `String` | No | A route parameter name (e.g. `id` in route `/users/:id`, or `page` in path `/users/1?page=3`, or a key in `history.state`) -**returns** | String|Object | | Returns a value for the specified key. If a key is not specified, it returns an object that contains all the interpolation keys +**returns** | `String|Object` | | Returns a value for the specified key. If a key is not specified, it returns an object that contains all the interpolation keys Note that in the `onmatch` function of a RouteResolver, the new route hasn't yet been fully resolved, and `m.route.param()` will return the parameters of the previous route, if any. `onmatch` receives the parameters of the new route as an argument. @@ -270,7 +222,7 @@ Argument | Type | Description `args` | `Object` | The [routing parameters](#routing-parameters) `requestedPath` | `String` | The router path requested by the last routing action, including interpolated routing parameter values, but without the prefix. When `onmatch` is called, the resolution for this path is not complete and `m.route.get()` still returns the previous path. `route` | `String` | The router path requested by the last routing action, excluding interpolated routing parameter values -**returns** | Component|Promise<Component>|undefined | Returns a component or a promise that resolves to a component +**returns** | `Component|Promise|undefined` | Returns a component or a promise that resolves to a component If `onmatch` returns a component or a promise that resolves to a component, this component is used as the `vnode.tag` for the first argument in the RouteResolver's `render` method. Otherwise, `vnode.tag` is set to `"div"`. Similarly, if the `onmatch` method is omitted, `vnode.tag` is also `"div"`. @@ -286,7 +238,7 @@ Argument | Type | Description ------------------- | -------------------- | ----------- `vnode` | `Object` | A [vnode](vnodes.md) whose attributes object contains routing parameters. If onmatch does not return a component or a promise that resolves to a component, the vnode's `tag` field defaults to `"div"` `vnode.attrs` | `Object` | A map of URL parameter values -**returns** | Array<Vnode>|Vnode | The [vnodes](vnodes.md) to be rendered +**returns** | `Array|Vnode` | The [vnodes](vnodes.md) to be rendered The `vnode` parameter is just `m(Component, m.route.param())` where `Component` is the resolved component for the route (after `routeResolver.onmatch`) and `m.route.param()` is as documented [here](#mrouteparam). If you omit this method, the default return value is `[vnode]`, wrapped in a fragment so you can use [key parameters](#key-parameter). Combined with a `:key` parameter, it becomes a [single-element keyed fragment](keys.md#reinitializing-views-with-single-child-keyed-fragments), since it ends up rendering to something like `[m(Component, {key: m.route.param("key"), ...})]`.