feat(social-media): implement social media component and integrate into store footer

This commit is contained in:
2026-07-23 15:07:29 -03:00
parent 67702ffdd7
commit 44bcb9aeba
10 changed files with 154 additions and 38 deletions

View File

@@ -0,0 +1,12 @@
<a
class="social-media"
[href]="url()"
target="_blank"
rel="noopener noreferrer"
[attr.aria-label]="name()"
[attr.data-social-media]="code()"
[title]="name()"
>
<i [class]="iconClass()" aria-hidden="true"></i>
<span class="visually-hidden">{{ name() }}</span>
</a>

View File

@@ -0,0 +1,31 @@
:host {
display: inline-flex;
}
.social-media {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
border-radius: 50%;
color: inherit;
font-size: 1.25rem;
line-height: 1;
text-decoration: none;
transition:
color 0.15s ease-in-out,
background-color 0.15s ease-in-out,
transform 0.15s ease-in-out;
&:hover {
color: #ffffff;
background-color: rgba(255, 255, 255, 0.12);
transform: translateY(-1px);
}
&:focus-visible {
outline: 2px solid #ffffff;
outline-offset: 2px;
}
}

View File

@@ -0,0 +1,40 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SocialMediaComponent } from './social-media.component';
describe('SocialMediaComponent', () => {
let fixture: ComponentFixture<SocialMediaComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SocialMediaComponent],
}).compileComponents();
fixture = TestBed.createComponent(SocialMediaComponent);
fixture.componentRef.setInput('code', 'instagram');
fixture.componentRef.setInput('icon', 'instagram');
fixture.componentRef.setInput('name', 'Instagram');
fixture.componentRef.setInput('url', 'https://instagram.com/acme');
fixture.detectChanges();
});
it('renders a safe external social media link', () => {
const link = fixture.nativeElement.querySelector('a') as HTMLAnchorElement;
const icon = fixture.nativeElement.querySelector('i') as HTMLElement;
expect(link.getAttribute('href')).toBe('https://instagram.com/acme');
expect(link.getAttribute('target')).toBe('_blank');
expect(link.getAttribute('rel')).toBe('noopener noreferrer');
expect(link.getAttribute('aria-label')).toBe('Instagram');
expect(link.dataset['socialMedia']).toBe('instagram');
expect(icon.className).toBe('fa-brands fa-instagram');
});
it('keeps a complete Font Awesome class', () => {
fixture.componentRef.setInput('icon', 'fa-brands fa-linkedin-in');
fixture.detectChanges();
const icon = fixture.nativeElement.querySelector('i') as HTMLElement;
expect(icon.className).toBe('fa-brands fa-linkedin-in');
});
});

View File

@@ -0,0 +1,21 @@
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
@Component({
selector: 'app-social-media',
imports: [],
templateUrl: './social-media.component.html',
styleUrl: './social-media.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SocialMediaComponent {
readonly code = input.required<string>();
readonly icon = input.required<string>();
readonly name = input.required<string>();
readonly url = input.required<string>();
protected readonly iconClass = computed(() => {
const icon = this.icon().trim();
return icon.includes('fa-') ? icon : `fa-brands fa-${icon}`;
});
}