Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion solidjs-tailwind/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,7 +89,7 @@ git clone https://github.com/thisdot/starter.dev.git

- `pnpm run dev` - Runs the development server on localhost port 3000 with HMR
- `pnpm run test` - Runs the test suite
- `pnpm run storbook` - To showcase the component library
- `pnpm run storybook` - To showcase the component library
- `pnpm run build` - Builds a production version of the app to deploy
- `pnpm run serve` - Serves a production build on localhost port 4173
- `pnpm run lint` - Uses eslint to find potential issues in the codebase
Expand Down
1 change: 1 addition & 0 deletions solidjs-tailwind/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@
"@heroicons/react": "1.0.5"
},
"dependencies": {
"solid-heroicons": "^3.1.0",
"solid-js": "1.6.0"
},
"keywords": [
Expand Down
9 changes: 9 additions & 0 deletions solidjs-tailwind/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions solidjs-tailwind/src/components/Icons/TwitterIcon.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
function TwitterIcon(className) {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class={className.class || 'w-6 h-6'}
fill="currentColor"
>
<path fill="none" d="M0 0h24v24H0z" />
<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" />
</svg>
);
}

export default TwitterIcon;
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/Icons/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export { default as TwitterIcon } from './TwitterIcon';
19 changes: 19 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/OrgList.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { For } from 'solid-js';
function OrgList(props) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use splitProps as it shows a major feature in SolidJs

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerry I don't understand this comment. Can you explain why splitProps is required and how what you mean by it shows a major feature in SolidJs?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerrysplitProps is used when you want to pick out certain properties from an object and pass the rest into a child component. It works the same with Pick in typescript.

It doesn't make sense to use it when all the properties passed are going to be used in that component.

If we need to show how major a feature it is, we can have a store that returns both user and repositories data in one object, then split it into two and pass it into their respective components(or into components where the data is needed). Or do the split inside the component and retrieve just the properties needed by the component.

@hdJerryhdJerryNov 14, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the props are not reactive then no need. @vyktoremario , though it also help to know the props expected in there just like how TS does. SO if I view the component with the spiltProps or mergeProps i know the props allowed in that component.

return (
<div class="mt-5 border-t border-gray-200">
<h2 class="my-2 pt-2 text-gray-800 font-bold">Organizations</h2>
<div data-testid="profile page orgs" class="flex flex-wrap space-x-2">
<For each={props.organizations}>
{(props) => (
<div class="relative w-9 h-9 rounded border border-gray-300 overflow-hidden">
<img src={props.avatarUrl} alt="Organization" />
</div>
)}
</For>
</div>
</div>
);
}

export default OrgList;
101 changes: 101 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
import {
users,
star,
buildingOffice,
mapPin,
link,
} from 'solid-heroicons/outline';
import { Icon } from 'solid-heroicons';
import { TwitterIcon } from '../Icons';
import OrgList from './OrgList';

const UserProfile = (userProfileProps) => {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<div>
<img
Comment thread
vyktoremario marked this conversation as resolved.
src={userProfileProps.avatarUrl}
alt="Avatar"
width={260}
height={260}
class="rounded-full shadow z-30"
/>
<h1 class="mt-2">
<div class="text-2xl text-gray-800 font-bold leading-tight">
{userProfileProps.name}
</div>
<div class="text-xl text-gray-500 font-light">
{userProfileProps.username}
</div>
</h1>
<div class="text-gray-800 mt-4">{userProfileProps.bio}</div>
<div class="text-sm text-gray-600 my-4">
<Icon path={users} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.followers}
</span>{' '}
followers
</span>
<span class="mx-1">·</span>
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.following}
</span>{' '}
following
</span>
<span class="mx-1">·</span>
<Icon path={star} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.starredRepos}
</span>{' '}
</span>
</div>
<div class="text-sm text-gray-800 space-y-1">
{userProfileProps.company && (
<div>
<Icon path={buildingOffice} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.company}
</div>
)}
{userProfileProps.location && (
<div>
<Icon path={mapPin} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.location}
</div>
)}
{userProfileProps.websiteUrl && (
<div>
<Icon path={link} class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={userProfileProps.websiteUrl}
target="_blank"
rel="noreferrer"
>
{userProfileProps.websiteUrl}
</a>
</div>
)}
{userProfileProps.twitterUsername && (
<div>
<TwitterIcon class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={`https:/twitter.com/${userProfileProps.twitterUsername}`}
target="_blank"
rel="noreferrer"
>
@{userProfileProps.twitterUsername}
</a>
</div>
)}
</div>
{userProfileProps.organizations.length > 0 && (
<OrgList organizations={userProfileProps.organizations} />
)}
</div>
);
};

export default UserProfile;
30 changes: 30 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.spec.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
import { Router } from '@solidjs/router';
import { render } from 'solid-testing-library';
import { beforeEach, describe, expect, it } from 'vitest';
import UserProfile from './UserProfile.jsx';
import { userProfileProps } from './data';

