ReviewOS

also looking at this

craft-native/craft

fix(native): retain keyed controls and correct map and tab chrome

#368
Merged chris wants to merge codex/native-repeated-selector-identity into main
32 files +449 -100

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

packages/android/src/index.test.tsmodified+2-1
Changes to packages/android/src/index.test.ts
@@ -558,7 +558,8 @@ describe('Craft Android builder', () => {
558558 expect(nativeActivity).toContain('if (!current.contains(message))')
559559 expect(nativeActivity).toContain('joinToString(", ")')
560560 expect(nativeActivity).toContain('screen.controls.putAll(next)')
561 expect(nativeActivity).toContain('if (layout.getChildAt(index) !== childView)')
561 expect(nativeActivity).toContain('if (childView.parent !== layout)')
562 expect(nativeActivity).toContain('desired.forEach(layout::bringChildToFront)')
562563 expect(nativeActivity).toContain('val modal = screen.modalVisibility.entries.lastOrNull')
563564 expect(nativeActivity).toContain('val suppressDismiss = !visible && consumeDismissSuppression()')
564565 expect(nativeActivity).toContain('(modal.key as? CraftNativeModalView)?.requestClose()')
packages/ios/fixtures/timed-input/HostApp.swiftmodified+10-1
Changes to packages/ios/fixtures/timed-input/HostApp.swift
@@ -12,7 +12,16 @@ struct HostApp: App {
1212 Text(taps.isEmpty ? "none" : taps.joined(separator: ","))
1313 .lineLimit(1)
1414 .accessibilityIdentifier("received-taps")
15 Spacer()
15 ScrollView {
16 VStack {
17 Button("Start of scroll fixture") {}
18 .accessibilityIdentifier("driver-scroll-start")
19 Color.clear.frame(height: 1600).accessibilityHidden(true)
20 Button("End of scroll fixture") {}
21 .accessibilityIdentifier("driver-scroll-end")
22 }
23 .frame(maxWidth: .infinity)
24 }
1625 HStack {
1726 ForEach(names, id: \.self) { name in
1827 Button(name) { taps.append(name) }
packages/ios/scripts/compile-templates.tsmodified+4-0
Changes to packages/ios/scripts/compile-templates.ts
@@ -259,6 +259,10 @@ try {
259259 ]) },
260260 { command: 'labelid', id: 'received-taps', text: inputSequence.join(',') },
261261 { command: 'selected', text: 'Four' },
262 { command: 'scrollto', text: 'End of scroll fixture' },
263 { command: 'expectid', text: 'driver-scroll-end' },
264 { command: 'scrollto', text: 'Start of scroll fixture' },
265 { command: 'expectid', text: 'driver-scroll-start' },
262266 ])
263267 initSimulatorDriver({ output: driverOutput, bundleId: 'dev.craft.timedinput.host',
264268 bundleIdPrefix: 'dev.craft.timedinput', projectName: 'TimedInputDriver', tabs: inputNames, script })
packages/ios/templates/testing/DriverTests.swift.templatemodified+5-1
Changes to packages/ios/templates/testing/DriverTests.swift.template
@@ -448,7 +448,11 @@ final class DriverTests: XCTestCase {
448448 var found = element(containing: arg)
449449 var swipes = 0
450450 while (found == nil || !(found!.isHittable)) && swipes < 8 {
451 app.swipeUp()
451 if let target = found, !target.frame.isEmpty, target.frame.midY < app.frame.midY {
452 app.swipeDown()
453 } else {
454 app.swipeUp()
455 }
452456 swipes += 1
453457 found = element(containing: arg)
454458 }
scripts/native-renderer-contract.test.tsmodified+2-1
Changes to scripts/native-renderer-contract.test.ts
@@ -3003,7 +3003,8 @@ describe('native renderer component contract', () => {
30033003 })
30043004
30053005 it('prefers authored keys over compiler ids when reconciling native rows', () => {
3006 expect(ios).toContain('let direct = [node["key"], props["key"], props["testID"]]')
3006 expect(ios).toContain('let direct = [node["key"], props["key"]]')
3007 expect(ios).toContain('[node["id"], props["testID"]]')
30073008 // Through a lone wrapped child (a component's wrapper) only.
30083009 expect(ios).toContain('return authoredKey(only, props: only["props"] as? [String: Any] ?? [:])')
30093010 expect(iosFlatList).toContain('[node["key"], props["key"], props["testID"], node["id"]]')