fix(flutter/a11y assessments): h1 missing a11y from each page on the web app (#152198)

Adds Semantic Widget to wrap the contents of each page's AppBar title attribute and adds header: true and headingLevel: 1 as Semantic attributes so the content of the title is compiled as an h1 tag to meet accessibility guidelines that each page must have a h1 tag present. Also updates the test cases for the home page and each use case page to check for its respective h1.

[Before Screenshot - Accessibility Assessments Home Page](https://screenshot.googleplex.com/4i9LuiGwvLnEcZ8)
[Before Screenshot - Checkbox List Title -- note: each use case page is missing an h1](https://screenshot.googleplex.com/3qQjfqvAMTehRsm)
[After Screenshot - Accessibility Assessments Home Page](https://screenshot.googleplex.com/APSJJXBmwNBP35m)
[After Screenshot - Checkbox List Title-- note: change is similar across each Accessibility use case page](https://screenshot.googleplex.com/6EGgZnTusEgeN5L)

Fixes b/338035526
This commit is contained in:
Joy Serquiña 2024-08-13 15:22:28 -07:00 committed by GitHub
parent 87abed2b23
commit 85b3d97673
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 132 additions and 20 deletions

View File

@ -77,7 +77,9 @@ class HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Accessibility Assessments')),
appBar: AppBar(
title: Semantics(headingLevel: 1, child: const Text('Accessibility Assessments')),
),
body: Center(
child: ListView(
controller: scrollController,

View File

@ -31,7 +31,7 @@ class MainWidgetState extends State<MainWidget> {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('ActionChip'),
title: Semantics(headingLevel:1, child: const Text('ActionChip')),
),
body: Center(
child: Column(

View File

@ -50,7 +50,7 @@ class _MainWidgetState extends State<_MainWidget> {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('AutoComplete'),
title: Semantics(headingLevel: 1, child: const Text('AutoComplete Demo')),
),
body: Center(
child: Column(

View File

@ -25,12 +25,13 @@ class MainWidget extends StatefulWidget {
}
class MainWidgetState extends State<MainWidget> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('Badge'),
title: Semantics(headingLevel: 1, child: const Text('Badge Demo')),
),
body: const Center(
child: Badge(

View File

@ -31,7 +31,7 @@ class MainWidgetState extends State<MainWidget> {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('Card'),
title: Semantics(headingLevel: 1, child: const Text('Card')),
),
body: const Center(
child: Column(

View File

@ -28,7 +28,9 @@ class _MainWidgetState extends State<_MainWidget> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('CheckBoxListTile')),
appBar: AppBar(
title: Semantics(headingLevel: 1, child: const Text('CheckBoxListTile Demo')),
),
body: ListView(
children: <Widget>[
CheckboxListTile(

View File

@ -30,7 +30,7 @@ class _MainWidgetState extends State<_MainWidget> {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('DatePicker'),
title: Semantics(headingLevel: 1, child: const Text('DatePicker Demo')),
),
body: Center(
child: TextButton(

View File

@ -24,7 +24,7 @@ class _MainWidget extends StatelessWidget {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('Dialog'),
title: Semantics(headingLevel: 1, child: const Text('Dialog Demo')),
),
body: Center(
child: TextButton(

View File

@ -31,8 +31,8 @@ class _DrawerExampleState extends State<DrawerExample> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Drawer Example'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('Drawer Demo')),
),
endDrawer: Drawer(
child: ListView(

View File

@ -32,7 +32,7 @@ class _ExpansionTileExampleState extends State<ExpansionTileExample> {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('ExpansionTile'),
title: Semantics(headingLevel: 1, child: const Text('ExpansionTile')),
),
body: Column(
children: <Widget>[

View File

@ -60,7 +60,7 @@ class MainWidgetState extends State<MainWidget> {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('MaterialBanner'),
title: Semantics(headingLevel: 1, child: const Text('MaterialBanner Demo')),
),
body: Center(
child: ElevatedButton(

View File

@ -32,7 +32,7 @@ class MainWidgetState extends State<MainWidget> {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('NavigationBar'),
title: Semantics(headingLevel: 1, child: const Text('NavigationBar Demo')),
),
bottomNavigationBar: NavigationBar(
onDestinationSelected: (int index) {

View File

@ -62,8 +62,8 @@ class _NavigationDrawerExampleState extends State<NavigationDrawerExample> {
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: const Text('Navigation Drawer Example'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('Navigation Drawer Demo')),
),
body: SafeArea(
bottom: false,

View File

@ -36,7 +36,9 @@ class _MainWidgetState extends State<_MainWidget> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Radio button')),
appBar: AppBar(
title: Semantics(headingLevel: 1, child: const Text('Radio button demo'))
),
body: ListView(
children: <Widget>[
RadioListTile<SingingCharacter>(

View File

@ -33,7 +33,7 @@ class MainWidgetState extends State<MainWidget> {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('Slider'),
title: Semantics(headingLevel: 1, child: const Text('Slider demo')),
),
body: Center(
child: Semantics(

View File

@ -30,7 +30,7 @@ class MainWidgetState extends State<MainWidget> {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('SnackBar'),
title: Semantics(headingLevel: 1, child: const Text('SnackBar')),
),
body: Center(
child: Column(

View File

@ -32,7 +32,7 @@ class _SwitchListTileExampleState extends State<SwitchListTileExample> {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('SwitchListTile'),
title: Semantics(headingLevel: 1, child: const Text('SwitchListTile')),
),
body: Center(
child: Column(

View File

@ -32,7 +32,7 @@ class MainWidgetState extends State<MainWidget> {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('TextButton'),
title: Semantics(headingLevel: 1, child: const Text('TextButton Demo')),
),
body: Center(
child: Column(

View File

@ -25,7 +25,7 @@ class _MainWidget extends StatelessWidget {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('TextField'),
title: Semantics(headingLevel: 1, child: const Text('TextField demo')),
),
body: ListView(
children: <Widget>[

View File

@ -25,7 +25,7 @@ class _MainWidget extends StatelessWidget {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('TextField password'),
title: Semantics(headingLevel: 1, child: const Text('TextField password demo')),
),
body: ListView(
children: const <Widget>[

View File

@ -16,4 +16,11 @@ void main() {
expect(find.text('apple'), findsOneWidget);
});
testWidgets('auto complete has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, AutoCompleteUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel(RegExp('AutoComplete Demo'));
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -13,4 +13,11 @@ void main() {
expect(find.semantics.byLabel('5 new messages'), findsOne);
expect(find.semantics.byLabel('Messages'), findsOne);
});
testWidgets('badge has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, BadgeUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel(RegExp('Badge Demo'));
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -14,4 +14,11 @@ void main() {
expect(find.text('a check box list title'), findsOneWidget);
expect(find.text('a disabled check box list title'), findsOneWidget);
});
testWidgets('check box list has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, CheckBoxListTile());
final Finder findHeadingLevelOnes = find.bySemanticsLabel(RegExp('CheckBoxListTile Demo'));
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -17,4 +17,11 @@ void main() {
await tester.pumpAndSettle();
expect(find.byType(DatePickerDialog), findsOneWidget);
});
testWidgets('datepicker has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, DatePickerUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('DatePicker Demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -28,4 +28,11 @@ void main() {
await tester.pumpAndSettle();
expect(find.text('This is a typical dialog.'), findsNothing);
});
testWidgets('dialog has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, DialogUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('Dialog Demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -20,4 +20,11 @@ void main() {
expect(find.byType(Drawer), findsExactly(1));
});
testWidgets('drawer has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, DrawerUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('Drawer Demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -140,4 +140,11 @@ void main() {
expect(appScheme.inversePrimary.value,
MaterialDynamicColors.inversePrimary.getArgb(highContrastScheme));
});
testWidgets('a11y assessments home page has one h1 tag', (WidgetTester tester) async {
await tester.pumpWidget(const App());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('Accessibility Assessments');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -20,4 +20,11 @@ void main() {
await tester.pumpAndSettle();
expect(find.text('Hello, I am a Material Banner'), findsNothing);
});
testWidgets('material banner has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, MaterialBannerUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('MaterialBanner Demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -20,4 +20,11 @@ void main() {
await tester.pumpAndSettle();
expect(find.text('Page 3'), findsOneWidget);
});
testWidgets('navigation bar has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, NavigationBarUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('NavigationBar Demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -20,4 +20,11 @@ void main() {
expect(find.byType(NavigationDrawer), findsExactly(1));
});
testWidgets('navigation drawer has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, NavigationDrawerUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('Navigation Drawer Demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -14,4 +14,11 @@ void main() {
expect(find.text('Lafayette'), findsOneWidget);
expect(find.text('Jefferson'), findsOneWidget);
});
testWidgets('radio button demo page has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, RadioListTileUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('Radio button demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -26,4 +26,11 @@ void main() {
find.bySemanticsLabel('Accessibility Test Slider');
expect(semanticsWidget, findsOneWidget);
});
testWidgets('slider demo page has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, SliderUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('Slider demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -27,4 +27,11 @@ void main() {
await tester.pumpAndSettle();
expect(find.text('Clicked 2 time(s).'), findsOneWidget);
});
testWidgets('text button demo page has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, TextButtonUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('TextButton Demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -50,4 +50,11 @@ void main() {
expect(textField.decoration?.hintText, isNull);
}
});
testWidgets('text field password demo page has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, TextFieldPasswordUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('TextField password demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}

View File

@ -64,4 +64,11 @@ void main() {
find.bySemanticsLabel(RegExp(textFieldLabel));
expect(semanticsWidgets, findsExactly(2));
});
testWidgets('text field demo page has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, TextFieldUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('TextField demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
}