Skip to content

Commit cd3ac3b

Browse files
Merge remote-tracking branch 'origin/master' into shayna-ch/np-legacy-issue-alerts
# Conflicts: # tests/sentry/notifications/notification_action/test_platform_dispatch.py Co-Authored-By: Shayna Chambless <shayna.chambless@sentry.io>
2 parents 7533c99 + 6e5367c commit cd3ac3b

2,199 files changed

Lines changed: 88876 additions & 50209 deletions

File tree

Some content is hidden

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

‎.agents/skills/design-system/SKILL.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,10 @@ If you answered yes to any of these, **use the primitive instead**.
523523

524524
## Other Core Components
525525

526+
### Menus and Selects
527+
528+
Use `DropdownMenu` for action menus and `CompactSelect` for choices. In forms, use the form system's `field.Select` or `field.CompactSelect`. `Menu` from `@sentry/scraps/menu` is an internal presentation primitive; use it directly only when implementing shared controls such as `DropdownMenu` and `CompactSelect`.
529+
526530
### Avatars
527531

528532
Use the core avatar components (`<UserAvatar/>`, `<TeamAvatar/>`, `<ProjectAvatar/>`, `<OrganizationAvatar/>`, `<SentryAppAvatar/>`, `<DocIntegrationAvatar/>`) from `static/app/components/core/avatar`. For lists of avatars, use `<AvatarList>`.

‎.agents/skills/generate-frontend-forms/SKILL.md‎

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: generate-frontend-forms
3-
description: Guide for creating forms using Sentry's new form system. Use when implementing forms, form fields, validation, or auto-save functionality.
3+
description: Guide for creating forms using Sentry's new form system. Use when implementing forms, form fields, display-only form content, validation, or auto-save functionality.
44
---
55

66
# Form System Guide
@@ -23,6 +23,7 @@ import {z} from 'zod';
2323
import {
2424
AutoSaveForm,
2525
defaultFormOptions,
26+
DisplayField,
2627
setFieldErrors,
2728
useScrapsForm,
2829
} from '@sentry/scraps/form';
@@ -98,7 +99,7 @@ function MyForm() {
9899

99100
## Field Components
100101

101-
All fields are accessed via the `field` render prop and follow consistent patterns.
102+
Registered fields are accessed via the `field` render prop and follow consistent patterns. For display-only information without an input, use `DisplayField` as described below.
102103

103104
### Input Field (Text)
104105

@@ -136,6 +137,8 @@ All fields are accessed via the `field` render prop and follow consistent patter
136137

137138
### Select Field (Single)
138139

140+
Use `field.Select` for form choices. Do not compose a form select from bare `Menu` components; those are internal presentation primitives for shared controls.
141+
139142
```tsx
140143
<form.AppField name="country">
141144
{field => (
@@ -186,6 +189,10 @@ All fields are accessed via the `field` render prop and follow consistent patter
186189
</form.AppField>
187190
```
188191

192+
### Checkbox Group (Multiple Selections)
193+
194+
Use `field.Checkbox.Group` and `field.Checkbox.Item` for multiple selections stored as one string array. Read the [checkbox group docs](../../../static/app/components/core/form/fields.mdx#checkbox-group) for the example, layout, validation, disabled state, and auto-save behavior.
195+
189196
### TextArea Field
190197

191198
```tsx
@@ -304,7 +311,7 @@ Use `field.Base` instead of building custom wrappers that duplicate this logic.
304311

305312
## Layouts
306313

307-
Two layout options are available for positioning labels and fields.
314+
Use `field.Layout.Stack` or `field.Layout.Row` for registered fields. These layouts require field context and provide label/input association. For display-only information, use `DisplayField.Stack` or `DisplayField.Row` instead.
308315

309316
### Stack Layout (Vertical)
310317

@@ -383,6 +390,38 @@ You are allowed to create new layouts if necessary, or not use any layouts at al
383390

384391
---
385392

393+
## Display-Only Information
394+
395+
Use `DisplayField.Row` or `DisplayField.Stack` for dates, status text, and other information without an input. Import `DisplayField` from `@sentry/scraps/form` and render it directly in a `FieldGroup` or alongside registered fields:
396+
397+
```tsx
398+
<DisplayField.Row label={t('Created')}>
399+
<Text>
400+
<DateTime date={data.dateCreated} />
401+
</Text>
402+
</DisplayField.Row>
403+
```
404+
405+
- `Row` uses the same responsive layout as `field.Layout.Row`: it stacks below the `md` container breakpoint when a query container is present. Without a query container, it stays in a row.
406+
- `Stack` always places the value below the term.
407+
- Both accept `label`, `children`, `hintText`, `padding`, and `variant="compact"`. Compact mode puts the hint in a tooltip; otherwise it appears below the term in a row and after the value in a stack.
408+
- They render `dl`/`dt`/`dd`, without a `<label>`, input association, validation, or error-focus registration. No form or field context is required.
409+
- Style the displayed value explicitly, for example with `<Text>`; `DisplayField` does not wrap its children in `Text`.
410+
411+
Do not register display-only information with `AppField` or `AutoSaveForm`, or add it to the schema or `defaultValues` just for layout. For a value derived from form state, calculate it during render with `form.Subscribe` rather than storing a second value with `form.setFieldValue`:
412+
413+
```tsx
414+
<form.Subscribe selector={state => state.values.provider}>
415+
{provider => (
416+
<DisplayField.Stack label={t('Selected provider')}>
417+
<Text>{provider ?? t('None selected')}</Text>
418+
</DisplayField.Stack>
419+
)}
420+
</form.Subscribe>
421+
```
422+
423+
A read-only input is still a real input. Keep `AppField`, `field.Layout.*`, and `field.Input readOnly` for values that users select or copy from an input. Keep custom controls that handle form state on `field.Base`; do not replace them with `DisplayField`.
424+
386425
## Field Groups
387426

388427
Group related fields into sections with a title.
@@ -1054,7 +1093,8 @@ When creating a new form:
10541093
- [ ] Set `defaultValues` matching schema shape (use `z.input<typeof schema>` if schema has `.refine()`)
10551094
- [ ] Set `validators: {onDynamic: schema}`
10561095
- [ ] Wrap with `<form.AppForm form={form}>`
1057-
- [ ] Use `<form.AppField>` for each field
1096+
- [ ] Use `<form.AppField>` for each registered input, including read-only inputs
1097+
- [ ] Use `DisplayField.Row` or `DisplayField.Stack` for display-only information; keep it out of form state
10581098
- [ ] Choose appropriate layout (Stack or Row)
10591099
- [ ] Narrow unknown errors to `RequestError` at the call site, convert them with `requestErrorToFieldErrors`, then call `setFieldErrors`
10601100
- [ ] Add `<form.SubmitButton>` for submission
@@ -1081,4 +1121,5 @@ When creating auto-save fields:
10811121
| `static/app/scrapsProviders/formError.tsx` | Sentry request-error mapper |
10821122
| `static/app/components/core/form/field/*.tsx` | Individual field components |
10831123
| `static/app/components/core/form/layout/index.tsx` | Layout components |
1124+
| `static/app/components/core/form/displayField.tsx` | Display-only layouts |
10841125
| `static/app/components/core/form/form.stories.tsx` | Usage examples |

‎.agents/skills/migrate-frontend-forms/SKILL.md‎

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,24 @@ This skill helps migrate forms from Sentry's legacy form system (JsonForm, FormM
99

1010
## Feature Mapping
1111

12-
| Old System | New System | Notes |
13-
| -------------------- | --------------------- | ---------------------------------------------------- |
14-
| `saveOnBlur: true` | `AutoSaveForm` | Default behavior |
15-
| `confirm` | `confirm` prop | `string \| ((value) => string \| undefined)` |
16-
| `showHelpInTooltip` | `variant="compact"` | On layout components |
17-
| `disabledReason` | `disabled="reason"` | String shows tooltip |
18-
| `extraHelp` | JSX in layout | Render `<Text>` below field |
19-
| `getData` | `mutationFn` | Transform data in mutation function |
20-
| `mapFormErrors` | Request error adapter | Explicit for regular forms; provided for auto-save |
21-
| `saveMessage` | `onSuccess` | Show toast in mutation onSuccess callback |
22-
| `formatMessageValue` | `onSuccess` | Control toast content in onSuccess callback |
23-
| `resetOnError` | `onError` | Call form.reset() in mutation onError |
24-
| `saveOnBlur: false` | `useScrapsForm` | Use regular form with explicit Save button |
25-
| (automatic) | `form.reset()` | Call after successful mutation if form stays on page |
26-
| `help` | `hintText` | On layout components |
27-
| `label` | `label` | On layout components |
28-
| `required` | `required` | On layout + Zod schema |
12+
| Old System | New System | Notes |
13+
| -------------------- | ----------------------------------------- | ---------------------------------------------------- |
14+
| `saveOnBlur: true` | `AutoSaveForm` | Default behavior |
15+
| `confirm` | `confirm` prop | `string \| ((value) => string \| undefined)` |
16+
| `showHelpInTooltip` | `variant="compact"` | On layout components |
17+
| `disabledReason` | `disabled="reason"` | String shows tooltip |
18+
| `extraHelp` | JSX in layout | Render `<Text>` below field |
19+
| `getData` | `mutationFn` | Transform data in mutation function |
20+
| `mapFormErrors` | Request error adapter | Explicit for regular forms; provided for auto-save |
21+
| `saveMessage` | `onSuccess` | Show toast in mutation onSuccess callback |
22+
| `formatMessageValue` | `onSuccess` | Control toast content in onSuccess callback |
23+
| `resetOnError` | `onError` | Call form.reset() in mutation onError |
24+
| `saveOnBlur: false` | `useScrapsForm` | Use regular form with explicit Save button |
25+
| (automatic) | `form.reset()` | Call after successful mutation if form stays on page |
26+
| `help` | `hintText` | On layout components |
27+
| `label` | `label` | On layout components |
28+
| `required` | `required` | On layout + Zod schema |
29+
| Display-only content | `DisplayField.Row` / `DisplayField.Stack` | No registered input or form state |
2930

3031
## Feature Details
3132

@@ -591,6 +592,24 @@ return (
591592

592593
A form that's never actually submitted bypasses validation, pending/disabled state, and field-error wiring.
593594

595+
## Display-Only Information
596+
597+
For dates, status text, or other content without an input, use `DisplayField.Row` or `DisplayField.Stack` from `@sentry/scraps/form`. Do not create an `AppField` or `AutoSaveForm` just to get a layout:
598+
599+
```tsx
600+
<DisplayField.Row label={t('Created')}>
601+
<Text>
602+
<DateTime date={data.dateCreated} />
603+
</Text>
604+
</DisplayField.Row>
605+
```
606+
607+
Remove schema entries and default values that existed only to display this content. Preserve text styling with `<Text>` where needed. Use an existing `form.Subscribe` to calculate derived display values during render; do not copy them into form state with `form.setFieldValue`.
608+
609+
`Row` matches the responsive field layout; `Stack` always stacks. These components use description-list semantics without a `<label>`, field context, or error-focus registration. Keep actual read-only inputs on `field.Input readOnly` inside `AppField` and `field.Layout.*` so they retain input association. Custom controls that handle form state still need `field.Base`.
610+
611+
See the [form generation guide](../generate-frontend-forms/SKILL.md#display-only-information) for layout props and a subscription example.
612+
594613
## Preserving Form Search Functionality
595614

596615
Sentry's SettingsSearch allows users to search for individual settings fields. When migrating forms, you must preserve this searchability by wrapping migrated forms with `FormSearch`.
@@ -728,7 +747,8 @@ For a nullable number field, use `z.number().nullable()` in the form schema and
728747
- [ ] Replace JsonForm/FormModel with useScrapsForm or AutoSaveForm
729748
- [ ] No generics on `useMutation` — type the `mutationFn` payload and use `fetchMutation<T>` for the return type
730749
- [ ] When using `useScrapsForm` with a Save button: mutation runs in `onSubmit`, triggered by `<form.SubmitButton>` (no form that's never submitted)
731-
- [ ] Convert field config objects to JSX AppField components
750+
- [ ] Convert input field config objects to JSX AppField components
751+
- [ ] Convert display-only content to `DisplayField.Row` or `DisplayField.Stack`, without schema or form-state entries
732752
- [ ] Validate conditionally required fields on the rendered `AppField`, with a permissive form schema
733753
- [ ] Replace `help` → `hintText` on layouts
734754
- [ ] Replace `showHelpInTooltip` → `variant="compact"`

‎.agents/skills/sentry-security/references/enforcement-layers.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Classes like `Validator`, `ManualTokenRefresher`, `GrantExchanger`, and `Refresh
7171

7272
## Non-DRF Views
7373

74-
OAuth views (`OAuthAuthorizeView`, `OAuthDeviceView`, `OAuthTokenView`) are plain Django views, **not** DRF endpoints. Layers 1–4 do not apply to the view itself. Check the view's own authentication decorators, `dispatch()`, and handler logic directly.
74+
OAuth token exchange (`OAuthTokenView`) and device code issuance (`OAuthDeviceAuthorizationView`) use plain Django views. Check their authentication, `dispatch()`, and handler logic directly. Consent uses the DRF `OAuthAuthorizeEndpoint` and `OAuthDeviceEndpoint`; layers 1–4 apply to those endpoints.
7575

7676
However, tokens issued by these views are later used at DRF API endpoints where layers 1–7 **do** apply. See "Cross-Flow Enforcement" below.
7777

‎.codeowners-config.yml‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ team_allowlist:
4949
- getsentry/profiling
5050
- getsentry/python-typing
5151
- getsentry/release-approvers
52-
- getsentry/replay-backend
53-
- getsentry/replay-frontend
52+
- getsentry/replay
5453
- getsentry/revenue
5554
- getsentry/scm
5655
- getsentry/security

‎.craft.yml‎

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
minVersion: '2.33.1'
1+
minVersion: '3.0.0'
22
workspaces:
33
sentry:
44
preReleaseCommand: bash scripts/bump-version.sh
55
releaseBranchPrefix: releases
66
changelog:
77
filePath: CHANGES
88
policy: auto
9+
# Files outside every workspace (src/, tests/, root config) belong to
10+
# this changelog only, not to the scraps or icons changelogs.
11+
unownedPaths: own
12+
# Craft treats pnpm workspace directories as separately owned and drops
13+
# their commits from this changelog unless they are listed here.
14+
includePaths:
15+
- api-docs
16+
- static/app
17+
- static/gsAdmin
18+
- static/gsApp
19+
- tests/js
920
statusProvider:
1021
name: github
1122
config:
@@ -38,7 +49,7 @@ workspaces:
3849
name: github
3950
config:
4051
contexts:
41-
- scraps-package-verify
52+
- scraps-package-pack
4253
artifactProvider:
4354
name: github
4455
config:
@@ -52,3 +63,27 @@ workspaces:
5263
access: public
5364
versioning:
5465
policy: auto
66+
static/packages/icons:
67+
preReleaseCommand: >-
68+
sh -c 'npm --prefix static/packages/icons --no-git-tag-version --package-lock=false --allow-same-version version "$CRAFT_NEW_VERSION"'
69+
releaseBranchPrefix: icons/release
70+
changelog:
71+
policy: auto
72+
statusProvider:
73+
name: github
74+
config:
75+
contexts:
76+
- icons-package-pack
77+
artifactProvider:
78+
name: github
79+
config:
80+
artifacts:
81+
Icons package:
82+
- icons-tarball
83+
targets:
84+
- name: github
85+
tagPrefix: icons@
86+
- name: npm
87+
access: public
88+
versioning:
89+
policy: auto

0 commit comments

Comments
 (0)