Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/861n1vx9t/avatar #14

Merged
merged 55 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
356c443
feat(Avatar): create empty Avatar component
wleklinskimateusz Jul 17, 2023
7fa7796
chore: create story
wleklinskimateusz Jul 18, 2023
d1367a3
Merge branch 'development' into feat/861n1vx9t/avatar
wleklinskimateusz Jul 19, 2023
90e9900
feat(Avatar): add Avatar styles and style builder
wleklinskimateusz Jul 20, 2023
fe13ac6
refactor: rename file styleBuilder
wleklinskimateusz Jul 20, 2023
b1d2424
feat: added styles builder that creates styles for a single element
wleklinskimateusz Jul 21, 2023
3e72517
feat: type correctly config and variant in stylesBuilder
wleklinskimateusz Jul 23, 2023
41148e5
feat: correctly type Custom type and add image to Avatar
wleklinskimateusz Jul 23, 2023
611bbf6
feat: display initials on Avatar
wleklinskimateusz Jul 24, 2023
90b24d3
feat: working nested elements in stylesBuilder
wleklinskimateusz Jul 26, 2023
35338a0
feat: add Avatar docs
wleklinskimateusz Jul 26, 2023
a1ebd38
feat: complete Avatar Storybook docs page
wleklinskimateusz Jul 26, 2023
138d8ae
fix: shorten import
wleklinskimateusz Jul 26, 2023
2d4dcd9
fix: format problems
wleklinskimateusz Jul 26, 2023
8e84d6c
chore: update space size on CI
wleklinskimateusz Jul 26, 2023
ee158fc
Merge branch 'development' into feat/861n1vx9t/avatar
mwleklinski Jul 26, 2023
73492f3
fix: StatusDot test format issue
wleklinskimateusz Jul 26, 2023
afdc707
feat: remove global styles builder at Avatar
wleklinskimateusz Aug 11, 2023
4191749
Merge branch 'development' into feat/861n1vx9t/avatar
wleklinskimateusz Aug 11, 2023
bf60c62
chore: center story
wleklinskimateusz Aug 11, 2023
d502a4c
chore: remove global stylesBuilder
wleklinskimateusz Aug 11, 2023
ae80e2c
fix: fix lint problems
wleklinskimateusz Aug 11, 2023
74d2762
fix: fix lint problems
wleklinskimateusz Aug 11, 2023
5356050
refactor: optimise docs building using SB
wleklinskimateusz Aug 21, 2023
0201430
fix: fix width issue on Avatar Docs
wleklinskimateusz Aug 21, 2023
6c7816a
Merge branch 'development' into feat/861n1vx9t/avatar
mwleklinski Aug 21, 2023
271cb2f
refactor: extract types to seperate files
wleklinskimateusz Aug 21, 2023
1a80ee6
Merge branch 'feat/861n1vx9t/avatar' of https://github.com/VirtusLab/…
wleklinskimateusz Aug 21, 2023
9c4b008
chore: pr fixes
wleklinskimateusz Aug 21, 2023
27fa625
chore: fix yarn lint
wleklinskimateusz Aug 21, 2023
8f43544
chore: update yarn.lock
mwleklinski Aug 21, 2023
975f873
chore: update package.json
wleklinskimateusz Aug 21, 2023
9394eb0
chore: update yarn.lock
wleklinskimateusz Aug 21, 2023
f0dd2e7
chore: change node version
wleklinskimateusz Aug 22, 2023
4caa623
chore: rollback eslint version
wleklinskimateusz Aug 22, 2023
f0b8c07
chore: rollback all dependencies
wleklinskimateusz Aug 22, 2023
c41f7e8
chore: add eslint-plugin-prettier
wleklinskimateusz Aug 22, 2023
11aaf57
chore: add prettier to package.json
wleklinskimateusz Aug 22, 2023
6fff11f
chore: rollback prettier version
wleklinskimateusz Aug 22, 2023
54f4088
chore: change yarn.lock
wleklinskimateusz Aug 22, 2023
ca32590
chore: remove prettier from package.json
wleklinskimateusz Aug 22, 2023
656593f
chore: remove eslint plugin from package.json
wleklinskimateusz Aug 22, 2023
224722d
chore: bump eslint config
wleklinskimateusz Aug 22, 2023
e4e1b0f
chore: update package.json
wleklinskimateusz Aug 22, 2023
f215c7c
chore: change yarn.lock
wleklinskimateusz Aug 22, 2023
66958bc
chore: debug yarn.lock
wleklinskimateusz Aug 22, 2023
7273842
chore: remove yarn.lock
wleklinskimateusz Aug 22, 2023
8805bae
chore: remove yarn cache
wleklinskimateusz Aug 22, 2023
d770f18
chore: add yarn.lock
wleklinskimateusz Aug 22, 2023
bea491f
chore: update yarn.lock
wleklinskimateusz Aug 22, 2023
1ac1a3f
chore: bump packages
wleklinskimateusz Aug 22, 2023
a37add8
chore: yarn lock update
adrian-potepa Aug 22, 2023
299c7e9
Merge branch 'feat/861n1vx9t/avatar' of github.com:VirtusLab/tetrisly…
adrian-potepa Aug 22, 2023
52f2779
chore: resolution strip ansi lib
adrian-potepa Aug 22, 2023
1b9a005
chore: resolution string width lib
adrian-potepa Aug 22, 2023
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
9 changes: 9 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,13 @@ module.exports = {
'plugin:storybook/recommended',
'@virtuslab/eslint-config-tetrisly',
],
rules: {
'@typescript-eslint/no-use-before-define': [
2,
{
functions: false,
typedefs: false,
},
],
},
};
10 changes: 6 additions & 4 deletions .github/workflows/deploy-storybook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name: Deploy Storybook
on:
# Runs on pushes targeting the default branch
push:
branches: ["gh-pages"]
branches: ['gh-pages']
pull_request:
branches: ["gh-pages"]
branches: ['gh-pages']

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
Expand All @@ -19,7 +19,7 @@ permissions:
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
group: 'pages'
cancel-in-progress: false

