diff --git a/src/views/domain-page/config/domain-page-metadata-extended-table.config.ts b/src/views/domain-page/config/domain-page-metadata-extended-table.config.ts index 1c919dfbb..0457908b7 100644 --- a/src/views/domain-page/config/domain-page-metadata-extended-table.config.ts +++ b/src/views/domain-page/config/domain-page-metadata-extended-table.config.ts @@ -1,8 +1,10 @@ import { createElement } from 'react'; +import isActiveActiveDomain from '@/views/shared/active-active/helpers/is-active-active-domain'; + import DomainPageMetadataClusters from '../domain-page-metadata-clusters/domain-page-metadata-clusters'; import DomainPageMetadataDescription from '../domain-page-metadata-description/domain-page-metadata-description'; -import DomainPageMetadataFailoverVersion from '../domain-page-metadata-failover-version/domain-page-metadata-failover-version'; +import DomainPageMetadataFailoverVersionActiveActive from '../domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active'; import { type MetadataItem } from '../domain-page-metadata-table/domain-page-metadata-table.types'; import DomainPageMetadataViewJson from '../domain-page-metadata-view-json/domain-page-metadata-view-json'; import getClusterOperationMode from '../helpers/get-cluster-operation-mode'; @@ -55,7 +57,12 @@ const domainPageMetadataExtendedTableConfig = [ description: 'The failover version of the domain', kind: 'simple', getValue: ({ domainDescription }) => - createElement(DomainPageMetadataFailoverVersion, domainDescription), + isActiveActiveDomain(domainDescription) + ? createElement( + DomainPageMetadataFailoverVersionActiveActive, + domainDescription + ) + : domainDescription.failoverVersion, }, { key: 'describeDomainJson', diff --git a/src/views/domain-page/config/domain-page-metadata-table.config.ts b/src/views/domain-page/config/domain-page-metadata-table.config.ts index d5a473693..40df25773 100644 --- a/src/views/domain-page/config/domain-page-metadata-table.config.ts +++ b/src/views/domain-page/config/domain-page-metadata-table.config.ts @@ -1,7 +1,10 @@ +import { createElement } from 'react'; + import type { ListTableItem } from '@/components/list-table/list-table.types'; +import isActiveActiveDomain from '@/views/shared/active-active/helpers/is-active-active-domain'; import DomainPageMetadataClusters from '../domain-page-metadata-clusters/domain-page-metadata-clusters'; -import DomainPageMetadataFailoverVersion from '../domain-page-metadata-failover-version/domain-page-metadata-failover-version'; +import DomainPageMetadataFailoverVersionActiveActive from '../domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active'; import { type DomainDescription } from '../domain-page.types'; import getClusterOperationMode from '../helpers/get-cluster-operation-mode'; @@ -31,7 +34,13 @@ const domainPageMetadataTableConfig: Array> = [ { key: 'failoverVersion', label: 'Failover version', - renderValue: DomainPageMetadataFailoverVersion, + renderValue: (domainDescription: DomainDescription) => + isActiveActiveDomain(domainDescription) + ? createElement( + DomainPageMetadataFailoverVersionActiveActive, + domainDescription + ) + : domainDescription.failoverVersion, }, ]; diff --git a/src/views/domain-page/domain-page-metadata-failover-version-active-active/__tests__/domain-page-metadata-failover-version-active-active.test.tsx b/src/views/domain-page/domain-page-metadata-failover-version-active-active/__tests__/domain-page-metadata-failover-version-active-active.test.tsx new file mode 100644 index 000000000..4cc4eee4a --- /dev/null +++ b/src/views/domain-page/domain-page-metadata-failover-version-active-active/__tests__/domain-page-metadata-failover-version-active-active.test.tsx @@ -0,0 +1,158 @@ +import { render, screen, userEvent } from '@/test-utils/rtl'; + +import { type ClusterAttributeScope } from '@/__generated__/proto-ts/uber/cadence/api/v1/ClusterAttributeScope'; +import { mockActiveActiveDomain } from '@/views/shared/active-active/__fixtures__/active-active-domain'; +import { type ActiveActiveDomain } from '@/views/shared/active-active/active-active.types'; + +import DomainPageMetadataFailoverVersionActiveActive from '../domain-page-metadata-failover-version-active-active'; + +describe(DomainPageMetadataFailoverVersionActiveActive.name, () => { + it('renders the table with headers', () => { + setup(); + + expect(screen.getByText('Scope')).toBeInTheDocument(); + expect(screen.getByText('Attribute')).toBeInTheDocument(); + expect(screen.getByText('Active Cluster')).toBeInTheDocument(); + expect(screen.getByText('Failover Version')).toBeInTheDocument(); + }); + + it('renders default entry with domain data', () => { + setup(); + + expect(screen.getByText('default')).toBeInTheDocument(); + expect(screen.getByText('-')).toBeInTheDocument(); + expect(screen.getByText('cluster0')).toBeInTheDocument(); + expect(screen.getByText('2')).toBeInTheDocument(); + }); + + it('renders multiple failover version entries', () => { + setup({ attributeCount: 2 }); + + // Default entry + expect(screen.getByText('default')).toBeInTheDocument(); + expect(screen.getByText('cluster0')).toBeInTheDocument(); + expect(screen.getByText('2')).toBeInTheDocument(); + + // Additional entries + expect(screen.getByText('scope1')).toBeInTheDocument(); + expect(screen.getByText('attribute1')).toBeInTheDocument(); + expect(screen.getByText('cluster_1')).toBeInTheDocument(); + expect(screen.getByText('1000')).toBeInTheDocument(); + + expect(screen.getByText('scope2')).toBeInTheDocument(); + expect(screen.getByText('attribute2')).toBeInTheDocument(); + expect(screen.getByText('cluster_2')).toBeInTheDocument(); + expect(screen.getByText('1001')).toBeInTheDocument(); + }); + + it('does not show "Show all" button when entries are less than or equal to MAX_ATTRIBUTES_COUNT_TRUNCATED', () => { + // 3 additional attributes + 1 default = 4 total (exactly at the limit) + setup({ attributeCount: 3 }); + + expect(screen.queryByText(/Show all/)).not.toBeInTheDocument(); + }); + + it('shows "Show all" button when entries exceed MAX_ATTRIBUTES_COUNT_TRUNCATED', () => { + // 5 additional attributes + 1 default = 6 total (exceeds limit of 4) + setup({ attributeCount: 5 }); + + expect(screen.getByText('Show all (6)')).toBeInTheDocument(); + }); + + it('truncates entries when not expanded', () => { + setup({ attributeCount: 5 }); + + // Should show first 4 entries only (default + first 3 attributes) + expect(screen.getByText('default')).toBeInTheDocument(); + expect(screen.getByText('scope1')).toBeInTheDocument(); + expect(screen.getByText('scope2')).toBeInTheDocument(); + expect(screen.getByText('scope3')).toBeInTheDocument(); + + // Should not show 5th and 6th entries + expect(screen.queryByText('scope4')).not.toBeInTheDocument(); + expect(screen.queryByText('scope5')).not.toBeInTheDocument(); + }); + + it('expands to show all entries when "Show all" button is clicked', async () => { + const { user } = setup({ attributeCount: 5 }); + + const showAllButton = screen.getByText('Show all (6)'); + await user.click(showAllButton); + + // Now all entries should be visible + expect(screen.getByText('default')).toBeInTheDocument(); + expect(screen.getByText('scope1')).toBeInTheDocument(); + expect(screen.getByText('scope2')).toBeInTheDocument(); + expect(screen.getByText('scope3')).toBeInTheDocument(); + expect(screen.getByText('scope4')).toBeInTheDocument(); + expect(screen.getByText('scope5')).toBeInTheDocument(); + + // Button text should change + expect(screen.getByText('Show less')).toBeInTheDocument(); + expect(screen.queryByText('Show all (6)')).not.toBeInTheDocument(); + }); + + it('collapses to show truncated entries when "Show less" button is clicked', async () => { + const { user } = setup({ attributeCount: 5 }); + + // Expand first + const showAllButton = screen.getByText('Show all (6)'); + await user.click(showAllButton); + + // Then collapse + const showLessButton = screen.getByText('Show less'); + await user.click(showLessButton); + + // Should show first 4 entries only + expect(screen.getByText('default')).toBeInTheDocument(); + expect(screen.getByText('scope1')).toBeInTheDocument(); + expect(screen.getByText('scope2')).toBeInTheDocument(); + expect(screen.getByText('scope3')).toBeInTheDocument(); + + // Should not show 5th and 6th entries + expect(screen.queryByText('scope4')).not.toBeInTheDocument(); + expect(screen.queryByText('scope5')).not.toBeInTheDocument(); + + // Button text should change back + expect(screen.getByText('Show all (6)')).toBeInTheDocument(); + expect(screen.queryByText('Show less')).not.toBeInTheDocument(); + }); +}); + +function setup({ + attributeCount = 0, +}: { + attributeCount?: number; +} = {}) { + const user = userEvent.setup(); + + const activeClustersByClusterAttribute: Record< + string, + ClusterAttributeScope + > = {}; + + // Create additional attributes beyond the default one + for (let i = 0; i < attributeCount; i++) { + const scope = `scope${i + 1}`; + activeClustersByClusterAttribute[scope] = { + clusterAttributes: { + [`attribute${i + 1}`]: { + activeClusterName: `cluster_${i + 1}`, + failoverVersion: `${1000 + i}`, + }, + }, + }; + } + + const domain: ActiveActiveDomain = { + ...mockActiveActiveDomain, + activeClusters: { + regionToCluster: {}, + activeClustersByClusterAttribute, + }, + }; + + render(); + + return { user }; +} diff --git a/src/views/domain-page/domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active.constants.ts b/src/views/domain-page/domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active.constants.ts new file mode 100644 index 000000000..9d51b93af --- /dev/null +++ b/src/views/domain-page/domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active.constants.ts @@ -0,0 +1 @@ +export const MAX_ATTRIBUTES_COUNT_TRUNCATED = 4; diff --git a/src/views/domain-page/domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active.styles.ts b/src/views/domain-page/domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active.styles.ts new file mode 100644 index 000000000..ff01bd84b --- /dev/null +++ b/src/views/domain-page/domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active.styles.ts @@ -0,0 +1,35 @@ +import { styled as createStyled, type Theme } from 'baseui'; +import { type TableOverrides } from 'baseui/table-semantic'; +import { type StyleObject } from 'styletron-react'; + +export const overrides = { + table: { + TableHeadCell: { + style: ({ $theme }: { $theme: Theme }): StyleObject => ({ + ...$theme.typography.LabelXSmall, + paddingTop: $theme.sizing.scale300, + paddingBottom: $theme.sizing.scale300, + paddingLeft: $theme.sizing.scale500, + paddingRight: $theme.sizing.scale500, + }), + }, + TableBodyCell: { + style: ({ $theme }: { $theme: Theme }): StyleObject => ({ + ...$theme.typography.ParagraphXSmall, + paddingTop: $theme.sizing.scale300, + paddingBottom: $theme.sizing.scale300, + paddingLeft: $theme.sizing.scale500, + paddingRight: $theme.sizing.scale500, + }), + }, + } satisfies TableOverrides, +}; + +export const styled = { + FailoverVersionsContainer: createStyled('div', ({ $theme }) => ({ + display: 'flex', + flexDirection: 'column', + alignItems: 'flex-start', + gap: $theme.sizing.scale500, + })), +}; diff --git a/src/views/domain-page/domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active.tsx b/src/views/domain-page/domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active.tsx new file mode 100644 index 000000000..b8181c446 --- /dev/null +++ b/src/views/domain-page/domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active.tsx @@ -0,0 +1,76 @@ +import { useMemo, useState } from 'react'; + +import { Button } from 'baseui/button'; +import { Table } from 'baseui/table-semantic'; +import { MdAdd, MdHorizontalRule } from 'react-icons/md'; + +import { type ActiveActiveDomain } from '@/views/shared/active-active/active-active.types'; + +import { MAX_ATTRIBUTES_COUNT_TRUNCATED } from './domain-page-metadata-failover-version-active-active.constants'; +import { + overrides, + styled, +} from './domain-page-metadata-failover-version-active-active.styles'; +import { type FailoverVersionEntryActiveActive } from './domain-page-metadata-failover-version-active-active.types'; + +export default function DomainPageMetadataFailoverVersionActiveActive( + domain: ActiveActiveDomain +) { + const [showAll, setShowAll] = useState(false); + + const allEntries = useMemo>(() => { + return [ + { + scope: 'default', + attribute: '-', + cluster: domain.activeClusterName, + version: domain.failoverVersion, + }, + ...Object.entries( + domain.activeClusters.activeClustersByClusterAttribute + ).flatMap(([scope, { clusterAttributes }]) => + Object.entries(clusterAttributes).map( + ([attribute, activeClusterInfo]) => ({ + scope, + attribute, + cluster: activeClusterInfo.activeClusterName, + version: activeClusterInfo.failoverVersion, + }) + ) + ), + ]; + }, [ + domain.activeClusters.activeClustersByClusterAttribute, + domain.activeClusterName, + domain.failoverVersion, + ]); + + const entriesToShow = useMemo(() => { + return showAll + ? allEntries + : allEntries.slice(0, MAX_ATTRIBUTES_COUNT_TRUNCATED); + }, [allEntries, showAll]); + + return ( + + + {allEntries.length > MAX_ATTRIBUTES_COUNT_TRUNCATED && ( + + )} + + ); +} diff --git a/src/views/domain-page/domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active.types.ts b/src/views/domain-page/domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active.types.ts new file mode 100644 index 000000000..be6fef04b --- /dev/null +++ b/src/views/domain-page/domain-page-metadata-failover-version-active-active/domain-page-metadata-failover-version-active-active.types.ts @@ -0,0 +1,6 @@ +export type FailoverVersionEntryActiveActive = { + scope: string; + attribute: string; + cluster: string; + version: string; +}; diff --git a/src/views/domain-page/domain-page-metadata-failover-version/__tests__/domain-page-metadata-failover-version.test.tsx b/src/views/domain-page/domain-page-metadata-failover-version/__tests__/domain-page-metadata-failover-version.test.tsx deleted file mode 100644 index 0ec040c4c..000000000 --- a/src/views/domain-page/domain-page-metadata-failover-version/__tests__/domain-page-metadata-failover-version.test.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { render, screen } from '@/test-utils/rtl'; - -import { mockActiveActiveDomain } from '@/views/shared/active-active/__fixtures__/active-active-domain'; - -import { mockDomainDescription } from '../../__fixtures__/domain-description'; -import DomainPageMetadataFailoverVersion from '../domain-page-metadata-failover-version'; - -jest.mock('@/views/shared/active-active/helpers/is-active-active-domain'); - -describe(DomainPageMetadataFailoverVersion.name, () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('renders failover version as text for non-active-active domains', () => { - render(); - - expect(screen.getByText('123456')).toBeInTheDocument(); - }); - - // TODO @adhitya.mamallan: add special rendering for failover versions for different cluster attributes - it('temp: renders failover version as text for active-active domains', () => { - render(); - - expect(screen.getByText('2')).toBeInTheDocument(); - }); -}); diff --git a/src/views/domain-page/domain-page-metadata-failover-version/domain-page-metadata-failover-version.tsx b/src/views/domain-page/domain-page-metadata-failover-version/domain-page-metadata-failover-version.tsx deleted file mode 100644 index dc4ea06cd..000000000 --- a/src/views/domain-page/domain-page-metadata-failover-version/domain-page-metadata-failover-version.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import isActiveActiveDomain from '@/views/shared/active-active/helpers/is-active-active-domain'; - -import { type DomainDescription } from '../domain-page.types'; - -export default function DomainPageMetadataFailoverVersion( - domainDescription: DomainDescription -) { - if (isActiveActiveDomain(domainDescription)) { - // TODO @adhitya.mamallan: add special rendering for failover versions for different cluster attributes - return domainDescription.failoverVersion; - } - - return domainDescription.failoverVersion; -}