Skip to content

Commit 67cd295

Browse files
committed
more demos
1 parent 392d3ca commit 67cd295

File tree

4 files changed

+122
-127
lines changed

4 files changed

+122
-127
lines changed

special-pages/pages/new-tab/app/privacy-stats/components/PrivacyStats.examples.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Fragment, h } from 'preact';
2-
import { PrivacyStatsMockProvider } from '../mocks/PrivacyStatsMockProvider.js';
3-
import { PrivacyStatsBody } from './PrivacyStats.js';
2+
import { BodyExpansionMockProvider, PrivacyStatsMockProvider } from '../mocks/PrivacyStatsMockProvider.js';
3+
import { ListFooter, PrivacyStatsBody } from './PrivacyStats.js';
44
import { privacyStatsMocks } from '../mocks/privacy-stats.mocks.js';
55
import { PrivacyStatsConsumer } from './PrivacyStatsConsumer.js';
66

@@ -47,6 +47,26 @@ export const privacyStatsExamples = {
4747
<PrivacyStatsBody trackerCompanies={privacyStatsMocks.few.trackerCompanies} id={'example-stats.list'} expansion={'expanded'} />
4848
),
4949
},
50+
'stats.footer': {
51+
factory: () => {
52+
const data = privacyStatsMocks.manyTopAndOther.trackerCompanies;
53+
return (
54+
<Fragment>
55+
<h2>Collapsed</h2>
56+
<br />
57+
<BodyExpansionMockProvider bodyExpansion={'collapsed'}>
58+
<ListFooter all={data} />
59+
</BodyExpansionMockProvider>
60+
<br />
61+
<h2>Expanded</h2>
62+
<br />
63+
<BodyExpansionMockProvider bodyExpansion={'expanded'}>
64+
<ListFooter all={data} />
65+
</BodyExpansionMockProvider>
66+
</Fragment>
67+
);
68+
},
69+
},
5070
};
5171

5272
export const otherPrivacyStatsExamples = {

special-pages/pages/new-tab/app/privacy-stats/components/PrivacyStats.js

Lines changed: 72 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@ import { useBodyExpansion, useBodyExpansionApi } from './BodyExpansionProvider.j
1414
* @typedef {enStrings} Strings
1515
* @typedef {import('../../../types/new-tab').TrackerCompany} TrackerCompany
1616
* @typedef {import('../../../types/new-tab').Expansion} Expansion
17-
* @typedef {import('../../../types/new-tab').Animation} Animation
1817
* @typedef {import('../../../types/new-tab').PrivacyStatsData} PrivacyStatsData
19-
* @typedef {import('../../../types/new-tab').StatsConfig} StatsConfig
20-
* @typedef {import("./PrivacyStatsProvider.js").Events} Events
2118
*/
2219

2320
/**
@@ -92,7 +89,7 @@ export function PrivacyStatsBody({ trackerCompanies, expansion = 'expanded', id
9289
: sorted.slice(0, DDG_STATS_DEFAULT_ROWS);
9390

9491
return (
95-
<div id={id} data-testid="PrivacyStatsBody">
92+
<div id={id} data-testid="PrivacyStatsBody" class={styles.body}>
9693
<CompanyList rows={visibleRows} largestTrackerCount={largestTrackerCount} formatter={formatter} />
9794
<ListFooter all={sorted} />
9895
</div>
@@ -116,6 +113,8 @@ export function CompanyList({ rows, formatter, largestTrackerCount }) {
116113
};
117114
const countText = formatter.format(company.count);
118115
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
119118
if (company.displayName === DDG_STATS_OTHER_COMPANY_IDENTIFIER) {
120119
return null;
121120
}
@@ -135,66 +134,42 @@ export function CompanyList({ rows, formatter, largestTrackerCount }) {
135134
);
136135
}
137136

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+
138147
/**
139148
* Renders a footer element that adapts its content and behavior based on provided data and state.
140149
*
141150
* @param {Object} props - The properties passed to the Footer component.
142151
* @param {TrackerCompany[]} props.all - An array of data objects used to determine content and state of the footer.
143152
*/
144153
export function ListFooter({ all }) {
145-
const { t } = useTypedTranslationWith(/** @type {Strings} */ ({}));
146154
const expansion = useBodyExpansion();
147-
const { showLess, showMore } = useBodyExpansionApi();
148155

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;
183158

184159
/** @type {states[number]} */
185160
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;
187162
if (all.length <= comparison) {
188-
if (hasOther) {
163+
if (hasOtherRow) {
189164
if (all.length === 1) {
190165
return 'few_other';
191166
}
192-
return 'few_top_other';
167+
return 'few_top+other';
193168
}
194169
return 'few_top';
195170
} 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';
198173
}
199174
return expansion === 'collapsed' ? 'many_top_collapsed' : 'many_top_expanded';
200175
}
@@ -203,39 +178,75 @@ export function ListFooter({ all }) {
203178
const contents = (() => {
204179
switch (state) {
205180
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} />;
213183
}
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} />;
216188
}
217-
case 'many_top_other_collapsed': {
218-
return pill;
219-
}
220-
case 'many_top_other_expanded':
189+
case 'many_top+other_expanded':
221190
return (
222191
<Fragment>
223-
<OtherText count={other.count} />
224-
{pill}
192+
<OtherText count={lastElement.count} />
193+
<PillShowMoreLess expansion={expansion} />
225194
</Fragment>
226195
);
196+
case 'few_top':
227197
default:
228-
return pill;
198+
return null;
229199
}
230200
})();
231201

202+
if (contents === null) return null;
203+
232204
return (
233205
<div class={styles.listFooter} data-testid="ListFooter">
234206
{contents}
235207
</div>
236208
);
237209
}
238210

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+
*/
239250
export function OtherText({ count }) {
240251
const { t } = useTypedTranslationWith(/** @type {Strings} */ ({}));
241252
const otherText = t('stats_otherCount', { count: String(count) });

special-pages/pages/new-tab/app/privacy-stats/components/PrivacyStats.module.css

Lines changed: 10 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,6 @@
4747
justify-content: center;
4848
padding-top: 0.5px;
4949

50-
[data-animation="heart02"] & {
51-
animation: heart02 2s infinite;
52-
}
53-
[data-animation="heart01"] & {
54-
animation: heart01 1.483s infinite cubic-bezier(0.67, 0, 0.33, 1);
55-
}
56-
5750
/* In this variant, everything is moved over to align with the body items (which are different to the stats */
5851
.activityVariant & {
5952
width: 24px;
@@ -67,49 +60,6 @@
6760
}
6861
}
6962

70-
@keyframes heart01 {
71-
0% {
72-
transform: scale(1);
73-
}
74-
75-
4.5% { /* 4/89 frames */
76-
transform: scale(1.25);
77-
}
78-
79-
16.85% { /* 15/89 frames */
80-
transform: scale(1);
81-
}
82-
83-
23.6% { /* 21/89 frames */
84-
transform: scale(1.1);
85-
}
86-
87-
100% { /* 89/89 frames */
88-
transform: scale(1);
89-
}
90-
}
91-
92-
@keyframes heart02 {
93-
0% {
94-
transform: scale(1);
95-
}
96-
5.6% {
97-
transform: scale(1.2);
98-
}
99-
11.2% {
100-
transform: scale(1);
101-
}
102-
22.4% {
103-
transform: scale(1.1);
104-
}
105-
33.7% {
106-
transform: scale(1);
107-
}
108-
100% {
109-
transform: scale(1);
110-
}
111-
}
112-
11363
.title {
11464
grid-area: title;
11565
font-size: var(--title-2-font-size);
@@ -144,6 +94,11 @@
14494
}
14595
}
14696

97+
.body {
98+
display: grid;
99+
grid-row-gap: var(--sp-3);
100+
}
101+
147102
.list {
148103
display: grid;
149104
grid-template-columns: auto;
@@ -209,8 +164,11 @@
209164
}
210165
}
211166

212-
.listFooter:not(:empty) {
213-
margin-top: var(--sp-3);
167+
.listFooter {
168+
display: flex;
169+
&:has(.otherTrackersRow) .listExpander {
170+
margin-left: auto;
171+
}
214172
}
215173

216174
.otherTrackersRow {
@@ -279,8 +237,4 @@
279237
}
280238

281239
.listExpander {
282-
button {
283-
color: var(--ntp-text-muted);
284-
opacity: 1;
285-
}
286240
}

special-pages/pages/new-tab/app/privacy-stats/mocks/PrivacyStatsMockProvider.js

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,21 +69,31 @@ export function PrivacyStatsMockProvider({
6969
}
7070
}, [state.config?.expansion]);
7171

72+
return (
73+
<PrivacyStatsContext.Provider value={{ state, toggle }}>
74+
<PrivacyStatsDispatchContext.Provider value={send}>
75+
<BodyExpansionMockProvider>{children}</BodyExpansionMockProvider>
76+
</PrivacyStatsDispatchContext.Provider>
77+
</PrivacyStatsContext.Provider>
78+
);
79+
}
80+
81+
/**
82+
* @param {Object} props - The props object containing the data.
83+
* @param {import("preact").ComponentChild} [props.children] - The children elements to be rendered.
84+
* @param {Expansion} [props.bodyExpansion]
85+
*/
86+
export function BodyExpansionMockProvider({ children, bodyExpansion = 'collapsed' }) {
7287
const [bodyExpansionState, setBodyExpansion] = useState(bodyExpansion);
7388
const showMore = useCallback(() => {
7489
setBodyExpansion('expanded');
7590
}, []);
7691
const showLess = useCallback(() => {
7792
setBodyExpansion('collapsed');
7893
}, []);
79-
8094
return (
81-
<PrivacyStatsContext.Provider value={{ state, toggle }}>
82-
<PrivacyStatsDispatchContext.Provider value={send}>
83-
<BodyExpansionContext.Provider value={bodyExpansionState}>
84-
<BodyExpansionApiContext.Provider value={{ showMore, showLess }}>{children}</BodyExpansionApiContext.Provider>
85-
</BodyExpansionContext.Provider>
86-
</PrivacyStatsDispatchContext.Provider>
87-
</PrivacyStatsContext.Provider>
95+
<BodyExpansionContext.Provider value={bodyExpansionState}>
96+
<BodyExpansionApiContext.Provider value={{ showMore, showLess }}>{children}</BodyExpansionApiContext.Provider>
97+
</BodyExpansionContext.Provider>
8898
);
8999
}

0 commit comments

Comments
 (0)