describe('User profile card', () => {
let wrapper;
beforeEach(async () => {
wrapper = await render(() => (
<Router>
<UserProfile {...userProfileProps} />
</Router>
));
});

it('should mount', () => {
expect(wrapper).toBeTruthy();
});

it('should show the user display name', async () => {
const fullName = await wrapper.getByText(userProfileProps.name);
expect(fullName).toBeVisible();
});

it('should have a link for user profile picture', async () => {
const avatar = await wrapper.getByAltText('Avatar');
Comment thread
vyktoremario marked this conversation as resolved.
expect(avatar).toBeVisible();
});
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { Router } from '@solidjs/router';
import { UserProfile } from '.';
import { userProfileProps } from './data.js';

export default {
title: 'Components/User Profile Card',
component: UserProfile,
};

const Template = (args) => (
<Router>
<UserProfile {...args} />
</Router>
);
export const Default = Template.bind({});

Default.args = {
...userProfileProps,
};
18 changes: 18 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/data.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
export const userProfileProps = {
avatarUrl: 'https://avatars.githubusercontent.com/u/2487968?v=4',
bio: `Senior Software Engineer @thisdot`,
company: '@thisdot',
followers: 24,
following: 20,
location: 'Washington, DC',
login: 'tvanantwerp',
name: 'Tom VanAntwerp',
twitterUsername: 'tvanantwerp',
websiteUrl: 'https://tomvanantwerp.com',
organizations: [
{
avatarUrl: 'https://avatars.githubusercontent.com/u/22839396?v=4',
login: 'thisdot',
},
],
};
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/UserProfile/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export {default as UserProfile } from './UserProfile';
2 changes: 2 additions & 0 deletions solidjs-tailwind/src/components/index.js
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
export * from './CounterExample';
export * from './FetchExample';
export * from './UserProfile';
export * from './Icons';
export * from './RepoFilter';
export * from './GistPanel';
export * from './RepoMeta';
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
[solidjs-tailwinf] Add user profile card by vyktoremario · Pull Request #830 · thisdot/starter.dev-github-showcases · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion solidjs-tailwind/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,7 +89,7 @@ git clone https://github.com/thisdot/starter.dev.git

- `pnpm run dev` - Runs the development server on localhost port 3000 with HMR
- `pnpm run test` - Runs the test suite
- `pnpm run storbook` - To showcase the component library
- `pnpm run storybook` - To showcase the component library
- `pnpm run build` - Builds a production version of the app to deploy
- `pnpm run serve` - Serves a production build on localhost port 4173
- `pnpm run lint` - Uses eslint to find potential issues in the codebase
Expand Down
1 change: 1 addition & 0 deletions solidjs-tailwind/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@
"@heroicons/react": "1.0.5"
},
"dependencies": {
"solid-heroicons": "^3.1.0",
"solid-js": "1.6.0"
},
"keywords": [
Expand Down
9 changes: 9 additions & 0 deletions solidjs-tailwind/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions solidjs-tailwind/src/components/Icons/TwitterIcon.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
function TwitterIcon(className) {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class={className.class || 'w-6 h-6'}
fill="currentColor"
>
<path fill="none" d="M0 0h24v24H0z" />
<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" />
</svg>
);
}

export default TwitterIcon;
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/Icons/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export { default as TwitterIcon } from './TwitterIcon';
19 changes: 19 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/OrgList.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { For } from 'solid-js';
function OrgList(props) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use splitProps as it shows a major feature in SolidJs

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerry I don't understand this comment. Can you explain why splitProps is required and how what you mean by it shows a major feature in SolidJs?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerrysplitProps is used when you want to pick out certain properties from an object and pass the rest into a child component. It works the same with Pick in typescript.

It doesn't make sense to use it when all the properties passed are going to be used in that component.

If we need to show how major a feature it is, we can have a store that returns both user and repositories data in one object, then split it into two and pass it into their respective components(or into components where the data is needed). Or do the split inside the component and retrieve just the properties needed by the component.

@hdJerryhdJerryNov 14, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the props are not reactive then no need. @vyktoremario , though it also help to know the props expected in there just like how TS does. SO if I view the component with the spiltProps or mergeProps i know the props allowed in that component.

return (
<div class="mt-5 border-t border-gray-200">
<h2 class="my-2 pt-2 text-gray-800 font-bold">Organizations</h2>
<div data-testid="profile page orgs" class="flex flex-wrap space-x-2">
<For each={props.organizations}>
{(props) => (
<div class="relative w-9 h-9 rounded border border-gray-300 overflow-hidden">
<img src={props.avatarUrl} alt="Organization" />
</div>
)}
</For>
</div>
</div>
);
}

export default OrgList;
101 changes: 101 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
import {
users,
star,
buildingOffice,
mapPin,
link,
} from 'solid-heroicons/outline';
import { Icon } from 'solid-heroicons';
import { TwitterIcon } from '../Icons';
import OrgList from './OrgList';

const UserProfile = (userProfileProps) => {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<div>
<img
Comment thread
vyktoremario marked this conversation as resolved.
src={userProfileProps.avatarUrl}
alt="Avatar"
width={260}
height={260}
class="rounded-full shadow z-30"
/>
<h1 class="mt-2">
<div class="text-2xl text-gray-800 font-bold leading-tight">
{userProfileProps.name}
</div>
<div class="text-xl text-gray-500 font-light">
{userProfileProps.username}
</div>
</h1>
<div class="text-gray-800 mt-4">{userProfileProps.bio}</div>
<div class="text-sm text-gray-600 my-4">
<Icon path={users} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.followers}
</span>{' '}
followers
</span>
<span class="mx-1">·</span>
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.following}
</span>{' '}
following
</span>
<span class="mx-1">·</span>
<Icon path={star} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.starredRepos}
</span>{' '}
</span>
</div>
<div class="text-sm text-gray-800 space-y-1">
{userProfileProps.company && (
<div>
<Icon path={buildingOffice} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.company}
</div>
)}
{userProfileProps.location && (
<div>
<Icon path={mapPin} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.location}
</div>
)}
{userProfileProps.websiteUrl && (
<div>
<Icon path={link} class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={userProfileProps.websiteUrl}
target="_blank"
rel="noreferrer"
>
{userProfileProps.websiteUrl}
</a>
</div>
)}
{userProfileProps.twitterUsername && (
<div>
<TwitterIcon class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={`https:/twitter.com/${userProfileProps.twitterUsername}`}
target="_blank"
rel="noreferrer"
>
@{userProfileProps.twitterUsername}
</a>
</div>
)}
</div>
{userProfileProps.organizations.length > 0 && (
<OrgList organizations={userProfileProps.organizations} />
)}
</div>
);
};

export default UserProfile;
30 changes: 30 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.spec.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
import { Router } from '@solidjs/router';
import { render } from 'solid-testing-library';
import { beforeEach, describe, expect, it } from 'vitest';
import UserProfile from './UserProfile.jsx';
import { userProfileProps } from './data';

