Skip to content

Commit f345a58

Browse files
committed
fix(ui-text-input): set IconButton height in TextInput to not break layout
INSTUI-4651
1 parent f5941b1 commit f345a58

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ class TextInput extends Component<TextInputProps, TextInputState> {
113113
afterElementHasWidth: this.getElementHasWidth(this._afterElement)
114114
})
115115
}
116+
this.adjustAfterElementHeight()
116117
this.props.makeStyles?.(this.makeStyleProps())
117118
}
118119

@@ -141,6 +142,35 @@ class TextInput extends Component<TextInputProps, TextInputState> {
141142
this.props.makeStyles?.(this.makeStyleProps())
142143
}
143144

145+
adjustAfterElementHeight() {
146+
const afterElementChild = this._afterElement
147+
?.firstElementChild as HTMLElement | null
148+
149+
// Check if the child is a button, then get the button's first child (the content span)
150+
const buttonContentSpan =
151+
afterElementChild?.tagName === 'BUTTON'
152+
? (afterElementChild.firstElementChild as HTMLElement | null)
153+
: null
154+
155+
// This is a necessary workaround for DateInput2 because it uses a Popover, which has a nested Button as an afterElement
156+
// Check if the child is a Popover's inner span containing a button, then get the button's first child (the content span)
157+
const popoverContentSpan =
158+
afterElementChild?.tagName === 'SPAN' &&
159+
afterElementChild.firstElementChild?.tagName === 'BUTTON'
160+
? (afterElementChild.firstElementChild
161+
.firstElementChild as HTMLElement | null)
162+
: null
163+
164+
const targetSpan = buttonContentSpan ?? popoverContentSpan
165+
166+
if (targetSpan) {
167+
// Set the height to 36px (the height of a medium TextInput) to avoid layout shift when the afterElement content changes
168+
// this temporary workaround is necessary because otherwise the layout breaks, later on IconButton's default height will be adjusted to the TextInput size
169+
// so this workaround will not be needed anymore
170+
targetSpan.style.height = '36px'
171+
}
172+
}
173+
144174
makeStyleProps = (): TextInputStyleProps => {
145175
const { interaction } = this
146176
const { hasFocus, afterElementHasWidth } = this.state

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,14 +208,20 @@ const generateStyle = (
208208
afterElement: {
209209
// the next couple lines (until the `label`) is needed so the IconButton looks OK inside the TextInput
210210
// explanation: if the content inside is not a button or a popover (which could contain a button) it should have some padding on the right
211+
// lineHeight is only needed if it is not popover or button
211212
'& > :not(button):not([data-position^="Popover"])': {
212-
marginRight: componentTheme.padding
213+
marginRight: componentTheme.padding,
214+
...(sizeVariants[size!] && {
215+
lineHeight: sizeVariants[size!].lineHeight
216+
})
213217
},
214-
marginTop: '1px',
215-
marginBottom: '2px',
216218
display: 'flex',
217219
alignItems: 'center',
218-
...sizeVariants[size!],
220+
// Spread all sizeVariants except lineHeight (handled above)
221+
...(sizeVariants[size!]
222+
? // eslint-disable-next-line @typescript-eslint/no-unused-vars
223+
(({ lineHeight, ...rest }) => rest)(sizeVariants[size!])
224+
: {}),
219225
label: 'textInput__afterElement',
220226
...viewBase,
221227
borderRadius: componentTheme.borderRadius,

0 commit comments

Comments
 (0)