Skip to content
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7cbc74a
Basic working emojicode replacement
holmesworcester Mar 20, 2025
9be1478
WIP with stubbed functions and instructions for writing tests
holmesworcester Mar 21, 2025
41d7a0d
replace as you type works, without it being over eager on :s as you t…
holmesworcester Mar 21, 2025
277b2f9
Emojis replace on typing, send. Not over-eager. Jest tests pass.
holmesworcester Mar 21, 2025
8f31d47
fixes bug where hearts weren't red in messages
holmesworcester Mar 21, 2025
712b990
adds big red hearts to the text entry field
holmesworcester Mar 21, 2025
2e61af1
adds a large emoji list
holmesworcester Mar 21, 2025
5d9e45c
Adds basic tab completion for emojicodes
holmesworcester Mar 21, 2025
9915d86
adds a nice dropdown for autocompletion
holmesworcester Mar 21, 2025
798b90f
adds some passing cypress tests
holmesworcester Mar 21, 2025
9e17571
updates CHANGELOG
holmesworcester Mar 21, 2025
ff1ae59
Emoji dropds matches layout in design for mentions
holmesworcester Mar 21, 2025
aabbae6
Increases number of emojis in dropdown and matches the designs better
holmesworcester Mar 21, 2025
f6c2754
Fixes bug where emoji dropdown did not disappear on sending message
holmesworcester Mar 21, 2025
088035c
dropdown now closes when clicking away
holmesworcester Mar 21, 2025
da11694
adds tests for emoji picker dropdown
holmesworcester Mar 21, 2025
9430c78
Adds a shout out to agiledev24 )(whose work I did not end up using be…
holmesworcester Mar 21, 2025
98999aa
Merge branch 'develop' into feat/540-emoji-codes
holmesworcester Apr 8, 2025
c2bd331
linter changes
holmesworcester Apr 8, 2025
4e15dc1
updates to avoid conflict with upstream?
holmesworcester Apr 8, 2025
2823a21
Merge branch 'develop' into feat/540-emoji-codes
holmesworcester Apr 8, 2025
8e5d649
cypress emojicode and emoji dropdown tests are working correctly
holmesworcester Apr 8, 2025
29dc4d4
updates snapshots
holmesworcester Apr 10, 2025
c71af05
Fixes breaking pageup/pagedown Cypress tests
holmesworcester Apr 10, 2025
21ddaab
Adds a storybook for messages with emojis
holmesworcester Apr 10, 2025
63b5da9
breaks out emoji dropdown to component
holmesworcester Apr 10, 2025
b5d0d8b
enter key (not just tab) autocompletes emoji
holmesworcester Apr 10, 2025
83bda79
emoji dropdown scrolls with arrow keys
holmesworcester Apr 10, 2025
056797a
renames emoji tests
holmesworcester Apr 10, 2025
4e22b02
adds test for ensuring dropdown scrolls, ensuring enter autocompletes
holmesworcester Apr 10, 2025
8b0e679
Merge remote-tracking branch 'origin/develop' into feat/540-emoji-codes
holmesworcester Apr 10, 2025
7e36e19
Updates changelog to remove redundant fixes section
holmesworcester Apr 10, 2025
d4a4176
Fixes emoji dropdown component layout in storybook
holmesworcester Apr 10, 2025
034ae8e
adds emoji dropdown to storybook
holmesworcester Apr 10, 2025
f80af1f
removes needless wait() statements
holmesworcester Apr 10, 2025
4f314ab
Update CSS class references in ChannelInput tests
holmesworcester Apr 10, 2025
d7bb1ee
Update CHANGELOG.md
holmesworcester Apr 11, 2025
2bbdbc8
Update packages/desktop/src/renderer/components/widgets/channels/Chan…
holmesworcester Apr 15, 2025
90369ff
Removes emoticons beginning with > to avoid conflict with markdown, b…
holmesworcester Apr 15, 2025
f91cc9d
Comments out emoticons whose quotes are being stripped by our linter
holmesworcester Apr 15, 2025
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
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@

* Adds sticky date markers to the chat view [#505](https://github.com/TryQuiet/quiet/issues/505)
* Adds meaningful text to date markers, like "Today", "Yesterday", "Friday", or "Nov 30, 1999" [#2745](https://github.com/TryQuiet/quiet/issues/2745)
* You can now type emoticons (<3) and emojicodes (:heart:) with tab completion and a handy dropdown. [#540](https://github.com/TryQuiet/quiet/issues/540) (thanks @agiledev24 for your initial work on this!)

### Fixes
* Fixes an issue where heart emojis were displaying all tiny, ASCII, and goth. Now our hearts are big and bright red, for vibes! [#510](https://github.com/TryQuiet/quiet/issues/510)
* Fixes back button navigation issues in user profile/edit screens ([#2570]https://github.com/TryQuiet/quiet/issues/2570)
Comment thread
holmesworcester marked this conversation as resolved.
Outdated

### Chores

* Improves speed, reliability, and documentation for Cypress tests

### Fixes

* Fixes back button navigation issues in user profile/edit screens ([#2570]https://github.com/TryQuiet/quiet/issues/2570)

## [4.0.3]

### New features
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ describe('Scroll behavior test', () => {
})

it('PageUp keydown should scroll message list up.', () => {
cy.get(messageInput).focus().type('{pageup}{pageup}{pageup}{pageup}{pageup}{pageup}{pageup}')
cy.get(messageInput).focus().type('{pageup}{pageup}{pageup}{pageup}{pageup}{pageup}{pageup}{pageup}{pageup}')

cy.get(channelContent).then($el => {
const container = $el[0]
Expand All @@ -90,8 +90,13 @@ describe('Scroll behavior test', () => {
})

it('PageDown keydown should scroll message list down.', () => {
// note that Cypress UI before/after views do not correctly display the scroll position; to see the effect for debugging purposes, insert wait statements to slow down the test
cy.get(channelContent).scrollTo(0, 0)
cy.get(messageInput).focus().type('{pagedown}{pagedown}{pagedown}{pagedown}{pagedown}{pagedown}{pagedown}')
cy.get(messageInput)
.focus()
.type(
'{pagedown}{pagedown}{pagedown}{pagedown}{pagedown}{pagedown}{pagedown}{pagedown}{pagedown}{pagedown}{pagedown}'
)
cy.get(channelContent).assertScrolledToBottom()
})

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React from 'react'
import CssBaseline from '@mui/material/CssBaseline'
import { composeStories, setGlobalConfig } from '@storybook/testing-react'
import { it, beforeEach, cy, Cypress, describe } from 'local-cypress'

import * as stories from './Channel.stories'
import { withTheme } from '../../storybook/decorators'
import { mount } from 'cypress/react18'

const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/
Cypress.on('uncaught:exception', err => {
// returning false here prevents Cypress from failing the test
if (resizeObserverLoopErrRe.test(err.message)) {
return false
}
})

// @ts-expect-error
setGlobalConfig(withTheme)

// Use SendingMessagesWithScroll story to avoid TypeScript errors in other stories
const { SendingMessagesWithScroll } = composeStories(stories)

describe('Emoji conversion in code blocks test', () => {
beforeEach(() => {
mount(
<React.Fragment>
<CssBaseline>
<SendingMessagesWithScroll />
</CssBaseline>
</React.Fragment>
)
cy.wait(0)
})

it('should NOT convert text typed inside an unclosed code fence', () => {
cy.get('[data-testid="messageInput"]').type('```Some code :) ')

// The code fence is still open (no closing triple backticks).
// So ":) " should remain literal and not become an emoji.
cy.get('[data-testid="messageInput"]').should('have.value', '```Some code :) ')
})

it('should convert text immediately after closing the code fence', () => {
cy.get('[data-testid="messageInput"]')
// Start an open code fence
.type('```Inside code block :smile:')
// Still open => :smile: remains literal
.should('have.value', '```Inside code block :smile:')
// Close the code block
.type('``` ')
// Now that fence is closed, the space after “``` ” is outside code block
// Type a known emoticon
.type(':p')
.should('have.value', '```Inside code block :smile:``` :p')
// Type punctuation => triggers conversion of :p
.type('.')

cy.get('[data-testid="messageInput"]').should('have.value', '```Inside code block :smile:``` 😛.')
})

it('should convert text typed entirely outside code fences', () => {
// Type something normal outside code block
cy.get('[data-testid="messageInput"]').type('Hello :smile: ').should('have.value', 'Hello 😄 ')
})

it('should handle multiple code fences correctly', () => {
cy.get('[data-testid="messageInput"]')
// First code fence
.type('```Block1 :)``` code between ```Block2 :heart: ')

// "Block1 :)" is inside the first code fence => no conversion
// "Block2 :heart:" is inside second code fence => no conversion yet
cy.get('[data-testid="messageInput"]').should('have.value', '```Block1 :)``` code between ```Block2 :heart: ')

// close second code fence
cy.get('[data-testid="messageInput"]').type('``` ')

// After closing the second fence, type a space + emoticon
cy.get('[data-testid="messageInput"]').type(':p ')

// Now the :p should convert to 😛 because we’re outside all fences
cy.get('[data-testid="messageInput"]').should(
'have.value',
'```Block1 :)``` code between ```Block2 :heart: ``` 😛 '
)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import React from 'react'
import CssBaseline from '@mui/material/CssBaseline'
import { composeStories, setGlobalConfig } from '@storybook/testing-react'
import { it, beforeEach, cy, Cypress, describe } from 'local-cypress'

import * as stories from './Channel.stories'
import { withTheme } from '../../storybook/decorators'
import { mount } from 'cypress/react18'
import { ArrowKeyStepper } from 'react-virtualized'

const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/
Cypress.on('uncaught:exception', err => {
if (resizeObserverLoopErrRe.test(err.message)) {
return false
}
})

// @ts-expect-error
setGlobalConfig(withTheme)

// Use SendingMessagesWithScroll story to avoid TypeScript errors in other stories
const { SendingMessagesWithScroll } = composeStories(stories)

describe('Emoji dropdown behavior', () => {
// Tests for checking that the emoji dropdown appears and disappears as expected
// - appears when typing colon + characters
// - disappears with Tab key (emoji selection)
// - disappears with mouse click (emoji selection)
// - disappears with Enter key (message sending)
// - disappears when clicking away
// - disappears when typing non-emoji pattern
beforeEach(() => {
mount(
<React.Fragment>
<CssBaseline>
<SendingMessagesWithScroll />
</CssBaseline>
</React.Fragment>
)
cy.wait(0)
})

it('should show dropdown when typing colon followed by characters', () => {
// Start typing an emoji code
cy.get('[data-testid="messageInput"]').type(':sm')

// Verify the dropdown appears
cy.get('[data-testid="emoji-dropdown"]').should('be.visible').should('contain', ':smile:')
})

it('should hide dropdown when selecting an emoji with Tab key', () => {
// Start typing an emoji code
cy.get('[data-testid="messageInput"]').type(':sm')

// Verify dropdown appears
cy.get('[data-testid="emoji-dropdown"]').should('be.visible')

// Instead of Tab, trigger the keydown event directly
cy.get('[data-testid="messageInput"]').trigger('keydown', {
key: 'Tab',
keyCode: 9,
which: 9,
code: 'Tab',
})

// Verify dropdown disappears
cy.get('[data-testid="emoji-dropdown"]').should('not.exist')
})

it('should hide dropdown when selecting an emoji with Enter key', () => {
// Start typing an emoji code
cy.get('[data-testid="messageInput"]').type(':sm')

// Verify dropdown appears
cy.get('[data-testid="emoji-dropdown"]').should('be.visible')

// Trigger the Enter keydown event directly
cy.get('[data-testid="messageInput"]').trigger('keydown', {
key: 'Enter',
keyCode: 13,
which: 13,
code: 'Enter',
})

// Verify dropdown disappears
cy.get('[data-testid="emoji-dropdown"]').should('not.exist')
})

it('should hide dropdown when clicking an emoji suggestion', () => {
// Start typing an emoji code
cy.get('[data-testid="messageInput"]').type(':sm')

// Verify dropdown appears
cy.get('[data-testid="emoji-dropdown"]').should('be.visible')

// Click the first emoji suggestion
cy.get('[data-testid="emoji-dropdown"] > div').first().click()

// Verify dropdown disappears
cy.get('[data-testid="emoji-dropdown"]').should('not.exist')

// Verify emoji was inserted (smiley emoji U+1F603)
cy.get('[data-testid="messageInput"]').should('have.value', '😃')
})

it('should hide dropdown when clicking away', () => {
// Start typing an emoji code
cy.get('[data-testid="messageInput"]').type(':sm')

// Verify dropdown appears
cy.get('[data-testid="emoji-dropdown"]').should('be.visible')

// Click away from the input
cy.get('body').click()

// Verify dropdown no longer exists in the DOM
cy.get('[data-testid="emoji-dropdown"]').should('not.exist')
})

it('should scroll dropdown when navigating with arrow keys', () => {
// Type ":h" to get a decent number of emoji suggestions without being too specific
cy.get('[data-testid="messageInput"]').type(':h')

// Verify dropdown appears
cy.get('[data-testid="emoji-dropdown"]').should('be.visible')

// Check initial scroll position
cy.get('[data-testid="emoji-dropdown"]').then($dropdown => {
const initialScrollTop = $dropdown[0].scrollTop

// Add a data attribute to track initial scroll position
$dropdown[0].setAttribute('data-initial-scroll', initialScrollTop.toString())

// Press arrow down key multiple times to navigate through suggestions
for (let i = 0; i < 8; i++) {
cy.get('[data-testid="messageInput"]').trigger('keydown', {
key: 'ArrowDown',
keyCode: 40,
which: 40,
code: 'ArrowDown',
bubbles: true,
})
}

// After key presses, verify the dropdown is still visible
cy.get('[data-testid="emoji-dropdown"]')
.should('be.visible')
.then($updatedDropdown => {
// Get the initial scroll position from the data attribute
const initialScroll = parseInt($updatedDropdown[0].getAttribute('data-initial-scroll') || '0')
const currentScrollTop = $updatedDropdown[0].scrollTop

// If scrollTop changed, scrolling occurred
expect(currentScrollTop).to.be.gte(initialScroll)
})
})
})

it('should hide dropdown when typing text that no longer matches emoji pattern', () => {
// Start typing an emoji code
cy.get('[data-testid="messageInput"]').type(':sm')

// Verify dropdown appears
cy.get('[data-testid="emoji-dropdown"]').should('be.visible')

// Type a space to break the emoji pattern
cy.get('[data-testid="messageInput"]').type(' ')

// Verify dropdown disappears
cy.get('[data-testid="emoji-dropdown"]').should('not.exist')
})
})
Loading
Loading