Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
3d993ab
added upload preview in input field
ravindrakele Feb 17, 2025
5b28567
fixes related to WP media uploader
ravindrakele Feb 18, 2025
910c26a
updated changelog.txt
ravindrakele Feb 18, 2025
e049773
Update changelog.txt
ravindrakele Feb 18, 2025
3bc7281
Remove file preview from the input
jaieds Mar 7, 2025
fa8324a
Create a separate File Preview component
jaieds Mar 7, 2025
7e6869b
Create a story for the file preview component
jaieds Mar 7, 2025
e28bc9e
chore: Lint
jaieds Mar 7, 2025
f07ca22
Merge branch 'staging' into new-component/file-preview
jaieds Mar 7, 2025
15ed544
Update description for better clarity about the prop
jaieds Mar 7, 2025
0303925
Update right side padding
jaieds Mar 7, 2025
85e6ed9
Extract and create a separate portal component
jaieds Mar 10, 2025
2986bcf
style: Enhance search input focus and disabled states
jaieds Mar 10, 2025
97d40a6
feat: Improve SearchBox component with new features and interactions
jaieds Mar 10, 2025
f403cdf
Update changelog.txt
jaieds Mar 10, 2025
cd1d4a5
style: Refine SearchBox input styling with minimal padding and height
jaieds Mar 10, 2025
6a4196c
feat: Enhance SearchBox with advanced keyboard navigation and accessi…
jaieds Mar 10, 2025
0ac4556
feat: Add `closeOnClick` option to SearchBox for improved interaction
jaieds Mar 10, 2025
42b444d
Prevent opening the dropdown
jaieds Mar 10, 2025
73e4237
added number and icon variant for completed step
ravindrakele Mar 11, 2025
d074ef5
Merge pull request #273 from jaieds/new-component/file-preview
jaieds Mar 12, 2025
9fb11a5
story and text fix
ravindrakele Mar 12, 2025
a54d2ae
string changed
ravindrakele Mar 12, 2025
d3b90ec
Removed props that are not required
jaieds Mar 12, 2025
e83a371
fix: TS errors
jaieds Mar 12, 2025
b65063d
Update search.stories.tsx
jaieds Mar 12, 2025
c4f8675
Moved filter prop to the root component
jaieds Mar 12, 2025
df1cf62
Apply className to the parent element
jaieds Mar 12, 2025
5bc68ad
Merge branch 'dev' of https://github.com/brainstormforce/force-ui int…
ravindrakele Mar 12, 2025
d627b3b
added changelog
ravindrakele Mar 12, 2025
9152210
Update prop name
jaieds Mar 13, 2025
36fc5f7
Set default value to undefined
jaieds Mar 13, 2025
ecff263
Add className to the wrapper
jaieds Mar 13, 2025
0fa3fa3
fix: FilePreview component accessibility issues
jaieds Mar 14, 2025
6a3de33
Merge pull request #278 from jaieds/accessibility-fixes
vrundakansara Mar 14, 2025
6455eb3
Merge branch 'dev' into searchbox-component/improvement
vrundakansara Mar 14, 2025
b9e1115
Show inner outline on focus for ghost variant
jaieds Mar 14, 2025
264f958
Merge branch 'searchbox-component/improvement' of https://github.com/…
jaieds Mar 14, 2025
ee98d5e
Update search result item padding
jaieds Mar 14, 2025
f05731b
Update dropdown offset value
jaieds Mar 14, 2025
6fa2baf
Update offset to match the Figma mockup
jaieds Mar 14, 2025
6e84472
Show keyboard shortcut based on OS
jaieds Mar 14, 2025
1f0c476
Merge pull request #277 from brainstormforce/progress-steps-fix
vrundakansara Mar 14, 2025
9e74b31
Merge branch 'dev' into pr/jaieds/275
jaieds Mar 14, 2025
09dbbf7
Merge pull request #275 from jaieds/searchbox-component/improvement
vrundakansara Mar 14, 2025
2ca5303
Update changelog.txt
vrundakansara Mar 14, 2025
c5f7550
Update search.stories.tsx
jaieds Mar 14, 2025
0213db5
Merge branch 'dev' of https://github.com/brainstormforce/force-ui int…
jaieds Mar 14, 2025
b6215b0
Merge pull request #280 from brainstormforce/test-fix
vrundakansara Mar 14, 2025
323e0e6
Update changelog.txt
jaieds Mar 14, 2025
d1c2d36
Bump version to `1.5.0`
jaieds Mar 14, 2025
b731a79
Merge branch 'dev' into version-bump
jaieds Mar 14, 2025
779d14d
Merge pull request #281 from brainstormforce/version-bump
jaieds Mar 14, 2025
82f2052
Merge pull request #279 from brainstormforce/dev
jaieds Mar 14, 2025
2a82fd6
Enhance FilePreview story by adding file input reference and resettin…
jaieds Mar 17, 2025
a3b661f
Merge pull request #283 from brainstormforce/file-preview-story-fix
vrundakansara Mar 17, 2025
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Using Force UI as a dependency in package.json -

