From 567aa6b3ae2423f23d450b25650bf1339e53eaa1 Mon Sep 17 00:00:00 2001 From: Adhitya Mamallan Date: Fri, 14 Nov 2025 18:51:54 +0100 Subject: [PATCH 1/4] Add failover history modal Signed-off-by: Adhitya Mamallan --- ...omain-page-failover-active-active.test.tsx | 11 + .../domain-page-failover-active-active.tsx | 71 ++-- .../domain-page-failover-modal.test.tsx | 341 ++++++++++++++++++ .../domain-page-failover-modal.styles.ts | 85 +++++ .../domain-page-failover-modal.tsx | 95 +++++ .../domain-page-failover-modal.types.ts | 7 + 6 files changed, 579 insertions(+), 31 deletions(-) create mode 100644 src/views/domain-page/domain-page-failover-modal/__tests__/domain-page-failover-modal.test.tsx create mode 100644 src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.styles.ts create mode 100644 src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.tsx create mode 100644 src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.types.ts diff --git a/src/views/domain-page/domain-page-failover-active-active/__tests__/domain-page-failover-active-active.test.tsx b/src/views/domain-page/domain-page-failover-active-active/__tests__/domain-page-failover-active-active.test.tsx index 42691611a..fa08a60c8 100644 --- a/src/views/domain-page/domain-page-failover-active-active/__tests__/domain-page-failover-active-active.test.tsx +++ b/src/views/domain-page/domain-page-failover-active-active/__tests__/domain-page-failover-active-active.test.tsx @@ -22,6 +22,10 @@ jest.mock( )) ); +jest.mock('../../domain-page-failover-modal/domain-page-failover-modal', () => + jest.fn(() =>
Modal
) +); + describe(DomainPageFailoverActiveActive.name, () => { beforeEach(() => { jest.clearAllMocks(); @@ -61,6 +65,7 @@ describe(DomainPageFailoverActiveActive.name, () => { ).toBeInTheDocument(); expect(screen.getByText('cluster-1 -> cluster-2')).toBeInTheDocument(); expect(screen.getByText('See more')).toBeInTheDocument(); + expect(screen.getByTestId('mock-failover-modal')).toBeInTheDocument(); }); it('renders cluster failover when matching non-primary cluster failover is found', () => { @@ -101,6 +106,7 @@ describe(DomainPageFailoverActiveActive.name, () => { ).toBeInTheDocument(); expect(screen.getByText('cluster-1 -> cluster-2')).toBeInTheDocument(); expect(screen.getByText('See more')).toBeInTheDocument(); + expect(screen.getByTestId('mock-failover-modal')).toBeInTheDocument(); }); it('does not render cluster failover section when clusterAttributeScope is set but clusterAttributeValue is undefined for non-primary scope', () => { @@ -138,6 +144,7 @@ describe(DomainPageFailoverActiveActive.name, () => { screen.queryByTestId('mock-single-cluster-failover') ).not.toBeInTheDocument(); expect(screen.getByText('See more')).toBeInTheDocument(); + expect(screen.getByTestId('mock-failover-modal')).toBeInTheDocument(); }); it('does not render cluster failover section when no matching cluster failover is found', () => { @@ -177,6 +184,7 @@ describe(DomainPageFailoverActiveActive.name, () => { screen.queryByTestId('mock-single-cluster-failover') ).not.toBeInTheDocument(); expect(screen.getByText('See more')).toBeInTheDocument(); + expect(screen.getByTestId('mock-failover-modal')).toBeInTheDocument(); }); it('does not render cluster failover section when clusterAttributeScope is undefined', () => { @@ -211,6 +219,7 @@ describe(DomainPageFailoverActiveActive.name, () => { screen.queryByTestId('mock-single-cluster-failover') ).not.toBeInTheDocument(); expect(screen.getByText('See more')).toBeInTheDocument(); + expect(screen.getByTestId('mock-failover-modal')).toBeInTheDocument(); }); it('renders "See more" button even when no matching cluster failover is found', () => { @@ -233,6 +242,7 @@ describe(DomainPageFailoverActiveActive.name, () => { screen.queryByTestId('mock-single-cluster-failover') ).not.toBeInTheDocument(); expect(screen.getByText('See more')).toBeInTheDocument(); + expect(screen.getByTestId('mock-failover-modal')).toBeInTheDocument(); }); it('selects the correct cluster failover when multiple cluster failovers exist', () => { @@ -286,6 +296,7 @@ describe(DomainPageFailoverActiveActive.name, () => { screen.getByTestId('mock-single-cluster-failover') ).toBeInTheDocument(); expect(screen.getByText('cluster-3 -> cluster-4')).toBeInTheDocument(); + expect(screen.getByTestId('mock-failover-modal')).toBeInTheDocument(); }); }); diff --git a/src/views/domain-page/domain-page-failover-active-active/domain-page-failover-active-active.tsx b/src/views/domain-page/domain-page-failover-active-active/domain-page-failover-active-active.tsx index c8d5ccc07..3cb3254d6 100644 --- a/src/views/domain-page/domain-page-failover-active-active/domain-page-failover-active-active.tsx +++ b/src/views/domain-page/domain-page-failover-active-active/domain-page-failover-active-active.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { Button } from 'baseui/button'; import { MdVisibility } from 'react-icons/md'; @@ -6,6 +6,7 @@ import { MdVisibility } from 'react-icons/md'; import usePageQueryParams from '@/hooks/use-page-query-params/use-page-query-params'; import domainPageQueryParamsConfig from '../config/domain-page-query-params.config'; +import DomainPageFailoverModal from '../domain-page-failover-modal/domain-page-failover-modal'; import DomainPageFailoverSingleCluster from '../domain-page-failover-single-cluster/domain-page-failover-single-cluster'; import { PRIMARY_CLUSTER_SCOPE } from '../domain-page-failovers/domain-page-failovers.constants'; import clusterFailoverMatchesAttribute from '../helpers/cluster-failover-matches-attribute'; @@ -16,6 +17,7 @@ import { type Props } from './domain-page-failover-active-active.types'; export default function DomainPageFailoverActiveActive({ failoverEvent, }: Props) { + const [isModalOpen, setIsModalOpen] = useState(false); const [{ clusterAttributeScope, clusterAttributeValue }] = usePageQueryParams( domainPageQueryParamsConfig ); @@ -42,35 +44,42 @@ export default function DomainPageFailoverActiveActive({ ]); return ( - - {clusterFailoverForMaybeSelectedAttribute && ( - - - {clusterAttributeScope === PRIMARY_CLUSTER_SCOPE - ? 'Primary:' - : `${clusterAttributeScope} (${clusterAttributeValue}):`} - - - - )} - - + <> + + {clusterFailoverForMaybeSelectedAttribute && ( + + + {clusterAttributeScope === PRIMARY_CLUSTER_SCOPE + ? 'Primary:' + : `${clusterAttributeScope} (${clusterAttributeValue}):`} + + + + )} + + + setIsModalOpen(false)} + /> + ); } diff --git a/src/views/domain-page/domain-page-failover-modal/__tests__/domain-page-failover-modal.test.tsx b/src/views/domain-page/domain-page-failover-modal/__tests__/domain-page-failover-modal.test.tsx new file mode 100644 index 000000000..4da6d7e41 --- /dev/null +++ b/src/views/domain-page/domain-page-failover-modal/__tests__/domain-page-failover-modal.test.tsx @@ -0,0 +1,341 @@ +import { type ModalProps } from 'baseui/modal'; + +import { render, screen } from '@/test-utils/rtl'; + +import { type FailoverEvent } from '@/route-handlers/list-failover-history/list-failover-history.types'; + +import DomainPageFailoverModal from '../domain-page-failover-modal'; + +jest.mock('baseui/modal', () => ({ + ...jest.requireActual('baseui/modal'), + Modal: ({ isOpen, children }: ModalProps) => + isOpen ? ( +
+ {typeof children === 'function' ? children() : children} +
+ ) : null, +})); + +jest.mock('@/components/formatted-date/formatted-date', () => + jest.fn(({ timestampMs }: { timestampMs: number | null }) => ( +
+ {timestampMs ? new Date(timestampMs).toISOString() : 'No date'} +
+ )) +); + +jest.mock( + '../../domain-page-failover-single-cluster/domain-page-failover-single-cluster', + () => + jest.fn((props: { fromCluster?: string; toCluster?: string }) => + props.fromCluster && props.toCluster ? ( +
+ {`${props.fromCluster} -> ${props.toCluster}`} +
+ ) : null + ) +); + +describe(DomainPageFailoverModal.name, () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders modal when isOpen is true', () => { + const failoverEvent: FailoverEvent = { + id: 'failover-1', + createdTime: { + seconds: '1700000000', + nanos: 0, + }, + failoverType: 'FAILOVER_TYPE_GRACEFUL', + clusterFailovers: [], + }; + + setup({ failoverEvent, isOpen: true }); + + expect(screen.getByRole('dialog')).toBeInTheDocument(); + }); + + it('does not render modal when isOpen is false', () => { + const failoverEvent: FailoverEvent = { + id: 'failover-1', + createdTime: { + seconds: '1700000000', + nanos: 0, + }, + failoverType: 'FAILOVER_TYPE_GRACEFUL', + clusterFailovers: [], + }; + + setup({ failoverEvent, isOpen: false }); + + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + }); + + it('displays failover ID correctly', () => { + const failoverEvent: FailoverEvent = { + id: 'failover-123', + createdTime: { + seconds: '1700000000', + nanos: 0, + }, + failoverType: 'FAILOVER_TYPE_GRACEFUL', + clusterFailovers: [], + }; + + setup({ failoverEvent, isOpen: true }); + + expect(screen.getByText('ID')).toBeInTheDocument(); + expect(screen.getByText('failover-123')).toBeInTheDocument(); + }); + + it('displays formatted time when createdTime is provided', () => { + const failoverEvent: FailoverEvent = { + id: 'failover-1', + createdTime: { + seconds: '1700000000', + nanos: 0, + }, + failoverType: 'FAILOVER_TYPE_GRACEFUL', + clusterFailovers: [], + }; + + setup({ failoverEvent, isOpen: true }); + + expect(screen.getByText('Time')).toBeInTheDocument(); + expect(screen.getByTestId('formatted-date')).toBeInTheDocument(); + }); + + it('handles null createdTime', () => { + const failoverEvent: FailoverEvent = { + id: 'failover-1', + createdTime: null, + failoverType: 'FAILOVER_TYPE_GRACEFUL', + clusterFailovers: [], + }; + + setup({ failoverEvent, isOpen: true }); + + expect(screen.getByText('Time')).toBeInTheDocument(); + expect(screen.getByTestId('formatted-date')).toBeInTheDocument(); + }); + + it('renders table with cluster failovers when clusterFailovers array is not empty', () => { + const failoverEvent: FailoverEvent = { + id: 'failover-1', + createdTime: { + seconds: '1700000000', + nanos: 0, + }, + failoverType: 'FAILOVER_TYPE_GRACEFUL', + clusterFailovers: [ + { + fromCluster: { + activeClusterName: 'cluster-1', + failoverVersion: '1', + }, + toCluster: { + activeClusterName: 'cluster-2', + failoverVersion: '2', + }, + clusterAttribute: null, + }, + ], + }; + + setup({ failoverEvent, isOpen: true }); + + expect(screen.getByText('Scope')).toBeInTheDocument(); + expect(screen.getByText('Attribute')).toBeInTheDocument(); + expect(screen.getByText('Clusters')).toBeInTheDocument(); + expect(screen.getByText('Primary')).toBeInTheDocument(); + expect(screen.getByText('-')).toBeInTheDocument(); + expect( + screen.getByTestId('mock-single-cluster-failover') + ).toBeInTheDocument(); + }); + + it('does not render table when clusterFailovers array is empty', () => { + const failoverEvent: FailoverEvent = { + id: 'failover-1', + createdTime: { + seconds: '1700000000', + nanos: 0, + }, + failoverType: 'FAILOVER_TYPE_GRACEFUL', + clusterFailovers: [], + }; + + setup({ failoverEvent, isOpen: true }); + + expect(screen.queryByText('Scope')).not.toBeInTheDocument(); + expect(screen.queryByText('Attribute')).not.toBeInTheDocument(); + expect(screen.queryByText('Clusters')).not.toBeInTheDocument(); + }); + + it('displays Primary scope and dash for attribute when clusterAttribute is null', () => { + const failoverEvent: FailoverEvent = { + id: 'failover-1', + createdTime: { + seconds: '1700000000', + nanos: 0, + }, + failoverType: 'FAILOVER_TYPE_GRACEFUL', + clusterFailovers: [ + { + fromCluster: { + activeClusterName: 'cluster-1', + failoverVersion: '1', + }, + toCluster: { + activeClusterName: 'cluster-2', + failoverVersion: '2', + }, + clusterAttribute: null, + }, + ], + }; + + setup({ failoverEvent, isOpen: true }); + + expect(screen.getByText('Primary')).toBeInTheDocument(); + expect(screen.getByText('-')).toBeInTheDocument(); + }); + + it('displays scope and attribute name when clusterAttribute is provided', () => { + const failoverEvent: FailoverEvent = { + id: 'failover-1', + createdTime: { + seconds: '1700000000', + nanos: 0, + }, + failoverType: 'FAILOVER_TYPE_GRACEFUL', + clusterFailovers: [ + { + fromCluster: { + activeClusterName: 'cluster-1', + failoverVersion: '1', + }, + toCluster: { + activeClusterName: 'cluster-2', + failoverVersion: '2', + }, + clusterAttribute: { + scope: 'city', + name: 'new_york', + }, + }, + ], + }; + + setup({ failoverEvent, isOpen: true }); + + expect(screen.getByText('city')).toBeInTheDocument(); + expect(screen.getByText('new_york')).toBeInTheDocument(); + }); + + it('renders multiple cluster failovers correctly', () => { + const failoverEvent: FailoverEvent = { + id: 'failover-1', + createdTime: { + seconds: '1700000000', + nanos: 0, + }, + failoverType: 'FAILOVER_TYPE_GRACEFUL', + clusterFailovers: [ + { + fromCluster: { + activeClusterName: 'cluster-1', + failoverVersion: '1', + }, + toCluster: { + activeClusterName: 'cluster-2', + failoverVersion: '2', + }, + clusterAttribute: null, + }, + { + fromCluster: { + activeClusterName: 'cluster-3', + failoverVersion: '3', + }, + toCluster: { + activeClusterName: 'cluster-4', + failoverVersion: '4', + }, + clusterAttribute: { + scope: 'region', + name: 'us-east', + }, + }, + ], + }; + + setup({ failoverEvent, isOpen: true }); + + expect(screen.getByText('Primary')).toBeInTheDocument(); + expect(screen.getByText('region')).toBeInTheDocument(); + expect(screen.getByText('us-east')).toBeInTheDocument(); + const clusterComponents = screen.getAllByTestId( + 'mock-single-cluster-failover' + ); + expect(clusterComponents).toHaveLength(2); + expect(clusterComponents[0]).toHaveTextContent('cluster-1 -> cluster-2'); + expect(clusterComponents[1]).toHaveTextContent('cluster-3 -> cluster-4'); + }); + + it('calls onClose when Close button is clicked', () => { + const failoverEvent: FailoverEvent = { + id: 'failover-1', + createdTime: { + seconds: '1700000000', + nanos: 0, + }, + failoverType: 'FAILOVER_TYPE_GRACEFUL', + clusterFailovers: [], + }; + + const mockOnClose = jest.fn(); + setup({ failoverEvent, isOpen: true, onClose: mockOnClose }); + + const closeButton = screen.getByText('Close'); + closeButton.click(); + + expect(mockOnClose).toHaveBeenCalledTimes(1); + }); + + it('displays modal title correctly', () => { + const failoverEvent: FailoverEvent = { + id: 'failover-1', + createdTime: { + seconds: '1700000000', + nanos: 0, + }, + failoverType: 'FAILOVER_TYPE_GRACEFUL', + clusterFailovers: [], + }; + + setup({ failoverEvent, isOpen: true }); + + expect(screen.getByText('Failover Information')).toBeInTheDocument(); + }); +}); + +function setup({ + failoverEvent, + isOpen = true, + onClose = jest.fn(), +}: { + failoverEvent: FailoverEvent; + isOpen?: boolean; + onClose?: () => void; +}) { + render( + + ); +} diff --git a/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.styles.ts b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.styles.ts new file mode 100644 index 000000000..87dc86b2b --- /dev/null +++ b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.styles.ts @@ -0,0 +1,85 @@ +import { styled as createStyled, withStyle, type Theme } from 'baseui'; +import { ModalBody, ModalFooter, type ModalOverrides } from 'baseui/modal'; +import { type TableOverrides } from 'baseui/table-semantic'; +import { type StyleObject } from 'styletron-react'; + +export const overrides = { + modal: { + Close: { + style: ({ $theme }: { $theme: Theme }): StyleObject => ({ + top: $theme.sizing.scale850, + right: $theme.sizing.scale800, + }), + }, + Dialog: { + style: (): StyleObject => ({ + width: '700px', + }), + }, + } satisfies ModalOverrides, + 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 = { + ModalHeader: createStyled('div', ({ $theme }: { $theme: Theme }) => ({ + padding: $theme.sizing.scale600, + })), + Title: createStyled('div', ({ $theme }: { $theme: Theme }) => ({ + ...$theme.typography.HeadingSmall, + })), + ModalBody: withStyle(ModalBody, ({ $theme }: { $theme: Theme }) => ({ + padding: $theme.sizing.scale600, + })), + InfoRow: createStyled('div', ({ $theme }: { $theme: Theme }) => ({ + display: 'flex', + gap: $theme.sizing.scale800, + marginBottom: $theme.sizing.scale500, + ':last-child': { + marginBottom: 0, + }, + })), + InfoItem: createStyled('div', { + display: 'flex', + flexDirection: 'column', + }), + InfoLabel: createStyled('div', ({ $theme }: { $theme: Theme }) => ({ + ...$theme.typography.LabelXSmall, + color: $theme.colors.contentSecondary, + marginBottom: $theme.sizing.scale200, + })), + InfoValue: createStyled('div', ({ $theme }: { $theme: Theme }) => ({ + ...$theme.typography.LabelXSmall, + color: $theme.colors.contentPrimary, + })), + TableContainer: createStyled('div', ({ $theme }: { $theme: Theme }) => ({ + marginTop: $theme.sizing.scale600, + maxHeight: '50vh', + overflowY: 'auto', + })), + ModalFooter: withStyle(ModalFooter, ({ $theme }: { $theme: Theme }) => ({ + padding: `${$theme.sizing.scale500} ${$theme.sizing.scale600}`, + borderTop: `1px solid ${$theme.colors.borderOpaque}`, + display: 'flex', + justifyContent: 'flex-end', + gap: $theme.sizing.scale400, + })), +}; diff --git a/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.tsx b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.tsx new file mode 100644 index 000000000..b4ac6416b --- /dev/null +++ b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.tsx @@ -0,0 +1,95 @@ +'use client'; +import { useMemo } from 'react'; + +import { Modal, ModalButton } from 'baseui/modal'; +import { Table } from 'baseui/table-semantic'; + +import FormattedDate from '@/components/formatted-date/formatted-date'; +import parseGrpcTimestamp from '@/utils/datetime/parse-grpc-timestamp'; + +import DomainPageFailoverSingleCluster from '../domain-page-failover-single-cluster/domain-page-failover-single-cluster'; + +import { overrides, styled } from './domain-page-failover-modal.styles'; +import { type Props } from './domain-page-failover-modal.types'; + +export default function DomainPageFailoverModal({ + failoverEvent, + isOpen, + onClose, +}: Props) { + const tableRows = useMemo(() => { + return failoverEvent.clusterFailovers.map((clusterFailover) => { + const fromCluster = clusterFailover.fromCluster?.activeClusterName; + const toCluster = clusterFailover.toCluster?.activeClusterName; + const clusters = ( + + ); + + const attribute = clusterFailover.clusterAttribute; + if (attribute === null) { + return { + scope: 'Primary', + attribute: '-', + clusters, + }; + } + + return { + scope: attribute.scope, + attribute: attribute.name, + clusters, + }; + }); + }, [failoverEvent]); + + const formattedTime = useMemo(() => { + if (!failoverEvent.createdTime) return null; + return parseGrpcTimestamp(failoverEvent.createdTime); + }, [failoverEvent]); + + return ( + + + Failover Information + + + + + ID + {failoverEvent.id} + + + Time + + + + + + {tableRows.length > 0 && ( + + + + )} + + + + Close + + + + ); +} diff --git a/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.types.ts b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.types.ts new file mode 100644 index 000000000..ca4eadf57 --- /dev/null +++ b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.types.ts @@ -0,0 +1,7 @@ +import { type FailoverEvent } from '@/route-handlers/list-failover-history/list-failover-history.types'; + +export type Props = { + failoverEvent: FailoverEvent; + isOpen: boolean; + onClose: () => void; +}; From 42b10d597a656a6b6cc7f39096c0633d8db0a4b5 Mon Sep 17 00:00:00 2001 From: Adhitya Mamallan Date: Fri, 14 Nov 2025 19:01:26 +0100 Subject: [PATCH 2/4] Address comments Signed-off-by: Adhitya Mamallan --- .../domain-page-failover-modal/domain-page-failover-modal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.tsx b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.tsx index b4ac6416b..3d0dc1f9f 100644 --- a/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.tsx +++ b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.tsx @@ -43,12 +43,12 @@ export default function DomainPageFailoverModal({ clusters, }; }); - }, [failoverEvent]); + }, [failoverEvent.clusterFailovers]); const formattedTime = useMemo(() => { if (!failoverEvent.createdTime) return null; return parseGrpcTimestamp(failoverEvent.createdTime); - }, [failoverEvent]); + }, [failoverEvent.createdTime]); return ( Date: Fri, 14 Nov 2025 19:12:25 +0100 Subject: [PATCH 3/4] Fix styles Signed-off-by: Adhitya Mamallan --- .../domain-page-failover-modal.styles.ts | 59 +++++++++---------- .../domain-page-failover-modal.tsx | 10 ++-- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.styles.ts b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.styles.ts index 87dc86b2b..456e963cf 100644 --- a/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.styles.ts +++ b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.styles.ts @@ -1,12 +1,17 @@ -import { styled as createStyled, withStyle, type Theme } from 'baseui'; -import { ModalBody, ModalFooter, type ModalOverrides } from 'baseui/modal'; +import { styled as createStyled, withStyle } from 'baseui'; +import { + ModalBody, + ModalFooter, + ModalHeader, + type ModalOverrides, +} from 'baseui/modal'; import { type TableOverrides } from 'baseui/table-semantic'; import { type StyleObject } from 'styletron-react'; export const overrides = { modal: { Close: { - style: ({ $theme }: { $theme: Theme }): StyleObject => ({ + style: ({ $theme }): StyleObject => ({ top: $theme.sizing.scale850, right: $theme.sizing.scale800, }), @@ -19,16 +24,17 @@ export const overrides = { } satisfies ModalOverrides, table: { TableHeadCell: { - style: ({ $theme }: { $theme: Theme }): StyleObject => ({ + style: ({ $theme }): StyleObject => ({ ...$theme.typography.LabelXSmall, paddingTop: $theme.sizing.scale300, paddingBottom: $theme.sizing.scale300, paddingLeft: $theme.sizing.scale500, paddingRight: $theme.sizing.scale500, + color: $theme.colors.contentTertiary, }), }, TableBodyCell: { - style: ({ $theme }: { $theme: Theme }): StyleObject => ({ + style: ({ $theme }): StyleObject => ({ ...$theme.typography.ParagraphXSmall, paddingTop: $theme.sizing.scale300, paddingBottom: $theme.sizing.scale300, @@ -40,46 +46,37 @@ export const overrides = { }; export const styled = { - ModalHeader: createStyled('div', ({ $theme }: { $theme: Theme }) => ({ - padding: $theme.sizing.scale600, + ModalHeader: withStyle(ModalHeader, ({ $theme }) => ({ + marginTop: $theme.sizing.scale850, })), - Title: createStyled('div', ({ $theme }: { $theme: Theme }) => ({ - ...$theme.typography.HeadingSmall, - })), - ModalBody: withStyle(ModalBody, ({ $theme }: { $theme: Theme }) => ({ - padding: $theme.sizing.scale600, - })), - InfoRow: createStyled('div', ({ $theme }: { $theme: Theme }) => ({ + InfoRow: createStyled('div', ({ $theme }) => ({ display: 'flex', gap: $theme.sizing.scale800, - marginBottom: $theme.sizing.scale500, - ':last-child': { - marginBottom: 0, - }, + marginBottom: 0, + paddingLeft: $theme.sizing.scale500, + paddingRight: $theme.sizing.scale500, })), - InfoItem: createStyled('div', { + InfoItem: createStyled('div', ({ $theme }) => ({ display: 'flex', flexDirection: 'column', - }), - InfoLabel: createStyled('div', ({ $theme }: { $theme: Theme }) => ({ + gap: $theme.sizing.scale200, + })), + InfoLabel: createStyled('div', ({ $theme }) => ({ ...$theme.typography.LabelXSmall, - color: $theme.colors.contentSecondary, - marginBottom: $theme.sizing.scale200, + color: $theme.colors.contentTertiary, })), - InfoValue: createStyled('div', ({ $theme }: { $theme: Theme }) => ({ + InfoValue: createStyled('div', ({ $theme }) => ({ ...$theme.typography.LabelXSmall, color: $theme.colors.contentPrimary, })), - TableContainer: createStyled('div', ({ $theme }: { $theme: Theme }) => ({ + TableContainer: createStyled('div', ({ $theme }) => ({ marginTop: $theme.sizing.scale600, maxHeight: '50vh', overflowY: 'auto', })), - ModalFooter: withStyle(ModalFooter, ({ $theme }: { $theme: Theme }) => ({ - padding: `${$theme.sizing.scale500} ${$theme.sizing.scale600}`, - borderTop: `1px solid ${$theme.colors.borderOpaque}`, + ModalFooter: withStyle(ModalFooter, { display: 'flex', - justifyContent: 'flex-end', - gap: $theme.sizing.scale400, - })), + flexDirection: 'row-reverse', + justifyContent: 'space-between', + }), }; diff --git a/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.tsx b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.tsx index 3d0dc1f9f..675cd9d45 100644 --- a/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.tsx +++ b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.tsx @@ -1,7 +1,7 @@ 'use client'; import { useMemo } from 'react'; -import { Modal, ModalButton } from 'baseui/modal'; +import { Modal, ModalBody, ModalButton } from 'baseui/modal'; import { Table } from 'baseui/table-semantic'; import FormattedDate from '@/components/formatted-date/formatted-date'; @@ -57,10 +57,8 @@ export default function DomainPageFailoverModal({ closeable overrides={overrides.modal} > - - Failover Information - - + Failover Information + ID @@ -84,7 +82,7 @@ export default function DomainPageFailoverModal({ /> )} - + Close From 4c25c033454f9cc4f566031e5bf3f009f96aa7f2 Mon Sep 17 00:00:00 2001 From: Adhitya Mamallan Date: Fri, 14 Nov 2025 19:14:47 +0100 Subject: [PATCH 4/4] Remove unused import Signed-off-by: Adhitya Mamallan --- .../domain-page-failover-modal.styles.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.styles.ts b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.styles.ts index 456e963cf..27a448f53 100644 --- a/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.styles.ts +++ b/src/views/domain-page/domain-page-failover-modal/domain-page-failover-modal.styles.ts @@ -1,10 +1,5 @@ import { styled as createStyled, withStyle } from 'baseui'; -import { - ModalBody, - ModalFooter, - ModalHeader, - type ModalOverrides, -} from 'baseui/modal'; +import { ModalFooter, ModalHeader, type ModalOverrides } from 'baseui/modal'; import { type TableOverrides } from 'baseui/table-semantic'; import { type StyleObject } from 'styletron-react';