@lowdefy/blocks-basic 0.0.0-experimental-20260927140906 → 0.0.0-experimental-20260928062533

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.
@@ -76,10 +76,12 @@
76
76
  },
77
77
  href: {
78
78
  type: 'string',
79
+ urlKind: 'href',
79
80
  description: 'The href to link to when the anchor link is clicked.'
80
81
  },
81
82
  url: {
82
83
  type: 'string',
84
+ urlKind: 'url',
83
85
  description: 'External url to link to when the anchor link is clicked.'
84
86
  },
85
87
  rel: {
@@ -18,8 +18,10 @@ import { withBlockDefaults, HtmlComponent } from '@lowdefy/block-utils';
18
18
  // (data-event="onEditClick" → events.onEditClick), so every target in the
19
19
  // markup has its own action chain. Its other data-* attributes are the event
20
20
  // object. HtmlComponent finds the target, including inside data-popover
21
- // content, and prevents the element's default action.
21
+ // content, and prevents the element's default action. Only the events listed
22
+ // in properties.dataEvents fire, since the markup can hold data.
22
23
  const ClickableHtml = ({ blockId, classNames, events, properties, methods, styles })=>/*#__PURE__*/ React.createElement(HtmlComponent, {
24
+ dataEvents: properties.dataEvents,
23
25
  div: true,
24
26
  events: events,
25
27
  html: properties.html,
@@ -16,6 +16,9 @@
16
16
  - id: buttons_basic
17
17
  type: ClickableHtml
18
18
  properties:
19
+ dataEvents:
20
+ - onSmallClick
21
+ - onLargeClick
19
22
  html: '<p>Pick a size: <button data-event="onSmallClick">Small</button> <button data-event="onLargeClick">Large</button></p>'
20
23
  events:
21
24
  onSmallClick:
@@ -44,6 +47,8 @@
44
47
  - id: list_rows
45
48
  type: ClickableHtml
46
49
  properties:
50
+ dataEvents:
51
+ - onRowClick
47
52
  html: '<ul style="list-style: none; padding: 0; margin: 0;"><li data-event="onRowClick" data-record-id="rec_1" style="padding: 8px; cursor: pointer; border-bottom: 1px solid var(--ant-color-border);">First record</li><li data-event="onRowClick" data-record-id="rec_2" style="padding: 8px; cursor: pointer; border-bottom: 1px solid var(--ant-color-border);">Second record</li><li data-event="onRowClick" data-record-id="rec_3" style="padding: 8px; cursor: pointer;">Third record</li></ul>'
48
53
  events:
49
54
  onRowClick:
@@ -69,6 +74,8 @@
69
74
  - id: mixed_content
70
75
  type: ClickableHtml
71
76
  properties:
77
+ dataEvents:
78
+ - onCommentClick
72
79
  html: '<p>This <a href="https://lowdefy.com" target="_blank">link</a> navigates normally, but <a href="#" data-event="onCommentClick" data-thread-id="t_9">this one</a> fires onCommentClick instead.</p>'
73
80
  events:
74
81
  onCommentClick:
@@ -99,6 +106,9 @@
99
106
  border: 1px solid var(--ant-color-border)
100
107
  background: var(--ant-color-bg-container)
101
108
  properties:
109
+ dataEvents:
110
+ - onEditClick
111
+ - onRemoveClick
102
112
  html: '<h4 style="margin: 0 0 8px 0;">Card</h4><p style="margin: 0 0 8px 0; color: var(--ant-color-text-secondary);">Yours to change or remove until tomorrow.</p><button data-event="onEditClick">Edit</button> <button data-event="onRemoveClick">Remove</button>'
103
113
  events:
104
114
  onEditClick:
@@ -117,6 +127,11 @@
117
127
  - id: row_actions
118
128
  type: ClickableHtml
119
129
  properties:
130
+ dataEvents:
131
+ - onEdit
132
+ - onDuplicate
133
+ - name: onDelete
134
+ confirm: Delete invoice INV-042?
120
135
  html: '<p>Invoice INV-042 <i data-icon="edit" data-event="onEdit" data-id="42" data-tooltip="Edit" style="cursor: pointer"></i> <span data-popover="more" style="cursor: pointer"><i data-icon="more-vertical"></i></span></p><div data-popover-content="more" hidden><p data-event="onDuplicate" data-id="42" style="cursor: pointer; margin: 0 0 8px"><i data-icon="copy"></i> Duplicate</p><p data-event="onDelete" data-id="42" style="cursor: pointer; margin: 0; color: var(--ant-color-error)"><i data-icon="delete"></i> Delete</p></div>'
121
136
  events:
122
137
  onEdit:
@@ -150,6 +165,10 @@
150
165
  - id: confirm_actions
151
166
  type: ClickableHtml
152
167
  properties:
168
+ dataEvents:
169
+ - name: onDelete
170
+ confirm: true
171
+ - onArchive
153
172
  html: '<p>INV-2026-0042 <i data-icon="delete" data-event="onDelete" data-id="42" data-tooltip="Delete" data-confirm="Delete invoice INV-2026-0042?" style="cursor: pointer; color: var(--ant-color-error)"></i> <span data-event="onArchive" data-id="42" data-confirm style="cursor: pointer; color: var(--ant-color-primary)">Archive</span></p>'
154
173
  events:
155
174
  onDelete:
@@ -25,9 +25,14 @@
25
25
  message: 'Content is sanitised with DOMPurify, so <style>, <script> and inline event handlers are removed before render. Clicks are wired through data-event attributes instead of inline handlers.',
26
26
  see: 'display-blocks/clickablehtml'
27
27
  },
28
+ {
29
+ id: 'clickable-html-data-events',
30
+ message: 'List every event the HTML fires in properties.dataEvents: a data-event whose name is not listed does nothing. HTML built from user or request data must escape that data, so the data cannot add data-event elements of its own: _nunjucks escapes {{ values }} unless they are marked safe, while _string.concat and _js do not escape.',
31
+ see: 'display-blocks/clickablehtml'
32
+ },
28
33
  {
29
34
  id: 'clickable-html-confirm-destructive',
30
- message: 'Add data-confirm="Delete this row?" to a data-event element whose event deletes or changes data, so a stray click cannot fire it. Only OK fires the event.',
35
+ message: 'Give an event that deletes or changes data confirm in dataEvents ({ name: onDelete, confirm: "Delete this row?" }), so every click on it asks first and only OK fires it. data-confirm="Delete this row?" on the element asks too, but only where the markup says so.',
31
36
  see: 'concepts/html-attributes'
32
37
  }
33
38
  ],
@@ -41,9 +46,47 @@
41
46
  type: 'object',
42
47
  additionalProperties: false,
43
48
  properties: {
49
+ dataEvents: {
50
+ type: 'array',
51
+ description: 'The events the HTML may fire. A data-event whose name is not listed does nothing, and a click on it logs a console warning; with no dataEvents the HTML fires no events. List an event by name, or as { name, confirm } to make every click on it ask first.',
52
+ items: {
53
+ oneOf: [
54
+ {
55
+ type: 'string',
56
+ description: 'An event name, as written in data-event.'
57
+ },
58
+ {
59
+ type: 'object',
60
+ additionalProperties: false,
61
+ required: [
62
+ 'name'
63
+ ],
64
+ properties: {
65
+ name: {
66
+ type: 'string',
67
+ description: 'An event name, as written in data-event.'
68
+ },
69
+ confirm: {
70
+ oneOf: [
71
+ {
72
+ type: 'boolean',
73
+ description: 'Ask before the event fires, whatever the HTML says, with the element\'s data-confirm message or "Are you sure?".'
74
+ },
75
+ {
76
+ type: 'string',
77
+ minLength: 1,
78
+ description: 'Ask before the event fires, whatever the HTML says, with this message.'
79
+ }
80
+ ]
81
+ }
82
+ }
83
+ }
84
+ ]
85
+ }
86
+ },
44
87
  html: {
45
88
  type: 'string',
46
- description: 'Content to be rendered as Html. An element with a data-event attribute fires the event it names when clicked (data-event="onEditClick" fires events.onEditClick), and its default browser action is prevented. The event object holds the element\'s other data-* attributes with snake_case keys, so data-event="onEditClick" data-record-id="42" gives { record_id: "42" }. Targets that are not links or buttons become keyboard focusable, and Enter or Space clicks them. A data-event inside popover content fires too, then closes the popover. A link with data-event fires the event and does not navigate. data-confirm="Delete this row?" on a data-event element asks first: only OK fires the event (bare data-confirm asks "Are you sure?"). All Html block attributes work too: data-icon, data-tooltip, data-popover, data-page-id links, data-new-tab, data-tag, data-status, data-time, data-format, data-avatar, data-copy, data-truncate and data-tone. See the HTML attributes docs page.',
89
+ description: 'Content to be rendered as Html. An element with a data-event attribute fires the event it names when clicked (data-event="onEditClick" fires events.onEditClick), if dataEvents lists it, and its default browser action is prevented. The event object holds the element\'s other data-* attributes with snake_case keys, so data-event="onEditClick" data-record-id="42" gives { record_id: "42" }. Targets that are not links or buttons become keyboard focusable, and Enter or Space clicks them. A data-event inside popover content fires too, then closes the popover. A link with data-event fires the event and does not navigate. data-confirm="Delete this row?" on a data-event element asks first: only OK fires the event (bare data-confirm asks "Are you sure?"). Escape user and request data in the HTML, so it cannot add data-event elements of its own. All Html block attributes work too: data-icon, data-tooltip, data-popover, data-page-id links, data-new-tab, data-tag, data-status, data-time, data-format, data-avatar, data-copy, data-truncate and data-tone. See the HTML attributes docs page.',
47
90
  docs: {
48
91
  displayType: 'text-area'
49
92
  }
@@ -18,6 +18,9 @@ blocks:
18
18
  - id: clickable_basic
19
19
  type: ClickableHtml
20
20
  properties:
21
+ dataEvents:
22
+ - onGreetClick
23
+ - onWaveClick
21
24
  html: '<p>Hello <button data-event="onGreetClick" data-user-id="u_1">World</button> <button data-event="onWaveClick" data-user-id="u_2">Wave</button></p>'
22
25
  events:
23
26
  onGreetClick:
@@ -71,6 +74,8 @@ blocks:
71
74
  - id: clickable_confirm
72
75
  type: ClickableHtml
73
76
  properties:
77
+ dataEvents:
78
+ - onDelete
74
79
  html: '<button id="confirm_delete" data-event="onDelete" data-row-id="7" data-confirm="Delete row 7?">Delete</button> <button id="proto_event" data-event="__proto__">Proto</button>'
75
80
  events:
76
81
  onDelete:
@@ -95,3 +100,89 @@ blocks:
95
100
  key: delete_count
96
101
  default: 0
97
102
  - '</span>'
103
+
104
+ - id: clickable_allowlist
105
+ type: ClickableHtml
106
+ properties:
107
+ dataEvents:
108
+ - onListedClick
109
+ html: '<span id="listed_target" data-event="onListedClick">Listed</span> <span id="unlisted_target" data-event="onUnlistedClick">Unlisted</span>'
110
+ events:
111
+ onListedClick:
112
+ - id: set_listed
113
+ type: SetState
114
+ params:
115
+ allowlist_fired: listed
116
+ onUnlistedClick:
117
+ - id: set_unlisted
118
+ type: SetState
119
+ params:
120
+ allowlist_fired: unlisted
121
+
122
+ - id: clickable_declared_confirm
123
+ type: ClickableHtml
124
+ properties:
125
+ dataEvents:
126
+ - name: onRemove
127
+ confirm: Remove row 3?
128
+ html: '<button id="declared_remove" data-event="onRemove" data-row-id="3">Remove</button>'
129
+ events:
130
+ onRemove:
131
+ - id: set_removed
132
+ type: SetState
133
+ params:
134
+ removed_row:
135
+ _event: row_id
136
+
137
+ - id: clickable_refresh
138
+ type: ClickableHtml
139
+ properties:
140
+ dataEvents:
141
+ - onRefreshSoon
142
+ - onRefreshDelete
143
+ html:
144
+ _string.concat:
145
+ - '<button id="refresh_soon" data-event="onRefreshSoon">Refresh soon</button><p id="refresh_count">Refreshed '
146
+ - _state:
147
+ key: refresh_count
148
+ default: 0
149
+ - '</p><button id="refresh_delete" data-event="onRefreshDelete" data-id="5" data-confirm="Delete 5?">Delete</button> <span id="refresh_more" data-popover="more">More</span><div data-popover-content="more" hidden><span class="refresh-menu">Menu after '
150
+ - _state:
151
+ key: refresh_count
152
+ default: 0
153
+ - '</span></div>'
154
+ events:
155
+ onRefreshSoon:
156
+ - id: wait
157
+ type: Wait
158
+ params:
159
+ ms: 1000
160
+ - id: refresh
161
+ type: SetState
162
+ params:
163
+ refresh_count: 1
164
+ onRefreshDelete:
165
+ - id: set_refresh_deleted
166
+ type: SetState
167
+ params:
168
+ refresh_deleted:
169
+ _event: id
170
+
171
+ - id: clickable_events_result
172
+ type: Html
173
+ properties:
174
+ html:
175
+ _string.concat:
176
+ - '<span id="allowlist_fired">'
177
+ - _state:
178
+ key: allowlist_fired
179
+ default: ''
180
+ - '</span><span id="removed_row">'
181
+ - _state:
182
+ key: removed_row
183
+ default: ''
184
+ - '</span><span id="refresh_deleted">'
185
+ - _state:
186
+ key: refresh_deleted
187
+ default: ''
188
+ - '</span>'
@@ -16,6 +16,8 @@
16
16
  - id: properties.html
17
17
  type: ClickableHtml
18
18
  properties:
19
+ dataEvents:
20
+ - onGoClick
19
21
  html: |
20
22
  <p>Plain markup <button data-event="onGoClick" data-item-id="1">Go</button></p>
21
23
  - id: properties.html-no-events
@@ -26,6 +28,8 @@
26
28
  - id: properties.html-bad-code
27
29
  type: ClickableHtml
28
30
  properties:
31
+ dataEvents:
32
+ - onOkClick
29
33
  html: |
30
34
  <button data-event="onOkClick" onclick="alert('inline handler stripped')">Ok</button>
31
35
  <script>alert('script tag');</script>
@@ -33,3 +37,12 @@
33
37
  type: ClickableHtml
34
38
  properties:
35
39
  html: 0
40
+ - id: properties.dataEvents
41
+ type: ClickableHtml
42
+ properties:
43
+ dataEvents:
44
+ - onEditClick
45
+ - name: onDeleteClick
46
+ confirm: Delete this row?
47
+ html: |
48
+ <p><button data-event="onEditClick">Edit</button> <button data-event="onDeleteClick">Delete</button></p>
@@ -65,3 +65,4 @@ blocks:
65
65
  <div data-popover-content="more" hidden><b class="enh-popover-body">Popover body</b></div>
66
66
  <a id="enh_page_link" data-page-id="clickablehtml" data-url-query="tab=a">Clickable</a>
67
67
  <a id="enh_dot_link" href="/.//evil.example/x" data-link>Dot link</a>
68
+ <span id="enh_copy_long" data-copy="npm install lowdefy-package-with-a-long-name --save-dev">Install</span>
@@ -28,6 +28,7 @@
28
28
  properties: {
29
29
  src: {
30
30
  type: 'string',
31
+ urlKind: 'src',
31
32
  description: 'The image URL.'
32
33
  },
33
34
  alt: {
@@ -69,6 +70,7 @@
69
70
  },
70
71
  srcSet: {
71
72
  type: 'string',
73
+ urlKind: 'srcSet',
72
74
  description: 'Possible image sources for the user agent to use, strings separated by commas.',
73
75
  docs: {
74
76
  displayType: 'text-area'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/blocks-basic",
3
- "version": "0.0.0-experimental-20260927140906",
3
+ "version": "0.0.0-experimental-20260928062533",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Basic html Lowdefy blocks.",
6
6
  "homepage": "https://lowdefy.com",
@@ -45,8 +45,8 @@
45
45
  "dist/*"
46
46
  ],
47
47
  "dependencies": {
48
- "@lowdefy/block-utils": "0.0.0-experimental-20260927140906",
49
- "@lowdefy/helpers": "0.0.0-experimental-20260927140906",
48
+ "@lowdefy/block-utils": "0.0.0-experimental-20260928062533",
49
+ "@lowdefy/helpers": "0.0.0-experimental-20260928062533",
50
50
  "dompurify": "3.4.13"
51
51
  },
52
52
  "peerDependencies": {
@@ -54,8 +54,8 @@
54
54
  "react-dom": ">=18"
55
55
  },
56
56
  "devDependencies": {
57
- "@lowdefy/block-dev-e2e": "0.0.0-experimental-20260927140906",
58
- "@lowdefy/e2e-utils": "0.0.0-experimental-20260927140906",
57
+ "@lowdefy/block-dev-e2e": "0.0.0-experimental-20260928062533",
58
+ "@lowdefy/e2e-utils": "0.0.0-experimental-20260928062533",
59
59
  "@playwright/test": "1.59.1",
60
60
  "@swc/cli": "0.8.1",
61
61
  "@swc/core": "1.15.32",