Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

display filter rank options in frontend #11910

Open
wants to merge 48 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
82ac4a9
test: should get landscape using axios with module rank
renanfranca Jan 28, 2025
f051ef4
feat: add rank field to RestLandscapeModule.ts
renanfranca Jan 28, 2025
7c21379
feat: return the module rank in the LandscapeModuleInformation
renanfranca Jan 28, 2025
498b6c7
test: should render the rank module filter in the landscape screen
renanfranca Jan 29, 2025
a673c00
test: create tests for LandscapeRankModuleFilterVue component
renanfranca Feb 6, 2025
d3b0364
feat: create LandscapeRankModuleFilterVue component
renanfranca Jan 29, 2025
580db81
feat: add LandscapeRankModuleFilterVue component inside the Landscape…
renanfranca Jan 29, 2025
8dda884
refactor: define a constant with the possible Module Ranks to avoid c…
renanfranca Jan 29, 2025
790b88f
test: set module infinitest as RANK_S
renanfranca Jan 29, 2025
fcc7539
test: should filter Landscape by rank
renanfranca Jan 29, 2025
1ed76d5
feat: filter Landscape by rank
renanfranca Jan 29, 2025
192ff06
test: fix regression test
renanfranca Jan 29, 2025
4352126
test: should filter modules from selected rank
renanfranca Jan 29, 2025
942d8a3
feat: filter Landscape screen by rank
renanfranca Jan 29, 2025
07a8856
test: has module different rank
renanfranca Feb 5, 2025
32233d3
feat: check if the specific module slug has a different rank than wha…
renanfranca Feb 5, 2025
4ee7336
refactor: use Optional for the selectedRank const
renanfranca Feb 5, 2025
85cf977
refactor: simplify the handleRankFilter method
renanfranca Feb 5, 2025
a13a3b0
feat: create tikui landscape-rank-module-filter organism
renanfranca Feb 6, 2025
555eded
feat: create tikui button-toggle atom
renanfranca Feb 11, 2025
cb88b0f
feat: integrate landscape-rank-module-filter organism into LandscapeR…
renanfranca Feb 11, 2025
88003c4
feat: set the position of the LandscapeRankModuleFilterVue inside the…
renanfranca Feb 11, 2025
50e8d82
test: should display correct description for rank button
renanfranca Feb 11, 2025
6c0b04b
feat: add tooltips to rank toggle buttons
renanfranca Feb 11, 2025
38370ec
feat: define the style for the -diff-rank-minimal-emphasis
renanfranca Feb 11, 2025
c413233
feat: add tooltips to rank toggle buttons
renanfranca Feb 12, 2025
9a82ddd
test: refactor LandscapeRankModuleFilterComponent.spec.ts by organize…
renanfranca Feb 12, 2025
51bdc81
test: should disable rank button without module rank associated
renanfranca Feb 12, 2025
ce8b8c0
feat: disable rank button without module rank associated
renanfranca Feb 12, 2025
31cecce
feat: set the new property of LandscapeRankModuleFilterVue component …
renanfranca Feb 13, 2025
779eecf
refactor: rename RankUsed to ModuleRankCount and rename RanksUsed to …
renanfranca Feb 13, 2025
c1aaa0a
refactor: improve LandscapeRankModuleFilterVue code
renanfranca Feb 13, 2025
d834646
refactor: improve ModuleRankStatistics code by make it more elegant
renanfranca Feb 13, 2025
135893b
refactor: tikui button-toggle atom by removing unnecessary styles and…
renanfranca Feb 13, 2025
e319466
test: setup tests to simulate the DOM update error on Landscape scree…
renanfranca Feb 13, 2025
3d57959
fix: ensure that every module and feature is correctly rendered befor…
renanfranca Feb 13, 2025
09d047a
test: should deselect preset option when a rank button is clicked
renanfranca Feb 14, 2025
86388c8
feat: deselect preset option when a rank button is clicked
renanfranca Feb 14, 2025
c78d592
test: refactor Rank module filter tests to avoid code duplication
renanfranca Feb 14, 2025
b2f1928
test: refactor Has module with improved test names for different ranks
renanfranca Feb 14, 2025
7b2b1d8
fix: sonar issue at ModuleRankStatistics.ts
renanfranca Feb 14, 2025
dce61fe
fix: sonar issue at ModuleRankStatistics.ts
renanfranca Feb 14, 2025
232d7c6
fix: sonar issue at Landscape.component.ts
renanfranca Feb 14, 2025
b9393e3
fix: correct the import order
renanfranca Feb 14, 2025
d6ed03f
fix: sonar issue to add at least one assertion to this test case
renanfranca Feb 14, 2025
7798657
fix: remove useless branch code
renanfranca Feb 14, 2025
f490f9c
refactor: remove unnecessary bracket on constant function declaration
renanfranca Feb 17, 2025
2189f99
refactor: explicit ignore the Promise result and avoid the warning Pr…
renanfranca Feb 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/style/atom/button/_button.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
@use 'button-main/button-main';
@use 'button-switch/button-switch';
@use 'button-toggle/button-toggle';
42 changes: 42 additions & 0 deletions src/main/style/atom/button/button-toggle/_button-toggle.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@use 'sass:math';
@use '../../../token/colors' as colors;
@use '../../../token/color/brand' as brand;
@use '../button-main/button-main' as button-main;