describe('User profile card', () => {
let wrapper;
beforeEach(async () => {
wrapper = await render(() => (
<Router>
<UserProfile {...userProfileProps} />
</Router>
));
});

it('should mount', () => {
expect(wrapper).toBeTruthy();
});

it('should show the user display name', async () => {
const fullName = await wrapper.getByText(userProfileProps.name);
expect(fullName).toBeVisible();
});

it('should have a link for user profile picture', async () => {
const avatar = await wrapper.getByAltText('Avatar');
Comment thread
vyktoremario marked this conversation as resolved.
expect(avatar).toBeVisible();
});
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { Router } from '@solidjs/router';
import { UserProfile } from '.';
import { userProfileProps } from './data.js';

export default {
title: 'Components/User Profile Card',
component: UserProfile,
};

const Template = (args) => (
<Router>
<UserProfile {...args} />
</Router>
);
export const Default = Template.bind({});

Default.args = {
...userProfileProps,
};
18 changes: 18 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/data.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
export const userProfileProps = {
avatarUrl: 'https://avatars.githubusercontent.com/u/2487968?v=4',
bio: `Senior Software Engineer @thisdot`,
company: '@thisdot',
followers: 24,
following: 20,
location: 'Washington, DC',
login: 'tvanantwerp',
name: 'Tom VanAntwerp',
twitterUsername: 'tvanantwerp',
websiteUrl: 'https://tomvanantwerp.com',
organizations: [
{
avatarUrl: 'https://avatars.githubusercontent.com/u/22839396?v=4',
login: 'thisdot',
},
],
};
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/UserProfile/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export {default as UserProfile } from './UserProfile';
2 changes: 2 additions & 0 deletions solidjs-tailwind/src/components/index.js
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
export * from './CounterExample';
export * from './FetchExample';
export * from './UserProfile';
export * from './Icons';
export * from './RepoFilter';
export * from './GistPanel';
export * from './RepoMeta';
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [solidjs-tailwinf] Add user profile card by vyktoremario · Pull Request #830 · thisdot/starter.dev-github-showcases · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion solidjs-tailwind/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,7 +89,7 @@ git clone https://github.com/thisdot/starter.dev.git

- `pnpm run dev` - Runs the development server on localhost port 3000 with HMR
- `pnpm run test` - Runs the test suite
- `pnpm run storbook` - To showcase the component library
- `pnpm run storybook` - To showcase the component library
- `pnpm run build` - Builds a production version of the app to deploy
- `pnpm run serve` - Serves a production build on localhost port 4173
- `pnpm run lint` - Uses eslint to find potential issues in the codebase
Expand Down
1 change: 1 addition & 0 deletions solidjs-tailwind/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@
"@heroicons/react": "1.0.5"
},
"dependencies": {
"solid-heroicons": "^3.1.0",
"solid-js": "1.6.0"
},
"keywords": [
Expand Down
9 changes: 9 additions & 0 deletions solidjs-tailwind/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions solidjs-tailwind/src/components/Icons/TwitterIcon.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
function TwitterIcon(className) {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class={className.class || 'w-6 h-6'}
fill="currentColor"
>
<path fill="none" d="M0 0h24v24H0z" />
<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" />
</svg>
);
}

export default TwitterIcon;
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/Icons/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export { default as TwitterIcon } from './TwitterIcon';
19 changes: 19 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/OrgList.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { For } from 'solid-js';
function OrgList(props) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use splitProps as it shows a major feature in SolidJs

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerry I don't understand this comment. Can you explain why splitProps is required and how what you mean by it shows a major feature in SolidJs?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerrysplitProps is used when you want to pick out certain properties from an object and pass the rest into a child component. It works the same with Pick in typescript.

It doesn't make sense to use it when all the properties passed are going to be used in that component.

If we need to show how major a feature it is, we can have a store that returns both user and repositories data in one object, then split it into two and pass it into their respective components(or into components where the data is needed). Or do the split inside the component and retrieve just the properties needed by the component.

@hdJerryhdJerryNov 14, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the props are not reactive then no need. @vyktoremario , though it also help to know the props expected in there just like how TS does. SO if I view the component with the spiltProps or mergeProps i know the props allowed in that component.

return (
<div class="mt-5 border-t border-gray-200">
<h2 class="my-2 pt-2 text-gray-800 font-bold">Organizations</h2>
<div data-testid="profile page orgs" class="flex flex-wrap space-x-2">
<For each={props.organizations}>
{(props) => (
<div class="relative w-9 h-9 rounded border border-gray-300 overflow-hidden">
<img src={props.avatarUrl} alt="Organization" />
</div>
)}
</For>
</div>
</div>
);
}

export default OrgList;
101 changes: 101 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
import {
users,
star,
buildingOffice,
mapPin,
link,
} from 'solid-heroicons/outline';
import { Icon } from 'solid-heroicons';
import { TwitterIcon } from '../Icons';
import OrgList from './OrgList';

const UserProfile = (userProfileProps) => {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<div>
<img
Comment thread
vyktoremario marked this conversation as resolved.
src={userProfileProps.avatarUrl}
alt="Avatar"
width={260}
height={260}
class="rounded-full shadow z-30"
/>
<h1 class="mt-2">
<div class="text-2xl text-gray-800 font-bold leading-tight">
{userProfileProps.name}
</div>
<div class="text-xl text-gray-500 font-light">
{userProfileProps.username}
</div>
</h1>
<div class="text-gray-800 mt-4">{userProfileProps.bio}</div>
<div class="text-sm text-gray-600 my-4">
<Icon path={users} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.followers}
</span>{' '}
followers
</span>
<span class="mx-1">·</span>
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.following}
</span>{' '}
following
</span>
<span class="mx-1">·</span>
<Icon path={star} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.starredRepos}
</span>{' '}
</span>
</div>
<div class="text-sm text-gray-800 space-y-1">
{userProfileProps.company && (
<div>
<Icon path={buildingOffice} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.company}
</div>
)}
{userProfileProps.location && (
<div>
<Icon path={mapPin} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.location}
</div>
)}
{userProfileProps.websiteUrl && (
<div>
<Icon path={link} class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={userProfileProps.websiteUrl}
target="_blank"
rel="noreferrer"
>
{userProfileProps.websiteUrl}
</a>
</div>
)}
{userProfileProps.twitterUsername && (
<div>
<TwitterIcon class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={`https:/twitter.com/${userProfileProps.twitterUsername}`}
target="_blank"
rel="noreferrer"
>
@{userProfileProps.twitterUsername}
</a>
</div>
)}
</div>
{userProfileProps.organizations.length > 0 && (
<OrgList organizations={userProfileProps.organizations} />
)}
</div>
);
};

export default UserProfile;
30 changes: 30 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.spec.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
import { Router } from '@solidjs/router';
import { render } from 'solid-testing-library';
import { beforeEach, describe, expect, it } from 'vitest';
import UserProfile from './UserProfile.jsx';
import { userProfileProps } from './data';

