diff --git a/README.md b/README.md index 1ec364cd..9d8f2e29 100644 --- a/README.md +++ b/README.md @@ -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.6.0" + "@bsf/force-ui": "git+https://github.com/brainstormforce/force-ui#1.6.1" } ``` @@ -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.6.0 +npm i -S @bsf/force-ui@git+https://github.com/brainstormforce/force-ui.git#1.6.1 ```
diff --git a/changelog.txt b/changelog.txt index 744e805f..b9259052 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +Version 1.6.1 - 11th April, 2025 +- Improvement - Added a new prop to the LineChart component to enable Biaxial Line chart functionality, along with various UI enhancements for improved user experience. +- Improvement - Added customizable properties for the LineChart and AreaChart components to tailor x-axis and y-axis ticks and labels for better data visualization. +- Improvement - Introduced a `className` prop in the Dropzone component to enhance customizability and allow for more flexible styling options. +- Fix - Corrected the positioning of days in the DatePicker component to ensure accurate display and improved user experience. + Version 1.6.0 - 8th April, 2025 - New - Introduced a versatile Text component that supports multiple HTML elements and customizable styles for enhanced typography flexibility. - Improvement - Display xAxis data on the tooltip when hovering. diff --git a/package-lock.json b/package-lock.json index bfc0a1f9..ff173138 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bsf/force-ui", - "version": "1.6.0", + "version": "1.6.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bsf/force-ui", - "version": "1.6.0", + "version": "1.6.1", "license": "ISC", "dependencies": { "@emotion/is-prop-valid": "^1.3.0", diff --git a/package.json b/package.json index 48870db4..642a6425 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bsf/force-ui", - "version": "1.6.0", + "version": "1.6.1", "description": "Library of components for the BSF project", "main": "./dist/force-ui.cjs.js", "module": "./dist/force-ui.es.js", diff --git a/src/components/area-chart/area-chart.stories.tsx b/src/components/area-chart/area-chart.stories.tsx index 69de4b6f..3ad97839 100644 --- a/src/components/area-chart/area-chart.stories.tsx +++ b/src/components/area-chart/area-chart.stories.tsx @@ -4,7 +4,7 @@ import Label from '../label'; import Button from '../button'; import Badge from '../badge'; import Container from '../container'; -import { ArrowUpRight, ArrowUp } from 'lucide-react'; +import { ArrowUpRight, ArrowUp, AlertCircle } from 'lucide-react'; const areaChartData = [ { month: 'January', sales: 186, expenses: 80 }, @@ -15,7 +15,21 @@ const areaChartData = [ { month: 'June', sales: 214, expenses: 140 }, ]; +// Data with large values to demonstrate Y-axis formatting +const largeValuesData = [ + { month: 'January', pageviews: 1200, sessions: 800 }, + { month: 'February', pageviews: 2800, sessions: 1500 }, + { month: 'March', pageviews: 5500, sessions: 2900 }, + { month: 'April', pageviews: 8200, sessions: 4100 }, + { month: 'May', pageviews: 14000, sessions: 6200 }, + { month: 'June', pageviews: 18500, sessions: 8800 }, +]; + +// Empty data for demonstrating custom no data component +const emptyData: { month: string; sales: number; expenses: number }[] = []; + const dataKeys = [ 'sales', 'expenses' ]; +const largeDataKeys = [ 'pageviews', 'sessions' ]; const chartDataIteractive = [ { date: '2024-04-01', desktop: 222, mobile: 150 }, @@ -121,6 +135,29 @@ const colors = [ // Custom tick formatter function for months const monthFormatter = ( value: string ) => value.slice( 0, 3 ); +// Custom Y-axis formatter function to display values in K/M format +const yAxisFormatter = ( value: number ) => { + if ( value >= 1000000 ) { + return `${ ( value / 1000000 ).toFixed( 1 ) }M`; + } + if ( value >= 1000 ) { + return `${ ( value / 1000 ).toFixed( 1 ) }K`; + } + return value.toString(); +}; + +// Custom No Data Component +const CustomNoDataComponent = () => ( +
+ +
No data found
+

+ There is no data available for this chart at the moment. Try + adjusting your filters or check back later. +

+
+); + const monthFormatterInteractive = ( value: string ) => { const date = new Date( value ); return date.toLocaleDateString( 'en-US', { @@ -187,7 +224,83 @@ export const AreaChartInteractive: Story = { }, }; +export const AreaChartWithFormattedYAxis: Story = { + args: { + chartWidth: 600, + chartHeight: 300, + data: largeValuesData, + dataKeys: largeDataKeys, + colors: [ + { stroke: '#3b82f6', fill: '#BFDBFE' }, + { stroke: '#f97316', fill: '#FFEDD5' }, + ], + variant: 'solid', + showXAxis: true, + xAxisDataKey: 'month', + showYAxis: true, + tickFormatter: monthFormatter, + yAxisTickFormatter: yAxisFormatter, + showLegend: true, + areaChartWrapperProps: { + margin: { + left: 35, + right: 14, + top: 6, + bottom: 6, + }, + }, + }, +}; + +export const AreaChartGradientWithFormattedYAxis: Story = { + args: { + chartWidth: 600, + chartHeight: 300, + data: largeValuesData, + dataKeys: largeDataKeys, + colors: [ + { stroke: '#3b82f6', fill: '#BFDBFE' }, + { stroke: '#f97316', fill: '#FFEDD5' }, + ], + variant: 'gradient', + showXAxis: true, + xAxisDataKey: 'month', + showYAxis: true, + tickFormatter: monthFormatter, + yAxisTickFormatter: yAxisFormatter, + showLegend: true, + areaChartWrapperProps: { + margin: { + left: 35, + right: 14, + top: 6, + bottom: 6, + }, + }, + }, +}; + +export const AreaChartWithCustomNoDataComponent: Story = { + args: { + chartWidth: 600, + chartHeight: 300, + data: emptyData, + dataKeys, + colors, + variant: 'solid', + showXAxis: true, + xAxisDataKey: 'month', + showYAxis: true, + noDataComponent: , + }, +}; + AreaChartInteractive.storyName = 'Area Chart Gradient with Legend'; +AreaChartWithFormattedYAxis.storyName = 'Area Chart with Formatted Y-Axis'; +AreaChartGradientWithFormattedYAxis.storyName = + 'Area Chart Gradient with Formatted Y-Axis'; +AreaChartWithCustomNoDataComponent.storyName = + 'Area Chart with Custom No Data Component'; type Story1 = StoryFn; diff --git a/src/components/area-chart/area-chart.tsx b/src/components/area-chart/area-chart.tsx index fe9f1c13..2255206f 100644 --- a/src/components/area-chart/area-chart.tsx +++ b/src/components/area-chart/area-chart.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useState, type ReactNode } from 'react'; import { AreaChart as AreaChartWrapper, Area, @@ -14,6 +14,13 @@ import ChartTooltipContent from './chart-tooltip-content'; import Label from '../label'; import type { CategoricalChartProps } from 'recharts/types/chart/generateCategoricalChart'; +// Default color constants +const DEFAULT_FONT_COLOR = '#6B7280'; +const DEFAULT_AREA_COLORS = [ + { stroke: '#2563EB', fill: '#BFDBFE' }, + { stroke: '#38BDF8', fill: '#BAE6FD' }, +]; + interface DataItem { [key: string]: number | string; // Adjust based on your data structure } @@ -56,9 +63,18 @@ interface AreaChartProps { /** Whether to display the ``, adding horizontal and vertical grid lines. */ showCartesianGrid?: boolean; - /** A function used to format the ticks on the axes, e.g., for formatting dates or numbers. */ + /** A function used to format the ticks on the x-axis, e.g., for formatting dates or numbers. */ + xAxisTickFormatter?: ( value: string ) => string; + + /** + * A function used to format the ticks on the x-axis, e.g., for formatting dates or numbers. + * @deprecated Use `xAxisTickFormatter` instead. + */ tickFormatter?: ( value: string ) => string; + /** A function used to format the ticks on the y-axis, e.g., for converting 1000 to 1K. */ + yAxisTickFormatter?: ( value: number ) => string; + /** The key in the data objects representing values for the x-axis. This is used to access the x-axis values from each data entry. */ xAxisDataKey?: string; @@ -85,6 +101,12 @@ interface AreaChartProps { CategoricalChartProps, 'width' | 'height' | 'data' >; + + /** + * Custom component to display when no data is available. + * If not provided, a default "No data available" message will be displayed. + */ + noDataComponent?: ReactNode; } const AreaChart = ( { @@ -99,11 +121,13 @@ const AreaChart = ( { tooltipLabelKey, showLegend = true, showCartesianGrid = true, + xAxisTickFormatter, tickFormatter, + yAxisTickFormatter, xAxisDataKey, yAxisDataKey, xAxisFontSize = 'sm', // sm, md, lg - xAxisFontColor = '#6B7280', + xAxisFontColor = DEFAULT_FONT_COLOR, chartWidth = 350, chartHeight = 200, areaChartWrapperProps = { @@ -114,17 +138,12 @@ const AreaChart = ( { bottom: 6, }, }, + noDataComponent, }: AreaChartProps ) => { const [ width, setWidth ] = useState( chartWidth ); const [ height, setHeight ] = useState( chartHeight ); - // Default colors - const defaultColors: Color[] = [ - { stroke: '#2563EB', fill: '#BFDBFE' }, - { stroke: '#38BDF8', fill: '#BAE6FD' }, - ]; - - const appliedColors = colors.length > 0 ? colors : defaultColors; + const appliedColors = colors.length > 0 ? colors : DEFAULT_AREA_COLORS; useEffect( () => { setWidth( chartWidth ); @@ -167,9 +186,11 @@ const AreaChart = ( { if ( ! data || data.length === 0 ) { return ( - + noDataComponent || ( + + ) ); } @@ -182,18 +203,20 @@ const AreaChart = ( { tickLine={ false } axisLine={ false } tickMargin={ 8 } - tickFormatter={ tickFormatter } + tickFormatter={ xAxisTickFormatter || tickFormatter } tick={ { fontSize: fontSizeVariant, fill: xAxisFontColor, } } hide={ ! showXAxis } + interval="preserveStartEnd" /> ) } -
+
{ item[ nameKey ] || item.dataKey } { formatter diff --git a/src/components/datepicker/datepicker-component.tsx b/src/components/datepicker/datepicker-component.tsx index 58319a9a..83fcc9ba 100644 --- a/src/components/datepicker/datepicker-component.tsx +++ b/src/components/datepicker/datepicker-component.tsx @@ -349,7 +349,10 @@ const DatePickerComponent = ( { const shouldShowDay = isThisMonth || isRangeEndInCurrentMonth || isPartOfRange; - const showOutsideDates = ! showOutsideDays && isOutside; + + // Fix: Corrected the logic for hiding outside dates + // Only hide outside days when showOutsideDays is false AND the day is outside + const hideOutsideDay = ! showOutsideDays && isOutside; // Common class for disabled outside days const disabledOutsideClass = @@ -398,7 +401,7 @@ const DatePickerComponent = ( { className={ cn( buttonClasses, isToday && 'font-semibold', - showOutsideDates && 'opacity-0', + hideOutsideDay && 'opacity-0', isRangeStart && 'fui-range-start', isRangeEnd && 'fui-range-end', isRangeMiddle && 'fui-range-middle', @@ -415,8 +418,7 @@ const DatePickerComponent = ( { data-selected={ isSelected } data-day={ format( day.date, 'yyyy-MM-dd' ) } > - { ( ! showOutsideDates || ( isPartOfRange && shouldShowDay ) ) && - customDayProps.children } + { customDayProps.children } { isToday && shouldShowDay && ( ) } @@ -553,6 +555,7 @@ const DatePickerComponent = ( { ...classNames, } } numberOfMonths={ numberOfMonths } + showOutsideDays={ true } components={ { MonthCaption: CustomMonthCaption as unknown as CustomComponents['MonthCaption'], diff --git a/src/components/dropzone/dropzone.tsx b/src/components/dropzone/dropzone.tsx index a4f8881d..09581dc7 100644 --- a/src/components/dropzone/dropzone.tsx +++ b/src/components/dropzone/dropzone.tsx @@ -21,6 +21,10 @@ export interface DropzoneProps { error?: boolean; /** Error text to display */ errorText?: string; + /** Custom class name for the dropzone */ + className?: string; + /** Custom class name for the wrapper */ + wrapperClassName?: string; } // Context interface for file data sharing @@ -127,6 +131,8 @@ export const Dropzone = ( { disabled = false, error = false, errorText = 'Upload failed, please try again.', + className = '', + wrapperClassName = '', }: DropzoneProps ) => { const [ isLoading, setIsLoading ] = useState( false ); const [ file, setFile ] = useState( null ); @@ -214,7 +220,7 @@ export const Dropzone = ( { -
+