Skip to content

Commit

Permalink
Merge pull request #3189 from lucacivale/3188
Browse files Browse the repository at this point in the history
Configure TabbedPage Title
  • Loading branch information
dansiegel authored Dec 15, 2024
2 parents 70b2560 + 0576566 commit bf70150
Show file tree
Hide file tree
Showing 8 changed files with 286 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ public interface ITabbedSegmentBuilder
/// <param name="segmentName">The name of the tab to select.</param>
/// <returns>The current instance of the <see cref="ITabbedSegmentBuilder"/>.</returns>
ITabbedSegmentBuilder SelectedTab(string segmentName);

/// <summary>
/// Sets the tabbed page title
/// </summary>
/// <param name="title">The title of the tabbed page.</param>
/// <returns>The current instance of the <see cref="ITabbedSegmentBuilder"/>.</returns>
ITabbedSegmentBuilder Title(string title);

/// <summary>
/// Adds a parameter to the current tab segment.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,16 @@ public ITabbedSegmentBuilder SelectedTab(string segmentName)
return AddSegmentParameter(KnownNavigationParameters.SelectedTab, segmentName);
}

public ITabbedSegmentBuilder Title(string title)
{
return AddSegmentParameter(KnownNavigationParameters.Title, title);
}

private string BuildSegment()
{
if (!_parameters.Any())
return SegmentName;

return SegmentName + _parameters.ToString();
}
}
}
5 changes: 5 additions & 0 deletions src/Maui/Prism.Maui/Navigation/KnownNavigationParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ public static class KnownNavigationParameters
/// Used to select an existing Tab when navigating to a TabbedPage.
/// </summary>
public const string SelectedTab = "selectedTab";

/// <summary>
/// Used to set the title to a TabbedPage.
/// </summary>
public const string Title = "title";

/// <summary>
/// Used to control the navigation stack. If <c>true</c> uses PopModalAsync, if <c>false</c> uses PopAsync.
Expand Down
4 changes: 4 additions & 0 deletions src/Maui/Prism.Maui/Navigation/PageNavigationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,10 @@ async Task ConfigureTabbedPage(TabbedPage tabbedPage, string segment, INavigatio
{
var tabParameters = UriParsingHelper.GetSegmentParameters(segment, parameters);

if (tabParameters.GetValue<string>(KnownNavigationParameters.Title) is string title)
{
tabbedPage.Title = title;
}
var tabsToCreate = tabParameters.GetValues<string>(KnownNavigationParameters.CreateTab);
foreach (var tabToCreateEncoded in tabsToCreate ?? Array.Empty<string>())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,26 @@ public void CreatesTabs_WithSingleContentPage()

Assert.Same(tabbedPage.Children[0], tabbedPage.CurrentPage);
}

[Fact]
public void CreatesTab_WithTitleAndSingleContentPage()
{
var mauiApp = CreateBuilder(prism => prism.CreateWindow(navigation =>
navigation.CreateBuilder()
.AddTabbedSegment(t =>
{
t.Title("MyTitle");
t.CreateTab("MockViewA");
})
.NavigateAsync())).Build();

var window = GetWindow(mauiApp);
Assert.IsType<TabbedPage>(window.Page);
var tabbedPage = (TabbedPage)window.Page;
Assert.Single(tabbedPage.Children);
Assert.IsType<MockViewA>(tabbedPage.Children[0]);
Assert.Equal("MyTitle", window.Page.Title);
}

[Fact]
public void CreatesTabs_WithNavigationPageAndContentPage()
Expand All @@ -53,6 +73,36 @@ public void CreatesTabs_WithNavigationPageAndContentPage()

Assert.Same(tabbedPage.Children[0], tabbedPage.CurrentPage);
}

