Skip to content

Commit

Permalink
feat: expanded layout, remove window (#8)
Browse files Browse the repository at this point in the history
* feat: remove window, use full screen

remove window component
add about component
new layout for page to prepare for full size display
add social and traditional contact methods
split everything in about in its own component
use unicode instead of ligatures for Material Symbols (perf)
add scss mixins for panel borders dry
add warning icon to noscript component
remove tabbed panes from display for now

* feat(contact): make links accessible

test item icons match names for social ones

* refactor(icons): add module to declare Material Symbols

* fix(icons): font awesome icons to display when no JS

* feat(icons): fill contact icons to match font awesome style
  • Loading branch information
davidlj95 authored Sep 17, 2023
1 parent b18a732 commit 1e087f2
Show file tree
Hide file tree
Showing 61 changed files with 981 additions and 599 deletions.
15 changes: 4 additions & 11 deletions scripts/src/generate-font-subsets.mts
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
import * as fs from 'fs';
import subsetFont from 'subset-font';
import { isMain, Log } from './utils.mjs';
import MaterialSymbols from '../../src/app/material-symbols.js';

async function generateFonts() {
Log.info("Generating font subset for Material Symbols Outlined")
const materialSymbolsFont = fs.readFileSync('assets/material-symbols-outlined.woff2');
const fontBuffer = Buffer.from(materialSymbolsFont);

const glyphs = [
'dark_mode',
'light_mode',
'code',
'history',
'apps',
'api',
'build',
];
Log.info("Glyphs to include in font")
glyphs.forEach((glyph) => Log.item(glyph));
// If using ligatures, file size increases by mystery
const glyphs = Object.values(MaterialSymbols);
Log.info("%d glyphs to include in font", glyphs.length)

const glyphText = glyphs.join('');

Expand Down
2 changes: 1 addition & 1 deletion src/app/_app-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
$text-palette: map-get($theme, text);

body {
background: map-get($background-palette, main);
background: map-get($background-palette, z0);
color: map-get($text-palette, primary);
}
}
Expand Down
22 changes: 22 additions & 0 deletions src/app/about/_about-theme.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@use "animations";
@use "profile-picture/profile-picture-theme";
@use "contact-social-icons/contact-social-icons-theme";
@use "contact-traditional-icons/contact-traditional-icons-theme";

@mixin color($theme) {
app-about {
@include profile-picture-theme.color($theme);
@include contact-traditional-icons-theme.color($theme);
@include contact-social-icons-theme.color($theme);
}
}

@mixin motion() {
app-about {
@include profile-picture-theme.motion()
}
}

@mixin theme($theme) {
@include color($theme);
}
14 changes: 14 additions & 0 deletions src/app/about/about.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<section class="about">
<div class="profile">
<app-profile-picture></app-profile-picture>
<h1>
<span>{{ realName }}</span>
<code>@{{ nickname }}</code>
</h1>
<div class="contacts">
<app-contact-traditional-icons></app-contact-traditional-icons>
<app-contact-social-icons></app-contact-social-icons>
</div>
</div>
<app-description></app-description>
</section>
62 changes: 62 additions & 0 deletions src/app/about/about.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@use "borders";
@use "margins";
@use "paddings";
@use "breakpoints";

:host {
section {
position: absolute;
width: 100%;
min-height: 100%;
display: flex;
flex-direction: row;
justify-content: center;

@include breakpoints.xs {
flex-direction: column;
align-items: center;
}

> * {
padding: paddings.$m;
}

.profile {
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
gap: margins.$m;

@include breakpoints.xs {
flex-direction: row;
align-items: center;
}

h1 {
font-size: 1.5em;
display: flex;
flex-direction: column;
}

.contacts {
display: flex;
gap: margins.$m;
flex-wrap: wrap;

@include breakpoints.xs {
flex-basis: 100%;
justify-content: center;
}

> * {
flex-basis: 50%;

@include breakpoints.xs {
flex-basis: unset;
}
}
}
}
}
}
62 changes: 62 additions & 0 deletions src/app/about/about.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { MockComponents, MockProvider } from 'ng-mocks';
import { ensureHasComponents } from '../../test/helpers/component-testers';
import { METADATA } from '../common/injection-tokens';
import { Metadata } from '../metadata';

import { AboutComponent } from './about.component';
import { ContactSocialIconsComponent } from './contact-social-icons/contact-social-icons.component';
import { ContactTraditionalIconsComponent } from './contact-traditional-icons/contact-traditional-icons.component';
import { DescriptionComponent } from './description/description.component';
import { ProfilePictureComponent } from './profile-picture/profile-picture.component';

describe('AboutComponent', () => {
let component: AboutComponent;
let fixture: ComponentFixture<AboutComponent>;
const fakeMetadata: Metadata = ({
nickname: 'bar',
realName: 'Foo',
} as Pick<Metadata, 'nickname' | 'realName'>) as Metadata;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
AboutComponent,
MockComponents(
ProfilePictureComponent,
ContactTraditionalIconsComponent,
ContactSocialIconsComponent,
DescriptionComponent,
)
],
providers: [
MockProvider(METADATA, fakeMetadata),
],
});
fixture = TestBed.createComponent(AboutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should display real name in header', () => {
const h1 = fixture.debugElement.query(By.css('h1'));
expect(h1.nativeElement.textContent).toContain(fakeMetadata.realName);
})

it('should display nickname preceded by \'@\' in header', () => {
const h1 = fixture.debugElement.query(By.css('h1'));
expect(h1.nativeElement.textContent).toContain(`@${fakeMetadata.nickname}`);
})

ensureHasComponents(() => fixture,
ProfilePictureComponent,
ContactTraditionalIconsComponent,
ContactSocialIconsComponent,
DescriptionComponent,
)
});
19 changes: 19 additions & 0 deletions src/app/about/about.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Component, Inject } from '@angular/core';
import { METADATA } from '../common/injection-tokens';
import { Metadata } from '../metadata';

@Component({
selector: 'app-about',
templateUrl: './about.component.html',
styleUrls: ['./about.component.scss'],
})
export class AboutComponent {
public realName = this.metadata.realName;
public nickname = this.metadata.nickname;

constructor(
@Inject(METADATA) private metadata: Metadata,
) {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@mixin color($theme) {
$text-palette: map-get($theme, text);

app-contact-social-icons {
color: map-get($theme, icon);

li:hover {
color: map-get($text-palette, secondary);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<ul>
<li *ngFor="let item of items">
<a [href]="item.url" [attr.aria-label]="item.name">
<fa-icon [icon]="item.icon"></fa-icon>
</a>
</li>
</ul>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@use "breakpoints";
@use "margins";

:host {
ul {
@include breakpoints.xs {
margin-left: 0;
}

// Position in same place as Material Symbols from app-contact-traditional-icons
margin-left: 2.75px;
display: flex;
flex-direction: row;
gap: margins.$m;

@include breakpoints.xs() {
justify-content: center;
}

li {
height: 32px;
}

fa-icon {
font-size: 26.5px; // to take 32px height;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { DebugElement } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { MockProvider } from 'ng-mocks';
import { getComponentSelector } from '../../../test/helpers/component-testers';
import { METADATA } from '../../common/injection-tokens';
import { Metadata } from '../../metadata';

import { ContactSocialIconsComponent } from './contact-social-icons.component';

describe('ContactSocialIconsComponent', () => {
let component: ContactSocialIconsComponent;
let fixture: ComponentFixture<ContactSocialIconsComponent>;
const nickname = 'foo';

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
ContactSocialIconsComponent,
FaIconComponent,
],
providers: [
MockProvider(METADATA, {nickname: nickname} as Pick<Metadata, 'nickname'> as Metadata)
]
});
fixture = TestBed.createComponent(ContactSocialIconsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

describe('#items', () => {
it('should have the nickname in every url', () => {
component.items.forEach((item, index) => {
expect(item.url.pathname).withContext(`item ${index}`).toContain(nickname);
})
})
it('should have an icon related to the name', () => {
component.items.forEach((item, index) => {
const iconNameWithoutDashes = item.icon.iconName.replace('-', '');
expect(iconNameWithoutDashes).withContext(`item ${index}`).toContain(item.name.toLowerCase());
})
})
})

it('should list all contact methods with their icons, links and accessibility labels', () => {
const itemElements = fixture.debugElement.queryAll(By.css('li'))
expect(itemElements.length).withContext('same number of items').toBe(component.items.length)
component.items.forEach((item, index) => {
const itemElement = itemElements[index];
const iconElement = itemElement.query(By.css(getComponentSelector(FaIconComponent)));
expect(getIconNameFromFontAwesomeElement(iconElement)).withContext(`item ${index} icon`).toEqual(item.icon.iconName);

// noinspection DuplicatedCode
const anchorElement = itemElement.query(By.css('a'));
expect(anchorElement).withContext(`item ${index} link exists`).toBeTruthy();
expect(anchorElement.attributes['href']).withContext(`item ${index} link URL`).toEqual(item.url.toString());
expect(anchorElement.attributes['aria-label']).withContext(`item ${index} accessibility label`).toEqual(item.name);
})
})

function getIconNameFromFontAwesomeElement(faElement: DebugElement): string {
return faElement.children[0].nativeElement.getAttribute('data-icon')
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Component, Inject } from '@angular/core';
import { faGithub, faLinkedinIn, faStackOverflow, faTwitter, IconDefinition } from '@fortawesome/free-brands-svg-icons';
import { METADATA } from '../../common/injection-tokens';
import { Metadata } from '../../metadata';

@Component({
selector: 'app-contact-social-icons',
templateUrl: './contact-social-icons.component.html',
styleUrls: ['./contact-social-icons.component.scss'],
})
export class ContactSocialIconsComponent {
public items: ReadonlyArray<{ name: string, icon: IconDefinition, url: URL }> = [
{
name: "GitHub",
icon: faGithub,
url: new URL(`https://github.com/${this.metadata.nickname}`),
},
{
name: "LinkedIn",
icon: faLinkedinIn,
url: new URL(`https://www.linkedin.com/in/${this.metadata.nickname}`),
},
{
name: "StackOverflow",
icon: faStackOverflow,
url: new URL(`https://stackoverflow.com/users/3263250/${this.metadata.nickname}`),
},
{
name: "Twitter",
icon: faTwitter,
url: new URL(`https://twitter.com/${this.metadata.nickname}`),
},
]

constructor(
@Inject(METADATA) private metadata: Metadata,
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@mixin color($theme) {
$text-palette: map-get($theme, text);

app-contact-traditional-icons {
color: map-get($theme, icon);

li:hover {
color: map-get($text-palette, secondary);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ul>
<li *ngFor="let item of items">
<a [href]="item.url" [attr.aria-label]="item.name" class="material-symbols-outlined">{{ item.icon }}</a>
</li>
</ul>
Loading

0 comments on commit 1e087f2

Please sign in to comment.