@datocms/astro 0.6.10 → 0.6.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/ContentLink/ContentLink.astro +27 -17
- package/src/ContentLink/README.md +267 -78
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@datocms/astro",
|
|
3
3
|
"description": "A set of components and utilities to work faster with DatoCMS in Astro projects.",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.6.
|
|
5
|
+
"version": "0.6.12",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@0no-co/graphql.web": "^1.0.7",
|
|
49
|
-
"@datocms/content-link": "^0.3.
|
|
49
|
+
"@datocms/content-link": "^0.3.19",
|
|
50
50
|
"datocms-listen": "^1.0.1",
|
|
51
51
|
"datocms-structured-text-generic-html-renderer": "^5.0.0",
|
|
52
52
|
"datocms-structured-text-utils": "^5.1.6"
|
|
@@ -80,9 +80,16 @@ export interface Props {
|
|
|
80
80
|
* @default false
|
|
81
81
|
*/
|
|
82
82
|
stripStega?: boolean;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Hue (0–359) of the overlay accent color.
|
|
86
|
+
*
|
|
87
|
+
* @default 17 (orange)
|
|
88
|
+
*/
|
|
89
|
+
hue?: number;
|
|
83
90
|
}
|
|
84
91
|
|
|
85
|
-
const { enableClickToEdit, stripStega = false } = Astro.props;
|
|
92
|
+
const { enableClickToEdit, stripStega = false, hue } = Astro.props;
|
|
86
93
|
|
|
87
94
|
// Serialize props for client-side script
|
|
88
95
|
const enableClickToEditValue =
|
|
@@ -98,12 +105,14 @@ const enableClickToEditValue =
|
|
|
98
105
|
? JSON.stringify(enableClickToEditValue)
|
|
99
106
|
: undefined}
|
|
100
107
|
data-strip-stega={stripStega ? 'true' : undefined}
|
|
108
|
+
data-hue={hue !== undefined ? String(hue) : undefined}
|
|
101
109
|
>
|
|
102
110
|
</datocms-content-link>
|
|
103
111
|
|
|
104
112
|
<script>
|
|
105
113
|
import { createController } from '@datocms/content-link';
|
|
106
114
|
import { navigate } from 'astro:transitions/client';
|
|
115
|
+
import type { ClickToEditOptions } from './ContentLink.astro';
|
|
107
116
|
|
|
108
117
|
class DatoCMSContentLink extends HTMLElement {
|
|
109
118
|
private controller: ReturnType<typeof createController> | null = null;
|
|
@@ -123,9 +132,11 @@ const enableClickToEditValue =
|
|
|
123
132
|
const stripStegaAttr = this.getAttribute('data-strip-stega');
|
|
124
133
|
|
|
125
134
|
const enableClickToEdit = enableClickToEditAttr
|
|
126
|
-
? JSON.parse(enableClickToEditAttr)
|
|
135
|
+
? (JSON.parse(enableClickToEditAttr) as boolean | ClickToEditOptions)
|
|
127
136
|
: undefined;
|
|
128
137
|
const stripStega = stripStegaAttr === 'true';
|
|
138
|
+
const hueAttr = this.getAttribute('data-hue');
|
|
139
|
+
const hue = hueAttr !== null ? Number(hueAttr) : undefined;
|
|
129
140
|
|
|
130
141
|
// Initialize the content-link controller
|
|
131
142
|
this.controller = createController({
|
|
@@ -137,6 +148,8 @@ const enableClickToEditValue =
|
|
|
137
148
|
},
|
|
138
149
|
// Strip stega encoding if requested
|
|
139
150
|
stripStega: stripStega,
|
|
151
|
+
// Accent color hue for overlays
|
|
152
|
+
hue: hue,
|
|
140
153
|
});
|
|
141
154
|
|
|
142
155
|
// Notify the controller of the current path
|
|
@@ -174,22 +187,19 @@ const enableClickToEditValue =
|
|
|
174
187
|
// Enable click-to-edit mode if requested via prop.
|
|
175
188
|
// Never enable when running inside an iframe (e.g. the DatoCMS Web Previews plugin),
|
|
176
189
|
// because the plugin already manages its own editing experience.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
+
if (
|
|
191
|
+
enableClickToEdit &&
|
|
192
|
+
window.self === window.top &&
|
|
193
|
+
(enableClickToEdit === true ||
|
|
194
|
+
!enableClickToEdit.hoverOnly ||
|
|
195
|
+
window.matchMedia('(hover: hover)').matches)
|
|
196
|
+
) {
|
|
197
|
+
this.controller.enableClickToEdit(
|
|
198
|
+
enableClickToEdit === true
|
|
199
|
+
? undefined
|
|
200
|
+
: { scrollToNearestTarget: enableClickToEdit.scrollToNearestTarget ?? false },
|
|
201
|
+
);
|
|
190
202
|
}
|
|
191
|
-
// Otherwise, click-to-edit overlays are not enabled by default.
|
|
192
|
-
// Users can press and hold Alt/Option key to temporarily enable click-to-edit mode
|
|
193
203
|
}
|
|
194
204
|
|
|
195
205
|
private cleanup() {
|
|
@@ -32,18 +32,26 @@ Visual Editing transforms how editors interact with your content by letting them
|
|
|
32
32
|
- [Usage](#usage)
|
|
33
33
|
- [Props](#props)
|
|
34
34
|
- [`enableClickToEdit` options](#enableclicktoedit-options)
|
|
35
|
-
- [
|
|
36
|
-
- [
|
|
37
|
-
|
|
38
|
-
- [
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
- [Data attributes reference](#data-attributes-reference)
|
|
36
|
+
- [Developer-specified attributes](#developer-specified-attributes)
|
|
37
|
+
- [`data-datocms-content-link-url`](#data-datocms-content-link-url)
|
|
38
|
+
- [`data-datocms-content-link-source`](#data-datocms-content-link-source)
|
|
39
|
+
- [`data-datocms-content-link-group`](#data-datocms-content-link-group)
|
|
40
|
+
- [`data-datocms-content-link-boundary`](#data-datocms-content-link-boundary)
|
|
41
|
+
- [Library-managed attributes](#library-managed-attributes)
|
|
42
|
+
- [`data-datocms-contains-stega`](#data-datocms-contains-stega)
|
|
43
|
+
- [`data-datocms-auto-content-link-url`](#data-datocms-auto-content-link-url)
|
|
44
|
+
- [How group and boundary resolution works](#how-group-and-boundary-resolution-works)
|
|
45
|
+
- [Structured Text fields](#structured-text-fields)
|
|
46
|
+
- [Rule 1: Always wrap the Structured Text component in a group](#rule-1-always-wrap-the-structured-text-component-in-a-group)
|
|
47
|
+
- [Rule 2: Wrap embedded blocks, inline blocks, and inline records in a boundary](#rule-2-wrap-embedded-blocks-inline-blocks-and-inline-records-in-a-boundary)
|
|
41
48
|
- [Low-level utilities](#low-level-utilities)
|
|
42
49
|
- [`stripStega()` works with any data type](#stripstega-works-with-any-data-type)
|
|
43
50
|
- [Troubleshooting](#troubleshooting)
|
|
44
51
|
- [Click-to-edit overlays not appearing](#click-to-edit-overlays-not-appearing)
|
|
45
52
|
- [Navigation not syncing in Web Previews plugin](#navigation-not-syncing-in-web-previews-plugin)
|
|
46
53
|
- [Content inside StructuredText not clickable](#content-inside-structuredtext-not-clickable)
|
|
54
|
+
- [Layout issues caused by stega encoding](#layout-issues-caused-by-stega-encoding)
|
|
47
55
|
|
|
48
56
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
|
49
57
|
|
|
@@ -145,6 +153,7 @@ You get the full Visual Editing experience regardless of your routing setup.
|
|
|
145
153
|
| ------------------- | --------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
146
154
|
| `enableClickToEdit` | `boolean \| { scrollToNearestTarget?: boolean; hoverOnly?: boolean }` | - | Enable click-to-edit overlays on mount. Use `true` for immediate activation, or pass options object (see below) |
|
|
147
155
|
| `stripStega` | `boolean` | `false` | Strip stega-encoded invisible characters from text content. When `true`, encoding is permanently removed (prevents controller recreation) |
|
|
156
|
+
| `hue` | `number` | `17` | Hue (0–359) of the overlay accent color. Default is the DatoCMS hue (`17`). Use this to match your brand or project colors |
|
|
148
157
|
|
|
149
158
|
### `enableClickToEdit` options
|
|
150
159
|
|
|
@@ -173,111 +182,289 @@ When passing an options object to `enableClickToEdit`, the following properties
|
|
|
173
182
|
|
|
174
183
|
The `hoverOnly` option is particularly useful for websites that receive traffic from both desktop and mobile users. On touch devices, the click-to-edit overlays can interfere with normal scrolling and tapping behavior. By setting `hoverOnly: true`, overlays will only appear automatically on devices with a mouse or trackpad, while touch device users can still access click-to-edit mode by pressing and holding the Alt/Option key.
|
|
175
184
|
|
|
176
|
-
##
|
|
185
|
+
## Data attributes reference
|
|
177
186
|
|
|
178
|
-
|
|
187
|
+
This library uses several `data-datocms-*` attributes. Some are **developer-specified** (you add them to your markup), and some are **library-managed** (added automatically during DOM stamping). Here's a complete reference.
|
|
179
188
|
|
|
180
|
-
###
|
|
189
|
+
### Developer-specified attributes
|
|
181
190
|
|
|
182
|
-
|
|
191
|
+
These attributes are added by you in your templates/components to control how editable regions behave.
|
|
183
192
|
|
|
184
|
-
|
|
193
|
+
#### `data-datocms-content-link-url`
|
|
194
|
+
|
|
195
|
+
Manually marks an element as editable with an explicit edit URL. Use this for non-text fields (booleans, numbers, dates, JSON) that cannot contain stega encoding. The recommended approach is to use the `_editingUrl` field available on all records:
|
|
196
|
+
|
|
197
|
+
```graphql
|
|
198
|
+
query {
|
|
199
|
+
product {
|
|
200
|
+
id
|
|
201
|
+
price
|
|
202
|
+
isActive
|
|
203
|
+
_editingUrl
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
185
207
|
|
|
186
208
|
```astro
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
209
|
+
<span data-datocms-content-link-url={product._editingUrl}>
|
|
210
|
+
${product.price}
|
|
211
|
+
</span>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
#### `data-datocms-content-link-source`
|
|
215
|
+
|
|
216
|
+
Attaches stega-encoded metadata without the need to render it as content. Useful for structural elements that cannot contain text (like `<video>`, `<audio>`, `<iframe>`, etc.) or when stega encoding in visible text would be problematic:
|
|
217
|
+
|
|
218
|
+
```astro
|
|
219
|
+
<div data-datocms-content-link-source={video.alt}>
|
|
220
|
+
<video src={video.url} poster={video.posterImage.url} controls></video>
|
|
221
|
+
</div>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
The value must be a stega-encoded string (any text field from the API will work). The library decodes the stega metadata from the attribute value and makes the element clickable to edit.
|
|
225
|
+
|
|
226
|
+
#### `data-datocms-content-link-group`
|
|
227
|
+
|
|
228
|
+
Expands the clickable area to a parent element. When the library encounters stega-encoded content, by default it makes the immediate parent of the text node clickable to edit. Adding this attribute to an ancestor makes that ancestor the clickable target instead:
|
|
229
|
+
|
|
230
|
+
```html
|
|
231
|
+
<article data-datocms-content-link-group>
|
|
232
|
+
<!-- product.title contains stega encoding -->
|
|
233
|
+
<h2>{product.title}</h2>
|
|
234
|
+
<p>${product.price}</p>
|
|
235
|
+
</article>
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Here, clicking anywhere in the `<article>` opens the editor, rather than requiring users to click precisely on the `<h2>`.
|
|
239
|
+
|
|
240
|
+
**Important:** A group should contain only one stega-encoded source. If multiple stega strings resolve to the same group, the library logs a collision warning and only the last URL wins.
|
|
241
|
+
|
|
242
|
+
#### `data-datocms-content-link-boundary`
|
|
243
|
+
|
|
244
|
+
Stops the upward DOM traversal that looks for a `data-datocms-content-link-group`, making the element where stega was found the clickable target instead. This creates an independent editable region that won't merge into a parent group (see [How group and boundary resolution works](#how-group-and-boundary-resolution-works) below for details):
|
|
190
245
|
|
|
191
|
-
|
|
246
|
+
```html
|
|
192
247
|
<div data-datocms-content-link-group>
|
|
193
|
-
|
|
248
|
+
<!-- page.title contains stega encoding → resolves to URL A -->
|
|
249
|
+
<h1>{page.title}</h1>
|
|
250
|
+
<section data-datocms-content-link-boundary>
|
|
251
|
+
<!-- page.author contains stega encoding → resolves to URL B -->
|
|
252
|
+
<span>{page.author}</span>
|
|
253
|
+
</section>
|
|
194
254
|
</div>
|
|
195
255
|
```
|
|
196
256
|
|
|
197
|
-
|
|
257
|
+
Without the boundary, clicking `page.author` would open URL A (the outer group). With the boundary, the `<span>` becomes the clickable target opening URL B.
|
|
198
258
|
|
|
199
|
-
|
|
259
|
+
The boundary can also be placed directly on the element that contains the stega text:
|
|
200
260
|
|
|
201
|
-
|
|
261
|
+
```html
|
|
262
|
+
<div data-datocms-content-link-group>
|
|
263
|
+
<!-- page.title contains stega encoding → resolves to URL A -->
|
|
264
|
+
<h1>{page.title}</h1>
|
|
265
|
+
<!-- page.author contains stega encoding → resolves to URL B -->
|
|
266
|
+
<span data-datocms-content-link-boundary>{page.author}</span>
|
|
267
|
+
</div>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Here, the `<span>` has the boundary and directly contains the stega text, so the `<span>` itself becomes the clickable target (since the starting element and the boundary element are the same).
|
|
271
|
+
|
|
272
|
+
### Library-managed attributes
|
|
273
|
+
|
|
274
|
+
These attributes are added automatically by the library during DOM stamping. You do not need to add them yourself, but you can target them in CSS or JavaScript.
|
|
275
|
+
|
|
276
|
+
#### `data-datocms-contains-stega`
|
|
277
|
+
|
|
278
|
+
Added to elements whose text content contains stega-encoded invisible characters. This attribute is only present when `stripStega` is `false` (the default), since with `stripStega: true` the characters are removed entirely. Useful for CSS workarounds — the zero-width characters can sometimes cause unexpected letter-spacing or text overflow:
|
|
279
|
+
|
|
280
|
+
```css
|
|
281
|
+
[data-datocms-contains-stega] {
|
|
282
|
+
letter-spacing: 0 !important;
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### `data-datocms-auto-content-link-url`
|
|
287
|
+
|
|
288
|
+
Added automatically to elements that the library has identified as editable targets (through stega decoding and group/boundary resolution). Contains the resolved edit URL.
|
|
289
|
+
|
|
290
|
+
This is the automatic counterpart to the developer-specified `data-datocms-content-link-url`. The library adds `data-datocms-auto-content-link-url` wherever it can extract an edit URL from stega encoding, while `data-datocms-content-link-url` is needed for non-text fields (booleans, numbers, dates, etc.) where stega encoding cannot be embedded. Both attributes are used by the click-to-edit overlay system to determine which elements are clickable and where they link to.
|
|
291
|
+
|
|
292
|
+
## How group and boundary resolution works
|
|
293
|
+
|
|
294
|
+
When the library encounters stega-encoded content inside an element, it walks up the DOM tree from that element:
|
|
295
|
+
|
|
296
|
+
1. If it finds a `data-datocms-content-link-group`, it stops and stamps **that** element as the clickable target.
|
|
297
|
+
2. If it finds a `data-datocms-content-link-boundary`, it stops and stamps the **starting element** as the clickable target — further traversal is prevented.
|
|
298
|
+
3. If it reaches the root without finding either, it stamps the **starting element**.
|
|
299
|
+
|
|
300
|
+
Here are some concrete examples to illustrate:
|
|
301
|
+
|
|
302
|
+
**Example 1: Nested groups**
|
|
303
|
+
|
|
304
|
+
```html
|
|
305
|
+
<div data-datocms-content-link-group>
|
|
306
|
+
<!-- page.title contains stega encoding → resolves to URL A -->
|
|
307
|
+
<h1>{page.title}</h1>
|
|
308
|
+
<div data-datocms-content-link-group>
|
|
309
|
+
<!-- page.subtitle contains stega encoding → resolves to URL B -->
|
|
310
|
+
<p>{page.subtitle}</p>
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
- **`page.title`**: walks up from `<h1>`, finds the outer group → the **outer `<div>`** becomes clickable (opens URL A).
|
|
316
|
+
- **`page.subtitle`**: walks up from `<p>`, finds the inner group first → the **inner `<div>`** becomes clickable (opens URL B). The outer group is never reached.
|
|
317
|
+
|
|
318
|
+
Each nested group creates an independent clickable region. The innermost group always wins for its own content.
|
|
319
|
+
|
|
320
|
+
**Example 2: Boundary preventing group propagation**
|
|
321
|
+
|
|
322
|
+
```html
|
|
323
|
+
<div data-datocms-content-link-group>
|
|
324
|
+
<!-- page.title contains stega encoding → resolves to URL A -->
|
|
325
|
+
<h1>{page.title}</h1>
|
|
326
|
+
<section data-datocms-content-link-boundary>
|
|
327
|
+
<!-- page.author contains stega encoding → resolves to URL B -->
|
|
328
|
+
<span>{page.author}</span>
|
|
329
|
+
</section>
|
|
330
|
+
</div>
|
|
331
|
+
```
|
|
202
332
|
|
|
203
|
-
-
|
|
204
|
-
-
|
|
333
|
+
- **`page.title`**: walks up from `<h1>`, finds the outer group → the **outer `<div>`** becomes clickable (opens URL A).
|
|
334
|
+
- **`page.author`**: walks up from `<span>`, hits the `<section>` boundary → traversal stops, the **`<span>`** itself becomes clickable (opens URL B). The outer group is not reached.
|
|
205
335
|
|
|
206
|
-
|
|
336
|
+
**Example 3: Boundary inside a group**
|
|
337
|
+
|
|
338
|
+
```html
|
|
339
|
+
<div data-datocms-content-link-group>
|
|
340
|
+
<!-- page.description contains stega encoding → resolves to URL A -->
|
|
341
|
+
<p>{page.description}</p>
|
|
342
|
+
<div data-datocms-content-link-boundary>
|
|
343
|
+
<!-- page.footnote contains stega encoding → resolves to URL B -->
|
|
344
|
+
<p>{page.footnote}</p>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
- **`page.description`**: walks up from `<p>`, finds the outer group → the **outer `<div>`** becomes clickable (opens URL A).
|
|
350
|
+
- **`page.footnote`**: walks up from `<p>`, hits the boundary → traversal stops, the **`<p>`** itself becomes clickable (opens URL B). The outer group is not reached.
|
|
351
|
+
|
|
352
|
+
**Example 4: Multiple stega strings without groups (collision warning)**
|
|
353
|
+
|
|
354
|
+
```html
|
|
355
|
+
<p>
|
|
356
|
+
<!-- Both product.name and product.tagline contain stega encoding -->
|
|
357
|
+
{product.name} {product.tagline}
|
|
358
|
+
</p>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Both stega-encoded strings resolve to the same `<p>` element. The library logs a console warning and the last URL wins. To fix this, wrap each piece of content in its own element:
|
|
362
|
+
|
|
363
|
+
```html
|
|
364
|
+
<p>
|
|
365
|
+
<span>{product.name}</span>
|
|
366
|
+
<span>{product.tagline}</span>
|
|
367
|
+
</p>
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Structured Text fields
|
|
371
|
+
|
|
372
|
+
Structured Text fields require special attention because of how stega encoding works within them:
|
|
373
|
+
|
|
374
|
+
- The DatoCMS API encodes stega information inside a single `<span>` within the structured text output. Without any configuration, only that small span would be clickable.
|
|
375
|
+
- Structured Text fields can contain **embedded blocks** and **inline records**, each with their own editing URL that should open a different record in the editor.
|
|
376
|
+
|
|
377
|
+
Here are the rules to follow:
|
|
378
|
+
|
|
379
|
+
### Rule 1: Always wrap the Structured Text component in a group
|
|
380
|
+
|
|
381
|
+
This makes the entire structured text area clickable, instead of just the tiny stega-encoded span:
|
|
207
382
|
|
|
208
383
|
```astro
|
|
209
384
|
---
|
|
210
385
|
import { StructuredText } from '@datocms/astro/StructuredText';
|
|
211
|
-
import BlogPost from './BlogPost.astro';
|
|
212
386
|
---
|
|
213
387
|
|
|
214
388
|
<div data-datocms-content-link-group>
|
|
215
|
-
<StructuredText
|
|
216
|
-
data={content.structuredTextField}
|
|
217
|
-
components={{
|
|
218
|
-
renderBlock: ({ record }) => {
|
|
219
|
-
// This boundary prevents the block from using the parent group
|
|
220
|
-
return (
|
|
221
|
-
<div data-datocms-content-link-boundary>
|
|
222
|
-
<BlogPost data={record} />
|
|
223
|
-
</div>
|
|
224
|
-
);
|
|
225
|
-
},
|
|
226
|
-
}}
|
|
227
|
-
/>
|
|
389
|
+
<StructuredText data={page.content} />
|
|
228
390
|
</div>
|
|
229
391
|
```
|
|
230
392
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
- Clicking the main text opens the Structured Text field editor
|
|
234
|
-
- Clicking an embedded block opens that specific block's editor
|
|
393
|
+
### Rule 2: Wrap embedded blocks, inline blocks, and inline records in a boundary
|
|
235
394
|
|
|
236
|
-
|
|
395
|
+
Embedded blocks, inline blocks, and inline records have their own edit URL (pointing to the block/record). Without a boundary, clicking them would bubble up to the parent group and open the structured text field editor instead. Add `data-datocms-content-link-boundary` to prevent them from merging into the parent group.
|
|
237
396
|
|
|
238
|
-
|
|
397
|
+
**Note:** Record links (`renderLinkToRecord`) don't need a boundary. They are typically just `<a>` tags wrapping text that already belongs to the surrounding structured text. Since they don't introduce a separate editing target, there's no URL collision and no reason to isolate them from the parent group — clicking a record link's text should open the structured text field editor, just like clicking any other text in the field.
|
|
239
398
|
|
|
240
|
-
|
|
399
|
+
Add `data-datocms-content-link-boundary` to the root element of each component that renders a block, inline block, or inline record. For example, given a `Cta` block component:
|
|
241
400
|
|
|
242
|
-
|
|
401
|
+
```astro
|
|
402
|
+
---
|
|
403
|
+
// src/components/Cta.astro
|
|
404
|
+
const { block } = Astro.props;
|
|
405
|
+
---
|
|
243
406
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
id
|
|
248
|
-
price
|
|
249
|
-
isActive
|
|
250
|
-
inStock
|
|
251
|
-
_editingUrl
|
|
252
|
-
}
|
|
253
|
-
}
|
|
407
|
+
<div data-datocms-content-link-boundary>
|
|
408
|
+
<a href={block.url}>{block.label}</a>
|
|
409
|
+
</div>
|
|
254
410
|
```
|
|
255
411
|
|
|
412
|
+
For inline blocks, use a `<span>` instead of a `<div>` since they appear within inline content:
|
|
413
|
+
|
|
256
414
|
```astro
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
</div>
|
|
415
|
+
---
|
|
416
|
+
// src/components/NewsletterSignup.astro
|
|
417
|
+
const { block } = Astro.props;
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
<span data-datocms-content-link-boundary>
|
|
421
|
+
<input type="email" placeholder={block.placeholder} />
|
|
422
|
+
</span>
|
|
266
423
|
```
|
|
267
424
|
|
|
268
|
-
|
|
425
|
+
Same for inline records:
|
|
426
|
+
|
|
427
|
+
```astro
|
|
428
|
+
---
|
|
429
|
+
// src/components/InlineTeamMember.astro
|
|
430
|
+
const { record } = Astro.props;
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
<span data-datocms-content-link-boundary>
|
|
434
|
+
<a href={`/team/${record.slug}`}>{record.name}</a>
|
|
435
|
+
</span>
|
|
436
|
+
```
|
|
269
437
|
|
|
270
|
-
|
|
438
|
+
Then use these components directly in your structured text rendering:
|
|
271
439
|
|
|
272
440
|
```astro
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
441
|
+
---
|
|
442
|
+
import { StructuredText } from '@datocms/astro/StructuredText';
|
|
443
|
+
import Cta from '~/components/Cta.astro';
|
|
444
|
+
import NewsletterSignup from '~/components/NewsletterSignup.astro';
|
|
445
|
+
import InlineTeamMember from '~/components/InlineTeamMember.astro';
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
<div data-datocms-content-link-group>
|
|
449
|
+
<StructuredText
|
|
450
|
+
data={page.content}
|
|
451
|
+
blockComponents={{
|
|
452
|
+
CtaRecord: Cta,
|
|
453
|
+
}}
|
|
454
|
+
inlineBlockComponents={{
|
|
455
|
+
NewsletterSignupRecord: NewsletterSignup,
|
|
456
|
+
}}
|
|
457
|
+
inlineRecordComponents={{
|
|
458
|
+
TeamMemberRecord: InlineTeamMember,
|
|
459
|
+
}}
|
|
460
|
+
/>
|
|
461
|
+
</div>
|
|
278
462
|
```
|
|
279
463
|
|
|
280
|
-
|
|
464
|
+
With this setup:
|
|
465
|
+
|
|
466
|
+
- Clicking the main text (paragraphs, headings, lists) opens the **structured text field editor**
|
|
467
|
+
- Clicking an embedded block, inline block, or inline record opens **that block/record's editor**
|
|
281
468
|
|
|
282
469
|
## Low-level utilities
|
|
283
470
|
|
|
@@ -370,15 +557,17 @@ If navigation isn't syncing between your preview and the DatoCMS interface:
|
|
|
370
557
|
If structured text content isn't opening the editor:
|
|
371
558
|
|
|
372
559
|
1. **Wrap with `data-datocms-content-link-group`:**
|
|
560
|
+
See [Rule 1: Always wrap the Structured Text component in a group](#rule-1-always-wrap-the-structured-text-component-in-a-group).
|
|
373
561
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
<StructuredText data={content.body} />
|
|
377
|
-
</div>
|
|
378
|
-
```
|
|
562
|
+
2. **Add boundaries for embedded blocks and inline records:**
|
|
563
|
+
See [Rule 2: Wrap embedded blocks and inline records in a boundary](#rule-2-wrap-embedded-blocks-and-inline-records-in-a-boundary).
|
|
379
564
|
|
|
380
|
-
|
|
565
|
+
3. **Check for `data-datocms-content-link-boundary` blocking clicks:**
|
|
381
566
|
Make sure you haven't accidentally added a boundary attribute that's preventing the click from reaching the group.
|
|
382
567
|
|
|
383
|
-
|
|
568
|
+
4. **Verify stega encoding is present:**
|
|
384
569
|
Use the browser inspector to check if the structured text HTML contains zero-width characters (stega encoding). If not, check your query options.
|
|
570
|
+
|
|
571
|
+
### Layout issues caused by stega encoding
|
|
572
|
+
|
|
573
|
+
The invisible zero-width characters can cause unexpected letter-spacing or text breaking out of containers. To fix this, either use `stripStega: true`, or use CSS: `[data-datocms-contains-stega] { letter-spacing: 0 !important; }`. This attribute is automatically added to elements with stega-encoded content when `stripStega: false` (the default). See [`data-datocms-contains-stega`](#data-datocms-contains-stega) for more details.
|