Skip to content

fix(Popup): placement type#2700

Open
xexys wants to merge 1 commit into
mainfrom
fix-popup-placement-types
Open

fix(Popup): placement type#2700
xexys wants to merge 1 commit into
mainfrom
fix-popup-placement-types

Conversation

@xexys

@xexys xexys commented Jun 3, 2026

Copy link
Copy Markdown

Summary by Sourcery

Align Popup and Popover placement typing and docs to support readonly placement arrays and introduce a shared helper for detecting readonly arrays.

Enhancements:

  • Update Popup and Popover README property tables to document placement props as accepting readonly placement arrays.
  • Refine PopupPlacement type to use ReadonlyArray instead of a mutable array type and adjust placement handling to rely on a readonly-array guard.
  • Add a reusable isReadonlyArray utility to standardize detection of readonly array-like placement values.

@xexys
xexys requested review from ValeraS, amje and korvin89 as code owners June 3, 2026 11:59
@sourcery-ai

sourcery-ai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Aligns Popup and Popover placement typing and docs with readonly placement arrays, and introduces a shared runtime guard for readonly arrays used in Popup placement handling.

File-Level Changes

Change Details Files
Allow Popup and Popover placement props to accept readonly arrays and reflect this in documentation.
  • Update PopupPlacement type to use ReadonlyArray instead of mutable Placement[] in TypeScript types.
  • Adjust Popup and Popover README tables to document placement as accepting ReadonlyArray values.
  • Keep existing literal auto/auto-start/auto-end options intact while broadening array typing to readonly.
src/components/Popup/types.ts
src/components/Popup/README.md
src/components/Popup/README-ru.md
src/components/Popover/README.md
Add and use a shared runtime type guard for readonly arrays in Popup placement logic.
  • Introduce isReadonlyArray utility that narrows unknown to ReadonlyArray using Array.isArray.
  • Replace Array.isArray check in getPlacementOptions with isReadonlyArray to keep runtime checks aligned with readonly typing.
src/components/Popup/utils.ts
src/components/utils/isReadonlyArray.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The new isReadonlyArray wrapper is currently just an alias for Array.isArray; consider either inlining Array.isArray where used or expanding this helper’s responsibilities (e.g., colocating with other shared type guards or documenting its intended use) so the indirection is justified.
  • You can make isReadonlyArray generic (e.g., export function isReadonlyArray<T>(value: readonly T[] | unknown): value is readonly T[]) so callers get proper element type narrowing instead of ReadonlyArray<unknown>.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `isReadonlyArray` wrapper is currently just an alias for `Array.isArray`; consider either inlining `Array.isArray` where used or expanding this helper’s responsibilities (e.g., colocating with other shared type guards or documenting its intended use) so the indirection is justified.
- You can make `isReadonlyArray` generic (e.g., `export function isReadonlyArray<T>(value: readonly T[] | unknown): value is readonly T[]`) so callers get proper element type narrowing instead of `ReadonlyArray<unknown>`.

## Individual Comments

### Comment 1
<location path="src/components/utils/isReadonlyArray.ts" line_range="1-2" />
<code_context>
+export function isReadonlyArray(value: unknown): value is ReadonlyArray<unknown> {
+    return Array.isArray(value);
+}
</code_context>
<issue_to_address>
**suggestion:** Preserve element type in the `isReadonlyArray` type guard using a generic signature.

The current predicate `value is ReadonlyArray<unknown>` prevents callers from getting element-type narrowing from this helper. Making it generic preserves the element type and avoids widening to `unknown`, e.g.:

```ts
export function isReadonlyArray<T>(
  value: readonly T[] | T[] | unknown,
): value is ReadonlyArray<T> {
  return Array.isArray(value);
}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/components/utils/isReadonlyArray.ts Outdated
@xexys
xexys marked this pull request as draft June 3, 2026 12:01
@gravity-ui

gravity-ui Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Preview is ready.

@xexys
xexys force-pushed the fix-popup-placement-types branch from 525303d to 59e2fd5 Compare June 3, 2026 12:03
@gravity-ui

gravity-ui Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

🎭 Component Tests Report is ready.

@xexys
xexys force-pushed the fix-popup-placement-types branch 3 times, most recently from a6394d3 to 71ff73a Compare June 3, 2026 12:17
@xexys xexys changed the title fix(Popup): placement types fix(Popup,Popover): placement types Jun 3, 2026
@xexys xexys changed the title fix(Popup,Popover): placement types fix(Popup,Popover): placement type Jun 3, 2026
@xexys
xexys force-pushed the fix-popup-placement-types branch from 71ff73a to 58253ad Compare June 3, 2026 12:20
@xexys
xexys marked this pull request as ready for review June 3, 2026 12:44

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 5 issues, and left some high level feedback:

  • The isReadonlyArray helper currently just forwards to Array.isArray; unless you plan to reuse this type guard in multiple places, it may be clearer to inline Array.isArray and avoid introducing a new utility with a potentially misleading name.
  • Renaming the union member from Placement[] to ReadonlyArray<Placement> doesn’t actually forbid mutable arrays at runtime; if the intent is only to better model existing readonly usage, consider adding a short code comment near PopupPlacement to clarify that both mutable and readonly arrays are accepted.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `isReadonlyArray` helper currently just forwards to `Array.isArray`; unless you plan to reuse this type guard in multiple places, it may be clearer to inline `Array.isArray` and avoid introducing a new utility with a potentially misleading name.
- Renaming the union member from `Placement[]` to `ReadonlyArray<Placement>` doesn’t actually forbid mutable arrays at runtime; if the intent is only to better model existing readonly usage, consider adding a short code comment near `PopupPlacement` to clarify that both mutable and readonly arrays are accepted.

## Individual Comments

### Comment 1
<location path="src/components/utils/isReadonlyArray.ts" line_range="1-2" />
<code_context>
+export function isReadonlyArray(value: unknown): value is ReadonlyArray<unknown> {
+    return Array.isArray(value);
+}
</code_context>
<issue_to_address>
**suggestion:** Consider making the type guard generic to preserve element types when narrowing.

The current predicate always narrows to `ReadonlyArray<unknown>`, discarding any existing element type. Consider making it generic, e.g. `export function isReadonlyArray<T>(value: unknown): value is ReadonlyArray<T>` (or `readonly T[]`), so callers preserve their specific element type after the check.

Suggested implementation:

```typescript
export function isReadonlyArray<T>(value: unknown): value is ReadonlyArray<T> {

```

```typescript
    return Array.isArray(value);
}

```
</issue_to_address>

### Comment 2
<location path="src/components/Popup/README-ru.md" line_range="157" />
<code_context>
+| focusOrder              | The order in which focus circle                                                            |            `Array<'reference' \| 'floating' \| 'content'>`            |  `['content']`  |
</code_context>
<issue_to_address>
**suggestion (typo):** Fix the incomplete sentence in the `focusOrder` description.

"The order in which focus circle" is grammatically incomplete. Please rephrase, e.g. "The order in which focus moves" or "The order in which focus is cycled," to clarify the description.

```suggestion
| focusOrder              | The order in which focus is cycled                                                         |            `Array<'reference' \| 'floating' \| 'content'>`            |  `['content']`  |
```
</issue_to_address>

### Comment 3
<location path="src/components/Popup/README-ru.md" line_range="165-168" />
<code_context>
+| onTransitionIn          | On start open popup animation                                                              |                              `Function`                               |                 |
</code_context>
<issue_to_address>
**suggestion (typo):** Improve grammar in the `onTransition*` property descriptions.

These descriptions are grammatically awkward. Suggest: "Called when the open popup animation starts/finishes" and "Called when the close popup animation starts/finishes" for clearer wording.

Suggested implementation:

```
| onTransitionIn          | Called when the open popup animation starts                                                |                              `Function`                               |                 |

```

```
| onTransitionInComplete  | Called when the open popup animation finishes                                              |                              `Function`                               |                 |

```

```
| onTransitionOut         | Called when the close popup animation starts                                               |                              `Function`                               |                 |

```

```
| onTransitionOutComplete | Called when the close popup animation finishes                                             |                              `Function`                               |                 |

```
</issue_to_address>

### Comment 4
<location path="src/components/Popup/README.md" line_range="157" />
<code_context>
+| focusOrder              | The order in which focus circle                                                            |            `Array<'reference' \| 'floating' \| 'content'>`            |  `['content']`  |
</code_context>
<issue_to_address>
**suggestion (typo):** Clarify and complete the `focusOrder` description sentence.

The phrase "The order in which focus circle" is incomplete. Consider revising to something like "The order in which focus moves" or "The order in which focus is cycled" so the description is grammatically correct.

```suggestion
| focusOrder              | The order in which focus is cycled between popup elements                                  |            `Array<'reference' \| 'floating' \| 'content'>`            |  `['content']`  |
```
</issue_to_address>

### Comment 5
<location path="src/components/Popup/README.md" line_range="165-168" />
<code_context>
+| onTransitionIn          | On start open popup animation                                                              |                              `Function`                               |                 |
</code_context>
<issue_to_address>
**suggestion (typo):** Tighten up the grammar in the `onTransition*` descriptions.

The current phrasing (e.g. "On start open popup animation") is ungrammatical. Consider something like "Called when the open popup animation starts/finishes" and "Called when the close popup animation starts/finishes" to improve readability.

Suggested implementation:

```
| onTransitionIn          | Called when the open popup animation starts                                               |                              `Function`                               |                 |

```

```
| onTransitionInComplete  | Called when the open popup animation finishes                                             |                              `Function`                               |                 |

```

```
| onTransitionOut         | Called when the close popup animation starts                                              |                              `Function`                               |                 |

```

```
| onTransitionOutComplete | Called when the close popup animation finishes                                            |                              `Function`                               |                 |

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/components/utils/isReadonlyArray.ts Outdated
Comment on lines +1 to +2
export function isReadonlyArray(value: unknown): value is ReadonlyArray<unknown> {
return Array.isArray(value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider making the type guard generic to preserve element types when narrowing.

The current predicate always narrows to ReadonlyArray<unknown>, discarding any existing element type. Consider making it generic, e.g. export function isReadonlyArray<T>(value: unknown): value is ReadonlyArray<T> (or readonly T[]), so callers preserve their specific element type after the check.

Suggested implementation:

export function isReadonlyArray<T>(value: unknown): value is ReadonlyArray<T> {
    return Array.isArray(value);
}

| floatingInteractions | Override `Floating UI` interactions | `Array<ElementProps>` | |
| floatingMiddlewares | `Floating UI` middlewares. If set, they will completely overwrite the default middlewares. | `Array<Middleware>` | |
| floatingStyles | Styles to apply to the `Floating UI` element | `React.CSSProperties` | |
| focusOrder | The order in which focus circle | `Array<'reference' \| 'floating' \| 'content'>` | `['content']` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (typo): Fix the incomplete sentence in the focusOrder description.

"The order in which focus circle" is grammatically incomplete. Please rephrase, e.g. "The order in which focus moves" or "The order in which focus is cycled," to clarify the description.

Suggested change
| focusOrder | The order in which focus circle | `Array<'reference' \| 'floating' \| 'content'>` | `['content']` |
| focusOrder | The order in which focus is cycled | `Array<'reference' \| 'floating' \| 'content'>` | `['content']` |

Comment on lines +165 to +168
| onTransitionIn | On start open popup animation | `Function` | |
| onTransitionInComplete | On finish open popup animation | `Function` | |
| onTransitionOut | On start close popup animation | `Function` | |
| onTransitionOutComplete | On finish close popup animation | `Function` | |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (typo): Improve grammar in the onTransition* property descriptions.

These descriptions are grammatically awkward. Suggest: "Called when the open popup animation starts/finishes" and "Called when the close popup animation starts/finishes" for clearer wording.

Suggested implementation:

| onTransitionIn          | Called when the open popup animation starts                                                |                              `Function`                               |                 |

| onTransitionInComplete  | Called when the open popup animation finishes                                              |                              `Function`                               |                 |

| onTransitionOut         | Called when the close popup animation starts                                               |                              `Function`                               |                 |

| onTransitionOutComplete | Called when the close popup animation finishes                                             |                              `Function`                               |                 |

| floatingInteractions | Override `Floating UI` interactions | `Array<ElementProps>` | |
| floatingMiddlewares | `Floating UI` middlewares. If set, they will completely overwrite the default middlewares. | `Array<Middleware>` | |
| floatingStyles | Styles to apply to the `Floating UI` element | `React.CSSProperties` | |
| focusOrder | The order in which focus circle | `Array<'reference' \| 'floating' \| 'content'>` | `['content']` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (typo): Clarify and complete the focusOrder description sentence.

The phrase "The order in which focus circle" is incomplete. Consider revising to something like "The order in which focus moves" or "The order in which focus is cycled" so the description is grammatically correct.

Suggested change
| focusOrder | The order in which focus circle | `Array<'reference' \| 'floating' \| 'content'>` | `['content']` |
| focusOrder | The order in which focus is cycled between popup elements | `Array<'reference' \| 'floating' \| 'content'>` | `['content']` |

Comment on lines +165 to +168
| onTransitionIn | On start open popup animation | `Function` | |
| onTransitionInComplete | On finish open popup animation | `Function` | |
| onTransitionOut | On start close popup animation | `Function` | |
| onTransitionOutComplete | On finish close popup animation | `Function` | |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (typo): Tighten up the grammar in the onTransition* descriptions.

The current phrasing (e.g. "On start open popup animation") is ungrammatical. Consider something like "Called when the open popup animation starts/finishes" and "Called when the close popup animation starts/finishes" to improve readability.

Suggested implementation:

| onTransitionIn          | Called when the open popup animation starts                                               |                              `Function`                               |                 |

| onTransitionInComplete  | Called when the open popup animation finishes                                             |                              `Function`                               |                 |

| onTransitionOut         | Called when the close popup animation starts                                              |                              `Function`                               |                 |

| onTransitionOutComplete | Called when the close popup animation finishes                                            |                              `Function`                               |                 |

@xexys
xexys force-pushed the fix-popup-placement-types branch from 2eed025 to b8cba09 Compare June 5, 2026 13:06
Comment thread src/components/utils/isReadonlyArray.ts Outdated
@@ -0,0 +1,3 @@
export function isReadonlyArray(value: unknown): value is ReadonlyArray<unknown> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use this helper in utils file, instead of adding new file

const isArray = Array.isArray as <T>(value: T) => value is Extract<T, readonly unknown[]>;

@amje amje changed the title fix(Popup,Popover): placement type fix(Popup): placement type Jun 11, 2026
@xexys
xexys force-pushed the fix-popup-placement-types branch from b8cba09 to 6b703ef Compare June 11, 2026 15:55
@xexys
xexys force-pushed the fix-popup-placement-types branch from 6b703ef to 6a3ea1b Compare June 11, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants