Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
323a326c
Commit
323a326c
authored
Jun 06, 2017
by
Alfredo Sumaran
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve pagination when searching or filtering
[ci skip]
parent
ea531e1e
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
130 additions
and
48 deletions
+130
-48
app/assets/javascripts/filterable_list.js
app/assets/javascripts/filterable_list.js
+41
-21
app/assets/javascripts/groups/components/groups.vue
app/assets/javascripts/groups/components/groups.vue
+6
-6
app/assets/javascripts/groups/groups_filterable_list.js
app/assets/javascripts/groups/groups_filterable_list.js
+44
-13
app/assets/javascripts/groups/index.js
app/assets/javascripts/groups/index.js
+31
-4
app/assets/javascripts/groups/services/groups_service.js
app/assets/javascripts/groups/services/groups_service.js
+5
-1
app/assets/javascripts/lib/utils/common_utils.js
app/assets/javascripts/lib/utils/common_utils.js
+2
-2
app/views/dashboard/groups/_groups.html.haml
app/views/dashboard/groups/_groups.html.haml
+1
-1
No files found.
app/assets/javascripts/filterable_list.js
View file @
323a326c
...
@@ -8,7 +8,15 @@ export default class FilterableList {
...
@@ -8,7 +8,15 @@ export default class FilterableList {
this
.
filterForm
=
form
;
this
.
filterForm
=
form
;
this
.
listFilterElement
=
filter
;
this
.
listFilterElement
=
filter
;
this
.
listHolderElement
=
holder
;
this
.
listHolderElement
=
holder
;
this
.
filterUrl
=
`
${
this
.
filterForm
.
getAttribute
(
'
action
'
)}
?
${
$
(
this
.
filterForm
).
serialize
()}
`
;
this
.
isBusy
=
false
;
}
getFilterEndpoint
()
{
return
`
${
this
.
filterForm
.
getAttribute
(
'
action
'
)}
?
${
$
(
this
.
filterForm
).
serialize
()}
`
;
}
getPagePath
()
{
return
this
.
getFilterEndpoint
();
}
}
initSearch
()
{
initSearch
()
{
...
@@ -20,9 +28,19 @@ export default class FilterableList {
...
@@ -20,9 +28,19 @@ export default class FilterableList {
}
}
onFilterInput
()
{
onFilterInput
()
{
const
url
=
this
.
filterForm
.
getAttribute
(
'
action
'
);
const
$form
=
$
(
this
.
filterForm
);
const
data
=
$
(
this
.
filterForm
).
serialize
();
const
queryData
=
{};
this
.
filterResults
(
url
,
data
,
'
filter-input
'
);
const
filterGroupsParam
=
$form
.
find
(
'
[name="filter_groups"]
'
).
val
();
if
(
filterGroupsParam
)
{
queryData
.
filter_groups
=
filterGroupsParam
;
}
this
.
filterResults
(
queryData
);
if
(
this
.
setDefaultFilterOption
)
{
this
.
setDefaultFilterOption
();
}
}
}
bindEvents
()
{
bindEvents
()
{
...
@@ -33,42 +51,44 @@ export default class FilterableList {
...
@@ -33,42 +51,44 @@ export default class FilterableList {
this
.
listFilterElement
.
removeEventListener
(
'
input
'
,
this
.
debounceFilter
);
this
.
listFilterElement
.
removeEventListener
(
'
input
'
,
this
.
debounceFilter
);
}
}
filterResults
(
url
,
data
,
comingFrom
)
{
filterResults
(
queryData
)
{
const
endpoint
=
url
||
this
.
filterForm
.
getAttribute
(
'
action
'
);
if
(
this
.
isBusy
)
{
const
additionalData
=
data
||
$
(
this
.
filterForm
).
serialize
();
return
false
;
}
$
(
this
.
listHolderElement
).
fadeTo
(
250
,
0.5
);
$
(
this
.
listHolderElement
).
fadeTo
(
250
,
0.5
);
return
$
.
ajax
({
return
$
.
ajax
({
url
:
endpoint
,
url
:
this
.
getFilterEndpoint
()
,
data
:
additional
Data
,
data
:
query
Data
,
type
:
'
GET
'
,
type
:
'
GET
'
,
dataType
:
'
json
'
,
dataType
:
'
json
'
,
context
:
this
,
context
:
this
,
complete
:
this
.
onFilterComplete
,
complete
:
this
.
onFilterComplete
,
beforeSend
:
()
=>
{
this
.
isBusy
=
true
;
},
success
:
(
response
,
textStatus
,
xhr
)
=>
{
success
:
(
response
,
textStatus
,
xhr
)
=>
{
if
(
this
.
preOnFilterSuccess
)
{
this
.
onFilterSuccess
(
response
,
xhr
,
queryData
);
this
.
preOnFilterSuccess
(
comingFrom
);
}
this
.
onFilterSuccess
(
response
,
xhr
);
},
},
});
});
}
}
onFilterSuccess
(
d
ata
)
{
onFilterSuccess
(
response
,
xhr
,
queryD
ata
)
{
if
(
data
.
html
)
{
if
(
response
.
html
)
{
this
.
listHolderElement
.
innerHTML
=
data
.
html
;
this
.
listHolderElement
.
innerHTML
=
response
.
html
;
}
}
// Change url so if user reload a page - search results are saved
// Change url so if user reload a page - search results are saved
return
window
.
history
.
replaceState
({
const
currentPath
=
this
.
getPagePath
(
queryData
);
page
:
this
.
filterUrl
,
},
document
.
title
,
this
.
filterUrl
);
return
window
.
history
.
replaceState
({
page
:
currentPath
,
},
document
.
title
,
currentPath
);
}
}
onFilterComplete
()
{
onFilterComplete
()
{
this
.
isBusy
=
false
;
$
(
this
.
listHolderElement
).
fadeTo
(
250
,
1
);
$
(
this
.
listHolderElement
).
fadeTo
(
250
,
1
);
}
}
}
}
app/assets/javascripts/groups/components/groups.vue
View file @
323a326c
<
script
>
<
script
>
import
TablePaginationComponent
from
'
~/vue_shared/components/table_pagination
'
;
import
TablePaginationComponent
from
'
~/vue_shared/components/table_pagination.vue
'
;
import
eventHub
from
'
../event_hub
'
;
export
default
{
export
default
{
components
:
{
components
:
{
...
@@ -16,11 +17,10 @@ export default {
...
@@ -16,11 +17,10 @@ export default {
},
},
},
},
methods
:
{
methods
:
{
change
(
pageNumber
)
{
change
(
page
)
{
const
param
=
gl
.
utils
.
setParamInURL
(
'
page
'
,
pageNumber
);
const
filterGroupsParam
=
gl
.
utils
.
getParameterByName
(
'
filter_groups
'
);
const
sortParam
=
gl
.
utils
.
getParameterByName
(
'
sort
'
);
gl
.
utils
.
visitUrl
(
param
);
eventHub
.
$emit
(
'
fetchPage
'
,
page
,
filterGroupsParam
,
sortParam
);
return
param
;
},
},
},
},
};
};
...
...
app/assets/javascripts/groups/groups_filterable_list.js
View file @
323a326c
...
@@ -2,12 +2,24 @@ import FilterableList from '~/filterable_list';
...
@@ -2,12 +2,24 @@ import FilterableList from '~/filterable_list';
import
eventHub
from
'
./event_hub
'
;
import
eventHub
from
'
./event_hub
'
;
export
default
class
GroupFilterableList
extends
FilterableList
{
export
default
class
GroupFilterableList
extends
FilterableList
{
constructor
(
form
,
filter
,
holder
)
{
constructor
(
{
form
,
filter
,
holder
,
filterEndpoint
,
pagePath
}
)
{
super
(
form
,
filter
,
holder
);
super
(
form
,
filter
,
holder
);
this
.
form
=
form
;
this
.
filterEndpoint
=
filterEndpoint
;
this
.
pagePath
=
pagePath
;
this
.
$dropdown
=
$
(
'
.js-group-filter-dropdown-wrap
'
);
this
.
$dropdown
=
$
(
'
.js-group-filter-dropdown-wrap
'
);
}
}
getFilterEndpoint
()
{
return
this
.
filterEndpoint
;
}
getPagePath
(
queryData
)
{
const
params
=
queryData
?
$
.
param
(
queryData
)
:
''
;
const
queryString
=
params
?
`?
${
params
}
`
:
''
;
return
`
${
this
.
pagePath
}${
queryString
}
`
;
}
bindEvents
()
{
bindEvents
()
{
super
.
bindEvents
();
super
.
bindEvents
();
...
@@ -21,26 +33,45 @@ export default class GroupFilterableList extends FilterableList {
...
@@ -21,26 +33,45 @@ export default class GroupFilterableList extends FilterableList {
onFormSubmit
(
e
)
{
onFormSubmit
(
e
)
{
e
.
preventDefault
();
e
.
preventDefault
();
this
.
filterResults
();
const
$form
=
$
(
this
.
form
);
const
filterGroupsParam
=
$form
.
find
(
'
[name="filter_groups"]
'
).
val
();
const
queryData
=
{};
if
(
filterGroupsParam
)
{
queryData
.
filter_groups
=
filterGroupsParam
;
}
this
.
filterResults
(
queryData
);
this
.
setDefaultFilterOption
();
}
setDefaultFilterOption
()
{
const
defaultOption
=
$
.
trim
(
this
.
$dropdown
.
find
(
'
.dropdown-menu a:first-child
'
).
text
());
this
.
$dropdown
.
find
(
'
.dropdown-label
'
).
text
(
defaultOption
);
}
}
onOptionClick
(
e
)
{
onOptionClick
(
e
)
{
e
.
preventDefault
();
e
.
preventDefault
();
const
currentOption
=
$
.
trim
(
e
.
currentTarget
.
text
);
this
.
filterUrl
=
e
.
currentTarget
.
href
;
const
queryData
=
{};
this
.
$dropdown
.
find
(
'
.dropdown-label
'
).
text
(
currentOption
);
const
sortParam
=
gl
.
utils
.
getParameterByName
(
'
sort
'
,
e
.
currentTarget
.
href
);
this
.
filterResults
(
this
.
filterUrl
);
}
preOnFilterSuccess
(
comingFrom
)
{
if
(
sortParam
)
{
if
(
comingFrom
===
'
filter-input
'
)
{
queryData
.
sort
=
sortParam
;
this
.
filterUrl
=
`
${
this
.
filterForm
.
getAttribute
(
'
action
'
)}
?
${
$
(
this
.
filterForm
).
serialize
()}
`
;
}
}
this
.
filterResults
(
queryData
);
// Active selected option
this
.
$dropdown
.
find
(
'
.dropdown-label
'
).
text
(
$
.
trim
(
e
.
currentTarget
.
text
));
// Clear current value on search form
this
.
form
.
querySelector
(
'
[name="filter_groups"]
'
).
value
=
''
;
}
}
onFilterSuccess
(
data
,
xhr
)
{
onFilterSuccess
(
data
,
xhr
,
queryData
)
{
super
.
onFilterSuccess
(
data
);
super
.
onFilterSuccess
(
data
,
xhr
,
queryData
);
const
paginationData
=
{
const
paginationData
=
{
'
X-Per-Page
'
:
xhr
.
getResponseHeader
(
'
X-Per-Page
'
),
'
X-Per-Page
'
:
xhr
.
getResponseHeader
(
'
X-Per-Page
'
),
'
X-Page
'
:
xhr
.
getResponseHeader
(
'
X-Page
'
),
'
X-Page
'
:
xhr
.
getResponseHeader
(
'
X-Page
'
),
...
...
app/assets/javascripts/groups/index.js
View file @
323a326c
...
@@ -37,7 +37,9 @@ document.addEventListener('DOMContentLoaded', () => {
...
@@ -37,7 +37,9 @@ document.addEventListener('DOMContentLoaded', () => {
let
parentId
=
null
;
let
parentId
=
null
;
let
getGroups
=
null
;
let
getGroups
=
null
;
let
page
=
null
;
let
page
=
null
;
let
sort
=
null
;
let
pageParam
=
null
;
let
pageParam
=
null
;
let
sortParam
=
null
;
let
filterGroups
=
null
;
let
filterGroups
=
null
;
let
filterGroupsParam
=
null
;
let
filterGroupsParam
=
null
;
...
@@ -55,9 +57,14 @@ document.addEventListener('DOMContentLoaded', () => {
...
@@ -55,9 +57,14 @@ document.addEventListener('DOMContentLoaded', () => {
filterGroups
=
filterGroupsParam
;
filterGroups
=
filterGroupsParam
;
}
}
getGroups
=
this
.
service
.
getGroups
(
parentId
,
page
,
filterGroups
);
sortParam
=
gl
.
utils
.
getParameterByName
(
'
sort
'
);
if
(
sortParam
)
{
sort
=
sortParam
;
}
getGroups
=
this
.
service
.
getGroups
(
parentId
,
page
,
filterGroups
,
sort
);
getGroups
.
then
((
response
)
=>
{
getGroups
.
then
((
response
)
=>
{
eventHub
.
$emit
(
'
updateGroups
'
,
response
.
json
(),
parentGroup
);
this
.
updateGroups
(
response
.
json
(),
parentGroup
);
})
})
.
catch
(()
=>
{
.
catch
(()
=>
{
// TODO: Handle error
// TODO: Handle error
...
@@ -65,6 +72,17 @@ document.addEventListener('DOMContentLoaded', () => {
...
@@ -65,6 +72,17 @@ document.addEventListener('DOMContentLoaded', () => {
return
getGroups
;
return
getGroups
;
},
},
fetchPage
(
page
,
filterGroups
,
sort
)
{
this
.
service
.
getGroups
(
null
,
page
,
filterGroups
,
sort
)
.
then
((
response
)
=>
{
this
.
updateGroups
(
response
.
json
());
this
.
updatePagination
(
response
.
headers
);
$
.
scrollTo
(
0
);
})
.
catch
(()
=>
{
// TODO: Handle error
});
},
toggleSubGroups
(
parentGroup
=
null
)
{
toggleSubGroups
(
parentGroup
=
null
)
{
if
(
!
parentGroup
.
isOpen
)
{
if
(
!
parentGroup
.
isOpen
)
{
this
.
store
.
resetGroups
(
parentGroup
);
this
.
store
.
resetGroups
(
parentGroup
);
...
@@ -95,9 +113,18 @@ document.addEventListener('DOMContentLoaded', () => {
...
@@ -95,9 +113,18 @@ document.addEventListener('DOMContentLoaded', () => {
const
filter
=
document
.
querySelector
(
'
.js-groups-list-filter
'
);
const
filter
=
document
.
querySelector
(
'
.js-groups-list-filter
'
);
const
holder
=
document
.
querySelector
(
'
.js-groups-list-holder
'
);
const
holder
=
document
.
querySelector
(
'
.js-groups-list-holder
'
);
groupFilterList
=
new
GroupFilterableList
(
form
,
filter
,
holder
);
const
opts
=
{
form
,
filter
,
holder
,
filterEndpoint
:
el
.
dataset
.
endpoint
,
pagePath
:
el
.
dataset
.
path
,
};
groupFilterList
=
new
GroupFilterableList
(
opts
);
groupFilterList
.
initSearch
();
groupFilterList
.
initSearch
();
eventHub
.
$on
(
'
fetchPage
'
,
this
.
fetchPage
);
eventHub
.
$on
(
'
toggleSubGroups
'
,
this
.
toggleSubGroups
);
eventHub
.
$on
(
'
toggleSubGroups
'
,
this
.
toggleSubGroups
);
eventHub
.
$on
(
'
leaveGroup
'
,
this
.
leaveGroup
);
eventHub
.
$on
(
'
leaveGroup
'
,
this
.
leaveGroup
);
eventHub
.
$on
(
'
updateGroups
'
,
this
.
updateGroups
);
eventHub
.
$on
(
'
updateGroups
'
,
this
.
updateGroups
);
...
@@ -106,7 +133,7 @@ document.addEventListener('DOMContentLoaded', () => {
...
@@ -106,7 +133,7 @@ document.addEventListener('DOMContentLoaded', () => {
mounted
()
{
mounted
()
{
this
.
fetchGroups
()
this
.
fetchGroups
()
.
then
((
response
)
=>
{
.
then
((
response
)
=>
{
eventHub
.
$emit
(
'
updatePagination
'
,
response
.
headers
);
this
.
updatePagination
(
response
.
headers
);
})
})
.
catch
(()
=>
{
.
catch
(()
=>
{
// TODO: Handle error
// TODO: Handle error
...
...
app/assets/javascripts/groups/services/groups_service.js
View file @
323a326c
...
@@ -8,7 +8,7 @@ export default class GroupsService {
...
@@ -8,7 +8,7 @@ export default class GroupsService {
this
.
groups
=
Vue
.
resource
(
endpoint
);
this
.
groups
=
Vue
.
resource
(
endpoint
);
}
}
getGroups
(
parentId
,
page
,
filterGroups
)
{
getGroups
(
parentId
,
page
,
filterGroups
,
sort
)
{
const
data
=
{};
const
data
=
{};
if
(
parentId
)
{
if
(
parentId
)
{
...
@@ -22,6 +22,10 @@ export default class GroupsService {
...
@@ -22,6 +22,10 @@ export default class GroupsService {
if
(
filterGroups
)
{
if
(
filterGroups
)
{
data
.
filter_groups
=
filterGroups
;
data
.
filter_groups
=
filterGroups
;
}
}
if
(
sort
)
{
data
.
sort
=
sort
;
}
}
}
return
this
.
groups
.
get
(
data
);
return
this
.
groups
.
get
(
data
);
...
...
app/assets/javascripts/lib/utils/common_utils.js
View file @
323a326c
...
@@ -167,8 +167,8 @@
...
@@ -167,8 +167,8 @@
if the name does not exist this function will return `null`
if the name does not exist this function will return `null`
otherwise it will return the value of the param key provided
otherwise it will return the value of the param key provided
*/
*/
w
.
gl
.
utils
.
getParameterByName
=
(
name
)
=>
{
w
.
gl
.
utils
.
getParameterByName
=
(
name
,
parseUrl
)
=>
{
const
url
=
window
.
location
.
href
;
const
url
=
parseUrl
||
window
.
location
.
href
;
name
=
name
.
replace
(
/
[
[
\]]
/g
,
'
\\
$&
'
);
name
=
name
.
replace
(
/
[
[
\]]
/g
,
'
\\
$&
'
);
const
regex
=
new
RegExp
(
`[?&]
${
name
}
(=([^&#]*)|&|#|$)`
);
const
regex
=
new
RegExp
(
`[?&]
${
name
}
(=([^&#]*)|&|#|$)`
);
const
results
=
regex
.
exec
(
url
);
const
results
=
regex
.
exec
(
url
);
...
...
app/views/dashboard/groups/_groups.html.haml
View file @
323a326c
.js-groups-list-holder
.js-groups-list-holder
#dashboard-group-app
{
data:
{
endpoint:
dashboard_groups_path
(
format: :json
)
}
}
#dashboard-group-app
{
data:
{
endpoint:
dashboard_groups_path
(
format: :json
)
,
path:
dashboard_groups_path
}
}
%groups-component
{
':groups'
=>
'state.groups'
,
':page-info'
=>
'state.pageInfo'
}
%groups-component
{
':groups'
=>
'state.groups'
,
':page-info'
=>
'state.pageInfo'
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment