diff --git a/BuildTools/Package.resolved b/BuildTools/Package.resolved index 75fea7b59..464e0eedb 100644 --- a/BuildTools/Package.resolved +++ b/BuildTools/Package.resolved @@ -123,8 +123,8 @@ "repositoryURL": "https://github.com/nicklockwood/SwiftFormat", "state": { "branch": null, - "revision": "dd989a46d0c6f15c016484bab8afe5e7a67a4022", - "version": "0.54.0" + "revision": "e4965d9e8acebb8341a6ebd20b910c882157482d", + "version": "0.54.1" } }, { diff --git a/Package.swift b/Package.swift index c0c7d6c8f..7962e20fd 100644 --- a/Package.swift +++ b/Package.swift @@ -43,6 +43,10 @@ let package = Package( .target( name: "CharcoalUIKit", dependencies: ["CharcoalShared"] + ), + .testTarget( + name: "charcoal-iosUIKitTests", + dependencies: ["CharcoalUIKit"] ) ] ) diff --git a/Sources/CharcoalUIKit/Components/Tooltip/CharcoalTooltip.swift b/Sources/CharcoalUIKit/Components/Tooltip/CharcoalTooltip.swift index 339139d44..e1326df36 100644 --- a/Sources/CharcoalUIKit/Components/Tooltip/CharcoalTooltip.swift +++ b/Sources/CharcoalUIKit/Components/Tooltip/CharcoalTooltip.swift @@ -70,8 +70,10 @@ public extension CharcoalTooltip { static func dismiss(id: CharcoalIdentifiableOverlayView.IDValue) { ChacoalOverlayManager.shared.dismiss(id: id) } +} - private static func tooltipX(anchorFrame: CGRect, tooltipSize: CGSize, canvasGeometrySize: CGSize, spacingToScreen: CGFloat) -> CGFloat { +extension CharcoalTooltip { + static func tooltipX(anchorFrame: CGRect, tooltipSize: CGSize, canvasGeometrySize: CGSize, spacingToScreen: CGFloat) -> CGFloat { let minX = anchorFrame.midX - (tooltipSize.width / 2.0) var edgeLeft = minX @@ -85,7 +87,7 @@ public extension CharcoalTooltip { return edgeLeft } - private static func tooltipY(anchorFrame: CGRect, arrowHeight: CGFloat, tooltipSize: CGSize, canvasGeometrySize: CGSize, spacingToTarget: CGFloat) -> CGFloat { + static func tooltipY(anchorFrame: CGRect, arrowHeight: CGFloat, tooltipSize: CGSize, canvasGeometrySize: CGSize, spacingToTarget: CGFloat) -> CGFloat { let minX = anchorFrame.maxY + spacingToTarget + arrowHeight var edgeBottom = anchorFrame.maxY + spacingToTarget + anchorFrame.height if edgeBottom + tooltipSize.height >= canvasGeometrySize.height { diff --git a/Tests/charcoal-iosTests/UIKit/CharcoalTextFieldViewTests.swift b/Tests/charcoal-iosTests/UIKit/CharcoalTextFieldViewTests.swift deleted file mode 100644 index 5de0df454..000000000 --- a/Tests/charcoal-iosTests/UIKit/CharcoalTextFieldViewTests.swift +++ /dev/null @@ -1,95 +0,0 @@ -@testable import Charcoal -import XCTest - -private let supplementaryText = "SupplementaryText." -private let assertiveTextWithLengthLimit = "AssertiveTextWithLengthLimit." -private let assertiveText = "AssertiveText." - -private let borderIdentifireBrand = "net.pixiv.color.brand" -private let borderIdentifireAssertive = "net.pixiv.color.assertive" - -final class CharcoalTextFieldViewTests: XCTestCase { - let textFieldView: CharcoalTextFieldView! - - override func setUp() { - textFieldView = CharcoalTextField() - } - - override func tearDown() { - textFieldView = nil - } -} - -// MARK: - AssertiveTextLabel - -extension CharcoalTextFieldViewTests { - func test_文字制限を超えた際にassertiveTextが正しく表示されていること() { - textFieldView.assertiveText = assertiveTextWithLengthLimit - textFieldView.showAssertiveText() - textFieldView.setHasError(true) - XCTAssertFalse(textFieldView.assertiveTextLabel.isHidden) - XCTAssertEqual(textFieldView.assertiveTextLabel.text, assertiveTextWithLengthLimit) - XCTAssertEqual(textFieldView.assertiveTextLabel.textColor, UIColor.charcoal.semanticAssertive) - } - - func test_設定したassertiveTextが正しく表示されていること() { - textFieldView.assertiveText = assertiveTextWithLengthLimit - textFieldView.showAssertiveText(text: assertiveText) - textFieldView.setHasError(true) - XCTAssertFalse(textFieldView.assertiveTextLabel.isHidden) - XCTAssertEqual(textFieldView.assertiveTextLabel.text, assertiveText) - XCTAssertEqual(textFieldView.assertiveTextLabel.textColor, UIColor.charcoal.semanticAssertive) - } - - func test_設定したsupplementaryTextが正しく表示されていること() { - textFieldView.assertiveText = assertiveTextWithLengthLimit - textFieldView.showAssertiveText(text: supplementaryText) - textFieldView.setHasError(false) - XCTAssertFalse(textFieldView.assertiveTextLabel.isHidden) - XCTAssertEqual(textFieldView.assertiveTextLabel.text, supplementaryText) - XCTAssertEqual(textFieldView.assertiveTextLabel.textColor, UIColor.charcoal.onSurfaceText3) - } - - func test_エラー文や補助文を切り替えても最初に設定したassertiveTextは初期化されないこと() { - textFieldView.assertiveText = assertiveTextWithLengthLimit - textFieldView.showAssertiveText(text: supplementaryText) - textFieldView.setHasError(false) - textFieldView.showAssertiveText(text: assertiveText) - textFieldView.setHasError(true) - textFieldView.showAssertiveText(text: supplementaryText) - textFieldView.setHasError(false) - textFieldView.showAssertiveText() - textFieldView.setHasError(true) - XCTAssertFalse(textFieldView.assertiveTextLabel.isHidden) - XCTAssertEqual(textFieldView.assertiveTextLabel.text, assertiveTextWithLengthLimit) - XCTAssertEqual(textFieldView.assertiveTextLabel.textColor, UIColor.charcoal.semanticAssertive) - } -} - -// MARK: - BorderLayer - -extension CharcoalTextFieldViewTests { - func test_初期化時にはボーダーが存在しないこと() { - XCTAssertFalse(textFieldView.charcoalTextField.layers.map { $0.name }.contains(borderIdentifireBrand)) - XCTAssertFalse(textFieldView.charcoalTextField.layers.map { $0.name }.contains(borderIdentifireAssertive)) - } - - func test_hasErrorがtrueの際にボーダー色がbrandに適用されていること() { - textFieldView.setHasError(true) - XCTAssertTrue(textFieldView.charcoalTextField.layers.map { $0.name }.contains(borderIdentifireBrand)) - XCTAssertFalse(textFieldView.charcoalTextField.layers.map { $0.name }.contains(borderIdentifireAssertive)) - } - - func test_hasErrorがfalseの際にボーダー色がassertiveに適用されていること() { - textFieldView.setHasError(false) - XCTAssertFalse(textFieldView.charcoalTextField.layers.map { $0.name }.contains(borderIdentifireBrand)) - XCTAssertTrue(textFieldView.charcoalTextField.layers.map { $0.name }.contains(borderIdentifireAssertive)) - } - - func test_ボーダーの切り替えが正しく動作していること() { - textFieldView.setHasError(true) - textFieldView.setHasError(false) - XCTAssertFalse(textFieldView.charcoalTextField.layers.map { $0.name }.contains(borderIdentifireBrand)) - XCTAssertTrue(textFieldView.charcoalTextField.layers.map { $0.name }.contains(borderIdentifireAssertive)) - } -} diff --git a/Tests/charcoal-iosUIKitTests/CharcoalTooltipTests.swift b/Tests/charcoal-iosUIKitTests/CharcoalTooltipTests.swift new file mode 100644 index 000000000..0fe82bf5b --- /dev/null +++ b/Tests/charcoal-iosUIKitTests/CharcoalTooltipTests.swift @@ -0,0 +1,52 @@ +@testable import CharcoalUIKit +import XCTest + +final class CharcoalTooltipTests: XCTestCase { + // The canvas is a rectangle where the tooltip can be placed + let canvasGeometryRect = CGRect(x: 0, y: 0, width: 320, height: 640) + // The size of the tooltip + let tooltipSize = CGSize(width: 100, height: 100) + // The spacing of the tooltip to the screen + let spacingToScreen: CGFloat = 16 + // The height of the arrow + let arrowHeight: CGFloat = 4 + // The spacing of the tooltip to the target + let spacingToTarget: CGFloat = 2 + // The size of the anchor view + let anchorViewSize = CGSize(width: 100, height: 100) + + func testLayoutTopLeft() throws { + // Here we put the anchor frame at top left corner + let anchorFrame = CGRect(x: 0, y: 0, width: anchorViewSize.width, height: anchorViewSize.height) + let x = CharcoalTooltip.tooltipX(anchorFrame: anchorFrame, tooltipSize: tooltipSize, canvasGeometrySize: canvasGeometryRect.size, spacingToScreen: spacingToScreen) + let y = CharcoalTooltip.tooltipY(anchorFrame: anchorFrame, arrowHeight: arrowHeight, tooltipSize: tooltipSize, canvasGeometrySize: canvasGeometryRect.size, spacingToTarget: spacingToTarget) + + // The tooltip should be at the top left corner and respect the spacing + XCTAssertEqual(x, spacingToScreen) + XCTAssertEqual(y, anchorFrame.maxY + spacingToTarget + arrowHeight) + } + + func testLayoutCenter() throws { + // Here we put the anchor frame at the center of the canvas + let canvasCenter = CGPoint(x: canvasGeometryRect.midX, y: canvasGeometryRect.midY) + let anchorFrame = CGRect(x: canvasCenter.x - anchorViewSize.width / 2.0, y: canvasCenter.y - anchorViewSize.height / 2.0, width: anchorViewSize.width, height: anchorViewSize.height) + + let x = CharcoalTooltip.tooltipX(anchorFrame: anchorFrame, tooltipSize: tooltipSize, canvasGeometrySize: canvasGeometryRect.size, spacingToScreen: spacingToScreen) + let y = CharcoalTooltip.tooltipY(anchorFrame: anchorFrame, arrowHeight: arrowHeight, tooltipSize: tooltipSize, canvasGeometrySize: canvasGeometryRect.size, spacingToTarget: spacingToTarget) + + // The tooltip should be at the center of the canvas + XCTAssertEqual(x, canvasCenter.x - tooltipSize.width / 2.0) + XCTAssertEqual(y, anchorFrame.maxY + spacingToTarget + arrowHeight) + } + + func testLayoutBottomRight() throws { + // Here we put the anchor frame at the bottom right corner + let anchorFrame = CGRect(x: canvasGeometryRect.maxX - anchorViewSize.width, y: canvasGeometryRect.maxY - anchorViewSize.height, width: anchorViewSize.width, height: anchorViewSize.height) + let x = CharcoalTooltip.tooltipX(anchorFrame: anchorFrame, tooltipSize: tooltipSize, canvasGeometrySize: canvasGeometryRect.size, spacingToScreen: spacingToScreen) + let y = CharcoalTooltip.tooltipY(anchorFrame: anchorFrame, arrowHeight: arrowHeight, tooltipSize: tooltipSize, canvasGeometrySize: canvasGeometryRect.size, spacingToTarget: spacingToTarget) + + // The tooltip should be at the bottom right corner and respect the spacing + XCTAssertEqual(x, canvasGeometryRect.maxX - tooltipSize.width - spacingToScreen) + XCTAssertEqual(y, anchorFrame.minY - spacingToTarget - arrowHeight - tooltipSize.height) + } +} diff --git a/scripts/create-pull-request/package-lock.json b/scripts/create-pull-request/package-lock.json index 8bf458eec..66e05d95a 100644 --- a/scripts/create-pull-request/package-lock.json +++ b/scripts/create-pull-request/package-lock.json @@ -129,13 +129,13 @@ } }, "node_modules/@octokit/rest": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.0.0.tgz", - "integrity": "sha512-XudXXOmiIjivdjNZ+fN71NLrnDM00sxSZlhqmPR3v0dVoJwyP628tSlc12xqn8nX3N0965583RBw5GPo6r8u4Q==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.0.1.tgz", + "integrity": "sha512-RWA6YU4CqK0h0J6tfYlUFnH3+YgBADlxaHXaKSG+BVr2y4PTfbU2tlKuaQoQZ83qaTbi4CUxLNAmbAqR93A6mQ==", "dependencies": { "@octokit/core": "^6.1.2", "@octokit/plugin-paginate-rest": "^11.0.0", - "@octokit/plugin-request-log": "^5.1.0", + "@octokit/plugin-request-log": "^5.3.1", "@octokit/plugin-rest-endpoint-methods": "^13.0.0" }, "engines": { @@ -193,9 +193,9 @@ } }, "node_modules/@octokit/rest/node_modules/@octokit/plugin-request-log": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.0.tgz", - "integrity": "sha512-FiGcyjdtYPlr03ExBk/0ysIlEFIFGJQAVoPPMxL19B24bVSEiZQnVGBunNtaAF1YnvE/EFoDpXmITtRnyCiypQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz", + "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", "engines": { "node": ">= 18" }, @@ -204,9 +204,9 @@ } }, "node_modules/@octokit/rest/node_modules/@octokit/request": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.1.tgz", - "integrity": "sha512-pyAguc0p+f+GbQho0uNetNQMmLG1e80WjkIaqqgUkihqUp0boRU6nKItXO4VWnr+nbZiLGEyy4TeKRwqaLvYgw==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz", + "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==", "dependencies": { "@octokit/endpoint": "^10.0.0", "@octokit/request-error": "^6.0.1", @@ -218,9 +218,9 @@ } }, "node_modules/@octokit/rest/node_modules/@octokit/request-error": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.1.tgz", - "integrity": "sha512-1mw1gqT3fR/WFvnoVpY/zUM2o/XkMs/2AszUUG9I69xn0JFLv6PGkPhNk5lbfvROs79wiS0bqiJNxfCZcRJJdg==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.4.tgz", + "integrity": "sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==", "dependencies": { "@octokit/types": "^13.0.0" }, @@ -529,13 +529,13 @@ } }, "@octokit/rest": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.0.0.tgz", - "integrity": "sha512-XudXXOmiIjivdjNZ+fN71NLrnDM00sxSZlhqmPR3v0dVoJwyP628tSlc12xqn8nX3N0965583RBw5GPo6r8u4Q==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.0.1.tgz", + "integrity": "sha512-RWA6YU4CqK0h0J6tfYlUFnH3+YgBADlxaHXaKSG+BVr2y4PTfbU2tlKuaQoQZ83qaTbi4CUxLNAmbAqR93A6mQ==", "requires": { "@octokit/core": "^6.1.2", "@octokit/plugin-paginate-rest": "^11.0.0", - "@octokit/plugin-request-log": "^5.1.0", + "@octokit/plugin-request-log": "^5.3.1", "@octokit/plugin-rest-endpoint-methods": "^13.0.0" }, "dependencies": { @@ -578,15 +578,15 @@ } }, "@octokit/plugin-request-log": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.0.tgz", - "integrity": "sha512-FiGcyjdtYPlr03ExBk/0ysIlEFIFGJQAVoPPMxL19B24bVSEiZQnVGBunNtaAF1YnvE/EFoDpXmITtRnyCiypQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz", + "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", "requires": {} }, "@octokit/request": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.1.tgz", - "integrity": "sha512-pyAguc0p+f+GbQho0uNetNQMmLG1e80WjkIaqqgUkihqUp0boRU6nKItXO4VWnr+nbZiLGEyy4TeKRwqaLvYgw==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz", + "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==", "requires": { "@octokit/endpoint": "^10.0.0", "@octokit/request-error": "^6.0.1", @@ -595,9 +595,9 @@ } }, "@octokit/request-error": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.1.tgz", - "integrity": "sha512-1mw1gqT3fR/WFvnoVpY/zUM2o/XkMs/2AszUUG9I69xn0JFLv6PGkPhNk5lbfvROs79wiS0bqiJNxfCZcRJJdg==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.4.tgz", + "integrity": "sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==", "requires": { "@octokit/types": "^13.0.0" } diff --git a/scripts/xcasset-gen/package-lock.json b/scripts/xcasset-gen/package-lock.json index 75e762edb..d91e55847 100644 --- a/scripts/xcasset-gen/package-lock.json +++ b/scripts/xcasset-gen/package-lock.json @@ -9,16 +9,16 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { - "@babel/runtime": "^7.24.7", + "@babel/runtime": "^7.24.8", "fs-extra": "^11.2.0", "globby": "^14.0.2", "yargs": "^17.7.2" } }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -515,9 +515,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", "requires": { "regenerator-runtime": "^0.14.0" } diff --git a/scripts/xcasset-gen/package.json b/scripts/xcasset-gen/package.json index c8987ed38..24bbb4779 100644 --- a/scripts/xcasset-gen/package.json +++ b/scripts/xcasset-gen/package.json @@ -10,7 +10,7 @@ }, "type": "module", "dependencies": { - "@babel/runtime": "^7.24.7", + "@babel/runtime": "^7.24.8", "fs-extra": "^11.2.0", "globby": "^14.0.2", "yargs": "^17.7.2"