Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix iOS SR Text with padding #561

Merged
merged 2 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,13 @@ internal struct RCTTextViewWireframesBuilder: SessionReplayNodeWireframesBuilder

let DEFAULT_FONT_COLOR = UIColor.black.cgColor

// Clipping should be 0 to avoid the text from overflowing when the
// numberOfLines prop is used.
private var clip: SRContentClip {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can drop the clip in that case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could... but keeping it (when it will be applied in the player) will enable us to handle ellipsis much better

let top = abs(contentRect.origin.y)
let left = abs(contentRect.origin.x)
let bottom = max(contentRect.height - attributes.frame.height - top, 0)
let right = max(contentRect.width - attributes.frame.width - left, 0)
let top = 0.0
let left = 0.0
let bottom = 0.0
let right = 0.0
return SRContentClip.create(
bottom: Int64(withNoOverflow: bottom),
left: Int64(withNoOverflow: left),
Expand All @@ -110,20 +112,31 @@ internal struct RCTTextViewWireframesBuilder: SessionReplayNodeWireframesBuilder

private var relativeIntersectedRect: CGRect {
return CGRect(
x: attributes.frame.origin.x - contentRect.origin.x,
y: attributes.frame.origin.y - contentRect.origin.y ,
x: attributes.frame.origin.x,
y: attributes.frame.origin.y,
width: max(contentRect.width, attributes.frame.width),
height: max(contentRect.height, attributes.frame.height)
)
}

private var textFrame: CGRect {
return CGRect(
x: attributes.frame.origin.x + contentRect.origin.x,
y: attributes.frame.origin.y + contentRect.origin.y,
width: contentRect.width,
height: contentRect.height
)
}

public func buildWireframes(with builder: SessionReplayWireframesBuilder) -> [SRWireframe] {
return [
builder.createTextWireframe(
id: wireframeID,
frame: relativeIntersectedRect,
text: textObfuscator.mask(text: text ?? ""),
textAlignment: .init(systemTextAlignment: textAlignment, vertical: .center),
textFrame: textFrame,
// Text alignment is top for all RCTTextView components.
textAlignment: .init(systemTextAlignment: textAlignment, vertical: .top),
clip: clip,
textColor: textColor ?? DEFAULT_FONT_COLOR,
font: font,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ import XCTest
@testable import DatadogSessionReplay
import React

let BACKGROUND_RECT = CGRect(x: 50, y: 50, width: 100, height: 100)
// Simulates view with padding vertical of 10 and horizontal of 20.
let INNER_TEXT_RECT = CGRect(x: 20, y: 10, width: 60, height: 80) // position inside the background view

internal class RCTTextViewRecorderTests: XCTestCase {
let mockAttributes = SessionReplayViewAttributes(
frame: CGRect(x: 0, y: 0, width: 100, height: 100),
frame: BACKGROUND_RECT,
backgroundColor: UIColor.white.cgColor,
layerBorderColor: UIColor.blue.cgColor,
layerBorderWidth: CGFloat(1.0),
Expand All @@ -21,14 +25,14 @@ internal class RCTTextViewRecorderTests: XCTestCase {
isHidden: false,
intrinsicContentSize: CGSize(width: 100.0, height: 100.0)
)

let mockAllowContext = SessionReplayViewTreeRecordingContext(
recorder: .init(privacy: SessionReplayPrivacyLevel.allow, applicationID: "app_id", sessionID: "session_id", viewID: "view_id", viewServerTimeOffset: nil),
coordinateSpace: UIView(),
ids: .init(),
imageDataProvider: ImageDataProvider()
)

var mockShadowView: RCTTextShadowView {
// The shadow view must be initialized with a bridge so that we can insert React Subviews into it.
let shadowView: RCTTextShadowView = .init(bridge: MockRCTBridge(delegate: .none));
Expand All @@ -37,9 +41,11 @@ internal class RCTTextViewRecorderTests: XCTestCase {
rawTextShadowView.text = "This is the test text."
shadowView.insertReactSubview(rawTextShadowView, at: 0)

shadowView.layoutMetrics.contentFrame = INNER_TEXT_RECT

return shadowView
}

var mockShadowViewNestedText: RCTTextShadowView {
// The shadow view must be initialized with a bridge so that we can insert React Subviews into it.
let shadowView: RCTTextShadowView = .init(bridge: MockRCTBridge(delegate: .none));
Expand Down Expand Up @@ -79,7 +85,7 @@ internal class RCTTextViewRecorderTests: XCTestCase {
let element = try XCTUnwrap(result as? SessionReplayInvisibleElement)
XCTAssertEqual(element, SessionReplayInvisibleElement.constant)
}

func testReturnsBuilderWithCorrectInformation() throws {
let reactTag = NSNumber(value: 44)
let uiManagerMock = MockUIManager(reactTag: reactTag, shadowView: mockShadowView)
Expand All @@ -94,6 +100,19 @@ internal class RCTTextViewRecorderTests: XCTestCase {
XCTAssertEqual(element.nodes.count, 1)
let wireframe = try XCTUnwrap(element.nodes[0].wireframesBuilder.buildWireframes(with: .init())[0].getAsTextWireframe())
XCTAssertEqual(wireframe.text, "This is the test text.")

// Wireframe represents the background box.
XCTAssertEqual(wireframe.height, 100)
XCTAssertEqual(wireframe.width, 100)
XCTAssertEqual(wireframe.x, 50)
XCTAssertEqual(wireframe.y, 50)

// Padding around the test box in the background box.
XCTAssertEqual(wireframe.textPosition?.padding, .init(
bottom: Int64(BACKGROUND_RECT.height - INNER_TEXT_RECT.height - INNER_TEXT_RECT.minY),
left: Int64(INNER_TEXT_RECT.minX),
right: Int64(BACKGROUND_RECT.width - INNER_TEXT_RECT.width - INNER_TEXT_RECT.minX),
top: Int64(INNER_TEXT_RECT.minY)))
}

func testReturnsBuilderWithCorrectInformationWhenNestedTextComponents() throws {
Expand Down