Skip to content

epic: Migrate all Lit-Element components to React with BAIModal #5364

@inureyes

Description

@inureyes

Epic: Full React Migration of Lit-Element Dialog Components

Overview

This epic tracks the complete migration of all backend-ai-dialog (Lit-Element + mwc-dialog) usages to React components using BAIModal from the backend.ai-ui package. Unlike the property-mapping approach in #5361, this epic covers full React migration of each parent Lit component that uses backend-ai-dialog.

Background

  • backend-ai-dialog wraps Material Web Component's mwc-dialog — a library no longer actively maintained
  • BAIModal (React + Ant Design) is already production-ready with draggable support, theme integration, and z-index management
  • The reactToWebComponent bridge pattern is well-established (6 components already migrated: SignupModal, TOTPActivateModal, ResetPasswordRequired, CopyableCodeText, SourceCodeView, SignoutModal)
  • The backend.ai-ui package provides 90+ React components covering all MWC equivalents (BAIButton, BAISelect, BAICard, BAITable, BAIFlex, BAIText, etc.)

Ultimate Goal: Full Lit Dependency Removal

This epic is a critical step toward the ultimate goal of removing all Lit-Element dependencies from the codebase. The dialog migration serves as the primary driver because backend-ai-dialog is the most cross-cutting Lit component.

Current Lit/legacy dependency footprint (28 files in src/components/):

