Skip to content

Commit

Permalink
feat: TET-853 add FileItem component (#143)
Browse files Browse the repository at this point in the history
* feat: TET-853 add FileItem component

* feat: TET-853 tests for files services

* fix: TET-853 remove unused import

* refactor: TET-853 FileItem styles cleanup

* fix: TET-853 decrease padding of compressed variant buttons

* refactor: TET-853 unify props of internal FileItem components

* refactor: TET-853 remove redundant properties from FileItem story

* refactor: TET-853 use -width-small instead -width-100

* fix: TET-853 appropriate dot

* refactor: TET-853 introduce flexbox to ensure accuracy between text and dot
  • Loading branch information
mjamrozekvl authored May 16, 2024
1 parent db37e79 commit 9f50c5e
Show file tree
Hide file tree
Showing 30 changed files with 1,381 additions and 1 deletion.
23 changes: 23 additions & 0 deletions src/components/FileItem/FileItem.props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { FileItemConfig } from './FileItem.styles';
import { FileItemState, FileItemThumbnail, FileItemCommonProps } from './types';

export type FileItemProps = FileItemCommonProps & {
custom?: FileItemConfig;
isExtended?: boolean;
thumbnail?: FileItemThumbnail;
timeLeftText?: string;
};

export type Fallback = {
state: FileItemState;
isInverted: boolean;
isExtended: boolean;
thumbnail: FileItemThumbnail;
};

export const fallback: Fallback = {
state: 'uploading',
isInverted: false,
isExtended: false,
thumbnail: 'none',
};
124 changes: 124 additions & 0 deletions src/components/FileItem/FileItem.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { action } from '@storybook/addon-actions';
import type { Meta, StoryObj } from '@storybook/react';

import { FileItem } from './FileItem';
import { mockTextFile, mockImageFile } from './mocks';

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

const meta = {
title: 'FileItem',
component: FileItem,
tags: ['autodocs'],
argTypes: {},
args: {
file: mockTextFile(),
state: 'uploaded',
isInverted: false,
isExtended: false,
thumbnail: 'none',
uploadedPercentage: 25,
timeLeftText: '7 seconds left',
alertText: 'Short alert text',
onReplaceClick: action('onReplaceClick'),
onRetryClick: action('onRetryClick'),
onCloseClick: action('onCloseClick'),
},
parameters: {
docs: {
description: {
component:
'Enable users to upload specific files, such as images, documents, or videos, to a particular location. The user can perform this action by dragging and dropping files into the designated area or browsing local storage.',
},
page: () => (
<TetDocs docs="https://docs.tetrisly.com/components/in-progress/fileuploader">
<FileItemDocs />
</TetDocs>
),
},
},
} satisfies Meta<typeof FileItem>;

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

export const Default: Story = {
args: {},
};

export const Uploading: Story = {
args: {
state: 'uploading',
},
};

export const Uploaded: Story = {
args: {},
};

export const Replaceable: Story = {
args: {
state: 'replaceable',
},
};

export const Alert: Story = {
args: {
state: 'alert',
},
};

export const ExtendedUploading: Story = {
args: {
state: 'uploading',
isExtended: true,
},
};

export const ExtendedUploaded: Story = {
args: {
isExtended: true,
},
};

export const ExtendedReplaceable: Story = {
args: {
state: 'replaceable',
isExtended: true,
},
};

export const ExtendedAlert: Story = {
args: {
state: 'alert',
isExtended: true,
},
};

export const ExtendedUploadingFile: Story = {
args: {
state: 'uploading',
isExtended: true,
thumbnail: 'file',
},
};

export const ExtendedAlertImage: Story = {
args: {
file: mockImageFile(),
state: 'alert',
isExtended: true,
thumbnail: 'photo',
},
};

export const ExtendedInvertedAlertImage: Story = {
args: {
file: mockImageFile(),
state: 'alert',
isExtended: true,
isInverted: true,
thumbnail: 'photo',
},
};
52 changes: 52 additions & 0 deletions src/components/FileItem/FileItem.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { compressedVariantStyles, extendedVariantStyles } from './components';

import type { BaseProps } from '@/types/BaseProps';

export type FileItemConfig = BaseProps & {
state?: {
uploading?: BaseProps;
uploaded?: BaseProps;
replaceable?: BaseProps;
alert?: BaseProps;
};
inverted?: BaseProps;
invertedAlert?: BaseProps;
compressed?: BaseProps;
extended?: BaseProps;
};

export const defaultConfig = {
display: 'flex',
flexDirection: 'column',
gap: '$space-component-gap-small',
borderRadius: '$border-radius-large',
state: {
uploading: {
backgroundColor: '$color-interaction-neutral-subtle-normal',
},
uploaded: {
backgroundColor: '$color-interaction-default-subtle-normal',
},
replaceable: {
backgroundColor: '$color-interaction-default-subtle-normal',
},
alert: {
backgroundColor: '$color-interaction-alert-subtle-normal',
},
},
inverted: {
backgroundColor: '$color-interaction-background-formField',
borderWidth: '$border-width-small',
borderStyle: '$border-style-solid',
borderColor: '$color-interaction-border-neutral-normal',
},
invertedAlert: {
borderColor: '$color-interaction-border-alert',
},
compressed: compressedVariantStyles.defaultConfig,
extended: extendedVariantStyles.defaultConfig,
} as const satisfies FileItemConfig;

export const fileItemStyles = {
defaultConfig,
};
151 changes: 151 additions & 0 deletions src/components/FileItem/FileItem.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { vi } from 'vitest';

import { FileItem } from './FileItem';
import { mockImageFile, mockTextFile } from './mocks';
import { render, screen, fireEvent } from '../../tests/render';

describe('FileItem', () => {
it('should render empty file item', () => {
render(<FileItem file={mockTextFile()} />);
const fileItem = screen.getByTestId('file-item');
expect(fileItem).toBeInTheDocument();

const fileName = screen.getByTestId('file-name');
expect(fileName).toBeInTheDocument();

const fileSize = screen.getByTestId('file-size');
expect(fileSize).toBeInTheDocument();

const closeIcon = screen.getByTestId('close-icon');
expect(closeIcon).toBeInTheDocument();
});

it('should call onReplaceClick after click to Replace button', () => {
const onReplaceClick = vi.fn();

render(
<FileItem
file={mockTextFile()}
state="replaceable"
onReplaceClick={onReplaceClick}
/>,
);

const replaceableButton = screen.getByTestId('replaceable-button');
expect(replaceableButton).toBeInTheDocument();

fireEvent.click(replaceableButton);
expect(onReplaceClick).toHaveBeenCalled();
});

it('should call onRetryClick after click to Retry button', () => {
const onRetryClick = vi.fn();

render(
<FileItem
file={mockTextFile()}
state="alert"
onRetryClick={onRetryClick}
/>,
);

const retryButton = screen.getByTestId('retry-button');
expect(retryButton).toBeInTheDocument();

fireEvent.click(retryButton);
expect(onRetryClick).toHaveBeenCalled();
});

it('should call onCloseClick after click to close button', () => {
const onCloseClick = vi.fn();

render(
<FileItem
file={mockTextFile()}
state="uploading"
onCloseClick={onCloseClick}
/>,
);

const closeButton = screen.getByTestId('close-icon');
expect(closeButton).toBeInTheDocument();

fireEvent.click(closeButton);
expect(onCloseClick).toHaveBeenCalled();
});

it('should render progress bar in uploading state', () => {
render(<FileItem file={mockTextFile()} state="uploading" />);
const progressBar = screen.getByTestId('progress-bar');
expect(progressBar).toBeInTheDocument();
});

it('should show uploaded percentage in uploading state', () => {
render(
<FileItem
file={mockTextFile()}
state="uploading"
isExtended
uploadedPercentage={76.25}
/>,
);
const uploadedPercentage = screen.getByTestId('uploaded-percentage');
expect(uploadedPercentage).toBeInTheDocument();
expect(uploadedPercentage.innerHTML).toEqual('76.25%');
});

it('should show alert text in compressed alert state', () => {
render(
<FileItem
file={mockTextFile()}
state="alert"
alertText="Short alert text"
/>,
);
const alertText = screen.getByTestId('alert-text');
expect(alertText).toBeInTheDocument();
expect(alertText.innerHTML).toEqual('Short alert text');
});

it('should show alert text in extended alert state', () => {
render(
<FileItem
file={mockTextFile()}
isExtended
state="alert"
alertText="Short alert text"
/>,
);
const alertInfo = screen.getByTestId('alert-info');
expect(alertInfo).toBeInTheDocument();
expect(alertInfo.innerHTML).toContain('Short alert text');
});

it('should show thumbnail file icon', () => {
render(
<FileItem
file={mockTextFile()}
isExtended
state="uploaded"
thumbnail="file"
/>,
);
const thumbnailFile = screen.getByTestId('thumbnail-file');
expect(thumbnailFile).toBeInTheDocument();
});

it('should show thumbnail photo image', () => {
Object.defineProperty(window.URL, 'createObjectURL', { value: () => '' });

render(
<FileItem
file={mockImageFile()}
isExtended
state="uploaded"
thumbnail="photo"
/>,
);
const thumbnailPhoto = screen.getByTestId('thumbnail-photo');
expect(thumbnailPhoto).toBeInTheDocument();
});
});
Loading

0 comments on commit 9f50c5e

Please sign in to comment.