Skip to content

Commit

Permalink
Merge pull request #127 from NIAEFEUP/feature/hexagon-team-members
Browse files Browse the repository at this point in the history
TeamMemberHexagon component
  • Loading branch information
LuisDuarte1 authored Oct 8, 2023
2 parents 4e6f20c + 6885c18 commit e45855f
Show file tree
Hide file tree
Showing 15 changed files with 405 additions and 312 deletions.
3 changes: 2 additions & 1 deletion .storybook/main.cts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ module.exports = {
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-coverage',
'@storybook/addon-a11y'
'@storybook/addon-a11y',
'storybook-addon-pseudo-states'
],
framework: {
name: '@storybook/sveltekit',
Expand Down
371 changes: 93 additions & 278 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,8 @@
"vite": "^4.4.9",
"vitest": "^0.31.4"
},
"type": "module"
"type": "module",
"dependencies": {
"storybook-addon-pseudo-states": "^2.1.0"
}
}
8 changes: 8 additions & 0 deletions src/lib/component/Icon.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,11 @@ export const TimesIcon = {
size: '50px'
}
};

export const GlobeIcon = {
args: {
src: Icons.Globe,
color: 'white',
size: '50px'
}
};
4 changes: 3 additions & 1 deletion src/lib/component/Icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import FaBrandsLinkedin from 'svelte-icons-pack/fa/FaBrandsLinkedin';
import FaSolidUser from 'svelte-icons-pack/fa/FaSolidUser';
import FaBars from 'svelte-icons-pack/fa/FaSolidBars';
import FaTimes from 'svelte-icons-pack/fa/FaSolidTimes';
import FaSolidGlobe from 'svelte-icons-pack/fa/FaSolidGlobe';

const Icons = {
Instagram: FaBrandsInstagram,
Expand All @@ -17,7 +18,8 @@ const Icons = {
Linkedin: FaBrandsLinkedin,
User: FaSolidUser,
Bars: FaBars,
Times: FaTimes
Times: FaTimes,
Globe: FaSolidGlobe
};

export default Icons;
14 changes: 0 additions & 14 deletions src/lib/layout/hexagons/BlueExampleHexagon.svelte

This file was deleted.

5 changes: 2 additions & 3 deletions src/lib/layout/hexagons/HexagonGrid.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script lang="ts">
import type RedExampleHexagon from './RedExampleHexagon.svelte';
import type BlueExampleHexagon from './BlueExampleHexagon.svelte';
import type TeamMemberHexagon from './TeamMemberHexagon.svelte';
// Inspired by https://github.com/sveltejs/svelte-virtual-list/blob/master/VirtualList.svelte
type T = $$Generic;
Expand All @@ -10,7 +9,7 @@
export let gap: 'small' | 'medium' | 'big' = 'medium';
export let orientation: 'horizontal' | 'vertical';
export let component: typeof BlueExampleHexagon | typeof RedExampleHexagon;
export let component: typeof TeamMemberHexagon;
const gridColumnsStyle =
orientation === 'horizontal'
Expand Down
14 changes: 0 additions & 14 deletions src/lib/layout/hexagons/RedExampleHexagon.svelte

This file was deleted.

139 changes: 139 additions & 0 deletions src/lib/layout/hexagons/TeamMemberHexagon.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { fireEvent, within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import TeamMemberHexagon from './TeamMemberHexagon.svelte';

export default {
title: 'Atoms/Hexagons/Team Member Hexagon',
component: TeamMemberHexagon,
parameters: {
layout: 'centered',
controls: { exclude: ['orientation', 'teamMember'] }
}
};

export const MobileHexagon = {
parameters: {
viewport: {
defaultViewport: 'mobile2'
}
},
args: {
data: {
name: 'Bruno Rosendo',
email: 'brunorosendo@gmail.com',
role: 'Co-Gestor de Projetos',
photo: 'images/previews/bruno_rosendo.png',
linkedin: 'https://pt.linkedin.com/',
gitHub: 'https://github.com/',
websites: [{ url: 'https://www.facebook.com/' }]
}
},
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);

await step('Open Hexagon', async () => {
const container = await canvas.findByTestId('container');
const role = await canvas.findByTestId('role');
await fireEvent.touchStart(container);
await expect(role).toHaveAttribute('style', 'opacity: 1;');
});

await step('Close Hexagon', async () => {
const container = await canvas.findByTestId('container');
const role = await canvas.findByTestId('role');
await fireEvent.touchStart(container);
await expect(role).toHaveAttribute('style', 'opacity: 0;');
});
}
};