Dependency Packages Files Using
Lit core lit, @lit/reactive-element, lit-translate 28 files
Material Web Components 24 @material/mwc-* packages (v0.27, unmaintained) 7 files
Vaadin 9 @vaadin/* packages 1 file (backend-ai-project-switcher.ts)

Remaining Lit components after this epic (not in scope, tracked separately):

Component Lit Dependencies Notes
backend-ai-webui.ts lit, mwc-drawer, mwc-top-app-bar-fixed App shell — migrate last
backend-ai-page.ts lit Page routing — migrate with app shell
backend-ai-edu-applauncher.ts lit, mwc-icon-button Education app launcher variant
backend-ai-serving-view.ts lit Model serving view
backend-ai-project-switcher.ts lit, @vaadin/combo-box Project switcher widget
lablup-notification.ts lit, mwc-snackbar Notification system
lablup-codemirror.ts lit Code editor wrapper
lablup-expansion.ts lit Collapsible panel
lablup-loading-dots.ts lit Loading indicator
backend-ai-indicator.ts lit, mwc-linear-progress Progress indicator
backend-ai-indicator-pool.ts lit Indicator management
backend-ai-tasker.ts lit Background task manager
backend-ai-metadata-store.ts lit Metadata state store
backend-ai-settings-store.ts lit Settings state store
backend-ai-common-utils.ts lit Shared utilities
backend-ai-painkiller.ts lit Error message handler
backend-ai-error-view.ts lit Error page view
backend-ai-general-styles.ts lit Shared CSS styles
backend-ai-webui-styles.ts lit WebUI-specific styles
paper-color.ts lit Color definitions

After Phases 0–4 complete, the 8 dialog-related Lit components will be removed. Phases 5–7 then address the remaining Lit components, ultimately enabling removal of:

  • lit (v3.3.2)
  • @lit/reactive-element (v2.1.2)
  • lit-translate (v2.0.1)
  • All 24 @material/mwc-* packages (v0.27.0)
  • All 9 @vaadin/* packages (v24.9.10)
  • Rollup-based Lit build pipeline (rollup.config.ts)

Each sub-issue in this epic should therefore ensure the complete removal of its Lit source file — not just dialog replacement but full component migration to React.

Scope

Components to migrate (8 files, 20+ dialog instances):

Component Dialogs Complexity Phase
backend-ai-splash.ts 1 (splash-panel) Low 1
backend-ai-email-verification-view.ts 2 (success, fail) Low 1
backend-ai-change-forgot-password-view.ts 2 (password, fail) Low 1
lablup-terms-of-service.ts 1 (TOS dialog) Medium 2
backend-ai-signup.ts 3 (signup, block, email-sent) Medium 2
backend-ai-login.ts 3 (login, block, help) High 3
backend-ai-app-launcher.ts 10 (app, ssh, tensorboard, vnc, xrdp, vscode, tcp, terminal-guide, confirmation, argument) Extreme 4
backend-ai-edu-applauncher.ts 0 (delegates to app-launcher) Moderate 4
backend-ai-app.ts + navigation in backend-ai-webui.ts N/A (routing logic) High 5
Plugin system in backend-ai-webui.ts N/A (plugin loading) High 5
backend-ai-webui.ts (app shell) + 17 remaining Lit utilities N/A (root component) Extreme 6

Implementation Rules

All sub-issues in this epic MUST follow these rules. These rules ensure consistency, quality, and compatibility across all migrated components.

1. React Component Structure

  • Add 'use memo' directive at the top of every new component function body for React Compiler optimization. Never remove or rename existing 'use memo' directives.
  • Define a Props interface extending BAIModalProps when wrapping BAIModal:
    interface MyModalProps extends BAIModalProps {
      onRequestClose: () => void;
      // component-specific props
    }
  • All variable names must start with lowercase (camelCase). PascalCase is only for component names, types, and interfaces.
  • Use discriminated unions for component variants when a single component handles multiple dialog states.

2. BAI Component Mandatory Usage

Always prefer BAI components over raw Ant Design equivalents. This is a blocking requirement.

Instead of Use
Modal from antd BAIModal from backend.ai-ui
Button from antd BAIButton from backend.ai-ui
Typography.Text from antd BAIText from backend.ai-ui
Flex from antd BAIFlex from backend.ai-ui
Select from antd (when possible) BAISelect from backend.ai-ui
Manual useState for loading BAIButton with action prop (handles loading automatically)
console.log / console.error useBAILogger hook

Ant Design components (Input, Form, Checkbox, Collapse, Carousel, Dropdown, etc.) are acceptable when no BAI equivalent exists.

3. reactToWebComponent Bridge Pattern

Every migrated component must be registered as a web component for backward compatibility with Lit parent components that haven't been migrated yet.

Registration (in react/src/index.tsx):

const MyModal = React.lazy(() => import('./components/MyModal'));

customElements.define(
  'backend-ai-react-my-modal',
  reactToWebComponent((props) => (
    <DefaultProviders {...props}>
      <MyModalInWebComponent {...props} />
    </DefaultProviders>
  )),
);

Naming convention: backend-ai-react-{kebab-case-name}

Props communication (Lit → React):

  • Pass props as JSON via value attribute: value="${JSON.stringify({...})}"
  • Parse in React via useWebComponentInfo<T>() hook

Event communication (React → Lit):

  • Use props.dispatchEvent('eventName', detail) to send events to Lit parent
  • Lit parent listens via @eventName="${handler}"

Popups and dropdowns: All popups must render inside shadowRoot. This is handled automatically by DefaultProviders — do not override getPopupContainer.

4. State Management

  • Dialog open/close: Always use declarative open prop, never imperative show()/hide().
    // ✅ Correct
    const [isOpen, setIsOpen] = useState(false);
    <BAIModal open={isOpen} onCancel={() => setIsOpen(false)} />
    
    // ❌ Wrong — no imperative refs for open/close
    dialogRef.current.show();
  • Form state: Use Ant Design Form with Form.useForm(). Do not manage form fields in individual useState calls.
  • Global state: Use Recoil atoms when state needs to be shared across components. Use React Context for UI-only state scoped to a subtree.
  • Fetch/mutation: Use useTanMutation for API calls, or useBAISignedRequestWithPromise for Backend.AI signed requests. Avoid raw fetch.

5. Modal Lifecycle

  • Wrap modals with BAIUnmountAfterClose when they contain forms, to ensure proper state cleanup:
    <BAIUnmountAfterClose open={isOpen}>
      <BAIModal open={isOpen} onCancel={...}>
        <Form form={form}>...</Form>
      </BAIModal>
    </BAIUnmountAfterClose>
  • Use destroyOnHidden (or destroyOnClose) prop on BAIModal when the modal content should be fully unmounted on close.
  • Use afterClose callback for post-close navigation or cleanup.

6. Internationalization (i18n)

  • Use react-i18next useTranslation hook: const { t } = useTranslation();
  • Reuse existing translation keys from resources/i18n/ when the same text exists in the Lit version. Do not create duplicate keys.
  • For new strings, follow the key convention: category.subcategory.KeyName (leaf keys start with Uppercase).
  • Never hard-code user-facing text. All labels, messages, errors, tooltips must use t().
  • Verify translations exist for all supported languages (en, ko, ja, zh-CN, zh-TW). Add missing translations.

7. Error Handling

  • Use BAIErrorBoundary for user-facing error UI, ErrorBoundaryWithNullFallback for silent failures.
  • Use useBAILogger hook for all logging. Never use console.log directly.
  • No empty catch blocks. Always handle or explicitly return:
    // ✅ Good
    try { await fetchData(); } catch (e) { logger.error('Failed:', e); }
    // ✅ Good (explicitly ignored)
    try { await fetchData(); } catch { return undefined; }
    // ❌ Bad
    try { await fetchData(); } catch (e) { }

8. Styling and Theming

  • Use Ant Design theme tokens via theme.useToken() for colors, spacing, etc. Never hard-code colors.
  • Both dark and light themes must work. Test in both modes.
  • Use antd-style (createStyles) only when Ant Design tokens alone are insufficient.
  • For modal sizing, use the width prop on BAIModal and styles prop for body/header customization.

9. Reference Implementation

react/src/components/SignupModal.tsx is the canonical reference for this migration. Study it before starting any sub-issue:

  • How it extends BAIModalProps
  • How it uses Form with validation rules
  • How it uses BAIButton with action prop for async submit
  • How it uses BAIFlex for layout
  • How it dispatches events back to Lit parent via dispatchEvent
  • How it's registered in react/src/index.tsx with reactToWebComponent

10. PR and Code Quality

  • Each sub-issue should be a separate PR (or stacked PRs via Graphite for closely related work).
  • PR title format: feat(FR-XXXX): description or refactor(FR-XXXX): description
  • Run pnpm run lint and pnpm run format before submitting.
  • Run pnpm run relay if any GraphQL fragments are added.
  • Ensure pnpm run build succeeds with no errors.
  • Do not leave dead code — when a Lit component is fully replaced, remove it in the same PR or the cleanup PR (chore: Remove backend-ai-dialog and unused MWC dependencies after full migration #5376).

11. Accessibility

  • Preserve keyboard navigation (Escape to close, Tab through form fields).
  • Ensure aria-label, role, and focus management work correctly.
  • BAIModal handles most accessibility automatically via Ant Design — verify it by testing with keyboard only.

12. Electron Compatibility

  • Some components behave differently in Electron vs web (especially TCP connections in app-launcher).
  • Test both environments if the component has Electron-specific logic.
  • Check for globalThis.isElectron conditionals in the Lit source and preserve equivalent behavior.

13. Complete Lit File Removal

  • Each migration must result in the complete removal of the corresponding Lit source file. Do not leave partial Lit components.
  • When removing a Lit file, also remove:
    • Its type declaration in src/types/ and d/components/
    • Its customElements.define() registration
    • Any imports of the Lit component from other files (replace with React web component tag or direct React import)
  • After removing a Lit file, verify no other Lit component imports or depends on it.
  • The ultimate goal is full removal of Lit, MWC, and Vaadin dependencies. Every sub-issue contributes to this goal.

Migration Strategy

Each Lit component will be fully converted to a React component and registered via reactToWebComponent for backward compatibility with Lit parent components. As parent components are also migrated, the web component wrappers can be removed.

Key architectural decisions:

  1. Declarative state — Replace imperative show()/hide() with React open state prop
  2. Ant Design Form — Replace mwc-textfield validation with Ant Design Form rules
  3. BAI components — Use BAIButton, BAIFlex, BAIModal, BAISelect, etc. instead of MWC equivalents
  4. Event callbacks — Replace custom events (dialog-closed, dialog-closing-confirm) with React callback props
  5. i18n — Migrate from lit-translate (_t, _text) to react-i18next (t())

Phased Approach

Phase 0: Infrastructure

Phase 1: Simple Standalone Views

Phase 2: Auth-Adjacent Components

Phase 3: Login Flow

Phase 4: App Launcher & Edu Launcher

Phase 5: Navigation & Plugin System

Phase 6: App Shell & Full Lit Removal

Phase 7: Cleanup & Verification

Acceptance Criteria

Dialog Migration (Phases 0–4):

  • All backend-ai-dialog usages replaced with React BAIModal-based components
  • All MWC form components (mwc-textfield, mwc-button, mwc-select, mwc-checkbox) replaced with Ant Design / BAI equivalents
  • backend-ai-dialog.ts deleted from codebase
  • No regressions in dialog functionality (open/close, validation, events, styling)

Full Lit Removal (Phases 5–7):

  • All Lit-Element components in src/components/ migrated to React or removed
  • Navigation fully handled by React Router (no Redux page state)
  • Plugin loading system migrated to React
  • backend-ai-webui.ts app shell replaced by React entry point
  • lit, @lit/reactive-element, lit-translate removed from package.json
  • All 24 @material/mwc-* packages removed from package.json
  • All 9 @vaadin/* packages removed from package.json
  • Rollup-based Lit build pipeline removed or replaced

Cross-Cutting:

  • i18n translations preserved
  • Dark/light theme support maintained
  • Keyboard navigation and accessibility maintained
  • Bundle size reduced (measured and documented)

Available BAI Components for Migration

The backend.ai-ui package provides all necessary React replacements:

MWC Component BAI/Ant Design Replacement
mwc-dialog BAIModal
mwc-button BAIButton (with action prop for async)
mwc-textfield Ant Design Input / Input.Password
mwc-select BAISelect / Ant Design Select
mwc-checkbox Ant Design Checkbox
mwc-icon-button Ant Design Button with icon
mwc-menu + mwc-list-item Ant Design Dropdown / Menu
lablup-expansion Ant Design Collapse
CSS flexbox BAIFlex
Text display BAIText

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:uxUI / UX issue.effort:hardNeed to understand many components / a large extent of contextual or historical information.platform:webWeb-specific issuetype:enhanceAdd new features

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions