GlAlert > Migrate to shared the Pajamas::AlertComponent ViewComponent to match the Vue component
## Purpose
This epic coordinates the migration of HAML alerts to the shared `Pajamas::AlertComponent` ViewComponent.
Leveraging the shared ViewComponent for all HAML alerts will...
* ...enforce the design specs on migrated alerts as the partial has been built to properly implement current specifications and matches `GlAlert`'s implementation.
* ...make future refactor easier as we won't need to migrate individual alerts anymore.
## How to proceed?
To inherit from the shared template, start by rendering the ViewComponent:
```haml
= render Pajamas::AlertComponent.new do
```
The partial yields a block for rendering the alert's content. An alert _must_ have a body and _can_ have actions. The body and actions can be defined by using the `body` and `actions` slots:
```haml
= render Pajamas::AlertComponent.new do |c|
= c.body do
-# Alert's body
= c.actions do
-# Alert's actions (optional)
```
Learn more about ViewComponent slots: https://viewcomponent.org/guide/slots.html
Other aspects are configurable via options:
| Option | Description | Default value |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------- |
| `title` | The alert's title, rendered in an `h4` element. | `nil` |
| `variant` | The alert's variant, one of `:info`. `:warning`, `:success`, `:danger`, `:tip`. | `:info` |
| `dismissible` | Whether the alert can be dismissed. When this is `true`, a dismiss button is rendered in the alert's top-right corner. | `true` |
| `alert_options` | Custom classes, data and html options to be added to the `.gl-alert` element. | `{}` |
| `close_button_options` | Custom classes, data and html options to be added to the dismiss button (`.gl-dismiss-btn`). | `{}` |
To set an option on an alert, pass it to the ViewComponent's constructor. Here's how you would set a `title` and pass data attributes to the close button for example:
```haml
= render Pajamas::AlertComponent.new(title: _('Open registration is enabled on your instance.'), close_button_options: { data: { project_id: project.id }}) do |c|
= c.body do
-# Alert's body
```
### ~~Alerts rendered inside the primary content~~
~~The partial attempts to constrain alerts' content so that is easily readable, no matter what layout is used (fluid or fixed). It needs a little help to properly set the padding on alerts rendered inside the primary content though. To do this, you **must** set the `is_contained` option to `true` for such alerts:~~
**Edit:** this property has since been deprecated and removed with gitlab-org/gitlab!81167
### Example
Here's how a single migration might look like. This diff is taken from https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63800/diffs.
```diff
- .gl-alert.gl-alert-info
- = sprite_icon('information-o', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
+ = render Pajamas::AlertComponent.new(title: _('You do not have an active license'), dismissible: false) do |c|
+ = c.body do
- %h4.gl-alert-title= _('You do not have an active license')
= _('You have a license that activates at a future date. Please see the License History table below.')
```
<details>
<summary markdown="span">View file list</summary>
- [ ] `app/views/admin/application_settings/integrations.html.haml`
- [ ] `app/views/admin/broadcast_messages/_form.html.haml`
- [ ] `app/views/admin/groups/_form.html.haml`
- [ ] `app/views/admin/projects/show.html.haml`
- [ ] `app/views/admin/runners/show.html.haml`
- [ ] `app/views/admin/services/index.html.haml`
- [ ] `app/views/clusters/clusters/_banner.html.haml`
- [ ] `app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml`
- [ ] `app/views/groups/dependency_proxies/show.html.haml`
- [ ] `app/views/import/shared/_errors.html.haml`
- [ ] `app/views/layouts/header/_registration_enabled_callout.html.haml`
- [ ] `app/views/profiles/accounts/show.html.haml`
- [ ] `app/views/profiles/notifications/show.html.haml`
- [ ] `app/views/profiles/two_factor_auths/show.html.haml`
- [ ] `app/views/projects/_deletion_failed.html.haml`
- [ ] `app/views/projects/blob/_upload.html.haml`
- [ ] `app/views/projects/blob/edit.html.haml`
- [ ] `app/views/projects/branches/new.html.haml`
- [ ] `app/views/projects/commits/_commits.html.haml`
- [ ] `app/views/projects/diffs/_warning.html.haml`
- [ ] `app/views/projects/forks/error.html.haml`
- [ ] `app/views/projects/issues/_alert_moved_from_service_desk.html.haml`
- [ ] `app/views/projects/merge_requests/_mr_title.html.haml`
- [ ] `app/views/projects/merge_requests/invalid.html.haml`
- [ ] `app/views/projects/milestones/show.html.haml`
- [ ] `app/views/projects/mirrors/_mirror_repos.html.haml`
- [ ] `app/views/projects/pages/_access.html.haml`
- [ ] `app/views/projects/pages_domains/_form.html.haml`
- [ ] `app/views/projects/pages_domains/show.html.haml`
- [ ] `app/views/projects/services/prometheus/_top.html.haml`
- [ ] `app/views/projects/settings/integrations/show.html.haml`
- [ ] `app/views/projects/tags/new.html.haml`
- [ ] `app/views/projects/tracings/show.html.haml`
- [ ] `app/views/shared/_alert_info.html.haml`
- [ ] `app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml`
- [ ] `app/views/shared/_check_recovery_settings.html.haml`
- [ ] `app/views/shared/_group_form.html.haml`
- [ ] `app/views/shared/_no_password.html.haml`
- [ ] `app/views/shared/_no_ssh.html.haml`
- [ ] `app/views/shared/_outdated_browser.html.haml`
- [ ] `app/views/shared/_ping_consent.html.haml`
- [ ] `app/views/shared/_project_limit.html.haml`
- [ ] `app/views/shared/issuable/_form.html.haml`
- [ ] `app/views/shared/issuable/form/_branch_chooser.html.haml`
- [ ] `app/views/shared/milestones/_top.html.haml`
- [ ] `ee/app/views/admin/emails/show.html.haml`
- [ ] `ee/app/views/admin/licenses/_breakdown.html.haml`
- [ ] `ee/app/views/admin/licenses/_repeat_trial_info.html.haml`
- [ ] `ee/app/views/admin/licenses/new.html.haml`
- [ ] `ee/app/views/admin/licenses/show.html.haml`
- [ ] `ee/app/views/admin/push_rules/_push_rules.html.haml`
- [ ] `ee/app/views/groups/push_rules/edit.html.haml`
- [ ] `ee/app/views/layouts/header/_ee_subscribable_banner.html.haml`
- [ ] `ee/app/views/layouts/header/_licensed_user_count_threshold.html.haml`
- [ ] `ee/app/views/layouts/header/_token_expiry_notification.html.haml`
- [ ] `ee/app/views/projects/_above_size_limit_warning.html.haml`
- [ ] `ee/app/views/projects/_merge_request_approvals_settings_form.html.haml`
- [ ] `ee/app/views/shared/_namespace_storage_limit_alert.html.haml`
- [ ] `ee/app/views/shared/_new_user_signups_cap_reached_alert.html.haml`
- [ ] `ee/app/views/vulnerabilities/_unable_to_link_vulnerability.html.haml`
</details>
<details>
<summary>Original instructions</summary>
The following HAML and Ruby files need to be updated to account for markup changes after https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/2094 is merged.
**Specifically:**
- Migrating the dismiss button to a GlButton with the `.gl-dismiss-btn` class.
- Considering if the content in the alert should be [horizontally constrained with a max-width](https://design.gitlab.com/components/alert#design) for a better reading experience (yes in most cases).
### Instructions
Updating HAML and Ruby instances involves changing to template structure and classes. Most, if not all, of the issues here can use `gl-alert-max-content` with `gl-alert` to prevent the content from stretching across the full viewport and becoming more difficult to read.
The changes to the classes will also ensure buttons are using **GlButton** while updating the alert structure.
#### Classes
- `.gl-alert` (required)
- `.gl-alert-max-content` (optional) - Applies a max-width to the alert content, including the icon and dismiss button.
- `.gl-alert-{variant}` (required) - Defines what variant the alert should be.
- `.gl-alert-not-dismissible` (optional) - Used when the alert can't be dismissed and the dismiss button isn't present.
- `.gl-alert-container` (required) - Wraps all of the alert content.
- `.gl-dismiss-btn` - Replaces `.gl-alert-dismiss` on the dismiss button. If the button hasn't been migrated to use **GlButton** it should also have these classes: `.btn .btn-default.btn-sm .gl-button .btn-default-tertiary .btn-icon`. The icon within the button should have these classes: `.gl-button-icon .gl-icon`.
- `.gl-alert-content` (required) - Contains the title, body, and actions.
- `.gl-alert-title`
- `.gl-alert-body` - Contains the text content.
- `.gl-alert-actions` - Contains the buttons that perform actions based on the alert content. If the button(s) within haven't been migrated to use **GlButton** they should also have these classes: `.btn .btn-confirm .gl-button` for the primary action, and `.btn .btn-default .gl-button` for the secondary action.
The basic structure of an alert follows these patterns (SVG and button attributes removed for simplicity):
**Dismissible info alert with max-width content**:
```HTML
<div class="gl-alert gl-alert-max-content gl-alert-info">
<div class="gl-alert-container">
<svg>Icon here related to alert variant</svg>
<button><svg>Close icon</svg></button>
<div role="alert" class="gl-alert-content">
<h4 class="gl-alert-title">Title</h4>
<div class="gl-alert-body">Alert content</div>
<div class="gl-alert-actions">
<button>Primary action</button><button>Secondary button</button>
</div>
</div>
</div>
</div>
```
**Non-dismissible info alert with max-width content**:
```HTML
<div class="gl-alert gl-alert-max-content gl-alert-not-dismissible gl-alert-info">
<div class="gl-alert-container">
<svg>Icon here related to alert variant</svg>
<div role="alert" class="gl-alert-content">
<h4 class="gl-alert-title">Title</h4>
<div class="gl-alert-body">Alert content</div>
<div class="gl-alert-actions">
<button>Primary action</button><button>Secondary button</button>
</div>
</div>
</div>
</div>
```
**Dismissible info alert with fluid content**:
```HTML
<div class="gl-alert gl-alert-info">
<div class="gl-alert-container">
<svg>Icon here related to alert variant</svg>
<button><svg>Close icon</svg></button>
<div role="alert" class="gl-alert-content">
<h4 class="gl-alert-title">Title</h4>
<div class="gl-alert-body">Alert content</div>
<div class="gl-alert-actions">
<button>Primary action</button><button>Secondary button</button>
</div>
</div>
</div>
</div>
```
</details>
epic