feat(components): text compoenent added, structure improved

This commit is contained in:
Junior Garcia 2022-10-02 15:22:33 -03:00
parent b0c3d8dcc1
commit 3a9f2be6b9
21 changed files with 675 additions and 117 deletions

View File

@ -18,6 +18,9 @@ export default function App() {
<Text color="error">
Almost before we knew it, we had left the ground.
</Text>
<Text color="$cyan600">
Almost before we knew it, we had left the ground.
</Text>
<Text color="#ff4ecd">
Almost before we knew it, we had left the ground.
</Text>

View File

@ -9,4 +9,11 @@ describe("Grid", () => {
expect(() => wrapper.unmount()).not.toThrow();
});
it("ref should be forwarded", () => {
const ref = React.createRef<HTMLDivElement>();
render(<Grid ref={ref} />);
expect(ref.current).not.toBeNull();
});
});

View File

@ -0,0 +1,24 @@
# @nextui-org/text
A Quick description of the component
> This is an internal utility, not intended for public usage.
## Installation
```sh
yarn add @nextui-org/text
# or
npm i @nextui-org/text
```
## Contribution
Yes please! See the
[contributing guidelines](https://github.com/nextui-org/nextui/blob/master/CONTRIBUTING.md)
for details.
## Licence
This project is licensed under the terms of the
[MIT license](https://github.com/nextui-org/nextui/blob/master/LICENSE).

View File

@ -0,0 +1,49 @@
import * as React from "react";
import {render} from "@testing-library/react";
import {Text} from "../src";
describe("Text", () => {
it("should render correctly", () => {
const wrapper = render(<Text />);
expect(() => wrapper.unmount()).not.toThrow();
});
it("ref should be forwarded", () => {
const ref = React.createRef<HTMLParagraphElement>();
render(<Text ref={ref} />);
expect(ref.current).not.toBeNull();
});
it("should render P element in the default", () => {
const {container} = render(<Text />);
expect(container.querySelector("p")).not.toBeNull();
});
it("should work with different colors", () => {
const {container} = render(<Text color="secondary" />);
expect(container.querySelector("p")).toHaveStyle("color: var(--nextui-colors-secondary)");
});
it("the specified element should be rendered", () => {
const {container} = render(<Text h1 />);
expect(container.querySelector("h1")).not.toBeNull();
});
it("should work with different sizes", () => {
const {container} = render(<Text size="$sm" />);
expect(container.querySelector("p")).toHaveStyle("font-size: var(--nextui-fontSizes-sm)");
});
it("should work with combined styles", () => {
const {container} = render(<Text b del />);
expect(container.querySelector("b")).toContainHTML("del");
});
});

View File

@ -0,0 +1,3 @@
{ "replace": { "main": "dist/index.cjs.js", "module": "dist/index.esm.js",
"types": "dist/index.d.ts", "exports": { ".": { "import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js" }, "./package.json": "./package.json" } } }

View File

@ -0,0 +1,49 @@
{
"name": "@nextui-org/text",
"version": "1.0.0-beta.11",
"description": "Text component is the used to render text and paragraphs within an interface using well-defined typographic styles. It renders a &lt;p&gt; tag by default.",
"keywords": [
"text"
],
"author": "Junior Garcia <jrgarciadev@gmail.com>",
"homepage": "https://nextui.org",
"license": "MIT",
"main": "src/index.ts",
"sideEffects": false,
"files": [
"dist"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/nextui-org/nextui.git",
"directory": "packages/components/text"
},
"bugs": {
"url": "https://github.com/nextui-org/nextui/issues"
},
"scripts": {
"build": "tsup src/index.ts --format=esm,cjs --dts",
"dev": "yarn build:fast -- --watch",
"clean": "rimraf dist .turbo",
"typecheck": "tsc --noEmit",
"build:fast": "tsup src/index.ts --format=esm,cjs",
"prepack": "clean-package",
"postpack": "clean-package restore"
},
"peerDependencies": {
"react": ">=16.8.0"
},
"dependencies": {
"@nextui-org/system": "workspace:*",
"@nextui-org/shared-utils": "workspace:*",
"@nextui-org/shared-css": "workspace:*",
"@nextui-org/dom-utils": "workspace:*"
},
"devDependencies": {
"clean-package": "2.1.1",
"react": "^17.0.2"
}
}

View File

@ -0,0 +1,5 @@
// export types
export type {TextProps} from "./text";
// export component
export {default as Text} from "./text";

View File

@ -0,0 +1,92 @@
import {useMemo} from "react";
import {HTMLNextUIProps, forwardRef, CSS} from "@nextui-org/system";
import {useDOMRef} from "@nextui-org/dom-utils";
import {clsx, __DEV__, isNormalColor} from "@nextui-org/shared-utils";
import {StyledText} from "./text.styles";
export interface TextChildProps extends Omit<HTMLNextUIProps<"p">, "color"> {
tag: keyof JSX.IntrinsicElements;
/**
* Text color.
* @default "default"
*/
color?: CSS["color"];
/**
* The **`font-size`** CSS property sets the size of the font. Changing the font size also updates the sizes of the font size-relative `<length>` units, such as `em`, `ex`, and so forth.
*
* **Syntax**: `<absolute-size> | <relative-size> | <length-percentage>`
*
* **Initial value**: `medium`
*
*/
size?: CSS["fontSize"];
/**
* The **`text-transform`** CSS property specifies how to capitalize an element's text. It can be used to make text appear in all-uppercase or all-lowercase, or with each word capitalized. It also can help improve legibility for ruby.
*
* **Syntax**: `none | capitalize | uppercase | lowercase | full-width | full-size-kana`
*
* **Initial value**: `none`
*
*/
transform?: CSS["textTransform"];
/**
* The **`font-weight`** CSS property specifies the weight (or boldness) of the font. The font weights available to you will depend on the `font-family` you are using. Some fonts are only available in `normal` and `bold`.
*/
weight?: CSS["fontWeight"];
}
const TextChild = forwardRef<TextChildProps, "p">((props, ref) => {
const {
tag,
css,
children,
color: userColor = "$text",
transform: textTransform,
size: fontSize,
weight: fontWeight,
className,
...otherProps
} = props;
const domRef = useDOMRef(ref);
const color = useMemo(() => {
if (isNormalColor(userColor as string)) {
switch (userColor) {
case "default":
return "$text";
default:
return `$${userColor}`;
}
}
return userColor;
}, [userColor]);
return (
<StyledText
ref={domRef}
as={tag}
className={clsx("nextui-text-child", className)}
css={{
color,
fontSize,
textTransform,
fontWeight,
...css,
}}
{...otherProps}
>
{children}
</StyledText>
);
});
if (__DEV__) {
TextChild.displayName = "NextUI.TextChild";
}
TextChild.toString = () => ".nextui-text-child";
export default TextChild;

View File

@ -0,0 +1,4 @@
import {styled} from "@nextui-org/system";
import {cssHideShowIn} from "@nextui-org/shared-css";
export const StyledText = styled("p", {}, cssHideShowIn);

View File

@ -0,0 +1,59 @@
import {ReactNode, useMemo} from "react";
import {forwardRef} from "@nextui-org/system";
import {useDOMRef} from "@nextui-org/dom-utils";
import {clsx, __DEV__} from "@nextui-org/shared-utils";
import {UseTextProps, TextRenderableElements, useText} from "./use-text";
import TextChild, {TextChildProps} from "./text-child";
export interface TextProps extends UseTextProps {}
const getModifierChild = (
tags: TextRenderableElements,
children: ReactNode,
size?: TextChildProps["size"],
transform?: TextChildProps["transform"],
) => {
if (!tags.length) return children;
const nextTag = tags.slice(1, tags.length);
return (
<TextChild size={size} tag={tags[0]} transform={transform}>
{getModifierChild(nextTag, children, size)}
</TextChild>
);
};
const Text = forwardRef<TextProps, "p">((props, ref) => {
const {children, size, transform, tag, renderableChildElements, className, ...otherProps} =
useText(props);
const domRef = useDOMRef(ref);
const modifers = useMemo(() => {
if (!renderableChildElements.length) return children;
return getModifierChild(renderableChildElements, children, size, transform);
}, [renderableChildElements, children, size, transform]);
return (
<TextChild
ref={domRef}
className={clsx("nextui-text", className)}
size={size}
tag={tag}
transform={transform}
{...otherProps}
>
{modifers}
</TextChild>
);
});
if (__DEV__) {
Text.displayName = "NextUI.Text";
}
Text.toString = () => ".nextui-text";
export default Text;

View File

@ -0,0 +1,84 @@
import {getKeyValue, TextTransforms} from "@nextui-org/shared-utils";
import {useMemo} from "react";
import {HTMLNextUIProps} from "@nextui-org/system";
import {HideShowInProps} from "@nextui-org/shared-css";
import {TextChildProps} from "./text-child";
export interface UseTextProps
extends HTMLNextUIProps<"p", Omit<TextChildProps, "tag">>,
HideShowInProps {
h1?: boolean;
h2?: boolean;
h3?: boolean;
h4?: boolean;
h5?: boolean;
h6?: boolean;
b?: boolean;
small?: boolean;
transform?: TextTransforms;
i?: boolean;
span?: boolean;
del?: boolean;
em?: boolean;
blockquote?: boolean;
}
type ElementMap = {[key in keyof JSX.IntrinsicElements]?: boolean};
export type TextRenderableElements = Array<keyof JSX.IntrinsicElements>;
export function useText(props: UseTextProps) {
const {
h1 = false,
h2 = false,
h3 = false,
h4 = false,
h5 = false,
h6 = false,
b = false,
small = false,
i = false,
span = false,
del = false,
em = false,
blockquote = false,
transform = "none",
size,
...otherProps
} = props;
const elements: ElementMap = {h1, h2, h3, h4, h5, h6, blockquote};
const inlineElements: ElementMap = {span, small, b, em, i, del};
const names = Object.keys(elements).filter((name: string) =>
getKeyValue(elements, name),
) as TextRenderableElements;
const inlineNames = Object.keys(inlineElements).filter((name: string) =>
getKeyValue(inlineElements, name),
) as TextRenderableElements;
/**
* Render element "p" only if no element is found.
* If there is only one modifier, just rendered one modifier element
* e.g.
* <Text /> => <p />
* <Text em /> => <em />
* <Text b em /> => <b><em>children</em></b>
*/
const tag = useMemo(() => {
if (names[0]) return names[0];
if (inlineNames[0]) return inlineNames[0];
return "p" as keyof JSX.IntrinsicElements;
}, [names, inlineNames]);
const renderableChildElements = inlineNames.filter(
(name: keyof JSX.IntrinsicElements) => name !== tag,
) as TextRenderableElements;
return {renderableChildElements, tag, transform, size, ...otherProps};
}
export type UseTextReturn = ReturnType<typeof useText>;

View File

@ -0,0 +1,124 @@
import React from "react";
import {Meta} from "@storybook/react";
import {Text} from "../src";
export default {
title: "General/Text",
component: Text,
decorators: [
(Story) => (
<div style={{maxWidth: "50%"}}>
<Story />
</div>
),
],
} as Meta;
const shortText = "Almost before we knew it, we had left the ground.";
const largeText =
"NextUI gives you the best developer experience with all the features you need for building beautiful and modern websites and applications.";
export const Headings = () => {
return (
<>
<Text h1>{shortText}</Text>
<Text h2>{shortText}</Text>
<Text h3>{shortText}</Text>
<Text h4>{shortText}</Text>
<Text h5>{shortText}</Text>
<Text h6>{shortText}</Text>
</>
);
};
export const Gradient = () => (
<>
<Text
h1
css={{
textGradient: "45deg, $blue600 0%, $cyan600 100%",
}}
weight="$bold"
>
Let&apos;s
</Text>
<Text
h1
css={{
textGradient: "45deg, $pink600 0%, $red600 100%",
}}
weight="$bold"
>
Make the web
</Text>
<Text
h1
css={{
textGradient: "45deg, $yellow600 0%, $red600 100%",
}}
weight="$bold"
>
Prettier
</Text>
</>
);
export const Paragraph = () => (
<>
<Text>{largeText}</Text>
<Text b>{largeText}</Text>
</>
);
export const Colors = () => (
<>
<Text>{shortText}</Text>
<Text color="primary">{shortText}</Text>
<Text color="secondary">{shortText}</Text>
<Text color="success">{shortText}</Text>
<Text color="warning">{shortText}</Text>
<Text color="error">{shortText}</Text>
<Text color="$pink500">{shortText}</Text>
<Text color="#ccc">{shortText}</Text>
</>
);
export const Small = () => (
<Text>
<Text small>{largeText}</Text>
<Text i small>
{largeText}
</Text>
</Text>
);
export const Blockquote = () => (
<>
<Text blockquote>{shortText}</Text>
</>
);
export const Sizes = () => (
<>
<Text size="$xs">Font Size: xs;</Text>
<Text size="$md">Font Size: md (base);</Text>
<Text size="$xl">Font Size: xl;</Text>
<Text size="$2xl">Font Size: 2xl;</Text>
<Text size="$3xl">Font Size: 3xl;</Text>
</>
);
export const Composed = () => (
<>
<Text weight="$hairline">{largeText}</Text>
<Text>
<Text del small>
{shortText}
</Text>
<Text b small>
{shortText}
</Text>
</Text>
</>
);

View File

@ -0,0 +1,9 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"paths": {
"@stitches/react": ["../../../node_modules/@stitches/react"]
}
},
"include": ["src", "index.ts"]
}

View File

@ -0,0 +1,13 @@
import {defineConfig} from "tsup";
import {findUpSync} from "find-up";
export default defineConfig({
clean: true,
minify: false,
treeshake: false,
format: ["cjs", "esm"],
outExtension(ctx) {
return {js: `.${ctx.format}.js`};
},
inject: process.env.JSX ? [findUpSync("react-shim.js")!] : undefined,
});

View File

@ -2,7 +2,7 @@
* Part of this code is taken from @chakra-ui/system
*/
import {forwardRef as baseForwardRef, NamedExoticComponent, ReactNode} from "react";
import {forwardRef as baseForwardRef} from "react";
import {CSS} from "./stitches.config";

View File

@ -0,0 +1,37 @@
import type {CSS} from "@nextui-org/system";
export const sharedFocus: CSS = {
WebkitTapHighlightColor: "transparent",
"&:focus:not(&:focus-visible)": {
boxShadow: "none",
},
"&:focus": {
outline: "none",
boxShadow: "0 0 0 2px $colors$background, 0 0 0 4px $colors$primary",
},
"@safari": {
WebkitTapHighlightColor: "transparent",
outline: "none",
},
};
export const cssFocusVisible: CSS = {
variants: {
isFocusVisible: {
true: {
outline: "transparent solid 2px",
outlineOffset: "2px",
boxShadow: "0 0 0 2px $colors$background, 0 0 0 4px $colors$primary",
},
false: {
outline: "none",
},
},
},
};
export const cssNoBlurriness: CSS = {
/* Avoid blurriness */
transform: "translateZ(0)",
backfaceVisibility: "hidden",
};

