Skip to content

Commit aa71010

Browse files
authored
Merge branch 'master' into sergical/docs-deno-vercel-ai
2 parents 4c1a3b1 + 33c8c77 commit aa71010

File tree

52 files changed

+683
-203
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+683
-203
lines changed

develop-docs/development-infrastructure/continuous-integration.mdx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ GitHub actions is our primary CI system and runs our tests on every pull request
1414

1515
## Docker images
1616

17-
We use Google Cloud Build to build our Docker containers. Every single commit (including from PRs) gets built, tested and becomes available at `us.gcr.io/sentryio/sentry:<sha>` with no retention guarantees. Every push to `master` gets built, tested and [pushed to Docker Hub](https://hub.docker.com/r/getsentry/sentry/tags) permanently. You can set `SENTRY_IMAGE` to the following, when working with [getsentry/self-hosted](https://github.com/getsentry/self-hosted):
17+
We primarily use [a composite GitHub action](https://github.com/getsentry/action-build-and-push-images) for building and pushing images. This ensures that the workflows for building and publishing images are standardized, and appropriate metadata is available in the images. The standard for artifact management can be found [here](https://www.notion.so/sentry/Standard-Spec-Artifact-Management-22a8b10e4b5d80ecb6dcca3eb4558f5b).
1818

19-
* `getsentry/sentry:<sha>`, `getsentry/sentry:<short_sha>`, or `getsentry/sentry:latest` to pull from a commit in [getsentry/sentry](https://github.com/getsentry/sentry) `master`
20-
* `us.gcr.io/sentryio/sentry:<sha>` to pull from a commit in any other branch or PR
19+
We use GHCR (github container registry) for images that are used in CI, self-hosted, and local development. GAR (google artifact registry) is used for images that are used in prod.
20+
21+
## Fast-Revert
22+
In order to facilitate quick changes to unblock failures on default branches, many of our repositories have a fast-revert workflow. In order to use it, PR's can be automatically reverted once a label `Trigger: Revert` is applied. More details found [here](https://www.notion.so/sentry/Standard-Spec-Fast-Revert-2388b10e4b5d8019bc98d863703d1b17?pvs=25)
2123

2224

2325
## CI workflow for testing changes with getsentry

develop-docs/engineering-practices/commit-messages.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ Must be one of the following:
119119
A code change that neither fixes a bug nor adds a feature (refactor)
120120
</td>
121121
</tr>
122+
<tr>
123+
<th>chore:</th>
124+
<td>Necessary changes for qualitative improvements that don't affect functionality, e.g. updating dependencies or adding instrumentation</td>
125+
</tr>
122126
<tr>
123127
<th>style:</th>
124128
<td>
Lines changed: 48 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,84 @@
11
---
2-
title: UI Component Library
2+
title: Component Library
33
sidebar_order: 40
44
---
55

66
<Alert>
7-
To jump straight in visit <Link to="https://sentry.io/orgredirect/organizations/:orgslug/stories/"><code>/stories/</code></Link> on your existing Sentry Org/Install.
7+
Jump straight in! Visit <Link to="https://sentry.io/orgredirect/organizations/:orgslug/stories/"><code>/stories/</code></Link> on your existing Sentry installation.
88
</Alert>
99

10-
## What is a Component Library
10+
## What is Scraps?
1111

12-
This component library is a (growing) collection of all the React components and hooks that are available within the getsentry/sentry codebase.
12+
Scraps is Sentry's design system, the **S**tandardized **C**ollection of **R**eusable **A**ssets and **P**atterns for **S**entry. It includes the core primitive React components, hooks, and design patterns that we use to build Sentry, maintained by the Design Engineering team. `/stories` also includes components and hooks for specific product verticals, maintained by the teams responsible for those product verticals.
1313

14-
The library consists of the framework itself, which helps to make stories available & discoverable, as well as the story content. Each story is meant to describe how to use a React Component or hook, as well as the properties, behaviors, and common or recommended combinations.
14+
Each story describes how to use a React component or hook, its public API, intended behavior, and common or recommended usage patterns.
1515

16-
## Accessing and Using the Library
16+
## Accessing Stories
1717

18-
The Component Library is implemented as a webpage inside the main sentry application. Whenever Sentry is running, the Library is available.
18+
Stories is a custom docs framework served under the `/stories` path inside the main Sentry app. It loads any `.mdx` and `.stories.tsx` files in the codebase.
1919

2020
- **Prod Access**<br />
21-
Log in and then visit [`/stories/`](https://sentry.io/orgredirect/organizations/:orgslug/stories/) on your existing Sentry Org. This works the same on sentry.io or for self-hosted installations.
21+
Log in and then visit [`/stories/`](https://sentry.io/orgredirect/organizations/:orgslug/stories/) on your existing Sentry Organization. This works the same on sentry.io or for self-hosted installations.
2222

2323
- **Dev Access**<br />
24-
Whether you are using `pnpm dev-ui` to start a development server, or `sentry devserver` to spin up all the services locally, you can access the Component Library by logging in and changing the path from `/issues/` (the default landing page) to `/stories/`.
24+
Whether you are using `pnpm dev-ui` to start a development server, or `sentry devserver` to spin up all the services locally, you can access Stories by logging in and changing the path from `/issues/` (the default landing page) to `/stories/`.
2525

2626
## Ownership
2727

28-
Stories are explicitly **not owned** by the original author or team that created a given component. **Stories are owned by the reader (you)**.
28+
Stories for core components are owned by the Design Engineering team. Stories for general components are not nessicarily maintained by the original author or team of that component—if you are reading a story and notice a mistake or omission, consider it an opportunity to leave the docs in better shape than you found them!
2929

30-
Therefore if you are reading a story and notice a mistake, omission, or something confusing then be empowered to make a change and improve things!
31-
32-
Similarly if you notice a component is missing from the library be empowered to add it. No one is required to be an expert when adding or editing an entry, but you will make things easier for yourself and your teammates by putting in the effort to be as clear and complete as possible.
30+
The Design Engineering team is always available in the `#discuss-design-engineering` channel on Slack for questions or general feedback.
3331

3432
## Creating new Stories
3533

36-
### Using the `storyBook()` framework
37-
38-
The easiest way to start documenting a single component is to import and use `storyBook()`.
39-
40-
Here's a template to copy, edit, and get started with:
41-
42-
```typescript
43-
// Filename: components/button.stories.tsx
34+
### Using `.mdx`
4435

45-
import {Button} from 'sentry/components/button'; // replace with your component
46-
import storyBook from 'sentry/stories/storyBook';
36+
The easiest way to document a single component is to create an `.mdx` file next to the component. For a new `myButton` component, the file can be named `index.mdx` or `myButton.mdx`.
4737

48-
export default storyBook(Button, story => {
49-
story('Default', () => <Button>Default Button</Button>);
50-
});
38+
```
39+
components/
40+
└── myButton/
41+
├── index.tsx
42+
└── index.mdx
5143
```
5244

53-
This template includes all the basics:
54-
55-
1. The filename is `button.stories.tsx` and sits directly beside `button.tsx` which is the component being described.
56-
2. The file uses a default export; there is no need to name things when there is only one export.
57-
3. To create the `storyBook()` we're passing in the `Button` component directly as the first argument. This sets the title at the top of the page, and avoids typos.
58-
4. We've implemented a function that accepts `story` as its argument. This is inspired by `jest` and the [`describe()`](https://jestjs.io/docs/api#describename-fn) function.
59-
5. Each time we call `story()` we can render out a unit onto the page. This includes a title, and the rendered component example. This is similar to `jest` and the [`test()` or `it()`](https://jestjs.io/docs/api#testname-fn-timeout) function, with the exception that `story` is not a global.
60-
61-
Using this framework you get some nice things automatically:
62-
63-
- Stories are printed in the same order they are written in the source file
64-
- Each `story()` callback can be async
65-
66-
Some tips to remember:
67-
68-
- Don't be afraid to import anything else you need to help illustrate use cases for your component. It's useful to leverage `<p>` and `<Fragment>` in most cases for example.
69-
- If you are demonstrating a component that relies on a global hook like `useOrganization()` or `usePageFilters()`, then you should wrap your story with those context providers manually.
70-
- Named exports, or a mix of named and default exports, are all supported. This is more useful with [non-framework stories](#non-framework-stories).
71-
- You can pass the component itself into `storyBook()` to get a nice title on the page. This avoids typos. But be careful of scenarios where a string should be used instead! For example, in a production build components that use `forwardRef` or HoC's can have minified names. Also hook names are always minified. Pass a string instead.
45+
The file can include the following frontmatter fields.
7246

73-
### Non-framework stories
47+
```yaml
48+
---
49+
title: My Button
50+
description: MyButton is a clickable element that triggers an action.
51+
source: 'sentry/components/myButton'
52+
# optionally include links to figma, js, and a11y resources:
53+
resources:
54+
figma: https://www.figma.com/design/...
55+
js: https://github.com/getsentry/sentry/...
56+
a11y:
57+
WCAG 1.4.3: https://www.w3.org/TR/WCAG22/#contrast-minimum
58+
WAI-ARIA Button Practices: https://www.w3.org/WAI/ARIA/apg/patterns/button/
59+
---
60+
```
7461

75-
It is possible to skip the `storyBook()` framework function and build stories from scratch.
62+
Components can include autogenerated API documentation using our [custom type-loader](https://github.com/getsentry/sentry/blob/master/static/app/stories/type-loader.ts) and exporting an object named `types`. The [inline loader syntax](https://rspack.rs/api/loader-api/inline) is weird, but it works.
7663

77-
Doing this allows full flexibility to define your story.
64+
```mdx
65+
import APIReference from '!!type-loader!sentry/components/myButton/index';
7866

79-
Using default exports, named exports, or a mixture of each is fully supported. But take note that the order they will be rendered is not consistent/defined.
67+
export const types = {MyButton: APIReference.MyButton};
68+
```
8069

81-
Look at [icons.stories.tsx](https://sentry.io/orgredirect/organizations/:orgslug/stories/?name=app/icons/icons.stories.tsx) for an example.
70+
For a good example, see the [button.mdx](https://github.com/getsentry/sentry/blob/master/static/app/components/core/button/index.mdx?plain=1) file.
8271

8372
### Helper Components
8473

85-
There are some helper components specifically built to make common patterns in stories easier to implement. Of course you can import anything in the sentry repo to make your stories richer, and create new helpers to make expressing stories easier!
74+
There are some common helper components available to make common patterns in stories easier to implement.
8675

87-
Helper Components:
76+
```mdx
77+
import * as Storybook from 'sentry/stories';
78+
```
79+
80+
- [`<Demo />`](https://github.com/getsentry/sentry/blob/master/static/app/components/stories/demo.tsx)<br />
81+
Renders a preview frame around a component.
8882

8983
- [`<JSXNode />`](https://github.com/getsentry/sentry/blob/master/static/app/components/stories/jsxNode.tsx)<br />
9084
Render a formatted JSX component name and some properties.<br />
@@ -146,63 +140,6 @@ Helper Components:
146140
</ThemeToggle>
147141
```
148142

143+
## Improving Stories
149144

150-
### Writing a new story
151-
152-
Now that you are armed and ready with the `storyBook()` template from above, and some helper components, it's time to write your new story. What's it going to say?
153-
154-
A good way to start out is just to do a minimal example that shows your component with all the defaults set. This sets a baseline to build on top of and you create for yourself an example to copy+paste and edit as we go.
155-
156-
For button this is the "Default" case and it fits all on one line:
157-
```typescript
158-
story('Default', () => <Button>Default Button</Button>);
159-
```
160-
161-
The next thing to do is follow it up with common and simple props, and all their possible values. Think about things that affects rendering for example sizes and colors, whether something is open or closed by default, things that don't need a lot of explination. One way to do this is by using the `<Matrix />` component, or just listing them out in their own stories one by one.
162-
163-
With the common and simple props out of the way, now is the time to get into more interesting props, like callbacks or complex example data & fixtures. One way to document a callbacks, like `onClick`, is to include `useState()` in your story, and implement a basic counter to demonstrate that the method is being executed; or if you have an `onChange` callback capture the callback args in state, and print that to the screen.
164-
165-
For example:
166-
```typescript
167-
story('onClick Prop', () => {
168-
const [clicks, setClicks] = useState(0);
169-
return (
170-
<Fragment>
171-
<p>clicked {clicks} times</p>
172-
<Button onClick={() => setClicks(prev => prev +1)}>Clicked Me</Button>
173-
</Fragment>
174-
);
175-
});
176-
177-
story('onChange Prop', () => {
178-
const [lastSelected, setLastSelected] = useState(undefined);
179-
return (
180-
<Fragment>
181-
<p>{selected} is selected</p>
182-
<Tabs onChange={(selected) => setLastSelected(selected)} />
183-
</Fragment>
184-
);
185-
}
186-
```
187-
188-
Finally, if you know some common combinations of components include those as well. For example, it's really helpful to be able to see that `<GridEditable />` and `useQueryBasedColumnResize()` are a good pair.
189-
190-
## Ownership & Next Steps
191-
192-
Since all the code for the stories framework is home-grown, anyone can tinker to create features and integrations to make the experience of discovering, reading, and writing, organizing stories much better.
193-
194-
Obviously, documenting a component is a valuable addition to the Component Library. Create a new file and give it a try!
195-
196-
Some ideas for you to tinker with:
197-
198-
- General: Design facelift.
199-
- File Tree: Fix cutoff labels, Maybe by side-scrolling? Click+drag to make file tree wider?
200-
- File Tree: Sort folder to the top.
201-
- File Tree: Add component search. Bonus points if we can search by more than just the filename.
202-
- Stories: Automatically generate a list of component props from typescript types.
203-
- Stories: Include a Code-Viewer, so people can see the code that powers a story inline.
204-
- Stories: Update TS types so `storyBook()` function can accept a hook as the first argument, in addition to `JSXElementConstructor`.
205-
- Helpers: Create a system to reference related components. "Button is related to Link & Link Button". Bonus points if we can verify those references at build time so they don't go stale.
206-
- Helpers: Create a helper (iframe?) to simulate browser window sizes, so `@media` CSS queries can be exercised.
207-
- GetSentry: Create a hook that getsentry can implement so stories from there are included.
208-
- Tests: Ability to use stories as test fixtures, so we can visually see fixtures that tests use. ie: tests that click buttons and verify render is updated.
145+
All code for the stories framework is built and maintained by Sentry's Design Engineering team. However, this tool is for all of us, so contributions are welcome! If you have ideas to improve the experience of discovering, reading, writing, or organizing stories, please share in the `#discuss-design-engineering` channel on Slack.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
title: URL State
3+
sidebar_order: 85
4+
---
5+
6+
**URL state** is any piece of state that should be reflected in the browser URL (typically as query parameters). If a user reloads, shares a link, navigates with back/forward, or opens the page in another tab, the state should be restored from the URL. Typical examples:
7+
8+
- Search queries and filters (e.g., `?project=11276&statsPeriod=14d`)
9+
- Pagination and sorting (e.g., `?page=3&sort=price`)
10+
- UI view controls (selected tab, map zoom, layout mode)
11+
- Lightweight feature switches and flags
12+
13+
<Alert level="warning">
14+
Don’t put sensitive or large data into the URL. Keep it human-readable and bounded.
15+
</Alert>
16+
17+
## nuqs
18+
19+
Going forward, we will be using [nuqs](https://nuqs.dev/) to manage URL state in React. Nuqs is a type-safe search params state manager for React that exposes a `useState` like API that syncs with the URL. It also gracefully handles un-parseable values, supports defaults and throttles updates to the URL out of the box. Nuqs has built-in parsers & serializers for common state types and is customizable so we can make our own.
20+
21+
## Parsers
22+
23+
Per default, everything is parsed as `string`. It is recommended to use any of the built-in parsers or to write your own over doing type assertions or coercions in the component. Types can be inferred from what the parsers return, and it also handles serialization back to the URL.
24+
25+
```tsx
26+
// parsed as string per default
27+
const [cursor, setCursor] = useQueryState('cursor')
28+
29+
// with a predefined parser
30+
const [page, setPage] = useQueryState('page', parseAsNumber)
31+
32+
// with a custom parser
33+
const [mailbox, setMailbox] = useQueryState('mailbox', parseAsMailbox);
34+
```
35+
36+
### Array Parsing
37+
38+
For `SingleParsers`, `nuqs` only considers the first value of a query param in the URL. That means arrays get represented as one value with a separator.
39+
40+
```jsx
41+
useQueryState('project', parseAsArrayOf(parseAsInteger))
42+
// ?project=1,2,3
43+
```
44+
45+
Since we usually want to have Array values represented as multiple url params, we have to use `parseAsNativeArrayOf`, which is a `MultiParser`:
46+
47+
```jsx
48+
useQueryState('foo', parseAsNativeArrayOf(parseAsInteger))
49+
// ?project=1&project=2&project=3
50+
```
51+
52+
## Default Options
53+
54+
Per default, `nuqs` will **replace** the current history entry **without scrolling** to the top of the page. This can be customized on the usage sites:
55+
56+
```jsx
57+
const [state, setState] = useQueryState(
58+
'foo',
59+
{
60+
...parseAsString,
61+
history: 'push',
62+
scroll: true,
63+
}
64+
)
65+
```
66+
67+
Options can also be provided as the second argument to the state setter:
68+
69+
```jsx
70+
setState('bar', { history: 'push', scroll: true })
71+
```

0 commit comments

Comments
 (0)