Skip to content

Commit dedb234

Browse files
committed
refactor(ui): extract a shared drink-count pluralisation helper
The 'count == 1 ? drink : drinks' ternary was inlined at four call sites, past the extract-a-helper threshold in AGENTS.md. The failure mode is silent and accessibility-facing: a new label that forgets the ternary announces "1 drinks" to a screen reader and nothing fails. Two of the four sites had exactly that bug until review caught it in #506. Adds StringFormattingHelper.drinkCountLabel and routes all four sites through it. Labels are unchanged. Fixes #513 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ER7MeRfgMqxnaKBGDaSRWy
1 parent 7138c8c commit dedb234

5 files changed

Lines changed: 32 additions & 7 deletions

File tree

lib/screens/my_festival_screen.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ class _MyFestivalScreenState extends State<MyFestivalScreen> {
220220
Widget _buildSectionHeader(BuildContext context, String title, int count) {
221221
return Semantics(
222222
header: true,
223-
label: '$title section, $count ${count == 1 ? 'drink' : 'drinks'}',
223+
label: '$title section, ${StringFormattingHelper.drinkCountLabel(count)}',
224224
child: Padding(
225225
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
226226
// Use the theme's titleLarge (the app's Playfair "poster" voice) rather

lib/utils/string_formatting_helper.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,14 @@ class StringFormattingHelper {
1414
if (text.isEmpty) return text;
1515
return text[0].toUpperCase() + text.substring(1);
1616
}
17+
18+
/// Format a drink count with the correctly pluralised noun.
19+
///
20+
/// Used in screen-reader labels, where an inlined `count == 1 ? ... : ...`
21+
/// ternary has twice been forgotten and announced '1 drinks' (#506, #513).
22+
///
23+
/// Example: 0 -> '0 drinks', 1 -> '1 drink', 2 -> '2 drinks'
24+
static String drinkCountLabel(int count) {
25+
return '$count ${count == 1 ? 'drink' : 'drinks'}';
26+
}
1727
}

lib/widgets/drink_filter_sheets.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ class CategoryFilterSheet extends StatelessWidget {
113113
);
114114
return Semantics(
115115
label:
116-
'Filter by $formattedCategory, $count '
117-
'${count == 1 ? 'drink' : 'drinks'}',
116+
'Filter by $formattedCategory, '
117+
'${StringFormattingHelper.drinkCountLabel(count)}',
118118
value: isSelected ? 'Selected' : 'Not selected',
119119
selected: isSelected,
120120
button: true,
@@ -337,8 +337,8 @@ class StyleFilterSheet extends StatelessWidget {
337337
final isSelected = selectedStyles.contains(style);
338338
return Semantics(
339339
label:
340-
'Filter by $style, $count '
341-
'${count == 1 ? 'drink' : 'drinks'}',
340+
'Filter by $style, '
341+
'${StringFormattingHelper.drinkCountLabel(count)}',
342342
value: isSelected ? 'Selected' : 'Not selected',
343343
selected: isSelected,
344344
button: true,

lib/widgets/festival_header.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:flutter/material.dart';
22
import '../models/models.dart';
33
import '../providers/providers.dart';
4+
import '../utils/utils.dart';
45

56
/// App-bar title for the drinks screen: app icon, current festival name, the
67
/// drink count, and a coloured status badge.
@@ -17,8 +18,7 @@ class FestivalHeader extends StatelessWidget {
1718
provider.sortedFestivals,
1819
);
1920
final drinkCount = provider.drinks.length;
20-
final drinkCountLabel =
21-
'$drinkCount ${drinkCount == 1 ? 'drink' : 'drinks'}';
21+
final drinkCountLabel = StringFormattingHelper.drinkCountLabel(drinkCount);
2222

2323
// Fold the status into the label and exclude child semantics so screen
2424
// readers announce one coherent phrase instead of the name, count, and

test/string_formatting_helper_test.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,20 @@ void main() {
2828
expect(StringFormattingHelper.capitalizeFirst('a'), 'A');
2929
});
3030
});
31+
32+
group('drinkCountLabel', () {
33+
test('pluralises zero', () {
34+
expect(StringFormattingHelper.drinkCountLabel(0), '0 drinks');
35+
});
36+
37+
test('uses the singular for exactly one', () {
38+
expect(StringFormattingHelper.drinkCountLabel(1), '1 drink');
39+
});
40+
41+
test('pluralises counts above one', () {
42+
expect(StringFormattingHelper.drinkCountLabel(2), '2 drinks');
43+
expect(StringFormattingHelper.drinkCountLabel(147), '147 drinks');
44+
});
45+
});
3146
});
3247
}

0 commit comments

Comments
 (0)