export const CustomIconHexagon = {
args: {
data: {
name: 'Bruno Rosendo',
email: 'brunorosendo@gmail.com',
role: 'Co-Gestor de Projetos',
photo: 'images/previews/bruno_rosendo.png',
linkedin: 'https://pt.linkedin.com/',
gitHub: 'https://github.com/',
websites: [{ iconPath: 'images/previews/facebook.png', url: 'https://www.facebook.com/' }]
}
}
};

export const HoveredCustomIconHexagon = {
args: {
data: {
name: 'Bruno Rosendo',
email: 'brunorosendo@gmail.com',
role: 'Co-Gestor de Projetos',
photo: 'images/previews/bruno_rosendo.png',
linkedin: 'https://pt.linkedin.com/',
gitHub: 'https://github.com/',
websites: [{ iconPath: 'images/previews/facebook.png', url: 'https://www.facebook.com/' }]
}
},
parameters: {
pseudo: { hover: true }
}
};

export const DefaultIconHexagon = {
args: {
data: {
name: 'Bruno Rosendo',
email: 'brunorosendo@gmail.com',
role: 'Co-Gestor de Projetos',
photo: 'images/previews/bruno_rosendo.png',
linkedin: 'https://pt.linkedin.com/',
gitHub: 'https://github.com/',
websites: [{ url: 'https://www.facebook.com/' }]
}
}
};

export const HoveredDefaultIconHexagon = {
args: {
data: {
name: 'Bruno Rosendo',
email: 'brunorosendo@gmail.com',
role: 'Co-Gestor de Projetos',
photo: 'images/previews/bruno_rosendo.png',
linkedin: 'https://pt.linkedin.com/',
gitHub: 'https://github.com/',
websites: [{ url: 'https://www.facebook.com/' }]
}
},
parameters: {
pseudo: { hover: true }
}
};

export const DefaultProfilePicHexagon = {
args: {
data: {
name: 'Bruno Rosendo',
email: 'brunorosendo@gmail.com',
role: 'Co-Gestor de Projetos',
linkedin: 'https://pt.linkedin.com/',
gitHub: 'https://github.com/',
websites: [{ url: 'https://www.facebook.com/' }]
}
}
};

export const HoveredDefaultProfilePicHexagon = {
args: {
data: {
name: 'Bruno Rosendo',
email: 'brunorosendo@gmail.com',
role: 'Co-Gestor de Projetos',
linkedin: 'https://pt.linkedin.com/',
gitHub: 'https://github.com/',
websites: [{ url: 'https://www.facebook.com/' }]
}
},
parameters: {
pseudo: { hover: true }
}
};
139 changes: 139 additions & 0 deletions src/lib/layout/hexagons/TeamMemberHexagon.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<script lang="ts">
import { onMount } from 'svelte';
import Hexagon from './Hexagon.svelte';
import Icon from '$lib/component/Icon.svelte';
import Icons from '$lib/component/Icons';
import type { TeamMember } from '@/model/TeamMember';
export let data;
export const orientation = 'horizontal';
export let teamMember = data as TeamMember;
const openHexagonAnimation = (
target: HTMLElement,
container: HTMLElement,
fullOpacityContainers: NodeListOf<HTMLElement>,
variableOpacityContainer: HTMLElement
) => {
container.style.top = '50%';
container.style.transform = 'translateY(-50%)';
fullOpacityContainers.forEach((i) => {
i.style.opacity = '100%';
});
variableOpacityContainer.style.opacity = '30%';
target.dataset.state = 'open';
};
const closeHexagonAnimation = (
target: HTMLElement,
container: HTMLElement,
fullOpacityContainers: NodeListOf<HTMLElement>,
variableOpacityContainer: HTMLElement
) => {
container.style.top = '';
container.style.transform = '';
fullOpacityContainers.forEach((i) => {
i.style.opacity = '0%';
});
variableOpacityContainer.style.opacity = '0%';
target.dataset.state = 'closed';
};
onMount(() => {
const target: HTMLElement = document.getElementById(teamMember.email) as HTMLElement;
const container: HTMLElement | null = target.querySelector('.container');
const fullOpacityContainers: NodeListOf<HTMLElement> | null =
target.querySelectorAll('.full-opacity');
const variableOpacityContainer: HTMLElement | null = target.querySelector('.variable-opacity');
target.addEventListener('touchstart', () => {
if (container && fullOpacityContainers && variableOpacityContainer) {
if (target.dataset.state == 'closed') {
openHexagonAnimation(target, container, fullOpacityContainers, variableOpacityContainer);
} else {
closeHexagonAnimation(target, container, fullOpacityContainers, variableOpacityContainer);
}
}
});
});
</script>

