aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/Filter.tsx76
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/Filter-test.tsx.snap688
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.tsx.snap172
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.tsx.snap142
-rw-r--r--server/sonar-web/src/main/js/apps/projects/styles.css108
-rw-r--r--server/sonar-web/src/main/js/components/search-navigator.css91
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties1
7 files changed, 696 insertions, 582 deletions
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/Filter.tsx b/server/sonar-web/src/main/js/apps/projects/filters/Filter.tsx
index e4961f26995..7db00ac9c10 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/Filter.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/filters/Filter.tsx
@@ -115,15 +115,16 @@ export default class Filter extends React.PureComponent<Props> {
);
}
- renderOption(option: Option) {
+ renderOption(option: Option, highlightable = false, lastHighlightable = false) {
const { facet, getFacetValueForOption = defaultGetFacetValueForOption, value } = this.props;
+ const active = this.isSelected(option);
const className = classNames(
'facet',
'search-navigator-facet',
'projects-facet',
'button-link',
{
- active: this.isSelected(option),
+ active,
'search-navigator-facet-half': this.props.halfWidth
},
this.props.optionClassName
@@ -139,26 +140,33 @@ export default class Filter extends React.PureComponent<Props> {
option > value;
return (
- <button
- aria-label={this.props.renderAccessibleLabel(option)}
- className={className}
- data-key={option}
- type="button"
- tabIndex={0}
+ <li
key={option}
- onClick={this.handleClick}
- role="checkbox"
- aria-checked={this.isSelected(option) || isUnderSelectedOption}>
- <span className="facet-name">
- {this.props.renderOption(option, this.isSelected(option) || isUnderSelectedOption)}
- </span>
- {facetValue != null && (
- <span className="facet-stat">
- {formatMeasure(facetValue, 'SHORT_INT')}
- {this.renderOptionBar(facetValue)}
+ className={classNames({
+ 'search-navigator-facet-worse-than-highlight': highlightable,
+ last: lastHighlightable,
+ active
+ })}>
+ <button
+ aria-label={this.props.renderAccessibleLabel(option)}
+ className={className}
+ data-key={option}
+ type="button"
+ tabIndex={0}
+ onClick={this.handleClick}
+ role="checkbox"
+ aria-checked={this.isSelected(option) || isUnderSelectedOption}>
+ <span className="facet-name">
+ {this.props.renderOption(option, this.isSelected(option) || isUnderSelectedOption)}
</span>
- )}
- </button>
+ {facetValue != null && (
+ <span className="facet-stat">
+ {formatMeasure(facetValue, 'SHORT_INT')}
+ {this.renderOptionBar(facetValue)}
+ </span>
+ )}
+ </button>
+ </li>
);
}
@@ -171,24 +179,26 @@ export default class Filter extends React.PureComponent<Props> {
const insideHighlight = options.slice(highlightUnder, max);
const afterHighlight = options.slice(max);
return (
- <div className="search-navigator-facet-list projects-facet-list">
+ <ul className="search-navigator-facet-list projects-facet-list">
{beforeHighlight.map(option => this.renderOption(option))}
- <div className="search-navigator-facet-highlight-under-container">
- {insideHighlight.map(option => this.renderOption(option))}
- </div>
+ {insideHighlight.map((option, i) =>
+ this.renderOption(option, true, i === insideHighlight.length - 1)
+ )}
{afterHighlight.map(option => this.renderOption(option))}
- </div>
- );
- } else {
- return (
- <div className="search-navigator-facet-list projects-facet-list">
- {options.map(option => this.renderOption(option))}
- </div>
+ </ul>
);
}
- } else {
- return <div className="search-navigator-facet-empty">{translate('no_results')}</div>;
+ return (
+ <ul className="search-navigator-facet-list projects-facet-list">
+ {options.map(option => this.renderOption(option))}
+ </ul>
+ );
}
+ return (
+ <div className="search-navigator-facet-empty">
+ <em>{translate('projects.facets.no_available_filters_clear_others')}</em>
+ </div>
+ );
};
render() {
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/Filter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/Filter-test.tsx.snap
index e45056ac4a0..891db262a5d 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/Filter-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/Filter-test.tsx.snap
@@ -5,35 +5,39 @@ exports[`highlights under 1`] = `
className="search-navigator-facet-box"
data-key="foo"
>
- <div
+ <ul
className="search-navigator-facet-list projects-facet-list"
>
- <button
- aria-checked={false}
- aria-label="1"
- className="facet search-navigator-facet projects-facet button-link"
- data-key={1}
+ <li
+ className=""
key="1"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={false}
+ aria-label="1"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key={1}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 1
- </span>
- </button>
- <div
- className="search-navigator-facet-highlight-under-container"
+ <span
+ className="facet-name"
+ >
+ 1
+ </span>
+ </button>
+ </li>
+ <li
+ className="search-navigator-facet-worse-than-highlight"
+ key="2"
>
<button
aria-checked={false}
aria-label="2"
className="facet search-navigator-facet projects-facet button-link"
data-key={2}
- key="2"
onClick={[Function]}
role="checkbox"
tabIndex={0}
@@ -45,12 +49,16 @@ exports[`highlights under 1`] = `
2
</span>
</button>
+ </li>
+ <li
+ className="search-navigator-facet-worse-than-highlight last"
+ key="3"
+ >
<button
aria-checked={false}
aria-label="3"
className="facet search-navigator-facet projects-facet button-link"
data-key={3}
- key="3"
onClick={[Function]}
role="checkbox"
tabIndex={0}
@@ -62,8 +70,8 @@ exports[`highlights under 1`] = `
3
</span>
</button>
- </div>
- </div>
+ </li>
+ </ul>
</div>
`;
@@ -72,35 +80,39 @@ exports[`hightlights under selected 1`] = `
className="search-navigator-facet-box"
data-key="foo"
>
- <div
+ <ul
className="search-navigator-facet-list projects-facet-list"
>
- <button
- aria-checked={false}
- aria-label="1"
- className="facet search-navigator-facet projects-facet button-link"
- data-key={1}
+ <li
+ className=""
key="1"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={false}
+ aria-label="1"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key={1}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 1
- </span>
- </button>
- <div
- className="search-navigator-facet-highlight-under-container"
+ <span
+ className="facet-name"
+ >
+ 1
+ </span>
+ </button>
+ </li>
+ <li
+ className="search-navigator-facet-worse-than-highlight active"
+ key="2"
>
<button
aria-checked={true}
aria-label="2"
className="facet search-navigator-facet projects-facet button-link active"
data-key={2}
- key="2"
onClick={[Function]}
role="checkbox"
tabIndex={0}
@@ -112,12 +124,16 @@ exports[`hightlights under selected 1`] = `
2
</span>
</button>
+ </li>
+ <li
+ className="search-navigator-facet-worse-than-highlight last"
+ key="3"
+ >
<button
aria-checked={true}
aria-label="3"
className="facet search-navigator-facet projects-facet button-link"
data-key={3}
- key="3"
onClick={[Function]}
role="checkbox"
tabIndex={0}
@@ -129,8 +145,8 @@ exports[`hightlights under selected 1`] = `
3
</span>
</button>
- </div>
- </div>
+ </li>
+ </ul>
</div>
`;
@@ -139,61 +155,73 @@ exports[`renders 1`] = `
className="search-navigator-facet-box"
data-key="foo"
>
- <div
+ <ul
className="search-navigator-facet-list projects-facet-list"
>
- <button
- aria-checked={false}
- aria-label="1"
- className="facet search-navigator-facet projects-facet button-link"
- data-key={1}
+ <li
+ className=""
key="1"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={false}
+ aria-label="1"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key={1}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 1
- </span>
- </button>
- <button
- aria-checked={false}
- aria-label="2"
- className="facet search-navigator-facet projects-facet button-link"
- data-key={2}
+ <span
+ className="facet-name"
+ >
+ 1
+ </span>
+ </button>
+ </li>
+ <li
+ className=""
key="2"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={false}
+ aria-label="2"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key={2}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 2
- </span>
- </button>
- <button
- aria-checked={false}
- aria-label="3"
- className="facet search-navigator-facet projects-facet button-link"
- data-key={3}
+ <span
+ className="facet-name"
+ >
+ 2
+ </span>
+ </button>
+ </li>
+ <li
+ className=""
key="3"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={false}
+ aria-label="3"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key={3}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 3
- </span>
- </button>
- </div>
+ <span
+ className="facet-name"
+ >
+ 3
+ </span>
+ </button>
+ </li>
+ </ul>
</div>
`;
@@ -202,112 +230,124 @@ exports[`renders facet bar chart 1`] = `
className="search-navigator-facet-box"
data-key="foo"
>
- <div
+ <ul
className="search-navigator-facet-list projects-facet-list"
>
- <button
- aria-checked={false}
- aria-label="a"
- className="facet search-navigator-facet projects-facet button-link"
- data-key="a"
+ <li
+ className=""
key="a"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
- >
- a
- </span>
- <span
- className="facet-stat"
+ <button
+ aria-checked={false}
+ aria-label="a"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key="a"
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 17
- <div
- className="projects-facet-bar"
+ <span
+ className="facet-name"
>
+ a
+ </span>
+ <span
+ className="facet-stat"
+ >
+ 17
<div
- className="projects-facet-bar-inner"
- style={
- Object {
- "width": 42.5,
+ className="projects-facet-bar"
+ >
+ <div
+ className="projects-facet-bar-inner"
+ style={
+ Object {
+ "width": 42.5,
+ }
}
- }
- />
- </div>
- </span>
- </button>
- <button
- aria-checked={false}
- aria-label="b"
- className="facet search-navigator-facet projects-facet button-link"
- data-key="b"
+ />
+ </div>
+ </span>
+ </button>
+ </li>
+ <li
+ className=""
key="b"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
- >
- b
- </span>
- <span
- className="facet-stat"
+ <button
+ aria-checked={false}
+ aria-label="b"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key="b"
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 15
- <div
- className="projects-facet-bar"
+ <span
+ className="facet-name"
+ >
+ b
+ </span>
+ <span
+ className="facet-stat"
>
+ 15
<div
- className="projects-facet-bar-inner"
- style={
- Object {
- "width": 37.5,
+ className="projects-facet-bar"
+ >
+ <div
+ className="projects-facet-bar-inner"
+ style={
+ Object {
+ "width": 37.5,
+ }
}
- }
- />
- </div>
- </span>
- </button>
- <button
- aria-checked={false}
- aria-label="c"
- className="facet search-navigator-facet projects-facet button-link"
- data-key="c"
+ />
+ </div>
+ </span>
+ </button>
+ </li>
+ <li
+ className=""
key="c"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
- >
- c
- </span>
- <span
- className="facet-stat"
+ <button
+ aria-checked={false}
+ aria-label="c"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key="c"
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 24
- <div
- className="projects-facet-bar"
+ <span
+ className="facet-name"
+ >
+ c
+ </span>
+ <span
+ className="facet-stat"
>
+ 24
<div
- className="projects-facet-bar-inner"
- style={
- Object {
- "width": 60,
+ className="projects-facet-bar"
+ >
+ <div
+ className="projects-facet-bar-inner"
+ style={
+ Object {
+ "width": 60,
+ }
}
- }
- />
- </div>
- </span>
- </button>
- </div>
+ />
+ </div>
+ </span>
+ </button>
+ </li>
+ </ul>
</div>
`;
@@ -317,61 +357,73 @@ exports[`renders header and footer 1`] = `
data-key="foo"
>
<header />
- <div
+ <ul
className="search-navigator-facet-list projects-facet-list"
>
- <button
- aria-checked={false}
- aria-label="1"
- className="facet search-navigator-facet projects-facet button-link"
- data-key={1}
+ <li
+ className=""
key="1"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={false}
+ aria-label="1"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key={1}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 1
- </span>
- </button>
- <button
- aria-checked={false}
- aria-label="2"
- className="facet search-navigator-facet projects-facet button-link"
- data-key={2}
+ <span
+ className="facet-name"
+ >
+ 1
+ </span>
+ </button>
+ </li>
+ <li
+ className=""
key="2"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={false}
+ aria-label="2"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key={2}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 2
- </span>
- </button>
- <button
- aria-checked={false}
- aria-label="3"
- className="facet search-navigator-facet projects-facet button-link"
- data-key={3}
+ <span
+ className="facet-name"
+ >
+ 2
+ </span>
+ </button>
+ </li>
+ <li
+ className=""
key="3"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={false}
+ aria-label="3"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key={3}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 3
- </span>
- </button>
- </div>
+ <span
+ className="facet-name"
+ >
+ 3
+ </span>
+ </button>
+ </li>
+ </ul>
<footer />
</div>
`;
@@ -381,61 +433,73 @@ exports[`renders multiple selected 1`] = `
className="search-navigator-facet-box"
data-key="foo"
>
- <div
+ <ul
className="search-navigator-facet-list projects-facet-list"
>
- <button
- aria-checked={true}
- aria-label="1"
- className="facet search-navigator-facet projects-facet button-link active"
- data-key={1}
+ <li
+ className="active"
key="1"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={true}
+ aria-label="1"
+ className="facet search-navigator-facet projects-facet button-link active"
+ data-key={1}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 1
- </span>
- </button>
- <button
- aria-checked={true}
- aria-label="2"
- className="facet search-navigator-facet projects-facet button-link active"
- data-key={2}
+ <span
+ className="facet-name"
+ >
+ 1
+ </span>
+ </button>
+ </li>
+ <li
+ className="active"
key="2"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={true}
+ aria-label="2"
+ className="facet search-navigator-facet projects-facet button-link active"
+ data-key={2}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 2
- </span>
- </button>
- <button
- aria-checked={false}
- aria-label="3"
- className="facet search-navigator-facet projects-facet button-link"
- data-key={3}
+ <span
+ className="facet-name"
+ >
+ 2
+ </span>
+ </button>
+ </li>
+ <li
+ className=""
key="3"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={false}
+ aria-label="3"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key={3}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 3
- </span>
- </button>
- </div>
+ <span
+ className="facet-name"
+ >
+ 3
+ </span>
+ </button>
+ </li>
+ </ul>
</div>
`;
@@ -447,7 +511,9 @@ exports[`renders no results 1`] = `
<div
className="search-navigator-facet-empty"
>
- no_results
+ <em>
+ projects.facets.no_available_filters_clear_others
+ </em>
</div>
</div>
`;
@@ -457,60 +523,72 @@ exports[`renders selected 1`] = `
className="search-navigator-facet-box"
data-key="foo"
>
- <div
+ <ul
className="search-navigator-facet-list projects-facet-list"
>
- <button
- aria-checked={false}
- aria-label="1"
- className="facet search-navigator-facet projects-facet button-link"
- data-key={1}
+ <li
+ className=""
key="1"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={false}
+ aria-label="1"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key={1}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 1
- </span>
- </button>
- <button
- aria-checked={true}
- aria-label="2"
- className="facet search-navigator-facet projects-facet button-link active"
- data-key={2}
+ <span
+ className="facet-name"
+ >
+ 1
+ </span>
+ </button>
+ </li>
+ <li
+ className="active"
key="2"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={true}
+ aria-label="2"
+ className="facet search-navigator-facet projects-facet button-link active"
+ data-key={2}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 2
- </span>
- </button>
- <button
- aria-checked={false}
- aria-label="3"
- className="facet search-navigator-facet projects-facet button-link"
- data-key={3}
+ <span
+ className="facet-name"
+ >
+ 2
+ </span>
+ </button>
+ </li>
+ <li
+ className=""
key="3"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={false}
+ aria-label="3"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key={3}
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 3
- </span>
- </button>
- </div>
+ <span
+ className="facet-name"
+ >
+ 3
+ </span>
+ </button>
+ </li>
+ </ul>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.tsx.snap
index c5301bbcb24..599f4d2d941 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.tsx.snap
@@ -72,100 +72,112 @@ exports[`should render the languages facet with the selected languages 2`] = `
<FilterHeader
name="projects.facets.languages"
/>
- <div
+ <ul
className="search-navigator-facet-list projects-facet-list"
>
- <button
- aria-checked={true}
- aria-label="projects.facets.label_text_x.projects.facets.languages.Java"
- className="facet search-navigator-facet projects-facet button-link active"
- data-key="java"
+ <li
+ className="active"
key="java"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={true}
+ aria-label="projects.facets.label_text_x.projects.facets.languages.Java"
+ className="facet search-navigator-facet projects-facet button-link active"
+ data-key="java"
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- <SearchableFilterOption
- option={
- Object {
- "key": "java",
- "name": "Java",
+ <span
+ className="facet-name"
+ >
+ <SearchableFilterOption
+ option={
+ Object {
+ "key": "java",
+ "name": "Java",
+ }
}
- }
- optionKey="java"
- />
- </span>
- <span
- className="facet-stat"
- >
- 39
- </span>
- </button>
- <button
- aria-checked={true}
- aria-label="projects.facets.label_text_x.projects.facets.languages.C#"
- className="facet search-navigator-facet projects-facet button-link active"
- data-key="cs"
+ optionKey="java"
+ />
+ </span>
+ <span
+ className="facet-stat"
+ >
+ 39
+ </span>
+ </button>
+ </li>
+ <li
+ className="active"
key="cs"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={true}
+ aria-label="projects.facets.label_text_x.projects.facets.languages.C#"
+ className="facet search-navigator-facet projects-facet button-link active"
+ data-key="cs"
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- <SearchableFilterOption
- option={
- Object {
- "key": "cs",
- "name": "C#",
+ <span
+ className="facet-name"
+ >
+ <SearchableFilterOption
+ option={
+ Object {
+ "key": "cs",
+ "name": "C#",
+ }
}
- }
- optionKey="cs"
- />
- </span>
- <span
- className="facet-stat"
- >
- 4
- </span>
- </button>
- <button
- aria-checked={false}
- aria-label="projects.facets.label_text_x.projects.facets.languages.JavaScript"
- className="facet search-navigator-facet projects-facet button-link"
- data-key="js"
+ optionKey="cs"
+ />
+ </span>
+ <span
+ className="facet-stat"
+ >
+ 4
+ </span>
+ </button>
+ </li>
+ <li
+ className=""
key="js"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={false}
+ aria-label="projects.facets.label_text_x.projects.facets.languages.JavaScript"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key="js"
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- <SearchableFilterOption
- option={
- Object {
- "key": "js",
- "name": "JavaScript",
+ <span
+ className="facet-name"
+ >
+ <SearchableFilterOption
+ option={
+ Object {
+ "key": "js",
+ "name": "JavaScript",
+ }
}
- }
- optionKey="js"
- />
- </span>
- <span
- className="facet-stat"
- >
- 1
- </span>
- </button>
- </div>
+ optionKey="js"
+ />
+ </span>
+ <span
+ className="facet-stat"
+ >
+ 1
+ </span>
+ </button>
+ </li>
+ </ul>
<SearchableFilterFooter
onQueryChange={[MockFunction]}
options={
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.tsx.snap
index 94c5ca062ed..5b04154d8cf 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.tsx.snap
@@ -157,82 +157,94 @@ exports[`should render the tags facet with the selected tags 2`] = `
<FilterHeader
name="projects.facets.tags"
/>
- <div
+ <ul
className="search-navigator-facet-list projects-facet-list"
>
- <button
- aria-checked={true}
- aria-label="projects.facets.label_text_x.projects.facets.tags.lang"
- className="facet search-navigator-facet projects-facet button-link active"
- data-key="lang"
+ <li
+ className="active"
key="lang"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
+ <button
+ aria-checked={true}
+ aria-label="projects.facets.label_text_x.projects.facets.tags.lang"
+ className="facet search-navigator-facet projects-facet button-link active"
+ data-key="lang"
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- <SearchableFilterOption
- optionKey="lang"
- />
- </span>
- <span
- className="facet-stat"
- >
- 4
- </span>
- </button>
- <button
- aria-checked={true}
- aria-label="projects.facets.label_text_x.projects.facets.tags.sonar"
- className="facet search-navigator-facet projects-facet button-link active"
- data-key="sonar"
+ <span
+ className="facet-name"
+ >
+ <SearchableFilterOption
+ optionKey="lang"
+ />
+ </span>
+ <span
+ className="facet-stat"
+ >
+ 4
+ </span>
+ </button>
+ </li>
+ <li
+ className="active"
key="sonar"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
- >
- <SearchableFilterOption
- optionKey="sonar"
- />
- </span>
- <span
- className="facet-stat"
+ <button
+ aria-checked={true}
+ aria-label="projects.facets.label_text_x.projects.facets.tags.sonar"
+ className="facet search-navigator-facet projects-facet button-link active"
+ data-key="sonar"
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 3
- </span>
- </button>
- <button
- aria-checked={false}
- aria-label="projects.facets.label_text_x.projects.facets.tags.csharp"
- className="facet search-navigator-facet projects-facet button-link"
- data-key="csharp"
+ <span
+ className="facet-name"
+ >
+ <SearchableFilterOption
+ optionKey="sonar"
+ />
+ </span>
+ <span
+ className="facet-stat"
+ >
+ 3
+ </span>
+ </button>
+ </li>
+ <li
+ className=""
key="csharp"
- onClick={[Function]}
- role="checkbox"
- tabIndex={0}
- type="button"
>
- <span
- className="facet-name"
- >
- <SearchableFilterOption
- optionKey="csharp"
- />
- </span>
- <span
- className="facet-stat"
+ <button
+ aria-checked={false}
+ aria-label="projects.facets.label_text_x.projects.facets.tags.csharp"
+ className="facet search-navigator-facet projects-facet button-link"
+ data-key="csharp"
+ onClick={[Function]}
+ role="checkbox"
+ tabIndex={0}
+ type="button"
>
- 1
- </span>
- </button>
- </div>
+ <span
+ className="facet-name"
+ >
+ <SearchableFilterOption
+ optionKey="csharp"
+ />
+ </span>
+ <span
+ className="facet-stat"
+ >
+ 1
+ </span>
+ </button>
+ </li>
+ </ul>
<SearchableFilterFooter
isLoading={false}
onInputChange={[Function]}
diff --git a/server/sonar-web/src/main/js/apps/projects/styles.css b/server/sonar-web/src/main/js/apps/projects/styles.css
index 6e226a562ed..018d8eb99bb 100644
--- a/server/sonar-web/src/main/js/apps/projects/styles.css
+++ b/server/sonar-web/src/main/js/apps/projects/styles.css
@@ -82,15 +82,107 @@
transition: width 0.3s ease;
}
-.search-navigator-facet.active .projects-facet-bar-inner,
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet.active
- ~ .search-navigator-facet
- .projects-facet-bar-inner {
- background-color: var(--blue);
-}
-
.projects-empty-list {
padding: calc(4 * var(--gridSize)) 0;
text-align: center;
}
+
+/***
+ Custom filter highlights.
+ Projects filters are special, as some elements allow the selection of "everything
+ worse than" filters (e.g., "Rating B or worse"). We still select a single element,
+ but we want to give a visual indication that we selected multiple fitlers.
+ That's where the following selectors come in, which extend and override styles
+ from ../../components/search-navigator.css
+***/
+
+/*
+ Completely remove the border of the child facet. Handle them at the parent
+ <li> level.
+*/
+.search-navigator-facet-worse-than-highlight .search-navigator-facet {
+ border: 0 !important;
+}
+
+.search-navigator-facet-worse-than-highlight {
+ padding: 1px 0;
+ border-width: 0 1px;
+ border-color: transparent;
+ border-style: solid;
+ box-sizing: border-box;
+}
+
+/*
+ When:
+ - Being hovered
+ - Or, being a sibling of something hovered
+ - Or, being active
+ - Or, being a sibling of something active
+ show the left and right borders.
+*/
+.search-navigator-facet-worse-than-highlight:hover,
+.search-navigator-facet-worse-than-highlight:hover ~ .search-navigator-facet-worse-than-highlight,
+.search-navigator-facet-worse-than-highlight.active,
+.search-navigator-facet-worse-than-highlight.active ~ .search-navigator-facet-worse-than-highlight {
+ border-left-color: var(--blue);
+ border-right-color: var(--blue);
+}
+
+/*
+ When:
+ - Being hovered
+ - Or, being active
+ show the top border, and remove the top padding.
+*/
+.search-navigator-facet-worse-than-highlight:hover,
+.search-navigator-facet-worse-than-highlight.active {
+ border-top: 1px solid var(--blue) !important;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 2px;
+ padding-top: 0 !important;
+}
+
+/*
+ When:
+ - Being hovered AND the last element of the highlightable group
+ - Or, being the last element of the highlightable group AND a sibling of something hovered
+ - Or, being active AND the last element of the highlightable group
+ - Or, being the last element of the highlightable group AND a sibling of something active
+ show the bottom border, and remove the bottom padding.
+*/
+.search-navigator-facet-worse-than-highlight.last:hover,
+.search-navigator-facet-worse-than-highlight:hover
+ ~ .search-navigator-facet-worse-than-highlight.last,
+.search-navigator-facet-worse-than-highlight.active.last,
+.search-navigator-facet-worse-than-highlight.active
+ ~ .search-navigator-facet-worse-than-highlight.last {
+ border-bottom: 1px solid var(--blue) !important;
+ border-bottom-left-radius: 2px;
+ border-bottom-right-radius: 2px;
+ padding-bottom: 0 !important;
+}
+
+/*
+ When:
+ - Being active
+ - Or, being a sibling of something active
+ show a light blue background color.
+*/
+.search-navigator-facet-worse-than-highlight.active,
+.search-navigator-facet-worse-than-highlight.active ~ .search-navigator-facet-worse-than-highlight {
+ background-color: var(--veryLightBlue);
+}
+
+/*
+ When:
+ - Being hovered AND a sibling of something active
+ - Or, being a sibling of something hovered AND a sibling of something active
+ show a darker blue background color.
+*/
+.search-navigator-facet-worse-than-highlight.active
+ ~ .search-navigator-facet-worse-than-highlight:hover,
+.search-navigator-facet-worse-than-highlight.active
+ ~ .search-navigator-facet-worse-than-highlight:hover
+ ~ .search-navigator-facet-worse-than-highlight {
+ background-color: #a1cde8;
+}
diff --git a/server/sonar-web/src/main/js/components/search-navigator.css b/server/sonar-web/src/main/js/components/search-navigator.css
index 074aa308d5c..d54c4cf9285 100644
--- a/server/sonar-web/src/main/js/components/search-navigator.css
+++ b/server/sonar-web/src/main/js/components/search-navigator.css
@@ -218,96 +218,6 @@ button.search-navigator-facet:focus,
margin-right: 10%;
}
-.search-navigator-facet-highlight-under-container {
- margin-bottom: 1px;
-}
-
-.search-navigator-facet-highlight-under-container .search-navigator-facet {
- margin-bottom: 0;
-}
-
-.search-navigator-facet-highlight-under-container .search-navigator-facet:hover,
-.search-navigator-facet-highlight-under-container .search-navigator-facet.active {
- border-bottom: none;
- padding-bottom: 1px;
- border-radius: 2px 2px 0 0;
-}
-
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet:hover
- ~ .search-navigator-facet,
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet.active
- ~ .search-navigator-facet {
- border-top: none;
- border-bottom: none;
- border-left-color: var(--blue);
- border-right-color: var(--blue);
- border-radius: 0;
-}
-
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet:hover
- ~ .search-navigator-facet:last-of-type,
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet.active
- ~ .search-navigator-facet:last-of-type {
- border-bottom: 1px solid var(--blue);
- border-radius: 0 0 2px 2px;
-}
-
-.search-navigator-facet-highlight-under-container .search-navigator-facet:hover:last-of-type,
-.search-navigator-facet-highlight-under-container .search-navigator-facet.active:last-of-type {
- border-bottom: 1px solid var(--blue);
- border-radius: 2px;
-}
-
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet.active
- ~ .search-navigator-facet {
- background-color: var(--veryLightBlue);
- text-decoration: none;
-}
-
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet.active
- ~ .search-navigator-facet
- .facet-toggle {
- display: inline;
-}
-
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet.active
- ~ .search-navigator-facet:hover,
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet:hover
- ~ .search-navigator-facet.active {
- border-top: 1px solid var(--blue);
-}
-
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet.active
- ~ .search-navigator-facet:hover,
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet.active
- ~ .search-navigator-facet:hover
- ~ .search-navigator-facet {
- background-color: #a1cde8;
- text-decoration: none;
-}
-
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet.active
- ~ .search-navigator-facet:hover
- .facet-toggle,
-.search-navigator-facet-highlight-under-container
- .search-navigator-facet.active
- ~ .search-navigator-facet:hover
- ~ .search-navigator-facet
- .facet-toggle {
- display: inline;
-}
-
.search-navigator-facet-header {
display: block;
flex-shrink: 0;
@@ -379,7 +289,6 @@ button.search-navigator-facet:focus,
padding: 0 10px 10px;
color: var(--baseFontColor);
font-size: var(--smallFontSize);
- white-space: nowrap;
}
.search-navigator-facet-footer {
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index eb7a42fcb26..e8b7b629d87 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -1091,6 +1091,7 @@ projects.facets.duplication.label.2=Click to filter projects with more than 3% d
projects.facets.duplication.label.3=Click to filter projects with more than 5% duplication
projects.facets.duplication.label.4=Click to filter projects with more than 10% duplication
projects.facets.duplication.label.5=Click to filter projects with more than 20% duplication
+projects.facets.no_available_filters_clear_others=No available filters. Clear other filters to see options.
projects.sort.disabled=Disabled because sorting cannot affect the displayed result with the current project selection.
projects.sort.analysis_date=by last analysis date (oldest first)
projects.sort.-analysis_date=by last analysis date (latest first)