Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
Version 1.6.0 - xth x, 2025
- New - Introduced a versatile Text component that supports multiple HTML elements and customizable styles for enhanced typography flexibility.

Version 1.5.1 - 28th March, 2025
- Improvement - Introduced the `areaChartWrapper` prop in the AreaChart component to allow for better customization options.
- Improvement - Removed the group selector from the Tabs component to simplify style overrides.
Expand Down
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ 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';
export { default as Text } from './text';
1 change: 1 addition & 0 deletions src/components/text/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './text';
63 changes: 63 additions & 0 deletions src/components/text/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* The class names for the font weight.
*/
export const fontWeightClassNames = {
400: 'font-normal',
500: 'font-medium',
600: 'font-semibold',
700: 'font-bold',
};

/**
* The class names for the font size.
*/
export const fontSizeClassNames = {
36: 'text-4xl',
30: 'text-3xl',
24: 'text-2xl',
20: 'text-xl',
18: 'text-lg',
16: 'text-base',
14: 'text-sm',
12: 'text-xs',
};

/**
* Line height class names.
*/
export const lineHeightClassNames = {
44: 'leading-11',
38: 'leading-9.5',
32: 'leading-8',
30: 'leading-7.5',
28: 'leading-7',
24: 'leading-6',
20: 'leading-5',
16: 'leading-4',
};

/**
* Letter spacing class names.
*/
export const letterSpacingClassNames = {
2: 'tracking-2', // 2px
};

/**
* Font color class names.
*/
export const fontColorClassNames = {
brand600: 'text-brand-primary-600',
link: 'text-link-primary',
primary: 'text-text-primary',
secondary: 'text-text-secondary',
tertiary: 'text-text-tertiary',
disabled: 'text-text-disabled',
help: 'text-field-helper',
label: 'text-field-label',
info: 'text-support-info',
success: 'text-support-success',
warning: 'text-support-warning',
error: 'text-support-error',
inverse: 'text-text-on-color',
};
102 changes: 102 additions & 0 deletions src/components/text/text.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Meta, StoryFn } from '@storybook/react';
import Text from './text';
import {
fontWeightClassNames,
fontSizeClassNames,
lineHeightClassNames,
letterSpacingClassNames,
fontColorClassNames,
} from './styles';

export default {
title: 'Atoms/Text',
component: Text,
tags: [ 'autodocs' ],
argTypes: {
weight: {
control: 'select',
options: Object.keys( fontWeightClassNames ).map( Number ),
description: 'The font weight of the text',
},
size: {
control: 'select',
options: Object.keys( fontSizeClassNames ).map( Number ),
description: 'The font size of the text',
},
lineHeight: {
control: 'select',
options: Object.keys( lineHeightClassNames ).map( Number ),
description: 'The line height of the text',
},
letterSpacing: {
control: 'select',
options: Object.keys( letterSpacingClassNames ).map( Number ),
description: 'The letter spacing of the text',
},
color: {
control: 'select',
options: Object.keys( fontColorClassNames ),
description: 'The font color of the text',
},
as: {
control: 'select',
options: [
'p',
'label',
'span',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'div',
],
description: 'The element to render the text as. Any HTML element or React component can be added here.',
},
children: {
control: 'text',
description: 'The content of the text',
},
className: {
control: 'text',
description: 'Additional class names to apply',
},
},
} satisfies Meta<typeof Text>;

// Create our story type
type Story = StoryFn<typeof Text>;

// Main template for all stories
const Template: Story = ( args ) => <Text { ...args } />;

// Default story
export const Default: Story = Template.bind( {} );
Default.args = {
children: 'The quick brown fox jumps over the lazy dog',
as: 'p',
color: 'primary',
};

// Heading example
export const Heading: Story = Template.bind( {} );
Heading.args = {
children: 'This is a heading',
as: 'h1',
size: 36,
weight: 600,
color: 'primary',
};