<Hexagon {orientation}>
<div
id={teamMember.email}
class="group relative h-full w-full"
data-testid="hexagon"
data-state="closed"
>
<div
data-testid="container"
class="container absolute bottom-0 z-20 w-full px-4 duration-500 group-hover:bottom-1/2 group-hover:translate-y-1/3"
>
<p
class="mx-auto w-[70%] text-center text-sm font-bold leading-tight text-gray-100 transition-all sm:text-sm md:text-base lg:text-lg xl:text-xl"
>
{teamMember.name}
</p>
<p
data-testid="role"
class="full-opacity mx-auto text-center text-xs leading-tight text-gray-100 opacity-0 transition-all duration-500 ease-out group-hover:opacity-100 sm:text-xs md:text-sm lg:text-base xl:text-lg"
>
{teamMember.role}
</p>
<div class="relative mt-1">
<div class="absolute flex w-full justify-center gap-1 md:gap-2">
{#if teamMember.linkedin}
<a
href={teamMember.linkedin}
class="full-opacity h-6 opacity-0 transition-all duration-500 ease-out group-hover:opacity-100 sm:h-6 md:h-7 lg:h-8 xl:h-9"
aria-label="{teamMember.name}'s LinkedIn"
>
<Icon src={Icons.Linkedin} color="white" size="100%" /></a
>
{/if}
{#if teamMember.gitHub}
<a
href={teamMember.gitHub}
class="full-opacity h-6 opacity-0 transition-all duration-500 ease-out group-hover:static group-hover:opacity-100 sm:h-6 md:h-7 lg:h-8 xl:h-9"
aria-label="{teamMember.name}'s GitHub"
><Icon src={Icons.Github} color="white" size="100%" /></a
>
{/if}
{#if teamMember.websites}
{#each teamMember.websites as customWebsite}
<a
href={customWebsite.url}
class="full-opacity h-5 opacity-0 transition-all duration-500 ease-out group-hover:opacity-100 sm:h-6 md:h-7 lg:h-8 xl:h-9"
aria-label="{teamMember.name}'s custom website"
>
{#if customWebsite.iconPath}
<img
src={customWebsite.iconPath}
alt="Icon of {teamMember.name}'s custom website"
class="icon h-full w-full object-cover"
/>
{:else}
<Icon src={Icons.Globe} color="white" size="100%" />
{/if}
</a>
{/each}
{/if}
</div>
</div>
</div>
<div
class="variable-opacity absolute inset-0 z-10 bg-black text-lg opacity-0 transition-opacity duration-500 group-hover:opacity-30"
/>
<img
src={teamMember.photo ? teamMember.photo : 'images/default_profile_pic.png'}
alt="NIAFEUP member {teamMember.name}"
class="z-0 h-full w-full object-cover"
/>
</div>
</Hexagon>
4 changes: 4 additions & 0 deletions src/model/CustomWebsite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type CustomWebsite = {
url: string;
iconPath?: string;
};
11 changes: 11 additions & 0 deletions src/model/TeamMember.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { CustomWebsite } from './CustomWebsite';

export type TeamMember = {
name: string;
email: string;
role: string;
photo?: string;
linkedin?: string;
gitHub?: string;
websites?: CustomWebsite[];
};
Binary file added static/images/default_profile_pic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/images/previews/bruno_rosendo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/images/previews/facebook.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e45855f

Please sign in to comment.