[Fact]
public void CreatesTabs_WithNavigationPageAndContentPageTitlesSet()
{
var mauiApp = CreateBuilder(prism => prism.CreateWindow(navigation =>
navigation.CreateBuilder()
.AddTabbedSegment(t =>
{
t.Title("MyTitle");
t.CreateTab(ct => ct.AddNavigationPage().AddSegment("MockViewA"))
.CreateTab(ct => ct.AddNavigationPage().AddSegment("MockViewB"));
})
.NavigateAsync())).Build();
var window = GetWindow(mauiApp);
Assert.IsType<TabbedPage>(window.Page);
var tabbedPage = (TabbedPage)window.Page;

Assert.Equal(2, tabbedPage.Children.Count);
Assert.IsType<PrismNavigationPage>(tabbedPage.Children[0]);
var tab0 = (NavigationPage)tabbedPage.Children[0];
Assert.IsType<MockViewA>(tab0.CurrentPage);
Assert.IsType<PrismNavigationPage>(tabbedPage.Children[1]);
var tab1 = (NavigationPage)tabbedPage.Children[1];
Assert.IsType<MockViewB>(tab1.CurrentPage);

Assert.Same(tabbedPage.Children[0], tabbedPage.CurrentPage);
Assert.Equal("MyTitle", tabbedPage.Title);
Assert.Equal(MockViewA.ExpectedTitle, tab0.Title);
Assert.Equal(MockViewB.ExpectedTitle, tab1.Title);
}

[Fact]
public async Task NavigatesModally_FromChild_OfNavigationPageTab()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,53 @@ public async Task NavigationPage_UsesRootPageTitle_WithTabbedParent()
Assert.Equal(navPage.Title, navPage.RootPage.Title);
Assert.Equal(MockViewA.ExpectedTitle, navPage.Title);
}

[Fact]
public async Task NavigationPage_UsesTabbedPageTitle()
{
var mauiApp = CreateBuilder(prism => prism
.CreateWindow(n => n.CreateBuilder()
.AddNavigationPage()
.AddTabbedSegment(s => s
.CreateTab("MockViewA"))
.NavigateAsync()))
.Build();

var window = GetWindow(mauiApp);
Assert.IsAssignableFrom<NavigationPage>(window.Page);
var navPage = window.Page as NavigationPage;
Assert.IsAssignableFrom<TabbedPage>(navPage.RootPage);
var tabbed = navPage.RootPage as TabbedPage;

Assert.NotNull(tabbed);
Assert.Single(tabbed.Children);
Assert.Equal(navPage.Title, tabbed.Title);
}

[Fact]
public async Task NavigationPage_OverrideTabbedPageTitle()
{
var mauiApp = CreateBuilder(prism => prism
.CreateWindow(n => n.CreateBuilder()
.AddNavigationPage()
.AddTabbedSegment(s =>
{
s.CreateTab("MockViewA");
s.Title("MyTitle");
})
.NavigateAsync()))
.Build();

var window = GetWindow(mauiApp);
Assert.IsAssignableFrom<NavigationPage>(window.Page);
var navPage = window.Page as NavigationPage;
Assert.IsAssignableFrom<TabbedPage>(navPage.RootPage);
var tabbed = navPage.RootPage as TabbedPage;

Assert.NotNull(tabbed);
Assert.Single(tabbed.Children);
Assert.Equal("MyTitle", tabbed.Title);
}

[Fact]
public async Task Navigation_HasDefault_AnimatedIsNull()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,53 @@ public void GeneratesTabbedPageUriWithCreatedTabs()

Assert.Equal("TabbedPage?createTab=ViewA&createTab=ViewB&createTab=ViewC", uri.ToString());
}

[Fact]
public void GeneratesTabbedPageUriWithTitle()
{
var container = new TestContainer();
container.RegisterForNavigation<TabbedPage>();

var navigationService = new Mock<INavigationService>();
navigationService
.As<IRegistryAware>()
.Setup(x => x.Registry)
.Returns(container.Resolve<NavigationRegistry>());
var uri = navigationService.Object
.CreateBuilder()
.AddTabbedSegment(b =>
{
b.Title("MyTitle");
})
.Uri;

Assert.Equal("TabbedPage?title=MyTitle", uri.ToString());
}

