From 82ac4a9f28afe0ea6cea613d8836e7b14066245c Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Tue, 28 Jan 2025 12:25:19 -0300 Subject: [PATCH 01/48] test: should get landscape using axios with module rank --- .../webapp/unit/module/domain/landscape/Landscape.fixture.ts | 3 +++ .../webapp/unit/module/secondary/RestModulesRepository.spec.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/test/webapp/unit/module/domain/landscape/Landscape.fixture.ts b/src/test/webapp/unit/module/domain/landscape/Landscape.fixture.ts index 97c7dcf9cd8..b15b2a52281 100644 --- a/src/test/webapp/unit/module/domain/landscape/Landscape.fixture.ts +++ b/src/test/webapp/unit/module/domain/landscape/Landscape.fixture.ts @@ -3,6 +3,7 @@ import { LandscapeElementId } from '@/module/domain/landscape/LandscapeElementId import { LandscapeFeature } from '@/module/domain/landscape/LandscapeFeature'; import { LandscapeFeatureSlug } from '@/module/domain/landscape/LandscapeFeatureSlug'; import { LandscapeModule } from '@/module/domain/landscape/LandscapeModule'; +import { ModuleRank } from '@/module/domain/landscape/ModuleRank'; import { ModulePropertyDefinition } from '@/module/domain/ModulePropertyDefinition'; import { ModuleSlug } from '@/module/domain/ModuleSlug'; import { @@ -84,12 +85,14 @@ const initialModule = ( operation: string, properties: ModulePropertyDefinition[], dependencies: LandscapeElementId[], + rank: ModuleRank = 'RANK_D', ): LandscapeModule => LandscapeModule.initialState({ slug: moduleSlug(slug), operation, properties, dependencies, + rank, }); const featureSlugs = (...slugs: string[]): LandscapeFeatureSlug[] => slugs.map(featureSlug); diff --git a/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts b/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts index a202535a546..6895e3ef2d6 100644 --- a/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts +++ b/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts @@ -1,3 +1,4 @@ +import { ModuleRank } from '@/module/domain/landscape/ModuleRank'; import { ModuleSlug } from '@/module/domain/ModuleSlug'; import { Presets } from '@/module/domain/Presets'; import { RestLandscape } from '@/module/secondary/RestLandscape'; @@ -234,12 +235,14 @@ const landscapeModule = ( operation: string, properties: RestModulePropertiesDefinitions, dependencies?: RestLandscapeDependency[], + rank: ModuleRank = 'RANK_D', ): RestLandscapeModule => ({ type: 'MODULE', slug, operation, properties, dependencies, + rank, }); const landscapeFeature = (slug: string, modules: RestLandscapeModule[]): RestLandscapeFeature => ({ From f051ef40b4ac4b13a71b59c9717455cf66c4d8e8 Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Tue, 28 Jan 2025 11:48:01 -0300 Subject: [PATCH 02/48] feat: add rank field to RestLandscapeModule.ts --- src/main/webapp/app/module/domain/landscape/ModuleRank.ts | 1 + src/main/webapp/app/module/secondary/RestLandscapeModule.ts | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 src/main/webapp/app/module/domain/landscape/ModuleRank.ts diff --git a/src/main/webapp/app/module/domain/landscape/ModuleRank.ts b/src/main/webapp/app/module/domain/landscape/ModuleRank.ts new file mode 100644 index 00000000000..0fd025c1fe8 --- /dev/null +++ b/src/main/webapp/app/module/domain/landscape/ModuleRank.ts @@ -0,0 +1 @@ +export type ModuleRank = 'RANK_D' | 'RANK_C' | 'RANK_B' | 'RANK_A' | 'RANK_S'; diff --git a/src/main/webapp/app/module/secondary/RestLandscapeModule.ts b/src/main/webapp/app/module/secondary/RestLandscapeModule.ts index aed1e5c30f2..a13b3fdb744 100644 --- a/src/main/webapp/app/module/secondary/RestLandscapeModule.ts +++ b/src/main/webapp/app/module/secondary/RestLandscapeModule.ts @@ -1,3 +1,4 @@ +import { ModuleRank } from '@/module/domain/landscape/ModuleRank'; import { LandscapeElementId } from '../domain/landscape/LandscapeElementId'; import { LandscapeElementType } from '../domain/landscape/LandscapeElementType'; import { LandscapeFeatureSlug } from '../domain/landscape/LandscapeFeatureSlug'; @@ -11,6 +12,7 @@ export interface RestLandscapeModule { operation: string; properties: RestModulePropertiesDefinitions; dependencies?: RestLandscapeDependency[]; + rank: ModuleRank; } export interface RestLandscapeDependency { From 7c2137998aace0bf57c74bd9422c669759d0256a Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Tue, 28 Jan 2025 12:26:02 -0300 Subject: [PATCH 03/48] feat: return the module rank in the LandscapeModuleInformation --- src/main/webapp/app/module/domain/landscape/LandscapeModule.ts | 2 ++ src/main/webapp/app/module/secondary/RestLandscapeModule.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/main/webapp/app/module/domain/landscape/LandscapeModule.ts b/src/main/webapp/app/module/domain/landscape/LandscapeModule.ts index 1795cc8f6d0..0a42dfd0db2 100644 --- a/src/main/webapp/app/module/domain/landscape/LandscapeModule.ts +++ b/src/main/webapp/app/module/domain/landscape/LandscapeModule.ts @@ -1,3 +1,4 @@ +import { ModuleRank } from '@/module/domain/landscape/ModuleRank'; import { ModulePropertyDefinition } from '../ModulePropertyDefinition'; import { ModuleSlug } from '../ModuleSlug'; import { LandscapeElement } from './LandscapeElement'; @@ -11,6 +12,7 @@ export interface LandscapeModuleInformation { operation: ModuleOperation; properties: ModulePropertyDefinition[]; dependencies: LandscapeElementId[]; + rank: ModuleRank; } export interface LandscapeModuleContext { diff --git a/src/main/webapp/app/module/secondary/RestLandscapeModule.ts b/src/main/webapp/app/module/secondary/RestLandscapeModule.ts index a13b3fdb744..7602a031b4f 100644 --- a/src/main/webapp/app/module/secondary/RestLandscapeModule.ts +++ b/src/main/webapp/app/module/secondary/RestLandscapeModule.ts @@ -26,6 +26,7 @@ export const toLandscapeModule = (module: RestLandscapeModule): LandscapeModule operation: module.operation, properties: toPropertiesDefinitions(module.properties), dependencies: toModuleDependencies(module.dependencies), + rank: module.rank, }); const toModuleDependencies = (dependencies: RestLandscapeDependency[] | undefined): LandscapeElementId[] => { From 498b6c7d97b91b1086f1583b5b6c74aabf386a5e Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 29 Jan 2025 09:29:06 -0300 Subject: [PATCH 04/48] test: should render the rank module filter in the landscape screen --- .../primary/landscape/LandscapeComponent.spec.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/webapp/unit/module/primary/landscape/LandscapeComponent.spec.ts b/src/test/webapp/unit/module/primary/landscape/LandscapeComponent.spec.ts index ca467bb26ce..01d2ce77176 100644 --- a/src/test/webapp/unit/module/primary/landscape/LandscapeComponent.spec.ts +++ b/src/test/webapp/unit/module/primary/landscape/LandscapeComponent.spec.ts @@ -11,6 +11,7 @@ import { ModulesRepository } from '@/module/domain/ModulesRepository'; import { ModulesToApply } from '@/module/domain/ModulesToApply'; import { LandscapeVue } from '@/module/primary/landscape'; import { LandscapePresetConfigurationVue } from '@/module/primary/landscape-preset-configuration'; +import { LandscapeRankModuleFilterVue } from '@/module/primary/landscape-rank-module-filter'; import { BodyCursorUpdater } from '@/module/primary/landscape/BodyCursorUpdater'; import { LandscapeScroller } from '@/module/primary/landscape/LandscapeScroller'; import { ALERT_BUS } from '@/shared/alert/application/AlertProvider'; @@ -1373,6 +1374,21 @@ describe('Landscape', () => { return { mockModuleRect, mockContainerRect, landscapeContainer }; }; }); + + describe('Rank module filter', () => { + it('should render the rank module filter', async () => { + const { presetComponent } = await setupRankTest(); + + expect(presetComponent.exists()).toBe(true); + }); + + const setupRankTest = async () => { + const wrapper = await componentWithLandscape(); + const presetComponent = wrapper.findComponent(LandscapeRankModuleFilterVue); + + return { wrapper, presetComponent }; + }; + }); }); const assertSelectableHighlightedConnectorsCount = (wrapper: VueWrapper, count: number): void => { From a673c00b441ce7516ea8169a6574021187e12880 Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Thu, 6 Feb 2025 10:29:40 -0300 Subject: [PATCH 05/48] test: create tests for LandscapeRankModuleFilterVue component --- ...LandscapeRankModuleFilterComponent.spec.ts | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/test/webapp/unit/module/primary/landscape/LandscapeRankModuleFilterComponent.spec.ts diff --git a/src/test/webapp/unit/module/primary/landscape/LandscapeRankModuleFilterComponent.spec.ts b/src/test/webapp/unit/module/primary/landscape/LandscapeRankModuleFilterComponent.spec.ts new file mode 100644 index 00000000000..516317b3578 --- /dev/null +++ b/src/test/webapp/unit/module/primary/landscape/LandscapeRankModuleFilterComponent.spec.ts @@ -0,0 +1,83 @@ +import { LandscapeRankModuleFilterVue } from '@/module/primary/landscape-rank-module-filter'; +import { mount } from '@vue/test-utils'; +import { describe, expect, it } from 'vitest'; +import { wrappedElement } from '../../../WrappedElement'; + +const RANKS = ['RANK_D', 'RANK_C', 'RANK_B', 'RANK_A', 'RANK_S']; + +describe('LandscapeRankModuleFilterComponent', () => { + it('should display all rank filters', () => { + const wrapper = mount(LandscapeRankModuleFilterVue); + + const buttons = wrapper.findAll('[data-testid^="rank-"]'); + expect(buttons).toHaveLength(RANKS.length); + }); + + it('should select rank when clicking on filter button', async () => { + const wrapper = mount(LandscapeRankModuleFilterVue); + + const rankDButton = wrapper.find(wrappedElement('rank-RANK_D-filter')); + await rankDButton.trigger('click'); + + expect(wrapper.emitted('selected')).toEqual([[RANKS[0]]]); + }); + + it('should deselect rank when clicking on selected filter button', async () => { + const wrapper = mount(LandscapeRankModuleFilterVue); + + const rankDButton = wrapper.find(wrappedElement('rank-RANK_D-filter')); + await rankDButton.trigger('click'); + await rankDButton.trigger('click'); + + expect(wrapper.emitted('selected')).toEqual([[RANKS[0]], [undefined]]); + }); + + it('should format rank labels correctly', () => { + const wrapper = mount(LandscapeRankModuleFilterVue); + + const buttons = wrapper.findAll('[data-testid^="rank-"]'); + RANKS.forEach((rank, index) => { + expect(buttons[index].text()).toBe(rank.replace('RANK_', '')); + }); + }); + + it('should apply selected style to active filter', async () => { + const wrapper = mount(LandscapeRankModuleFilterVue); + + const rankDButton = wrapper.find(wrappedElement('rank-RANK_D-filter')); + await rankDButton.trigger('click'); + + expect(rankDButton.classes()).toContain('-selected'); + }); + + it('should emit selected rank when clicking a filter button', async () => { + const wrapper = mount(LandscapeRankModuleFilterVue); + + const rankAButton = wrapper.find(wrappedElement('rank-RANK_A-filter')); + await rankAButton.trigger('click'); + + expect(wrapper.emitted('selected')).toEqual([[RANKS[3]]]); + }); + + it('should emit undefined when deselecting a rank', async () => { + const wrapper = mount(LandscapeRankModuleFilterVue); + + const rankSButton = wrapper.find(wrappedElement('rank-RANK_S-filter')); + await rankSButton.trigger('click'); + await rankSButton.trigger('click'); + + expect(wrapper.emitted('selected')).toEqual([[RANKS[4]], [undefined]]); + }); + + it('should only emit one rank at a time', async () => { + const wrapper = mount(LandscapeRankModuleFilterVue); + + const rankBButton = wrapper.find(wrappedElement('rank-RANK_B-filter')); + const rankCButton = wrapper.find(wrappedElement('rank-RANK_C-filter')); + + await rankBButton.trigger('click'); + await rankCButton.trigger('click'); + + expect(wrapper.emitted('selected')).toEqual([[RANKS[2]], [RANKS[1]]]); + }); +}); From d3b036400da418ad72693c4d9837d0c984474a67 Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 29 Jan 2025 10:11:20 -0300 Subject: [PATCH 06/48] feat: create LandscapeRankModuleFilterVue component --- .../LandscapeRankModuleFilter.component.ts | 35 +++++++++++++++++++ .../LandscapeRankModuleFilter.html | 14 ++++++++ .../LandscapeRankModuleFilter.vue | 3 ++ .../landscape-rank-module-filter/index.ts | 3 ++ 4 files changed, 55 insertions(+) create mode 100644 src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.component.ts create mode 100644 src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.html create mode 100644 src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.vue create mode 100644 src/main/webapp/app/module/primary/landscape-rank-module-filter/index.ts diff --git a/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.component.ts b/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.component.ts new file mode 100644 index 00000000000..9556c5dea8d --- /dev/null +++ b/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.component.ts @@ -0,0 +1,35 @@ +import { ModuleRank } from '@/module/domain/landscape/ModuleRank'; +import { defineComponent, ref } from 'vue'; + +export default defineComponent({ + name: 'LandscapeRankModuleFilterVue', + emits: ['selected'], + setup(_, { emit }) { + const ranks: ModuleRank[] = ['RANK_S', 'RANK_A', 'RANK_B', 'RANK_C', 'RANK_D']; + const selectedRank = ref(undefined); + + const isRankSelected = (rank: ModuleRank): boolean => { + return 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 => { + return rank.replace('RANK_', ''); + }; + + return { + ranks, + isRankSelected, + toggleRank, + formatRank, + }; + }, +}); diff --git a/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.html b/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.html new file mode 100644 index 00000000000..8c4d4263688 --- /dev/null +++ b/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.html @@ -0,0 +1,14 @@ +
+
+ +
+
diff --git a/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.vue b/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.vue new file mode 100644 index 00000000000..2bb55a48920 --- /dev/null +++ b/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.vue @@ -0,0 +1,3 @@ + + + diff --git a/src/main/webapp/app/module/primary/landscape-rank-module-filter/index.ts b/src/main/webapp/app/module/primary/landscape-rank-module-filter/index.ts new file mode 100644 index 00000000000..55a0d7b7b2e --- /dev/null +++ b/src/main/webapp/app/module/primary/landscape-rank-module-filter/index.ts @@ -0,0 +1,3 @@ +import LandscapeRankModuleFilterVue from './LandscapeRankModuleFilter.vue'; + +export { LandscapeRankModuleFilterVue }; From 580db8134fafbdbfd5968e151ce0e408d32a16d0 Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 29 Jan 2025 10:20:27 -0300 Subject: [PATCH 07/48] feat: add LandscapeRankModuleFilterVue component inside the Landscape.html screen --- .../webapp/app/module/primary/landscape/Landscape.component.ts | 2 ++ src/main/webapp/app/module/primary/landscape/Landscape.html | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/main/webapp/app/module/primary/landscape/Landscape.component.ts b/src/main/webapp/app/module/primary/landscape/Landscape.component.ts index 48b13eaa701..4a0f6b0d506 100644 --- a/src/main/webapp/app/module/primary/landscape/Landscape.component.ts +++ b/src/main/webapp/app/module/primary/landscape/Landscape.component.ts @@ -19,6 +19,7 @@ import { LandscapeFeatureSlug } from '@/module/domain/landscape/LandscapeFeature import { LandscapeLevel } from '@/module/domain/landscape/LandscapeLevel'; import { LandscapeModule } from '@/module/domain/landscape/LandscapeModule'; import { LandscapeSelectionElement } from '@/module/domain/landscape/LandscapeSelectionElement'; +import { LandscapeRankModuleFilterVue } from '@/module/primary/landscape-rank-module-filter'; import { ALERT_BUS } from '@/shared/alert/application/AlertProvider'; import { IconVue } from '@/shared/icon/infrastructure/primary'; import { Loader } from '@/shared/loader/infrastructure/primary/Loader'; @@ -46,6 +47,7 @@ export default defineComponent({ LandscapeLoaderVue, LandscapeMiniMapVue, LandscapePresetConfigurationVue, + LandscapeRankModuleFilterVue, }, setup() { const applicationListener = inject(APPLICATION_LISTENER); diff --git a/src/main/webapp/app/module/primary/landscape/Landscape.html b/src/main/webapp/app/module/primary/landscape/Landscape.html index 7a26fd98163..8a961640101 100644 --- a/src/main/webapp/app/module/primary/landscape/Landscape.html +++ b/src/main/webapp/app/module/primary/landscape/Landscape.html @@ -22,6 +22,9 @@ +
+ +
From 8dda884275b32e264dad575dcc096af130cefc75 Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 29 Jan 2025 10:29:50 -0300 Subject: [PATCH 08/48] refactor: define a constant with the possible Module Ranks to avoid code duplication --- src/main/webapp/app/module/domain/landscape/ModuleRank.ts | 3 ++- .../LandscapeRankModuleFilter.component.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/app/module/domain/landscape/ModuleRank.ts b/src/main/webapp/app/module/domain/landscape/ModuleRank.ts index 0fd025c1fe8..749b4638ec1 100644 --- a/src/main/webapp/app/module/domain/landscape/ModuleRank.ts +++ b/src/main/webapp/app/module/domain/landscape/ModuleRank.ts @@ -1 +1,2 @@ -export type ModuleRank = 'RANK_D' | 'RANK_C' | 'RANK_B' | 'RANK_A' | 'RANK_S'; +export const RANKS = ['RANK_D', 'RANK_C', 'RANK_B', 'RANK_A', 'RANK_S'] as const; +export type ModuleRank = (typeof RANKS)[number]; diff --git a/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.component.ts b/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.component.ts index 9556c5dea8d..3908c7d3601 100644 --- a/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.component.ts +++ b/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.component.ts @@ -1,11 +1,11 @@ -import { ModuleRank } from '@/module/domain/landscape/ModuleRank'; +import { ModuleRank, RANKS } from '@/module/domain/landscape/ModuleRank'; import { defineComponent, ref } from 'vue'; export default defineComponent({ name: 'LandscapeRankModuleFilterVue', emits: ['selected'], setup(_, { emit }) { - const ranks: ModuleRank[] = ['RANK_S', 'RANK_A', 'RANK_B', 'RANK_C', 'RANK_D']; + const ranks = RANKS; const selectedRank = ref(undefined); const isRankSelected = (rank: ModuleRank): boolean => { From 790b88f2436b45ee4d703879a31b14f757da051f Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 29 Jan 2025 11:18:38 -0300 Subject: [PATCH 09/48] test: set module infinitest as RANK_S --- .../webapp/unit/module/secondary/RestModulesRepository.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts b/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts index 6895e3ef2d6..f019455b1dc 100644 --- a/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts +++ b/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts @@ -176,7 +176,7 @@ const restLandscape = (): RestLandscape => ({ levels: [ { elements: [ - landscapeModule('infinitest', 'Add infinitest filters', applicationBaseNameProperties()), + landscapeModule('infinitest', 'Add infinitest filters', applicationBaseNameProperties(), [], 'RANK_S'), landscapeModule('init', 'Add some initial tools', applicationBaseNameProperties()), landscapeModule('init-props', 'Add some initial tools with extra properties', initPropsProperties()), landscapeModule('prettier', 'Add prettier', applicationBaseNameProperties()), From fcc7539a304e66eeecbafc760f153d151beaefa3 Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 29 Jan 2025 12:30:51 -0300 Subject: [PATCH 10/48] test: should filter Landscape by rank --- .../domain/landscape/LandscapeModule.ts | 4 + .../domain/landscape/Landscape.fixture.ts | 6 +- .../module/domain/landscape/Landscape.spec.ts | 107 ++++++++++++++++++ .../secondary/RestModulesRepository.spec.ts | 4 +- 4 files changed, 116 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/app/module/domain/landscape/LandscapeModule.ts b/src/main/webapp/app/module/domain/landscape/LandscapeModule.ts index 0a42dfd0db2..0f52f6fd199 100644 --- a/src/main/webapp/app/module/domain/landscape/LandscapeModule.ts +++ b/src/main/webapp/app/module/domain/landscape/LandscapeModule.ts @@ -51,6 +51,10 @@ export class LandscapeModule implements LandscapeElement { return this.information.dependencies; } + rank(): ModuleRank { + return this.information.rank; + } + operation(): string { return this.information.operation; } diff --git a/src/test/webapp/unit/module/domain/landscape/Landscape.fixture.ts b/src/test/webapp/unit/module/domain/landscape/Landscape.fixture.ts index b15b2a52281..3f85fe92b73 100644 --- a/src/test/webapp/unit/module/domain/landscape/Landscape.fixture.ts +++ b/src/test/webapp/unit/module/domain/landscape/Landscape.fixture.ts @@ -38,7 +38,7 @@ export const defaultLandscape = (): Landscape => { elements: [ new LandscapeFeature(featureSlug('client'), [ - initialModule('vue', 'Add vue', [], moduleSlugs('init')), + initialModule('vue', 'Add vue', [], moduleSlugs('init'), 'RANK_S'), initialModule('react', 'Add react', [], moduleSlugs('init')), initialModule('angular', 'Add angular', [], moduleSlugs('init')), ]), @@ -50,8 +50,8 @@ export const defaultLandscape = (): Landscape => initialModule('java-base', 'Add base java classes', [], featureSlugs('java-build-tools')), initialModule('spring-boot', 'Add spring boot core', [], featureSlugs('java-build-tools')), new LandscapeFeature(featureSlug('ci'), [ - initialModule('gitlab-maven', 'Add simple gitlab ci for maven', [], moduleSlugs('maven')), - initialModule('gitlab-gradle', 'Add simple gitlab ci for gradle', [], moduleSlugs('gradle')), + initialModule('gitlab-maven', 'Add simple gitlab ci for maven', [], moduleSlugs('maven'), 'RANK_S'), + initialModule('gitlab-gradle', 'Add simple gitlab ci for gradle', [], moduleSlugs('gradle'), 'RANK_S'), ]), ], }, diff --git a/src/test/webapp/unit/module/domain/landscape/Landscape.spec.ts b/src/test/webapp/unit/module/domain/landscape/Landscape.spec.ts index 915f17ae412..7d67ba33e34 100644 --- a/src/test/webapp/unit/module/domain/landscape/Landscape.spec.ts +++ b/src/test/webapp/unit/module/domain/landscape/Landscape.spec.ts @@ -1,3 +1,4 @@ +import { LandscapeFeature } from '@/module/domain/landscape/LandscapeFeature'; import { LandscapeSelectionElement } from '@/module/domain/landscape/LandscapeSelectionElement'; import { LandscapeSelectionTree } from '@/module/domain/landscape/LandscapeSelectionTree'; import { describe, expect, it } from 'vitest'; @@ -286,6 +287,112 @@ describe('Landscape', () => { expect(properties).toEqual([applicationBaseNamePropertyDefinition(), optionalBooleanPropertyDefinition()]); }); }); + + describe('Filter by rank', () => { + it('should return same landscape when no rank filter is applied', () => { + const landscape = defaultLandscape(); + + const filteredLandscape = landscape.filterByRank(undefined); + + expect(filteredLandscape).toEqual(landscape); + }); + + it('should filter modules by rank', () => { + const landscape = defaultLandscape(); + + const filteredLandscape = landscape.filterByRank('RANK_S'); + + const levels = filteredLandscape.standaloneLevels(); + expect(levels).toHaveLength(3); + const rootModules = levels[0].elements.flatMap(element => element.allModules()); + expect(rootModules).toHaveLength(2); + expect(rootModules).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + information: expect.objectContaining({ + slug: moduleSlug('infinitest'), + rank: 'RANK_S', + }), + }), + expect.objectContaining({ + information: expect.objectContaining({ + slug: moduleSlug('init'), + rank: 'RANK_S', + }), + }), + ]), + ); + const clientFeatureModules = levels[1].elements[0].allModules(); + expect(clientFeatureModules).toHaveLength(1); + expect(clientFeatureModules[0]).toEqual( + expect.objectContaining({ + information: expect.objectContaining({ + slug: moduleSlug('vue'), + rank: 'RANK_S', + }), + }), + ); + }); + + it('should filter modules in feature keeping only modules with specified rank', () => { + const landscape = defaultLandscape(); + + const filteredLandscape = landscape.filterByRank('RANK_S'); + + const clientFeature = filteredLandscape + .standaloneLevels()[1] + .elements.find(element => element instanceof LandscapeFeature && element.slugString() === 'client'); + expect(clientFeature).toBeDefined(); + expect(clientFeature?.allModules()).toEqual([ + expect.objectContaining({ + information: expect.objectContaining({ + slug: moduleSlug('vue'), + rank: 'RANK_S', + }), + }), + ]); + }); + + it('should keep dependency modules with different ranks than the rank filter applied', () => { + const landscape = defaultLandscape(); + + const filteredLandscape = landscape.filterByRank('RANK_D'); + + const levels = filteredLandscape.standaloneLevels(); + const initModule = levels[0].elements[0].allModules()[0]; + expect(initModule).toEqual( + expect.objectContaining({ + information: expect.objectContaining({ + slug: moduleSlug('init'), + rank: 'RANK_S', + }), + }), + ); + const reactModule = levels[1].elements[0].allModules()[0]; + expect(reactModule).toEqual( + expect.objectContaining({ + information: expect.objectContaining({ + slug: moduleSlug('react'), + rank: 'RANK_D', + }), + }), + ); + }); + + it('should keep features with different ranks modules than the rank filter applied', () => { + const landscape = defaultLandscape(); + + const filteredLandscape = landscape.filterByRank('RANK_D'); + + const levels = filteredLandscape.standaloneLevels(); + const ciFeature = levels[2].elements[2]; + expect(ciFeature).toEqual( + expect.objectContaining({ + featureSlug: featureSlug('ci'), + }), + ); + }); + }); }); const selectableModule = (slug: string): LandscapeSelectionElement => ({ slug: moduleSlug(slug), selectable: true }); diff --git a/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts b/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts index f019455b1dc..6e003f135b3 100644 --- a/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts +++ b/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts @@ -200,8 +200,8 @@ const restLandscape = (): RestLandscape => ({ landscapeModule('java-base', 'Add base java classes', emptyProperties(), [featureDependency('java-build-tools')]), landscapeModule('spring-boot', 'Add spring boot core', emptyProperties(), [featureDependency('java-build-tools')]), landscapeFeature('ci', [ - landscapeModule('gitlab-maven', 'Add simple gitlab ci for maven', emptyProperties(), [moduleDependency('maven')]), - landscapeModule('gitlab-gradle', 'Add simple gitlab ci for gradle', emptyProperties(), [moduleDependency('gradle')]), + landscapeModule('gitlab-maven', 'Add simple gitlab ci for maven', emptyProperties(), [moduleDependency('maven')], 'RANK_S'), + landscapeModule('gitlab-gradle', 'Add simple gitlab ci for gradle', emptyProperties(), [moduleDependency('gradle')], 'RANK_S'), ]), ], }, From 1ed76d50050e0c0f54285ef2028dc85e76d104c5 Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 29 Jan 2025 12:45:29 -0300 Subject: [PATCH 11/48] feat: filter Landscape by rank --- .../app/module/domain/landscape/Landscape.ts | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/main/webapp/app/module/domain/landscape/Landscape.ts b/src/main/webapp/app/module/domain/landscape/Landscape.ts index f06d90a2a9b..e2b7c105222 100644 --- a/src/main/webapp/app/module/domain/landscape/Landscape.ts +++ b/src/main/webapp/app/module/domain/landscape/Landscape.ts @@ -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'; @@ -404,6 +406,62 @@ export class Landscape { selectedModulesProperties(): ModulePropertyDefinition[] { return this.properties; } + + public filterByRank(rank: ModuleRank | undefined): Landscape { + return Optional.ofNullable(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 { + return element instanceof LandscapeFeature ? this.filterFeature(element, rank) : this.filterModule(element, rank); + } + + private filterFeature(feature: LandscapeFeature, rank: ModuleRank): Optional { + 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 { + return Optional.of(element.allModules()[0]) + .filter(module => this.moduleMatchingRank(module, rank)) + .map(() => element); + } } class LandscapeState { From 192ff065dfa86551d448819ef035c96a8c813ba9 Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 29 Jan 2025 13:03:02 -0300 Subject: [PATCH 12/48] test: fix regression test --- .../unit/module/secondary/RestModulesRepository.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts b/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts index 6e003f135b3..843e691d8cd 100644 --- a/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts +++ b/src/test/webapp/unit/module/secondary/RestModulesRepository.spec.ts @@ -177,7 +177,7 @@ const restLandscape = (): RestLandscape => ({ { elements: [ landscapeModule('infinitest', 'Add infinitest filters', applicationBaseNameProperties(), [], 'RANK_S'), - landscapeModule('init', 'Add some initial tools', applicationBaseNameProperties()), + landscapeModule('init', 'Add some initial tools', applicationBaseNameProperties(), [], 'RANK_S'), landscapeModule('init-props', 'Add some initial tools with extra properties', initPropsProperties()), landscapeModule('prettier', 'Add prettier', applicationBaseNameProperties()), ], @@ -185,7 +185,7 @@ const restLandscape = (): RestLandscape => ({ { elements: [ landscapeFeature('client', [ - landscapeModule('vue', 'Add vue', emptyProperties(), [moduleDependency('init')]), + landscapeModule('vue', 'Add vue', emptyProperties(), [moduleDependency('init')], 'RANK_S'), landscapeModule('react', 'Add react', emptyProperties(), [moduleDependency('init')]), landscapeModule('angular', 'Add angular', emptyProperties(), [moduleDependency('init')]), ]), From 4352126cb9b3a8cccc1c437bdd47e23cf6a51ded Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 29 Jan 2025 10:44:29 -0300 Subject: [PATCH 13/48] test: should filter modules from selected rank --- .../domain/landscape/Landscape.fixture.ts | 4 +- .../landscape/LandscapeComponent.spec.ts | 51 +++++++++++++++++-- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/test/webapp/unit/module/domain/landscape/Landscape.fixture.ts b/src/test/webapp/unit/module/domain/landscape/Landscape.fixture.ts index 3f85fe92b73..6b2008329c1 100644 --- a/src/test/webapp/unit/module/domain/landscape/Landscape.fixture.ts +++ b/src/test/webapp/unit/module/domain/landscape/Landscape.fixture.ts @@ -19,8 +19,8 @@ export const defaultLandscape = (): Landscape => Landscape.initialState([ { elements: [ - initialModule('infinitest', 'Add infinitest filters', [applicationBaseNamePropertyDefinition()], []), - initialModule('init', 'Add some initial tools', [applicationBaseNamePropertyDefinition()], []), + initialModule('infinitest', 'Add infinitest filters', [applicationBaseNamePropertyDefinition()], [], 'RANK_S'), + initialModule('init', 'Add some initial tools', [applicationBaseNamePropertyDefinition()], [], 'RANK_S'), initialModule( 'init-props', 'Add some initial tools with extra properties', diff --git a/src/test/webapp/unit/module/primary/landscape/LandscapeComponent.spec.ts b/src/test/webapp/unit/module/primary/landscape/LandscapeComponent.spec.ts index 01d2ce77176..e8fcf8b032a 100644 --- a/src/test/webapp/unit/module/primary/landscape/LandscapeComponent.spec.ts +++ b/src/test/webapp/unit/module/primary/landscape/LandscapeComponent.spec.ts @@ -1377,16 +1377,59 @@ describe('Landscape', () => { describe('Rank module filter', () => { it('should render the rank module filter', async () => { - const { presetComponent } = await setupRankTest(); + const { rankComponent } = await setupRankTest(); - expect(presetComponent.exists()).toBe(true); + expect(rankComponent.exists()).toBe(true); + }); + + it('should filter modules from selected rank', async () => { + const { wrapper, rankComponent } = await setupRankTest(); + + const rankButton = rankComponent.find(wrappedElement('rank-RANK_S-filter')); + await rankButton.trigger('click'); + await flushPromises(); + await wrapper.vm.$nextTick(); + + expect(wrapper.find(wrappedElement('infinitest-module')).exists()).toBe(true); + expect(wrapper.find(wrappedElement('init-module')).exists()).toBe(true); + expect(wrapper.find(wrappedElement('vue-module')).exists()).toBe(true); + expect(wrapper.find(wrappedElement('java-base')).exists()).toBe(false); + }); + + it('should show all modules when deselect rank', async () => { + const { wrapper, rankComponent } = await setupRankTest(); + const rankButton = rankComponent.find(wrappedElement('rank-RANK_S-filter')); + await rankButton.trigger('click'); + await flushPromises(); + await wrapper.vm.$nextTick(); + + await rankButton.trigger('click'); + await flushPromises(); + await wrapper.vm.$nextTick(); + + expect(wrapper.find(wrappedElement('infinitest-module')).exists()).toBe(true); + expect(wrapper.find(wrappedElement('init-module')).exists()).toBe(true); + expect(wrapper.find(wrappedElement('vue-module')).exists()).toBe(true); + expect(wrapper.find(wrappedElement('java-base-module')).exists()).toBe(true); + }); + + it('should present distinctly with minimal emphasis on dependency modules of different ranks than the selected one', async () => { + const { wrapper, rankComponent } = await setupRankTest(); + + const rankButton = rankComponent.find(wrappedElement('rank-RANK_D-filter')); + await rankButton.trigger('click'); + await flushPromises(); + await wrapper.vm.$nextTick(); + + expect(wrapper.find(wrappedElement('init-module')).exists()).toBe(true); + expect(wrapper.find(wrappedElement('init-module')).classes()).toContain('-diff-rank-minimal-emphasis'); }); const setupRankTest = async () => { const wrapper = await componentWithLandscape(); - const presetComponent = wrapper.findComponent(LandscapeRankModuleFilterVue); + const rankComponent = wrapper.findComponent(LandscapeRankModuleFilterVue); - return { wrapper, presetComponent }; + return { wrapper, rankComponent: rankComponent }; }; }); }); From 942d8a3e66fb0ea25a29498227ad288a906bb45b Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 29 Jan 2025 14:06:07 -0300 Subject: [PATCH 14/48] feat: filter Landscape screen by rank --- .../app/module/domain/landscape/Landscape.ts | 6 +++ .../primary/landscape/Landscape.component.ts | 44 +++++++++++++++++++ .../module/primary/landscape/Landscape.html | 2 +- 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/app/module/domain/landscape/Landscape.ts b/src/main/webapp/app/module/domain/landscape/Landscape.ts index e2b7c105222..b9b5ce8aa0a 100644 --- a/src/main/webapp/app/module/domain/landscape/Landscape.ts +++ b/src/main/webapp/app/module/domain/landscape/Landscape.ts @@ -407,6 +407,12 @@ export class Landscape { return this.properties; } + public hasModuleDifferentRank(module: ModuleSlug, rank: ModuleRank): boolean { + return Optional.ofNullable(this.modules.get(module.get())) + .map(currentModule => currentModule.rank() !== rank) + .orElse(false); + } + public filterByRank(rank: ModuleRank | undefined): Landscape { return Optional.ofNullable(rank) .map(currentRank => this.createFilteredLandscape(currentRank)) diff --git a/src/main/webapp/app/module/primary/landscape/Landscape.component.ts b/src/main/webapp/app/module/primary/landscape/Landscape.component.ts index 4a0f6b0d506..8bf8f6f1cc7 100644 --- a/src/main/webapp/app/module/primary/landscape/Landscape.component.ts +++ b/src/main/webapp/app/module/primary/landscape/Landscape.component.ts @@ -19,6 +19,7 @@ import { LandscapeFeatureSlug } from '@/module/domain/landscape/LandscapeFeature import { LandscapeLevel } from '@/module/domain/landscape/LandscapeLevel'; import { LandscapeModule } from '@/module/domain/landscape/LandscapeModule'; import { LandscapeSelectionElement } from '@/module/domain/landscape/LandscapeSelectionElement'; +import { ModuleRank } from '@/module/domain/landscape/ModuleRank'; import { LandscapeRankModuleFilterVue } from '@/module/primary/landscape-rank-module-filter'; import { ALERT_BUS } from '@/shared/alert/application/AlertProvider'; import { IconVue } from '@/shared/icon/infrastructure/primary'; @@ -58,8 +59,10 @@ export default defineComponent({ const projectFolders = inject(PROJECT_FOLDERS_REPOSITORY); const selectedMode = ref('COMPACTED'); + const selectedRank = ref(undefined); const landscape = ref(Loader.loading()); + const originalLandscape = ref(Loader.loading()); const levels = ref(Loader.loading()); const canLoadMiniMap = ref(false); @@ -144,6 +147,8 @@ export default defineComponent({ }; const loadLandscape = async (response: Landscape): Promise => { + originalLandscape.value.loaded(response); + landscape.value.loaded(response); levels.value.loaded(response.standaloneLevels()); @@ -282,6 +287,14 @@ export default defineComponent({ await nextTick().then(updateConnectors); }; + const diffRankMinimalEmphasisClass = (module: LandscapeElementId): string => { + if (!selectedRank.value || module instanceof LandscapeFeatureSlug) { + return ''; + } + + return landscapeValue().hasModuleDifferentRank(module as ModuleSlug, selectedRank.value) ? ' -diff-rank-minimal-emphasis' : ''; + }; + const anchorPointClass = (module: LandscapeElementId): string => { if (module instanceof LandscapeFeatureSlug) { return ''; @@ -313,6 +326,7 @@ export default defineComponent({ + flavorClass() + anchorPointClass(module) + searchHighlightClass(module) + + diffRankMinimalEmphasisClass(module) ); }; @@ -622,6 +636,35 @@ export default defineComponent({ } }; + const handleRankFilter = (rank: ModuleRank | undefined): void => { + selectedRank.value = rank; + resetToOriginalLandscape().then(() => loadRankFilteredLandscape(landscapeValue().filterByRank(rank))); + }; + + const resetToOriginalLandscape = (): Promise => { + return loadOriginalLandscape(originalLandscape.value.value()); + }; + + const loadOriginalLandscape = async (response: Landscape): Promise => { + landscape.value.loaded(response); + levels.value.loaded(response.standaloneLevels()); + }; + + const loadRankFilteredLandscape = async (response: Landscape): Promise => { + landscapeElements.value = new Map(); + + landscape.value.loaded(response); + levels.value.loaded(response.standaloneLevels()); + + await nextTick(); + + updateConnectors(); + + landscapeNavigation.value.loaded(new LandscapeNavigation(landscapeElements.value, levels.value.value())); + + loadAnchorPointModulesMap(); + }; + return { levels, isFeature, @@ -664,6 +707,7 @@ export default defineComponent({ canLoadMiniMap, selectedPresetName, performSearch, + handleRankFilter, }; }, }); diff --git a/src/main/webapp/app/module/primary/landscape/Landscape.html b/src/main/webapp/app/module/primary/landscape/Landscape.html index 8a961640101..b4e1ce0b7b3 100644 --- a/src/main/webapp/app/module/primary/landscape/Landscape.html +++ b/src/main/webapp/app/module/primary/landscape/Landscape.html @@ -23,7 +23,7 @@
- +
From 07a8856249e715d5039a34c3aeae18340b8db4ca Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 5 Feb 2025 12:26:43 -0300 Subject: [PATCH 15/48] test: has module different rank --- .../module/domain/landscape/Landscape.spec.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/webapp/unit/module/domain/landscape/Landscape.spec.ts b/src/test/webapp/unit/module/domain/landscape/Landscape.spec.ts index 7d67ba33e34..fd09971052a 100644 --- a/src/test/webapp/unit/module/domain/landscape/Landscape.spec.ts +++ b/src/test/webapp/unit/module/domain/landscape/Landscape.spec.ts @@ -288,6 +288,26 @@ describe('Landscape', () => { }); }); + describe('Has module different rank', () => { + it('should return false for unknown module', () => { + const landscape = defaultLandscape(); + + expect(landscape.hasModuleDifferentRank(moduleSlug('unknown'), 'RANK_S')).toBe(false); + }); + + it('should return false when module has same rank', () => { + const landscape = defaultLandscape(); + + expect(landscape.hasModuleDifferentRank(moduleSlug('init'), 'RANK_S')).toBe(false); + }); + + it('should return true when module has different rank', () => { + const landscape = defaultLandscape(); + + expect(landscape.hasModuleDifferentRank(moduleSlug('react'), 'RANK_S')).toBe(true); + }); + }); + describe('Filter by rank', () => { it('should return same landscape when no rank filter is applied', () => { const landscape = defaultLandscape(); From 32233d3203be2590bafa24ebd512579022561179 Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 5 Feb 2025 12:29:29 -0300 Subject: [PATCH 16/48] feat: check if the specific module slug has a different rank than what is passed --- src/main/webapp/app/module/domain/landscape/Landscape.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/app/module/domain/landscape/Landscape.ts b/src/main/webapp/app/module/domain/landscape/Landscape.ts index b9b5ce8aa0a..cf3d4872581 100644 --- a/src/main/webapp/app/module/domain/landscape/Landscape.ts +++ b/src/main/webapp/app/module/domain/landscape/Landscape.ts @@ -408,7 +408,7 @@ export class Landscape { } public hasModuleDifferentRank(module: ModuleSlug, rank: ModuleRank): boolean { - return Optional.ofNullable(this.modules.get(module.get())) + return this.getModule(module) .map(currentModule => currentModule.rank() !== rank) .orElse(false); } From 4ee73361c33f94d809bf444691c39c7d1b4f5a61 Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 5 Feb 2025 13:01:34 -0300 Subject: [PATCH 17/48] refactor: use Optional for the selectedRank const --- .../app/module/domain/landscape/Landscape.ts | 6 ++--- .../primary/landscape/Landscape.component.ts | 26 +++++++++++-------- .../module/domain/landscape/Landscape.spec.ts | 11 ++++---- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/main/webapp/app/module/domain/landscape/Landscape.ts b/src/main/webapp/app/module/domain/landscape/Landscape.ts index cf3d4872581..1f2dc01f215 100644 --- a/src/main/webapp/app/module/domain/landscape/Landscape.ts +++ b/src/main/webapp/app/module/domain/landscape/Landscape.ts @@ -413,10 +413,8 @@ export class Landscape { .orElse(false); } - public filterByRank(rank: ModuleRank | undefined): Landscape { - return Optional.ofNullable(rank) - .map(currentRank => this.createFilteredLandscape(currentRank)) - .orElse(this); + public filterByRank(rank: Optional): Landscape { + return rank.map(currentRank => this.createFilteredLandscape(currentRank)).orElse(this); } private createFilteredLandscape(rank: ModuleRank): Landscape { diff --git a/src/main/webapp/app/module/primary/landscape/Landscape.component.ts b/src/main/webapp/app/module/primary/landscape/Landscape.component.ts index 8bf8f6f1cc7..ee6dbcf0a02 100644 --- a/src/main/webapp/app/module/primary/landscape/Landscape.component.ts +++ b/src/main/webapp/app/module/primary/landscape/Landscape.component.ts @@ -59,7 +59,6 @@ export default defineComponent({ const projectFolders = inject(PROJECT_FOLDERS_REPOSITORY); const selectedMode = ref('COMPACTED'); - const selectedRank = ref(undefined); const landscape = ref(Loader.loading()); const originalLandscape = ref(Loader.loading()); @@ -95,6 +94,8 @@ export default defineComponent({ const highlightedModule = ref>(Optional.empty()); + const selectedRank = ref>(Optional.empty()); + onMounted(() => { modules .landscape() @@ -287,14 +288,6 @@ export default defineComponent({ await nextTick().then(updateConnectors); }; - const diffRankMinimalEmphasisClass = (module: LandscapeElementId): string => { - if (!selectedRank.value || module instanceof LandscapeFeatureSlug) { - return ''; - } - - return landscapeValue().hasModuleDifferentRank(module as ModuleSlug, selectedRank.value) ? ' -diff-rank-minimal-emphasis' : ''; - }; - const anchorPointClass = (module: LandscapeElementId): string => { if (module instanceof LandscapeFeatureSlug) { return ''; @@ -407,6 +400,17 @@ export default defineComponent({ .orElse(''); }; + const diffRankMinimalEmphasisClass = (module: LandscapeElementId): string => { + if (module instanceof LandscapeFeatureSlug) { + return ''; + } + + return selectedRank.value + .map(rank => landscapeValue().hasModuleDifferentRank(module as ModuleSlug, rank)) + .map(hasDifferentRank => (hasDifferentRank ? ' -diff-rank-minimal-emphasis' : '')) + .orElse(''); + }; + const modeClass = (): string => { switch (selectedMode.value) { case 'COMPACTED': @@ -637,8 +641,8 @@ export default defineComponent({ }; const handleRankFilter = (rank: ModuleRank | undefined): void => { - selectedRank.value = rank; - resetToOriginalLandscape().then(() => loadRankFilteredLandscape(landscapeValue().filterByRank(rank))); + selectedRank.value = Optional.ofNullable(rank); + resetToOriginalLandscape().then(() => loadRankFilteredLandscape(landscapeValue().filterByRank(selectedRank.value))); }; const resetToOriginalLandscape = (): Promise => { diff --git a/src/test/webapp/unit/module/domain/landscape/Landscape.spec.ts b/src/test/webapp/unit/module/domain/landscape/Landscape.spec.ts index fd09971052a..aa91af0b47d 100644 --- a/src/test/webapp/unit/module/domain/landscape/Landscape.spec.ts +++ b/src/test/webapp/unit/module/domain/landscape/Landscape.spec.ts @@ -1,6 +1,7 @@ import { LandscapeFeature } from '@/module/domain/landscape/LandscapeFeature'; import { LandscapeSelectionElement } from '@/module/domain/landscape/LandscapeSelectionElement'; import { LandscapeSelectionTree } from '@/module/domain/landscape/LandscapeSelectionTree'; +import { Optional } from '@/shared/optional/domain/Optional'; import { describe, expect, it } from 'vitest'; import { applicationBaseNamePropertyDefinition, moduleSlug, optionalBooleanPropertyDefinition } from '../Modules.fixture'; import { defaultLandscape, featureSlug } from './Landscape.fixture'; @@ -312,7 +313,7 @@ describe('Landscape', () => { it('should return same landscape when no rank filter is applied', () => { const landscape = defaultLandscape(); - const filteredLandscape = landscape.filterByRank(undefined); + const filteredLandscape = landscape.filterByRank(Optional.empty()); expect(filteredLandscape).toEqual(landscape); }); @@ -320,7 +321,7 @@ describe('Landscape', () => { it('should filter modules by rank', () => { const landscape = defaultLandscape(); - const filteredLandscape = landscape.filterByRank('RANK_S'); + const filteredLandscape = landscape.filterByRank(Optional.of('RANK_S')); const levels = filteredLandscape.standaloneLevels(); expect(levels).toHaveLength(3); @@ -357,7 +358,7 @@ describe('Landscape', () => { it('should filter modules in feature keeping only modules with specified rank', () => { const landscape = defaultLandscape(); - const filteredLandscape = landscape.filterByRank('RANK_S'); + const filteredLandscape = landscape.filterByRank(Optional.of('RANK_S')); const clientFeature = filteredLandscape .standaloneLevels()[1] @@ -376,7 +377,7 @@ describe('Landscape', () => { it('should keep dependency modules with different ranks than the rank filter applied', () => { const landscape = defaultLandscape(); - const filteredLandscape = landscape.filterByRank('RANK_D'); + const filteredLandscape = landscape.filterByRank(Optional.of('RANK_D')); const levels = filteredLandscape.standaloneLevels(); const initModule = levels[0].elements[0].allModules()[0]; @@ -402,7 +403,7 @@ describe('Landscape', () => { it('should keep features with different ranks modules than the rank filter applied', () => { const landscape = defaultLandscape(); - const filteredLandscape = landscape.filterByRank('RANK_D'); + const filteredLandscape = landscape.filterByRank(Optional.of('RANK_D')); const levels = filteredLandscape.standaloneLevels(); const ciFeature = levels[2].elements[2]; From 85cf9776019c842d7c644ba2417753739210c942 Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Wed, 5 Feb 2025 13:35:43 -0300 Subject: [PATCH 18/48] refactor: simplify the handleRankFilter method --- .../primary/landscape/Landscape.component.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/main/webapp/app/module/primary/landscape/Landscape.component.ts b/src/main/webapp/app/module/primary/landscape/Landscape.component.ts index ee6dbcf0a02..226bd7070c8 100644 --- a/src/main/webapp/app/module/primary/landscape/Landscape.component.ts +++ b/src/main/webapp/app/module/primary/landscape/Landscape.component.ts @@ -642,30 +642,17 @@ export default defineComponent({ const handleRankFilter = (rank: ModuleRank | undefined): void => { selectedRank.value = Optional.ofNullable(rank); - resetToOriginalLandscape().then(() => loadRankFilteredLandscape(landscapeValue().filterByRank(selectedRank.value))); + reloadLandscape(originalLandscape.value.value().filterByRank(selectedRank.value)).then(() => {}); }; - const resetToOriginalLandscape = (): Promise => { - return loadOriginalLandscape(originalLandscape.value.value()); - }; - - const loadOriginalLandscape = async (response: Landscape): Promise => { - landscape.value.loaded(response); - levels.value.loaded(response.standaloneLevels()); - }; - - const loadRankFilteredLandscape = async (response: Landscape): Promise => { + const reloadLandscape = async (response: Landscape): Promise => { landscapeElements.value = new Map(); - landscape.value.loaded(response); levels.value.loaded(response.standaloneLevels()); await nextTick(); - updateConnectors(); - landscapeNavigation.value.loaded(new LandscapeNavigation(landscapeElements.value, levels.value.value())); - loadAnchorPointModulesMap(); }; From a13a3b08ae4d710afb51e0b4fc428342321799f0 Mon Sep 17 00:00:00 2001 From: Renan Franca Date: Thu, 6 Feb 2025 11:41:23 -0300 Subject: [PATCH 19/48] feat: create tikui landscape-rank-module-filter organism --- src/main/style/organism/_organism.scss | 1 + .../_landscape-rank-module-filter.scss | 9 +++++++++ .../landscape-rank-module-filter.code.pug | 3 +++ .../landscape-rank-module-filter.md | 1 + .../landscape-rank-module-filter.mixin.pug | 10 ++++++++++ .../landscape-rank-module-filter.render.pug | 4 ++++ src/main/style/organism/organism.pug | 2 ++ .../LandscapeRankModuleFilter.html | 4 ++-- 8 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 src/main/style/organism/landscape-rank-module-filter/_landscape-rank-module-filter.scss create mode 100644 src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.code.pug create mode 100644 src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.md create mode 100644 src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.mixin.pug create mode 100644 src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.render.pug diff --git a/src/main/style/organism/_organism.scss b/src/main/style/organism/_organism.scss index 99a46441d9c..b5c61cb5a6f 100644 --- a/src/main/style/organism/_organism.scss +++ b/src/main/style/organism/_organism.scss @@ -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'; diff --git a/src/main/style/organism/landscape-rank-module-filter/_landscape-rank-module-filter.scss b/src/main/style/organism/landscape-rank-module-filter/_landscape-rank-module-filter.scss new file mode 100644 index 00000000000..ec9600ebd4d --- /dev/null +++ b/src/main/style/organism/landscape-rank-module-filter/_landscape-rank-module-filter.scss @@ -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; + } +} diff --git a/src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.code.pug b/src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.code.pug new file mode 100644 index 00000000000..8e1b90b5c0e --- /dev/null +++ b/src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.code.pug @@ -0,0 +1,3 @@ +include landscape-rank-module-filter.mixin.pug + ++jhlite-landscape-rank-module-filter diff --git a/src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.md b/src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.md new file mode 100644 index 00000000000..3474467def7 --- /dev/null +++ b/src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.md @@ -0,0 +1 @@ +## Landscape rank module filter diff --git a/src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.mixin.pug b/src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.mixin.pug new file mode 100644 index 00000000000..973b52c00f0 --- /dev/null +++ b/src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.mixin.pug @@ -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 diff --git a/src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.render.pug b/src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.render.pug new file mode 100644 index 00000000000..f944ccb27d7 --- /dev/null +++ b/src/main/style/organism/landscape-rank-module-filter/landscape-rank-module-filter.render.pug @@ -0,0 +1,4 @@ +extends /layout + +block body + include landscape-rank-module-filter.code.pug diff --git a/src/main/style/organism/organism.pug b/src/main/style/organism/organism.pug index b772faf30ea..9648434611b 100644 --- a/src/main/style/organism/organism.pug +++ b/src/main/style/organism/organism.pug @@ -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 diff --git a/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.html b/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.html index 8c4d4263688..e4b244a94a7 100644 --- a/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.html +++ b/src/main/webapp/app/module/primary/landscape-rank-module-filter/LandscapeRankModuleFilter.html @@ -1,9 +1,9 @@
-
+
-
- -
+
+ +