Introduction
Let us continue building our chakra components using styled-components & styled-system. In this tutorial we will be cloning the Chakra UI Text component.
- I would like you to first check the chakra docs for text.
- We will create the
Textcomponent from scratch. - All the code for this tutorial can be found here under the atom-typography-text branch.
Prerequisite
Please check the previous post where we have completed the Grid Components. Also please check the Chakra Text Component code here.
In this tutorial we will -
- Create a Text component.
- Create stories for the Text component.
Setup
- First let us create a branch, from the main branch run -
git checkout -b atom-typography-text
Create a new folder under the
atomsfolder calledtypography. Under thetypographyfolder create a new folder calledtext.Under the
atoms/typography/textfolder we will create 2 filesindex.tsx&text.stories.tsx.So our folder structure stands like - src/components/atoms/typography/text.
Text Component
For our Text component we have 2 additional props,
noOfLinesandisTruncated. Given noOfLines = 2, it will truncate the text and add an ellipses (...) on the second line. Similarly if we are to pass isTruncated it will limit the text only to one line and add an ellipses (...).Let me paste the following code -
import * as React from "react";
import styled, { CSSProperties } from "styled-components";
import shouldForwardProp from "@styled-system/should-forward-prop";
import {
compose,
display,
space,
typography,
color,
borderRadius,
layout,
system,
DisplayProps,
SpaceProps,
TypographyProps,
ColorProps,
BorderRadiusProps,
LayoutProps,
ResponsiveValue,
} from "styled-system";
interface TextOptions {
isTruncated?: boolean;
noOfLines?: number;
whiteSpace?: ResponsiveValue<CSSProperties["whiteSpace"]>;
textOverflow?: ResponsiveValue<CSSProperties["textOverflow"]>;
decoration?: ResponsiveValue<CSSProperties["textDecoration"]>;
transform?: ResponsiveValue<CSSProperties["textTransform"]>;
}
export type TextProps = DisplayProps &
SpaceProps &
TypographyProps &
ColorProps &
BorderRadiusProps &
LayoutProps &
React.ComponentPropsWithoutRef<"p"> &
TextOptions & {
as?: React.ElementType;
children?: React.ReactNode;
};
const BaseText = styled.p.withConfig({
shouldForwardProp,
})<TextProps>`
${compose(space, display, typography, color, borderRadius, layout)}
${system({
whiteSpace: true,
textOverflow: true,
decoration: {
property: "textDecoration",
},
transform: {
property: "textTransform",
},
})}
${({ noOfLines }) =>
noOfLines &&
`
display: -webkit-box;
line-clamp: ${noOfLines};
overflow: hidden;
-webkit-line-clamp: ${noOfLines};
-webkit-box-orient: vertical;
`};
`;
export const Text = React.forwardRef<HTMLParagraphElement, TextProps>(
(props, ref) => {
const { children, isTruncated, ...delegated } = props;
const truncatedProps = isTruncated
? {
textOverflow: "ellipsis",
overflow: "hidden",
whiteSpace: "nowrap",
}
: {};
return (
<BaseText ref={ref} {...truncatedProps} {...delegated}>
{children}
</BaseText>
);
}
);
export const VisuallyHiddenText = styled.span`
position: absolute;
overflow: hidden;
clip: rect(0 0 0 0);
height: 1px;
width: 1px;
margin: -1px;
padding: 0;
border: 0;
`;
Notice we have added / extended our system with the following props, whiteSpace, textOverflow, decoration (text-decoration), transform (text-transform).
Also we have regular style utility functions from styled-system like space, display, typography, layout, etc.
Also check that we have intercepted the isTruncated props and we conditionally add necessary props.
We also have an additional
VisuallyHiddenTextwhich we will use for accessibility purposes, example say we have a checkbox with a label and we want the screen reader to read some different text.Under
src/theme/typography.tspaste the following -
export function fontSizesOptions() {
const options = Object.keys(typography.fontSizes);
const labels = Object.entries(typography.fontSizes).reduce(
(acc, [key, value]) => {
acc[key] = `${key} (${value})`;
return acc;
},
{}
);
return { options, labels };
}
Story
- With the above our
Textcomponent is completed, let us create a story. - Under the
src/components/atoms/typography/text/text.stories.tsxfile we add the below story code. - We will create 3 stories - Playground, Default, TextTypes.
import * as React from "react";
import { fontSizesOptions } from "../../../../theme/typography";
import { Text, TextProps } from ".";
export default {
title: "Atoms/Typography/Text",
};
export const Playground = {
argTypes: {
fontSize: {
name: "fontSize",
type: { name: "string", required: false },
defaultValue: "md",
description: "Font Size from theme.fontSizes",
table: {
type: { summary: "string" },
defaultValue: { summary: "-" },
},
control: {
type: "select",
...fontSizesOptions(),
},
},
isTruncated: {
name: "isTruncated",
type: { name: "boolean", required: false },
defaultValue: false,
description: "Truncate Text.",
table: {
type: { summary: "boolean" },
defaultValue: { summary: "false" },
},
},
noOfLines: {
name: "noOfLines",
type: { name: "number", required: false },
defaultValue: 0,
description: "Number of Lines to show",
table: {
type: { summary: "number" },
defaultValue: { summary: "-" },
},
},
},
render: (args: TextProps) => (
<Text {...args}>
Lorem ipsum is placeholder text commonly used in the graphic, print, and
publishing industries for previewing layouts and visual mockups.
</Text>
),
};
export const Default = {
render: () => (
<Text>
Text component is the used to render text and paragraphs within an
interface. It renders a p tag by default.
</Text>
),
};
export const TextTypes = {
argTypes: {
as: {
name: "as",
type: { name: "string", required: false },
defaultValue: "p",
description: "Element type to render.",
table: {
type: { summary: "string" },
defaultValue: { summary: "p" },
},
control: {
type: "select",
options: [
"i",
"u",
"abbr",
"cite",
"del",
"em",
"ins",
"kbd",
"mark",
"s",
"samp",
"sub",
"sup",
],
},
},
},
render: (args: TextProps) => <Text {...args}>Sample Text</Text>,
};
Now run
npm run storybookcheck the stories. Under the Playground stories check the controls section play with the props, add more controls if you like.Also check the TextTypes story we are using the styled-component polymorphic
as prop.
Build the Library
Under
atom/typographyfolder create anindex.tsfile.Under the
/typography/index.tsfile and paste the following -
export * from "./text";
- Under the
atom/index.tsfile paste the following -
export * from "./layout";
export * from "./typography";
Now
npm run build.Under the folder
example/src/App.tsxwe can test ourTextcomponent. Copy paste the following code and runnpm run startfrom theexampledirectory.
import * as React from "react";
import { Text } from "chakra-ui-clone";
export function App() {
return (
<>
<Text fontSize="50px" color="tomato">
I'm using a custom font-size value for this text
</Text>
<Text as="i">Italic</Text>
<br />
<Text as="u">Underline</Text>
<br />
<Text as="s">Strike Through</Text>
</>
);
}
Summary
There you go guys in this tutorial we created Text components just like chakra ui and stories for them. You can find the code for this tutorial under the atom-typography-text branch here. In the next tutorial we will create a Heading component. Until next time PEACE.
Top comments (0)