Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

breaking(TextArea): remove autoHeight prop #3465

Merged
merged 1 commit into from
Feb 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

This file was deleted.

This file was deleted.

This file was deleted.

38 changes: 24 additions & 14 deletions docs/src/examples/addons/TextArea/Usage/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
import React from 'react'
import { Button, Message, Segment } from 'semantic-ui-react'

import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample'
import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection'

const TextAreaUsageExamples = () => (
<ExampleSection title='Usage'>
<Segment>
<Message warning>
<Message.Header>Auto height</Message.Header>
We don't support `autoHeight` more. If you need this feature you can use{' '}
<a
href='https://www.npmjs.com/package/react-textarea-autosize'
rel='noopener noreferrer'
target='_blank'
>
<code>react-textarea-autosize</code>
</a>{' '}
with <code>TextArea</code>.
</Message>
<Button
content='Try on CodeSandbox'
color='black'
href='https://codesandbox.io/s/1v67906ll4'
icon='codepen'
size='small'
target='_blank'
/>
</Segment>

<ComponentExample
title='Min Height'
description='A TextArea can have a minimum height.'
Expand All @@ -16,20 +40,6 @@ const TextAreaUsageExamples = () => (
description='A TextArea can have a minimum number of rows.'
examplePath='addons/TextArea/Usage/TextAreaExampleRows'
/>

<ComponentExample
title='Auto Height'
description='A TextArea can adjust its height to fit its contents.'
examplePath='addons/TextArea/Usage/TextAreaExampleAutoHeight'
/>
<ComponentExample
description='A TextArea can adjust its height to fit its contents and depend on minHeight value.'
examplePath='addons/TextArea/Usage/TextAreaExampleAutoHeightMinHeight'
/>
<ComponentExample
description='A TextArea can adjust its height to fit its contents and depend on rows value.'
examplePath='addons/TextArea/Usage/TextAreaExampleAutoHeightRows'
/>
</ExampleSection>
)

Expand Down
24 changes: 24 additions & 0 deletions docs/src/pages/Prototypes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,28 @@ export const meta = {
</Button.Group>
</Card.Content>
</Card>
<Card>
<Image src='/images/prototypes/react-textarea-autosize.png' />
<Card.Content>
<Card.Header>react-textarea-autosize</Card.Header>
<Card.Description>Examples of auto sized textareas.</Card.Description>
</Card.Content>
<Card.Content extra>
<Button.Group fluid size='small' vertical>
<Button
content='Try on CodeSandbox'
href='https://codesandbox.io/s/1v67906ll4'
icon='codepen'
target='_blank'
/>
<Button
color='black'
content='Source on Github'
href='https://github.com/layershifter/semantic-ui-react-with-textarea-autosize'
icon='github'
target='_blank'
/>
</Button.Group>
</Card.Content>
</Card>
</Card.Group>
6 changes: 0 additions & 6 deletions src/addons/TextArea/TextArea.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ export interface StrictTextAreaProps {
/** An element type to render as (string or function). */
as?: any

/** Indicates whether height of the textarea fits the content or not. */
autoHeight?: boolean

/**
* Called on change.
*
Expand All @@ -30,9 +27,6 @@ export interface StrictTextAreaProps {
/** Indicates row count for a TextArea. */
rows?: number | string

/** Custom TextArea style. */
style?: Object

/** The value of the textarea. */
value?: number | string
}
Expand Down
53 changes: 1 addition & 52 deletions src/addons/TextArea/TextArea.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ class TextArea extends Component {
/** An element type to render as (string or function). */
as: customPropTypes.as,

/** Indicates whether height of the textarea fits the content or not. */
autoHeight: PropTypes.bool,

/**
* Called on change.
* @param {SyntheticEvent} event - The React SyntheticEvent object
Expand All @@ -33,9 +30,6 @@ class TextArea extends Component {
/** Indicates row count for a TextArea. */
rows: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

/** Custom TextArea style. */
style: PropTypes.object,

/** The value of the textarea. */
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
}
Expand All @@ -47,21 +41,6 @@ class TextArea extends Component {

ref = createRef()

componentDidMount() {
this.updateHeight()
}

componentDidUpdate(prevProps) {
// removed autoHeight
if (!this.props.autoHeight && prevProps.autoHeight) {
this.removeAutoHeightStyles()
}
// added autoHeight or value changed
if ((this.props.autoHeight && !prevProps.autoHeight) || prevProps.value !== this.props.value) {
this.updateHeight()
}
}

focus = () => this.ref.current.focus()

handleChange = (e) => {
Expand All @@ -74,50 +53,20 @@ class TextArea extends Component {
const value = _.get(e, 'target.value')

_.invoke(this.props, 'onInput', e, { ...this.props, value })
this.updateHeight()
}

removeAutoHeightStyles = () => {
this.ref.current.style.height = null
this.ref.current.style.resize = null
}

updateHeight = () => {
const { autoHeight } = this.props
if (!this.ref.current || !autoHeight) return

const { minHeight, borderBottomWidth, borderTopWidth } = window.getComputedStyle(
this.ref.current,
)

const borderHeight = _.sum([borderBottomWidth, borderTopWidth].map(x => parseFloat(x)))
const scrollHeight = this.ref.current.scrollHeight

// Measure the scrollHeight and update the height to match.
this.ref.current.style.height = 'auto'
this.ref.current.style.overflowY = 'hidden'
this.ref.current.style.height = `${Math.max(
parseFloat(minHeight),
Math.ceil(scrollHeight + borderHeight),
)}px`
this.ref.current.style.overflowY = ''
}

render() {
const { autoHeight, rows, style, value } = this.props
const { rows, value } = this.props
const rest = getUnhandledProps(TextArea, this.props)
const ElementType = getElementType(TextArea, this.props)

const resize = autoHeight ? 'none' : ''

return (
<ElementType
{...rest}
onChange={this.handleChange}
onInput={this.handleInput}
ref={this.ref}
rows={rows}
style={{ resize, ...style }}
value={value}
/>
)
Expand Down
110 changes: 10 additions & 100 deletions test/specs/addons/TextArea/TextArea-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,84 +40,6 @@ describe('TextArea', () => {
},
})

describe('autoHeight', () => {
// simplify styles to make height assertions easier
const style = { padding: 0, fontSize: '10px', lineHeight: 1, border: 'none' }

const assertHeight = (height) => {
const element = document.querySelector('textarea')

if (!height) {
element.style.should.have.property('resize', '')
element.style.should.have.property('height', '')
return
}

element.style.should.have.property('resize', 'none')

// CI renders textareas with an extra pixel
// assert height with a margin of error of one pixel
const parsedHeight = parseInt(height, 10)
parseInt(element.style.height, 10).should.be.within(parsedHeight - 1, parsedHeight + 1)
}

it('sets styles when true', () => {
wrapperMount(<TextArea autoHeight style={style} />)
assertHeight('30px') // 3 lines
})

it('does not set styles when not set', () => {
wrapperMount(<TextArea style={style} />)
assertHeight('') // no height
})

it('depends on minHeight value of style', () => {
wrapperMount(<TextArea autoHeight style={{ ...style, minHeight: '50px' }} />)
assertHeight('50px')
})

it('depends on rows value', () => {
wrapperMount(<TextArea autoHeight style={style} rows={1} />)
assertHeight('10px') // 1 line
})

it('sets styles when there is a multiline value', () => {
wrapperMount(
<TextArea
autoHeight
style={style}
value={'line1\nline2\nline3\nline4'}
/>,
)
assertHeight('40px') // 4 lines
})

it('updates the height on change', () => {
wrapperMount(<TextArea autoHeight style={style} />)

// initial height
assertHeight('30px') // 3 lines

// update the value and fire a change event
wrapper.setProps({ value: 'line1\nline2\nline3\nline4' })
assertHeight('40px') // 4 lines
})

it('adds styles when toggled to true', () => {
wrapperMount(<TextArea style={style} />)
wrapper.setProps({ autoHeight: true, rows: 1 })

assertHeight('10px') // 1 line
})

it('removes styles when toggled to false', () => {
wrapperMount(<TextArea autoHeight style={style} />)
wrapper.setProps({ autoHeight: false })

assertHeight('') // no height
})
})

describe('focus', () => {
it('can be set via a ref', () => {
wrapperMount(<TextArea />)
Expand All @@ -130,51 +52,39 @@ describe('TextArea', () => {

describe('onChange', () => {
it('is called with (e, data) on change', () => {
const spy = sandbox.spy()
const onChange = sandbox.spy()
const e = { target: { value: 'name' } }
const props = { 'data-foo': 'bar', onChange: spy }
const props = { 'data-foo': 'bar', onChange }

wrapperShallow(<TextArea {...props} />)
wrapper.find('textarea').simulate('change', e)

spy.should.have.been.calledOnce()
spy.should.have.been.calledWithMatch(e, { ...props, value: e.target.value })
onChange.should.have.been.calledOnce()
onChange.should.have.been.calledWithMatch(e, { ...props, value: e.target.value })
})
})

describe('onInput', () => {
it('is called with (e, data) on input', () => {
const spy = sandbox.spy()
const onInput = sandbox.spy()
const e = { target: { value: 'name' } }
const props = { 'data-foo': 'bar', onInput: spy }
const props = { 'data-foo': 'bar', onInput }

wrapperShallow(<TextArea {...props} />)
wrapper.find('textarea').simulate('input', e)

spy.should.have.been.calledOnce()
spy.should.have.been.calledWithMatch(e, { ...props, value: e.target.value })
onInput.should.have.been.calledOnce()
onInput.should.have.been.calledWithMatch(e, { ...props, value: e.target.value })
})
})

describe('rows', () => {
it('has default value', () => {
shallow(<TextArea />)
.should.have.prop('rows', 3)
shallow(<TextArea />).should.have.prop('rows', 3)
})

it('sets prop', () => {
shallow(<TextArea rows={1} />)
.should.have.prop('rows', 1)
})
})

describe('style', () => {
it('applies defined style', () => {
const style = { marginTop: '1em', top: 0 }

wrapperShallow(<TextArea style={style} />)
wrapper.should.have.style('margin-top', '1em')
wrapper.should.have.style('top', '0')
shallow(<TextArea rows={1} />).should.have.prop('rows', 1)
})
})
})