Skip to content

Commit 7b56c00

Browse files
authored
Merge pull request #114 from github/docs-add-annotations-for-code-samples
docs: add annotations for code samples
2 parents 0ff68f1 + 93112f3 commit 7b56c00

7 files changed

Lines changed: 176 additions & 8 deletions

File tree

docs/_guide/actions.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ Remember! Actions are _automatically_ bound using the `@controller` decorator. T
1414
<div class="d-flex my-4">
1515
<div class="">
1616

17+
<!-- annotations
18+
data-action "click.*": Will call `greet()` when clicked
19+
-->
20+
1721
```html
1822
<hello-world>
1923
<input
@@ -35,6 +39,10 @@ Remember! Actions are _automatically_ bound using the `@controller` decorator. T
3539
</div>
3640
<div class="ml-4">
3741

42+
<!-- annotations
43+
greet: All public methods can be called with `data-action`
44+
-->
45+
3846
```js
3947
import { controller, target } from "@github/catalyst"
4048

@@ -65,6 +73,10 @@ The actions syntax follows a pattern of `event:controller#method`.
6573

6674
Multiple actions can be bound to multiple events, methods, and controllers. For example:
6775

76+
<!-- annotations
77+
data-action: Fires all of these methods depending on the event
78+
-->
79+
6880
```html
6981
<analytics-tracking>
7082
<hello-world>
@@ -95,6 +107,10 @@ Multiple actions can be bound to multiple events, methods, and controllers. For
95107

96108
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:
97109

110+
<!-- annotations
111+
data-action "loaded: Calls enable() on the `loaded` custom event
112+
-->
113+
98114
```html
99115
<hover-card disabled>
100116
<lazy-loader data-url="/user/1" data-action="loaded:hover-card#enable">
@@ -103,6 +119,11 @@ A Controller may emit custom events, which may be listened to by other Controlle
103119
</hover-card>
104120
```
105121

122+
<!-- annotations
123+
this . dispatchEvent . new CustomEvent . . loaded . . : Dispatches custom "loaded" event
124+
enable: All public methods can be called with `data-action`
125+
-->
126+
106127
```js
107128
import {controller} from '@github/catalyst'
108129

docs/_guide/attrs.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ To use the `@attr` decorator, attach it to a class field, and it will get/set th
1818

1919
### Example
2020

21+
<!-- annotations
22+
attr foo: Maps to get/setAttribute('datafoo')
23+
-->
24+
2125
```js
2226
import { controller, attr } from "@github/catalyst"
2327

@@ -66,6 +70,10 @@ Below is a handy reference for the small differences, this is all explained in m
6670

6771
If an attribute is first set to a `string`, then it can only ever be a `string` during the lifetime of an element. The property will return an empty string (`''`) if the attribute doesn't exist, and trying to set it to something that isn't a string will turn it into one before assignment.
6872

73+
<!-- annotations
74+
attr foo: Maps to get/setAttribute('data-foo')
75+
-->
76+
6977
```js
7078
import { controller, attr } from "@github/catalyst"
7179

@@ -87,6 +95,10 @@ class HelloWorldElement extends HTMLElement {
8795

8896
If an attribute is first set to a boolean, then it can only ever be a boolean during the lifetime of an element. Boolean properties check for _presence_ of an attribute, sort of like how [`required`, `disabled` & `readonly` attributes work on forms](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes#boolean_attributes) The property will return `false` if the attribute doesn't exist, and `true` if it does, regardless of the value. If the property is set to `false` then `removeAttribute` is called, whereas `setAttribute(name, '')` is called when setting to a truthy value.
8997

98+
<!-- annotations
99+
attr foo: Maps to has/toggleAttribute('data-foo')
100+
-->
101+
90102
```js
91103
import { controller, attr } from "@github/catalyst"
92104

@@ -108,6 +120,10 @@ class HelloWorldElement extends HTMLElement {
108120

109121
If an attribute is first set to a number, then it can only ever be a number during the lifetime of an element. This is sort of like the [`maxlength` attribute on inputs](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/maxlength). The property will return `0` if the attribute doesn't exist, and will be coerced to `Number` if it does - this means it is _possible_ to get back `NaN`. Negative numbers and floats are also valid.
110122

123+
<!-- annotations
124+
attr foo: Maps to get/setAttribute('data-foo')
125+
-->
126+
111127
```js
112128
import { controller, attr } from "@github/catalyst"
113129

@@ -139,6 +155,10 @@ Remember! The values defined in the class field are the _default_. They won't be
139155

140156
The following example illustrates this behavior:
141157

158+
<!-- annotations
159+
attr name: Maps to get/setAttribute('data-name')
160+
-->
161+
142162
```js
143163
import { controller, attr } from "@github/catalyst"
144164
@controller
@@ -150,6 +170,10 @@ class HelloWorldElement extends HTMLElement {
150170
}
151171
```
152172

173+
<!-- annotations
174+
data-name ".*": Will set the value of `name`
175+
-->
176+
153177
```html
154178
<hello-world></hello-world>
155179
// This will render `Hello World`

docs/_guide/targets.md

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ To create a Target, use the `@target` decorator on a class field, and add the ma
1313

1414
<div class="d-flex my-4">
1515
<div>
16+
<!-- annotations
17+
data-target ".*": This maps to the `@target output` property
18+
-->
1619

1720
```html
1821
<hello-world>
@@ -25,6 +28,10 @@ To create a Target, use the `@target` decorator on a class field, and add the ma
2528
</div>
2629
<div class="ml-4">
2730

31+
<!-- annotations
32+
@ target output: This maps to the data-target attribute
33+
-->
34+
2835
```js
2936
import { controller, target } from "@github/catalyst"
3037

@@ -58,23 +65,35 @@ The `@target` decorator will only ever return _one_ element, just like `querySel
5865

5966
Elements can be referenced as multiple targets, and targets may be referenced multiple times within the HTML:
6067

68+
<!-- annotations
69+
data-targets ".*users": This maps to the `@targets users` property
70+
data-target ".*read": This maps to the `@target read` property
71+
data-target ".*write": This maps to the `@target write` property
72+
-->
73+
6174
```html
6275
<team-members>
6376
<user-list>
6477
<user-settings data-targets="user-list.users">
65-
<input type="checkbox" data-targets="team-members.reads user-settings.reads">
66-
<input type="checkbox" data-targets="team-members.writes user-settings.writes">
78+
<input type="checkbox" data-target="user-settings.read">
79+
<input type="checkbox" data-target="user-settings.write">
6780
</user-settings>
6881
<user-settings data-targets="user-list.users">
69-
<input type="checkbox" data-targets="team-members.reads user-settings.reads">
70-
<input type="checkbox" data-targets="team-members.writes user-settings.writes">
82+
<input type="checkbox" data-target="user-settings.read">
83+
<input type="checkbox" data-target="user-settings.write">
7184
</user-settings>
7285
</user-list>
7386
</team-members>
7487
```
7588

7689
<br>
7790

91+
<!-- annotations
92+
@ targets users: This maps to the data-targets attribute
93+
@ target read: This maps to the data-target attribute
94+
@ target write: This maps to the data-target attribute
95+
-->
96+
7897
```js
7998
import { controller, target, targets } from "@github/catalyst"
8099

@@ -84,7 +103,7 @@ class UserSettingsElement extends HTMLElement {
84103
@target write: HTMLInputElement
85104

86105
valid() {
87-
// One checkbox must be checked!
106+
// At least one checkbox must be checked!
88107
return this.read.checked || this.write.checked
89108
}
90109
}
@@ -125,8 +144,8 @@ If you're not using decorators, then you'll need to make a `getter`, and call `f
125144
import {findTarget, findTargets} from '@github/catalyst'
126145
class HelloWorldElement extends HTMLElement {
127146

128-
get outputTarget() {
129-
return findTarget(this, 'outputTarget')
147+
get output() {
148+
return findTarget(this, 'output')
130149
}
131150

132151
get pages() {

docs/_guide/your-first-component.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,15 @@ chapter: 3
77

88
Catalyst's `@controller` decorator lets you create Custom Elements with virtually no boilerplate, by automatically calling `customElements.register`, and by adding ["Actions"]({{ site.baseurl }}/guide/actions) and ["Targets"]({{ site.baseurl }}/guide/targets) features described later. Using TypeScript (with `decorators` support enabled), simply add `@controller` to the top of your class:
99

10+
<!-- annotations
11+
controller: This must be added to all Catalyst controllers.
12+
extends HTMLElement: This must be added to all Catalyst controllers.
13+
connectedCallback: This runs when the element is added to the DOM | {{ site.baseurl }}/guide/lifecycle-hooks/#codeconnectedcallbackcode
14+
-->
15+
1016
```js
1117
import {controller} from '@github/catalyst'
18+
1219
@controller
1320
class HelloWorldElement extends HTMLElement {
1421
connectedCallback() {
@@ -62,6 +69,8 @@ Using the `@controller` decorator saves on having to write this boilerplate for
6269
If you don't want to use TypeScript decorators, you can use `controller` as a regular function, and just pass it your class:
6370

6471
```js
72+
import {controller} from '@github/catalyst'
73+
6574
controller(
6675
class HelloWorldElement extends HTMLElement {
6776
//...

docs/custom.css

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,22 @@ body {
4343
.bg-gray { background-color: var(--color-bg-canvas-tertiary) !important }
4444

4545
/* Code Blocks & Syntax */
46-
.markdown-body .highlight pre, .markdown-body pre { background-color: var(--color-bg-canvas-tertiary) }
46+
.markdown-body .highlight pre, .markdown-body pre {
47+
background-color: var(--color-bg-canvas-tertiary);
48+
overflow: inherit
49+
}
4750

4851
/* Inline Code */
4952
.markdown-body code, .markdown-body tt { background-color: var(--color-markdown-code-bg) }
5053

5154
/* Tables */
5255
.markdown-body table tr:nth-of-type(odd) th, .markdown-body table tr:nth-of-type(odd) td { background-color: var(--color-bg-canvas) }
5356
.markdown-body table tr:nth-of-type(even) th, .markdown-body table tr:nth-of-type(even) td { background-color: var(--color-bg-canvas-tertiary) }
57+
58+
/* Override Primer .tooltipped default aria-label */
59+
.code-tooltip:after { content: attr(data-title); text-align: left; font-size: 100%; }
60+
/* :after text will be announced by AT, this adds a separation between textContent and :after text */
61+
.code-tooltip:before { content: ': '; font-size: 0; }
62+
.code-tooltip {
63+
scroll-margin-top: 150px;
64+
}

docs/github-syntax.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ html[data-prefers-color-scheme=dark] {
4747

4848
.highlight .hll { background-color: var(--color-syntax-highlight) }
4949

50+
.highlight .nx,
51+
.highlight .p { color: var(--color-syntax-primary); }
52+
5053
/* Comment, Comment.Multiline, Comment.Single */
5154
.highlight .c,
5255
.highlight .cm,
@@ -90,6 +93,7 @@ html[data-prefers-color-scheme=dark] {
9093
.highlight .sr,
9194
.highlight .s1,
9295
.highlight .ss,
96+
.highlight .dl,
9397
.highlight .il { color: var(--color-syntax-string) }
9498

9599
/* Name.Attribute, Name.Constant, Name.Variable, Name.Variable.Class, Name.Variable.Global, Name.Variable.Instance */

docs/index.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,83 @@ function applyColorSchemePreference() {
3838

3939
storeColorSchemePreference()
4040
applyColorSchemePreference()
41+
42+
function addAnnotations() {
43+
for (const codeBlock of document.querySelectorAll('.highlighter-rouge')) {
44+
const comment = parseCommentNode(codeBlock)
45+
if (comment.annotations) annotate(codeBlock, comment.annotations)
46+
}
47+
}
48+
49+
function parseCommentNode(el) {
50+
const stopAtEl = el.previousElementSibling
51+
let t = el.previousSibling
52+
if (!stopAtEl && !t) return
53+
let comment
54+
while (t && t !== stopAtEl) {
55+
if (t.nodeType === 8) {
56+
comment = t
57+
break
58+
} else {
59+
t = t.previousSibling
60+
}
61+
}
62+
63+
if (!comment) return {}
64+
65+
const [type, ...details] = comment.textContent.trim().split('\n')
66+
67+
return {
68+
noDemo: type.match(/no_demo/),
69+
onlyDemo: type.match(/only_demo/),
70+
annotations: type.match(/annotations/) && details
71+
}
72+
}
73+
74+
let matchIndex = 0
75+
function annotate(codeBlock, items) {
76+
const noMatch = new Set(items)
77+
const annotated = new WeakMap()
78+
for (const el of codeBlock.querySelectorAll('code > span')) {
79+
for (const item of items) {
80+
let currentNode = el
81+
const [pattern, rest] = item.split(/: /)
82+
const [title, link] = (rest || '').split(/ \| /)
83+
const parts = pattern.split(' ')
84+
let toAnnotate = []
85+
for (const part of parts) {
86+
if (currentNode && currentNode.textContent.match(part)) {
87+
toAnnotate.push(currentNode)
88+
currentNode = currentNode.nextElementSibling
89+
} else {
90+
toAnnotate = []
91+
break
92+
}
93+
}
94+
for (const node of toAnnotate) {
95+
noMatch.delete(item)
96+
if (title) {
97+
if (annotated.get(node)) {
98+
continue
99+
}
100+
annotated.set(node, title)
101+
const a = document.createElement('a')
102+
a.className = `${node.className} code-tooltip tooltipped tooltipped-multiline tooltipped-se bg-gray text-underline`
103+
a.id = `match-${matchIndex++}`
104+
a.href = link || `#${a.id}`
105+
a.setAttribute('data-title', title)
106+
a.textContent = node.textContent
107+
node.replaceWith(a)
108+
} else {
109+
node.classList.add('bg-gray')
110+
}
111+
}
112+
}
113+
}
114+
for (const pattern of noMatch) {
115+
// eslint-disable-next-line no-console
116+
console.error(`Code annotations: No match found for "${pattern}"`)
117+
}
118+
}
119+
120+
addAnnotations()

0 commit comments

Comments
 (0)