Skip to content

Commit

Permalink
Merge pull request #136 from AppQuality/counter-textarea
Browse files Browse the repository at this point in the history
feat(textarea): add counter
  • Loading branch information
iacopolea authored Sep 7, 2023
2 parents 564f980 + ff2d2e6 commit ec3aacb
Show file tree
Hide file tree
Showing 7 changed files with 17,727 additions and 48 deletions.
5 changes: 4 additions & 1 deletion src/stories/form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const BasicErrorMessage = ({
className?: string;
}) => (
<div className={className}>
<FormikErrorMessage name={name} />{" "}
<FormikErrorMessage name={name} />
</div>
);

Expand All @@ -28,6 +28,9 @@ export const ErrorMessage = styled(BasicErrorMessage)`
width: 100%;
margin-top: 0.25rem;
font-size: 0.875em;
&:empty {
display: none;
}
`;

export const Field = ({
Expand Down
73 changes: 49 additions & 24 deletions src/stories/form/textareaField/TextareaField.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Story, Meta } from "@storybook/react";
import { Form, Formik, FormikProps } from "formik";
import { Formik, FormikProps } from "formik";
import { TextareaField } from "./TextareaField";
import {
initialTextareaFormValues,
Expand All @@ -21,14 +21,12 @@ export const Simple: Story = () => (
}}
>
{(props: FormikProps<any>) => (
<Form id="textareaForm">
<TextareaField
className="aq-mb-3"
name="description"
label="Description"
placeholder="Enter description"
/>
</Form>
<TextareaField
className="aq-mb-3"
name="description"
label="Description"
placeholder="Enter description"
/>
)}
</Formik>
);
Expand All @@ -42,16 +40,14 @@ export const Autoresize: Story = () => (
}}
>
{(props: FormikProps<any>) => (
<Form id="textareaForm">
<TextareaField
className="aq-mb-3"
name="description"
label="Description"
placeholder="Enter description"
height="2.55rem"
autoResize={true}
/>
</Form>
<TextareaField
className="aq-mb-3"
name="description"
label="Description"
placeholder="Enter description"
height="2.55rem"
autoResize={true}
/>
)}
</Formik>
);
Expand All @@ -65,16 +61,45 @@ export const Disabled: Story = () => (
}}
>
{(props: FormikProps<any>) => (
<Form id="textareaForm">
<TextareaField
className="aq-mb-3"
name="description"
label="Description"
placeholder="Enter description"
height="2.55rem"
disabled
/>
)}
</Formik>
);

export const CounterMax: Story = () => (
<Formik
initialValues={initialTextareaFormValues}
validationSchema={yup.object({
description: yup
.string()
.max(100, "Max 100 characters")
.required("mandatory field"),
})}
onSubmit={(data) => {
console.log(data);
}}
>
{(props: FormikProps<any>) => (
<div style={{ padding: "20px" }}>
<TextareaField
className="aq-mb-3"
name="description"
label="Description"
label={
<div>
<span style={{ fontWeight: "600" }}>this is</span> a Description
</div>
}
placeholder="Enter description"
height="2.55rem"
disabled
counterMax={100}
/>
</Form>
</div>
)}
</Formik>
);
57 changes: 38 additions & 19 deletions src/stories/form/textareaField/TextareaField.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Field as FormikField, FieldProps } from "formik";
import { ErrorMessage } from "../Form";
import FormLabel from "../formlabel/FormLabel";
import { StyledTextareaField } from "./_styles";
import { Counter, StyledTextareaField } from "./_styles";
import { TextareaFieldProps } from "./_types";
import { useState } from "react";

