From b7298c4585e020bc4924e1f05281f79c72b2a89f Mon Sep 17 00:00:00 2001 From: Abhinav Deval Date: Mon, 29 Jun 2026 22:41:09 +0530 Subject: [PATCH 1/7] feat: implement SubscriptionTable scaffolding and register global module exports Signed-off-by: Abhinav Deval --- .../SubscriptionTable/SubscriptionTable.tsx | 138 ++++++++++++++++++ src/custom/SubscriptionTable/index.ts | 1 + src/custom/SubscriptionTable/style.tsx | 29 ++++ src/custom/index.ts | 1 + src/custom/index.tsx | 3 + 5 files changed, 172 insertions(+) create mode 100644 src/custom/SubscriptionTable/SubscriptionTable.tsx create mode 100644 src/custom/SubscriptionTable/index.ts create mode 100644 src/custom/SubscriptionTable/style.tsx diff --git a/src/custom/SubscriptionTable/SubscriptionTable.tsx b/src/custom/SubscriptionTable/SubscriptionTable.tsx new file mode 100644 index 000000000..ec5ea3469 --- /dev/null +++ b/src/custom/SubscriptionTable/SubscriptionTable.tsx @@ -0,0 +1,138 @@ +import CheckIcon from '@mui/icons-material/Check'; +import CloseIcon from '@mui/icons-material/Close'; +import { + Box, + Button, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography +} from '@mui/material'; +import React from 'react'; + +// Strict TypeScript interfaces to handle dynamic table data +export interface PlanFeature { + featureName: string; + freePlan: boolean | string; + teamPlan: boolean | string; + enterprisePlan: boolean | string; +} + +export interface SubscriptionTableProps { + title?: string; + features: PlanFeature[]; + onPlanSelect?: (planType: 'free' | 'team' | 'enterprise') => void; +} + +export const SubscriptionTable: React.FC = ({ + title = 'Subscription Plans Comparison', + features, + onPlanSelect +}) => { + // Helper function to render true/false values as Crisp Icons or Text + const renderValue = (value: boolean | string) => { + if (typeof value === 'boolean') { + return value ? ( + + ) : ( + + ); + } + return ( + + {value} + + ); + }; + + return ( + + {title && ( + + {title} + + )} + + + + + + Features + + Free Plan + + + + + + Team Plan + + + + + + Enterprise Plan + + + + + + + + + {features.map((row, index) => ( + + + {row.featureName} + + {renderValue(row.freePlan)} + {renderValue(row.teamPlan)} + {renderValue(row.enterprisePlan)} + + ))} + +
+
+
+ ); +}; + +SubscriptionTable.displayName = 'SubscriptionTable'; diff --git a/src/custom/SubscriptionTable/index.ts b/src/custom/SubscriptionTable/index.ts new file mode 100644 index 000000000..121092eba --- /dev/null +++ b/src/custom/SubscriptionTable/index.ts @@ -0,0 +1 @@ +export * from './SubscriptionTable'; diff --git a/src/custom/SubscriptionTable/style.tsx b/src/custom/SubscriptionTable/style.tsx new file mode 100644 index 000000000..c487461a6 --- /dev/null +++ b/src/custom/SubscriptionTable/style.tsx @@ -0,0 +1,29 @@ +import { Paper, TableCell, TableRow } from '@mui/material'; +import { styled } from '@mui/material/styles'; + +// Custom Styled Container aligned with Sistent Theme +export const StyledTableContainer = styled(Paper)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius * 2, + overflow: 'hidden', + boxShadow: theme.shadows[2], + border: `1px solid ${theme.palette.divider}` +})); + +// Custom Styled Header Row +export const StyledHeaderRow = styled(TableRow)(({ theme }) => ({ + backgroundColor: theme.palette.mode === 'light' ? '#f9fafb' : '#1e1e1e' +})); + +// Custom Styled Table Cell for Typography token enforcement +export const StyledTableCell = styled(TableCell)(({ theme }) => ({ + fontFamily: 'Open Sans, sans-serif', + borderColor: theme.palette.divider, + fontWeight: 500 +})); + +// Feature Name Column styling (High Emphasis) +export const FeatureHeaderCell = styled(StyledTableCell)(() => ({ + fontFamily: 'Qanelas Soft, sans-serif', + fontWeight: 700, + color: 'text.primary' +})); diff --git a/src/custom/index.ts b/src/custom/index.ts index 3f35c351e..1900311ea 100644 --- a/src/custom/index.ts +++ b/src/custom/index.ts @@ -5,3 +5,4 @@ export * from './Markdown'; export * from './Modal'; export * from './RJSFFormWrapper'; export * from './StyledAccordion'; +export * from './SubscriptionTable'; diff --git a/src/custom/index.tsx b/src/custom/index.tsx index 51a104ce5..0b5f97969 100644 --- a/src/custom/index.tsx +++ b/src/custom/index.tsx @@ -44,6 +44,7 @@ import ResponsiveDataTable, { } from './ResponsiveDataTable'; import SearchBar, { SearchBarProps } from './SearchBar'; import { StyledCardProps } from './StyledCard/StyledCard'; +import { SubscriptionTable } from './SubscriptionTable'; import { getCopyDeepLinkAction, TableAction } from './TableActions'; import { TeamTable, TeamTableConfiguration } from './TeamTable'; import { TooltipIcon } from './TooltipIconButton'; @@ -111,6 +112,7 @@ export { StyledDialogActions, StyledDialogContent, StyledDialogTitle, + SubscriptionTable, TeamTable, TeamTableConfiguration, TooltipIcon, @@ -173,5 +175,6 @@ export * from './permissions'; export * from './ResourceDetailFormatters'; export * from './RJSFFormWrapper'; export * from './ShareModal'; +export * from './SubscriptionTable'; export * from './UserSearchField'; export * from './Workspaces'; From 812e1ac8e1c4dc6f632dd70bed3a96317303ffbd Mon Sep 17 00:00:00 2001 From: Abhinav Deval Date: Mon, 29 Jun 2026 23:15:06 +0530 Subject: [PATCH 2/7] feat: implement SubscriptionTable scaffolding and register global module exports Signed-off-by: Abhinav Deval --- .../SubscriptionTable/SubscriptionTable.tsx | 92 +++++++++---------- 1 file changed, 43 insertions(+), 49 deletions(-) diff --git a/src/custom/SubscriptionTable/SubscriptionTable.tsx b/src/custom/SubscriptionTable/SubscriptionTable.tsx index ec5ea3469..441e7f73d 100644 --- a/src/custom/SubscriptionTable/SubscriptionTable.tsx +++ b/src/custom/SubscriptionTable/SubscriptionTable.tsx @@ -1,37 +1,33 @@ import CheckIcon from '@mui/icons-material/Check'; import CloseIcon from '@mui/icons-material/Close'; -import { - Box, - Button, - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - Typography -} from '@mui/material'; +import { Box, Button, Table, TableBody, TableHead, Typography } from '@mui/material'; import React from 'react'; // Strict TypeScript interfaces to handle dynamic table data -export interface PlanFeature { - featureName: string; - freePlan: boolean | string; - teamPlan: boolean | string; - enterprisePlan: boolean | string; -} - export interface SubscriptionTableProps { title?: string; - features: PlanFeature[]; + features?: PlanFeature[]; onPlanSelect?: (planType: 'free' | 'team' | 'enterprise') => void; + featuresLabel?: string; + freePlanLabel?: string; + freePlanButtonLabel?: string; + teamPlanLabel?: string; + teamPlanButtonLabel?: string; + enterprisePlanLabel?: string; + enterprisePlanButtonLabel?: string; } export const SubscriptionTable: React.FC = ({ title = 'Subscription Plans Comparison', - features, - onPlanSelect + features = [], + onPlanSelect, + featuresLabel = 'Features', + freePlanLabel = 'Free Plan', + freePlanButtonLabel = 'Get Started', + teamPlanLabel = 'Team Plan', + teamPlanButtonLabel = 'Upgrade', + enterprisePlanLabel = 'Enterprise Plan', + enterprisePlanButtonLabel = 'Contact Us' }) => { // Helper function to render true/false values as Crisp Icons or Text const renderValue = (value: boolean | string) => { @@ -65,21 +61,23 @@ export const SubscriptionTable: React.FC = ({ )} - + - - - Features - - Free Plan + + + + {featuresLabel} + + + {freePlanLabel} - - - Team Plan + + + {teamPlanLabel} - - - Enterprise Plan + + + {enterprisePlanLabel} - - + + @@ -116,21 +114,17 @@ export const SubscriptionTable: React.FC = ({ '&:hover': { backgroundColor: 'action.hover' } }} > - + {row.featureName} - - {renderValue(row.freePlan)} - {renderValue(row.teamPlan)} - {renderValue(row.enterprisePlan)} + + {renderValue(row.freePlan)} + {renderValue(row.teamPlan)} + {renderValue(row.enterprisePlan)} ))}
-
+ ); }; From 43af05b56a0a8c451e5c324f199e37d31ee5735d Mon Sep 17 00:00:00 2001 From: Abhinav Deval Date: Mon, 6 Jul 2026 12:01:44 +0530 Subject: [PATCH 3/7] refactor: resolve bot feedback for SubscriptionTable Signed-off-by: Abhinav Deval --- src/custom/SubscriptionTable/SubscriptionTable.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/custom/SubscriptionTable/SubscriptionTable.tsx b/src/custom/SubscriptionTable/SubscriptionTable.tsx index 441e7f73d..f8c26e6f7 100644 --- a/src/custom/SubscriptionTable/SubscriptionTable.tsx +++ b/src/custom/SubscriptionTable/SubscriptionTable.tsx @@ -1,9 +1,16 @@ import CheckIcon from '@mui/icons-material/Check'; import CloseIcon from '@mui/icons-material/Close'; -import { Box, Button, Table, TableBody, TableHead, Typography } from '@mui/material'; +import { Box, Button, Table, TableBody, TableHead, TableRow, Typography } from '@mui/material'; import React from 'react'; +import { FeatureHeaderCell, StyledHeaderRow, StyledTableCell, StyledTableContainer } from './style'; + +export interface PlanFeature { + featureName: string; + freePlan: boolean | string; + teamPlan: boolean | string; + enterprisePlan: boolean | string; +} -// Strict TypeScript interfaces to handle dynamic table data export interface SubscriptionTableProps { title?: string; features?: PlanFeature[]; @@ -60,7 +67,6 @@ export const SubscriptionTable: React.FC = ({ {title} )} - @@ -104,7 +110,6 @@ export const SubscriptionTable: React.FC = ({ - {features.map((row, index) => ( Date: Sat, 29 Aug 2026 19:21:03 +0530 Subject: [PATCH 4/7] refactor: resolve bot feedback on responsive overflow, tokens, and accessibility Signed-off-by: Abhinav Deval --- .../SubscriptionTable/SubscriptionTable.tsx | 8 +++----- src/custom/SubscriptionTable/style.tsx | 17 +++++++---------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/custom/SubscriptionTable/SubscriptionTable.tsx b/src/custom/SubscriptionTable/SubscriptionTable.tsx index f8c26e6f7..b0ec3eebb 100644 --- a/src/custom/SubscriptionTable/SubscriptionTable.tsx +++ b/src/custom/SubscriptionTable/SubscriptionTable.tsx @@ -36,13 +36,12 @@ export const SubscriptionTable: React.FC = ({ enterprisePlanLabel = 'Enterprise Plan', enterprisePlanButtonLabel = 'Contact Us' }) => { - // Helper function to render true/false values as Crisp Icons or Text const renderValue = (value: boolean | string) => { if (typeof value === 'boolean') { return value ? ( - + ) : ( - + ); } return ( @@ -60,8 +59,7 @@ export const SubscriptionTable: React.FC = ({ component="h2" sx={{ mb: 3, - fontWeight: 700, - fontFamily: 'Qanelas Soft, sans-serif' // Figma semantic typography mapping + fontWeight: 700 }} > {title} diff --git a/src/custom/SubscriptionTable/style.tsx b/src/custom/SubscriptionTable/style.tsx index c487461a6..c22334870 100644 --- a/src/custom/SubscriptionTable/style.tsx +++ b/src/custom/SubscriptionTable/style.tsx @@ -1,29 +1,26 @@ import { Paper, TableCell, TableRow } from '@mui/material'; import { styled } from '@mui/material/styles'; -// Custom Styled Container aligned with Sistent Theme export const StyledTableContainer = styled(Paper)(({ theme }) => ({ borderRadius: theme.shape.borderRadius * 2, - overflow: 'hidden', + overflowX: 'auto', + overflowY: 'hidden', boxShadow: theme.shadows[2], border: `1px solid ${theme.palette.divider}` })); -// Custom Styled Header Row export const StyledHeaderRow = styled(TableRow)(({ theme }) => ({ - backgroundColor: theme.palette.mode === 'light' ? '#f9fafb' : '#1e1e1e' + backgroundColor: theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.grey[900] })); -// Custom Styled Table Cell for Typography token enforcement export const StyledTableCell = styled(TableCell)(({ theme }) => ({ - fontFamily: 'Open Sans, sans-serif', + fontFamily: theme.typography.fontFamily, borderColor: theme.palette.divider, fontWeight: 500 })); -// Feature Name Column styling (High Emphasis) -export const FeatureHeaderCell = styled(StyledTableCell)(() => ({ - fontFamily: 'Qanelas Soft, sans-serif', +export const FeatureHeaderCell = styled(StyledTableCell)(({ theme }) => ({ + fontFamily: theme.typography.fontFamily, fontWeight: 700, - color: 'text.primary' + color: theme.palette.text.primary })); From efd53ac8a2678c6d3c67423978d2eb9f4af215c2 Mon Sep 17 00:00:00 2001 From: Abhinav Deval Date: Wed, 2 Sep 2026 01:05:23 +0530 Subject: [PATCH 5/7] feat: add root exports, default export and unit tests for SubscriptionTable Signed-off-by: Abhinav Deval --- .../SubscriptionTable.test.tsx | 63 +++++++++++++++++++ .../SubscriptionTable/SubscriptionTable.tsx | 2 + src/index.tsx | 8 +++ 3 files changed, 73 insertions(+) create mode 100644 src/custom/SubscriptionTable/SubscriptionTable.test.tsx diff --git a/src/custom/SubscriptionTable/SubscriptionTable.test.tsx b/src/custom/SubscriptionTable/SubscriptionTable.test.tsx new file mode 100644 index 000000000..2d1d71534 --- /dev/null +++ b/src/custom/SubscriptionTable/SubscriptionTable.test.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { SubscriptionTable, PlanFeature } from './SubscriptionTable'; + +const mockFeatures: PlanFeature[] = [ + { + featureName: 'Cloud Storage', + freePlan: '5GB', + teamPlan: '50GB', + enterprisePlan: 'Unlimited' + }, + { + featureName: 'Priority Support', + freePlan: false, + teamPlan: true, + enterprisePlan: true + } +]; + +describe('SubscriptionTable Component', () => { + it('renders table title and feature rows correctly', () => { + render(); + expect(screen.getByText('Custom Pricing Table')).toBeInTheDocument(); + expect(screen.getByText('Cloud Storage')).toBeInTheDocument(); + expect(screen.getByText('Priority Support')).toBeInTheDocument(); + expect(screen.getByText('5GB')).toBeInTheDocument(); + }); + + it('renders boolean indicators with accessibility labels', () => { + render(); + expect(screen.getAllByTestId('check-icon').length).toBeGreaterThan(0); + expect(screen.getAllByTestId('close-icon').length).toBeGreaterThan(0); + }); + + it('calls onPlanSelect when action buttons are clicked', () => { + const handlePlanSelect = jest.fn(); + render(); + + fireEvent.click(screen.getByText('Get Started')); + expect(handlePlanSelect).toHaveBeenCalledWith('free'); + + fireEvent.click(screen.getByText('Upgrade')); + expect(handlePlanSelect).toHaveBeenCalledWith('team'); + + fireEvent.click(screen.getByText('Contact Us')); + expect(handlePlanSelect).toHaveBeenCalledWith('enterprise'); + }); + + it('renders custom labels when passed via props', () => { + render( + + ); + expect(screen.getByText('Key Capabilities')).toBeInTheDocument(); + expect(screen.getByText('Starter')).toBeInTheDocument(); + expect(screen.getByText('Join Now')).toBeInTheDocument(); + }); +}); diff --git a/src/custom/SubscriptionTable/SubscriptionTable.tsx b/src/custom/SubscriptionTable/SubscriptionTable.tsx index b0ec3eebb..77e3f9ad1 100644 --- a/src/custom/SubscriptionTable/SubscriptionTable.tsx +++ b/src/custom/SubscriptionTable/SubscriptionTable.tsx @@ -133,3 +133,5 @@ export const SubscriptionTable: React.FC = ({ }; SubscriptionTable.displayName = 'SubscriptionTable'; + +export default SubscriptionTable; diff --git a/src/index.tsx b/src/index.tsx index 24a87e1a1..8a0893380 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -164,3 +164,11 @@ export { type Team as TeamPickerRecord, type TeamSearchFieldProps } from './custom/DashboardWidgets/GettingStartedWidget/TeamSearchField'; + +// Explicit re-export for SubscriptionTable to prevent rollup-plugin-dts from +// dropping the component and its types from the generated dist/index.d.ts bundle. +export { + SubscriptionTable, + type SubscriptionTableProps, + type PlanFeature +} from './custom/SubscriptionTable'; From 171f8be2cc29d1d14e265f0b807dfcac08785de4 Mon Sep 17 00:00:00 2001 From: Abhinav Deval Date: Sat, 26 Sep 2026 12:16:20 +0530 Subject: [PATCH 6/7] refactor(SubscriptionTable): switch to standard typography variants and stable keys Signed-off-by: Abhinav Deval --- .../SubscriptionTable/SubscriptionTable.tsx | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/custom/SubscriptionTable/SubscriptionTable.tsx b/src/custom/SubscriptionTable/SubscriptionTable.tsx index 77e3f9ad1..eb751a542 100644 --- a/src/custom/SubscriptionTable/SubscriptionTable.tsx +++ b/src/custom/SubscriptionTable/SubscriptionTable.tsx @@ -45,7 +45,7 @@ export const SubscriptionTable: React.FC = ({ ); } return ( - + {value} ); @@ -59,7 +59,7 @@ export const SubscriptionTable: React.FC = ({ component="h2" sx={{ mb: 3, - fontWeight: 700 + fontWeight: 'fontWeightBold' }} > {title} @@ -69,19 +69,25 @@ export const SubscriptionTable: React.FC = ({
- - {featuresLabel} + + + {featuresLabel} + - - {freePlanLabel} + + + {freePlanLabel} + - - {teamPlanLabel} + + + {teamPlanLabel} + - - {enterprisePlanLabel} + + + {enterprisePlanLabel} +