describe('User profile card', () => {
let wrapper;
beforeEach(async () => {
wrapper = await render(() => (
<Router>
<UserProfile {...userProfileProps} />
</Router>
));
});

it('should mount', () => {
expect(wrapper).toBeTruthy();
});

it('should show the user display name', async () => {
const fullName = await wrapper.getByText(userProfileProps.name);
expect(fullName).toBeVisible();
});

it('should have a link for user profile picture', async () => {
const avatar = await wrapper.getByAltText('Avatar');
Comment thread
vyktoremario marked this conversation as resolved.
expect(avatar).toBeVisible();
});
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { Router } from '@solidjs/router';
import { UserProfile } from '.';
import { userProfileProps } from './data.js';

export default {
title: 'Components/User Profile Card',
component: UserProfile,
};

const Template = (args) => (
<Router>
<UserProfile {...args} />
</Router>
);
export const Default = Template.bind({});

Default.args = {
...userProfileProps,
};
18 changes: 18 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/data.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
export const userProfileProps = {
avatarUrl: 'https://avatars.githubusercontent.com/u/2487968?v=4',
bio: `Senior Software Engineer @thisdot`,
company: '@thisdot',
followers: 24,
following: 20,
location: 'Washington, DC',
login: 'tvanantwerp',
name: 'Tom VanAntwerp',
twitterUsername: 'tvanantwerp',
websiteUrl: 'https://tomvanantwerp.com',
organizations: [
{
avatarUrl: 'https://avatars.githubusercontent.com/u/22839396?v=4',
login: 'thisdot',
},
],
};
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/UserProfile/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export {default as UserProfile } from './UserProfile';
2 changes: 2 additions & 0 deletions solidjs-tailwind/src/components/index.js
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
export * from './CounterExample';
export * from './FetchExample';
export * from './UserProfile';
export * from './Icons';
export * from './RepoFilter';
export * from './GistPanel';
export * from './RepoMeta';
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [solidjs-tailwinf] Add user profile card by vyktoremario · Pull Request #830 · thisdot/starter.dev-github-showcases · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion solidjs-tailwind/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,7 +89,7 @@ git clone https://github.com/thisdot/starter.dev.git

- `pnpm run dev` - Runs the development server on localhost port 3000 with HMR
- `pnpm run test` - Runs the test suite
- `pnpm run storbook` - To showcase the component library
- `pnpm run storybook` - To showcase the component library
- `pnpm run build` - Builds a production version of the app to deploy
- `pnpm run serve` - Serves a production build on localhost port 4173
- `pnpm run lint` - Uses eslint to find potential issues in the codebase
Expand Down
1 change: 1 addition & 0 deletions solidjs-tailwind/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@
"@heroicons/react": "1.0.5"
},
"dependencies": {
"solid-heroicons": "^3.1.0",
"solid-js": "1.6.0"
},
"keywords": [
Expand Down
9 changes: 9 additions & 0 deletions solidjs-tailwind/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions solidjs-tailwind/src/components/Icons/TwitterIcon.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
function TwitterIcon(className) {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class={className.class || 'w-6 h-6'}
fill="currentColor"
>
<path fill="none" d="M0 0h24v24H0z" />
<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" />
</svg>
);
}

export default TwitterIcon;
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/Icons/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export { default as TwitterIcon } from './TwitterIcon';
19 changes: 19 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/OrgList.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { For } from 'solid-js';
function OrgList(props) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use splitProps as it shows a major feature in SolidJs

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerry I don't understand this comment. Can you explain why splitProps is required and how what you mean by it shows a major feature in SolidJs?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerrysplitProps is used when you want to pick out certain properties from an object and pass the rest into a child component. It works the same with Pick in typescript.

It doesn't make sense to use it when all the properties passed are going to be used in that component.

If we need to show how major a feature it is, we can have a store that returns both user and repositories data in one object, then split it into two and pass it into their respective components(or into components where the data is needed). Or do the split inside the component and retrieve just the properties needed by the component.

@hdJerryhdJerryNov 14, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the props are not reactive then no need. @vyktoremario , though it also help to know the props expected in there just like how TS does. SO if I view the component with the spiltProps or mergeProps i know the props allowed in that component.

return (
<div class="mt-5 border-t border-gray-200">
<h2 class="my-2 pt-2 text-gray-800 font-bold">Organizations</h2>
<div data-testid="profile page orgs" class="flex flex-wrap space-x-2">
<For each={props.organizations}>
{(props) => (
<div class="relative w-9 h-9 rounded border border-gray-300 overflow-hidden">
<img src={props.avatarUrl} alt="Organization" />
</div>
)}
</For>
</div>
</div>
);
}

export default OrgList;
101 changes: 101 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
import {
users,
star,
buildingOffice,
mapPin,
link,
} from 'solid-heroicons/outline';
import { Icon } from 'solid-heroicons';
import { TwitterIcon } from '../Icons';
import OrgList from './OrgList';

const UserProfile = (userProfileProps) => {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<div>
<img
Comment thread
vyktoremario marked this conversation as resolved.
src={userProfileProps.avatarUrl}
alt="Avatar"
width={260}
height={260}
class="rounded-full shadow z-30"
/>
<h1 class="mt-2">
<div class="text-2xl text-gray-800 font-bold leading-tight">
{userProfileProps.name}
</div>
<div class="text-xl text-gray-500 font-light">
{userProfileProps.username}
</div>
</h1>
<div class="text-gray-800 mt-4">{userProfileProps.bio}</div>
<div class="text-sm text-gray-600 my-4">
<Icon path={users} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.followers}
</span>{' '}
followers
</span>
<span class="mx-1">·</span>
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.following}
</span>{' '}
following
</span>
<span class="mx-1">·</span>
<Icon path={star} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.starredRepos}
</span>{' '}
</span>
</div>
<div class="text-sm text-gray-800 space-y-1">
{userProfileProps.company && (
<div>
<Icon path={buildingOffice} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.company}
</div>
)}
{userProfileProps.location && (
<div>
<Icon path={mapPin} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.location}
</div>
)}
{userProfileProps.websiteUrl && (
<div>
<Icon path={link} class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={userProfileProps.websiteUrl}
target="_blank"
rel="noreferrer"
>
{userProfileProps.websiteUrl}
</a>
</div>
)}
{userProfileProps.twitterUsername && (
<div>
<TwitterIcon class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={`https:/twitter.com/${userProfileProps.twitterUsername}`}
target="_blank"
rel="noreferrer"
>
@{userProfileProps.twitterUsername}
</a>
</div>
)}
</div>
{userProfileProps.organizations.length > 0 && (
<OrgList organizations={userProfileProps.organizations} />
)}
</div>
);
};

export default UserProfile;
30 changes: 30 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.spec.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
import { Router } from '@solidjs/router';
import { render } from 'solid-testing-library';
import { beforeEach, describe, expect, it } from 'vitest';
import UserProfile from './UserProfile.jsx';
import { userProfileProps } from './data';

