Skip to content

Commit ee9cafd

Browse files
committed
fix(ui-select,ui-text-input): fix long before elements overflowing in TextInput, Select, SimpleSelect
Closes: INSTUI-4344 When adding lots of elements to renderBeforeInput for example Tags it was overflowing the input field. Also when there are more than one line of elements keep the input field in the same line if there is enough space TEST PLAN: Add lots of elements to renderBeforeInput to Select,SimpleSelect,TextInput. They should wrap, not overflow
1 parent ef3e930 commit ee9cafd

File tree

7 files changed

+47
-95
lines changed

7 files changed

+47
-95
lines changed

packages/ui-form-field/src/FormFieldLayout/index.tsx

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import { Component } from 'react'
2828
import { hasVisibleChildren } from '@instructure/ui-a11y-utils'
2929
import { ScreenReaderContent } from '@instructure/ui-a11y-content'
3030
import { Grid } from '@instructure/ui-grid'
31-
import { logError as error } from '@instructure/console'
3231
import {
3332
omitProps,
3433
pickProps,
@@ -67,16 +66,7 @@ class FormFieldLayout extends Component<FormFieldLayoutProps> {
6766

6867
constructor(props: FormFieldLayoutProps) {
6968
super(props)
70-
7169
this._messagesId = props.messagesId || props.deterministicId!()
72-
73-
error(
74-
typeof props.width !== 'undefined' ||
75-
!props.inline ||
76-
props.layout !== 'inline',
77-
`[FormFieldLayout] The 'inline' prop is true, and the 'layout' is set to 'inline'.
78-
This will cause a layout issue in Internet Explorer 11 unless you also add a value for the 'width' prop.`
79-
)
8070
}
8171

8272
private _messagesId: string
@@ -212,7 +202,9 @@ class FormFieldLayout extends Component<FormFieldLayoutProps> {
212202
elementRef={this.handleInputContainerRef}
213203
>
214204
{hasNewErrorMsg && (
215-
<div css={styles?.groupErrorMessage}>{this.renderVisibleMessages()}</div>
205+
<div css={styles?.groupErrorMessage}>
206+
{this.renderVisibleMessages()}
207+
</div>
216208
)}
217209
{children}
218210
</Grid.Col>

packages/ui-react-utils/src/omitProps.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
* Return an object with the remaining props after the given props are omitted.
3030
*
3131
* Automatically excludes the following props:
32-
* 'theme', 'children', 'className', 'style', 'styles', 'makeStyles', 'themeOverride', 'deterministicId'
32+
* `theme`, `children`, `className`, `style`, `styles`, `makeStyles`,
33+
* `themeOverride`, `deterministicId`
3334
* @module omitProps
3435
* @param props The object to process
3536
* @param propsToOmit list disallowed prop keys or an object whose

packages/ui-select/src/Select/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,9 @@ To mark an option as "highlighted", use the option's `isHighlighted` prop. Note
850850
{this.getOptionById(id).label}
851851
</AccessibleContent>
852852
}
853-
margin={index > 0 ? 'xxx-small 0 xxx-small xx-small' : 'xxx-small 0'}
853+
margin={
854+
index > 0 ? 'xxx-small xx-small xxx-small 0' : '0 xx-small 0 0'
855+
}
854856
onClick={(e) => this.dismissTag(e, id)}
855857
/>
856858
))
@@ -1079,7 +1081,9 @@ To mark an option as "highlighted", use the option's `isHighlighted` prop. Note
10791081
{this.getOptionById(id).label}
10801082
</AccessibleContent>
10811083
}
1082-
margin={index > 0 ? 'xxx-small 0 xxx-small xx-small' : 'xxx-small 0'}
1084+
margin={
1085+
index > 0 ? 'xxx-small xx-small xxx-small 0' : '0 xx-small 0 0'
1086+
}
10831087
onClick={(e) => dismissTag(e, id)}
10841088
/>
10851089
))

