diff --git a/WordPress/Classes/ViewRelated/Gutenberg/Collapsable Header/Collapsable Header Collection View Cell/CollapsableHeaderCollectionViewCell.swift b/WordPress/Classes/ViewRelated/Gutenberg/Collapsable Header/Collapsable Header Collection View Cell/CollapsableHeaderCollectionViewCell.swift index 904367fef43a..3316164a7442 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/Collapsable Header/Collapsable Header Collection View Cell/CollapsableHeaderCollectionViewCell.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/Collapsable Header/Collapsable Header Collection View Cell/CollapsableHeaderCollectionViewCell.swift @@ -25,6 +25,8 @@ class CollapsableHeaderCollectionViewCell: UICollectionViewCell { } } + var showsCheckMarkWhenSelected = true + override func prepareForReuse() { super.prepareForReuse() imageView.cancelImageDownload() @@ -117,6 +119,11 @@ class CollapsableHeaderCollectionViewCell: UICollectionViewCell { } private func checkmarkHidden(_ isHidden: Bool, animated: Bool = false) { + guard showsCheckMarkWhenSelected else { + checkmarkContainerView.isHidden = true + return + } + guard animated else { checkmarkContainerView.isHidden = isHidden return diff --git a/WordPress/Classes/ViewRelated/Gutenberg/Layout Picker/CategorySectionTableViewCell.swift b/WordPress/Classes/ViewRelated/Gutenberg/Layout Picker/CategorySectionTableViewCell.swift index 2b8504214c16..bbe310e505fe 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/Layout Picker/CategorySectionTableViewCell.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/Layout Picker/CategorySectionTableViewCell.swift @@ -51,6 +51,7 @@ class CategorySectionTableViewCell: UITableViewCell { } var isGhostCell: Bool = false + var showsCheckMarkWhenSelected = true override func prepareForReuse() { section?.scrollOffset = collectionView.contentOffset @@ -90,6 +91,7 @@ extension CategorySectionTableViewCell: UICollectionViewDelegate { deselectItem(indexPath) return false } + return true } @@ -126,6 +128,7 @@ extension CategorySectionTableViewCell: UICollectionViewDataSource { let thumbnail = thumbnails[indexPath.row] cell.previewURL = thumbnailUrl(forThumbnail: thumbnail) + cell.showsCheckMarkWhenSelected = showsCheckMarkWhenSelected cell.isAccessibilityElement = true cell.accessibilityLabel = thumbnail.slug return cell diff --git a/WordPress/Classes/ViewRelated/Site Creation/Design Selection/RemoteSiteDesign+Thumbnail.swift b/WordPress/Classes/ViewRelated/Site Creation/Design Selection/RemoteSiteDesign+Thumbnail.swift new file mode 100644 index 000000000000..97331ffadce4 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Site Creation/Design Selection/RemoteSiteDesign+Thumbnail.swift @@ -0,0 +1,7 @@ +import Foundation + +extension RemoteSiteDesign: Thumbnail { + var urlDesktop: String? { screenshot } + var urlTablet: String? { tabletScreenshot } + var urlMobile: String? { mobileScreenshot} +} diff --git a/WordPress/Classes/ViewRelated/Site Creation/Design Selection/SiteDesignContentCollectionViewController.swift b/WordPress/Classes/ViewRelated/Site Creation/Design Selection/SiteDesignContentCollectionViewController.swift index 44636bed9c9a..8ca0fbb76cf1 100644 --- a/WordPress/Classes/ViewRelated/Site Creation/Design Selection/SiteDesignContentCollectionViewController.swift +++ b/WordPress/Classes/ViewRelated/Site Creation/Design Selection/SiteDesignContentCollectionViewController.swift @@ -1,49 +1,32 @@ import UIKit import WordPressKit -extension RemoteSiteDesign: Thumbnail { - var urlDesktop: String? { screenshot } - var urlTablet: String? { tabletScreenshot } - var urlMobile: String? { mobileScreenshot} -} - -class SiteDesignSection: CategorySection { - var category: RemoteSiteDesignCategory - var designs: [RemoteSiteDesign] - - var categorySlug: String { category.slug } - var title: String { category.title } - var emoji: String? { category.emoji } - var description: String? { category.description } - var thumbnails: [Thumbnail] { designs } - var scrollOffset: CGPoint - - init(category: RemoteSiteDesignCategory, designs: [RemoteSiteDesign]) { - self.category = category - self.designs = designs - self.scrollOffset = .zero - } -} - -class SiteDesignContentCollectionViewController: FilterableCategoriesViewController, UIPopoverPresentationControllerDelegate { +class SiteDesignContentCollectionViewController: CollapsableHeaderViewController { typealias TemplateGroup = SiteDesignRequest.TemplateGroup + typealias PreviewDevice = PreviewDeviceSelectionViewController.PreviewDevice + private let createsSite: Bool private let templateGroups: [TemplateGroup] = [.stable, .singlePage] - - let completion: SiteDesignStep.SiteDesignSelection - let restAPI = WordPressComRestApi.anonymousApi(userAgent: WPUserAgent.wordPress(), localeKey: WordPressComRestApi.LocaleKeyV2) - var selectedIndexPath: IndexPath? = nil + private let tableView: UITableView + private let completion: SiteDesignStep.SiteDesignSelection + private let restAPI = WordPressComRestApi.anonymousApi( + userAgent: WPUserAgent.wordPress(), + localeKey: WordPressComRestApi.LocaleKeyV2 + ) private var sections: [SiteDesignSection] = [] - internal override var categorySections: [CategorySection] { get { sections }} + private var isLoading: Bool = true { + didSet { + if isLoading { + tableView.startGhostAnimation(style: GhostCellStyle.muriel) + } else { + tableView.stopGhostAnimation() + } - override var selectedPreviewDevice: PreviewDevice { - get { .mobile } - set { /* no op */ } + tableView.reloadData() + } } - - private lazy var previewViewSelectedPreviewDevice = PreviewDevice.default - - var siteDesigns = RemoteSiteDesigns() { + private var previewViewSelectedPreviewDevice = PreviewDevice.default + private var siteDesigns = RemoteSiteDesigns() { didSet { if oldValue.categories.count == 0 { scrollableView.setContentOffset(.zero, animated: false) @@ -52,26 +35,25 @@ class SiteDesignContentCollectionViewController: FilterableCategoriesViewControl SiteDesignSection(category: category, designs: siteDesigns.designs.filter { design in design.categories.map({$0.slug}).contains(category.slug) }) } - NSLog("sections: %@", String(describing: sections)) contentSizeWillChange() tableView.reloadData() } } - - var selectedDesign: RemoteSiteDesign? { - guard let sectionIndex = selectedItem?.section, let position = selectedItem?.item else { return nil } - return sections[sectionIndex].designs[position] - } + let selectedPreviewDevice = PreviewDevice.mobile init(createsSite: Bool, _ completion: @escaping SiteDesignStep.SiteDesignSelection) { self.completion = completion self.createsSite = createsSite + tableView = UITableView(frame: .zero, style: .plain) + tableView.separatorStyle = .singleLine + tableView.separatorInset = .zero + tableView.showsVerticalScrollIndicator = false super.init( - analyticsLocation: "site_creation", + scrollableView: tableView, mainTitle: TextContent.mainTitle, - primaryActionTitle: createsSite ? TextContent.createSiteButton : TextContent.chooseButton, - secondaryActionTitle: TextContent.previewButton + // the primary action button is never shown + primaryActionTitle: "" ) } @@ -81,6 +63,8 @@ class SiteDesignContentCollectionViewController: FilterableCategoriesViewControl override func viewDidLoad() { super.viewDidLoad() + tableView.register(CategorySectionTableViewCell.nib, forCellReuseIdentifier: CategorySectionTableViewCell.cellReuseIdentifier) + tableView.dataSource = self navigationItem.backButtonTitle = TextContent.backButtonTitle fetchSiteDesigns() configureCloseButton() @@ -119,39 +103,18 @@ class SiteDesignContentCollectionViewController: FilterableCategoriesViewControl navigationItem.leftBarButtonItem = UIBarButtonItem(title: TextContent.cancelButtonTitle, style: .done, target: self, action: #selector(closeButtonTapped)) } - @objc func skipButtonTapped(_ sender: Any) { + @objc + private func closeButtonTapped(_ sender: Any) { + dismiss(animated: true) + } + + @objc + private func skipButtonTapped(_ sender: Any) { presentedViewController?.dismiss(animated: true) SiteCreationAnalyticsHelper.trackSiteDesignSkipped() completion(nil) } - override func primaryActionSelected(_ sender: Any) { - guard let design = selectedDesign else { - completion(nil) - return - } - SiteCreationAnalyticsHelper.trackSiteDesignSelected(design) - completion(design) - } - - override func secondaryActionSelected(_ sender: Any) { - guard let design = selectedDesign else { return } - - let previewVC = SiteDesignPreviewViewController( - siteDesign: design, - selectedPreviewDevice: previewViewSelectedPreviewDevice, - createsSite: createsSite, - onDismissWithDeviceSelected: { [weak self] device in - self?.previewViewSelectedPreviewDevice = device - }, - completion: completion - ) - - let navController = GutenbergLightNavigationController(rootViewController: previewVC) - navController.modalPresentationStyle = .pageSheet - navigationController?.present(navController, animated: true) - } - private func handleError(_ error: Error) { SiteCreationAnalyticsHelper.trackError(error) let titleText = TextContent.errorTitle @@ -161,9 +124,6 @@ class SiteDesignContentCollectionViewController: FilterableCategoriesViewControl private enum TextContent { static let mainTitle = NSLocalizedString("Choose a theme", comment: "Title for the screen to pick a theme and homepage for a site.") - static let createSiteButton = NSLocalizedString("Create Site", comment: "Title for the button to progress with creating the site with the selected design.") - static let chooseButton = NSLocalizedString("Choose", comment: "Title for the button to progress with the selected site homepage design.") - static let previewButton = NSLocalizedString("Preview", comment: "Title for button to preview a selected homepage design.") static let backButtonTitle = NSLocalizedString("Design", comment: "Shortened version of the main title to be used in back navigation.") static let skipButtonTitle = NSLocalizedString("Skip", comment: "Continue without making a selection.") static let cancelButtonTitle = NSLocalizedString("Cancel", comment: "Cancel site creation.") @@ -173,8 +133,69 @@ class SiteDesignContentCollectionViewController: FilterableCategoriesViewControl } // MARK: - NoResultsViewControllerDelegate + extension SiteDesignContentCollectionViewController: NoResultsViewControllerDelegate { func actionButtonPressed() { fetchSiteDesigns() } } + +// MARK: - UITableViewDataSource + +extension SiteDesignContentCollectionViewController: UITableViewDataSource { + + func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { + return CategorySectionTableViewCell.estimatedCellHeight + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return isLoading ? 1 : (sections.count) + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cellReuseIdentifier = CategorySectionTableViewCell.cellReuseIdentifier + guard let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) as? CategorySectionTableViewCell else { + fatalError("Expected the cell with identifier \"\(cellReuseIdentifier)\" to be a \(CategorySectionTableViewCell.self). Please make sure the table view is registering the correct nib before loading the data") + } + cell.delegate = self + cell.selectionStyle = UITableViewCell.SelectionStyle.none + cell.section = isLoading ? nil : sections[indexPath.row] + cell.isGhostCell = isLoading + cell.showsCheckMarkWhenSelected = false + cell.layer.masksToBounds = false + cell.clipsToBounds = false + cell.collectionView.allowsSelection = !isLoading + return cell + } +} + +// MARK: - CategorySectionTableViewCellDelegate + +extension SiteDesignContentCollectionViewController: CategorySectionTableViewCellDelegate { + func didSelectItemAt(_ position: Int, forCell cell: CategorySectionTableViewCell, slug: String) { + guard let sectionIndex = sections.firstIndex(where: { $0.categorySlug == slug }) else { return } + let design = sections[sectionIndex].designs[position] + + let previewVC = SiteDesignPreviewViewController( + siteDesign: design, + selectedPreviewDevice: previewViewSelectedPreviewDevice, + createsSite: createsSite, + onDismissWithDeviceSelected: { [weak self] device in + self?.previewViewSelectedPreviewDevice = device + cell.deselectItems() + }, + completion: completion + ) + + let navController = GutenbergLightNavigationController(rootViewController: previewVC) + navController.modalPresentationStyle = .pageSheet + navigationController?.present(navController, animated: true) + } + + func didDeselectItem(forCell cell: CategorySectionTableViewCell) {} + + func accessibilityElementDidBecomeFocused(forCell cell: CategorySectionTableViewCell) { + guard UIAccessibility.isVoiceOverRunning, let cellIndexPath = tableView.indexPath(for: cell) else { return } + tableView.scrollToRow(at: cellIndexPath, at: .middle, animated: true) + } +} diff --git a/WordPress/Classes/ViewRelated/Site Creation/Design Selection/SiteDesignSection.swift b/WordPress/Classes/ViewRelated/Site Creation/Design Selection/SiteDesignSection.swift new file mode 100644 index 000000000000..1fe1f4da4f1c --- /dev/null +++ b/WordPress/Classes/ViewRelated/Site Creation/Design Selection/SiteDesignSection.swift @@ -0,0 +1,19 @@ +import Foundation + +class SiteDesignSection: CategorySection { + var category: RemoteSiteDesignCategory + var designs: [RemoteSiteDesign] + + var categorySlug: String { category.slug } + var title: String { category.title } + var emoji: String? { category.emoji } + var description: String? { category.description } + var thumbnails: [Thumbnail] { designs } + var scrollOffset: CGPoint + + init(category: RemoteSiteDesignCategory, designs: [RemoteSiteDesign]) { + self.category = category + self.designs = designs + self.scrollOffset = .zero + } +} diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 875af7b8e1b1..333fe70069e8 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -2189,6 +2189,10 @@ C373D6EA280452F6008F8C26 /* SiteIntentDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C373D6E9280452F6008F8C26 /* SiteIntentDataTests.swift */; }; C387B7A22638D66F00BDEF86 /* PostAuthorSelectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E2462826277B7700B99EA6 /* PostAuthorSelectorViewController.swift */; }; C38C5D8127F61D2C002F517E /* MenuItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38C5D8027F61D2C002F517E /* MenuItemTests.swift */; }; + C395FB232821FE4400AE7C11 /* SiteDesignSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C395FB222821FE4400AE7C11 /* SiteDesignSection.swift */; }; + C395FB242821FE4B00AE7C11 /* SiteDesignSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C395FB222821FE4400AE7C11 /* SiteDesignSection.swift */; }; + C395FB262821FE7B00AE7C11 /* RemoteSiteDesign+Thumbnail.swift in Sources */ = {isa = PBXBuildFile; fileRef = C395FB252821FE7B00AE7C11 /* RemoteSiteDesign+Thumbnail.swift */; }; + C395FB272822148400AE7C11 /* RemoteSiteDesign+Thumbnail.swift in Sources */ = {isa = PBXBuildFile; fileRef = C395FB252821FE7B00AE7C11 /* RemoteSiteDesign+Thumbnail.swift */; }; C396C80B280F2401006FE7AC /* SiteDesignTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C396C80A280F2401006FE7AC /* SiteDesignTests.swift */; }; C3C39B0726F50D3900B1238D /* WordPressSupportSourceTag+Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C39B0626F50D3900B1238D /* WordPressSupportSourceTag+Editor.swift */; }; C3C39B0826F50D3900B1238D /* WordPressSupportSourceTag+Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C39B0626F50D3900B1238D /* WordPressSupportSourceTag+Editor.swift */; }; @@ -6958,6 +6962,8 @@ C373D6E628045281008F8C26 /* SiteIntentData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteIntentData.swift; sourceTree = ""; }; C373D6E9280452F6008F8C26 /* SiteIntentDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteIntentDataTests.swift; sourceTree = ""; }; C38C5D8027F61D2C002F517E /* MenuItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MenuItemTests.swift; path = Menus/MenuItemTests.swift; sourceTree = ""; }; + C395FB222821FE4400AE7C11 /* SiteDesignSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteDesignSection.swift; sourceTree = ""; }; + C395FB252821FE7B00AE7C11 /* RemoteSiteDesign+Thumbnail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RemoteSiteDesign+Thumbnail.swift"; sourceTree = ""; }; C396C80A280F2401006FE7AC /* SiteDesignTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteDesignTests.swift; sourceTree = ""; }; C3ABE791263099F7009BD402 /* WordPress 121.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 121.xcdatamodel"; sourceTree = ""; }; C3C39B0626F50D3900B1238D /* WordPressSupportSourceTag+Editor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WordPressSupportSourceTag+Editor.swift"; sourceTree = ""; }; @@ -10108,8 +10114,10 @@ isa = PBXGroup; children = ( 464688D5255C719500ECA61C /* Preview */, + C395FB222821FE4400AE7C11 /* SiteDesignSection.swift */, 46241C0E2540BD01002B8A12 /* SiteDesignStep.swift */, 46241C3A2540D483002B8A12 /* SiteDesignContentCollectionViewController.swift */, + C395FB252821FE7B00AE7C11 /* RemoteSiteDesign+Thumbnail.swift */, ); path = "Design Selection"; sourceTree = ""; @@ -17731,6 +17739,7 @@ 7462BFD02028C49800B552D8 /* ShareNoticeViewModel.swift in Sources */, 17A4A36920EE51870071C2CA /* Routes+Reader.swift in Sources */, 0807CB721CE670A800CDBDAC /* WPContentSearchHelper.swift in Sources */, + C395FB262821FE7B00AE7C11 /* RemoteSiteDesign+Thumbnail.swift in Sources */, 9A73B7152362FBAE004624A8 /* SiteStatsViewModel+AsyncBlock.swift in Sources */, 74AF4D7C1FE417D200E3EBFE /* PostUploadOperation.swift in Sources */, 37022D931981C19000F322B7 /* VerticallyStackedButton.m in Sources */, @@ -19151,6 +19160,7 @@ E1A03EE217422DCF0085D192 /* BlogToAccount.m in Sources */, C77FC90928009C7000726F00 /* OnboardingQuestionsPromptViewController.swift in Sources */, 8BE6F92C27EE27DB0008BDC7 /* BlogDashboardPostCardGhostCell.swift in Sources */, + C395FB232821FE4400AE7C11 /* SiteDesignSection.swift in Sources */, FA4F660525946B5F00EAA9F5 /* JetpackRestoreHeaderView.swift in Sources */, FEA6517B281C491C002EA086 /* BloggingPromptsService.swift in Sources */, 436D55DF210F866900CEAA33 /* StoryboardLoadable.swift in Sources */, @@ -21457,6 +21467,7 @@ FABB256B2602FC2C00C8785C /* InteractivePostView.swift in Sources */, FABB256C2602FC2C00C8785C /* LinearGradientView.swift in Sources */, FABB256D2602FC2C00C8785C /* WizardStep.swift in Sources */, + C395FB242821FE4B00AE7C11 /* SiteDesignSection.swift in Sources */, FABB256E2602FC2C00C8785C /* PostPostViewController.swift in Sources */, FABB256F2602FC2C00C8785C /* QuickStartSpotlightView.swift in Sources */, FABB25702602FC2C00C8785C /* ReaderReblogAction.swift in Sources */, @@ -21663,6 +21674,7 @@ FABB26172602FC2C00C8785C /* MediaExternalExporter.swift in Sources */, FABB26182602FC2C00C8785C /* RegisterDomainDetailsViewModel+SectionDefinitions.swift in Sources */, FABB26192602FC2C00C8785C /* ActionRow.swift in Sources */, + C395FB272822148400AE7C11 /* RemoteSiteDesign+Thumbnail.swift in Sources */, 9815D0B426B49A0600DF7226 /* Comment+CoreDataProperties.swift in Sources */, FABB261A2602FC2C00C8785C /* AppSettingsViewController.swift in Sources */, FABB261B2602FC2C00C8785C /* ReaderPostService+PostsV2.swift in Sources */, diff --git a/WordPress/WordPressTest/SiteCreation/SiteDesignTests.swift b/WordPress/WordPressTest/SiteCreation/SiteDesignTests.swift index 2b0bbd2396a5..c813fd7d2543 100644 --- a/WordPress/WordPressTest/SiteCreation/SiteDesignTests.swift +++ b/WordPress/WordPressTest/SiteCreation/SiteDesignTests.swift @@ -8,40 +8,6 @@ class SiteDesignTests: XCTestCase { return try! JSONDecoder().decode(RemoteSiteDesign.self, from: siteDesignPayload.data(using: .utf8)!) } - func testSiteDesignPrimaryButtonTextNotLastStep() throws { - - // given - let creator = SiteCreator() - let siteDesignStep = SiteDesignStep(creator: creator, isLastStep: false) - let expectedPrimaryTitle = "Choose" - - // when - let siteDesignVC = try XCTUnwrap(siteDesignStep.content as? SiteDesignContentCollectionViewController) - siteDesignVC.loadViewIfNeeded() - siteDesignVC.viewDidLoad() - - // then - let currentTitle = siteDesignVC.primaryActionButton.currentTitle - XCTAssertEqual(expectedPrimaryTitle, currentTitle) - } - - func testSiteDesignPrimaryButtonTextLastStep() throws { - - // given - let creator = SiteCreator() - let siteDesignStep = SiteDesignStep(creator: creator, isLastStep: true) - let expectedPrimaryTitle = "Create Site" - - // when - let siteDesignVC = try XCTUnwrap(siteDesignStep.content as? SiteDesignContentCollectionViewController) - siteDesignVC.loadViewIfNeeded() - siteDesignVC.viewDidLoad() - - // then - let currentTitle = siteDesignVC.primaryActionButton.currentTitle - XCTAssertEqual(expectedPrimaryTitle, currentTitle) - } - func testSiteDesignPreviewButtonTextNotLastStep() throws { // given @@ -73,19 +39,4 @@ class SiteDesignTests: XCTestCase { let currentTitle = siteDesignPreviewVC.primaryActionButton.currentTitle XCTAssertEqual(expectedPrimaryTitle, currentTitle) } - - /// Tests that the preview device on the Design view cannot be changed - func testSiteDesignPreviewDeviceIsAlwaysMobile() throws { - - // given - let siteDesignVC = SiteDesignContentCollectionViewController(createsSite: false) { _ in } - let expectedDevice = PreviewDeviceSelectionViewController.PreviewDevice.mobile - - // when - XCTAssertEqual(siteDesignVC.selectedPreviewDevice, expectedDevice) - siteDesignVC.selectedPreviewDevice = PreviewDeviceSelectionViewController.PreviewDevice.tablet - - // then - XCTAssertEqual(siteDesignVC.selectedPreviewDevice, expectedDevice) - } }