describe('User profile card', () => {
let wrapper;
beforeEach(async () => {
wrapper = await render(() => (
<Router>
<UserProfile {...userProfileProps} />
</Router>
));
});

it('should mount', () => {
expect(wrapper).toBeTruthy();
});

it('should show the user display name', async () => {
const fullName = await wrapper.getByText(userProfileProps.name);
expect(fullName).toBeVisible();
});

it('should have a link for user profile picture', async () => {
const avatar = await wrapper.getByAltText('Avatar');
Comment thread
vyktoremario marked this conversation as resolved.
expect(avatar).toBeVisible();
});
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { Router } from '@solidjs/router';
import { UserProfile } from '.';
import { userProfileProps } from './data.js';

export default {
title: 'Components/User Profile Card',
component: UserProfile,
};

const Template = (args) => (
<Router>
<UserProfile {...args} />
</Router>
);
export const Default = Template.bind({});

Default.args = {
...userProfileProps,
};
18 changes: 18 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/data.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
export const userProfileProps = {
avatarUrl: 'https://avatars.githubusercontent.com/u/2487968?v=4',
bio: `Senior Software Engineer @thisdot`,
company: '@thisdot',
followers: 24,
following: 20,
location: 'Washington, DC',
login: 'tvanantwerp',
name: 'Tom VanAntwerp',
twitterUsername: 'tvanantwerp',
websiteUrl: 'https://tomvanantwerp.com',
organizations: [
{
avatarUrl: 'https://avatars.githubusercontent.com/u/22839396?v=4',
login: 'thisdot',
},
],
};
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/UserProfile/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export {default as UserProfile } from './UserProfile';
2 changes: 2 additions & 0 deletions solidjs-tailwind/src/components/index.js
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
export * from './CounterExample';
export * from './FetchExample';
export * from './UserProfile';
export * from './Icons';
export * from './RepoFilter';
export * from './GistPanel';
export * from './RepoMeta';
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [solidjs-tailwinf] Add user profile card by vyktoremario · Pull Request #830 · thisdot/starter.dev-github-showcases · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion solidjs-tailwind/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,7 +89,7 @@ git clone https://github.com/thisdot/starter.dev.git

- `pnpm run dev` - Runs the development server on localhost port 3000 with HMR
- `pnpm run test` - Runs the test suite
- `pnpm run storbook` - To showcase the component library
- `pnpm run storybook` - To showcase the component library
- `pnpm run build` - Builds a production version of the app to deploy
- `pnpm run serve` - Serves a production build on localhost port 4173
- `pnpm run lint` - Uses eslint to find potential issues in the codebase
Expand Down
1 change: 1 addition & 0 deletions solidjs-tailwind/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@
"@heroicons/react": "1.0.5"
},
"dependencies": {
"solid-heroicons": "^3.1.0",
"solid-js": "1.6.0"
},
"keywords": [
Expand Down
9 changes: 9 additions & 0 deletions solidjs-tailwind/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions solidjs-tailwind/src/components/Icons/TwitterIcon.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
function TwitterIcon(className) {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class={className.class || 'w-6 h-6'}
fill="currentColor"
>
<path fill="none" d="M0 0h24v24H0z" />
<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" />
</svg>
);
}

export default TwitterIcon;
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/Icons/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export { default as TwitterIcon } from './TwitterIcon';
19 changes: 19 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/OrgList.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { For } from 'solid-js';
function OrgList(props) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use splitProps as it shows a major feature in SolidJs

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerry I don't understand this comment. Can you explain why splitProps is required and how what you mean by it shows a major feature in SolidJs?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerrysplitProps is used when you want to pick out certain properties from an object and pass the rest into a child component. It works the same with Pick in typescript.

It doesn't make sense to use it when all the properties passed are going to be used in that component.

If we need to show how major a feature it is, we can have a store that returns both user and repositories data in one object, then split it into two and pass it into their respective components(or into components where the data is needed). Or do the split inside the component and retrieve just the properties needed by the component.

@hdJerryhdJerryNov 14, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the props are not reactive then no need. @vyktoremario , though it also help to know the props expected in there just like how TS does. SO if I view the component with the spiltProps or mergeProps i know the props allowed in that component.

return (
<div class="mt-5 border-t border-gray-200">
<h2 class="my-2 pt-2 text-gray-800 font-bold">Organizations</h2>
<div data-testid="profile page orgs" class="flex flex-wrap space-x-2">
<For each={props.organizations}>
{(props) => (
<div class="relative w-9 h-9 rounded border border-gray-300 overflow-hidden">
<img src={props.avatarUrl} alt="Organization" />
</div>
)}
</For>
</div>
</div>
);
}

export default OrgList;
101 changes: 101 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
import {
users,
star,
buildingOffice,
mapPin,
link,
} from 'solid-heroicons/outline';
import { Icon } from 'solid-heroicons';
import { TwitterIcon } from '../Icons';
import OrgList from './OrgList';

const UserProfile = (userProfileProps) => {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<div>
<img
Comment thread
vyktoremario marked this conversation as resolved.
src={userProfileProps.avatarUrl}
alt="Avatar"
width={260}
height={260}
class="rounded-full shadow z-30"
/>
<h1 class="mt-2">
<div class="text-2xl text-gray-800 font-bold leading-tight">
{userProfileProps.name}
</div>
<div class="text-xl text-gray-500 font-light">
{userProfileProps.username}
</div>
</h1>
<div class="text-gray-800 mt-4">{userProfileProps.bio}</div>
<div class="text-sm text-gray-600 my-4">
<Icon path={users} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.followers}
</span>{' '}
followers
</span>
<span class="mx-1">·</span>
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.following}
</span>{' '}
following
</span>
<span class="mx-1">·</span>
<Icon path={star} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.starredRepos}
</span>{' '}
</span>
</div>
<div class="text-sm text-gray-800 space-y-1">
{userProfileProps.company && (
<div>
<Icon path={buildingOffice} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.company}
</div>
)}
{userProfileProps.location && (
<div>
<Icon path={mapPin} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.location}
</div>
)}
{userProfileProps.websiteUrl && (
<div>
<Icon path={link} class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={userProfileProps.websiteUrl}
target="_blank"
rel="noreferrer"
>
{userProfileProps.websiteUrl}
</a>
</div>
)}
{userProfileProps.twitterUsername && (
<div>
<TwitterIcon class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={`https:/twitter.com/${userProfileProps.twitterUsername}`}
target="_blank"
rel="noreferrer"
>
@{userProfileProps.twitterUsername}
</a>
</div>
)}
</div>
{userProfileProps.organizations.length > 0 && (
<OrgList organizations={userProfileProps.organizations} />
)}
</div>
);
};

export default UserProfile;
30 changes: 30 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.spec.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
import { Router } from '@solidjs/router';
import { render } from 'solid-testing-library';
import { beforeEach, describe, expect, it } from 'vitest';
import UserProfile from './UserProfile.jsx';
import { userProfileProps } from './data';

describe('User profile card', () => {
let wrapper;
beforeEach(async () => {
wrapper = await render(() => (
<Router>
<UserProfile {...userProfileProps} />
</Router>
));
});

it('should mount', () => {
expect(wrapper).toBeTruthy();
});

it('should show the user display name', async () => {
const fullName = await wrapper.getByText(userProfileProps.name);
expect(fullName).toBeVisible();
});

it('should have a link for user profile picture', async () => {
const avatar = await wrapper.getByAltText('Avatar');
Comment thread
vyktoremario marked this conversation as resolved.
expect(avatar).toBeVisible();
});
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { Router } from '@solidjs/router';
import { UserProfile } from '.';
import { userProfileProps } from './data.js';

export default {
title: 'Components/User Profile Card',
component: UserProfile,
};

const Template = (args) => (
<Router>
<UserProfile {...args} />
</Router>
);
export const Default = Template.bind({});

Default.args = {
...userProfileProps,
};
18 changes: 18 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/data.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
export const userProfileProps = {
avatarUrl: 'https://avatars.githubusercontent.com/u/2487968?v=4',
bio: `Senior Software Engineer @thisdot`,
company: '@thisdot',
followers: 24,
following: 20,
location: 'Washington, DC',
login: 'tvanantwerp',
name: 'Tom VanAntwerp',
twitterUsername: 'tvanantwerp',
websiteUrl: 'https://tomvanantwerp.com',
organizations: [
{
avatarUrl: 'https://avatars.githubusercontent.com/u/22839396?v=4',
login: 'thisdot',
},
],
};
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/UserProfile/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export {default as UserProfile } from './UserProfile';
2 changes: 2 additions & 0 deletions solidjs-tailwind/src/components/index.js
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
export * from './CounterExample';
export * from './FetchExample';
export * from './UserProfile';
export * from './Icons';
export * from './RepoFilter';
export * from './GistPanel';
export * from './RepoMeta';
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [solidjs-tailwinf] Add user profile card by vyktoremario · Pull Request #830 · thisdot/starter.dev-github-showcases · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion solidjs-tailwind/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,7 +89,7 @@ git clone https://github.com/thisdot/starter.dev.git

- `pnpm run dev` - Runs the development server on localhost port 3000 with HMR
- `pnpm run test` - Runs the test suite
- `pnpm run storbook` - To showcase the component library
- `pnpm run storybook` - To showcase the component library
- `pnpm run build` - Builds a production version of the app to deploy
- `pnpm run serve` - Serves a production build on localhost port 4173
- `pnpm run lint` - Uses eslint to find potential issues in the codebase
Expand Down
1 change: 1 addition & 0 deletions solidjs-tailwind/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@
"@heroicons/react": "1.0.5"
},
"dependencies": {
"solid-heroicons": "^3.1.0",
"solid-js": "1.6.0"
},
"keywords": [
Expand Down
9 changes: 9 additions & 0 deletions solidjs-tailwind/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions solidjs-tailwind/src/components/Icons/TwitterIcon.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
function TwitterIcon(className) {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class={className.class || 'w-6 h-6'}
fill="currentColor"
>
<path fill="none" d="M0 0h24v24H0z" />
<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" />
</svg>
);
}

export default TwitterIcon;
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/Icons/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export { default as TwitterIcon } from './TwitterIcon';
19 changes: 19 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/OrgList.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { For } from 'solid-js';
function OrgList(props) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use splitProps as it shows a major feature in SolidJs

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerry I don't understand this comment. Can you explain why splitProps is required and how what you mean by it shows a major feature in SolidJs?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerrysplitProps is used when you want to pick out certain properties from an object and pass the rest into a child component. It works the same with Pick in typescript.

It doesn't make sense to use it when all the properties passed are going to be used in that component.

If we need to show how major a feature it is, we can have a store that returns both user and repositories data in one object, then split it into two and pass it into their respective components(or into components where the data is needed). Or do the split inside the component and retrieve just the properties needed by the component.

@hdJerryhdJerryNov 14, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the props are not reactive then no need. @vyktoremario , though it also help to know the props expected in there just like how TS does. SO if I view the component with the spiltProps or mergeProps i know the props allowed in that component.

return (
<div class="mt-5 border-t border-gray-200">
<h2 class="my-2 pt-2 text-gray-800 font-bold">Organizations</h2>
<div data-testid="profile page orgs" class="flex flex-wrap space-x-2">
<For each={props.organizations}>
{(props) => (
<div class="relative w-9 h-9 rounded border border-gray-300 overflow-hidden">
<img src={props.avatarUrl} alt="Organization" />
</div>
)}
</For>
</div>
</div>
);
}