```json
"dependencies": {
"@bsf/force-ui": "git+https://github.com/brainstormforce/force-ui#1.4.2"
"@bsf/force-ui": "git+https://github.com/brainstormforce/force-ui#1.5.0"
}
```

Expand All @@ -28,7 +28,7 @@ npm install
Or you can directly run the following command to install the package -

```bash
npm i -S @bsf/force-ui@git+https://github.com/brainstormforce/force-ui.git#1.4.2
npm i -S @bsf/force-ui@git+https://github.com/brainstormforce/force-ui.git#1.5.0
```

<br />
Expand Down
5 changes: 5 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Version 1.5.0 - 14th March, 2025
- New - Added new Atom, File Preview - to show uploaded file preview.
- New - Added new variants in Progress Steps component to show icon and number in the completed step.
- Improvement - Enhanced the UI and functionality of the Searchbox component for better flexibility and user experience.

Version 1.4.2 - 6th March, 2025
- New - Added new size 'xs' to the Switch component.
- Improvement - Adjusted the ring width and padding of the Radio Button component.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bsf/force-ui",
"version": "1.4.2",
"version": "1.5.0",
"description": "Library of components for the BSF project",
"main": "./dist/force-ui.cjs.js",
"module": "./dist/force-ui.es.js",
Expand Down
113 changes: 113 additions & 0 deletions src/components/file-preview/file-preview.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { Meta, StoryFn } from '@storybook/react';
import { FilePreview, FilePreviewFile, FilePreviewProps } from './file-preview';
import { Input } from '@/index';
import { useRef, useState } from 'react';

type InputValue = string | FileList | null;

const meta = {
title: 'Atoms/FilePreview',
component: FilePreview,
parameters: {
layout: 'centered',
},
tags: [ 'autodocs' ],
argTypes: {
onRemove: { action: 'removed' },
},
decorators: [
( Story: React.FC ) => (
<div className="w-72">
<Story />
</div>
),
],
} satisfies Meta<typeof FilePreview>;

export default meta;

type Story = StoryFn<typeof FilePreview>;

const Template: Story = ( args: FilePreviewProps ) => <FilePreview { ...args } />;

const placeholderFile = {
name: 'example-file.png',
url: 'https://placehold.co/600x400.png',
type: 'image/png',
size: 102400,
} as FilePreviewFile;

export const Default: Story = Template.bind( {} );
Default.args = {
file: placeholderFile,
onRemove: () => {},
disabled: false,
size: 'md',
error: false,
};

export const ErrorState = Template.bind( {} );
ErrorState.args = {
file: placeholderFile,
onRemove: () => {},
disabled: false,
size: 'md',
error: true,
};

export const DisabledState = Template.bind( {} );
DisabledState.args = {
file: placeholderFile,
onRemove: () => {},
disabled: true,
size: 'md',
error: false,
};

export const FileInputWithPreview: Story = ( args ) => {
const fileInputRef = useRef<HTMLInputElement>( null );
const [ selectedFile, setSelectedFile ] = useState<File | null>( null );

const handleFileChange = ( value: InputValue ) => {
if ( ! value ) {
setSelectedFile( null );
return;
}

let files: FileList | null = null;

if ( value instanceof FileList ) {
files = value;
}

setSelectedFile( files?.[ 0 ] || null );
};

return (
<>
<Input
ref={ fileInputRef }
label="Upload File"
type="file"
size="md"
disabled={ false }
error={ false }
onChange={ handleFileChange }
/>
{ selectedFile && (
<div className="mt-2">
<FilePreview
{ ...args }
file={ selectedFile }
onRemove={ () => {
setSelectedFile( null );
if ( fileInputRef.current ) {
fileInputRef.current.value = '';
}
} }
/>
</div>
) }
</>
);
};
135 changes: 135 additions & 0 deletions src/components/file-preview/file-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { cn, formatFileSize } from '@/utilities/functions';
import { File, ImageOff, Trash } from 'lucide-react';

export type FilePreviewFile =
| File
| { name: string; url: string; type: string; size: number };

export interface FilePreviewProps {
/** The file to display. It can be a File object or an object with name, url, type, and size properties. */
file: FilePreviewFile;

/** Function called when the file is removed. The parameter is the selected file object, which can be a File object or an object with name, url, type, and size properties or null. */
onRemove: ( selectedFile: FilePreviewFile ) => void;

/** Indicates whether the file preview is disabled. */
disabled?: boolean;

/** The size of the file preview. */
size?: 'sm' | 'md' | 'lg';

/** Indicates whether the file preview has an error. */
error?: boolean;
}

