Skip to content

Commit b749bb3

Browse files
authored
Merge pull request #42 from github/docs-guide-proofread-and-various-tweaks
docs: Guide proofread and various tweaks
2 parents 81db73b + 39a6ac0 commit b749bb3

5 files changed

Lines changed: 121 additions & 30 deletions

File tree

docs/_guide/actions.md

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Multiple actions can be bound to multiple events, methods, and controllers. For
8585
data-action="
8686
input:hello-world#validate
8787
blur:hello-world#validate
88-
focus:analytics-tracking#hover
88+
focus:analytics-tracking#focus
8989
"
9090
type="text"
9191
>
@@ -105,32 +105,34 @@ Multiple actions can be bound to multiple events, methods, and controllers. For
105105

106106
### Custom Events
107107

108-
A Controller may emit custom events, which may be listened to by other Controllers using the same Actions Syntax. There is no extra syntax needed for this. For example a `lazy-load` Controller may dispatch a `loaded` event, once its contents are loaded, and other controllers can listen to this event:
108+
A Controller may emit custom events, which may be listened to by other Controllers using the same Actions Syntax. There is no extra syntax needed for this. For example a `lazy-loader` Controller might dispatch a `loaded` event, once its contents are loaded, and other controllers can listen to this event:
109109

110110
```html
111111
<hover-card disabled>
112-
<lazy-load data-url="/user/1" data-action="loaded:hover-card#enable">
112+
<lazy-loader data-url="/user/1" data-action="loaded:hover-card#enable">
113113
<loading-spinner>
114-
</lazy-load>
114+
</lazy-loader>
115115
</hover-card>
116116
```
117117