View File

@ -1,115 +1,3 @@
import type {CSS} from "@nextui-org/system";
export const sharedFocus: CSS = {
WebkitTapHighlightColor: "transparent",
"&:focus:not(&:focus-visible)": {
boxShadow: "none",
},
"&:focus": {
outline: "none",
boxShadow: "0 0 0 2px $colors$background, 0 0 0 4px $colors$primary",
},
"@safari": {
WebkitTapHighlightColor: "transparent",
outline: "none",
},
};
export const cssFocusVisible: CSS = {
variants: {
isFocusVisible: {
true: {
outline: "transparent solid 2px",
outlineOffset: "2px",
boxShadow: "0 0 0 2px $colors$background, 0 0 0 4px $colors$primary",
},
false: {
outline: "none",
},
},
},
};
export const cssNoBlurriness: CSS = {
/* Avoid blurriness */
transform: "translateZ(0)",
backfaceVisibility: "hidden",
};
export const sharedVisuallyHidden: CSS = {
border: "0px",
clip: "rect(0px, 0px, 0px, 0px)",
height: "1px",
width: "1px",
margin: "-1px",
padding: "0px",
overflow: "hidden",
whiteSpace: "nowrap",
position: "absolute",
};
export const cssHideIn: CSS = {
variants: {
hideIn: {
xs: {
"@xsMax": {
display: "none",
},
},
sm: {
"@smMax": {
display: "none",
},
},
md: {
"@mdMax": {
display: "none",
},
},
lg: {
"@lgMax": {
display: "none",
},
},
xl: {
"@xlMax": {
display: "none",
},
},
},
},
};
export const cssShowIn: CSS = {
variants: {
showIn: {
xs: {
"@xs": {
display: "none",
},
},
sm: {
"@sm": {
display: "none",
},
},
md: {
"@md": {
display: "none",
},
},
lg: {
"@lg": {
display: "none",
},
},
xl: {
"@xl": {
display: "none",
},
},
},
},
};
export const cssHideShowIn: CSS = {...cssHideIn, ...cssShowIn};
export * from "./types";
export * from "./focus";
export * from "./visibility";

View File

@ -0,0 +1,12 @@
export type HideShowInType = "xs" | "sm" | "md" | "lg" | "xl";
export interface HideShowInProps {
/**
* Whether the Text should be visible only from selected breakpoint.
*/
showIn?: HideShowInType;
/**
* Whether the Text should be hidden from selected breakpoint.
*/
hideIn?: HideShowInType;
}

View File

@ -0,0 +1,79 @@
import type {CSS} from "@nextui-org/system";
export const sharedVisuallyHidden: CSS = {
border: "0px",
clip: "rect(0px, 0px, 0px, 0px)",
height: "1px",
width: "1px",
margin: "-1px",
padding: "0px",
overflow: "hidden",
whiteSpace: "nowrap",
position: "absolute",
};
export const cssHideIn: CSS = {
variants: {
hideIn: {
xs: {
"@xsMax": {
display: "none",
},
},
sm: {
"@smMax": {
display: "none",
},
},
md: {
"@mdMax": {
display: "none",
},
},
lg: {
"@lgMax": {
display: "none",
},
},
xl: {
"@xlMax": {
display: "none",
},
},
},
},
};
export const cssShowIn: CSS = {
variants: {
showIn: {
xs: {
"@xs": {
display: "none",
},
},
sm: {
"@sm": {
display: "none",
},
},
md: {
"@md": {
display: "none",
},
},
lg: {
"@lg": {
display: "none",
},
},
xl: {
"@xl": {
display: "none",
},
},
},
},
};
export const cssHideShowIn: CSS = {...cssHideIn, ...cssShowIn};

View File

@ -1,6 +1,6 @@
import * as React from "react";
import {render} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { {{capitalize componentName}} } from "../src";
describe("{{capitalize componentName}}", () => {

17
pnpm-lock.yaml generated
View File

@ -410,6 +410,23 @@ importers:
clean-package: 2.1.1
react: 17.0.2
packages/components/text:
specifiers:
'@nextui-org/dom-utils': workspace:*
'@nextui-org/shared-css': workspace:*
'@nextui-org/shared-utils': workspace:*
'@nextui-org/system': workspace:*
clean-package: 2.1.1
react: ^17.0.2
dependencies:
'@nextui-org/dom-utils': link:../../utilities/dom-utils
'@nextui-org/shared-css': link:../../utilities/shared-css
'@nextui-org/shared-utils': link:../../utilities/shared-utils
'@nextui-org/system': link:../../core/system
devDependencies:
clean-package: 2.1.1
react: 17.0.2
packages/core/react:
specifiers:
'@babel/runtime': ^7.6.2