const commonFilePreviewClasses = {
sm: {
image: 'w-8 h-8',
name: 'text-xs',
fileIcon: 'h-8',
uploadText: 'text-xs',
},
md: {
image: 'w-10 h-10',
name: 'text-sm',
fileIcon: 'h-10',
uploadText: 'text-xs',
},
lg: {
image: 'w-10 h-10',
name: 'text-sm',
fileIcon: 'h-10',
uploadText: 'text-xs',
},
};

export const FilePreview = ( {
file,
onRemove,
error,
disabled,
size = 'sm',
}: FilePreviewProps ) => {
const renderFileIcon = () => (
<span
className={ cn(
'inline-flex self-start p-0.5',
commonFilePreviewClasses[ size ].fileIcon
) }
>
<File className="size-5 text-icon-primary" />
</span>
);

return (
<div
className={ cn(
'w-full flex items-start justify-between rounded mt-2 bg-field-primary-background p-2 gap-3',
error && 'border-alert-border-danger bg-alert-background-danger'
) }
>
<div className="flex items-center gap-3 w-full">
{ file.type.startsWith( 'image' ) ? (
<div
className={ cn(
'rounded-sm flex items-center justify-center shrink-0',
error && 'bg-gray-200'
) }
>
{ error ? (
<ImageOff className="size-6 text-field-helper" />
) : (
<img
src={
'url' in file
? file.url
: URL.createObjectURL( file )
}
alt="Preview"
className={ cn(
'w-full object-contain rounded-sm',
commonFilePreviewClasses[ size ].image
) }
/>
) }
</div>
) : (
renderFileIcon()
) }

<div className="text-left flex flex-col gap-0 w-[calc(100%_-_5.5rem)]">
<span
className={ cn(
commonFilePreviewClasses[ size ].name,
'font-medium text-field-label truncate'
) }
>
{ file.name }
</span>
{ file.size && file.size > 0 && (
<span
className={ cn(
commonFilePreviewClasses[ size ].uploadText,
'text-xs text-field-helper',
error && 'text-support-error'
) }
>
{ formatFileSize( file.size ) }
</span>
) }
</div>
{ ! disabled && (
<button
onClick={ () => onRemove( file ) }
className="inline-flex cursor-pointer bg-transparent border-0 p-1 my-0 ml-auto mr-0 ring-0 focus:outline-none self-start"
aria-label="Remove file"
>
<Trash className="size-4 text-support-error" />
</button>
) }
</div>
</div>
);
};

export default FilePreview;
1 change: 1 addition & 0 deletions src/components/file-preview/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './file-preview';
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ export { default as PieChart } from './pie-chart';
export { default as AreaChart } from './area-chart';
export { default as Dropzone } from './dropzone';
export { default as Table } from './table';
export { default as FilePreview } from './file-preview';
11 changes: 7 additions & 4 deletions src/components/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export declare interface InputProps {
disabled?: boolean;

/** Function called when the input value changes. */
onChange?: ( value: string | null ) => void;
onChange?: ( value: string | FileList | null ) => void;

/** Indicates whether the input has an error state. */
error?: boolean;
Expand Down Expand Up @@ -77,7 +77,10 @@ export const InputComponent = (
label = '',
...props
}: InputProps &
Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'prefix'>,
Omit<
React.InputHTMLAttributes<HTMLInputElement>,
'size' | 'prefix' | 'onChange'
>,
ref: React.ForwardedRef<HTMLInputElement>
) => {
const inputRef = useRef<HTMLInputElement>( null );
Expand All @@ -96,7 +99,7 @@ export const InputComponent = (
return;
}

let newValue: string | FileList | null;
let newValue: FileList | string | null;
if ( type === 'file' ) {
newValue = event.target.files;
if ( newValue && newValue.length > 0 ) {
Expand All @@ -115,7 +118,7 @@ export const InputComponent = (
if ( typeof onChange !== 'function' ) {
return;
}
onChange( newValue as string );
onChange( newValue );
};

const handleReset = () => {
Expand Down
34 changes: 33 additions & 1 deletion src/components/progress-steps/progress-steps.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ProgressSteps, { ProgressStepsProps } from './progress-steps';
import { Check, Home } from 'lucide-react';
import { Check, Home, BadgeCheck } from 'lucide-react';
import type { Meta, StoryFn } from '@storybook/react';

// ProgressSteps.Step display name for better documentation in Storybook
Expand Down Expand Up @@ -108,3 +108,35 @@ export const StackType = {
},
render: Template,
};

// Numbered Completed Steps Variant
export const NumberedCompletedSteps = {
args: {
variant: 'number',
size: 'md',
type: 'inline',
currentStep: 3,
completedVariant: 'number',
},
parameters: {
docs: {
description: {
story: 'Shows completed steps with step numbers in colored circles',
},
},
},
render: Template,
};

// custom icon in completed steps
export const CustomIconInCompletedSteps = {
args: {
variant: 'icon',
size: 'md',
type: 'inline',
currentStep: 3,
completedVariant: 'icon',
completedIcon: <BadgeCheck />,
},
render: Template,
};
Loading
Loading