Skip to content

Commit

Permalink
MOON-471: Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Eevolee committed Jan 8, 2025
1 parent ee38938 commit 9f46fcf
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 54 deletions.
26 changes: 13 additions & 13 deletions src/components/Textarea/ControlledTextarea.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import clsx from 'clsx';
import './Textarea.scss';
import {ControlledTextareaProps} from './Textarea.types';
import type {ControlledTextareaProps} from './Textarea.types';

export const ControlledTextarea = React.forwardRef<HTMLTextAreaElement, ControlledTextareaProps>(({
value,
Expand All @@ -18,18 +18,18 @@ export const ControlledTextarea = React.forwardRef<HTMLTextAreaElement, Controll
}, ref) => {
return (
<textarea
ref={ref}
className={clsx('moonstone-textarea', isResizable && 'moonstone-textarea_resizable', className)}
value={value}
id={id}
placeholder={placeholder}
disabled={isDisabled}
readOnly={isReadOnly}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
{...props}
/>
ref={ref}
className={clsx('moonstone-textarea', isResizable && 'moonstone-textarea_resizable', className)}
value={value}
id={id}
placeholder={placeholder}
disabled={isDisabled}
readOnly={isReadOnly}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
{...props}
/>
);
});

Expand Down
22 changes: 3 additions & 19 deletions src/components/Textarea/Textarea.scss
Original file line number Diff line number Diff line change
@@ -1,29 +1,13 @@
@use '../../utils/index';

$height-size: 50px;
$width-size: 40px;

.moonstone-textarea-container {
position: relative;

display: inline-flex;
align-items: center;
width: 100%;
min-width: $width-size;
min-height: $height-size;

color: var(--color-gray_dark);

cursor: text;
}
$minHeight-size: 50px;