[Fact]
public void GeneratesTabbedPageUriWithCreatedTabsWithTitle()
{
var container = new TestContainer();
container.RegisterForNavigation<TabbedPage>();

var navigationService = new Mock<INavigationService>();
navigationService
.As<IRegistryAware>()
.Setup(x => x.Registry)
.Returns(container.Resolve<NavigationRegistry>());
var uri = navigationService.Object
.CreateBuilder()
.AddTabbedSegment(b =>
{
b.CreateTab("ViewA")
.CreateTab("ViewB")
.CreateTab("ViewC");
b.Title("MyTitle");
})
.Uri;

Assert.Equal("TabbedPage?createTab=ViewA&createTab=ViewB&createTab=ViewC&title=MyTitle", uri.ToString());
}

[Fact]
public void GeneratesTabbedPageUriWithCreatedTabsWithParameters()
Expand Down Expand Up @@ -114,6 +161,39 @@ public void GeneratesTabbedPageUriWithCreatedTabsWithParameters()
Assert.Contains(parameters, x => HttpUtility.UrlDecode(x.Value.ToString()) == "ViewA?id=5");
Assert.Contains(parameters, x => HttpUtility.UrlDecode(x.Value.ToString()) == "ViewB?foo=bar");
}

[Fact]
public void GeneratesTabbedPageUriWithCreatedTabsWithParametersWithTitle()
{
var container = new TestContainer();
container.RegisterForNavigation<TabbedPage>();

var navigationService = new Mock<INavigationService>();
navigationService
.As<IRegistryAware>()
.Setup(x => x.Registry)
.Returns(container.Resolve<NavigationRegistry>());
var uri = navigationService.Object
.CreateBuilder()
.AddTabbedSegment(b =>
{
b.CreateTab("ViewA", t => t.AddParameter("id", 5))
.CreateTab("ViewB", t => t.AddParameter("foo", "bar"))
.CreateTab("ViewC");
b.Title("MyTitle");
})
.Uri;

Assert.Equal("TabbedPage?createTab=ViewA%3Fid%3D5&createTab=ViewB%3Ffoo%3Dbar&createTab=ViewC&title=MyTitle", uri.ToString());

var parameters = UriParsingHelper.GetSegmentParameters(uri.ToString());
Assert.Equal(4, parameters.Count);
Assert.True(parameters.ContainsKey(KnownNavigationParameters.CreateTab));
Assert.True(parameters.ContainsKey(KnownNavigationParameters.Title));

Assert.Contains(parameters, x => HttpUtility.UrlDecode(x.Value.ToString()) == "ViewA?id=5");
Assert.Contains(parameters, x => HttpUtility.UrlDecode(x.Value.ToString()) == "ViewB?foo=bar");
}

[Fact]
public void GeneratesCustomTabbedPageUriWithCreatedTabs()
Expand All @@ -139,6 +219,29 @@ public void GeneratesCustomTabbedPageUriWithCreatedTabs()

Assert.Equal("TabbedPageEmptyMock?createTab=ViewA&createTab=ViewB&createTab=ViewC", uri.ToString());
}

[Fact]
public void GeneratesCustomTabbedPageUriWithTitle()
{
var container = new TestContainer();
container.RegisterForNavigation<TabbedPage>();
container.RegisterForNavigation<TabbedPageEmptyMock>();

var navigationService = new Mock<INavigationService>();
navigationService
.As<IRegistryAware>()
.Setup(x => x.Registry)
.Returns(container.Resolve<NavigationRegistry>());
var uri = navigationService.Object
.CreateBuilder()
.AddTabbedSegment("TabbedPageEmptyMock", b =>
{
b.Title("MyTitle");
})
.Uri;

Assert.Equal("TabbedPageEmptyMock?title=MyTitle", uri.ToString());
}

