From f991644989568463ac6342f1fcbf8a9f5c8e4a5f Mon Sep 17 00:00:00 2001 From: yyh Date: Wed, 4 Mar 2026 19:26:54 +0800 Subject: [PATCH] refactor(pricing): migrate to base ui dialog and extract category types --- .../billing/pricing-modal-flow.test.tsx | 19 +---- .../billing/pricing/__tests__/footer.spec.tsx | 2 +- .../billing/pricing/__tests__/index.spec.tsx | 8 +- web/app/components/billing/pricing/footer.tsx | 7 +- web/app/components/billing/pricing/header.tsx | 12 +-- web/app/components/billing/pricing/index.tsx | 73 +++++++++---------- .../plan-switcher/__tests__/index.spec.tsx | 2 +- .../billing/pricing/plan-switcher/index.tsx | 2 +- web/app/components/billing/pricing/types.ts | 6 ++ web/eslint-suppressions.json | 10 --- 10 files changed, 57 insertions(+), 84 deletions(-) create mode 100644 web/app/components/billing/pricing/types.ts diff --git a/web/__tests__/billing/pricing-modal-flow.test.tsx b/web/__tests__/billing/pricing-modal-flow.test.tsx index 6b8fb57f83..7326ee3559 100644 --- a/web/__tests__/billing/pricing-modal-flow.test.tsx +++ b/web/__tests__/billing/pricing-modal-flow.test.tsx @@ -295,24 +295,7 @@ describe('Pricing Modal Flow', () => { }) }) - // ─── 6. Close Handling ─────────────────────────────────────────────────── - describe('Close handling', () => { - it('should call onCancel when pressing ESC key', () => { - render() - - // ahooks useKeyPress listens on document for keydown events - document.dispatchEvent(new KeyboardEvent('keydown', { - key: 'Escape', - code: 'Escape', - keyCode: 27, - bubbles: true, - })) - - expect(onCancel).toHaveBeenCalledTimes(1) - }) - }) - - // ─── 7. Pricing URL ───────────────────────────────────────────────────── + // ─── 6. Pricing URL ───────────────────────────────────────────────────── describe('Pricing page URL', () => { it('should render pricing link with correct URL', () => { render() diff --git a/web/app/components/billing/pricing/__tests__/footer.spec.tsx b/web/app/components/billing/pricing/__tests__/footer.spec.tsx index 7ef78180de..762d0ad211 100644 --- a/web/app/components/billing/pricing/__tests__/footer.spec.tsx +++ b/web/app/components/billing/pricing/__tests__/footer.spec.tsx @@ -1,7 +1,7 @@ import { render, screen } from '@testing-library/react' import * as React from 'react' -import { CategoryEnum } from '..' import Footer from '../footer' +import { CategoryEnum } from '../types' vi.mock('next/link', () => ({ default: ({ children, href, className, target }: { children: React.ReactNode, href: string, className?: string, target?: string }) => ( diff --git a/web/app/components/billing/pricing/__tests__/index.spec.tsx b/web/app/components/billing/pricing/__tests__/index.spec.tsx index 54813ae0d7..1be2234cf9 100644 --- a/web/app/components/billing/pricing/__tests__/index.spec.tsx +++ b/web/app/components/billing/pricing/__tests__/index.spec.tsx @@ -74,15 +74,11 @@ describe('Pricing', () => { }) describe('Props', () => { - it('should allow switching categories and handle esc key', () => { - const handleCancel = vi.fn() - render() + it('should allow switching categories', () => { + render() fireEvent.click(screen.getByText('billing.plansCommon.self')) expect(screen.queryByRole('switch')).not.toBeInTheDocument() - - fireEvent.keyDown(window, { key: 'Escape', keyCode: 27 }) - expect(handleCancel).toHaveBeenCalled() }) }) diff --git a/web/app/components/billing/pricing/footer.tsx b/web/app/components/billing/pricing/footer.tsx index 7569ccaa76..6a213eca00 100644 --- a/web/app/components/billing/pricing/footer.tsx +++ b/web/app/components/billing/pricing/footer.tsx @@ -1,10 +1,9 @@ -import type { Category } from '.' -import { RiArrowRightUpLine } from '@remixicon/react' +import type { Category } from './types' import Link from 'next/link' import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' -import { CategoryEnum } from '.' +import { CategoryEnum } from './types' type FooterProps = { pricingPageURL: string @@ -34,7 +33,7 @@ const Footer = ({ > {t('plansCommon.comparePlanAndFeatures', { ns: 'billing' })} - + diff --git a/web/app/components/billing/pricing/header.tsx b/web/app/components/billing/pricing/header.tsx index b130f23981..e5107e5677 100644 --- a/web/app/components/billing/pricing/header.tsx +++ b/web/app/components/billing/pricing/header.tsx @@ -1,6 +1,6 @@ -import { RiCloseLine } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' +import { DialogDescription, DialogTitle } from '@/app/components/base/ui/dialog' import Button from '../../base/button' import DifyLogo from '../../base/logo/dify-logo' @@ -20,19 +20,19 @@ const Header = ({
- + {t('plansCommon.title.plans', { ns: 'billing' })} - + -