export default OrgList;
101 changes: 101 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
import {
users,
star,
buildingOffice,
mapPin,
link,
} from 'solid-heroicons/outline';
import { Icon } from 'solid-heroicons';
import { TwitterIcon } from '../Icons';
import OrgList from './OrgList';

const UserProfile = (userProfileProps) => {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<div>
<img
Comment thread
vyktoremario marked this conversation as resolved.
src={userProfileProps.avatarUrl}
alt="Avatar"
width={260}
height={260}
class="rounded-full shadow z-30"
/>
<h1 class="mt-2">
<div class="text-2xl text-gray-800 font-bold leading-tight">
{userProfileProps.name}
</div>
<div class="text-xl text-gray-500 font-light">
{userProfileProps.username}
</div>
</h1>
<div class="text-gray-800 mt-4">{userProfileProps.bio}</div>
<div class="text-sm text-gray-600 my-4">
<Icon path={users} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.followers}
</span>{' '}
followers
</span>
<span class="mx-1">·</span>
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.following}
</span>{' '}
following
</span>
<span class="mx-1">·</span>
<Icon path={star} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.starredRepos}
</span>{' '}
</span>
</div>
<div class="text-sm text-gray-800 space-y-1">
{userProfileProps.company && (
<div>
<Icon path={buildingOffice} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.company}
</div>
)}
{userProfileProps.location && (
<div>
<Icon path={mapPin} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.location}
</div>
)}
{userProfileProps.websiteUrl && (
<div>
<Icon path={link} class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={userProfileProps.websiteUrl}
target="_blank"
rel="noreferrer"
>
{userProfileProps.websiteUrl}
</a>
</div>
)}
{userProfileProps.twitterUsername && (
<div>
<TwitterIcon class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={`https:/twitter.com/${userProfileProps.twitterUsername}`}
target="_blank"
rel="noreferrer"
>
@{userProfileProps.twitterUsername}
</a>
</div>
)}
</div>
{userProfileProps.organizations.length > 0 && (
<OrgList organizations={userProfileProps.organizations} />
)}
</div>
);
};

export default UserProfile;
30 changes: 30 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.spec.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
import { Router } from '@solidjs/router';
import { render } from 'solid-testing-library';
import { beforeEach, describe, expect, it } from 'vitest';
import UserProfile from './UserProfile.jsx';
import { userProfileProps } from './data';

describe('User profile card', () => {
let wrapper;
beforeEach(async () => {
wrapper = await render(() => (
<Router>
<UserProfile {...userProfileProps} />
</Router>
));
});

it('should mount', () => {
expect(wrapper).toBeTruthy();
});

it('should show the user display name', async () => {
const fullName = await wrapper.getByText(userProfileProps.name);
expect(fullName).toBeVisible();
});

it('should have a link for user profile picture', async () => {
const avatar = await wrapper.getByAltText('Avatar');
Comment thread
vyktoremario marked this conversation as resolved.
expect(avatar).toBeVisible();
});
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { Router } from '@solidjs/router';
import { UserProfile } from '.';
import { userProfileProps } from './data.js';

export default {
title: 'Components/User Profile Card',
component: UserProfile,
};

const Template = (args) => (
<Router>
<UserProfile {...args} />
</Router>
);
export const Default = Template.bind({});

Default.args = {
...userProfileProps,
};
18 changes: 18 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/data.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
export const userProfileProps = {
avatarUrl: 'https://avatars.githubusercontent.com/u/2487968?v=4',
bio: `Senior Software Engineer @thisdot`,
company: '@thisdot',
followers: 24,
following: 20,
location: 'Washington, DC',
login: 'tvanantwerp',
name: 'Tom VanAntwerp',
twitterUsername: 'tvanantwerp',
websiteUrl: 'https://tomvanantwerp.com',
organizations: [
{
avatarUrl: 'https://avatars.githubusercontent.com/u/22839396?v=4',
login: 'thisdot',
},
],
};
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/UserProfile/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export {default as UserProfile } from './UserProfile';
2 changes: 2 additions & 0 deletions solidjs-tailwind/src/components/index.js
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
export * from './CounterExample';
export * from './FetchExample';
export * from './UserProfile';
export * from './Icons';
export * from './RepoFilter';
export * from './GistPanel';
export * from './RepoMeta';
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [solidjs-tailwinf] Add user profile card by vyktoremario · Pull Request #830 · thisdot/starter.dev-github-showcases · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion solidjs-tailwind/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,7 +89,7 @@ git clone https://github.com/thisdot/starter.dev.git

- `pnpm run dev` - Runs the development server on localhost port 3000 with HMR
- `pnpm run test` - Runs the test suite
- `pnpm run storbook` - To showcase the component library
- `pnpm run storybook` - To showcase the component library
- `pnpm run build` - Builds a production version of the app to deploy
- `pnpm run serve` - Serves a production build on localhost port 4173
- `pnpm run lint` - Uses eslint to find potential issues in the codebase
Expand Down
1 change: 1 addition & 0 deletions solidjs-tailwind/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@
"@heroicons/react": "1.0.5"
},
"dependencies": {
"solid-heroicons": "^3.1.0",
"solid-js": "1.6.0"
},
"keywords": [
Expand Down
9 changes: 9 additions & 0 deletions solidjs-tailwind/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions solidjs-tailwind/src/components/Icons/TwitterIcon.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
function TwitterIcon(className) {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class={className.class || 'w-6 h-6'}
fill="currentColor"
>
<path fill="none" d="M0 0h24v24H0z" />
<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" />
</svg>
);
}

export default TwitterIcon;
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/Icons/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export { default as TwitterIcon } from './TwitterIcon';
19 changes: 19 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/OrgList.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { For } from 'solid-js';
function OrgList(props) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use splitProps as it shows a major feature in SolidJs

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerry I don't understand this comment. Can you explain why splitProps is required and how what you mean by it shows a major feature in SolidJs?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerrysplitProps is used when you want to pick out certain properties from an object and pass the rest into a child component. It works the same with Pick in typescript.

It doesn't make sense to use it when all the properties passed are going to be used in that component.

If we need to show how major a feature it is, we can have a store that returns both user and repositories data in one object, then split it into two and pass it into their respective components(or into components where the data is needed). Or do the split inside the component and retrieve just the properties needed by the component.

@hdJerryhdJerryNov 14, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the props are not reactive then no need. @vyktoremario , though it also help to know the props expected in there just like how TS does. SO if I view the component with the spiltProps or mergeProps i know the props allowed in that component.

return (
<div class="mt-5 border-t border-gray-200">
<h2 class="my-2 pt-2 text-gray-800 font-bold">Organizations</h2>
<div data-testid="profile page orgs" class="flex flex-wrap space-x-2">
<For each={props.organizations}>
{(props) => (
<div class="relative w-9 h-9 rounded border border-gray-300 overflow-hidden">
<img src={props.avatarUrl} alt="Organization" />
</div>
)}
</For>
</div>
</div>
);
}

