summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/releases/components
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/releases/components')
-rw-r--r--app/assets/javascripts/releases/components/app_edit_new.vue2
-rw-r--r--app/assets/javascripts/releases/components/app_index.vue287
-rw-r--r--app/assets/javascripts/releases/components/app_index_apollo_client.vue275
-rw-r--r--app/assets/javascripts/releases/components/release_block_footer.vue2
-rw-r--r--app/assets/javascripts/releases/components/releases_pagination.vue20
-rw-r--r--app/assets/javascripts/releases/components/releases_pagination_apollo_client.vue37
-rw-r--r--app/assets/javascripts/releases/components/releases_sort.vue71
-rw-r--r--app/assets/javascripts/releases/components/releases_sort_apollo_client.vue91
8 files changed, 283 insertions, 502 deletions
diff --git a/app/assets/javascripts/releases/components/app_edit_new.vue b/app/assets/javascripts/releases/components/app_edit_new.vue
index 39140216bc5..8365e6a5ab0 100644
--- a/app/assets/javascripts/releases/components/app_edit_new.vue
+++ b/app/assets/javascripts/releases/components/app_edit_new.vue
@@ -185,7 +185,7 @@ export default {
<gl-button
class="mr-auto js-no-auto-disable"
category="primary"
- variant="success"
+ variant="confirm"
type="submit"
:disabled="isFormSubmissionDisabled"
data-testid="submit-button"
diff --git a/app/assets/javascripts/releases/components/app_index.vue b/app/assets/javascripts/releases/components/app_index.vue
index e53bfea7389..59fa2fca736 100644
--- a/app/assets/javascripts/releases/components/app_index.vue
+++ b/app/assets/javascripts/releases/components/app_index.vue
@@ -1,67 +1,237 @@
<script>
-import { GlEmptyState, GlLink, GlButton } from '@gitlab/ui';
-import { mapState, mapActions } from 'vuex';
-import { getParameterByName } from '~/lib/utils/url_utility';
+import { GlButton } from '@gitlab/ui';
+import createFlash from '~/flash';
+import { historyPushState } from '~/lib/utils/common_utils';
+import { scrollUp } from '~/lib/utils/scroll_utils';
+import { setUrlParams, getParameterByName } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
+import { PAGE_SIZE, DEFAULT_SORT } from '~/releases/constants';
+import { convertAllReleasesGraphQLResponse } from '~/releases/util';
+import allReleasesQuery from '../graphql/queries/all_releases.query.graphql';
import ReleaseBlock from './release_block.vue';
import ReleaseSkeletonLoader from './release_skeleton_loader.vue';
+import ReleasesEmptyState from './releases_empty_state.vue';
import ReleasesPagination from './releases_pagination.vue';
import ReleasesSort from './releases_sort.vue';
export default {
- name: 'ReleasesApp',
+ name: 'ReleasesIndexApp',
components: {
- GlEmptyState,
- GlLink,
GlButton,
ReleaseBlock,
- ReleasesPagination,
ReleaseSkeletonLoader,
+ ReleasesEmptyState,
+ ReleasesPagination,
ReleasesSort,
},
+ inject: {
+ projectPath: {
+ default: '',
+ },
+ newReleasePath: {
+ default: '',
+ },
+ },
+ apollo: {
+ /**
+ * The same query as `fullGraphqlResponse`, except that it limits its
+ * results to a single item. This causes this request to complete much more
+ * quickly than `fullGraphqlResponse`, which allows the page to show
+ * meaningful content to the user much earlier.
+ */
+ singleGraphqlResponse: {
+ query: allReleasesQuery,
+ // This trick only works when paginating _forward_.
+ // When paginating backwards, limiting the query to a single item loads
+ // the _last_ item in the page, which is not useful for our purposes.
+ skip() {
+ return !this.includeSingleQuery;
+ },
+ variables() {
+ return {
+ ...this.queryVariables,
+ first: 1,
+ };
+ },
+ update(data) {
+ return { data };
+ },
+ error() {
+ this.singleRequestError = true;
+ },
+ },
+ fullGraphqlResponse: {
+ query: allReleasesQuery,
+ variables() {
+ return this.queryVariables;
+ },
+ update(data) {
+ return { data };
+ },
+ error(error) {
+ this.fullRequestError = true;
+
+ createFlash({
+ message: this.$options.i18n.errorMessage,
+ captureError: true,
+ error,
+ });
+ },
+ },
+ },
+ data() {
+ return {
+ singleRequestError: false,
+ fullRequestError: false,
+ cursors: {
+ before: getParameterByName('before'),
+ after: getParameterByName('after'),
+ },
+ sort: DEFAULT_SORT,
+ };
+ },
computed: {
- ...mapState('index', [
- 'documentationPath',
- 'illustrationPath',
- 'newReleasePath',
- 'isLoading',
- 'releases',
- 'hasError',
- ]),
- shouldRenderEmptyState() {
- return !this.releases.length && !this.hasError && !this.isLoading;
+ queryVariables() {
+ let paginationParams = { first: PAGE_SIZE };
+ if (this.cursors.after) {
+ paginationParams = {
+ after: this.cursors.after,
+ first: PAGE_SIZE,
+ };
+ } else if (this.cursors.before) {
+ paginationParams = {
+ before: this.cursors.before,
+ last: PAGE_SIZE,
+ };
+ }
+
+ return {
+ fullPath: this.projectPath,
+ ...paginationParams,
+ sort: this.sort,
+ };
+ },
+ /**
+ * @returns {Boolean} Whether or not to request/include
+ * the results of the single-item query
+ */
+ includeSingleQuery() {
+ return Boolean(!this.cursors.before || this.cursors.after);
+ },
+ isSingleRequestLoading() {
+ return this.$apollo.queries.singleGraphqlResponse.loading;
},
- shouldRenderSuccessState() {
- return this.releases.length && !this.isLoading && !this.hasError;
+ isFullRequestLoading() {
+ return this.$apollo.queries.fullGraphqlResponse.loading;
+ },
+ /**
+ * @returns {Boolean} `true` if the `singleGraphqlResponse`
+ * query has finished loading without errors
+ */
+ isSingleRequestLoaded() {
+ return Boolean(!this.isSingleRequestLoading && this.singleGraphqlResponse?.data.project);
+ },
+ /**
+ * @returns {Boolean} `true` if the `fullGraphqlResponse`
+ * query has finished loading without errors
+ */
+ isFullRequestLoaded() {
+ return Boolean(!this.isFullRequestLoading && this.fullGraphqlResponse?.data.project);
+ },
+ releases() {
+ if (this.isFullRequestLoaded) {
+ return convertAllReleasesGraphQLResponse(this.fullGraphqlResponse).data;
+ }
+
+ if (this.isSingleRequestLoaded && this.includeSingleQuery) {
+ return convertAllReleasesGraphQLResponse(this.singleGraphqlResponse).data;
+ }
+
+ return [];
},
- emptyStateText() {
- return __(
- "Releases are based on Git tags and mark specific points in a project's development history. They can contain information about the type of changes and can also deliver binaries, like compiled versions of your software.",
+ pageInfo() {
+ if (!this.isFullRequestLoaded) {
+ return {
+ hasPreviousPage: false,
+ hasNextPage: false,
+ };
+ }
+
+ return this.fullGraphqlResponse.data.project.releases.pageInfo;
+ },
+ shouldRenderEmptyState() {
+ return this.isFullRequestLoaded && this.releases.length === 0;
+ },
+ shouldRenderLoadingIndicator() {
+ return (
+ (this.isSingleRequestLoading && !this.singleRequestError && !this.isFullRequestLoaded) ||
+ (this.isFullRequestLoading && !this.fullRequestError)
);
},
+ shouldRenderPagination() {
+ return this.isFullRequestLoaded && !this.shouldRenderEmptyState;
+ },
},
created() {
- this.fetchReleases();
+ this.updateQueryParamsFromUrl();
- window.addEventListener('popstate', this.fetchReleases);
+ window.addEventListener('popstate', this.updateQueryParamsFromUrl);
+ },
+ destroyed() {
+ window.removeEventListener('popstate', this.updateQueryParamsFromUrl);
},
methods: {
- ...mapActions('index', {
- fetchReleasesStoreAction: 'fetchReleases',
- }),
- fetchReleases() {
- this.fetchReleasesStoreAction({
- before: getParameterByName('before'),
- after: getParameterByName('after'),
- });
+ getReleaseKey(release, index) {
+ return [release.tagName, release.name, index].join('|');
+ },
+ updateQueryParamsFromUrl() {
+ this.cursors.before = getParameterByName('before');
+ this.cursors.after = getParameterByName('after');
+ },
+ onPaginationButtonPress() {
+ this.updateQueryParamsFromUrl();
+
+ // In some cases, Apollo Client is able to pull its results from the cache instead of making
+ // a new network request. In these cases, the page's content gets swapped out immediately without
+ // changing the page's scroll, leaving the user looking at the bottom of the new page.
+ // To make the experience consistent, regardless of how the data is sourced, we manually
+ // scroll to the top of the page every time a pagination button is pressed.
+ scrollUp();
+ },
+ onSortChanged(newSort) {
+ if (this.sort === newSort) {
+ return;
+ }
+
+ // Remove the "before" and "after" query parameters from the URL,
+ // effectively placing the user back on page 1 of the results.
+ // This prevents the frontend from requesting the results sorted
+ // by one field (e.g. `released_at`) while using a pagination cursor
+ // intended for a different field (e.g.) `created_at`).
+ // For more details, see the MR that introduced this change:
+ // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63434
+ historyPushState(
+ setUrlParams({
+ before: null,
+ after: null,
+ }),
+ );
+
+ this.updateQueryParamsFromUrl();
+
+ this.sort = newSort;
},
},
+ i18n: {
+ newRelease: __('New release'),
+ errorMessage: __('An error occurred while fetching the releases. Please try again.'),
+ },
};
</script>
<template>
<div class="flex flex-column mt-2">
<div class="gl-align-self-end gl-mb-3">
- <releases-sort class="gl-mr-2" @sort:changed="fetchReleases" />
+ <releases-sort :value="sort" class="gl-mr-2" @input="onSortChanged" />
<gl-button
v-if="newReleasePath"
@@ -69,44 +239,27 @@ export default {
:aria-describedby="shouldRenderEmptyState && 'releases-description'"
category="primary"
variant="confirm"
- data-testid="new-release-button"
+ >{{ $options.i18n.newRelease }}</gl-button
>
- {{ __('New release') }}
- </gl-button>
</div>
- <release-skeleton-loader v-if="isLoading" />
-
- <gl-empty-state
- v-else-if="shouldRenderEmptyState"
- data-testid="empty-state"
- :title="__('Getting started with releases')"
- :svg-path="illustrationPath"
- >
- <template #description>
- <span id="releases-description">
- {{ emptyStateText }}
- <gl-link
- :href="documentationPath"
- :aria-label="__('Releases documentation')"
- target="_blank"
- >
- {{ __('More information') }}
- </gl-link>
- </span>
- </template>
- </gl-empty-state>
-
- <div v-else-if="shouldRenderSuccessState" data-testid="success-state">
- <release-block
- v-for="(release, index) in releases"
- :key="index"
- :release="release"
- :class="{ 'linked-card': releases.length > 1 && index !== releases.length - 1 }"
- />
- </div>
+ <releases-empty-state v-if="shouldRenderEmptyState" />
+
+ <release-block
+ v-for="(release, index) in releases"
+ :key="getReleaseKey(release, index)"
+ :release="release"
+ :class="{ 'linked-card': releases.length > 1 && index !== releases.length - 1 }"
+ />
+
+ <release-skeleton-loader v-if="shouldRenderLoadingIndicator" />
- <releases-pagination v-if="!isLoading" />
+ <releases-pagination
+ v-if="shouldRenderPagination"
+ :page-info="pageInfo"
+ @prev="onPaginationButtonPress"
+ @next="onPaginationButtonPress"
+ />
</div>
</template>
<style>
diff --git a/app/assets/javascripts/releases/components/app_index_apollo_client.vue b/app/assets/javascripts/releases/components/app_index_apollo_client.vue
deleted file mode 100644
index f49c44a399f..00000000000
--- a/app/assets/javascripts/releases/components/app_index_apollo_client.vue
+++ /dev/null
@@ -1,275 +0,0 @@
-<script>
-import { GlButton } from '@gitlab/ui';
-import allReleasesQuery from 'shared_queries/releases/all_releases.query.graphql';
-import createFlash from '~/flash';
-import { historyPushState } from '~/lib/utils/common_utils';
-import { scrollUp } from '~/lib/utils/scroll_utils';
-import { setUrlParams, getParameterByName } from '~/lib/utils/url_utility';
-import { __ } from '~/locale';
-import { PAGE_SIZE, DEFAULT_SORT } from '~/releases/constants';
-import { convertAllReleasesGraphQLResponse } from '~/releases/util';
-import ReleaseBlock from './release_block.vue';
-import ReleaseSkeletonLoader from './release_skeleton_loader.vue';
-import ReleasesEmptyState from './releases_empty_state.vue';
-import ReleasesPaginationApolloClient from './releases_pagination_apollo_client.vue';
-import ReleasesSortApolloClient from './releases_sort_apollo_client.vue';
-
-export default {
- name: 'ReleasesIndexApolloClientApp',
- components: {
- GlButton,
- ReleaseBlock,
- ReleaseSkeletonLoader,
- ReleasesEmptyState,
- ReleasesPaginationApolloClient,
- ReleasesSortApolloClient,
- },
- inject: {
- projectPath: {
- default: '',
- },
- newReleasePath: {
- default: '',
- },
- },
- apollo: {
- /**
- * The same query as `fullGraphqlResponse`, except that it limits its
- * results to a single item. This causes this request to complete much more
- * quickly than `fullGraphqlResponse`, which allows the page to show
- * meaningful content to the user much earlier.
- */
- singleGraphqlResponse: {
- query: allReleasesQuery,
- // This trick only works when paginating _forward_.
- // When paginating backwards, limiting the query to a single item loads
- // the _last_ item in the page, which is not useful for our purposes.
- skip() {
- return !this.includeSingleQuery;
- },
- variables() {
- return {
- ...this.queryVariables,
- first: 1,
- };
- },
- update(data) {
- return { data };
- },
- error() {
- this.singleRequestError = true;
- },
- },
- fullGraphqlResponse: {
- query: allReleasesQuery,
- variables() {
- return this.queryVariables;
- },
- update(data) {
- return { data };
- },
- error(error) {
- this.fullRequestError = true;
-
- createFlash({
- message: this.$options.i18n.errorMessage,
- captureError: true,
- error,
- });
- },
- },
- },
- data() {
- return {
- singleRequestError: false,
- fullRequestError: false,
- cursors: {
- before: getParameterByName('before'),
- after: getParameterByName('after'),
- },
- sort: DEFAULT_SORT,
- };
- },
- computed: {
- queryVariables() {
- let paginationParams = { first: PAGE_SIZE };
- if (this.cursors.after) {
- paginationParams = {
- after: this.cursors.after,
- first: PAGE_SIZE,
- };
- } else if (this.cursors.before) {
- paginationParams = {
- before: this.cursors.before,
- last: PAGE_SIZE,
- };
- }
-
- return {
- fullPath: this.projectPath,
- ...paginationParams,
- sort: this.sort,
- };
- },
- /**
- * @returns {Boolean} Whether or not to request/include
- * the results of the single-item query
- */
- includeSingleQuery() {
- return Boolean(!this.cursors.before || this.cursors.after);
- },
- isSingleRequestLoading() {
- return this.$apollo.queries.singleGraphqlResponse.loading;
- },
- isFullRequestLoading() {
- return this.$apollo.queries.fullGraphqlResponse.loading;
- },
- /**
- * @returns {Boolean} `true` if the `singleGraphqlResponse`
- * query has finished loading without errors
- */
- isSingleRequestLoaded() {
- return Boolean(!this.isSingleRequestLoading && this.singleGraphqlResponse?.data.project);
- },
- /**
- * @returns {Boolean} `true` if the `fullGraphqlResponse`
- * query has finished loading without errors
- */
- isFullRequestLoaded() {
- return Boolean(!this.isFullRequestLoading && this.fullGraphqlResponse?.data.project);
- },
- releases() {
- if (this.isFullRequestLoaded) {
- return convertAllReleasesGraphQLResponse(this.fullGraphqlResponse).data;
- }
-
- if (this.isSingleRequestLoaded && this.includeSingleQuery) {
- return convertAllReleasesGraphQLResponse(this.singleGraphqlResponse).data;
- }
-
- return [];
- },
- pageInfo() {
- if (!this.isFullRequestLoaded) {
- return {
- hasPreviousPage: false,
- hasNextPage: false,
- };
- }
-
- return this.fullGraphqlResponse.data.project.releases.pageInfo;
- },
- shouldRenderEmptyState() {
- return this.isFullRequestLoaded && this.releases.length === 0;
- },
- shouldRenderLoadingIndicator() {
- return (
- (this.isSingleRequestLoading && !this.singleRequestError && !this.isFullRequestLoaded) ||
- (this.isFullRequestLoading && !this.fullRequestError)
- );
- },
- shouldRenderPagination() {
- return this.isFullRequestLoaded && !this.shouldRenderEmptyState;
- },
- },
- created() {
- this.updateQueryParamsFromUrl();
-
- window.addEventListener('popstate', this.updateQueryParamsFromUrl);
- },
- destroyed() {
- window.removeEventListener('popstate', this.updateQueryParamsFromUrl);
- },
- methods: {
- getReleaseKey(release, index) {
- return [release.tagName, release.name, index].join('|');
- },
- updateQueryParamsFromUrl() {
- this.cursors.before = getParameterByName('before');
- this.cursors.after = getParameterByName('after');
- },
- onPaginationButtonPress() {
- this.updateQueryParamsFromUrl();
-
- // In some cases, Apollo Client is able to pull its results from the cache instead of making
- // a new network request. In these cases, the page's content gets swapped out immediately without
- // changing the page's scroll, leaving the user looking at the bottom of the new page.
- // To make the experience consistent, regardless of how the data is sourced, we manually
- // scroll to the top of the page every time a pagination button is pressed.
- scrollUp();
- },
- onSortChanged(newSort) {
- if (this.sort === newSort) {
- return;
- }
-
- // Remove the "before" and "after" query parameters from the URL,
- // effectively placing the user back on page 1 of the results.
- // This prevents the frontend from requesting the results sorted
- // by one field (e.g. `released_at`) while using a pagination cursor
- // intended for a different field (e.g.) `created_at`).
- // For more details, see the MR that introduced this change:
- // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63434
- historyPushState(
- setUrlParams({
- before: null,
- after: null,
- }),
- );
-
- this.updateQueryParamsFromUrl();
-
- this.sort = newSort;
- },
- },
- i18n: {
- newRelease: __('New release'),
- errorMessage: __('An error occurred while fetching the releases. Please try again.'),
- },
-};
-</script>
-<template>
- <div class="flex flex-column mt-2">
- <div class="gl-align-self-end gl-mb-3">
- <releases-sort-apollo-client :value="sort" class="gl-mr-2" @input="onSortChanged" />
-
- <gl-button
- v-if="newReleasePath"
- :href="newReleasePath"
- :aria-describedby="shouldRenderEmptyState && 'releases-description'"
- category="primary"
- variant="success"
- >{{ $options.i18n.newRelease }}</gl-button
- >
- </div>
-
- <releases-empty-state v-if="shouldRenderEmptyState" />
-
- <release-block
- v-for="(release, index) in releases"
- :key="getReleaseKey(release, index)"
- :release="release"
- :class="{ 'linked-card': releases.length > 1 && index !== releases.length - 1 }"
- />
-
- <release-skeleton-loader v-if="shouldRenderLoadingIndicator" />
-
- <releases-pagination-apollo-client
- v-if="shouldRenderPagination"
- :page-info="pageInfo"
- @prev="onPaginationButtonPress"
- @next="onPaginationButtonPress"
- />
- </div>
-</template>
-<style>
-.linked-card::after {
- width: 1px;
- content: ' ';
- border: 1px solid #e5e5e5;
- height: 17px;
- top: 100%;
- position: absolute;
- left: 32px;
-}
-</style>
diff --git a/app/assets/javascripts/releases/components/release_block_footer.vue b/app/assets/javascripts/releases/components/release_block_footer.vue
index cb795b3cba7..91d6d0911a4 100644
--- a/app/assets/javascripts/releases/components/release_block_footer.vue
+++ b/app/assets/javascripts/releases/components/release_block_footer.vue
@@ -104,9 +104,11 @@ export default {
<div v-if="author" class="d-flex">
<span class="text-secondary">{{ __('by') }}&nbsp;</span>
<user-avatar-link
+ class="gl-my-n1"
:link-href="author.webUrl"
:img-src="author.avatarUrl"
:img-alt="userImageAltDescription"
+ :img-size="24"
:tooltip-text="author.username"
tooltip-placement="bottom"
/>
diff --git a/app/assets/javascripts/releases/components/releases_pagination.vue b/app/assets/javascripts/releases/components/releases_pagination.vue
index fddf85ead1e..52ad991d61a 100644
--- a/app/assets/javascripts/releases/components/releases_pagination.vue
+++ b/app/assets/javascripts/releases/components/releases_pagination.vue
@@ -1,26 +1,24 @@
<script>
import { GlKeysetPagination } from '@gitlab/ui';
-import { mapActions, mapState } from 'vuex';
+import { isBoolean } from 'lodash';
import { historyPushState, buildUrlWithCurrentLocation } from '~/lib/utils/common_utils';
export default {
- name: 'ReleasesPaginationGraphql',
+ name: 'ReleasesPagination',
components: { GlKeysetPagination },
- computed: {
- ...mapState('index', ['pageInfo']),
- showPagination() {
- return this.pageInfo.hasPreviousPage || this.pageInfo.hasNextPage;
+ props: {
+ pageInfo: {
+ type: Object,
+ required: true,
+ validator: (info) => isBoolean(info.hasPreviousPage) && isBoolean(info.hasNextPage),
},
},
methods: {
- ...mapActions('index', ['fetchReleases']),
onPrev(before) {
historyPushState(buildUrlWithCurrentLocation(`?before=${before}`));
- this.fetchReleases({ before });
},
onNext(after) {
historyPushState(buildUrlWithCurrentLocation(`?after=${after}`));
- this.fetchReleases({ after });
},
},
};
@@ -28,8 +26,10 @@ export default {
<template>
<div class="gl-display-flex gl-justify-content-center">
<gl-keyset-pagination
- v-if="showPagination"
v-bind="pageInfo"
+ :prev-text="__('Prev')"
+ :next-text="__('Next')"
+ v-on="$listeners"
@prev="onPrev($event)"
@next="onNext($event)"
/>
diff --git a/app/assets/javascripts/releases/components/releases_pagination_apollo_client.vue b/app/assets/javascripts/releases/components/releases_pagination_apollo_client.vue
deleted file mode 100644
index 73339677a4b..00000000000
--- a/app/assets/javascripts/releases/components/releases_pagination_apollo_client.vue
+++ /dev/null
@@ -1,37 +0,0 @@
-<script>
-import { GlKeysetPagination } from '@gitlab/ui';
-import { isBoolean } from 'lodash';
-import { historyPushState, buildUrlWithCurrentLocation } from '~/lib/utils/common_utils';
-
-export default {
- name: 'ReleasesPaginationApolloClient',
- components: { GlKeysetPagination },
- props: {
- pageInfo: {
- type: Object,
- required: true,
- validator: (info) => isBoolean(info.hasPreviousPage) && isBoolean(info.hasNextPage),
- },
- },
- methods: {
- onPrev(before) {
- historyPushState(buildUrlWithCurrentLocation(`?before=${before}`));
- },
- onNext(after) {
- historyPushState(buildUrlWithCurrentLocation(`?after=${after}`));
- },
- },
-};
-</script>
-<template>
- <div class="gl-display-flex gl-justify-content-center">
- <gl-keyset-pagination
- v-bind="pageInfo"
- :prev-text="__('Prev')"
- :next-text="__('Next')"
- v-on="$listeners"
- @prev="onPrev($event)"
- @next="onNext($event)"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/releases/components/releases_sort.vue b/app/assets/javascripts/releases/components/releases_sort.vue
index d4210dad19c..0f14b579da0 100644
--- a/app/assets/javascripts/releases/components/releases_sort.vue
+++ b/app/assets/javascripts/releases/components/releases_sort.vue
@@ -1,7 +1,17 @@
<script>
import { GlSorting, GlSortingItem } from '@gitlab/ui';
-import { mapState, mapActions } from 'vuex';
-import { ASCENDING_ORDER, DESCENDING_ORDER, SORT_OPTIONS } from '../constants';
+import {
+ ASCENDING_ORDER,
+ DESCENDING_ORDER,
+ SORT_OPTIONS,
+ RELEASED_AT,
+ CREATED_AT,
+ RELEASED_AT_ASC,
+ RELEASED_AT_DESC,
+ CREATED_ASC,
+ ALL_SORTS,
+ SORT_MAP,
+} from '../constants';
export default {
name: 'ReleasesSort',
@@ -9,35 +19,54 @@ export default {
GlSorting,
GlSortingItem,
},
+ props: {
+ value: {
+ type: String,
+ required: true,
+ validator: (sort) => ALL_SORTS.includes(sort),
+ },
+ },
computed: {
- ...mapState('index', {
- orderBy: (state) => state.sorting.orderBy,
- sort: (state) => state.sorting.sort,
- }),
+ orderBy() {
+ if (this.value === RELEASED_AT_ASC || this.value === RELEASED_AT_DESC) {
+ return RELEASED_AT;
+ }
+
+ return CREATED_AT;
+ },
+ direction() {
+ if (this.value === RELEASED_AT_ASC || this.value === CREATED_ASC) {
+ return ASCENDING_ORDER;
+ }
+
+ return DESCENDING_ORDER;
+ },
sortOptions() {
return SORT_OPTIONS;
},
sortText() {
- const option = this.sortOptions.find((s) => s.orderBy === this.orderBy);
- return option.label;
+ return this.sortOptions.find((s) => s.orderBy === this.orderBy).label;
},
- isSortAscending() {
- return this.sort === ASCENDING_ORDER;
+ isDirectionAscending() {
+ return this.direction === ASCENDING_ORDER;
},
},
methods: {
- ...mapActions('index', ['setSorting']),
onDirectionChange() {
- const sort = this.isSortAscending ? DESCENDING_ORDER : ASCENDING_ORDER;
- this.setSorting({ sort });
- this.$emit('sort:changed');
+ const direction = this.isDirectionAscending ? DESCENDING_ORDER : ASCENDING_ORDER;
+ this.emitInputEventIfChanged(this.orderBy, direction);
},
onSortItemClick(item) {
- this.setSorting({ orderBy: item });
- this.$emit('sort:changed');
+ this.emitInputEventIfChanged(item.orderBy, this.direction);
},
isActiveSortItem(item) {
- return this.orderBy === item;
+ return this.orderBy === item.orderBy;
+ },
+ emitInputEventIfChanged(orderBy, direction) {
+ const newSort = SORT_MAP[orderBy][direction];
+ if (newSort !== this.value) {
+ this.$emit('input', SORT_MAP[orderBy][direction]);
+ }
},
},
};
@@ -46,15 +75,15 @@ export default {
<template>
<gl-sorting
:text="sortText"
- :is-ascending="isSortAscending"
+ :is-ascending="isDirectionAscending"
data-testid="releases-sort"
@sortDirectionChange="onDirectionChange"
>
<gl-sorting-item
- v-for="item in sortOptions"
+ v-for="item of sortOptions"
:key="item.orderBy"
- :active="isActiveSortItem(item.orderBy)"
- @click="onSortItemClick(item.orderBy)"
+ :active="isActiveSortItem(item)"
+ @click="onSortItemClick(item)"
>
{{ item.label }}
</gl-sorting-item>
diff --git a/app/assets/javascripts/releases/components/releases_sort_apollo_client.vue b/app/assets/javascripts/releases/components/releases_sort_apollo_client.vue
deleted file mode 100644
index 7257b34bbf6..00000000000
--- a/app/assets/javascripts/releases/components/releases_sort_apollo_client.vue
+++ /dev/null
@@ -1,91 +0,0 @@
-<script>
-import { GlSorting, GlSortingItem } from '@gitlab/ui';
-import {
- ASCENDING_ORDER,
- DESCENDING_ORDER,
- SORT_OPTIONS,
- RELEASED_AT,
- CREATED_AT,
- RELEASED_AT_ASC,
- RELEASED_AT_DESC,
- CREATED_ASC,
- ALL_SORTS,
- SORT_MAP,
-} from '../constants';
-
-export default {
- name: 'ReleasesSortApolloclient',
- components: {
- GlSorting,
- GlSortingItem,
- },
- props: {
- value: {
- type: String,
- required: true,
- validator: (sort) => ALL_SORTS.includes(sort),
- },
- },
- computed: {
- orderBy() {
- if (this.value === RELEASED_AT_ASC || this.value === RELEASED_AT_DESC) {
- return RELEASED_AT;
- }
-
- return CREATED_AT;
- },
- direction() {
- if (this.value === RELEASED_AT_ASC || this.value === CREATED_ASC) {
- return ASCENDING_ORDER;
- }
-
- return DESCENDING_ORDER;
- },
- sortOptions() {
- return SORT_OPTIONS;
- },
- sortText() {
- return this.sortOptions.find((s) => s.orderBy === this.orderBy).label;
- },
- isDirectionAscending() {
- return this.direction === ASCENDING_ORDER;
- },
- },
- methods: {
- onDirectionChange() {
- const direction = this.isDirectionAscending ? DESCENDING_ORDER : ASCENDING_ORDER;
- this.emitInputEventIfChanged(this.orderBy, direction);
- },
- onSortItemClick(item) {
- this.emitInputEventIfChanged(item.orderBy, this.direction);
- },
- isActiveSortItem(item) {
- return this.orderBy === item.orderBy;
- },
- emitInputEventIfChanged(orderBy, direction) {
- const newSort = SORT_MAP[orderBy][direction];
- if (newSort !== this.value) {
- this.$emit('input', SORT_MAP[orderBy][direction]);
- }
- },
- },
-};
-</script>
-
-<template>
- <gl-sorting
- :text="sortText"
- :is-ascending="isDirectionAscending"
- data-testid="releases-sort"
- @sortDirectionChange="onDirectionChange"
- >
- <gl-sorting-item
- v-for="item of sortOptions"
- :key="item.orderBy"
- :active="isActiveSortItem(item)"
- @click="onSortItemClick(item)"
- >
- {{ item.label }}
- </gl-sorting-item>
- </gl-sorting>
-</template>