Skip to content

Commit 6c91f27

Browse files
vyktoremariohdJerry
authored andcommitted
[solidjs-tailwinf] Add user profile card (#830)
1 parent 6ae55b0 commit 6c91f27

File tree

12 files changed

+217
-1
lines changed

12 files changed

+217
-1
lines changed

solidjs-tailwind/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ git clone https://github.com/thisdot/starter.dev.git
8989

9090
- `pnpm run dev` - Runs the development server on localhost port 3000 with HMR
9191
- `pnpm run test` - Runs the test suite
92-
- `pnpm run storbook` - To showcase the component library
92+
- `pnpm run storybook` - To showcase the component library
9393
- `pnpm run build` - Builds a production version of the app to deploy
9494
- `pnpm run serve` - Serves a production build on localhost port 4173
9595
- `pnpm run lint` - Uses eslint to find potential issues in the codebase

solidjs-tailwind/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"@heroicons/react": "1.0.5"
5151
},
5252
"dependencies": {
53+
"solid-heroicons": "^3.1.0",
5354
"solid-js": "1.6.0"
5455
},
5556
"keywords": [

solidjs-tailwind/pnpm-lock.yaml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
function TwitterIcon(className) {
2+
return (
3+
<svg
4+
xmlns="http://www.w3.org/2000/svg"
5+
viewBox="0 0 24 24"
6+
class={className.class || 'w-6 h-6'}
7+
fill="currentColor"
8+
>
9+
<path fill="none" d="M0 0h24v24H0z" />
10+
<path d="M22.162 5.656a8.384 8.384 0 0 1-2.402.658A4.196 4.196 0 0 0 21.6 4c-.82.488-1.719.83-2.656 1.015a4.182 4.182 0 0 0-7.126 3.814 11.874 11.874 0 0 1-8.62-4.37 4.168 4.168 0 0 0-.566 2.103c0 1.45.738 2.731 1.86 3.481a4.168 4.168 0 0 1-1.894-.523v.052a4.185 4.185 0 0 0 3.355 4.101 4.21 4.21 0 0 1-1.89.072A4.185 4.185 0 0 0 7.97 16.65a8.394 8.394 0 0 1-6.191 1.732 11.83 11.83 0 0 0 6.41 1.88c7.693 0 11.9-6.373 11.9-11.9 0-.18-.005-.362-.013-.54a8.496 8.496 0 0 0 2.087-2.165z" />
11+
</svg>
12+
);
13+
}
14+
15+
export default TwitterIcon;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as TwitterIcon } from './TwitterIcon';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { For } from 'solid-js';
2+
function OrgList(props) {
3+
return (
4+
<div class="mt-5 border-t border-gray-200">
5+
<h2 class="my-2 pt-2 text-gray-800 font-bold">Organizations</h2>
6+
<div data-testid="profile page orgs" class="flex flex-wrap space-x-2">
7+
<For each={props.organizations}>
8+
{(props) => (
9+
<div class="relative w-9 h-9 rounded border border-gray-300 overflow-hidden">
10+
<img src={props.avatarUrl} alt="Organization" />
11+
</div>
12+
)}
13+
</For>
14+
</div>
15+
</div>
16+
);
17+
}
18+
19+
export default OrgList;
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import {
2+
users,
3+
star,
4+
buildingOffice,
5+
mapPin,
6+
link,
7+
} from 'solid-heroicons/outline';
8+
import { Icon } from 'solid-heroicons';
9+
import { TwitterIcon } from '../Icons';
10+
import OrgList from './OrgList';
11+
12+
const UserProfile = (userProfileProps) => {
13+
return (
14+
<div>
15+
<img
16+
src={userProfileProps.avatarUrl}
17+
alt="Avatar"
18+
width={260}
19+
height={260}
20+
class="rounded-full shadow z-30"
21+
/>
22+
<h1 class="mt-2">
23+
<div class="text-2xl text-gray-800 font-bold leading-tight">
24+
{userProfileProps.name}
25+
</div>
26+
<div class="text-xl text-gray-500 font-light">
27+
{userProfileProps.username}
28+
</div>
29+
</h1>
30+
<div class="text-gray-800 mt-4">{userProfileProps.bio}</div>
31+
<div class="text-sm text-gray-600 my-4">
32+
<Icon path={users} class="w-4 h-4 mb-0.5 mr-1 inline" />
33+
<span class="inline-block">
34+
<span class="font-medium text-gray-900">
35+
{userProfileProps.followers}
36+
</span>{' '}
37+
followers
38+
</span>
39+
<span class="mx-1">·</span>
40+
<span class="inline-block">
41+
<span class="font-medium text-gray-900">
42+
{userProfileProps.following}
43+
</span>{' '}
44+
following
45+
</span>
46+
<span class="mx-1">·</span>
47+
<Icon path={star} class="w-4 h-4 mb-0.5 mr-1 inline" />
48+
<span class="inline-block">
49+
<span class="font-medium text-gray-900">
50+
{userProfileProps.starredRepos}
51+
</span>{' '}
52+
</span>
53+
</div>
54+
<div class="text-sm text-gray-800 space-y-1">
55+
{userProfileProps.company && (
56+
<div>
57+
<Icon path={buildingOffice} class="w-4 h-4 mb-0.5 mr-1 inline" />
58+
{userProfileProps.company}
59+
</div>
60+
)}
61+
{userProfileProps.location && (
62+
<div>
63+
<Icon path={mapPin} class="w-4 h-4 mb-0.5 mr-1 inline" />
64+
{userProfileProps.location}
65+
</div>
66+
)}
67+
{userProfileProps.websiteUrl && (
68+
<div>
69+
<Icon path={link} class="w-4 h-4 mb-0.5 mr-1 inline" />
70+
<a
71+
class="hover:text-blue-600 hover:underline"
72+
href={userProfileProps.websiteUrl}
73+
target="_blank"
74+
rel="noreferrer"
75+
>
76+
{userProfileProps.websiteUrl}
77+
</a>
78+
</div>
79+
)}
80+
{userProfileProps.twitterUsername && (
81+
<div>
82+
<TwitterIcon class="w-4 h-4 mb-0.5 mr-1 inline" />
83+
<a
84+
class="hover:text-blue-600 hover:underline"
85+
href={`https:/twitter.com/${userProfileProps.twitterUsername}`}
86+
target="_blank"
87+
rel="noreferrer"
88+
>
89+
@{userProfileProps.twitterUsername}
90+
</a>
91+
</div>
92+
)}
93+
</div>
94+
{userProfileProps.organizations.length > 0 && (
95+
<OrgList organizations={userProfileProps.organizations} />
96+
)}
97+
</div>
98+
);
99+
};
100+
101+
export default UserProfile;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Router } from '@solidjs/router';
2+
import { render } from 'solid-testing-library';
3+
import { beforeEach, describe, expect, it } from 'vitest';
4+
import UserProfile from './UserProfile.jsx';
5+
import { userProfileProps } from './data';
6+
7+
describe('User profile card', () => {
8+
let wrapper;
9+
beforeEach(async () => {
10+
wrapper = await render(() => (
11+
<Router>
12+
<UserProfile {...userProfileProps} />
13+
</Router>
14+
));
15+
});
16+
17+
it('should mount', () => {
18+
expect(wrapper).toBeTruthy();
19+
});
20+
21+
it('should show the user display name', async () => {
22+
const fullName = await wrapper.getByText(userProfileProps.name);
23+
expect(fullName).toBeVisible();
24+
});
25+
26+
it('should have a link for user profile picture', async () => {
27+
const avatar = await wrapper.getByAltText('Avatar');
28+
expect(avatar).toBeVisible();
29+
});
30+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Router } from '@solidjs/router';
2+
import { UserProfile } from '.';
3+
import { userProfileProps } from './data.js';
4+
5+
export default {
6+
title: 'Components/User Profile Card',
7+
component: UserProfile,
8+
};
9+
10+
const Template = (args) => (
11+
<Router>
12+
<UserProfile {...args} />
13+
</Router>
14+
);
15+
export const Default = Template.bind({});
16+
17+
Default.args = {
18+
...userProfileProps,
19+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const userProfileProps = {
2+
avatarUrl: 'https://avatars.githubusercontent.com/u/2487968?v=4',
3+
bio: `Senior Software Engineer @thisdot`,
4+
company: '@thisdot',
5+
followers: 24,
6+
following: 20,
7+
location: 'Washington, DC',
8+
login: 'tvanantwerp',
9+
name: 'Tom VanAntwerp',
10+
twitterUsername: 'tvanantwerp',
11+
websiteUrl: 'https://tomvanantwerp.com',
12+
organizations: [
13+
{
14+
avatarUrl: 'https://avatars.githubusercontent.com/u/22839396?v=4',
15+
login: 'thisdot',
16+
},
17+
],
18+
};

0 commit comments

Comments
 (0)