From 9bd5c2f8ec55928d4ecdb0f4c066895ae009f770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Fri, 24 Apr 2026 15:59:37 +0800 Subject: [PATCH] fix: app icon could not only change background (#35537) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- eslint-suppressions.json | 5 ----- .../app-icon-picker/__tests__/index.spec.tsx | 19 ++++++++++++++-- .../components/base/app-icon-picker/index.tsx | 14 +++++++++++- .../components/base/emoji-picker/Inner.tsx | 11 ++++++---- .../emoji-picker/__tests__/Inner.spec.tsx | 9 ++++++++ .../create-app-modal/__tests__/index.spec.tsx | 22 ++++++------------- .../explore/create-app-modal/index.tsx | 6 ++--- 7 files changed, 56 insertions(+), 30 deletions(-) diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 943d38878e..0da7515286 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -1080,11 +1080,6 @@ "count": 1 } }, - "web/app/components/base/emoji-picker/Inner.tsx": { - "react/set-state-in-effect": { - "count": 1 - } - }, "web/app/components/base/emoji-picker/index.tsx": { "no-restricted-imports": { "count": 1 diff --git a/web/app/components/base/app-icon-picker/__tests__/index.spec.tsx b/web/app/components/base/app-icon-picker/__tests__/index.spec.tsx index 07dd809f41..7f452e64e9 100644 --- a/web/app/components/base/app-icon-picker/__tests__/index.spec.tsx +++ b/web/app/components/base/app-icon-picker/__tests__/index.spec.tsx @@ -1,3 +1,4 @@ +import type { ComponentProps } from 'react' import type { Area } from 'react-easy-crop' import type { ImageFile } from '@/types/app' import { fireEvent, render, screen, waitFor } from '@testing-library/react' @@ -122,11 +123,11 @@ describe('AppIconPicker', () => { }) } - const renderPicker = () => { + const renderPicker = (props: Partial> = {}) => { const onSelect = vi.fn() const onClose = vi.fn() - const { container } = render() + const { container } = render() return { onSelect, onClose, container } } @@ -220,6 +221,20 @@ describe('AppIconPicker', () => { expect(onSelect).not.toHaveBeenCalled() }) + + it('should submit the initial emoji when provided', async () => { + const { onSelect } = renderPicker({ initialEmoji: { icon: 'rabbit', background: '#E4FBCC' } }) + + await userEvent.click(screen.getByText(/ok/i)) + + await waitFor(() => { + expect(onSelect).toHaveBeenCalledWith({ + type: 'emoji', + icon: 'rabbit', + background: '#E4FBCC', + }) + }) + }) }) describe('Image Upload', () => { diff --git a/web/app/components/base/app-icon-picker/index.tsx b/web/app/components/base/app-icon-picker/index.tsx index 77bc0cd434..64a88f16e1 100644 --- a/web/app/components/base/app-icon-picker/index.tsx +++ b/web/app/components/base/app-icon-picker/index.tsx @@ -34,12 +34,17 @@ export type AppIconSelection = AppIconEmojiSelection | AppIconImageSelection type AppIconPickerProps = { onSelect?: (payload: AppIconSelection) => void onClose?: () => void + initialEmoji?: { + icon: string + background?: string | null + } className?: string } const AppIconPicker: FC = ({ onSelect, onClose, + initialEmoji, className, }) => { const { t } = useTranslation() @@ -138,7 +143,14 @@ const AppIconPicker: FC = ({ )} - {activeTab === 'emoji' && } + {activeTab === 'emoji' && ( + + )} {activeTab === 'image' && } diff --git a/web/app/components/base/emoji-picker/Inner.tsx b/web/app/components/base/emoji-picker/Inner.tsx index e2595c5efb..36a98f7dd1 100644 --- a/web/app/components/base/emoji-picker/Inner.tsx +++ b/web/app/components/base/emoji-picker/Inner.tsx @@ -45,20 +45,21 @@ type IEmojiPickerInnerProps = { } const EmojiPickerInner: FC = ({ + emoji, + background, onSelect, className, }) => { const { categories } = data as EmojiMartData - const [selectedEmoji, setSelectedEmoji] = useState('') - const [selectedBackground, setSelectedBackground] = useState(backgroundColors[0]) - const [showStyleColors, setShowStyleColors] = useState(false) + const [selectedEmoji, setSelectedEmoji] = useState(emoji || '') + const [selectedBackground, setSelectedBackground] = useState(background || backgroundColors[0]) + const [showStyleColors, setShowStyleColors] = useState(!!emoji) const [searchedEmojis, setSearchedEmojis] = useState([]) const [isSearching, setIsSearching] = useState(false) React.useEffect(() => { if (selectedEmoji) { - setShowStyleColors(true) /* v8 ignore next 2 - @preserve */ if (selectedBackground) onSelect?.(selectedEmoji, selectedBackground) @@ -105,6 +106,7 @@ const EmojiPickerInner: FC = ({ className="inline-flex h-10 w-10 items-center justify-center rounded-lg" onClick={() => { setSelectedEmoji(emoji) + setShowStyleColors(true) }} >
@@ -130,6 +132,7 @@ const EmojiPickerInner: FC = ({ className="inline-flex h-10 w-10 items-center justify-center rounded-lg" onClick={() => { setSelectedEmoji(emoji) + setShowStyleColors(true) }} >
diff --git a/web/app/components/base/emoji-picker/__tests__/Inner.spec.tsx b/web/app/components/base/emoji-picker/__tests__/Inner.spec.tsx index f0cf3091d7..41683d7af3 100644 --- a/web/app/components/base/emoji-picker/__tests__/Inner.spec.tsx +++ b/web/app/components/base/emoji-picker/__tests__/Inner.spec.tsx @@ -45,6 +45,15 @@ describe('EmojiPickerInner', () => { expect(screen.getByText('food'))!.toBeInTheDocument() expect(screen.getByPlaceholderText('Search emojis...'))!.toBeInTheDocument() }) + + it('initializes selected emoji and background when provided', async () => { + render() + + expect(screen.getByText('Choose Style'))!.toBeInTheDocument() + await waitFor(() => { + expect(mockOnSelect).toHaveBeenCalledWith('rabbit', '#E4FBCC') + }) + }) }) describe('User Interactions', () => { diff --git a/web/app/components/explore/create-app-modal/__tests__/index.spec.tsx b/web/app/components/explore/create-app-modal/__tests__/index.spec.tsx index 5a14807478..882dc8f9a0 100644 --- a/web/app/components/explore/create-app-modal/__tests__/index.spec.tsx +++ b/web/app/components/explore/create-app-modal/__tests__/index.spec.tsx @@ -359,7 +359,7 @@ describe('CreateAppModal', () => { } }) - it('should reset emoji icon to initial props when picker is cancelled', async () => { + it('should allow changing only the background for the current emoji icon', async () => { vi.useFakeTimers() try { const { onConfirm } = await setup({ @@ -370,22 +370,14 @@ describe('CreateAppModal', () => { fireEvent.click(getAppIconTrigger()) - const categoryLabel = screen.getByText('people') - const emojiGrid = categoryLabel.nextElementSibling - const clickableEmojiWrapper = emojiGrid?.firstElementChild - if (!(clickableEmojiWrapper instanceof HTMLElement)) - throw new Error('Failed to locate emoji wrapper') - fireEvent.click(clickableEmojiWrapper) + const colorOption = Array.from(document.querySelectorAll('[style^="background:"]')) + .find(element => element.getAttribute('style')?.includes('#E4FBCC')) + if (!(colorOption instanceof HTMLElement) || !(colorOption.parentElement instanceof HTMLElement)) + throw new Error('Failed to locate background color option') + fireEvent.click(colorOption.parentElement) fireEvent.click(screen.getByRole('button', { name: 'app.iconPicker.ok' })) - expect(screen.queryByRole('button', { name: 'app.iconPicker.cancel' })).not.toBeInTheDocument() - - fireEvent.click(getAppIconTrigger()) - fireEvent.click(screen.getByRole('button', { name: 'app.iconPicker.cancel' })) - - expect(screen.queryByRole('button', { name: 'app.iconPicker.cancel' })).not.toBeInTheDocument() - fireEvent.click(screen.getByRole('button', { name: /common\.operation\.create/ })) await act(async () => { vi.advanceTimersByTime(300) @@ -396,7 +388,7 @@ describe('CreateAppModal', () => { expect(payload).toMatchObject({ icon_type: 'emoji', icon: '🤖', - icon_background: '#FFEAD5', + icon_background: '#E4FBCC', }) } finally { diff --git a/web/app/components/explore/create-app-modal/index.tsx b/web/app/components/explore/create-app-modal/index.tsx index ebe5a79a16..a7c9e06655 100644 --- a/web/app/components/explore/create-app-modal/index.tsx +++ b/web/app/components/explore/create-app-modal/index.tsx @@ -206,14 +206,14 @@ const CreateAppModal = ({ {showAppIconPicker && ( { setAppIcon(payload) setShowAppIconPicker(false) }} onClose={() => { - setAppIcon(appIconType === 'image' - ? { type: 'image' as const, url: appIconUrl, fileId: _appIcon } - : { type: 'emoji' as const, icon: _appIcon, background: appIconBackground }) setShowAppIconPicker(false) }} />