export default OrgList;
101 changes: 101 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
import {
users,
star,
buildingOffice,
mapPin,
link,
} from 'solid-heroicons/outline';
import { Icon } from 'solid-heroicons';
import { TwitterIcon } from '../Icons';
import OrgList from './OrgList';

const UserProfile = (userProfileProps) => {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<div>
<img
Comment thread
vyktoremario marked this conversation as resolved.
src={userProfileProps.avatarUrl}
alt="Avatar"
width={260}
height={260}
class="rounded-full shadow z-30"
/>
<h1 class="mt-2">
<div class="text-2xl text-gray-800 font-bold leading-tight">
{userProfileProps.name}
</div>
<div class="text-xl text-gray-500 font-light">
{userProfileProps.username}
</div>
</h1>
<div class="text-gray-800 mt-4">{userProfileProps.bio}</div>
<div class="text-sm text-gray-600 my-4">
<Icon path={users} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.followers}
</span>{' '}
followers
</span>
<span class="mx-1">·</span>
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.following}
</span>{' '}
following
</span>
<span class="mx-1">·</span>
<Icon path={star} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.starredRepos}
</span>{' '}
</span>
</div>
<div class="text-sm text-gray-800 space-y-1">
{userProfileProps.company && (
<div>
<Icon path={buildingOffice} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.company}
</div>
)}
{userProfileProps.location && (
<div>
<Icon path={mapPin} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.location}
</div>
)}
{userProfileProps.websiteUrl && (
<div>
<Icon path={link} class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={userProfileProps.websiteUrl}
target="_blank"
rel="noreferrer"
>
{userProfileProps.websiteUrl}
</a>
</div>
)}
{userProfileProps.twitterUsername && (
<div>
<TwitterIcon class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={`https:/twitter.com/${userProfileProps.twitterUsername}`}
target="_blank"
rel="noreferrer"
>
@{userProfileProps.twitterUsername}
</a>
</div>
)}
</div>
{userProfileProps.organizations.length > 0 && (
<OrgList organizations={userProfileProps.organizations} />
)}
</div>
);
};

export default UserProfile;
30 changes: 30 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.spec.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
import { Router } from '@solidjs/router';
import { render } from 'solid-testing-library';
import { beforeEach, describe, expect, it } from 'vitest';
import UserProfile from './UserProfile.jsx';
import { userProfileProps } from './data';

describe('User profile card', () => {
let wrapper;
beforeEach(async () => {
wrapper = await render(() => (
<Router>
<UserProfile {...userProfileProps} />
</Router>
));
});

it('should mount', () => {
expect(wrapper).toBeTruthy();
});

it('should show the user display name', async () => {
const fullName = await wrapper.getByText(userProfileProps.name);
expect(fullName).toBeVisible();
});

it('should have a link for user profile picture', async () => {
const avatar = await wrapper.getByAltText('Avatar');
Comment thread
vyktoremario marked this conversation as resolved.
expect(avatar).toBeVisible();
});
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { Router } from '@solidjs/router';
import { UserProfile } from '.';
import { userProfileProps } from './data.js';

export default {
title: 'Components/User Profile Card',
component: UserProfile,
};

const Template = (args) => (
<Router>
<UserProfile {...args} />
</Router>
);
export const Default = Template.bind({});

Default.args = {
...userProfileProps,
};
18 changes: 18 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/data.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
export const userProfileProps = {
avatarUrl: 'https://avatars.githubusercontent.com/u/2487968?v=4',
bio: `Senior Software Engineer @thisdot`,
company: '@thisdot',
followers: 24,
following: 20,
location: 'Washington, DC',
login: 'tvanantwerp',
name: 'Tom VanAntwerp',
twitterUsername: 'tvanantwerp',
websiteUrl: 'https://tomvanantwerp.com',
organizations: [
{
avatarUrl: 'https://avatars.githubusercontent.com/u/22839396?v=4',
login: 'thisdot',
},
],
};
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/UserProfile/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export {default as UserProfile } from './UserProfile';
2 changes: 2 additions & 0 deletions solidjs-tailwind/src/components/index.js
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
export * from './CounterExample';
export * from './FetchExample';
export * from './UserProfile';
export * from './Icons';
export * from './RepoFilter';
export * from './GistPanel';
export * from './RepoMeta';
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [solidjs-tailwinf] Add user profile card by vyktoremario · Pull Request #830 · thisdot/starter.dev-github-showcases · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion solidjs-tailwind/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,7 +89,7 @@ git clone https://github.com/thisdot/starter.dev.git

- `pnpm run dev` - Runs the development server on localhost port 3000 with HMR
- `pnpm run test` - Runs the test suite
- `pnpm run storbook` - To showcase the component library
- `pnpm run storybook` - To showcase the component library
- `pnpm run build` - Builds a production version of the app to deploy
- `pnpm run serve` - Serves a production build on localhost port 4173
- `pnpm run lint` - Uses eslint to find potential issues in the codebase
Expand Down
1 change: 1 addition & 0 deletions solidjs-tailwind/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@
"@heroicons/react": "1.0.5"
},
"dependencies": {
"solid-heroicons": "^3.1.0",
"solid-js": "1.6.0"
},
"keywords": [
Expand Down
9 changes: 9 additions & 0 deletions solidjs-tailwind/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions solidjs-tailwind/src/components/Icons/TwitterIcon.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
function TwitterIcon(className) {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class={className.class || 'w-6 h-6'}
fill="currentColor"
>
<path fill="none" d="M0 0h24v24H0z" />
<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" />
</svg>
);
}

export default TwitterIcon;
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/Icons/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export { default as TwitterIcon } from './TwitterIcon';
19 changes: 19 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/OrgList.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { For } from 'solid-js';
function OrgList(props) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use splitProps as it shows a major feature in SolidJs

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerry I don't understand this comment. Can you explain why splitProps is required and how what you mean by it shows a major feature in SolidJs?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdJerrysplitProps is used when you want to pick out certain properties from an object and pass the rest into a child component. It works the same with Pick in typescript.

It doesn't make sense to use it when all the properties passed are going to be used in that component.

If we need to show how major a feature it is, we can have a store that returns both user and repositories data in one object, then split it into two and pass it into their respective components(or into components where the data is needed). Or do the split inside the component and retrieve just the properties needed by the component.

@hdJerryhdJerryNov 14, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the props are not reactive then no need. @vyktoremario , though it also help to know the props expected in there just like how TS does. SO if I view the component with the spiltProps or mergeProps i know the props allowed in that component.

return (
<div class="mt-5 border-t border-gray-200">
<h2 class="my-2 pt-2 text-gray-800 font-bold">Organizations</h2>
<div data-testid="profile page orgs" class="flex flex-wrap space-x-2">
<For each={props.organizations}>
{(props) => (
<div class="relative w-9 h-9 rounded border border-gray-300 overflow-hidden">
<img src={props.avatarUrl} alt="Organization" />
</div>
)}
</For>
</div>
</div>
);
}