jobs:
Expand All @@ -28,6 +28,8 @@ jobs:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
env:
NODE_OPTIONS: '--max_old_space_size=4096'
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -40,7 +42,7 @@ jobs:
uses: actions/upload-pages-artifact@v1
with:
# Upload entire repository
path: "."
path: '.'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
9 changes: 4 additions & 5 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ name: Node.js CI

on:
push:
branches: ["development"]
branches: ['development']
pull_request:
branches: ["development"]
branches: ['development']

jobs:
build:
runs-on: ubuntu-latest
environment:
name: development
env:
HELLO: ${{ secrets.HELLO }}

strategy:
matrix:
Expand All @@ -28,9 +26,10 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: "yarn"
cache: 'yarn'

- run: yarn install --frozen-lockfile
- run: yarn build
- run: yarn lint
- run: yarn build-storybook
- run: yarn test
8 changes: 1 addition & 7 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ const config: StorybookConfig = {
'@storybook/addon-interactions',
'@storybook/addon-styling',
'@storybook/addon-docs',
{
name: '@storybook/addon-styling',
options: {},
},
],
framework: {
name: '@storybook/react-vite',
Expand All @@ -22,9 +18,7 @@ const config: StorybookConfig = {
docs: {
autodocs: 'tag',
},
core: {
builder: '@storybook/builder-vite',
},
core: {},
async viteFinal(config) {
if (!config.plugins) {
config.plugins = [];
Expand Down
12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-is": "^18.2.0",
"styled-components": "^5.3.3"
"styled-components": "5.3.3"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.0.26",
Expand All @@ -39,23 +39,29 @@
"@types/styled-components": "^5.1.26",
"@typescript-eslint/eslint-plugin": "^5.61.0",
"@typescript-eslint/parser": "^5.61.0",
"@virtuslab/eslint-config-tetrisly": "^1.1.8",
"@virtuslab/eslint-config-tetrisly": "^1.2.3",
"@vitejs/plugin-react-swc": "^3.3.2",
"eslint": "^8.44.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.2",
"jsdom": "^22.1.0",
"prettier": "^3.0.2",
"storybook": "^7.0.26",
"typescript": "^5.1.6",
"vite": "^4.4.2",
"vite-plugin-dts": "^3.2.0",
"vite-tsconfig-paths": "^4.2.0",
"vitest": "^0.33.0"
"vitest": "^0.34.2"
},
"exports": {
".": {
"import": "./dist/tetrisly-icons.es.js",
"require": "./dist/tetrisly-icons.umd.js"
}
},
"resolutions": {
"strip-ansi": "^6.0.1",
"string-width": "^4.2.2"
}
}
28 changes: 28 additions & 0 deletions src/components/Avatar/Avatar.props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ImgHTMLAttributes } from 'react';

import { AvatarConfig } from './Avatar.styles';
import { AvatarShape } from './AvatarShape.type';
import { Appearance } from '../../types/Appearance';

import { Emphasis } from '@/types/Emphasis';
import { MarginProps } from '@/types/MarginProps';
import { Size } from '@/types/Size';
import { DeepPartial } from '@/utility-types/DeepPartial';

export type AvatarProps = (
| {
img: Omit<ImgHTMLAttributes<HTMLImageElement>, 'color'>;
appearance: 'image';
emphasis?: 'low';
initials?: never;
mwleklinski marked this conversation as resolved.
Show resolved Hide resolved
}
| {
appearance?: Appearance;
emphasis?: Emphasis;
initials: string;
}
) & {
shape?: AvatarShape;
size?: Size;
custom?: DeepPartial<AvatarConfig>;
} & MarginProps;
53 changes: 53 additions & 0 deletions src/components/Avatar/Avatar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Meta, StoryObj } from '@storybook/react';

import { Avatar } from './Avatar';
import { appearances } from '../../types/Appearance';

import { AvatarDocs } from '@/docs-components/AvatarDocs';
import { TetDocs } from '@/docs-components/TetDocs';

