內容整理自官方開發文件
系列
- 1 分鐘快速使用 Docker 上手最新版 Sentry-CLI - 建立版本
- 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps
- Sentry For React 完整接入詳解
- Sentry For Vue 完整接入詳解
- Sentry-CLI 使用詳解
- Sentry Web 效能監控 - Web Vitals
- Sentry Web 效能監控 - Metrics
- Sentry Web 效能監控 - Trends
- Sentry Web 前端監控 - 最佳實踐(官方教程)
- Sentry 後端監控 - 最佳實踐(官方教程)
- Sentry 監控 - Discover 大資料查詢分析引擎
- Sentry 監控 - Dashboards 資料視覺化大屏
- Sentry 監控 - Environments 區分不同部署環境的事件資料
- Sentry 監控 - Security Policy 安全策略報告
- Sentry 監控 - Search 搜尋查詢實戰
- Sentry 監控 - Alerts 告警
- Sentry 監控 - Distributed Tracing 分散式跟蹤
- Sentry 監控 - 面向全棧開發人員的分散式跟蹤 101 系列教程(一)
- Sentry 監控 - Snuba 資料中臺架構簡介(Kafka+Clickhouse)
- Sentry 監控 - Snuba 資料中臺架構(Data Model 簡介)
- Sentry 監控 - Snuba 資料中臺架構(Query Processing 簡介)
- Sentry 官方 JavaScript SDK 簡介與除錯指南
- Sentry 監控 - Snuba 資料中臺架構(編寫和測試 Snuba 查詢)
- Sentry 監控 - Snuba 資料中臺架構(SnQL 查詢語言簡介)
- Sentry 監控 - Snuba 資料中臺本地開發環境配置實戰
- Sentry 監控 - 私有 Docker Compose 部署與故障排除詳解
目錄
- 前端手冊
- 目錄結構
- 資料夾和檔案結構
- 檔案命名
- 使用
index.(j|t)?(sx)
- React
- 定義
React
元件 - 元件與檢視
- PropTypes
- 事件處理程式
- 定義
- CSS 和 Emotion
stylelint
錯誤
- 狀態管理
- 測試
- 選擇器
- 測試中未定義的
theme
屬性
- Babel 語法外掛
- 新語法
- 可選鏈
- 語法
- 空值合併
- Lodash
- Typescript
- 遷移指南
- Storybook Styleguide
- 我們使用它嗎?
- 它部署在某個地方嗎?
- Typing DefaultProps
- 類(
Class
)元件 - 函式(
Function
)元件 - 參考
- 類(
- 使用 Hooks
- 使用庫中的
hooks
- 使用
react
的內建hooks
- 使用
context
- 使用自定義
hooks
- 注意
hooks
的規則和注意事項 - 我們的基礎檢視元件仍然是基於類的
- 不要為
hooks
重寫
- 使用庫中的
- 使用 React Testing Library
- 查詢
- 技巧
- 遷移 - grid-emotion
- 元件
- 屬性
margin
和padding
flexbox
前端手冊
本指南涵蓋了我們如何在 Sentry
編寫前端程式碼,
並特別關注
Sentry
和
Getsentry
程式碼庫。
它假設您使用的是
eslint-config-sentry
概述的 eslint
規則;
因此,這裡不會討論由這些 linting
規則強制執行的程式碼風格。
目錄結構
前端程式碼庫當前位於 sentry
中的 src/sentry/static/sentry/app
和 getentry
中的 static/getsentry
下。 (我們打算在未來與 static/sentry
保持一致。)
資料夾和檔案結構
檔案命名
- 根據模組的功能或類的使用方式或使用它們的應用程式部分,有意義地命名檔案。
- 除非必要,否則不要使用字首或字尾(即
dataScrubbingEditModal
、dataScrubbingAddModal
),而是使用像dataScrubbing/editModal
這樣的名稱。
使用 index.(j|t)?(sx)
在資料夾中有一個
index
檔案提供了一種隱式匯入主檔案而不指定它的方法
index
檔案的使用應遵循以下規則:
-
如果建立資料夾來對一起使用的元件進行分組,並且有一個入口點元件,它使用分組內的元件(
examples
、avatar
、idBadge
)。入口點元件應該是index
檔案。 -
不要使用
index.(j|t)?(sx)
檔案,如果資料夾包含在應用程式的其他部分使用的元件,與入口點檔案無關。(即,actionCreators
,panels
) -
不要僅僅為了重新匯出而使用
index
檔案。更傾向於匯入單個元件。
React
定義 React 元件
新元件在需要訪問 this
時使用 class
語法,以及類欄位+箭頭函式方法定義。
class Note extends React.Component {
static propTypes = {
author: PropTypes.object.isRequired,
onEdit: PropTypes.func.isRequired,
};
// 請注意,方法是使用箭頭函式類欄位定義的(繫結“this”)
handleChange = value => {
let user = ConfigStore.get('user');
if (user.isSuperuser) {
this.props.onEdit(value);
}
};
render() {
let {content} = this.props; // 對 props 使用解構賦值
return <div onChange={this.handleChange}>{content}</div>;
}
}
export default Note;
一些較舊的元件使用 createReactClass
和 mixins
,但這已被棄用。
元件與檢視
app/components/
和 app/views
資料夾都包含 React
元件。
- 使用通常不會在程式碼庫的其他部分重用的 UI 檢視。
- 使用設計為高度可重用的 UI 元件。
元件應該有一個關聯的 .stories.js
檔案來記錄它應該如何使用。
使用 yarn storybook
在本地執行 Storybook
或在 https://storybook.getsentry.net/
上檢視託管版本
PropTypes
使用它們,要明確,儘可能使用共享的自定義屬性。
更傾向 Proptypes.arrayOf
而不是 PropTypes.array
和 PropTypes.shape
而不是 PropTypes.object
如果你使用一組重要的、定義良好的 key
(你的元件依賴)傳遞物件,那麼使用 PropTypes.shape
顯式定義它們:
PropTypes.shape({
username: PropTypes.string.isRequired,
email: PropTypes.string
})
如果您要重複使用自定義 prop-type
或傳遞常見的共享 shape
(如 organization
、project
或 user
),
請確保從我們有用的自定義集合中匯入 proptype
!
事件處理程式
我們使用不同的字首來更好地區分事件處理程式
和事件回撥屬性
。
對事件處理程式使用 handle
字首,例如:
<Button onClick={this.handleDelete}/>
對於傳遞給元件的事件回撥屬性,請使用 on
字首,例如:
<Button onClick={this.props.onDelete}>
CSS 和 Emotion
- 使用
Emotion
,使用theme
物件。 - 最好的樣式是您不編寫的樣式 - 儘可能使用現有元件。
- 新程式碼應該使用
css-in-js
庫 e m o t i o n - 它允許您將樣式繫結到元素而無需全域性選擇器的間接性。 你甚至不需要開啟另一個檔案! - 從 props.theme 獲取常量(
z-indexes
,paddings
,colors
)
import styled from 'react-emotion';
const SomeComponent = styled('div')`
border-radius: 1.45em;
font-weight: bold;
z-index: ${p => p.theme.zIndex.modal};
padding: ${p => p.theme.grid}px ${p => p.theme.grid * 2}px;
border: 1px solid ${p => p.theme.borderLight};
color: ${p => p.theme.purple};
box-shadow: ${p => p.theme.dropShadowHeavy};
`;
export default SomeComponent;
- 請注意,
reflexbox
(例如Flex
和Box
)已被棄用,請避免在新程式碼中使用。
stylelint
錯誤
"No duplicate selectors"
當您使用樣式元件(styled component)
作為選擇器時會發生這種情況,我們需要通過使用註釋來輔助 linter
來告訴 stylelint
我們正在插入的是一個選擇器。 例如
const ButtonBar = styled("div")`
${/* sc-selector */Button) {
border-radius: 0;
}
`;
有關其他標籤和更多資訊,請參閱。
狀態管理
我們目前使用 Reflux 來管理全域性狀態。
Reflux
實現了 Flux 概述的單向資料流模式。
Store
註冊在 app/stores
下,用於儲存應用程式使用的各種資料。
Action
需要在 app/actions
下注冊。
我們使用 action creator
函式(在 app/actionCreators
下)來分派 action
。
Reflux store
監聽 action
並相應地更新自己。
我們目前正在探索 Reflux
庫的替代方案以供將來使用。
測試
注意:你的檔名必須是 .spec.jsx
否則 jest
不會執行它!
我們在 setup.js 中定義了有用的 fixtures
,使用這些!
如果您以重複的方式定義模擬資料,則可能值得新增此檔案。routerContext
是一種特別有用的方法,用於提供大多數檢視所依賴的上下文物件。
Client.addMockResponse
是模擬 API
請求的最佳方式。
這是我們的程式碼,
所以如果它讓您感到困惑,只需將 console.log()
語句放入其邏輯中即可!
我們測試環境中的一個重要問題是,enzyme
修改了 react
生命週期的許多方面以同步評估(即使它們通常是非同步的)。
當您觸發某些邏輯並且沒有立即在您的斷言邏輯中反映出來時,這可能會使您陷入一種虛假的安全感。
標記您的測試方法 async
並使用 await tick();
實用程式可以讓事件迴圈重新整理執行事件並修復此問題:
wrapper.find('ExpandButton').simulate('click');
await tick();
expect(wrapper.find('CommitRow')).toHaveLength(2);
選擇器
如果您正在編寫 jest
測試,您可以使用 Component
(和 Styled Component
)名稱作為選擇器。
此外,如果您需要使用 DOM
查詢選擇器,請使用 data-test-id
而不是類名。我們目前沒有,但我們可以在構建過程中使用 babel
去除它。
測試中未定義的 theme
屬性
而不是使用來自 enzyme
的 mount()
...使用這個:import {mountWithTheme} from 'sentry-test/enzyme'
以便被測元件用 <ThemeProvider>
。
Babel 語法外掛
我們決定只使用處於 stage 3
(或更高版本)的 ECMAScript 提案(參見 TC39 提案)。
此外,因為我們正在遷移到 typescript
,我們將與他們的編譯器支援的內容保持一致。唯一的例外是裝飾器。
新語法
可選鏈
可選鏈 幫助我們訪問 [巢狀] 物件,
而無需在每個屬性/方法
訪問之前檢查是否存在。
如果我們嘗試訪問 undefined
或 null
物件的屬性,它將停止並返回 undefined
。
語法
可選鏈操作符拼寫為 ?.
。它可能出現在三個位置:
obj?.prop // 可選的靜態屬性訪問
obj?.[expr] // 可選的動態屬性訪問
func?.(...args) // 可選的函式或方法呼叫
空值合併
這是一種設定“預設”值的方法。例如:以前你會做類似的事情
let x = volume || 0.5;
這是一個問題,因為 0
是 volume
的有效值,但因為它的計算結果為 false
-y,我們不會使表示式短路,並且 x
的值為 0.5
如果我們使用空值合併
let x = volume ?? 0.5
如果 volume
為 null
或 undefined
,它只會預設為 0.5
。
語法
基本情況。如果表示式在 ??
的左側運算子計算為 undefined
或 null
,則返回其右側。
const response = {
settings: {
nullValue: null,
height: 400,
animationDuration: 0,
headerText: '',
showSplashScreen: false
}
};
const undefinedValue = response.settings.undefinedValue ?? 'some other default'; // result: 'some other default'
const nullValue = response.settings.nullValue ?? 'some other default'; // result: 'some other default'
const headerText = response.settings.headerText ?? 'Hello, world!'; // result: ''
const animationDuration = response.settings.animationDuration ?? 300; // result: 0
const showSplashScreen = response.settings.showSplashScreen ?? true; // result: false
Lodash
確保不要使用預設的 lodash
包匯入 lodash
實用程式。
有一個 eslint
規則來確保這不會發生。
而是直接匯入實用程式,例如 import isEqual from 'lodash/isEqual';
。
以前我們使用了 lodash-webpack-plugin 和
babel-plugin-lodash 的組合,
但是在嘗試使用新的 lodash
實用程式(例如這個 PR)時很容易忽略這些外掛和配置。
通過 webpack tree shaking
和 eslint
強制執行,我們應該能夠保持合理的包大小。
- https://www.npmjs.com/package/lodash-webpack-plugin
- https://github.com/lodash/babel-plugin-lodash
- https://github.com/getsentry/sentry/pull/13834
有關更多資訊,請參閱此 PR。
我們更喜歡使用可選鏈
和空值合併
而不是來自 lodash/get
的 get
。
Typescript
Typing DefaultProps
遷移指南
Grid-Emotion
Storybook Styleguide
引用其文件,“Storybook
是用於 UI
元件的 UI
開發環境。
有了它,您可以視覺化 UI
元件的不同狀態並以互動方式開發它們。”
更多細節在這裡:
我們使用它嗎?
是的!我們將 Storybook
用於 getsentry/sentry 專案。
Storybook
的配置可以在 https://github.com/getsentry/sentry/tree/master/.storybook 中找到。
要在本地執行 Storybook
,請在 getsentry/sentry
儲存庫的根目錄中執行 npm run storybook
。
它部署在某個地方嗎?
Sentry 的 Storybook 是使用 Vercel 構建和部署的。
每個 Pull Request
都有自己的部署,每次推送到主分支都會部署到 https://storybook.sentry.dev。
Typing DefaultProps
由於 Typescript 3.0
預設 props
可以更簡單地輸入。有幾種不同的方法適合不同的場景。
類(Class)元件
import React from 'react';
type DefaultProps = {
size: 'Small' | 'Medium' | 'Large'; // 這些不應標記為可選
};
// 沒有 Partial<DefaultProps>
type Props = DefaultProps & {
name: string;
codename?: string;
};
class Planet extends React.Component<Props> {
// 沒有 Partial<Props> 因為它會將所有內容標記為可選
static defaultProps: DefaultProps = {
size: 'Medium',
};
render() {
const {name, size, codename} = this.props;
return (
<p>
{name} is a {size.toLowerCase()} planet.
{codename && ` Its codename is ${codename}`}
</p>
);
}
}
const planet = <Planet name="Mars" />;
或在 typeof
的幫助下:
import React from 'react';
const defaultProps = {
size: 'Medium' as 'Small' | 'Medium' | 'Large',
};
type Props = {
name: string;
codename?: string;
} & typeof defaultProps;
// 沒有 Partial<typeof defaultProps> 因為它會將所有內容標記為可選
class Planet extends React.Component<Props> {
static defaultProps = defaultProps;
render() {
const {name, size, codename} = this.props;
return (
<p>
{name} is a {size.toLowerCase()} planet. Its color is{' '}
{codename && ` Its codename is ${codename}`}
</p>
);
}
}
const planet = <Planet name="Mars" />;
函式式(Function)元件
import React from 'react';
// 函式元件上的 defaultProps 將在未來停止使用
// https://twitter.com/dan_abramov/status/1133878326358171650
// https://github.com/reactjs/rfcs/pull/107
// 我們應該使用預設引數
type Props = {
name: string;
size?: 'Small' | 'Medium' | 'Large'; // 具有 es6 預設引數的屬性應標記為可選
codename?: string;
};
// 共識是輸入解構的 Props 比使用 React.FC<Props> 稍微好一點
// https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#function-components
const Planet = ({name, size = 'Medium', codename}: Props) => {
return (
<p>
{name} is a {size.toLowerCase()} planet.
{codename && ` Its codename is ${codename}`}
</p>
);
};
const planet = <Planet name="Mars" />;
參考
使用 Hooks
為了使元件更易於重用和更易於理解,React
和 React 生態系統
一直趨向於函式式元件和 hooks
。
Hooks
是一種向功能元件新增狀態
和副作用
的便捷方式。它們還為庫提供了一種公開行為的便捷方式。
雖然我們通常支援 hooks
,但我們有一些關於 hooks
應該如何與 Sentry 前端
一起使用的建議。
使用庫中的 hooks
如果一個庫提供了 hooks
,你應該使用它們。
通常,這將是使用庫的唯一方法。
例如,dnd-kit
通過鉤子公開了它的所有原語(primitives
),我們應該按照預期的方式使用該庫。
我們不喜歡使用不用 hooks
的庫。
相反,與具有更大、更復雜的 API
或更大的包大小的庫相比,
更喜歡具有更清晰、更簡單的 API
和更小的包大小的庫。
使用 react 的內建 hooks
useState
, useMemo
, useCallback
, useContext
和 useRef
hooks 在任何函式式元件中都是受歡迎的。
在需要少量狀態或訪問 react
原語(如引用和上下文)的展示元件中,它們通常是一個不錯的選擇。
例如,具有滑出(slide-out)
或可展開狀態(expandable state)
的元件。
useEffect
hook 更復雜,您需要小心地跟蹤您的依賴項並確保通過清理回撥取消訂閱。
應避免 useEffect
的複雜鏈式應用程式,此時 'controller'
元件應保持基於類(class
)。
同樣,useReducer
鉤子與目前尚未確定的狀態管理重疊。
我們希望避免 又一個 狀態管理模式,因此此時避免使用useReducer
。
使用 context
當我們計劃遠離 Reflux
的路徑時,useContext
hook 提供了一個更簡單的實現選項來共享狀態和行為。
當您需要建立新的共享狀態源時,請考慮使用 context
和 useContext
而不是 Reflux
。
此外,可以利用蟲洞狀態管理模式來公開共享狀態
和突變函式
。
使用自定義 hooks
可以建立自定義 hooks
來共享應用程式中的可重用邏輯。
建立自定義 hook
時,函式名稱必須遵循約定,以 “use”
開頭(例如 useTheme
),
並且可以在自定義 hooks
內呼叫其他 hooks
。
注意 hooks 的規則和注意事項
React hooks
有一些規則。
請注意 hooks
建立的規則和限制。
我們使用 ESLint
規則來防止大多數 hook
規則被非法侵入。
此外,我們建議您儘量少使用 useEffect
。
使用多個 useEffect
回撥錶示您有一個高度有狀態
的元件,
您應該使用類(class)
元件來代替。
我們的基礎檢視元件仍然是基於類的
我們的基礎檢視元件(AsyncView
和 AsyncComponent
)是基於類的,並且會持續很長時間。
在構建檢視時請記住這一點。
您將需要額外的 wrapper
元件來訪問 hooks
或將 hook state
轉換為您的 AsyncComponent
的 props
。
不要為 hooks 重寫
雖然 hooks
可以在新程式碼中符合人體工程學,但我們應該避免重寫現有程式碼以利用 hooks
。
重寫需要時間,使我們面臨風險,並且為終端使用者提供的價值很小。
如果您需要重新設計一個元件以使用庫中的 hooks
,那麼還可以考慮從一個類轉換為一個函式元件。
使用 React Testing Library
我們正在將我們的測試從 Enzyme
轉換為 React Testing Library
。
在本指南中,您將找到遵循最佳實踐和避免常見陷阱的技巧。
我們有兩個 ESLint 規則來幫助解決這個問題:
我們努力以一種與應用程式使用方式非常相似的方式編寫測試。
我們不是處理渲染元件
的例項,而是以與使用者相同的方式查詢 DOM
。
我們通過 label
文字找到表單元素(就像使用者一樣),我們從他們的文字中找到連結和按鈕(就像使用者一樣)。
作為此目標的一部分,我們避免測試實現細節,因此重構(更改實現但不是功能)不會破壞測試。
我們通常贊成用例覆蓋
而不是程式碼覆蓋
。
查詢
- 儘可能使用
getBy...
- 僅在檢查不存在時使用
queryBy...
- 僅當期望元素在可能不會立即發生的
DOM
更改後出現時才使用await findBy...
為確保測試類似於使用者與我們的程式碼互動的方式,我們建議使用以下優先順序進行查詢:
getByRole
- 這應該是幾乎所有東西的首選選擇器。
作為這個選擇器的一個很好的獎勵,我們確保我們的應用程式是可訪問的。
它很可能與name
選項getByRole('button', {name: /save/i})
一起使用。
name
通常是表單元素的label
或button
的文字內容,或aria-label
屬性的值。
如果不確定,請使用 logRoles 功能
或查閱可用角色列表。
- https://testing-library.com/docs/dom-testing-library/api-accessibility/#logroles
- https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#roles
getByLabelText
/getByPlaceholderText
- 使用者使用label
文字查詢表單元素,因此在測試表單時首選此選項。getByText
- 在表單之外,文字內容是使用者查詢元素的主要方式。此方法可用於查詢非互動式元素(如div
、span
和paragraph
)。getByTestId
- 因為這不反映使用者如何與應用互動,所以只推薦用於不能使用任何其他選擇器的情況
如果您仍然無法決定使用哪個查詢,
請檢視 testing-playground.com
以及 screen.logTestingPlaygroundURL()
及其瀏覽器擴充套件。
不要忘記,你可以在測試中的任何地方放置 screen.debug()
來檢視當前的 DOM
。
在官方文件中閱讀有關查詢的更多資訊。
技巧
避免從 render
方法中解構查詢函式,而是使用 screen
(examples)。
當您新增/刪除
您需要的查詢時,您不必使 render
呼叫解構保持最新。
您只需要輸入 screen
並讓您的編輯器的自動完成功能處理其餘的工作。
import { mountWithTheme, screen } from "sentry-test/reactTestingLibrary";
// ❌
const { getByRole } = mountWithTheme(<Example />);
const errorMessageNode = getByRole("alert");
// ✅
mountWithTheme(<Example />);
const errorMessageNode = screen.getByRole("alert");
除了檢查不存在(examples)之外,避免將 queryBy...
用於任何事情。
如果沒有找到元素,getBy...
和 findBy...
變數將丟擲更有用的錯誤訊息。
import { mountWithTheme, screen } from "sentry-test/reactTestingLibrary";
// ❌
mountWithTheme(<Example />);
expect(screen.queryByRole("alert")).toBeInTheDocument();
// ✅
mountWithTheme(<Example />);
expect(screen.getByRole("alert")).toBeInTheDocument();
expect(screen.queryByRole("button")).not.toBeInTheDocument();
避免使用 waitFor
等待出現,而是使用 findBy...
(examples)。
這兩個基本上是等價的(findBy...
甚至在其裡面使用了 waitFor
),但是 findBy...
更簡單,我們得到的錯誤資訊也會更好。
import {
mountWithTheme,
screen,
waitFor,
} from "sentry-test/reactTestingLibrary";
// ❌
mountWithTheme(<Example />);
await waitFor(() => {
expect(screen.getByRole("alert")).toBeInTheDocument();
});
// ✅
mountWithTheme(<Example />);
expect(await screen.findByRole("alert")).toBeInTheDocument();
避免使用 waitFor
等待消失,使用 waitForElementToBeRemoved
代替(examples)。
後者使用 MutationObserver
,這比使用 waitFor
定期輪詢 DOM
更有效。
import {
mountWithTheme,
screen,
waitFor,
waitForElementToBeRemoved,
} from "sentry-test/reactTestingLibrary";
// ❌
mountWithTheme(<Example />);
await waitFor(() =>
expect(screen.queryByRole("alert")).not.toBeInTheDocument()
);
// ✅
mountWithTheme(<Example />);
await waitForElementToBeRemoved(() => screen.getByRole("alert"));
更喜歡使用 jest-dom
斷言(examples)。
使用這些推薦的斷言的優點是更好的錯誤訊息、整體語義、一致性和統一性。
import { mountWithTheme, screen } from "sentry-test/reactTestingLibrary";
// ❌
mountWithTheme(<Example />);
expect(screen.getByRole("alert")).toBeTruthy();
expect(screen.getByRole("alert").textContent).toEqual("abc");
expect(screen.queryByRole("button")).toBeFalsy();
expect(screen.queryByRole("button")).toBeNull();
// ✅
mountWithTheme(<Example />);
expect(screen.getByRole("alert")).toBeInTheDocument();
expect(screen.getByRole("alert")).toHaveTextContent("abc");
expect(screen.queryByRole("button")).not.toBeInTheDocument();
按文字搜尋時,最好使用不區分大小寫的正規表示式。它將使測試更能適應變化。
import { mountWithTheme, screen } from "sentry-test/reactTestingLibrary";
// ❌
mountWithTheme(<Example />);
expect(screen.getByText("Hello World")).toBeInTheDocument();
// ✅
mountWithTheme(<Example />);
expect(screen.getByText(/hello world/i)).toBeInTheDocument();
儘可能在 fireEvent
上使用 userEvent
。
userEvent
來自 @testing-library/user-event
包,它構建在 fireEvent
之上,但它提供了幾種更類似於使用者互動的方法。
// ❌
import {
mountWithTheme,
screen,
fireEvent,
} from "sentry-test/reactTestingLibrary";
mountWithTheme(<Example />);
fireEvent.change(screen.getByLabelText("Search by name"), {
target: { value: "sentry" },
});
// ✅
import {
mountWithTheme,
screen,
userEvent,
} from "sentry-test/reactTestingLibrary";
mountWithTheme(<Example />);
userEvent.type(screen.getByLabelText("Search by name"), "sentry");
遷移 - grid-emotion
grid-emotion 已經被棄用一年多了,新專案是 reflexbox。
為了升級到最新版本的 emotion,我們需要遷移出 grid-emotion
。
要遷移,請使用 emotion
將匯入的 <Flex>
和 <Box>
元件替換為帶樣式的元件。
元件
用下面的替換元件,然後刪除必要的 props
並移動到 styled component
。
<Flex>
const Flex = styled('div')`
display: flex;
`;
<Box>
const Box = styled('div')`
`;
props
如果您正在修改匯出的元件,請確保通過該元件的程式碼庫進行 grep
以確保它沒有被渲染為特定於 grid-emotion
的附加屬性。示例是<Panel>
元件。
margin 和 padding
Margin
屬性 以 m
開頭,以 p
填充。下面的例子將使用 margin
作為例子
舊 (grid-emotion) | 新 (css/emotion/styled) |
---|---|
m={2} |
margin: ${space(2); |
mx={2} |
margin-left: ${space(2); margin-right: ${space(2)}; |
my={2} |
margin-top: ${space(2); margin-bottom: ${space(2)}; |
ml={2} |
margin-left: ${space(2); |
mr={2} |
margin-right: ${space(2); |
mt={2} |
margin-top: ${space(2); |
mb={2} |
margin-bottom: ${space(2); |
flexbox
這些是 flexbox
屬性
舊 (grid-emotion) | 新 (css/emotion/styled) |
---|---|
align="center" |
align-items: center; |
justify="center" |
justify-content: center; |
direction="column" |
flex-direction: column; |
wrap="wrap" |
flex-wrap: wrap; |
現在只需忽略 grid-emotion
的匯入語句,例如 // eslint-disable-line no-restricted-imports