// Paragraph example
export const Paragraph: Story = Template.bind( {} );
Paragraph.args = {
children:
'This is a paragraph with some longer text to demonstrate how the component handles multi-line content. The Text component is designed to be flexible and work with various HTML elements.',
as: 'p',
size: 16,
weight: 400,
lineHeight: 24,
color: 'help',
};
132 changes: 132 additions & 0 deletions src/components/text/text.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { cn } from '@/utilities/functions';
import {
fontColorClassNames,
fontSizeClassNames,
fontWeightClassNames,
letterSpacingClassNames,
lineHeightClassNames,
} from './styles';
import {
forwardRef,
type ElementType,
type ReactNode,
type ComponentType,
type PropsWithoutRef,
} from 'react';

// Polymorphic component type utilities
export type PropsOf<
C extends keyof JSX.IntrinsicElements | ComponentType<unknown>,
> = JSX.LibraryManagedAttributes<C, React.ComponentPropsWithoutRef<C>>;

type AsProp<C extends ElementType> = {
/**
* The element to render the text as.
*
* @default 'p'
*/
as?: C;
};

export type ExtendableProps<
ExtendedProps = object,
OverrideProps = object,
> = OverrideProps & Omit<ExtendedProps, keyof OverrideProps>;

export type InheritableElementProps<
C extends ElementType,
Props = object,
> = ExtendableProps<PropsOf<C>, Props>;

export type PolymorphicComponentProps<
C extends ElementType,
Props = object,
> = InheritableElementProps<C, Props & AsProp<C>>;

export type PolymorphicRef<C extends ElementType> =
React.ComponentPropsWithRef<C>['ref'];

export type PolymorphicComponentPropsWithRef<
C extends ElementType,
Props = object,
> = PolymorphicComponentProps<C, Props> & { ref?: PolymorphicRef<C> };

// Base props for the Text component
export interface TextBaseProps {
/**
* The content of the text.
*/
children: ReactNode;
/**
* The font weight of the text.
*/
weight?: keyof typeof fontWeightClassNames;
/**
* The font size of the text.
*/
size?: keyof typeof fontSizeClassNames;
/**
* The line height of the text.
*/
lineHeight?: keyof typeof lineHeightClassNames;
/**
* The letter spacing of the text.
*/
letterSpacing?: keyof typeof letterSpacingClassNames;
/**
* The font color of the text.
*/
color?: keyof typeof fontColorClassNames;
/**
* Additional class names to apply
*/
className?: string;
}

export type TextProps<C extends ElementType = 'p'> =
PolymorphicComponentPropsWithRef<C, TextBaseProps>;

// Type definition for Text component with proper forwarded ref
export type TextComponent = <C extends ElementType = 'p'>(
props: TextProps<C>
) => JSX.Element;

// Create component with properly typed forwardRef
const Text = forwardRef( function Text<C extends ElementType = 'p'>(
{
as,
children,
weight,
size,
lineHeight,
letterSpacing,
color = 'primary',
className,
...rest
}: PropsWithoutRef<TextProps<C>>,
ref: PolymorphicRef<C>
) {
const Component = as || 'p';

return (
<Component
ref={ ref }
className={ cn(
'm-0 p-0',
weight ? fontWeightClassNames[ weight ] : '',
size ? fontSizeClassNames[ size ] : '',
lineHeight ? lineHeightClassNames[ lineHeight ] : '',
letterSpacing ? letterSpacingClassNames[ letterSpacing ] : '',
color ? fontColorClassNames[ color ] : '',
className
) }
{ ...rest }
>
{ children }
</Component>
);
} ) as TextComponent;

export { Text };

export default Text;
8 changes: 8 additions & 0 deletions src/theme/default-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,14 @@ const defaultTheme = {
fontSize: {
tiny: '0.625rem',
},
lineHeight: {
11: '2.75rem', // 44px
9.5: '2.375rem', // 38px
7.5: '1.875rem', // 30px
},
letterSpacing: {
2: '0.125em', // 2px
},
size: {
3.25: '0.8125rem', // 13px
},
Expand Down
Loading