diff --git a/src/components/Experience.astro b/src/components/Experience.astro index ea81f57..85dd798 100644 --- a/src/components/Experience.astro +++ b/src/components/Experience.astro @@ -7,6 +7,15 @@ function split({ summary, link }: Mission): (string | NonNullable + missions.filter((mission) => earlier || mission.earlier), +); +const years = earlier.flatMap(({ period }) => period.match(/\d{4}/g) ?? []).sort(); +const span = years.length ? `${years[0]}–${years.at(-1)}` : ''; ---
@@ -16,9 +25,9 @@ function split({ summary, link }: Mission): (string | NonNullableWhere I've made an impact.

By employer, with the clients I worked for as a consultant.

-
    - {experience.map(({ name, period, summary, current, missions }) => ( -
  1. +
      + {experience.map(({ name, period, summary, current, earlier, missions }) => ( +
    1. {name} @@ -29,7 +38,7 @@ function split({ summary, link }: Mission): (string | NonNullable
        {missions.map((mission) => ( -
      1. +
      2. {mission.client ?? mission.role}

        {mission.client &&

        {mission.role}

        }

        @@ -57,14 +66,81 @@ function split({ summary, link }: Mission): (string | NonNullable ))}

      + {earlier.length > 0 && ( + + )}

+ + diff --git a/src/data/profile.ts b/src/data/profile.ts index 1ea0d80..5383792 100644 --- a/src/data/profile.ts +++ b/src/data/profile.ts @@ -137,6 +137,8 @@ export interface Mission { /** A phrase of the summary that downloads a document, such as a thesis. */ link?: { text: string; url: string }; stack: string[]; + /** Folded under "Show earlier experience" until asked for. */ + earlier?: boolean; } /** An employer, and the missions done while employed there, newest first. */ @@ -146,6 +148,8 @@ export interface Employer { /** What the employment was, when it is more than its missions. */ summary?: string; current?: boolean; + /** Folded, with all its missions, under "Show earlier experience". */ + earlier?: boolean; missions: Mission[]; } @@ -233,6 +237,7 @@ export const experience: Employer[] = [ { role: 'Software Engineer', client: 'SoccerLAB', + earlier: true, period: 'Aug – Sep 2022', place: 'Hasselt, Belgium', summary: 'A Svelte proof of concept against React and Angular, with a design system.', @@ -241,6 +246,7 @@ export const experience: Employer[] = [ { role: 'Data Scientist', client: 'Datavillage', + earlier: true, period: 'Jan – Apr 2022', place: 'Liège, Belgium', summary: @@ -251,6 +257,7 @@ export const experience: Employer[] = [ }, { name: 'IDLab – imec', + earlier: true, period: 'Feb – Jun 2021', missions: [ { diff --git a/src/styles/global.css b/src/styles/global.css index 458084e..7f5d86d 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -105,7 +105,9 @@ html { scroll-behavior: smooth; - scroll-padding-top: 80px; + /* The navigation's height, its 1px border included (see Nav.astro), so a + link lands with its section starting right under the bar. */ + scroll-padding-top: 73px; /* What a browser shows past the page's edges: behind Safari's toolbar on iPhone, and when a scroll bounces. The theme's background at the top, the footer's green once the footer is on screen (set in Base.astro), so the @@ -276,6 +278,10 @@ h2 { } @media (max-width: 920px) { + html { + scroll-padding-top: 61px; + } + section { padding: 80px 0; } diff --git a/tests/site.spec.ts b/tests/site.spec.ts index 8686d0b..eb113e4 100644 --- a/tests/site.spec.ts +++ b/tests/site.spec.ts @@ -277,6 +277,26 @@ test("paints past the page's edges in the theme's colour, then the footer's", as await expect(edge).toBeHidden(); }); +test('folds the earliest roles under a button, and unfolds them', async ({ page }) => { + await page.goto('/'); + const earlier = page.locator('#experience .earlier'); + // Found by place, as its name changes from "Show" to "Hide". + const more = page.locator('#experience .more'); + await expect(more).toHaveAttribute('aria-expanded', 'false'); + await expect(more).toContainText('3 roles · 2021–2022'); + for (const role of await earlier.all()) await expect(role).toBeHidden(); + await expect(page.getByRole('heading', { name: 'Partenamut' })).toBeVisible(); + + await more.click(); + await expect(more).toHaveAttribute('aria-expanded', 'true'); + await expect(more).toContainText('Hide earlier experience'); + await expect(page.getByRole('heading', { name: 'SoccerLAB' })).toBeVisible(); + await expect(page.getByRole('heading', { name: 'IDLab – imec' })).toBeVisible(); + + await more.click(); + for (const role of await earlier.all()) await expect(role).toBeHidden(); +}); + test('copies the email address in one click, and says so', async ({ page }) => { // A stand-in clipboard, the same in every browser, that records what it is given. await page.addInitScript(() => { @@ -312,6 +332,7 @@ test('counts the visit, and the CV, the thesis, an email and a link out', async await page.evaluate(() => document.addEventListener('click', (event) => event.preventDefault())); await page.locator('#top a[download]').click(); + await page.getByRole('button', { name: /Show earlier experience/ }).click(); await page.locator(`#experience a[href$="master-thesis.pdf"]`).click(); await page.locator('#contact a[href^="mailto:"]').click(); await page.locator('#contact a[href*="linkedin.com"]').click(); @@ -320,6 +341,7 @@ test('counts the visit, and the CV, the thesis, an email and a link out', async expect(await page.evaluate(() => (window as unknown as { counted: string[] }).counted)).toEqual([ '/', 'download terencio-agozzino-cv.pdf', + 'show earlier experience', 'download terencio-agozzino-master-thesis.pdf', 'email', 'out www.linkedin.com/in/terencioagozzino',