[Fact]
public void GeneratesCustomTabbedPageUriWithCreatedTabsWithParameters()
Expand Down Expand Up @@ -171,6 +274,40 @@ public void GeneratesCustomTabbedPageUriWithCreatedTabsWithParameters()
Assert.Contains(parameters, x => HttpUtility.UrlDecode(x.Value.ToString()) == "ViewA?id=5");
Assert.Contains(parameters, x => HttpUtility.UrlDecode(x.Value.ToString()) == "ViewB?foo=bar");
}

[Fact]
public void GeneratesCustomTabbedPageUriWithCreatedTabsWithTitleWithParameters()
{
var container = new TestContainer();
container.RegisterForNavigation<TabbedPage>();
container.RegisterForNavigation<TabbedPageEmptyMock>();

var navigationService = new Mock<INavigationService>();
navigationService
.As<IRegistryAware>()
.Setup(x => x.Registry)
.Returns(container.Resolve<NavigationRegistry>());
var uri = navigationService.Object
.CreateBuilder()
.AddTabbedSegment("TabbedPageEmptyMock", b =>
{
b.CreateTab("ViewA", t => t.AddParameter("id", 5))
.CreateTab("ViewB", t => t.AddParameter("foo", "bar"))
.CreateTab("ViewC");
b.Title("MyTitle");
})
.Uri;

Assert.Equal("TabbedPageEmptyMock?createTab=ViewA%3Fid%3D5&createTab=ViewB%3Ffoo%3Dbar&createTab=ViewC&title=MyTitle", uri.ToString());

var parameters = UriParsingHelper.GetSegmentParameters(uri.ToString());
Assert.Equal(4, parameters.Count);
Assert.True(parameters.ContainsKey(KnownNavigationParameters.CreateTab));
Assert.True(parameters.ContainsKey(KnownNavigationParameters.Title));

Assert.Contains(parameters, x => HttpUtility.UrlDecode(x.Value.ToString()) == "ViewA?id=5");
Assert.Contains(parameters, x => HttpUtility.UrlDecode(x.Value.ToString()) == "ViewB?foo=bar");
}

[Fact]
public void GeneratesDeepLinkTabCreation()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,36 @@ public async void Navigate_FromContentPage_ToTabbedPage_SelectedTab()
Assert.NotNull(tabbedPage.CurrentPage);
Assert.IsType<Tab2Mock>(tabbedPage.CurrentPage);
}

[Fact]
public async void Navigate_FromContentPage_ToTabbedPage_WithTitleWithSelectedTab()
{
var navigationService = new PageNavigationServiceMock(_container, _app);
var rootPage = new ContentPage();
((IPageAware)navigationService).Page = rootPage;

await navigationService.NavigateAsync($"TabbedPage?{KnownNavigationParameters.SelectedTab}=Tab2&{KnownNavigationParameters.Title}=MyTitle");

var tabbedPage = rootPage.Navigation.ModalStack[0] as TabbedPageMock;
Assert.NotNull(tabbedPage);
Assert.NotNull(tabbedPage.CurrentPage);
Assert.IsType<Tab2Mock>(tabbedPage.CurrentPage);
Assert.Equal("MyTitle", tabbedPage.Title);
}

[Fact]
public async void Navigate_FromContentPage_ToTabbedPage_WithTitle()
{
var navigationService = new PageNavigationServiceMock(_container, _app);
var rootPage = new ContentPage();
((IPageAware)navigationService).Page = rootPage;

await navigationService.NavigateAsync($"TabbedPage?{KnownNavigationParameters.Title}=MyTitle");

var tabbedPage = rootPage.Navigation.ModalStack[0] as TabbedPageMock;
Assert.NotNull(tabbedPage);
Assert.Equal("MyTitle", tabbedPage.Title);
}

[Fact]
public async void Navigate_FromContentPage_ToTabbedPage_SelectedTab_NavigationPage()
Expand Down

0 comments on commit bf70150

Please sign in to comment.