From f64f0ff03c39cd1c02ea8c47d1d77e9ad1f4769f Mon Sep 17 00:00:00 2001 From: Jaied Al Sabid <87969327+jaieds@users.noreply.github.com> Date: Fri, 13 Dec 2024 09:34:51 +0600 Subject: [PATCH 01/27] wip: Table component --- src/components/table/index.ts | 2 + src/components/table/table.stories.tsx | 89 +++++++ src/components/table/table.tsx | 311 +++++++++++++++++++++++++ src/theme/default-config.js | 2 + 4 files changed, 404 insertions(+) create mode 100644 src/components/table/index.ts create mode 100644 src/components/table/table.stories.tsx create mode 100644 src/components/table/table.tsx diff --git a/src/components/table/index.ts b/src/components/table/index.ts new file mode 100644 index 00000000..9fa9eee7 --- /dev/null +++ b/src/components/table/index.ts @@ -0,0 +1,2 @@ +export { default as Table } from './table'; +export type { TableProps, Column } from './table'; \ No newline at end of file diff --git a/src/components/table/table.stories.tsx b/src/components/table/table.stories.tsx new file mode 100644 index 00000000..48c5ccba --- /dev/null +++ b/src/components/table/table.stories.tsx @@ -0,0 +1,89 @@ +import type { ComponentType } from 'react'; +import Table from './table'; +import { StoryFn, Meta } from '@storybook/react'; +import { Pagination } from '@/components'; + +const meta = { + title: 'Atoms/Table', + component: Table, + subcomponents: { + 'Table.Head': Table.Head, + 'Table.HeadCell': Table.HeadCell, + 'Table.Body': Table.Body, + 'Table.Row': Table.Row, + 'Table.Cell': Table.Cell, + 'Table.Footer': Table.Footer, + } as Record>, + tags: [ 'autodocs' ], +} satisfies Meta; +export default meta; + +type Story = StoryFn; + +const data = [ + { + name: 'John Doe', + age: 30, + email: 'KXk7g@example.com', + phone: '1234567890', + }, + { + name: 'Jane Doe', + age: 25, + email: 'oXHsO@example.com', + phone: '1234567890', + }, + { + name: 'Bob Smith', + age: 40, + email: 'oXHsO@example.com', + phone: '1234567890', + }, + { + name: 'Alice Johnson', + age: 35, + email: 'oXHsO@example.com', + phone: '1234567890', + }, +]; + +export const Default: Story = () => { + return ( + + + Name + Age + Email + Phone + + + { data.map( ( item, index ) => ( + + { item.name } + { item.age } + { item.email } + { item.phone } + + ) ) } + + +
+ Page 1 out of 10 + + + + 1 + 2 + 3 + + 7 + 8 + 9 + + + +
+
+
+ ); +}; diff --git a/src/components/table/table.tsx b/src/components/table/table.tsx new file mode 100644 index 00000000..c9db6cc3 --- /dev/null +++ b/src/components/table/table.tsx @@ -0,0 +1,311 @@ +import { cn } from '@/utilities/functions'; +import React, { Children, createContext, useContext, type ReactNode } from 'react'; + +/** + * Common props for all table components. + */ +export interface TableCommonProps { + /** + * Children to render within the component. + */ + children?: ReactNode; + /** + * Class name to apply to the component. + */ + className?: string; +} + +/** + * Interface for column configurations. + */ +export interface Column { + /** + * Header text to display in the column. + */ + header: string; + /** + * Property key from data object to display in this column. + */ + accessor: keyof T; + /** + * Whether this column can be sorted. + */ + sortable?: boolean; + /** + * Custom sort function for this column. + */ + sortFn?: ( a: T, b: T ) => number; + /** + * Custom cell renderer for this column. + */ + Cell?: ( props: { value: unknown; row: T; index: number } ) => ReactNode; +} + +/** + * Interface for table props. + */ +export interface TableProps extends TableCommonProps { + /** + * Whether to show checkboxes for row selection. + */ + showCheckbox?: boolean; + /** + * Callback fired when row selection changes. + */ + onSelectionChange?: ( selectedItems: T[] ) => void; + /** + * Child components to render within the table. + */ + children?: ReactNode; +} + +/** + * Interface for table context. + */ +export interface TableContextType { + /** + * Set of indices for selected rows. + */ + selectedRows?: Set; + /** + * Whether to show checkboxes for row selection. + */ + checkboxSelection?: boolean; +} + +/** + * Interface for base table props. + */ +export interface BaseTableProps extends TableCommonProps { + /** + * Child components to render within the table. + * + * @default undefined + */ + children?: ReactNode; + /** + * Whether to show checkboxes for row selection. + */ + checkboxSelection?: boolean; +} + +/** + * Interface for table head props. + */ +export interface TableHeadProps extends TableCommonProps { + /** + * Child components to render within the table head. + */ + children?: ReactNode; +} + +/** + * Interface for table head cell props. + */ +export interface TableHeadCellProps extends TableCommonProps { + /** + * Content to display in the header cell. + */ + children?: ReactNode; +} + +/** + * Interface for table body props. + */ +export interface TableBodyProps extends TableCommonProps { + /** + * Child components to render within the table body. + */ + children?: ReactNode; +} + +/** + * Interface for table row props. + */ +export interface TableRowProps extends TableCommonProps { + /** + * Child components to render within the table row. + */ + children?: ReactNode; + /** + * value of the row. + */ + value?: string; + /** + * Whether the row is selected. + */ + selected?: boolean; +} + +/** + * Interface for table cell props. + */ +export interface TableCellProps extends TableCommonProps { + /** + * Content to display in the table cell. + */ + children?: ReactNode; +} + +/** + * Interface for table footer props. + */ +export interface TableFooterProps extends TableCommonProps { + /** + * Child components to render within the table footer. + */ + children?: ReactNode; +} + +const TableContext = createContext( undefined ); + +const useTableContext = () => { + const context = useContext( TableContext ); + if ( ! context ) { + throw new Error( 'Table components must be used within Table component' ); + } + return context; +}; + +export const Table = ( { + children, + className, + checkboxSelection = false, +}: BaseTableProps ) => { + const contextValue: TableContextType = { + checkboxSelection, + }; + + // Extract footer from children + const footer = Children.toArray( children ).find( + ( child ) => React.isValidElement( child ) && child.type === TableFooter + ); + const restChildren = Children.toArray( children ).filter( + ( child ) => React.isValidElement( child ) && child.type !== TableFooter + ); + + return ( + +
+ + { restChildren } +
+ { footer } +
+
+ ); +}; + +// Head Components +export const TableHead: React.FC = ( { children } ) => { + return ( + + { children } + + ); +}; + +export const TableHeadCell: React.FC = ( { + children, + className, +} ) => { + return ( + + { children } + + ); +}; + +// Body Components +export const TableBody: React.FC = ( { + children, + className, +} ) => { + return ( + + { children } + + ); +}; + +export const TableRow: React.FC = ( { + children, + selected, + className, +} ) => { + return ( + + { children } + + ); +}; + +export const TableCell: React.FC = ( { + children, + className, +} ) => { + return ( + + { children } + + ); +}; + +// Table Footer +export const TableFooter: React.FC = ( { + children, + className, +} ) => { + const { checkboxSelection } = useTableContext(); + return ( +
+ { children } +
+ ); +}; + +// Update display name +Table.displayName = 'Table'; +TableHead.displayName = 'Table.Head'; +TableHeadCell.displayName = 'Table.HeadCell'; +TableBody.displayName = 'Table.Body'; +TableRow.displayName = 'Table.Row'; +TableCell.displayName = 'Table.Cell'; +TableFooter.displayName = 'Table.Footer'; + +// Assign compound components +Table.Head = TableHead; +Table.HeadCell = TableHeadCell; +Table.Body = TableBody; +Table.Row = TableRow; +Table.Cell = TableCell; +Table.Footer = TableFooter; + +export default Table; diff --git a/src/theme/default-config.js b/src/theme/default-config.js index aa5153cd..c0b6e7de 100644 --- a/src/theme/default-config.js +++ b/src/theme/default-config.js @@ -197,6 +197,8 @@ const defaultTheme = { tiny: '0.625rem', }, spacing: { + //18px + 4.5: '1.125rem', 120: '30rem', // 480px 95: '23.75rem', // 380px 141.5: '35.375rem', // 566px From 555adddd17db6caaf35277d04ec4bb764998a98f Mon Sep 17 00:00:00 2001 From: Jaied Al Sabid <87969327+jaieds@users.noreply.github.com> Date: Fri, 13 Dec 2024 09:39:09 +0600 Subject: [PATCH 02/27] fix: TailwindCSS not bundled in storybook after making any change --- .storybook/preview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index cad2f823..e09f6e48 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,8 +1,8 @@ import React from 'react'; import type { Preview } from '@storybook/react'; - +import '../src/tailwind.css'; /** @type { import('@storybook/react').Preview } */ -import '../dist/style.css'; + const preview: Preview = { parameters: { controls: { From 6e7eb2f19dea327802935653de027f9f67c5dded Mon Sep 17 00:00:00 2001 From: Jaied Al Sabid <87969327+jaieds@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:18:45 +0600 Subject: [PATCH 03/27] fix: Asterisk missing on required input label --- src/components/input/input.stories.tsx | 10 ++++++++++ src/components/input/input.tsx | 12 +++++++++--- src/components/label/label.tsx | 9 ++++++--- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/components/input/input.stories.tsx b/src/components/input/input.stories.tsx index c97c38b9..4fee7533 100644 --- a/src/components/input/input.stories.tsx +++ b/src/components/input/input.stories.tsx @@ -79,3 +79,13 @@ WithPrefixSuffix.args = { suffix: '#', defaultValue: '', }; + +// Required Input with Label +export const Required = Template.bind( {} ); +Required.args = { + type: 'text', + size: 'sm', + required: true, + label: 'Required Input', + defaultValue: 'Required Input', +}; diff --git a/src/components/input/input.tsx b/src/components/input/input.tsx index 839ffe40..50974e47 100644 --- a/src/components/input/input.tsx +++ b/src/components/input/input.tsx @@ -5,10 +5,12 @@ import React, { forwardRef, useRef, type ReactNode, + LabelHTMLAttributes, } from 'react'; import { nanoid } from 'nanoid'; import { cn } from '@/utilities/functions'; import { Upload, X } from 'lucide-react'; +import Label from '../label'; export declare interface InputProps { /** Unique identifier for the input element. */ @@ -52,6 +54,9 @@ export declare interface InputProps { /** Placeholder text for the input field. */ placeholder?: string; + + /** Indicates whether the input is required. */ + required?: boolean; } export const InputComponent = ( @@ -245,12 +250,13 @@ export const InputComponent = ( return null; } return ( - + ); }, [ label, size, inputId ] ); diff --git a/src/components/label/label.tsx b/src/components/label/label.tsx index b9fd168e..7a261cad 100644 --- a/src/components/label/label.tsx +++ b/src/components/label/label.tsx @@ -17,7 +17,7 @@ export interface LabelProps { } const Label = forwardRef( - ( + ( { children = null, tag: Tag = 'label', @@ -26,7 +26,7 @@ const Label = forwardRef( variant = 'neutral', // neutral, help, error, disabled required = false, ...props - }: LabelProps, + }: LabelProps & T, ref: React.Ref ) => { // Base classes. - Mandatory classes. @@ -78,4 +78,7 @@ const Label = forwardRef( } ); -export default Label; +export default Label as ( + props: LabelProps & T, + ref: React.Ref +) => React.ReactNode; From 752778cb01d375e79bea9f46285a46d403613cc7 Mon Sep 17 00:00:00 2001 From: Jaied Al Sabid <87969327+jaieds@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:20:01 +0600 Subject: [PATCH 04/27] Update changelog.txt --- changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog.txt b/changelog.txt index 62b39fe6..4a314439 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,6 @@ +Version x.x.x - x x, x +- Fixed - Asterisk missing on required input label. + Version 1.2.2 - 4th December, 2024 - Improvement - Removed margin and added new props to the Line Chart component for customizability. From b082558ced74af1f8f6583a199e141adb383b2fe Mon Sep 17 00:00:00 2001 From: Jaied Al Sabid <87969327+jaieds@users.noreply.github.com> Date: Fri, 13 Dec 2024 12:34:36 +0600 Subject: [PATCH 05/27] fix: Preview font family and box sizing --- .storybook/preview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index e09f6e48..d7377c93 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -20,7 +20,7 @@ const preview: Preview = { }, decorators: [ (Story) => ( -
+
), From a4aa43664fd3dd29c653726bf6a505d72c279c42 Mon Sep 17 00:00:00 2001 From: Jaied Al Sabid <87969327+jaieds@users.noreply.github.com> Date: Fri, 13 Dec 2024 12:35:42 +0600 Subject: [PATCH 06/27] chore: Added comment --- src/theme/default-config.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/theme/default-config.js b/src/theme/default-config.js index c0b6e7de..5d2732f1 100644 --- a/src/theme/default-config.js +++ b/src/theme/default-config.js @@ -197,8 +197,7 @@ const defaultTheme = { tiny: '0.625rem', }, spacing: { - //18px - 4.5: '1.125rem', + 4.5: '1.125rem', // 18px 120: '30rem', // 480px 95: '23.75rem', // 380px 141.5: '35.375rem', // 566px From e48f3402ab75000db4f7f95cf94ccb1dbfe8b2ad Mon Sep 17 00:00:00 2001 From: Jaied Al Sabid <87969327+jaieds@users.noreply.github.com> Date: Fri, 13 Dec 2024 12:36:05 +0600 Subject: [PATCH 07/27] fix: Checkbox alignment and added a story --- src/components/checkbox/checkbox.stories.tsx | 3 +++ src/components/checkbox/checkbox.tsx | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/checkbox/checkbox.stories.tsx b/src/components/checkbox/checkbox.stories.tsx index 8d6ca63a..f4a753dc 100644 --- a/src/components/checkbox/checkbox.stories.tsx +++ b/src/components/checkbox/checkbox.stories.tsx @@ -35,3 +35,6 @@ Disabled.args = { label: { heading: 'Checkbox Label', description: 'Checkbox Description' }, disabled: true, }; + +export const WithOutLabel = Template.bind( {} ); +WithOutLabel.args = {}; diff --git a/src/components/checkbox/checkbox.tsx b/src/components/checkbox/checkbox.tsx index b3054fc9..1c6e547e 100644 --- a/src/components/checkbox/checkbox.tsx +++ b/src/components/checkbox/checkbox.tsx @@ -145,14 +145,14 @@ export const CheckboxComponent = ( return (