Skip to content

Commit 6a505ab

Browse files
authored
Fixed better ally for navigationlistitem (#885)
1 parent c838191 commit 6a505ab

19 files changed

Lines changed: 613 additions & 11 deletions

.github/copilot-instructions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ Format: `[Component/Feature] Description` (see existing entries for style)
254254
6. **Don't** use `FontFamily="monospace"` - use `FontFamily="Body"`, `"UI"`, or `"Header"`
255255
7. **Don't** forget to test on both platforms - behavior often differs
256256
8. **When reusing an existing component in a new context** (e.g. embedding a component inside a new handler, effect, or renderer), always read the component's existing canonical platform consumer first and replicate **all** of its event subscriptions, lifecycle hooks, and teardown logic. Never assume that rendering the component is sufficient. Components often have additional contracts (update events, binding context propagation, etc.) that only the canonical consumer reveals. Missing these causes silent regressions where the component renders correctly initially but fails to update afterwards.
257+
9. **Don't** put navigation-row accessibility defaults on the generic `ListItem`. `ListItem` is used for many patterns, including rows with switches or other interactive content. Defaults that make a row expose parent button semantics must be scoped to `NavigationListItem` or another navigation-specific component.
258+
10. **NavigationListItem accessibility**: The row itself must be the accessibility element and must have the platform Button trait so screen readers announce `"{Title}, Button"` / `"{Title}, knapp"`. Set the semantic description and button trait on the outer `NavigationListItem`, exclude internal visual children from the accessibility tree, and when the item is hosted in a `CollectionView`, propagate the trait to all relevant native cell/content wrappers because the focused accessibility element may be the cell, the cell content view, or the MAUI platform view.
257259

258260
## Key Files to Reference
259261
- `API/Builder/AppHostBuilderExtensions.cs` - Library initialization and handler registration

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## [60.2.4]
2+
- [NavigationListItem] Groups the row as one accessibility element, applies the platform button trait including when used in a CollectionView, and uses `Title` as the default screen reader description while still allowing consumers to override `SemanticProperties.Description`.
3+
14
## [60.2.3]
25
- [CollectionView] Fixed auto-hide-last-divider and auto-corner-radius bugs caused by stale cell caches during virtualization and dynamic item changes. The implementation now uses a stateless reset-then-apply strategy that correctly handles cell recycling, item additions, and removals.
36

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<dui:ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
5+
xmlns:dui="http://dips.com/mobile.ui"
6+
xmlns:helpers="clr-namespace:Components.Helpers"
7+
xmlns:localizedStrings="clr-namespace:Components.Resources.LocalizedStrings"
8+
xmlns:local="clr-namespace:Components.AccessibilitySamples.VoiceOverSamples.NavigationListItemSamples"
9+
x:Class="Components.AccessibilitySamples.VoiceOverSamples.NavigationListItemSamples.NavigationListItemAccessibilitySamples"
10+
x:DataType="local:NavigationListItemAccessibilitySamplesViewModel"
11+
Title="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Title}">
12+
13+
<dui:ContentPage.BindingContext>
14+
<local:NavigationListItemAccessibilitySamplesViewModel />
15+
</dui:ContentPage.BindingContext>
16+
17+
<dui:ScrollView Padding="{dui:Sizes size_4}">
18+
<dui:VerticalStackLayout Spacing="{dui:Sizes size_4}">
19+
<dui:Label Text="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Description}"
20+
Style="{dui:Styles Label=UI200}"
21+
TextColor="{dui:Colors color_text_subtle}" />
22+
23+
<dui:VerticalStackLayout Spacing="{dui:Sizes size_0}">
24+
<dui:NavigationListItem Title="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Standalone_Title}"
25+
Subtitle="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Standalone_Subtitle}"
26+
HasBottomDivider="True"
27+
Command="{helpers:NavigationCommand ContentPageType={x:Type local:NavigationListItemStandaloneSamples}}" />
28+
29+
<dui:NavigationListItem Title="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Collection_Title}"
30+
Subtitle="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Collection_Subtitle}"
31+
Command="{helpers:NavigationCommand ContentPageType={x:Type local:NavigationListItemCollectionViewSamples}}" />
32+
</dui:VerticalStackLayout>
33+
</dui:VerticalStackLayout>
34+
</dui:ScrollView>
35+
36+
</dui:ContentPage>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Components.AccessibilitySamples.VoiceOverSamples.NavigationListItemSamples;
2+
3+
public partial class NavigationListItemAccessibilitySamples
4+
{
5+
public NavigationListItemAccessibilitySamples()
6+
{
7+
InitializeComponent();
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System.Windows.Input;
2+
using Components.Resources.LocalizedStrings;
3+
using DIPS.Mobile.UI.MVVM;
4+
5+
namespace Components.AccessibilitySamples.VoiceOverSamples.NavigationListItemSamples;
6+
7+
public class NavigationListItemAccessibilitySamplesViewModel : ViewModel
8+
{
9+
public NavigationListItemAccessibilitySamplesViewModel()
10+
{
11+
ItemActivatedCommand = new Command(() => { });
12+
13+
CollectionItems =
14+
[
15+
new NavigationListItemSampleItem(LocalizedStrings.Buttons, LocalizedStrings.VoiceOver_NavigationListItem_Collection_ButtonSubtitle),
16+
new NavigationListItemSampleItem(LocalizedStrings.ListItems, LocalizedStrings.VoiceOver_NavigationListItem_Collection_ListItemSubtitle),
17+
new NavigationListItemSampleItem(LocalizedStrings.Pickers, LocalizedStrings.VoiceOver_NavigationListItem_Collection_PickerSubtitle),
18+
new NavigationListItemSampleItem(LocalizedStrings.TextFields, LocalizedStrings.VoiceOver_NavigationListItem_Collection_TextFieldSubtitle)
19+
];
20+
}
21+
22+
public ICommand ItemActivatedCommand { get; }
23+
24+
public IReadOnlyList<NavigationListItemSampleItem> CollectionItems { get; }
25+
}
26+
27+
public class NavigationListItemSampleItem
28+
{
29+
public NavigationListItemSampleItem(string title, string subtitle)
30+
{
31+
Title = title;
32+
Subtitle = subtitle;
33+
Command = new Command(() => { });
34+
}
35+
36+
public string Title { get; }
37+
38+
public string Subtitle { get; }
39+
40+
public ICommand Command { get; }
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<dui:ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
5+
xmlns:dui="http://dips.com/mobile.ui"
6+
xmlns:localizedStrings="clr-namespace:Components.Resources.LocalizedStrings"
7+
xmlns:local="clr-namespace:Components.AccessibilitySamples.VoiceOverSamples.NavigationListItemSamples"
8+
x:Class="Components.AccessibilitySamples.VoiceOverSamples.NavigationListItemSamples.NavigationListItemCollectionViewSamples"
9+
x:DataType="local:NavigationListItemAccessibilitySamplesViewModel"
10+
Title="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Collection_Title}">
11+
12+
<dui:ContentPage.BindingContext>
13+
<local:NavigationListItemAccessibilitySamplesViewModel />
14+
</dui:ContentPage.BindingContext>
15+
16+
<Grid RowDefinitions="Auto,*"
17+
RowSpacing="{dui:Sizes size_4}"
18+
Padding="{dui:Sizes size_4}">
19+
<dui:VerticalStackLayout Spacing="{dui:Sizes size_2}">
20+
<dui:Label Text="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Collection_Description}"
21+
Style="{dui:Styles Label=UI200}"
22+
TextColor="{dui:Colors color_text_subtle}" />
23+
</dui:VerticalStackLayout>
24+
25+
<dui:CollectionView Grid.Row="1"
26+
ItemsSource="{Binding CollectionItems}"
27+
ItemSpacing="0"
28+
dui:Layout.AutoHideLastDivider="True">
29+
<dui:CollectionView.ItemTemplate>
30+
<DataTemplate x:DataType="local:NavigationListItemSampleItem">
31+
<dui:NavigationListItem Title="{Binding Title}"
32+
Subtitle="{Binding Subtitle}"
33+
HasBottomDivider="True"
34+
Command="{Binding Command}" />
35+
</DataTemplate>
36+
</dui:CollectionView.ItemTemplate>
37+
</dui:CollectionView>
38+
</Grid>
39+
40+
</dui:ContentPage>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Components.AccessibilitySamples.VoiceOverSamples.NavigationListItemSamples;
2+
3+
public partial class NavigationListItemCollectionViewSamples
4+
{
5+
public NavigationListItemCollectionViewSamples()
6+
{
7+
InitializeComponent();
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<dui:ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
5+
xmlns:dui="http://dips.com/mobile.ui"
6+
xmlns:localizedStrings="clr-namespace:Components.Resources.LocalizedStrings"
7+
xmlns:local="clr-namespace:Components.AccessibilitySamples.VoiceOverSamples.NavigationListItemSamples"
8+
x:Class="Components.AccessibilitySamples.VoiceOverSamples.NavigationListItemSamples.NavigationListItemStandaloneSamples"
9+
x:DataType="local:NavigationListItemAccessibilitySamplesViewModel"
10+
Title="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Standalone_Title}">
11+
12+
<dui:ContentPage.BindingContext>
13+
<local:NavigationListItemAccessibilitySamplesViewModel />
14+
</dui:ContentPage.BindingContext>
15+
16+
<dui:ScrollView Padding="{dui:Sizes size_4}">
17+
<dui:VerticalStackLayout Spacing="{dui:Sizes size_4}">
18+
<dui:Label Text="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Standalone_Description}"
19+
Style="{dui:Styles Label=UI200}"
20+
TextColor="{dui:Colors color_text_subtle}" />
21+
22+
<dui:VerticalStackLayout Spacing="{dui:Sizes size_0}">
23+
<dui:NavigationListItem Title="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Appointment_Title}"
24+
Subtitle="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Appointment_Subtitle}"
25+
HasBottomDivider="True"
26+
Command="{Binding ItemActivatedCommand}" />
27+
28+
<dui:NavigationListItem Title="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_TestResults_Title}"
29+
Subtitle="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_TestResults_Subtitle}"
30+
HasBottomDivider="True"
31+
Command="{Binding ItemActivatedCommand}" />
32+
33+
<dui:NavigationListItem Title="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Medications_Title}"
34+
SemanticProperties.Description="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Medications_Description}"
35+
Command="{Binding ItemActivatedCommand}" />
36+
</dui:VerticalStackLayout>
37+
</dui:VerticalStackLayout>
38+
</dui:ScrollView>
39+
40+
</dui:ContentPage>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Components.AccessibilitySamples.VoiceOverSamples.NavigationListItemSamples;
2+
3+
public partial class NavigationListItemStandaloneSamples
4+
{
5+
public NavigationListItemStandaloneSamples()
6+
{
7+
InitializeComponent();
8+
}
9+
}

src/app/Components/AccessibilitySamples/VoiceOverSamples/VoiceOverSamples.xaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
xmlns:helpers="clr-namespace:Components.Helpers"
88
xmlns:voiceOverSamples="clr-namespace:Components.AccessibilitySamples.VoiceOverSamples"
99
xmlns:listItemInteractiveContentSamples="clr-namespace:Components.AccessibilitySamples.VoiceOverSamples.ListItemInteractiveContentSamples"
10+
xmlns:navigationListItemSamples="clr-namespace:Components.AccessibilitySamples.VoiceOverSamples.NavigationListItemSamples"
1011
xmlns:traitSamples="clr-namespace:Components.AccessibilitySamples.VoiceOverSamples.TraitSamples"
1112
x:Class="Components.AccessibilitySamples.VoiceOverSamples.VoiceOverSamples"
1213
Title="{x:Static localizedStrings:LocalizedStrings.VoiceOver}">
@@ -17,6 +18,11 @@
1718
<dui:ListItem Title="{x:Static localizedStrings:LocalizedStrings.VoiceOver_GroupChildren_Title}"
1819
HasBottomDivider="True"
1920
Command="{helpers:NavigationCommand ContentPageType={x:Type voiceOverSamples:GroupChildrenSamples}}" />
21+
22+
<dui:NavigationListItem Title="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Title}"
23+
Subtitle="{x:Static localizedStrings:LocalizedStrings.VoiceOver_NavigationListItem_Subtitle}"
24+
HasBottomDivider="True"
25+
Command="{helpers:NavigationCommand ContentPageType={x:Type navigationListItemSamples:NavigationListItemAccessibilitySamples}}" />
2026

2127
<dui:ListItem Title="{x:Static localizedStrings:LocalizedStrings.VoiceOver_ListItem_Interactive_Title}"
2228
Subtitle="{x:Static localizedStrings:LocalizedStrings.VoiceOver_ListItem_Interactive_Subtitle}"

0 commit comments

Comments
 (0)