diff --git a/contribute/engineering/terminology.md b/contribute/engineering/terminology.md
index 5b28cb7f4b1..5392f9050e4 100644
--- a/contribute/engineering/terminology.md
+++ b/contribute/engineering/terminology.md
@@ -2,16 +2,14 @@
-This document defines technical terms used in Grafana.
+This glossary defines technical terms used in Grafana.
## TLS/SSL
The acronyms [TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security) (Transport Layer Security) and
-[SSL](https://en.wikipedia.org/wiki/SSL) (Secure Socket Layer) are both used to describe the HTTPS security layer,
-and are in practice synonymous. However, TLS is considered the current name for the technology, and SSL is considered
+[SSL](https://en.wikipedia.org/wiki/SSL) (Secure Socket Layer) are both used to describe the HTTPS security layer.
+In practice, they are synonymous. However, TLS is considered the current name for the technology, and SSL is considered
[deprecated](https://tools.ietf.org/html/rfc7568).
-As such, while both terms are in use (also in our codebase) and are indeed interchangeable, TLS is the preferred term.
-That said however, we have at Grafana Labs decided to use both acronyms in combination when referring to this type of
-technology, i.e. _TLS/SSL_. This is in order to not confuse those who may not be aware of them being synonymous,
-and SSL still being so prevalent in common discourse.
+As such, while we use both terms in our codebase and documentation, we generally prefer TLS.
+However, we use both acronyms in combination when referring to this type of technology, that is, _TLS/SSL_. We do this because we don't want to confuse readers who may not be aware of them being synonymous, and SSL is still prevalent in common discourse.
diff --git a/contribute/style-guides/frontend.md b/contribute/style-guides/frontend.md
index 07055362fff..c0eed80ad5d 100644
--- a/contribute/style-guides/frontend.md
+++ b/contribute/style-guides/frontend.md
@@ -1,41 +1,6 @@
-# Frontend Style Guide
-
-Generally we follow the Airbnb [React Style Guide](https://github.com/airbnb/javascript/tree/master/react).
-
-## Table of Contents
-
-- [Frontend Style Guide](#frontend-style-guide)
-
- - [Table of Contents](#table-of-contents)
- - [Basic rules](#basic-rules)
- - [Naming conventions](#naming-conventions)
- - [Use `PascalCase` for:](#use-pascalcase-for)
- - [Typescript class names](#typescript-class-names)
- - [Types and interfaces](#types-and-interfaces)
- - [Enums](#enums)
- - [Use `camelCase` for:](#use-camelcase-for)
- - [Functions](#functions)
- - [Methods](#methods)
- - [Variables](#variables)
- - [React state and properties](#react-state-and-properties)
- - [Emotion class names](#emotion-class-names)
- - [Use `ALL_CAPS` for constants.](#use-all_caps-for-constants)
- - [Use BEM convention for SASS styles.](#use-bem-convention-for-sass-styles)
- - [Typing](#typing)
- - [File and directory naming conventions](#file-and-directory-naming-conventions)
- - [Code organization](#code-organization)
- - [Exports](#exports)
- - [Comments](#comments)
- - [Linting](#linting)
- - [React](#react)
- - [Props](#props)
- - [Name callback props and handlers with an "on" prefix.](#name-callback-props-and-handlers-with-an-on-prefix)
- - [React Component definitions](#react-component-definitions)
- - [React Component constructor](#react-component-constructor)
- - [React Component defaultProps](#react-component-defaultprops)
- - [State management](#state-management)
-
- - [Proposal for removing or replacing Angular dependencies](https://github.com/grafana/grafana/pull/23048)
+# Frontend style guide
+
+Grafana Labs follows the [Airbnb React/JSX Style Guide](https://github.com/airbnb/javascript/tree/master/react) in matters pertaining to React. This guide provides highlights of the style rules we follow.
## Basic rules
@@ -43,11 +8,13 @@ Generally we follow the Airbnb [React Style Guide](https://github.com/airbnb/jav
- Break large components up into sub-components.
- Use spaces for indentation.
-### Naming conventions
+## Naming conventions
+
+Follow these guidelines when naming elements of your code.
-#### Use `PascalCase` for:
+### Class names
-##### Typescript class names
+Use PascalCase. For example:
```typescript
// bad
@@ -61,37 +28,44 @@ class DataLink {
}
```
-##### Types and interfaces
+### Constants
-```
-// bad
-interface buttonProps {
- //...
-}
+Use ALL CAPS for constants. For example:
+
+```typescript
// bad
-interface button_props {
- //...
-}
+const constantValue = "This string won't change";
// bad
-interface IButtonProps {
- //...
-}
+const constant_value = "This string won't change";
// good
-interface ButtonProps {
- //...
-}
+const CONSTANT_VALUE = "This string won't change";
+```
-// bad
-type requestInfo = ...
-// bad
-type request_info = ...
+### Emotion class names
-// good
-type RequestInfo = ...
+Use camelCase. For example:
+
+```typescript
+const getStyles = (theme: GrafanaTheme2) => ({
+ // bad
+ ElementWrapper: css`...`,
+ // bad
+ ['element-wrapper']: css`...`,
+
+ // good
+ elementWrapper: css({
+ padding: theme.spacing(1, 2),
+ background: theme.colors.background.secondary,
+ }),
+});
```
-##### Enums
+Use hook useStyles2(getStyles) to memoize the styles generation and try to avoid passing props to the getStyles function and instead compose classes using Emotion CX function.
+
+### Enums
+
+Use PascalCase. For example:
```
// bad
@@ -105,9 +79,29 @@ enum ButtonVariant {
}
```
-#### Use `camelCase` for:
+### Files and directories
+
+Name files according to the primary export:
+
+- When the primary export is a class or React component, use PascalCase.
+- When the primary export is a function, use camelCase.
+
+For files that export multiple utility functions, use the name that describes the responsibility of grouped utilities. For example, a file that exports math utilities should be named `math.ts`.
+
+- Use `constants.ts` for files that export constants.
+- Use `actions.ts` for files that export Redux actions.
+- Use `reducers.ts` for Redux reducers.
+- Use `*.test.ts(x)` for test files.
+
+For directory names, use dash-case (sometimes called kebab-case).
-##### Functions
+- Use `features/new-important-feature/utils.ts`
+
+### Functions
+
+Use PascalCase. For example:
+
+Use camelCase.
```typescript
// bad
@@ -119,7 +113,43 @@ const calculate_percentage = () => { ... }
const calculatePercentage = () => { ... }
```
-##### Methods
+### Interfaces
+
+Use PascalCase. For example:
+
+```
+// bad
+interface buttonProps {
+ //...
+}
+// bad
+interface button_props {
+ //...
+}
+// bad
+interface IButtonProps {
+ //...
+}
+
+// good
+interface ButtonProps {
+ //...
+}
+
+// bad
+type requestInfo = ...
+// bad
+type request_info = ...
+
+// good
+type RequestInfo = ...
+```
+
+### Methods
+
+Use PascalCase. For example:
+
+Use camelCase.
```typescript
class DateCalculator {
@@ -137,75 +167,103 @@ class DateCalculator {
}
```
-##### Variables
+### React components
-```typescript
-// bad
-const QueryTargets = [];
+Follow these guidelines for naming React components.
+
+#### React callback props and handlers
+
+Name callback props and handlers with an _on_ prefix. For example:
+
+```tsx
// bad
-const query_targets = [];
+handleChange = () => {
-// good
-const queryTargets = [];
-```
+};
-##### React state and properties
+render() {
+ return (
+
+ );
+}
-```typescript
-interface ModalState {
- // bad
- IsActive: boolean;
- // bad
- is_active: boolean;
+// good
+onChange = () => {
- // good
- isActive: boolean;
+};
+
+render() {
+ return (
+
+ );
}
+
```
-##### Emotion class names
+#### React component constructor
+
+Use the following convention when implementing these React components:
```typescript
-const getStyles = (theme: GrafanaTheme2) => ({
- // bad
- ElementWrapper: css`...`,
- // bad
- ['element-wrapper']: css`...`,
+// bad
+constructor(props) {...}
- // good
- elementWrapper: css({
- padding: theme.spacing(1, 2),
- background: theme.colors.background.secondary,
- }),
-});
+// good
+constructor(props: Props) {...}
```
-Use hook useStyles2(getStyles) to memoize the styles generation and try to avoid passing props to the getStyles function and instead compose classes using emotion cx function.
+#### React component defaultProps
-#### Use `ALL_CAPS` for constants.
+Use the following convention when implementing these React components:
```typescript
// bad
-const constantValue = "This string won't change";
+static defaultProps = { ... }
+
+// good
+static defaultProps: Partial = { ... }
+```
+
+#### React component definitions
+
+Use the following convention when implementing these React components:
+
+```jsx
// bad
-const constant_value = "This string won't change";
+export class YourClass extends PureComponent { ... }
// good
-const CONSTANT_VALUE = "This string won't change";
+export class YourClass extends PureComponent<{},{}> { ... }
+```
+
+#### React state and properties
+
+Use camelCase. For example:
+
+```typescript
+interface ModalState {
+ // bad
+ IsActive: boolean;
+ // bad
+ is_active: boolean;
+
+ // good
+ isActive: boolean;
+}
```
-#### Use [BEM](http://getbem.com/) convention for SASS styles.
+### SASS
-_SASS styles are deprecated. Please migrate to Emotion whenever you need to modify SASS styles._
+SASS styles are deprecated. You should migrate to Emotion whenever you need to modify SASS styles.
-### Typing
+### Types
-In general, you should let Typescript infer the types so that there's no need to explicitly define type for each variable.
+In general, you should let TypeScript infer the types so that there's no need to explicitly define the type for each variable.
There are some exceptions to this:
```typescript
-// Typescript needs to know type of arrays or objects otherwise it would infer it as array of any
+// TypeScript needs to know the type of arrays or objects; otherwise, it infers type as an array of any
// bad
const stringArray = [];
@@ -216,7 +274,7 @@ const stringArray: string[] = [];
Specify function return types explicitly in new code. This improves readability by being able to tell what a function returns just by looking at the signature. It also prevents errors when a function's return type is broader than expected by the author.
-> **Note:** We don't have linting for this enabled because of lots of old code that needs to be fixed first.
+> **Note:** Linting is not enabled for this issue because there is old code that needs to be fixed first.
```typescript
// bad
@@ -236,124 +294,61 @@ function transform(value?: string): TransformedValue | undefined {
}
```
-### File and directory naming conventions
-
-Name files according to the primary export:
+### Variables
-- When the primary export is a class or React component, use PascalCase.
-- When the primary export is a function, use camelCase.
+Use PascalCase. For example:
-For files exporting multiple utility functions, use the name that describes the responsibility of grouped utilities. For example, a file exporting math utilities should be named `math.ts`.
+Use camelCase.
-- Use `constants.ts` for files exporting constants.
-- Use `actions.ts` for files exporting Redux actions.
-- Use `reducers.ts` Redux reducers.
-- Use `*.test.ts(x)` for test files.
+```typescript
+// bad
+const QueryTargets = [];
+// bad
+const query_targets = [];
-- Use kebab case for directory names: lowercase, words delimited by hyphen ( `-` ). For example, `features/new-important-feature/utils.ts`.
+// good
+const queryTargets = [];
+```
-### Code organization
+## Code organization
Organize your code in a directory that encloses feature code:
-- Put Redux state and domain logic code in `state` directory (i.e. `features/my-feature/state/actions.ts`).
-- Put React components in `components` directory (i.e. `features/my-feature/components/ButtonPeopleDreamOf.tsx`).
+- Put Redux state and domain logic code in the `state` directory (for example, `features/my-feature/state/actions.ts`).
+- Put React components in the `components` directory (for example, `features/my-feature/components/ButtonPeopleDreamOf.tsx`).
- Put test files next to the test subject.
-- Put containers (pages) in feature root (i.e. `features/my-feature/DashboardPage.tsx`).
-- Put API function calls that isn't a redux thunk in an `api.ts` file within the same directory.
-- Subcomponents can live in the component folders. Small component do not need their own folder.
+- Put containers (pages) in the feature root (for example, `features/my-feature/DashboardPage.tsx`).
+- Put API function calls that aren't a Redux thunk in an `api.ts` file within the same directory.
+- Subcomponents should live in the component folders. Small components don't need their own folder.
- Component SASS styles should live in the same folder as component code.
-For code that needs to be used by external plugin:
+For code that needs to be used by an external plugin:
- Put components and types in `@grafana/ui`.
- Put data models and data utilities in `@grafana/data`.
- Put runtime services interfaces in `@grafana/runtime`.
-#### Exports
+### Exports
- Use named exports for all code you want to export from a file.
-- Use declaration exports (i.e. `export const foo = ...`).
+- Use declaration exports (that is, `export const foo = ...`).
- Avoid using default exports (for example, `export default foo`).
- Export only the code that is meant to be used outside the module.
-### Comments
+### Code comments
- Use [TSDoc](https://github.com/microsoft/tsdoc) comments to document your code.
- Use [react-docgen](https://github.com/reactjs/react-docgen) comments (`/** ... */`) for props documentation.
-- Use inline comments for comments inside functions, classes etc.
+- Use inline comments for comments inside functions, classes, etc.
- Please try to follow the [code comment guidelines](./code-comments.md) when adding comments.
-### Linting
+## Linting
Linting is performed using [@grafana/eslint-config](https://github.com/grafana/eslint-config-grafana).
-## React
-
-Use the following conventions when implementing React components:
-
-### Props
-
-##### Name callback props and handlers with an "on" prefix.
-
-```tsx
-// bad
-handleChange = () => {
-
-};
-
-render() {
- return (
-
- );
-}
-
-// good
-onChange = () => {
+## Functional components
-};
-
-render() {
- return (
-
- );
-}
-
-```
-
-##### React Component definitions
-
-```jsx
-// bad
-export class YourClass extends PureComponent { ... }
-
-// good
-export class YourClass extends PureComponent<{},{}> { ... }
-```
-
-##### React Component constructor
-
-```typescript
-// bad
-constructor(props) {...}
-
-// good
-constructor(props: Props) {...}
-```
-
-##### React Component defaultProps
-
-```typescript
-// bad
-static defaultProps = { ... }
-
-// good
-static defaultProps: Partial = { ... }
-```
-
-### How to declare functional components
-
-We prefer using function declarations over function expressions when creating a new react functional component.
+Use function declarations instead of function expressions when creating a new React functional component. For example:
```typescript
// bad
@@ -366,13 +361,6 @@ export const Component: React.FC = (props) => { ... }
export function Component(props: Props) { ... }
```
-Some interesting readings on the topic:
-
-- [Create React App: Remove React.FC from typescript template](https://github.com/facebook/create-react-app/pull/8177)
-- [Kent C. Dodds: How to write a React Component in Typescript](https://kentcdodds.com/blog/how-to-write-a-react-component-in-typescript)
-- [Kent C. Dodds: Function forms](https://kentcdodds.com/blog/function-forms)
-- [Sam Hendrickx: Why you probably shouldn't use React.FC?](https://medium.com/raccoons-group/why-you-probably-shouldnt-use-react-fc-to-type-your-react-components-37ca1243dd13)
-
## State management
- Don't mutate state in reducers or thunks.
diff --git a/contribute/style-guides/redux.md b/contribute/style-guides/redux.md
index 5952b706ddf..85c59859669 100644
--- a/contribute/style-guides/redux.md
+++ b/contribute/style-guides/redux.md
@@ -2,16 +2,20 @@
Grafana uses [Redux Toolkit](https://redux-toolkit.js.org/) to handle Redux boilerplate code.
-> Some of our Reducers are used by Angular and therefore state is to be considered as mutable for those reducers.
+> **Note:** Some of our reducers are used by Angular; therefore, consider state to be mutable for those reducers.
## Test functionality
+Here's how to test the functioning of your Redux reducers.
+
### reducerTester
-Fluent API that simplifies the testing of reducers
+Use the Fluent API framework to simplify the testing of reducers.
#### Usage
+Example of `reducerTester` in use:
+
```typescript
reducerTester()
.givenReducer(someReducer, initialState)
@@ -21,9 +25,9 @@ reducerTester()
#### Complex usage
-Sometimes you encounter a `resulting state` that contains properties that are hard to compare, such as `Dates`, but you still want to compare that other props in state are correct.
+Sometimes you encounter a _resulting state_ that contains properties that are hard to compare, such as `Dates`, but you still want to evaluate whether other props in state are correct.
-Then you can use `thenStatePredicateShouldEqual` function on `reducerTester` that will return the `resulting state` so that you can expect upon individual properties..
+In these cases, you can evaluate individual properties by using `thenStatePredicateShouldEqual` function on `reducerTester` that will return the resulting state. For example:
```typescript
reducerTester()
@@ -37,10 +41,12 @@ reducerTester()
### thunkTester
-Fluent API that simplifies the testing of thunks.
+Here's a Fluent API function that simplifies the testing of thunks.
#### Usage
+Example of `thunkTester` in use:
+
```typescript
const dispatchedActions = await thunkTester(initialState).givenThunk(someThunk).whenThunkIsDispatched(arg1, arg2, arg3);
@@ -49,7 +55,7 @@ expect(dispatchedActions).toEqual([someAction('reducer tests')]);
## Typing of connected props
-It is possible to infer connected props automatically from `mapStateToProps` and `mapDispatchToProps` using a helper type `ConnectedProps` from Redux. For this to work the `connect` call has to be split into two parts.
+It is possible to infer connected props automatically from `mapStateToProps` and `mapDispatchToProps` using a helper type `ConnectedProps` from Redux. For this to work properly, split the `connect` call into two parts like so:
```typescript
import { connect, ConnectedProps } from 'react-redux';
@@ -80,4 +86,4 @@ class PanelEditorUnconnected extends PureComponent {}
export const PanelEditor = connector(PanelEditorUnconnected);
```
-For more examples, refer to the [Redux docs](https://react-redux.js.org/using-react-redux/static-typing#inferring-the-connected-props-automatically).
+For more examples, refer to the [Redux documentation](https://react-redux.js.org/using-react-redux/static-typing#inferring-the-connected-props-automatically).
diff --git a/contribute/style-guides/testing.md b/contribute/style-guides/testing.md
index 18e4c13e248..18ae9b27be8 100644
--- a/contribute/style-guides/testing.md
+++ b/contribute/style-guides/testing.md
@@ -1,10 +1,11 @@
-# Testing Guidelines
+# Testing guidelines
The goal of this document is to address the most frequently asked "How to" questions related to unit testing.
-## Best practices
+## Some recommended practices for testing
-- Default to the `*ByRole` queries when testing components as it encourages testing with accessibility concerns in mind. It's also possible to use `*ByLabelText` queries. However, the `*ByRole` queries are [more robust](https://testing-library.com/docs/queries/bylabeltext/#name) and are generally recommended over the former.
+- Default to the `*ByRole` queries when testing components because it encourages testing with accessibility concerns in mind.
+- Alternatively, you could use `*ByLabelText` queries for testing components. However, we recommend the `*ByRole` queries because they are [more robust](https://testing-library.com/docs/queries/bylabeltext/#name).
## Testing User Interactions
@@ -13,7 +14,7 @@ We use the [user-event](https://testing-library.com/docs/user-event/intro) libra
There are two important considerations when working with `userEvent`:
1. All methods in `userEvent` are asynchronous, and thus require the use of `await` when called.
-2. Directly calling methods from `userEvent` may not be supported in future versions. As such, it's necessary to first call `userEvent.setup()` prior to the tests. This method returns a `userEvent` instance, complete with all its methods. This setup process can be simplified using a utility function:
+1. Directly calling methods from `userEvent` may not be supported in future versions. As such, it's necessary to first call `userEvent.setup()` prior to the tests. This method returns a `userEvent` instance, complete with all its methods. This setup process can be simplified using a utility function:
```tsx
import { render, screen } from '@testing-library/react';
@@ -32,15 +33,15 @@ it('should render', async () => {
});
```
-## Debugging Tests
+## Debug tests
There are a few utilities that can be useful for debugging tests:
-- [screen.debug()](https://testing-library.com/docs/queries/about/#screendebug) - This function prints a human-readable representation of the document's DOM tree when called without arguments, or the DOM tree of specific node(s) when provided with arguments. It is internally using `console.log` to log the output to terminal.
-- [Testing Playground](https://testing-playground.com/) - An interactive sandbox that allows testing which queries work with specific HTML elements.
-- [logRoles](https://testing-library.com/docs/dom-testing-library/api-debugging/#prettydom) - A utility function that prints out all the implicit ARIA roles for a given DOM tree.
+- [screen.debug()](https://testing-library.com/docs/queries/about/#screendebug) - This function prints a human-readable representation of the document's DOM tree when called without arguments, or the DOM tree of specific node or nodes when provided with arguments. It is internally using `console.log` to log the output to terminal.
+- [Testing Playground](https://testing-playground.com/) - An interactive sandbox that allows testing of which queries work with specific HTML elements.
+- [prettyDOM logRoles](https://testing-library.com/docs/dom-testing-library/api-debugging/#prettydom) - A utility function that prints out all the implicit ARIA roles for a given DOM tree.
-## Testing Select Components
+## Testing select components
Here, the [OrgRolePicker](https://github.com/grafana/grafana/blob/38863844e7ac72c7756038a1097f89632f9985ff/public/app/features/admin/OrgRolePicker.tsx) component is used as an example. This component essentially serves as a wrapper for the `Select` component, complete with its own set of options.
@@ -78,7 +79,7 @@ export function OrgRolePicker({ value, onChange, 'aria-label': ariaLabel, inputI
### Querying the Select Component
-The recommended way to query `Select` components is by using a label. Add a `label` element and provide the `htmlFor` prop with a matching `inputId`. Alternatively, `aria-label` can be specified on the `Select`.
+It is a recommended practice to query `Select` components by using a label. Add a `label` element and provide the `htmlFor` prop with a matching `inputId`. Alternatively, you can specify `aria-label` on the `Select` statement.
```tsx
describe('OrgRolePicker', () => {
@@ -94,7 +95,7 @@ describe('OrgRolePicker', () => {
});
```
-### Testing the Display of Correct Options
+### Test the display of correct options
At times, it might be necessary to verify that the `Select` component is displaying the correct options. In such instances, the best solution is to click the `Select` component and match the desired option using the `*ByText` query.
@@ -129,11 +130,11 @@ it('should select an option', async () => {
});
```
-## Mocking Objects and Functions
+## Mock objects and functions
-### Mocking the `window` Object and Its Methods
+### Mock the `window` object and its methods
-The recommended approach for mocking the `window` object is to use Jest spies. Jest's spy functions provide a built-in mechanism for restoring mocks. This feature eliminates the need to manually save a reference to the `window` object.
+The recommended approach for mocking the `window` object is to use [Jest spies](https://jestjs.io/docs/jest-object). Jest's spy functions provide a built-in mechanism for restoring mocks. This feature eliminates the need to manually save a reference to the `window` object.
```tsx
let windowSpy: jest.SpyInstance;
@@ -156,7 +157,7 @@ it('should test with window', function () {
### Mocking getBackendSrv()
-The `getBackendSrv()` function is used to make HTTP requests to the Grafana backend. It is possible to mock this function using the `jest.mock` method.
+Use the `getBackendSrv()` function to make HTTP requests to the Grafana backend. It is possible to mock this function using the `jest.mock` method.
```tsx
jest.mock('@grafana/runtime', () => ({
@@ -169,9 +170,9 @@ jest.mock('@grafana/runtime', () => ({
#### Mocking getBackendSrv for AsyncSelect
-The `AsyncSelect` component is used to asynchronously load options. As such, it often relies on the `getBackendSrv` for loading the options.
+Use the `AsyncSelect` component to asynchronously load options. This component often relies on the `getBackendSrv` for loading the options.
-Here's how the test would look like for this [OrgPicker](https://github.com/grafana/grafana/blob/38863844e7ac72c7756038a1097f89632f9985ff/public/app/core/components/Select/OrgPicker.tsx) component, which uses `AsyncSelect` under the hood.
+Here's what the test looks like for this [OrgPicker](https://github.com/grafana/grafana/blob/38863844e7ac72c7756038a1097f89632f9985ff/public/app/core/components/Select/OrgPicker.tsx) component, which uses `AsyncSelect` under the hood:
```tsx
import { screen, render } from '@testing-library/react';