summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
authorJose Vargas <jvargas@gitlab.com>2018-09-25 11:22:38 -0500
committerJose Vargas <jvargas@gitlab.com>2018-09-25 11:22:38 -0500
commitefedd968fb35a1113414885b48798c2723c7578d (patch)
treea417e431a24caa67ef2ac56373c3a0f869fefaa4 /app/assets
parentd0f81b60e45a0eed8eb863aa9fc9070d1f416f14 (diff)
parent45b365b0e5abdb86ff6ac23b00578d5398134af9 (diff)
downloadgitlab-ce-efedd968fb35a1113414885b48798c2723c7578d.tar.gz
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into jivl-fix-monitoring-dashboard-resizing-navbar
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/images/cluster_app_logos/elasticsearch.pngbin0 -> 796 bytes
-rw-r--r--app/assets/images/cluster_app_logos/gitlab.pngbin0 -> 1757 bytes
-rw-r--r--app/assets/images/cluster_app_logos/helm.pngbin0 -> 1438 bytes
-rw-r--r--app/assets/images/cluster_app_logos/jeager.pngbin0 -> 2619 bytes
-rw-r--r--app/assets/images/cluster_app_logos/jupyterhub.pngbin0 -> 895 bytes
-rw-r--r--app/assets/images/cluster_app_logos/kubernetes.pngbin0 -> 1437 bytes
-rw-r--r--app/assets/images/cluster_app_logos/meltano.pngbin0 -> 580 bytes
-rw-r--r--app/assets/images/cluster_app_logos/prometheus.pngbin0 -> 923 bytes
-rw-r--r--app/assets/javascripts/badges/components/badge.vue6
-rw-r--r--app/assets/javascripts/badges/components/badge_form.vue4
-rw-r--r--app/assets/javascripts/badges/components/badge_list.vue6
-rw-r--r--app/assets/javascripts/badges/components/badge_list_row.vue4
-rw-r--r--app/assets/javascripts/behaviors/index.js6
-rw-r--r--app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js19
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_gfm.js2
-rw-r--r--app/assets/javascripts/behaviors/preview_markdown.js (renamed from app/assets/javascripts/preview_markdown.js)0
-rw-r--r--app/assets/javascripts/behaviors/shortcuts.js35
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts.js (renamed from app/assets/javascripts/shortcuts.js)6
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js (renamed from app/assets/javascripts/shortcuts_blob.js)2
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js (renamed from app/assets/javascripts/shortcuts_find_file.js)0
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js (renamed from app/assets/javascripts/shortcuts_issuable.js)4
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js (renamed from app/assets/javascripts/shortcuts_navigation.js)2
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js (renamed from app/assets/javascripts/shortcuts_network.js)0
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js (renamed from app/assets/javascripts/shortcuts_wiki.js)2
-rw-r--r--app/assets/javascripts/blob/3d_viewer/index.js4
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue10
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue2
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.vue4
-rw-r--r--app/assets/javascripts/boards/components/modal/index.vue4
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue6
-rw-r--r--app/assets/javascripts/boards/index.js6
-rw-r--r--app/assets/javascripts/build_variables.js10
-rw-r--r--app/assets/javascripts/clusters/components/application_row.vue137
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue439
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.vue4
-rw-r--r--app/assets/javascripts/commons/gitlab_ui.js4
-rw-r--r--app/assets/javascripts/commons/polyfills.js2
-rw-r--r--app/assets/javascripts/commons/polyfills/element.js23
-rw-r--r--app/assets/javascripts/commons/polyfills/svg.js5
-rw-r--r--app/assets/javascripts/deploy_keys/components/action_btn.vue6
-rw-r--r--app/assets/javascripts/deploy_keys/components/app.vue6
-rw-r--r--app/assets/javascripts/deploy_keys/components/key.vue10
-rw-r--r--app/assets/javascripts/diffs/components/app.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue6
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_gutter_avatars.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue50
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_table_row.vue6
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue4
-rw-r--r--app/assets/javascripts/diffs/components/no_changes.vue2
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue4
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_table_row.vue5
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_view.vue2
-rw-r--r--app/assets/javascripts/diffs/constants.js1
-rw-r--r--app/assets/javascripts/diffs/store/actions.js40
-rw-r--r--app/assets/javascripts/diffs/store/modules/diff_state.js2
-rw-r--r--app/assets/javascripts/diffs/store/modules/index.js4
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js8
-rw-r--r--app/assets/javascripts/diffs/store/utils.js32
-rw-r--r--app/assets/javascripts/dispatcher.js89
-rw-r--r--app/assets/javascripts/dropzone_input.js2
-rw-r--r--app/assets/javascripts/environments/components/container.vue6
-rw-r--r--app/assets/javascripts/environments/components/empty_state.vue2
-rw-r--r--app/assets/javascripts/environments/components/environment_actions.vue4
-rw-r--r--app/assets/javascripts/environments/components/environment_rollback.vue4
-rw-r--r--app/assets/javascripts/environments/components/environments_app.vue2
-rw-r--r--app/assets/javascripts/environments/components/environments_table.vue8
-rw-r--r--app/assets/javascripts/environments/mixins/environments_mixin.js2
-rw-r--r--app/assets/javascripts/feature_highlight/feature_highlight.js2
-rw-r--r--app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js14
-rw-r--r--app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue4
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js2
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js6
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js4
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_token_keys.js115
-rw-r--r--app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js77
-rw-r--r--app/assets/javascripts/filtered_search/null_dropdown.js9
-rw-r--r--app/assets/javascripts/frequent_items/components/app.vue6
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue2
-rw-r--r--app/assets/javascripts/gl_form.js2
-rw-r--r--app/assets/javascripts/groups/components/app.vue6
-rw-r--r--app/assets/javascripts/groups/components/groups.vue27
-rw-r--r--app/assets/javascripts/groups/components/item_actions.vue4
-rw-r--r--app/assets/javascripts/ide/components/branches/search_list.vue8
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue1
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list.vue4
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_item.vue4
-rw-r--r--app/assets/javascripts/ide/components/error_message.vue6
-rw-r--r--app/assets/javascripts/ide/components/file_finder/index.vue2
-rw-r--r--app/assets/javascripts/ide/components/file_finder/item.vue12
-rw-r--r--app/assets/javascripts/ide/components/file_row_extra.vue104
-rw-r--r--app/assets/javascripts/ide/components/file_templates/dropdown.vue6
-rw-r--r--app/assets/javascripts/ide/components/ide_tree_list.vue12
-rw-r--r--app/assets/javascripts/ide/components/jobs/list.vue6
-rw-r--r--app/assets/javascripts/ide/components/jobs/stage.vue6
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/list.vue6
-rw-r--r--app/assets/javascripts/ide/components/pipelines/list.vue6
-rw-r--r--app/assets/javascripts/ide/components/preview/clientside.vue6
-rw-r--r--app/assets/javascripts/ide/components/preview/navigator.vue4
-rw-r--r--app/assets/javascripts/ide/components/repo_file.vue227
-rw-r--r--app/assets/javascripts/ide/components/repo_file_status_icon.vue2
-rw-r--r--app/assets/javascripts/ide/index.js30
-rw-r--r--app/assets/javascripts/issuable_bulk_update_actions.js8
-rw-r--r--app/assets/javascripts/issuable_bulk_update_sidebar.js6
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue1
-rw-r--r--app/assets/javascripts/issue_show/components/edit_actions.vue17
-rw-r--r--app/assets/javascripts/issue_show/components/form.vue5
-rw-r--r--app/assets/javascripts/issue_show/components/title.vue2
-rw-r--r--app/assets/javascripts/jobs/components/artifacts_block.vue68
-rw-r--r--app/assets/javascripts/jobs/components/commit_block.vue72
-rw-r--r--app/assets/javascripts/jobs/components/header.vue8
-rw-r--r--app/assets/javascripts/jobs/components/job_log_controllers.vue4
-rw-r--r--app/assets/javascripts/jobs/components/jobs_container.vue2
-rw-r--r--app/assets/javascripts/jobs/components/sidebar_details_block.vue203
-rw-r--r--app/assets/javascripts/jobs/components/trigger_block.vue28
-rw-r--r--app/assets/javascripts/jobs/job_details_mediator.js2
-rw-r--r--app/assets/javascripts/labels_select.js4
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js67
-rw-r--r--app/assets/javascripts/lib/utils/navigation_utility.js (renamed from app/assets/javascripts/shortcuts_dashboard_navigation.js)2
-rw-r--r--app/assets/javascripts/main.js14
-rw-r--r--app/assets/javascripts/merge_request_tabs.js3
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue4
-rw-r--r--app/assets/javascripts/monitoring/components/graph/legend.vue4
-rw-r--r--app/assets/javascripts/mr_notes/stores/index.js4
-rw-r--r--app/assets/javascripts/notebook/index.vue4
-rw-r--r--app/assets/javascripts/notes.js6
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue2
-rw-r--r--app/assets/javascripts/notes/components/diff_file_header.vue2
-rw-r--r--app/assets/javascripts/notes/components/note_actions.vue10
-rw-r--r--app/assets/javascripts/notes/components/note_awards_list.vue2
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue4
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue4
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue6
-rw-r--r--app/assets/javascripts/notes/stores/actions.js39
-rw-r--r--app/assets/javascripts/notes/stores/index.js12
-rw-r--r--app/assets/javascripts/notes/stores/modules/index.js4
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js15
-rw-r--r--app/assets/javascripts/notes/stores/utils.js2
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/index.js5
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/index.js8
-rw-r--r--app/assets/javascripts/pages/admin/runners/index.js10
-rw-r--r--app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue5
-rw-r--r--app/assets/javascripts/pages/constants.js1
-rw-r--r--app/assets/javascripts/pages/groups/boards/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/issues/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/merge_requests/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/show/index.js2
-rw-r--r--app/assets/javascripts/pages/ide/index.js10
-rw-r--r--app/assets/javascripts/pages/projects/activity/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/artifacts/browse/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/artifacts/file/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/boards/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/commit/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/commits/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/find_file/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/init_blob.js4
-rw-r--r--app/assets/javascripts/pages/projects/issues/form.js2
-rw-r--r--app/assets/javascripts/pages/projects/issues/index/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/issues/show.js2
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/index/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js2
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js2
-rw-r--r--app/assets/javascripts/pages/projects/network/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue2
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue12
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/tree/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue4
-rw-r--r--app/assets/javascripts/pages/projects/wikis/index.js2
-rw-r--r--app/assets/javascripts/performance_bar/components/performance_bar_app.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component.vue8
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_component.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/header_component.vue6
-rw-r--r--app/assets/javascripts/pipelines/components/nav_controls.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_url.vue8
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/stage.vue6
-rw-r--r--app/assets/javascripts/pipelines/mixins/pipelines.js2
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js2
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue6
-rw-r--r--app/assets/javascripts/registry/components/app.vue6
-rw-r--r--app/assets/javascripts/registry/components/collapsible_container.vue8
-rw-r--r--app/assets/javascripts/registry/components/table_registry.vue2
-rw-r--r--app/assets/javascripts/reports/components/grouped_test_reports_app.vue4
-rw-r--r--app/assets/javascripts/reports/components/report_issues.vue4
-rw-r--r--app/assets/javascripts/reports/components/summary_row.vue4
-rw-r--r--app/assets/javascripts/search_autocomplete.js6
-rw-r--r--app/assets/javascripts/sidebar/components/participants/participants.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/todo_toggle/todo.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue12
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/index.js5
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/bar_chart.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/file_icon.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/file_row.vue210
-rw-r--r--app/assets/javascripts/vue_shared/components/header_ci_component.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/loading_button.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/loading_icon.vue45
-rw-r--r--app/assets/javascripts/vue_shared/components/pagination_links.vue34
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/toggle_button.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue2
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss1
-rw-r--r--app/assets/stylesheets/framework/buttons.scss8
-rw-r--r--app/assets/stylesheets/framework/filters.scss5
-rw-r--r--app/assets/stylesheets/framework/lists.scss1
-rw-r--r--app/assets/stylesheets/framework/responsive_tables.scss2
-rw-r--r--app/assets/stylesheets/framework/snippets.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss142
-rw-r--r--app/assets/stylesheets/page_bundles/xterm.scss (renamed from app/assets/stylesheets/pages/xterm.scss)2
-rw-r--r--app/assets/stylesheets/pages/boards.scss10
-rw-r--r--app/assets/stylesheets/pages/clusters.scss57
-rw-r--r--app/assets/stylesheets/pages/groups.scss4
-rw-r--r--app/assets/stylesheets/pages/issuable.scss11
-rw-r--r--app/assets/stylesheets/pages/members.scss2
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss2
-rw-r--r--app/assets/stylesheets/pages/note_form.scss2
-rw-r--r--app/assets/stylesheets/pages/profile.scss4
-rw-r--r--app/assets/stylesheets/pages/search.scss2
239 files changed, 1753 insertions, 1559 deletions
diff --git a/app/assets/images/cluster_app_logos/elasticsearch.png b/app/assets/images/cluster_app_logos/elasticsearch.png
new file mode 100644
index 00000000000..96e9e0ff934
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/elasticsearch.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/gitlab.png b/app/assets/images/cluster_app_logos/gitlab.png
new file mode 100644
index 00000000000..cb2195fc6a2
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/gitlab.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/helm.png b/app/assets/images/cluster_app_logos/helm.png
new file mode 100644
index 00000000000..2989cae7b93
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/helm.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/jeager.png b/app/assets/images/cluster_app_logos/jeager.png
new file mode 100644
index 00000000000..be5bf2a0c9c
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/jeager.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/jupyterhub.png b/app/assets/images/cluster_app_logos/jupyterhub.png
new file mode 100644
index 00000000000..80c7343067f
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/jupyterhub.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/kubernetes.png b/app/assets/images/cluster_app_logos/kubernetes.png
new file mode 100644
index 00000000000..4d774909c10
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/kubernetes.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/meltano.png b/app/assets/images/cluster_app_logos/meltano.png
new file mode 100644
index 00000000000..7a2d82fbe27
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/meltano.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/prometheus.png b/app/assets/images/cluster_app_logos/prometheus.png
new file mode 100644
index 00000000000..a8663449b88
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/prometheus.png
Binary files differ
diff --git a/app/assets/javascripts/badges/components/badge.vue b/app/assets/javascripts/badges/components/badge.vue
index 155c348286c..97232d7f783 100644
--- a/app/assets/javascripts/badges/components/badge.vue
+++ b/app/assets/javascripts/badges/components/badge.vue
@@ -1,13 +1,11 @@
<script>
import Icon from '~/vue_shared/components/icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import Tooltip from '~/vue_shared/directives/tooltip';
export default {
name: 'Badge',
components: {
Icon,
- LoadingIcon,
Tooltip,
},
directives: {
@@ -80,7 +78,7 @@ export default {
/>
</a>
- <loading-icon
+ <gl-loading-icon
v-show="isLoading"
:inline="true"
/>
@@ -105,8 +103,8 @@ export default {
</div>
<button
- v-tooltip
v-show="hasError"
+ v-tooltip
:title="s__('Badges|Reload badge image')"
class="btn btn-transparent btn-sm text-primary"
type="button"
diff --git a/app/assets/javascripts/badges/components/badge_form.vue b/app/assets/javascripts/badges/components/badge_form.vue
index b3f25da87ce..aff7c4180e3 100644
--- a/app/assets/javascripts/badges/components/badge_form.vue
+++ b/app/assets/javascripts/badges/components/badge_form.vue
@@ -4,7 +4,6 @@ import { mapActions, mapState } from 'vuex';
import createFlash from '~/flash';
import { s__, sprintf } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import createEmptyBadge from '../empty_badge';
import Badge from './badge.vue';
@@ -15,7 +14,6 @@ export default {
components: {
Badge,
LoadingButton,
- LoadingIcon,
},
props: {
isEditing: {
@@ -207,7 +205,7 @@ export default {
:link-url="renderedLinkUrl"
/>
<p v-show="isRendering">
- <loading-icon
+ <gl-loading-icon
:inline="true"
/>
</p>
diff --git a/app/assets/javascripts/badges/components/badge_list.vue b/app/assets/javascripts/badges/components/badge_list.vue
index d2ec0fbb2c0..359d3e10380 100644
--- a/app/assets/javascripts/badges/components/badge_list.vue
+++ b/app/assets/javascripts/badges/components/badge_list.vue
@@ -1,6 +1,5 @@
<script>
import { mapState } from 'vuex';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import BadgeListRow from './badge_list_row.vue';
import { GROUP_BADGE } from '../constants';
@@ -8,7 +7,6 @@ export default {
name: 'BadgeList',
components: {
BadgeListRow,
- LoadingIcon,
},
computed: {
...mapState(['badges', 'isLoading', 'kind']),
@@ -31,10 +29,10 @@ export default {
class="badge badge-pill"
>{{ badges.length }}</span>
</div>
- <loading-icon
+ <gl-loading-icon
v-show="isLoading"
+ :size="2"
class="card-body"
- size="2"
/>
<div
v-if="hasNoBadges"
diff --git a/app/assets/javascripts/badges/components/badge_list_row.vue b/app/assets/javascripts/badges/components/badge_list_row.vue
index 712d81d0430..5d16ba3ce6d 100644
--- a/app/assets/javascripts/badges/components/badge_list_row.vue
+++ b/app/assets/javascripts/badges/components/badge_list_row.vue
@@ -2,7 +2,6 @@
import { mapActions, mapState } from 'vuex';
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import { PROJECT_BADGE } from '../constants';
import Badge from './badge.vue';
@@ -11,7 +10,6 @@ export default {
components: {
Badge,
Icon,
- LoadingIcon,
},
props: {
badge: {
@@ -79,7 +77,7 @@ export default {
name="remove"
/>
</button>
- <loading-icon
+ <gl-loading-icon
v-show="badge.isDeleting"
:inline="true"
/>
diff --git a/app/assets/javascripts/behaviors/index.js b/app/assets/javascripts/behaviors/index.js
index 84fef4d8b4f..8c4eccc34a3 100644
--- a/app/assets/javascripts/behaviors/index.js
+++ b/app/assets/javascripts/behaviors/index.js
@@ -1,15 +1,19 @@
import './autosize';
import './bind_in_out';
import './markdown/render_gfm';
+import initGFMInput from './markdown/gfm_auto_complete';
import initCopyAsGFM from './markdown/copy_as_gfm';
import initCopyToClipboard from './copy_to_clipboard';
import './details_behavior';
import installGlEmojiElement from './gl_emoji';
import './quick_submit';
import './requires_input';
+import initPageShortcuts from './shortcuts';
import './toggler_behavior';
-import '../preview_markdown';
+import './preview_markdown';
installGlEmojiElement();
+initGFMInput();
initCopyAsGFM();
initCopyToClipboard();
+initPageShortcuts();
diff --git a/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js b/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js
new file mode 100644
index 00000000000..a303e504cc7
--- /dev/null
+++ b/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js
@@ -0,0 +1,19 @@
+import $ from 'jquery';
+import { convertPermissionToBoolean } from '~/lib/utils/common_utils';
+import GfmAutoComplete from '~/gfm_auto_complete';
+
+export default function initGFMInput() {
+ $('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => {
+ const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources);
+ const enableGFM = convertPermissionToBoolean(el.dataset.supportsAutocomplete);
+
+ gfm.setup($(el), {
+ emojis: true,
+ members: enableGFM,
+ issues: enableGFM,
+ milestones: enableGFM,
+ mergeRequests: enableGFM,
+ labels: enableGFM,
+ });
+ });
+}
diff --git a/app/assets/javascripts/behaviors/markdown/render_gfm.js b/app/assets/javascripts/behaviors/markdown/render_gfm.js
index dbff2bd4b10..429455f97ec 100644
--- a/app/assets/javascripts/behaviors/markdown/render_gfm.js
+++ b/app/assets/javascripts/behaviors/markdown/render_gfm.js
@@ -3,7 +3,7 @@ import syntaxHighlight from '~/syntax_highlight';
import renderMath from './render_math';
import renderMermaid from './render_mermaid';
-// Render Gitlab flavoured Markdown
+// Render GitLab flavoured Markdown
//
// Delegates to syntax highlight and render math & mermaid diagrams.
//
diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/behaviors/preview_markdown.js
index 0964baf8954..0964baf8954 100644
--- a/app/assets/javascripts/preview_markdown.js
+++ b/app/assets/javascripts/behaviors/preview_markdown.js
diff --git a/app/assets/javascripts/behaviors/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts.js
new file mode 100644
index 00000000000..7987a533ae5
--- /dev/null
+++ b/app/assets/javascripts/behaviors/shortcuts.js
@@ -0,0 +1,35 @@
+import Shortcuts from './shortcuts/shortcuts';
+
+export default function initPageShortcuts() {
+ const { page } = document.body.dataset;
+ const pagesWithCustomShortcuts = [
+ 'projects:activity',
+ 'projects:artifacts:browse',
+ 'projects:artifacts:file',
+ 'projects:blame:show',
+ 'projects:blob:show',
+ 'projects:commit:show',
+ 'projects:commits:show',
+ 'projects:find_file:show',
+ 'projects:issues:edit',
+ 'projects:issues:index',
+ 'projects:issues:new',
+ 'projects:issues:show',
+ 'projects:merge_requests:creations:diffs',
+ 'projects:merge_requests:creations:new',
+ 'projects:merge_requests:edit',
+ 'projects:merge_requests:index',
+ 'projects:merge_requests:show',
+ 'projects:network:show',
+ 'projects:show',
+ 'projects:tree:show',
+ 'groups:show',
+ ];
+
+ // the pages above have their own shortcuts sub-classes instantiated elsewhere
+ // TODO: replace this whitelist with something more automated/maintainable
+ if (page && !pagesWithCustomShortcuts.includes(page)) {
+ return new Shortcuts();
+ }
+ return false;
+}
diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
index 99c71d6524a..6719bfd6d22 100644
--- a/app/assets/javascripts/shortcuts.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
@@ -1,9 +1,9 @@
import $ from 'jquery';
import Cookies from 'js-cookie';
import Mousetrap from 'mousetrap';
-import axios from './lib/utils/axios_utils';
-import { refreshCurrentPage, visitUrl } from './lib/utils/url_utility';
-import findAndFollowLink from './shortcuts_dashboard_navigation';
+import axios from '../../lib/utils/axios_utils';
+import { refreshCurrentPage, visitUrl } from '../../lib/utils/url_utility';
+import findAndFollowLink from '../../lib/utils/navigation_utility';
const defaultStopCallback = Mousetrap.stopCallback;
Mousetrap.stopCallback = (e, element, combo) => {
diff --git a/app/assets/javascripts/shortcuts_blob.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js
index 908b9cab93d..052e33b4a2b 100644
--- a/app/assets/javascripts/shortcuts_blob.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js
@@ -1,5 +1,5 @@
import Mousetrap from 'mousetrap';
-import { getLocationHash, visitUrl } from './lib/utils/url_utility';
+import { getLocationHash, visitUrl } from '../../lib/utils/url_utility';
import Shortcuts from './shortcuts';
const defaults = {
diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js
index 8658081c6c2..8658081c6c2 100644
--- a/app/assets/javascripts/shortcuts_find_file.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js
diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
index e9451be31fd..5e48bf5a35c 100644
--- a/app/assets/javascripts/shortcuts_issuable.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
@@ -1,9 +1,9 @@
import $ from 'jquery';
import Mousetrap from 'mousetrap';
import _ from 'underscore';
-import Sidebar from './right_sidebar';
+import Sidebar from '../../right_sidebar';
import Shortcuts from './shortcuts';
-import { CopyAsGFM } from './behaviors/markdown/copy_as_gfm';
+import { CopyAsGFM } from '../markdown/copy_as_gfm';
export default class ShortcutsIssuable extends Shortcuts {
constructor(isMergeRequest) {
diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js
index 6b595764bc5..fa9b2c9f755 100644
--- a/app/assets/javascripts/shortcuts_navigation.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js
@@ -1,5 +1,5 @@
import Mousetrap from 'mousetrap';
-import findAndFollowLink from './shortcuts_dashboard_navigation';
+import findAndFollowLink from '../../lib/utils/navigation_utility';
import Shortcuts from './shortcuts';
export default class ShortcutsNavigation extends Shortcuts {
diff --git a/app/assets/javascripts/shortcuts_network.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js
index a88c280fa3b..a88c280fa3b 100644
--- a/app/assets/javascripts/shortcuts_network.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js
diff --git a/app/assets/javascripts/shortcuts_wiki.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js
index 41865dcf4ba..8b7e6a56d25 100644
--- a/app/assets/javascripts/shortcuts_wiki.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js
@@ -1,6 +1,6 @@
import Mousetrap from 'mousetrap';
import ShortcutsNavigation from './shortcuts_navigation';
-import findAndFollowLink from './shortcuts_dashboard_navigation';
+import findAndFollowLink from '../../lib/utils/navigation_utility';
export default class ShortcutsWiki extends ShortcutsNavigation {
constructor() {
diff --git a/app/assets/javascripts/blob/3d_viewer/index.js b/app/assets/javascripts/blob/3d_viewer/index.js
index 68d4ddad551..1bdf1aeb76c 100644
--- a/app/assets/javascripts/blob/3d_viewer/index.js
+++ b/app/assets/javascripts/blob/3d_viewer/index.js
@@ -29,12 +29,12 @@ export default class Renderer {
this.scene.add(this.camera);
- // Setup the viewer
+ // Set up the viewer
this.setupRenderer();
this.setupGrid();
this.setupLight();
- // Setup OrbitControls
+ // Set up OrbitControls
this.controls = new OrbitControls(
this.camera,
this.renderer.domElement,
diff --git a/app/assets/javascripts/boards/components/board_blank_state.vue b/app/assets/javascripts/boards/components/board_blank_state.vue
index 286529b4d13..cde22725a89 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.vue
+++ b/app/assets/javascripts/boards/components/board_blank_state.vue
@@ -83,7 +83,7 @@ export default {
right on the way to making the most of your board.
</p>
<button
- class="btn btn-create btn-inverted btn-block"
+ class="btn btn-success btn-inverted btn-block"
type="button"
@click.stop="addDefaultLists">
Add default lists
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index bfc8d9b03ad..7ddb22ad824 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -3,7 +3,6 @@ import Sortable from 'sortablejs';
import boardNewIssue from './board_new_issue.vue';
import boardCard from './board_card.vue';
import eventHub from '../eventhub';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
const Store = gl.issueBoards.BoardsStore;
@@ -12,7 +11,6 @@ export default {
components: {
boardCard,
boardNewIssue,
- loadingIcon,
},
props: {
groupId: {
@@ -217,7 +215,7 @@ export default {
v-if="loading"
class="board-list-loading text-center"
aria-label="Loading issues">
- <loading-icon />
+ <gl-loading-icon />
</div>
<board-new-issue
v-if="list.type !== 'closed' && showIssueForm"
@@ -233,19 +231,19 @@ export default {
<board-card
v-for="(issue, index) in issues"
ref="issue"
+ :key="issue.id"
:index="index"
:list="list"
:issue="issue"
:issue-link-base="issueLinkBase"
:group-id="groupId"
:root-path="rootPath"
- :disabled="disabled"
- :key="issue.id" />
+ :disabled="disabled" />
<li
v-if="showCount"
class="board-list-count text-center"
data-issue-id="-1">
- <loading-icon
+ <gl-loading-icon
v-show="list.loadingMore"
label="Loading more issues"
/>
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index 1e3cd43d1f0..f248f53fa51 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -110,9 +110,9 @@ export default {
Title
</label>
<input
+ :id="list.id + '-title'"
ref="input"
v-model="title"
- :id="list.id + '-title'"
class="form-control"
type="text"
name="issue_title"
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue
index d50641dc3a9..f56d3fe000c 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.vue
+++ b/app/assets/javascripts/boards/components/issue_card_inner.vue
@@ -170,8 +170,8 @@
tooltip-placement="bottom"
/>
<span
- v-tooltip
v-if="shouldRenderCounter"
+ v-tooltip
:title="assigneeCounterTooltip"
class="avatar-counter"
>
@@ -184,10 +184,10 @@
class="board-card-footer"
>
<button
- v-tooltip
v-for="label in issue.labels"
v-if="showLabel(label)"
:key="label.id"
+ v-tooltip
:style="labelStyle(label)"
:title="label.description"
class="badge color-label"
diff --git a/app/assets/javascripts/boards/components/modal/index.vue b/app/assets/javascripts/boards/components/modal/index.vue
index 7b33a7573e7..0c4c709324d 100644
--- a/app/assets/javascripts/boards/components/modal/index.vue
+++ b/app/assets/javascripts/boards/components/modal/index.vue
@@ -1,7 +1,6 @@
<script>
/* global ListIssue */
import { urlParamsToObject } from '~/lib/utils/common_utils';
- import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import ModalHeader from './header.vue';
import ModalList from './list.vue';
import ModalFooter from './footer.vue';
@@ -14,7 +13,6 @@
ModalHeader,
ModalList,
ModalFooter,
- loadingIcon,
},
props: {
newIssuePath: {
@@ -167,7 +165,7 @@
class="add-issues-list text-center"
>
<div class="add-issues-list-loading">
- <loading-icon />
+ <gl-loading-icon />
</div>
</section>
<modal-footer/>
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
index ef9844d5562..d4676914e02 100644
--- a/app/assets/javascripts/boards/components/project_select.vue
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -2,14 +2,10 @@
import $ from 'jquery';
import _ from 'underscore';
import eventHub from '../eventhub';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import Api from '../../api';
export default {
name: 'BoardProjectSelect',
- components: {
- loadingIcon,
- },
props: {
groupId: {
type: Number,
@@ -119,7 +115,7 @@ export default {
</div>
<div class="dropdown-content"></div>
<div class="dropdown-loading">
- <loading-icon />
+ <gl-loading-icon />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index bc263cbbfea..662363a6f26 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -9,7 +9,7 @@ import '~/vue_shared/models/assignee';
import FilteredSearchBoards from './filtered_search_boards';
import eventHub from './eventhub';
-import sidebarEventHub from '~/sidebar/event_hub'; // eslint-disable-line import/first
+import sidebarEventHub from '~/sidebar/event_hub';
import './models/issue';
import './models/list';
import './models/milestone';
@@ -24,7 +24,7 @@ import './components/board';
import './components/board_sidebar';
import './components/new_list_dropdown';
import BoardAddIssuesModal from './components/modal/index.vue';
-import '~/vue_shared/vue_resource_interceptor'; // eslint-disable-line import/first
+import '~/vue_shared/vue_resource_interceptor';
export default () => {
const $boardApp = document.getElementById('board-app');
@@ -229,7 +229,7 @@ export default () => {
template: `
<div class="board-extra-actions">
<button
- class="btn btn-create prepend-left-10"
+ class="btn btn-success prepend-left-10"
type="button"
data-placement="bottom"
ref="addIssuesButton"
diff --git a/app/assets/javascripts/build_variables.js b/app/assets/javascripts/build_variables.js
deleted file mode 100644
index d398e4a4c83..00000000000
--- a/app/assets/javascripts/build_variables.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import $ from 'jquery';
-
-export default function handleRevealVariables() {
- $('.js-reveal-variables')
- .off('click')
- .on('click', function click() {
- $('.js-build-variables').toggle();
- $(this).hide();
- });
-}
diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue
index 651f3b50236..0452729d3ff 100644
--- a/app/assets/javascripts/clusters/components/application_row.vue
+++ b/app/assets/javascripts/clusters/components/application_row.vue
@@ -2,6 +2,7 @@
/* eslint-disable vue/require-default-prop */
import { s__, sprintf } from '../../locale';
import eventHub from '../event_hub';
+ import identicon from '../../vue_shared/components/identicon.vue';
import loadingButton from '../../vue_shared/components/loading_button.vue';
import {
APPLICATION_STATUS,
@@ -13,6 +14,7 @@
export default {
components: {
loadingButton,
+ identicon,
},
props: {
id: {
@@ -31,6 +33,16 @@
type: String,
required: false,
},
+ logoUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
status: {
type: String,
required: false,
@@ -60,6 +72,18 @@
isKnownStatus() {
return Object.values(APPLICATION_STATUS).includes(this.status);
},
+ isInstalled() {
+ return (
+ this.status === APPLICATION_STATUS.INSTALLED || this.status === APPLICATION_STATUS.UPDATED
+ );
+ },
+ hasLogo() {
+ return !!this.logoUrl;
+ },
+ identiconId() {
+ // generate a deterministic integer id for the identicon background
+ return this.id.charCodeAt(0);
+ },
rowJsClass() {
return `js-cluster-application-row-${this.id}`;
},
@@ -128,37 +152,81 @@
<template>
<div
- :class="rowJsClass"
- class="gl-responsive-table-row gl-responsive-table-row-col-span"
+ :class="[
+ rowJsClass,
+ isInstalled && 'cluster-application-installed',
+ disabled && 'cluster-application-disabled'
+ ]"
+ class="cluster-application-row gl-responsive-table-row gl-responsive-table-row-col-span"
>
<div
class="gl-responsive-table-row-layout"
role="row"
>
- <a
- v-if="titleLink"
- :href="titleLink"
- target="blank"
- rel="noopener noreferrer"
+ <div
+ class="table-section append-right-8 section-align-top"
role="gridcell"
- class="table-section section-15 section-align-top js-cluster-application-title"
>
- {{ title }}
- </a>
- <span
- v-else
- class="table-section section-15 section-align-top js-cluster-application-title"
- >
- {{ title }}
- </span>
+ <img
+ v-if="hasLogo"
+ :src="logoUrl"
+ :alt="`${title} logo`"
+ class="cluster-application-logo avatar s40"
+ />
+ <identicon
+ v-else
+ :entity-id="identiconId"
+ :entity-name="title"
+ size-class="s40"
+ />
+ </div>
<div
- class="table-section section-wrap"
+ class="table-section cluster-application-description section-wrap"
role="gridcell"
>
+ <strong>
+ <a
+ v-if="titleLink"
+ :href="titleLink"
+ target="blank"
+ rel="noopener noreferrer"
+ class="js-cluster-application-title"
+ >
+ {{ title }}
+ </a>
+ <span
+ v-else
+ class="js-cluster-application-title"
+ >
+ {{ title }}
+ </span>
+ </strong>
<slot name="description"></slot>
+ <div
+ v-if="hasError || isUnknownStatus"
+ class="cluster-application-error text-danger prepend-top-10"
+ >
+ <p class="js-cluster-application-general-error-message append-bottom-0">
+ {{ generalErrorDescription }}
+ </p>
+ <ul v-if="statusReason || requestReason">
+ <li
+ v-if="statusReason"
+ class="js-cluster-application-status-error-message"
+ >
+ {{ statusReason }}
+ </li>
+ <li
+ v-if="requestReason"
+ class="js-cluster-application-request-error-message"
+ >
+ {{ requestReason }}
+ </li>
+ </ul>
+ </div>
</div>
<div
- :class="{ 'section-20': showManageButton, 'section-15': !showManageButton }"
+ :class="{ 'section-25': showManageButton, 'section-15': !showManageButton }"
class="table-section table-button-footer section-align-top"
role="gridcell"
>
@@ -168,6 +236,7 @@
>
<a
:href="manageLink"
+ :class="{ disabled: disabled }"
class="btn"
>
{{ manageButtonLabel }}
@@ -176,7 +245,7 @@
<div class="btn-group table-action-buttons">
<loading-button
:loading="installButtonLoading"
- :disabled="installButtonDisabled"
+ :disabled="disabled || installButtonDisabled"
:label="installButtonLabel"
class="js-cluster-application-install-button"
@click="installClicked"
@@ -184,35 +253,5 @@
</div>
</div>
</div>
- <div
- v-if="hasError || isUnknownStatus"
- class="gl-responsive-table-row-layout"
- role="row"
- >
- <div
- class="alert alert-danger alert-block append-bottom-0 clusters-error-alert"
- role="gridcell"
- >
- <div>
- <p class="js-cluster-application-general-error-message">
- {{ generalErrorDescription }}
- </p>
- <ul v-if="statusReason || requestReason">
- <li
- v-if="statusReason"
- class="js-cluster-application-status-error-message"
- >
- {{ statusReason }}
- </li>
- <li
- v-if="requestReason"
- class="js-cluster-application-request-error-message"
- >
- {{ requestReason }}
- </li>
- </ul>
- </div>
- </div>
- </div>
</div>
</template>
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index d708a9e595a..a1069985178 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -1,5 +1,14 @@
<script>
import _ from 'underscore';
+import helmInstallIllustration from '@gitlab-org/gitlab-svgs/illustrations/kubernetes-installation.svg';
+import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png';
+import gitlabLogo from 'images/cluster_app_logos/gitlab.png';
+import helmLogo from 'images/cluster_app_logos/helm.png';
+import jeagerLogo from 'images/cluster_app_logos/jeager.png';
+import jupyterhubLogo from 'images/cluster_app_logos/jupyterhub.png';
+import kubernetesLogo from 'images/cluster_app_logos/kubernetes.png';
+import meltanoLogo from 'images/cluster_app_logos/meltano.png';
+import prometheusLogo from 'images/cluster_app_logos/prometheus.png';
import { s__, sprintf } from '../../locale';
import applicationRow from './application_row.vue';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
@@ -37,21 +46,21 @@ export default {
default: '',
},
},
+ data: () => ({
+ elasticsearchLogo,
+ gitlabLogo,
+ helmLogo,
+ jeagerLogo,
+ jupyterhubLogo,
+ kubernetesLogo,
+ meltanoLogo,
+ prometheusLogo,
+ }),
computed: {
- generalApplicationDescription() {
- return sprintf(
- _.escape(
- s__(
- `ClusterIntegration|Install applications on your Kubernetes cluster.
- Read more about %{helpLink}`,
- ),
- ),
- {
- helpLink: `<a href="${this.helpPath}">
- ${_.escape(s__('ClusterIntegration|installing applications'))}
- </a>`,
- },
- false,
+ helmInstalled() {
+ return (
+ this.applications.helm.status === APPLICATION_STATUS.INSTALLED ||
+ this.applications.helm.status === APPLICATION_STATUS.UPDATED
);
},
ingressId() {
@@ -128,224 +137,240 @@ export default {
return this.applications.jupyter.hostname;
},
},
+ created() {
+ this.helmInstallIllustration = helmInstallIllustration;
+ },
};
</script>
<template>
- <section
- id="cluster-applications"
- class="settings no-animate expanded"
- >
- <div class="settings-header">
- <h4>
- {{ s__('ClusterIntegration|Applications') }}
- </h4>
- <p
- class="append-bottom-0"
- v-html="generalApplicationDescription"
- >
- </p>
- </div>
+ <section id="cluster-applications">
+ <h4>
+ {{ s__('ClusterIntegration|Applications') }}
+ </h4>
+ <p class="append-bottom-0">
+ {{ s__(`ClusterIntegration|Choose which applications to install on your Kubernetes cluster.
+ Helm Tiller is required to install any of the following applications.`) }}
+ <a :href="helpPath">
+ {{ __('More information') }}
+ </a>
+ </p>
- <div class="settings-content">
- <div class="append-bottom-20">
- <application-row
- id="helm"
- :title="applications.helm.title"
- :status="applications.helm.status"
- :status-reason="applications.helm.statusReason"
- :request-status="applications.helm.requestStatus"
- :request-reason="applications.helm.requestReason"
- title-link="https://docs.helm.sh/"
- >
- <div slot="description">
- {{ s__(`ClusterIntegration|Helm streamlines installing
- and managing Kubernetes applications.
- Tiller runs inside of your Kubernetes Cluster,
- and manages releases of your charts.`) }}
- </div>
- </application-row>
- <application-row
- :id="ingressId"
- :title="applications.ingress.title"
- :status="applications.ingress.status"
- :status-reason="applications.ingress.statusReason"
- :request-status="applications.ingress.requestStatus"
- :request-reason="applications.ingress.requestReason"
- title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/"
+ <div class="cluster-application-list prepend-top-10">
+ <application-row
+ id="helm"
+ :logo-url="helmLogo"
+ :title="applications.helm.title"
+ :status="applications.helm.status"
+ :status-reason="applications.helm.statusReason"
+ :request-status="applications.helm.requestStatus"
+ :request-reason="applications.helm.requestReason"
+ class="rounded-top"
+ title-link="https://docs.helm.sh/"
+ >
+ <div slot="description">
+ {{ s__(`ClusterIntegration|Helm streamlines installing
+ and managing Kubernetes applications.
+ Tiller runs inside of your Kubernetes Cluster,
+ and manages releases of your charts.`) }}
+ </div>
+ </application-row>
+ <div
+ v-show="!helmInstalled"
+ class="cluster-application-warning"
+ >
+ <div
+ class="svg-container"
+ v-html="helmInstallIllustration"
>
- <div slot="description">
- <p>
- {{ s__(`ClusterIntegration|Ingress gives you a way to route
- requests to services based on the request host or path,
- centralizing a number of services into a single entrypoint.`) }}
- </p>
+ </div>
+ {{ s__(`ClusterIntegration|You must first install Helm Tiller before
+ installing the applications below`) }}
+ </div>
+ <application-row
+ :id="ingressId"
+ :logo-url="kubernetesLogo"
+ :title="applications.ingress.title"
+ :status="applications.ingress.status"
+ :status-reason="applications.ingress.statusReason"
+ :request-status="applications.ingress.requestStatus"
+ :request-reason="applications.ingress.requestReason"
+ :disabled="!helmInstalled"
+ title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/"
+ >
+ <div slot="description">
+ <p>
+ {{ s__(`ClusterIntegration|Ingress gives you a way to route
+ requests to services based on the request host or path,
+ centralizing a number of services into a single entrypoint.`) }}
+ </p>
- <template v-if="ingressInstalled">
- <div class="form-group">
- <label for="ingress-ip-address">
- {{ s__('ClusterIntegration|Ingress IP Address') }}
- </label>
- <div
- v-if="ingressExternalIp"
- class="input-group"
- >
- <input
- id="ingress-ip-address"
- :value="ingressExternalIp"
- type="text"
- class="form-control js-ip-address"
- readonly
- />
- <span class="input-group-append">
- <clipboard-button
- :text="ingressExternalIp"
- :title="s__('ClusterIntegration|Copy Ingress IP Address to clipboard')"
- class="input-group-text js-clipboard-btn"
- />
- </span>
- </div>
+ <template v-if="ingressInstalled">
+ <div class="form-group">
+ <label for="ingress-ip-address">
+ {{ s__('ClusterIntegration|Ingress IP Address') }}
+ </label>
+ <div
+ v-if="ingressExternalIp"
+ class="input-group"
+ >
<input
- v-else
+ id="ingress-ip-address"
+ :value="ingressExternalIp"
type="text"
class="form-control js-ip-address"
readonly
- value="?"
/>
+ <span class="input-group-append">
+ <clipboard-button
+ :text="ingressExternalIp"
+ :title="s__('ClusterIntegration|Copy Ingress IP Address to clipboard')"
+ class="input-group-text js-clipboard-btn"
+ />
+ </span>
</div>
+ <input
+ v-else
+ type="text"
+ class="form-control js-ip-address"
+ readonly
+ value="?"
+ />
+ </div>
- <p
- v-if="!ingressExternalIp"
- class="settings-message js-no-ip-message"
- >
- {{ s__(`ClusterIntegration|The IP address is in
- the process of being assigned. Please check your Kubernetes
- cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) }}
+ <p
+ v-if="!ingressExternalIp"
+ class="settings-message js-no-ip-message"
+ >
+ {{ s__(`ClusterIntegration|The IP address is in
+ the process of being assigned. Please check your Kubernetes
+ cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) }}
- <a
- :href="ingressHelpPath"
- target="_blank"
- rel="noopener noreferrer"
- >
- {{ __('More information') }}
- </a>
- </p>
+ <a
+ :href="ingressHelpPath"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ {{ __('More information') }}
+ </a>
+ </p>
- <p>
- {{ s__(`ClusterIntegration|Point a wildcard DNS to this
- generated IP address in order to access
- your application after it has been deployed.`) }}
- <a
- :href="ingressDnsHelpPath"
- target="_blank"
- rel="noopener noreferrer"
- >
- {{ __('More information') }}
- </a>
- </p>
+ <p>
+ {{ s__(`ClusterIntegration|Point a wildcard DNS to this
+ generated IP address in order to access
+ your application after it has been deployed.`) }}
+ <a
+ :href="ingressDnsHelpPath"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ {{ __('More information') }}
+ </a>
+ </p>
- </template>
- <div
- v-else
- v-html="ingressDescription"
- >
- </div>
- </div>
- </application-row>
- <application-row
- id="prometheus"
- :title="applications.prometheus.title"
- :manage-link="managePrometheusPath"
- :status="applications.prometheus.status"
- :status-reason="applications.prometheus.statusReason"
- :request-status="applications.prometheus.requestStatus"
- :request-reason="applications.prometheus.requestReason"
- title-link="https://prometheus.io/docs/introduction/overview/"
- >
+ </template>
<div
- slot="description"
- v-html="prometheusDescription"
+ v-html="ingressDescription"
>
</div>
- </application-row>
- <application-row
- id="runner"
- :title="applications.runner.title"
- :status="applications.runner.status"
- :status-reason="applications.runner.statusReason"
- :request-status="applications.runner.requestStatus"
- :request-reason="applications.runner.requestReason"
- title-link="https://docs.gitlab.com/runner/"
- >
- <div slot="description">
- {{ s__(`ClusterIntegration|GitLab Runner connects to this
- project's repository and executes CI/CD jobs,
- pushing results back and deploying,
- applications to production.`) }}
- </div>
- </application-row>
- <application-row
- id="jupyter"
- :title="applications.jupyter.title"
- :status="applications.jupyter.status"
- :status-reason="applications.jupyter.statusReason"
- :request-status="applications.jupyter.requestStatus"
- :request-reason="applications.jupyter.requestReason"
- :install-application-request-params="{ hostname: applications.jupyter.hostname }"
- title-link="https://jupyterhub.readthedocs.io/en/stable/"
+ </div>
+ </application-row>
+ <application-row
+ id="prometheus"
+ :logo-url="prometheusLogo"
+ :title="applications.prometheus.title"
+ :manage-link="managePrometheusPath"
+ :status="applications.prometheus.status"
+ :status-reason="applications.prometheus.statusReason"
+ :request-status="applications.prometheus.requestStatus"
+ :request-reason="applications.prometheus.requestReason"
+ :disabled="!helmInstalled"
+ title-link="https://prometheus.io/docs/introduction/overview/"
+ >
+ <div
+ slot="description"
+ v-html="prometheusDescription"
>
- <div slot="description">
- <p>
- {{ s__(`ClusterIntegration|JupyterHub, a multi-user Hub, spawns,
- manages, and proxies multiple instances of the single-user
- Jupyter notebook server. JupyterHub can be used to serve
- notebooks to a class of students, a corporate data science group,
- or a scientific research group.`) }}
- </p>
+ </div>
+ </application-row>
+ <application-row
+ id="runner"
+ :logo-url="gitlabLogo"
+ :title="applications.runner.title"
+ :status="applications.runner.status"
+ :status-reason="applications.runner.statusReason"
+ :request-status="applications.runner.requestStatus"
+ :request-reason="applications.runner.requestReason"
+ :disabled="!helmInstalled"
+ title-link="https://docs.gitlab.com/runner/"
+ >
+ <div slot="description">
+ {{ s__(`ClusterIntegration|GitLab Runner connects to this
+ project's repository and executes CI/CD jobs,
+ pushing results back and deploying,
+ applications to production.`) }}
+ </div>
+ </application-row>
+ <application-row
+ id="jupyter"
+ :logo-url="jupyterhubLogo"
+ :title="applications.jupyter.title"
+ :status="applications.jupyter.status"
+ :status-reason="applications.jupyter.statusReason"
+ :request-status="applications.jupyter.requestStatus"
+ :request-reason="applications.jupyter.requestReason"
+ :install-application-request-params="{ hostname: applications.jupyter.hostname }"
+ :disabled="!helmInstalled"
+ class="hide-bottom-border rounded-bottom"
+ title-link="https://jupyterhub.readthedocs.io/en/stable/"
+ >
+ <div slot="description">
+ <p>
+ {{ s__(`ClusterIntegration|JupyterHub, a multi-user Hub, spawns,
+ manages, and proxies multiple instances of the single-user
+ Jupyter notebook server. JupyterHub can be used to serve
+ notebooks to a class of students, a corporate data science group,
+ or a scientific research group.`) }}
+ </p>
- <template v-if="ingressExternalIp">
- <div class="form-group">
- <label for="jupyter-hostname">
- {{ s__('ClusterIntegration|Jupyter Hostname') }}
- </label>
+ <template v-if="ingressExternalIp">
+ <div class="form-group">
+ <label for="jupyter-hostname">
+ {{ s__('ClusterIntegration|Jupyter Hostname') }}
+ </label>
- <div class="input-group">
- <input
- v-model="applications.jupyter.hostname"
- :readonly="jupyterInstalled"
- type="text"
- class="form-control js-hostname"
+ <div class="input-group">
+ <input
+ v-model="applications.jupyter.hostname"
+ :readonly="jupyterInstalled"
+ type="text"
+ class="form-control js-hostname"
+ />
+ <span
+ class="input-group-btn"
+ >
+ <clipboard-button
+ :text="jupyterHostname"
+ :title="s__('ClusterIntegration|Copy Jupyter Hostname to clipboard')"
+ class="js-clipboard-btn"
/>
- <span
- class="input-group-btn"
- >
- <clipboard-button
- :text="jupyterHostname"
- :title="s__('ClusterIntegration|Copy Jupyter Hostname to clipboard')"
- class="js-clipboard-btn"
- />
- </span>
- </div>
+ </span>
</div>
- <p v-if="ingressInstalled">
- {{ s__(`ClusterIntegration|Replace this with your own hostname if you want.
- If you do so, point hostname to Ingress IP Address from above.`) }}
- <a
- :href="ingressDnsHelpPath"
- target="_blank"
- rel="noopener noreferrer"
- >
- {{ __('More information') }}
- </a>
- </p>
- </template>
- </div>
- </application-row>
- <!--
- NOTE: Don't forget to update `clusters.scss`
- min-height for this block and uncomment `application_spec` tests
- -->
- </div>
+ </div>
+ <p v-if="ingressInstalled">
+ {{ s__(`ClusterIntegration|Replace this with your own hostname if you want.
+ If you do so, point hostname to Ingress IP Address from above.`) }}
+ <a
+ :href="ingressDnsHelpPath"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ {{ __('More information') }}
+ </a>
+ </p>
+ </template>
+ </div>
+ </application-row>
</div>
</section>
</template>
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
index 95c4be64d35..4849b0fa3db 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -76,10 +76,10 @@
<template>
<div class="content-list pipelines">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
:label="s__('Pipelines|Loading Pipelines')"
- size="3"
+ :size="3"
class="prepend-top-20"
/>
diff --git a/app/assets/javascripts/commons/gitlab_ui.js b/app/assets/javascripts/commons/gitlab_ui.js
index 14c2db24205..aed26adfa5c 100644
--- a/app/assets/javascripts/commons/gitlab_ui.js
+++ b/app/assets/javascripts/commons/gitlab_ui.js
@@ -1,12 +1,16 @@
import Vue from 'vue';
+import Pagination from '@gitlab-org/gitlab-ui/dist/components/base/pagination';
import progressBar from '@gitlab-org/gitlab-ui/dist/components/base/progress_bar';
import modal from '@gitlab-org/gitlab-ui/dist/components/base/modal';
+import loadingIcon from '@gitlab-org/gitlab-ui/dist/components/base/loading_icon';
import dModal from '@gitlab-org/gitlab-ui/dist/directives/modal';
import dTooltip from '@gitlab-org/gitlab-ui/dist/directives/tooltip';
+Vue.component('gl-pagination', Pagination);
Vue.component('gl-progress-bar', progressBar);
Vue.component('gl-ui-modal', modal);
+Vue.component('gl-loading-icon', loadingIcon);
Vue.directive('gl-modal', dModal);
Vue.directive('gl-tooltip', dTooltip);
diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js
index 742cf490ad2..539d0d29e0d 100644
--- a/app/assets/javascripts/commons/polyfills.js
+++ b/app/assets/javascripts/commons/polyfills.js
@@ -14,10 +14,10 @@ import 'core-js/es6/map';
import 'core-js/es6/weak-map';
// Browser polyfills
-import 'classlist-polyfill';
import 'formdata-polyfill';
import './polyfills/custom_event';
import './polyfills/element';
import './polyfills/event';
import './polyfills/nodelist';
import './polyfills/request_idle_callback';
+import './polyfills/svg';
diff --git a/app/assets/javascripts/commons/polyfills/element.js b/app/assets/javascripts/commons/polyfills/element.js
index b593bde6aa2..dde5e8f54f9 100644
--- a/app/assets/javascripts/commons/polyfills/element.js
+++ b/app/assets/javascripts/commons/polyfills/element.js
@@ -1,12 +1,17 @@
-Element.prototype.closest = Element.prototype.closest ||
+// polyfill Element.classList and DOMTokenList with classList.js
+import 'classlist-polyfill';
+
+Element.prototype.closest =
+ Element.prototype.closest ||
function closest(selector, selectedElement = this) {
if (!selectedElement) return null;
- return selectedElement.matches(selector) ?
- selectedElement :
- Element.prototype.closest(selector, selectedElement.parentElement);
+ return selectedElement.matches(selector)
+ ? selectedElement
+ : Element.prototype.closest(selector, selectedElement.parentElement);
};
-Element.prototype.matches = Element.prototype.matches ||
+Element.prototype.matches =
+ Element.prototype.matches ||
Element.prototype.matchesSelector ||
Element.prototype.mozMatchesSelector ||
Element.prototype.msMatchesSelector ||
@@ -15,13 +20,15 @@ Element.prototype.matches = Element.prototype.matches ||
function matches(selector) {
const elms = (this.document || this.ownerDocument).querySelectorAll(selector);
let i = elms.length - 1;
- while (i >= 0 && elms.item(i) !== this) { i -= 1; }
+ while (i >= 0 && elms.item(i) !== this) {
+ i -= 1;
+ }
return i > -1;
};
// From the polyfill on MDN, https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove#Polyfill
-((arr) => {
- arr.forEach((item) => {
+(arr => {
+ arr.forEach(item => {
if (Object.prototype.hasOwnProperty.call(item, 'remove')) {
return;
}
diff --git a/app/assets/javascripts/commons/polyfills/svg.js b/app/assets/javascripts/commons/polyfills/svg.js
new file mode 100644
index 00000000000..8648a568f6f
--- /dev/null
+++ b/app/assets/javascripts/commons/polyfills/svg.js
@@ -0,0 +1,5 @@
+import svg4everybody from 'svg4everybody';
+
+// polyfill support for external SVG file references via <use xlink:href>
+// @see https://css-tricks.com/svg-use-external-source/
+svg4everybody();
diff --git a/app/assets/javascripts/deploy_keys/components/action_btn.vue b/app/assets/javascripts/deploy_keys/components/action_btn.vue
index 7399fc97d45..10548da8ec5 100644
--- a/app/assets/javascripts/deploy_keys/components/action_btn.vue
+++ b/app/assets/javascripts/deploy_keys/components/action_btn.vue
@@ -1,11 +1,7 @@
<script>
-import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import eventHub from '../eventhub';
export default {
- components: {
- loadingIcon,
- },
props: {
deployKey: {
type: Object,
@@ -45,7 +41,7 @@ export default {
class="btn"
@click="doAction">
<slot></slot>
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
:inline="true"
/>
diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue
index d91e4809126..aa52f120fe7 100644
--- a/app/assets/javascripts/deploy_keys/components/app.vue
+++ b/app/assets/javascripts/deploy_keys/components/app.vue
@@ -1,7 +1,6 @@
<script>
import { s__ } from '~/locale';
import Flash from '~/flash';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
import eventHub from '../eventhub';
import DeployKeysService from '../service';
@@ -11,7 +10,6 @@ import KeysPanel from './keys_panel.vue';
export default {
components: {
KeysPanel,
- LoadingIcon,
NavigationTabs,
},
props: {
@@ -114,10 +112,10 @@ export default {
<template>
<div class="append-bottom-default deploy-keys">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading && !hasKeys"
:label="s__('DeployKeys|Loading deploy keys')"
- size="2"
+ :size="2"
/>
<template v-else-if="hasKeys">
<div class="top-area scrolling-tabs-container inner-page-scroll-tabs">
diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue
index f66ca070445..c05b9b1de79 100644
--- a/app/assets/javascripts/deploy_keys/components/key.vue
+++ b/app/assets/javascripts/deploy_keys/components/key.vue
@@ -145,8 +145,8 @@ export default {
<icon :name="firstProject.can_push ? 'lock-open' : 'lock'"/>
</a>
<a
- v-tooltip
v-if="isExpandable"
+ v-tooltip
:title="restProjectsTooltip"
class="label deploy-project-label"
@click="toggleExpanded"
@@ -154,10 +154,10 @@ export default {
<span>{{ restProjectsLabel }}</span>
</a>
<a
- v-tooltip
v-for="deployKeysProject in restProjects"
v-else-if="isExpanded"
:key="deployKeysProject.project.full_path"
+ v-tooltip
:href="deployKeysProject.project.full_path"
:title="projectTooltipTitle(deployKeysProject)"
class="label deploy-project-label"
@@ -198,8 +198,8 @@ export default {
{{ __('Enable') }}
</action-btn>
<a
- v-tooltip
v-if="deployKey.can_edit"
+ v-tooltip
:href="editDeployKeyPath"
:title="__('Edit')"
class="btn btn-default text-secondary"
@@ -208,8 +208,8 @@ export default {
<icon name="pencil"/>
</a>
<action-btn
- v-tooltip
v-if="isRemovable"
+ v-tooltip
:deploy-key="deployKey"
:title="__('Remove')"
btn-css-class="btn-danger"
@@ -219,8 +219,8 @@ export default {
<icon name="remove"/>
</action-btn>
<action-btn
- v-tooltip
v-else-if="isEnabled"
+ v-tooltip
:deploy-key="deployKey"
:title="__('Disable')"
btn-css-class="btn-warning"
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 4261a99c52b..bfb992340bc 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -4,7 +4,6 @@ import Icon from '~/vue_shared/components/icon.vue';
import { __ } from '~/locale';
import createFlash from '~/flash';
import eventHub from '../../notes/event_hub';
-import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
import CompareVersions from './compare_versions.vue';
import ChangedFiles from './changed_files.vue';
import DiffFile from './diff_file.vue';
@@ -15,7 +14,6 @@ export default {
name: 'DiffsApp',
components: {
Icon,
- LoadingIcon,
CompareVersions,
ChangedFiles,
DiffFile,
@@ -168,7 +166,7 @@ export default {
v-if="isLoading"
class="loading"
>
- <loading-icon />
+ <gl-loading-icon />
</div>
<div
v-else
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 67e85c4eee3..bcbe374a90c 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -3,7 +3,6 @@ import { mapActions, mapGetters } from 'vuex';
import _ from 'underscore';
import { __, sprintf } from '~/locale';
import createFlash from '~/flash';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import DiffFileHeader from './diff_file_header.vue';
import DiffContent from './diff_content.vue';
@@ -11,7 +10,6 @@ export default {
components: {
DiffFileHeader,
DiffContent,
- LoadingIcon,
},
props: {
file: {
@@ -46,7 +44,7 @@ export default {
},
showExpandMessage() {
return (
- !this.isCollapsed &&
+ this.isCollapsed ||
!this.file.highlightedDiffLines &&
!this.isLoadingCollapsedDiff &&
!this.file.tooLarge &&
@@ -144,7 +142,7 @@ export default {
:class="{ hidden: isCollapsed || file.tooLarge }"
:diff-file="file"
/>
- <loading-icon
+ <gl-loading-icon
v-if="showLoadingIcon"
class="diff-content loading"
/>
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index d3ffbe0415a..517fbf400e8 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -181,8 +181,8 @@ export default {
</span>
<strong
- v-tooltip
v-else
+ v-tooltip
:title="filePath"
class="file-title-name"
data-container="body"
@@ -255,8 +255,8 @@ export default {
</a>
<a
- v-tooltip
v-if="diffFile.externalUrl"
+ v-tooltip
:href="diffFile.externalUrl"
:title="`View on ${diffFile.formattedExternalUrl}`"
target="_blank"
diff --git a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue
index a02c41f39ab..1b59777f901 100644
--- a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue
+++ b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue
@@ -87,8 +87,8 @@ export default {
@click.native="toggleDiscussions"
/>
<span
- v-gl-tooltip
v-if="moreText"
+ v-gl-tooltip
:title="moreText"
class="diff-comments-more-count js-diff-comment-avatar js-diff-comment-plus"
data-container="body"
diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
index a0dc381ccc7..bb9bb821de3 100644
--- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
@@ -1,12 +1,9 @@
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
-import createFlash from '~/flash';
import { s__ } from '~/locale';
import noteForm from '../../notes/components/note_form.vue';
-import { getNoteFormData } from '../store/utils';
import autosave from '../../notes/mixins/autosave';
import { DIFF_NOTE_TYPE } from '../constants';
-import { reduceDiscussionsToLineCodes } from '../../notes/stores/utils';
export default {
components: {
@@ -22,7 +19,7 @@ export default {
type: Object,
required: true,
},
- position: {
+ linePosition: {
type: String,
required: false,
default: '',
@@ -39,6 +36,16 @@ export default {
}),
...mapGetters('diffs', ['getDiffFileByHash']),
...mapGetters(['isLoggedIn', 'noteableType', 'getNoteableData', 'getNotesDataByProp']),
+ formData() {
+ return {
+ noteableData: this.noteableData,
+ noteableType: this.noteableType,
+ noteTargetLine: this.noteTargetLine,
+ diffViewType: this.diffViewType,
+ diffFile: this.getDiffFileByHash(this.diffFileHash),
+ linePosition: this.linePosition,
+ };
+ },
},
mounted() {
if (this.isLoggedIn) {
@@ -53,8 +60,7 @@ export default {
}
},
methods: {
- ...mapActions('diffs', ['cancelCommentForm', 'assignDiscussionsToDiff']),
- ...mapActions(['saveNote', 'refetchDiscussionById']),
+ ...mapActions('diffs', ['cancelCommentForm', 'assignDiscussionsToDiff', 'saveDiffDiscussion']),
handleCancelCommentForm(shouldConfirm, isDirty) {
if (shouldConfirm && isDirty) {
const msg = s__('Notes|Are you sure you want to cancel creating this comment?');
@@ -73,35 +79,9 @@ export default {
});
},
handleSaveNote(note) {
- const selectedDiffFile = this.getDiffFileByHash(this.diffFileHash);
- const postData = getNoteFormData({
- note,
- noteableData: this.noteableData,
- noteableType: this.noteableType,
- noteTargetLine: this.noteTargetLine,
- diffViewType: this.diffViewType,
- diffFile: selectedDiffFile,
- linePosition: this.position,
- });
-
- this.saveNote(postData)
- .then(result => {
- const endpoint = this.getNotesDataByProp('discussionsPath');
-
- this.refetchDiscussionById({ path: endpoint, discussionId: result.discussion_id })
- .then(selectedDiscussion => {
- const lineCodeDiscussions = reduceDiscussionsToLineCodes([selectedDiscussion]);
- this.assignDiscussionsToDiff(lineCodeDiscussions);
-
- this.handleCancelCommentForm();
- })
- .catch(() => {
- createFlash(s__('MergeRequests|Updating discussions failed'));
- });
- })
- .catch(() => {
- createFlash(s__('MergeRequests|Saving the comment failed'));
- });
+ return this.saveDiffDiscussion({ note, formData: this.formData }).then(() =>
+ this.handleCancelCommentForm(),
+ );
},
},
};
diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
index 0e306f39a9f..62fa34e835a 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
@@ -1,5 +1,5 @@
<script>
-import { mapGetters } from 'vuex';
+import { mapGetters, mapActions } from 'vuex';
import DiffTableCell from './diff_table_cell.vue';
import {
NEW_LINE_TYPE,
@@ -63,7 +63,11 @@ export default {
this.linePositionLeft = LINE_POSITION_LEFT;
this.linePositionRight = LINE_POSITION_RIGHT;
},
+ mounted() {
+ this.scrollToLineIfNeededInline(this.line);
+ },
methods: {
+ ...mapActions('diffs', ['scrollToLineIfNeededInline']),
handleMouseMove(e) {
// To show the comment icon on the gutter we need to know if we hover the line.
// Current table structure doesn't allow us to do this with CSS in both of the diff view types
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index 947e7c98fae..fbf9e77ac07 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -43,18 +43,18 @@ export default {
v-for="(line, index) in diffLines"
>
<inline-diff-table-row
+ :key="line.lineCode"
:file-hash="diffFile.fileHash"
:context-lines-path="diffFile.contextLinesPath"
:line="line"
:is-bottom="index + 1 === diffLinesLength"
- :key="line.lineCode"
/>
<inline-diff-comment-row
v-if="shouldRenderInlineCommentRow(line)"
+ :key="index"
:diff-file-hash="diffFile.fileHash"
:line="line"
:line-index="index"
- :key="index"
/>
</template>
</tbody>
diff --git a/app/assets/javascripts/diffs/components/no_changes.vue b/app/assets/javascripts/diffs/components/no_changes.vue
index d817157fbcd..6905630ad8c 100644
--- a/app/assets/javascripts/diffs/components/no_changes.vue
+++ b/app/assets/javascripts/diffs/components/no_changes.vue
@@ -38,7 +38,7 @@ export default {
<div class="text-center">
<a
:href="newBlobPath"
- class="btn btn-save"
+ class="btn btn-success"
>
{{ __('Create commit') }}
</a>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
index 26417c350cb..3339c56cbb6 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
@@ -92,7 +92,7 @@ export default {
:diff-file-hash="diffFileHash"
:line="line.left"
:note-target-line="line.left"
- position="left"
+ line-position="left"
/>
</td>
<td class="notes_line new"></td>
@@ -111,7 +111,7 @@ export default {
:diff-file-hash="diffFileHash"
:line="line.right"
:note-target-line="line.right"
- position="right"
+ line-position="right"
/>
</td>
</tr>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
index fb68d191091..fcc3b3e9117 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
@@ -1,4 +1,5 @@
<script>
+import { mapActions } from 'vuex';
import $ from 'jquery';
import DiffTableCell from './diff_table_cell.vue';
import {
@@ -64,7 +65,11 @@ export default {
this.oldLineType = OLD_LINE_TYPE;
this.parallelDiffViewType = PARALLEL_DIFF_VIEW_TYPE;
},
+ mounted() {
+ this.scrollToLineIfNeededParallel(this.line);
+ },
methods: {
+ ...mapActions('diffs', ['scrollToLineIfNeededParallel']),
handleMouseMove(e) {
const isHover = e.type === 'mouseover';
const hoveringCell = e.target.closest('td');
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
index 501bd4450d8..3452f0d2b00 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
@@ -45,11 +45,11 @@ export default {
v-for="(line, index) in diffLines"
>
<parallel-diff-table-row
+ :key="index"
:file-hash="diffFile.fileHash"
:context-lines-path="diffFile.contextLinesPath"
:line="line"
:is-bottom="index + 1 === diffLinesLength"
- :key="index"
/>
<parallel-diff-comment-row
v-if="shouldRenderParallelCommentRow(line)"
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index f68afa44837..2795dddfc48 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -7,6 +7,7 @@ export const CONTEXT_LINE_TYPE = 'context';
export const EMPTY_CELL_TYPE = 'empty-cell';
export const COMMENT_FORM_TYPE = 'commentForm';
export const DIFF_NOTE_TYPE = 'DiffNote';
+export const LEGACY_DIFF_NOTE_TYPE = 'LegacyDiffNote';
export const NOTE_TYPE = 'Note';
export const NEW_LINE_TYPE = 'new';
export const OLD_LINE_TYPE = 'old';
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 027df2ec841..98d8d5943f9 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -1,9 +1,12 @@
import Vue from 'vue';
import axios from '~/lib/utils/axios_utils';
import Cookies from 'js-cookie';
+import createFlash from '~/flash';
+import { s__ } from '~/locale';
import { handleLocationHash, historyPushState } from '~/lib/utils/common_utils';
-import { mergeUrlParams } from '~/lib/utils/url_utility';
-import { getDiffPositionByLineCode } from './utils';
+import { mergeUrlParams, getLocationHash } from '~/lib/utils/url_utility';
+import { reduceDiscussionsToLineCodes } from '../../notes/stores/utils';
+import { getDiffPositionByLineCode, getNoteFormData } from './utils';
import * as types from './mutation_types';
import {
PARALLEL_DIFF_VIEW_TYPE,
@@ -120,6 +123,25 @@ export const loadMoreLines = ({ commit }, options) => {
});
};
+export const scrollToLineIfNeededInline = (_, line) => {
+ const hash = getLocationHash();
+
+ if (hash && line.lineCode === hash) {
+ handleLocationHash();
+ }
+};
+
+export const scrollToLineIfNeededParallel = (_, line) => {
+ const hash = getLocationHash();
+
+ if (
+ hash &&
+ ((line.left && line.left.lineCode === hash) || (line.right && line.right.lineCode === hash))
+ ) {
+ handleLocationHash();
+ }
+};
+
export const loadCollapsedDiff = ({ commit }, file) =>
axios.get(file.loadCollapsedDiffUrl).then(res => {
commit(types.ADD_COLLAPSED_DIFFS, {
@@ -159,5 +181,19 @@ export const toggleFileDiscussions = ({ getters, dispatch }, diff) => {
});
};
+export const saveDiffDiscussion = ({ dispatch }, { note, formData }) => {
+ const postData = getNoteFormData({
+ note,
+ ...formData,
+ });
+
+ return dispatch('saveNote', postData, { root: true })
+ .then(result => dispatch('updateDiscussion', result.discussion, { root: true }))
+ .then(discussion =>
+ dispatch('assignDiscussionsToDiff', reduceDiscussionsToLineCodes([discussion])),
+ )
+ .catch(() => createFlash(s__('MergeRequests|Saving the comment failed')));
+};
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js
index 39d90a64aab..eb596b251c1 100644
--- a/app/assets/javascripts/diffs/store/modules/diff_state.js
+++ b/app/assets/javascripts/diffs/store/modules/diff_state.js
@@ -11,8 +11,10 @@ export default () => ({
endpoint: '',
basePath: '',
commit: null,
+ startVersion: null,
diffFiles: [],
mergeRequestDiffs: [],
+ mergeRequestDiff: null,
diffLineCommentForms: {},
diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType,
});
diff --git a/app/assets/javascripts/diffs/store/modules/index.js b/app/assets/javascripts/diffs/store/modules/index.js
index 20d1ebbe049..6860e24db6b 100644
--- a/app/assets/javascripts/diffs/store/modules/index.js
+++ b/app/assets/javascripts/diffs/store/modules/index.js
@@ -3,10 +3,10 @@ import * as getters from '../getters';
import mutations from '../mutations';
import createState from './diff_state';
-export default {
+export default () => ({
namespaced: true,
state: createState(),
getters,
actions,
mutations,
-};
+});
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index 6dc5bf16c65..59a2c09e54f 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -90,16 +90,18 @@ export default {
const firstDiscussion = discussions[0];
const isDiffDiscussion = firstDiscussion.diff_discussion;
const hasLineCode = firstDiscussion.line_code;
- const isResolvable = firstDiscussion.resolvable;
const diffPosition = diffPositionByLineCode[firstDiscussion.line_code];
if (
selectedFile &&
isDiffDiscussion &&
hasLineCode &&
- isResolvable &&
diffPosition &&
- isDiscussionApplicableToLine(firstDiscussion, diffPosition)
+ isDiscussionApplicableToLine({
+ discussion: firstDiscussion,
+ diffPosition,
+ latestDiff: state.latestDiff,
+ })
) {
const targetLine = selectedFile.parallelDiffLines.find(
line =>
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index b7e52a8f37f..631e3de311e 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -4,6 +4,7 @@ import {
LINE_POSITION_LEFT,
LINE_POSITION_RIGHT,
TEXT_DIFF_POSITION_TYPE,
+ LEGACY_DIFF_NOTE_TYPE,
DIFF_NOTE_TYPE,
NEW_LINE_TYPE,
OLD_LINE_TYPE,
@@ -54,13 +55,17 @@ export function getNoteFormData(params) {
note_project_id: '',
target_type: noteableData.targetType,
target_id: noteableData.id,
+ return_discussion: true,
note: {
note,
position,
noteable_type: noteableType,
noteable_id: noteableData.id,
commit_id: '',
- type: DIFF_NOTE_TYPE,
+ type:
+ diffFile.diffRefs.startSha && diffFile.diffRefs.headSha
+ ? DIFF_NOTE_TYPE
+ : LEGACY_DIFF_NOTE_TYPE,
line_code: noteTargetLine.lineCode,
},
};
@@ -230,7 +235,16 @@ export function getDiffPositionByLineCode(diffFiles) {
const { lineCode, oldLine, newLine } = line;
if (lineCode) {
- acc[lineCode] = { baseSha, headSha, startSha, newPath, oldPath, oldLine, newLine };
+ acc[lineCode] = {
+ baseSha,
+ headSha,
+ startSha,
+ newPath,
+ oldPath,
+ oldLine,
+ newLine,
+ lineCode,
+ };
}
});
}
@@ -241,9 +255,15 @@ export function getDiffPositionByLineCode(diffFiles) {
// This method will check whether the discussion is still applicable
// to the diff line in question regarding different versions of the MR
-export function isDiscussionApplicableToLine(discussion, diffPosition) {
- const originalRefs = convertObjectPropsToCamelCase(discussion.original_position.formatter);
- const refs = convertObjectPropsToCamelCase(discussion.position.formatter);
+export function isDiscussionApplicableToLine({ discussion, diffPosition, latestDiff }) {
+ const { lineCode, ...diffPositionCopy } = diffPosition;
- return _.isEqual(refs, diffPosition) || _.isEqual(originalRefs, diffPosition);
+ if (discussion.original_position && discussion.position) {
+ const originalRefs = convertObjectPropsToCamelCase(discussion.original_position.formatter);
+ const refs = convertObjectPropsToCamelCase(discussion.position.formatter);
+
+ return _.isEqual(refs, diffPositionCopy) || _.isEqual(originalRefs, diffPositionCopy);
+ }
+
+ return latestDiff && discussion.active && lineCode === discussion.line_code;
}
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
deleted file mode 100644
index a5af37e80b6..00000000000
--- a/app/assets/javascripts/dispatcher.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/* eslint-disable consistent-return, no-new */
-
-import $ from 'jquery';
-import GfmAutoComplete from './gfm_auto_complete';
-import { convertPermissionToBoolean } from './lib/utils/common_utils';
-import GlFieldErrors from './gl_field_errors';
-import Shortcuts from './shortcuts';
-import SearchAutocomplete from './search_autocomplete';
-import performanceBar from './performance_bar';
-
-function initSearch() {
- // Only when search form is present
- if ($('.search').length) {
- return new SearchAutocomplete();
- }
-}
-
-function initFieldErrors() {
- $('.gl-show-field-errors').each((i, form) => {
- new GlFieldErrors(form);
- });
-}
-
-function initPageShortcuts(page) {
- const pagesWithCustomShortcuts = [
- 'projects:activity',
- 'projects:artifacts:browse',
- 'projects:artifacts:file',
- 'projects:blame:show',
- 'projects:blob:show',
- 'projects:commit:show',
- 'projects:commits:show',
- 'projects:find_file:show',
- 'projects:issues:edit',
- 'projects:issues:index',
- 'projects:issues:new',
- 'projects:issues:show',
- 'projects:merge_requests:creations:diffs',
- 'projects:merge_requests:creations:new',
- 'projects:merge_requests:edit',
- 'projects:merge_requests:index',
- 'projects:merge_requests:show',
- 'projects:network:show',
- 'projects:show',
- 'projects:tree:show',
- 'groups:show',
- ];
-
- if (pagesWithCustomShortcuts.indexOf(page) === -1) {
- new Shortcuts();
- }
-}
-
-function initGFMInput() {
- $('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => {
- const gfm = new GfmAutoComplete(
- gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources,
- );
- const enableGFM = convertPermissionToBoolean(
- el.dataset.supportsAutocomplete,
- );
- gfm.setup($(el), {
- emojis: true,
- members: enableGFM,
- issues: enableGFM,
- milestones: enableGFM,
- mergeRequests: enableGFM,
- labels: enableGFM,
- });
- });
-}
-
-function initPerformanceBar() {
- if (document.querySelector('#js-peek')) {
- performanceBar({ container: '#js-peek' });
- }
-}
-
-export default () => {
- initSearch();
- initFieldErrors();
-
- const page = $('body').attr('data-page');
- if (page) {
- initPageShortcuts(page);
- initGFMInput();
- initPerformanceBar();
- }
-};
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index ff969bb94a4..d2778bcdf1c 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -1,7 +1,7 @@
import $ from 'jquery';
import Dropzone from 'dropzone';
import _ from 'underscore';
-import './preview_markdown';
+import './behaviors/preview_markdown';
import csrf from './lib/utils/csrf';
import axios from './lib/utils/axios_utils';
diff --git a/app/assets/javascripts/environments/components/container.vue b/app/assets/javascripts/environments/components/container.vue
index 9aa224fa407..9de851c9409 100644
--- a/app/assets/javascripts/environments/components/container.vue
+++ b/app/assets/javascripts/environments/components/container.vue
@@ -1,12 +1,10 @@
<script>
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import environmentTable from '../components/environments_table.vue';
export default {
components: {
environmentTable,
- loadingIcon,
tablePagination,
},
props: {
@@ -42,11 +40,11 @@
<template>
<div class="environments-container">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
+ :size="3"
class="prepend-top-default"
label="Loading environments"
- size="3"
/>
<slot name="emptyState"></slot>
diff --git a/app/assets/javascripts/environments/components/empty_state.vue b/app/assets/javascripts/environments/components/empty_state.vue
index 00e63c3467a..cf78f89981e 100644
--- a/app/assets/javascripts/environments/components/empty_state.vue
+++ b/app/assets/javascripts/environments/components/empty_state.vue
@@ -35,7 +35,7 @@ code gets deployed, such as staging or production.`) }}
<a
v-if="canCreateEnvironment"
:href="newPath"
- class="btn btn-create js-new-environment-button"
+ class="btn btn-success js-new-environment-button"
>
{{ s__("Environments|New environment") }}
</a>
diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue
index 63d83e307ee..e1f9248bc4c 100644
--- a/app/assets/javascripts/environments/components/environment_actions.vue
+++ b/app/assets/javascripts/environments/components/environment_actions.vue
@@ -1,7 +1,6 @@
<script>
import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '../event_hub';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
@@ -9,7 +8,6 @@ export default {
tooltip,
},
components: {
- loadingIcon,
Icon,
},
props: {
@@ -67,7 +65,7 @@ export default {
aria-hidden="true"
>
</i>
- <loading-icon v-if="isLoading" />
+ <gl-loading-icon v-if="isLoading" />
</span>
</button>
diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue
index 4deeef4beb9..efbf88d0f11 100644
--- a/app/assets/javascripts/environments/components/environment_rollback.vue
+++ b/app/assets/javascripts/environments/components/environment_rollback.vue
@@ -9,12 +9,10 @@ import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import eventHub from '../event_hub';
-import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
components: {
Icon,
- LoadingIcon,
},
directives: {
@@ -70,6 +68,6 @@ export default {
v-else
name="redo"/>
- <loading-icon v-if="isLoading" />
+ <gl-loading-icon v-if="isLoading" />
</button>
</template>
diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue
index 8efdfb8abe0..e2ecf426e64 100644
--- a/app/assets/javascripts/environments/components/environments_app.vue
+++ b/app/assets/javascripts/environments/components/environments_app.vue
@@ -107,7 +107,7 @@
>
<a
:href="newEnvironmentPath"
- class="btn btn-create"
+ class="btn btn-success"
>
{{ s__("Environments|New environment") }}
</a>
diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue
index 016e9f7c7b3..16abafebbc0 100644
--- a/app/assets/javascripts/environments/components/environments_table.vue
+++ b/app/assets/javascripts/environments/components/environments_table.vue
@@ -2,13 +2,11 @@
/**
* Render environments table.
*/
-import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import environmentItem from './environment_item.vue';
export default {
components: {
environmentItem,
- loadingIcon,
},
props: {
@@ -85,10 +83,10 @@ export default {
:model="model">
<div
is="environment-item"
+ :key="`environment-item-${i}`"
:model="model"
:can-create-deployment="canCreateDeployment"
:can-read-environment="canReadEnvironment"
- :key="`environment-item-${i}`"
/>
<template
@@ -97,17 +95,17 @@ export default {
<div
v-if="model.isLoadingFolderContent"
:key="`loading-item-${i}`">
- <loading-icon size="2" />
+ <gl-loading-icon :size="2" />
</div>
<template v-else>
<div
is="environment-item"
v-for="(children, index) in model.children"
+ :key="`env-item-${i}-${index}`"
:model="children"
:can-create-deployment="canCreateDeployment"
:can-read-environment="canReadEnvironment"
- :key="`env-item-${i}-${index}`"
/>
<div :key="`sub-div-${i}`">
diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js
index d88624f7f8d..d71964612c5 100644
--- a/app/assets/javascripts/environments/mixins/environments_mixin.js
+++ b/app/assets/javascripts/environments/mixins/environments_mixin.js
@@ -13,7 +13,6 @@ import eventHub from '../event_hub';
import EnvironmentsStore from '../stores/environments_store';
import EnvironmentsService from '../services/environments_service';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import environmentTable from '../components/environments_table.vue';
import tabs from '../../vue_shared/components/navigation_tabs.vue';
@@ -24,7 +23,6 @@ export default {
components: {
environmentTable,
container,
- loadingIcon,
tabs,
tablePagination,
},
diff --git a/app/assets/javascripts/feature_highlight/feature_highlight.js b/app/assets/javascripts/feature_highlight/feature_highlight.js
index 2f27c9351bc..03dfa942d69 100644
--- a/app/assets/javascripts/feature_highlight/feature_highlight.js
+++ b/app/assets/javascripts/feature_highlight/feature_highlight.js
@@ -16,7 +16,7 @@ export function setupFeatureHighlightPopover(id, debounceTimeout = 300) {
const hideOnScroll = togglePopover.bind($selector, false);
$selector
- // Setup popover
+ // Set up popover
.data('content', $popoverContent.prop('outerHTML'))
.popover({
html: true,
diff --git a/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js
new file mode 100644
index 00000000000..b4588cc1318
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js
@@ -0,0 +1,14 @@
+import FilteredSearchTokenKeys from './filtered_search_token_keys';
+
+const tokenKeys = [{
+ key: 'status',
+ type: 'string',
+ param: 'status',
+ symbol: '',
+ icon: 'messages',
+ tag: 'status',
+}];
+
+const AdminRunnersFilteredSearchTokenKeys = new FilteredSearchTokenKeys(tokenKeys);
+
+export default AdminRunnersFilteredSearchTokenKeys;
diff --git a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
index a8eb8d94be3..21b5ccdb613 100644
--- a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
+++ b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
@@ -72,8 +72,8 @@ export default {
@click="onItemActivated(item.text)">
<span>
<span
- v-for="(token, index) in item.tokens"
- :key="`dropdown-token-${index}`"
+ v-for="(token, tokenIndex) in item.tokens"
+ :key="`dropdown-token-${tokenIndex}`"
class="filtered-search-history-dropdown-token"
>
<span class="name">{{ token.prefix }}</span>
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index 184b34b7b5e..8aecf9725e6 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js
@@ -62,7 +62,7 @@ export default class DropdownHint extends FilteredSearchDropdown {
renderContent() {
const dropdownData = this.tokenKeys.get()
.map(tokenKey => ({
- icon: `fa-${tokenKey.icon}`,
+ icon: `${gon.sprite_icons}#${tokenKey.icon}`,
hint: tokenKey.key,
tag: `:${tokenKey.tag}`,
type: tokenKey.type,
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index 296571606d6..a750647f8be 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -7,6 +7,7 @@ import DropdownHint from './dropdown_hint';
import DropdownEmoji from './dropdown_emoji';
import DropdownNonUser from './dropdown_non_user';
import DropdownUser from './dropdown_user';
+import NullDropdown from './null_dropdown';
import FilteredSearchVisualTokens from './filtered_search_visual_tokens';
export default class FilteredSearchDropdownManager {
@@ -90,6 +91,11 @@ export default class FilteredSearchDropdownManager {
gl: DropdownEmoji,
element: this.container.querySelector('#js-dropdown-my-reaction'),
},
+ status: {
+ reference: null,
+ gl: NullDropdown,
+ element: this.container.querySelector('#js-dropdown-admin-runner-status'),
+ },
};
supportedTokens.forEach((type) => {
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 81286c54c4c..d25f6f95b22 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -3,10 +3,10 @@ import {
getParameterByName,
getUrlParamsArray,
} from '~/lib/utils/common_utils';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { visitUrl } from '../lib/utils/url_utility';
import Flash from '../flash';
import FilteredSearchContainer from './container';
-import FilteredSearchTokenKeys from './filtered_search_token_keys';
import RecentSearchesRoot from './recent_searches_root';
import RecentSearchesStore from './stores/recent_searches_store';
import RecentSearchesService from './services/recent_searches_service';
@@ -23,7 +23,7 @@ export default class FilteredSearchManager {
isGroup = false,
isGroupAncestor = true,
isGroupDecendent = false,
- filteredSearchTokenKeys = FilteredSearchTokenKeys,
+ filteredSearchTokenKeys = IssuableFilteredSearchTokenKeys,
stateFiltersSelector = '.issues-state-filters',
}) {
this.isGroup = isGroup;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
index 087ef5cd6f2..5d131b396a0 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
@@ -1,103 +1,38 @@
-const tokenKeys = [{
- key: 'author',
- type: 'string',
- param: 'username',
- symbol: '@',
- icon: 'pencil',
- tag: '@author',
-}, {
- key: 'assignee',
- type: 'string',
- param: 'username',
- symbol: '@',
- icon: 'user',
- tag: '@assignee',
-}, {
- key: 'milestone',
- type: 'string',
- param: 'title',
- symbol: '%',
- icon: 'clock-o',
- tag: '%milestone',
-}, {
- key: 'label',
- type: 'array',
- param: 'name[]',
- symbol: '~',
- icon: 'tag',
- tag: '~label',
-}];
-
-if (gon.current_user_id) {
- // Appending tokenkeys only logged-in
- tokenKeys.push({
- key: 'my-reaction',
- type: 'string',
- param: 'emoji',
- symbol: '',
- icon: 'thumbs-up',
- tag: 'emoji',
- });
-}
-
-const alternativeTokenKeys = [{
- key: 'label',
- type: 'string',
- param: 'name',
- symbol: '~',
-}];
-
-const tokenKeysWithAlternative = tokenKeys.concat(alternativeTokenKeys);
+export default class FilteredSearchTokenKeys {
+ constructor(tokenKeys = [], alternativeTokenKeys = [], conditions = []) {
+ this.tokenKeys = tokenKeys;
+ this.alternativeTokenKeys = alternativeTokenKeys;
+ this.conditions = conditions;
-const conditions = [{
- url: 'assignee_id=0',
- tokenKey: 'assignee',
- value: 'none',
-}, {
- url: 'milestone_title=No+Milestone',
- tokenKey: 'milestone',
- value: 'none',
-}, {
- url: 'milestone_title=%23upcoming',
- tokenKey: 'milestone',
- value: 'upcoming',
-}, {
- url: 'milestone_title=%23started',
- tokenKey: 'milestone',
- value: 'started',
-}, {
- url: 'label_name[]=No+Label',
- tokenKey: 'label',
- value: 'none',
-}];
+ this.tokenKeysWithAlternative = this.tokenKeys.concat(this.alternativeTokenKeys);
+ }
-export default class FilteredSearchTokenKeys {
- static get() {
- return tokenKeys;
+ get() {
+ return this.tokenKeys;
}
- static getKeys() {
- return tokenKeys.map(i => i.key);
+ getKeys() {
+ return this.tokenKeys.map(i => i.key);
}
- static getAlternatives() {
- return alternativeTokenKeys;
+ getAlternatives() {
+ return this.alternativeTokenKeys;
}
- static getConditions() {
- return conditions;
+ getConditions() {
+ return this.conditions;
}
- static searchByKey(key) {
- return tokenKeys.find(tokenKey => tokenKey.key === key) || null;
+ searchByKey(key) {
+ return this.tokenKeys.find(tokenKey => tokenKey.key === key) || null;
}
- static searchBySymbol(symbol) {
- return tokenKeys.find(tokenKey => tokenKey.symbol === symbol) || null;
+ searchBySymbol(symbol) {
+ return this.tokenKeys.find(tokenKey => tokenKey.symbol === symbol) || null;
}
- static searchByKeyParam(keyParam) {
- return tokenKeysWithAlternative.find((tokenKey) => {
+ searchByKeyParam(keyParam) {
+ return this.tokenKeysWithAlternative.find((tokenKey) => {
let tokenKeyParam = tokenKey.key;
// Replace hyphen with underscore to compare keyParam with tokenKeyParam
@@ -112,12 +47,12 @@ export default class FilteredSearchTokenKeys {
}) || null;
}
- static searchByConditionUrl(url) {
- return conditions.find(condition => condition.url === url) || null;
+ searchByConditionUrl(url) {
+ return this.conditions.find(condition => condition.url === url) || null;
}
- static searchByConditionKeyValue(key, value) {
- return conditions
+ searchByConditionKeyValue(key, value) {
+ return this.conditions
.find(condition => condition.tokenKey === key && condition.value === value) || null;
}
}
diff --git a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
new file mode 100644
index 00000000000..cc7291c9f59
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
@@ -0,0 +1,77 @@
+import FilteredSearchTokenKeys from './filtered_search_token_keys';
+
+export const tokenKeys = [{
+ key: 'author',
+ type: 'string',
+ param: 'username',
+ symbol: '@',
+ icon: 'pencil',
+ tag: '@author',
+}, {
+ key: 'assignee',
+ type: 'string',
+ param: 'username',
+ symbol: '@',
+ icon: 'user',
+ tag: '@assignee',
+}, {
+ key: 'milestone',
+ type: 'string',
+ param: 'title',
+ symbol: '%',
+ icon: 'clock',
+ tag: '%milestone',
+}, {
+ key: 'label',
+ type: 'array',
+ param: 'name[]',
+ symbol: '~',
+ icon: 'labels',
+ tag: '~label',
+}];
+
+if (gon.current_user_id) {
+ // Appending tokenkeys only logged-in
+ tokenKeys.push({
+ key: 'my-reaction',
+ type: 'string',
+ param: 'emoji',
+ symbol: '',
+ icon: 'thumb-up',
+ tag: 'emoji',
+ });
+}
+
+export const alternativeTokenKeys = [{
+ key: 'label',
+ type: 'string',
+ param: 'name',
+ symbol: '~',
+}];
+
+export const conditions = [{
+ url: 'assignee_id=0',
+ tokenKey: 'assignee',
+ value: 'none',
+}, {
+ url: 'milestone_title=No+Milestone',
+ tokenKey: 'milestone',
+ value: 'none',
+}, {
+ url: 'milestone_title=%23upcoming',
+ tokenKey: 'milestone',
+ value: 'upcoming',
+}, {
+ url: 'milestone_title=%23started',
+ tokenKey: 'milestone',
+ value: 'started',
+}, {
+ url: 'label_name[]=No+Label',
+ tokenKey: 'label',
+ value: 'none',
+}];
+
+const IssuableFilteredSearchTokenKeys =
+ new FilteredSearchTokenKeys(tokenKeys, alternativeTokenKeys, conditions);
+
+export default IssuableFilteredSearchTokenKeys;
diff --git a/app/assets/javascripts/filtered_search/null_dropdown.js b/app/assets/javascripts/filtered_search/null_dropdown.js
new file mode 100644
index 00000000000..4cfce2a5beb
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/null_dropdown.js
@@ -0,0 +1,9 @@
+import FilteredSearchDropdown from './filtered_search_dropdown';
+
+export default class NullDropdown extends FilteredSearchDropdown {
+ renderContent(forceShowList = false) {
+ this.droplab.changeHookList(this.hookId, this.dropdown, [], this.config);
+
+ super.renderContent(forceShowList);
+ }
+}
diff --git a/app/assets/javascripts/frequent_items/components/app.vue b/app/assets/javascripts/frequent_items/components/app.vue
index 2f030de8967..70a8838b772 100644
--- a/app/assets/javascripts/frequent_items/components/app.vue
+++ b/app/assets/javascripts/frequent_items/components/app.vue
@@ -1,6 +1,5 @@
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import AccessorUtilities from '~/lib/utils/accessor';
import eventHub from '../event_hub';
import store from '../store/';
@@ -13,7 +12,6 @@ import frequentItemsMixin from './frequent_items_mixin';
export default {
store,
components: {
- LoadingIcon,
FrequentItemsSearchInput,
FrequentItemsList,
},
@@ -98,11 +96,11 @@ export default {
<frequent-items-search-input
:namespace="namespace"
/>
- <loading-icon
+ <gl-loading-icon
v-if="isLoadingItems"
:label="translations.loadingMessage"
+ :size="2"
class="loading-animation prepend-top-20"
- size="2"
/>
<div
v-if="!isLoadingItems && !hasSearchQuery"
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
index 1f1665ff7fe..2399ee15332 100644
--- a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
@@ -1,5 +1,5 @@
<script>
-/* eslint-disable vue/require-default-prop, vue/require-prop-types */
+/* eslint-disable vue/require-default-prop */
import Identicon from '../../vue_shared/components/identicon.vue';
export default {
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index c74de7ac34d..e672284a2d0 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -18,7 +18,7 @@ export default class GLForm {
});
// Before we start, we should clean up any previous data for this form
this.destroy();
- // Setup the form
+ // Set up the form
this.setupForm();
this.form.data('glForm', this);
}
diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue
index 69f192ac75e..a032f291546 100644
--- a/app/assets/javascripts/groups/components/app.vue
+++ b/app/assets/javascripts/groups/components/app.vue
@@ -3,7 +3,6 @@
import $ from 'jquery';
import { s__, sprintf } from '~/locale';
-import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { HIDDEN_CLASS } from '~/lib/utils/constants';
import { getParameterByName } from '~/lib/utils/common_utils';
@@ -15,7 +14,6 @@ import groupsComponent from './groups.vue';
export default {
components: {
- loadingIcon,
DeprecatedModal,
groupsComponent,
},
@@ -241,11 +239,11 @@ export default {
<template>
<div>
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
:label="s__('GroupsTree|Loading groups')"
+ :size="2"
class="loading-animation prepend-top-20"
- size="2"
/>
<groups-component
v-if="!isLoading"
diff --git a/app/assets/javascripts/groups/components/groups.vue b/app/assets/javascripts/groups/components/groups.vue
index a1beb222950..81b2e5ea37b 100644
--- a/app/assets/javascripts/groups/components/groups.vue
+++ b/app/assets/javascripts/groups/components/groups.vue
@@ -1,11 +1,11 @@
<script>
-import tablePagination from '~/vue_shared/components/table_pagination.vue';
+import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
import eventHub from '../event_hub';
import { getParameterByName } from '../../lib/utils/common_utils';
export default {
components: {
- tablePagination,
+ PaginationLinks,
},
props: {
groups: {
@@ -49,15 +49,18 @@ export default {
>
{{ searchEmptyMessage }}
</div>
- <group-folder
- v-if="!searchEmpty"
- :groups="groups"
- :action="action"
- />
- <table-pagination
- v-if="!searchEmpty"
- :change="change"
- :page-info="pageInfo"
- />
+ <template
+ v-else
+ >
+ <group-folder
+ :groups="groups"
+ :action="action"
+ />
+ <pagination-links
+ :change="change"
+ :page-info="pageInfo"
+ class="d-flex justify-content-center prepend-top-default"
+ />
+ </template>
</div>
</template>
diff --git a/app/assets/javascripts/groups/components/item_actions.vue b/app/assets/javascripts/groups/components/item_actions.vue
index 6e700b8bf8a..c1783d5ce25 100644
--- a/app/assets/javascripts/groups/components/item_actions.vue
+++ b/app/assets/javascripts/groups/components/item_actions.vue
@@ -46,8 +46,8 @@ export default {
<template>
<div class="controls">
<a
- v-tooltip
v-if="group.canEdit"
+ v-tooltip
:href="group.editPath"
:title="editBtnTitle"
:aria-label="editBtnTitle"
@@ -57,8 +57,8 @@ export default {
<icon name="settings"/>
</a>
<a
- v-tooltip
v-if="group.canLeave"
+ v-tooltip
:href="group.leavePath"
:title="leaveBtnTitle"
:aria-label="leaveBtnTitle"
diff --git a/app/assets/javascripts/ide/components/branches/search_list.vue b/app/assets/javascripts/ide/components/branches/search_list.vue
index 6db7b9d6b0e..52ccc537c9d 100644
--- a/app/assets/javascripts/ide/components/branches/search_list.vue
+++ b/app/assets/javascripts/ide/components/branches/search_list.vue
@@ -1,13 +1,11 @@
<script>
import { mapActions, mapState } from 'vuex';
import _ from 'underscore';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
import Item from './item.vue';
export default {
components: {
- LoadingIcon,
Item,
Icon,
},
@@ -62,8 +60,8 @@ export default {
<div class="position-relative">
<input
ref="searchInput"
- :placeholder="__('Search branches')"
v-model="search"
+ :placeholder="__('Search branches')"
type="search"
class="form-control dropdown-input-field"
@input="searchBranches"
@@ -76,10 +74,10 @@ export default {
</div>
</div>
<div class="dropdown-content ide-merge-requests-dropdown-content d-flex">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
+ :size="2"
class="mt-3 mb-3 align-self-center ml-auto mr-auto"
- size="2"
/>
<ul
v-else
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue b/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue
index c3ca147e850..3aca38399fb 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue
@@ -52,6 +52,7 @@ export default {
</strong>
<changed-file-icon
:file="activeFile"
+ class="ml-0"
/>
<div class="ml-auto">
<button
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
index 3fdd35ad228..3e3539e364b 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
@@ -113,8 +113,8 @@ export default {
</strong>
<div class="d-flex ml-auto">
<button
- v-tooltip
ref="actionBtn"
+ v-tooltip
:title="actionBtnText"
:aria-label="actionBtnText"
:disabled="!filesLength"
@@ -135,8 +135,8 @@ export default {
/>
</button>
<button
- v-tooltip
v-if="!stagedList"
+ v-tooltip
:title="__('Discard all changes')"
:aria-label="__('Discard all changes')"
:disabled="!filesLength"
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
index 10c78a80302..ee0e72cd05f 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
@@ -120,10 +120,6 @@ export default {
:css-classes="iconClass"
/>
</div>
- <component
- :is="actionComponent"
- :path="file.path"
- />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/ide/components/error_message.vue b/app/assets/javascripts/ide/components/error_message.vue
index acbc98b7a7b..a20dc0a7006 100644
--- a/app/assets/javascripts/ide/components/error_message.vue
+++ b/app/assets/javascripts/ide/components/error_message.vue
@@ -1,11 +1,7 @@
<script>
import { mapActions } from 'vuex';
-import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
- components: {
- LoadingIcon,
- },
props: {
message: {
type: Object,
@@ -59,7 +55,7 @@ export default {
@click.stop.prevent="clickAction"
>
{{ message.actionText }}
- <loading-icon
+ <gl-loading-icon
v-show="isLoading"
inline
/>
diff --git a/app/assets/javascripts/ide/components/file_finder/index.vue b/app/assets/javascripts/ide/components/file_finder/index.vue
index 0ba33053717..760ed8654ee 100644
--- a/app/assets/javascripts/ide/components/file_finder/index.vue
+++ b/app/assets/javascripts/ide/components/file_finder/index.vue
@@ -174,8 +174,8 @@ export default {
<div class="dropdown-input">
<input
ref="searchInput"
- :placeholder="__('Search files')"
v-model="searchText"
+ :placeholder="__('Search files')"
type="search"
class="dropdown-input-field"
autocomplete="off"
diff --git a/app/assets/javascripts/ide/components/file_finder/item.vue b/app/assets/javascripts/ide/components/file_finder/item.vue
index f5252ce7706..a612739d641 100644
--- a/app/assets/javascripts/ide/components/file_finder/item.vue
+++ b/app/assets/javascripts/ide/components/file_finder/item.vue
@@ -78,10 +78,10 @@ export default {
class="diff-changed-file-name"
>
<span
- v-for="(char, index) in file.name.split('')"
- :key="index + char"
+ v-for="(char, charIndex) in file.name.split('')"
+ :key="charIndex + char"
:class="{
- highlighted: nameSearchTextOccurences.indexOf(index) >= 0,
+ highlighted: nameSearchTextOccurences.indexOf(charIndex) >= 0,
}"
v-text="char"
>
@@ -91,10 +91,10 @@ export default {
class="diff-changed-file-path prepend-top-5"
>
<span
- v-for="(char, index) in pathWithEllipsis.split('')"
- :key="index + char"
+ v-for="(char, charIndex) in pathWithEllipsis.split('')"
+ :key="charIndex + char"
:class="{
- highlighted: pathSearchTextOccurences.indexOf(index) >= 0,
+ highlighted: pathSearchTextOccurences.indexOf(charIndex) >= 0,
}"
v-text="char"
>
diff --git a/app/assets/javascripts/ide/components/file_row_extra.vue b/app/assets/javascripts/ide/components/file_row_extra.vue
new file mode 100644
index 00000000000..44a360ab909
--- /dev/null
+++ b/app/assets/javascripts/ide/components/file_row_extra.vue
@@ -0,0 +1,104 @@
+<script>
+import { mapGetters } from 'vuex';
+import { n__, __, sprintf } from '~/locale';
+import tooltip from '~/vue_shared/directives/tooltip';
+import Icon from '~/vue_shared/components/icon.vue';
+import NewDropdown from './new_dropdown/index.vue';
+import ChangedFileIcon from './changed_file_icon.vue';
+import MrFileIcon from './mr_file_icon.vue';
+
+export default {
+ name: 'FileRowExtra',
+ directives: {
+ tooltip,
+ },
+ components: {
+ Icon,
+ NewDropdown,
+ ChangedFileIcon,
+ MrFileIcon,
+ },
+ props: {
+ file: {
+ type: Object,
+ required: true,
+ },
+ mouseOver: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapGetters([
+ 'getChangesInFolder',
+ 'getUnstagedFilesCountForPath',
+ 'getStagedFilesCountForPath',
+ ]),
+ folderUnstagedCount() {
+ return this.getUnstagedFilesCountForPath(this.file.path);
+ },
+ folderStagedCount() {
+ return this.getStagedFilesCountForPath(this.file.path);
+ },
+ changesCount() {
+ return this.getChangesInFolder(this.file.path);
+ },
+ folderChangesTooltip() {
+ if (this.changesCount === 0) return undefined;
+
+ if (this.folderUnstagedCount > 0 && this.folderStagedCount === 0) {
+ return n__('%d unstaged change', '%d unstaged changes', this.folderUnstagedCount);
+ } else if (this.folderUnstagedCount === 0 && this.folderStagedCount > 0) {
+ return n__('%d staged change', '%d staged changes', this.folderStagedCount);
+ }
+
+ return sprintf(__('%{unstaged} unstaged and %{staged} staged changes'), {
+ unstaged: this.folderUnstagedCount,
+ staged: this.folderStagedCount,
+ });
+ },
+ showTreeChangesCount() {
+ return this.file.type === 'tree' && this.changesCount > 0 && !this.file.opened;
+ },
+ showChangedFileIcon() {
+ return this.file.changed || this.file.tempFile || this.file.staged;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="float-right ide-file-icon-holder">
+ <mr-file-icon
+ v-if="file.mrChange"
+ />
+ <span
+ v-if="showTreeChangesCount"
+ class="ide-tree-changes"
+ >
+ {{ changesCount }}
+ <icon
+ v-tooltip
+ :title="folderChangesTooltip"
+ :size="12"
+ data-container="body"
+ data-placement="right"
+ name="file-modified"
+ css-classes="prepend-left-5 ide-file-modified"
+ />
+ </span>
+ <changed-file-icon
+ v-else-if="showChangedFileIcon"
+ :file="file"
+ :show-tooltip="true"
+ :show-staged-icon="true"
+ :force-modified-icon="true"
+ />
+ <new-dropdown
+ :type="file.type"
+ :path="file.path"
+ :mouse-over="mouseOver"
+ class="prepend-left-8"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/file_templates/dropdown.vue b/app/assets/javascripts/ide/components/file_templates/dropdown.vue
index 13059937f85..ef1f6de3a86 100644
--- a/app/assets/javascripts/ide/components/file_templates/dropdown.vue
+++ b/app/assets/javascripts/ide/components/file_templates/dropdown.vue
@@ -1,13 +1,11 @@
<script>
import $ from 'jquery';
import { mapActions, mapState } from 'vuex';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
export default {
components: {
DropdownButton,
- LoadingIcon,
},
props: {
data: {
@@ -102,9 +100,9 @@ export default {
></i>
</div>
<div class="dropdown-content">
- <loading-icon
+ <gl-loading-icon
v-if="showLoading"
- size="2"
+ :size="2"
/>
<ul v-else>
<li
diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue
index 00ae5ea2c15..e658d1bf956 100644
--- a/app/assets/javascripts/ide/components/ide_tree_list.vue
+++ b/app/assets/javascripts/ide/components/ide_tree_list.vue
@@ -2,15 +2,16 @@
import { mapActions, mapGetters, mapState } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-import RepoFile from './repo_file.vue';
+import FileRow from '~/vue_shared/components/file_row.vue';
import NavDropdown from './nav_dropdown.vue';
+import FileRowExtra from './file_row_extra.vue';
export default {
components: {
Icon,
- RepoFile,
SkeletonLoadingContainer,
NavDropdown,
+ FileRow,
},
props: {
viewerType: {
@@ -34,8 +35,9 @@ export default {
this.updateViewer(this.viewerType);
},
methods: {
- ...mapActions(['updateViewer']),
+ ...mapActions(['updateViewer', 'toggleTreeOpen']),
},
+ FileRowExtra,
};
</script>
@@ -63,11 +65,13 @@ export default {
<div
class="ide-tree-body h-100"
>
- <repo-file
+ <file-row
v-for="file in currentTree.tree"
:key="file.key"
:file="file"
:level="0"
+ :extra-component="$options.FileRowExtra"
+ @toggleTreeOpen="toggleTreeOpen"
/>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/jobs/list.vue b/app/assets/javascripts/ide/components/jobs/list.vue
index 3b16b860ecd..acd37605d16 100644
--- a/app/assets/javascripts/ide/components/jobs/list.vue
+++ b/app/assets/javascripts/ide/components/jobs/list.vue
@@ -1,11 +1,9 @@
<script>
import { mapActions } from 'vuex';
-import LoadingIcon from '../../../vue_shared/components/loading_icon.vue';
import Stage from './stage.vue';
export default {
components: {
- LoadingIcon,
Stage,
},
props: {
@@ -26,10 +24,10 @@ export default {
<template>
<div>
- <loading-icon
+ <gl-loading-icon
v-if="loading && !stages.length"
+ :size="2"
class="prepend-top-default"
- size="2"
/>
<template v-else>
<stage
diff --git a/app/assets/javascripts/ide/components/jobs/stage.vue b/app/assets/javascripts/ide/components/jobs/stage.vue
index 15e881b7bc8..ec168d36b9e 100644
--- a/app/assets/javascripts/ide/components/jobs/stage.vue
+++ b/app/assets/javascripts/ide/components/jobs/stage.vue
@@ -2,7 +2,6 @@
import tooltip from '../../../vue_shared/directives/tooltip';
import Icon from '../../../vue_shared/components/icon.vue';
import CiIcon from '../../../vue_shared/components/ci_icon.vue';
-import LoadingIcon from '../../../vue_shared/components/loading_icon.vue';
import Item from './item.vue';
export default {
@@ -12,7 +11,6 @@ export default {
components: {
Icon,
CiIcon,
- LoadingIcon,
Item,
},
props: {
@@ -71,8 +69,8 @@ export default {
:size="24"
/>
<strong
- v-tooltip="showTooltip"
ref="stageTitle"
+ v-tooltip="showTooltip"
:title="showTooltip ? stage.name : null"
data-container="body"
class="prepend-left-8 ide-stage-title"
@@ -96,7 +94,7 @@ export default {
v-show="!stage.isCollapsed"
class="card-body"
>
- <loading-icon
+ <gl-loading-icon
v-if="showLoadingIcon"
/>
<template v-else>
diff --git a/app/assets/javascripts/ide/components/merge_requests/list.vue b/app/assets/javascripts/ide/components/merge_requests/list.vue
index fc612956688..c8343e77860 100644
--- a/app/assets/javascripts/ide/components/merge_requests/list.vue
+++ b/app/assets/javascripts/ide/components/merge_requests/list.vue
@@ -3,7 +3,6 @@ import { mapActions, mapState } from 'vuex';
import _ from 'underscore';
import { __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import Item from './item.vue';
import TokenedInput from '../shared/tokened_input.vue';
@@ -14,7 +13,6 @@ const SEARCH_TYPES = [
export default {
components: {
- LoadingIcon,
TokenedInput,
Item,
Icon,
@@ -98,10 +96,10 @@ export default {
</div>
</div>
<div class="dropdown-content ide-merge-requests-dropdown-content d-flex">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
+ :size="2"
class="mt-3 mb-3 align-self-center ml-auto mr-auto"
- size="2"
/>
<template v-else>
<ul
diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue
index 5757dfdc925..0a2681b7a1e 100644
--- a/app/assets/javascripts/ide/components/pipelines/list.vue
+++ b/app/assets/javascripts/ide/components/pipelines/list.vue
@@ -2,7 +2,6 @@
import { mapActions, mapGetters, mapState } from 'vuex';
import _ from 'underscore';
import { sprintf, __ } from '../../../locale';
-import LoadingIcon from '../../../vue_shared/components/loading_icon.vue';
import Icon from '../../../vue_shared/components/icon.vue';
import CiIcon from '../../../vue_shared/components/ci_icon.vue';
import Tabs from '../../../vue_shared/components/tabs/tabs';
@@ -12,7 +11,6 @@ import JobsList from '../jobs/list.vue';
export default {
components: {
- LoadingIcon,
Icon,
CiIcon,
Tabs,
@@ -50,10 +48,10 @@ export default {
<template>
<div class="ide-pipeline">
- <loading-icon
+ <gl-loading-icon
v-if="showLoadingIcon"
+ :size="2"
class="prepend-top-default"
- size="2"
/>
<template v-else-if="latestPipeline !== null">
<header
diff --git a/app/assets/javascripts/ide/components/preview/clientside.vue b/app/assets/javascripts/ide/components/preview/clientside.vue
index 39a1bd1f61b..37a8ad36507 100644
--- a/app/assets/javascripts/ide/components/preview/clientside.vue
+++ b/app/assets/javascripts/ide/components/preview/clientside.vue
@@ -3,14 +3,12 @@ import { mapActions, mapGetters, mapState } from 'vuex';
import _ from 'underscore';
import { Manager } from 'smooshpack';
import { listen } from 'codesandbox-api';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import Navigator from './navigator.vue';
import { packageJsonPath } from '../../constants';
import { createPathWithExt } from '../../utils';
export default {
components: {
- LoadingIcon,
Navigator,
},
data() {
@@ -177,9 +175,9 @@ export default {
{{ s__('IDE|Get started with Live Preview') }}
</a>
</div>
- <loading-icon
+ <gl-loading-icon
v-else
- size="2"
+ :size="2"
class="align-self-center mt-auto mb-auto"
/>
</div>
diff --git a/app/assets/javascripts/ide/components/preview/navigator.vue b/app/assets/javascripts/ide/components/preview/navigator.vue
index 4bf346946b6..42f23801692 100644
--- a/app/assets/javascripts/ide/components/preview/navigator.vue
+++ b/app/assets/javascripts/ide/components/preview/navigator.vue
@@ -1,12 +1,10 @@
<script>
import { listen } from 'codesandbox-api';
import Icon from '~/vue_shared/components/icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
export default {
components: {
Icon,
- LoadingIcon,
},
props: {
manager: {
@@ -138,7 +136,7 @@ export default {
class="ide-navigator-location form-control bg-white"
readonly
/>
- <loading-icon
+ <gl-loading-icon
v-if="loading"
class="position-absolute ide-preview-loading-icon"
/>
diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue
deleted file mode 100644
index 110eda83bb4..00000000000
--- a/app/assets/javascripts/ide/components/repo_file.vue
+++ /dev/null
@@ -1,227 +0,0 @@
-<script>
-import { mapActions, mapGetters } from 'vuex';
-import { n__, __, sprintf } from '~/locale';
-import tooltip from '~/vue_shared/directives/tooltip';
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-import Icon from '~/vue_shared/components/icon.vue';
-import FileIcon from '~/vue_shared/components/file_icon.vue';
-import router from '../ide_router';
-import NewDropdown from './new_dropdown/index.vue';
-import FileStatusIcon from './repo_file_status_icon.vue';
-import ChangedFileIcon from './changed_file_icon.vue';
-import MrFileIcon from './mr_file_icon.vue';
-
-export default {
- name: 'RepoFile',
- directives: {
- tooltip,
- },
- components: {
- SkeletonLoadingContainer,
- NewDropdown,
- FileStatusIcon,
- FileIcon,
- ChangedFileIcon,
- MrFileIcon,
- Icon,
- },
- props: {
- file: {
- type: Object,
- required: true,
- },
- level: {
- type: Number,
- required: true,
- },
- },
- data() {
- return {
- mouseOver: false,
- };
- },
- computed: {
- ...mapGetters([
- 'getChangesInFolder',
- 'getUnstagedFilesCountForPath',
- 'getStagedFilesCountForPath',
- ]),
- folderUnstagedCount() {
- return this.getUnstagedFilesCountForPath(this.file.path);
- },
- folderStagedCount() {
- return this.getStagedFilesCountForPath(this.file.path);
- },
- changesCount() {
- return this.getChangesInFolder(this.file.path);
- },
- folderChangesTooltip() {
- if (this.changesCount === 0) return undefined;
-
- if (this.folderUnstagedCount > 0 && this.folderStagedCount === 0) {
- return n__('%d unstaged change', '%d unstaged changes', this.folderUnstagedCount);
- } else if (this.folderUnstagedCount === 0 && this.folderStagedCount > 0) {
- return n__('%d staged change', '%d staged changes', this.folderStagedCount);
- }
-
- return sprintf(__('%{unstaged} unstaged and %{staged} staged changes'), {
- unstaged: this.folderUnstagedCount,
- staged: this.folderStagedCount,
- });
- },
- isTree() {
- return this.file.type === 'tree';
- },
- isBlob() {
- return this.file.type === 'blob';
- },
- levelIndentation() {
- return {
- marginLeft: `${this.level * 16}px`,
- };
- },
- fileClass() {
- return {
- 'file-open': this.isBlob && this.file.opened,
- 'file-active': this.isBlob && this.file.active,
- folder: this.isTree,
- 'is-open': this.file.opened,
- };
- },
- showTreeChangesCount() {
- return this.isTree && this.changesCount > 0 && !this.file.opened;
- },
- showChangedFileIcon() {
- return this.file.changed || this.file.tempFile || this.file.staged;
- },
- },
- watch: {
- 'file.active': function fileActiveWatch(active) {
- if (this.file.type === 'blob' && active) {
- this.scrollIntoView();
- }
- },
- },
- mounted() {
- if (this.hasPathAtCurrentRoute()) {
- this.scrollIntoView(true);
- }
- },
- methods: {
- ...mapActions(['toggleTreeOpen']),
- clickFile() {
- // Manual Action if a tree is selected/opened
- if (this.isTree && this.hasUrlAtCurrentRoute()) {
- this.toggleTreeOpen(this.file.path);
- }
-
- router.push(`/project${this.file.url}`);
- },
- scrollIntoView(isInit = false) {
- const block = isInit && this.isTree ? 'center' : 'nearest';
-
- this.$el.scrollIntoView({
- behavior: 'smooth',
- block,
- });
- },
- hasPathAtCurrentRoute() {
- if (!this.$router || !this.$router.currentRoute) {
- return false;
- }
-
- // - strip route up to "/-/" and ending "/"
- const routePath = this.$router.currentRoute.path
- .replace(/^.*?[/]-[/]/g, '')
- .replace(/[/]$/g, '');
-
- // - strip ending "/"
- const filePath = this.file.path.replace(/[/]$/g, '');
-
- return filePath === routePath;
- },
- hasUrlAtCurrentRoute() {
- return this.$router.currentRoute.path === `/project${this.file.url}`;
- },
- toggleHover(over) {
- this.mouseOver = over;
- },
- },
-};
-</script>
-
-<template>
- <div>
- <div
- :class="fileClass"
- class="file"
- role="button"
- @click="clickFile"
- @mouseover="toggleHover(true)"
- @mouseout="toggleHover(false)"
- >
- <div
- class="file-name"
- >
- <span
- :style="levelIndentation"
- class="ide-file-name str-truncated"
- >
- <file-icon
- :file-name="file.name"
- :loading="file.loading"
- :folder="isTree"
- :opened="file.opened"
- :size="16"
- />
- {{ file.name }}
- <file-status-icon
- :file="file"
- />
- </span>
- <span class="float-right ide-file-icon-holder">
- <mr-file-icon
- v-if="file.mrChange"
- />
- <span
- v-if="showTreeChangesCount"
- class="ide-tree-changes"
- >
- {{ changesCount }}
- <icon
- v-tooltip
- :title="folderChangesTooltip"
- :size="12"
- data-container="body"
- data-placement="right"
- name="file-modified"
- css-classes="prepend-left-5 ide-file-modified"
- />
- </span>
- <changed-file-icon
- v-else-if="showChangedFileIcon"
- :file="file"
- :show-tooltip="true"
- :show-staged-icon="true"
- :force-modified-icon="true"
- class="float-right"
- />
- </span>
- <new-dropdown
- :type="file.type"
- :path="file.path"
- :mouse-over="mouseOver"
- class="float-right prepend-left-8"
- />
- </div>
- </div>
- <template v-if="file.opened">
- <repo-file
- v-for="childFile in file.tree"
- :key="childFile.key"
- :file="childFile"
- :level="level + 1"
- />
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_file_status_icon.vue b/app/assets/javascripts/ide/components/repo_file_status_icon.vue
index 76a3333be50..97589e116c5 100644
--- a/app/assets/javascripts/ide/components/repo_file_status_icon.vue
+++ b/app/assets/javascripts/ide/components/repo_file_status_icon.vue
@@ -26,8 +26,8 @@ export default {
<template>
<span
- v-tooltip
v-if="file.file_lock"
+ v-tooltip
:title="lockTooltip"
data-container="body"
>
diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js
index 79e38ae911e..c90f8694326 100644
--- a/app/assets/javascripts/ide/index.js
+++ b/app/assets/javascripts/ide/index.js
@@ -8,9 +8,21 @@ import { convertPermissionToBoolean } from '../lib/utils/common_utils';
Vue.use(Translate);
-export function initIde(el) {
+/**
+ * Initialize the IDE on the given element.
+ *
+ * @param {Element} el - The element that will contain the IDE.
+ * @param {Object} options - Extra options for the IDE (Used by EE).
+ * @param {(e:Element) => Object} options.extraInitialData -
+ * Function that returns extra properties to seed initial data.
+ */
+export function initIde(el, options = {}) {
if (!el) return null;
+ const {
+ extraInitialData = () => ({}),
+ } = options;
+
return new Vue({
el,
store,
@@ -32,6 +44,7 @@ export function initIde(el) {
});
this.setInitialData({
clientsidePreviewEnabled: convertPermissionToBoolean(el.dataset.clientsidePreviewEnabled),
+ ...extraInitialData(el),
});
},
methods: {
@@ -52,3 +65,18 @@ export function resetServiceWorkersPublicPath() {
const webpackAssetPath = `${relativeRootPath}/assets/webpack/`;
__webpack_public_path__ = webpackAssetPath; // eslint-disable-line camelcase
}
+
+/**
+ * Start the IDE.
+ *
+ * @param {Objects} options - Extra options for the IDE (Used by EE).
+ */
+export function startIde(options) {
+ document.addEventListener('DOMContentLoaded', () => {
+ const ideElement = document.getElementById('ide');
+ if (ideElement) {
+ resetServiceWorkersPublicPath();
+ initIde(ideElement, options);
+ }
+ });
+}
diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js
index 35eaf21a836..9e848699163 100644
--- a/app/assets/javascripts/issuable_bulk_update_actions.js
+++ b/app/assets/javascripts/issuable_bulk_update_actions.js
@@ -36,7 +36,7 @@ export default {
},
getSelectedIssues() {
- return this.issues.has('.selected_issue:checked');
+ return this.issues.has('.selected-issuable:checked');
},
getLabelsFromSelection() {
@@ -110,7 +110,7 @@ export default {
getOriginalCommonIds() {
const labelIds = [];
- this.getElement('.selected_issue:checked').each((i, el) => {
+ this.getElement('.selected-issuable:checked').each((i, el) => {
labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels'));
});
return _.intersection.apply(this, labelIds);
@@ -119,7 +119,7 @@ export default {
// From issuable's initial bulk selection
getOriginalMarkedIds() {
const labelIds = [];
- this.getElement('.selected_issue:checked').each((i, el) => {
+ this.getElement('.selected-issuable:checked').each((i, el) => {
labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels'));
});
return _.intersection.apply(this, labelIds);
@@ -132,7 +132,7 @@ export default {
let issuableLabels = [];
// Collect unique label IDs for all checked issues
- this.getElement('.selected_issue:checked').each((i, el) => {
+ this.getElement('.selected-issuable:checked').each((i, el) => {
issuableLabels = this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels');
issuableLabels.forEach((labelId) => {
// Store unique IDs
diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js
index 2307c8e0d85..74150ce3a8b 100644
--- a/app/assets/javascripts/issuable_bulk_update_sidebar.js
+++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js
@@ -30,7 +30,7 @@ export default class IssuableBulkUpdateSidebar {
this.$otherFilters = $('.issues-other-filters');
this.$checkAllContainer = $('.check-all-holder');
this.$issueChecks = $('.issue-check');
- this.$issuesList = $('.selected_issue');
+ this.$issuesList = $('.selected-issuable');
this.$issuableIdsInput = $('#update_issuable_ids');
}
@@ -55,7 +55,7 @@ export default class IssuableBulkUpdateSidebar {
}
updateFormState() {
- const noCheckedIssues = !$('.selected_issue:checked').length;
+ const noCheckedIssues = !$('.selected-issuable:checked').length;
this.toggleSubmitButtonDisabled(noCheckedIssues);
this.updateSelectedIssuableIds();
@@ -123,7 +123,7 @@ export default class IssuableBulkUpdateSidebar {
}
static getCheckedIssueIds() {
- const $checkedIssues = $('.selected_issue:checked');
+ const $checkedIssues = $('.selected-issuable:checked');
if ($checkedIssues.length > 0) {
return $.map($checkedIssues, value => $(value).data('id'));
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index ad928484952..c6ad3aa3e0d 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -293,6 +293,7 @@
:show-delete-button="showDeleteButton"
:can-attach-file="canAttachFile"
:enable-autocomplete="enableAutocomplete"
+ :issuable-type="issuableType"
/>
<recaptcha-modal
diff --git a/app/assets/javascripts/issue_show/components/edit_actions.vue b/app/assets/javascripts/issue_show/components/edit_actions.vue
index 597c6d69a81..bcf8686afcc 100644
--- a/app/assets/javascripts/issue_show/components/edit_actions.vue
+++ b/app/assets/javascripts/issue_show/components/edit_actions.vue
@@ -1,7 +1,13 @@
<script>
+ import { __, sprintf } from '~/locale';
import updateMixin from '../mixins/update';
import eventHub from '../event_hub';
+ const issuableTypes = {
+ issue: __('Issue'),
+ epic: __('Epic'),
+ };
+
export default {
mixins: [updateMixin],
props: {
@@ -18,6 +24,10 @@
required: false,
default: true,
},
+ issuableType: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
@@ -37,8 +47,11 @@
eventHub.$emit('close.form');
},
deleteIssuable() {
+ const confirmMessage = sprintf(__('%{issuableType} will be removed! Are you sure?'), {
+ issuableType: issuableTypes[this.issuableType],
+ });
// eslint-disable-next-line no-alert
- if (window.confirm('Issue will be removed! Are you sure?')) {
+ if (window.confirm(confirmMessage)) {
this.deleteLoading = true;
eventHub.$emit('delete.issuable');
@@ -53,7 +66,7 @@
<button
:class="{ disabled: formState.updateLoading || !isSubmitEnabled }"
:disabled="formState.updateLoading || !isSubmitEnabled"
- class="btn btn-save float-left"
+ class="btn btn-success float-left"
type="submit"
@click.prevent="updateIssuable">
Save changes
diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue
index e509bb52f7d..03d8d0ec67c 100644
--- a/app/assets/javascripts/issue_show/components/form.vue
+++ b/app/assets/javascripts/issue_show/components/form.vue
@@ -27,6 +27,10 @@
required: false,
default: () => [],
},
+ issuableType: {
+ type: String,
+ required: true,
+ },
markdownPreviewPath: {
type: String,
required: true,
@@ -110,6 +114,7 @@
:form-state="formState"
:can-destroy="canDestroy"
:show-delete-button="showDeleteButton"
+ :issuable-type="issuableType"
/>
</form>
</template>
diff --git a/app/assets/javascripts/issue_show/components/title.vue b/app/assets/javascripts/issue_show/components/title.vue
index b5e8e0ea44b..cf99e9a9cd8 100644
--- a/app/assets/javascripts/issue_show/components/title.vue
+++ b/app/assets/javascripts/issue_show/components/title.vue
@@ -76,8 +76,8 @@ export default {
>
</h2>
<button
- v-tooltip
v-if="showInlineEditButton && canUpdate"
+ v-tooltip
type="button"
class="btn btn-default btn-edit btn-svg js-issuable-edit"
title="Edit title and description"
diff --git a/app/assets/javascripts/jobs/components/artifacts_block.vue b/app/assets/javascripts/jobs/components/artifacts_block.vue
index 525c5eec91a..d5866f9b9f1 100644
--- a/app/assets/javascripts/jobs/components/artifacts_block.vue
+++ b/app/assets/javascripts/jobs/components/artifacts_block.vue
@@ -1,40 +1,27 @@
<script>
- import TimeagoTooltiop from '~/vue_shared/components/time_ago_tooltip.vue';
+ import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+ import timeagoMixin from '~/vue_shared/mixins/timeago';
export default {
components: {
- TimeagoTooltiop,
+ TimeagoTooltip,
},
+ mixins: [
+ timeagoMixin,
+ ],
props: {
- // @build.artifacts_expired?
- haveArtifactsExpired: {
- type: Boolean,
+ artifact: {
+ type: Object,
required: true,
},
- // @build.has_expiring_artifacts?
- willArtifactsExpire: {
- type: Boolean,
- required: true,
- },
- expireAt: {
- type: String,
- required: false,
- default: null,
- },
- keepArtifactsPath: {
- type: String,
- required: false,
- default: null,
- },
- downloadArtifactsPath: {
- type: String,
- required: false,
- default: null,
+ },
+ computed: {
+ isExpired() {
+ return this.artifact.expired;
},
- browseArtifactsPath: {
- type: String,
- required: false,
- default: null,
+ // Only when the key is `false` we can render this block
+ willExpire() {
+ return this.artifact.expired === false;
},
},
};
@@ -46,21 +33,22 @@
</div>
<p
- v-if="haveArtifactsExpired"
+ v-if="isExpired"
class="js-artifacts-removed build-detail-row"
>
{{ s__('Job|The artifacts were removed') }}
</p>
+
<p
- v-else-if="willArtifactsExpire"
+ v-else-if="willExpire"
class="js-artifacts-will-be-removed build-detail-row"
>
- {{ s__('Job|The artifacts will be removed') }}
+ {{ s__('Job|The artifacts will be removed in') }}
</p>
- <timeago-tooltiop
- v-if="expireAt"
- :time="expireAt"
+ <timeago-tooltip
+ v-if="artifact.expire_at"
+ :time="artifact.expire_at"
/>
<div
@@ -68,8 +56,8 @@
role="group"
>
<a
- v-if="keepArtifactsPath"
- :href="keepArtifactsPath"
+ v-if="artifact.keep_path"
+ :href="artifact.keep_path"
class="js-keep-artifacts btn btn-sm btn-default"
data-method="post"
>
@@ -77,8 +65,8 @@
</a>
<a
- v-if="downloadArtifactsPath"
- :href="downloadArtifactsPath"
+ v-if="artifact.download_path"
+ :href="artifact.download_path"
class="js-download-artifacts btn btn-sm btn-default"
download
rel="nofollow"
@@ -87,8 +75,8 @@
</a>
<a
- v-if="browseArtifactsPath"
- :href="browseArtifactsPath"
+ v-if="artifact.browse_path"
+ :href="artifact.browse_path"
class="js-browse-artifacts btn btn-sm btn-default"
>
{{ s__('Job|Browse') }}
diff --git a/app/assets/javascripts/jobs/components/commit_block.vue b/app/assets/javascripts/jobs/components/commit_block.vue
index 7f485295513..39a4ff159e2 100644
--- a/app/assets/javascripts/jobs/components/commit_block.vue
+++ b/app/assets/javascripts/jobs/components/commit_block.vue
@@ -1,64 +1,56 @@
<script>
-import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+ import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-export default {
- components: {
- ClipboardButton,
- },
- props: {
- pipelineShortSha: {
- type: String,
- required: true,
+ export default {
+ components: {
+ ClipboardButton,
},
- pipelineShaPath: {
- type: String,
- required: true,
+ props: {
+ commit: {
+ type: Object,
+ required: true,
+ },
+ mergeRequest: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ isLastBlock: {
+ type: Boolean,
+ required: true,
+ },
},
- mergeRequestReference: {
- type: String,
- required: false,
- default: null,
- },
- mergeRequestPath: {
- type: String,
- required: false,
- default: null,
- },
- gitCommitTitlte: {
- type: String,
- required: true,
- },
- },
-};
+ };
</script>
<template>
- <div class="block">
+ <div
+ :class="{
+ 'block-last': isLastBlock,
+ block: !isLastBlock
+ }">
<p>
{{ __('Commit') }}
<a
- :href="pipelineShaPath"
+ :href="commit.commit_path"
class="js-commit-sha commit-sha link-commit"
- >
- {{ pipelineShortSha }}
- </a>
+ >{{ commit.short_id }}</a>
<clipboard-button
- :text="pipelineShortSha"
+ :text="commit.short_id"
:title="__('Copy commit SHA to clipboard')"
+ css-class="btn btn-clipboard btn-transparent"
/>
<a
- v-if="mergeRequestPath && mergeRequestReference"
- :href="mergeRequestPath"
+ v-if="mergeRequest"
+ :href="mergeRequest.path"
class="js-link-commit link-commit"
- >
- {{ mergeRequestReference }}
- </a>
+ >{{ mergeRequest.iid }}</a>
</p>
<p class="build-light-text append-bottom-0">
- {{ gitCommitTitlte }}
+ {{ commit.title }}
</p>
</div>
</template>
diff --git a/app/assets/javascripts/jobs/components/header.vue b/app/assets/javascripts/jobs/components/header.vue
index 1e7f4b2c3f7..63324e68d68 100644
--- a/app/assets/javascripts/jobs/components/header.vue
+++ b/app/assets/javascripts/jobs/components/header.vue
@@ -1,13 +1,11 @@
<script>
import ciHeader from '../../vue_shared/components/header_ci_component.vue';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import callout from '../../vue_shared/components/callout.vue';
export default {
name: 'JobHeaderSection',
components: {
ciHeader,
- loadingIcon,
callout,
},
props: {
@@ -59,7 +57,7 @@ export default {
actions.push({
label: 'New issue',
path: this.job.new_issue_path,
- cssClass: 'js-new-issue btn btn-new btn-inverted d-none d-md-block d-lg-block d-xl-block',
+ cssClass: 'js-new-issue btn btn-success btn-inverted d-none d-md-block d-lg-block d-xl-block',
type: 'link',
});
}
@@ -82,9 +80,9 @@ export default {
:should-render-triggered-label="jobStarted"
item-name="Job"
/>
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
- size="2"
+ :size="2"
class="prepend-top-default append-bottom-default"
/>
</div>
diff --git a/app/assets/javascripts/jobs/components/job_log_controllers.vue b/app/assets/javascripts/jobs/components/job_log_controllers.vue
index 513851e376f..2cbf0f85266 100644
--- a/app/assets/javascripts/jobs/components/job_log_controllers.vue
+++ b/app/assets/javascripts/jobs/components/job_log_controllers.vue
@@ -78,8 +78,8 @@
<div class="controllers float-right">
<!-- links -->
<a
- v-tooltip
v-if="rawTracePath"
+ v-tooltip
:title="s__('Job|Show complete raw')"
:href="rawTracePath"
class="js-raw-link-controller controllers-buttons"
@@ -89,8 +89,8 @@
</a>
<button
- v-tooltip
v-if="canEraseJob"
+ v-tooltip
:title="s__('Job|Erase job log')"
type="button"
class="js-erase-link controllers-buttons"
diff --git a/app/assets/javascripts/jobs/components/jobs_container.vue b/app/assets/javascripts/jobs/components/jobs_container.vue
index b81109bdd06..93e2292ff84 100644
--- a/app/assets/javascripts/jobs/components/jobs_container.vue
+++ b/app/assets/javascripts/jobs/components/jobs_container.vue
@@ -25,9 +25,9 @@
class="build-job"
>
<a
- v-tooltip
v-for="job in jobs"
:key="job.id"
+ v-tooltip
:href="job.path"
:title="job.tooltip"
:class="{ active: job.active, retried: job.retried }"
diff --git a/app/assets/javascripts/jobs/components/sidebar_details_block.vue b/app/assets/javascripts/jobs/components/sidebar_details_block.vue
index 36d4a3e2bc9..a591fcfb482 100644
--- a/app/assets/javascripts/jobs/components/sidebar_details_block.vue
+++ b/app/assets/javascripts/jobs/components/sidebar_details_block.vue
@@ -1,91 +1,113 @@
<script>
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
-import timeagoMixin from '~/vue_shared/mixins/timeago';
-import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
-import Icon from '~/vue_shared/components/icon.vue';
-import DetailRow from './sidebar_detail_row.vue';
+ import _ from 'underscore';
+ import timeagoMixin from '~/vue_shared/mixins/timeago';
+ import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
+ import Icon from '~/vue_shared/components/icon.vue';
+ import DetailRow from './sidebar_detail_row.vue';
+ import ArtifactsBlock from './artifacts_block.vue';
+ import TriggerBlock from './trigger_block.vue';
+ import CommitBlock from './commit_block.vue';
-export default {
- name: 'SidebarDetailsBlock',
- components: {
- DetailRow,
- LoadingIcon,
- Icon,
- },
- mixins: [timeagoMixin],
- props: {
- job: {
- type: Object,
- required: true,
+ export default {
+ name: 'SidebarDetailsBlock',
+ components: {
+ ArtifactsBlock,
+ CommitBlock,
+ DetailRow,
+ Icon,
+ TriggerBlock,
},
- isLoading: {
- type: Boolean,
- required: true,
+ mixins: [timeagoMixin],
+ props: {
+ job: {
+ type: Object,
+ required: true,
+ },
+ isLoading: {
+ type: Boolean,
+ required: true,
+ },
+ runnerHelpUrl: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ terminalPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
- runnerHelpUrl: {
- type: String,
- required: false,
- default: '',
- },
- terminalPath: {
- type: String,
- required: false,
- default: null,
- },
- },
- computed: {
- shouldRenderContent() {
- return !this.isLoading && Object.keys(this.job).length > 0;
- },
- coverage() {
- return `${this.job.coverage}%`;
- },
- duration() {
- return timeIntervalInWords(this.job.duration);
- },
- queued() {
- return timeIntervalInWords(this.job.queued);
- },
- runnerId() {
- return `${this.job.runner.description} (#${this.job.runner.id})`;
- },
- retryButtonClass() {
- let className =
- 'js-retry-button float-right btn btn-retry d-none d-md-block d-lg-block d-xl-block';
- className +=
- this.job.status && this.job.recoverable ? ' btn-primary' : ' btn-inverted-secondary';
- return className;
- },
- hasTimeout() {
- return this.job.metadata != null && this.job.metadata.timeout_human_readable !== null;
- },
- timeout() {
- if (this.job.metadata == null) {
- return '';
- }
+ computed: {
+ shouldRenderContent() {
+ return !this.isLoading && Object.keys(this.job).length > 0;
+ },
+ coverage() {
+ return `${this.job.coverage}%`;
+ },
+ duration() {
+ return timeIntervalInWords(this.job.duration);
+ },
+ queued() {
+ return timeIntervalInWords(this.job.queued);
+ },
+ runnerId() {
+ return `${this.job.runner.description} (#${this.job.runner.id})`;
+ },
+ retryButtonClass() {
+ let className =
+ 'js-retry-button float-right btn btn-retry d-none d-md-block d-lg-block d-xl-block';
+ className +=
+ this.job.status && this.job.recoverable ? ' btn-primary' : ' btn-inverted-secondary';
+ return className;
+ },
+ hasTimeout() {
+ return this.job.metadata != null && this.job.metadata.timeout_human_readable !== null;
+ },
+ timeout() {
+ if (this.job.metadata == null) {
+ return '';
+ }
- let t = this.job.metadata.timeout_human_readable;
- if (this.job.metadata.timeout_source !== '') {
- t += ` (from ${this.job.metadata.timeout_source})`;
- }
+ let t = this.job.metadata.timeout_human_readable;
+ if (this.job.metadata.timeout_source !== '') {
+ t += ` (from ${this.job.metadata.timeout_source})`;
+ }
- return t;
- },
- renderBlock() {
- return (
- this.job.merge_request ||
- this.job.duration ||
- this.job.finished_data ||
- this.job.erased_at ||
- this.job.queued ||
- this.job.runner ||
- this.job.coverage ||
- this.job.tags.length ||
- this.job.cancel_path
- );
+ return t;
+ },
+ renderBlock() {
+ return (
+ this.job.merge_request ||
+ this.job.duration ||
+ this.job.finished_data ||
+ this.job.erased_at ||
+ this.job.queued ||
+ this.job.runner ||
+ this.job.coverage ||
+ this.job.tags.length ||
+ this.job.cancel_path
+ );
+ },
+ hasArtifact() {
+ return !_.isEmpty(this.job.artifact);
+ },
+ hasTriggers() {
+ return !_.isEmpty(this.job.trigger);
+ },
+ hasStages() {
+ return (
+ this.job &&
+ this.job.pipeline &&
+ this.job.pipeline.stages &&
+ this.job.pipeline.stages.length > 0
+ ) || false;
+ },
+ commit() {
+ return this.job.pipeline.commit || {};
+ },
},
- },
-};
+ };
</script>
<template>
<div>
@@ -132,7 +154,7 @@ export default {
<a
v-if="job.new_issue_path"
:href="job.new_issue_path"
- class="js-new-issue btn btn-new btn-inverted"
+ class="js-new-issue btn btn-success btn-inverted"
>
{{ __('New issue') }}
</a>
@@ -231,11 +253,24 @@ export default {
</a>
</div>
</div>
+ <artifacts-block
+ v-if="hasArtifact"
+ :artifact="job.artifact"
+ />
+ <trigger-block
+ v-if="hasTriggers"
+ :trigger="job.trigger"
+ />
+ <commit-block
+ :is-last-block="hasStages"
+ :commit="commit"
+ :merge-request="job.merge_request"
+ />
</template>
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
+ :size="2"
class="prepend-top-10"
- size="2"
/>
</div>
</template>
diff --git a/app/assets/javascripts/jobs/components/trigger_block.vue b/app/assets/javascripts/jobs/components/trigger_block.vue
index 8a88e5da6aa..d7b3c4fcb5b 100644
--- a/app/assets/javascripts/jobs/components/trigger_block.vue
+++ b/app/assets/javascripts/jobs/components/trigger_block.vue
@@ -1,16 +1,9 @@
<script>
export default {
props: {
- shortToken: {
- type: String,
- required: false,
- default: null,
- },
-
- variables: {
+ trigger: {
type: Object,
- required: false,
- default: () => ({}),
+ required: true,
},
},
data() {
@@ -20,7 +13,7 @@
},
computed: {
hasVariables() {
- return Object.keys(this.variables).length > 0;
+ return this.trigger.variables && this.trigger.variables.length > 0;
},
},
methods: {
@@ -38,17 +31,18 @@
</h4>
<p
- v-if="shortToken"
+ v-if="trigger.short_token"
class="js-short-token"
>
<span class="build-light-text">
{{ __('Token') }}
</span>
- {{ shortToken }}
+ {{ trigger.short_token }}
</p>
<p v-if="hasVariables">
<button
+ v-if="!areVariablesVisible"
type="button"
class="btn btn-default group js-reveal-variables"
@click="revealVariables"
@@ -63,20 +57,20 @@
class="js-build-variables trigger-build-variables"
>
<template
- v-for="(value, key) in variables"
+ v-for="variable in trigger.variables"
>
<dt
- :key="`${key}-variable`"
+ :key="`${variable.key}-variable`"
class="js-build-variable trigger-build-variable"
>
- {{ key }}
+ {{ variable.key }}
</dt>
<dd
- :key="`${key}-value`"
+ :key="`${variable.key}-value`"
class="js-build-value trigger-build-value"
>
- {{ value }}
+ {{ variable.value }}
</dd>
</template>
</dl>
diff --git a/app/assets/javascripts/jobs/job_details_mediator.js b/app/assets/javascripts/jobs/job_details_mediator.js
index 89019da9d1e..073e518baa0 100644
--- a/app/assets/javascripts/jobs/job_details_mediator.js
+++ b/app/assets/javascripts/jobs/job_details_mediator.js
@@ -4,7 +4,6 @@ import Poll from '../lib/utils/poll';
import JobStore from './stores/job_store';
import JobService from './services/job_service';
import Job from '../job';
-import handleRevealVariables from '../build_variables';
export default class JobMediator {
constructor(options = {}) {
@@ -20,7 +19,6 @@ export default class JobMediator {
initBuildClass() {
this.build = new Job();
- handleRevealVariables();
}
fetchJob() {
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 6499b919787..1c7bca78df3 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -449,11 +449,11 @@ export default class LabelsSelect {
}
bindEvents() {
- return $('body').on('change', '.selected_issue', this.onSelectCheckboxIssue);
+ return $('body').on('change', '.selected-issuable', this.onSelectCheckboxIssue);
}
// eslint-disable-next-line class-methods-use-this
onSelectCheckboxIssue() {
- if ($('.selected_issue:checked').length) {
+ if ($('.selected-issuable:checked').length) {
return;
}
return $('.issues-bulk-update .labels-filter .dropdown-toggle-text').text('Label');
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 0849d97bc1a..30925940807 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -56,7 +56,8 @@ export const rstrip = val => {
return val;
};
-export const updateTooltipTitle = ($tooltipEl, newTitle) => $tooltipEl.attr('title', newTitle).tooltip('_fixTitle');
+export const updateTooltipTitle = ($tooltipEl, newTitle) =>
+ $tooltipEl.attr('title', newTitle).tooltip('_fixTitle');
export const disableButtonIfEmptyField = (fieldSelector, buttonSelector, eventName = 'input') => {
const field = $(fieldSelector);
@@ -86,6 +87,7 @@ export const handleLocationHash = () => {
const fixedTabs = document.querySelector('.js-tabs-affix');
const fixedDiffStats = document.querySelector('.js-diff-files-changed');
const fixedNav = document.querySelector('.navbar-gitlab');
+ const performanceBar = document.querySelector('#js-peek');
let adjustment = 0;
if (fixedNav) adjustment -= fixedNav.offsetHeight;
@@ -102,6 +104,10 @@ export const handleLocationHash = () => {
adjustment -= fixedDiffStats.offsetHeight;
}
+ if (performanceBar) {
+ adjustment -= performanceBar.offsetHeight;
+ }
+
window.scrollBy(0, adjustment);
};
@@ -131,21 +137,20 @@ export const parseUrlPathname = url => {
return parsedUrl.pathname.charAt(0) === '/' ? parsedUrl.pathname : `/${parsedUrl.pathname}`;
};
-const splitPath = (path = '') => path
- .replace(/^\?/, '')
- .split('&');
+const splitPath = (path = '') => path.replace(/^\?/, '').split('&');
-export const urlParamsToArray = (path = '') => splitPath(path)
- .filter(param => param.length > 0)
- .map(param => {
- const split = param.split('=');
- return [decodeURI(split[0]), split[1]].join('=');
- });
+export const urlParamsToArray = (path = '') =>
+ splitPath(path)
+ .filter(param => param.length > 0)
+ .map(param => {
+ const split = param.split('=');
+ return [decodeURI(split[0]), split[1]].join('=');
+ });
export const getUrlParamsArray = () => urlParamsToArray(window.location.search);
-export const urlParamsToObject = (path = '') => splitPath(path)
- .reduce((dataParam, filterParam) => {
+export const urlParamsToObject = (path = '') =>
+ splitPath(path).reduce((dataParam, filterParam) => {
if (filterParam === '') {
return dataParam;
}
@@ -216,7 +221,7 @@ export const getParameterByName = (name, urlToParse) => {
return decodeURIComponent(results[2].replace(/\+/g, ' '));
};
-const handleSelectedRange = (range) => {
+const handleSelectedRange = range => {
const container = range.commonAncestorContainer;
// add context to fragment if needed
if (container.tagName === 'OL') {
@@ -453,7 +458,7 @@ export const backOff = (fn, timeout = 60000) => {
export const createOverlayIcon = (iconPath, overlayPath) => {
const faviconImage = document.createElement('img');
- return new Promise((resolve) => {
+ return new Promise(resolve => {
faviconImage.onload = () => {
const size = 32;
@@ -464,13 +469,29 @@ export const createOverlayIcon = (iconPath, overlayPath) => {
const context = canvas.getContext('2d');
context.clearRect(0, 0, size, size);
context.drawImage(
- faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, size, size,
+ faviconImage,
+ 0,
+ 0,
+ faviconImage.width,
+ faviconImage.height,
+ 0,
+ 0,
+ size,
+ size,
);
const overlayImage = document.createElement('img');
overlayImage.onload = () => {
context.drawImage(
- overlayImage, 0, 0, overlayImage.width, overlayImage.height, 0, 0, size, size,
+ overlayImage,
+ 0,
+ 0,
+ overlayImage.width,
+ overlayImage.height,
+ 0,
+ 0,
+ size,
+ size,
);
const faviconWithOverlayUrl = canvas.toDataURL();
@@ -483,17 +504,21 @@ export const createOverlayIcon = (iconPath, overlayPath) => {
});
};
-export const setFaviconOverlay = (overlayPath) => {
+export const setFaviconOverlay = overlayPath => {
const faviconEl = document.getElementById('favicon');
- if (!faviconEl) { return null; }
+ if (!faviconEl) {
+ return null;
+ }
const iconPath = faviconEl.getAttribute('data-original-href');
- return createOverlayIcon(iconPath, overlayPath).then(faviconWithOverlayUrl => faviconEl.setAttribute('href', faviconWithOverlayUrl));
+ return createOverlayIcon(iconPath, overlayPath).then(faviconWithOverlayUrl =>
+ faviconEl.setAttribute('href', faviconWithOverlayUrl),
+ );
};
-export const setFavicon = (faviconPath) => {
+export const setFavicon = faviconPath => {
const faviconEl = document.getElementById('favicon');
if (faviconEl && faviconPath) {
faviconEl.setAttribute('href', faviconPath);
@@ -518,7 +543,7 @@ export const setCiStatusFavicon = pageUrl =>
}
return resetFavicon();
})
- .catch((error) => {
+ .catch(error => {
resetFavicon();
throw error;
});
diff --git a/app/assets/javascripts/shortcuts_dashboard_navigation.js b/app/assets/javascripts/lib/utils/navigation_utility.js
index 9f69f110d06..1579b225e44 100644
--- a/app/assets/javascripts/shortcuts_dashboard_navigation.js
+++ b/app/assets/javascripts/lib/utils/navigation_utility.js
@@ -1,4 +1,4 @@
-import { visitUrl } from './lib/utils/url_utility';
+import { visitUrl } from './url_utility';
/**
* Helper function that finds the href of the fiven selector and updates the location.
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index c5a5f64abac..e8aac51a299 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -2,7 +2,6 @@
import jQuery from 'jquery';
import Cookies from 'js-cookie';
-import svg4everybody from 'svg4everybody';
// bootstrap webpack, common libs, polyfills, and behaviors
import './webpack';
@@ -25,11 +24,12 @@ import initLayoutNav from './layout_nav';
import './feature_highlight/feature_highlight_options';
import LazyLoader from './lazy_loader';
import initLogoAnimation from './logo';
-import './milestone_select';
import './frequent_items';
import initBreadcrumbs from './breadcrumb';
-import initDispatcher from './dispatcher';
import initUsagePingConsent from './usage_ping_consent';
+import initPerformanceBar from './performance_bar';
+import initSearchAutocomplete from './search_autocomplete';
+import GlFieldErrors from './gl_field_errors';
// expose jQuery as global (TODO: remove these)
window.jQuery = jQuery;
@@ -41,8 +41,6 @@ if (process.env.NODE_ENV !== 'production' && gon && gon.test_env) {
import(/* webpackMode: "eager" */ './test_utils/');
}
-svg4everybody();
-
document.addEventListener('beforeunload', () => {
// Unbind scroll events
$(document).off('scroll');
@@ -81,6 +79,9 @@ document.addEventListener('DOMContentLoaded', () => {
initLogoAnimation();
initUsagePingConsent();
+ if (document.querySelector('.search')) initSearchAutocomplete();
+ if (document.querySelector('#js-peek')) initPerformanceBar({ container: '#js-peek' });
+
// Set the default path for all cookies to GitLab's root directory
Cookies.defaults.path = gon.relative_url_root || '/';
@@ -270,5 +271,6 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
- initDispatcher();
+ // initialize field errors
+ $('.gl-show-field-errors').each((i, form) => new GlFieldErrors(form));
});
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 53d7504de35..763429d7242 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -115,8 +115,9 @@ export default class MergeRequestTabs {
this.mergeRequestTabs &&
this.mergeRequestTabs.querySelector(`a[data-action='${action}']`) &&
this.mergeRequestTabs.querySelector(`a[data-action='${action}']`).click
- )
+ ) {
this.mergeRequestTabs.querySelector(`a[data-action='${action}']`).click();
+ }
this.initAffix();
}
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 524f4d1f568..228eff006f7 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -220,8 +220,8 @@ export default {
:show-panels="showPanels"
>
<graph
- v-for="(graphData, index) in groupData.metrics"
- :key="index"
+ v-for="(graphData, graphIndex) in groupData.metrics"
+ :key="graphIndex"
:graph-data="graphData"
:hover-data="hoverData"
:deployment-data="store.deploymentData"
diff --git a/app/assets/javascripts/monitoring/components/graph/legend.vue b/app/assets/javascripts/monitoring/components/graph/legend.vue
index 3276f3a1ceb..ef18ae5c2c8 100644
--- a/app/assets/javascripts/monitoring/components/graph/legend.vue
+++ b/app/assets/javascripts/monitoring/components/graph/legend.vue
@@ -58,8 +58,8 @@ export default {
</td>
<template v-for="(track, trackIndex) in series.tracksLegend">
<track-line
- :track="track"
- :key="`track-line-${trackIndex}`"/>
+ :key="`track-line-${trackIndex}`"
+ :track="track"/>
<td :key="`track-info-${trackIndex}`">
<track-info
:track="track"
diff --git a/app/assets/javascripts/mr_notes/stores/index.js b/app/assets/javascripts/mr_notes/stores/index.js
index dd2019001db..446eb477efc 100644
--- a/app/assets/javascripts/mr_notes/stores/index.js
+++ b/app/assets/javascripts/mr_notes/stores/index.js
@@ -9,7 +9,7 @@ Vue.use(Vuex);
export default new Vuex.Store({
modules: {
page: mrPageModule,
- notes: notesModule,
- diffs: diffsModule,
+ notes: notesModule(),
+ diffs: diffsModule(),
},
});
diff --git a/app/assets/javascripts/notebook/index.vue b/app/assets/javascripts/notebook/index.vue
index e2e3b08c77f..f241df9620d 100644
--- a/app/assets/javascripts/notebook/index.vue
+++ b/app/assets/javascripts/notebook/index.vue
@@ -51,10 +51,10 @@
<template>
<div v-if="hasNotebook">
<component
- v-for="(cell, index) in cells"
:is="cellType(cell.cell_type)"
- :cell="cell"
+ v-for="(cell, index) in cells"
:key="index"
+ :cell="cell"
:code-css-class="codeCssClass" />
</div>
</template>
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 8b1d8f6055e..0c966e0808a 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -631,7 +631,7 @@ export default class Notes {
*
* deactivates the submit button when text is empty
* hides the preview button when text is empty
- * setup GFM auto complete
+ * set up GFM auto complete
* show the form
*/
setupNoteForm(form, enableGFM = defaultAutocompleteConfig) {
@@ -954,7 +954,7 @@ export default class Notes {
* Note: dataHolder must have the "discussionId" and "lineCode" data attributes set.
*/
setupDiscussionNoteForm(dataHolder, form) {
- // setup note target
+ // set up note target
let diffFileData = dataHolder.closest('.text-file');
if (diffFileData.length === 0) {
@@ -1036,7 +1036,7 @@ export default class Notes {
$diffFile[0].dispatchEvent(clickEvent);
- // Setup comment form
+ // Set up comment form
let newForm;
const $noteContainer = $link.closest('.diff-viewer').find('.note-container');
const $form = $noteContainer.find('> .discussion-form');
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 6612bc44e0b..7735133c470 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -374,7 +374,7 @@ js-gfm-input js-autosize markdown-area js-vue-textarea"
append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown">
<button
:disabled="isSubmitButtonDisabled"
- class="btn btn-create comment-btn js-comment-button js-comment-submit-button"
+ class="btn btn-success comment-btn js-comment-button js-comment-submit-button"
type="submit"
@click.prevent="handleSave()">
{{ __(commentButtonTitle) }}
diff --git a/app/assets/javascripts/notes/components/diff_file_header.vue b/app/assets/javascripts/notes/components/diff_file_header.vue
index fc7b52be241..4fd93304a03 100644
--- a/app/assets/javascripts/notes/components/diff_file_header.vue
+++ b/app/assets/javascripts/notes/components/diff_file_header.vue
@@ -41,8 +41,8 @@ export default {
</div>
<template v-else>
<component
- ref="titleWrapper"
:is="titleTag"
+ ref="titleWrapper"
:href="diffFile.discussionPath"
>
<span v-html="diffFile.blobIcon"></span>
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index 87fc002fcbc..beb53da0e6d 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -7,7 +7,6 @@ import editSvg from 'icons/_icon_pencil.svg';
import resolveDiscussionSvg from 'icons/_icon_resolve_discussion.svg';
import resolvedDiscussionSvg from 'icons/_icon_status_success_solid.svg';
import ellipsisSvg from 'icons/_ellipsis_v.svg';
-import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
export default {
@@ -15,9 +14,6 @@ export default {
directives: {
tooltip,
},
- components: {
- loadingIcon,
- },
props: {
authorId: {
type: Number,
@@ -153,9 +149,9 @@ export default {
v-else
v-html="resolveDiscussionSvg"></div>
</template>
- <loading-icon
+ <gl-loading-icon
v-else
- :inline="true"
+ inline
/>
</button>
</div>
@@ -172,7 +168,7 @@ export default {
href="#"
title="Add reaction"
>
- <loading-icon :inline="true" />
+ <gl-loading-icon inline/>
<span
class="link-highlight award-control-icon-neutral"
v-html="emojiSmiling">
diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue
index 051b17e9aa9..c68860d98ae 100644
--- a/app/assets/javascripts/notes/components/note_awards_list.vue
+++ b/app/assets/javascripts/notes/components/note_awards_list.vue
@@ -182,9 +182,9 @@ export default {
<div class="note-awards">
<div class="awards js-awards-block">
<button
- v-tooltip
v-for="(awardList, awardName, index) in groupedAwards"
:key="index"
+ v-tooltip
:class="getAwardClassBindings(awardList)"
:title="awardTitle(awardList)"
class="btn award-control"
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index c41ed070383..2d47d55f33c 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -171,8 +171,8 @@ export default {
id="note_note"
ref="textarea"
slot="textarea"
- :data-supports-quick-actions="!isEditing"
v-model="updatedNoteBody"
+ :data-supports-quick-actions="!isEditing"
name="note[note]"
class="note-textarea js-gfm-input js-note-text
js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
@@ -188,7 +188,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
<button
:disabled="isDisabled"
type="button"
- class="js-vue-issue-save btn btn-save js-comment-button "
+ class="js-vue-issue-save btn btn-success js-comment-button "
@click="handleUpdate()">
{{ saveButtonTitle }}
</button>
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index afe86911230..6ede7562edf 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -348,10 +348,10 @@ Please check your network connection and try again.`;
<div class="discussion-notes">
<ul class="notes">
<component
- v-for="note in discussion.notes"
:is="componentName(note)"
- :note="componentData(note)"
+ v-for="note in discussion.notes"
:key="note.id"
+ :note="componentData(note)"
@handleDeleteNote="deleteNoteHandler"
/>
</ul>
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 7f9d23b211b..d8e8efb982a 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -10,7 +10,6 @@ import systemNote from '../../vue_shared/components/notes/system_note.vue';
import commentForm from './comment_form.vue';
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import skeletonLoadingContainer from '../../vue_shared/components/notes/skeleton_note.vue';
export default {
@@ -20,7 +19,6 @@ export default {
noteableDiscussion,
systemNote,
commentForm,
- loadingIcon,
placeholderNote,
placeholderSystemNote,
},
@@ -189,10 +187,10 @@ export default {
class="notes main-notes-list timeline"
>
<component
- v-for="discussion in allDiscussions"
:is="getComponentName(discussion)"
- v-bind="getComponentData(discussion)"
+ v-for="discussion in allDiscussions"
:key="discussion.id"
+ v-bind="getComponentData(discussion)"
/>
</ul>
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 9a2ec15debd..320dfa47d5a 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -10,6 +10,7 @@ import service from '../services/notes_service';
import loadAwardsHandler from '../../awards_handler';
import sidebarTimeTrackingEventHub from '../../sidebar/event_hub';
import { isInViewport, scrollToElement } from '../../lib/utils/common_utils';
+import mrWidgetEventHub from '../../vue_merge_request_widget/event_hub';
let eTagPoll;
@@ -43,27 +44,17 @@ export const fetchDiscussions = ({ commit }, path) =>
commit(types.SET_INITIAL_DISCUSSIONS, discussions);
});
-export const refetchDiscussionById = ({ commit, state }, { path, discussionId }) =>
- new Promise(resolve => {
- service
- .fetchDiscussions(path)
- .then(res => res.json())
- .then(discussions => {
- const selectedDiscussion = discussions.find(discussion => discussion.id === discussionId);
- if (selectedDiscussion) {
- commit(types.UPDATE_DISCUSSION, selectedDiscussion);
- // We need to refetch as it is now the transformed one in state
- const discussion = utils.findNoteObjectById(state.discussions, discussionId);
-
- resolve(discussion);
- }
- })
- .catch(() => {});
- });
+export const updateDiscussion = ({ commit, state }, discussion) => {
+ commit(types.UPDATE_DISCUSSION, discussion);
-export const deleteNote = ({ commit }, note) =>
+ return utils.findNoteObjectById(state.discussions, discussion.id);
+};
+
+export const deleteNote = ({ commit, dispatch }, note) =>
service.deleteNote(note.path).then(() => {
commit(types.DELETE_NOTE, note);
+
+ dispatch('updateMergeRequestWidget');
});
export const updateNote = ({ commit }, { endpoint, note }) =>
@@ -84,20 +75,22 @@ export const replyToDiscussion = ({ commit }, { endpoint, data }) =>
return res;
});
-export const createNewNote = ({ commit }, { endpoint, data }) =>
+export const createNewNote = ({ commit, dispatch }, { endpoint, data }) =>
service
.createNewNote(endpoint, data)
.then(res => res.json())
.then(res => {
if (!res.errors) {
commit(types.ADD_NEW_NOTE, res);
+
+ dispatch('updateMergeRequestWidget');
}
return res;
});
export const removePlaceholderNotes = ({ commit }) => commit(types.REMOVE_PLACEHOLDER_NOTES);
-export const toggleResolveNote = ({ commit }, { endpoint, isResolved, discussion }) =>
+export const toggleResolveNote = ({ commit, dispatch }, { endpoint, isResolved, discussion }) =>
service
.toggleResolveNote(endpoint, isResolved)
.then(res => res.json())
@@ -105,6 +98,8 @@ export const toggleResolveNote = ({ commit }, { endpoint, isResolved, discussion
const mutationType = discussion ? types.UPDATE_DISCUSSION : types.UPDATE_NOTE;
commit(mutationType, res);
+
+ dispatch('updateMergeRequestWidget');
});
export const closeIssue = ({ commit, dispatch, state }) => {
@@ -333,5 +328,9 @@ export const fetchDiscussionDiffLines = ({ commit }, discussion) =>
});
});
+export const updateMergeRequestWidget = () => {
+ mrWidgetEventHub.$emit('mr.discussion.updated');
+};
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/notes/stores/index.js b/app/assets/javascripts/notes/stores/index.js
index 0f48b8880f4..f105b7d0d11 100644
--- a/app/assets/javascripts/notes/stores/index.js
+++ b/app/assets/javascripts/notes/stores/index.js
@@ -1,16 +1,8 @@
import Vue from 'vue';
import Vuex from 'vuex';
-import * as actions from './actions';
-import * as getters from './getters';
-import mutations from './mutations';
-import module from './modules';
+import notesModule from './modules';
Vue.use(Vuex);
export default () =>
- new Vuex.Store({
- state: module.state,
- actions,
- getters,
- mutations,
- });
+ new Vuex.Store(notesModule());
diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js
index b4cb9267e0f..61dbb075586 100644
--- a/app/assets/javascripts/notes/stores/modules/index.js
+++ b/app/assets/javascripts/notes/stores/modules/index.js
@@ -2,7 +2,7 @@ import * as actions from '../actions';
import * as getters from '../getters';
import mutations from '../mutations';
-export default {
+export default () => ({
state: {
discussions: [],
targetNoteHash: null,
@@ -24,4 +24,4 @@ export default {
actions,
getters,
mutations,
-};
+});
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index 2c04bfea122..73e55705f39 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -4,7 +4,8 @@ import * as constants from '../constants';
import { isInMRPage } from '../../lib/utils/common_utils';
export default {
- [types.ADD_NEW_NOTE](state, note) {
+ [types.ADD_NEW_NOTE](state, data) {
+ const note = data.discussion ? data.discussion.notes[0] : data;
const { discussion_id, type } = note;
const [exists] = state.discussions.filter(n => n.id === note.discussion_id);
const isDiscussion = type === constants.DISCUSSION_NOTE || type === constants.DIFF_NOTE;
@@ -100,7 +101,10 @@ export default {
discussionsData.forEach(discussion => {
if (discussion.diff_file) {
- Object.assign(discussion, { fileHash: discussion.diff_file.file_hash });
+ Object.assign(discussion, {
+ fileHash: discussion.diff_file.file_hash,
+ truncated_diff_lines: discussion.truncated_diff_lines || [],
+ });
}
// To support legacy notes, should be very rare case.
@@ -214,12 +218,7 @@ export default {
[types.SET_DISCUSSION_DIFF_LINES](state, { discussionId, diffLines }) {
const discussion = utils.findNoteObjectById(state.discussions, discussionId);
- const index = state.discussions.indexOf(discussion);
-
- const discussionWithDiffLines = Object.assign({}, discussion, {
- truncated_diff_lines: diffLines,
- });
- state.discussions.splice(index, 1, discussionWithDiffLines);
+ discussion.truncated_diff_lines = diffLines;
},
};
diff --git a/app/assets/javascripts/notes/stores/utils.js b/app/assets/javascripts/notes/stores/utils.js
index 8ccbdb4c130..0e41ff03d67 100644
--- a/app/assets/javascripts/notes/stores/utils.js
+++ b/app/assets/javascripts/notes/stores/utils.js
@@ -27,7 +27,7 @@ export const getQuickActionText = note => {
export const reduceDiscussionsToLineCodes = selectedDiscussions =>
selectedDiscussions.reduce((acc, note) => {
- if (note.diff_discussion && note.line_code && note.resolvable) {
+ if (note.diff_discussion && note.line_code) {
// For context about line notes: there might be multiple notes with the same line code
const items = acc[note.line_code] || [];
items.push(note);
diff --git a/app/assets/javascripts/pages/admin/application_settings/index.js b/app/assets/javascripts/pages/admin/application_settings/index.js
index 069f8ce55d0..47bd70537f1 100644
--- a/app/assets/javascripts/pages/admin/application_settings/index.js
+++ b/app/assets/javascripts/pages/admin/application_settings/index.js
@@ -1,13 +1,8 @@
import initSettingsPanels from '~/settings_panels';
import projectSelect from '~/project_select';
-import UsagePingPayload from './usage_ping_payload';
document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
initSettingsPanels();
projectSelect();
- new UsagePingPayload(
- document.querySelector('.js-usage-ping-payload-trigger'),
- document.querySelector('.js-usage-ping-payload'),
- ).init();
});
diff --git a/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/index.js b/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/index.js
new file mode 100644
index 00000000000..c40503603be
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/index.js
@@ -0,0 +1,8 @@
+import UsagePingPayload from './../usage_ping_payload';
+
+document.addEventListener('DOMContentLoaded', () => {
+ new UsagePingPayload(
+ document.querySelector('.js-usage-ping-payload-trigger'),
+ document.querySelector('.js-usage-ping-payload'),
+ ).init();
+});
diff --git a/app/assets/javascripts/pages/admin/runners/index.js b/app/assets/javascripts/pages/admin/runners/index.js
new file mode 100644
index 00000000000..ce8fd18b6a2
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/runners/index.js
@@ -0,0 +1,10 @@
+import initFilteredSearch from '~/pages/search/init_filtered_search';
+import AdminRunnersFilteredSearchTokenKeys from '~/filtered_search/admin_runners_filtered_search_token_keys';
+import { FILTERED_SEARCH } from '~/pages/constants';
+
+document.addEventListener('DOMContentLoaded', () => {
+ initFilteredSearch({
+ page: FILTERED_SEARCH.ADMIN_RUNNERS,
+ filteredSearchTokenKeys: AdminRunnersFilteredSearchTokenKeys,
+ });
+});
diff --git a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
index d6aa4bb95d2..8d5efcdcd96 100644
--- a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
+++ b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
@@ -155,10 +155,7 @@
/>
</form>
</template>
- <template
- slot="secondary-button"
- slot-scope="props"
- >
+ <template slot="secondary-button">
<button
:disabled="!canSubmit"
type="button"
diff --git a/app/assets/javascripts/pages/constants.js b/app/assets/javascripts/pages/constants.js
index 328b6541636..5e119454ce1 100644
--- a/app/assets/javascripts/pages/constants.js
+++ b/app/assets/javascripts/pages/constants.js
@@ -3,4 +3,5 @@
export const FILTERED_SEARCH = {
MERGE_REQUESTS: 'merge_requests',
ISSUES: 'issues',
+ ADMIN_RUNNERS: 'admin/runners',
};
diff --git a/app/assets/javascripts/pages/groups/boards/index.js b/app/assets/javascripts/pages/groups/boards/index.js
index 5cfe8723204..79c3be771d0 100644
--- a/app/assets/javascripts/pages/groups/boards/index.js
+++ b/app/assets/javascripts/pages/groups/boards/index.js
@@ -1,5 +1,5 @@
import UsersSelect from '~/users_select';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import initBoards from '~/boards';
document.addEventListener('DOMContentLoaded', () => {
diff --git a/app/assets/javascripts/pages/groups/issues/index.js b/app/assets/javascripts/pages/groups/issues/index.js
index 914f804fdd3..736c6a62610 100644
--- a/app/assets/javascripts/pages/groups/issues/index.js
+++ b/app/assets/javascripts/pages/groups/issues/index.js
@@ -1,11 +1,13 @@
import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
isGroupDecendent: true,
+ filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
projectSelect();
});
diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js
index 1600faa3611..b798a254459 100644
--- a/app/assets/javascripts/pages/groups/merge_requests/index.js
+++ b/app/assets/javascripts/pages/groups/merge_requests/index.js
@@ -1,11 +1,13 @@
import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS,
isGroupDecendent: true,
+ filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
projectSelect();
});
diff --git a/app/assets/javascripts/pages/groups/show/index.js b/app/assets/javascripts/pages/groups/show/index.js
index 5b8c2ae7e81..3a45fd70d02 100644
--- a/app/assets/javascripts/pages/groups/show/index.js
+++ b/app/assets/javascripts/pages/groups/show/index.js
@@ -6,7 +6,7 @@ import NewGroupChild from '~/groups/new_group_child';
import notificationsDropdown from '~/notifications_dropdown';
import NotificationsForm from '~/notifications_form';
import ProjectsList from '~/projects_list';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import GroupTabs from './group_tabs';
document.addEventListener('DOMContentLoaded', () => {
diff --git a/app/assets/javascripts/pages/ide/index.js b/app/assets/javascripts/pages/ide/index.js
index efadf6967aa..d192df3561e 100644
--- a/app/assets/javascripts/pages/ide/index.js
+++ b/app/assets/javascripts/pages/ide/index.js
@@ -1,9 +1,3 @@
-import { initIde, resetServiceWorkersPublicPath } from '~/ide/index';
+import { startIde } from '~/ide/index';
-document.addEventListener('DOMContentLoaded', () => {
- const ideElement = document.getElementById('ide');
- if (ideElement) {
- resetServiceWorkersPublicPath();
- initIde(ideElement);
- }
-});
+startIde();
diff --git a/app/assets/javascripts/pages/projects/activity/index.js b/app/assets/javascripts/pages/projects/activity/index.js
index 5543ad82428..d39ea3d10bf 100644
--- a/app/assets/javascripts/pages/projects/activity/index.js
+++ b/app/assets/javascripts/pages/projects/activity/index.js
@@ -1,5 +1,5 @@
import Activities from '~/activities';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
document.addEventListener('DOMContentLoaded', () => {
new Activities(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/artifacts/browse/index.js b/app/assets/javascripts/pages/projects/artifacts/browse/index.js
index ea7458fe9b8..26dc90a56d7 100644
--- a/app/assets/javascripts/pages/projects/artifacts/browse/index.js
+++ b/app/assets/javascripts/pages/projects/artifacts/browse/index.js
@@ -1,5 +1,5 @@
import BuildArtifacts from '~/build_artifacts';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
document.addEventListener('DOMContentLoaded', () => {
new ShortcutsNavigation(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/artifacts/file/index.js b/app/assets/javascripts/pages/projects/artifacts/file/index.js
index 8484e5e9848..249900d6cb7 100644
--- a/app/assets/javascripts/pages/projects/artifacts/file/index.js
+++ b/app/assets/javascripts/pages/projects/artifacts/file/index.js
@@ -1,5 +1,5 @@
import BlobViewer from '~/blob/viewer/index';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
document.addEventListener('DOMContentLoaded', () => {
new ShortcutsNavigation(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/boards/index.js b/app/assets/javascripts/pages/projects/boards/index.js
index 5cfe8723204..79c3be771d0 100644
--- a/app/assets/javascripts/pages/projects/boards/index.js
+++ b/app/assets/javascripts/pages/projects/boards/index.js
@@ -1,5 +1,5 @@
import UsersSelect from '~/users_select';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import initBoards from '~/boards';
document.addEventListener('DOMContentLoaded', () => {
diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js
index 2e23cce11ce..f477424811d 100644
--- a/app/assets/javascripts/pages/projects/commit/show/index.js
+++ b/app/assets/javascripts/pages/projects/commit/show/index.js
@@ -3,7 +3,7 @@
import $ from 'jquery';
import Diff from '~/diff';
import ZenMode from '~/zen_mode';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown';
import initNotes from '~/init_notes';
import initChangesDropdown from '~/init_changes_dropdown';
diff --git a/app/assets/javascripts/pages/projects/commits/show/index.js b/app/assets/javascripts/pages/projects/commits/show/index.js
index 3682020579b..ad671ce9351 100644
--- a/app/assets/javascripts/pages/projects/commits/show/index.js
+++ b/app/assets/javascripts/pages/projects/commits/show/index.js
@@ -1,6 +1,6 @@
import CommitsList from '~/commits';
import GpgBadges from '~/gpg_badges';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
document.addEventListener('DOMContentLoaded', () => {
new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/find_file/show/index.js b/app/assets/javascripts/pages/projects/find_file/show/index.js
index 24630c2aa05..388d7d7bdda 100644
--- a/app/assets/javascripts/pages/projects/find_file/show/index.js
+++ b/app/assets/javascripts/pages/projects/find_file/show/index.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import ProjectFindFile from '~/project_find_file';
-import ShortcutsFindFile from '~/shortcuts_find_file';
+import ShortcutsFindFile from '~/behaviors/shortcuts/shortcuts_find_file';
document.addEventListener('DOMContentLoaded', () => {
const findElement = document.querySelector('.js-file-finder');
diff --git a/app/assets/javascripts/pages/projects/index.js b/app/assets/javascripts/pages/projects/index.js
index 9c074b74c3b..5659e13981a 100644
--- a/app/assets/javascripts/pages/projects/index.js
+++ b/app/assets/javascripts/pages/projects/index.js
@@ -1,7 +1,7 @@
import initDismissableCallout from '~/dismissable_callout';
import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
import Project from './project';
-import ShortcutsNavigation from '../../shortcuts_navigation';
+import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation';
document.addEventListener('DOMContentLoaded', () => {
const { page } = document.body.dataset;
diff --git a/app/assets/javascripts/pages/projects/init_blob.js b/app/assets/javascripts/pages/projects/init_blob.js
index 56ab3fcdfcb..bc08ccf3584 100644
--- a/app/assets/javascripts/pages/projects/init_blob.js
+++ b/app/assets/javascripts/pages/projects/init_blob.js
@@ -1,7 +1,7 @@
import LineHighlighter from '~/line_highlighter';
import BlobLinePermalinkUpdater from '~/blob/blob_line_permalink_updater';
-import ShortcutsNavigation from '~/shortcuts_navigation';
-import ShortcutsBlob from '~/shortcuts_blob';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
+import ShortcutsBlob from '~/behaviors/shortcuts/shortcuts_blob';
import BlobForkSuggestion from '~/blob/blob_fork_suggestion';
import initBlobBundle from '~/blob_edit/blob_bundle';
diff --git a/app/assets/javascripts/pages/projects/issues/form.js b/app/assets/javascripts/pages/projects/issues/form.js
index b2b8e5d2300..197bfa8a394 100644
--- a/app/assets/javascripts/pages/projects/issues/form.js
+++ b/app/assets/javascripts/pages/projects/issues/form.js
@@ -5,7 +5,7 @@ import GLForm from '~/gl_form';
import IssuableForm from '~/issuable_form';
import LabelsSelect from '~/labels_select';
import MilestoneSelect from '~/milestone_select';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import IssuableTemplateSelectors from '~/templates/issuable_template_selectors';
export default () => {
diff --git a/app/assets/javascripts/pages/projects/issues/index/index.js b/app/assets/javascripts/pages/projects/issues/index/index.js
index 70fdb0ef40d..a56c0bb6be8 100644
--- a/app/assets/javascripts/pages/projects/issues/index/index.js
+++ b/app/assets/javascripts/pages/projects/issues/index/index.js
@@ -1,15 +1,17 @@
/* eslint-disable no-new */
import IssuableIndex from '~/issuable_index';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import UsersSelect from '~/users_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
+ filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
new IssuableIndex(ISSUABLE_INDEX.ISSUE);
diff --git a/app/assets/javascripts/pages/projects/issues/show.js b/app/assets/javascripts/pages/projects/issues/show.js
index 500fbd27340..74b3a515e84 100644
--- a/app/assets/javascripts/pages/projects/issues/show.js
+++ b/app/assets/javascripts/pages/projects/issues/show.js
@@ -1,6 +1,6 @@
import initIssuableSidebar from '~/init_issuable_sidebar';
import Issue from '~/issue';
-import ShortcutsIssuable from '~/shortcuts_issuable';
+import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable';
import ZenMode from '~/zen_mode';
import '~/notes/index';
import '~/issue_show/index';
diff --git a/app/assets/javascripts/pages/projects/merge_requests/index/index.js b/app/assets/javascripts/pages/projects/merge_requests/index/index.js
index a7aa616319f..3647048a872 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/index/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js
@@ -1,13 +1,15 @@
import IssuableIndex from '~/issuable_index';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import UsersSelect from '~/users_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS,
+ filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
new IssuableIndex(ISSUABLE_INDEX.MERGE_REQUEST); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
index 3a3c21f2202..e3971618da5 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
@@ -2,7 +2,7 @@
import $ from 'jquery';
import Diff from '~/diff';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import GLForm from '~/gl_form';
import IssuableForm from '~/issuable_form';
import LabelsSelect from '~/labels_select';
diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
index 26ead75cec4..7bfb83a2204 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
@@ -1,6 +1,6 @@
import ZenMode from '~/zen_mode';
import initIssuableSidebar from '~/init_issuable_sidebar';
-import ShortcutsIssuable from '~/shortcuts_issuable';
+import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable';
import { handleLocationHash } from '~/lib/utils/common_utils';
import howToMerge from '~/how_to_merge';
import initPipelines from '~/commit/pipelines/pipelines_bundle';
diff --git a/app/assets/javascripts/pages/projects/network/show/index.js b/app/assets/javascripts/pages/projects/network/show/index.js
index a0b14fed10f..9f05f63b742 100644
--- a/app/assets/javascripts/pages/projects/network/show/index.js
+++ b/app/assets/javascripts/pages/projects/network/show/index.js
@@ -1,5 +1,5 @@
import $ from 'jquery';
-import ShortcutsNetwork from '../../../../shortcuts_network';
+import ShortcutsNetwork from '~/behaviors/shortcuts/shortcuts_network';
import Network from '../network';
document.addEventListener('DOMContentLoaded', () => {
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
index 0d05668b285..ef53d67e7cb 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
@@ -147,8 +147,8 @@
<div class="cron-interval-input-wrapper">
<input
id="schedule_cron"
- :placeholder="__('Define a custom pattern with cron syntax')"
v-model="cronInterval"
+ :placeholder="__('Define a custom pattern with cron syntax')"
:name="inputNameAttribute"
:disabled="!isEditable"
class="form-control inline cron-interval-input"
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index ae88b765abf..875f6928bed 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -240,8 +240,8 @@
help-text="Lightweight issue tracking system for this project"
>
<project-feature-setting
- :options="featureAccessLevelOptions"
v-model="issuesAccessLevel"
+ :options="featureAccessLevelOptions"
name="project[project_feature_attributes][issues_access_level]"
/>
</project-setting-row>
@@ -250,8 +250,8 @@
help-text="View and edit files in this project"
>
<project-feature-setting
- :options="featureAccessLevelOptions"
v-model="repositoryAccessLevel"
+ :options="featureAccessLevelOptions"
name="project[project_feature_attributes][repository_access_level]"
/>
</project-setting-row>
@@ -261,8 +261,8 @@
help-text="Submit changes to be merged upstream"
>
<project-feature-setting
- :options="repoFeatureAccessLevelOptions"
v-model="mergeRequestsAccessLevel"
+ :options="repoFeatureAccessLevelOptions"
:disabled-input="!repositoryEnabled"
name="project[project_feature_attributes][merge_requests_access_level]"
/>
@@ -272,8 +272,8 @@
help-text="Build, test, and deploy your changes"
>
<project-feature-setting
- :options="repoFeatureAccessLevelOptions"
v-model="buildsAccessLevel"
+ :options="repoFeatureAccessLevelOptions"
:disabled-input="!repositoryEnabled"
name="project[project_feature_attributes][builds_access_level]"
/>
@@ -308,8 +308,8 @@
help-text="Pages for project documentation"
>
<project-feature-setting
- :options="featureAccessLevelOptions"
v-model="wikiAccessLevel"
+ :options="featureAccessLevelOptions"
name="project[project_feature_attributes][wiki_access_level]"
/>
</project-setting-row>
@@ -318,8 +318,8 @@
help-text="Share code pastes with others out of Git repository"
>
<project-feature-setting
- :options="featureAccessLevelOptions"
v-model="snippetsAccessLevel"
+ :options="featureAccessLevelOptions"
name="project[project_feature_attributes][snippets_access_level]"
/>
</project-setting-row>
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index 0507f67843f..7302c1ab202 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import initBlob from '~/blob_edit/blob_bundle';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import NotificationsForm from '~/notifications_form';
import UserCallout from '~/user_callout';
import TreeView from '~/tree';
diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js
index 33d69d891d8..400aed35e32 100644
--- a/app/assets/javascripts/pages/projects/tree/show/index.js
+++ b/app/assets/javascripts/pages/projects/tree/show/index.js
@@ -4,7 +4,7 @@ import initBlob from '~/blob_edit/blob_bundle';
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
import GpgBadges from '~/gpg_badges';
import TreeView from '../../../../tree';
-import ShortcutsNavigation from '../../../../shortcuts_navigation';
+import ShortcutsNavigation from '../../../../behaviors/shortcuts/shortcuts_navigation';
import BlobViewer from '../../../../blob/viewer';
import NewCommitForm from '../../../../new_commit_form';
import { ajaxGet } from '../../../../lib/utils/common_utils';
diff --git a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue
index 42c37bc8cd8..75cb6374ad5 100644
--- a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue
+++ b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue
@@ -1,12 +1,8 @@
<script>
import _ from 'underscore';
-import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale';
export default {
- components: {
- GlModal,
- },
props: {
deleteWikiUrl: {
type: String,
diff --git a/app/assets/javascripts/pages/projects/wikis/index.js b/app/assets/javascripts/pages/projects/wikis/index.js
index b0a323a71cd..c2629090f01 100644
--- a/app/assets/javascripts/pages/projects/wikis/index.js
+++ b/app/assets/javascripts/pages/projects/wikis/index.js
@@ -2,8 +2,8 @@ import $ from 'jquery';
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import csrf from '~/lib/utils/csrf';
+import ShortcutsWiki from '~/behaviors/shortcuts/shortcuts_wiki';
import Wikis from './wikis';
-import ShortcutsWiki from '../../../shortcuts_wiki';
import ZenMode from '../../../zen_mode';
import GLForm from '../../../gl_form';
import deleteWikiModal from './components/delete_wiki_modal.vue';
diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
index 0fdb0a080cf..7836d4f3b09 100644
--- a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
+++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
@@ -130,8 +130,8 @@ export default {
</div>
<simple-metric
v-for="metric in $options.simpleMetrics"
- :current-request="currentRequest"
:key="metric"
+ :current-request="currentRequest"
:metric="metric"
/>
<div
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
index 1952dd453f4..9b4ba0c1a9a 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
@@ -1,12 +1,10 @@
<script>
import _ from 'underscore';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import StageColumnComponent from './stage_column_component.vue';
export default {
components: {
StageColumnComponent,
- LoadingIcon,
},
props: {
isLoading: {
@@ -59,9 +57,9 @@ export default {
<div class="build-content middle-block js-pipeline-graph">
<div class="pipeline-visualization pipeline-graph pipeline-tab-content">
<div class="text-center">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
- size="3"
+ :size="3"
/>
</div>
@@ -70,9 +68,9 @@ export default {
class="stage-column-list">
<stage-column-component
v-for="(stage, index) in graph"
+ :key="stage.name"
:title="capitalizeStageName(stage.name)"
:jobs="stage.groups"
- :key="stage.name"
:stage-connector-class="stageConnectorClass(index, stage)"
:is-first-column="isFirstColumn(index)"
@refreshPipelineGraph="refreshPipelineGraph"
diff --git a/app/assets/javascripts/pipelines/components/graph/job_component.vue b/app/assets/javascripts/pipelines/components/graph/job_component.vue
index 9ac16b7e541..a1504592bbc 100644
--- a/app/assets/javascripts/pipelines/components/graph/job_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/job_component.vue
@@ -98,8 +98,8 @@ export default {
<template>
<div class="ci-job-component">
<a
- v-tooltip
v-if="status.has_details"
+ v-tooltip
:href="status.details_path"
:title="tooltipText"
:class="cssClassJobName"
@@ -115,8 +115,8 @@ export default {
</a>
<div
- v-tooltip
v-else
+ v-tooltip
:title="tooltipText"
:class="cssClassJobName"
class="js-job-component-tooltip non-details-job-component"
diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
index e7b2de52f76..567ea119343 100644
--- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
@@ -62,9 +62,9 @@ export default {
<ul>
<li
v-for="(job, index) in jobs"
+ :id="jobId(job)"
:key="job.id"
:class="buildConnnectorClass(index)"
- :id="jobId(job)"
class="build"
>
diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue
index 001eaeaa065..1f9187c3d65 100644
--- a/app/assets/javascripts/pipelines/components/header_component.vue
+++ b/app/assets/javascripts/pipelines/components/header_component.vue
@@ -1,13 +1,11 @@
<script>
import ciHeader from '../../vue_shared/components/header_ci_component.vue';
import eventHub from '../event_hub';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
name: 'PipelineHeaderSection',
components: {
ciHeader,
- loadingIcon,
},
props: {
pipeline: {
@@ -89,9 +87,9 @@ export default {
item-name="Pipeline"
@actionClicked="postAction"
/>
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
- size="2"
+ :size="2"
class="prepend-top-default append-bottom-default"
/>
</div>
diff --git a/app/assets/javascripts/pipelines/components/nav_controls.vue b/app/assets/javascripts/pipelines/components/nav_controls.vue
index 9501afb7493..efb80d3a3c0 100644
--- a/app/assets/javascripts/pipelines/components/nav_controls.vue
+++ b/app/assets/javascripts/pipelines/components/nav_controls.vue
@@ -43,7 +43,7 @@ export default {
<a
v-if="newPipelinePath"
:href="newPipelinePath"
- class="btn btn-create js-run-pipeline"
+ class="btn btn-success js-run-pipeline"
>
{{ s__('Pipelines|Run Pipeline') }}
</a>
diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue
index 75db1e9ae7c..40df07650c9 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_url.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue
@@ -67,29 +67,29 @@ export default {
</span>
<div class="label-container">
<span
- v-tooltip
v-if="pipeline.flags.latest"
+ v-tooltip
class="js-pipeline-url-latest badge badge-success"
title="Latest pipeline for this branch">
latest
</span>
<span
- v-tooltip
v-if="pipeline.flags.yaml_errors"
+ v-tooltip
:title="pipeline.yaml_errors"
class="js-pipeline-url-yaml badge badge-danger">
yaml invalid
</span>
<span
- v-tooltip
v-if="pipeline.flags.failure_reason"
+ v-tooltip
:title="pipeline.failure_reason"
class="js-pipeline-url-failure badge badge-danger">
error
</span>
<a
- v-popover="popoverOptions"
v-if="pipeline.flags.auto_devops"
+ v-popover="popoverOptions"
tabindex="0"
class="js-pipeline-url-autodevops badge badge-info autodevops-badge"
role="button">
diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue
index c9d2dc3a3c5..ea526cf1309 100644
--- a/app/assets/javascripts/pipelines/components/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines.vue
@@ -319,10 +319,10 @@ export default {
<div class="content-list pipelines">
- <loading-icon
+ <gl-loading-icon
v-if="stateToRender === $options.stateMap.loading"
:label="s__('Pipelines|Loading Pipelines')"
- size="3"
+ :size="3"
class="prepend-top-20"
/>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
index 1c8d7303c52..017dd560621 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
@@ -1,6 +1,5 @@
<script>
import eventHub from '../event_hub';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import icon from '../../vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
@@ -9,7 +8,6 @@ export default {
tooltip,
},
components: {
- loadingIcon,
icon,
},
props: {
@@ -60,7 +58,7 @@ export default {
class="fa fa-caret-down"
aria-hidden="true">
</i>
- <loading-icon v-if="isLoading" />
+ <gl-loading-icon v-if="isLoading" />
</button>
<ul class="dropdown-menu dropdown-menu-right">
diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue
index c7df69c69ed..47c15b1a9c4 100644
--- a/app/assets/javascripts/pipelines/components/stage.vue
+++ b/app/assets/javascripts/pipelines/components/stage.vue
@@ -18,14 +18,12 @@ import Flash from '../../flash';
import axios from '../../lib/utils/axios_utils';
import eventHub from '../event_hub';
import Icon from '../../vue_shared/components/icon.vue';
-import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
import JobComponent from './graph/job_component.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import { PIPELINES_TABLE } from '../constants';
export default {
components: {
- LoadingIcon,
Icon,
JobComponent,
},
@@ -157,9 +155,9 @@ export default {
<template>
<div class="dropdown">
<button
- v-tooltip
id="stageDropdown"
ref="dropdown"
+ v-tooltip
:class="triggerButtonClass"
:title="stage.title"
class="mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button"
@@ -191,7 +189,7 @@ export default {
class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container"
aria-labelledby="stageDropdown"
>
- <loading-icon v-if="isLoading"/>
+ <gl-loading-icon v-if="isLoading"/>
<ul
v-else
class="js-builds-dropdown-list scrollable-menu"
diff --git a/app/assets/javascripts/pipelines/mixins/pipelines.js b/app/assets/javascripts/pipelines/mixins/pipelines.js
index 2cb558b0dec..8929b397f6c 100644
--- a/app/assets/javascripts/pipelines/mixins/pipelines.js
+++ b/app/assets/javascripts/pipelines/mixins/pipelines.js
@@ -4,7 +4,6 @@ import Flash from '../../flash';
import Poll from '../../lib/utils/poll';
import EmptyState from '../components/empty_state.vue';
import SvgBlankState from '../components/blank_state.vue';
-import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
import PipelinesTableComponent from '../components/pipelines_table.vue';
import eventHub from '../event_hub';
import { CANCEL_REQUEST } from '../constants';
@@ -14,7 +13,6 @@ export default {
PipelinesTableComponent,
SvgBlankState,
EmptyState,
- LoadingIcon,
},
data() {
return {
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js
index c15d8ba49e1..d5266544307 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js
@@ -1,5 +1,4 @@
import _ from 'underscore';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
@@ -9,7 +8,6 @@ import store from '../store';
export default {
store,
components: {
- LoadingIcon,
DropdownButton,
DropdownSearchInput,
DropdownHiddenInput,
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue
index d4497924ad8..2c02f436b69 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue
@@ -126,7 +126,7 @@ export default {
</ul>
</div>
<div class="dropdown-loading">
- <loading-icon />
+ <gl-loading-icon />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue
index 08d0a122579..fc17e2fab49 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue
@@ -187,7 +187,7 @@ export default {
</ul>
</div>
<div class="dropdown-loading">
- <loading-icon />
+ <gl-loading-icon />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
index b5476684c6a..ca7c79f75f0 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
@@ -100,7 +100,7 @@ export default {
</ul>
</div>
<div class="dropdown-loading">
- <loading-icon />
+ <gl-loading-icon />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
index 1c1e17563a1..120b4fc2f2b 100644
--- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
+++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
@@ -1,7 +1,6 @@
<script>
import Visibility from 'visibilityjs';
import ciIcon from '~/vue_shared/components/ci_icon.vue';
-import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import Poll from '~/lib/utils/poll';
import Flash from '~/flash';
import { s__, sprintf } from '~/locale';
@@ -14,7 +13,6 @@ export default {
},
components: {
ciIcon,
- loadingIcon,
},
props: {
endpoint: {
@@ -100,10 +98,10 @@ export default {
</script>
<template>
<div class="ci-status-link">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
+ :size="3"
label="Loading pipeline status"
- size="3"
/>
<a
v-else
diff --git a/app/assets/javascripts/registry/components/app.vue b/app/assets/javascripts/registry/components/app.vue
index 31f88675912..7e2287ac4db 100644
--- a/app/assets/javascripts/registry/components/app.vue
+++ b/app/assets/javascripts/registry/components/app.vue
@@ -1,7 +1,6 @@
<script>
import { mapGetters, mapActions } from 'vuex';
import Flash from '../../flash';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import store from '../stores';
import collapsibleContainer from './collapsible_container.vue';
import { errorMessages, errorMessagesTypes } from '../constants';
@@ -10,7 +9,6 @@
name: 'RegistryListApp',
components: {
collapsibleContainer,
- loadingIcon,
},
props: {
endpoint: {
@@ -42,9 +40,9 @@
</script>
<template>
<div>
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
- size="3"
+ :size="3"
/>
<collapsible-container
diff --git a/app/assets/javascripts/registry/components/collapsible_container.vue b/app/assets/javascripts/registry/components/collapsible_container.vue
index cea409aa130..d9bf41924d1 100644
--- a/app/assets/javascripts/registry/components/collapsible_container.vue
+++ b/app/assets/javascripts/registry/components/collapsible_container.vue
@@ -2,7 +2,6 @@
import { mapActions } from 'vuex';
import Flash from '../../flash';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import tableRegistry from './table_registry.vue';
import { errorMessages, errorMessagesTypes } from '../constants';
@@ -12,7 +11,6 @@
name: 'CollapsibeContainerRegisty',
components: {
clipboardButton,
- loadingIcon,
tableRegistry,
},
directives: {
@@ -90,8 +88,8 @@
<div class="controls d-none d-sm-block float-right">
<button
- v-tooltip
v-if="repo.canDelete"
+ v-tooltip
:title="s__('ContainerRegistry|Remove repository')"
:aria-label="s__('ContainerRegistry|Remove repository')"
type="button"
@@ -107,10 +105,10 @@
</div>
</div>
- <loading-icon
+ <gl-loading-icon
v-if="repo.isLoading"
+ :size="2"
class="append-bottom-20"
- size="2"
/>
<div
diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue
index 9f4973c3490..fafb35bd69a 100644
--- a/app/assets/javascripts/registry/components/table_registry.vue
+++ b/app/assets/javascripts/registry/components/table_registry.vue
@@ -118,8 +118,8 @@
<td class="content">
<button
- v-tooltip
v-if="item.canDelete"
+ v-tooltip
:title="s__('ContainerRegistry|Remove tag')"
:aria-label="s__('ContainerRegistry|Remove tag')"
type="button"
diff --git a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue
index 7b37f4e9a97..fb8c6402d02 100644
--- a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue
+++ b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue
@@ -92,16 +92,16 @@
v-for="(report, i) in reports"
>
<summary-row
+ :key="`summary-row-${i}`"
:summary="reportText(report)"
:status-icon="getReportIcon(report)"
- :key="`summary-row-${i}`"
/>
<issues-list
v-if="shouldRenderIssuesList(report)"
+ :key="`issues-list-${i}`"
:unresolved-issues="report.existing_failures"
:new-issues="report.new_failures"
:resolved-issues="report.resolved_failures"
- :key="`issues-list-${i}`"
:component="$options.componentNames.TestIssueBody"
class="report-block-group-list"
/>
diff --git a/app/assets/javascripts/reports/components/report_issues.vue b/app/assets/javascripts/reports/components/report_issues.vue
index c553a374f66..a2a03945ae3 100644
--- a/app/assets/javascripts/reports/components/report_issues.vue
+++ b/app/assets/javascripts/reports/components/report_issues.vue
@@ -37,8 +37,8 @@ export default {
<ul class="report-block-list">
<li
v-for="(issue, index) in issues"
- :class="{ 'is-dismissed': issue.isDismissed }"
:key="index"
+ :class="{ 'is-dismissed': issue.isDismissed }"
class="report-block-list-issue"
>
<issue-status-icon
@@ -47,8 +47,8 @@ export default {
/>
<component
- v-if="component"
:is="component"
+ v-if="component"
:issue="issue"
:status="issue.status || status"
:is-new="isNew"
diff --git a/app/assets/javascripts/reports/components/summary_row.vue b/app/assets/javascripts/reports/components/summary_row.vue
index 4456d84c968..51188981bed 100644
--- a/app/assets/javascripts/reports/components/summary_row.vue
+++ b/app/assets/javascripts/reports/components/summary_row.vue
@@ -1,6 +1,5 @@
<script>
import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import Popover from '~/vue_shared/components/help_popover.vue';
/**
@@ -15,7 +14,6 @@ export default {
name: 'ReportSummaryRow',
components: {
CiIcon,
- LoadingIcon,
Popover,
},
props: {
@@ -46,7 +44,7 @@ export default {
<template>
<div class="report-block-list-issue report-block-list-issue-parent">
<div class="report-block-list-icon append-right-10 prepend-left-5">
- <loading-icon
+ <gl-loading-icon
v-if="statusIcon === 'loading'"
css-class="report-block-list-loading-icon"
/>
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index aec09b8bc0a..50dd3c12382 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -68,7 +68,7 @@ function setSearchOptions() {
}
}
-export default class SearchAutocomplete {
+export class SearchAutocomplete {
constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) {
setSearchOptions();
this.bindEventContext();
@@ -499,3 +499,7 @@ export default class SearchAutocomplete {
this.dropdownMenu.toggleClass('fade-out', !this.isScrolledUp());
}
}
+
+export default function initSearchAutocomplete(opts) {
+ return new SearchAutocomplete(opts);
+}
diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue
index 56d57f6aac8..286a16f7bbf 100644
--- a/app/assets/javascripts/sidebar/components/participants/participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/participants.vue
@@ -1,7 +1,6 @@
<script>
import { __, n__, sprintf } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
- import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
export default {
@@ -9,7 +8,6 @@
tooltip,
},
components: {
- loadingIcon,
userAvatarImage,
},
props: {
@@ -93,7 +91,7 @@
aria-hidden="true"
>
</i>
- <loading-icon
+ <gl-loading-icon
v-if="loading"
class="js-participants-collapsed-loading-icon"
/>
@@ -105,7 +103,7 @@
</span>
</div>
<div class="title hide-collapsed">
- <loading-icon
+ <gl-loading-icon
v-if="loading"
:inline="true"
class="js-participants-expanded-loading-icon"
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
index ca3b9338c29..2ee3e1f322e 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
@@ -19,19 +19,23 @@ export default {
TimeTrackingHelpState,
},
props: {
+ // eslint-disable-next-line vue/prop-name-casing
time_estimate: {
type: Number,
required: true,
},
+ // eslint-disable-next-line vue/prop-name-casing
time_spent: {
type: Number,
required: true,
},
+ // eslint-disable-next-line vue/prop-name-casing
human_time_estimate: {
type: String,
required: false,
default: '',
},
+ // eslint-disable-next-line vue/prop-name-casing
human_time_spent: {
type: String,
required: false,
diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
index ffaed9c7193..a6b3a674952 100644
--- a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
+++ b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
@@ -3,7 +3,6 @@ import { __ } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
const MARK_TEXT = __('Mark todo as done');
const TODO_TEXT = __('Add todo');
@@ -14,7 +13,6 @@ export default {
},
components: {
Icon,
- LoadingIcon,
},
props: {
issuableId: {
@@ -90,7 +88,7 @@ export default {
>
{{ buttonLabel }}
</span>
- <loading-icon
+ <gl-loading-icon
v-show="isActionActive"
:inline="true"
/>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
index d530ab2767b..70518ad73e8 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
@@ -106,8 +106,8 @@ export default {
</tooltip-on-truncate>
</template>
<span
- v-tooltip
v-if="hasDeploymentTime"
+ v-tooltip
:title="deployment.deployed_at_formatted"
class="js-deploy-time"
>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
index 9aff95dcfec..035ae791a1d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
@@ -1,11 +1,9 @@
<script>
import ciIcon from '../../vue_shared/components/ci_icon.vue';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
components: {
ciIcon,
- loadingIcon,
},
props: {
status: {
@@ -37,7 +35,7 @@
v-if="isLoading"
class="mr-widget-icon"
>
- <loading-icon />
+ <gl-loading-icon />
</div>
<ci-icon
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
index 2133124347c..01294d5b40c 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
@@ -1,5 +1,4 @@
<script>
- import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon.vue';
@@ -7,7 +6,6 @@
name: 'MRWidgetAutoMergeFailed',
components: {
statusIcon,
- loadingIcon,
},
props: {
mr: {
@@ -44,7 +42,7 @@
class="btn btn-sm btn-default"
@click="refreshWidget"
>
- <loading-icon
+ <gl-loading-icon
v-if="isRefreshing"
:inline="true"
/>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
index 1a444c04a1d..8184ef33022 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
@@ -1,7 +1,6 @@
<script>
import Flash from '~/flash';
import tooltip from '~/vue_shared/directives/tooltip';
- import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import { s__, __ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
@@ -15,7 +14,6 @@
},
components: {
MrWidgetAuthorTime,
- loadingIcon,
statusIcon,
ClipboardButton,
},
@@ -116,8 +114,8 @@
:date-readable="mr.metrics.readableMergedAt"
/>
<a
- v-tooltip
v-if="mr.canRevertInCurrentMR"
+ v-tooltip
:title="revertTitle"
class="btn btn-close btn-sm"
href="#modal-revert-commit"
@@ -127,8 +125,8 @@
{{ revertLabel }}
</a>
<a
- v-tooltip
v-else-if="mr.revertInForkPath"
+ v-tooltip
:href="mr.revertInForkPath"
:title="revertTitle"
class="btn btn-close btn-sm"
@@ -137,8 +135,8 @@
{{ revertLabel }}
</a>
<a
- v-tooltip
v-if="mr.canCherryPickInCurrentMR"
+ v-tooltip
:title="cherryPickTitle"
class="btn btn-default btn-sm"
href="#modal-cherry-pick-commit"
@@ -148,8 +146,8 @@
{{ cherryPickLabel }}
</a>
<a
- v-tooltip
v-else-if="mr.cherryPickInForkPath"
+ v-tooltip
:href="mr.cherryPickInForkPath"
:title="cherryPickTitle"
class="btn btn-default btn-sm"
@@ -195,7 +193,7 @@
</button>
</p>
<p v-if="shouldShowSourceBranchRemoving">
- <loading-icon :inline="true" />
+ <gl-loading-icon :inline="true" />
<span>
{{ s__("mrWidget|The source branch is being removed") }}
</span>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
index 2d8c3d6be87..f31c7a3edb8 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
@@ -2,14 +2,12 @@
import simplePoll from '../../../lib/utils/simple_poll';
import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon.vue';
- import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import Flash from '../../../flash';
export default {
name: 'MRWidgetRebase',
components: {
statusIcon,
- loadingIcon,
},
props: {
mr: {
@@ -115,7 +113,7 @@ js-toggle-container accept-action media space-children"
class="btn btn-sm btn-reopen btn-success qa-mr-rebase-button"
@click="rebase"
>
- <loading-icon v-if="isMakingRequest" />
+ <gl-loading-icon v-if="isMakingRequest" />
Rebase
</button>
<span
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue
index 25c1044fe2b..25ad329e196 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue
@@ -37,8 +37,8 @@ export default {
<div class="accept-control inline">
<label class="merge-param-checkbox">
<input
- :disabled="isMergeButtonDisabled"
v-model="squashBeforeMerge"
+ :disabled="isMergeButtonDisabled"
type="checkbox"
name="squash"
class="qa-squash-checkbox"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
index 086dbabe77e..e73b7e410d5 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
@@ -37,7 +37,7 @@ export default {
<a
v-if="mr.newBlobPath"
:href="mr.newBlobPath"
- class="btn btn-inverted btn-save">
+ class="btn btn-inverted btn-success">
Create file
</a>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index a5ca7b719a1..23c3284cd21 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -255,7 +255,7 @@ export default {
data-toggle="dropdown"
aria-label="Select merge moment">
<i
- class="fa fa-chevron-down"
+ class="fa fa-chevron-down qa-merge-moment-dropdown"
aria-hidden="true"
></i>
</button>
@@ -265,7 +265,7 @@ export default {
role="menu">
<li>
<a
- class="merge_when_pipeline_succeeds"
+ class="merge_when_pipeline_succeeds qa-merge-when-pipeline-succeeds-option"
href="#"
@click.prevent="handleMergeButtonClick(true)">
<span class="media">
@@ -279,7 +279,7 @@ export default {
</li>
<li>
<a
- class="accept-merge-request"
+ class="accept-merge-request qa-merge-immediately-option"
href="#"
@click.prevent="handleMergeButtonClick(false, true)">
<span class="media">
diff --git a/app/assets/javascripts/vue_merge_request_widget/index.js b/app/assets/javascripts/vue_merge_request_widget/index.js
index 69a9132a2da..cc6e620f365 100644
--- a/app/assets/javascripts/vue_merge_request_widget/index.js
+++ b/app/assets/javascripts/vue_merge_request_widget/index.js
@@ -1,7 +1,4 @@
-import {
- Vue,
- mrWidgetOptions,
-} from './dependencies';
+import { Vue, mrWidgetOptions } from './dependencies';
import Translate from '../vue_shared/translate';
Vue.use(Translate);
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index dc6be025f11..0e445a29de4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -107,10 +107,16 @@ export default {
created() {
this.initPolling();
this.bindEventHubListeners();
+ eventHub.$on('mr.discussion.updated', this.checkStatus);
},
mounted() {
this.handleMounted();
},
+ beforeDestroy() {
+ eventHub.$off('mr.discussion.updated', this.checkStatus);
+ this.pollingInterval.destroy();
+ this.deploymentsInterval.destroy();
+ },
methods: {
createService(store) {
const endpoints = {
diff --git a/app/assets/javascripts/vue_shared/components/bar_chart.vue b/app/assets/javascripts/vue_shared/components/bar_chart.vue
index 3ced4eb691a..33af7a7f1df 100644
--- a/app/assets/javascripts/vue_shared/components/bar_chart.vue
+++ b/app/assets/javascripts/vue_shared/components/bar_chart.vue
@@ -291,8 +291,8 @@ export default {
<template
v-for="(data, index) in graphData">
<rect
- v-tooltip
:key="index"
+ v-tooltip
:width="xScale.bandwidth()"
:x="xScale(data.name)"
:y="yScale(data.value)"
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
index d3cbe3c7e74..cfc5343217c 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
@@ -46,7 +46,7 @@ export default {
}
},
basePath() {
- // We might get the project path from rails with the relative url already setup
+ // We might get the project path from rails with the relative url already set up
return this.projectPath.indexOf('/') === 0 ? '' : `${gon.relative_url_root}/`;
},
fullOldPath() {
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue
index af5ebcdc40a..31087017968 100644
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue
+++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue
@@ -1,11 +1,7 @@
<script>
import { __ } from '~/locale';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
export default {
- components: {
- LoadingIcon,
- },
props: {
isDisabled: {
type: Boolean,
@@ -34,7 +30,7 @@ export default {
data-toggle="dropdown"
aria-expanded="false"
>
- <loading-icon
+ <gl-loading-icon
v-show="isLoading"
:inline="true"
/>
diff --git a/app/assets/javascripts/vue_shared/components/file_icon.vue b/app/assets/javascripts/vue_shared/components/file_icon.vue
index 878c805ada5..408f7d7965f 100644
--- a/app/assets/javascripts/vue_shared/components/file_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/file_icon.vue
@@ -1,6 +1,5 @@
<script>
import getIconForFile from './file_icon/file_icon_map';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import icon from '../../vue_shared/components/icon.vue';
/* This is a re-usable vue component for rendering a svg sprite
@@ -17,7 +16,6 @@ import icon from '../../vue_shared/components/icon.vue';
*/
export default {
components: {
- loadingIcon,
icon,
},
props: {
@@ -84,7 +82,7 @@ export default {
:size="size"
css-classes="folder-icon"
/>
- <loading-icon
+ <gl-loading-icon
v-if="loading"
:inline="true"
/>
diff --git a/app/assets/javascripts/vue_shared/components/file_row.vue b/app/assets/javascripts/vue_shared/components/file_row.vue
new file mode 100644
index 00000000000..c797ad62a5d
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/file_row.vue
@@ -0,0 +1,210 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
+
+export default {
+ name: 'FileRow',
+ components: {
+ FileIcon,
+ Icon,
+ },
+ props: {
+ file: {
+ type: Object,
+ required: true,
+ },
+ level: {
+ type: Number,
+ required: true,
+ },
+ extraComponent: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ mouseOver: false,
+ };
+ },
+ computed: {
+ isTree() {
+ return this.file.type === 'tree';
+ },
+ isBlob() {
+ return this.file.type === 'blob';
+ },
+ levelIndentation() {
+ return {
+ marginLeft: `${this.level * 16}px`,
+ };
+ },
+ fileClass() {
+ return {
+ 'file-open': this.isBlob && this.file.opened,
+ 'is-active': this.isBlob && this.file.active,
+ folder: this.isTree,
+ 'is-open': this.file.opened,
+ };
+ },
+ },
+ watch: {
+ 'file.active': function fileActiveWatch(active) {
+ if (this.file.type === 'blob' && active) {
+ this.scrollIntoView();
+ }
+ },
+ },
+ mounted() {
+ if (this.hasPathAtCurrentRoute()) {
+ this.scrollIntoView(true);
+ }
+ },
+ methods: {
+ toggleTreeOpen(path) {
+ this.$emit('toggleTreeOpen', path);
+ },
+ clickFile() {
+ // Manual Action if a tree is selected/opened
+ if (this.isTree && this.hasUrlAtCurrentRoute()) {
+ this.toggleTreeOpen(this.file.path);
+ }
+
+ if (this.$router) this.$router.push(`/project${this.file.url}`);
+ },
+ scrollIntoView(isInit = false) {
+ const block = isInit && this.isTree ? 'center' : 'nearest';
+
+ this.$el.scrollIntoView({
+ behavior: 'smooth',
+ block,
+ });
+ },
+ hasPathAtCurrentRoute() {
+ if (!this.$router || !this.$router.currentRoute) {
+ return false;
+ }
+
+ // - strip route up to "/-/" and ending "/"
+ const routePath = this.$router.currentRoute.path
+ .replace(/^.*?[/]-[/]/g, '')
+ .replace(/[/]$/g, '');
+
+ // - strip ending "/"
+ const filePath = this.file.path.replace(/[/]$/g, '');
+
+ return filePath === routePath;
+ },
+ hasUrlAtCurrentRoute() {
+ if (!this.$router || !this.$router.currentRoute) return true;
+
+ return this.$router.currentRoute.path === `/project${this.file.url}`;
+ },
+ toggleHover(over) {
+ this.mouseOver = over;
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <div
+ :class="fileClass"
+ class="file-row"
+ role="button"
+ @click="clickFile"
+ @mouseover="toggleHover(true)"
+ @mouseout="toggleHover(false)"
+ >
+ <div
+ class="file-row-name-container"
+ >
+ <span
+ :style="levelIndentation"
+ class="file-row-name str-truncated"
+ >
+ <file-icon
+ :file-name="file.name"
+ :loading="file.loading"
+ :folder="isTree"
+ :opened="file.opened"
+ :size="16"
+ />
+ {{ file.name }}
+ </span>
+ <component
+ :is="extraComponent"
+ v-if="extraComponent"
+ :file="file"
+ :mouse-over="mouseOver"
+ />
+ </div>
+ </div>
+ <template v-if="file.opened">
+ <file-row
+ v-for="childFile in file.tree"
+ :key="childFile.key"
+ :file="childFile"
+ :level="level + 1"
+ :extra-component="extraComponent"
+ @toggleTreeOpen="toggleTreeOpen"
+ />
+ </template>
+ </div>
+</template>
+
+<style>
+.file-row {
+ display: flex;
+ align-items: center;
+ height: 32px;
+ padding: 4px 8px;
+ margin-left: -8px;
+ margin-right: -8px;
+ border-radius: 3px;
+ text-align: left;
+ cursor: pointer;
+}
+
+.file-row:hover,
+.file-row:focus {
+ background: #f2f2f2;
+}
+
+.file-row:active {
+ background: #dfdfdf;
+}
+
+.file-row.is-active {
+ background: #f2f2f2;
+}
+
+.file-row-name-container {
+ display: flex;
+ width: 100%;
+ align-items: center;
+ overflow: visible;
+}
+
+.file-row-name {
+ display: inline-block;
+ flex: 1;
+ max-width: inherit;
+ height: 18px;
+ line-height: 16px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.file-row-name svg {
+ margin-right: 2px;
+ vertical-align: middle;
+}
+
+.file-row-name .loading-container {
+ display: inline-block;
+ margin-right: 4px;
+}
+</style>
diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
index 49fbce75110..b371b6adf7e 100644
--- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue
+++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
@@ -1,6 +1,5 @@
<script>
import CiIconBadge from './ci_badge_link.vue';
-import LoadingIcon from './loading_icon.vue';
import TimeagoTooltip from './time_ago_tooltip.vue';
import tooltip from '../directives/tooltip';
import UserAvatarImage from './user_avatar/user_avatar_image.vue';
@@ -15,7 +14,6 @@ import UserAvatarImage from './user_avatar/user_avatar_image.vue';
export default {
components: {
CiIconBadge,
- LoadingIcon,
TimeagoTooltip,
UserAvatarImage,
},
@@ -128,18 +126,18 @@ export default {
>
<a
v-if="action.type === 'link'"
+ :key="i"
:href="action.path"
:class="action.cssClass"
- :key="i"
>
{{ action.label }}
</a>
<a
v-else-if="action.type === 'ujs-link'"
+ :key="i"
:href="action.path"
:class="action.cssClass"
- :key="i"
data-method="post"
rel="nofollow"
>
@@ -148,9 +146,9 @@ export default {
<button
v-else-if="action.type === 'button'"
+ :key="i"
:disabled="action.isLoading"
:class="action.cssClass"
- :key="i"
type="button"
@click="onClickAction(action)"
>
diff --git a/app/assets/javascripts/vue_shared/components/loading_button.vue b/app/assets/javascripts/vue_shared/components/loading_button.vue
index 2ff0c056b9c..4cbd3e6429d 100644
--- a/app/assets/javascripts/vue_shared/components/loading_button.vue
+++ b/app/assets/javascripts/vue_shared/components/loading_button.vue
@@ -17,12 +17,7 @@
*/
- import loadingIcon from './loading_icon.vue';
-
export default {
- components: {
- loadingIcon,
- },
props: {
loading: {
type: Boolean,
@@ -60,7 +55,7 @@
@click="onClick"
>
<transition name="fade">
- <loading-icon
+ <gl-loading-icon
v-if="loading"
:inline="true"
:class="{
diff --git a/app/assets/javascripts/vue_shared/components/loading_icon.vue b/app/assets/javascripts/vue_shared/components/loading_icon.vue
deleted file mode 100644
index db22c5f02cd..00000000000
--- a/app/assets/javascripts/vue_shared/components/loading_icon.vue
+++ /dev/null
@@ -1,45 +0,0 @@
-<script>
- export default {
- props: {
- label: {
- type: String,
- required: false,
- default: 'Loading',
- },
-
- size: {
- type: String,
- required: false,
- default: '1',
- },
-
- inline: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
-
- computed: {
- rootElementType() {
- return this.inline ? 'span' : 'div';
- },
- cssClass() {
- return `fa-${this.size}x`;
- },
- },
- };
-</script>
-<template>
- <component
- :is="rootElementType"
- class="loading-container text-center">
- <i
- :class="cssClass"
- :aria-label="label"
- class="fa fa-spin fa-spinner"
- aria-hidden="true"
- >
- </i>
- </component>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/pagination_links.vue b/app/assets/javascripts/vue_shared/components/pagination_links.vue
new file mode 100644
index 00000000000..1f2a679c145
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/pagination_links.vue
@@ -0,0 +1,34 @@
+<script>
+import { s__ } from '../../locale';
+
+export default {
+ props: {
+ change: {
+ type: Function,
+ required: true,
+ },
+ pageInfo: {
+ type: Object,
+ required: true,
+ },
+ },
+ firstText: s__('Pagination|« First'),
+ prevText: s__('Pagination|Prev'),
+ nextText: s__('Pagination|Next'),
+ lastText: s__('Pagination|Last »'),
+};
+</script>
+
+<template>
+ <gl-pagination
+ v-bind="$attrs"
+ :change="change"
+ :page="pageInfo.page"
+ :per-page="pageInfo.perPage"
+ :total-items="pageInfo.total"
+ :first-text="$options.firstText"
+ :prev-text="$options.prevText"
+ :next-text="$options.nextText"
+ :last-text="$options.lastText"
+ />
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
index 74998a4787d..9d757b27edc 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
@@ -1,6 +1,5 @@
<script>
import datePicker from '../pikaday.vue';
- import loadingIcon from '../loading_icon.vue';
import toggleSidebar from './toggle_sidebar.vue';
import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
import { dateInWords } from '../../../lib/utils/datetime_utility';
@@ -10,7 +9,6 @@
components: {
datePicker,
toggleSidebar,
- loadingIcon,
collapsedCalendarIcon,
},
props: {
@@ -112,7 +110,7 @@
/>
<div class="title">
{{ label }}
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
:inline="true"
/>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue
index a3fc358130f..3df286de129 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue
@@ -3,7 +3,6 @@ import $ from 'jquery';
import { __ } from '~/locale';
import LabelsSelect from '~/labels_select';
import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
-import LoadingIcon from '../../loading_icon.vue';
import DropdownTitle from './dropdown_title.vue';
import DropdownValue from './dropdown_value.vue';
@@ -16,7 +15,6 @@ import DropdownCreateLabel from './dropdown_create_label.vue';
export default {
components: {
- LoadingIcon,
DropdownTitle,
DropdownValue,
DropdownValueCollapsed,
@@ -164,7 +162,7 @@ dropdown-menu-labels dropdown-menu-selectable"
<dropdown-search-input/>
<div class="dropdown-content"></div>
<div class="dropdown-loading">
- <loading-icon />
+ <gl-loading-icon />
</div>
<dropdown-footer
v-if="showCreate"
diff --git a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
index 78fde463507..cd3ee544344 100644
--- a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
+++ b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
@@ -99,8 +99,8 @@ export default {
{{ __("Not available") }}
</span>
<span
- v-tooltip
v-if="successPercent"
+ v-tooltip
:title="successTooltip"
:style="successBarStyle"
class="status-green"
@@ -109,8 +109,8 @@ export default {
{{ successPercent }}%
</span>
<span
- v-tooltip
v-if="neutralPercent"
+ v-tooltip
:title="neutralTooltip"
:style="neutralBarStyle"
class="status-neutral"
@@ -119,8 +119,8 @@ export default {
{{ neutralPercent }}%
</span>
<span
- v-tooltip
v-if="failurePercent"
+ v-tooltip
:title="failureTooltip"
:style="failureBarStyle"
class="status-red"
diff --git a/app/assets/javascripts/vue_shared/components/toggle_button.vue b/app/assets/javascripts/vue_shared/components/toggle_button.vue
index a897300b62b..5b9c51786d6 100644
--- a/app/assets/javascripts/vue_shared/components/toggle_button.vue
+++ b/app/assets/javascripts/vue_shared/components/toggle_button.vue
@@ -1,7 +1,6 @@
<script>
import { s__ } from '../../locale';
import icon from './icon.vue';
- import loadingIcon from './loading_icon.vue';
const ICON_ON = 'status_success_borderless';
const ICON_OFF = 'status_failed_borderless';
@@ -11,7 +10,6 @@
export default {
components: {
icon,
- loadingIcon,
},
model: {
@@ -78,7 +76,7 @@
class="project-feature-toggle"
@click="toggleFeature"
>
- <loadingIcon class="loading-icon" />
+ <gl-loading-icon class="loading-icon" />
<span class="toggle-icon">
<icon
:name="toggleIcon"
diff --git a/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue
index 125826da6c3..d5b58574123 100644
--- a/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue
+++ b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue
@@ -51,8 +51,8 @@ export default {
<template>
<span
- v-tooltip
v-if="showTooltip"
+ v-tooltip
:title="title"
:data-placement="placement"
class="js-show-tooltip"
diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
index 01c36fec41a..08e102e57c3 100644
--- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
+++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
@@ -94,8 +94,8 @@ export default {
:tooltip-text="avatarTooltipText"
:tooltip-placement="tooltipPlacement"
/><span
- v-tooltip
v-if="shouldShowUsername"
+ v-tooltip
:title="tooltipText"
:tooltip-placement="tooltipPlacement"
>{{ username }}</span>
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index c91f5e279ea..af73954bd2e 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -93,7 +93,6 @@ hr {
}
.form-group.row .col-form-label {
- padding-top: 0;
// Bootstrap 4 aligns labels to the left
// for horizontal forms
@include media-breakpoint-up(md) {
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index ab62ca07573..686ce0c63a4 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -147,18 +147,12 @@
}
&.btn-success,
- &.btn-new,
- &.btn-create,
- &.btn-save,
&.btn-register {
@include btn-green;
}
&.btn-inverted {
- &.btn-success,
- &.btn-new,
- &.btn-create,
- &.btn-save {
+ &.btn-success {
@include btn-outline($white-light, $green-600, $green-500, $green-500, $white-light, $green-600, $green-600, $green-700);
}
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index e9b074236cc..d5693a5d1a1 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -389,9 +389,8 @@
.btn {
text-overflow: ellipsis;
- .fa {
- width: 15px;
- line-height: $line-height-base;
+ svg {
+ margin-right: $gl-padding-8;
}
.dropdown-label-box {
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index fdc0454d837..d9d4a210f5f 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -111,6 +111,7 @@ ul.content-list {
border-color: $white-normal;
font-size: $gl-font-size;
color: $gl-text-color;
+ word-break: break-word;
&.no-description {
.title {
diff --git a/app/assets/stylesheets/framework/responsive_tables.scss b/app/assets/stylesheets/framework/responsive_tables.scss
index 764bebd82c6..fc185ccfaad 100644
--- a/app/assets/stylesheets/framework/responsive_tables.scss
+++ b/app/assets/stylesheets/framework/responsive_tables.scss
@@ -39,7 +39,7 @@
.table-section {
white-space: nowrap;
- $section-widths: 10 15 20 25 30 40 50 100;
+ $section-widths: 5 10 15 20 25 30 40 50 100;
@each $width in $section-widths {
&.section-#{$width} {
flex: 0 0 #{$width + '%'};
diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss
index 7152ef9bcfd..36ab38f1c9d 100644
--- a/app/assets/stylesheets/framework/snippets.scss
+++ b/app/assets/stylesheets/framework/snippets.scss
@@ -45,7 +45,7 @@
}
}
-.snippet-scope-menu .btn-new {
+.snippet-scope-menu .btn-success {
margin-top: 15px;
}
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
index 45df8391f9a..65f0a0d18e2 100644
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -53,83 +53,9 @@ $ide-commit-header-height: 48px;
flex: 1;
min-height: 0; // firefox fix
- .file {
- height: 32px;
- cursor: pointer;
-
- &.file-active {
- background: $theme-gray-100;
- }
-
- .ide-file-name {
- flex: 1;
- white-space: nowrap;
- text-overflow: ellipsis;
- max-width: inherit;
- line-height: 16px;
- display: inline-block;
- height: 18px;
-
- svg {
- vertical-align: middle;
- margin-right: 2px;
- }
-
- .loading-container {
- margin-right: 4px;
- display: inline-block;
- }
- }
-
- .ide-file-icon-holder {
- display: flex;
- align-items: center;
- color: $theme-gray-700;
- }
-
- .ide-file-changed-icon {
- margin-left: auto;
-
- > svg {
- display: block;
- }
- }
-
- .ide-new-btn {
- display: none;
-
- .btn {
- padding: 2px 5px;
- }
- }
-
- &:hover,
- &:focus {
- .ide-new-btn {
- display: block;
- }
- }
-
- .folder-icon {
- fill: $gl-text-color-secondary;
- }
- }
-
a {
color: $gl-text-color;
}
-
- th {
- position: sticky;
- top: 0;
- }
-}
-
-.file-name {
- display: flex;
- overflow: visible;
- align-items: center;
- width: 100%;
}
.multi-file-loading-container {
@@ -625,8 +551,7 @@ $ide-commit-header-height: 48px;
}
}
-.multi-file-commit-list-path,
-.ide-file-list .file {
+.multi-file-commit-list-path {
display: flex;
align-items: center;
margin-left: -$grid-size;
@@ -634,28 +559,14 @@ $ide-commit-header-height: 48px;
padding: $grid-size / 2 $grid-size;
border-radius: $border-radius-default;
text-align: left;
-
- &:hover,
- &:focus {
- background: $theme-gray-100;
- }
-
- &:active {
- background: $theme-gray-200;
- }
-}
-
-.multi-file-commit-list-path {
cursor: pointer;
height: $ide-commit-row-height;
padding-right: 0;
- &.is-active {
- background-color: $white-normal;
- }
-
&:hover,
&:focus {
+ background: $theme-gray-100;
+
outline: 0;
.multi-file-discard-btn {
@@ -665,6 +576,14 @@ $ide-commit-header-height: 48px;
}
}
+ &:active {
+ background: $theme-gray-200;
+ }
+
+ &.is-active {
+ background-color: $white-normal;
+ }
+
svg {
min-width: 16px;
vertical-align: middle;
@@ -1398,9 +1317,17 @@ $ide-commit-header-height: 48px;
}
}
-.ide-new-btn .dropdown.show .ide-entry-dropdown-toggle {
- color: $white-normal;
- background-color: $blue-500;
+.ide-new-btn {
+ display: none;
+
+ .btn {
+ padding: 2px 5px;
+ }
+
+ .dropdown.show .ide-entry-dropdown-toggle {
+ color: $white-normal;
+ background-color: $blue-500;
+ }
}
.ide-preview-header {
@@ -1465,3 +1392,28 @@ $ide-commit-header-height: 48px;
width: $ide-commit-row-height;
height: $ide-commit-row-height;
}
+
+.ide-file-icon-holder {
+ display: flex;
+ align-items: center;
+ color: $theme-gray-700;
+}
+
+.ide-file-changed-icon {
+ margin-left: auto;
+
+ > svg {
+ display: block;
+ }
+}
+
+.file-row:hover,
+.file-row:focus {
+ .ide-new-btn {
+ display: block;
+ }
+
+ .folder-icon {
+ fill: $gl-text-color-secondary;
+ }
+}
diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/page_bundles/xterm.scss
index 7d40c61da26..7f040ac9b96 100644
--- a/app/assets/stylesheets/pages/xterm.scss
+++ b/app/assets/stylesheets/page_bundles/xterm.scss
@@ -1,3 +1,5 @@
+@import 'framework/variables';
+
.build-page {
// color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg
// see also: https://gist.github.com/jasonm23/2868981
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 69d7de886b4..48db91a5786 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -136,15 +136,23 @@
right: 0;
bottom: 0;
left: 0;
+
+ button {
+ display: none;
+ }
}
.board-title {
padding: 0;
border-bottom: 0;
+ justify-content: center;
> span {
+ width: 100%;
+ margin-top: -12px;
display: block;
- transform: rotate(90deg) translate(35px, 10px);
+ transform: rotate(90deg) translate(35px, 0);
+ overflow: initial;
}
}
diff --git a/app/assets/stylesheets/pages/clusters.scss b/app/assets/stylesheets/pages/clusters.scss
index 0f22fe21143..71a3fd544f2 100644
--- a/app/assets/stylesheets/pages/clusters.scss
+++ b/app/assets/stylesheets/pages/clusters.scss
@@ -4,9 +4,60 @@
}
}
-.cluster-applications-table {
- // Wait for the Vue to kick-in and render the applications block
- min-height: 628px;
+.cluster-application-row {
+ background: $gray-lighter;
+
+ &.cluster-application-installed {
+ background: none;
+ }
+
+ .settings-message {
+ padding: $gl-vert-padding $gl-padding-8;
+ }
+}
+
+@media (min-width: map-get($grid-breakpoints, md)) {
+ .cluster-application-list {
+ border: 1px solid $border-color;
+ border-radius: $border-radius-default;
+ }
+
+ .cluster-application-row {
+ border-bottom: 1px solid $border-color;
+ padding: $gl-padding;
+ }
+}
+
+.cluster-application-logo {
+ border: 3px solid $white-light;
+ box-shadow: 0 0 0 1px $gray-normal;
+
+ &.avatar:hover {
+ border-color: $white-light;
+ }
+}
+
+.cluster-application-warning {
+ font-weight: bold;
+ text-align: center;
+ padding: $gl-padding;
+ border-bottom: 1px solid $white-normal;
+
+ .svg-container {
+ display: inline-block;
+ vertical-align: middle;
+ margin-right: $gl-padding-8;
+ width: 40px;
+ height: 40px;
+ }
+}
+
+.cluster-application-description {
+ flex: 1;
+}
+
+.cluster-application-disabled {
+ opacity: 0.5;
}
.clusters-dropdown-menu {
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index 394c99268be..fe792a53b44 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -106,7 +106,7 @@
&,
.dropdown,
.dropdown .dropdown-toggle,
- .btn-new {
+ .btn-success {
display: block;
}
@@ -118,7 +118,7 @@
.group-filter-form,
.dropdown .dropdown-toggle,
- .btn-new {
+ .btn-success {
width: 100%;
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index cb29b5d4313..62a9f97caa9 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -703,7 +703,8 @@
text-overflow: ellipsis;
.user-status-emoji {
- margin: 0 $gl-padding-8 0 $gl-padding-4;
+ margin-left: $gl-padding-4;
+ margin-right: 0;
}
}
@@ -725,13 +726,13 @@
display: flex;
}
- .issue-info-container {
+ .issuable-info-container {
-webkit-flex: 1;
flex: 1;
display: flex;
padding-right: $gl-padding;
- .issue-main-info {
+ .issuable-main-info {
flex: 1 auto;
margin-right: 10px;
}
@@ -767,7 +768,7 @@
margin-bottom: 10px;
min-width: 15px;
- .selected_issue {
+ .selected-issuable {
vertical-align: text-top;
}
}
@@ -799,7 +800,7 @@
}
.issuable-list li,
-.issue-info-container .controls {
+.issuable-info-container .controls {
.avatar-counter {
display: inline-block;
vertical-align: middle;
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
index 5fdb2b4a90a..99609a96976 100644
--- a/app/assets/stylesheets/pages/members.scss
+++ b/app/assets/stylesheets/pages/members.scss
@@ -4,7 +4,7 @@
}
.users-project-form {
- .btn-create {
+ .btn-success {
margin-right: 10px;
}
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 9d46c2cf4fa..97b131687d3 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -460,7 +460,7 @@
display: -webkit-flex;
display: flex;
- .issue-info-container {
+ .issuable-info-container {
-webkit-flex: 1;
flex: 1;
}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index ac7b701c2e2..4268e194ed7 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -2,7 +2,7 @@
* Note Form
*/
.comment-btn {
- @extend .btn-create;
+ @extend .btn-success;
}
.diff-file .diff-content {
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 17f34319050..caa839c32a5 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -279,6 +279,10 @@ table.u2f-registrations {
}
}
+.codes {
+ padding-top: 14px;
+}
+
.oauth-application-show {
.scope-name {
font-weight: $gl-font-weight-bold;
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 77119aea9e2..04151b1cd59 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -218,7 +218,7 @@ input[type='checkbox']:hover {
}
.btn-search,
- .btn-new {
+ .btn-success {
width: 100%;
margin-top: 5px;