-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(textarea): first implementation the textarea component (#92)
- Loading branch information
1 parent
ea15b04
commit 9ad5d31
Showing
30 changed files
with
2,236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Change Log | ||
|
||
All notable changes to this project will be documented in this file. | ||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# HeyCar-UIKit - Textarea | ||
|
||
[![Tests](https://github.com/hey-car/heycar-uikit/actions/workflows/build.yml/badge.svg)](https://github.com/hey-car/heycar-uikit/actions/workflows/build.yml) | ||
[![Coverage Status](https://coveralls.io/repos/github/hey-car/heycar-uikit/badge.svg)](https://coveralls.io/github/hey-car/heycar-uikit) | ||
[![Demo build](https://github.com/hey-car/heycar-uikit/actions/workflows/main.yml/badge.svg)](https://github.com/hey-car/heycar-uikit/actions/workflows/main.yml) | ||
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) | ||
|
||
The Button component is used to trigger an action or event, such as submitting a form, opening a dialog, canceling an action, or performing a delete operation. | ||
|
||
## Installation | ||
|
||
To install and save in your package.json dependencies, run the command below using npm: | ||
|
||
```bash | ||
npm install @heycar-uikit/textarea | ||
``` | ||
|
||
## Usage | ||
|
||
```tsx | ||
import Textarea from '@heycar-uikit/textarea'; | ||
|
||
function App() { | ||
return <Textarea value="text" />; | ||
} | ||
``` | ||
|
||
## Documentation and sandbox | ||
|
||
[Storybook](https://hey-car.github.io/heycar-uikit/main/?path=/docs/components-textarea--textarea) documentation and sandbox |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ | ||
"name": "@heycar-uikit/textarea", | ||
"version": "0.0.1", | ||
"description": "HeyCar-UIKit Textarea", | ||
"main": "dist/index.js", | ||
"module": "./dist/esm/index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"keywords": [ | ||
"ui", | ||
"uikit", | ||
"react", | ||
"components", | ||
"heycar", | ||
"typescript", | ||
"ui-components", | ||
"design-systems", | ||
"textarea" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/hey-car/heycar-uikit.git" | ||
}, | ||
"homepage": "https://github.com/hey-car/heycar-uikit/tree/main/packages/textarea#readme", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"peerDependencies": { | ||
"react": "^17.0.1" | ||
}, | ||
"dependencies": { | ||
"@heycar-uikit/form-control": "^1.0.0", | ||
"classnames": "^2.3.1" | ||
}, | ||
"author": "HeyCar Team", | ||
"license": "UNLICENSED" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import React, { ChangeEvent, FocusEvent, useCallback, useState } from 'react'; | ||
import cn from 'classnames'; | ||
|
||
import FormControl from '@heycar-uikit/form-control'; | ||
|
||
import TextareaCounter from './components/TextareaCounter'; | ||
import { TextareaProps } from './Textarea.types'; | ||
|
||
import styles from './styles/default.module.css'; | ||
|
||
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>( | ||
( | ||
{ | ||
value, | ||
defaultValue, | ||
hint, | ||
error, | ||
label, | ||
maxLength, | ||
fullWidth = false, | ||
resize = 'none', | ||
disabled, | ||
readOnly, | ||
onFocus, | ||
onBlur, | ||
onChange, | ||
className, | ||
dataTestId, | ||
...restProps | ||
}, | ||
ref, | ||
) => { | ||
const textareaClassNames = cn(styles.textarea, className, { | ||
[styles.resizeVertical]: resize === 'vertical', | ||
}); | ||
const isUncontrolled = value === undefined; | ||
const ariaLabel = typeof label === 'string' ? label : undefined; | ||
const [isFocused, setFocused] = useState(restProps.autoFocus); | ||
const [stateValue, setStateValue] = useState(defaultValue || ''); | ||
const isFilled = Boolean(isUncontrolled ? stateValue : value); | ||
const handleTextareaFocus = useCallback( | ||
(event: FocusEvent<HTMLTextAreaElement>) => { | ||
if (!readOnly) setFocused(true); | ||
if (onFocus) onFocus(event); | ||
}, | ||
[onFocus, readOnly], | ||
); | ||
const handleTextareaBlur = useCallback( | ||
(event: FocusEvent<HTMLTextAreaElement>) => { | ||
setFocused(false); | ||
|
||
if (onBlur) onBlur(event); | ||
}, | ||
[onBlur], | ||
); | ||
const handleTextareaChange = useCallback( | ||
(event: ChangeEvent<HTMLTextAreaElement>) => { | ||
if (onChange) onChange(event, { value: event.target.value }); | ||
if (isUncontrolled) setStateValue(event.target.value); | ||
}, | ||
[onChange, isUncontrolled], | ||
); | ||
const getValueLength = (): number => { | ||
if (isUncontrolled) return stateValue.length; | ||
|
||
return value.length; | ||
}; | ||
|
||
return ( | ||
<FormControl | ||
bottomAddons={ | ||
!!maxLength && ( | ||
<TextareaCounter length={getValueLength()} maxLength={maxLength} /> | ||
) | ||
} | ||
className={styles.formControl} | ||
disabled={disabled} | ||
error={error} | ||
filled={isFilled || isFocused} | ||
focused={isFocused} | ||
fullWidth={fullWidth} | ||
hint={hint} | ||
label={label} | ||
> | ||
<textarea | ||
{...restProps} | ||
aria-label={ariaLabel} | ||
className={textareaClassNames} | ||
data-test-id={dataTestId} | ||
disabled={disabled} | ||
maxLength={maxLength} | ||
onBlur={handleTextareaBlur} | ||
onChange={handleTextareaChange} | ||
onFocus={handleTextareaFocus} | ||
readOnly={readOnly} | ||
ref={ref} | ||
value={value} | ||
/> | ||
</FormControl> | ||
); | ||
}, | ||
); | ||
|
||
Textarea.displayName = 'Textarea'; | ||
|
||
export default Textarea; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import React, { ReactNode, TextareaHTMLAttributes } from 'react'; | ||
|
||
export type TextareaProps = Omit< | ||
TextareaHTMLAttributes<HTMLTextAreaElement>, | ||
'style' | 'value' | 'defaultValue' | 'onChange' | ||
> & { | ||
/** | ||
* The value of the `input` element, required for a controlled component | ||
*/ | ||
value?: string; | ||
/** | ||
* The default value. Use when the component is not controlled | ||
*/ | ||
defaultValue?: string; | ||
/** | ||
* If `true`, the `input` will take up the full width of its container | ||
*/ | ||
fullWidth?: boolean; | ||
/** | ||
* The hint for the error state | ||
*/ | ||
error?: ReactNode | boolean; | ||
/** | ||
* The short `hint` displayed under the input | ||
*/ | ||
hint?: ReactNode; | ||
/** | ||
* The label of the input component | ||
*/ | ||
label?: React.ReactNode; | ||
/** | ||
* Controlling the ability to resize a component | ||
*/ | ||
resize?: 'vertical' | 'none'; | ||
/** | ||
* Additional `class` names to be added | ||
*/ | ||
className?: string; | ||
/** | ||
* Callback fired when the value is changed | ||
*/ | ||
onChange?: ( | ||
event: React.ChangeEvent<HTMLTextAreaElement>, | ||
payload: { value: string }, | ||
) => void; | ||
/** | ||
* The id for testing | ||
*/ | ||
dataTestId?: string; | ||
}; |
Oops, something went wrong.
9ad5d31
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coverage report
Test suite run success
228 tests passing in 32 suites.
Report generated by 🧪jest coverage report action from 9ad5d31