diff --git a/app/assets/javascripts/accordion/index.js b/app/assets/javascripts/accordion/index.js new file mode 100644 index 0000000000000000000000000000000000000000..44296d44465e883eeab0e95d0eb6175eba131ce1 --- /dev/null +++ b/app/assets/javascripts/accordion/index.js @@ -0,0 +1,30 @@ +import { spriteIcon } from '~/lib/utils/common_utils'; + +/** + * This adds interactivity to accordions created via HAML + */ +export default (el) => { + if (!el) return; + + const accordionTrigger = el.querySelector('button'); + const accordionItem = el.querySelector('.accordion-item'); + const iconClass = 's16 gl-icon gl-button-icon js-chevron-icon'; + const chevronRight = spriteIcon('chevron-right', iconClass); + const chevronDown = spriteIcon('chevron-down', iconClass); + + accordionTrigger.addEventListener('click', () => { + const chevronIcon = el.querySelector('.js-chevron-icon'); + accordionItem.classList.toggle('show'); + + if (accordionItem.classList.contains('show')) { + // eslint-disable-next-line no-unsanitized/property + chevronIcon.outerHTML = chevronDown; + accordionTrigger.setAttribute('aria-expanded', 'true'); + return; + } + + // eslint-disable-next-line no-unsanitized/property + chevronIcon.outerHTML = chevronRight; + accordionTrigger.setAttribute('aria-expanded', 'false'); + }); +}; diff --git a/app/assets/javascripts/projects/new/index.js b/app/assets/javascripts/projects/new/index.js index a5a833dc73bb140ff120376120f0c1d7159fdab5..1560e47c33ef2f1fbfbd0be10f10e41d2cfe3d73 100644 --- a/app/assets/javascripts/projects/new/index.js +++ b/app/assets/javascripts/projects/new/index.js @@ -2,6 +2,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; import { parseBoolean } from '~/lib/utils/common_utils'; +import initAccordion from '~/accordion'; import NewProjectCreationApp from './components/app.vue'; import NewProjectUrlSelect from './components/new_project_url_select.vue'; import DeploymentTargetSelect from './components/deployment_target_select.vue'; @@ -89,3 +90,5 @@ export function initDeploymentTargetSelect() { render: (createElement) => createElement(DeploymentTargetSelect), }); } + +initAccordion(document.getElementById('js-experimental-setting-accordion')); diff --git a/app/components/pajamas/accordion_item_component.html.haml b/app/components/pajamas/accordion_item_component.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..5b1b99f617b335118985a0bc9e9d943f22263fdc --- /dev/null +++ b/app/components/pajamas/accordion_item_component.html.haml @@ -0,0 +1,7 @@ +.gl-accordion-item + %h3.gl-accordion-item-header + = render Pajamas::ButtonComponent.new(variant: :link, icon: icon, icon_classes: "js-chevron-icon", button_options: { "aria-controls": "accordion-item", "aria-expanded": expanded }) do + = @title + + .accordion-item.gl-mt-3.gl-font-base.collapse{ **body_class } + = content diff --git a/app/components/pajamas/accordion_item_component.rb b/app/components/pajamas/accordion_item_component.rb new file mode 100644 index 0000000000000000000000000000000000000000..e489d31b3644c5faba79cb75a7bc2b7432887f1a --- /dev/null +++ b/app/components/pajamas/accordion_item_component.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# Renders a accordion component +module Pajamas + class AccordionItemComponent < Pajamas::Component + # @param [String] title + # @param [Symbol] state + def initialize(title: nil, state: :closed) + @title = title + @state = filter_attribute(state.to_sym, STATE_OPTIONS) + end + + delegate :sprite_icon, to: :helpers + + STATE_OPTIONS = [:opened, :closed].freeze + + def icon + @state == :opened ? "chevron-down" : "chevron-right" + end + + def body_class + @state == :opened ? { class: 'show' } : {} + end + + def expanded + @state == :opened + end + end +end diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml index 58ae445fc028372f34704e3b17edb2b971583502..302e7e9bcd5eb08611bad310d0a87a1dd71dbd00 100644 --- a/app/views/projects/_new_project_fields.html.haml +++ b/app/views/projects/_new_project_fields.html.haml @@ -93,12 +93,15 @@ = link_to _('Learn more.'), help_page_path('user/application_security/sast/index'), target: '_blank', rel: 'noopener noreferrer', data: { track_action: 'followed' } - if display_sha256_repository - .form-group - = render Pajamas::CheckboxTagComponent.new(name: 'project[use_sha256_repository]') do |c| - - c.with_label do - = s_('ProjectsNew|Use SHA-256 as the repository hashing algorithm') - - c.with_help_text do - = s_('ProjectsNew|Default hashing algorithm is SHA-1.') + #js-experimental-setting-accordion.form-group.gl-mb-6 + = render Pajamas::AccordionItemComponent.new(title: s_("ProjectsNew|Experimental settings"), state: :closed) do + = render Pajamas::CheckboxTagComponent.new(name: 'project[use_sha256_repository]') do |c| + - c.with_label do + = s_('ProjectsNew|Use SHA-256 for repository hashing algorithm') + = render_if_exists 'shared/experimental_badge_tag' + - c.with_help_text do + = s_("ProjectsNew|Might break existing functionality with other repositories or APIs. It's not possible to change SHA-256 repositories back to the default SHA-1 hashing algorithm.") + -# this partial is from JiHu, see details in https://jihulab.com/gitlab-cn/gitlab/-/merge_requests/675 = render_if_exists 'shared/other_project_options', f: f, visibility_level: visibility_level, track_label: track_label diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2a2f7c434b950ed04ccfc0408427082c740142de..4837d7e3632558365a646b995af186be0fdf0c03 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -39402,15 +39402,15 @@ msgstr "" msgid "ProjectsNew|Create new project" msgstr "" -msgid "ProjectsNew|Default hashing algorithm is SHA-1." -msgstr "" - msgid "ProjectsNew|Description format" msgstr "" msgid "ProjectsNew|Enable Static Application Security Testing (SAST)" msgstr "" +msgid "ProjectsNew|Experimental settings" +msgstr "" + msgid "ProjectsNew|Group name" msgstr "" @@ -39426,6 +39426,9 @@ msgstr "" msgid "ProjectsNew|Initialize repository with a README" msgstr "" +msgid "ProjectsNew|Might break existing functionality with other repositories or APIs. It's not possible to change SHA-256 repositories back to the default SHA-1 hashing algorithm." +msgstr "" + msgid "ProjectsNew|Migrate your data from an external source like GitHub, Bitbucket, or another instance of GitLab." msgstr "" @@ -39468,7 +39471,7 @@ msgstr "" msgid "ProjectsNew|Unable to suggest a path. Please refresh and try again." msgstr "" -msgid "ProjectsNew|Use SHA-256 as the repository hashing algorithm" +msgid "ProjectsNew|Use SHA-256 for repository hashing algorithm" msgstr "" msgid "ProjectsNew|Visibility Level" diff --git a/spec/components/pajamas/accordion_item_component_spec.rb b/spec/components/pajamas/accordion_item_component_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..10aef4a44ad37e889cecc09734a4de3b269ac059 --- /dev/null +++ b/spec/components/pajamas/accordion_item_component_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Pajamas::AccordionItemComponent, type: :component, feature_category: :shared do + let(:title) { "This is a title" } + let(:content) { "This is the content" } + let(:state) { :opened } + + before do + render_inline(described_class.new(title: title, state: state)) do |_c| + content + end + end + + describe "title param" do + it "is shown inside the accordion" do + expect(page).to have_button(title) + end + end + + describe "content" do + it "is shown inside the accordion" do + expect(page).to have_css ".accordion-item", text: content + end + end + + describe "state (opened) param" do + it "renders the show class" do + expect(page).to have_selector('.show') + end + + it "renders a chevron-down icon" do + expect(page).to have_selector('[data-testid="chevron-down-icon"]') + end + + it "renders a button with the correct aria-expanded value" do + expect(page).to have_selector('button[aria-expanded="true"]') + end + end + + describe "state (closed) param" do + before do + render_inline(described_class.new(title: title, state: :closed)) + end + + it "does not render the show class" do + expect(page).not_to have_selector('.show') + end + + it "renders a chevron-right icon" do + expect(page).to have_selector('[data-testid="chevron-right-icon"]') + end + + it "renders a button with the correct aria-expanded value" do + expect(page).to have_selector('button[aria-expanded="false"]') + end + end +end diff --git a/spec/components/previews/pajamas/accordion_component_preview.rb b/spec/components/previews/pajamas/accordion_component_preview.rb new file mode 100644 index 0000000000000000000000000000000000000000..5c722cdee695983316d083b225a1c76e184bb990 --- /dev/null +++ b/spec/components/previews/pajamas/accordion_component_preview.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Pajamas + class AccordionComponentPreview < ViewComponent::Preview + # @param title text + # @param body text + # @param state + def default(title: "Accordion title (open)", body: "Accordion body", state: :opened) + render(Pajamas::AccordionComponent.new( + title: title, + body: body, + state: state + )) + end + + def closed(title: "Accordion title (closed)", body: "Accordion body", state: :closed) + render(Pajamas::AccordionComponent.new( + title: title, + body: body, + state: state + )) + end + end +end diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb index 96156f14cfc3e9815a243dc0ed11b32a0589a14c..b715733ca20710d2c4be0e1a03270716b732f633 100644 --- a/spec/features/projects/user_creates_project_spec.rb +++ b/spec/features/projects/user_creates_project_spec.rb @@ -58,12 +58,13 @@ end context 'when creating a project with SHA256 repository' do - let(:sha256_field) { 'Use SHA-256 as the repository hashing algorithm' } + let(:sha256_field) { 'Use SHA-256 for repository hashing algorithm' } it 'creates a new project' do visit(new_project_path) click_link 'Create blank project' + click_button 'Experimental settings' fill_in(:project_name, with: 'With initial commits') expect(page).to have_checked_field 'Initialize repository with a README' diff --git a/spec/frontend/accordion/index_spec.js b/spec/frontend/accordion/index_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..10ce1cdd00e43a1d19a9586b6cc9e7aa842ebbbd --- /dev/null +++ b/spec/frontend/accordion/index_spec.js @@ -0,0 +1,52 @@ +import initAccordion from '~/accordion'; +import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; + +describe('Init Accordion component', () => { + beforeEach(() => { + setHTMLFixture( + '