diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-28 12:07:06 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-28 12:07:06 +0300 |
commit | b9e30139939a4fe3f75faf149f641477d38d0cd9 (patch) | |
tree | 79c86b8be4a1d1f000de309ee33a04b06e09167f | |
parent | c222aa089016e16374c92fa868c920a239278ef5 (diff) |
Add latest changes from gitlab-org/gitlab@master
56 files changed, 428 insertions, 216 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index a7bfedf4e61..8e19280cc02 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -36aaf4e475fdcc4ae89f14772662fa89125d7716 +e302aa4a8caf07caad38c236d610fea49a41aa2f diff --git a/app/assets/images/checkmark.png b/app/assets/images/checkmark.png Binary files differdeleted file mode 100644 index 6e47fda5cdc..00000000000 --- a/app/assets/images/checkmark.png +++ /dev/null diff --git a/app/assets/javascripts/clusters_list/components/clusters_actions.vue b/app/assets/javascripts/clusters_list/components/clusters_actions.vue index ccb973f1eb8..7fceb63fe91 100644 --- a/app/assets/javascripts/clusters_list/components/clusters_actions.vue +++ b/app/assets/javascripts/clusters_list/components/clusters_actions.vue @@ -1,13 +1,5 @@ <script> -import { - GlButton, - GlDropdown, - GlDropdownItem, - GlModalDirective, - GlTooltipDirective, - GlDropdownDivider, - GlDropdownSectionHeader, -} from '@gitlab/ui'; +import { GlDropdown, GlDropdownItem, GlModalDirective, GlTooltip } from '@gitlab/ui'; import { INSTALL_AGENT_MODAL_ID, CLUSTERS_ACTIONS } from '../constants'; @@ -15,37 +7,40 @@ export default { i18n: CLUSTERS_ACTIONS, INSTALL_AGENT_MODAL_ID, components: { - GlButton, GlDropdown, GlDropdownItem, - GlDropdownDivider, - GlDropdownSectionHeader, + GlTooltip, }, directives: { GlModalDirective, - GlTooltip: GlTooltipDirective, }, inject: [ 'newClusterPath', 'addClusterPath', + 'newClusterDocsPath', 'canAddCluster', 'displayClusterAgents', 'certificateBasedClustersEnabled', ], computed: { - tooltip() { - const { connectWithAgent, connectExistingCluster, dropdownDisabledHint } = this.$options.i18n; + shouldTriggerModal() { + return this.canAddCluster && this.displayClusterAgents; + }, + defaultActionText() { + const { connectCluster, connectWithAgent, connectClusterDeprecated } = this.$options.i18n; - if (!this.canAddCluster) { - return dropdownDisabledHint; - } else if (this.displayClusterAgents) { - return connectWithAgent; + if (!this.displayClusterAgents) { + return connectClusterDeprecated; + } else if (!this.certificateBasedClustersEnabled) { + return connectCluster; } - - return connectExistingCluster; + return connectWithAgent; }, - shouldTriggerModal() { - return this.canAddCluster && this.displayClusterAgents; + defaultActionUrl() { + if (this.displayClusterAgents) { + return null; + } + return this.addClusterPath; }, }, }; @@ -53,46 +48,50 @@ export default { <template> <div class="nav-controls gl-ml-auto"> + <gl-tooltip + v-if="!canAddCluster" + :target="() => $refs.dropdown.$el" + :title="$options.i18n.dropdownDisabledHint" + /> + <gl-dropdown - v-if="certificateBasedClustersEnabled" ref="dropdown" v-gl-modal-directive="shouldTriggerModal && $options.INSTALL_AGENT_MODAL_ID" - v-gl-tooltip="tooltip" category="primary" variant="confirm" - :text="$options.i18n.actionsButton" + :text="defaultActionText" :disabled="!canAddCluster" - :split="displayClusterAgents" + :split-href="defaultActionUrl" + split right > - <template v-if="displayClusterAgents"> - <gl-dropdown-section-header>{{ $options.i18n.agent }}</gl-dropdown-section-header> - <gl-dropdown-item - v-gl-modal-directive="$options.INSTALL_AGENT_MODAL_ID" - data-testid="connect-new-agent-link" - > - {{ $options.i18n.connectWithAgent }} + <gl-dropdown-item + v-if="displayClusterAgents" + :href="newClusterDocsPath" + data-testid="create-cluster-link" + @click.stop + > + {{ $options.i18n.createCluster }} + </gl-dropdown-item> + + <template v-if="displayClusterAgents && certificateBasedClustersEnabled"> + <gl-dropdown-item :href="newClusterPath" data-testid="new-cluster-link" @click.stop> + {{ $options.i18n.createClusterCertificate }} + </gl-dropdown-item> + + <gl-dropdown-item :href="addClusterPath" data-testid="connect-cluster-link" @click.stop> + {{ $options.i18n.connectClusterCertificate }} </gl-dropdown-item> - <gl-dropdown-divider /> - <gl-dropdown-section-header>{{ $options.i18n.certificate }}</gl-dropdown-section-header> </template> - <gl-dropdown-item :href="newClusterPath" data-testid="new-cluster-link" @click.stop> - {{ $options.i18n.createNewCluster }} - </gl-dropdown-item> - <gl-dropdown-item :href="addClusterPath" data-testid="connect-cluster-link" @click.stop> - {{ $options.i18n.connectExistingCluster }} + <gl-dropdown-item + v-if="certificateBasedClustersEnabled && !displayClusterAgents" + :href="newClusterPath" + data-testid="new-cluster-link" + @click.stop + > + {{ $options.i18n.createClusterDeprecated }} </gl-dropdown-item> </gl-dropdown> - <gl-button - v-else - v-gl-modal-directive="$options.INSTALL_AGENT_MODAL_ID" - v-gl-tooltip="tooltip" - :disabled="!canAddCluster" - category="primary" - variant="confirm" - > - {{ $options.i18n.connectWithAgent }} - </gl-button> </div> </template> diff --git a/app/assets/javascripts/clusters_list/constants.js b/app/assets/javascripts/clusters_list/constants.js index c914ee518b2..7bf1ed607b9 100644 --- a/app/assets/javascripts/clusters_list/constants.js +++ b/app/assets/javascripts/clusters_list/constants.js @@ -252,12 +252,13 @@ export const CERTIFICATE_TAB = { export const CLUSTERS_TABS = [ALL_TAB, AGENT_TAB, CERTIFICATE_TAB]; export const CLUSTERS_ACTIONS = { - actionsButton: s__('ClusterAgents|Actions'), - createNewCluster: s__('ClusterAgents|Create a new cluster'), - connectWithAgent: s__('ClusterAgents|Connect with an agent'), - connectExistingCluster: s__('ClusterAgents|Connect with a certificate'), - agent: s__('ClusterAgents|Agent'), - certificate: s__('ClusterAgents|Certificate'), + connectCluster: s__('ClusterAgents|Connect a cluster'), + connectWithAgent: s__('ClusterAgents|Connect a cluster (agent)'), + connectClusterDeprecated: s__('ClusterAgents|Connect a cluster (deprecated)'), + createClusterDeprecated: s__('ClusterAgents|Create a cluster (deprecated)'), + createCluster: s__('ClusterAgents|Create a cluster'), + createClusterCertificate: s__('ClusterAgents|Create a cluster (certificate - deprecated)'), + connectClusterCertificate: s__('ClusterAgents|Connect a cluster (certificate - deprecated)'), dropdownDisabledHint: s__( 'ClusterAgents|Requires a Maintainer or greater role to perform these actions', ), diff --git a/app/assets/javascripts/clusters_list/index.js b/app/assets/javascripts/clusters_list/index.js index 27eebc9d891..f6dfb96ffd9 100644 --- a/app/assets/javascripts/clusters_list/index.js +++ b/app/assets/javascripts/clusters_list/index.js @@ -25,6 +25,7 @@ export default () => { kasAddress, newClusterPath, addClusterPath, + newClusterDocsPath, emptyStateHelpText, clustersEmptyStateImage, canAddCluster, @@ -43,6 +44,7 @@ export default () => { kasAddress, newClusterPath, addClusterPath, + newClusterDocsPath, emptyStateHelpText, clustersEmptyStateImage, canAddCluster: parseBoolean(canAddCluster), diff --git a/app/assets/javascripts/content_editor/extensions/diagram.js b/app/assets/javascripts/content_editor/extensions/diagram.js new file mode 100644 index 00000000000..d192b815092 --- /dev/null +++ b/app/assets/javascripts/content_editor/extensions/diagram.js @@ -0,0 +1,56 @@ +import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants'; +import CodeBlockHighlight from './code_block_highlight'; + +export default CodeBlockHighlight.extend({ + name: 'diagram', + + isolating: true, + + addAttributes() { + return { + language: { + default: null, + parseHTML: (element) => { + return element.dataset.diagram; + }, + }, + }; + }, + + parseHTML() { + return [ + { + priority: PARSE_HTML_PRIORITY_HIGHEST, + tag: '[data-diagram]', + getContent(element, schema) { + const source = atob(element.dataset.diagramSrc.replace('data:text/plain;base64,', '')); + const node = schema.node('paragraph', {}, [schema.text(source)]); + return node.content; + }, + }, + ]; + }, + + renderHTML({ HTMLAttributes: { language, ...HTMLAttributes } }) { + return [ + 'div', + [ + 'pre', + { + language, + class: `content-editor-code-block code highlight`, + ...HTMLAttributes, + }, + ['code', {}, 0], + ], + ]; + }, + + addCommands() { + return {}; + }, + + addInputRules() { + return []; + }, +}); diff --git a/app/assets/javascripts/content_editor/services/create_content_editor.js b/app/assets/javascripts/content_editor/services/create_content_editor.js index 5b637eee176..7ed62ee17fb 100644 --- a/app/assets/javascripts/content_editor/services/create_content_editor.js +++ b/app/assets/javascripts/content_editor/services/create_content_editor.js @@ -15,6 +15,7 @@ import DescriptionItem from '../extensions/description_item'; import DescriptionList from '../extensions/description_list'; import Details from '../extensions/details'; import DetailsContent from '../extensions/details_content'; +import Diagram from '../extensions/diagram'; import Division from '../extensions/division'; import Document from '../extensions/document'; import Dropcursor from '../extensions/dropcursor'; @@ -100,6 +101,7 @@ export const createContentEditor = ({ Details, DetailsContent, Document, + Diagram, Division, Dropcursor, Emoji, diff --git a/app/assets/javascripts/content_editor/services/markdown_serializer.js b/app/assets/javascripts/content_editor/services/markdown_serializer.js index eaaf69c3068..c2be7bc9195 100644 --- a/app/assets/javascripts/content_editor/services/markdown_serializer.js +++ b/app/assets/javascripts/content_editor/services/markdown_serializer.js @@ -13,6 +13,7 @@ import DescriptionList from '../extensions/description_list'; import Details from '../extensions/details'; import DetailsContent from '../extensions/details_content'; import Division from '../extensions/division'; +import Diagram from '../extensions/diagram'; import Emoji from '../extensions/emoji'; import Figure from '../extensions/figure'; import FigureCaption from '../extensions/figure_caption'; @@ -48,6 +49,7 @@ import Video from '../extensions/video'; import WordBreak from '../extensions/word_break'; import { isPlainURL, + renderCodeBlock, renderHardBreak, renderTable, renderTableCell, @@ -130,13 +132,8 @@ const defaultSerializerConfig = { } }, [BulletList.name]: defaultMarkdownSerializer.nodes.bullet_list, - [CodeBlockHighlight.name]: (state, node) => { - state.write(`\`\`\`${node.attrs.language || ''}\n`); - state.text(node.textContent, false); - state.ensureNewLine(); - state.write('```'); - state.closeBlock(node); - }, + [CodeBlockHighlight.name]: renderCodeBlock, + [Diagram.name]: renderCodeBlock, [Division.name]: (state, node) => { if (node.attrs.className?.includes('js-markdown-code')) { state.renderInline(node); diff --git a/app/assets/javascripts/content_editor/services/serialization_helpers.js b/app/assets/javascripts/content_editor/services/serialization_helpers.js index 5fdd294aa96..3e48434c6f9 100644 --- a/app/assets/javascripts/content_editor/services/serialization_helpers.js +++ b/app/assets/javascripts/content_editor/services/serialization_helpers.js @@ -341,3 +341,11 @@ export function renderImage(state, node) { export function renderPlayable(state, node) { renderImage(state, node); } + +export function renderCodeBlock(state, node) { + state.write(`\`\`\`${node.attrs.language || ''}\n`); + state.text(node.textContent, false); + state.ensureNewLine(); + state.write('```'); + state.closeBlock(node); +} diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index b3cb93e74f2..4bfa39f2a20 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -127,7 +127,7 @@ function deferredInitialisation() { // In case the user started searching before we bootstrapped, let's pass the search along. const initialSearchValue = searchInputBox.value; await initHeaderSearchApp(initialSearchValue); - searchInputBox.focus(); + document.querySelector('#search').focus(); }) .catch(() => {}); } else { diff --git a/app/assets/javascripts/milestones/components/milestone_combobox.vue b/app/assets/javascripts/milestones/components/milestone_combobox.vue index a840e696386..d7ffdfd7c5f 100644 --- a/app/assets/javascripts/milestones/components/milestone_combobox.vue +++ b/app/assets/javascripts/milestones/components/milestone_combobox.vue @@ -192,10 +192,12 @@ export default { @keydown.enter.prevent="onSearchBoxEnter" /> - <gl-dropdown-item @click="selectNoMilestone()"> - <span :class="{ 'gl-pl-6': true, 'selected-item': selectedMilestones.length === 0 }"> - {{ $options.translations.noMilestone }} - </span> + <gl-dropdown-item + :is-checked="selectedMilestones.length === 0" + is-check-item + @click="selectNoMilestone()" + > + {{ $options.translations.noMilestone }} </gl-dropdown-item> <gl-dropdown-divider /> @@ -241,9 +243,10 @@ export default { v-for="(item, idx) in extraLinks" :key="idx" :href="item.url" + :is-check-item="true" data-testid="milestone-combobox-extra-links" > - <span class="gl-pl-6">{{ item.text }}</span> + {{ item.text }} </gl-dropdown-item> </gl-dropdown> </template> diff --git a/app/assets/javascripts/milestones/components/milestone_results_section.vue b/app/assets/javascripts/milestones/components/milestone_results_section.vue index b866977b974..e3c691b14c7 100644 --- a/app/assets/javascripts/milestones/components/milestone_results_section.vue +++ b/app/assets/javascripts/milestones/components/milestone_results_section.vue @@ -77,10 +77,14 @@ export default { </div> </template> <template v-else> - <gl-dropdown-item v-for="{ title } in items" :key="title" @click="$emit('selected', title)"> - <span class="gl-pl-6" :class="{ 'selected-item': isSelectedMilestone(title) }"> - {{ title }} - </span> + <gl-dropdown-item + v-for="{ title } in items" + :key="title" + :is-checked="isSelectedMilestone(title)" + is-check-item + @click="$emit('selected', title)" + > + {{ title }} </gl-dropdown-item> <gl-dropdown-divider /> </template> diff --git a/app/assets/stylesheets/components/milestone_combobox.scss b/app/assets/stylesheets/components/milestone_combobox.scss index 5d1709c22ec..94d295c324b 100644 --- a/app/assets/stylesheets/components/milestone_combobox.scss +++ b/app/assets/stylesheets/components/milestone_combobox.scss @@ -1,13 +1,4 @@ .milestone-combobox { - .selected-item { - /* stylelint-disable-next-line function-url-quotes */ - background: url(asset_path('checkmark.png')) no-repeat 0 2px; - } - - .dropdown-item-space { - padding: 8px 12px; - } - .dropdown-menu.show { overflow: hidden; } diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb index d9179129983..21868cec313 100644 --- a/app/controllers/clusters/clusters_controller.rb +++ b/app/controllers/clusters/clusters_controller.rb @@ -14,7 +14,7 @@ class Clusters::ClustersController < Clusters::BaseController before_action :authorize_create_cluster!, only: [:new, :connect, :authorize_aws_role] before_action :authorize_update_cluster!, only: [:update] before_action :update_applications_status, only: [:cluster_status] - before_action :ensure_feature_enabled!, except: :index + before_action :ensure_feature_enabled!, except: [:index, :new_cluster_docs] helper_method :token_in_session diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb index 959dac1254e..fe057fb3412 100644 --- a/app/helpers/clusters_helper.rb +++ b/app/helpers/clusters_helper.rb @@ -19,6 +19,7 @@ module ClustersHelper empty_state_help_text: clusterable.empty_state_help_text, new_cluster_path: clusterable.new_path, add_cluster_path: clusterable.connect_path, + new_cluster_docs_path: clusterable.new_cluster_docs_path, can_add_cluster: clusterable.can_add_cluster?.to_s, can_admin_cluster: clusterable.can_admin_cluster?.to_s, display_cluster_agents: display_cluster_agents?(clusterable).to_s, diff --git a/app/presenters/clusterable_presenter.rb b/app/presenters/clusterable_presenter.rb index 82152ce42ae..c2ed40d8b0c 100644 --- a/app/presenters/clusterable_presenter.rb +++ b/app/presenters/clusterable_presenter.rb @@ -36,6 +36,10 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated polymorphic_path([clusterable, :clusters], action: :connect) end + def new_cluster_docs_path + polymorphic_path([clusterable, :clusters], action: :new_cluster_docs) + end + def authorize_aws_role_path polymorphic_path([clusterable, :clusters], action: :authorize_aws_role) end diff --git a/app/presenters/instance_clusterable_presenter.rb b/app/presenters/instance_clusterable_presenter.rb index 9e4a3b403ea..001c9cbb4e9 100644 --- a/app/presenters/instance_clusterable_presenter.rb +++ b/app/presenters/instance_clusterable_presenter.rb @@ -43,6 +43,11 @@ class InstanceClusterablePresenter < ClusterablePresenter connect_admin_clusters_path end + override :new_cluster_docs_path + def new_cluster_docs_path + nil + end + override :create_user_clusters_path def create_user_clusters_path create_user_admin_clusters_path diff --git a/app/views/clusters/clusters/_sidebar.html.haml b/app/views/clusters/clusters/_sidebar.html.haml index bda774ee780..045c03df4fa 100644 --- a/app/views/clusters/clusters/_sidebar.html.haml +++ b/app/views/clusters/clusters/_sidebar.html.haml @@ -1,8 +1,14 @@ +- is_connect_page = local_assigns.fetch(:is_connect_page, false) +- docs_mode = local_assigns.fetch(:docs_mode, false) +- title = is_connect_page ? s_('ClusterIntegration|Connect a Kubernetes cluster') : s_('ClusterIntegration|Create a Kubernetes cluster') + %h3 - = s_('ClusterIntegration|Connect a Kubernetes cluster') + = title %p = clusterable.sidebar_text -%p - = clusterable.learn_more_link -= render 'clusters/clusters/multiple_clusters_message' +- if !docs_mode + %p + = clusterable.learn_more_link + + = render 'clusters/clusters/multiple_clusters_message' diff --git a/app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml b/app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml index 826dc749dad..807f98b7b0a 100644 --- a/app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml +++ b/app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml @@ -1,12 +1,15 @@ - provider = local_assigns.fetch(:provider) - is_current_provider = provider == params[:provider] - logo_path = local_assigns.fetch(:logo_path) +- help_path = local_assigns.fetch(:help_path) - label = local_assigns.fetch(:label) - last = local_assigns.fetch(:last, false) -- classes = ["btn btn-confirm gl-button btn-confirm-secondary gl-flex-direction-column gl-w-half js-create-#{provider}-cluster-button"] -- conditional_classes = [('gl-mr-5' unless last), ('active' if is_current_provider)] +- docs_mode = local_assigns.fetch(:docs_mode, false) +- classes = ["btn btn-confirm gl-button btn-confirm-secondary gl-flex-direction-column gl-w-half"] +- conditional_classes = [("gl-mr-5" unless last), ("active" if is_current_provider && !docs_mode), ("js-create-#{provider}-cluster-button" if !docs_mode)] +- link = docs_mode ? help_path : clusterable.new_path(provider: provider) -= link_to clusterable.new_path(provider: provider), class: classes + conditional_classes do - .svg-content.gl-p-3= image_tag logo_path, alt: label, class: 'gl-w-64 gl-h-64' += link_to link, class: classes + conditional_classes do + .svg-content.gl-p-3= image_tag logo_path, alt: label, class: "gl-w-64 gl-h-64" %span = label diff --git a/app/views/clusters/clusters/cloud_providers/_cloud_provider_selector.html.haml b/app/views/clusters/clusters/cloud_providers/_cloud_provider_selector.html.haml index 321fb854e0d..69250141816 100644 --- a/app/views/clusters/clusters/cloud_providers/_cloud_provider_selector.html.haml +++ b/app/views/clusters/clusters/cloud_providers/_cloud_provider_selector.html.haml @@ -1,11 +1,15 @@ - gke_label = s_('ClusterIntegration|Google GKE') - eks_label = s_('ClusterIntegration|Amazon EKS') - create_cluster_label = s_('ClusterIntegration|Where do you want to create a cluster?') +- eks_help_path = help_page_path('user/infrastructure/clusters/connect/new_eks_cluster') +- gke_help_path = help_page_path('user/infrastructure/clusters/connect/new_gke_cluster') +- docs_mode = local_assigns.fetch(:docs_mode, false) + .gl-p-5 %h4.gl-mb-5 = create_cluster_label .gl-display-flex = render partial: 'clusters/clusters/cloud_providers/cloud_provider_button', - locals: { provider: 'aws', label: eks_label, logo_path: 'illustrations/logos/amazon_eks.svg' } + locals: { provider: 'aws', label: eks_label, logo_path: 'illustrations/logos/amazon_eks.svg', help_path: eks_help_path, docs_mode: docs_mode } = render partial: 'clusters/clusters/cloud_providers/cloud_provider_button', - locals: { provider: 'gcp', label: gke_label, logo_path: 'illustrations/logos/google_gke.svg', last: true } + locals: { provider: 'gcp', label: gke_label, logo_path: 'illustrations/logos/google_gke.svg', help_path: gke_help_path, docs_mode: docs_mode, last: true } diff --git a/app/views/clusters/clusters/connect.html.haml b/app/views/clusters/clusters/connect.html.haml index 1043f78bd3c..2976c7e2035 100644 --- a/app/views/clusters/clusters/connect.html.haml +++ b/app/views/clusters/clusters/connect.html.haml @@ -3,9 +3,9 @@ - breadcrumb_title _('Connect a cluster') - page_title _('Connect a Kubernetes Cluster') -.row.gl-mt-3 - .col-md-3 - = render 'sidebar' - .col-md-9 +.gl-md-display-flex.gl-mt-3 + .gl-w-quarter.gl-xs-w-full.gl-flex-shrink-0.gl-md-mr-5 + = render 'sidebar', is_connect_page: true + .gl-w-full #js-cluster-new{ data: js_cluster_new } = render 'clusters/clusters/user/form' diff --git a/app/views/clusters/clusters/new.html.haml b/app/views/clusters/clusters/new.html.haml index a184f412565..095410ae11d 100644 --- a/app/views/clusters/clusters/new.html.haml +++ b/app/views/clusters/clusters/new.html.haml @@ -6,10 +6,10 @@ = render_gcp_signup_offer -.row.gl-mt-3 - .col-md-3 - = render 'sidebar' - .col-md-9 +.gl-md-display-flex.gl-mt-3 + .gl-w-quarter.gl-xs-w-full.gl-flex-shrink-0.gl-md-mr-5 + = render 'sidebar', is_connect_page: false + .gl-w-full = render 'clusters/clusters/cloud_providers/cloud_provider_selector' - if ['aws', 'gcp'].include?(provider) diff --git a/app/views/clusters/clusters/new_cluster_docs.html.haml b/app/views/clusters/clusters/new_cluster_docs.html.haml new file mode 100644 index 00000000000..9b99aef0e72 --- /dev/null +++ b/app/views/clusters/clusters/new_cluster_docs.html.haml @@ -0,0 +1,13 @@ +- @content_class = 'limit-container-width' unless fluid_layout +- add_to_breadcrumbs _('Kubernetes Clusters'), clusterable.index_path +- breadcrumb_title _('Create a cluster') +- page_title _('Create a Kubernetes cluster') +- docs_mode = true + += render_gcp_signup_offer + +.gl-md-display-flex.gl-mt-3 + .gl-w-quarter.gl-xs-w-full.gl-flex-shrink-0.gl-md-mr-5 + = render 'sidebar', docs_mode: docs_mode, is_connect_page: false + .gl-w-full + = render 'clusters/clusters/cloud_providers/cloud_provider_selector', docs_mode: docs_mode diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 512a4185bee..c15a5e54a42 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -40,7 +40,7 @@ - search_menu_item = top_nav_search_menu_item_attrs %li.nav-item.header-search-new.d-none.d-lg-block.m-auto - unless current_controller?(:search) - - if Feature.enabled?(:new_header_search) + - if Feature.enabled?(:new_header_search, default_enabled: :yaml) = render 'layouts/header_search' - else = render 'layouts/search' diff --git a/config/feature_flags/development/new_header_search.yml b/config/feature_flags/development/new_header_search.yml index 086aee4c4b0..58bd1781dae 100644 --- a/config/feature_flags/development/new_header_search.yml +++ b/config/feature_flags/development/new_header_search.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339348 milestone: '14.3' type: development group: group::global search -default_enabled: false +default_enabled: true diff --git a/config/routes.rb b/config/routes.rb index 92fc9025e98..c2114b3478d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -237,6 +237,7 @@ Rails.application.routes.draw do resources :clusters, only: [:index, :new, :show, :update, :destroy] do collection do get :connect + get :new_cluster_docs post :create_user post :create_gcp post :create_aws diff --git a/lib/banzai/filter/base_sanitization_filter.rb b/lib/banzai/filter/base_sanitization_filter.rb index 4e350a59fa0..3b00d1a9824 100644 --- a/lib/banzai/filter/base_sanitization_filter.rb +++ b/lib/banzai/filter/base_sanitization_filter.rb @@ -39,6 +39,9 @@ module Banzai allowlist[:attributes][:all].delete('name') allowlist[:attributes]['a'].push('name') + allowlist[:attributes]['img'].push('data-diagram') + allowlist[:attributes]['img'].push('data-diagram-src') + # Allow any protocol in `a` elements # and then remove links with unsafe protocols allowlist[:protocols].delete('a') diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb index 44acc7805b4..60881b5f511 100644 --- a/lib/banzai/filter/image_link_filter.rb +++ b/lib/banzai/filter/image_link_filter.rb @@ -27,6 +27,13 @@ module Banzai # make sure the original non-proxied src carries over to the link link['data-canonical-src'] = img['data-canonical-src'] if img['data-canonical-src'] + if img['data-diagram'] && img['data-diagram-src'] + link['data-diagram'] = img['data-diagram'] + link['data-diagram-src'] = img['data-diagram-src'] + img.remove_attribute('data-diagram') + img.remove_attribute('data-diagram-src') + end + link.children = if link_replaces_image img['alt'] || img['data-src'] || img['src'] else diff --git a/lib/banzai/filter/kroki_filter.rb b/lib/banzai/filter/kroki_filter.rb index 3803302c324..75ccf998f60 100644 --- a/lib/banzai/filter/kroki_filter.rb +++ b/lib/banzai/filter/kroki_filter.rb @@ -22,7 +22,14 @@ module Banzai doc.xpath(xpath).each do |node| diagram_type = node.parent['lang'] img_tag = Nokogiri::HTML::DocumentFragment.parse(%(<img src="#{create_image_src(diagram_type, diagram_format, node.content)}"/>)) - node.parent.replace(img_tag) + img_tag = img_tag.children.first + + unless img_tag.nil? + img_tag.set_attribute('data-diagram', node.parent['lang']) + img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}") + + node.parent.replace(img_tag) + end end doc diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb index 68a99702d6f..cbcd547120d 100644 --- a/lib/banzai/filter/plantuml_filter.rb +++ b/lib/banzai/filter/plantuml_filter.rb @@ -15,8 +15,14 @@ module Banzai doc.xpath(lang_tag).each do |node| img_tag = Nokogiri::HTML::DocumentFragment.parse( - Asciidoctor::PlantUml::Processor.plantuml_content(node.content, {})) - node.parent.replace(img_tag) + Asciidoctor::PlantUml::Processor.plantuml_content(node.content, {})).css('img').first + + unless img_tag.nil? + img_tag.set_attribute('data-diagram', 'plantuml') + img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}") + + node.parent.replace(img_tag) + end end doc diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index df8151b3296..f4964f779ca 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -12,6 +12,7 @@ module Banzai def self.filters @filters ||= FilterArray[ Filter::PlantumlFilter, + Filter::KrokiFilter, # Must always be before the SanitizationFilter to prevent XSS attacks Filter::SpacedLinkFilter, Filter::SanitizationFilter, @@ -19,7 +20,6 @@ module Banzai Filter::SyntaxHighlightFilter, Filter::MathFilter, Filter::ColorFilter, - Filter::KrokiFilter, Filter::MermaidFilter, Filter::VideoLinkFilter, Filter::AudioLinkFilter, diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 51decc88b24..3d7f53b60fb 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -7822,9 +7822,6 @@ msgstr "" msgid "ClusterAgents|Access tokens" msgstr "" -msgid "ClusterAgents|Actions" -msgstr "" - msgid "ClusterAgents|Add an agent configuration file to %{linkStart}this repository%{linkEnd} and select it, or create a new one to register with GitLab:" msgstr "" @@ -7879,16 +7876,22 @@ msgstr "" msgid "ClusterAgents|Configuration" msgstr "" -msgid "ClusterAgents|Connect a cluster through an agent" +msgid "ClusterAgents|Connect a cluster" msgstr "" -msgid "ClusterAgents|Connect existing cluster" +msgid "ClusterAgents|Connect a cluster (agent)" +msgstr "" + +msgid "ClusterAgents|Connect a cluster (certificate - deprecated)" +msgstr "" + +msgid "ClusterAgents|Connect a cluster (deprecated)" msgstr "" -msgid "ClusterAgents|Connect with a certificate" +msgid "ClusterAgents|Connect a cluster through an agent" msgstr "" -msgid "ClusterAgents|Connect with an agent" +msgid "ClusterAgents|Connect existing cluster" msgstr "" msgid "ClusterAgents|Connect with the GitLab Agent" @@ -7906,7 +7909,13 @@ msgstr "" msgid "ClusterAgents|Copy token" msgstr "" -msgid "ClusterAgents|Create a new cluster" +msgid "ClusterAgents|Create a cluster" +msgstr "" + +msgid "ClusterAgents|Create a cluster (certificate - deprecated)" +msgstr "" + +msgid "ClusterAgents|Create a cluster (deprecated)" msgstr "" msgid "ClusterAgents|Create agent access token" @@ -8289,6 +8298,9 @@ msgstr "" msgid "ClusterIntegration|Create Kubernetes cluster" msgstr "" +msgid "ClusterIntegration|Create a Kubernetes cluster" +msgstr "" + msgid "ClusterIntegration|Creating Kubernetes cluster" msgstr "" diff --git a/scripts/frontend/startup_css/constants.js b/scripts/frontend/startup_css/constants.js index 83f43143e1b..10d60657e09 100644 --- a/scripts/frontend/startup_css/constants.js +++ b/scripts/frontend/startup_css/constants.js @@ -50,7 +50,7 @@ const createMainOutput = ({ outFile, cssKeys, type }) => ({ htmlPaths: [ path.join(FIXTURES_ROOT, `startup_css/project-${type}.html`), path.join(FIXTURES_ROOT, `startup_css/project-${type}-signed-out.html`), - path.join(FIXTURES_ROOT, `startup_css/project-${type}-search-ff-on.html`), + path.join(FIXTURES_ROOT, `startup_css/project-${type}-search-ff-off.html`), ], cssKeys, purgeOptions: { diff --git a/spec/features/admin/clusters/eks_spec.rb b/spec/features/admin/clusters/eks_spec.rb index 71d2bba73b1..4667f9c20a1 100644 --- a/spec/features/admin/clusters/eks_spec.rb +++ b/spec/features/admin/clusters/eks_spec.rb @@ -15,8 +15,8 @@ RSpec.describe 'Instance-level AWS EKS Cluster', :js do before do visit admin_clusters_path - click_button 'Actions' - click_link 'Create a new cluster' + click_button(class: 'dropdown-toggle-split') + click_link 'Create a cluster (deprecated)' end context 'when user creates a cluster on AWS EKS' do diff --git a/spec/features/clusters/create_agent_spec.rb b/spec/features/clusters/create_agent_spec.rb index e03126d344e..db8de24e105 100644 --- a/spec/features/clusters/create_agent_spec.rb +++ b/spec/features/clusters/create_agent_spec.rb @@ -25,7 +25,7 @@ RSpec.describe 'Cluster agent registration', :js do end it 'allows the user to select an agent to install, and displays the resulting agent token' do - click_button('Actions') + click_button('Connect a cluster') expect(page).to have_content('Register') click_button('Select an agent') diff --git a/spec/features/error_tracking/user_searches_sentry_errors_spec.rb b/spec/features/error_tracking/user_searches_sentry_errors_spec.rb index 89bf79ebb81..40718deed75 100644 --- a/spec/features/error_tracking/user_searches_sentry_errors_spec.rb +++ b/spec/features/error_tracking/user_searches_sentry_errors_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'When a user searches for Sentry errors', :js, :use_clean_rails_m expect(results.count).to be(3) end - find('.gl-form-input').set('NotFound').native.send_keys(:return) + find('.filtered-search-input-container .gl-form-input').set('NotFound').native.send_keys(:return) page.within(find('.gl-table')) do results = page.all('.table-row') diff --git a/spec/features/groups/clusters/eks_spec.rb b/spec/features/groups/clusters/eks_spec.rb index 3cca2d0919c..0e64a2faf3e 100644 --- a/spec/features/groups/clusters/eks_spec.rb +++ b/spec/features/groups/clusters/eks_spec.rb @@ -20,8 +20,8 @@ RSpec.describe 'Group AWS EKS Cluster', :js do before do visit group_clusters_path(group) - click_button 'Actions' - click_link 'Create a new cluster' + click_button(class: 'dropdown-toggle-split') + click_link 'Create a cluster (deprecated)' end context 'when user creates a cluster on AWS EKS' do diff --git a/spec/features/projects/clusters/eks_spec.rb b/spec/features/projects/clusters/eks_spec.rb index 0dd6effe551..7e599ff1198 100644 --- a/spec/features/projects/clusters/eks_spec.rb +++ b/spec/features/projects/clusters/eks_spec.rb @@ -20,7 +20,7 @@ RSpec.describe 'AWS EKS Cluster', :js do visit project_clusters_path(project) click_button(class: 'dropdown-toggle-split') - click_link 'Create a new cluster' + click_link 'Create a cluster (certificate - deprecated)' end context 'when user creates a cluster on AWS EKS' do diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index 90d7e2d02e9..28e30a3965d 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -135,7 +135,7 @@ RSpec.describe 'Gcp Cluster', :js do visit project_clusters_path(project) click_button(class: 'dropdown-toggle-split') - click_link 'Connect with a certificate' + click_link 'Connect a cluster (certificate - deprecated)' end it 'user sees the "Environment scope" field' do @@ -220,6 +220,6 @@ RSpec.describe 'Gcp Cluster', :js do def visit_create_cluster_page click_button(class: 'dropdown-toggle-split') - click_link 'Create a new cluster' + click_link 'Create a cluster (certificate - deprecated)' end end diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb index b9a544144c3..e85788217d6 100644 --- a/spec/features/projects/clusters_spec.rb +++ b/spec/features/projects/clusters_spec.rb @@ -222,11 +222,11 @@ RSpec.describe 'Clusters', :js do visit project_clusters_path(project) click_button(class: 'dropdown-toggle-split') - click_link 'Create a new cluster' + click_link 'Create a cluster (certificate - deprecated)' end def visit_connect_cluster_page click_button(class: 'dropdown-toggle-split') - click_link 'Connect with a certificate' + click_link 'Connect a cluster (certificate - deprecated)' end end diff --git a/spec/features/search/user_searches_for_projects_spec.rb b/spec/features/search/user_searches_for_projects_spec.rb index c38ad077cd0..562da56275c 100644 --- a/spec/features/search/user_searches_for_projects_spec.rb +++ b/spec/features/search/user_searches_for_projects_spec.rb @@ -8,6 +8,8 @@ RSpec.describe 'User searches for projects', :js do context 'when signed out' do context 'when block_anonymous_global_searches is disabled' do before do + allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit).and_return(1000) + allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit_unauthenticated).and_return(1000) stub_feature_flags(block_anonymous_global_searches: false) end diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb index 8736f16b991..7350a54e8df 100644 --- a/spec/features/search/user_uses_header_search_field_spec.rb +++ b/spec/features/search/user_uses_header_search_field_spec.rb @@ -17,12 +17,15 @@ RSpec.describe 'User uses header search field', :js do end before do + allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit).and_return(1000) + allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit_unauthenticated).and_return(1000) sign_in(user) end shared_examples 'search field examples' do before do visit(url) + wait_for_all_requests end it 'starts searching by pressing the enter key' do @@ -37,7 +40,6 @@ RSpec.describe 'User uses header search field', :js do before do find('#search') find('body').native.send_keys('s') - wait_for_all_requests end @@ -49,6 +51,7 @@ RSpec.describe 'User uses header search field', :js do context 'when clicking the search field' do before do page.find('#search').click + wait_for_all_requests end it 'shows category search dropdown', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/250285' do @@ -59,7 +62,7 @@ RSpec.describe 'User uses header search field', :js do let!(:issue) { create(:issue, project: project, author: user, assignees: [user]) } it 'shows assigned issues' do - find('.search-input-container .dropdown-menu').click_link('Issues assigned to me') + find('[data-testid="header-search-dropdown-menu"]').click_link('Issues assigned to me') expect(page).to have_selector('.issues-list .issue') expect_tokens([assignee_token(user.name)]) @@ -67,7 +70,7 @@ RSpec.describe 'User uses header search field', :js do end it 'shows created issues' do - find('.search-input-container .dropdown-menu').click_link("Issues I've created") + find('[data-testid="header-search-dropdown-menu"]').click_link("Issues I've created") expect(page).to have_selector('.issues-list .issue') expect_tokens([author_token(user.name)]) @@ -79,7 +82,7 @@ RSpec.describe 'User uses header search field', :js do let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignees: [user]) } it 'shows assigned merge requests' do - find('.search-input-container .dropdown-menu').click_link('Merge requests assigned to me') + find('[data-testid="header-search-dropdown-menu"]').click_link('Merge requests assigned to me') expect(page).to have_selector('.mr-list .merge-request') expect_tokens([assignee_token(user.name)]) @@ -87,7 +90,7 @@ RSpec.describe 'User uses header search field', :js do end it 'shows created merge requests' do - find('.search-input-container .dropdown-menu').click_link("Merge requests I've created") + find('[data-testid="header-search-dropdown-menu"]').click_link("Merge requests I've created") expect(page).to have_selector('.mr-list .merge-request') expect_tokens([author_token(user.name)]) @@ -150,10 +153,9 @@ RSpec.describe 'User uses header search field', :js do it 'displays search options' do fill_in_search('test') - - expect(page).to have_selector(scoped_search_link('test')) - expect(page).to have_selector(scoped_search_link('test', group_id: group.id)) - expect(page).to have_selector(scoped_search_link('test', project_id: project.id, group_id: group.id)) + expect(page).to have_selector(scoped_search_link('test', search_code: true)) + expect(page).to have_selector(scoped_search_link('test', group_id: group.id, search_code: true)) + expect(page).to have_selector(scoped_search_link('test', project_id: project.id, group_id: group.id, search_code: true)) end end @@ -165,10 +167,9 @@ RSpec.describe 'User uses header search field', :js do it 'displays search options' do fill_in_search('test') - - expect(page).to have_selector(scoped_search_link('test')) - expect(page).not_to have_selector(scoped_search_link('test', group_id: project.namespace_id)) - expect(page).to have_selector(scoped_search_link('test', project_id: project.id)) + expect(page).to have_selector(scoped_search_link('test', search_code: true, repository_ref: 'master')) + expect(page).not_to have_selector(scoped_search_link('test', search_code: true, group_id: project.namespace_id, repository_ref: 'master')) + expect(page).to have_selector(scoped_search_link('test', search_code: true, project_id: project.id, repository_ref: 'master')) end it 'displays a link to project merge requests' do @@ -217,7 +218,6 @@ RSpec.describe 'User uses header search field', :js do it 'displays search options' do fill_in_search('test') - expect(page).to have_selector(scoped_search_link('test')) expect(page).to have_selector(scoped_search_link('test', group_id: group.id)) expect(page).not_to have_selector(scoped_search_link('test', project_id: project.id)) @@ -248,18 +248,20 @@ RSpec.describe 'User uses header search field', :js do end end - def scoped_search_link(term, project_id: nil, group_id: nil) + def scoped_search_link(term, project_id: nil, group_id: nil, search_code: nil, repository_ref: nil) # search_path will accept group_id and project_id but the order does not match # what is expected in the href, so the variable must be built manually href = search_path(search: term) + href.concat("&nav_source=navbar") href.concat("&project_id=#{project_id}") if project_id href.concat("&group_id=#{group_id}") if group_id - href.concat("&nav_source=navbar") + href.concat("&search_code=true") if search_code + href.concat("&repository_ref=#{repository_ref}") if repository_ref - ".dropdown a[href='#{href}']" + "[data-testid='header-search-dropdown-menu'] a[href='#{href}']" end def dashboard_search_options_popup_menu - "div[data-testid='dashboard-search-options']" + "[data-testid='header-search-dropdown-menu'] .header-search-dropdown-content" end end diff --git a/spec/fixtures/markdown/markdown_golden_master_examples.yml b/spec/fixtures/markdown/markdown_golden_master_examples.yml index 8556811974d..70a23d09c62 100644 --- a/spec/fixtures/markdown/markdown_golden_master_examples.yml +++ b/spec/fixtures/markdown/markdown_golden_master_examples.yml @@ -377,6 +377,34 @@ </ol> </details> +- name: diagram_kroki_nomnoml + markdown: |- + ```nomnoml + #stroke: #a86128 + [<frame>Decorator pattern| + [<abstract>Component||+ operation()] + [Client] depends --> [Component] + [Decorator|- next: Component] + [Decorator] decorates -- [ConcreteComponent] + [Component] <:- [Decorator] + [Component] <:- [ConcreteComponent] + ] + ``` + html: |- + <a class="no-attachment-icon" href="http://localhost:8000/nomnoml/svg/eNp1jbsOwjAMRfd-haUuIJQBBlRFVZb2L1CGkBqpgtpR6oEhH0_CW6hsts-9xwD1LJHPqKF2zX67ayqAQ3uKbkLTo-fohCMEJ4KRUoYFu2MuOS-m4ykwIUlKG-CAOT0yrdb2EewuY2YWBgxIwwxKmXx8dZ6h95ekgPAqGv4miuk-YnEVFfmIgr-Fzw6tVt-CZb7osdUNUAReJA==" target="_blank" rel="noopener noreferrer" data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,ICAjc3Ryb2tlOiAjYTg2MTI4CiAgWzxmcmFtZT5EZWNvcmF0b3IgcGF0dGVybnwKICAgIFs8YWJzdHJhY3Q+Q29tcG9uZW50fHwrIG9wZXJhdGlvbigpXQogICAgW0NsaWVudF0gZGVwZW5kcyAtLT4gW0NvbXBvbmVudF0KICAgIFtEZWNvcmF0b3J8LSBuZXh0OiBDb21wb25lbnRdCiAgICBbRGVjb3JhdG9yXSBkZWNvcmF0ZXMgLS0gW0NvbmNyZXRlQ29tcG9uZW50XQogICAgW0NvbXBvbmVudF0gPDotIFtEZWNvcmF0b3JdCiAgICBbQ29tcG9uZW50XSA8Oi0gW0NvbmNyZXRlQ29tcG9uZW50XQogIF0K"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" class="lazy" data-src="http://localhost:8000/nomnoml/svg/eNp1jbsOwjAMRfd-haUuIJQBBlRFVZb2L1CGkBqpgtpR6oEhH0_CW6hsts-9xwD1LJHPqKF2zX67ayqAQ3uKbkLTo-fohCMEJ4KRUoYFu2MuOS-m4ykwIUlKG-CAOT0yrdb2EewuY2YWBgxIwwxKmXx8dZ6h95ekgPAqGv4miuk-YnEVFfmIgr-Fzw6tVt-CZb7osdUNUAReJA=="></a> + +- name: diagram_plantuml + markdown: |- + ```plantuml + Alice -> Bob: Authentication Request + Bob --> Alice: Authentication Response + + Alice -> Bob: Another authentication Request + Alice <-- Bob: Another authentication Response + ``` + html: |- + <a class="no-attachment-icon" href="http://localhost:8080/png/U9nJK73CoKnELT2rKt3AJx9IS2mjoKZDAybCJYp9pCzJ24ejB4qjBk5I0Cagw09LWPLZKLTSa9zNdCe5L8bcO5u-K6MHGY8kWo7ARNHr2QY7MW00AeWxTG00" target="_blank" rel="noopener noreferrer" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,ICBBbGljZSAtPiBCb2I6IEF1dGhlbnRpY2F0aW9uIFJlcXVlc3QKICBCb2IgLS0+IEFsaWNlOiBBdXRoZW50aWNhdGlvbiBSZXNwb25zZQoKICBBbGljZSAtPiBCb2I6IEFub3RoZXIgYXV0aGVudGljYXRpb24gUmVxdWVzdAogIEFsaWNlIDwtLSBCb2I6IEFub3RoZXIgYXV0aGVudGljYXRpb24gUmVzcG9uc2UK"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" class="lazy" data-src="http://localhost:8080/png/U9nJK73CoKnELT2rKt3AJx9IS2mjoKZDAybCJYp9pCzJ24ejB4qjBk5I0Cagw09LWPLZKLTSa9zNdCe5L8bcO5u-K6MHGY8kWo7ARNHr2QY7MW00AeWxTG00"></a> + - name: div markdown: |- <div>plain text</div> diff --git a/spec/frontend/clusters_list/components/clusters_actions_spec.js b/spec/frontend/clusters_list/components/clusters_actions_spec.js index 312df12ab5f..21dcc66c639 100644 --- a/spec/frontend/clusters_list/components/clusters_actions_spec.js +++ b/spec/frontend/clusters_list/components/clusters_actions_spec.js @@ -1,4 +1,4 @@ -import { GlDropdown, GlDropdownItem, GlButton } from '@gitlab/ui'; +import { GlDropdown, GlDropdownItem, GlTooltip } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import ClustersActions from '~/clusters_list/components/clusters_actions.vue'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; @@ -7,12 +7,14 @@ import { INSTALL_AGENT_MODAL_ID, CLUSTERS_ACTIONS } from '~/clusters_list/consta describe('ClustersActionsComponent', () => { let wrapper; - const newClusterPath = 'path/to/create/cluster'; + const newClusterPath = 'path/to/add/cluster'; const addClusterPath = 'path/to/connect/existing/cluster'; + const newClusterDocsPath = 'path/to/create/new/cluster'; const defaultProvide = { newClusterPath, addClusterPath, + newClusterDocsPath, canAddCluster: true, displayClusterAgents: true, certificateBasedClustersEnabled: true, @@ -20,12 +22,13 @@ describe('ClustersActionsComponent', () => { const findDropdown = () => wrapper.findComponent(GlDropdown); const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); + const findTooltip = () => wrapper.findComponent(GlTooltip); const findDropdownItemIds = () => findDropdownItems().wrappers.map((x) => x.attributes('data-testid')); + const findDropdownItemTexts = () => findDropdownItems().wrappers.map((x) => x.text()); const findNewClusterLink = () => wrapper.findByTestId('new-cluster-link'); + const findNewClusterDocsLink = () => wrapper.findByTestId('create-cluster-link'); const findConnectClusterLink = () => wrapper.findByTestId('connect-cluster-link'); - const findConnectNewAgentLink = () => wrapper.findByTestId('connect-new-agent-link'); - const findConnectWithAgentButton = () => wrapper.findComponent(GlButton); const createWrapper = (provideData = {}) => { wrapper = shallowMountExtended(ClustersActions, { @@ -35,7 +38,6 @@ describe('ClustersActionsComponent', () => { }, directives: { GlModalDirective: createMockDirective(), - GlTooltip: createMockDirective(), }, }); }; @@ -49,12 +51,15 @@ describe('ClustersActionsComponent', () => { }); describe('when the certificate based clusters are enabled', () => { it('renders actions menu', () => { - expect(findDropdown().props('text')).toBe(CLUSTERS_ACTIONS.actionsButton); + expect(findDropdown().exists()).toBe(true); }); - it('renders correct href attributes for the links', () => { - expect(findNewClusterLink().attributes('href')).toBe(newClusterPath); - expect(findConnectClusterLink().attributes('href')).toBe(addClusterPath); + it('shows split button in dropdown', () => { + expect(findDropdown().props('split')).toBe(true); + }); + + it("doesn't show the tooltip", () => { + expect(findTooltip().exists()).toBe(false); }); describe('when user cannot add clusters', () => { @@ -67,8 +72,7 @@ describe('ClustersActionsComponent', () => { }); it('shows tooltip explaining why dropdown is disabled', () => { - const tooltip = getBinding(findDropdown().element, 'gl-tooltip'); - expect(tooltip.value).toBe(CLUSTERS_ACTIONS.dropdownDisabledHint); + expect(findTooltip().attributes('title')).toBe(CLUSTERS_ACTIONS.dropdownDisabledHint); }); it('does not bind split dropdown button', () => { @@ -79,33 +83,36 @@ describe('ClustersActionsComponent', () => { }); describe('when on project level', () => { - it('renders a dropdown with 3 actions items', () => { - expect(findDropdownItemIds()).toEqual([ - 'connect-new-agent-link', - 'new-cluster-link', - 'connect-cluster-link', - ]); + it(`displays default action as ${CLUSTERS_ACTIONS.connectWithAgent}`, () => { + expect(findDropdown().props('text')).toBe(CLUSTERS_ACTIONS.connectWithAgent); }); - it('renders correct modal id for the agent link', () => { - const binding = getBinding(findConnectNewAgentLink().element, 'gl-modal-directive'); + it('renders correct modal id for the default action', () => { + const binding = getBinding(findDropdown().element, 'gl-modal-directive'); expect(binding.value).toBe(INSTALL_AGENT_MODAL_ID); }); - it('shows tooltip', () => { - const tooltip = getBinding(findDropdown().element, 'gl-tooltip'); - expect(tooltip.value).toBe(CLUSTERS_ACTIONS.connectWithAgent); + it('renders a dropdown with 3 actions items', () => { + expect(findDropdownItemIds()).toEqual([ + 'create-cluster-link', + 'new-cluster-link', + 'connect-cluster-link', + ]); }); - it('shows split button in dropdown', () => { - expect(findDropdown().props('split')).toBe(true); + it('renders correct texts for the dropdown items', () => { + expect(findDropdownItemTexts()).toEqual([ + CLUSTERS_ACTIONS.createCluster, + CLUSTERS_ACTIONS.createClusterCertificate, + CLUSTERS_ACTIONS.connectClusterCertificate, + ]); }); - it('binds split button with modal id', () => { - const binding = getBinding(findDropdown().element, 'gl-modal-directive'); - - expect(binding.value).toBe(INSTALL_AGENT_MODAL_ID); + it('renders correct href attributes for the links', () => { + expect(findNewClusterDocsLink().attributes('href')).toBe(newClusterDocsPath); + expect(findNewClusterLink().attributes('href')).toBe(newClusterPath); + expect(findConnectClusterLink().attributes('href')).toBe(addClusterPath); }); }); @@ -114,17 +121,20 @@ describe('ClustersActionsComponent', () => { createWrapper({ displayClusterAgents: false }); }); - it('renders a dropdown with 2 actions items', () => { - expect(findDropdownItemIds()).toEqual(['new-cluster-link', 'connect-cluster-link']); + it(`displays default action as ${CLUSTERS_ACTIONS.connectClusterDeprecated}`, () => { + expect(findDropdown().props('text')).toBe(CLUSTERS_ACTIONS.connectClusterDeprecated); }); - it('shows tooltip', () => { - const tooltip = getBinding(findDropdown().element, 'gl-tooltip'); - expect(tooltip.value).toBe(CLUSTERS_ACTIONS.connectExistingCluster); + it('renders a dropdown with 1 action item', () => { + expect(findDropdownItemIds()).toEqual(['new-cluster-link']); }); - it('does not show split button in dropdown', () => { - expect(findDropdown().props('split')).toBe(false); + it('renders correct text for the dropdown item', () => { + expect(findDropdownItemTexts()).toEqual([CLUSTERS_ACTIONS.createClusterDeprecated]); + }); + + it('renders correct href attributes for the links', () => { + expect(findNewClusterLink().attributes('href')).toBe(newClusterPath); }); it('does not bind dropdown button to modal', () => { @@ -140,17 +150,26 @@ describe('ClustersActionsComponent', () => { createWrapper({ certificateBasedClustersEnabled: false }); }); - it('it does not show the the dropdown', () => { - expect(findDropdown().exists()).toBe(false); + it(`displays default action as ${CLUSTERS_ACTIONS.connectCluster}`, () => { + expect(findDropdown().props('text')).toBe(CLUSTERS_ACTIONS.connectCluster); }); - it('shows the connect with agent button', () => { - expect(findConnectWithAgentButton().props()).toMatchObject({ - disabled: !defaultProvide.canAddCluster, - category: 'primary', - variant: 'confirm', - }); - expect(findConnectWithAgentButton().text()).toBe(CLUSTERS_ACTIONS.connectWithAgent); + it('renders correct modal id for the default action', () => { + const binding = getBinding(findDropdown().element, 'gl-modal-directive'); + + expect(binding.value).toBe(INSTALL_AGENT_MODAL_ID); + }); + + it('renders a dropdown with 1 action item', () => { + expect(findDropdownItemIds()).toEqual(['create-cluster-link']); + }); + + it('renders correct text for the dropdown item', () => { + expect(findDropdownItemTexts()).toEqual([CLUSTERS_ACTIONS.createCluster]); + }); + + it('renders correct href attributes for the links', () => { + expect(findNewClusterDocsLink().attributes('href')).toBe(newClusterDocsPath); }); }); }); diff --git a/spec/frontend/fixtures/startup_css.rb b/spec/frontend/fixtures/startup_css.rb index e19a98c3bab..cf7383fa6ca 100644 --- a/spec/frontend/fixtures/startup_css.rb +++ b/spec/frontend/fixtures/startup_css.rb @@ -41,12 +41,12 @@ RSpec.describe 'Startup CSS fixtures', type: :controller do expect(response).to be_successful end - # This Feature Flag is off by default + # This Feature Flag is on by default # This ensures that the correct css is generated - # When the feature flag is off, the general startup will capture it + # When the feature flag is on, the general startup will capture it # This will be removed as part of https://gitlab.com/gitlab-org/gitlab/-/issues/339348 - it "startup_css/project-#{type}-search-ff-on.html" do - stub_feature_flags(new_header_search: true) + it "startup_css/project-#{type}-search-ff-off.html" do + stub_feature_flags(new_header_search: false) get :show, params: { namespace_id: project.namespace.to_param, diff --git a/spec/frontend/milestones/components/milestone_combobox_spec.js b/spec/frontend/milestones/components/milestone_combobox_spec.js index 1af39aff30c..afd85fb78ce 100644 --- a/spec/frontend/milestones/components/milestone_combobox_spec.js +++ b/spec/frontend/milestones/components/milestone_combobox_spec.js @@ -340,7 +340,9 @@ describe('Milestone combobox component', () => { await nextTick(); expect( - findFirstProjectMilestonesDropdownItem().find('span').classes('selected-item'), + findFirstProjectMilestonesDropdownItem() + .find('svg') + .classes('gl-new-dropdown-item-check-icon'), ).toBe(true); selectFirstProjectMilestone(); @@ -348,8 +350,8 @@ describe('Milestone combobox component', () => { await nextTick(); expect( - findFirstProjectMilestonesDropdownItem().find('span').classes('selected-item'), - ).toBe(false); + findFirstProjectMilestonesDropdownItem().find('svg').classes('gl-visibility-hidden'), + ).toBe(true); }); describe('when a project milestones is selected', () => { @@ -464,17 +466,19 @@ describe('Milestone combobox component', () => { await nextTick(); - expect(findFirstGroupMilestonesDropdownItem().find('span').classes('selected-item')).toBe( - true, - ); + expect( + findFirstGroupMilestonesDropdownItem() + .find('svg') + .classes('gl-new-dropdown-item-check-icon'), + ).toBe(true); selectFirstGroupMilestone(); await nextTick(); - expect(findFirstGroupMilestonesDropdownItem().find('span').classes('selected-item')).toBe( - false, - ); + expect( + findFirstGroupMilestonesDropdownItem().find('svg').classes('gl-visibility-hidden'), + ).toBe(true); }); describe('when a group milestones is selected', () => { diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb index 53d33f2875f..4feb9d1a2cd 100644 --- a/spec/helpers/clusters_helper_spec.rb +++ b/spec/helpers/clusters_helper_spec.rb @@ -74,6 +74,10 @@ RSpec.describe ClustersHelper do expect(subject[:add_cluster_path]).to eq("#{project_path(project)}/-/clusters/connect") end + it 'displays create cluster path' do + expect(subject[:new_cluster_docs_path]).to eq("#{project_path(project)}/-/clusters/new_cluster_docs") + end + it 'displays project default branch' do expect(subject[:default_branch_name]).to eq(project.default_branch) end diff --git a/spec/lib/banzai/filter/image_link_filter_spec.rb b/spec/lib/banzai/filter/image_link_filter_spec.rb index 238c3cdb9c1..6326d894b08 100644 --- a/spec/lib/banzai/filter/image_link_filter_spec.rb +++ b/spec/lib/banzai/filter/image_link_filter_spec.rb @@ -46,6 +46,16 @@ RSpec.describe Banzai::Filter::ImageLinkFilter do expect(doc.at_css('img')['data-canonical-src']).to eq doc.at_css('a')['data-canonical-src'] end + it 'moves the data-diagram* attributes' do + doc = filter(%q(<img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,Qm9iIC0+IFNhcmEgOiBIZWxsbw==">), context) + + expect(doc.at_css('a')['data-diagram']).to eq "plantuml" + expect(doc.at_css('a')['data-diagram-src']).to eq "data:text/plain;base64,Qm9iIC0+IFNhcmEgOiBIZWxsbw==" + + expect(doc.at_css('a img')['data-diagram']).to be_nil + expect(doc.at_css('a img')['data-diagram-src']).to be_nil + end + it 'adds no-attachment icon class to the link' do doc = filter(image(path), context) diff --git a/spec/lib/banzai/filter/kroki_filter_spec.rb b/spec/lib/banzai/filter/kroki_filter_spec.rb index 57caba1d4d7..d9e48754559 100644 --- a/spec/lib/banzai/filter/kroki_filter_spec.rb +++ b/spec/lib/banzai/filter/kroki_filter_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Banzai::Filter::KrokiFilter do stub_application_setting(kroki_enabled: true, kroki_url: "http://localhost:8000") doc = filter("<pre lang='nomnoml'><code>[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]</code></pre>") - expect(doc.to_s).to eq '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==">' + expect(doc.to_s).to eq '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==" data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,W1BpcmF0ZXxleWVDb3VudDogSW50fHJhaWQoKTtwaWxsYWdlKCl8CiAgW2JlYXJkXS0tW3BhcnJvdF0KICBbYmVhcmRdLTo+W2ZvdWwgbW91dGhdCl0=">' end it 'replaces nomnoml pre tag with img tag if both kroki and plantuml are enabled' do @@ -19,7 +19,7 @@ RSpec.describe Banzai::Filter::KrokiFilter do plantuml_url: "http://localhost:8080") doc = filter("<pre lang='nomnoml'><code>[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]</code></pre>") - expect(doc.to_s).to eq '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==">' + expect(doc.to_s).to eq '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==" data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,W1BpcmF0ZXxleWVDb3VudDogSW50fHJhaWQoKTtwaWxsYWdlKCl8CiAgW2JlYXJkXS0tW3BhcnJvdF0KICBbYmVhcmRdLTo+W2ZvdWwgbW91dGhdCl0=">' end it 'does not replace nomnoml pre tag with img tag if kroki is disabled' do diff --git a/spec/lib/banzai/filter/plantuml_filter_spec.rb b/spec/lib/banzai/filter/plantuml_filter_spec.rb index 2d1a01116e0..dcfeb2ce3ba 100644 --- a/spec/lib/banzai/filter/plantuml_filter_spec.rb +++ b/spec/lib/banzai/filter/plantuml_filter_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Banzai::Filter::PlantumlFilter do stub_application_setting(plantuml_enabled: true, plantuml_url: "http://localhost:8080") input = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>' - output = '<div class="imageblock"><div class="content"><img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq"></div></div>' + output = '<img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,Qm9iIC0+IFNhcmEgOiBIZWxsbw==">' doc = filter(input) expect(doc.to_s).to eq output @@ -29,7 +29,7 @@ RSpec.describe Banzai::Filter::PlantumlFilter do stub_application_setting(plantuml_enabled: true, plantuml_url: "invalid") input = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>' - output = '<div class="listingblock"><div class="content"><pre class="plantuml plantuml-error"> Error: cannot connect to PlantUML server at "invalid"</pre></div></div>' + output = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>' doc = filter(input) expect(doc.to_s).to eq output diff --git a/spec/presenters/project_clusterable_presenter_spec.rb b/spec/presenters/project_clusterable_presenter_spec.rb index 900630bb6e2..bd4319c9411 100644 --- a/spec/presenters/project_clusterable_presenter_spec.rb +++ b/spec/presenters/project_clusterable_presenter_spec.rb @@ -49,6 +49,12 @@ RSpec.describe ProjectClusterablePresenter do it { is_expected.to eq(connect_project_clusters_path(project)) } end + describe '#new_cluster_docs_path' do + subject { presenter.new_cluster_docs_path } + + it { is_expected.to eq(new_cluster_docs_project_clusters_path(project)) } + end + describe '#authorize_aws_role_path' do subject { presenter.authorize_aws_role_path } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a72c8d2c4e8..86a7c079ea9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -329,11 +329,6 @@ RSpec.configure do |config| stub_feature_flags(disable_anonymous_search: false) stub_feature_flags(disable_anonymous_project_search: false) - # Disable the refactored top nav search until there is functionality - # Can be removed once all existing functionality has been replicated - # For more information check https://gitlab.com/gitlab-org/gitlab/-/issues/339348 - stub_feature_flags(new_header_search: false) - allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enable_rugged) else unstub_all_feature_flags diff --git a/spec/support/helpers/search_helpers.rb b/spec/support/helpers/search_helpers.rb index f5a1a97a1d0..581ef07752e 100644 --- a/spec/support/helpers/search_helpers.rb +++ b/spec/support/helpers/search_helpers.rb @@ -2,9 +2,12 @@ module SearchHelpers def fill_in_search(text) - page.within('.search-input-wrap') do + # Once the `new_header_search` feature flag has been removed + # We can remove the `.search-input-wrap` selector + # https://gitlab.com/gitlab-org/gitlab/-/issues/339348 + page.within('.header-search-new') do find('#search').click - fill_in('search', with: text) + fill_in 'search', with: text end wait_for_all_requests diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb index f01c4075eeb..1932f78506f 100644 --- a/spec/support/matchers/markdown_matchers.rb +++ b/spec/support/matchers/markdown_matchers.rb @@ -270,7 +270,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_link(href: 'http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==') + expect(actual).to have_link(href: 'http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjliuUCAE_tHdw=') end end end diff --git a/spec/support/shared_contexts/markdown_golden_master_shared_examples.rb b/spec/support/shared_contexts/markdown_golden_master_shared_examples.rb index d0915bbf158..dea03af2248 100644 --- a/spec/support/shared_contexts/markdown_golden_master_shared_examples.rb +++ b/spec/support/shared_contexts/markdown_golden_master_shared_examples.rb @@ -64,6 +64,9 @@ RSpec.shared_context 'API::Markdown Golden Master shared context' do |markdown_y let(:substitutions) { markdown_example.fetch(:substitutions, {}) } it "verifies conversion of GFM to HTML", :unlimited_max_formatted_output_length do + stub_application_setting(plantuml_enabled: true, plantuml_url: 'http://localhost:8080') + stub_application_setting(kroki_enabled: true, kroki_url: 'http://localhost:8000') + pending pending_reason if pending_reason normalized_example_html = normalize_html(example_html, substitutions) diff --git a/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb b/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb index bb5460e2a6f..095c48cade8 100644 --- a/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb +++ b/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb @@ -11,6 +11,7 @@ RSpec.shared_examples 'search timeouts' do |scope| end it 'renders timeout information' do + # expect(page).to have_content('This endpoint has been requested too many times.') expect(page).to have_content('Your search timed out') end |