$jhlite-button-toggle-padding: math.div(5, 16) * 1rem math.div(15, 16) * 1rem;
$jhlite-button-toggle-border: 1px solid brand.$jhlite-color-brand-600;
$jhlite-button-toggle-font-size: math.div(13, 16) * 1rem;
$jhlite-button-toggle-hover-color-background: rgba(button-main.$jhlite-button-color-background, 0.1);
$jhlite-button-toggle-disabled-color-background: button-main.$jhlite-button-disabled-color-background;
$jhlite-button-toggle-active-color-background: button-main.$jhlite-button-color-background;
$jhlite-button-toggle-active-hover-color-background: button-main.$jhlite-button-hover-color-background;

.jhlite-button-toggle {
@extend %jhlite-button-main;

border: $jhlite-button-toggle-border;
background-color: transparent;
padding: $jhlite-button-toggle-padding;
text-transform: none;
color: var(--jhlite-global-color-text);
font-size: $jhlite-button-toggle-font-size;

&:hover {
background-color: $jhlite-button-toggle-hover-color-background;
}

&:disabled {
&:hover {
background-color: $jhlite-button-toggle-disabled-color-background;
}
}

&.-active {
background-color: $jhlite-button-toggle-active-color-background;
color: colors.$jhlite-global-color-text-light;

&:hover {
background-color: $jhlite-button-toggle-active-hover-color-background;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include button-toggle.mixin.pug

+jhlite-button-toggle Toggle
+jhlite-button-toggle({active: true}) Toggle active
+jhlite-button-toggle({disabled: true}) Toggle disabled
1 change: 1 addition & 0 deletions src/main/style/atom/button/button-toggle/button-toggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
### Button toggle
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mixin jhlite-button-toggle(opts)
- const { active, disabled } = opts || {};
- const activeClass = active ? '-active' : null;
button.jhlite-button-toggle(class=activeClass, disabled=disabled)
block
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
extends /layout

block body
include button-toggle.code.pug
2 changes: 2 additions & 0 deletions src/main/style/atom/button/button.pug
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
include:componentDoc(height=150) button-main/button-main.md
.tikui-vertical-spacing--line
include:componentDoc(height=150) button-switch/button-switch.md
.tikui-vertical-spacing--line
include:componentDoc(height=150) button-toggle/button-toggle.md
1 change: 1 addition & 0 deletions src/main/style/organism/_organism.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
@use 'landscape-loader/landscape-loader';
@use 'landscape-minimap/landscape-minimap';
@use 'landscape-preset-configuration/landscape-preset-configuration';
@use 'landscape-rank-module-filter/landscape-rank-module-filter';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@use '../../token/size';

.jhlite-landscape-rank-module-filter {
&--ranks {
display: flex;
gap: size.$jhlite-global-size-field-padding;
align-items: center;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include landscape-rank-module-filter.mixin.pug

+jhlite-landscape-rank-module-filter
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## Landscape rank module filter
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
include /atom/button/button-toggle/button-toggle.mixin.pug

mixin jhlite-landscape-rank-module-filter
.jhlite-landscape-rank-module-filter
.jhlite-landscape-rank-module-filter--ranks
+jhlite-button-toggle D
+jhlite-button-toggle({ disabled: true }) C
+jhlite-button-toggle({ disabled: true }) B
+jhlite-button-toggle({ disabled: true }) A
+jhlite-button-toggle({ active: true }) S
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
extends /layout

block body
include landscape-rank-module-filter.code.pug
25 changes: 24 additions & 1 deletion src/main/style/organism/landscape/_landscape.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
@use '../../token/size';

$jhlite-landscape-padding: 20px;
$jhipster-landscape-modes-selection-top-position: $jhlite-landscape-padding;
$jhipster-landscape-preset-selection-top-position: 85px;
$jhipster-landscape-modes-selection-top-position-large-screen: 85px;
$jhipster-landscape-preset-selection-top-position-large-screen: 150px;
$jhipster-landscape-preset-selection-left-position: $jhlite-landscape-padding;
$jhlite-landscape-line-color: colors.$jhlite-global-line-color;
$jhlite-landscape-box-radius: colors.$jhlite-global-box-radius;
Expand All @@ -23,7 +26,7 @@ $jhlite-landscape-primary-alternative-color: colors.$jhlite-global-primary-alter

.jhipster-landscape-modes-selection {
position: absolute;
top: $jhlite-landscape-padding;
top: $jhipster-landscape-modes-selection-top-position;
left: $jhlite-landscape-padding;
z-index: 3;
border: 1px dotted $jhlite-landscape-line-color;
Expand All @@ -40,6 +43,13 @@ $jhlite-landscape-primary-alternative-color: colors.$jhlite-global-primary-alter
background: var(--jhlite-chip-bg-color);
}

.jhipster-landscape-rank-module-selection {
display: flex;
justify-content: center;
margin-left: $jhlite-landscape-padding;
background: var(--jhlite-chip-bg-color);
}

.jhipster-landscape-content {
&--header {
display: none;
Expand Down Expand Up @@ -184,6 +194,11 @@ $jhlite-landscape-primary-alternative-color: colors.$jhlite-global-primary-alter
border-radius: size.$jhlite-global-size-field-radius;
box-shadow: 0 0 0 size.$jhlite-global-size-field-border-focus colors.$jhlite-attention-highlight-color;
}

.-diff-rank-minimal-emphasis {
opacity: 0.5;
border: 1px dotted $jhlite-landscape-line-color;
}
}

.jhipster-landscape {
Expand All @@ -192,6 +207,14 @@ $jhlite-landscape-primary-alternative-color: colors.$jhlite-global-primary-alter

@media screen and (min-width: breakpoint.$jhlite-global-breakpoint-small-medium) {
.jhipster-landscape {
.jhipster-landscape-modes-selection {
top: $jhipster-landscape-modes-selection-top-position-large-screen;
}

.jhipster-landscape-preset-selection {
top: $jhipster-landscape-preset-selection-top-position-large-screen;
}

.jhipster-landscape-content {
display: flex;
flex-direction: column;
Expand Down
2 changes: 2 additions & 0 deletions src/main/style/organism/organism.pug
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ block content
include:componentDoc(height=160) landscape-minimap/landscape-minimap.md
.tikui-vertical-spacing--line
include:componentDoc(height=160) landscape-preset-configuration/landscape-preset-configuration.md
.tikui-vertical-spacing--line
include:componentDoc(height=160) landscape-rank-module-filter/landscape-rank-module-filter.md
.tikui-vertical-spacing--line
include:componentDoc(height=230) module-parameters/module-parameters.md
.tikui-vertical-spacing--line
Expand Down
8 changes: 8 additions & 0 deletions src/main/webapp/app/module/domain/ModuleRankCount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ModuleRank } from '@/module/domain/landscape/ModuleRank';

type ModuleRankCountQuantity = number;

export type ModuleRankCount = {
rank: ModuleRank;
quantity: ModuleRankCountQuantity;
};
25 changes: 25 additions & 0 deletions src/main/webapp/app/module/domain/ModuleRankStatistics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ModuleRankCount } from '@/module/domain/ModuleRankCount';
import { Landscape } from '@/module/domain/landscape/Landscape';
import { ModuleRank, RANKS } from '@/module/domain/landscape/ModuleRank';

export type ModuleRankStatistics = ModuleRankCount[];

export const toModuleRankStatistics = (landscape: Landscape): ModuleRankStatistics => {
const rankCounts = landscape
.standaloneLevels()
.flatMap(level => level.elements)
.flatMap(element => element.allModules())
.reduce(
(counts, module) => {
const currentCount = counts.get(module.rank())!;
counts.set(module.rank(), currentCount + 1);
return counts;
},
new Map<ModuleRank, number>(RANKS.map(rank => [rank, 0])),
);

return RANKS.map(rank => ({
rank,
quantity: rankCounts.get(rank)!,
}));
};
3 changes: 3 additions & 0 deletions src/main/webapp/app/module/domain/RankDescription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type RankDescription = {
[key: string]: string;
};
62 changes: 62 additions & 0 deletions src/main/webapp/app/module/domain/landscape/Landscape.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { LandscapeElement } from '@/module/domain/landscape/LandscapeElement';
import { ModuleRank } from '@/module/domain/landscape/ModuleRank';
import { Memoizer } from '@/shared/memoizer/domain/Memoizer';
import { Optional } from '@/shared/optional/domain/Optional';
import { ModulePropertyDefinition } from '../ModulePropertyDefinition';
Expand Down Expand Up @@ -404,6 +406,66 @@ export class Landscape {
selectedModulesProperties(): ModulePropertyDefinition[] {
return this.properties;
}

public hasModuleDifferentRank(module: ModuleSlug, rank: ModuleRank): boolean {
return this.getModule(module)
.map(currentModule => currentModule.rank() !== rank)
.orElse(false);
}

public filterByRank(rank: Optional<ModuleRank>): Landscape {
return rank.map(currentRank => this.createFilteredLandscape(currentRank)).orElse(this);
}

private createFilteredLandscape(rank: ModuleRank): Landscape {
const filteredLevels = this.projections.levels.map(level => this.filterLevel(level, rank)).filter(level => level.elements.length > 0);

return new Landscape(this.state, new LevelsProjections(filteredLevels));
}

private filterLevel(level: LandscapeLevel, rank: ModuleRank): { elements: LandscapeElement[] } {
return {
elements: level.elements.map(element => this.filterElementByRank(element, rank)).flatMap(optional => optional.toArray()),
};
}

private filterElementByRank(element: LandscapeElement, rank: ModuleRank): Optional<LandscapeElement> {
return element instanceof LandscapeFeature ? this.filterFeature(element, rank) : this.filterModule(element, rank);
}

private filterFeature(feature: LandscapeFeature, rank: ModuleRank): Optional<LandscapeElement> {
if (this.dependencyFeatureOfRankedModule(feature.slug(), rank)) {
return Optional.of(feature);
}

return Optional.of(feature.modules)
.filter(modules => modules.some(module => this.moduleMatchingRank(module, rank)))
.map(modules => modules.filter(module => this.moduleMatchingRank(module, rank)))
.map(filteredModules => new LandscapeFeature(feature.slug(), filteredModules));
}

private dependencyFeatureOfRankedModule(featureSlug: LandscapeFeatureSlug, rank: ModuleRank): boolean {
return Array.from(this.modules.values())
.filter(module => module.rank() === rank)
.some(rankedModule => rankedModule.dependencies().some(dep => dep.get() === featureSlug.get()));
}

private moduleMatchingRank(module: LandscapeModule, rank: ModuleRank): boolean {
return module.rank() === rank || this.dependencyOfRankedModule(module, rank);
}

private dependencyOfRankedModule(module: LandscapeModule, rank: ModuleRank): boolean {
return Optional.of(Array.from(this.modules.values()))
.map(modules => modules.filter(m => m.rank() === rank))
.map(rankedModules => rankedModules.some(rankedModule => rankedModule.dependencies().some(dep => dep.get() === module.slug().get())))
.orElse(false);
}

private filterModule(element: LandscapeElement, rank: ModuleRank): Optional<LandscapeElement> {
return Optional.of(element.allModules()[0])
.filter(module => this.moduleMatchingRank(module, rank))
.map(() => element);
}
}

class LandscapeState {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ModuleRank } from '@/module/domain/landscape/ModuleRank';
import { ModulePropertyDefinition } from '../ModulePropertyDefinition';
import { ModuleSlug } from '../ModuleSlug';
import { LandscapeElement } from './LandscapeElement';
Expand All @@ -11,6 +12,7 @@ export interface LandscapeModuleInformation {
operation: ModuleOperation;
properties: ModulePropertyDefinition[];
dependencies: LandscapeElementId[];
rank: ModuleRank;
}

export interface LandscapeModuleContext {
Expand Down Expand Up @@ -49,6 +51,10 @@ export class LandscapeModule implements LandscapeElement {
return this.information.dependencies;
}

rank(): ModuleRank {
return this.information.rank;
}

operation(): string {
return this.information.operation;
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/webapp/app/module/domain/landscape/ModuleRank.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const RANKS = ['RANK_D', 'RANK_C', 'RANK_B', 'RANK_A', 'RANK_S'] as const;
export type ModuleRank = (typeof RANKS)[number];
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { ModuleRank } from '@/module/domain/landscape/ModuleRank';
import { RANKS } from '@/module/domain/landscape/ModuleRank';
import type { ModuleRankStatistics } from '@/module/domain/ModuleRankStatistics';
import type { RankDescription } from '@/module/domain/RankDescription';
import { PropType, defineComponent, ref } from 'vue';

export default defineComponent({
name: 'LandscapeRankModuleFilterVue',
props: {
moduleRankStatistics: {
type: Array as PropType<ModuleRankStatistics>,
required: true,
},
},
emits: ['selected'],
setup(props, { emit }) {
const ranks = RANKS;
const selectedRank = ref<ModuleRank | undefined>(undefined);

const rankDescriptions: RankDescription = {
RANK_D: 'Experimental or advanced module requiring specific expertise',
RANK_C: 'Module without known production usage',
RANK_B: 'Module with at least one confirmed production usage',
RANK_A: 'Module with multiple production usages across different projects and documented through talks, books or blog posts',
RANK_S: 'Production-proven module providing unique features, validated by community feedback (10+ endorsements)',
};

const isRankSelected = (rank: ModuleRank): boolean => selectedRank.value === rank;

const toggleRank = (rank: ModuleRank): void => {
if (selectedRank.value === rank) {
selectedRank.value = undefined;
} else {
selectedRank.value = rank;
}
emit('selected', selectedRank.value);
};

const formatRank = (rank: ModuleRank): string => rank.replace('RANK_', '');

const getRankDescription = (rank: ModuleRank): string => rankDescriptions[rank];

const isRankDisabled = (rank: ModuleRank): boolean => props.moduleRankStatistics.find(ru => ru.rank === rank)?.quantity === 0;

return {
ranks,
isRankSelected,
toggleRank,
formatRank,
getRankDescription,
isRankDisabled,
};
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<div class="landscape-rank-module-filter">
<div class="jhlite-landscape-rank-module-filter--ranks">
<button
v-for="rank in ranks"
:key="rank"
class="jhlite-button-toggle"
:class="{ '-active': isRankSelected(rank) }"
@click="toggleRank(rank)"
:data-testid="`rank-${rank}-filter`"
:title="getRankDescription(rank)"
:disabled="isRankDisabled(rank)"
>
{{ formatRank(rank) }}
</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template src="./LandscapeRankModuleFilter.html"></template>

<script lang="ts" src="./LandscapeRankModuleFilter.component.ts"></script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import LandscapeRankModuleFilterVue from './LandscapeRankModuleFilter.vue';

export { LandscapeRankModuleFilterVue };
Loading