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