diff --git a/app/assets/javascripts/vue_shared/components/crud_component.vue b/app/assets/javascripts/vue_shared/components/crud_component.vue index 00e9015e87f70fc1f8812996f0fe6b9a2c6ed312..5c7d899eb64773306405f8e932a0f36d95db0fcd 100644 --- a/app/assets/javascripts/vue_shared/components/crud_component.vue +++ b/app/assets/javascripts/vue_shared/components/crud_component.vue @@ -53,6 +53,11 @@ export default { required: false, default: false, }, + containerTag: { + type: String, + required: false, + default: 'section', + }, isLoading: { type: Boolean, required: false, @@ -164,7 +169,8 @@ export default { diff --git a/app/components/layouts/crud_component.haml b/app/components/layouts/crud_component.haml index d7ac74ac8c826e56fb46635039e75b5574092dff..71321e90ea5282666a93c3a98d254c508776ccb0 100644 --- a/app/components/layouts/crud_component.haml +++ b/app/components/layouts/crud_component.haml @@ -1,4 +1,4 @@ -%section.crud.gl-bg-subtle.gl-border.gl-border-section.gl-rounded-base{ options_attrs, id: id } += content_tag @container_tag, **options_attrs, id: id do %header.crud-header.gl-flex.gl-flex-wrap.gl-justify-between.gl-gap-x-5.gl-gap-y-2.gl-px-5.gl-py-4.gl-bg-section.gl-border-b.gl-border-section.gl-rounded-t-base.gl-relative{ class: ('gl-pr-10' if @is_collapsible) } .gl-flex.gl-flex-col.gl-self-center %h2.gl-text-base.gl-font-bold.gl-leading-normal.gl-inline-flex.gl-gap-3.gl-m-0{ data: { testid: 'crud-title' } } diff --git a/app/components/layouts/crud_component.rb b/app/components/layouts/crud_component.rb index 3a847cedd757837cef1ee4f3c4ad7383b91253ed..99576d7e0dbc9ef4818cbaf242706d6a3aaa7ca9 100644 --- a/app/components/layouts/crud_component.rb +++ b/app/components/layouts/crud_component.rb @@ -15,11 +15,12 @@ class CrudComponent < ViewComponent::Base # @param [Hash] toggle_options # @param [Hash] footer_options # @param [Boolean] is_collapsible + # @param [String] container_tag def initialize( title, description: nil, count: nil, icon: nil, icon_class: nil, toggle_text: nil, options: {}, count_options: {}, body_options: {}, form_options: {}, toggle_options: {}, footer_options: {}, - is_collapsible: false + is_collapsible: false, container_tag: 'section' ) @title = title @description = description @@ -34,6 +35,7 @@ def initialize( @toggle_options = toggle_options @footer_options = footer_options @is_collapsible = is_collapsible + @container_tag = container_tag end renders_one :description @@ -48,11 +50,13 @@ def id end def options_attrs + default_testid = 'haml-crud' default_classes = [ + 'crud', 'gl-bg-subtle', 'gl-border', 'gl-border-section', 'gl-rounded-base', ('js-toggle-container' if @toggle_text), ('js-crud-collapsible-section' if @is_collapsible) ] - @options.merge(default_attrs(@options, nil, default_classes)) + @options.merge(default_attrs(@options, default_testid, default_classes)) end def body_options_attrs diff --git a/spec/components/layouts/crud_component_spec.rb b/spec/components/layouts/crud_component_spec.rb index 5c4cc1ff49d3ebefa6dfcc00d4ee41fd3e41e03a..577e78637b3d73a9f335b6efa1ed72817f164c3a 100644 --- a/spec/components/layouts/crud_component_spec.rb +++ b/spec/components/layouts/crud_component_spec.rb @@ -15,6 +15,13 @@ let(:pagination) { 'Pagination' } let(:component_title) { described_class.new(title) } + describe 'container_tag' do + it 'renders the element specified by container_tag option' do + render_inline described_class.new(title, container_tag: :div) + expect(page).to have_selector('div[data-testid="haml-crud"]') + end + end + describe 'slots' do it 'renders title' do render_inline component_title diff --git a/spec/frontend/vue_shared/components/crud_component_spec.js b/spec/frontend/vue_shared/components/crud_component_spec.js index 01bc9914852aab9b634a739a0e4fabef2f463230..f88820663064e3d69c9ba7713ac1e4accdf063b3 100644 --- a/spec/frontend/vue_shared/components/crud_component_spec.js +++ b/spec/frontend/vue_shared/components/crud_component_spec.js @@ -46,6 +46,11 @@ describe('CRUD Component', () => { expect(findTitle().text()).toBe('CRUD Component title'); }); + it('renders the element specified by the containerTag prop', () => { + createComponent({ containerTag: 'span' }); + expect(wrapper.element.tagName).toBe('SPAN'); + }); + it('renders `title` slot', () => { createComponent({}, { title: '

Title slot

' });