Skip to content

Commit 06e7f05

Browse files
RecxsmacxAceTheCreatorthulieblack
authored
feat: implement horizontal footer layout with modern social media icons (#763)
* feat: implement horizontal footer layout with modern social media icons - Refactor footer to use consistent horizontal layout across all screen sizes - Create new Socials component with custom styled circular icons - Add interactive tooltips with platform-specific hover colors - Update Cypress tests to include social media icon testing - Add CSS styles for tooltip animations and hover effects - Replace image-based icons with inline SVG for better performance Changes: - components/Footer/footer.tsx: horizontal layout with flex-wrap - components/Footer/socials.tsx: new component with LinkedIn, GitHub, Twitter, YouTube - cypress/e2e/Footer.cy.ts: enhanced test suite with social media tests - styles/globals.css: tooltip styles and hover animations * fix: add CSS support for tooltip visibility in Cypress tests - Add .show-for-test class support to make tooltips visible during testing - Use !important to override default opacity: 0 for test scenarios - All 12 Cypress tests now passing including tooltip visibility tests * feat: make footer responsive for mobile devices * feat: make footer responsive for mobile devices * Update footer.tsx with social components * fix: Add JSX import to socials component and install react-dom types - Fix TypeScript error in socials.tsx by importing JSX - Install @types/react-dom to resolve popup component build error - Maintain functionality while ensuring type safety * feat: Add AsyncAPI Conference 2025 PDF document - Add conf 2025.pdf to cypress/Downloads directory - Document contains conference information and schedule - Referenced in Cypress tests for download functionality * chore: sync package-lock.json with package.json * applied for each in cypress tests * Delete cypress/Downloads/conf 2025.pdf * imported pre available socials svgs * dependency issue resolved * fixed formatting issues * applie data-test across the footer file and the cy file * cypress fixed --------- Co-authored-by: Azeez Elegbede <40604284+AceTheCreator@users.noreply.github.com> Co-authored-by: V Thulisile Sibanda <66913810+thulieblack@users.noreply.github.com>
1 parent 66ee720 commit 06e7f05

File tree

4 files changed

+205
-46
lines changed

4 files changed

+205
-46
lines changed

components/Footer/footer.tsx

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,36 @@
11
import React, { JSX } from 'react';
22
import ILink from '../illustration/link';
3-
import socials, { SocialWithIcon } from '../../config/socials';
3+
import Socials from "./socials";
44

55
function Footer(): JSX.Element {
66
return (
77
<div className="container" data-test="footer">
88
<div
9-
className="w-full flex justify-between items-center p-4 sm:flex-col sm:gap-3"
9+
className="w-full flex flex-row justify-between items-center p-2 md:p-4 gap-4 sm:flex-col sm:gap-3"
1010
data-test="footer-asyncAPI-logo"
1111
>
12-
<div className="mt-2 text-[14px] text-gray-100 ">
12+
{/* Code of Conduct Section */}
13+
<div className="text-[16px] md:text-[18px] text-gray-100 flex items-center flex-shrink-0 sm:hidden">
1314
<a
1415
href="https://github.com/asyncapi/community/blob/master/CODE_OF_CONDUCT.md"
1516
target="_blank"
1617
rel="noreferrer"
1718
className="hover:underline text-white duration-200 ease-in-out flex items-center"
1819
data-test="code-of-conduct"
1920
>
20-
<span> Code of Conduct </span>
21-
<span>
22-
<ILink className="w-4 ml-2" fill="white" />
23-
</span>
21+
<span>Code of Conduct</span>
22+
<ILink className="w-3 md:w-5 ml-1 md:ml-2" fill="white" />
2423
</a>
2524
</div>
26-
<div></div>
27-
<div className="flex items-center justify-between sm:flex-col sm:items-center">
28-
<div className="text-white text-center">
29-
Made with ❤️ by AsyncAPI contributors. By the community for the
30-
community!
31-
</div>
32-
<div className="w-[0.9px] h-4 bg-white ml-4 sm:hidden" />
33-
<div className="ml-4 flex justify-between items-center gap-2 sm:mt-4">
34-
{socials.map((social: SocialWithIcon) => {
35-
const IconComponent = social.icon;
36-
return (
37-
<a
38-
key={social.name}
39-
href={social.href}
40-
target="_blank"
41-
rel="noreferrer"
42-
className="rounded-lg flex items-center justify-center hover:border-[#AD20E2] duration-150 ease-in-out"
43-
data-test={`footer-${social.name}`}
44-
>
45-
<IconComponent className="w-[20px] h-[20px]" fill="white" />
46-
</a>
47-
);
48-
})}
25+
26+
{/* "Made with ❤️" and Socials Section - Grouped together */}
27+
<div className="flex flex-row items-center gap-4 text-center sm:flex-col sm:items-center">
28+
<span className="text-white text-[16px] md:text-[18px] leading-tight">
29+
Made with ❤️ by AsyncAPI contributors. By the community for the community!
30+
</span>
31+
<div className="w-[1px] h-6 bg-white sm:hidden" />
32+
<div className="flex items-center gap-2 flex-shrink-0">
33+
<Socials />
4934
</div>
5035
</div>
5136
</div>

components/Footer/socials.tsx

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import React, { JSX } from 'react';
2+
import LinkedIn from '../illustration/Socials/LinkedIn';
3+
import Github from '../illustration/Socials/Github';
4+
import X from '../illustration/Socials/X';
5+
import Youtube from '../illustration/Socials/Youtube';
6+
7+
function Socials(): JSX.Element {
8+
return (
9+
<div className="social-wrapper" data-test="social-wrapper">
10+
<ul className="inline-flex list-none justify-center font-['Poppins',sans-serif]">
11+
<li className="icon linkedin relative bg-white rounded-full m-[5px] w-[35px] h-[35px] text-lg flex justify-center items-center flex-col shadow-md cursor-pointer transition-all duration-200 hover:bg-[#0077b5] hover:text-white" data-test="footer-icon-linkedin">
12+
<a
13+
href="https://www.linkedin.com/company/asyncapi"
14+
target="_blank"
15+
rel="noreferrer"
16+
className="w-full h-full flex justify-center items-center"
17+
data-test="footer-Linkedin"
18+
>
19+
<span className="tooltip absolute top-0 text-sm bg-white text-white py-[5px] px-[8px] rounded-md shadow-md opacity-0 pointer-events-none transition-all duration-300" data-test="footer-tooltip-linkedin">
20+
LinkedIn
21+
</span>
22+
<LinkedIn className="h-[1em]" fill="currentColor" />
23+
</a>
24+
</li>
25+
<li className="icon github relative bg-white rounded-full m-[5px] w-[35px] h-[35px] text-lg flex justify-center items-center flex-col shadow-md cursor-pointer transition-all duration-200 hover:bg-[#333] hover:text-white" data-test="footer-icon-github">
26+
<a
27+
href="https://github.com/asyncapi"
28+
target="_blank"
29+
rel="noreferrer"
30+
className="w-full h-full flex justify-center items-center"
31+
data-test="footer-Github"
32+
>
33+
<span className="tooltip absolute top-0 text-sm bg-white text-white py-[5px] px-[8px] rounded-md shadow-md opacity-0 pointer-events-none transition-all duration-300" data-test="footer-tooltip-github">
34+
GitHub
35+
</span>
36+
<Github className="h-[1.4em]" fill="currentColor" />
37+
</a>
38+
</li>
39+
<li className="icon twitter relative bg-white rounded-full m-[5px] w-[35px] h-[35px] text-lg flex justify-center items-center flex-col shadow-md cursor-pointer transition-all duration-200 hover:bg-[#333] hover:text-white" data-test="footer-icon-twitter">
40+
<a
41+
href="https://x.com/asyncapispec"
42+
target="_blank"
43+
rel="noreferrer"
44+
className="w-full h-full flex justify-center items-center"
45+
data-test="footer-Twitter(X)"
46+
>
47+
<span className="tooltip absolute top-0 text-sm bg-white text-white py-[5px] px-[8px] rounded-md shadow-md opacity-0 pointer-events-none transition-all duration-300" data-test="footer-tooltip-twitter">
48+
Twitter
49+
</span>
50+
<X className="h-[1.4em]" fill="currentColor" />
51+
</a>
52+
</li>
53+
<li className="icon youtube relative bg-white rounded-full m-[5px] w-[35px] h-[35px] text-lg flex justify-center items-center flex-col shadow-md cursor-pointer transition-all duration-200 hover:bg-[#ff0000] hover:text-white" data-test="footer-icon-youtube">
54+
<a
55+
href="https://www.youtube.com/asyncapi"
56+
target="_blank"
57+
rel="noreferrer"
58+
className="w-full h-full flex justify-center items-center"
59+
data-test="footer-youtube"
60+
>
61+
<span className="tooltip absolute top-0 text-sm bg-white text-white py-[5px] px-[8px] rounded-md shadow-md opacity-0 pointer-events-none transition-all duration-300" data-test="footer-tooltip-youtube">
62+
YouTube
63+
</span>
64+
<Youtube className="h-[1.4em]" fill="currentColor" />
65+
</a>
66+
</li>
67+
</ul>
68+
</div>
69+
);
70+
}
71+
72+
export default Socials;

cypress/e2e/Footer.cy.ts

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,62 @@ describe('Footer links', () => {
3636
it('Linkedin should redirect to correct page', () => {
3737
cy.getTestData('footer-Linkedin').invoke('removeAttr', 'target').click();
3838

39-
cy.origin('https://www.linkedin.com/company/asyncapi/', () => {
40-
cy.url().should('eq', 'https://www.linkedin.com/company/asyncapi/');
39+
cy.origin('https://www.linkedin.com', () => {
40+
cy.url().should('include', 'linkedin.com/company/asyncapi');
4141
});
4242
});
4343

4444
it('Twitter(X) should redirect to correct page', () => {
4545
cy.getTestData('footer-Twitter(X)').invoke('removeAttr', 'target').click();
4646

47-
cy.origin('https://x.com/asyncapispec', () => {
48-
cy.url().should('match', /.*asyncapispec.*/);
47+
cy.origin('https://x.com', () => {
48+
cy.url().should('include', 'asyncapispec');
4949
});
5050
});
5151

52-
it('Should Contain AsycAPI Conference Logo', () => {
52+
it('YouTube should redirect to correct page', () => {
53+
cy.getTestData('footer-youtube')
54+
.should('have.attr', 'href', 'https://www.youtube.com/asyncapi');
55+
});
56+
57+
it('Should Contain AsyncAPI Conference Logo', () => {
5358
cy.getTestData('footer-asyncAPI-logo').should('be.visible');
5459
});
60+
61+
describe('Social Media Icons', () => {
62+
beforeEach(() => {
63+
cy.visit('/');
64+
});
65+
66+
it('Should display social media icons', () => {
67+
cy.getTestData('social-wrapper').should('exist');
68+
cy.getTestData('footer-icon-linkedin').should('exist');
69+
cy.getTestData('footer-icon-github').should('exist');
70+
cy.getTestData('footer-icon-twitter').should('exist');
71+
cy.getTestData('footer-icon-youtube').should('exist');
72+
});
73+
74+
const socialPlatforms = [
75+
{ dataTest: 'footer-tooltip-linkedin', name: 'LinkedIn' },
76+
{ dataTest: 'footer-tooltip-github', name: 'GitHub' },
77+
{ dataTest: 'footer-tooltip-twitter', name: 'Twitter' },
78+
{ dataTest: 'footer-tooltip-youtube', name: 'YouTube' },
79+
];
80+
81+
socialPlatforms.forEach((platform) => {
82+
it(`Should display tooltip when hovering over ${platform.name} icon`, () => {
83+
cy.getTestData(platform.dataTest).invoke(
84+
'addClass',
85+
'show-for-test'
86+
);
87+
cy.getTestData(platform.dataTest).should(
88+
'be.visible'
89+
);
90+
cy.getTestData(platform.dataTest).should(
91+
'contain',
92+
platform.name
93+
);
94+
});
95+
});
96+
});
5597
});

styles/globals.css

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,64 @@ body {
1717
background: #1b1130;
1818
}
1919

20+
/* Social media icon tooltip */
21+
.social-wrapper .icon .tooltip {
22+
position: absolute;
23+
top: 0;
24+
background: #fff;
25+
color: #fff;
26+
font-size: 14px;
27+
padding: 5px 8px;
28+
border-radius: 5px;
29+
box-shadow: 0 10px 10px rgba(0, 0, 0, 0.1);
30+
opacity: 0;
31+
pointer-events: none;
32+
transition: all 0.3s ease;
33+
}
34+
35+
.social-wrapper .icon .tooltip::before {
36+
position: absolute;
37+
content: "";
38+
height: 8px;
39+
width: 8px;
40+
background: #fff;
41+
bottom: -3px;
42+
left: 50%;
43+
transform: translateX(-50%) rotate(45deg);
44+
transition: all 0.3s ease;
45+
}
46+
47+
.social-wrapper .icon:hover .tooltip {
48+
opacity: 1;
49+
pointer-events: auto;
50+
top: -45px;
51+
}
52+
.social-wrapper .icon .tooltip.show-for-test {
53+
opacity: 1 !important;
54+
pointer-events: auto;
55+
top: -45px;
56+
}
57+
58+
.social-wrapper .linkedin:hover .tooltip,
59+
.social-wrapper .linkedin:hover .tooltip::before {
60+
background: #0077b5;
61+
}
62+
63+
.social-wrapper .github:hover .tooltip,
64+
.social-wrapper .github:hover .tooltip::before {
65+
background: #333;
66+
}
67+
68+
.social-wrapper .twitter:hover .tooltip,
69+
.social-wrapper .twitter:hover .tooltip::before {
70+
background: #333;
71+
}
72+
73+
.social-wrapper .youtube:hover .tooltip,
74+
.social-wrapper .youtube:hover .tooltip::before {
75+
background: #ff0000;
76+
}
77+
2078
.text-gradient {
2179
background: linear-gradient(
2280
230deg,
@@ -168,22 +226,24 @@ backdrop-filter: blur(10px); */
168226
mix-blend-mode: lighten;
169227
}
170228

229+
171230
.color-effect {
172-
position: absolute;
173-
width: 100vw;
174-
max-width: none;
175-
bottom: 400px;
176-
will-change: transform;
177-
transform: translate3d(0%, 9.9964%, 0px) scale3d(1, 1, 1) rotateX(0deg)
178-
rotateY(0deg) rotateZ(0deg) skew(0deg, 0deg);
179-
transform-style: preserve-3d;
180-
vertical-align: middle;
181-
display: inline-block;
231+
position: absolute;
232+
width: 100vw;
233+
max-width: none;
234+
bottom: 400px;
235+
will-change: transform;
236+
transform: translate3d(0%, 9.9964%, 0px) scale3d(1, 1, 1) rotateX(0deg) rotateY(0deg) rotateZ(0deg) skew(0deg, 0deg);
237+
transform-style: preserve-3d;
238+
vertical-align: middle;
239+
display: inline-block;
240+
z-index: 0;
182241
}
183242

184243
@media (max-width: 780px) {
185244
.color-effect {
186-
bottom: 700px;
245+
bottom: 900px;
246+
opacity: 0.7;
187247
}
188248
}
189249

0 commit comments

Comments
 (0)