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
8f96459e
Commit
8f96459e
authored
Oct 18, 2020
by
Daniel Tian
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make vulnerability filter template reusable and extensible
parent
1af30cd8
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
238 additions
and
13 deletions
+238
-13
ee/app/assets/javascripts/security_dashboard/components/filters.vue
...ets/javascripts/security_dashboard/components/filters.vue
+3
-3
ee/app/assets/javascripts/security_dashboard/components/filters/filter_body.vue
...pts/security_dashboard/components/filters/filter_body.vue
+85
-0
ee/app/assets/javascripts/security_dashboard/components/filters/standard_filter.vue
...security_dashboard/components/filters/standard_filter.vue
+60
-0
ee/app/assets/javascripts/security_dashboard/components/first_class_vulnerability_filters.vue
...ashboard/components/first_class_vulnerability_filters.vue
+3
-3
ee/spec/frontend/security_dashboard/components/filters/filter_body_spec.js
...security_dashboard/components/filters/filter_body_spec.js
+80
-0
ee/spec/frontend/security_dashboard/components/filters/standard_filter_spec.js
...rity_dashboard/components/filters/standard_filter_spec.js
+4
-4
ee/spec/frontend/security_dashboard/components/first_class_vulnerability_filters_spec.js
...oard/components/first_class_vulnerability_filters_spec.js
+2
-2
qa/qa/ee/page/component/secure_report.rb
qa/qa/ee/page/component/secure_report.rb
+1
-1
No files found.
ee/app/assets/javascripts/security_dashboard/components/filters.vue
View file @
8f96459e
<
script
>
import
{
mapGetters
,
mapActions
}
from
'
vuex
'
;
import
DashboardFilter
from
'
./filters/
filter.vue
'
;
import
StandardFilter
from
'
./filters/standard_
filter.vue
'
;
import
GlToggleVuex
from
'
~/vue_shared/components/gl_toggle_vuex.vue
'
;
export
default
{
components
:
{
Dashbo
ardFilter
,
Stand
ardFilter
,
GlToggleVuex
,
},
computed
:
{
...
...
@@ -20,7 +20,7 @@ export default {
<
template
>
<div
class=
"dashboard-filters border-bottom bg-gray-light"
>
<div
class=
"row mx-0 p-2"
>
<
dashbo
ard-filter
<
stand
ard-filter
v-for=
"filter in visibleFilters"
:key=
"filter.id"
class=
"col-sm-6 col-md-4 col-lg-2 p-2 js-filter"
...
...
ee/app/assets/javascripts/security_dashboard/components/filters/filter.vue
→
ee/app/assets/javascripts/security_dashboard/components/filters/filter
_body
.vue
View file @
8f96459e
<
script
>
import
{
GlDropdown
,
GlSearchBoxByType
,
GlIcon
,
GlTruncate
,
GlDropdownText
}
from
'
@gitlab/ui
'
;
import
FilterItem
from
'
./filter_item.vue
'
;
export
default
{
components
:
{
...
...
@@ -9,47 +8,41 @@ export default {
GlIcon
,
GlTruncate
,
GlDropdownText
,
FilterItem
,
},
props
:
{
filter
:
{
type
:
Object
,
value
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
name
:
{
type
:
String
,
required
:
true
,
},
},
data
()
{
return
{
filterTerm
:
''
,
};
},
computed
:
{
filterId
()
{
return
this
.
filter
.
id
;
selectedOptions
:
{
type
:
Array
,
required
:
true
,
},
selection
()
{
return
this
.
filter
.
selection
;
showSearchBox
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
computed
:
{
firstSelectedOption
()
{
return
this
.
filter
.
options
.
find
(
option
=>
this
.
selection
.
has
(
option
.
id
))?.
name
||
'
-
'
;
return
this
.
selectedOptions
[
0
]
||
'
-
'
;
},
extraOptionCount
()
{
return
this
.
selection
.
size
-
1
;
},
filteredOptions
()
{
return
this
.
filter
.
options
.
filter
(
option
=>
option
.
name
.
toLowerCase
().
includes
(
this
.
filterTerm
.
toLowerCase
()),
);
return
this
.
selectedOptions
.
length
-
1
;
},
qaSelector
()
{
return
`filter_
${
this
.
filter
.
name
.
toLowerCase
().
replace
(
'
'
,
'
_
'
)}
_dropdown`
;
return
`filter_
${
this
.
name
.
toLowerCase
().
replace
(
'
'
,
'
_
'
)}
_dropdown`
;
},
},
methods
:
{
clickFilter
(
option
)
{
this
.
$emit
(
'
setFilter
'
,
{
filterId
:
this
.
filterId
,
optionId
:
option
.
id
});
},
isSelected
(
option
)
{
return
this
.
selection
.
has
(
option
.
id
);
emitInput
(
value
)
{
this
.
$emit
(
'
input
'
,
value
);
},
},
};
...
...
@@ -57,11 +50,11 @@ export default {
<
template
>
<div
class=
"dashboard-filter"
>
<strong
class=
"js-name"
>
{{
filter
.
name
}}
</strong>
<strong
data-testid=
"name"
>
{{
name
}}
</strong>
<gl-dropdown
class=
"gl-mt-2 gl-w-full"
menu-class=
"dropdown-extended-height"
:header-text=
"
filter.
name"
:header-text=
"name"
toggle-class=
"gl-w-full"
>
<template
#button-content
>
...
...
@@ -77,22 +70,16 @@ export default {
</
template
>
<gl-search-box-by-type
v-if=
"filter.options.length >= 20"
v-model=
"filterTerm"
v-if=
"showSearchBox"
:placeholder=
"__('Filter...')"
@
input=
"emitInput"
/>
<filter-item
v-for=
"option in filteredOptions"
:key=
"option.id"
:is-checked=
"isSelected(option)"
:text=
"option.name"
@
click=
"clickFilter(option)"
/>
<gl-dropdown-text
v-if=
"filteredOptions.length <= 0"
>
<span
class=
"gl-text-gray-500"
>
{{ __('No matching results') }}
</span>
</gl-dropdown-text>
<slot>
<gl-dropdown-text>
<span
class=
"gl-text-gray-500"
>
{{ __('No matching results') }}
</span>
</gl-dropdown-text>
</slot>
</gl-dropdown>
</div>
</template>
ee/app/assets/javascripts/security_dashboard/components/filters/standard_filter.vue
0 → 100644
View file @
8f96459e
<
script
>
import
FilterBody
from
'
./filter_body.vue
'
;
import
FilterItem
from
'
./filter_item.vue
'
;
export
default
{
components
:
{
FilterBody
,
FilterItem
,
},
props
:
{
filter
:
{
type
:
Object
,
required
:
true
,
},
},
data
()
{
return
{
searchTerm
:
''
,
};
},
computed
:
{
selection
()
{
return
this
.
filter
.
selection
;
},
filteredOptions
()
{
return
this
.
filter
.
options
.
filter
(
option
=>
option
.
name
.
toLowerCase
().
includes
(
this
.
searchTerm
.
toLowerCase
()),
);
},
selectedOptionsNames
()
{
return
Array
.
from
(
this
.
selection
).
map
(
id
=>
this
.
filter
.
options
.
find
(
x
=>
x
.
id
===
id
).
name
);
},
},
methods
:
{
clickFilter
(
option
)
{
this
.
$emit
(
'
setFilter
'
,
{
filterId
:
this
.
filter
.
id
,
optionId
:
option
.
id
});
},
isSelected
(
option
)
{
return
this
.
selection
.
has
(
option
.
id
);
},
},
};
</
script
>
<
template
>
<filter-body
v-model.trim=
"searchTerm"
:name=
"filter.name"
:selected-options=
"selectedOptionsNames"
:show-search-box=
"filter.options.length >= 20"
>
<filter-item
v-for=
"option in filteredOptions"
:key=
"option.id"
:is-checked=
"isSelected(option)"
:text=
"option.name"
@
click=
"clickFilter(option)"
/>
</filter-body>
</
template
>
ee/app/assets/javascripts/security_dashboard/components/first_class_vulnerability_filters.vue
View file @
8f96459e
...
...
@@ -2,12 +2,12 @@
import
{
isEqual
}
from
'
lodash
'
;
import
{
ALL
,
STATE
}
from
'
ee/security_dashboard/store/modules/filters/constants
'
;
import
{
setFilter
}
from
'
ee/security_dashboard/store/modules/filters/utils
'
;
import
DashboardFilter
from
'
ee/security_dashboard/components/filters/
filter.vue
'
;
import
StandardFilter
from
'
ee/security_dashboard/components/filters/standard_
filter.vue
'
;
import
{
initFirstClassVulnerabilityFilters
,
mapProjects
}
from
'
ee/security_dashboard/helpers
'
;
export
default
{
components
:
{
Dashbo
ardFilter
,
Stand
ardFilter
,
},
props
:
{
projects
:
{
type
:
Array
,
required
:
false
,
default
:
undefined
},
...
...
@@ -85,7 +85,7 @@ export default {
<
template
>
<div
class=
"dashboard-filters border-bottom bg-gray-light"
>
<div
class=
"row mx-0 p-2"
>
<
dashbo
ard-filter
<
stand
ard-filter
v-for=
"filter in filters"
:key=
"filter.id"
class=
"col-sm-6 col-md-4 col-lg-2 p-2"
...
...
ee/spec/frontend/security_dashboard/components/filters/filter_body_spec.js
0 → 100644
View file @
8f96459e
import
{
GlDropdown
,
GlSearchBoxByType
}
from
'
@gitlab/ui
'
;
import
FilterBody
from
'
ee/security_dashboard/components/filters/filter_body.vue
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
describe
(
'
Filter Body component
'
,
()
=>
{
let
wrapper
;
const
defaultProps
=
{
name
:
'
Some Name
'
,
selectedOptions
:
[],
};
const
createComponent
=
(
props
,
slotContent
=
''
)
=>
{
wrapper
=
mount
(
FilterBody
,
{
propsData
:
{
...
defaultProps
,
...
props
},
slots
:
{
default
:
slotContent
},
});
};
const
dropdownButton
=
()
=>
wrapper
.
find
(
'
.dropdown-toggle
'
);
const
searchBox
=
()
=>
wrapper
.
find
(
GlSearchBoxByType
);
afterEach
(()
=>
{
wrapper
.
destroy
();
});
it
(
'
shows the correct label name and dropdown header name
'
,
()
=>
{
createComponent
();
expect
(
wrapper
.
find
(
'
[data-testid="name"]
'
).
text
()).
toBe
(
defaultProps
.
name
);
expect
(
wrapper
.
find
(
GlDropdown
).
props
(
'
headerText
'
)).
toBe
(
defaultProps
.
name
);
});
describe
(
'
dropdown button
'
,
()
=>
{
it
(
'
shows the selected option name if only one option is selected
'
,
()
=>
{
const
props
=
{
selectedOptions
:
[
'
Some Selected Option
'
]
};
createComponent
(
props
);
expect
(
dropdownButton
().
text
()).
toBe
(
props
.
selectedOptions
[
0
]);
});
it
(
'
shows the selected option name and "+x more" if more than one option is selected
'
,
()
=>
{
const
props
=
{
selectedOptions
:
[
'
Option 1
'
,
'
Option 2
'
,
'
Option 3
'
]
};
createComponent
(
props
);
expect
(
dropdownButton
().
text
()).
toMatch
(
/Option 1
\s
+
\+
2 more/
);
});
});
describe
(
'
search box
'
,
()
=>
{
it
.
each
([
true
,
false
])(
'
shows/hides search box when the showSearchBox prop is %s
'
,
show
=>
{
createComponent
({
showSearchBox
:
show
});
expect
(
searchBox
().
exists
()).
toBe
(
show
);
});
it
(
'
emits input event on component when search box input is changed
'
,
()
=>
{
const
text
=
'
abc
'
;
createComponent
({
showSearchBox
:
true
});
searchBox
().
vm
.
$emit
(
'
input
'
,
text
);
expect
(
wrapper
.
emitted
(
'
input
'
)[
0
][
0
]).
toBe
(
text
);
});
});
describe
(
'
dropdown body
'
,
()
=>
{
it
(
'
shows slot content
'
,
()
=>
{
const
slotContent
=
'
some slot content
'
;
createComponent
({},
slotContent
);
expect
(
wrapper
.
text
()).
toContain
(
slotContent
);
});
it
(
'
shows no matching results text if there is no slot content
'
,
()
=>
{
createComponent
();
expect
(
wrapper
.
text
()).
toContain
(
'
No matching results
'
);
});
});
});
ee/spec/frontend/security_dashboard/components/filters/filter_spec.js
→
ee/spec/frontend/security_dashboard/components/filters/
standard_
filter_spec.js
View file @
8f96459e
import
{
GlDropdown
,
GlDropdownItem
,
GlSearchBoxByType
}
from
'
@gitlab/ui
'
;
import
Filter
from
'
ee/security_dashboard/components/filters/
filter.vue
'
;
import
StandardFilter
from
'
ee/security_dashboard/components/filters/standard_
filter.vue
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
{
trimText
}
from
'
helpers/text_helper
'
;
...
...
@@ -12,11 +12,11 @@ const generateOptions = length => {
return
Array
.
from
({
length
}).
map
((
_
,
i
)
=>
generateOption
(
i
));
};
describe
(
'
Filter component
'
,
()
=>
{
describe
(
'
Standard
Filter component
'
,
()
=>
{
let
wrapper
;
const
createWrapper
=
propsData
=>
{
wrapper
=
mount
(
Filter
,
{
propsData
});
wrapper
=
mount
(
Standard
Filter
,
{
propsData
});
};
const
findSearchBox
=
()
=>
wrapper
.
find
(
GlSearchBoxByType
);
...
...
@@ -58,7 +58,7 @@ describe('Filter component', () => {
});
it
(
'
should display "Severity" as the option name
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-name
'
).
text
()).
toContain
(
'
Severity
'
);
expect
(
wrapper
.
find
(
'
[data-testid="name"]
'
).
text
()).
toEqual
(
'
Severity
'
);
});
it
(
'
should not have a search box
'
,
()
=>
{
...
...
ee/spec/frontend/security_dashboard/components/first_class_vulnerability_filters_spec.js
View file @
8f96459e
...
...
@@ -2,7 +2,7 @@ import VueRouter from 'vue-router';
import
{
createLocalVue
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
initFirstClassVulnerabilityFilters
}
from
'
ee/security_dashboard/helpers
'
;
import
Filters
from
'
ee/security_dashboard/components/first_class_vulnerability_filters.vue
'
;
import
Filter
from
'
ee/security_dashboard/components/filters/
filter.vue
'
;
import
StandardFilter
from
'
ee/security_dashboard/components/filters/standard_
filter.vue
'
;
const
router
=
new
VueRouter
();
const
localVue
=
createLocalVue
();
...
...
@@ -17,7 +17,7 @@ describe('First class vulnerability filters component', () => {
{
id
:
'
gid://gitlab/Project/12
'
,
name
:
'
GitLab Com
'
},
];
const
findFilters
=
()
=>
wrapper
.
findAll
(
Filter
);
const
findFilters
=
()
=>
wrapper
.
findAll
(
Standard
Filter
);
const
findStateFilter
=
()
=>
findFilters
().
at
(
0
);
const
findSeverityFilter
=
()
=>
findFilters
().
at
(
1
);
const
findReportTypeFilter
=
()
=>
findFilters
().
at
(
2
);
...
...
qa/qa/ee/page/component/secure_report.rb
View file @
8f96459e
...
...
@@ -11,7 +11,7 @@ module QA
super
base
.
class_eval
do
view
'ee/app/assets/javascripts/security_dashboard/components/filter.vue'
do
view
'ee/app/assets/javascripts/security_dashboard/components/filter
s/standard_filter
.vue'
do
element
:filter_dropdown
,
':data-qa-selector="qaSelector"'
# rubocop:disable QA/ElementWithPattern
element
:filter_dropdown_content
end
...
...
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