fix: assorted UI/UX fixes (Giphy, polls, dialogs, composer, headers)#3081
fix: assorted UI/UX fixes (Giphy, polls, dialogs, composer, headers)#3081
Conversation
Giphy images used percentage-based max-height/max-width (100%) which don't resolve to pixel values, causing getCSSSizeRestrictions to warn about invalid sizing and preventing CDN image resizing from working. Switch to pixel-based constraints via --str-chat__attachment-max-width and add the clamped-height-from-original-image-dimensions mixin, matching how regular image attachments are sized.
|
Size Change: +776 B (+0.13%) Total Size: 615 kB π¦ View Changed
βΉοΈ View Unchanged
|
Codecov Reportβ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #3081 +/- ##
==========================================
+ Coverage 79.08% 79.18% +0.09%
==========================================
Files 426 427 +1
Lines 12155 12177 +22
Branches 3919 3919
==========================================
+ Hits 9613 9642 +29
+ Misses 2542 2535 -7 β View full report in Codecov by Sentry. π New features to boost your workflow:
|
β¦play name The PollVoteAuthor component fell back to "Anonymous" when a voter had no `name` set, making public polls appear anonymous. Now falls back to the user's ID before "Anonymous". Also fixes a double-click bug in SwitchField where the label had both htmlFor and a programmatic onClick, causing the checkbox to toggle twice (canceling itself out) when clicking the label text. Also bumps PlaybackRateButton min-width from 40px to 48px.
The dialog overlay (`str-chat__dialog-overlay`) used position: absolute with inset: 0, expanding to the nearest positioned ancestor. When a DialogManagerProvider was rendered inside the sidebar (e.g. ActionsMenu), the overlay covered the entire chat view, freezing the channel list. Set pointer-events: none on the overlay so clicks pass through to elements below, with pointer-events: auto on dialog contents. Removed the now-unreachable overlay onClick handler and updated the document-level click handler to treat clicks on the overlay element itself as outside clicks.
β¦area The textarea's native scrollbar looked jarring inside the rounded compose area (border-radius: var(--radius-3xl)). Hide it with scrollbar-width: none, matching the pattern used in polls, reactions, and search components. Scroll functionality is preserved.
Giphy messages (command === 'giphy') should not be editable since their content is generated by the /giphy command. Added a check alongside the existing poll exclusion in useUserRole.
β¦ edit When exiting edit mode, the composer now restores to the state it was in before editing began. If the composer had text, that text is shown; if it was empty, it returns to the placeholder. Uses a WeakMap-based snapshot mechanism that captures the full composer state (text, attachments, link previews, location, polls, custom data) before entering edit mode and restores it on cancel. The snapshot is discarded (not restored) after a successful edit save.
| onClick={(event) => { | ||
| if (!dialogManager) return; | ||
| if (event.target !== event.currentTarget) return; | ||
| Object.values(dialogManager.state.getLatestValue().dialogsById).forEach( | ||
| (dialog) => { | ||
| if (!dialog.isOpen) return; | ||
| if ( | ||
| !shouldCloseOnOutsideClick({ | ||
| dialog, | ||
| managerCloseOnClickOutside: dialogManager.closeOnClickOutside, | ||
| }) | ||
| ) | ||
| return; | ||
| dialogManager.close(dialog.id); | ||
| }, | ||
| ); | ||
| }} |
There was a problem hiding this comment.
Why was this removed?
There was a problem hiding this comment.
The dialog overlay (str-chat__dialog-overlay) used position: absolute with inset: 0, expanding to the nearest positioned ancestor. When a DialogManagerProvider was rendered inside the sidebar (e.g. ActionsMenu in the vite example), the overlay covered the entire chat view, freezing the channel list. Set pointer-events: none on the overlay so clicks pass through, with pointer-events: auto on .str-chat__dialog-contents. Removed the now-unreachable overlay onClick handler and updated the document-level click handler to treat clicks on the overlay element itself as outside clicks.
We now utilize pointer-events, and this event won't ever fire.
The channel/thread name wasn't centered because the flanking elements (toggle button, avatar/close button) had different widths. Wrapped them in always-rendered start/end container divs with flex: 1, so the center element is truly centered regardless of flanking content. Applies to both ChannelHeader and ThreadHeader.
β¦l pointer events The ChannelList lacked `position: relative`, so its dialog overlay (`position: absolute; inset: 0`) escaped to the nearest positioned ancestor higher in the treeβcovering the entire sidebar when consumers override `transform: none` on the channel list. The GlobalModal portals into `.str-chat__dialog-overlay` (which has `pointer-events: none`) without using `DialogAnchor`, so its content never received the `.str-chat__dialog-contents` class that restores `pointer-events: auto`. The modal was visible but completely click-through.
## [14.0.0-beta.5](v14.0.0-beta.4...v14.0.0-beta.5) (2026-03-31) ### Bug Fixes * assorted UI/UX fixes (Giphy, polls, dialogs, composer, headers) ([#3081](#3081)) ([6c06e04](6c06e04)) * close CSS gaps, fix ChannelList dialog portal, and clean up icons ([#3079](#3079)) ([a47981f](a47981f)) * **Icons:** sync icon catalog with refreshed Line SVGs ([#3080](#3080)) ([9472f7b](9472f7b)) * **MessageList:** set width on message list scroll container ([#3077](#3077)) ([3f09362](3f09362)) * post-review `MessageReactionsDetail` adjustments ([#3082](#3082)) ([a82bdcb](a82bdcb)) * use link icon for link-type attachments ([#3083](#3083)) ([241209e](241209e)) ### Features * **examples:** add RTL direction toggle to vite example app ([#3084](#3084)) ([887a326](887a326)) * **examples:** refresh react tutorial app for v14 ([#3078](#3078)) ([86ada37](86ada37))
|
π This PR is included in version 14.0.0-beta.5 π The release is available on: Your semantic-release bot π¦π |
π― Goal
Bundle of UI/UX fixes across multiple components.
π Implementation details
fix(Attachment): use pixel-based sizing constraints for Giphy images
Giphy images used percentage-based CSS constraints which don't resolve to pixel values. Replaced with pixel-based constraints via
var(--str-chat__attachment-max-width)and theclamped-height-from-original-image-dimensionsmixin.fix(Message): prevent editing Giphy messages
Giphy messages (
command === 'giphy') are no longer editable since their content is generated by the /giphy command.fix(Form): remove redundant onClick on SwitchField label
SwitchFieldLabelhad bothhtmlForand a programmaticonClick, causing the checkbox to toggle twice when clicking label text. Removed the redundantonClick.fix(Poll): fall back to user ID instead of "Anonymous" for voters without a display name
PollVoteAuthorfell back to "Anonymous" whenvote.user.namewas falsy, making public polls appear anonymous. Now falls back tovote.user.id.fix(Dialog): prevent dialog overlay from blocking pointer events
The dialog overlay expanded to the nearest positioned ancestor, blocking the channel list. Set
pointer-events: noneon the overlay withpointer-events: autoon dialog contents.fix(MessageComposer): hide textarea scrollbar inside rounded compose area
Hidden with
scrollbar-width: none, matching the pattern used in polls, reactions, and search.fix(MessageComposer): restore pre-edit composer state when cancelling edit
When exiting edit mode, the composer restores to its pre-edit state using a WeakMap-based snapshot mechanism. The snapshot is discarded after a successful edit save.
fix(ChannelHeader, ThreadHeader): truly center title text
The title wasn't centered because flanking elements had different widths. Wrapped the toggle button and avatar/close button in always-rendered start/end container divs with
flex: 1, so the center element is truly centered regardless of flanking content.style(AudioPlayback): bump PlaybackRateButton min-width
Increased from 40px to 48px to prevent text clipping.
fix(ChannelList): scope dialog overlay with
position: relativeThe ChannelList lacked
position: relative, so its dialog overlay (position: absolute; inset: 0) escaped to the nearest positioned ancestor higher in the tree β covering the entire sidebar when consumers overridetransform: noneon the channel list.fix(Modal): restore pointer events on GlobalModal
GlobalModalportals into.str-chat__dialog-overlay(which haspointer-events: none) without usingDialogAnchor, so its content never received the.str-chat__dialog-contentsclass that restorespointer-events: auto. The modal was visible but completely click-through.π¨ UI Changes