Commit bcd217dd authored by Martin Wortschack's avatar Martin Wortschack Committed by Phil Hughes

Add identifiers for onboarding popovers

- Add implementation for rendering action popovers
- Don't render component if onboarding is dismissed
- Add goldenTanukiSvgPath prop
- Enable users to dismiss onboarding on every page
- Fix qa selector (due to additional class)
parent 9339c449
...@@ -60,7 +60,7 @@ export default { ...@@ -60,7 +60,7 @@ export default {
</script> </script>
<template> <template>
<div class="table-section section-10 d-none d-sm-none d-md-block pipeline-tags"> <div class="table-section section-10 d-none d-sm-none d-md-block pipeline-tags">
<gl-link :href="pipeline.path" class="js-pipeline-url-link"> <gl-link :href="pipeline.path" class="js-pipeline-url-link js-onboarding-pipeline-item">
<span class="pipeline-id">#{{ pipeline.id }}</span> <span class="pipeline-id">#{{ pipeline.id }}</span>
</gl-link> </gl-link>
<div class="label-container"> <div class="label-container">
......
...@@ -135,11 +135,5 @@ ...@@ -135,11 +135,5 @@
.popover { .popover {
min-width: auto; min-width: auto;
max-width: 40%; max-width: 40%;
.popover-body {
padding-top: $gl-padding;
padding-bottom: $gl-padding;
font-size: $gl-font-size-small;
}
} }
} }
%li.header-new.dropdown{ data: { track_label: "new_dropdown", track_event: "click_dropdown" } } %li.header-new.dropdown{ data: { track_label: "new_dropdown", track_event: "click_dropdown" } }
= link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", title: _("New..."), ref: 'tooltip', aria: { label: _("New...") }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static' } do = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", id: "js-onboarding-new-project-link", title: _("New..."), ref: 'tooltip', aria: { label: _("New...") }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static' } do
= sprite_icon('plus-square', size: 16) = sprite_icon('plus-square', size: 16)
= sprite_icon('angle-down', css_class: 'caret-down') = sprite_icon('angle-down', css_class: 'caret-down')
.dropdown-menu.dropdown-menu-right .dropdown-menu.dropdown-menu-right
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
= link_to project_tree_path(@project), class: 'shortcuts-tree qa-project-menu-repo' do = link_to project_tree_path(@project), class: 'shortcuts-tree qa-project-menu-repo' do
.nav-icon-container .nav-icon-container
= sprite_icon('doc-text') = sprite_icon('doc-text')
%span.nav-item-name %span.nav-item-name#js-onboarding-repo-link
= _('Repository') = _('Repository')
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
...@@ -64,11 +64,11 @@ ...@@ -64,11 +64,11 @@
= _('Files') = _('Files')
= nav_link(controller: [:commit, :commits]) do = nav_link(controller: [:commit, :commits]) do
= link_to project_commits_path(@project, current_ref) do = link_to project_commits_path(@project, current_ref), id: 'js-onboarding-commits-link' do
= _('Commits') = _('Commits')
= nav_link(html_options: {class: branches_tab_class}) do = nav_link(html_options: {class: branches_tab_class}) do
= link_to project_branches_path(@project), class: 'qa-branches-link' do = link_to project_branches_path(@project), class: 'qa-branches-link', id: 'js-onboarding-branches-link' do
= _('Branches') = _('Branches')
= nav_link(controller: [:tags]) do = nav_link(controller: [:tags]) do
...@@ -98,7 +98,7 @@ ...@@ -98,7 +98,7 @@
= link_to project_issues_path(@project), class: 'shortcuts-issues qa-issues-item' do = link_to project_issues_path(@project), class: 'shortcuts-issues qa-issues-item' do
.nav-icon-container .nav-icon-container
= sprite_icon('issues') = sprite_icon('issues')
%span.nav-item-name %span.nav-item-name#js-onboarding-issues-link
= _('Issues') = _('Issues')
- if @project.issues_enabled? - if @project.issues_enabled?
%span.badge.badge-pill.count.issue_counter %span.badge.badge-pill.count.issue_counter
...@@ -153,7 +153,7 @@ ...@@ -153,7 +153,7 @@
= link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests qa-merge-requests-link' do = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests qa-merge-requests-link' do
.nav-icon-container .nav-icon-container
= sprite_icon('git-merge') = sprite_icon('git-merge')
%span.nav-item-name %span.nav-item-name#js-onboarding-mr-link
= _('Merge Requests') = _('Merge Requests')
%span.badge.badge-pill.count.merge_counter.js-merge-counter %span.badge.badge-pill.count.merge_counter.js-merge-counter
= number_with_delimiter(@project.open_merge_requests_count) = number_with_delimiter(@project.open_merge_requests_count)
...@@ -170,7 +170,7 @@ ...@@ -170,7 +170,7 @@
= link_to project_pipelines_path(@project), class: 'shortcuts-pipelines qa-link-pipelines' do = link_to project_pipelines_path(@project), class: 'shortcuts-pipelines qa-link-pipelines' do
.nav-icon-container .nav-icon-container
= sprite_icon('rocket') = sprite_icon('rocket')
%span.nav-item-name %span.nav-item-name#js-onboarding-pipelines-link
= _('CI / CD') = _('CI / CD')
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
...@@ -335,7 +335,7 @@ ...@@ -335,7 +335,7 @@
= link_to edit_project_path(@project), class: 'shortcuts-tree' do = link_to edit_project_path(@project), class: 'shortcuts-tree' do
.nav-icon-container .nav-icon-container
= sprite_icon('settings') = sprite_icon('settings')
%span.nav-item-name.qa-settings-item %span.nav-item-name.qa-settings-item#js-onboarding-settings-link
= _('Settings') = _('Settings')
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
...@@ -351,7 +351,7 @@ ...@@ -351,7 +351,7 @@
%span %span
= _('General') = _('General')
= nav_link(controller: :project_members) do = nav_link(controller: :project_members) do
= link_to project_project_members_path(@project), title: _('Members'), class: 'qa-link-members-settings' do = link_to project_project_members_path(@project), title: _('Members'), class: 'qa-link-members-settings', id: 'js-onboarding-settings-members-link' do
%span %span
= _('Members') = _('Members')
- if can_edit - if can_edit
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
- if branch.name != @repository.root_ref - if branch.name != @repository.root_ref
= link_to project_compare_path(@project, @repository.root_ref, branch.name), = link_to project_compare_path(@project, @repository.root_ref, branch.name),
class: "btn btn-default #{'prepend-left-10' unless merge_project}", class: "btn btn-default js-onboarding-compare-branches #{'prepend-left-10' unless merge_project}",
title: s_('Branches|Compare') do title: s_('Branches|Compare') do
= s_('Branches|Compare') = s_('Branches|Compare')
......
...@@ -20,9 +20,9 @@ ...@@ -20,9 +20,9 @@
.commit-detail.flex-list .commit-detail.flex-list
.commit-content.qa-commit-content .commit-content.qa-commit-content
- if view_details && merge_request - if view_details && merge_request
= link_to commit.title, project_commit_path(project, commit.id, merge_request_iid: merge_request.iid), class: "commit-row-message item-title" = link_to commit.title, project_commit_path(project, commit.id, merge_request_iid: merge_request.iid), class: "commit-row-message item-title js-onboarding-commit-item"
- else - else
= link_to_markdown_field(commit, :title, link, class: "commit-row-message item-title") = link_to_markdown_field(commit, :title, link, class: "commit-row-message item-title js-onboarding-commit-item")
%span.commit-row-message.d-inline.d-sm-none %span.commit-row-message.d-inline.d-sm-none
&middot; &middot;
= commit.short_id = commit.short_id
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
.issuable-info-container .issuable-info-container
.issuable-main-info .issuable-main-info
.issue-title.title .issue-title.title
%span.issue-title-text{ dir: "auto" } %span.issue-title-text.js-onboarding-issue-item{ dir: "auto" }
- if issue.confidential? - if issue.confidential?
%span.has-tooltip{ title: _('Confidential') } %span.has-tooltip{ title: _('Confidential') }
= confidential_icon(issue) = confidential_icon(issue)
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
.issuable-info-container .issuable-info-container
.issuable-main-info .issuable-main-info
.merge-request-title.title .merge-request-title.title
%span.merge-request-title-text %span.merge-request-title-text.js-onboarding-mr-item
= link_to merge_request.title, merge_request_path(merge_request) = link_to merge_request.title, merge_request_path(merge_request)
- if merge_request.tasks? - if merge_request.tasks?
%span.task-status.d-none.d-sm-inline-block %span.task-status.d-none.d-sm-inline-block
......
const renderPopover = () => { import Vue from 'vue';
// TODO: implementation will be added in a separate MR import ActionPopover from './components/action_popover.vue';
// retry for 10 times (5 seconds in total)
const maxTries = 10;
const timeout = 500;
const mountComponent = (intervalId, el, { target, content, placement, showPopover }) => {
clearInterval(intervalId);
return new Vue({
el,
render(h) {
return h(ActionPopover, {
props: {
target,
content,
placement,
showDefault: showPopover,
},
});
},
});
};
const renderPopover = (popoverSelector, content, placement, showPopover) => {
const popoverContainer = document.getElementById('js-onboarding-action-popover');
let retry = 0;
if (!popoverContainer) {
return false;
}
// continuously check if target element already exists (might be delayed to to dynamic component creation)
const intervalId = setInterval(() => {
if (retry >= maxTries) {
clearInterval(intervalId);
}
retry += 1;
const target = document.querySelector(popoverSelector);
if (!target) {
return false;
}
return mountComponent(intervalId, popoverContainer, {
target,
content,
placement,
showPopover,
});
}, timeout);
return intervalId;
}; };
const actionPopoverUtils = { const actionPopoverUtils = {
......
...@@ -121,9 +121,14 @@ export default { ...@@ -121,9 +121,14 @@ export default {
}, },
handleClickPopoverButton(button) { handleClickPopoverButton(button) {
const { showExitTourContent, exitTour, redirectPath, nextPart, dismissPopover } = button; const { showExitTourContent, exitTour, redirectPath, nextPart, dismissPopover } = button;
const helpContentItems = this.stepContent.getHelpContent({ projectName: this.projectName }); const helpContentItems = this.stepContent
? this.stepContent.getHelpContent({ projectName: this.projectName })
: null;
const showNextContentItem = const showNextContentItem =
helpContentItems.length > 1 && this.helpContentIndex < helpContentItems.length - 1; helpContentItems &&
helpContentItems.length > 1 &&
this.helpContentIndex < helpContentItems.length - 1;
// display exit tour content // display exit tour content
if (showExitTourContent) { if (showExitTourContent) {
...@@ -166,6 +171,7 @@ export default { ...@@ -166,6 +171,7 @@ export default {
}, },
showExitTourContent(showExitTour) { showExitTourContent(showExitTour) {
this.dismissPopover = false; this.dismissPopover = false;
this.showStepContent = true;
this.setExitTour(showExitTour); this.setExitTour(showExitTour);
}, },
handleExitTour() { handleExitTour() {
......
...@@ -53,16 +53,20 @@ export default { ...@@ -53,16 +53,20 @@ export default {
<div> <div>
<p v-html="helpContent.text"></p> <p v-html="helpContent.text"></p>
<template v-if="helpContent.buttons"> <template v-if="helpContent.buttons">
<template v-for="(button, index) in helpContent.buttons">
<gl-button <gl-button
v-for="(button, index) in helpContent.buttons" v-if="!button.readOnly"
:key="index" :key="index"
:class="button.btnClass" :class="button.btnClass"
class="btn btn-sm mr-2" class="btn btn-sm mr-2"
:disabled="button.disabled"
@click="callButtonAction(button)" @click="callButtonAction(button)"
> >
{{ button.text }} {{ button.text }}
</gl-button> </gl-button>
<span v-else :key="index" :class="button.btnClass" class="btn btn-sm mr-2">
{{ button.text }}
</span>
</template>
</template> </template>
</div> </div>
</gl-popover> </gl-popover>
......
...@@ -13,14 +13,14 @@ export default function() { ...@@ -13,14 +13,14 @@ export default function() {
return false; return false;
} }
const { projectFullPath, projectName } = el.dataset;
const tourData = onboardingUtils.getOnboardingLocalStorageState(); const tourData = onboardingUtils.getOnboardingLocalStorageState();
const url = window.location.href;
if (!tourData) { if (!tourData || onboardingUtils.isOnboardingDismissed()) {
return false; return false;
} }
const { projectFullPath, projectName, goldenTanukiSvgPath } = el.dataset;
const url = window.location.href;
const { tourKey, lastStepIndex, createdProjectPath } = tourData; const { tourKey, lastStepIndex, createdProjectPath } = tourData;
const store = createStore(); const store = createStore();
...@@ -51,6 +51,7 @@ export default function() { ...@@ -51,6 +51,7 @@ export default function() {
props: { props: {
tourTitles: TOUR_TITLES, tourTitles: TOUR_TITLES,
exitTourContent: EXIT_TOUR_CONTENT, exitTourContent: EXIT_TOUR_CONTENT,
goldenTanukiSvgPath,
}, },
}); });
}, },
......
...@@ -42,7 +42,7 @@ export default { ...@@ -42,7 +42,7 @@ export default {
target: null, target: null,
content: { content: {
text: __('White helpers give contextual information.'), text: __('White helpers give contextual information.'),
buttons: [{ text: __('OK'), btnClass: 'btn-primary', disabled: true }], buttons: [{ text: __('OK'), btnClass: 'btn-primary', readOnly: true }],
}, },
}, },
actionPopover: { actionPopover: {
......
...@@ -2,5 +2,5 @@ ...@@ -2,5 +2,5 @@
- return unless onboarding_project - return unless onboarding_project
#js-onboarding-helper{ data: { project_full_path: onboarding_project[:project_full_path], project_name: onboarding_project[:project_name] } } #js-onboarding-helper{ data: { project_full_path: onboarding_project[:project_full_path], project_name: onboarding_project[:project_name], golden_tanuki_svg_path: image_path('illustrations/golden_tanuki.svg') } }
#js-onboarding-action-popover #js-onboarding-action-popover
...@@ -4,7 +4,7 @@ module QA::Page ...@@ -4,7 +4,7 @@ module QA::Page
module Project::Pipeline module Project::Pipeline
class Index < QA::Page::Base class Index < QA::Page::Base
view 'app/assets/javascripts/pipelines/components/pipeline_url.vue' do view 'app/assets/javascripts/pipelines/components/pipeline_url.vue' do
element :pipeline_link, 'class="js-pipeline-url-link"' # rubocop:disable QA/ElementWithPattern element :pipeline_link, 'class="js-pipeline-url-link' # rubocop:disable QA/ElementWithPattern
end end
def click_on_latest_pipeline def click_on_latest_pipeline
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment