Repository: gravity-ui/uikit
Pull request #2180: feat(Disclosure): dynamic Qa
Author: login: itwillwork
Created: 2025-03-24T10:19:30Z | Merged: 2025-03-26T10:18:34Z

## Summary by Sourcery

Implement dynamic QA identifiers for the Disclosure component, enhancing test flexibility and customization. Refactor related components to support this change and update tests accordingly.

New Features:
- Introduce dynamic QA identifiers for the Disclosure component, allowing for more flexible and customizable QA testing.

Enhancements:
- Refactor the Disclosure component to pass QA identifiers dynamically to child components, improving the flexibility of test identification.

Tests:
- Update tests for the Disclosure component to use dynamic QA identifiers, ensuring they align with the new implementation.


Diff:
diff --git a/src/components/Disclosure/Disclosure.tsx b/src/components/Disclosure/Disclosure.tsx
index 09ef0bc5e9..295b0d2645 100644
--- a/src/components/Disclosure/Disclosure.tsx
+++ b/src/components/Disclosure/Disclosure.tsx
@@ -61,7 +61,10 @@ export const Disclosure: React.FunctionComponent<DisclosureProps> & DisclosureCo
             qa,
         } = props;
 
-        const [summaryContent, detailsContent] = prepareChildren(children);
+        const [summaryContent, detailsContent] = prepareChildren(children, {
+            disclosureQa: qa,
+        });
+
         return (
             <DisclosureProvider
                 disabled={disabled}
@@ -81,7 +84,11 @@ export const Disclosure: React.FunctionComponent<DisclosureProps> & DisclosureCo
         );
     });
 
-function prepareChildren(children: React.ReactNode) {
+interface PrepareParams {
+    disclosureQa?: string;
+}
+
+function prepareChildren(children: React.ReactNode, {disclosureQa}: PrepareParams) {
     const items = React.Children.toArray(children);
 
     let summary, details;
@@ -100,11 +107,15 @@ function prepareChildren(children: React.ReactNode) {
         content.push(item);
     }
     if (content.length > 0) {
-        details = <DisclosureDetails>{content}</DisclosureDetails>;
+        details = (
+            <DisclosureDetails qa={disclosureQa && `${disclosureQa}-details`}>
+                {content}
+            </DisclosureDetails>
+        );
     }
     if (!summary) {
         summary = (
-            <DisclosureSummary>
+            <DisclosureSummary qa={disclosureQa && `${disclosureQa}-summary`}>
                 {(props) => <DefaultDisclosureSummary {...props} />}
             </DisclosureSummary>
         );
diff --git a/src/components/Disclosure/DisclosureDetails/DisclosureDetails.tsx b/src/components/Disclosure/DisclosureDetails/DisclosureDetails.tsx
index 750e03a898..0b213c5590 100644
--- a/src/components/Disclosure/DisclosureDetails/DisclosureDetails.tsx
+++ b/src/components/Disclosure/DisclosureDetails/DisclosureDetails.tsx
@@ -4,15 +4,16 @@ import * as React from 'react';
 
 import {CSSTransition} from 'react-transition-group';
 
+import type {QAProps} from '../../types';
 import {getCSSTransitionClassNames} from '../../utils/transition';
 import {useDisclosureAttributes} from '../DisclosureContext';
 import {DisclosureQa, b} from '../constants';
 
-export interface DisclosureDetailsProps {
+export interface DisclosureDetailsProps extends QAProps {
     children: React.ReactNode;
 }
 
-export function DisclosureDetails({children}: DisclosureDetailsProps) {
+export function DisclosureDetails({children, qa}: DisclosureDetailsProps) {
     const containerRef = React.useRef<HTMLDivElement>(null);
     const {ariaControls, ariaLabelledby, keepMounted, expanded} = useDisclosureAttributes();
 
@@ -32,7 +33,7 @@ export function DisclosureDetails({children}: DisclosureDetailsProps) {
                 role="region"
                 aria-labelledby={ariaLabelledby}
                 className={b('content', {visible: expanded})}
-                data-qa={DisclosureQa.DETAILS}
+                data-qa={qa || DisclosureQa.DETAILS}
             >
                 {children}
             </div>
diff --git a/src/components/Disclosure/DisclosureSummary/DisclosureSummary.tsx b/src/components/Disclosure/DisclosureSummary/DisclosureSummary.tsx
index 7e31e8dbff..24f6355eb8 100644
--- a/src/components/Disclosure/DisclosureSummary/DisclosureSummary.tsx
+++ b/src/components/Disclosure/DisclosureSummary/DisclosureSummary.tsx
@@ -1,6 +1,7 @@
 import type * as React from 'react';
 
 import {ArrowToggle} from '../../ArrowToggle';
+import type {QAProps} from '../../types';
 import {warnOnce} from '../../utils/warn';
 import type {DisclosureSize} from '../Disclosure';
 import {useDisclosureAttributes, useToggleDisclosure} from '../DisclosureContext';
@@ -18,7 +19,7 @@ function warnAboutPhysicalValues() {
     );
 }
 
-interface DisclosureSummaryRenderFunctionProps {
+interface DisclosureSummaryRenderFunctionProps extends QAProps {
     onClick: (e: React.SyntheticEvent) => void;
     ariaControls: string;
     id: string;
@@ -26,17 +27,17 @@ interface DisclosureSummaryRenderFunctionProps {
     disabled?: boolean;
 }
 
-export interface DisclosureSummaryProps {
+export interface DisclosureSummaryProps extends QAProps {
     children: (
         props: DisclosureSummaryRenderFunctionProps,
         defaultSummary: React.ReactElement,
     ) => React.ReactElement;
 }
 
-export function DisclosureSummary({children: renderFunction}: DisclosureSummaryProps) {
+export function DisclosureSummary({children: renderFunction, qa}: DisclosureSummaryProps) {
     const handleToggle = useToggleDisclosure();
     const {ariaControls, ariaLabelledby: id, expanded, disabled} = useDisclosureAttributes();
-    const props = {onClick: handleToggle, ariaControls, id, expanded, disabled};
+    const props = {onClick: handleToggle, ariaControls, id, expanded, disabled, qa};
 
     return renderFunction(props, <DefaultDisclosureSummary {...props} />);
 }
@@ -47,6 +48,7 @@ export function DefaultDisclosureSummary({
     id,
     expanded,
     disabled,
+    qa,
 }: DisclosureSummaryRenderFunctionProps) {
     const {size, summary, arrowPosition} = useDisclosureAttributes();
     let arrowMod = arrowPosition;
@@ -68,7 +70,7 @@ export function DefaultDisclosureSummary({
             id={id}
             onClick={onClick}
             disabled={disabled}
-            data-qa={DisclosureQa.SUMMARY}
+            data-qa={qa || DisclosureQa.SUMMARY}
         >
             <ArrowToggle
                 size={ComponentSizeToIconSizeMap[size]}
diff --git a/src/components/Disclosure/README-ru.md b/src/components/Disclosure/README-ru.md
index a2a6a876ac..c0732d0c48 100644
--- a/src/components/Disclosure/README-ru.md
+++ b/src/components/Disclosure/README-ru.md
@@ -16,6 +16,21 @@
 | keepMounted     | `Boolean`                     |              | `true`                | Сохранение контента в DOM.                                       |
 | onUpdate        | `(expanded: boolean) => void` |              |                       | Обратный вызов, срабатывающий при изменении состояния раскрытия. |
 | children        | `React.ReactNode`             |              |                       | Контент.                                                         |
+| qa              | `String`                      |              |                       | Идентификатор для тестирования.                                  |
+
+### Свойства Disclosure.Summary
+
+| Имя      | Тип                                             | Обязательное | Значение по умолчанию | Описание                        |
+| :------- | :---------------------------------------------- | :----------: | :-------------------- | :------------------------------ |
+| children | `(props, defaultSummary) => React.ReactElement` |      Да      |                       | Функция рендеринга.             |
+| qa       | `String`                                        |              | `disclosure-summary`  | Идентификатор для тестирования. |
+
+### Свойства Disclosure.Details
+
+| Имя      | Тип               | Обязательное | Значение по умолчанию | Описание                        |
+| :------- | :---------------- | :----------: | :-------------------- | :------------------------------ |
+| children | `React.ReactNode` |      Да      |                       | Контент                         |
+| qa       | `String`          |              | `disclosure-details`  | Идентификатор для тестирования. |
 
 ### Примеры
 
diff --git a/src/components/Disclosure/README.md b/src/components/Disclosure/README.md
index 53f1c4f872..1b7f5cc6dd 100644
--- a/src/components/Disclosure/README.md
+++ b/src/components/Disclosure/README.md
@@ -16,6 +16,21 @@ This is the disclosure component that shows and hides enclosed content.
 | keepMounted     | `Boolean`                     |          | `true`  | Keep content in DOM                                         |
 | onUpdate        | `(expanded: boolean) => void` |          |         | Callback is fired when the expand/collapse state is changed |
 | children        | `React.ReactNode`             |          |         | Content                                                     |
+| qa              | `String`                      |          |         | Test identifier                                             |
+
+### Disclosure.Summary PropTypes
+
+| Name     | Type                                            | Required | Default              | Description     |
+| :------- | :---------------------------------------------- | :------: | :------------------- | :-------------- |
+| children | `(props, defaultSummary) => React.ReactElement` |   Yes    |                      | Render function |
+| qa       | `String`                                        |          | `disclosure-summary` | Test identifier |
+
+### Disclosure.Details PropTypes
+
+| Name     | Type              | Required | Default              | Description     |
+| :------- | :---------------- | :------: | :------------------- | :-------------- |
+| children | `React.ReactNode` |   Yes    |                      | Content         |
+| qa       | `String`          |          | `disclosure-details` | Test identifier |
 
 ### Examples
 
diff --git a/src/components/Disclosure/__stories__/Disclosure.stories.tsx b/src/components/Disclosure/__stories__/Disclosure.stories.tsx
index 8d892f9ff3..ea61a66f3e 100644
--- a/src/components/Disclosure/__stories__/Disclosure.stories.tsx
+++ b/src/components/Disclosure/__stories__/Disclosure.stories.tsx
@@ -19,6 +19,7 @@ export default {
         size: 'm',
         disabled: false,
         arrowPosition: 'start',
+        qa: 'disclosure',
     },
 } as Meta;
 
diff --git a/src/components/Disclosure/__tests__/Disclosure.test.tsx b/src/components/Disclosure/__tests__/Disclosure.test.tsx
index 11d76912fb..22cae21bd5 100644
--- a/src/components/Disclosure/__tests__/Disclosure.test.tsx
+++ b/src/components/Disclosure/__tests__/Disclosure.test.tsx
@@ -5,8 +5,6 @@ import {Disclosure} from '../Disclosure';
 import type {DisclosureSize} from '../Disclosure';
 import {DisclosureQa} from '../constants';
 
-const qaId = 'disclosure-component';
-
 describe.only('Disclosure', () => {
     test('render disclosure by default', () => {
         render(<Disclosure />);
@@ -18,7 +16,8 @@ describe.only('Disclosure', () => {
     });
 
     test.each(new Array<DisclosureSize>('m', 'l', 'xl'))('render with given "%s" size', (size) => {
-        render(<Disclosure qa={qaId} size={size} />);
+        const qaId = 'disclosure';
+        render(<Disclosure size={size} qa={qaId} />);
         const component = screen.getByTestId(qaId);
 
         expect(component).toHaveClass(`g-disclosure_size_${size}`);
@@ -48,34 +47,37 @@ describe.only('Disclosure', () => {
     });
 
     test('show given node summary', () => {
+        const contentQaId = 'disclosure-content';
         const className = 'content';
         const content = (
-            <div data-qa={qaId} className={className}>
+            <div data-qa={contentQaId} className={className}>
                 Some content
             </div>
         );
 
         render(<Disclosure summary={content} />);
-        const component = screen.getByTestId(qaId);
+        const component = screen.getByTestId(contentQaId);
         const wrapperComponent = screen.getByTestId(DisclosureQa.SUMMARY);
 
         expect(wrapperComponent).toBeVisible();
         expect(component).toHaveClass(className);
     });
     test('render custom summary without default', () => {
+        const contentQaId = 'disclosure-content';
+
         const className = 'content';
         render(
             <Disclosure>
                 <Disclosure.Summary>
                     {() => (
-                        <div data-qa={qaId} className={className}>
+                        <div data-qa={contentQaId} className={className}>
                             Some content
                         </div>
                     )}
                 </Disclosure.Summary>
             </Disclosure>,
         );
-        const component = screen.getByTestId(qaId);
+        const component = screen.getByTestId(contentQaId);
         const wrapperComponent = screen.queryByTestId(DisclosureQa.SUMMARY);
 
         expect(wrapperComponent).toBeNull();
@@ -83,11 +85,13 @@ describe.only('Disclosure', () => {
     });
     test('render custom summary with default', () => {
         const className = 'content';
+        const contentQaId = 'disclosure-content';
+
         render(
             <Disclosure>
                 <Disclosure.Summary>
                     {(_, defaultButton) => (
-                        <div data-qa={qaId} className={className}>
+                        <div data-qa={contentQaId} className={className}>
                             {defaultButton}
                             Some content
                         </div>
@@ -95,7 +99,7 @@ describe.only('Disclosure', () => {
                 </Disclosure.Summary>
             </Disclosure>,
         );
-        const component = screen.getByTestId(qaId);
+        const component = screen.getByTestId(contentQaId);
         const wrapperComponent = screen.getByTestId(DisclosureQa.SUMMARY);
 
         expect(wrapperComponent).toBeVisible();
@@ -103,6 +107,7 @@ describe.only('Disclosure', () => {
     });
 
     test('add className', () => {
+        const qaId = 'disclosure';
         const className = 'my-class';
 
         render(<Disclosure className={className} qa={qaId} />);


Reviews:
user: login: sourcery-ai[bot]
body: Hey @itwillwork - I've reviewed your changes and they look great!

<details>
<summary>Here's what I looked at during the review</summary>

- 🟢 **General issues**: all looks good
- 🟢 **Security**: all looks good
- 🟢 **Testing**: all looks good
- 🟢 **Complexity**: all looks good
- 🟢 **Documentation**: all looks good
</details>

***

<details>
<summary>Sourcery is free for open source - if you like our reviews please consider sharing them ✨</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://sourcery.ai)
- [Facebook](https://www.facebook.com/sharer/sharer.php?u=https://sourcery.ai)

</details>

<sub>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T10:20:16Z
user: login: korvin89
body: It is a breaking changes. Lets use constants as a defaults
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T10:38:55Z
user: login: Raubzeug
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T11:46:47Z
user: login: korvin89
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T13:05:43Z
user: login: itwillwork
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T11:36:34Z
user: login: itwillwork
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T11:36:44Z
user: login: korvin89
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T12:32:41Z
user: login: itwillwork
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T12:36:41Z
user: login: korvin89
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T12:40:41Z
user: login: korvin89
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T10:18:18Z

Review comments:
diff_hunk: @@ -100,11 +107,15 @@ function prepareChildren(children: React.ReactNode) {
         content.push(item);
     }
     if (content.length > 0) {
-        details = <DisclosureDetails>{content}</DisclosureDetails>;
+        details = (
+            <DisclosureDetails qa={disclosureQa && `${disclosureQa}-details`}>
+                {content}
+            </DisclosureDetails>
+        );
     }
     if (!summary) {
         summary = (
-            <DisclosureSummary>
+            <DisclosureSummary qa={disclosureQa && `${disclosureQa}-summary`}>
path: src/components/Disclosure/Disclosure.tsx
user: login: korvin89
body: Could u add some information about new qa attributes? It may be a new small clause of documentation in readme
created_at: 2025-03-24T13:04:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,6 +15,7 @@ describe.only('Disclosure', () => {
     });
 
     test.each(new Array<DisclosureSize>('m', 'l', 'xl'))('render with given "%s" size', (size) => {
+        const qaId = 'disclosure-component';
path: src/components/Disclosure/__tests__/Disclosure.test.tsx
user: login: korvin89
body: Lets use default qa in tests
created_at: 2025-03-24T13:05:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -100,11 +107,15 @@ function prepareChildren(children: React.ReactNode) {
         content.push(item);
     }
     if (content.length > 0) {
-        details = <DisclosureDetails>{content}</DisclosureDetails>;
+        details = (
+            <DisclosureDetails qa={disclosureQa && `${disclosureQa}-details`}>
+                {content}
+            </DisclosureDetails>
+        );
     }
     if (!summary) {
         summary = (
-            <DisclosureSummary>
+            <DisclosureSummary qa={disclosureQa && `${disclosureQa}-summary`}>
path: src/components/Disclosure/Disclosure.tsx
user: login: itwillwork
body: done
https://github.com/gravity-ui/uikit/pull/2180/commits/e6dd98223287aca8a6b71c9edcc3e3ac9f3bdd16
https://github.com/gravity-ui/uikit/pull/2180/commits/45b3f8191f62f6ff7bc97c6cef363f8e2299645e
created_at: 2025-03-25T11:36:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,6 +15,7 @@ describe.only('Disclosure', () => {
     });
 
     test.each(new Array<DisclosureSize>('m', 'l', 'xl'))('render with given "%s" size', (size) => {
+        const qaId = 'disclosure-component';
path: src/components/Disclosure/__tests__/Disclosure.test.tsx
user: login: itwillwork
body: done 
https://github.com/gravity-ui/uikit/pull/2180/commits/bd3f8d3c083c2933d14c7a0c80bd0712efe6abb6

created_at: 2025-03-25T11:36:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,7 +16,8 @@ describe.only('Disclosure', () => {
     });
 
     test.each(new Array<DisclosureSize>('m', 'l', 'xl'))('render with given "%s" size', (size) => {
-        render(<Disclosure qa={qaId} size={size} />);
+        const qaId = 'disclosure';
path: src/components/Disclosure/__tests__/Disclosure.test.tsx
user: login: korvin89
body: Why we need any changes in tests?
created_at: 2025-03-25T12:32:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,7 +16,8 @@ describe.only('Disclosure', () => {
     });
 
     test.each(new Array<DisclosureSize>('m', 'l', 'xl'))('render with given "%s" size', (size) => {
-        render(<Disclosure qa={qaId} size={size} />);
+        const qaId = 'disclosure';
path: src/components/Disclosure/__tests__/Disclosure.test.tsx
user: login: itwillwork
body: without this qa props, there will be no data-qa attribute on the div
![image](https://github.com/user-attachments/assets/5272b356-c17e-4766-98e0-0a420737ac0a)

created_at: 2025-03-25T12:36:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,7 +16,8 @@ describe.only('Disclosure', () => {
     });
 
     test.each(new Array<DisclosureSize>('m', 'l', 'xl'))('render with given "%s" size', (size) => {
-        render(<Disclosure qa={qaId} size={size} />);
+        const qaId = 'disclosure';
path: src/components/Disclosure/__tests__/Disclosure.test.tsx
user: login: korvin89
body: My point is that your changes should work without changing the **old** tests, otherwise it's still a breaking change
created_at: 2025-03-25T12:40:40Z
author_association: CONTRIBUTOR

PR comments:
user: login: sourcery-ai[bot]
created_at: 2025-03-24T10:19:35Z
body: <!-- Generated by sourcery-ai[bot]: start review_guide -->

## Reviewer's Guide by Sourcery

The pull request refactors the Disclosure component to support dynamic QA identifiers, removing hardcoded constants and allowing QA props to be passed dynamically. This involves changes in the test files, component implementation, and removal of constants.

#### Updated class diagram for Disclosure components

```mermaid
classDiagram
    class Disclosure {
        +prepareChildren(children: React.ReactNode, params: PrepareParams)
    }
    class DisclosureSummary {
        +children: (props: DisclosureSummaryRenderFunctionProps, defaultSummary: React.ReactElement) => React.ReactElement
        +qa: string
    }
    class DisclosureDetails {
        +children: React.ReactNode
        +qa: string
    }
    class PrepareParams {
        +disclosureQa: string
    }
    class DisclosureSummaryRenderFunctionProps {
        +onClick: (e: React.SyntheticEvent) => void
        +ariaControls: string
        +id: string
        +expanded: boolean
        +disabled: boolean
        +qa: string
    }
    class QAProps {
        +qa: string
    }
    DisclosureSummary --> QAProps
    DisclosureDetails --> QAProps
    DisclosureSummaryRenderFunctionProps --> QAProps
    Disclosure --> PrepareParams
```

### File-Level Changes

| Change | Details | Files |
| ------ | ------- | ----- |
| Refactor Disclosure component to support dynamic QA identifiers. | <ul><li>Added a 'qa' prop to the Disclosure component and its subcomponents to allow dynamic setting of QA identifiers.</li><li>Modified the prepareChildren function to accept a disclosureQa parameter and use it for setting QA identifiers on DisclosureDetails and DisclosureSummary.</li><li>Updated DisclosureSummary and DisclosureDetails components to accept and use a 'qa' prop for setting data-qa attributes.</li></ul> | `src/components/Disclosure/Disclosure.tsx`<br/>`src/components/Disclosure/DisclosureSummary/DisclosureSummary.tsx`<br/>`src/components/Disclosure/DisclosureDetails/DisclosureDetails.tsx` |
| Remove hardcoded QA constants from the Disclosure component. | <ul><li>Deleted the DisclosureQa constants from the constants file.</li><li>Removed references to DisclosureQa constants in the DisclosureSummary and DisclosureDetails components.</li></ul> | `src/components/Disclosure/constants.ts`<br/>`src/components/Disclosure/DisclosureSummary/DisclosureSummary.tsx`<br/>`src/components/Disclosure/DisclosureDetails/DisclosureDetails.tsx` |
| Update tests to use dynamic QA identifiers. | <ul><li>Replaced hardcoded QA identifiers with dynamic ones in the Disclosure component tests.</li><li>Updated test cases to use the new 'qa' prop for setting and querying QA identifiers.</li></ul> | `src/components/Disclosure/__tests__/Disclosure.test.tsx` |
| Update storybook stories to include the 'qa' prop. | <ul><li>Added 'qa' prop to the default args in the Disclosure storybook configuration.</li></ul> | `src/components/Disclosure/__stories__/Disclosure.stories.tsx` |
| Remove export of DisclosureQa from the index file. | <ul><li>Deleted the export of DisclosureQa from the Disclosure index file.</li></ul> | `src/components/Disclosure/index.ts` |

---

<details>
<summary>Tips and commands</summary>

#### 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!
- **Generate a plan of action for an issue:** Comment `@sourcery-ai plan` on
  an issue to generate a plan of action for it.

#### Customizing Your Experience

Access your [dashboard](https://app.sourcery.ai) 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

- [Contact our support team](mailto:support@sourcery.ai) for questions or feedback.
- Visit our [documentation](https://docs.sourcery.ai) for detailed guides and information.
- Keep in touch with the Sourcery team by following us on [X/Twitter](https://x.com/SourceryAI), [LinkedIn](https://www.linkedin.com/company/sourcery-ai/) or [GitHub](https://github.com/sourcery-ai).

</details>

<!-- Generated by sourcery-ai[bot]: end review_guide -->
author_association: CONTRIBUTOR
user: login: gravity-ui-bot
created_at: 2025-03-24T10:22:28Z
body: [Preview](https://preview.gravity-ui.com/uikit/2180/) is ready.
<!-- Sticky Pull Request Comment -->
author_association: CONTRIBUTOR
user: login: gravity-ui-bot
created_at: 2025-03-24T10:22:56Z
body: [Visual Tests Report](https://storage.yandexcloud.net/playwright-reports/uikit/pulls/2180/index.html) is ready.
<!-- Sticky Pull Request Commentvisual tests report -->
author_association: CONTRIBUTOR
user: login: itwillwork
created_at: 2025-03-24T11:31:06Z
body: > It is a breaking changes. Lets use constants as a defaults

done
https://github.com/gravity-ui/uikit/pull/2180/commits/80d99077fcd26037d5dc8d0fa8aa1db115f6ced4

author_association: CONTRIBUTOR