Skip to content

Commit 14d1782

Browse files
Add: Make client logos auto slide (#174)
* Add: Make client logos auto slide * update icon * fix test in services * fix test in text-content * fix: remove padding inside client component * feat: add fade out effect, make slide slower, and pause on hover --------- Co-authored-by: Hari Nugraha <haricnugraha@gmail.com>
1 parent e897c1b commit 14d1782

22 files changed

+534
-108
lines changed

app/[lang]/(hyperjump)/components/clients.tsx

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,36 @@
1+
"use client";
2+
13
import Image from "next/image";
24

35
type Client = {
4-
imageUrl: string;
56
name: string;
7+
imageUrl: string;
68
};
79

810
type ClientsProps = {
9-
isPriorityLoad?: boolean;
1011
clients: Client[];
1112
};
1213

13-
export function Clients({
14-
clients = [],
15-
isPriorityLoad = false
16-
}: ClientsProps) {
14+
export function Clients({ clients }: ClientsProps) {
15+
if (clients.length === 0) return null;
16+
17+
const repeatedClients = Array(4).fill(clients).flat();
18+
1719
return (
18-
<div className="relative px-8 py-4 md:px-20">
19-
<div className="flex flex-wrap items-center justify-center gap-8">
20-
{clients.map(({ imageUrl, name }) => (
21-
<Image
22-
key={name}
23-
src={imageUrl}
24-
alt={name}
25-
height={36}
26-
width={120}
27-
className="h-6 w-auto object-contain opacity-60 grayscale transition hover:opacity-100 sm:h-7"
28-
priority={isPriorityLoad}
29-
/>
30-
))}
20+
<div className="relative overflow-hidden py-6">
21+
<div className="marquee">
22+
<div className="marquee__track">
23+
{repeatedClients.map(({ imageUrl, name }, index) => (
24+
<Image
25+
key={`${name}-${index}`}
26+
src={imageUrl}
27+
alt={name}
28+
width={120}
29+
height={36}
30+
className="h-6 w-auto object-contain sm:h-7"
31+
/>
32+
))}
33+
</div>
3134
</div>
3235
</div>
3336
);

app/[lang]/(hyperjump)/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ function Hero({ lang }: HeroProps) {
137137
</p>
138138
</div>
139139
<div className="relative top-15">
140-
<Clients clients={data.clients} isPriorityLoad />
140+
<Clients clients={data.clients} />
141141
</div>
142142
</div>
143143
</section>

app/globals.css

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,35 @@
183183
rgba(21, 19, 55, 0) 23.58%
184184
);
185185
}
186+
187+
.marquee {
188+
overflow: hidden;
189+
width: 100%;
190+
mask-image: linear-gradient(
191+
to right,
192+
transparent,
193+
#020f15 10%,
194+
#020f15 90%,
195+
transparent
196+
);
197+
}
198+
199+
.marquee:hover .marquee__track {
200+
animation-play-state: paused;
201+
}
202+
203+
.marquee__track {
204+
display: flex;
205+
gap: 3rem;
206+
width: max-content;
207+
animation: marquee 60s linear infinite;
208+
}
209+
210+
@keyframes marquee {
211+
from {
212+
transform: translateX(0);
213+
}
214+
to {
215+
transform: translateX(-25%);
216+
}
217+
}

data.json

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"imageUrl": "/images/clients/bank-tabungan-negara.svg"
1010
},
1111
{
12-
"name": "Eka Mas Republik",
12+
"name": "Eka Mas Republik (MyRepublic)",
1313
"imageUrl": "/images/clients/my-republic.svg"
1414
},
1515
{
@@ -23,6 +23,43 @@
2323
{
2424
"name": "Smartfren",
2525
"imageUrl": "/images/clients/smartfren.svg"
26+
},
27+
28+
{
29+
"name": "IDN Media",
30+
"imageUrl": "/images/clients/idn-media.svg"
31+
},
32+
{
33+
"name": "Ismaya Group",
34+
"imageUrl": "/images/clients/ismaya.svg"
35+
},
36+
{
37+
"name": "Aruna",
38+
"imageUrl": "/images/clients/aruna.svg"
39+
},
40+
{
41+
"name": "Cashbac",
42+
"imageUrl": "/images/clients/cashbac.svg"
43+
},
44+
{
45+
"name": "Ausvet",
46+
"imageUrl": "/images/clients/ausvet.svg"
47+
},
48+
{
49+
"name": "CoHive",
50+
"imageUrl": "/images/clients/cohive.svg"
51+
},
52+
{
53+
"name": "Trimegah Sekuritas",
54+
"imageUrl": "/images/clients/trimegah.svg"
55+
},
56+
{
57+
"name": "Bali United",
58+
"imageUrl": "/images/clients/bali-united.svg"
59+
},
60+
{
61+
"name": "1Engage",
62+
"imageUrl": "/images/clients/1engage.svg"
2663
}
2764
],
2865
"description": "Your partner in building reliable, modern software.",

e2e/homepage.spec.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,31 +39,29 @@ test.describe("Homepage", () => {
3939
});
4040

4141
// Test Structure / UI Sections
42-
test("Hero Section: should display hero title, subtitle, and CTA button correctly", async ({
43-
page
44-
}) => {
42+
test("Hero Section: should display partner logos", async ({ page }) => {
4543
await page.goto(URL);
4644

47-
// Hero title and subtitle
48-
const heroHeading = page.getByRole("heading", {
49-
name: "Your partner in building"
50-
});
51-
await expect(heroHeading).toBeVisible();
52-
53-
const heroSubtitle = page.getByText("We help organizations deliver");
54-
await expect(heroSubtitle).toBeVisible();
55-
56-
// Partner logos
5745
const partnerLogos = [
5846
"Amman Mineral Internasional",
5947
"Bank Tabungan Negara",
60-
"Eka Mas Republik",
48+
"Eka Mas Republik (MyRepublic)",
6149
"Sinar Mas Digital Day",
62-
"Smartfren"
50+
"SMDV",
51+
"Smartfren",
52+
"IDN Media",
53+
"Ismaya Group",
54+
"Aruna",
55+
"Cashbac",
56+
"Ausvet",
57+
"CoHive",
58+
"Trimegah Sekuritas",
59+
"Bali United",
60+
"1Engage"
6361
];
6462

6563
for (const name of partnerLogos) {
66-
const logo = page.getByRole("img", { name });
64+
const logo = page.locator(`img[alt="${name}"]`).first();
6765
await expect(logo).toBeVisible();
6866
}
6967
});

e2e/shared-test.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,20 +109,27 @@ async function expectAllImagesLoaded(page: Page) {
109109

110110
for (let i = 0; i < count; i++) {
111111
const el = images.nth(i);
112-
const tag = await el.evaluate((n: any) => n.tagName.toLowerCase());
112+
const { tag, isAnimated } = await el.evaluate((n: any) => {
113+
const tag = n.tagName.toLowerCase();
114+
const isAnimated = !!n.closest(".marquee");
115+
return { tag, isAnimated };
116+
});
113117

114118
if (tag === "img" || tag === "image") {
115-
await el.scrollIntoViewIfNeeded();
119+
if (!isAnimated) {
120+
await el.scrollIntoViewIfNeeded();
121+
}
122+
116123
await expect(el, `Image #${i} not visible`).toBeVisible();
117124

118125
const { isLoaded, src } = await el.evaluate((img: any) => {
119-
const nw = img.naturalWidth ?? 1;
120-
const nh = img.naturalHeight ?? 1;
121-
const isSVG = img.href?.baseVal;
122-
123-
// Allow lazy images that are replaced with placeholder
124-
const isLoaded = (nw > 0 && nh > 0) || isSVG;
125126
const src = img.currentSrc || img.src || img.href?.baseVal || "(none)";
127+
const isSvgFile = typeof src === "string" && src.endsWith(".svg");
128+
const rect = img.getBoundingClientRect();
129+
const hasLayoutSize = rect.width > 0 && rect.height > 0;
130+
const hasRasterSize =
131+
(img.naturalWidth ?? 0) > 0 && (img.naturalHeight ?? 0) > 0;
132+
const isLoaded = hasRasterSize || (isSvgFile && hasLayoutSize);
126133

127134
return { isLoaded, src };
128135
});

e2e/text-content.spec.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,23 @@ test.describe("Text and Content", () => {
8585
const partners = [
8686
"Amman Mineral Internasional",
8787
"Bank Tabungan Negara",
88-
"Eka Mas Republik",
88+
"Eka Mas Republik (MyRepublic)",
8989
"Sinar Mas Digital Day",
90-
"Smartfren"
90+
"SMDV",
91+
"Smartfren",
92+
"IDN Media",
93+
"Ismaya Group",
94+
"Aruna",
95+
"Cashbac",
96+
"Ausvet",
97+
"CoHive",
98+
"Trimegah Sekuritas",
99+
"Bali United",
100+
"1Engage"
91101
];
92102

93103
for (const partner of partners) {
94-
const logo = page.getByRole("img", { name: partner });
104+
const logo = page.locator(`img[alt="${partner}"]`).first();
95105
await expect(logo).toBeVisible({ timeout: 10_000 });
96106
}
97107

public/images/clients/1engage.svg

Lines changed: 10 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)