Commit e40a1608 authored by Olena Horal-Koretska's avatar Olena Horal-Koretska Committed by Natalia Tepluhina

Improve error list UI on mobile viewports

Create adaptive layout for xs viewport
parent 383ead5d
...@@ -25,10 +25,33 @@ export default { ...@@ -25,10 +25,33 @@ export default {
PREV_PAGE: 1, PREV_PAGE: 1,
NEXT_PAGE: 2, NEXT_PAGE: 2,
fields: [ fields: [
{ key: 'error', label: __('Open errors'), thClass: 'w-70p' }, {
{ key: 'events', label: __('Events') }, key: 'error',
{ key: 'users', label: __('Users') }, label: __('Error'),
{ key: 'lastSeen', label: __('Last seen'), thClass: 'w-15p' }, thClass: 'w-70p',
tdClass: 'table-col d-flex align-items-center d-sm-table-cell',
},
{
key: 'events',
label: __('Events'),
tdClass: 'table-col d-flex align-items-center d-sm-table-cell',
},
{
key: 'users',
label: __('Users'),
tdClass: 'table-col d-flex align-items-center d-sm-table-cell',
},
{
key: 'lastSeen',
label: __('Last seen'),
thClass: 'w-15p',
tdClass: 'table-col d-flex align-items-center d-sm-table-cell',
},
{
key: 'details',
tdClass: 'table-col d-sm-none d-flex align-items-center',
thClass: 'invisible w-0',
},
], ],
sortFields: { sortFields: {
last_seen: __('Last Seen'), last_seen: __('Last Seen'),
...@@ -149,12 +172,13 @@ export default { ...@@ -149,12 +172,13 @@ export default {
<div class="error-list"> <div class="error-list">
<div v-if="errorTrackingEnabled"> <div v-if="errorTrackingEnabled">
<div <div
class="d-flex flex-row justify-content-around align-items-center bg-secondary border mt-2" class="row flex-column flex-sm-row align-items-sm-center row-top m-0 mt-sm-2 mx-sm-1 p-0 p-sm-3"
> >
<div class="filtered-search-box flex-grow-1 my-3 ml-3 mr-2"> <div class="search-box flex-fill mr-sm-2 my-3 m-sm-0 p-3 p-sm-0">
<div class="filtered-search-box mb-0">
<gl-dropdown <gl-dropdown
:text="__('Recent searches')" :text="__('Recent searches')"
class="filtered-search-history-dropdown-wrapper d-none d-md-block" class="filtered-search-history-dropdown-wrapper"
toggle-class="filtered-search-history-dropdown-toggle-button" toggle-class="filtered-search-history-dropdown-toggle-button"
:disabled="loading" :disabled="loading"
> >
...@@ -166,12 +190,12 @@ export default { ...@@ -166,12 +190,12 @@ export default {
v-for="searchQuery in recentSearches" v-for="searchQuery in recentSearches"
:key="searchQuery" :key="searchQuery"
@click="setSearchText(searchQuery)" @click="setSearchText(searchQuery)"
>{{ searchQuery }}</gl-dropdown-item >{{ searchQuery }}
> </gl-dropdown-item>
<gl-dropdown-divider /> <gl-dropdown-divider />
<gl-dropdown-item ref="clearRecentSearches" @click="clearRecentSearches">{{ <gl-dropdown-item ref="clearRecentSearches" @click="clearRecentSearches"
__('Clear recent searches') >{{ __('Clear recent searches') }}
}}</gl-dropdown-item> </gl-dropdown-item>
</template> </template>
<div v-else class="px-3">{{ __("You don't have any recent searches") }}</div> <div v-else class="px-3">{{ __("You don't have any recent searches") }}</div>
</gl-dropdown> </gl-dropdown>
...@@ -198,12 +222,13 @@ export default { ...@@ -198,12 +222,13 @@ export default {
</gl-button> </gl-button>
</div> </div>
</div> </div>
</div>
<gl-dropdown <gl-dropdown
class="sort-control"
:text="$options.sortFields[sortField]" :text="$options.sortFields[sortField]"
left left
:disabled="loading" :disabled="loading"
class="mr-3"
menu-class="sort-dropdown" menu-class="sort-dropdown"
> >
<gl-dropdown-item <gl-dropdown-item
...@@ -227,51 +252,65 @@ export default { ...@@ -227,51 +252,65 @@ export default {
<gl-loading-icon size="md" /> <gl-loading-icon size="md" />
</div> </div>
<template v-else>
<h4 class="d-block d-sm-none my-3">{{ __('Open errors') }}</h4>
<gl-table <gl-table
v-else
class="mt-3" class="mt-3"
:items="errors" :items="errors"
:fields="$options.fields" :fields="$options.fields"
:show-empty="true" :show-empty="true"
fixed fixed
stacked="sm" stacked="sm"
tbody-tr-class="table-row mb-4"
> >
<template slot="HEAD_events" slot-scope="data"> <template v-slot:head(error)>
<div class="text-md-right">{{ data.label }}</div> <div class="d-none d-sm-block">{{ __('Open errors') }}</div>
</template>
<template v-slot:head(events)="data">
<div class="text-sm-right">{{ data.label }}</div>
</template> </template>
<template slot="HEAD_users" slot-scope="data"> <template v-slot:head(users)="data">
<div class="text-md-right">{{ data.label }}</div> <div class="text-sm-right">{{ data.label }}</div>
</template> </template>
<template slot="error" slot-scope="errors">
<template v-slot:error="errors">
<div class="d-flex flex-column"> <div class="d-flex flex-column">
<gl-link class="d-flex text-dark" :href="getDetailsLink(errors.item.id)"> <gl-link class="d-flex mw-100 text-dark" :href="getDetailsLink(errors.item.id)">
<strong class="text-truncate">{{ errors.item.title.trim() }}</strong> <strong class="text-truncate">{{ errors.item.title.trim() }}</strong>
</gl-link> </gl-link>
<span class="text-secondary text-truncate"> <span class="text-secondary text-truncate mw-100">
{{ errors.item.culprit }} {{ errors.item.culprit }}
</span> </span>
</div> </div>
</template> </template>
<template slot="events" slot-scope="errors"> <template v-slot:events="errors">
<div class="text-md-right">{{ errors.item.count }}</div> <div class="text-right">{{ errors.item.count }}</div>
</template> </template>
<template slot="users" slot-scope="errors"> <template v-slot:users="errors">
<div class="text-md-right">{{ errors.item.userCount }}</div> <div class="text-right">{{ errors.item.userCount }}</div>
</template> </template>
<template slot="lastSeen" slot-scope="errors"> <template v-slot:lastSeen="errors">
<div class="d-flex align-items-center"> <div class="text-md-left text-right">
<time-ago :time="errors.item.lastSeen" class="text-secondary" /> <time-ago :time="errors.item.lastSeen" class="text-secondary" />
</div> </div>
</template> </template>
<template slot="empty"> <template v-slot:details="errors">
<div ref="empty"> <gl-button
:href="getDetailsLink(errors.item.id)"
variant="outline-info"
class="d-block"
>
{{ __('More details') }}
</gl-button>
</template>
<template v-slot:empty>
{{ __('No errors to display.') }} {{ __('No errors to display.') }}
<gl-link class="js-try-again" @click="restartPolling"> <gl-link class="js-try-again" @click="restartPolling">
{{ __('Check again') }} {{ __('Check again') }}
</gl-link> </gl-link>
</div>
</template> </template>
</gl-table> </gl-table>
<gl-pagination <gl-pagination
...@@ -283,6 +322,7 @@ export default { ...@@ -283,6 +322,7 @@ export default {
align="center" align="center"
@input="goToPage" @input="goToPage"
/> />
</template>
</div> </div>
<div v-else-if="userCanEnableErrorTracking"> <div v-else-if="userCanEnableErrorTracking">
<gl-empty-state <gl-empty-state
......
$gray-border: 1px solid $border-color;
.error-list {
.sort-control {
.btn {
padding-right: 2rem;
}
.gl-dropdown-caret {
position: absolute;
right: 0.5rem;
top: 0.5rem;
}
}
@include media-breakpoint-up(sm) {
.row-top {
border: $gray-border;
background-color: $gray-50;
}
}
@include media-breakpoint-down(xs) {
.table-row {
border: $gray-border;
border-radius: 4px;
}
.search-box {
border-top: $gray-border;
border-bottom: $gray-border;
background-color: $gray-50;
}
.table-col {
min-height: 68px;
&::before {
text-align: left !important;
}
&:first-child {
div {
padding: 0 !important;
align-items: flex-end;
}
}
&:last-child {
height: 64px;
background-color: $gray-normal;
&::before {
content: none !important;
}
div {
width: 100% !important;
padding: 0 !important;
a {
color: $blue-500;
border-color: $blue-500;
}
}
}
}
}
}
---
title: Improve error list UI on mobile viewports
merge_request: 21192
author:
type: added
...@@ -11548,6 +11548,9 @@ msgstr "" ...@@ -11548,6 +11548,9 @@ msgstr ""
msgid "More actions" msgid "More actions"
msgstr "" msgstr ""
msgid "More details"
msgstr ""
msgid "More info" msgid "More info"
msgstr "" msgstr ""
......
...@@ -272,6 +272,7 @@ describe('ErrorTrackingList', () => { ...@@ -272,6 +272,7 @@ describe('ErrorTrackingList', () => {
describe('When pagination is not required', () => { describe('When pagination is not required', () => {
beforeEach(() => { beforeEach(() => {
store.state.list.loading = false;
store.state.list.pagination = {}; store.state.list.pagination = {};
mountComponent(); mountComponent();
}); });
...@@ -284,6 +285,7 @@ describe('ErrorTrackingList', () => { ...@@ -284,6 +285,7 @@ describe('ErrorTrackingList', () => {
describe('When pagination is required', () => { describe('When pagination is required', () => {
describe('and the user is on the first page', () => { describe('and the user is on the first page', () => {
beforeEach(() => { beforeEach(() => {
store.state.list.loading = false;
mountComponent({ sync: false }); mountComponent({ sync: false });
}); });
...@@ -295,6 +297,7 @@ describe('ErrorTrackingList', () => { ...@@ -295,6 +297,7 @@ describe('ErrorTrackingList', () => {
describe('and the user is not on the first page', () => { describe('and the user is not on the first page', () => {
describe('and the previous button is clicked', () => { describe('and the previous button is clicked', () => {
beforeEach(() => { beforeEach(() => {
store.state.list.loading = false;
mountComponent({ sync: false }); mountComponent({ sync: false });
wrapper.setData({ pageValue: 2 }); wrapper.setData({ pageValue: 2 });
}); });
...@@ -313,6 +316,7 @@ describe('ErrorTrackingList', () => { ...@@ -313,6 +316,7 @@ describe('ErrorTrackingList', () => {
describe('and the next page button is clicked', () => { describe('and the next page button is clicked', () => {
beforeEach(() => { beforeEach(() => {
store.state.list.loading = false;
mountComponent({ sync: false }); mountComponent({ sync: false });
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment