mirror of
https://github.com/langgenius/dify.git
synced 2026-09-19 10:11:30 +08:00
refactor(web): migrate app card icon controls (#40652)
This commit is contained in:
@@ -682,9 +682,13 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
it('should star the app from the card action without navigating', async () => {
|
||||
const user = userEvent.setup()
|
||||
render(<AppCard app={mockApp} />)
|
||||
|
||||
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(<AppCard app={starredApp} />)
|
||||
|
||||
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(<AppCard app={mockApp} />)
|
||||
|
||||
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 () => {
|
||||
|
||||
@@ -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<HTMLButtonElement>) => {
|
||||
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(
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
render={
|
||||
<button
|
||||
type="button"
|
||||
aria-label={starActionLabel}
|
||||
<Toggle
|
||||
pressed={app.is_starred}
|
||||
disabled={isTogglingStar}
|
||||
className="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 disabled:opacity-70"
|
||||
onClick={handleToggleStar}
|
||||
>
|
||||
<StarIcon
|
||||
aria-hidden
|
||||
className={cn(
|
||||
app.is_starred ? 'text-text-warning-secondary' : 'text-text-tertiary',
|
||||
'size-4.5',
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
onPressedChange={handleToggleStar}
|
||||
render={
|
||||
<IconButton
|
||||
size="lg"
|
||||
aria-label={starToggleLabel}
|
||||
className="group disabled:opacity-70"
|
||||
>
|
||||
<StarIcon
|
||||
aria-hidden
|
||||
className="size-4.5 text-text-tertiary group-data-pressed:text-text-warning-secondary"
|
||||
/>
|
||||
</IconButton>
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<TooltipContent>{starActionLabel}</TooltipContent>
|
||||
@@ -400,33 +402,32 @@ export const AppCardActionBar = memo(
|
||||
onOpenChange={setIsOperationsMenuOpen}
|
||||
>
|
||||
<DropdownMenuTrigger
|
||||
aria-label={
|
||||
isExporting
|
||||
? t(($) => $['operation.exporting'], { ns: 'common' })
|
||||
: t(($) => $['operation.moreActionsFor'], {
|
||||
ns: 'common',
|
||||
name: app.name,
|
||||
})
|
||||
render={
|
||||
<IconButton
|
||||
size="lg"
|
||||
aria-label={
|
||||
isExporting
|
||||
? t(($) => $['operation.exporting'], { ns: 'common' })
|
||||
: t(($) => $['operation.moreActionsFor'], {
|
||||
ns: 'common',
|
||||
name: app.name,
|
||||
})
|
||||
}
|
||||
disabled={isExporting}
|
||||
className="data-popup-open:bg-state-base-hover"
|
||||
>
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'size-4.5 text-text-tertiary',
|
||||
isExporting
|
||||
? 'i-ri-loader-2-line animate-spin motion-reduce:animate-none'
|
||||
: 'i-ri-more-fill',
|
||||
)}
|
||||
/>
|
||||
</IconButton>
|
||||
}
|
||||
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()
|
||||
}}
|
||||
>
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'h-4.5 w-4.5 text-text-tertiary',
|
||||
isExporting
|
||||
? 'i-ri-loader-2-line animate-spin motion-reduce:animate-none'
|
||||
: 'i-ri-more-fill',
|
||||
)}
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
/>
|
||||
<DropdownMenuContent
|
||||
placement="bottom-end"
|
||||
sideOffset={4}
|
||||
|
||||
Reference in New Issue
Block a user