From dd1cdbbd41edaa302deb670eae3819e56bed311a Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Mon, 11 May 2026 13:57:30 +0800 Subject: [PATCH] refactor(web): split premium badge button semantics (#36026) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- eslint-suppressions.json | 5 -- .../(commonLayout)/account-page/index.tsx | 2 +- web/app/account/(commonLayout)/avatar.tsx | 2 +- .../app/overview/settings/index.tsx | 6 +- .../premium-badge/__tests__/index.spec.tsx | 25 +++++- .../components/base/premium-badge/index.css | 2 +- .../base/premium-badge/index.stories.tsx | 18 ++--- .../components/base/premium-badge/index.tsx | 58 +++++++++++--- .../upgrade-btn/__tests__/index.spec.tsx | 76 ++++++++++--------- .../components/billing/upgrade-btn/index.tsx | 18 +++-- .../header/__tests__/index.spec.tsx | 2 +- .../header/account-dropdown/compliance.tsx | 2 +- .../workplace-selector/index.tsx | 2 +- web/app/components/header/index.tsx | 2 +- .../components/header/license-env/index.tsx | 2 +- .../plan-badge/__tests__/index.spec.tsx | 19 ++++- .../components/header/plan-badge/index.tsx | 54 +++++++++---- .../rag-pipeline-header/publisher/popup.tsx | 4 +- .../__tests__/upgrade-modal.spec.tsx | 9 --- .../delivery-method/upgrade-modal.tsx | 9 +-- .../applied-education-content.tsx | 2 +- 21 files changed, 202 insertions(+), 117 deletions(-) diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 97d2f93a59..4adca38aa0 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -1741,11 +1741,6 @@ "count": 4 } }, - "web/app/components/billing/upgrade-btn/index.tsx": { - "ts/no-explicit-any": { - "count": 3 - } - }, "web/app/components/datasets/common/image-previewer/index.tsx": { "no-irregular-whitespace": { "count": 1 diff --git a/web/app/account/(commonLayout)/account-page/index.tsx b/web/app/account/(commonLayout)/account-page/index.tsx index 0de33a2a71..b85fb1abba 100644 --- a/web/app/account/(commonLayout)/account-page/index.tsx +++ b/web/app/account/(commonLayout)/account-page/index.tsx @@ -166,7 +166,7 @@ export default function AccountPage() { {userProfile.name} {isEducationAccount && ( - + )} diff --git a/web/app/account/(commonLayout)/avatar.tsx b/web/app/account/(commonLayout)/avatar.tsx index 3fefb8a319..ef1c4b7e8c 100644 --- a/web/app/account/(commonLayout)/avatar.tsx +++ b/web/app/account/(commonLayout)/avatar.tsx @@ -62,7 +62,7 @@ export default function AppSelector() { {userProfile.name} {isEducationAccount && ( - + )} diff --git a/web/app/components/app/overview/settings/index.tsx b/web/app/components/app/overview/settings/index.tsx index aefe339b94..f64972b1a1 100644 --- a/web/app/components/app/overview/settings/index.tsx +++ b/web/app/components/app/overview/settings/index.tsx @@ -17,7 +17,7 @@ import AppIcon from '@/app/components/base/app-icon' import AppIconPicker from '@/app/components/base/app-icon-picker' import Divider from '@/app/components/base/divider' import Input from '@/app/components/base/input' -import PremiumBadge from '@/app/components/base/premium-badge' +import { PremiumBadgeButton } from '@/app/components/base/premium-badge' import Textarea from '@/app/components/base/textarea' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' import { useModalContext } from '@/context/modal-context' @@ -395,14 +395,14 @@ const SettingsModal: FC = ({ {/* upgrade button */} {enableBilling && isFreePlan && (
- + +
)} diff --git a/web/app/components/base/premium-badge/__tests__/index.spec.tsx b/web/app/components/base/premium-badge/__tests__/index.spec.tsx index c3eb31c72e..ccbe4dfc92 100644 --- a/web/app/components/base/premium-badge/__tests__/index.spec.tsx +++ b/web/app/components/base/premium-badge/__tests__/index.spec.tsx @@ -1,5 +1,6 @@ import { render, screen } from '@testing-library/react' -import PremiumBadge from '../index' +import userEvent from '@testing-library/user-event' +import PremiumBadge, { PremiumBadgeButton } from '../index' describe('PremiumBadge', () => { it('renders with default props', () => { @@ -24,9 +25,9 @@ describe('PremiumBadge', () => { it('applies allowHover class when allowHover is true', () => { render( - + Premium - , + , ) const badge = screen.getByText('Premium') expect(badge).toBeInTheDocument() @@ -43,4 +44,22 @@ describe('PremiumBadge', () => { expect(badge).toBeInTheDocument() expect(badge).toHaveStyle('background-color: red') }) + + it('renders a static badge without button semantics', () => { + render(Premium) + + expect(screen.queryByRole('button')).not.toBeInTheDocument() + }) + + it('renders an action badge as a button', async () => { + const user = userEvent.setup() + const handleClick = vi.fn() + + render(Upgrade) + + const button = screen.getByRole('button', { name: 'Upgrade' }) + await user.click(button) + + expect(handleClick).toHaveBeenCalledTimes(1) + }) }) diff --git a/web/app/components/base/premium-badge/index.css b/web/app/components/base/premium-badge/index.css index f192a51e57..0796bf3cf6 100644 --- a/web/app/components/base/premium-badge/index.css +++ b/web/app/components/base/premium-badge/index.css @@ -1,5 +1,5 @@ @utility premium-badge { - @apply shrink-0 relative inline-flex justify-center items-center rounded-md box-border border border-transparent text-white shadow-xs hover:shadow-lg bg-origin-border overflow-hidden transition-all duration-100 ease-out; + @apply shrink-0 relative inline-flex justify-center items-center rounded-md box-border border border-transparent text-white shadow-xs hover:shadow-lg bg-origin-border overflow-hidden transition-[background-color,background-image,box-shadow] duration-100 ease-out motion-reduce:transition-none; background-clip: padding-box, border-box; } diff --git a/web/app/components/base/premium-badge/index.stories.tsx b/web/app/components/base/premium-badge/index.stories.tsx index 5f3786a2ea..a0c16c19aa 100644 --- a/web/app/components/base/premium-badge/index.stories.tsx +++ b/web/app/components/base/premium-badge/index.stories.tsx @@ -1,14 +1,12 @@ import type { Meta, StoryObj } from '@storybook/nextjs-vite' -import PremiumBadge from '.' +import PremiumBadge, { PremiumBadgeButton } from '.' const colors: Array['color']>> = ['blue', 'indigo', 'gray', 'orange'] const PremiumBadgeGallery = ({ size = 'm', - allowHover = false, }: { size?: 's' | 'm' - allowHover?: boolean }) => { return (
@@ -16,7 +14,7 @@ const PremiumBadgeGallery = ({
{colors.map(color => (
- + Premium {color} @@ -43,11 +41,9 @@ const meta = { control: 'radio', options: ['s', 'm'], }, - allowHover: { control: 'boolean' }, }, args: { size: 'm', - allowHover: false, }, tags: ['autodocs'], } satisfies Meta @@ -57,8 +53,10 @@ type Story = StoryObj export const Playground: Story = {} -export const HoverEnabled: Story = { - args: { - allowHover: true, - }, +export const Action: Story = { + render: () => ( + {}}> + Upgrade + + ), } diff --git a/web/app/components/base/premium-badge/index.tsx b/web/app/components/base/premium-badge/index.tsx index 42ce158606..55628ea0e9 100644 --- a/web/app/components/base/premium-badge/index.tsx +++ b/web/app/components/base/premium-badge/index.tsx @@ -1,8 +1,7 @@ import type { VariantProps } from 'class-variance-authority' -import type { CSSProperties, ReactNode } from 'react' +import type { ButtonHTMLAttributes, CSSProperties, ReactNode } from 'react' import { cn } from '@langgenius/dify-ui/cn' import { cva } from 'class-variance-authority' -import * as React from 'react' import { Highlight } from '@/app/components/base/icons/src/public/common' const PremiumBadgeVariants = cva( @@ -38,31 +37,66 @@ type PremiumBadgeProps = { color?: 'blue' | 'indigo' | 'gray' | 'orange' allowHover?: boolean styleCss?: CSSProperties + className?: string children?: ReactNode -} & React.HTMLAttributes & VariantProps +} & VariantProps -const PremiumBadge: React.FC = ({ +type PremiumBadgeButtonProps = Omit, 'color'> & Omit & { + style?: CSSProperties +} + +function BadgeHighlight({ size }: { size?: PremiumBadgeProps['size'] }) { + return ( +