118-
### Private Methods
119-
120-
Actions can always be bound to any method that is available on the Controller's prototype. If you need a method on a class that _must not_ be invoked within Actions, then you can instead use a [_class field_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields) or a [_private class field_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields#Private_fields).
121-
122118
```js
123119
import {controller} from '@github/catalyst'
124120

125121
@controller
126-
class HelloWorldElement extends HTMLElement {
122+
class LazyLoader extends HTMLElement {
127123

128-
hidden = () => {
129-
console.log('data-action cannot call this hidden method, but other JavaScript can!')
124+
connectedCallback() {
125+
this.innerHTML = await (await fetch(this.dataset.url)).text()
126+
this.dispatchEvent(new CustomEvent('loaded'))
130127
}
131128

132-
#reallyHidden = () => {
133-
console.log('data-action cannot call this hidden method, neither can other JavaScript!')
129+
}
130+
131+
@controller
132+
class HoverCard extenda HTMLElement {
133+
134+
enable() {
135+
this.disabled = false
134136
}
135137

136138
}

docs/_guide/anti-patterns.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
chapter: 8
3+
subtitle: Anti Patterns
4+
---

docs/_guide/decorators.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class HelloWorldElement extends HTMLElement {}
2020

2121
### Class Field Decorators
2222

23-
Catalyst comes with the `@target` and `@targets` decorators for more [read about Targets](/guide/targets). These get added on top or to the left of the field name, like so:
23+
Catalyst comes with the `@target` and `@targets` decorators (for more on these [read the Targets guide section](/guide/targets)). These get added on top or to the left of the field name, like so:
2424

2525
```js
2626
class HelloWorldElement extends HTMLElement {
@@ -59,9 +59,11 @@ class HelloWorldElement extends HTMLElement {
5959
}
6060
```
6161

62-
### Function Call Decorators
62+
### Function Calling Decorators
6363

64-
Some decorators are customisable - they get called with additional arguments, just like a function call. An example of this is the `@debounce` decorator in the [`@github/mini-throttle`](https://github.com/github/mini-throttle) package:
64+
You might see some decorators that look like function calls, and that's because they are! Some decorators allow for customisation; calling with additional arguments. Decorators that expect to be called are generally not interchangeable with the non-call variant, a decorators documentation should tell you how to use it.
65+
66+
Catalyst doesn't ship with any decorators that can be called like a function; but an example of one can be found in the `@debounce` decorator in the [`@github/mini-throttle`](https://github.com/github/mini-throttle) package:
6567

6668
```js
6769
class HelloWorldElement extends HTMLElement {

docs/_guide/patterns.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
---
2+
chapter: 7
3+
subtitle: Patterns
4+
---
5+
6+
An aim of Catalyst is to be as light weight as possible, and so we often avoid including helper functions for otherwise fine code. We also want to keep Catalyst focussed, and so where some helper functions might be reasonable, we recommend judicious use of other small libraries.
7+
8+
Here are a few common patterns which we've avoided introducing into the Catalyst code base, and instead encourage you to take the example code and run with that:
9+
10+
11+
### Debouncing or Throttling events
12+
13+
Often times you'll want to do something computationally intensive (or network intensive) based on a user event. It's worth throttling the amount of times a function can be called for these events, to prevent saturation of the CPU or network. For this we can use the "debounce" or "throttle" patterns. We recommend using the [`@github/mini-throttle`](https://github.com/github/mini-throttle) library for this, which provides convenient decorators to put on methods which will add throttling to them:
14+
15+
```typescript
16+
import {controller} from '@github/catalyst'
17+
import {debounce} from '@github/mini-throttle/decorators'
18+
19+
@controller
20+
class FuzzySearchElement extends HTMLElement {
21+
22+
// Adding `@debounce(100)` here means this method will only be called once in a 100ms period.
23+
@debounce(100)
24+
search(event: Event) {
25+
const value = event.currentTarget.value
26+
// This function is very computationally intensive, so we should run it as little as possible
27+
this.filterAllItemsWithValue(value)
28+
}
29+
30+
}
31+
```
32+
33+
### Aborting Network Requests
34+
35+
When making network requests using `fetch`, based on user input, you can cancel old requests as new ones come in, this is useful for performance as well as UI responsiveness, as old requests that aren't cancelled might complete later than newer ones causing the UI to jump around. Aborting network requests requires you to use [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) (a web platform feature).
36+
37+
```typescript
38+
@controller
39+
class RemoveSearchElement extends HTMLElement {
40+
41+
#remoteSearchController: AbortController|null
42+
43+
async search(event: Event) {
44+
// Abort the old Request
45+
this.#remoteSearchController?.abort()
46+
47+
// To start making a new request, construct an AbortController
48+
const {signal} = (this.#remoteSearchController = new AbortController())
49+
50+
try {
51+
const res = await fetch(myUrl, {signal})
52+
53+
// ... Add logic here with the completed network response
54+
} catch (e) {
55+
56+
// ... Add logic here if you need to report a failed network request.
57+
// Do not rethrow for network errors!
58+
59+
}
60+
61+
if (signal.aborted) {
62+
// Here you can add logic for if the request was cancelled, but
63+
// usually what you want to do is just return early to avoid
64+
// cleaning up the loading UI (bear in mind if the request is
65+
// cancelled then another one will be in its place).
66+
return
67+
}
68+
69+
// ... Add cleanup logic here, such as removing `loading` classes.
70+
71+
}
72+
}
73+
```

docs/_guide/targets.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ chapter: 5
33
subtitle: Querying Descendants
44
---
55

6-
One of the three [core patterns](/guide/introduction#three-core-concepts-observe-listen-query) is Querying. In Catalyst, Targets are the preferred way to query. Target use `querySelectorAll` under the hood, but make it a lot simpler to work with.
6+
One of the three [core patterns](/guide/introduction#three-core-concepts-observe-listen-query) is Querying. In Catalyst, Targets are the preferred way to query. Targets use `querySelectorAll` under the hood, but in a way that makes it a lot simpler to work with.
77

8-
Catalyst Components are really just Web Components, so you could simply use `querySelector` or `querySelectorAll` to select descendants of the element. Targets avoid some of the problems of `querySelector`; they provide a more consistent interface and handle nesting intuitively. Targets are also a little more ergonomic to reuse in a class. We'd recommend using Targets over `querySelector` wherever you can.
8+
Catalyst Components are really just Web Components, so you could simply use `querySelector` or `querySelectorAll` to select descendants of the element. Targets avoid some of the problems of `querySelector`; they provide a more consistent interface, avoid coupling CSS classes or HTML tag names to JS, and they handle subtle issues like nested components. Targets are also a little more ergonomic to reuse in a class. We'd recommend using Targets over `querySelector` wherever you can.
99

1010
To create a Target, use the `@target` decorator on a class field, and add the matching `data-target` attribute to your HTML, like so:
1111

@@ -66,20 +66,20 @@ Remember! There are two decorators available, `@target` which fetches only one e
6666
</div>
6767
</div>
6868

69-
The `@target` decorator will only ever return _one_ element, just like `querySelector`. If you want to get multiple Targets, you need the `@targets` decorator which works almost identically, but it'll return an _array_ of _N_ elements.
69+
The `@target` decorator will only ever return _one_ element, just like `querySelector`. If you want to get multiple Targets, you need the `@targets` decorator which works almost identically, but it'll return an _array_ of elements. To put this into types: `@target` returns `Element|undefined` while `@targets` returns `Array<Element>`
7070

7171
Elements can be referenced as multiple targets, and targets may be referenced multiple times within the HTML:
7272

7373
```html
7474
<team-members>
7575
<user-list>
7676
<user-settings data-target="user-list.user">
77-
<input type="checkbox" data-target="team-members.readCheckbox">
78-
<input type="checkbox" data-target="team-members.writeCheckbox">
77+
<input type="checkbox" data-target="team-members.read user-settings.read">
78+
<input type="checkbox" data-target="team-members.write user-settings.write">
7979
</user-settings>
8080
<user-settings data-target="user-list.user">
81-
<input type="checkbox" data-target="team-members.readCheckbox">
82-
<input type="checkbox" data-target="team-members.writeCheckbox">
81+
<input type="checkbox" data-target="team-members.read user-settings.read">
82+
<input type="checkbox" data-target="team-members.write user-settings.write">
8383
</user-settings>
8484
</user-list>
8585
</team-members>
@@ -88,16 +88,26 @@ Elements can be referenced as multiple targets, and targets may be referenced mu
8888
<br>
8989

9090
```js
91-
import { controller, targets } from "@github/catalyst"
91+
import { controller, target, targets } from "@github/catalyst"
9292

9393
@controller
94-
class HelloWorldElement extends HTMLElement {
95-
@targets readCheckbox!: HTMLElement
96-
@targets writeCheckbox!: HTMLElement
94+
class UserSettingsElement extends HTMLElement {
95+
@target read!: HTMLInputElement
96+
@target write!: HTMLInputElement
9797

98-
validate() {
98+
valid() {
9999
// One checkbox must be checked!
100-
return this.readCheckbox.length > 0 && this.writeCheckbox.length > 0
100+
return this.read.checked || this.write.checked
101+
}
102+
}
103+
104+
@controller
105+
class UserListElement extends HTMLElement {
106+
@targets user!: HTMLElement
107+
108+
valid() {
109+
// Every user must be valid!
110+
return this.user.every(user => user.valid())
101111
}
102112
}
103113
```
@@ -106,7 +116,7 @@ class HelloWorldElement extends HTMLElement {
106116

107117
If you're using decorators, then the `@target` and `@targets` decorators will turn the decorated properties into getters.
108118

109-
If you're not using decorators, then you'll need to call `findTarget(this, key)` or `findTargets(this, key)` in the getter, for example:
119+
If you're not using decorators, then you'll need to make a `getter`, and call `findTarget(this, key)` or `findTargets(this, key)` in the getter, for example:
110120

111121
```js
112122
import {findTarget, findTargets} from '@github/catalyst'

0 commit comments

Comments
 (0)