+ {t('plansCommon.title.description', { ns: 'billing' })} -

+ diff --git a/web/app/components/billing/pricing/index.tsx b/web/app/components/billing/pricing/index.tsx index 2b58158146..21ca7d0a6f 100644 --- a/web/app/components/billing/pricing/index.tsx +++ b/web/app/components/billing/pricing/index.tsx @@ -1,9 +1,9 @@ 'use client' import type { FC } from 'react' -import { useKeyPress } from 'ahooks' +import type { Category } from './types' import * as React from 'react' import { useState } from 'react' -import { createPortal } from 'react-dom' +import { Dialog, DialogContent } from '@/app/components/base/ui/dialog' import { useAppContext } from '@/context/app-context' import { useGetPricingPageLanguage } from '@/context/i18n' import { useProviderContext } from '@/context/provider-context' @@ -13,13 +13,7 @@ import Header from './header' import PlanSwitcher from './plan-switcher' import { PlanRange } from './plan-switcher/plan-range-switcher' import Plans from './plans' - -export enum CategoryEnum { - CLOUD = 'cloud', - SELF = 'self', -} - -export type Category = CategoryEnum.CLOUD | CategoryEnum.SELF +import { CategoryEnum } from './types' type PricingProps = { onCancel: () => void @@ -33,42 +27,47 @@ const Pricing: FC = ({ const [planRange, setPlanRange] = React.useState(PlanRange.monthly) const [currentCategory, setCurrentCategory] = useState(CategoryEnum.CLOUD) const canPay = isCurrentWorkspaceManager - useKeyPress(['esc'], onCancel) const pricingPageLanguage = useGetPricingPageLanguage() const pricingPageURL = pricingPageLanguage ? `https://dify.ai/${pricingPageLanguage}/pricing#plans-and-features` : 'https://dify.ai/pricing#plans-and-features' - return createPortal( -
e.stopPropagation()} + return ( + { + if (!open) + onCancel() + }} > -
-
- + +
+
+ +
+
+ + +
+
+ +
-
- - -
-
, - document.body, + +
) } export default React.memo(Pricing) diff --git a/web/app/components/billing/pricing/plan-switcher/__tests__/index.spec.tsx b/web/app/components/billing/pricing/plan-switcher/__tests__/index.spec.tsx index 51e074e305..e6b8d8430f 100644 --- a/web/app/components/billing/pricing/plan-switcher/__tests__/index.spec.tsx +++ b/web/app/components/billing/pricing/plan-switcher/__tests__/index.spec.tsx @@ -1,6 +1,6 @@ import { fireEvent, render, screen } from '@testing-library/react' import * as React from 'react' -import { CategoryEnum } from '../../index' +import { CategoryEnum } from '../../types' import PlanSwitcher from '../index' import { PlanRange } from '../plan-range-switcher' diff --git a/web/app/components/billing/pricing/plan-switcher/index.tsx b/web/app/components/billing/pricing/plan-switcher/index.tsx index 60e0bdf8de..be0bd3e2b1 100644 --- a/web/app/components/billing/pricing/plan-switcher/index.tsx +++ b/web/app/components/billing/pricing/plan-switcher/index.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import type { Category } from '../index' +import type { Category } from '../types' import type { PlanRange } from './plan-range-switcher' import * as React from 'react' import { useTranslation } from 'react-i18next' diff --git a/web/app/components/billing/pricing/types.ts b/web/app/components/billing/pricing/types.ts new file mode 100644 index 0000000000..843d98e6f5 --- /dev/null +++ b/web/app/components/billing/pricing/types.ts @@ -0,0 +1,6 @@ +export enum CategoryEnum { + CLOUD = 'cloud', + SELF = 'self', +} + +export type Category = CategoryEnum.CLOUD | CategoryEnum.SELF diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json index aa60a9c8ea..5f15dc8ba9 100644 --- a/web/eslint-suppressions.json +++ b/web/eslint-suppressions.json @@ -2964,16 +2964,6 @@ "count": 2 } }, - "app/components/billing/pricing/header.tsx": { - "tailwindcss/enforce-consistent-class-order": { - "count": 1 - } - }, - "app/components/billing/pricing/index.tsx": { - "react-refresh/only-export-components": { - "count": 1 - } - }, "app/components/billing/pricing/plan-switcher/plan-range-switcher.tsx": { "react-refresh/only-export-components": { "count": 1