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
1
Merge Requests
1
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
nexedi
gitlab-ce
Commits
41cf4507
Commit
41cf4507
authored
Feb 08, 2021
by
Nicolò Maria Mezzopera
Committed by
Phil Hughes
Feb 08, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor package_search to be reusable
- new registry_search component - tests
parent
bcb377af
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
271 additions
and
170 deletions
+271
-170
app/assets/javascripts/packages/list/components/package_search.vue
...s/javascripts/packages/list/components/package_search.vue
+25
-72
app/assets/javascripts/packages/list/components/packages_list_app.vue
...avascripts/packages/list/components/packages_list_app.vue
+1
-1
app/assets/javascripts/packages/list/constants.js
app/assets/javascripts/packages/list/constants.js
+0
-3
app/assets/javascripts/vue_shared/components/registry/registry_search.vue
...cripts/vue_shared/components/registry/registry_search.vue
+90
-0
spec/frontend/packages/list/components/packages_list_app_spec.js
...ontend/packages/list/components/packages_list_app_spec.js
+4
-10
spec/frontend/packages/list/components/packages_search_spec.js
...frontend/packages/list/components/packages_search_spec.js
+46
-84
spec/frontend/vue_shared/components/registry/registry_search_spec.js
...nd/vue_shared/components/registry/registry_search_spec.js
+105
-0
No files found.
app/assets/javascripts/packages/list/components/package_search.vue
View file @
41cf4507
<
script
>
import
{
GlSorting
,
GlSortingItem
,
GlFilteredSearch
}
from
'
@gitlab/ui
'
;
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
{
__
,
s__
}
from
'
~/locale
'
;
import
{
ASCENDING_ODER
,
DESCENDING_ORDER
}
from
'
../constants
'
;
import
RegistrySearch
from
'
~/vue_shared/components/registry/registry_search.vue
'
;
import
getTableHeaders
from
'
../utils
'
;
import
PackageTypeToken
from
'
./tokens/package_type_token.vue
'
;
export
default
{
components
:
{
GlSorting
,
GlSortingItem
,
GlFilteredSearch
,
},
tokens
:
[
{
type
:
'
type
'
,
icon
:
'
package
'
,
title
:
s__
(
'
PackageRegistry|Type
'
),
unique
:
true
,
token
:
PackageTypeToken
,
operators
:
[{
value
:
'
=
'
,
description
:
__
(
'
is
'
),
default
:
'
true
'
}],
},
],
components
:
{
RegistrySearch
},
computed
:
{
...
mapState
({
isGroupPage
:
(
state
)
=>
state
.
config
.
isGroupPage
,
orderBy
:
(
state
)
=>
state
.
sorting
.
orderBy
,
sort
:
(
state
)
=>
state
.
sorting
.
sort
,
sorting
:
(
state
)
=>
state
.
sorting
,
filter
:
(
state
)
=>
state
.
filter
,
}),
internalFilter
:
{
get
()
{
return
this
.
filter
;
},
set
(
value
)
{
this
.
setFilter
(
value
);
},
},
sortText
()
{
const
field
=
this
.
sortableFields
.
find
((
s
)
=>
s
.
orderBy
===
this
.
orderBy
);
return
field
?
field
.
label
:
''
;
},
sortableFields
()
{
return
getTableHeaders
(
this
.
isGroupPage
);
},
isSortAscending
()
{
return
this
.
sort
===
ASCENDING_ODER
;
},
tokens
()
{
return
[
{
type
:
'
type
'
,
icon
:
'
package
'
,
title
:
s__
(
'
PackageRegistry|Type
'
),
unique
:
true
,
token
:
PackageTypeToken
,
operators
:
[{
value
:
'
=
'
,
description
:
__
(
'
is
'
),
default
:
'
true
'
}],
},
];
},
},
methods
:
{
...
mapActions
([
'
setSorting
'
,
'
setFilter
'
]),
onDirectionChange
()
{
const
sort
=
this
.
isSortAscending
?
DESCENDING_ORDER
:
ASCENDING_ODER
;
this
.
setSorting
({
sort
});
this
.
$emit
(
'
sort:changed
'
);
},
onSortItemClick
(
item
)
{
this
.
setSorting
({
orderBy
:
item
});
this
.
$emit
(
'
sort:changed
'
);
},
clearSearch
()
{
this
.
setFilter
([]);
this
.
$emit
(
'
filter:changed
'
);
updateSorting
(
newValue
)
{
this
.
setSorting
(
newValue
);
this
.
$emit
(
'
update
'
);
},
},
};
</
script
>
<
template
>
<div
class=
"gl-display-flex gl-p-5 gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100"
>
<gl-filtered-search
v-model=
"internalFilter"
class=
"gl-mr-4 gl-flex-fill-1"
:placeholder=
"__('Filter results')"
:available-tokens=
"tokens"
@
submit=
"$emit('filter:changed')"
@
clear=
"clearSearch"
/>
<gl-sorting
:text=
"sortText"
:is-ascending=
"isSortAscending"
@
sortDirectionChange=
"onDirectionChange"
>
<gl-sorting-item
v-for=
"item in sortableFields"
ref=
"packageListSortItem"
:key=
"item.orderBy"
@
click=
"onSortItemClick(item.orderBy)"
>
{{
item
.
label
}}
</gl-sorting-item>
</gl-sorting>
</div>
<registry-search
:filter=
"filter"
:sorting=
"sorting"
:tokens=
"$options.tokens"
:sortable-fields=
"sortableFields"
@
sorting:changed=
"updateSorting"
@
filter:changed=
"setFilter"
@
filter:submit=
"$emit('update')"
/>
</
template
>
app/assets/javascripts/packages/list/components/packages_list_app.vue
View file @
41cf4507
...
...
@@ -75,7 +75,7 @@ export default {
<
template
>
<div>
<package-title
:package-help-url=
"packageHelpUrl"
:packages-count=
"packagesCount"
/>
<package-search
@
sort:changed=
"requestPackagesList"
@
filter:changed
=
"requestPackagesList"
/>
<package-search
@
update
=
"requestPackagesList"
/>
<package-list
@
page:changed=
"onPageChanged"
@
package:delete=
"onPackageDeleteRequest"
>
<template
#empty-state
>
...
...
app/assets/javascripts/packages/list/constants.js
View file @
41cf4507
...
...
@@ -26,9 +26,6 @@ export const LIST_LABEL_PACKAGE_TYPE = __('Type');
export
const
LIST_LABEL_CREATED_AT
=
__
(
'
Published
'
);
export
const
LIST_LABEL_ACTIONS
=
''
;
export
const
ASCENDING_ODER
=
'
asc
'
;
export
const
DESCENDING_ORDER
=
'
desc
'
;
// The following is not translated because it is used to build a JavaScript exception error message
export
const
MISSING_DELETE_PATH_ERROR
=
'
Missing delete_api_path link
'
;
...
...
app/assets/javascripts/vue_shared/components/registry/registry_search.vue
0 → 100644
View file @
41cf4507
<
script
>
import
{
GlSorting
,
GlSortingItem
,
GlFilteredSearch
}
from
'
@gitlab/ui
'
;
const
ASCENDING_ORDER
=
'
asc
'
;
const
DESCENDING_ORDER
=
'
desc
'
;
export
default
{
components
:
{
GlSorting
,
GlSortingItem
,
GlFilteredSearch
,
},
props
:
{
filter
:
{
type
:
Array
,
required
:
true
,
},
sorting
:
{
type
:
Object
,
required
:
true
,
},
tokens
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[],
},
sortableFields
:
{
type
:
Array
,
required
:
true
,
},
},
computed
:
{
internalFilter
:
{
get
()
{
return
this
.
filter
;
},
set
(
value
)
{
this
.
$emit
(
'
filter:changed
'
,
value
);
},
},
sortText
()
{
const
field
=
this
.
sortableFields
.
find
((
s
)
=>
s
.
orderBy
===
this
.
sorting
.
orderBy
);
return
field
?
field
.
label
:
''
;
},
isSortAscending
()
{
return
this
.
sorting
.
sort
===
ASCENDING_ORDER
;
},
},
methods
:
{
onDirectionChange
()
{
const
sort
=
this
.
isSortAscending
?
DESCENDING_ORDER
:
ASCENDING_ORDER
;
this
.
$emit
(
'
sorting:changed
'
,
{
sort
});
},
onSortItemClick
(
item
)
{
this
.
$emit
(
'
sorting:changed
'
,
{
orderBy
:
item
});
},
clearSearch
()
{
this
.
$emit
(
'
filter:changed
'
,
[]);
this
.
$emit
(
'
filter:submit
'
);
},
},
};
</
script
>
<
template
>
<div
class=
"gl-display-flex gl-p-5 gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100"
>
<gl-filtered-search
v-model=
"internalFilter"
class=
"gl-mr-4 gl-flex-fill-1"
:placeholder=
"__('Filter results')"
:available-tokens=
"tokens"
@
submit=
"$emit('filter:submit')"
@
clear=
"clearSearch"
/>
<gl-sorting
:text=
"sortText"
:is-ascending=
"isSortAscending"
@
sortDirectionChange=
"onDirectionChange"
>
<gl-sorting-item
v-for=
"item in sortableFields"
ref=
"packageListSortItem"
:key=
"item.orderBy"
@
click=
"onSortItemClick(item.orderBy)"
>
{{
item
.
label
}}
</gl-sorting-item>
</gl-sorting>
</div>
</
template
>
spec/frontend/packages/list/components/packages_list_app_spec.js
View file @
41cf4507
...
...
@@ -93,6 +93,7 @@ describe('packages_list_app', () => {
it
(
'
call requestPackagesList on page:changed
'
,
()
=>
{
mountComponent
();
store
.
dispatch
.
mockClear
();
const
list
=
findListComponent
();
list
.
vm
.
$emit
(
'
page:changed
'
,
1
);
...
...
@@ -107,14 +108,6 @@ describe('packages_list_app', () => {
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestDeletePackage
'
,
'
foo
'
);
});
it
(
'
calls requestPackagesList on sort:changed
'
,
()
=>
{
mountComponent
();
const
list
=
findListComponent
();
list
.
vm
.
$emit
(
'
sort:changed
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestPackagesList
'
);
});
it
(
'
does not call requestPackagesList two times on render
'
,
()
=>
{
mountComponent
();
...
...
@@ -142,10 +135,11 @@ describe('packages_list_app', () => {
expect
(
findPackageSearch
().
exists
()).
toBe
(
true
);
});
it
.
each
([
'
sort:changed
'
,
'
filter:changed
'
])(
'
on %p fetches data from the store
'
,
(
event
)
=>
{
it
(
'
on update fetches data from the store
'
,
(
)
=>
{
mountComponent
();
store
.
dispatch
.
mockClear
();
findPackageSearch
().
vm
.
$emit
(
event
);
findPackageSearch
().
vm
.
$emit
(
'
update
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestPackagesList
'
);
});
...
...
spec/frontend/packages/list/components/packages_search_spec.js
View file @
41cf4507
import
Vuex
from
'
vuex
'
;
import
{
GlSorting
,
GlSortingItem
,
GlFilteredSearch
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
component
from
'
~/packages/list/components/package_search.vue
'
;
import
RegistrySearch
from
'
~/vue_shared/components/registry/registry_search.vue
'
;
import
PackageTypeToken
from
'
~/packages/list/components/tokens/package_type_token.vue
'
;
import
getTableHeaders
from
'
~/packages/list/utils
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
...
...
@@ -10,12 +11,8 @@ localVue.use(Vuex);
describe
(
'
Package Search
'
,
()
=>
{
let
wrapper
;
let
store
;
let
sorting
;
let
sortingItems
;
const
findPackageListSorting
=
()
=>
wrapper
.
find
(
GlSorting
);
const
findSortingItems
=
()
=>
wrapper
.
findAll
(
GlSortingItem
);
const
findFilteredSearch
=
()
=>
wrapper
.
find
(
GlFilteredSearch
);
const
findRegistrySearch
=
()
=>
wrapper
.
find
(
RegistrySearch
);
const
createStore
=
(
isGroupPage
)
=>
{
const
state
=
{
...
...
@@ -40,9 +37,6 @@ describe('Package Search', () => {
wrapper
=
shallowMount
(
component
,
{
localVue
,
store
,
stubs
:
{
GlSortingItem
,
},
});
};
...
...
@@ -51,95 +45,63 @@ describe('Package Search', () => {
wrapper
=
null
;
});
describe
(
'
searching
'
,
()
=>
{
it
(
'
has a filtered-search component
'
,
()
=>
{
mountComponent
();
expect
(
findFilteredSearch
().
exists
()).
toBe
(
true
);
it
(
'
has a registry search component
'
,
()
=>
{
mountComponent
();
expect
(
findRegistrySearch
().
exists
()).
toBe
(
true
);
expect
(
findRegistrySearch
().
props
()).
toMatchObject
({
filter
:
store
.
state
.
filter
,
sorting
:
store
.
state
.
sorting
,
tokens
:
expect
.
arrayContaining
([
expect
.
objectContaining
({
token
:
PackageTypeToken
,
type
:
'
type
'
,
icon
:
'
package
'
}),
]),
sortableFields
:
getTableHeaders
(),
});
});
it
(
'
binds the correct props to filtered-search
'
,
()
=>
{
mountComponent
();
expect
(
findFilteredSearch
().
props
()).
toMatchObject
({
value
:
[],
placeholder
:
'
Filter results
'
,
availableTokens
:
wrapper
.
vm
.
tokens
,
});
it
.
each
`
isGroupPage | page
${
false
}
|
${
'
project
'
}
${
true
}
|
${
'
group
'
}
`
(
'
in a $page page binds the right props
'
,
({
isGroupPage
})
=>
{
mountComponent
(
isGroupPage
);
expect
(
findRegistrySearch
().
props
()).
toMatchObject
({
filter
:
store
.
state
.
filter
,
sorting
:
store
.
state
.
sorting
,
tokens
:
expect
.
arrayContaining
([
expect
.
objectContaining
({
token
:
PackageTypeToken
,
type
:
'
type
'
,
icon
:
'
package
'
}),
]),
sortableFields
:
getTableHeaders
(
isGroupPage
),
});
});
it
(
'
updates vuex when value changes
'
,
()
=>
{
mountComponent
();
findFilteredSearch
().
vm
.
$emit
(
'
input
'
,
[
'
foo
'
]);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
setFilter
'
,
[
'
foo
'
]);
});
it
(
'
on sorting:changed emits update event and calls vuex setSorting
'
,
()
=>
{
const
payload
=
{
sort
:
'
foo
'
};
it
(
'
emits filter:changed on submit event
'
,
()
=>
{
mountComponent
();
mountComponent
();
findFilteredSearch
().
vm
.
$emit
(
'
submit
'
);
expect
(
wrapper
.
emitted
(
'
filter:changed
'
)).
toEqual
([[]]);
});
findRegistrySearch
().
vm
.
$emit
(
'
sorting:changed
'
,
payload
);
it
(
'
emits filter:changed on clear event and reset vuex
'
,
()
=>
{
mountComponent
();
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
setSorting
'
,
payload
);
expect
(
wrapper
.
emitted
(
'
update
'
)).
toEqual
([[]]);
});
findFilteredSearch
().
vm
.
$emit
(
'
clear
'
);
it
(
'
on filter:changed calls vuex setFilter
'
,
()
=>
{
const
payload
=
[
'
foo
'
];
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
setFilter
'
,
[]);
expect
(
wrapper
.
emitted
(
'
filter:changed
'
)).
toEqual
([[]]);
});
mountComponent
();
it
(
'
has a PackageTypeToken token
'
,
()
=>
{
mountComponent
();
findRegistrySearch
().
vm
.
$emit
(
'
filter:changed
'
,
payload
);
expect
(
findFilteredSearch
().
props
(
'
availableTokens
'
)).
toEqual
(
expect
.
arrayContaining
([
expect
.
objectContaining
({
token
:
PackageTypeToken
,
type
:
'
type
'
,
icon
:
'
package
'
}),
]),
);
});
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
setFilter
'
,
payload
);
});
describe
(
'
sorting
'
,
()
=>
{
describe
(
'
when is in projects
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
sorting
=
findPackageListSorting
();
sortingItems
=
findSortingItems
();
});
it
(
'
has all the sortable items
'
,
()
=>
{
expect
(
sortingItems
).
toHaveLength
(
wrapper
.
vm
.
sortableFields
.
length
);
});
it
(
'
on sort change set sorting in vuex and emit event
'
,
()
=>
{
sorting
.
vm
.
$emit
(
'
sortDirectionChange
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
setSorting
'
,
{
sort
:
'
asc
'
});
expect
(
wrapper
.
emitted
(
'
sort:changed
'
)).
toBeTruthy
();
});
it
(
'
on sort item click set sorting and emit event
'
,
()
=>
{
const
item
=
sortingItems
.
at
(
0
);
const
{
orderBy
}
=
wrapper
.
vm
.
sortableFields
[
0
];
item
.
vm
.
$emit
(
'
click
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
setSorting
'
,
{
orderBy
});
expect
(
wrapper
.
emitted
(
'
sort:changed
'
)).
toBeTruthy
();
});
});
it
(
'
on filter:submit emits update event
'
,
()
=>
{
mountComponent
();
describe
(
'
when is in group
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
(
true
);
sorting
=
findPackageListSorting
();
sortingItems
=
findSortingItems
();
});
findRegistrySearch
().
vm
.
$emit
(
'
filter:submit
'
);
it
(
'
has all the sortable items
'
,
()
=>
{
expect
(
sortingItems
).
toHaveLength
(
wrapper
.
vm
.
sortableFields
.
length
);
});
});
expect
(
wrapper
.
emitted
(
'
update
'
)).
toEqual
([[]]);
});
});
spec/frontend/vue_shared/components/registry/registry_search_spec.js
0 → 100644
View file @
41cf4507
import
{
GlSorting
,
GlSortingItem
,
GlFilteredSearch
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
component
from
'
~/vue_shared/components/registry/registry_search.vue
'
;
describe
(
'
Registry Search
'
,
()
=>
{
let
wrapper
;
const
findPackageListSorting
=
()
=>
wrapper
.
find
(
GlSorting
);
const
findSortingItems
=
()
=>
wrapper
.
findAll
(
GlSortingItem
);
const
findFilteredSearch
=
()
=>
wrapper
.
find
(
GlFilteredSearch
);
const
defaultProps
=
{
filter
:
[],
sorting
:
{
sort
:
'
asc
'
,
orderBy
:
'
name
'
},
tokens
:
[
'
foo
'
],
sortableFields
:
[{
label
:
'
name
'
,
orderBy
:
'
name
'
},
{
label
:
'
baz
'
}],
};
const
mountComponent
=
(
propsData
=
defaultProps
)
=>
{
wrapper
=
shallowMount
(
component
,
{
propsData
,
stubs
:
{
GlSortingItem
,
},
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
describe
(
'
searching
'
,
()
=>
{
it
(
'
has a filtered-search component
'
,
()
=>
{
mountComponent
();
expect
(
findFilteredSearch
().
exists
()).
toBe
(
true
);
});
it
(
'
binds the correct props to filtered-search
'
,
()
=>
{
mountComponent
();
expect
(
findFilteredSearch
().
props
()).
toMatchObject
({
value
:
[],
placeholder
:
'
Filter results
'
,
availableTokens
:
wrapper
.
vm
.
tokens
,
});
});
it
(
'
emits filter:changed when value changes
'
,
()
=>
{
mountComponent
();
findFilteredSearch
().
vm
.
$emit
(
'
input
'
,
'
foo
'
);
expect
(
wrapper
.
emitted
(
'
filter:changed
'
)).
toEqual
([[
'
foo
'
]]);
});
it
(
'
emits filter:submit on submit event
'
,
()
=>
{
mountComponent
();
findFilteredSearch
().
vm
.
$emit
(
'
submit
'
);
expect
(
wrapper
.
emitted
(
'
filter:submit
'
)).
toEqual
([[]]);
});
it
(
'
emits filter:changed and filter:submit on clear event
'
,
()
=>
{
mountComponent
();
findFilteredSearch
().
vm
.
$emit
(
'
clear
'
);
expect
(
wrapper
.
emitted
(
'
filter:changed
'
)).
toEqual
([[[]]]);
expect
(
wrapper
.
emitted
(
'
filter:submit
'
)).
toEqual
([[]]);
});
it
(
'
binds tokens prop
'
,
()
=>
{
mountComponent
();
expect
(
findFilteredSearch
().
props
(
'
availableTokens
'
)).
toEqual
(
defaultProps
.
tokens
);
});
});
describe
(
'
sorting
'
,
()
=>
{
it
(
'
has all the sortable items
'
,
()
=>
{
mountComponent
();
expect
(
findSortingItems
()).
toHaveLength
(
defaultProps
.
sortableFields
.
length
);
});
it
(
'
on sort change emits sorting:changed event
'
,
()
=>
{
mountComponent
();
findPackageListSorting
().
vm
.
$emit
(
'
sortDirectionChange
'
);
expect
(
wrapper
.
emitted
(
'
sorting:changed
'
)).
toEqual
([[{
sort
:
'
desc
'
}]]);
});
it
(
'
on sort item click emits sorting:changed event
'
,
()
=>
{
mountComponent
();
findSortingItems
().
at
(
0
).
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
emitted
(
'
sorting:changed
'
)).
toEqual
([
[{
orderBy
:
defaultProps
.
sortableFields
[
0
].
orderBy
}],
]);
});
});
});
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