.moonstone-textarea {
@extend %variant-caption;

display: block;
width: 100%; // CSS trick to force element to shrink
min-width: 50px;
min-height: $height-size;
min-height: $minHeight-size;
margin: 0; // Reset for Safari

border: var(--border-selector);
Expand All @@ -34,7 +18,7 @@ $width-size: 40px;
-webkit-tap-highlight-color: transparent;

&.moonstone-textarea_resizable {
resize: auto;
resize: vertical;
}

// Reset Firefox invalid required input style
Expand Down
19 changes: 12 additions & 7 deletions src/components/Textarea/Textarea.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ describe('Textarea', () => {
expect(screen.getByTestId('moonstone-textarea')).toHaveAttribute('readonly');
});

it('should be resizeable', () => {
render(<Textarea isResizable data-testid="moonstone-textarea"/>);
expect(screen.getByTestId('moonstone-textarea')).toHaveClass('moonstone-textarea_resizable');
});

it('should work when no value or defaultValue is specified', async () => {
const user = userEvent.setup();

Expand All @@ -55,13 +60,6 @@ describe('UncontrolledTextarea', () => {
expect(screen.getByDisplayValue('test-default-value')).toBeInTheDocument();
});

it('should not update when value is specified', async () => {
const user = userEvent.setup();
render(<Textarea data-testid="moonstone-textarea" value="test-value"/>);
await user.type(screen.getByTestId('moonstone-textarea'), '-updated');
expect(screen.queryByDisplayValue('test-value-updated')).not.toBeInTheDocument();
});

it('should update specified defaultValue', async () => {
const user = userEvent.setup();
render(<Textarea data-testid="moonstone-textarea" defaultValue="test-default-value"/>);
Expand All @@ -85,6 +83,13 @@ describe('UncontrolledTextarea', () => {
expect(screen.getByDisplayValue('test-value')).toBeInTheDocument();
});

it('should not update when value is specified', async () => {
const user = userEvent.setup();
render(<Textarea data-testid="moonstone-textarea" value="test-value"/>);
await user.type(screen.getByTestId('moonstone-textarea'), '-updated');
expect(screen.queryByDisplayValue('test-value-updated')).not.toBeInTheDocument();
});

it('should call specified onChange function', async () => {
const user = userEvent.setup();
const handleChange = jest.fn();
Expand Down
17 changes: 13 additions & 4 deletions src/components/Textarea/Textarea.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useState} from 'react';
import {StoryObj, Meta} from '@storybook/react';

import {Textarea} from './index';
Expand Down Expand Up @@ -32,12 +32,21 @@ type Story = StoryObj<typeof Textarea>;

export const Uncontrolled: Story = {
args: {
value: 'Uncontrolled textarea'
placeholder: 'Uncontrolled textarea'
}
};

export const Controlled: Story = {
args: {
placeholder: 'Controlled textarea'

render: args => {
const [textareaValue, setTextareaValue] = useState('Default value');

return (
<Textarea
value={textareaValue}
onChange={e => setTextareaValue(e.target.value)}
{...args}
/>
);
}
};
8 changes: 4 additions & 4 deletions src/components/Textarea/Textarea.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React from 'react';
import {TextareaProps} from './Textarea.types';
import type {TextareaProps} from './Textarea.types';
import {UncontrolledTextarea} from './UncontrolledTextarea';
import {ControlledTextarea} from './ControlledTextarea';

export const Textarea: React.FC<TextareaProps> = ({value, ...props}) => {
export const Textarea: React.FC<TextareaProps> = ({value, onChange, ...props}) => {
if (typeof value === 'undefined') {
return <UncontrolledTextarea {...props}/>;
return <UncontrolledTextarea onChange={onChange} {...props}/>;
}

return <ControlledTextarea value={value} {...props}/>;
return <ControlledTextarea value={value} onChange={onChange} {...props}/>;
};

Textarea.displayName = 'Textarea';
14 changes: 9 additions & 5 deletions src/components/Textarea/Textarea.types.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import * as React from 'react';

export type BaseTextareaProps = Omit<React.ComponentPropsWithRef<'textarea'>, 'value' | 'defaultValue' | 'onChange' | 'onFocus' | 'onBlur' | 'className'> & {
export type BaseTextareaProps = Omit<React.ComponentPropsWithRef<'textarea'>, 'id' | 'value' | 'defaultValue' | 'onChange' | 'onFocus' | 'onBlur' | 'className'> & {
/**
* Required id
*/
id: string
/**
* Textarea's function onChange
*/
onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
/**
* Textarea's function onFocus
*/
Expand Down Expand Up @@ -44,13 +40,21 @@ export type ControlledProps = {
* Textarea's value
*/
value: string
/**
* Textarea's function onChange
*/
onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
}

export type UncontrolledProps = {
/**
* Textarea's default value
*/
defaultValue?: string
/**
* Textarea's function onChange
*/
onChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
}

export type TextareaProps = BaseTextareaProps & Partial<ControlledProps> & Partial<UncontrolledProps>;
Expand Down
4 changes: 2 additions & 2 deletions src/components/Textarea/UncontrolledTextarea.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {useState, ChangeEvent} from 'react';
import {UncontrolledTextareaProps} from './Textarea.types';
import type {UncontrolledTextareaProps} from './Textarea.types';
import {ControlledTextarea} from './ControlledTextarea';

export const UncontrolledTextarea: React.FC<UncontrolledTextareaProps> = ({defaultValue, onChange, ...props}) => {
Expand All @@ -13,7 +13,7 @@ export const UncontrolledTextarea: React.FC<UncontrolledTextareaProps> = ({defau
}
};

return <ControlledTextarea value={textareaValue} onChange={handleOnChange} {...props}/>;
return <ControlledTextarea value={textareaValue} className="uncontrolled" onChange={handleOnChange} {...props}/>;
};

UncontrolledTextarea.displayName = 'UncontrolledTextarea';

0 comments on commit 9f46fcf

Please sign in to comment.