Skip to content

Commit

Permalink
Merge pull request #90 from web3ui/feat-form-input-textarea-custom-width
Browse files Browse the repository at this point in the history
  • Loading branch information
rayyan224 authored Jan 24, 2022
2 parents 81c8c4d + 9e8022c commit 6663b34
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 9 deletions.
3 changes: 3 additions & 0 deletions src/components/Form/Form.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ DemoForm.args = {
name: 'first name',
type: 'text',
value: '',
inputWidth: '100%',
},
{
name: 'your email',
type: 'email',
value: '',
inputWidth: '100%',
validation: {
required: true,
regExp: validateRegExp.email,
Expand Down Expand Up @@ -85,6 +87,7 @@ DemoForm.args = {
name: 'Any more comments?',
type: 'textarea',
value: '',
inputWidth: '100%',
validation: { required: true },
},
],
Expand Down
4 changes: 3 additions & 1 deletion src/components/Form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const Form: React.FC<FormProps> = ({
name={input.name}
onChange={(e) => (data[index].value = e.target.value)}
type={type}
width={input.inputWidth}
validation={{
characterMaxLength: input.validation?.characterMaxLength,
characterMinLength: input.validation?.characterMinLength,
Expand Down Expand Up @@ -121,8 +122,9 @@ const Form: React.FC<FormProps> = ({
<TextArea
id={`textarea_${index}`}
name={input.name}
value={input.value}
onChange={(e) => (data[index].value = e.target.value)}
value={input.value}
width={input.inputWidth}
validation={{
characterMaxLength: input.validation?.characterMaxLength,
characterMinLength: input.validation?.characterMinLength,
Expand Down
5 changes: 5 additions & 0 deletions src/components/Form/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,9 @@ export type DataInput = {
* You can validate your inputs
*/
validation?: ValidateInput;

/**
* You can set an input width
*/
inputWidth?: string;
};
1 change: 0 additions & 1 deletion src/components/Input/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ export interface InputProps {
/**
* input width
*/

width?: string;

/**
Expand Down
2 changes: 0 additions & 2 deletions src/components/Tabs/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export { default as TabList } from './TabsList';
export { Tab } from './TabsList';
export type { ITabList, ITab } from './types';
// above is boilerplate stuff
// replace with your component & props
8 changes: 8 additions & 0 deletions src/components/TextArea/TextArea.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,11 @@ Confirmed.args = {
state: 'confirmed',
value: 'Test Value',
};

export const CustomWidth = Template.bind({});
CustomWidth.args = {
label: 'Standard',
name: 'Test TextArea Default',
placeholder: 'Type here field',
width: '100%',
};
10 changes: 7 additions & 3 deletions src/components/TextArea/TextArea.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import color from '../../styles/colors';
import styled from 'styled-components';
import { TextAreaProps } from './types';

export const TextAreaWrapper = styled.div<Pick<TextAreaProps, 'state'>>`
type TStyleProps = Pick<TextAreaProps, 'state' | 'width'>;

export const TextAreaWrapper = styled.div<TStyleProps>`
${resetCSS};
background-color: ${color.white};
border-radius: 16px;
border: 1px solid ${color.greyLight};
display: inline-block;
max-width: 100%;
padding: 12px;
position: relative;
transition: all 0.1s linear;
width: ${(p) => (p.width ? p.width : '294px')};
&:hover {
border-color: ${(p) =>
Expand Down Expand Up @@ -73,16 +77,16 @@ export const LabelStyled = styled.label`
transition: all 0.1s ease-out;
`;

export const TextAreaStyled = styled.textarea`
export const TextAreaStyled = styled.textarea<TStyleProps>`
${resetCSS}
${fonts.text}
background-color: transparent;
display: block;
max-width: 100%;
min-height: 128px;
width: 294px;
overflow: hidden;
padding: 2px;
width: 100%;
::-webkit-resizer {
visibility: hidden;
Expand Down
103 changes: 102 additions & 1 deletion src/components/TextArea/TextArea.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import * as stories from './TextArea.stories';
import React from 'react';
import color from '../../styles/colors';

const { Default, Error, Confirmed, Disabled } = composeStories(stories);
const { Default, Error, Confirmed, Disabled, CustomWidth } =
composeStories(stories);

let container: HTMLDivElement;
const testValue = 'Test Value';
Expand Down Expand Up @@ -360,3 +361,103 @@ describe('TextArea - Disabled', () => {
expect(testEvent).toHaveBeenCalled();
});
});

describe('TextArea - CustomWidth', () => {
const testLabel = CustomWidth?.args?.label;
const testName = CustomWidth?.args?.name;
const testWidth = CustomWidth?.args?.width;

beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
ReactDOM.render(
<CustomWidth
onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) =>
testEvent(event.target)
}
/>,
container,
);
});
afterEach(() => {
document.body.removeChild(container);
container.remove();
});

it('renders the component', () => {
const textarea: HTMLTextAreaElement | null = container.querySelector(
`[data-testid="${testTextAreaId}"]`,
);
expect(textarea).not.toBeNull();
});

it('renders textarea with the value and placeholder passed', () => {
const textarea: HTMLTextAreaElement | null = container.querySelector(
`[data-testid="${testTextAreaId}"]`,
);
expect(textarea).not.toBeNull();
textarea && expect(textarea.value).toBe('');
textarea && expect(textarea.placeholder).toBe(testPlaceholder);
});

it('renders textarea with the name passed', () => {
const textarea: HTMLTextAreaElement | null = container.querySelector(
`[data-testid="${testTextAreaId}"]`,
);
expect(textarea).not.toBeNull();
textarea && expect(textarea.name).toBe(testName);
});

it('renders wrapper correct border color', () => {
const textareaWrapper: HTMLDivElement | null = container.querySelector(
`[data-testid="${testWrapperId}"]`,
);
const styles = textareaWrapper && getComputedStyle(textareaWrapper);
expect(styles?.borderColor.toUpperCase()).toBe(color.greyLight);
});

it('renders label text', () => {
const label = container.querySelector(`[data-testid="${testLabelId}"]`);
expect(label).not.toBeNull();
expect(label?.textContent).toBe(testLabel);
});

it("should conditionally render 'empty / filled' className", () => {
const div: HTMLDivElement | null = container.querySelector(
`[data-testid="${testWrapperId}"]`,
);
const textarea: HTMLTextAreaElement | null = container.querySelector(
`[data-testid="${testTextAreaId}"]`,
);
expect(div?.classList.contains('filled')).toBeFalsy;
expect(div?.classList.contains('empty')).toBeTruthy;

expect(textarea).not.toBeNull();
textarea?.focus();
textarea && fireEvent.change(textarea, { target: { value: 'foo' } });
textarea && expect(textarea.value).toBe('foo');

expect(div?.classList.contains('filled')).toBeTruthy;
expect(div?.classList.contains('empty')).toBeFalsy;
});

it('onChange event is returned, testEvent => event.target', () => {
const textarea: HTMLTextAreaElement | null = container.querySelector(
`[data-testid="${testTextAreaId}"]`,
);
textarea?.focus();
textarea && fireEvent.change(textarea, { target: { value: 'foo' } });

expect(textarea).not.toBeNull();
textarea && expect(textarea.value).toBe('foo');
expect(testEvent).toHaveBeenCalled();
});

it('renders width property', () => {
const textareaWrapper: HTMLDivElement | null = container.querySelector(
`[data-testid="${testWrapperId}"]`,
);
const styles = textareaWrapper && getComputedStyle(textareaWrapper);
expect(styles?.width).toBe(testWidth);
});
});
4 changes: 3 additions & 1 deletion src/components/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const TextArea: React.FC<TextAreaProps> = ({
state,
validation,
value = '',
width = '300px',
}: TextAreaProps) => {
const [currentValue, setCurrentValue] = useState(value);

Expand All @@ -37,9 +38,10 @@ const TextArea: React.FC<TextAreaProps> = ({

return (
<TextAreaWrapper
state={state}
className={currentValue.length > 0 ? 'filled' : 'empty'}
data-testid="test-textarea-wrapper"
state={state}
width={width}
>
<Icon
svg={iconTypes.expand}
Expand Down
5 changes: 5 additions & 0 deletions src/components/TextArea/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ export interface TextAreaProps {
*/
value?: string;

/**
* you can pass a CSS value for the components width
*/
width?: string;

/**
* You can validate your textarea
* characterMaxLength, characterMinLength, numberMax, numberMin, regExp, regExpInvalidMessage & required
Expand Down

0 comments on commit 6663b34

Please sign in to comment.