From b599c3eb029b5ed29404eb5b02e1cd4122c48d61 Mon Sep 17 00:00:00 2001 From: arbulu89 Date: Tue, 29 Aug 2023 09:29:47 +0200 Subject: [PATCH 1/4] Refactor DatabasesOverview component and add story --- .../DatabasesOverview/DatabasesOverview.jsx | 57 ++++-------- .../DatabasesOverview.stories.jsx | 80 +++++++++++++++++ .../DatabasesOverview.test.jsx | 87 +++++-------------- .../DatabasesOverviewPage.jsx | 47 ++++++++++ .../js/components/DatabasesOverview/index.js | 4 +- .../InstanceOverview/InstanceOverview.jsx | 16 ++-- .../js/lib/test-utils/factories/databases.js | 2 + 7 files changed, 179 insertions(+), 114 deletions(-) create mode 100644 assets/js/components/DatabasesOverview/DatabasesOverview.stories.jsx create mode 100644 assets/js/components/DatabasesOverview/DatabasesOverviewPage.jsx diff --git a/assets/js/components/DatabasesOverview/DatabasesOverview.jsx b/assets/js/components/DatabasesOverview/DatabasesOverview.jsx index 1072608104..d4b08cadba 100644 --- a/assets/js/components/DatabasesOverview/DatabasesOverview.jsx +++ b/assets/js/components/DatabasesOverview/DatabasesOverview.jsx @@ -1,35 +1,24 @@ /* eslint-disable react/no-unstable-nested-components */ -import React, { Fragment } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; +import React from 'react'; import { Link, useSearchParams } from 'react-router-dom'; +import { filter } from 'lodash'; + import PageHeader from '@components/PageHeader'; import HealthIcon from '@components/Health'; import Table from '@components/Table'; import Tags from '@components/Tags'; -import { addTagToDatabase, removeTagFromDatabase } from '@state/databases'; - -import { post, del } from '@lib/network'; -import { getCounters } from '@components/HealthSummary/summarySelection'; import HealthSummary from '@components/HealthSummary/HealthSummary'; -import DatabaseItemOverview from './DatabaseItemOverview'; - -const byDatabase = (id) => (instance) => instance.sap_system_id === id; - -const addTag = (tag, sapSystemId) => { - post(`/databases/${sapSystemId}/tags`, { - value: tag, - }); -}; +import { getCounters } from '@components/HealthSummary/summarySelection'; -const removeTag = (tag, sapSystemId) => { - del(`/databases/${sapSystemId}/tags/${tag}`); -}; +import DatabaseItemOverview from './DatabaseItemOverview'; -function DatabasesOverview() { - const { databases, databaseInstances, loading } = useSelector( - (state) => state.databasesList - ); - const dispatch = useDispatch(); +function DatabasesOverview({ + databases, + databaseInstances, + loading, + onTagAdded, + onTagRemoved, +}) { const [searchParams, setSearchParams] = useSearchParams(); const config = { @@ -103,25 +92,15 @@ function DatabasesOverview() { key: 'tags', className: 'w-80', filterFromParams: true, - filter: (filter, key) => (element) => - element[key].some((tag) => filter.includes(tag)), + filter: (filters, key) => (element) => + element[key].some((tag) => filters.includes(tag)), render: (content, item) => ( {}} - onAdd={(tag) => { - addTag(tag, item.id); - dispatch( - addTagToDatabase({ tags: [{ value: tag }], id: item.id }) - ); - }} - onRemove={(tag) => { - removeTag(tag, item.id); - dispatch( - removeTagFromDatabase({ tags: [{ value: tag }], id: item.id }) - ); - }} + onAdd={(tag) => onTagAdded(tag, item.id)} + onRemove={(tag) => onTagRemoved(tag, item.id)} /> ), }, @@ -138,7 +117,9 @@ function DatabasesOverview() { attachedRdbms: database.tenant, tenant: database.tenant, dbAddress: database.db_host, - databaseInstances: databaseInstances.filter(byDatabase(database.id)), + databaseInstances: filter(databaseInstances, { + sap_system_id: database.id, + }), tags: (database.tags && database.tags.map((tag) => tag.value)) || [], })); diff --git a/assets/js/components/DatabasesOverview/DatabasesOverview.stories.jsx b/assets/js/components/DatabasesOverview/DatabasesOverview.stories.jsx new file mode 100644 index 0000000000..90ed7353a8 --- /dev/null +++ b/assets/js/components/DatabasesOverview/DatabasesOverview.stories.jsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; + +import { + clusterFactory, + databaseFactory, + databaseInstanceFactory, + hostFactory, +} from '@lib/test-utils/factories'; + +import DatabasesOverview from './DatabasesOverview'; + +const databases = databaseFactory.buildList(3); + +const enrichedInstances = databases[0].database_instances + .concat(databases[1].database_instances) + .concat(databases[2].database_instances) + .map((instance) => { + const cluster = clusterFactory.build(); + return { + ...instance, + host: { + ...hostFactory.build({ id: instance.host_id, cluster_id: cluster.id }), + cluster, + }, + }; + }); + +const databaseWithSR = databaseFactory.build(); + +const systemReplicationInstances = [ + databaseInstanceFactory.build({ + sap_system_id: databaseWithSR.id, + system_replication: 'Primary', + }), + databaseInstanceFactory.build({ + sap_system_id: databaseWithSR.id, + system_replication: 'Secondary', + system_replication_status: 'ACTIVE', + }), +]; + +function ContainerWrapper({ children }) { + return ( +
{children}
+ ); +} + +export default { + title: 'DatabasesOverview', + components: DatabasesOverview, + decorators: [ + (Story) => ( + + + + ), + ], + render: (args) => ( + + + + ), +}; + +export const Databases = { + args: { + databases, + databaseInstances: enrichedInstances, + loading: false, + }, +}; + +export const WithSystemReplication = { + args: { + databases: [databaseWithSR], + databaseInstances: systemReplicationInstances, + loading: false, + }, +}; diff --git a/assets/js/components/DatabasesOverview/DatabasesOverview.test.jsx b/assets/js/components/DatabasesOverview/DatabasesOverview.test.jsx index 8f9f57ee62..10f96ddeaa 100644 --- a/assets/js/components/DatabasesOverview/DatabasesOverview.test.jsx +++ b/assets/js/components/DatabasesOverview/DatabasesOverview.test.jsx @@ -3,84 +3,53 @@ import { screen, waitFor } from '@testing-library/react'; import 'intersection-observer'; import '@testing-library/jest-dom'; import { databaseFactory } from '@lib/test-utils/factories'; -import { renderWithRouter, withState } from '@lib/test-utils'; +import { renderWithRouter } from '@lib/test-utils'; import { filterTable, clearFilter } from '@components/Table/Table.test'; import DatabasesOverview from './DatabasesOverview'; describe('DatabasesOverview component', () => { describe('filtering', () => { - const cleanInitialState = { - hostsList: { - hosts: [], - }, - clustersList: { - clusters: [], - }, - databasesList: { - databases: [], - databaseInstances: [], - }, - }; - const scenarios = [ { filter: 'Health', options: ['unknown', 'passing', 'warning', 'critical'], - state: { - ...cleanInitialState, - databasesList: { - databases: [].concat( - databaseFactory.buildList(2, { health: 'unknown' }), - databaseFactory.buildList(2, { health: 'passing' }), - databaseFactory.buildList(2, { health: 'warning' }), - databaseFactory.buildList(2, { health: 'critical' }) - ), - databaseInstances: [], - }, - }, + databases: [].concat( + databaseFactory.buildList(2, { health: 'unknown' }), + databaseFactory.buildList(2, { health: 'passing' }), + databaseFactory.buildList(2, { health: 'warning' }), + databaseFactory.buildList(2, { health: 'critical' }) + ), expectedRows: 2, }, { filter: 'SID', options: ['PRD', 'QAS'], - state: { - ...cleanInitialState, - databasesList: { - databases: [].concat( - databaseFactory.buildList(4), - databaseFactory.buildList(2, { sid: 'PRD' }), - databaseFactory.buildList(2, { sid: 'QAS' }) - ), - databaseInstances: [], - }, - }, + databases: [].concat( + databaseFactory.buildList(4), + databaseFactory.buildList(2, { sid: 'PRD' }), + databaseFactory.buildList(2, { sid: 'QAS' }) + ), expectedRows: 2, }, { filter: 'Tags', options: ['Tag1', 'Tag2'], - state: { - ...cleanInitialState, - databasesList: { - databases: [].concat( - databaseFactory.buildList(2), - databaseFactory.buildList(2, { tags: [{ value: 'Tag1' }] }), - databaseFactory.buildList(2, { tags: [{ value: 'Tag2' }] }) - ), - databaseInstances: [], - }, - }, + databases: [].concat( + databaseFactory.buildList(2), + databaseFactory.buildList(2, { tags: [{ value: 'Tag1' }] }), + databaseFactory.buildList(2, { tags: [{ value: 'Tag2' }] }) + ), expectedRows: 2, }, ]; it.each(scenarios)( 'should filter the table content by $filter filter', - ({ filter, options, state, expectedRows }) => { - const [StatefulDatbaseList] = withState(, state); - - renderWithRouter(StatefulDatbaseList); + ({ filter, options, databases, expectedRows }) => { + renderWithRouter( + + ); options.forEach(async (option) => { filterTable(filter, option); @@ -101,21 +70,11 @@ describe('DatabasesOverview component', () => { tags: [{ value: 'Tag1' }], }); - const state = { - ...cleanInitialState, - databasesList: { - databases, - databaseInstances: [], - }, - }; - const { health, sid, tags } = databases[0]; - const [StatefulDatabasesOverview] = withState( - , - state + renderWithRouter( + ); - renderWithRouter(StatefulDatabasesOverview); [ ['Health', health], diff --git a/assets/js/components/DatabasesOverview/DatabasesOverviewPage.jsx b/assets/js/components/DatabasesOverview/DatabasesOverviewPage.jsx new file mode 100644 index 0000000000..d793afa7d2 --- /dev/null +++ b/assets/js/components/DatabasesOverview/DatabasesOverviewPage.jsx @@ -0,0 +1,47 @@ +/* eslint-disable react/no-unstable-nested-components */ +import React from 'react'; +import { useSelector, useDispatch } from 'react-redux'; + +import { getEnrichedDatabaseInstances } from '@state/selectors/sapSystem'; +import { addTagToDatabase, removeTagFromDatabase } from '@state/databases'; +import { post, del } from '@lib/network'; + +import DatabasesOverview from './DatabasesOverview'; + +const addTag = (tag, databaseID) => { + post(`/databases/${databaseID}/tags`, { + value: tag, + }); +}; + +const removeTag = (tag, databaseID) => { + del(`/databases/${databaseID}/tags/${tag}`); +}; + +function DatabasesOverviewPage() { + const { databases, loading } = useSelector((state) => state.databasesList); + const enrichedDatabaseInstances = useSelector((state) => + getEnrichedDatabaseInstances(state) + ); + const dispatch = useDispatch(); + + return ( + { + addTag(tag, databaseID); + dispatch(addTagToDatabase({ tags: [{ value: tag }], id: databaseID })); + }} + onTagRemoved={(tag, databaseID) => { + removeTag(tag, databaseID); + dispatch( + removeTagFromDatabase({ tags: [{ value: tag }], id: databaseID }) + ); + }} + /> + ); +} + +export default DatabasesOverviewPage; diff --git a/assets/js/components/DatabasesOverview/index.js b/assets/js/components/DatabasesOverview/index.js index 7ed43e0434..53a828b2c8 100644 --- a/assets/js/components/DatabasesOverview/index.js +++ b/assets/js/components/DatabasesOverview/index.js @@ -1,3 +1,3 @@ -import DatabasesOverview from './DatabasesOverview'; +import DatabasesOverviewPage from './DatabasesOverviewPage'; -export default DatabasesOverview; +export default DatabasesOverviewPage; diff --git a/assets/js/components/InstanceOverview/InstanceOverview.jsx b/assets/js/components/InstanceOverview/InstanceOverview.jsx index 64ef4e83f1..f0bc01a67b 100644 --- a/assets/js/components/InstanceOverview/InstanceOverview.jsx +++ b/assets/js/components/InstanceOverview/InstanceOverview.jsx @@ -1,7 +1,5 @@ import React from 'react'; -import { useSelector } from 'react-redux'; -import { getHost } from '@state/selectors/host'; -import { getCluster } from '@state/selectors/cluster'; + import HealthIcon from '@components/Health'; import { Features } from '@components/SapSystemDetails'; import { DATABASE_TYPE } from '@lib/model'; @@ -17,14 +15,12 @@ function InstanceOverview({ system_replication_status: systemReplicationStatus, instance_number: instanceNumber, features, - host_id: hostId, + host_id: hostID, + host, }, }) { const isDatabase = DATABASE_TYPE === instanceType; - const host = useSelector(getHost(hostId)); - const cluster = useSelector(getCluster(host?.cluster_id)); - return (
@@ -41,8 +37,8 @@ function InstanceOverview({
)}
- {cluster ? ( - + {host?.cluster ? ( + ) : (

not available @@ -50,7 +46,7 @@ function InstanceOverview({ )}

- {host && host.hostname} + {host && host.hostname}
); diff --git a/assets/js/lib/test-utils/factories/databases.js b/assets/js/lib/test-utils/factories/databases.js index 9bf7cd8eca..4e2d84e108 100644 --- a/assets/js/lib/test-utils/factories/databases.js +++ b/assets/js/lib/test-utils/factories/databases.js @@ -18,6 +18,8 @@ export const databaseInstanceFactory = Factory.define(() => ({ sid: faker.random.alpha({ casing: 'upper', count: 3 }), features: features().join('|'), start_priority: faker.datatype.number({ min: 1, max: 9 }).toString(), + system_replication: '', + system_replication_status: '', })); export const databaseFactory = Factory.define(({ params }) => { From f3b1742493d9a4d5b43ce45437df3a904961b58f Mon Sep 17 00:00:00 2001 From: arbulu89 Date: Tue, 29 Aug 2023 09:44:49 +0200 Subject: [PATCH 2/4] Refactor DatabasesOverview component and add story --- .../SapSystemsOverview/SapSystemsOverview.jsx | 58 ++--- .../SapSystemsOverview.stories.jsx | 72 ++++++ .../SapSystemsOverview.test.jsx | 224 +++++++----------- .../SapSystemsOverviewPage.jsx | 56 +++++ .../js/components/SapSystemsOverview/index.js | 4 +- 5 files changed, 239 insertions(+), 175 deletions(-) create mode 100644 assets/js/components/SapSystemsOverview/SapSystemsOverview.stories.jsx create mode 100644 assets/js/components/SapSystemsOverview/SapSystemsOverviewPage.jsx diff --git a/assets/js/components/SapSystemsOverview/SapSystemsOverview.jsx b/assets/js/components/SapSystemsOverview/SapSystemsOverview.jsx index b27b7fc624..bb5d68a205 100644 --- a/assets/js/components/SapSystemsOverview/SapSystemsOverview.jsx +++ b/assets/js/components/SapSystemsOverview/SapSystemsOverview.jsx @@ -1,7 +1,7 @@ /* eslint-disable react/no-unstable-nested-components */ import React from 'react'; -import { useSelector, useDispatch } from 'react-redux'; import { Link, useSearchParams } from 'react-router-dom'; +import { filter } from 'lodash'; import PageHeader from '@components/PageHeader'; import HealthIcon from '@components/Health'; @@ -12,26 +12,14 @@ import HealthSummary from '@components/HealthSummary/HealthSummary'; import { getCounters } from '@components/HealthSummary/summarySelection'; import { renderEnsaVersion } from '@components/SapSystemDetails'; -import { addTagToSAPSystem, removeTagFromSAPSystem } from '@state/sapSystems'; - -import { post, del } from '@lib/network'; - -const bySapSystem = (id) => (instance) => instance.sap_system_id === id; - -const addTag = (tag, sapSystemId) => { - post(`/sap_systems/${sapSystemId}/tags`, { - value: tag, - }); -}; - -const removeTag = (tag, sapSystemId) => { - del(`/sap_systems/${sapSystemId}/tags/${tag}`); -}; - -function SapSystemsOverview() { - const { sapSystems, applicationInstances, databaseInstances, loading } = - useSelector((state) => state.sapSystemsList); - const dispatch = useDispatch(); +function SapSystemsOverview({ + sapSystems, + applicationInstances, + databaseInstances, + loading, + onTagAdded, + onTagRemoved, +}) { const [searchParams, setSearchParams] = useSearchParams(); const config = { @@ -94,25 +82,15 @@ function SapSystemsOverview() { key: 'tags', className: 'w-80', filterFromParams: true, - filter: (filter, key) => (element) => - element[key].some((tag) => filter.includes(tag)), + filter: (filters, key) => (element) => + element[key].some((tag) => filters.includes(tag)), render: (content, item) => ( {}} - onAdd={(tag) => { - addTag(tag, item.id); - dispatch( - addTagToSAPSystem({ tags: [{ value: tag }], id: item.id }) - ); - }} - onRemove={(tag) => { - removeTag(tag, item.id); - dispatch( - removeTagFromSAPSystem({ tags: [{ value: tag }], id: item.id }) - ); - }} + onAdd={(tag) => onTagAdded(tag, item.id)} + onRemove={(tag) => onTagRemoved(tag, item.id)} /> ), }, @@ -130,10 +108,12 @@ function SapSystemsOverview() { tenant: sapSystem.tenant, dbAddress: sapSystem.db_host, ensaVersion: sapSystem.ensa_version || '-', - applicationInstances: applicationInstances.filter( - bySapSystem(sapSystem.id) - ), - databaseInstances: databaseInstances.filter(bySapSystem(sapSystem.id)), + applicationInstances: filter(applicationInstances, { + sap_system_id: sapSystem.id, + }), + databaseInstances: filter(databaseInstances, { + sap_system_id: sapSystem.id, + }), tags: (sapSystem.tags && sapSystem.tags.map((tag) => tag.value)) || [], })); diff --git a/assets/js/components/SapSystemsOverview/SapSystemsOverview.stories.jsx b/assets/js/components/SapSystemsOverview/SapSystemsOverview.stories.jsx new file mode 100644 index 0000000000..da6de236ff --- /dev/null +++ b/assets/js/components/SapSystemsOverview/SapSystemsOverview.stories.jsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; + +import { + clusterFactory, + hostFactory, + sapSystemFactory, +} from '@lib/test-utils/factories'; + +import SapSystemsOverview from './SapSystemsOverview'; + +const sapSystems = sapSystemFactory.buildList(3); + +const enrichedApplicationInstances = sapSystems[0].application_instances + .concat(sapSystems[1].application_instances) + .concat(sapSystems[2].application_instances) + .map((instance) => { + const cluster = clusterFactory.build(); + return { + ...instance, + host: { + ...hostFactory.build({ id: instance.host_id, cluster_id: cluster.id }), + cluster, + }, + }; + }); + +const enrichedDatabaseInstances = sapSystems[0].database_instances + .concat(sapSystems[1].database_instances) + .concat(sapSystems[2].database_instances) + .map((instance) => { + const cluster = clusterFactory.build(); + return { + ...instance, + host: { + ...hostFactory.build({ id: instance.host_id, cluster_id: cluster.id }), + cluster, + }, + }; + }); + +function ContainerWrapper({ children }) { + return ( +
{children}
+ ); +} + +export default { + title: 'SapSystemsOverview', + components: SapSystemsOverview, + decorators: [ + (Story) => ( + + + + ), + ], + render: (args) => ( + + + + ), +}; + +export const SapSystems = { + args: { + sapSystems, + applicationInstances: enrichedApplicationInstances, + databaseInstances: enrichedDatabaseInstances, + loading: false, + }, +}; diff --git a/assets/js/components/SapSystemsOverview/SapSystemsOverview.test.jsx b/assets/js/components/SapSystemsOverview/SapSystemsOverview.test.jsx index e0af17f3fe..77f167baed 100644 --- a/assets/js/components/SapSystemsOverview/SapSystemsOverview.test.jsx +++ b/assets/js/components/SapSystemsOverview/SapSystemsOverview.test.jsx @@ -7,42 +7,26 @@ import { hostFactory, sapSystemFactory, } from '@lib/test-utils/factories'; -import { renderWithRouter, withState } from '@lib/test-utils'; +import { renderWithRouter } from '@lib/test-utils'; import { filterTable, clearFilter } from '@components/Table/Table.test'; import SapSystemsOverview from './SapSystemsOverview'; -const cleanInitialState = { - hostsList: { - hosts: [], - }, - clustersList: { - clusters: [], - }, - sapSystemsList: { - sapSystems: [], - applicationInstances: [], - databaseInstances: [], - }, -}; - describe('SapSystemsOverviews component', () => { describe('overview content', () => { it('should display the correct number of SAP systems', () => { const sapSystemCount = 3; const expectedRowCount = sapSystemCount * 2; - const state = { - ...cleanInitialState, - sapSystemsList: { - sapSystems: sapSystemFactory.buildList(sapSystemCount), - applicationInstances: [], - databaseInstances: [], - }, - }; - const [StatefulSapSystemList] = withState(, state); + const sapSystems = sapSystemFactory.buildList(sapSystemCount); - renderWithRouter(StatefulSapSystemList); + renderWithRouter( + + ); expect( screen.getByRole('table').querySelectorAll('tbody > tr') @@ -60,18 +44,13 @@ describe('SapSystemsOverviews component', () => { database_instances: databaseInstances, } = sapSystem; - const state = { - ...cleanInitialState, - sapSystemsList: { - sapSystems: [sapSystem], - applicationInstances, - databaseInstances, - }, - }; - - const [StatefulSapSystemList] = withState(, state); - - renderWithRouter(StatefulSapSystemList); + renderWithRouter( + + ); const rows = screen.getByRole('table').querySelectorAll('tbody > tr'); const mainRow = rows[0]; @@ -106,32 +85,43 @@ describe('SapSystemsOverviews component', () => { database_instances: databaseInstances, } = sapSystem; - const hosts = applicationInstances - .concat(databaseInstances) - .map(({ host_id: hostID }) => hostFactory.build({ id: hostID })); - - const clusters = hosts.map(({ cluster_id: clusterID }) => - clusterFactory.build({ id: clusterID, type: 'hana_scale_up' }) + const enrichedApplicationInstances = applicationInstances.map( + (instance) => { + const host = hostFactory.build({ id: instance.host_id }); + return { + ...instance, + host: { + ...host, + cluster: clusterFactory.build({ + id: host.cluster_id, + type: 'hana_scale_up', + }), + }, + }; + } ); - const state = { - ...cleanInitialState, - sapSystemsList: { - sapSystems: [sapSystem], - applicationInstances, - databaseInstances, - }, - hostsList: { - hosts, - }, - clustersList: { - clusters, - }, - }; - - const [StatefulSapSystemList] = withState(, state); + const enrichedDatabaseInstances = databaseInstances.map((instance) => { + const host = hostFactory.build({ id: instance.host_id }); + return { + ...instance, + host: { + ...host, + cluster: clusterFactory.build({ + id: host.cluster_id, + type: 'hana_scale_up', + }), + }, + }; + }); - renderWithRouter(StatefulSapSystemList); + renderWithRouter( + + ); const detailsRow = screen .getByRole('table') @@ -147,52 +137,44 @@ describe('SapSystemsOverviews component', () => { '.table-row-group > .table-row' ); - applicationInstances.forEach((instance, index) => { + enrichedApplicationInstances.forEach((instance, index) => { expect( appInstanceRows[index].querySelector('.table-cell:nth-child(2)') ).toHaveTextContent(instance.instance_number); expect( appInstanceRows[index].querySelector('.table-cell:nth-child(4)') - ).toHaveTextContent(clusters[index].name); + ).toHaveTextContent(instance.host.cluster.name); expect( appInstanceRows[index].querySelector('.table-cell:nth-child(4) > a') - ).toHaveAttribute('href', `/clusters/${clusters[index].id}`); + ).toHaveAttribute('href', `/clusters/${instance.host.cluster.id}`); expect( appInstanceRows[index].querySelector('.table-cell:nth-child(5)') - ).toHaveTextContent(hosts[index].hostname); + ).toHaveTextContent(instance.host.hostname); expect( appInstanceRows[index].querySelector( '.table-cell:nth-child(5) > span > a' ) - ).toHaveAttribute('href', `/hosts/${hosts[index].id}`); + ).toHaveAttribute('href', `/hosts/${instance.host.id}`); }); - databaseInstances.forEach((instance, index) => { + enrichedDatabaseInstances.forEach((instance, index) => { expect( dbInstanceRows[index].querySelector('.table-cell:nth-child(2)') ).toHaveTextContent(instance.instance_number); expect( dbInstanceRows[index].querySelector('.table-cell:nth-child(5)') - ).toHaveTextContent(clusters[applicationInstances.length + index].name); + ).toHaveTextContent(instance.host.cluster.name); expect( dbInstanceRows[index].querySelector('.table-cell:nth-child(5) > a') - ).toHaveAttribute( - 'href', - `/clusters/${clusters[applicationInstances.length + index].id}` - ); + ).toHaveAttribute('href', `/clusters/${instance.host.cluster.id}`); expect( dbInstanceRows[index].querySelector('.table-cell:nth-child(6)') - ).toHaveTextContent( - hosts[applicationInstances.length + index].hostname - ); + ).toHaveTextContent(instance.host.hostname); expect( dbInstanceRows[index].querySelector( '.table-cell:nth-child(6) > span > a' ) - ).toHaveAttribute( - 'href', - `/hosts/${hosts[applicationInstances.length + index].id}` - ); + ).toHaveAttribute('href', `/hosts/${instance.host.id}`); }); }); }); @@ -202,67 +184,48 @@ describe('SapSystemsOverviews component', () => { { filter: 'Health', options: ['unknown', 'passing', 'warning', 'critical'], - state: { - ...cleanInitialState, - sapSystemsList: { - sapSystems: [].concat( - sapSystemFactory.buildList(2, { health: 'unknown' }), - sapSystemFactory.buildList(2, { health: 'passing' }), - sapSystemFactory.buildList(2, { health: 'warning' }), - sapSystemFactory.buildList(2, { health: 'critical' }) - ), - applicationInstances: [], - databaseInstances: [], - }, - }, + sapSystems: [].concat( + sapSystemFactory.buildList(2, { health: 'unknown' }), + sapSystemFactory.buildList(2, { health: 'passing' }), + sapSystemFactory.buildList(2, { health: 'warning' }), + sapSystemFactory.buildList(2, { health: 'critical' }) + ), expectedRows: 2, }, { filter: 'SID', options: ['PRD', 'QAS'], - state: { - ...cleanInitialState, - sapSystemsList: { - sapSystems: [].concat( - sapSystemFactory.buildList(4), - sapSystemFactory.buildList(2, { sid: 'PRD' }), - sapSystemFactory.buildList(2, { sid: 'QAS' }) - ), - applicationInstances: [], - databaseInstances: [], - }, - }, + sapSystems: [].concat( + sapSystemFactory.buildList(4), + sapSystemFactory.buildList(2, { sid: 'PRD' }), + sapSystemFactory.buildList(2, { sid: 'QAS' }) + ), + expectedRows: 2, }, { filter: 'Tags', options: ['Tag1', 'Tag2'], - state: { - ...cleanInitialState, - sapSystemsList: { - sapSystems: [].concat( - sapSystemFactory.buildList(2), - sapSystemFactory.buildList(2, { tags: [{ value: 'Tag1' }] }), - sapSystemFactory.buildList(2, { tags: [{ value: 'Tag2' }] }) - ), - applicationInstances: [], - databaseInstances: [], - }, - }, + sapSystems: [].concat( + sapSystemFactory.buildList(2), + sapSystemFactory.buildList(2, { tags: [{ value: 'Tag1' }] }), + sapSystemFactory.buildList(2, { tags: [{ value: 'Tag2' }] }) + ), expectedRows: 2, }, ]; it.each(scenarios)( 'should filter the table content by $filter filter', - ({ filter, options, state, expectedRows }) => { - const [StatefulSapSystemList] = withState( - , - state + ({ filter, options, sapSystems, expectedRows }) => { + renderWithRouter( + ); - renderWithRouter(StatefulSapSystemList); - options.forEach(async (option) => { filterTable(filter, option); screen.getByRole('table'); @@ -282,22 +245,15 @@ describe('SapSystemsOverviews component', () => { tags: [{ value: 'Tag1' }], }); - const state = { - ...cleanInitialState, - sapSystemsList: { - sapSystems, - applicationInstances: [], - databaseInstances: [], - }, - }; - const { health, sid, tags } = sapSystems[0]; - const [StatefulSapSystemsOverview] = withState( - , - state + renderWithRouter( + ); - renderWithRouter(StatefulSapSystemsOverview); [ ['Health', health], diff --git a/assets/js/components/SapSystemsOverview/SapSystemsOverviewPage.jsx b/assets/js/components/SapSystemsOverview/SapSystemsOverviewPage.jsx new file mode 100644 index 0000000000..e9b5525bbd --- /dev/null +++ b/assets/js/components/SapSystemsOverview/SapSystemsOverviewPage.jsx @@ -0,0 +1,56 @@ +/* eslint-disable react/no-unstable-nested-components */ +import React from 'react'; +import { useSelector, useDispatch } from 'react-redux'; + +import { + getEnrichedApplicationInstances, + getEnrichedDatabaseInstances, +} from '@state/selectors/sapSystem'; +import { addTagToSAPSystem, removeTagFromSAPSystem } from '@state/sapSystems'; +import { post, del } from '@lib/network'; + +import SapSystemsOverview from './SapSystemsOverview'; + +const addTag = (tag, sapSystemID) => { + post(`/sap_systems/${sapSystemID}/tags`, { + value: tag, + }); +}; + +const removeTag = (tag, sapSystemID) => { + del(`/sap_systems/${sapSystemID}/tags/${tag}`); +}; + +function SapSystemOverviewPage() { + const { sapSystems, loading } = useSelector((state) => state.sapSystemsList); + const enrichedApplicationInstances = useSelector((state) => + getEnrichedApplicationInstances(state) + ); + const enrichedDatabaseInstances = useSelector((state) => + getEnrichedDatabaseInstances(state) + ); + const dispatch = useDispatch(); + + return ( + { + addTag(tag, sapSystemID); + dispatch( + addTagToSAPSystem({ tags: [{ value: tag }], id: sapSystemID }) + ); + }} + onTagRemoved={(tag, sapSystemID) => { + removeTag(tag, sapSystemID); + dispatch( + removeTagFromSAPSystem({ tags: [{ value: tag }], id: sapSystemID }) + ); + }} + /> + ); +} + +export default SapSystemOverviewPage; diff --git a/assets/js/components/SapSystemsOverview/index.js b/assets/js/components/SapSystemsOverview/index.js index 80dc9e839c..f787ef38fd 100644 --- a/assets/js/components/SapSystemsOverview/index.js +++ b/assets/js/components/SapSystemsOverview/index.js @@ -1,3 +1,3 @@ -import SapSystemsOverview from './SapSystemsOverview'; +import SapSystemsOverviewPage from './SapSystemsOverviewPage'; -export default SapSystemsOverview; +export default SapSystemsOverviewPage; From 7bdc06efc371a4953665dbd5455996a570218699 Mon Sep 17 00:00:00 2001 From: arbulu89 Date: Tue, 29 Aug 2023 09:44:58 +0200 Subject: [PATCH 3/4] Update router --- assets/js/trento.jsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/assets/js/trento.jsx b/assets/js/trento.jsx index f5a52efe78..c0e927564d 100644 --- a/assets/js/trento.jsx +++ b/assets/js/trento.jsx @@ -16,9 +16,9 @@ import ClustersList from '@components/ClustersList'; import ClusterDetailsPage from '@components/ClusterDetails'; import ClusterSettingsPage from '@components/ClusterSettingsPage'; import { ExecutionResultsPage } from '@components/ExecutionResults'; -import SapSystemsOverview from '@components/SapSystemsOverview'; +import SapSystemsOverviewPage from '@components/SapSystemsOverview'; import HostDetails, { HostSettingsPage } from '@components/HostDetails'; -import DatabasesOverview from '@components/DatabasesOverview'; +import DatabasesOverviewPage from '@components/DatabasesOverview'; import ChecksCatalog from '@components/ChecksCatalog'; import NotFound from '@components/NotFound'; import SomethingWentWrong from '@components/SomethingWentWrong'; @@ -64,8 +64,11 @@ function App() { element={} /> } /> - } /> - } /> + } + /> + } /> } /> } /> } /> From 1e7af1d2d41fa51537202d08c7ae95ffccd0dd88 Mon Sep 17 00:00:00 2001 From: arbulu89 Date: Tue, 29 Aug 2023 10:24:17 +0200 Subject: [PATCH 4/4] Add argTypes to stories --- .../DatabasesOverview.stories.jsx | 26 ++++++++++++++++ .../SapSystemsOverview.stories.jsx | 30 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/assets/js/components/DatabasesOverview/DatabasesOverview.stories.jsx b/assets/js/components/DatabasesOverview/DatabasesOverview.stories.jsx index 90ed7353a8..dcbc918ac9 100644 --- a/assets/js/components/DatabasesOverview/DatabasesOverview.stories.jsx +++ b/assets/js/components/DatabasesOverview/DatabasesOverview.stories.jsx @@ -49,6 +49,32 @@ function ContainerWrapper({ children }) { export default { title: 'DatabasesOverview', components: DatabasesOverview, + argTypes: { + databases: { + control: { type: 'array' }, + description: 'Databases', + }, + databaseInstances: { + control: { type: 'array' }, + description: 'Database instances', + }, + loading: { + control: { type: 'boolean' }, + description: 'Loading', + table: { + type: { summary: 'string' }, + defaultValue: { summary: false }, + }, + }, + onTagAdded: { + action: 'Add tag', + description: 'Called when a new tag is added', + }, + onTagRemoved: { + action: 'Remove tag', + description: 'Called when an existing tag is removed', + }, + }, decorators: [ (Story) => ( diff --git a/assets/js/components/SapSystemsOverview/SapSystemsOverview.stories.jsx b/assets/js/components/SapSystemsOverview/SapSystemsOverview.stories.jsx index da6de236ff..baf7db5de2 100644 --- a/assets/js/components/SapSystemsOverview/SapSystemsOverview.stories.jsx +++ b/assets/js/components/SapSystemsOverview/SapSystemsOverview.stories.jsx @@ -48,6 +48,36 @@ function ContainerWrapper({ children }) { export default { title: 'SapSystemsOverview', components: SapSystemsOverview, + argTypes: { + sapSystems: { + control: { type: 'array' }, + description: 'SAP systems', + }, + applicationInstances: { + control: { type: 'array' }, + description: 'Application instances', + }, + databaseInstances: { + control: { type: 'array' }, + description: 'Database instances', + }, + loading: { + control: { type: 'boolean' }, + description: 'Loading', + table: { + type: { summary: 'string' }, + defaultValue: { summary: false }, + }, + }, + onTagAdded: { + action: 'Add tag', + description: 'Called when a new tag is added', + }, + onTagRemoved: { + action: 'Remove tag', + description: 'Called when an existing tag is removed', + }, + }, decorators: [ (Story) => (