diff options
author | Suzanne Selhorn <sselhorn@gitlab.com> | 2022-12-12 20:12:59 +0300 |
---|---|---|
committer | Suzanne Selhorn <sselhorn@gitlab.com> | 2022-12-12 20:12:59 +0300 |
commit | 6ed1eba87a989a36f3203da03be1a49c237d9da7 (patch) | |
tree | eedaa840f41915be5c4f435d6e0ae25e6815d042 | |
parent | 170cdf3858f92643751f4e4f67189e050c6a1e55 (diff) | |
parent | 44a6968c0e93823b3a28a720a6214ae8d87cd903 (diff) |
Merge branch 'sarahg/breaking-change-filter' into 'main'
Add a breaking changes filter to the deprecations list
See merge request https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/3342
Merged-by: Suzanne Selhorn <sselhorn@gitlab.com>
Reviewed-by: Brian Rhea <brhea@gitlab.com>
Co-authored-by: Sarah German <sgerman@gitlab.com>
-rw-r--r-- | content/assets/stylesheets/_variables.scss | 3 | ||||
-rw-r--r-- | content/assets/stylesheets/stylesheet.scss | 5 | ||||
-rw-r--r-- | content/frontend/deprecations/components/deprecation_filters.vue | 164 | ||||
-rw-r--r-- | content/frontend/deprecations/filters.js | 17 | ||||
-rw-r--r-- | spec/frontend/deprecations/deprecation_filters_spec.js | 26 |
5 files changed, 124 insertions, 91 deletions
diff --git a/content/assets/stylesheets/_variables.scss b/content/assets/stylesheets/_variables.scss index c71275b5..e1c00677 100644 --- a/content/assets/stylesheets/_variables.scss +++ b/content/assets/stylesheets/_variables.scss @@ -375,3 +375,6 @@ $gl-font-size-markdown-h3-md: 1.25rem; $gl-font-size-markdown-h1-xl: 2.813rem; $gl-font-size-markdown-h2-xl: 2rem; $gl-font-size-markdown-h3-xl: 1.438rem; + +// Workaround for https://gitlab.com/gitlab-org/gitlab-ui/-/issues/2095 +$gl-icon-select-chevron-down: url('data:image/svg+xml,%3Csvg width="8" height="5" viewBox="0 0 8 5" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath fill-rule="evenodd" clip-rule="evenodd" d="M0.21967 0.21967C0.512563 -0.0732232 0.987437 -0.0732232 1.28033 0.21967L4 2.93934L6.71967 0.21967C7.01256 -0.073223 7.48744 -0.0732229 7.78033 0.21967C8.07322 0.512563 8.07322 0.987437 7.78033 1.28033L4.53033 4.53033C4.23744 4.82322 3.76256 4.82322 3.46967 4.53033L0.21967 1.28033C-0.0732233 0.987437 -0.0732233 0.512563 0.21967 0.21967Z" fill="%23666666"/%3E%3C/svg%3E%0A'); diff --git a/content/assets/stylesheets/stylesheet.scss b/content/assets/stylesheets/stylesheet.scss index 44a97ef1..256c05ec 100644 --- a/content/assets/stylesheets/stylesheet.scss +++ b/content/assets/stylesheets/stylesheet.scss @@ -583,3 +583,8 @@ a.gl-tab-nav-item:hover { box-shadow: 0 0 0.125rem $landing-gl-black-50, 0 0.25rem 1.5rem $landing-gl-blue-950; } } + +select[name='removal_milestone'], +select[name='breaking'] { + background-image: $gl-icon-select-chevron-down; +} diff --git a/content/frontend/deprecations/components/deprecation_filters.vue b/content/frontend/deprecations/components/deprecation_filters.vue index b8381d8d..fb9042a2 100644 --- a/content/frontend/deprecations/components/deprecation_filters.vue +++ b/content/frontend/deprecations/components/deprecation_filters.vue @@ -1,12 +1,13 @@ <script> -import { GlFormSelect } from '@gitlab/ui'; +import { GlFormSelect, GlToggle } from '@gitlab/ui'; export default { components: { GlFormSelect, + GlToggle, }, props: { - milestonesList: { + milestonesOptions: { type: Array, required: true, }, @@ -17,88 +18,125 @@ export default { }, data() { return { - selected: this.showAllText, + emptyText: 'No deprecations found.', + hiddenClass: 'gl-display-none', + selected: { + removal_milestone: this.showAllText, + breaking_only: false, + }, + deprecations: [], + selectedDeprecations: [], + filtered: false, }; }, + computed: { + noResults() { + return this.filtered && this.selectedDeprecations.length === 0; + }, + }, created() { - // Pre-filter the page if the URL includes a valid version parameter. + // Initialize with an array of all deprecations. + document.querySelectorAll('.deprecation').forEach((el) => { + this.deprecations.push(el.getAttribute('data-deprecation-id')); + }); + + // Pre-filter the page if the URL includes a parameter. const searchParams = new URLSearchParams(window.location.search); - if (!searchParams.has('removal_milestone')) { - return; - } - const version = searchParams.get('removal_milestone').replace(/\./g, ''); - if (this.isValidVersion(version)) { - this.filterDeprecationList(version); - this.selected = version; + if (searchParams.has('removal_milestone') || searchParams.has('breaking_only')) { + this.selected.removal_milestone = searchParams.get('removal_milestone'); + this.selected.breaking_only = searchParams.get('breaking_only') === 'true'; + this.filterList(); } }, methods: { - isValidVersion(version) { - return this.milestonesList.some((e) => e.value === version); - }, - updateURLParams(option) { - const item = this.milestonesList.find((x) => x.value === option); + updateURLParams() { const url = new URL(window.location); - - if (item.text.length > 0 && item.text !== this.showAllText) { - url.searchParams.set('removal_milestone', item.text); - } else { - url.searchParams.delete('removal_milestone'); - } + Object.keys(this.selected).forEach((selectName) => { + if (this.selected[selectName] !== this.showAllText) { + url.searchParams.set(selectName, this.selected[selectName]); + } + }); window.history.pushState(null, '', url.toString()); }, - /** - * Filters the page down to a specified removal version. - * - * This method hides all deprecations that do not have the selected version - * in their wrapper div's class lists. - * - * @param {String} option - */ - filterDeprecationList(option) { - const hiddenClass = 'd-none'; + filterList() { + // Run the deprecations list through both filters. + this.selectedDeprecations = this.filterByBreaking(this.filterByVersion(this.deprecations)); - // Reset the list and show all deprecations and headers. - document.querySelectorAll('.deprecation, h2').forEach(function showAllSections(el) { - el.classList.remove(hiddenClass); + // Hide all headers initially. + document.querySelectorAll('.announcement-milestone').forEach((section) => { + section.children[0].classList.add(this.hiddenClass); }); - if (option !== this.showAllText) { - // Hide deprecations with non-selected versions. - document - .querySelectorAll(`.deprecation:not(.removal-${option})`) - .forEach(function hideDeprecationsAndHeader(el) { - el.classList.add(hiddenClass); - // Hide the "announcement version" section header. - el.parentElement.children[0].classList.add(hiddenClass); - }); + // Show selected deprecations; hide the others. + this.deprecations.forEach((depId) => { + const element = document.querySelector(`[data-deprecation-id="${depId}"]`); + if (this.selectedDeprecations.includes(depId)) { + element.classList.remove(this.hiddenClass); + // Ensure the section header is visible. + element.parentElement.children[0].classList.remove(this.hiddenClass); + } else { + element.classList.add(this.hiddenClass); + } + }); - // Show the "announcement version" header if we have deprecations in this section. - document - .querySelectorAll(`.deprecation.removal-${option}`) - .forEach(function showHeader(el) { - el.parentElement.children[0].classList.remove(hiddenClass); - }); + this.updateURLParams(); + this.filtered = true; + }, + filterByVersion(deps) { + let filteredDeps = deps; + if (this.selected.removal_milestone !== this.showAllText) { + filteredDeps = deps.filter((depID) => + document + .querySelector(`[data-deprecation-id="${depID}"]`) + .classList.contains(`removal-${this.selected.removal_milestone}`), + ); } - - // Update the removal_milestone parameter in the URL. - this.updateURLParams(option); + return filteredDeps; + }, + filterByBreaking(deps) { + let filteredDeps = deps; + if (this.selected.breaking_only === true) { + filteredDeps = deps.filter((depID) => + document + .querySelector(`[data-deprecation-id="${depID}"]`) + .classList.contains('breaking-change'), + ); + } + return filteredDeps; }, }, }; </script> <template> - <div class="mt-3 row"> - <div class="col-4"> - <label for="milestone" class="d-block col-form-label">Filter by removal version:</label> - <gl-form-select - v-model="selected" - name="milestone" - :options="milestonesList" - data-testid="removal-milestone-filter" - @change="filterDeprecationList(selected)" - /> + <div> + <div class="gl-mt-7 row"> + <div class="col gl-md-display-flex"> + <label + for="removal_milestone" + class="gl-font-weight-bold gl-mb-0 gl-mr-4 gl-display-flex gl-align-items-center" + >Filter by removal version</label + > + <gl-form-select + v-model="selected.removal_milestone" + class="gl-md-max-w-15p gl-mr-6" + name="removal_milestone" + :options="milestonesOptions" + data-testid="removal-milestone-filter" + @change="filterList()" + /> + + <gl-toggle + v-model="selected.breaking_only" + label="Show only breaking changes" + class="gl-mt-5 gl-md-mt-0" + name="breaking_only" + data-testid="breaking-filter" + label-position="left" + @change="filterList()" + /> + </div> </div> + <p v-if="noResults" class="gl-mt-5!">{{ emptyText }}</p> </div> </template> diff --git a/content/frontend/deprecations/filters.js b/content/frontend/deprecations/filters.js index 1be9d3a8..472cba50 100644 --- a/content/frontend/deprecations/filters.js +++ b/content/frontend/deprecations/filters.js @@ -3,28 +3,33 @@ import { compareVersions } from 'compare-versions'; import DeprecationFilters from './components/deprecation_filters.vue'; /** + * Add some helper markup to allow for simpler filter logic. + */ +document.querySelectorAll('.deprecation').forEach((el, index) => { + el.setAttribute('data-deprecation-id', index + 1); +}); + +/** * Builds an array of removal milestone options from page content. * * Each milestone object contains: * - A text string, used for labels in the select options list. - * This also appears as a query string value in the URL when filtering. * - A value string, which is the same as the text string, but without periods. * This is used to match the query with CSS classes on deprecations. * CSS classes cannot include periods, so we drop those for this element. * * @param {String} showAllText - * Label for default/unselected state. * @return {Array} */ const buildMilestonesList = (showAllText) => { let milestones = []; - document.querySelectorAll('.removal-milestone').forEach(function addOption(el) { + document.querySelectorAll('.removal-milestone').forEach((el) => { if (!milestones.includes(el.innerText)) { milestones.push(el.innerText); } }); milestones.sort(compareVersions).reverse(); - milestones = milestones.map(function addValues(el) { + milestones = milestones.map((el) => { return { value: el.replaceAll('.', ''), text: el }; }); milestones.unshift({ value: showAllText, text: showAllText }); @@ -33,7 +38,7 @@ const buildMilestonesList = (showAllText) => { document.addEventListener('DOMContentLoaded', () => { const showAllText = 'Show all'; - const milestonesList = buildMilestonesList(showAllText); + const milestonesOptions = buildMilestonesList(showAllText); return new Vue({ el: '.js-deprecation-filters', @@ -43,7 +48,7 @@ document.addEventListener('DOMContentLoaded', () => { render(createElement) { return createElement(DeprecationFilters, { props: { - milestonesList, + milestonesOptions, showAllText, }, }); diff --git a/spec/frontend/deprecations/deprecation_filters_spec.js b/spec/frontend/deprecations/deprecation_filters_spec.js index d5149792..5cf28357 100644 --- a/spec/frontend/deprecations/deprecation_filters_spec.js +++ b/spec/frontend/deprecations/deprecation_filters_spec.js @@ -5,32 +5,14 @@ import { mount } from '@vue/test-utils'; import DeprecationFilters from '../../../content/frontend/deprecations/components/deprecation_filters.vue'; -const propsData = { showAllText: 'Show all', milestonesList: [{ value: '160', text: '16.0' }] }; +const propsData = { showAllText: 'Show all', milestonesOptions: [{ value: '160', text: '16.0' }] }; const removalsFilterSelector = '[data-testid="removal-milestone-filter"]'; +const breakingFilterSelector = '[data-testid="breaking-filter"]'; describe('component: Deprecations Filter', () => { - it('Filter is visible', () => { + it('Filters are visible', () => { const wrapper = mount(DeprecationFilters, { propsData }); expect(wrapper.find(removalsFilterSelector).isVisible()).toBe(true); - }); - - it('Validates a URL parameter', () => { - const location = { - ...window.location, - search: '?removal_milestone=16.0', - toString: () => { - return 'http://localhost/ee/update/deprecations.html'; - }, - }; - Object.defineProperty(window, 'location', { - writable: true, - value: location, - }); - - const searchParams = new URLSearchParams(window.location.search); - const versionValue = searchParams.get('removal_milestone').replace(/\./g, ''); - - const wrapper = mount(DeprecationFilters, { propsData }); - expect(wrapper.vm.isValidVersion(versionValue)).toBe(true); + expect(wrapper.find(breakingFilterSelector).isVisible()).toBe(true); }); }); |