export const TextareaField = ({
name,
Expand All @@ -13,7 +14,10 @@ export const TextareaField = ({
resize,
height,
autoResize,
counterMax,
...props
}: TextareaFieldProps) => {
const [isFocus, setIsFocus] = useState(false);
return (
<FormikField name={name}>
{({ field, meta }: FieldProps) => {
Expand All @@ -24,26 +28,41 @@ export const TextareaField = ({
isInvalid={meta.touched && typeof meta.error == "string"}
height={height}
autoResize={autoResize}
{...props}
>
{label && <FormLabel htmlFor={field.name} label={label} />}
<textarea
id={field.name}
name={field.name}
value={field.value}
placeholder={placeholder}
disabled={disabled}
onChange={(e) => field.onChange(e)}
onBlur={(e) => field.onBlur(e)}
onInput={
autoResize
? (e) => {
e.currentTarget.style.height = "1px";
e.currentTarget.style.height =
e.currentTarget.scrollHeight + "px";
}
: undefined
}
/>
<div style={{ position: "relative" }}>
<textarea
id={field.name}
name={field.name}
value={field.value}
placeholder={placeholder}
disabled={disabled}
onChange={(e) => field.onChange(e)}
onBlur={(e) => {
field.onBlur(e);
setIsFocus(false);
}}
onFocus={() => setIsFocus(true)}
onInput={
autoResize
? (e) => {
e.currentTarget.style.height = "1px";
e.currentTarget.style.height =
e.currentTarget.scrollHeight + "px";
}
: undefined
}
/>
{counterMax && (
<Counter
isFocus={isFocus}
isInvalid={meta.touched && typeof meta.error == "string"}
>
<span>{field.value.length}</span>/{counterMax}
</Counter>
)}
</div>
<ErrorMessage name={field.name} />
</StyledTextareaField>
);
Expand Down
25 changes: 23 additions & 2 deletions src/stories/form/textareaField/_styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,39 @@ const InvalidTextareaFocusStyle = css`
box-shadow: 0 0 0 0.25rem ${(p) => p.theme.colors.red100};
`;

export const Counter = styled.div<{ isInvalid?: boolean; isFocus?: boolean }>`
position: absolute;
text-align: right;
bottom: 3px;
right: 4px;
pointer-events: none;
font-size: 12px;
line-height: 1;
margin: 0;
background-color: rgba(255, 255, 255, 0.8);
span {
color: ${(p) =>
p.isInvalid ? p.theme.palette.danger : p.theme.palette.secondary};
}
transition: opacity 0.2s ease-in-out;
${(p) => (p.isFocus ? "opacity: 1;" : "opacity: 0;")}
`;

export const StyledTextareaField = styled.div<{
resize?: string;
isInvalid?: boolean;
height?: string;
autoResize?: boolean;
}>`
position: relative;
textarea {
display: block;
width: 100%;
min-height: ${(p) => (p.height ? p.height : "5.715rem")};
height: ${(p) => (p.height ? p.height : "5.715rem")};
resize: ${(p) => (p.resize ? p.resize : "none")};
padding: 0.5rem 0.75rem;
padding: 0.5rem 0.75rem 14px;
color: ${(p) => p.theme.palette.primary};
font-family: inherit;
font-size: ${(p) => p.theme.typography.fontSize.base};
Expand All @@ -32,7 +53,7 @@ export const StyledTextareaField = styled.div<{
box-shadow: none;
${(props) => (props.isInvalid ? InvalidTextareaStyle : "")}
${(p) => (p.autoResize ? "overflow: hidden;" : "")}
&:focus,
&:focus-visible {
color: ${(p) => p.theme.palette.primary};
Expand Down
7 changes: 5 additions & 2 deletions src/stories/form/textareaField/_types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
export interface TextareaFieldProps {
import { ReactNode } from "react";

export interface TextareaFieldProps extends GeneralComponentProps {
name: string;
label?: string;
label?: ReactNode;
className?: string;
placeholder?: string;
disabled?: boolean;
resize?: "none" | "both" | "horizontal" | "vertical";
height?: string;
autoResize?: boolean;
counterMax?: number;
}
9 changes: 9 additions & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
interface GeneralComponentProps {
id?: string;
className?: string;
key?: string;
"data-testid"?: string;
"data-qa"?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
}
Loading

0 comments on commit ec3aacb

Please sign in to comment.