Merge pull request #34116 from drodil/fix_mui_to_bui_skill
docs(mui-to-bui): fix mangled jsx examples
This commit is contained in:
@@ -5,8 +5,8 @@ description: Migrate Backstage plugins from Material-UI (MUI) to Backstage UI (B
|
||||
|
||||
# MUI to BUI Migration Skill
|
||||
|
||||
This skill helps migrate Backstage plugins from Material-UI (@material-ui/core, @material-ui/icons) to Backstage UI (
|
||||
@backstage/ui).
|
||||
This skill helps migrate Backstage plugins from Material-UI (@material-ui/core, @material-ui/icons) to
|
||||
Backstage UI (@backstage/ui).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -19,6 +19,7 @@ Before starting migration:
|
||||
```
|
||||
|
||||
2. Add the CSS import to your root file (typically `src/index.ts` or app entry point):
|
||||
|
||||
```typescript
|
||||
import '@backstage/ui/css/styles.css';
|
||||
```
|
||||
@@ -38,11 +39,14 @@ Before starting migration:
|
||||
- `Accordion` - Collapsible content panels (`Accordion`, `AccordionTrigger`, `AccordionPanel`, `AccordionGroup`)
|
||||
- `Alert` - Alert/notification banners (`status`, `title`, `description`)
|
||||
- `Avatar` - User/entity avatars
|
||||
- `Badge` - Inline badge/label with optional icon (`size`, `icon`)
|
||||
- `Button` - Action buttons (`variant="primary"`, `variant="secondary"`, `variant="tertiary"`, `isDisabled`, `destructive`, `loading`)
|
||||
- `ButtonIcon` - Icon-only buttons (`icon`, `onPress`, `variant`)
|
||||
- `ButtonLink` - Link styled as button
|
||||
- `Card` - Content cards (`Card`, `CardHeader`, `CardBody`, `CardFooter`)
|
||||
- `Checkbox` - Checkbox input
|
||||
- `CheckboxGroup` - Grouped checkboxes with shared label (`label`, `orientation`, `isRequired`)
|
||||
- `DateRangePicker` - Date range input field (`label`, `value`, `onChange`)
|
||||
- `Dialog` - Modal dialogs (`DialogTrigger`, `Dialog`, `DialogHeader`, `DialogBody`, `DialogFooter`)
|
||||
- `FieldLabel` - Form field label with description and secondary label
|
||||
- `Header` - Page headers with breadcrumbs and tabs
|
||||
@@ -57,6 +61,7 @@ Before starting migration:
|
||||
- `SearchField` - Search input
|
||||
- `Select` - Dropdown select (single and multiple selection modes)
|
||||
- `Skeleton` - Loading skeleton
|
||||
- `Slider` - Range slider input (`label`, `minValue`, `maxValue`, `step`)
|
||||
- `Switch` - Toggle switch
|
||||
- `Table` - Data tables (with `useTable` hook for data management)
|
||||
- `TablePagination` - Standalone pagination component
|
||||
@@ -103,9 +108,9 @@ Create a `.module.css` file alongside your component using BUI CSS variables.
|
||||
|
||||
**Before (MUI `makeStyles`):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
// MyComponent.tsx
|
||||
import {makeStyles, Theme} from '@material-ui/core/styles';
|
||||
import { makeStyles, Theme } from '@material-ui/core/styles';
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
container: {
|
||||
@@ -130,18 +135,16 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||
function MyComponent() {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<div className = {classes.container} >
|
||||
<Typography className = {classes.title} > Title < /Typography>
|
||||
< div
|
||||
className = {classes.listItem} >
|
||||
<div className = {classes.icon} >
|
||||
<SomeIcon / >
|
||||
<div className={classes.container}>
|
||||
<Typography className={classes.title}>Title</Typography>
|
||||
<div className={classes.listItem}>
|
||||
<div className={classes.icon}>
|
||||
<SomeIcon />
|
||||
</div>
|
||||
<span>Content</span>
|
||||
</div>
|
||||
</div>
|
||||
< span > Content < /span>
|
||||
< /div>
|
||||
< /div>
|
||||
)
|
||||
;
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -177,27 +180,24 @@ function MyComponent() {
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
// MyComponent.tsx
|
||||
import {Box, Text} from '@backstage/ui';
|
||||
import {RiSomeIcon} from '@remixicon/react';
|
||||
import { Box, Text } from '@backstage/ui';
|
||||
import { RiSomeIcon } from '@remixicon/react';
|
||||
import styles from './MyComponent.module.css';
|
||||
|
||||
function MyComponent() {
|
||||
return (
|
||||
<Box className = {styles.container} >
|
||||
<Text className = {styles.title} > Title < /Text>
|
||||
< div
|
||||
className = {styles.listItem} >
|
||||
<div className = {styles.icon} >
|
||||
<RiSomeIcon size = {24}
|
||||
/>
|
||||
< /div>
|
||||
< span > Content < /span>
|
||||
< /div>
|
||||
< /Box>
|
||||
)
|
||||
;
|
||||
<Box className={styles.container}>
|
||||
<Text className={styles.title}>Title</Text>
|
||||
<div className={styles.listItem}>
|
||||
<div className={styles.icon}>
|
||||
<RiSomeIcon size={24} />
|
||||
</div>
|
||||
<span>Content</span>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -205,38 +205,27 @@ function MyComponent() {
|
||||
|
||||
**Before (MUI Box with display prop):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
<Box
|
||||
display = "flex"
|
||||
flexDirection = "column"
|
||||
alignItems = "center"
|
||||
justifyContent = "space-between"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Box display = "flex"
|
||||
flexDirection = "row"
|
||||
gap = {2} >
|
||||
{children}
|
||||
< /Box>
|
||||
< /Box>
|
||||
<Box display="flex" flexDirection="row" gap={2}>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
```
|
||||
|
||||
**After (BUI `Flex` component):**
|
||||
|
||||
```typescript
|
||||
<Flex direction = "column"
|
||||
align = "center"
|
||||
justify = "between" >
|
||||
<Flex direction = "row"
|
||||
style = {
|
||||
{
|
||||
gap: 'var(--bui-space-4)'
|
||||
}
|
||||
}>
|
||||
{
|
||||
children
|
||||
}
|
||||
```tsx
|
||||
<Flex direction="column" align="center" justify="between">
|
||||
<Flex direction="row" style={{ gap: 'var(--bui-space-4)' }}>
|
||||
{children}
|
||||
</Flex>
|
||||
</Flex>
|
||||
< /Flex>
|
||||
```
|
||||
|
||||
Note: BUI `Flex` uses `justify="between"` not `justify="space-between"`.
|
||||
@@ -245,70 +234,40 @@ Note: BUI `Flex` uses `justify="between"` not `justify="space-between"`.
|
||||
|
||||
**Before (MUI Grid):**
|
||||
|
||||
```typescript
|
||||
<Grid container
|
||||
spacing = {3} >
|
||||
<Grid item
|
||||
xs = {12}
|
||||
md = {6} >
|
||||
{content}
|
||||
< /Grid>
|
||||
< /Grid>
|
||||
```tsx
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} md={6}>
|
||||
{content}
|
||||
</Grid>
|
||||
</Grid>
|
||||
```
|
||||
|
||||
**After (BUI Grid):**
|
||||
|
||||
```typescript
|
||||
<Grid.Root columns = {
|
||||
{
|
||||
sm: '12'
|
||||
}
|
||||
}
|
||||
gap = "6" >
|
||||
<Grid.Item colSpan = {
|
||||
{
|
||||
sm: '12', md
|
||||
:
|
||||
'6'
|
||||
}
|
||||
}>
|
||||
{
|
||||
content
|
||||
}
|
||||
</Grid.Item>
|
||||
< /Grid.Root>
|
||||
```tsx
|
||||
<Grid.Root columns={{ sm: '12' }} gap="6">
|
||||
<Grid.Item colSpan={{ sm: '12', md: '6' }}>{content}</Grid.Item>
|
||||
</Grid.Root>
|
||||
```
|
||||
|
||||
### 5. Typography to Text
|
||||
|
||||
**Before (MUI Typography):**
|
||||
|
||||
```typescript
|
||||
<Typography variant = "h1" > Heading < /Typography>
|
||||
< Typography
|
||||
variant = "h6" > Subheading < /Typography>
|
||||
< Typography
|
||||
variant = "body1" > Body
|
||||
text < /Typography>
|
||||
< Typography
|
||||
variant = "body2"
|
||||
color = "textSecondary" > Secondary
|
||||
text < /Typography>
|
||||
```tsx
|
||||
<Typography variant="h1">Heading</Typography>
|
||||
<Typography variant="h6">Subheading</Typography>
|
||||
<Typography variant="body1">Body text</Typography>
|
||||
<Typography variant="body2" color="textSecondary">Secondary text</Typography>
|
||||
```
|
||||
|
||||
**After (BUI Text):**
|
||||
|
||||
```typescript
|
||||
<Text variant = "title-large" > Heading < /Text>
|
||||
< Text
|
||||
variant = "title-small" > Subheading < /Text>
|
||||
< Text
|
||||
variant = "body-medium" > Body
|
||||
text < /Text>
|
||||
< Text
|
||||
variant = "body-small"
|
||||
color = "secondary" > Secondary
|
||||
text < /Text>
|
||||
```tsx
|
||||
<Text variant="title-large">Heading</Text>
|
||||
<Text variant="title-small">Subheading</Text>
|
||||
<Text variant="body-medium">Body text</Text>
|
||||
<Text variant="body-small" color="secondary">Secondary text</Text>
|
||||
```
|
||||
|
||||
Valid Text variants: `title-large`, `title-medium`, `title-small`, `title-x-small`, `body-large`, `body-medium`,
|
||||
@@ -318,19 +277,17 @@ Valid Text variants: `title-large`, `title-medium`, `title-small`, `title-x-smal
|
||||
|
||||
**Before (MUI Tooltip):**
|
||||
|
||||
```typescript
|
||||
import {Tooltip, Typography} from '@material-ui/core';
|
||||
```tsx
|
||||
import { Tooltip, Typography } from '@material-ui/core';
|
||||
|
||||
<Tooltip title = { < Typography > Tooltip
|
||||
content < /Typography>}>
|
||||
< span > Hover
|
||||
me < /span>
|
||||
< /Tooltip>;
|
||||
<Tooltip title={<Typography>Tooltip content</Typography>}>
|
||||
<span>Hover me</span>
|
||||
</Tooltip>;
|
||||
```
|
||||
|
||||
**After (BUI TooltipTrigger pattern):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
import { Tooltip, TooltipTrigger, Text } from '@backstage/ui';
|
||||
|
||||
<TooltipTrigger>
|
||||
@@ -343,26 +300,23 @@ import { Tooltip, TooltipTrigger, Text } from '@backstage/ui';
|
||||
|
||||
**Before (MUI Dialog):**
|
||||
|
||||
```typescript
|
||||
import {Dialog, DialogTitle, DialogActions, Button} from '@material-ui/core';
|
||||
```tsx
|
||||
import { Dialog, DialogTitle, DialogActions, Button } from '@material-ui/core';
|
||||
|
||||
<Dialog open = {isOpen}
|
||||
onClose = {onClose} >
|
||||
<DialogTitle>Title < /DialogTitle>
|
||||
< DialogActions >
|
||||
<Button onClick = {onClose} > Cancel < /Button>
|
||||
< Button
|
||||
onClick = {onConfirm}
|
||||
color = "primary" >
|
||||
Confirm
|
||||
< /Button>
|
||||
< /DialogActions>
|
||||
< /Dialog>;
|
||||
<Dialog open={isOpen} onClose={onClose}>
|
||||
<DialogTitle>Title</DialogTitle>
|
||||
<DialogActions>
|
||||
<Button onClick={onClose}>Cancel</Button>
|
||||
<Button onClick={onConfirm} color="primary">
|
||||
Confirm
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>;
|
||||
```
|
||||
|
||||
**After (BUI Dialog):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
import {
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
@@ -373,78 +327,58 @@ import {
|
||||
|
||||
<DialogTrigger>
|
||||
<Dialog
|
||||
isOpen = {isOpen}
|
||||
isDismissable
|
||||
onOpenChange = {open
|
||||
=>
|
||||
{
|
||||
if (!open) onClose();
|
||||
}
|
||||
}
|
||||
>
|
||||
<DialogHeader>Title < /DialogHeader>
|
||||
< DialogFooter >
|
||||
<Button onClick = {onConfirm}
|
||||
variant = "primary" >
|
||||
Confirm
|
||||
< /Button>
|
||||
< Button
|
||||
onClick = {onClose}
|
||||
variant = "secondary"
|
||||
slot = "close" >
|
||||
Cancel
|
||||
< /Button>
|
||||
< /DialogFooter>
|
||||
< /Dialog>
|
||||
< /DialogTrigger>;
|
||||
isOpen={isOpen}
|
||||
isDismissable
|
||||
onOpenChange={open => {
|
||||
if (!open) onClose();
|
||||
}}
|
||||
>
|
||||
<DialogHeader>Title</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button onClick={onConfirm} variant="primary">
|
||||
Confirm
|
||||
</Button>
|
||||
<Button onClick={onClose} variant="secondary" slot="close">
|
||||
Cancel
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
</DialogTrigger>;
|
||||
```
|
||||
|
||||
### 8. Button Changes
|
||||
|
||||
**Before (MUI Button):**
|
||||
|
||||
```typescript
|
||||
<Button variant = "contained"
|
||||
color = "primary"
|
||||
disabled = {loading}
|
||||
onClick = {handleClick} >
|
||||
```tsx
|
||||
<Button variant="contained" color="primary" disabled={loading} onClick={handleClick}>
|
||||
Submit
|
||||
< /Button>
|
||||
< IconButton
|
||||
onClick = {handleDelete}
|
||||
disabled = {!
|
||||
canDelete
|
||||
}>
|
||||
<DeleteIcon / >
|
||||
</Button>
|
||||
<IconButton onClick={handleDelete} disabled={!canDelete}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
```
|
||||
|
||||
**After (BUI Button):**
|
||||
|
||||
```typescript
|
||||
<Button variant = "primary"
|
||||
isDisabled = {loading}
|
||||
onClick = {handleClick} >
|
||||
```tsx
|
||||
<Button variant="primary" isDisabled={loading} onClick={handleClick}>
|
||||
Submit
|
||||
< /Button>
|
||||
< ButtonIcon
|
||||
aria - label = "delete"
|
||||
isDisabled = {!
|
||||
canDelete
|
||||
}
|
||||
onPress = {handleDelete}
|
||||
icon = { < RiDeleteBinLine
|
||||
size = {16}
|
||||
/>}
|
||||
variant = "secondary"
|
||||
/ >
|
||||
</Button>
|
||||
<ButtonIcon
|
||||
aria-label="delete"
|
||||
isDisabled={!canDelete}
|
||||
onPress={handleDelete}
|
||||
icon={<RiDeleteBinLine size={16} />}
|
||||
variant="secondary"
|
||||
/>
|
||||
```
|
||||
|
||||
### 9. TextField Changes
|
||||
|
||||
**Before (MUI TextField):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
<TextField
|
||||
required
|
||||
name="title"
|
||||
@@ -457,7 +391,7 @@ variant = "secondary"
|
||||
|
||||
**After (BUI TextField):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
<TextField
|
||||
isRequired
|
||||
id="title"
|
||||
@@ -473,89 +407,72 @@ Note: BUI TextField `onChange` receives the string value directly, not an event
|
||||
|
||||
**Before (MUI Tabs):**
|
||||
|
||||
```typescript
|
||||
import {Tab} from '@material-ui/core';
|
||||
import {TabContext, TabList, TabPanel} from '@material-ui/lab';
|
||||
```tsx
|
||||
import { Tab } from '@material-ui/core';
|
||||
import { TabContext, TabList, TabPanel } from '@material-ui/lab';
|
||||
|
||||
<TabContext value = {tab} >
|
||||
<TabList onChange = {handleChange} >
|
||||
<Tab label = "Tab 1"
|
||||
value = "tab1" / >
|
||||
<Tab label = "Tab 2"
|
||||
value = "tab2" / >
|
||||
<TabContext value={tab}>
|
||||
<TabList onChange={handleChange}>
|
||||
<Tab label="Tab 1" value="tab1" />
|
||||
<Tab label="Tab 2" value="tab2" />
|
||||
</TabList>
|
||||
< TabPanel
|
||||
value = "tab1" > Content
|
||||
1 < /TabPanel>
|
||||
< TabPanel
|
||||
value = "tab2" > Content
|
||||
2 < /TabPanel>
|
||||
< /TabContext>;
|
||||
<TabPanel value="tab1">Content 1</TabPanel>
|
||||
<TabPanel value="tab2">Content 2</TabPanel>
|
||||
</TabContext>;
|
||||
```
|
||||
|
||||
**After (BUI Tabs):**
|
||||
|
||||
```typescript
|
||||
import {Tabs, TabList, Tab, TabPanel} from '@backstage/ui';
|
||||
```tsx
|
||||
import { Tabs, TabList, Tab, TabPanel } from '@backstage/ui';
|
||||
|
||||
<Tabs defaultSelectedKey = "tab1" >
|
||||
<TabList>
|
||||
<Tab id = "tab1" > Tab
|
||||
1 < /Tab>
|
||||
< Tab
|
||||
id = "tab2" > Tab
|
||||
2 < /Tab>
|
||||
< /TabList>
|
||||
< TabPanel
|
||||
id = "tab1" > Content
|
||||
1 < /TabPanel>
|
||||
< TabPanel
|
||||
id = "tab2" > Content
|
||||
2 < /TabPanel>
|
||||
< /Tabs>;
|
||||
<Tabs defaultSelectedKey="tab1">
|
||||
<TabList>
|
||||
<Tab id="tab1">Tab 1</Tab>
|
||||
<Tab id="tab2">Tab 2</Tab>
|
||||
</TabList>
|
||||
<TabPanel id="tab1">Content 1</TabPanel>
|
||||
<TabPanel id="tab2">Content 2</TabPanel>
|
||||
</Tabs>;
|
||||
```
|
||||
|
||||
### 11. Menu Pattern
|
||||
|
||||
**Before (MUI Menu):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
import {IconButton, Popover, MenuList, MenuItem} from '@material-ui/core';
|
||||
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
||||
|
||||
<IconButton onClick = {handleOpen} > <MoreVertIcon / > </IconButton>
|
||||
< Popover
|
||||
open = {open}
|
||||
anchorEl = {anchorEl}
|
||||
onClose = {handleClose} >
|
||||
<MenuList>
|
||||
<MenuItem onClick = {handleAction} > Action < /MenuItem>
|
||||
< /MenuList>
|
||||
< /Popover>
|
||||
<IconButton onClick={handleOpen}>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
<Popover open={open} anchorEl={anchorEl} onClose={handleClose}>
|
||||
<MenuList>
|
||||
<MenuItem onClick={handleAction}>Action</MenuItem>
|
||||
</MenuList>
|
||||
</Popover>
|
||||
```
|
||||
|
||||
**After (BUI Menu):**
|
||||
|
||||
```typescript
|
||||
import {ButtonIcon, Menu, MenuItem, MenuTrigger} from '@backstage/ui';
|
||||
import {RiMore2Line} from '@remixicon/react';
|
||||
```tsx
|
||||
import { ButtonIcon, Menu, MenuItem, MenuTrigger } from '@backstage/ui';
|
||||
import { RiMore2Line } from '@remixicon/react';
|
||||
|
||||
<MenuTrigger>
|
||||
<ButtonIcon aria - label = "more"
|
||||
icon = { < RiMore2Line / >
|
||||
}
|
||||
variant = "secondary" / >
|
||||
<Menu>
|
||||
<MenuItem onAction = {handleAction} > Action < /MenuItem>
|
||||
< /Menu>
|
||||
< /MenuTrigger>;
|
||||
<ButtonIcon aria-label="more" icon={<RiMore2Line />} variant="secondary" />
|
||||
<Menu>
|
||||
<MenuItem onAction={handleAction}>Action</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>;
|
||||
```
|
||||
|
||||
### 12. List to BUI List
|
||||
|
||||
**Before (MUI List):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
import { List, ListItem, ListItemIcon, ListItemText } from '@material-ui/core';
|
||||
|
||||
<List>
|
||||
@@ -570,7 +487,7 @@ import { List, ListItem, ListItemIcon, ListItemText } from '@material-ui/core';
|
||||
|
||||
**After (BUI List):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
import { List, ListRow } from '@backstage/ui';
|
||||
import { RiSomeIcon } from '@remixicon/react';
|
||||
|
||||
@@ -587,7 +504,7 @@ Note: `ListRow` supports `icon`, `description`, `menuItems`, and `customActions`
|
||||
|
||||
**Before (MUI Chip):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
import { Chip } from '@material-ui/core';
|
||||
|
||||
<Chip label="Category" size="small" />;
|
||||
@@ -595,17 +512,17 @@ import { Chip } from '@material-ui/core';
|
||||
|
||||
**After (BUI Tag):**
|
||||
|
||||
```typescript
|
||||
import {Tag} from '@backstage/ui';
|
||||
```tsx
|
||||
import { Tag } from '@backstage/ui';
|
||||
|
||||
<Tag size = "small" > Category < /Tag>;
|
||||
<Tag size="small">Category</Tag>;
|
||||
```
|
||||
|
||||
### 14. Alert Pattern
|
||||
|
||||
**Before (MUI Alert):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
import { Alert, AlertTitle } from '@material-ui/lab';
|
||||
|
||||
<Alert severity="error">
|
||||
@@ -616,7 +533,7 @@ import { Alert, AlertTitle } from '@material-ui/lab';
|
||||
|
||||
**After (BUI Alert):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
import { Alert } from '@backstage/ui';
|
||||
|
||||
<Alert
|
||||
@@ -637,22 +554,21 @@ Use `loading` for a loading spinner, and `customActions` for action buttons.
|
||||
|
||||
**Before (MUI Icons):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
import CloseIcon from '@material-ui/icons/Close';
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
|
||||
<CloseIcon / >
|
||||
<SearchIcon fontSize = "small" / >
|
||||
<CloseIcon />
|
||||
<SearchIcon fontSize="small" />
|
||||
```
|
||||
|
||||
**After (Remix Icons):**
|
||||
|
||||
```typescript
|
||||
```tsx
|
||||
import {RiCloseLine, RiSearchLine} from '@remixicon/react';
|
||||
|
||||
<RiCloseLine / >
|
||||
<RiSearchLine size = {16}
|
||||
/>
|
||||
<RiCloseLine />
|
||||
<RiSearchLine size={16} />
|
||||
```
|
||||
|
||||
Common icon mappings:
|
||||
@@ -683,6 +599,239 @@ Common icon mappings:
|
||||
|
||||
Find more icons at: https://remixicon.com/
|
||||
|
||||
### 16. Paper to Card
|
||||
|
||||
**Before (MUI Paper):**
|
||||
|
||||
```tsx
|
||||
import { Paper, Typography } from '@material-ui/core';
|
||||
|
||||
<Paper elevation={2}>
|
||||
<Typography variant="h6">Title</Typography>
|
||||
<Typography>Body content</Typography>
|
||||
</Paper>;
|
||||
```
|
||||
|
||||
**After (BUI Card):**
|
||||
|
||||
```tsx
|
||||
import { Card, CardHeader, CardBody, Text } from '@backstage/ui';
|
||||
|
||||
<Card>
|
||||
<CardHeader>Title</CardHeader>
|
||||
<CardBody>
|
||||
<Text>Body content</Text>
|
||||
</CardBody>
|
||||
</Card>;
|
||||
```
|
||||
|
||||
### 17. Select
|
||||
|
||||
**Before (MUI Select):**
|
||||
|
||||
```tsx
|
||||
import { FormControl, InputLabel, Select, MenuItem } from '@material-ui/core';
|
||||
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>Framework</InputLabel>
|
||||
<Select value={value} onChange={e => setValue(e.target.value as string)}>
|
||||
<MenuItem value="react">React</MenuItem>
|
||||
<MenuItem value="angular">Angular</MenuItem>
|
||||
</Select>
|
||||
</FormControl>;
|
||||
```
|
||||
|
||||
**After (BUI Select):**
|
||||
|
||||
```tsx
|
||||
import { Select } from '@backstage/ui';
|
||||
|
||||
<Select
|
||||
label="Framework"
|
||||
selectedKey={value}
|
||||
onSelectionChange={key => setValue(key as string)}
|
||||
options={[
|
||||
{ value: 'react', label: 'React' },
|
||||
{ value: 'angular', label: 'Angular' },
|
||||
]}
|
||||
/>;
|
||||
```
|
||||
|
||||
Note: BUI `Select` accepts flat `options` arrays or grouped `OptionSection` arrays. Pass `multiple` for multi-select.
|
||||
|
||||
### 18. Accordion
|
||||
|
||||
**Before (MUI Accordion):**
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
} from '@material-ui/core';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
Section title
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>Content goes here</AccordionDetails>
|
||||
</Accordion>;
|
||||
```
|
||||
|
||||
**After (BUI Accordion):**
|
||||
|
||||
```tsx
|
||||
import { Accordion, AccordionTrigger, AccordionPanel } from '@backstage/ui';
|
||||
|
||||
<Accordion>
|
||||
<AccordionTrigger title="Section title" />
|
||||
<AccordionPanel>Content goes here</AccordionPanel>
|
||||
</Accordion>;
|
||||
```
|
||||
|
||||
Use `AccordionGroup` to wrap multiple `Accordion` items and control whether multiple panels can be open simultaneously.
|
||||
|
||||
### 19. RadioGroup
|
||||
|
||||
**Before (MUI RadioGroup):**
|
||||
|
||||
```tsx
|
||||
import {
|
||||
FormControl,
|
||||
FormLabel,
|
||||
RadioGroup,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
} from '@material-ui/core';
|
||||
|
||||
<FormControl>
|
||||
<FormLabel>Frequency</FormLabel>
|
||||
<RadioGroup value={value} onChange={e => setValue(e.target.value)}>
|
||||
<FormControlLabel value="daily" control={<Radio />} label="Daily" />
|
||||
<FormControlLabel value="weekly" control={<Radio />} label="Weekly" />
|
||||
</RadioGroup>
|
||||
</FormControl>;
|
||||
```
|
||||
|
||||
**After (BUI RadioGroup):**
|
||||
|
||||
```tsx
|
||||
import { RadioGroup, Radio } from '@backstage/ui';
|
||||
|
||||
<RadioGroup label="Frequency" value={value} onChange={setValue}>
|
||||
<Radio value="daily">Daily</Radio>
|
||||
<Radio value="weekly">Weekly</Radio>
|
||||
</RadioGroup>;
|
||||
```
|
||||
|
||||
### 20. Badge
|
||||
|
||||
**Before (MUI Badge):**
|
||||
|
||||
```tsx
|
||||
import { Badge } from '@material-ui/core';
|
||||
|
||||
<Badge badgeContent={4} color="primary">
|
||||
<MailIcon />
|
||||
</Badge>;
|
||||
```
|
||||
|
||||
**After (BUI Badge):**
|
||||
|
||||
```tsx
|
||||
import { Badge } from '@backstage/ui';
|
||||
import { RiMailLine } from 'react-icons/ri';
|
||||
|
||||
<Badge>New</Badge>
|
||||
<Badge size="small" icon={<RiMailLine size={12} />}>4</Badge>
|
||||
```
|
||||
|
||||
Note: BUI `Badge` is a label-style badge (inline text with optional icon), not a notification counter overlay.
|
||||
For notification counters overlaid on icons, use CSS positioning.
|
||||
|
||||
### 21. Slider
|
||||
|
||||
**Before (MUI Slider):**
|
||||
|
||||
```tsx
|
||||
import { Slider } from '@material-ui/core';
|
||||
|
||||
<Slider
|
||||
value={value}
|
||||
onChange={(_, newValue) => setValue(newValue as number)}
|
||||
min={0}
|
||||
max={100}
|
||||
step={10}
|
||||
/>;
|
||||
```
|
||||
|
||||
**After (BUI Slider):**
|
||||
|
||||
```tsx
|
||||
import { Slider } from '@backstage/ui';
|
||||
|
||||
<Slider
|
||||
label="Volume"
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
step={10}
|
||||
/>;
|
||||
```
|
||||
|
||||
Note: BUI `Slider` `onChange` receives the new value directly. Use `minValue`/`maxValue` instead of `min`/`max`.
|
||||
|
||||
### 22. CheckboxGroup
|
||||
|
||||
**Before (MUI FormGroup with Checkboxes):**
|
||||
|
||||
```tsx
|
||||
import {
|
||||
FormControl,
|
||||
FormLabel,
|
||||
FormGroup,
|
||||
FormControlLabel,
|
||||
Checkbox,
|
||||
} from '@material-ui/core';
|
||||
|
||||
<FormControl>
|
||||
<FormLabel>Options</FormLabel>
|
||||
<FormGroup>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={values.a}
|
||||
onChange={e => handleChange('a', e.target.checked)}
|
||||
/>
|
||||
}
|
||||
label="Option A"
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={values.b}
|
||||
onChange={e => handleChange('b', e.target.checked)}
|
||||
/>
|
||||
}
|
||||
label="Option B"
|
||||
/>
|
||||
</FormGroup>
|
||||
</FormControl>;
|
||||
```
|
||||
|
||||
**After (BUI CheckboxGroup):**
|
||||
|
||||
```tsx
|
||||
import { CheckboxGroup, Checkbox } from '@backstage/ui';
|
||||
|
||||
<CheckboxGroup label="Options" value={selected} onChange={setSelected}>
|
||||
<Checkbox value="a">Option A</Checkbox>
|
||||
<Checkbox value="b">Option B</Checkbox>
|
||||
</CheckboxGroup>;
|
||||
```
|
||||
|
||||
## CSS Variable Reference
|
||||
|
||||
### Spacing
|
||||
@@ -758,18 +907,23 @@ When migrating a plugin:
|
||||
13. [ ] Replace MUI `Dialog` with BUI `DialogTrigger` pattern
|
||||
14. [ ] Replace MUI `Tooltip` with BUI `TooltipTrigger` pattern (both from `@backstage/ui`)
|
||||
15. [ ] Replace MUI `Tabs` with BUI `Tabs`
|
||||
16. [ ] Replace MUI `Menu` with BUI `MenuTrigger` pattern
|
||||
16. [ ] Replace MUI `Menu`/`Popover` with BUI `MenuTrigger` pattern
|
||||
17. [ ] Replace `Chip` with `Tag`
|
||||
18. [ ] Replace `IconButton` with `ButtonIcon`
|
||||
19. [ ] Replace MUI `Alert` with BUI `Alert`
|
||||
20. [ ] Replace MUI `List` with BUI `List` and `ListRow`
|
||||
21. [ ] Update `Button` props (`disabled` → `isDisabled`, `variant="contained"` → `variant="primary"`)
|
||||
22. [ ] Update `TextField` props (`required` → `isRequired`, `onChange` signature)
|
||||
23. [ ] Replace MUI icons with Remix icons
|
||||
24. [ ] Run `yarn tsc` to check for type errors
|
||||
25. [ ] Run `yarn build` to verify build
|
||||
26. [ ] Run `yarn lint` to check for missing dependencies
|
||||
27. [ ] Test component rendering and functionality
|
||||
21. [ ] Replace MUI `Select`/`FormControl` with BUI `Select`
|
||||
22. [ ] Replace MUI `Accordion` with BUI `Accordion`/`AccordionTrigger`/`AccordionPanel`
|
||||
23. [ ] Replace MUI `RadioGroup`/`FormControlLabel` with BUI `RadioGroup`/`Radio`
|
||||
24. [ ] Replace MUI `FormGroup` with BUI `CheckboxGroup`
|
||||
25. [ ] Replace MUI `Slider` with BUI `Slider`
|
||||
26. [ ] Update `Button` props (`disabled` → `isDisabled`, `variant="contained"` → `variant="primary"`)
|
||||
27. [ ] Update `TextField` props (`required` → `isRequired`, `onChange` signature)
|
||||
28. [ ] Replace MUI icons with Remix icons
|
||||
29. [ ] Run `yarn tsc` to check for type errors
|
||||
30. [ ] Run the project's build command (e.g. `yarn build`, `yarn build:all`, or `yarn workspace <pkg> build`) to verify build
|
||||
31. [ ] Run `yarn lint` to check for missing dependencies
|
||||
32. [ ] Test component rendering and functionality
|
||||
|
||||
## Reference
|
||||
|
||||
|
||||
Reference in New Issue
Block a user