mosaic-headless 1.23.0 → 1.24.0

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.
@@ -143,9 +143,27 @@ class Surface:
143
143
  # them, and wysiwyg-variable inside a text node is verified
144
144
  # to render (references/dynamic-content.md)
145
145
  or (t.startswith("wysiwyg-")
146
- and any(d.startswith("wysiwyg-") for d in parent_defaults)))
146
+ and any(d.startswith("wysiwyg-") for d in parent_defaults))
147
+ # A family member under its own family head. The table
148
+ # cannot say this when the head's rule is `any` and its
149
+ # allowed_children column is therefore empty - `slider`
150
+ # accepts anything, so nothing records that
151
+ # `slider-slides` is exactly where it belongs.
152
+ or (parent_type and t.startswith(parent_type + "-")))
153
+ # The comment above covers types that need their own PARENT. The other half
154
+ # of the same story is a family HEAD, which needs its own CHILDREN: a bare
155
+ # `slider` is measured COMMIT_500 because it has no `slider-slides`, not
156
+ # because a div cannot hold it. So a node whose type prefixes other
157
+ # registered types - slider/slider-slides, accordion/accordion-item,
158
+ # form/form-input - and which actually carries one of them is not the thing
159
+ # that was measured, and the verdict does not apply to it.
160
+ family = [x for x in self.types if x.startswith(t + "-")]
161
+ kids = [c.get("type") for c in (node.get("children") or []) if isinstance(c, dict)]
162
+ nested_as_family = bool(family) and any(k in family for k in kids)
163
+
147
164
  outcome = self.outcome.get(t)
148
- if not declared_child and (outcome == "BROKE_PAGE" or (outcome or "").startswith("COMMIT_5")):
165
+ if (not declared_child and not nested_as_family
166
+ and (outcome == "BROKE_PAGE" or (outcome or "").startswith("COMMIT_5"))):
149
167
  problems.append("%s is measured %s under a plain container, and %s is not its declared parent"
150
168
  % (t, outcome, parent_type or "<root>"))
151
169
  # The wysiwyg family is inline CONTENT, not a child in the placement sense, and
@@ -32,6 +32,7 @@ from __future__ import annotations
32
32
 
33
33
  import argparse
34
34
  import csv
35
+ import os
35
36
  import sys
36
37
  import time
37
38
 
@@ -80,7 +81,14 @@ READ = """id => {
80
81
  bulletLabels: bullets.map(b => b.getAttribute('aria-label')),
81
82
  bulletRole: bullets[0] ? bullets[0].getAttribute('role') : null,
82
83
  bulletTab: bullets[0] ? bullets[0].getAttribute('tabindex') : null,
83
- prev: arrow('left'), next: arrow('right')};
84
+ prev: arrow('left'), next: arrow('right'),
85
+ // what the TRACK actually is. `isCarousel` is widely assumed to mean
86
+ // "show several at once"; this is the measurement that settles it.
87
+ trackAreas: track ? getComputedStyle(track).gridTemplateAreas : null,
88
+ trackCols: track ? getComputedStyle(track).gridTemplateColumns : null,
89
+ trackW: track ? Math.round(track.getBoundingClientRect().width) : null,
90
+ slideW: slides.map(el => Math.round(el.getBoundingClientRect().width)),
91
+ slideLeft: slides.map(el => Math.round(el.getBoundingClientRect().left))};
84
92
  }"""
85
93
 
86
94
  CLICK_NTH = """([id, kind, n]) => {
@@ -110,6 +118,11 @@ def main():
110
118
  ap.add_argument("--autoplay", type=int, default=0,
111
119
  help="declared autoplay delay in ms; 0 means autoplay is off")
112
120
  ap.add_argument("--csv")
121
+ ap.add_argument("--label", help="name for this slider in the CSV's first column; "
122
+ "a page usually carries more than one")
123
+ ap.add_argument("--append", action="store_true",
124
+ help="add to the CSV instead of replacing it, so several "
125
+ "sliders on one page land in one table")
113
126
  a = ap.parse_args()
114
127
 
115
128
  rows, fails = [], []
@@ -184,12 +197,46 @@ def main():
184
197
  now = pg.evaluate(READ, a.slider)
185
198
  check("BULLET_GOES", now["current"] == 0,
186
199
  "bullet 0 -> slide %s" % now["current"])
187
- check("ARROW_ENDS",
188
- bool(now["prev"]) and now["prev"]["hidden"]
189
- and bool(now["next"]) and not now["next"]["hidden"],
190
- "on slide 0 prev hides itself (hidden=%s), next does not (hidden=%s)"
191
- % (now["prev"] and now["prev"]["hidden"],
192
- now["next"] and now["next"]["hidden"]))
200
+ # A CAROUSEL has no end it cannot pass - it wraps - so neither arrow
201
+ # ever hides, and asserting the plain-slider rule against one reports a
202
+ # failure for correct behaviour. Branch on what the host declares.
203
+ is_carousel = str(now.get("carousel") or st.get("carousel")).lower() in (
204
+ "1", "true", "yes")
205
+ if is_carousel:
206
+ check("ARROW_ENDS",
207
+ bool(now["prev"]) and not now["prev"]["hidden"]
208
+ and bool(now["next"]) and not now["next"]["hidden"],
209
+ "carousel: it wraps, so neither arrow hides "
210
+ "(prev hidden=%s, next hidden=%s)"
211
+ % (now["prev"] and now["prev"]["hidden"],
212
+ now["next"] and now["next"]["hidden"]))
213
+ else:
214
+ check("ARROW_ENDS",
215
+ bool(now["prev"]) and now["prev"]["hidden"]
216
+ and bool(now["next"]) and not now["next"]["hidden"],
217
+ "on slide 0 prev hides itself (hidden=%s), next does not (hidden=%s)"
218
+ % (now["prev"] and now["prev"]["hidden"],
219
+ now["next"] and now["next"]["hidden"]))
220
+ # `isCarousel` does NOT widen the view - every slide is one track wide
221
+ # in both modes. What it does is keep a slide positioned BEFORE the
222
+ # first one, which is the same fact as "it wraps" and as "no arrow
223
+ # ever hides".
224
+ full = (now["trackW"] and now["slideW"]
225
+ and all(abs(w - now["trackW"]) <= 2 for w in now["slideW"]))
226
+ if is_carousel:
227
+ # A slide wrapped round to a NEGATIVE left is the visible sign of
228
+ # wrapping, but it is transient - it is only there while the
229
+ # rotation has carried one past an end, and on slide 0 at rest
230
+ # there is none. The claim that always holds, and the one being
231
+ # corrected, is about WIDTH: a carousel is not a wider view.
232
+ check("CAROUSEL_LAYOUT", bool(full),
233
+ "grid %s, every slide still one track wide (%s vs %s) - "
234
+ "isCarousel wraps the ends, it does not show more than one"
235
+ % (now["trackAreas"], sorted(set(now["slideW"])), now["trackW"]))
236
+ else:
237
+ check("TRACK_LAYOUT", bool(full),
238
+ "grid %s, every slide one track wide (%s vs %s)"
239
+ % (now["trackAreas"], sorted(set(now["slideW"])), now["trackW"]))
193
240
  if now["next"]:
194
241
  check("ARROW_ARIA",
195
242
  now["next"]["role"] == "button" and now["next"]["tabindex"] == "0"
@@ -244,10 +291,17 @@ def main():
244
291
 
245
292
  print("\n%d of %d checks passed" % (len(rows) - len(fails), len(rows)))
246
293
  if a.csv:
294
+ label = a.label or a.slider
295
+ existing = []
296
+ if a.append and os.path.exists(a.csv):
297
+ with open(a.csv, newline="", encoding="utf-8") as fh:
298
+ existing = [r for r in csv.reader(fh)][1:]
299
+ existing = [r for r in existing if r and r[0] != label]
247
300
  with open(a.csv, "w", newline="", encoding="utf-8") as fh:
248
301
  w = csv.writer(fh)
249
- w.writerow(["check", "result", "detail"])
250
- w.writerows(rows)
302
+ w.writerow(["slider", "check", "result", "detail"])
303
+ w.writerows(existing)
304
+ w.writerows([[label] + r for r in rows])
251
305
  print("wrote %s" % a.csv)
252
306
  sys.exit(1 if fails else 0)
253
307