@@ -14,10 +14,7 @@ import { useBodyExpansion, useBodyExpansionApi } from './BodyExpansionProvider.j
14
14
* @typedef {enStrings } Strings
15
15
* @typedef {import('../../../types/new-tab').TrackerCompany } TrackerCompany
16
16
* @typedef {import('../../../types/new-tab').Expansion } Expansion
17
- * @typedef {import('../../../types/new-tab').Animation } Animation
18
17
* @typedef {import('../../../types/new-tab').PrivacyStatsData } PrivacyStatsData
19
- * @typedef {import('../../../types/new-tab').StatsConfig } StatsConfig
20
- * @typedef {import("./PrivacyStatsProvider.js").Events } Events
21
18
*/
22
19
23
20
/**
@@ -92,7 +89,7 @@ export function PrivacyStatsBody({ trackerCompanies, expansion = 'expanded', id
92
89
: sorted . slice ( 0 , DDG_STATS_DEFAULT_ROWS ) ;
93
90
94
91
return (
95
- < div id = { id } data-testid = "PrivacyStatsBody" >
92
+ < div id = { id } data-testid = "PrivacyStatsBody" class = { styles . body } >
96
93
< CompanyList rows = { visibleRows } largestTrackerCount = { largestTrackerCount } formatter = { formatter } />
97
94
< ListFooter all = { sorted } />
98
95
</ div >
@@ -116,6 +113,8 @@ export function CompanyList({ rows, formatter, largestTrackerCount }) {
116
113
} ;
117
114
const countText = formatter . format ( company . count ) ;
118
115
const displayName = displayNameForCompany ( company . displayName ) ;
116
+
117
+ // We don't actually render the 'other' row in the main loop - it's appended as a separate element later
119
118
if ( company . displayName === DDG_STATS_OTHER_COMPANY_IDENTIFIER ) {
120
119
return null ;
121
120
}
@@ -135,66 +134,42 @@ export function CompanyList({ rows, formatter, largestTrackerCount }) {
135
134
) ;
136
135
}
137
136
137
+ const states = /** @type {const } */ ( [
138
+ 'few_top+other' ,
139
+ 'few_top' ,
140
+ 'few_other' ,
141
+ 'many_top+other_collapsed' ,
142
+ 'many_top+other_expanded' ,
143
+ 'many_top_collapsed' ,
144
+ 'many_top_expanded' ,
145
+ ] ) ;
146
+
138
147
/**
139
148
* Renders a footer element that adapts its content and behavior based on provided data and state.
140
149
*
141
150
* @param {Object } props - The properties passed to the Footer component.
142
151
* @param {TrackerCompany[] } props.all - An array of data objects used to determine content and state of the footer.
143
152
*/
144
153
export function ListFooter ( { all } ) {
145
- const { t } = useTypedTranslationWith ( /** @type {Strings } */ ( { } ) ) ;
146
154
const expansion = useBodyExpansion ( ) ;
147
- const { showLess, showMore } = useBodyExpansionApi ( ) ;
148
155
149
- const toggleListExpansion = ( ) => {
150
- if ( expansion === 'collapsed' ) {
151
- showMore ( ) ;
152
- } else {
153
- showLess ( ) ;
154
- }
155
- } ;
156
-
157
- const states = /** @type {const } */ ( [
158
- 'few_top_other' ,
159
- 'few_top' ,
160
- 'few_other' ,
161
- 'many_top_other_collapsed' ,
162
- 'many_top_other_expanded' ,
163
- 'many_top_collapsed' ,
164
- 'many_top_expanded' ,
165
- ] ) ;
166
-
167
- const other = all [ all . length - 1 ] ;
168
- const hasOther = other ?. displayName === DDG_STATS_OTHER_COMPANY_IDENTIFIER ;
169
-
170
- const pill = (
171
- < div class = { styles . listExpander } >
172
- < ShowHideButtonPill
173
- onClick = { toggleListExpansion }
174
- label = { undefined }
175
- text = { expansion === 'collapsed' ? t ( 'ntp_show_more' ) : t ( 'ntp_show_less' ) }
176
- buttonAttrs = { {
177
- 'aria-expanded' : expansion === 'expanded' ,
178
- 'aria-pressed' : expansion === 'expanded' ,
179
- } }
180
- />
181
- </ div >
182
- ) ;
156
+ const lastElement = all [ all . length - 1 ] ;
157
+ const hasOtherRow = lastElement ?. displayName === DDG_STATS_OTHER_COMPANY_IDENTIFIER ;
183
158
184
159
/** @type {states[number] } */
185
160
const state = ( ( ) => {
186
- const comparison = hasOther ? DDG_STATS_DEFAULT_ROWS + 1 : DDG_STATS_DEFAULT_ROWS ;
161
+ const comparison = hasOtherRow ? DDG_STATS_DEFAULT_ROWS + 1 : DDG_STATS_DEFAULT_ROWS ;
187
162
if ( all . length <= comparison ) {
188
- if ( hasOther ) {
163
+ if ( hasOtherRow ) {
189
164
if ( all . length === 1 ) {
190
165
return 'few_other' ;
191
166
}
192
- return 'few_top_other ' ;
167
+ return 'few_top+other ' ;
193
168
}
194
169
return 'few_top' ;
195
170
} else {
196
- if ( hasOther ) {
197
- return expansion === 'collapsed' ? 'many_top_other_collapsed ' : 'many_top_other_expanded ' ;
171
+ if ( hasOtherRow ) {
172
+ return expansion === 'collapsed' ? 'many_top+other_collapsed ' : 'many_top+other_expanded ' ;
198
173
}
199
174
return expansion === 'collapsed' ? 'many_top_collapsed' : 'many_top_expanded' ;
200
175
}
@@ -203,39 +178,75 @@ export function ListFooter({ all }) {
203
178
const contents = ( ( ) => {
204
179
switch ( state ) {
205
180
case 'few_other' :
206
- case 'few_top_other' : {
207
- return < OtherText count = { other . count } /> ;
208
- }
209
- case 'few_top' :
210
- return null ;
211
- case 'many_top_collapsed' : {
212
- return pill ;
181
+ case 'few_top+other' : {
182
+ return < OtherText count = { lastElement . count } /> ;
213
183
}
214
- case 'many_top_expanded' : {
215
- return pill ;
184
+ case 'many_top_collapsed' :
185
+ case 'many_top_expanded' :
186
+ case 'many_top+other_collapsed' : {
187
+ return < PillShowMoreLess expansion = { expansion } /> ;
216
188
}
217
- case 'many_top_other_collapsed' : {
218
- return pill ;
219
- }
220
- case 'many_top_other_expanded' :
189
+ case 'many_top+other_expanded' :
221
190
return (
222
191
< Fragment >
223
- < OtherText count = { other . count } />
224
- { pill }
192
+ < OtherText count = { lastElement . count } />
193
+ < PillShowMoreLess expansion = { expansion } />
225
194
</ Fragment >
226
195
) ;
196
+ case 'few_top' :
227
197
default :
228
- return pill ;
198
+ return null ;
229
199
}
230
200
} ) ( ) ;
231
201
202
+ if ( contents === null ) return null ;
203
+
232
204
return (
233
205
< div class = { styles . listFooter } data-testid = "ListFooter" >
234
206
{ contents }
235
207
</ div >
236
208
) ;
237
209
}
238
210
211
+ /**
212
+ * Renders a pill component that toggles between "Show More" and "Show Less" states.
213
+ *
214
+ * @param {Object } props - The properties object.
215
+ * @param {Expansion } props.expansion - Indicates the current state of expansion.
216
+ */
217
+ function PillShowMoreLess ( { expansion } ) {
218
+ const { t } = useTypedTranslationWith ( /** @type {Strings } */ ( { } ) ) ;
219
+ const { showLess, showMore } = useBodyExpansionApi ( ) ;
220
+
221
+ const toggleListExpansion = ( ) => {
222
+ if ( expansion === 'collapsed' ) {
223
+ showMore ( ) ;
224
+ } else {
225
+ showLess ( ) ;
226
+ }
227
+ } ;
228
+
229
+ return (
230
+ < div class = { styles . listExpander } >
231
+ < ShowHideButtonPill
232
+ onClick = { toggleListExpansion }
233
+ label = { undefined }
234
+ text = { expansion === 'collapsed' ? t ( 'ntp_show_more' ) : t ( 'ntp_show_less' ) }
235
+ buttonAttrs = { {
236
+ 'aria-expanded' : expansion === 'expanded' ,
237
+ 'aria-pressed' : expansion === 'expanded' ,
238
+ } }
239
+ />
240
+ </ div >
241
+ ) ;
242
+ }
243
+
244
+ /**
245
+ * Generates a localized text element displaying a count.
246
+ *
247
+ * @param {Object } props - The parameters for generating the text.
248
+ * @param {number } props.count - The count to be included in the localized text.
249
+ */
239
250
export function OtherText ( { count } ) {
240
251
const { t } = useTypedTranslationWith ( /** @type {Strings } */ ( { } ) ) ;
241
252
const otherText = t ( 'stats_otherCount' , { count : String ( count ) } ) ;
0 commit comments