packages/ui-text-input/src/TextInput/README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ Focusable content will be focused separately from the input itself.
205205
value={this.state.value}
206206
onChange={this.handleChange}
207207
renderBeforeInput={
208-
<View display="block" padding="xxx-small 0">
208+
<>
209209
{this.state.value !== '' && (
210210
<Tag
211211
text={this.state.value}
@@ -233,7 +233,7 @@ Focusable content will be focused separately from the input itself.
233233
margin="xxx-small xxx-small xxx-small none"
234234
onClick={() => console.log('Strawberry')}
235235
/>
236-
</View>
236+
</>
237237
}
238238
renderAfterInput={() => (
239239
<Avatar name="Paula Panda" src={avatarSquare} size="x-small" />
@@ -260,7 +260,7 @@ Focusable content will be focused separately from the input itself.
260260
value={value}
261261
onChange={handleChange}
262262
renderBeforeInput={
263-
<View display="block" padding="xxx-small 0">
263+
<>
264264
{value !== '' && (
265265
<Tag
266266
text={value}
@@ -288,7 +288,7 @@ Focusable content will be focused separately from the input itself.
288288
margin="xxx-small xxx-small xxx-small none"
289289
onClick={() => console.log('Strawberry')}
290290
/>
291-
</View>
291+
</>
292292
}
293293
renderAfterInput={() => (
294294
<Avatar name="Paula Panda" src={avatarSquare} size="x-small" />
@@ -346,7 +346,7 @@ type: example
346346
<TextInput
347347
renderLabel="I will wrap"
348348
renderBeforeInput={
349-
<div>
349+
<>
350350
<Tag
351351
text="English 101"
352352
margin="xx-small xxx-small"
@@ -355,7 +355,7 @@ type: example
355355
text="History 205"
356356
margin="xx-small xxx-small"
357357
/>
358-
</div>
358+
</>
359359
}
360360
renderAfterInput={<Avatar name="Paula Panda" src={avatarSquare} size="x-small" />}
361361
/>
@@ -374,6 +374,7 @@ type: embed
374374
<Figure.Item>Left align text (exceptions may apply)</Figure.Item>
375375
<Figure.Item>Place labels on top or to the left (inline)</Figure.Item>
376376
<Figure.Item>Make placeholder text different than the label</Figure.Item>
377+
<Figure.Item>Use React fragments for <code>renderBeforeInput</code>. This will nicely float the text input box to the remaining space</Figure.Item>
377378
</Figure>
378379
<Figure recommendation="no" title="Don't">
379380
<Figure.Item>Place labels to the right of the input</Figure.Item>

packages/ui-text-input/src/TextInput/index.tsx

Lines changed: 15 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ class TextInput extends Component<TextInputProps, TextInputState> {
7777
super(props)
7878
this.state = {
7979
hasFocus: false,
80-
beforeElementHasWidth: undefined,
8180
afterElementHasWidth: undefined
8281
}
8382
this._defaultId = props.deterministicId!()
@@ -87,7 +86,6 @@ class TextInput extends Component<TextInputProps, TextInputState> {
8786
ref: Element | null = null
8887

8988
private _input: HTMLInputElement | null = null
90-
private _beforeElement: HTMLSpanElement | null = null
9189
private _afterElement: HTMLSpanElement | null = null
9290

9391
private readonly _defaultId: string
@@ -112,13 +110,10 @@ class TextInput extends Component<TextInputProps, TextInputState> {
112110
'focus',
113111
this.handleFocus
114112
)
115-
116113
this.setState({
117-
beforeElementHasWidth: this.getElementHasWidth(this._beforeElement),
118114
afterElementHasWidth: this.getElementHasWidth(this._afterElement)
119115
})
120116
}
121-
122117
this.props.makeStyles?.(this.makeStyleProps())
123118
}
124119

@@ -129,11 +124,6 @@ class TextInput extends Component<TextInputProps, TextInputState> {
129124
}
130125

131126
componentDidUpdate(prevProps: TextInputProps) {
132-
if (prevProps.renderBeforeInput !== this.props.renderBeforeInput) {
133-
this.setState({
134-
beforeElementHasWidth: this.getElementHasWidth(this._beforeElement)
135-
})
136-
}
137127
if (prevProps.renderAfterInput !== this.props.renderAfterInput) {
138128
this.setState({
139129
afterElementHasWidth: this.getElementHasWidth(this._afterElement)
@@ -154,13 +144,13 @@ class TextInput extends Component<TextInputProps, TextInputState> {
154144

155145
makeStyleProps = (): TextInputStyleProps => {
156146
const { interaction } = this
157-
const { hasFocus, beforeElementHasWidth, afterElementHasWidth } = this.state
147+
const { hasFocus, afterElementHasWidth } = this.state
158148
return {
159149
disabled: interaction === 'disabled',
160150
invalid: this.invalid,
161151
focused: hasFocus,
162-
beforeElementHasWidth,
163-
afterElementHasWidth
152+
afterElementHasWidth: afterElementHasWidth,
153+
beforeElementExists: this.props.renderBeforeInput != undefined
164154
}
165155
}
166156

@@ -282,7 +272,7 @@ class TextInput extends Component<TextInputProps, TextInputState> {
282272
)
283273
}
284274

285-
getElementHasWidth(element: HTMLSpanElement | null) {
275+
getElementHasWidth(element: Element | null) {
286276
if (!element) {
287277
return undefined
288278
}
@@ -360,39 +350,24 @@ class TextInput extends Component<TextInputProps, TextInputState> {
360350
>
361351
<span css={styles?.facade}>
362352
{renderBeforeOrAfter ? (
363-
<div>
364-
<span css={styles?.layout}>
365-
{beforeElement && (
353+
<span css={styles?.layout}>
354+
{beforeElement}
355+
{/* The input and content after input should not wrap,
356+
so they're in their own flex container */}
357+
<span css={styles?.inputLayout}>
358+
{this.renderInput()}
359+
{afterElement && (
366360
<span
367-
css={styles?.beforeElement}
361+
css={styles?.afterElement}
368362
ref={(e) => {
369-
this._beforeElement = e
363+
this._afterElement = e
370364
}}
371365
>
372-
{beforeElement}
366+
{afterElement}
373367
</span>
374368
)}
375-
<span css={styles?.innerWrapper}>
376-
{/*
377-
The input and content after input should not wrap,
378-
so they're in their own flex container
379-
*/}
380-
<span css={styles?.inputLayout}>
381-
<span css={styles?.innerWrapper}>{this.renderInput()}</span>
382-
{afterElement && (
383-
<span
384-
css={styles?.afterElement}
385-
ref={(e) => {
386-
this._afterElement = e
387-
}}
388-
>
389-
{afterElement}
390-
</span>
391-
)}
392-
</span>
393-
</span>
394369
</span>
395-
</div>
370+
</span>
396371
) : (
397372
/* If no prepended or appended content, don't render Flex layout */
398373
this.renderInput()

packages/ui-text-input/src/TextInput/props.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,9 @@ type TextInputStyle = ComponentStyle<
193193
| 'textInput'
194194
| 'facade'
195195
| 'layout'
196-
| 'beforeElement'
197-
| 'innerWrapper'
198-
| 'inputLayout'
199196
| 'afterElement'
200197
| 'requiredInvalid'
198+
| 'inputLayout'
201199
>
202200

203201
const propTypes: PropValidators<PropKeys> = {
@@ -254,16 +252,15 @@ const allowedProps: AllowedPropKeys = [
254252

255253
type TextInputState = {
256254
hasFocus: boolean
257-
beforeElementHasWidth?: boolean
258255
afterElementHasWidth?: boolean
259256
}
260257

261258
type TextInputStyleProps = {
262259
disabled: boolean
263260
invalid: boolean
264261
focused: TextInputState['hasFocus']
265-
beforeElementHasWidth: TextInputState['beforeElementHasWidth']
266262
afterElementHasWidth: TextInputState['afterElementHasWidth']
263+
beforeElementExists: boolean
267264
}
268265

269266
export type {

packages/ui-text-input/src/TextInput/styles.ts

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ const generateStyle = (
4949
disabled,
5050
invalid,
5151
focused,
52-
beforeElementHasWidth,
53-
afterElementHasWidth
52+
afterElementHasWidth,
53+
beforeElementExists
5454
} = state
5555

5656
const sizeVariants = {
@@ -103,15 +103,14 @@ const generateStyle = (
103103

104104
const inputStyle = {
105105
all: 'initial',
106-
107106
'&::-ms-clear': {
108107
display: 'none'
109108
},
109+
width: '100%',
110110
WebkitFontSmoothing: 'antialiased',
111111
MozOsxFontSmoothing: 'grayscale',
112112
appearance: 'none',
113113
margin: 0,
114-
width: '100%',
115114
display: 'block',
116115
boxSizing: 'border-box',
117116
outline: 'none',
@@ -151,11 +150,6 @@ const generateStyle = (
151150
flexDirection: 'row'
152151
}
153152

154-
const flexItemBase = {
155-
...viewBase,
156-
flexShrink: 0
157-
}
158-
159153
return {
160154
requiredInvalid: {
161155
color: componentTheme.requiredInvalidColor
@@ -198,30 +192,17 @@ const generateStyle = (
198192
},
199193
layout: {
200194
label: 'textInput__layout',
201-
...flexBase,
202-
...(!shouldNotWrap && { flexWrap: 'wrap' })
203-
},
204-
beforeElement: {
205-
display: 'inline-flex',
195+
...viewBase,
196+
display: 'flex',
206197
alignItems: 'center',
207-
label: 'textInput__beforeElement',
208-
...flexItemBase,
209-
paddingInlineStart: componentTheme.padding,
210-
// we only override the padding once the width is calculated,
211-
// it needs the padding on render
212-
...(beforeElementHasWidth === false && {
213-
paddingInlineStart: 0
214-
})
215-
},
216-
innerWrapper: {
217-
label: 'textInput__innerWrapper',
218-
...flexItemBase,
219-
minWidth: '0.0625rem',
220-
flexShrink: 1,
221-
flexGrow: 1
198+
justifyContent: 'flex-start',
199+
flexDirection: 'row',
200+
...(!shouldNotWrap && { flexWrap: 'wrap' }),
201+
...(beforeElementExists && { paddingInlineStart: componentTheme.padding })
222202
},
223203
inputLayout: {
224204
label: 'textInput__inputLayout',
205+
flexGrow: 1,
225206
...flexBase
226207
},
227208
afterElement: {
@@ -231,7 +212,8 @@ const generateStyle = (
231212
marginTop: '-1px',
232213
marginBottom: '-1px',
233214
label: 'textInput__afterElement',
234-
...flexItemBase,
215+
...viewBase,
216+
flexShrink: 0,
235217
paddingInlineEnd: componentTheme.padding,
236218
// we only override the padding once the width is calculated,
237219
// it needs the padding on render

0 commit comments

Comments
 (0)