const meta = {
component: Avatar,
tags: ['autodocs'],
args: {
img: { src: 'https://thispersondoesnotexist.com/' },
},
argTypes: {
img: { if: { arg: 'appearance', eq: 'image' } },
initials: { if: { arg: 'appearance', neq: 'image' } },
emphasis: { if: { arg: 'appearance', neq: 'image' } },
},
parameters: {
controls: { sort: 'alpha' },
docs: {
description: {
component:
"A visual representation of a user's identity, often displayed as a small image or icon. Avatars can be personalized with user-uploaded photos or auto-generated images and are commonly used in profile sections, comments, and messaging interfaces.",
},
page: () => (
<TetDocs docs="https://docs.tetrisly.com/components/list/avatar">
<AvatarDocs />
</TetDocs>
),
},
},
} satisfies Meta<typeof Avatar>;

export default meta;
type Story = StoryObj<typeof Avatar>;

export const Initials: Story = {
argTypes: {
appearance: appearances,
},
args: {
initials: 'M',
appearance: 'cyan',
},
};

export const Image: Story = {
args: {
appearance: 'image',
},
};
76 changes: 76 additions & 0 deletions src/components/Avatar/Avatar.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { AvatarShape } from './AvatarShape.type';
import { Appearance, appearances } from '../../types/Appearance';

import { fromEntries } from '@/services/fromEntries';
import { Emphasis } from '@/types/Emphasis';
import { Size, sizes } from '@/types/Size';
import { BaseConfigProps } from '@/utility-types/BaseConfigProps';

export type AvatarConfig = {
nestedImage: BaseConfigProps;
shape: Record<AvatarShape, BaseConfigProps>;
size: Record<Size, BaseConfigProps>;
appearance: Record<
Appearance | 'image',
{
emphasis: Record<Emphasis, BaseConfigProps>;
}
>;
} & BaseConfigProps;

export const config = {
nestedImage: {
w: '100%',
h: '100%',
},
overflow: 'hidden',
position: 'relative',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
shape: {
rounded: {
borderRadius: 'full',
},
square: {
borderRadius: 'large',
},
},
size: sizes.reduce(
(acc, size) => ({
...acc,
[size]: {
w: size,
h: size,
text: `body-${size.includes('xSmall') ? 'strong-xSmall' : size}`,
},
}),
{} as Record<Size, BaseConfigProps>,
),

appearance: {
image: {
emphasis: {
high: {},
low: {},
},
},
...fromEntries(
appearances.map((appearance) => [
appearance,
{
emphasis: {
high: {
color: 'nonSemantic-white-content-primary',
backgroundColor: `nonSemantic-${appearance}-background-strong`,
},
low: {
color: `nonSemantic-${appearance}-content-primary`,
backgroundColor: `nonSemantic-${appearance}-background-muted`,
},
},
},
]),
),
},
} satisfies AvatarConfig;
42 changes: 42 additions & 0 deletions src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { FC, ImgHTMLAttributes } from 'react';

import { AvatarProps } from './Avatar.props';
import { config } from './Avatar.styles';
import { stylesBuilder } from './stylesBuilder';

import { tet } from '@/tetrisly';
import { MarginProps } from '@/types/MarginProps';

export const Avatar: FC<AvatarProps> = ({
appearance = 'blue',
emphasis = 'low',
shape = 'rounded',
size = 'medium',
custom = {},
initials,
...rest
}) => {
const { nestedImage, ...styles } = stylesBuilder({
config,
custom,
variant: { appearance, emphasis, shape, size },
});

const [img, marginProps] = extractImage(rest);

return (
<tet.div {...{ ...styles, ...marginProps }}>
{img !== null ? <tet.img {...nestedImage} {...img} /> : initials}
</tet.div>
);
};

function extractImage<T extends object>(obj: T) {
if ('img' in obj) {
const { img, ...marginProps } = obj as {
img: Omit<ImgHTMLAttributes<HTMLImageElement>, 'color'>;
} & MarginProps;
return [img, marginProps] as const;
}
return [null, obj] as const;
}
1 change: 1 addition & 0 deletions src/components/Avatar/AvatarShape.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type AvatarShape = 'rounded' | 'square';
33 changes: 33 additions & 0 deletions src/components/Avatar/stylesBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { merge } from 'lodash';

import { AvatarProps } from './Avatar.props';
import { AvatarConfig } from './Avatar.styles';

import { DeepPartial } from '@/utility-types/DeepPartial';

export function stylesBuilder({
config: originalConfig,
custom,
variant,
}: {
config: AvatarConfig;
custom: DeepPartial<AvatarConfig>;
variant: Required<
Pick<AvatarProps, 'appearance' | 'emphasis' | 'shape' | 'size'>
>;
}) {
const {
appearance: appearanceStyles,
shape: shapeStyles,
size: sizeStyles,
...base
} = merge(originalConfig, custom);
const { appearance, emphasis, shape, size } = variant;

return {
...base,
...appearanceStyles[appearance].emphasis[emphasis],
...shapeStyles[shape],
...sizeStyles[size],
};
}
Loading