diff options
author | David O'Regan <doregan@gitlab.com> | 2023-08-01 18:28:25 +0300 |
---|---|---|
committer | David O'Regan <doregan@gitlab.com> | 2023-08-01 18:28:25 +0300 |
commit | 25828f26c2323658b2c9fa1fd6cb5e2bc89c2df6 (patch) | |
tree | d890b22df42027f20de9ee79b5febeed9fe51a6b | |
parent | ba6860ee32c0272444e4276885b4ba243d38c09e (diff) | |
parent | 2c6d1c0de42086d1fb5b85c3e26d5476b852e3ab (diff) |
Merge branch 'replace-collapsible-container' into 'main'
Retire the collapsible_container component
See merge request https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/4111
Merged-by: David O'Regan <doregan@gitlab.com>
Approved-by: David O'Regan <doregan@gitlab.com>
Co-authored-by: Sarah German <sgerman@gitlab.com>
8 files changed, 16 insertions, 273 deletions
diff --git a/content/frontend/default/components/collapsible_container.vue b/content/frontend/default/components/collapsible_container.vue deleted file mode 100644 index 59653ab8..00000000 --- a/content/frontend/default/components/collapsible_container.vue +++ /dev/null @@ -1,86 +0,0 @@ -<script> -import { getOuterHeight } from '../../shared/dom'; - -export default { - name: 'CollapsibleContainer', - model: { - prop: 'isCollapsed', - event: 'change', - }, - props: { - isCollapsed: { - type: Boolean, - required: true, - }, - collapsingClass: { - type: String, - required: false, - default: 'sm-collapsing', - }, - collapsedClass: { - type: String, - required: false, - default: 'sm-collapsed', - }, - }, - data() { - return { - isCollapsing: false, - collapsingHeight: 0, - }; - }, - computed: { - styles() { - if (this.isCollapsing) { - return { - height: `${this.collapsingHeight}px`, - }; - } - - return {}; - }, - classes() { - if (this.isCollapsing) { - return this.collapsingClass; - } - if (this.isCollapsed) { - return this.collapsedClass; - } - - return ''; - }, - }, - methods: { - collapse(shouldCollapse) { - if (this.isCollapsing) { - return; - } - // Right away let's flag that we're collapsing so we don't accept anymore updates - this.isCollapsing = true; - - // Let's let our parent go ahead and treat us as collapsed. - this.$emit('change', shouldCollapse); - - // Get start/stop height based on if we're collapsing or expanding - const containerHeight = getOuterHeight(this.$el); - const startHeight = shouldCollapse ? containerHeight : 0; - const stopHeight = shouldCollapse ? 0 : containerHeight; - - // Kick off transition - this.collapsingHeight = startHeight; - setTimeout(() => { - this.collapsingHeight = stopHeight; - }, 50); - - setTimeout(() => { - this.isCollapsing = false; - }, 400); - }, - }, -}; -</script> -<template> - <div :class="classes" :style="styles"> - <slot></slot> - </div> -</template> diff --git a/content/frontend/default/components/table_of_contents.vue b/content/frontend/default/components/table_of_contents.vue index 542cd50b..7ffbc570 100644 --- a/content/frontend/default/components/table_of_contents.vue +++ b/content/frontend/default/components/table_of_contents.vue @@ -1,25 +1,28 @@ <script> -import { GlIcon } from '@gitlab/ui'; +import { GlIcon, GlCollapse } from '@gitlab/ui'; import { flattenItems } from '../../shared/toc/flatten_items'; -import CollapsibleContainer from './collapsible_container.vue'; import TableOfContentsList from './table_of_contents_list.vue'; export default { name: 'TableOfContents', components: { - CollapsibleContainer, - TableOfContentsList, + GlCollapse, GlIcon, + TableOfContentsList, }, props: { items: { type: Array, required: true, }, + initialCollapsed: { + type: Boolean, + required: true, + }, }, data() { return { - isCollapsed: true, + isCollapsed: this.initialCollapsed, }; }, computed: { @@ -33,7 +36,7 @@ export default { }, methods: { toggleCollapse() { - this.$refs.container.collapse(!this.isCollapsed); + this.isCollapsed = !this.isCollapsed; }, }, }; @@ -55,9 +58,9 @@ export default { > </h4> <h4 class="border-0 gl-font-base font-weight-bold toc-lg">On this page</h4> - <collapsible-container ref="container" v-model="isCollapsed" data-testid="container"> + <gl-collapse ref="container" :visible="!isCollapsed" data-testid="container"> <table-of-contents-list :items="allItems" class="my-0" data-testid="main-list" /> - </collapsible-container> + </gl-collapse> </div> </div> </template> diff --git a/content/frontend/default/setup_table_of_contents.js b/content/frontend/default/setup_table_of_contents.js index 5f0c7d73..c0aff655 100644 --- a/content/frontend/default/setup_table_of_contents.js +++ b/content/frontend/default/setup_table_of_contents.js @@ -35,6 +35,7 @@ export const setupTableOfContents = () => { return h(TableOfContents, { props: { items, + initialCollapsed: sidebarEl.classList.contains('toc-mobile'), }, directives: [ { diff --git a/content/frontend/shared/dom.js b/content/frontend/shared/dom.js index f4dbe540..4e285a01 100644 --- a/content/frontend/shared/dom.js +++ b/content/frontend/shared/dom.js @@ -1,16 +1,3 @@ -/* global $ */ - -/** - * Returns outerHeight of element **even if it's hidden** - * - * NOTE: Uses jQuery because there is no trivial way to do this in - * vaniall JS, and it's nice that jQuery has a reliable out-of-the-box - * solution. - * - * @param {Element} el - */ -export const getOuterHeight = (el) => $(el).outerHeight(); - /** * Find the first child of the given element with the given tag name * diff --git a/layouts/default.html b/layouts/default.html index 2d7905ea..abc1ee99 100644 --- a/layouts/default.html +++ b/layouts/default.html @@ -40,7 +40,7 @@ </div> <div class="row d-xl-none"> <div class="col"> - <div class="doc-nav"></div> + <div class="doc-nav toc-mobile"></div> </div> </div> <main> diff --git a/spec/frontend/default/components/__snapshots__/table_of_contents_spec.js.snap b/spec/frontend/default/components/__snapshots__/table_of_contents_spec.js.snap index e66a4c63..3880d071 100644 --- a/spec/frontend/default/components/__snapshots__/table_of_contents_spec.js.snap +++ b/spec/frontend/default/components/__snapshots__/table_of_contents_spec.js.snap @@ -29,11 +29,8 @@ exports[`frontend/default/components/table_of_contents matches snapshot 1`] = ` > On this page </h4> - <collapsible-container-stub - collapsedclass="sm-collapsed" - collapsingclass="sm-collapsing" + <gl-collapse-stub data-testid="container" - iscollapsed="true" > <table-of-contents-list-stub class="my-0" @@ -41,7 +38,7 @@ exports[`frontend/default/components/table_of_contents matches snapshot 1`] = ` items="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]" level="0" /> - </collapsible-container-stub> + </gl-collapse-stub> </div> </div> `; diff --git a/spec/frontend/default/components/collapsible_container_spec.js b/spec/frontend/default/components/collapsible_container_spec.js deleted file mode 100644 index 87395647..00000000 --- a/spec/frontend/default/components/collapsible_container_spec.js +++ /dev/null @@ -1,128 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { shallowMount } from '@vue/test-utils'; -import CollapsibleContainer from '../../../../content/frontend/default/components/collapsible_container.vue'; -import * as dom from '../../../../content/frontend/shared/dom'; - -const TEST_COLLAPSING_CLASS = 'test-collapsing'; -const TEST_COLLAPSED_CLASS = 'test-collapsed'; -const TEST_SLOT = 'Lorem ipsum dolar sit amit'; -const TEST_OUTER_HEIGHT = 400; -const KICKOFF_DELAY = 50; -const FINISH_DELAY = 400; - -describe('frontend/default/components/collapsible_container', () => { - let wrapper; - - beforeEach(() => { - // jquery is not available in Jest yet so we need to mock this method - jest.spyOn(dom, 'getOuterHeight').mockImplementation((x) => Number(x.dataset.testOuterHeight)); - jest.useFakeTimers(); - }); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - - jest.useRealTimers(); - }); - - const createComponent = (props = {}) => { - wrapper = shallowMount(CollapsibleContainer, { - propsData: { - collapsingClass: TEST_COLLAPSING_CLASS, - collapsedClass: TEST_COLLAPSED_CLASS, - ...props, - }, - slots: { - default: TEST_SLOT, - }, - attrs: { - 'data-test-outer-height': TEST_OUTER_HEIGHT.toString(), - }, - }); - }; - const findStyleHeight = () => wrapper.element.style.height; - const waitForKickoff = () => jest.advanceTimersByTime(KICKOFF_DELAY); - const waitForTransition = () => jest.advanceTimersByTime(FINISH_DELAY - KICKOFF_DELAY); - - describe.each` - isCollapsed | startHeight | endHeight | startClasses | endClasses - ${true} | ${0} | ${TEST_OUTER_HEIGHT} | ${[TEST_COLLAPSED_CLASS]} | ${[]} - ${false} | ${TEST_OUTER_HEIGHT} | ${0} | ${[]} | ${[TEST_COLLAPSED_CLASS]} - `( - 'when isCollapsed = $isCollapsed', - ({ isCollapsed, startHeight, endHeight, startClasses, endClasses }) => { - beforeEach(() => { - createComponent({ isCollapsed }); - - return wrapper.vm.$nextTick(); - }); - - it('renders slot', () => { - expect(wrapper.text()).toBe(TEST_SLOT); - }); - - it('has starting classes', () => { - expect(wrapper.classes()).toEqual(startClasses); - }); - - it('has not emitted anything', () => { - expect(wrapper.emitted()).toEqual({}); - }); - - describe('when collapse is triggered', () => { - beforeEach(() => { - wrapper.vm.collapse(!isCollapsed); - - // set props because this is what would naturally happen with `v-model` - wrapper.setProps({ isCollapsed: !isCollapsed }); - }); - - it('has collapsing class', () => { - expect(wrapper.classes()).toEqual([TEST_COLLAPSING_CLASS]); - }); - - it('emits change', () => { - expect(wrapper.emitted().change).toEqual([[!isCollapsed]]); - }); - - it('sets starting height', () => { - expect(findStyleHeight()).toEqual(`${startHeight}px`); - }); - - it('triggering collapse again does not do anything', () => { - wrapper.vm.collapse(isCollapsed); - - expect(wrapper.emitted().change).toEqual([[!isCollapsed]]); - }); - - describe('after animation kickoff delay', () => { - beforeEach(() => { - waitForKickoff(); - }); - - it('sets ending height', () => { - expect(findStyleHeight()).toEqual(`${endHeight}px`); - }); - - describe('after transition', () => { - beforeEach(() => { - waitForTransition(); - }); - - it('does not set height', () => { - expect(findStyleHeight()).toBe(''); - }); - - it('sets ending classes', () => { - expect(wrapper.classes()).toEqual(endClasses); - }); - }); - }); - }); - }, - ); -}); diff --git a/spec/frontend/default/components/table_of_contents_spec.js b/spec/frontend/default/components/table_of_contents_spec.js index 851258c3..88146c13 100644 --- a/spec/frontend/default/components/table_of_contents_spec.js +++ b/spec/frontend/default/components/table_of_contents_spec.js @@ -4,7 +4,6 @@ import { shallowMount, mount } from '@vue/test-utils'; import TableOfContents from '../../../../content/frontend/default/components/table_of_contents.vue'; -import * as dom from '../../../../content/frontend/shared/dom'; import { flattenItems } from '../../../../content/frontend/shared/toc/flatten_items'; import { createExampleToc } from '../../shared/toc_helper'; @@ -18,15 +17,11 @@ describe('frontend/default/components/table_of_contents', () => { wrapper = null; }); - beforeEach(() => { - // jquery is not available in Jest yet so we need to mock this method - jest.spyOn(dom, 'getOuterHeight').mockReturnValue(100); - }); - const createComponent = (props = {}, mountFn = shallowMount) => { wrapper = mountFn(TableOfContents, { propsData: { items: TEST_ITEMS, + initialCollapsed: true, ...props, }, }); @@ -34,14 +29,11 @@ describe('frontend/default/components/table_of_contents', () => { const findCollapseButton = () => wrapper.find('[data-testid="collapse"]'); const findCollapseIcon = () => findCollapseButton().find('svg'); - const findCollapsibleContainer = () => wrapper.find('[data-testid="container"]'); const findMainList = () => wrapper.find('[data-testid="main-list"]'); const findMainListItems = () => findMainList().props('items'); - const clickCollapseButton = () => findCollapseButton().trigger('click'); const expectCollapsed = (isCollapsed = true) => { expect(findCollapseButton().attributes('aria-expanded')).toBe(isCollapsed ? undefined : 'true'); - expect(findCollapsibleContainer().props('isCollapsed')).toBe(isCollapsed); expect(findCollapseIcon().attributes('data-testid')).toBe( isCollapsed ? 'chevron-right-icon' : 'chevron-down-icon', ); @@ -64,28 +56,5 @@ describe('frontend/default/components/table_of_contents', () => { it('is initially collapsed', () => { expectCollapsed(true); }); - - describe('when collapse button is pressed', () => { - beforeEach(() => { - clickCollapseButton(); - }); - - it('starts expanding', () => { - expect(findCollapsibleContainer().classes('sm-collapsing')).toBe(true); - }); - - it('immediately updates collapse status', () => { - expectCollapsed(false); - }); - - it('when button pressed again, nothing happens because in the middle of collapsing', () => { - clickCollapseButton(); - - return wrapper.vm.$nextTick(() => { - expect(findCollapsibleContainer().classes('sm-collapsing')).toBe(true); - expectCollapsed(false); - }); - }); - }); }); }); |