export default OrgList;
101 changes: 101 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
import {
users,
star,
buildingOffice,
mapPin,
link,
} from 'solid-heroicons/outline';
import { Icon } from 'solid-heroicons';
import { TwitterIcon } from '../Icons';
import OrgList from './OrgList';

const UserProfile = (userProfileProps) => {
Comment thread
vyktoremario marked this conversation as resolved.
return (
<div>
<img
Comment thread
vyktoremario marked this conversation as resolved.
src={userProfileProps.avatarUrl}
alt="Avatar"
width={260}
height={260}
class="rounded-full shadow z-30"
/>
<h1 class="mt-2">
<div class="text-2xl text-gray-800 font-bold leading-tight">
{userProfileProps.name}
</div>
<div class="text-xl text-gray-500 font-light">
{userProfileProps.username}
</div>
</h1>
<div class="text-gray-800 mt-4">{userProfileProps.bio}</div>
<div class="text-sm text-gray-600 my-4">
<Icon path={users} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.followers}
</span>{' '}
followers
</span>
<span class="mx-1">·</span>
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.following}
</span>{' '}
following
</span>
<span class="mx-1">·</span>
<Icon path={star} class="w-4 h-4 mb-0.5 mr-1 inline" />
<span class="inline-block">
<span class="font-medium text-gray-900">
{userProfileProps.starredRepos}
</span>{' '}
</span>
</div>
<div class="text-sm text-gray-800 space-y-1">
{userProfileProps.company && (
<div>
<Icon path={buildingOffice} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.company}
</div>
)}
{userProfileProps.location && (
<div>
<Icon path={mapPin} class="w-4 h-4 mb-0.5 mr-1 inline" />
{userProfileProps.location}
</div>
)}
{userProfileProps.websiteUrl && (
<div>
<Icon path={link} class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={userProfileProps.websiteUrl}
target="_blank"
rel="noreferrer"
>
{userProfileProps.websiteUrl}
</a>
</div>
)}
{userProfileProps.twitterUsername && (
<div>
<TwitterIcon class="w-4 h-4 mb-0.5 mr-1 inline" />
<a
class="hover:text-blue-600 hover:underline"
href={`https:/twitter.com/${userProfileProps.twitterUsername}`}
target="_blank"
rel="noreferrer"
>
@{userProfileProps.twitterUsername}
</a>
</div>
)}
</div>
{userProfileProps.organizations.length > 0 && (
<OrgList organizations={userProfileProps.organizations} />
)}
</div>
);
};

export default UserProfile;
30 changes: 30 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/UserProfile.spec.jsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
import { Router } from '@solidjs/router';
import { render } from 'solid-testing-library';
import { beforeEach, describe, expect, it } from 'vitest';
import UserProfile from './UserProfile.jsx';
import { userProfileProps } from './data';

describe('User profile card', () => {
let wrapper;
beforeEach(async () => {
wrapper = await render(() => (
<Router>
<UserProfile {...userProfileProps} />
</Router>
));
});

it('should mount', () => {
expect(wrapper).toBeTruthy();
});

it('should show the user display name', async () => {
const fullName = await wrapper.getByText(userProfileProps.name);
expect(fullName).toBeVisible();
});

it('should have a link for user profile picture', async () => {
const avatar = await wrapper.getByAltText('Avatar');
Comment thread
vyktoremario marked this conversation as resolved.
expect(avatar).toBeVisible();
});
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { Router } from '@solidjs/router';
import { UserProfile } from '.';
import { userProfileProps } from './data.js';

export default {
title: 'Components/User Profile Card',
component: UserProfile,
};

const Template = (args) => (
<Router>
<UserProfile {...args} />
</Router>
);
export const Default = Template.bind({});

Default.args = {
...userProfileProps,
};
18 changes: 18 additions & 0 deletions solidjs-tailwind/src/components/UserProfile/data.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
export const userProfileProps = {
avatarUrl: 'https://avatars.githubusercontent.com/u/2487968?v=4',
bio: `Senior Software Engineer @thisdot`,
company: '@thisdot',
followers: 24,
following: 20,
location: 'Washington, DC',
login: 'tvanantwerp',
name: 'Tom VanAntwerp',
twitterUsername: 'tvanantwerp',
websiteUrl: 'https://tomvanantwerp.com',
organizations: [
{
avatarUrl: 'https://avatars.githubusercontent.com/u/22839396?v=4',
login: 'thisdot',
},
],
};
1 change: 1 addition & 0 deletions solidjs-tailwind/src/components/UserProfile/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
export {default as UserProfile } from './UserProfile';
2 changes: 2 additions & 0 deletions solidjs-tailwind/src/components/index.js
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
export * from './CounterExample';
export * from './FetchExample';
export * from './UserProfile';
export * from './Icons';
export * from './RepoFilter';
export * from './GistPanel';
export * from './RepoMeta';
Expand Down