diff --git a/web/app/components/apps/__tests__/app-card.spec.tsx b/web/app/components/apps/__tests__/app-card.spec.tsx
index c243efa1ea8..b3ed64d22bc 100644
--- a/web/app/components/apps/__tests__/app-card.spec.tsx
+++ b/web/app/components/apps/__tests__/app-card.spec.tsx
@@ -682,9 +682,13 @@ describe('AppCard', () => {
})
it('should star the app from the card action without navigating', async () => {
+ const user = userEvent.setup()
render()
- fireEvent.click(screen.getByRole('button', { name: 'app.studio.starApp' }))
+ const starToggle = screen.getByRole('button', { name: 'app.studio.starApp' })
+ expect(starToggle).toHaveAttribute('aria-pressed', 'false')
+
+ await user.click(starToggle)
await waitFor(() => {
expect(mockStarAppMutation).toHaveBeenCalledWith({
@@ -695,10 +699,14 @@ describe('AppCard', () => {
})
it('should unstar the app from the filled star action', async () => {
+ const user = userEvent.setup()
const starredApp = createMockApp({ is_starred: true })
render()
- fireEvent.click(screen.getByRole('button', { name: 'app.studio.unstarApp' }))
+ const starToggle = screen.getByRole('button', { name: 'app.studio.starApp' })
+ expect(starToggle).toHaveAttribute('aria-pressed', 'true')
+
+ await user.click(starToggle)
await waitFor(() => {
expect(mockUnstarAppMutation).toHaveBeenCalledWith({
@@ -724,13 +732,15 @@ describe('AppCard', () => {
})
it('should show edit option when dropdown menu is opened', async () => {
+ const user = userEvent.setup()
render()
- fireEvent.click(getOperationsTrigger())
+ await user.click(getOperationsTrigger())
await waitFor(() => {
expect(screen.getByText('app.editApp')).toBeInTheDocument()
})
+ expect(mockPush).not.toHaveBeenCalled()
})
it('should show duplicate option when dropdown menu is opened', async () => {
diff --git a/web/app/components/apps/app-card/action-bar/index.tsx b/web/app/components/apps/app-card/action-bar/index.tsx
index e96a19e5ea7..157744975c5 100644
--- a/web/app/components/apps/app-card/action-bar/index.tsx
+++ b/web/app/components/apps/app-card/action-bar/index.tsx
@@ -4,7 +4,7 @@ import type {
AppPartial,
EnvironmentVariableItemResponse,
} from '@dify/contracts/api/console/apps/types.gen'
-import type { FormEventHandler, MouseEvent } from 'react'
+import type { FormEventHandler } from 'react'
import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal'
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
import { zIconType } from '@dify/contracts/api/console/apps/zod.gen'
@@ -24,7 +24,9 @@ import {
DropdownMenuTrigger,
} from '@langgenius/dify-ui/dropdown-menu'
import { Field, FieldControl, FieldLabel } from '@langgenius/dify-ui/field'
+import { IconButton } from '@langgenius/dify-ui/icon-button'
import { toast } from '@langgenius/dify-ui/toast'
+import { Toggle } from '@langgenius/dify-ui/toggle'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useMutation, useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
@@ -311,13 +313,10 @@ export const AppCardActionBar = memo(
}
const handleToggleStar = useCallback(
- (e: MouseEvent) => {
- e.stopPropagation()
- e.preventDefault()
-
+ (pressed: boolean) => {
if (isTogglingStar) return
- const mutateStar = app.is_starred ? unstarApp : starApp
+ const mutateStar = pressed ? starApp : unstarApp
try {
mutateStar(
{ params: { app_id: app.id } },
@@ -338,7 +337,7 @@ export const AppCardActionBar = memo(
)
}
},
- [app.id, app.is_starred, isTogglingStar, starApp, t, unstarApp],
+ [app.id, isTogglingStar, starApp, t, unstarApp],
)
const shouldShowEditOption = appACLCapabilities.canEdit
@@ -359,6 +358,7 @@ export const AppCardActionBar = memo(
const starActionLabel = app.is_starred
? t(($) => $['studio.unstarApp'], { ns: 'app' })
: t(($) => $['studio.starApp'], { ns: 'app' })
+ const starToggleLabel = t(($) => $['studio.starApp'], { ns: 'app' })
return (
<>
@@ -374,21 +374,23 @@ export const AppCardActionBar = memo(
-
-
+ onPressedChange={handleToggleStar}
+ render={
+
+
+
+ }
+ />
}
/>
{starActionLabel}
@@ -400,33 +402,32 @@ export const AppCardActionBar = memo(
onOpenChange={setIsOperationsMenuOpen}
>
$['operation.exporting'], { ns: 'common' })
- : t(($) => $['operation.moreActionsFor'], {
- ns: 'common',
- name: app.name,
- })
+ render={
+ $['operation.exporting'], { ns: 'common' })
+ : t(($) => $['operation.moreActionsFor'], {
+ ns: 'common',
+ name: app.name,
+ })
+ }
+ disabled={isExporting}
+ className="data-popup-open:bg-state-base-hover"
+ >
+
+
}
- disabled={isExporting}
- className={cn(
- 'flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg hover:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden disabled:cursor-not-allowed data-popup-open:bg-state-base-hover',
- )}
- onClick={(e) => {
- e.stopPropagation()
- e.preventDefault()
- }}
- >
-
-
+ />