From f1da308f906347cad4ae7ca0e4970debb2aaa216 Mon Sep 17 00:00:00 2001 From: Adam Thompson Date: Wed, 22 Oct 2025 17:28:29 -0400 Subject: [PATCH 01/43] rm step wrapper --- .changeset/wizard-patch-0.md | 5 +++ packages/wizard/src/Wizard/Wizard.tsx | 63 +++++++++++++-------------- 2 files changed, 35 insertions(+), 33 deletions(-) create mode 100644 .changeset/wizard-patch-0.md diff --git a/.changeset/wizard-patch-0.md b/.changeset/wizard-patch-0.md new file mode 100644 index 0000000000..6f7f76d3f7 --- /dev/null +++ b/.changeset/wizard-patch-0.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/wizard': patch +--- + +Removes wrapper div around step children diff --git a/packages/wizard/src/Wizard/Wizard.tsx b/packages/wizard/src/Wizard/Wizard.tsx index c1d2a7a4ed..60c0e6efcd 100644 --- a/packages/wizard/src/Wizard/Wizard.tsx +++ b/packages/wizard/src/Wizard/Wizard.tsx @@ -2,16 +2,18 @@ import React, { useCallback } from 'react'; import { CompoundComponent, + findChild, findChildren, } from '@leafygreen-ui/compound-component'; +import { Direction } from '@leafygreen-ui/descendants'; import { useControlled } from '@leafygreen-ui/hooks'; import { WizardSubComponentProperties } from '../constants'; -import { getLgIds } from '../utils/getLgIds'; import { WizardProvider } from '../WizardContext/WizardContext'; import { WizardFooter } from '../WizardFooter'; import { WizardStep } from '../WizardStep'; +import { wizardContainerStyles } from './Wizard.styles'; import { WizardProps } from './Wizard.types'; export const Wizard = CompoundComponent( @@ -19,13 +21,16 @@ export const Wizard = CompoundComponent( activeStep: activeStepProp, onStepChange, children, - 'data-lgid': dataLgId, + ...rest }: WizardProps) => { - const lgIds = getLgIds(dataLgId); const stepChildren = findChildren( children, WizardSubComponentProperties.Step, ); + const footerChild = findChild( + children, + WizardSubComponentProperties.Footer, + ); // Controlled/Uncontrolled activeStep value const { value: activeStep, updateValue: setActiveStep } = @@ -43,46 +48,38 @@ export const Wizard = CompoundComponent( } const updateStep = useCallback( - (step: number) => { - // Clamp the step value between 0 and stepChildren.length - 1 - const clampedStep = Math.max( - 0, - Math.min(step, stepChildren.length - 1), - ); - setActiveStep(clampedStep); + (direction: Direction) => { + const getNextStep = (curr: number) => { + switch (direction) { + case Direction.Next: + return Math.min(curr + 1, stepChildren.length - 1); + case Direction.Prev: + return Math.max(curr - 1, 0); + } + }; + + // TODO pass getNextStep into setter as callback https://jira.mongodb.org/browse/LG-5607 + const nextStep = getNextStep(activeStep); + setActiveStep(nextStep); }, - [setActiveStep, stepChildren.length], + [activeStep, setActiveStep, stepChildren.length], ); - /** - * NB: We're intentionally do _not_ wrap the `Wizard` (or `WizardStep`) component in a container element. - * This is done to ensure the Wizard is flexible, and can be rendered in any containing layout. - */ + // Get the current step to render + const currentStep = stepChildren[activeStep] || null; + return ( - - {stepChildren.map((child, i) => (i === activeStep ? child : null))} + +
+ {currentStep} + {footerChild} +
); }, { displayName: 'Wizard', - /** - * A single step in the wizard. A Wizard will only render Steps as children - */ Step: WizardStep, - - /** - * The footer of a Step component. - * Render this inside of each Step with the relevant button props for that Step. - * - * Back and Primary buttons trigger onStepChange. - * Automatically renders the "Back" button for all Steps except the first - */ Footer: WizardFooter, }, ); From 1c67385b650866c52969a36d9be08e4162bd0c1c Mon Sep 17 00:00:00 2001 From: Adam Thompson Date: Wed, 22 Oct 2025 17:57:45 -0400 Subject: [PATCH 02/43] rm descendants dep --- .changeset/wizard-patch-1.md | 5 +++++ packages/wizard/src/Wizard/Wizard.tsx | 23 ++++++++--------------- 2 files changed, 13 insertions(+), 15 deletions(-) create mode 100644 .changeset/wizard-patch-1.md diff --git a/.changeset/wizard-patch-1.md b/.changeset/wizard-patch-1.md new file mode 100644 index 0000000000..90935fc790 --- /dev/null +++ b/.changeset/wizard-patch-1.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/wizard': patch +--- + +Removes descendants dep. Clamp activeStep between 0 & step count \ No newline at end of file diff --git a/packages/wizard/src/Wizard/Wizard.tsx b/packages/wizard/src/Wizard/Wizard.tsx index 60c0e6efcd..a96c678243 100644 --- a/packages/wizard/src/Wizard/Wizard.tsx +++ b/packages/wizard/src/Wizard/Wizard.tsx @@ -5,7 +5,6 @@ import { findChild, findChildren, } from '@leafygreen-ui/compound-component'; -import { Direction } from '@leafygreen-ui/descendants'; import { useControlled } from '@leafygreen-ui/hooks'; import { WizardSubComponentProperties } from '../constants'; @@ -48,21 +47,15 @@ export const Wizard = CompoundComponent( } const updateStep = useCallback( - (direction: Direction) => { - const getNextStep = (curr: number) => { - switch (direction) { - case Direction.Next: - return Math.min(curr + 1, stepChildren.length - 1); - case Direction.Prev: - return Math.max(curr - 1, 0); - } - }; - - // TODO pass getNextStep into setter as callback https://jira.mongodb.org/browse/LG-5607 - const nextStep = getNextStep(activeStep); - setActiveStep(nextStep); + (step: number) => { + // Clamp the step value between 0 and stepChildren.length - 1 + const clampedStep = Math.max( + 0, + Math.min(step, stepChildren.length - 1), + ); + setActiveStep(clampedStep); }, - [activeStep, setActiveStep, stepChildren.length], + [setActiveStep, stepChildren.length], ); // Get the current step to render From 79c4d84be043b5c245be61a907aecdb357a62ff7 Mon Sep 17 00:00:00 2001 From: Adam Thompson Date: Wed, 22 Oct 2025 18:07:30 -0400 Subject: [PATCH 03/43] export WizardProvider --- .changeset/wizard-patch-2.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wizard-patch-2.md diff --git a/.changeset/wizard-patch-2.md b/.changeset/wizard-patch-2.md new file mode 100644 index 0000000000..4a924c356f --- /dev/null +++ b/.changeset/wizard-patch-2.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/wizard': patch +--- + +Exports WizardProvider From aab32ab964ac0a0b9b43e556ca168a9de2ddfed2 Mon Sep 17 00:00:00 2001 From: Adam Thompson Date: Tue, 4 Nov 2025 14:01:53 -0500 Subject: [PATCH 04/43] delete-wizard-demo private endpoints useFetchRequiredActionTableData renam ReqAct cards composable basic table stream processing card federated db card applications card clusters card wizard step context Delete requiredActionsConfig.tsx re-enable wizard add useRequiredActionAcknowledgements mv required action. add skeleton Update ModelApiKeysCard.tsx --- .npmrc | 7 +- packages/descendants/package.json | 2 +- packages/toast/package.json | 2 +- packages/wizard/src/HELPERS/BasicTable.tsx | 52 +++ .../src/HELPERS/TableHeaderWithSubtitle.tsx | 35 ++ packages/wizard/src/Wizard/Wizard.tsx | 21 +- .../wizard/src/demo/DeleteWizard.stories.tsx | 78 ++++ .../src/demo/DeleteWizardStepContent.types.ts | 1 + .../wizard/src/demo/RecommendedActionCard.tsx | 44 +++ .../wizard/src/demo/RecommendedActions.tsx | 69 ++++ .../RequiredActionCards/ApplicationsCard.tsx | 73 ++++ .../demo/RequiredActionCards/ClustersCard.tsx | 234 ++++++++++++ .../RequiredActionCards/FederatedDbCard.tsx | 141 +++++++ .../RequiredActionCards/ModelApiKeysCard.tsx | 105 ++++++ .../PrivateEndpointsCard.tsx | 348 ++++++++++++++++++ .../RequiredActionCard.tsx | 97 +++++ .../StreamProcessingCard.tsx | 110 ++++++ .../src/demo/RequiredActionCards/index.ts | 6 + packages/wizard/src/demo/RequiredActions.tsx | 96 +++++ packages/wizard/src/demo/constants.ts | 3 + .../hooks/useFetchRequiredActionTableData.ts | 41 +++ .../useRequiredActionAcknowledgements.ts | 44 +++ .../wizard/src/demo/recommendedActionData.tsx | 67 ++++ 23 files changed, 1654 insertions(+), 22 deletions(-) create mode 100644 packages/wizard/src/HELPERS/BasicTable.tsx create mode 100644 packages/wizard/src/HELPERS/TableHeaderWithSubtitle.tsx create mode 100644 packages/wizard/src/demo/DeleteWizard.stories.tsx create mode 100644 packages/wizard/src/demo/DeleteWizardStepContent.types.ts create mode 100644 packages/wizard/src/demo/RecommendedActionCard.tsx create mode 100644 packages/wizard/src/demo/RecommendedActions.tsx create mode 100644 packages/wizard/src/demo/RequiredActionCards/ApplicationsCard.tsx create mode 100644 packages/wizard/src/demo/RequiredActionCards/ClustersCard.tsx create mode 100644 packages/wizard/src/demo/RequiredActionCards/FederatedDbCard.tsx create mode 100644 packages/wizard/src/demo/RequiredActionCards/ModelApiKeysCard.tsx create mode 100644 packages/wizard/src/demo/RequiredActionCards/PrivateEndpointsCard.tsx create mode 100644 packages/wizard/src/demo/RequiredActionCards/RequiredActionCard.tsx create mode 100644 packages/wizard/src/demo/RequiredActionCards/StreamProcessingCard.tsx create mode 100644 packages/wizard/src/demo/RequiredActionCards/index.ts create mode 100644 packages/wizard/src/demo/RequiredActions.tsx create mode 100644 packages/wizard/src/demo/constants.ts create mode 100644 packages/wizard/src/demo/hooks/useFetchRequiredActionTableData.ts create mode 100644 packages/wizard/src/demo/hooks/useRequiredActionAcknowledgements.ts create mode 100644 packages/wizard/src/demo/recommendedActionData.tsx diff --git a/.npmrc b/.npmrc index d805c4429b..6b1b4eaaef 100644 --- a/.npmrc +++ b/.npmrc @@ -8,4 +8,9 @@ public-hoist-pattern[]=@testing-library/* public-hoist-pattern[]=@types/* public-hoist-pattern[]=*jest* public-hoist-pattern[]=react -public-hoist-pattern[]=react-dom \ No newline at end of file +public-hoist-pattern[]=react-dom +# @leafygreen-ui:registry=http://localhost:4873 +# @lg-chat:registry=http://localhost:4873 +# @lg-charts:registry=http://localhost:4873 +# @lg-tools:registry=http://localhost:4873 +# //localhost:4873/:_authToken=YzNjMGRkMDY3ZjI3N2IzNzUxYzk3NjlkOWNjNTc1MGI6NjgwNmIyZDAwMDIzYjEyNzVjN2Q5NWZhZmRkZmFlMTY4ZTMzOWRhMDAwOTllOWMzZDMxYzcxZmFkMTE5 \ No newline at end of file diff --git a/packages/descendants/package.json b/packages/descendants/package.json index b360383fac..2f6ce5d968 100644 --- a/packages/descendants/package.json +++ b/packages/descendants/package.json @@ -21,7 +21,7 @@ }, "devDependencies": { "@faker-js/faker": "^8.0.0", - "@storybook/react": "8.6.14", + "@storybook/react": "latest", "@leafygreen-ui/button": "workspace:^", "@leafygreen-ui/emotion": "workspace:^", "@leafygreen-ui/popover": "workspace:^", diff --git a/packages/toast/package.json b/packages/toast/package.json index d66de64937..8296006898 100644 --- a/packages/toast/package.json +++ b/packages/toast/package.json @@ -35,7 +35,7 @@ "@faker-js/faker": "^8.0.0", "@leafygreen-ui/button": "workspace:^", "@lg-tools/build": "workspace:^", - "@storybook/types": "^8.5.3", + "@storybook/types": "latest", "react-test-renderer": "^18.2.0" }, "homepage": "https://github.com/mongodb/leafygreen-ui/tree/main/packages/toast", diff --git a/packages/wizard/src/HELPERS/BasicTable.tsx b/packages/wizard/src/HELPERS/BasicTable.tsx new file mode 100644 index 0000000000..74c7c2cfe4 --- /dev/null +++ b/packages/wizard/src/HELPERS/BasicTable.tsx @@ -0,0 +1,52 @@ +import React from 'react'; + +import { + Cell, + flexRender, + HeaderCell, + HeaderRow, + LeafyGreenTable, + LGRowData, + Row, + Table, + TableBody, + TableHead, +} from '@leafygreen-ui/table'; + +interface BasicTableProps { + table: LeafyGreenTable; +} + +export const BasicTable = ({ + table, +}: BasicTableProps) => { + return ( + + + {table.getHeaderGroups().map(headerGroup => ( + + {headerGroup.headers.map(header => ( + + {flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ))} + + ))} + + + {table.getRowModel().rows.map(row => ( + + {row.getVisibleCells().map(cell => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ))} + +
+ ); +}; diff --git a/packages/wizard/src/HELPERS/TableHeaderWithSubtitle.tsx b/packages/wizard/src/HELPERS/TableHeaderWithSubtitle.tsx new file mode 100644 index 0000000000..1039a2169b --- /dev/null +++ b/packages/wizard/src/HELPERS/TableHeaderWithSubtitle.tsx @@ -0,0 +1,35 @@ +import React from 'react'; + +import { css } from '@leafygreen-ui/emotion'; +import { useDarkMode } from '@leafygreen-ui/leafygreen-provider'; +import { color } from '@leafygreen-ui/tokens'; + +export const TableHeaderWithSubtitle = ({ + title, + subtitle, +}: { + title: string; + subtitle: string; +}) => { + const { theme } = useDarkMode(); + + return ( +
+
+ {title} +
+
+ {subtitle} +
+
+ ); +}; diff --git a/packages/wizard/src/Wizard/Wizard.tsx b/packages/wizard/src/Wizard/Wizard.tsx index a96c678243..a4f5a0fff2 100644 --- a/packages/wizard/src/Wizard/Wizard.tsx +++ b/packages/wizard/src/Wizard/Wizard.tsx @@ -2,7 +2,6 @@ import React, { useCallback } from 'react'; import { CompoundComponent, - findChild, findChildren, } from '@leafygreen-ui/compound-component'; import { useControlled } from '@leafygreen-ui/hooks'; @@ -12,24 +11,14 @@ import { WizardProvider } from '../WizardContext/WizardContext'; import { WizardFooter } from '../WizardFooter'; import { WizardStep } from '../WizardStep'; -import { wizardContainerStyles } from './Wizard.styles'; import { WizardProps } from './Wizard.types'; export const Wizard = CompoundComponent( - ({ - activeStep: activeStepProp, - onStepChange, - children, - ...rest - }: WizardProps) => { + ({ activeStep: activeStepProp, onStepChange, children }: WizardProps) => { const stepChildren = findChildren( children, WizardSubComponentProperties.Step, ); - const footerChild = findChild( - children, - WizardSubComponentProperties.Footer, - ); // Controlled/Uncontrolled activeStep value const { value: activeStep, updateValue: setActiveStep } = @@ -58,15 +47,9 @@ export const Wizard = CompoundComponent( [setActiveStep, stepChildren.length], ); - // Get the current step to render - const currentStep = stepChildren[activeStep] || null; - return ( -
- {currentStep} - {footerChild} -
+ {stepChildren.map((child, i) => (i === activeStep ? child : null))}
); }, diff --git a/packages/wizard/src/demo/DeleteWizard.stories.tsx b/packages/wizard/src/demo/DeleteWizard.stories.tsx new file mode 100644 index 0000000000..0630ed55f0 --- /dev/null +++ b/packages/wizard/src/demo/DeleteWizard.stories.tsx @@ -0,0 +1,78 @@ +import React from 'react'; + +import { Variant as ButtonVariant } from '@leafygreen-ui/button'; +import { css } from '@leafygreen-ui/emotion'; +import { Icon } from '@leafygreen-ui/icon'; + +import { Wizard } from '../'; + +import { DELETE_PAGE_MAX_WIDTH } from './constants'; +import { RecommendedActions } from './RecommendedActions'; +import { RequiredActions } from './RequiredActions'; + +const stepStyles = css` + overflow: scroll; +`; + +const footerStyles = css` + bottom: 0; +`; +const footerContentStyles = css` + margin-inline: auto; + max-width: ${DELETE_PAGE_MAX_WIDTH}px; +`; + +export default { + title: 'Components/DeleteWizard', + component: Wizard, +}; + +export const DeleteProjectWizard = () => { + return ( +
+ + + + + + + + + , + }} + /> + + +
+ ); +}; diff --git a/packages/wizard/src/demo/DeleteWizardStepContent.types.ts b/packages/wizard/src/demo/DeleteWizardStepContent.types.ts new file mode 100644 index 0000000000..3dcd23405d --- /dev/null +++ b/packages/wizard/src/demo/DeleteWizardStepContent.types.ts @@ -0,0 +1 @@ +export interface DeleteWizardStepContentProps {} diff --git a/packages/wizard/src/demo/RecommendedActionCard.tsx b/packages/wizard/src/demo/RecommendedActionCard.tsx new file mode 100644 index 0000000000..d76a453f96 --- /dev/null +++ b/packages/wizard/src/demo/RecommendedActionCard.tsx @@ -0,0 +1,44 @@ +import React, { ReactNode } from 'react'; + +import { Badge } from '@leafygreen-ui/badge'; +import { Card } from '@leafygreen-ui/card'; +import { css } from '@leafygreen-ui/emotion'; +import { BaseFontSize, spacing } from '@leafygreen-ui/tokens'; +import { Body, Description } from '@leafygreen-ui/typography'; + +export interface RecommendedActionCardProps { + category: string; + title: string; + description: string; + link: ReactNode; +} + +export const RecommendedActionCard = ({ + category, + title, + description, + link, +}: RecommendedActionCardProps) => { + return ( + + {category} + + {title} + + + {description} + + {link} + + ); +}; diff --git a/packages/wizard/src/demo/RecommendedActions.tsx b/packages/wizard/src/demo/RecommendedActions.tsx new file mode 100644 index 0000000000..415b5f3d7e --- /dev/null +++ b/packages/wizard/src/demo/RecommendedActions.tsx @@ -0,0 +1,69 @@ +import React from 'react'; + +import { Checkbox } from '@leafygreen-ui/checkbox'; +import { css } from '@leafygreen-ui/emotion'; +import { spacing } from '@leafygreen-ui/tokens'; +import { Body, Description, H3 } from '@leafygreen-ui/typography'; + +import { useWizardStepContext } from '../'; + +import { RecommendedActionCard } from './RecommendedActionCard'; +import { recommendedActionsConfig } from './recommendedActionData'; + +export const RecommendedActions = () => { + const { isAcknowledged, setAcknowledged } = useWizardStepContext(); + return ( + <> +

Recommended Actions

+ + If you delete a project, the action is irreversible and permanently + deletes all data. We strongly recommend that you complete the following + data governance checks before you proceed. + + + + ); +}; diff --git a/packages/wizard/src/demo/RequiredActionCards/ApplicationsCard.tsx b/packages/wizard/src/demo/RequiredActionCards/ApplicationsCard.tsx new file mode 100644 index 0000000000..e04f57bfb9 --- /dev/null +++ b/packages/wizard/src/demo/RequiredActionCards/ApplicationsCard.tsx @@ -0,0 +1,73 @@ +import React from 'react'; + +import { + LGColumnDef, + LGTableDataType, + useLeafyGreenTable, +} from '@leafygreen-ui/table'; +import { Description, Link } from '@leafygreen-ui/typography'; + +import { BasicTable } from '../../HELPERS/BasicTable'; +import { useFetchRequiredActionTableData } from '../hooks/useFetchRequiredActionTableData'; + +import { + InheritedRequiredActionCardProps, + RequiredActionCard, + TitleEm, +} from './RequiredActionCard'; + +interface ApplicationsTableData { + name: string; +} + +const applicationsColumns: Array> = [ + { accessorKey: 'name', header: 'Applications' }, +]; + +const demoApplicationsData: Array> = [ + { name: 'Application-1' }, + { name: 'Application-2' }, + { name: 'Application-3' }, +]; + +export const ApplicationsCard = ({ + ...props +}: InheritedRequiredActionCardProps) => { + const { isLoading, tableData } = useFetchRequiredActionTableData({ + demoData: demoApplicationsData, + }); + + const table = useLeafyGreenTable({ + data: tableData, + columns: applicationsColumns, + }); + + return ( + + Delete {tableData?.length} applications + + } + description={ + + All applications will be deleted upon project deletion. Review the + applications below or go to the{' '} + e.stopPropagation()} + > + Apps + {' '} + page. + + } + {...props} + > + + + ); +}; diff --git a/packages/wizard/src/demo/RequiredActionCards/ClustersCard.tsx b/packages/wizard/src/demo/RequiredActionCards/ClustersCard.tsx new file mode 100644 index 0000000000..afe03c50da --- /dev/null +++ b/packages/wizard/src/demo/RequiredActionCards/ClustersCard.tsx @@ -0,0 +1,234 @@ +import React, { ComponentType } from 'react'; + +import { Badge, Variant as BadgeVariant } from '@leafygreen-ui/badge'; +import { Button } from '@leafygreen-ui/button'; +import { css } from '@leafygreen-ui/emotion'; +import ReplicaSet from '@leafygreen-ui/icon/ReplicaSet'; +import ShardedCluster from '@leafygreen-ui/icon/ShardedCluster'; +import { useDarkMode } from '@leafygreen-ui/leafygreen-provider'; +import { TableSkeleton } from '@leafygreen-ui/skeleton-loader'; +import { + LGColumnDef, + LGTableDataType, + useLeafyGreenTable, +} from '@leafygreen-ui/table'; +import { color, spacing } from '@leafygreen-ui/tokens'; +import { Body, Description, Link } from '@leafygreen-ui/typography'; + +import { BasicTable } from '../../HELPERS/BasicTable'; +import { useFetchRequiredActionTableData } from '../hooks/useFetchRequiredActionTableData'; + +import { + InheritedRequiredActionCardProps, + RequiredActionCard, + TitleEm, +} from './RequiredActionCard'; + +// TODO: This is likely defined somewhere in MMS +type ClusterTier = 'Free' | 'Flex' | 'Dedicated'; +type ClusterType = 'Shard' | 'ReplicaSet'; + +const TierToVariantMap: Record = { + Free: BadgeVariant.LightGray, + Flex: BadgeVariant.Yellow, + Dedicated: BadgeVariant.Blue, +}; + +const TypeToIconMap: Record = { + Shard: ShardedCluster, + ReplicaSet: ReplicaSet, +}; + +interface ClusterTableData { + id: string; + name: string; + tier: ClusterTier; + clusterType: ClusterType; + version: string; + region: string; + backup: boolean; +} + +const clustersColumns: Array> = [ + { + accessorKey: 'name', + header: 'Cluster Name', + minSize: 384, + cell: info => { + const clusterType = info.row.original.clusterType; + const TypeIcon = TypeToIconMap[clusterType]; + const name = info.getValue() as string; + return ( +
+ + <>{name} +
+ ); + }, + }, + { + accessorKey: 'tier', + header: 'Tier', + maxSize: 96, + cell: info => { + const val: ClusterTier = info.getValue() as ClusterTier; + return {val}; + }, + }, + { accessorKey: 'version', header: 'Version', maxSize: 64 }, + { accessorKey: 'region', header: 'Region', minSize: 128 }, + { + accessorKey: 'backup', + header: 'Backup', + maxSize: 64, + cell: info => { + const backupEnabled: boolean = info.getValue() as boolean; + // eslint-disable-next-line react-hooks/rules-of-hooks + const { theme } = useDarkMode(); + return backupEnabled ? ( + + ON + + ) : ( + OFF + ); + }, + }, + { + id: 'downloadBackup', + accessorKey: 'backup', + header: () => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const { theme } = useDarkMode(); + return ( + e.stopPropagation()} + className={css` + color: ${color[theme].text.primary.default}; + font-weight: 600; + + &, + &:hover { + // Emulating a LG Inline definition + text-decoration: underline dotted 2px; + text-underline-offset: 0.125em; + } + + & > svg { + color: ${color[theme].text.link.default}; + } + `} + > + Download Backup + + ); + }, + maxSize: 96, + cell: info => { + const backupEnabled: boolean = info.getValue() as boolean; + // TODO: What does this download button do? + return ( + + ); + }, + }, +]; + +const demoClustersData: Array> = [ + { + id: 'abc123', + name: 'Cluster1', + clusterType: 'Shard', + tier: 'Dedicated', + version: '8.0.12', + region: 'AWS / N. Virginia (us-east-1)', + backup: true, + }, + { + id: 'xyz789', + name: 'PUXFest2025', + clusterType: 'ReplicaSet', + tier: 'Free', + version: '8.0.12', + region: 'GCP / Iowa (us-central1)', + backup: false, + }, + { + id: '456lmnop', + name: 'PUX Design Strategy', + clusterType: 'ReplicaSet', + tier: 'Flex', + version: '7.0.22', + region: 'AWS / Oregon (us-west-2)', + backup: false, + }, +]; + +export const ClustersCard = ({ + ...props +}: InheritedRequiredActionCardProps) => { + const { isLoading, tableData } = useFetchRequiredActionTableData({ + demoData: demoClustersData, + }); + + const table = useLeafyGreenTable({ + data: tableData, + columns: clustersColumns, + }); + + return ( + + Terminate {tableData?.length} clusters + + } + description={ + + All clusters will be terminated upon project deletion. Review the + clusters below or go to the{' '} + e.stopPropagation()} + > + Clusters + {' '} + page. + + } + {...props} + > + {isLoading ? ( + ( + <> + {typeof col.header === 'function' + ? col.header({} as any) + : col.header ?? ''} + + ))} + /> + ) : ( + + )} + + ); +}; diff --git a/packages/wizard/src/demo/RequiredActionCards/FederatedDbCard.tsx b/packages/wizard/src/demo/RequiredActionCards/FederatedDbCard.tsx new file mode 100644 index 0000000000..cbfa5f24bd --- /dev/null +++ b/packages/wizard/src/demo/RequiredActionCards/FederatedDbCard.tsx @@ -0,0 +1,141 @@ +import React from 'react'; +import { format } from 'date-fns'; + +import { LGColumnDef, useLeafyGreenTable } from '@leafygreen-ui/table'; +import { Description, Link } from '@leafygreen-ui/typography'; + +import { BasicTable } from '../../HELPERS/BasicTable'; +import { TableHeaderWithSubtitle } from '../../HELPERS/TableHeaderWithSubtitle'; +import { useFetchRequiredActionTableData } from '../hooks/useFetchRequiredActionTableData'; + +import { + InheritedRequiredActionCardProps, + RequiredActionCard, + TitleEm, +} from './RequiredActionCard'; + +type FederatedDBType = 'Atlas SQL' | 'Online Archive' | 'Federated Instance'; + +interface FederatedDBTableData { + name: string; + type: FederatedDBType; + region: string; + queriesExecuted: number; + dataProcessed: string; + dataReturned: string; + dateCreated: Date; +} + +const federatedDbColumns: Array> = [ + { accessorKey: 'name', header: 'Federated database instances' }, + { accessorKey: 'type', header: 'Type', minSize: 96 }, + { accessorKey: 'region', header: 'Cloud provider & region', minSize: 196 }, + { + accessorKey: 'queriesExecuted', + header: () => ( + + ), + }, + { + accessorKey: 'dataProcessed', + header: () => ( + + ), + }, + { + accessorKey: 'dataReturned', + header: () => ( + + ), + }, + { + accessorKey: 'dateCreated', + header: 'Date created', + cell: info => { + // TODO: Use consistent, localized Time-in-Atlas format + return format(info.getValue() as Date, 'yyyy-MM-dd HH:MM'); + }, + }, +]; + +const demoFederatedDbData: Array = [ + { + name: 'FederatedDatabaseInstance0', + type: 'Atlas SQL', + region: 'AWS / N. Virginia (us-east-1)', + queriesExecuted: 5, + dataProcessed: '48 KB', + dataReturned: '64 KB', + dateCreated: new Date(), + }, + { + name: 'FederatedDatabaseInstance1', + type: 'Online Archive', + region: 'AWS / N. Virginia (us-east-1)', + queriesExecuted: 28, + dataProcessed: '48 KB', + dataReturned: '64 KB', + dateCreated: new Date(), + }, + { + name: 'FederatedDatabaseInstance2', + type: 'Federated Instance', + region: 'AWS / N. Virginia (us-east-1)', + queriesExecuted: 1, + dataProcessed: '48 KB', + dataReturned: '64 KB', + dateCreated: new Date(), + }, + { + name: 'FederatedDatabaseInstance3', + type: 'Federated Instance', + region: 'AWS / N. Virginia (us-east-1)', + queriesExecuted: 7, + dataProcessed: '48 KB', + dataReturned: '64 KB', + dateCreated: new Date(), + }, +]; + +export const FederatedDbCard = ({ + ...props +}: InheritedRequiredActionCardProps) => { + const { isLoading, tableData } = useFetchRequiredActionTableData({ + demoData: demoFederatedDbData, + }); + + const table = useLeafyGreenTable({ + data: tableData, + columns: federatedDbColumns, + }); + + return ( + + Terminate {tableData?.length} federated database + instances + + } + description={ + + All federated database instances will be terminated upon project + deletion. Review the federated database instances below or go to the{' '} + e.stopPropagation()} + > + Data Federation + {' '} + page. + + } + {...props} + > + + + ); +}; diff --git a/packages/wizard/src/demo/RequiredActionCards/ModelApiKeysCard.tsx b/packages/wizard/src/demo/RequiredActionCards/ModelApiKeysCard.tsx new file mode 100644 index 0000000000..447642b34c --- /dev/null +++ b/packages/wizard/src/demo/RequiredActionCards/ModelApiKeysCard.tsx @@ -0,0 +1,105 @@ +import React from 'react'; +import { format } from 'date-fns'; + +import { + CellContext, + LGColumnDef, + LGTableDataType, + useLeafyGreenTable, +} from '@leafygreen-ui/table'; +import { Description } from '@leafygreen-ui/typography'; + +import { BasicTable } from '../../HELPERS/BasicTable'; +import { useFetchRequiredActionTableData } from '../hooks/useFetchRequiredActionTableData'; + +import { + InheritedRequiredActionCardProps, + RequiredActionCard, + TitleEm, +} from './RequiredActionCard'; + +const DateCell = ( + cellData: CellContext, unknown>, +) => { + const val = cellData.getValue() as Date | undefined; + + if (!val) return <>Never; + + return <>{format(val, 'yyyy-MM-dd')}; +}; + +interface ModelAPIKeyTableData { + keyName: string; + createdDate: Date; + lastUsedDate?: Date; + createdBy: string; +} + +const modelApiKeysColumns: Array> = [ + { accessorKey: 'keyName', header: 'Key Name' }, + { accessorKey: 'key', header: 'Secret key', cell: () => <>******** }, + { + accessorKey: 'createdDate', + header: 'Created on (UTC)', + cell: DateCell, + }, + { + accessorKey: 'lastUsedDate', + header: 'Last used (UTC)', + cell: DateCell, + }, + { accessorKey: 'createdBy', header: 'Created by' }, +]; + +const demoModelApiKeysData: Array> = [ + { + keyName: 'voyage-api-key-1', + createdDate: new Date('2019-04-22'), + lastUsedDate: new Date(), + createdBy: 'Mike Waltzer', + }, + { + keyName: 'voyage-api-key-2', + createdDate: new Date('2022-08-29'), + lastUsedDate: new Date(), + createdBy: 'Lauren Fox', + }, + { + keyName: 'voyage-api-key-3', + createdDate: new Date('2021-06-07'), + createdBy: 'Adam Thompson', + }, +]; + +export const ModelApiKeysCard = ({ + ...props +}: InheritedRequiredActionCardProps) => { + const { isLoading, tableData } = useFetchRequiredActionTableData({ + demoData: demoModelApiKeysData, + }); + + const table = useLeafyGreenTable({ + data: tableData, + columns: modelApiKeysColumns, + }); + + return ( + + Delete {tableData?.length} Model API keys + + } + description={ + + All Model API keys will be deleted upon project deletion. + + } + {...props} + > + + + ); +}; diff --git a/packages/wizard/src/demo/RequiredActionCards/PrivateEndpointsCard.tsx b/packages/wizard/src/demo/RequiredActionCards/PrivateEndpointsCard.tsx new file mode 100644 index 0000000000..f8003183b8 --- /dev/null +++ b/packages/wizard/src/demo/RequiredActionCards/PrivateEndpointsCard.tsx @@ -0,0 +1,348 @@ +import React, { useState } from 'react'; +import upperFirst from 'lodash/upperFirst'; + +import { css } from '@leafygreen-ui/emotion'; +import Circle from '@leafygreen-ui/icon/Circle'; +import { useDarkMode } from '@leafygreen-ui/leafygreen-provider'; +import { Theme } from '@leafygreen-ui/lib'; +import { + SegmentedControl, + SegmentedControlOption, +} from '@leafygreen-ui/segmented-control'; +import { + CellContext, + LGColumnDef, + LGTableDataType, + useLeafyGreenTable, +} from '@leafygreen-ui/table'; +import { color, spacing } from '@leafygreen-ui/tokens'; +import { Description, Link } from '@leafygreen-ui/typography'; + +import { BasicTable } from '../../HELPERS/BasicTable'; +import { useFetchRequiredActionTableData } from '../hooks/useFetchRequiredActionTableData'; + +import { + InheritedRequiredActionCardProps, + RequiredActionCard, + TitleEm, +} from './RequiredActionCard'; + +const ServiceStatus = { + Available: 'available', +} as const; +type ServiceStatus = (typeof ServiceStatus)[keyof typeof ServiceStatus]; + +const ConfigurationStatus = { + Configured: 'configured', +} as const; +type ConfigurationStatus = + (typeof ConfigurationStatus)[keyof typeof ConfigurationStatus]; + +const statusToColorMap: Record< + ServiceStatus | ConfigurationStatus, + Record +> = { + [ServiceStatus.Available]: { + [Theme.Light]: color.light.icon.success.default, + [Theme.Dark]: color.dark.icon.success.default, + }, + [ConfigurationStatus.Configured]: { + [Theme.Light]: color.light.icon.success.default, + [Theme.Dark]: color.dark.icon.success.default, + }, +}; + +const StatusCell = < + D extends + | DedicatedClusterPrivateEndpointTableData + | FederatedDBInstancePrivateEndpointTableData + | StreamProcessingPrivateEndpointTableData, +>( + cellData: CellContext, unknown>, +) => { + const { theme } = useDarkMode(); + const value = cellData.getValue() as ServiceStatus | ConfigurationStatus; + const dotColor = statusToColorMap[value][theme]; + return ( + <> + + {upperFirst(value)} + + ); +}; + +const PrivateEndpointType = { + Dedicated: 'dedicated', + Federated: 'federated', + StreamProcessing: 'stream-processing', +} as const; +type PrivateEndpointType = + (typeof PrivateEndpointType)[keyof typeof PrivateEndpointType]; + +interface DedicatedClusterPrivateEndpointTableData { + id: string; + provider: string; + region: string; + endpointService: string; + endpointStatus: ServiceStatus; + configurationStatus: ConfigurationStatus; +} + +const dedicatedClusterPrivateEndpointsColumns: Array< + LGColumnDef +> = [ + { accessorKey: 'provider', header: 'Cloud Provider', maxSize: 96 }, + { accessorKey: 'region', header: 'Region', maxSize: 96 }, + { accessorKey: 'endpointService', header: 'Atlas Endpoint Service' }, + { + accessorKey: 'endpointStatus', + header: 'Atlas Endpoint Service Status', + cell: StatusCell, + }, + { accessorKey: 'id', header: 'Endpoint' }, + { + accessorKey: 'configurationStatus', + header: 'Endpoint Status', + maxSize: 112, + cell: StatusCell, + }, +]; + +const demoDedicatedClusterPrivateEndpointsData: Array< + LGTableDataType +> = [ + { + id: 'endpoint-1', + provider: 'AWS', + region: 'us-east-1', + endpointService: 'com.amazonaws.vpce.us-east-1.vpce-svc-054161d3958725abb', + endpointStatus: 'available', + configurationStatus: 'configured', + }, + { + id: 'endpoint-2', + provider: 'GCP', + region: 'us-east-1', + endpointService: 'com.amazonaws.vpce.us-east-1.vpce-svc-054161d3958725abb', + endpointStatus: 'available', + configurationStatus: 'configured', + }, + { + id: 'endpoint-3', + provider: 'AWS', + region: 'us-central1', + endpointService: 'com.amazonaws.vpce.us-east-1.vpce-svc-054161d3958725abb', + endpointStatus: 'available', + configurationStatus: 'configured', + }, +]; + +interface FederatedDBInstancePrivateEndpointTableData { + id: string; + provider: string; + endpointStatus: ServiceStatus; + region: string; + comment?: string; +} + +const federatedDBInstancePrivateEndpointsColumns: Array< + LGColumnDef +> = [ + { accessorKey: 'provider', header: 'Cloud Provider', maxSize: 96 }, + { accessorKey: 'region', header: 'Region', maxSize: 96 }, + { + accessorKey: 'endpointStatus', + header: 'Endpoint Status', + maxSize: 96, + cell: StatusCell, + }, + { accessorKey: 'id', header: 'Endpoint' }, + { + accessorKey: 'comment', + header: 'Comment', + cell: info => { + const val = info.getValue() as string | undefined; + + if (!val || val.length === 0) { + return '—'; + } + + return val; + }, + }, +]; + +const demoFederatedDBInstancePrivateEndpointsData: Array< + LGTableDataType +> = [ + { + id: 'vpce-0b9c5701325cb07e6', + provider: 'AWS', + endpointStatus: 'available', + region: 'us-east-1', + comment: 'Production endpoint', + }, + { + id: 'vpce-1a2b3c4d5e6f7g8h9', + provider: 'AWS', + endpointStatus: 'available', + region: 'us-west-2', + }, +]; + +interface StreamProcessingPrivateEndpointTableData { + provider: string; + vendor: string; + endpoint: string; + endpointStatus: ServiceStatus; + accountId: string; +} + +const streamProcessingPrivateEndpointsColumns: Array< + LGColumnDef +> = [ + { accessorKey: 'provider', header: 'Cloud Provider', maxSize: 96 }, + { accessorKey: 'vendor', header: 'Vendor', maxSize: 96 }, + { accessorKey: 'endpoint', header: 'Endpoint' }, + { + accessorKey: 'endpointStatus', + header: 'Endpoint Status', + maxSize: 112, + cell: StatusCell, + }, + { accessorKey: 'accountId', header: 'Account ID' }, +]; + +const demoStreamProcessingPrivateEndpointsData: Array< + LGTableDataType +> = [ + { + provider: 'AWS', + vendor: 'S3', + endpoint: 'vpce-0a1b2c3d4e5f6g7h8', + endpointStatus: 'available', + accountId: '123456789012', + }, + { + provider: 'AWS', + vendor: 'Confluent', + endpoint: 'vpce-9h8g7f6e5d4c3b2a1', + endpointStatus: 'available', + accountId: '123456789012', + }, +]; + +export const PrivateEndpointsCard = ({ + ...props +}: InheritedRequiredActionCardProps) => { + const [segCtrlValue, setSegCtrlValue] = useState( + PrivateEndpointType.Dedicated, + ); + + const { + isLoading: isDedicatedClustersLoading, + tableData: dedicatedClusterTableData, + } = useFetchRequiredActionTableData({ + demoData: demoDedicatedClusterPrivateEndpointsData, + }); + + const { isLoading: isFederatedDBLoading, tableData: federatedDBTableData } = + useFetchRequiredActionTableData({ + demoData: demoFederatedDBInstancePrivateEndpointsData, + }); + + const { + isLoading: isStreamProcessingLoading, + tableData: streamProcessingTableData, + } = useFetchRequiredActionTableData({ + demoData: demoStreamProcessingPrivateEndpointsData, + }); + + const dedicatedClusterTable = useLeafyGreenTable({ + data: dedicatedClusterTableData, + columns: dedicatedClusterPrivateEndpointsColumns, + }); + + const federatedDBTable = useLeafyGreenTable({ + data: federatedDBTableData, + columns: federatedDBInstancePrivateEndpointsColumns, + }); + + const streamProcessingTable = useLeafyGreenTable({ + data: streamProcessingTableData, + columns: streamProcessingPrivateEndpointsColumns, + }); + + const isLoading = + isDedicatedClustersLoading || + isFederatedDBLoading || + isStreamProcessingLoading; + + const totalEndpoints = + dedicatedClusterTableData?.length + + federatedDBTableData?.length + + streamProcessingTableData?.length; + + return ( + + Remove {totalEndpoints} private endpoint + connections + + } + description={ + + All private endpoint connections will be removed upon project + deletion. Review the private endpoint connections below or go to the{' '} + + Network Access + + + } + {...props} + > + {isLoading ? null : ( + <> + setSegCtrlValue(val as PrivateEndpointType)} + className={css` + margin: ${spacing[200]}px ${spacing[600]}px; + `} + > + + Dedicated Cluster + + + Federated database Instance / Online Archive + + + Atlas stream processing + + + + {segCtrlValue === PrivateEndpointType.Dedicated && ( + + )} + {segCtrlValue === PrivateEndpointType.Federated && ( + + )} + {segCtrlValue === PrivateEndpointType.StreamProcessing && ( + + )} + + )} + + ); +}; diff --git a/packages/wizard/src/demo/RequiredActionCards/RequiredActionCard.tsx b/packages/wizard/src/demo/RequiredActionCards/RequiredActionCard.tsx new file mode 100644 index 0000000000..ca7e30731a --- /dev/null +++ b/packages/wizard/src/demo/RequiredActionCards/RequiredActionCard.tsx @@ -0,0 +1,97 @@ +import React, { PropsWithChildren, ReactNode } from 'react'; + +import { css } from '@leafygreen-ui/emotion'; +import { + ExpandableCard, + ExpandableCardProps, +} from '@leafygreen-ui/expandable-card'; +import { useDarkMode } from '@leafygreen-ui/leafygreen-provider'; +import { + Size as SpinnerSize, + Spinner, +} from '@leafygreen-ui/loading-indicator/spinner'; +import { Skeleton } from '@leafygreen-ui/skeleton-loader'; +import { color, spacing } from '@leafygreen-ui/tokens'; + +const requiredActionCardStyles = css` + & h6 { + display: block; + } +`; + +const expandableCardContentStyles = css` + padding: unset; +`; + +const cardContentWrapperStyles = css` + padding-bottom: ${spacing[400]}px; + overflow: hidden; +`; + +export interface RequiredActionCardProps extends ExpandableCardProps { + isLoading?: boolean; + loadingTitle?: ReactNode; + loadingDescription?: ReactNode; +} + +export interface InheritedRequiredActionCardProps + extends Omit {} + +export const TitleEm = ({ children }: PropsWithChildren<{}>) => { + const { theme } = useDarkMode(); + return ( + + {children} + + ); +}; + +export const RequiredActionCard = ({ + title, + description, + isLoading, + children, + loadingTitle = ( + + ), + loadingDescription = 'This may take a few moments', + ...rest +}: RequiredActionCardProps) => { + return ( + + + {loadingDescription} + + ) : ( + description + ) + } + className={requiredActionCardStyles} + contentClassName={expandableCardContentStyles} + defaultOpen={false} + {...rest} + > +
{children}
+
+ ); +}; diff --git a/packages/wizard/src/demo/RequiredActionCards/StreamProcessingCard.tsx b/packages/wizard/src/demo/RequiredActionCards/StreamProcessingCard.tsx new file mode 100644 index 0000000000..57698b5004 --- /dev/null +++ b/packages/wizard/src/demo/RequiredActionCards/StreamProcessingCard.tsx @@ -0,0 +1,110 @@ +import React from 'react'; + +import { LGTableDataType, useLeafyGreenTable } from '@leafygreen-ui/table'; +import { Description, Link } from '@leafygreen-ui/typography'; + +import { BasicTable } from '../../HELPERS/BasicTable'; +import { TableHeaderWithSubtitle } from '../../HELPERS/TableHeaderWithSubtitle'; +import { useFetchRequiredActionTableData } from '../hooks/useFetchRequiredActionTableData'; + +import { + InheritedRequiredActionCardProps, + RequiredActionCard, + TitleEm, +} from './RequiredActionCard'; + +interface StreamProcessingTableData { + name: string; + region: string; + started: number; + stopped: number; + failed: number; +} + +const streamProcessingColumns = [ + { accessorKey: 'name', header: 'Workspace name' }, + { accessorKey: 'region', header: 'Region', maxSize: 180 }, + { + accessorKey: 'started', + maxSize: 96, + header: () => ( + + ), + }, + { + accessorKey: 'stopped', + maxSize: 96, + header: () => ( + + ), + }, + { + accessorKey: 'failed', + maxSize: 96, + header: () => ( + + ), + }, +]; + +const demoStreamProcessingData: Array< + LGTableDataType +> = [ + { + name: 'Workspace-1', + region: 'us-east-1', + started: 1, + stopped: 2, + failed: 0, + }, + { + name: 'Workspace-2', + region: 'us-west-2', + started: 1, + stopped: 2, + failed: 0, + }, +]; + +export const StreamProcessingCard = ({ + ...props +}: InheritedRequiredActionCardProps) => { + const { isLoading, tableData } = useFetchRequiredActionTableData({ + demoData: demoStreamProcessingData, + }); + + const table = useLeafyGreenTable({ + data: tableData, + columns: streamProcessingColumns, + }); + + return ( + + Terminate {tableData?.length} stream processing + workspaces + + } + description={ + + All Stream Processing workspaces will be terminated upon project + deletion. Review the workspaces below or go to the{' '} + e.stopPropagation()} + > + Stream Processing + {' '} + page. + + } + {...props} + > + + + ); +}; diff --git a/packages/wizard/src/demo/RequiredActionCards/index.ts b/packages/wizard/src/demo/RequiredActionCards/index.ts new file mode 100644 index 0000000000..70d2b2bd5d --- /dev/null +++ b/packages/wizard/src/demo/RequiredActionCards/index.ts @@ -0,0 +1,6 @@ +export { ApplicationsCard } from './ApplicationsCard'; +export { ClustersCard } from './ClustersCard'; +export { FederatedDbCard } from './FederatedDbCard'; +export { ModelApiKeysCard } from './ModelApiKeysCard'; +export { PrivateEndpointsCard } from './PrivateEndpointsCard'; +export { StreamProcessingCard } from './StreamProcessingCard'; diff --git a/packages/wizard/src/demo/RequiredActions.tsx b/packages/wizard/src/demo/RequiredActions.tsx new file mode 100644 index 0000000000..10262be135 --- /dev/null +++ b/packages/wizard/src/demo/RequiredActions.tsx @@ -0,0 +1,96 @@ +import React from 'react'; + +import { Checkbox } from '@leafygreen-ui/checkbox'; +import { css } from '@leafygreen-ui/emotion'; +import { spacing } from '@leafygreen-ui/tokens'; +import { Body, Description, H3, Link } from '@leafygreen-ui/typography'; + +import { useRequiredActionAcknowledgements } from './hooks/useRequiredActionAcknowledgements'; +import { + ApplicationsCard, + ClustersCard, + FederatedDbCard, + ModelApiKeysCard, + PrivateEndpointsCard, + StreamProcessingCard, +} from './RequiredActionCards'; + +const reviewCardsContainerStyles = css` + display: flex; + flex-direction: column; + gap: ${spacing[3]}px; + margin-block: ${spacing[4]}px; +`; + +const checkboxContainerStyles = css` + display: flex; + flex-direction: column; + gap: ${spacing[3]}px; + margin-top: ${spacing[3]}px; +`; + +const acknowledgmentLabelStyles = css` + margin-bottom: ${spacing[3]}px; +`; + +const sectionDividerStyles = css` + border-top: 1px solid #e8edeb; + margin-block: ${spacing[5]}px; +`; + +export const RequiredActions = () => { + const { acknowledgementsState, setAcknowledgementState } = + useRequiredActionAcknowledgements(); + + return ( + <> +

Required Actions

+ + Access to all of the following deployments and associated data will be + permanently lost and cannot be recovered.{' '} + Manage Project Access Docs + + +
+ + + + + + +
+ +
+ + + By deleting the project, you acknowledge this irreversible action: + + +
+ setAcknowledgementState(0, e.target.checked)} + /> + + setAcknowledgementState(1, e.target.checked)} + /> + + setAcknowledgementState(2, e.target.checked)} + /> +
+ + ); +}; diff --git a/packages/wizard/src/demo/constants.ts b/packages/wizard/src/demo/constants.ts new file mode 100644 index 0000000000..ce523186ac --- /dev/null +++ b/packages/wizard/src/demo/constants.ts @@ -0,0 +1,3 @@ +import { breakpoints } from '@leafygreen-ui/tokens'; + +export const DELETE_PAGE_MAX_WIDTH = breakpoints.XLDesktop; diff --git a/packages/wizard/src/demo/hooks/useFetchRequiredActionTableData.ts b/packages/wizard/src/demo/hooks/useFetchRequiredActionTableData.ts new file mode 100644 index 0000000000..6627809b2f --- /dev/null +++ b/packages/wizard/src/demo/hooks/useFetchRequiredActionTableData.ts @@ -0,0 +1,41 @@ +import { Dispatch, SetStateAction, useEffect, useState } from 'react'; + +import { LGRowData, LGTableDataType } from '@leafygreen-ui/table'; + +interface FetchRequiredActionTableDataArgs { + demoData: Array>; + _demoDelay?: number; +} + +interface FetchRequiredActionTableDataReturnType { + tableData: Array>; + setTableData: Dispatch>>>; + isLoading: boolean; +} + +export const useFetchRequiredActionTableData = ({ + demoData, + _demoDelay, +}: FetchRequiredActionTableDataArgs): FetchRequiredActionTableDataReturnType => { + const [isLoading, setLoading] = useState(false); + const [tableData, setTableData] = useState>>([]); + + useEffect(() => { + if (_demoDelay === 0) { + setTableData(demoData); + return; + } + + setLoading(true); + setTimeout(() => { + setLoading(false); + setTableData(demoData); + }, _demoDelay ?? 500 + 5000 * Math.random()); + }, [_demoDelay, demoData]); + + return { + isLoading, + tableData, + setTableData, + }; +}; diff --git a/packages/wizard/src/demo/hooks/useRequiredActionAcknowledgements.ts b/packages/wizard/src/demo/hooks/useRequiredActionAcknowledgements.ts new file mode 100644 index 0000000000..b99d952ab8 --- /dev/null +++ b/packages/wizard/src/demo/hooks/useRequiredActionAcknowledgements.ts @@ -0,0 +1,44 @@ +import { Reducer, useReducer } from 'react'; + +import { useWizardStepContext } from '../../WizardStep'; + +export type RequiredActionsAcknowledgementsState = [boolean, boolean, boolean]; + +export interface RequiredActionsAcknowledgementsAction { + index: 0 | 1 | 2; + value: boolean; +} + +export const useRequiredActionAcknowledgements = () => { + const { isAcknowledged, setAcknowledged } = useWizardStepContext(); + + const [acknowledgementsState, dispatch] = useReducer< + Reducer< + RequiredActionsAcknowledgementsState, + RequiredActionsAcknowledgementsAction + > + >( + (state, action) => { + const newState: RequiredActionsAcknowledgementsState = [...state]; + newState[action.index] = action.value; + + if (newState.every(Boolean)) { + setAcknowledged(true); + } else { + setAcknowledged(false); + } + + return newState; + }, + [false, false, false], + ); + + const setAcknowledgementState = (index: 0 | 1 | 2, value: boolean) => + dispatch({ index, value }); + + return { + acknowledgementsState, + setAcknowledgementState, + isAcknowledged, + }; +}; diff --git a/packages/wizard/src/demo/recommendedActionData.tsx b/packages/wizard/src/demo/recommendedActionData.tsx new file mode 100644 index 0000000000..971f25911c --- /dev/null +++ b/packages/wizard/src/demo/recommendedActionData.tsx @@ -0,0 +1,67 @@ +import React from 'react'; + +import { Link } from '@leafygreen-ui/typography'; + +import { RecommendedActionCardProps } from './RecommendedActionCard'; + +export const recommendedActionsConfig: Array = [ + { + category: 'Data Retention', + title: 'Export most recent Backup Snapshot', + description: + 'To safeguard critical information, provide a recovery option to ensure you have access to your data after you delete your project.', + link: ( + + Learn how to export backup snapshots + + ), + }, + + { + category: 'Compliance', + title: 'Export audit logs', + description: + 'Retain a record of essential data regarding changes and actions within your project for compliance, audits, and future reference.', + link: ( + + Learn how to export audit logs + + ), + }, + { + category: 'Auditing', + title: 'Export Project Activity Feed events', + description: + 'Ensure you have access to key details about project changes, actions, and events for audits, compliance, or future reference.', + link: ( + + {' '} + Learn how to return events + + ), + }, + + { + category: 'Security', + title: 'Disconnect third-party integrations', + description: + 'Remove API keys to secure of your data, prevent unauthorized access, and avoid any unintended interactions with external systems.', + link: ( + + Go to integrations + + ), + }, + + { + category: 'Visualizations', + title: 'Export Charts and Dashboards', + description: + 'Retain critical insights and visualizations from dashboards, data sources, and charts associated with your project.', + link: ( + + Export charts dashboards + + ), + }, +]; From b8758f4c025ef75a72c432896ce0655c074bdf72 Mon Sep 17 00:00:00 2001 From: Adam Thompson Date: Fri, 7 Nov 2025 16:51:56 -0500 Subject: [PATCH 05/43] Update pnpm Update package.json --- packages/descendants/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/descendants/package.json b/packages/descendants/package.json index 2f6ce5d968..b360383fac 100644 --- a/packages/descendants/package.json +++ b/packages/descendants/package.json @@ -21,7 +21,7 @@ }, "devDependencies": { "@faker-js/faker": "^8.0.0", - "@storybook/react": "latest", + "@storybook/react": "8.6.14", "@leafygreen-ui/button": "workspace:^", "@leafygreen-ui/emotion": "workspace:^", "@leafygreen-ui/popover": "workspace:^", From 7757e2a322632788e2f6cd745c42fc067597eccb Mon Sep 17 00:00:00 2001 From: Adam Thompson Date: Fri, 14 Nov 2025 11:54:42 -0500 Subject: [PATCH 06/43] fix wizard changes --- .changeset/wizard-patch-3.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wizard-patch-3.md diff --git a/.changeset/wizard-patch-3.md b/.changeset/wizard-patch-3.md new file mode 100644 index 0000000000..4a924c356f --- /dev/null +++ b/.changeset/wizard-patch-3.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/wizard': patch +--- + +Exports WizardProvider From aef3a304494b55caa6318eaf2f71ceff18b067f2 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 11:31:13 -0500 Subject: [PATCH 07/43] Adds `requiresAcknowledgement` prop to Wizard.Step --- .changeset/wizard-patch-5.md | 5 +++++ .changeset/wizard-patch-6.md | 5 +++++ packages/wizard/src/Wizard/Wizard.tsx | 11 +++++++++++ 3 files changed, 21 insertions(+) create mode 100644 .changeset/wizard-patch-5.md create mode 100644 .changeset/wizard-patch-6.md diff --git a/.changeset/wizard-patch-5.md b/.changeset/wizard-patch-5.md new file mode 100644 index 0000000000..053bd3ac44 --- /dev/null +++ b/.changeset/wizard-patch-5.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/wizard': patch +--- + +Exports `WizardSubComponentProperties` \ No newline at end of file diff --git a/.changeset/wizard-patch-6.md b/.changeset/wizard-patch-6.md new file mode 100644 index 0000000000..8b8f8e6398 --- /dev/null +++ b/.changeset/wizard-patch-6.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/wizard': patch +--- + +Adds `requiresAcknowledgement` prop to `Wizard.Step` \ No newline at end of file diff --git a/packages/wizard/src/Wizard/Wizard.tsx b/packages/wizard/src/Wizard/Wizard.tsx index a4f5a0fff2..8a9c25f377 100644 --- a/packages/wizard/src/Wizard/Wizard.tsx +++ b/packages/wizard/src/Wizard/Wizard.tsx @@ -55,7 +55,18 @@ export const Wizard = CompoundComponent( }, { displayName: 'Wizard', + /** + * A single step in the wizard. A Wizard will only render Steps as children + */ Step: WizardStep, + + /** + * The footer of a Step component. + * Render this inside of each Step with the relevant button props for that Step. + * + * Back and Primary buttons trigger onStepChange. + * Automatically renders the "Back" button for all Steps except the first + */ Footer: WizardFooter, }, ); From 76920c55c06f3a9ec3a9ffe2dfdf0fb02de8769a Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 12:48:42 -0500 Subject: [PATCH 08/43] Implements `isAcknowledged` state inside provider --- .changeset/wizard-patch-7.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wizard-patch-7.md diff --git a/.changeset/wizard-patch-7.md b/.changeset/wizard-patch-7.md new file mode 100644 index 0000000000..57aa375db8 --- /dev/null +++ b/.changeset/wizard-patch-7.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/wizard': patch +--- + +Implements `isAcknowledged` state inside provider \ No newline at end of file From 674a76ba552db0795a3a873631ff6b5c7baf46e4 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 15:20:18 -0500 Subject: [PATCH 09/43] rm delete demo --- packages/wizard/src/HELPERS/BasicTable.tsx | 52 --- .../src/HELPERS/TableHeaderWithSubtitle.tsx | 35 -- .../wizard/src/demo/DeleteWizard.stories.tsx | 78 ---- .../src/demo/DeleteWizardStepContent.types.ts | 1 - .../wizard/src/demo/RecommendedActionCard.tsx | 44 --- .../wizard/src/demo/RecommendedActions.tsx | 69 ---- .../RequiredActionCards/ApplicationsCard.tsx | 73 ---- .../demo/RequiredActionCards/ClustersCard.tsx | 234 ------------ .../RequiredActionCards/FederatedDbCard.tsx | 141 ------- .../RequiredActionCards/ModelApiKeysCard.tsx | 105 ------ .../PrivateEndpointsCard.tsx | 348 ------------------ .../RequiredActionCard.tsx | 97 ----- .../StreamProcessingCard.tsx | 110 ------ .../src/demo/RequiredActionCards/index.ts | 6 - packages/wizard/src/demo/RequiredActions.tsx | 96 ----- packages/wizard/src/demo/constants.ts | 3 - .../hooks/useFetchRequiredActionTableData.ts | 41 --- .../useRequiredActionAcknowledgements.ts | 44 --- .../wizard/src/demo/recommendedActionData.tsx | 67 ---- 19 files changed, 1644 deletions(-) delete mode 100644 packages/wizard/src/HELPERS/BasicTable.tsx delete mode 100644 packages/wizard/src/HELPERS/TableHeaderWithSubtitle.tsx delete mode 100644 packages/wizard/src/demo/DeleteWizard.stories.tsx delete mode 100644 packages/wizard/src/demo/DeleteWizardStepContent.types.ts delete mode 100644 packages/wizard/src/demo/RecommendedActionCard.tsx delete mode 100644 packages/wizard/src/demo/RecommendedActions.tsx delete mode 100644 packages/wizard/src/demo/RequiredActionCards/ApplicationsCard.tsx delete mode 100644 packages/wizard/src/demo/RequiredActionCards/ClustersCard.tsx delete mode 100644 packages/wizard/src/demo/RequiredActionCards/FederatedDbCard.tsx delete mode 100644 packages/wizard/src/demo/RequiredActionCards/ModelApiKeysCard.tsx delete mode 100644 packages/wizard/src/demo/RequiredActionCards/PrivateEndpointsCard.tsx delete mode 100644 packages/wizard/src/demo/RequiredActionCards/RequiredActionCard.tsx delete mode 100644 packages/wizard/src/demo/RequiredActionCards/StreamProcessingCard.tsx delete mode 100644 packages/wizard/src/demo/RequiredActionCards/index.ts delete mode 100644 packages/wizard/src/demo/RequiredActions.tsx delete mode 100644 packages/wizard/src/demo/constants.ts delete mode 100644 packages/wizard/src/demo/hooks/useFetchRequiredActionTableData.ts delete mode 100644 packages/wizard/src/demo/hooks/useRequiredActionAcknowledgements.ts delete mode 100644 packages/wizard/src/demo/recommendedActionData.tsx diff --git a/packages/wizard/src/HELPERS/BasicTable.tsx b/packages/wizard/src/HELPERS/BasicTable.tsx deleted file mode 100644 index 74c7c2cfe4..0000000000 --- a/packages/wizard/src/HELPERS/BasicTable.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; - -import { - Cell, - flexRender, - HeaderCell, - HeaderRow, - LeafyGreenTable, - LGRowData, - Row, - Table, - TableBody, - TableHead, -} from '@leafygreen-ui/table'; - -interface BasicTableProps { - table: LeafyGreenTable; -} - -export const BasicTable = ({ - table, -}: BasicTableProps) => { - return ( - - - {table.getHeaderGroups().map(headerGroup => ( - - {headerGroup.headers.map(header => ( - - {flexRender( - header.column.columnDef.header, - header.getContext(), - )} - - ))} - - ))} - - - {table.getRowModel().rows.map(row => ( - - {row.getVisibleCells().map(cell => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - ))} - -
- ); -}; diff --git a/packages/wizard/src/HELPERS/TableHeaderWithSubtitle.tsx b/packages/wizard/src/HELPERS/TableHeaderWithSubtitle.tsx deleted file mode 100644 index 1039a2169b..0000000000 --- a/packages/wizard/src/HELPERS/TableHeaderWithSubtitle.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; - -import { css } from '@leafygreen-ui/emotion'; -import { useDarkMode } from '@leafygreen-ui/leafygreen-provider'; -import { color } from '@leafygreen-ui/tokens'; - -export const TableHeaderWithSubtitle = ({ - title, - subtitle, -}: { - title: string; - subtitle: string; -}) => { - const { theme } = useDarkMode(); - - return ( -
-
- {title} -
-
- {subtitle} -
-
- ); -}; diff --git a/packages/wizard/src/demo/DeleteWizard.stories.tsx b/packages/wizard/src/demo/DeleteWizard.stories.tsx deleted file mode 100644 index 0630ed55f0..0000000000 --- a/packages/wizard/src/demo/DeleteWizard.stories.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react'; - -import { Variant as ButtonVariant } from '@leafygreen-ui/button'; -import { css } from '@leafygreen-ui/emotion'; -import { Icon } from '@leafygreen-ui/icon'; - -import { Wizard } from '../'; - -import { DELETE_PAGE_MAX_WIDTH } from './constants'; -import { RecommendedActions } from './RecommendedActions'; -import { RequiredActions } from './RequiredActions'; - -const stepStyles = css` - overflow: scroll; -`; - -const footerStyles = css` - bottom: 0; -`; -const footerContentStyles = css` - margin-inline: auto; - max-width: ${DELETE_PAGE_MAX_WIDTH}px; -`; - -export default { - title: 'Components/DeleteWizard', - component: Wizard, -}; - -export const DeleteProjectWizard = () => { - return ( -
- - - - - - - - - , - }} - /> - - -
- ); -}; diff --git a/packages/wizard/src/demo/DeleteWizardStepContent.types.ts b/packages/wizard/src/demo/DeleteWizardStepContent.types.ts deleted file mode 100644 index 3dcd23405d..0000000000 --- a/packages/wizard/src/demo/DeleteWizardStepContent.types.ts +++ /dev/null @@ -1 +0,0 @@ -export interface DeleteWizardStepContentProps {} diff --git a/packages/wizard/src/demo/RecommendedActionCard.tsx b/packages/wizard/src/demo/RecommendedActionCard.tsx deleted file mode 100644 index d76a453f96..0000000000 --- a/packages/wizard/src/demo/RecommendedActionCard.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React, { ReactNode } from 'react'; - -import { Badge } from '@leafygreen-ui/badge'; -import { Card } from '@leafygreen-ui/card'; -import { css } from '@leafygreen-ui/emotion'; -import { BaseFontSize, spacing } from '@leafygreen-ui/tokens'; -import { Body, Description } from '@leafygreen-ui/typography'; - -export interface RecommendedActionCardProps { - category: string; - title: string; - description: string; - link: ReactNode; -} - -export const RecommendedActionCard = ({ - category, - title, - description, - link, -}: RecommendedActionCardProps) => { - return ( - - {category} - - {title} - - - {description} - - {link} - - ); -}; diff --git a/packages/wizard/src/demo/RecommendedActions.tsx b/packages/wizard/src/demo/RecommendedActions.tsx deleted file mode 100644 index 415b5f3d7e..0000000000 --- a/packages/wizard/src/demo/RecommendedActions.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; - -import { Checkbox } from '@leafygreen-ui/checkbox'; -import { css } from '@leafygreen-ui/emotion'; -import { spacing } from '@leafygreen-ui/tokens'; -import { Body, Description, H3 } from '@leafygreen-ui/typography'; - -import { useWizardStepContext } from '../'; - -import { RecommendedActionCard } from './RecommendedActionCard'; -import { recommendedActionsConfig } from './recommendedActionData'; - -export const RecommendedActions = () => { - const { isAcknowledged, setAcknowledged } = useWizardStepContext(); - return ( - <> -

Recommended Actions

- - If you delete a project, the action is irreversible and permanently - deletes all data. We strongly recommend that you complete the following - data governance checks before you proceed. - - - - ); -}; diff --git a/packages/wizard/src/demo/RequiredActionCards/ApplicationsCard.tsx b/packages/wizard/src/demo/RequiredActionCards/ApplicationsCard.tsx deleted file mode 100644 index e04f57bfb9..0000000000 --- a/packages/wizard/src/demo/RequiredActionCards/ApplicationsCard.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; - -import { - LGColumnDef, - LGTableDataType, - useLeafyGreenTable, -} from '@leafygreen-ui/table'; -import { Description, Link } from '@leafygreen-ui/typography'; - -import { BasicTable } from '../../HELPERS/BasicTable'; -import { useFetchRequiredActionTableData } from '../hooks/useFetchRequiredActionTableData'; - -import { - InheritedRequiredActionCardProps, - RequiredActionCard, - TitleEm, -} from './RequiredActionCard'; - -interface ApplicationsTableData { - name: string; -} - -const applicationsColumns: Array> = [ - { accessorKey: 'name', header: 'Applications' }, -]; - -const demoApplicationsData: Array> = [ - { name: 'Application-1' }, - { name: 'Application-2' }, - { name: 'Application-3' }, -]; - -export const ApplicationsCard = ({ - ...props -}: InheritedRequiredActionCardProps) => { - const { isLoading, tableData } = useFetchRequiredActionTableData({ - demoData: demoApplicationsData, - }); - - const table = useLeafyGreenTable({ - data: tableData, - columns: applicationsColumns, - }); - - return ( - - Delete {tableData?.length} applications - - } - description={ - - All applications will be deleted upon project deletion. Review the - applications below or go to the{' '} - e.stopPropagation()} - > - Apps - {' '} - page. - - } - {...props} - > - - - ); -}; diff --git a/packages/wizard/src/demo/RequiredActionCards/ClustersCard.tsx b/packages/wizard/src/demo/RequiredActionCards/ClustersCard.tsx deleted file mode 100644 index afe03c50da..0000000000 --- a/packages/wizard/src/demo/RequiredActionCards/ClustersCard.tsx +++ /dev/null @@ -1,234 +0,0 @@ -import React, { ComponentType } from 'react'; - -import { Badge, Variant as BadgeVariant } from '@leafygreen-ui/badge'; -import { Button } from '@leafygreen-ui/button'; -import { css } from '@leafygreen-ui/emotion'; -import ReplicaSet from '@leafygreen-ui/icon/ReplicaSet'; -import ShardedCluster from '@leafygreen-ui/icon/ShardedCluster'; -import { useDarkMode } from '@leafygreen-ui/leafygreen-provider'; -import { TableSkeleton } from '@leafygreen-ui/skeleton-loader'; -import { - LGColumnDef, - LGTableDataType, - useLeafyGreenTable, -} from '@leafygreen-ui/table'; -import { color, spacing } from '@leafygreen-ui/tokens'; -import { Body, Description, Link } from '@leafygreen-ui/typography'; - -import { BasicTable } from '../../HELPERS/BasicTable'; -import { useFetchRequiredActionTableData } from '../hooks/useFetchRequiredActionTableData'; - -import { - InheritedRequiredActionCardProps, - RequiredActionCard, - TitleEm, -} from './RequiredActionCard'; - -// TODO: This is likely defined somewhere in MMS -type ClusterTier = 'Free' | 'Flex' | 'Dedicated'; -type ClusterType = 'Shard' | 'ReplicaSet'; - -const TierToVariantMap: Record = { - Free: BadgeVariant.LightGray, - Flex: BadgeVariant.Yellow, - Dedicated: BadgeVariant.Blue, -}; - -const TypeToIconMap: Record = { - Shard: ShardedCluster, - ReplicaSet: ReplicaSet, -}; - -interface ClusterTableData { - id: string; - name: string; - tier: ClusterTier; - clusterType: ClusterType; - version: string; - region: string; - backup: boolean; -} - -const clustersColumns: Array> = [ - { - accessorKey: 'name', - header: 'Cluster Name', - minSize: 384, - cell: info => { - const clusterType = info.row.original.clusterType; - const TypeIcon = TypeToIconMap[clusterType]; - const name = info.getValue() as string; - return ( -
- - <>{name} -
- ); - }, - }, - { - accessorKey: 'tier', - header: 'Tier', - maxSize: 96, - cell: info => { - const val: ClusterTier = info.getValue() as ClusterTier; - return {val}; - }, - }, - { accessorKey: 'version', header: 'Version', maxSize: 64 }, - { accessorKey: 'region', header: 'Region', minSize: 128 }, - { - accessorKey: 'backup', - header: 'Backup', - maxSize: 64, - cell: info => { - const backupEnabled: boolean = info.getValue() as boolean; - // eslint-disable-next-line react-hooks/rules-of-hooks - const { theme } = useDarkMode(); - return backupEnabled ? ( - - ON - - ) : ( - OFF - ); - }, - }, - { - id: 'downloadBackup', - accessorKey: 'backup', - header: () => { - // eslint-disable-next-line react-hooks/rules-of-hooks - const { theme } = useDarkMode(); - return ( - e.stopPropagation()} - className={css` - color: ${color[theme].text.primary.default}; - font-weight: 600; - - &, - &:hover { - // Emulating a LG Inline definition - text-decoration: underline dotted 2px; - text-underline-offset: 0.125em; - } - - & > svg { - color: ${color[theme].text.link.default}; - } - `} - > - Download Backup - - ); - }, - maxSize: 96, - cell: info => { - const backupEnabled: boolean = info.getValue() as boolean; - // TODO: What does this download button do? - return ( - - ); - }, - }, -]; - -const demoClustersData: Array> = [ - { - id: 'abc123', - name: 'Cluster1', - clusterType: 'Shard', - tier: 'Dedicated', - version: '8.0.12', - region: 'AWS / N. Virginia (us-east-1)', - backup: true, - }, - { - id: 'xyz789', - name: 'PUXFest2025', - clusterType: 'ReplicaSet', - tier: 'Free', - version: '8.0.12', - region: 'GCP / Iowa (us-central1)', - backup: false, - }, - { - id: '456lmnop', - name: 'PUX Design Strategy', - clusterType: 'ReplicaSet', - tier: 'Flex', - version: '7.0.22', - region: 'AWS / Oregon (us-west-2)', - backup: false, - }, -]; - -export const ClustersCard = ({ - ...props -}: InheritedRequiredActionCardProps) => { - const { isLoading, tableData } = useFetchRequiredActionTableData({ - demoData: demoClustersData, - }); - - const table = useLeafyGreenTable({ - data: tableData, - columns: clustersColumns, - }); - - return ( - - Terminate {tableData?.length} clusters - - } - description={ - - All clusters will be terminated upon project deletion. Review the - clusters below or go to the{' '} - e.stopPropagation()} - > - Clusters - {' '} - page. - - } - {...props} - > - {isLoading ? ( - ( - <> - {typeof col.header === 'function' - ? col.header({} as any) - : col.header ?? ''} - - ))} - /> - ) : ( - - )} - - ); -}; diff --git a/packages/wizard/src/demo/RequiredActionCards/FederatedDbCard.tsx b/packages/wizard/src/demo/RequiredActionCards/FederatedDbCard.tsx deleted file mode 100644 index cbfa5f24bd..0000000000 --- a/packages/wizard/src/demo/RequiredActionCards/FederatedDbCard.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import React from 'react'; -import { format } from 'date-fns'; - -import { LGColumnDef, useLeafyGreenTable } from '@leafygreen-ui/table'; -import { Description, Link } from '@leafygreen-ui/typography'; - -import { BasicTable } from '../../HELPERS/BasicTable'; -import { TableHeaderWithSubtitle } from '../../HELPERS/TableHeaderWithSubtitle'; -import { useFetchRequiredActionTableData } from '../hooks/useFetchRequiredActionTableData'; - -import { - InheritedRequiredActionCardProps, - RequiredActionCard, - TitleEm, -} from './RequiredActionCard'; - -type FederatedDBType = 'Atlas SQL' | 'Online Archive' | 'Federated Instance'; - -interface FederatedDBTableData { - name: string; - type: FederatedDBType; - region: string; - queriesExecuted: number; - dataProcessed: string; - dataReturned: string; - dateCreated: Date; -} - -const federatedDbColumns: Array> = [ - { accessorKey: 'name', header: 'Federated database instances' }, - { accessorKey: 'type', header: 'Type', minSize: 96 }, - { accessorKey: 'region', header: 'Cloud provider & region', minSize: 196 }, - { - accessorKey: 'queriesExecuted', - header: () => ( - - ), - }, - { - accessorKey: 'dataProcessed', - header: () => ( - - ), - }, - { - accessorKey: 'dataReturned', - header: () => ( - - ), - }, - { - accessorKey: 'dateCreated', - header: 'Date created', - cell: info => { - // TODO: Use consistent, localized Time-in-Atlas format - return format(info.getValue() as Date, 'yyyy-MM-dd HH:MM'); - }, - }, -]; - -const demoFederatedDbData: Array = [ - { - name: 'FederatedDatabaseInstance0', - type: 'Atlas SQL', - region: 'AWS / N. Virginia (us-east-1)', - queriesExecuted: 5, - dataProcessed: '48 KB', - dataReturned: '64 KB', - dateCreated: new Date(), - }, - { - name: 'FederatedDatabaseInstance1', - type: 'Online Archive', - region: 'AWS / N. Virginia (us-east-1)', - queriesExecuted: 28, - dataProcessed: '48 KB', - dataReturned: '64 KB', - dateCreated: new Date(), - }, - { - name: 'FederatedDatabaseInstance2', - type: 'Federated Instance', - region: 'AWS / N. Virginia (us-east-1)', - queriesExecuted: 1, - dataProcessed: '48 KB', - dataReturned: '64 KB', - dateCreated: new Date(), - }, - { - name: 'FederatedDatabaseInstance3', - type: 'Federated Instance', - region: 'AWS / N. Virginia (us-east-1)', - queriesExecuted: 7, - dataProcessed: '48 KB', - dataReturned: '64 KB', - dateCreated: new Date(), - }, -]; - -export const FederatedDbCard = ({ - ...props -}: InheritedRequiredActionCardProps) => { - const { isLoading, tableData } = useFetchRequiredActionTableData({ - demoData: demoFederatedDbData, - }); - - const table = useLeafyGreenTable({ - data: tableData, - columns: federatedDbColumns, - }); - - return ( - - Terminate {tableData?.length} federated database - instances - - } - description={ - - All federated database instances will be terminated upon project - deletion. Review the federated database instances below or go to the{' '} - e.stopPropagation()} - > - Data Federation - {' '} - page. - - } - {...props} - > - - - ); -}; diff --git a/packages/wizard/src/demo/RequiredActionCards/ModelApiKeysCard.tsx b/packages/wizard/src/demo/RequiredActionCards/ModelApiKeysCard.tsx deleted file mode 100644 index 447642b34c..0000000000 --- a/packages/wizard/src/demo/RequiredActionCards/ModelApiKeysCard.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import React from 'react'; -import { format } from 'date-fns'; - -import { - CellContext, - LGColumnDef, - LGTableDataType, - useLeafyGreenTable, -} from '@leafygreen-ui/table'; -import { Description } from '@leafygreen-ui/typography'; - -import { BasicTable } from '../../HELPERS/BasicTable'; -import { useFetchRequiredActionTableData } from '../hooks/useFetchRequiredActionTableData'; - -import { - InheritedRequiredActionCardProps, - RequiredActionCard, - TitleEm, -} from './RequiredActionCard'; - -const DateCell = ( - cellData: CellContext, unknown>, -) => { - const val = cellData.getValue() as Date | undefined; - - if (!val) return <>Never; - - return <>{format(val, 'yyyy-MM-dd')}; -}; - -interface ModelAPIKeyTableData { - keyName: string; - createdDate: Date; - lastUsedDate?: Date; - createdBy: string; -} - -const modelApiKeysColumns: Array> = [ - { accessorKey: 'keyName', header: 'Key Name' }, - { accessorKey: 'key', header: 'Secret key', cell: () => <>******** }, - { - accessorKey: 'createdDate', - header: 'Created on (UTC)', - cell: DateCell, - }, - { - accessorKey: 'lastUsedDate', - header: 'Last used (UTC)', - cell: DateCell, - }, - { accessorKey: 'createdBy', header: 'Created by' }, -]; - -const demoModelApiKeysData: Array> = [ - { - keyName: 'voyage-api-key-1', - createdDate: new Date('2019-04-22'), - lastUsedDate: new Date(), - createdBy: 'Mike Waltzer', - }, - { - keyName: 'voyage-api-key-2', - createdDate: new Date('2022-08-29'), - lastUsedDate: new Date(), - createdBy: 'Lauren Fox', - }, - { - keyName: 'voyage-api-key-3', - createdDate: new Date('2021-06-07'), - createdBy: 'Adam Thompson', - }, -]; - -export const ModelApiKeysCard = ({ - ...props -}: InheritedRequiredActionCardProps) => { - const { isLoading, tableData } = useFetchRequiredActionTableData({ - demoData: demoModelApiKeysData, - }); - - const table = useLeafyGreenTable({ - data: tableData, - columns: modelApiKeysColumns, - }); - - return ( - - Delete {tableData?.length} Model API keys - - } - description={ - - All Model API keys will be deleted upon project deletion. - - } - {...props} - > - - - ); -}; diff --git a/packages/wizard/src/demo/RequiredActionCards/PrivateEndpointsCard.tsx b/packages/wizard/src/demo/RequiredActionCards/PrivateEndpointsCard.tsx deleted file mode 100644 index f8003183b8..0000000000 --- a/packages/wizard/src/demo/RequiredActionCards/PrivateEndpointsCard.tsx +++ /dev/null @@ -1,348 +0,0 @@ -import React, { useState } from 'react'; -import upperFirst from 'lodash/upperFirst'; - -import { css } from '@leafygreen-ui/emotion'; -import Circle from '@leafygreen-ui/icon/Circle'; -import { useDarkMode } from '@leafygreen-ui/leafygreen-provider'; -import { Theme } from '@leafygreen-ui/lib'; -import { - SegmentedControl, - SegmentedControlOption, -} from '@leafygreen-ui/segmented-control'; -import { - CellContext, - LGColumnDef, - LGTableDataType, - useLeafyGreenTable, -} from '@leafygreen-ui/table'; -import { color, spacing } from '@leafygreen-ui/tokens'; -import { Description, Link } from '@leafygreen-ui/typography'; - -import { BasicTable } from '../../HELPERS/BasicTable'; -import { useFetchRequiredActionTableData } from '../hooks/useFetchRequiredActionTableData'; - -import { - InheritedRequiredActionCardProps, - RequiredActionCard, - TitleEm, -} from './RequiredActionCard'; - -const ServiceStatus = { - Available: 'available', -} as const; -type ServiceStatus = (typeof ServiceStatus)[keyof typeof ServiceStatus]; - -const ConfigurationStatus = { - Configured: 'configured', -} as const; -type ConfigurationStatus = - (typeof ConfigurationStatus)[keyof typeof ConfigurationStatus]; - -const statusToColorMap: Record< - ServiceStatus | ConfigurationStatus, - Record -> = { - [ServiceStatus.Available]: { - [Theme.Light]: color.light.icon.success.default, - [Theme.Dark]: color.dark.icon.success.default, - }, - [ConfigurationStatus.Configured]: { - [Theme.Light]: color.light.icon.success.default, - [Theme.Dark]: color.dark.icon.success.default, - }, -}; - -const StatusCell = < - D extends - | DedicatedClusterPrivateEndpointTableData - | FederatedDBInstancePrivateEndpointTableData - | StreamProcessingPrivateEndpointTableData, ->( - cellData: CellContext, unknown>, -) => { - const { theme } = useDarkMode(); - const value = cellData.getValue() as ServiceStatus | ConfigurationStatus; - const dotColor = statusToColorMap[value][theme]; - return ( - <> - - {upperFirst(value)} - - ); -}; - -const PrivateEndpointType = { - Dedicated: 'dedicated', - Federated: 'federated', - StreamProcessing: 'stream-processing', -} as const; -type PrivateEndpointType = - (typeof PrivateEndpointType)[keyof typeof PrivateEndpointType]; - -interface DedicatedClusterPrivateEndpointTableData { - id: string; - provider: string; - region: string; - endpointService: string; - endpointStatus: ServiceStatus; - configurationStatus: ConfigurationStatus; -} - -const dedicatedClusterPrivateEndpointsColumns: Array< - LGColumnDef -> = [ - { accessorKey: 'provider', header: 'Cloud Provider', maxSize: 96 }, - { accessorKey: 'region', header: 'Region', maxSize: 96 }, - { accessorKey: 'endpointService', header: 'Atlas Endpoint Service' }, - { - accessorKey: 'endpointStatus', - header: 'Atlas Endpoint Service Status', - cell: StatusCell, - }, - { accessorKey: 'id', header: 'Endpoint' }, - { - accessorKey: 'configurationStatus', - header: 'Endpoint Status', - maxSize: 112, - cell: StatusCell, - }, -]; - -const demoDedicatedClusterPrivateEndpointsData: Array< - LGTableDataType -> = [ - { - id: 'endpoint-1', - provider: 'AWS', - region: 'us-east-1', - endpointService: 'com.amazonaws.vpce.us-east-1.vpce-svc-054161d3958725abb', - endpointStatus: 'available', - configurationStatus: 'configured', - }, - { - id: 'endpoint-2', - provider: 'GCP', - region: 'us-east-1', - endpointService: 'com.amazonaws.vpce.us-east-1.vpce-svc-054161d3958725abb', - endpointStatus: 'available', - configurationStatus: 'configured', - }, - { - id: 'endpoint-3', - provider: 'AWS', - region: 'us-central1', - endpointService: 'com.amazonaws.vpce.us-east-1.vpce-svc-054161d3958725abb', - endpointStatus: 'available', - configurationStatus: 'configured', - }, -]; - -interface FederatedDBInstancePrivateEndpointTableData { - id: string; - provider: string; - endpointStatus: ServiceStatus; - region: string; - comment?: string; -} - -const federatedDBInstancePrivateEndpointsColumns: Array< - LGColumnDef -> = [ - { accessorKey: 'provider', header: 'Cloud Provider', maxSize: 96 }, - { accessorKey: 'region', header: 'Region', maxSize: 96 }, - { - accessorKey: 'endpointStatus', - header: 'Endpoint Status', - maxSize: 96, - cell: StatusCell, - }, - { accessorKey: 'id', header: 'Endpoint' }, - { - accessorKey: 'comment', - header: 'Comment', - cell: info => { - const val = info.getValue() as string | undefined; - - if (!val || val.length === 0) { - return '—'; - } - - return val; - }, - }, -]; - -const demoFederatedDBInstancePrivateEndpointsData: Array< - LGTableDataType -> = [ - { - id: 'vpce-0b9c5701325cb07e6', - provider: 'AWS', - endpointStatus: 'available', - region: 'us-east-1', - comment: 'Production endpoint', - }, - { - id: 'vpce-1a2b3c4d5e6f7g8h9', - provider: 'AWS', - endpointStatus: 'available', - region: 'us-west-2', - }, -]; - -interface StreamProcessingPrivateEndpointTableData { - provider: string; - vendor: string; - endpoint: string; - endpointStatus: ServiceStatus; - accountId: string; -} - -const streamProcessingPrivateEndpointsColumns: Array< - LGColumnDef -> = [ - { accessorKey: 'provider', header: 'Cloud Provider', maxSize: 96 }, - { accessorKey: 'vendor', header: 'Vendor', maxSize: 96 }, - { accessorKey: 'endpoint', header: 'Endpoint' }, - { - accessorKey: 'endpointStatus', - header: 'Endpoint Status', - maxSize: 112, - cell: StatusCell, - }, - { accessorKey: 'accountId', header: 'Account ID' }, -]; - -const demoStreamProcessingPrivateEndpointsData: Array< - LGTableDataType -> = [ - { - provider: 'AWS', - vendor: 'S3', - endpoint: 'vpce-0a1b2c3d4e5f6g7h8', - endpointStatus: 'available', - accountId: '123456789012', - }, - { - provider: 'AWS', - vendor: 'Confluent', - endpoint: 'vpce-9h8g7f6e5d4c3b2a1', - endpointStatus: 'available', - accountId: '123456789012', - }, -]; - -export const PrivateEndpointsCard = ({ - ...props -}: InheritedRequiredActionCardProps) => { - const [segCtrlValue, setSegCtrlValue] = useState( - PrivateEndpointType.Dedicated, - ); - - const { - isLoading: isDedicatedClustersLoading, - tableData: dedicatedClusterTableData, - } = useFetchRequiredActionTableData({ - demoData: demoDedicatedClusterPrivateEndpointsData, - }); - - const { isLoading: isFederatedDBLoading, tableData: federatedDBTableData } = - useFetchRequiredActionTableData({ - demoData: demoFederatedDBInstancePrivateEndpointsData, - }); - - const { - isLoading: isStreamProcessingLoading, - tableData: streamProcessingTableData, - } = useFetchRequiredActionTableData({ - demoData: demoStreamProcessingPrivateEndpointsData, - }); - - const dedicatedClusterTable = useLeafyGreenTable({ - data: dedicatedClusterTableData, - columns: dedicatedClusterPrivateEndpointsColumns, - }); - - const federatedDBTable = useLeafyGreenTable({ - data: federatedDBTableData, - columns: federatedDBInstancePrivateEndpointsColumns, - }); - - const streamProcessingTable = useLeafyGreenTable({ - data: streamProcessingTableData, - columns: streamProcessingPrivateEndpointsColumns, - }); - - const isLoading = - isDedicatedClustersLoading || - isFederatedDBLoading || - isStreamProcessingLoading; - - const totalEndpoints = - dedicatedClusterTableData?.length + - federatedDBTableData?.length + - streamProcessingTableData?.length; - - return ( - - Remove {totalEndpoints} private endpoint - connections - - } - description={ - - All private endpoint connections will be removed upon project - deletion. Review the private endpoint connections below or go to the{' '} - - Network Access - - - } - {...props} - > - {isLoading ? null : ( - <> - setSegCtrlValue(val as PrivateEndpointType)} - className={css` - margin: ${spacing[200]}px ${spacing[600]}px; - `} - > - - Dedicated Cluster - - - Federated database Instance / Online Archive - - - Atlas stream processing - - - - {segCtrlValue === PrivateEndpointType.Dedicated && ( - - )} - {segCtrlValue === PrivateEndpointType.Federated && ( - - )} - {segCtrlValue === PrivateEndpointType.StreamProcessing && ( - - )} - - )} - - ); -}; diff --git a/packages/wizard/src/demo/RequiredActionCards/RequiredActionCard.tsx b/packages/wizard/src/demo/RequiredActionCards/RequiredActionCard.tsx deleted file mode 100644 index ca7e30731a..0000000000 --- a/packages/wizard/src/demo/RequiredActionCards/RequiredActionCard.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import React, { PropsWithChildren, ReactNode } from 'react'; - -import { css } from '@leafygreen-ui/emotion'; -import { - ExpandableCard, - ExpandableCardProps, -} from '@leafygreen-ui/expandable-card'; -import { useDarkMode } from '@leafygreen-ui/leafygreen-provider'; -import { - Size as SpinnerSize, - Spinner, -} from '@leafygreen-ui/loading-indicator/spinner'; -import { Skeleton } from '@leafygreen-ui/skeleton-loader'; -import { color, spacing } from '@leafygreen-ui/tokens'; - -const requiredActionCardStyles = css` - & h6 { - display: block; - } -`; - -const expandableCardContentStyles = css` - padding: unset; -`; - -const cardContentWrapperStyles = css` - padding-bottom: ${spacing[400]}px; - overflow: hidden; -`; - -export interface RequiredActionCardProps extends ExpandableCardProps { - isLoading?: boolean; - loadingTitle?: ReactNode; - loadingDescription?: ReactNode; -} - -export interface InheritedRequiredActionCardProps - extends Omit {} - -export const TitleEm = ({ children }: PropsWithChildren<{}>) => { - const { theme } = useDarkMode(); - return ( - - {children} - - ); -}; - -export const RequiredActionCard = ({ - title, - description, - isLoading, - children, - loadingTitle = ( - - ), - loadingDescription = 'This may take a few moments', - ...rest -}: RequiredActionCardProps) => { - return ( - - - {loadingDescription} -
- ) : ( - description - ) - } - className={requiredActionCardStyles} - contentClassName={expandableCardContentStyles} - defaultOpen={false} - {...rest} - > -
{children}
- - ); -}; diff --git a/packages/wizard/src/demo/RequiredActionCards/StreamProcessingCard.tsx b/packages/wizard/src/demo/RequiredActionCards/StreamProcessingCard.tsx deleted file mode 100644 index 57698b5004..0000000000 --- a/packages/wizard/src/demo/RequiredActionCards/StreamProcessingCard.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import React from 'react'; - -import { LGTableDataType, useLeafyGreenTable } from '@leafygreen-ui/table'; -import { Description, Link } from '@leafygreen-ui/typography'; - -import { BasicTable } from '../../HELPERS/BasicTable'; -import { TableHeaderWithSubtitle } from '../../HELPERS/TableHeaderWithSubtitle'; -import { useFetchRequiredActionTableData } from '../hooks/useFetchRequiredActionTableData'; - -import { - InheritedRequiredActionCardProps, - RequiredActionCard, - TitleEm, -} from './RequiredActionCard'; - -interface StreamProcessingTableData { - name: string; - region: string; - started: number; - stopped: number; - failed: number; -} - -const streamProcessingColumns = [ - { accessorKey: 'name', header: 'Workspace name' }, - { accessorKey: 'region', header: 'Region', maxSize: 180 }, - { - accessorKey: 'started', - maxSize: 96, - header: () => ( - - ), - }, - { - accessorKey: 'stopped', - maxSize: 96, - header: () => ( - - ), - }, - { - accessorKey: 'failed', - maxSize: 96, - header: () => ( - - ), - }, -]; - -const demoStreamProcessingData: Array< - LGTableDataType -> = [ - { - name: 'Workspace-1', - region: 'us-east-1', - started: 1, - stopped: 2, - failed: 0, - }, - { - name: 'Workspace-2', - region: 'us-west-2', - started: 1, - stopped: 2, - failed: 0, - }, -]; - -export const StreamProcessingCard = ({ - ...props -}: InheritedRequiredActionCardProps) => { - const { isLoading, tableData } = useFetchRequiredActionTableData({ - demoData: demoStreamProcessingData, - }); - - const table = useLeafyGreenTable({ - data: tableData, - columns: streamProcessingColumns, - }); - - return ( - - Terminate {tableData?.length} stream processing - workspaces - - } - description={ - - All Stream Processing workspaces will be terminated upon project - deletion. Review the workspaces below or go to the{' '} - e.stopPropagation()} - > - Stream Processing - {' '} - page. - - } - {...props} - > - - - ); -}; diff --git a/packages/wizard/src/demo/RequiredActionCards/index.ts b/packages/wizard/src/demo/RequiredActionCards/index.ts deleted file mode 100644 index 70d2b2bd5d..0000000000 --- a/packages/wizard/src/demo/RequiredActionCards/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { ApplicationsCard } from './ApplicationsCard'; -export { ClustersCard } from './ClustersCard'; -export { FederatedDbCard } from './FederatedDbCard'; -export { ModelApiKeysCard } from './ModelApiKeysCard'; -export { PrivateEndpointsCard } from './PrivateEndpointsCard'; -export { StreamProcessingCard } from './StreamProcessingCard'; diff --git a/packages/wizard/src/demo/RequiredActions.tsx b/packages/wizard/src/demo/RequiredActions.tsx deleted file mode 100644 index 10262be135..0000000000 --- a/packages/wizard/src/demo/RequiredActions.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React from 'react'; - -import { Checkbox } from '@leafygreen-ui/checkbox'; -import { css } from '@leafygreen-ui/emotion'; -import { spacing } from '@leafygreen-ui/tokens'; -import { Body, Description, H3, Link } from '@leafygreen-ui/typography'; - -import { useRequiredActionAcknowledgements } from './hooks/useRequiredActionAcknowledgements'; -import { - ApplicationsCard, - ClustersCard, - FederatedDbCard, - ModelApiKeysCard, - PrivateEndpointsCard, - StreamProcessingCard, -} from './RequiredActionCards'; - -const reviewCardsContainerStyles = css` - display: flex; - flex-direction: column; - gap: ${spacing[3]}px; - margin-block: ${spacing[4]}px; -`; - -const checkboxContainerStyles = css` - display: flex; - flex-direction: column; - gap: ${spacing[3]}px; - margin-top: ${spacing[3]}px; -`; - -const acknowledgmentLabelStyles = css` - margin-bottom: ${spacing[3]}px; -`; - -const sectionDividerStyles = css` - border-top: 1px solid #e8edeb; - margin-block: ${spacing[5]}px; -`; - -export const RequiredActions = () => { - const { acknowledgementsState, setAcknowledgementState } = - useRequiredActionAcknowledgements(); - - return ( - <> -

Required Actions

- - Access to all of the following deployments and associated data will be - permanently lost and cannot be recovered.{' '} - Manage Project Access Docs - - -
- - - - - - -
- -
- - - By deleting the project, you acknowledge this irreversible action: - - -
- setAcknowledgementState(0, e.target.checked)} - /> - - setAcknowledgementState(1, e.target.checked)} - /> - - setAcknowledgementState(2, e.target.checked)} - /> -
- - ); -}; diff --git a/packages/wizard/src/demo/constants.ts b/packages/wizard/src/demo/constants.ts deleted file mode 100644 index ce523186ac..0000000000 --- a/packages/wizard/src/demo/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { breakpoints } from '@leafygreen-ui/tokens'; - -export const DELETE_PAGE_MAX_WIDTH = breakpoints.XLDesktop; diff --git a/packages/wizard/src/demo/hooks/useFetchRequiredActionTableData.ts b/packages/wizard/src/demo/hooks/useFetchRequiredActionTableData.ts deleted file mode 100644 index 6627809b2f..0000000000 --- a/packages/wizard/src/demo/hooks/useFetchRequiredActionTableData.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Dispatch, SetStateAction, useEffect, useState } from 'react'; - -import { LGRowData, LGTableDataType } from '@leafygreen-ui/table'; - -interface FetchRequiredActionTableDataArgs { - demoData: Array>; - _demoDelay?: number; -} - -interface FetchRequiredActionTableDataReturnType { - tableData: Array>; - setTableData: Dispatch>>>; - isLoading: boolean; -} - -export const useFetchRequiredActionTableData = ({ - demoData, - _demoDelay, -}: FetchRequiredActionTableDataArgs): FetchRequiredActionTableDataReturnType => { - const [isLoading, setLoading] = useState(false); - const [tableData, setTableData] = useState>>([]); - - useEffect(() => { - if (_demoDelay === 0) { - setTableData(demoData); - return; - } - - setLoading(true); - setTimeout(() => { - setLoading(false); - setTableData(demoData); - }, _demoDelay ?? 500 + 5000 * Math.random()); - }, [_demoDelay, demoData]); - - return { - isLoading, - tableData, - setTableData, - }; -}; diff --git a/packages/wizard/src/demo/hooks/useRequiredActionAcknowledgements.ts b/packages/wizard/src/demo/hooks/useRequiredActionAcknowledgements.ts deleted file mode 100644 index b99d952ab8..0000000000 --- a/packages/wizard/src/demo/hooks/useRequiredActionAcknowledgements.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Reducer, useReducer } from 'react'; - -import { useWizardStepContext } from '../../WizardStep'; - -export type RequiredActionsAcknowledgementsState = [boolean, boolean, boolean]; - -export interface RequiredActionsAcknowledgementsAction { - index: 0 | 1 | 2; - value: boolean; -} - -export const useRequiredActionAcknowledgements = () => { - const { isAcknowledged, setAcknowledged } = useWizardStepContext(); - - const [acknowledgementsState, dispatch] = useReducer< - Reducer< - RequiredActionsAcknowledgementsState, - RequiredActionsAcknowledgementsAction - > - >( - (state, action) => { - const newState: RequiredActionsAcknowledgementsState = [...state]; - newState[action.index] = action.value; - - if (newState.every(Boolean)) { - setAcknowledged(true); - } else { - setAcknowledged(false); - } - - return newState; - }, - [false, false, false], - ); - - const setAcknowledgementState = (index: 0 | 1 | 2, value: boolean) => - dispatch({ index, value }); - - return { - acknowledgementsState, - setAcknowledgementState, - isAcknowledged, - }; -}; diff --git a/packages/wizard/src/demo/recommendedActionData.tsx b/packages/wizard/src/demo/recommendedActionData.tsx deleted file mode 100644 index 971f25911c..0000000000 --- a/packages/wizard/src/demo/recommendedActionData.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react'; - -import { Link } from '@leafygreen-ui/typography'; - -import { RecommendedActionCardProps } from './RecommendedActionCard'; - -export const recommendedActionsConfig: Array = [ - { - category: 'Data Retention', - title: 'Export most recent Backup Snapshot', - description: - 'To safeguard critical information, provide a recovery option to ensure you have access to your data after you delete your project.', - link: ( - - Learn how to export backup snapshots - - ), - }, - - { - category: 'Compliance', - title: 'Export audit logs', - description: - 'Retain a record of essential data regarding changes and actions within your project for compliance, audits, and future reference.', - link: ( - - Learn how to export audit logs - - ), - }, - { - category: 'Auditing', - title: 'Export Project Activity Feed events', - description: - 'Ensure you have access to key details about project changes, actions, and events for audits, compliance, or future reference.', - link: ( - - {' '} - Learn how to return events - - ), - }, - - { - category: 'Security', - title: 'Disconnect third-party integrations', - description: - 'Remove API keys to secure of your data, prevent unauthorized access, and avoid any unintended interactions with external systems.', - link: ( - - Go to integrations - - ), - }, - - { - category: 'Visualizations', - title: 'Export Charts and Dashboards', - description: - 'Retain critical insights and visualizations from dashboards, data sources, and charts associated with your project.', - link: ( - - Export charts dashboards - - ), - }, -]; From 9c33bd0ba7aaeb8616049d6bfe252cd8a9f3437d Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 16:14:01 -0500 Subject: [PATCH 10/43] rm temp changesets --- .changeset/wizard-patch-0.md | 5 ----- .changeset/wizard-patch-1.md | 5 ----- .changeset/wizard-patch-2.md | 5 ----- .changeset/wizard-patch-3.md | 5 ----- .changeset/wizard-patch-5.md | 5 ----- .changeset/wizard-patch-6.md | 5 ----- .changeset/wizard-patch-7.md | 5 ----- 7 files changed, 35 deletions(-) delete mode 100644 .changeset/wizard-patch-0.md delete mode 100644 .changeset/wizard-patch-1.md delete mode 100644 .changeset/wizard-patch-2.md delete mode 100644 .changeset/wizard-patch-3.md delete mode 100644 .changeset/wizard-patch-5.md delete mode 100644 .changeset/wizard-patch-6.md delete mode 100644 .changeset/wizard-patch-7.md diff --git a/.changeset/wizard-patch-0.md b/.changeset/wizard-patch-0.md deleted file mode 100644 index 6f7f76d3f7..0000000000 --- a/.changeset/wizard-patch-0.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@leafygreen-ui/wizard': patch ---- - -Removes wrapper div around step children diff --git a/.changeset/wizard-patch-1.md b/.changeset/wizard-patch-1.md deleted file mode 100644 index 90935fc790..0000000000 --- a/.changeset/wizard-patch-1.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@leafygreen-ui/wizard': patch ---- - -Removes descendants dep. Clamp activeStep between 0 & step count \ No newline at end of file diff --git a/.changeset/wizard-patch-2.md b/.changeset/wizard-patch-2.md deleted file mode 100644 index 4a924c356f..0000000000 --- a/.changeset/wizard-patch-2.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@leafygreen-ui/wizard': patch ---- - -Exports WizardProvider diff --git a/.changeset/wizard-patch-3.md b/.changeset/wizard-patch-3.md deleted file mode 100644 index 4a924c356f..0000000000 --- a/.changeset/wizard-patch-3.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@leafygreen-ui/wizard': patch ---- - -Exports WizardProvider diff --git a/.changeset/wizard-patch-5.md b/.changeset/wizard-patch-5.md deleted file mode 100644 index 053bd3ac44..0000000000 --- a/.changeset/wizard-patch-5.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@leafygreen-ui/wizard': patch ---- - -Exports `WizardSubComponentProperties` \ No newline at end of file diff --git a/.changeset/wizard-patch-6.md b/.changeset/wizard-patch-6.md deleted file mode 100644 index 8b8f8e6398..0000000000 --- a/.changeset/wizard-patch-6.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@leafygreen-ui/wizard': patch ---- - -Adds `requiresAcknowledgement` prop to `Wizard.Step` \ No newline at end of file diff --git a/.changeset/wizard-patch-7.md b/.changeset/wizard-patch-7.md deleted file mode 100644 index 57aa375db8..0000000000 --- a/.changeset/wizard-patch-7.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@leafygreen-ui/wizard': patch ---- - -Implements `isAcknowledged` state inside provider \ No newline at end of file From 84b431709e6419c015aade49530ff2194668d9f8 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 16:38:46 -0500 Subject: [PATCH 11/43] footer tests --- .../src/WizardFooter/WizardFooter.spec.tsx | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) diff --git a/packages/wizard/src/WizardFooter/WizardFooter.spec.tsx b/packages/wizard/src/WizardFooter/WizardFooter.spec.tsx index 9f8f10d366..bd9e8336d1 100644 --- a/packages/wizard/src/WizardFooter/WizardFooter.spec.tsx +++ b/packages/wizard/src/WizardFooter/WizardFooter.spec.tsx @@ -256,4 +256,216 @@ describe('packages/wizard-footer', () => { expect(getByTestId('step-1')).toBeInTheDocument(); }); }); + + describe('primary button behavior', () => { + test('primary button is enabled by default', () => { + const { getByRole } = render( + + + + + , + ); + + const primaryButton = getByRole('button', { name: 'Continue' }); + expect(primaryButton).toBeEnabled(); + }); + + test('primary button advances to next step when clicked', async () => { + const onStepChange = jest.fn(); + + const { getByRole, getByTestId } = render( + + +
Step 1
+ +
+ +
Step 2
+ +
+
, + ); + + expect(getByTestId('step-1')).toBeInTheDocument(); + + const nextButton = getByRole('button', { name: 'Next' }); + await userEvent.click(nextButton); + + expect(onStepChange).toHaveBeenCalledWith(1); + expect(getByTestId('step-2')).toBeInTheDocument(); + }); + }); + + describe('requiresAcknowledgement', () => { + test('primary button is disabled when step requires acknowledgement and is not acknowledged', () => { + const { getByRole } = render( + + +
Step content
+ +
+
, + ); + + const primaryButton = getByRole('button', { name: 'Continue' }); + expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); + }); + + test('primary button is enabled when step requires acknowledgement and is acknowledged', async () => { + const TestComponent = () => { + const { setAcknowledged } = useWizardStepContext(); + return ( + <> +
Step content
+ + + + ); + }; + + const { getByRole } = render( + + + + + , + ); + + const primaryButton = getByRole('button', { name: 'Continue' }); + expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); + + await userEvent.click(getByRole('button', { name: 'Acknowledge' })); + + expect(primaryButton).toHaveAttribute('aria-disabled', 'false'); + }); + + test('primary button is enabled when step does not require acknowledgement', () => { + const { getByRole } = render( + + +
Step content
+ +
+
, + ); + + const primaryButton = getByRole('button', { name: 'Continue' }); + expect(primaryButton).toHaveAttribute('aria-disabled', 'false'); + }); + + test('primary button can advance step after acknowledgement', async () => { + const TestComponent = () => { + const { setAcknowledged } = useWizardStepContext(); + return ( + <> +
Step content
+ + + + ); + }; + + const { getByRole, getByTestId } = render( + + +
Step 1
+ +
+ +
Step 2
+
+
, + ); + + expect(getByTestId('step-1')).toBeInTheDocument(); + + const primaryButton = getByRole('button', { name: 'Continue' }); + expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); + + // Acknowledge the step + await userEvent.click(getByRole('button', { name: 'Acknowledge' })); + expect(primaryButton).toHaveAttribute('aria-disabled', 'false'); + + // Advance to next step + await userEvent.click(primaryButton); + expect(getByTestId('step-2')).toBeInTheDocument(); + }); + }); + + describe('back button', () => { + test('back button is not rendered on first step', () => { + const { queryByRole } = render( + + + + + , + ); + + expect(queryByRole('button', { name: 'Back' })).not.toBeInTheDocument(); + }); + + test('back button is rendered on subsequent steps', async () => { + const { getByRole } = render( + + +
Step 1
+ +
+ +
Step 2
+ +
+
, + ); + + // Move to step 2 + await userEvent.click(getByRole('button', { name: 'Next' })); + + // Back button should now be visible + expect(getByRole('button', { name: 'Back' })).toBeInTheDocument(); + }); + + test('back button navigates to previous step', async () => { + const onStepChange = jest.fn(); + + const { getByRole, getByTestId } = render( + + +
Step 1
+ +
+ +
Step 2
+ +
+
, + ); + + // Move to step 2 + await userEvent.click(getByRole('button', { name: 'Next' })); + expect(getByTestId('step-2')).toBeInTheDocument(); + + // Go back to step 1 + await userEvent.click(getByRole('button', { name: 'Back' })); + expect(onStepChange).toHaveBeenCalledWith(0); + expect(getByTestId('step-1')).toBeInTheDocument(); + }); + }); }); From d7c0135262db437d0ae386ee9fdaa8d889dc888d Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 17:13:33 -0500 Subject: [PATCH 12/43] revert toast changes? --- packages/toast/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/toast/package.json b/packages/toast/package.json index 8296006898..d66de64937 100644 --- a/packages/toast/package.json +++ b/packages/toast/package.json @@ -35,7 +35,7 @@ "@faker-js/faker": "^8.0.0", "@leafygreen-ui/button": "workspace:^", "@lg-tools/build": "workspace:^", - "@storybook/types": "latest", + "@storybook/types": "^8.5.3", "react-test-renderer": "^18.2.0" }, "homepage": "https://github.com/mongodb/leafygreen-ui/tree/main/packages/toast", From 2879a5893d9a85c303c3ff88fd86f2b4fd6507dc Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 17:22:40 -0500 Subject: [PATCH 13/43] Update .npmrc --- .npmrc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.npmrc b/.npmrc index 6b1b4eaaef..d805c4429b 100644 --- a/.npmrc +++ b/.npmrc @@ -8,9 +8,4 @@ public-hoist-pattern[]=@testing-library/* public-hoist-pattern[]=@types/* public-hoist-pattern[]=*jest* public-hoist-pattern[]=react -public-hoist-pattern[]=react-dom -# @leafygreen-ui:registry=http://localhost:4873 -# @lg-chat:registry=http://localhost:4873 -# @lg-charts:registry=http://localhost:4873 -# @lg-tools:registry=http://localhost:4873 -# //localhost:4873/:_authToken=YzNjMGRkMDY3ZjI3N2IzNzUxYzk3NjlkOWNjNTc1MGI6NjgwNmIyZDAwMDIzYjEyNzVjN2Q5NWZhZmRkZmFlMTY4ZTMzOWRhMDAwOTllOWMzZDMxYzcxZmFkMTE5 \ No newline at end of file +public-hoist-pattern[]=react-dom \ No newline at end of file From 954b44e926f557a3321dea39ad56b465f39ddbc1 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Mon, 24 Nov 2025 14:21:29 -0500 Subject: [PATCH 14/43] adds `totalSteps` to wizard context --- packages/wizard/src/Wizard/Wizard.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/wizard/src/Wizard/Wizard.tsx b/packages/wizard/src/Wizard/Wizard.tsx index 8a9c25f377..6ac52ea50e 100644 --- a/packages/wizard/src/Wizard/Wizard.tsx +++ b/packages/wizard/src/Wizard/Wizard.tsx @@ -48,7 +48,12 @@ export const Wizard = CompoundComponent( ); return ( - + {stepChildren.map((child, i) => (i === activeStep ? child : null))} ); From 59dc8f107afc13cf1a6338979d7031660a6a5ef7 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Mon, 24 Nov 2025 15:08:43 -0500 Subject: [PATCH 15/43] fix bad merge --- packages/wizard/src/Wizard/Wizard.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/wizard/src/Wizard/Wizard.tsx b/packages/wizard/src/Wizard/Wizard.tsx index 6ac52ea50e..2b2383b5e1 100644 --- a/packages/wizard/src/Wizard/Wizard.tsx +++ b/packages/wizard/src/Wizard/Wizard.tsx @@ -52,7 +52,6 @@ export const Wizard = CompoundComponent( activeStep={activeStep} updateStep={updateStep} totalSteps={stepChildren.length} - lgIds={lgIds} > {stepChildren.map((child, i) => (i === activeStep ? child : null))} From 1a5f04677ed1491f83e596730c90d17b4ec5d16f Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 16:50:45 -0500 Subject: [PATCH 16/43] adds LGIDs --- packages/wizard/src/Wizard/Wizard.tsx | 9 ++++++++- packages/wizard/src/WizardStep/WizardStep.tsx | 18 ++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/wizard/src/Wizard/Wizard.tsx b/packages/wizard/src/Wizard/Wizard.tsx index 2b2383b5e1..029483a3cc 100644 --- a/packages/wizard/src/Wizard/Wizard.tsx +++ b/packages/wizard/src/Wizard/Wizard.tsx @@ -7,6 +7,7 @@ import { import { useControlled } from '@leafygreen-ui/hooks'; import { WizardSubComponentProperties } from '../constants'; +import { getLgIds } from '../utils/getLgIds'; import { WizardProvider } from '../WizardContext/WizardContext'; import { WizardFooter } from '../WizardFooter'; import { WizardStep } from '../WizardStep'; @@ -14,7 +15,13 @@ import { WizardStep } from '../WizardStep'; import { WizardProps } from './Wizard.types'; export const Wizard = CompoundComponent( - ({ activeStep: activeStepProp, onStepChange, children }: WizardProps) => { + ({ + activeStep: activeStepProp, + onStepChange, + children, + 'data-lgid': dataLgId, + }: WizardProps) => { + const lgIds = getLgIds(dataLgId); const stepChildren = findChildren( children, WizardSubComponentProperties.Step, diff --git a/packages/wizard/src/WizardStep/WizardStep.tsx b/packages/wizard/src/WizardStep/WizardStep.tsx index 26cd587ea2..3f9fc8478c 100644 --- a/packages/wizard/src/WizardStep/WizardStep.tsx +++ b/packages/wizard/src/WizardStep/WizardStep.tsx @@ -17,7 +17,7 @@ import { WizardStepProvider } from './WizardStepContext'; export const WizardStep = CompoundSubComponent( ({ children, requiresAcknowledgement = false }: WizardStepProps) => { const stepId = useIdAllocator({ prefix: 'wizard-step' }); - const { isWizardContext } = useWizardContext(); + const { isWizardContext, lgIds } = useWizardContext(); if (!isWizardContext) { consoleOnce.error( @@ -40,13 +40,15 @@ export const WizardStep = CompoundSubComponent( * This is done to ensure the Wizard is flexible, and can be rendered in any containing layout. */ return ( - - {restChildren} - {footerChild} - +
+ + {restChildren} + {footerChild} + +
); }, { From b65d79260e5d68d7f17061c885d9b189825b976c Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 18:54:40 -0500 Subject: [PATCH 17/43] lint --- packages/wizard/src/testing/getTestUtils.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/wizard/src/testing/getTestUtils.tsx b/packages/wizard/src/testing/getTestUtils.tsx index 6dd5b53162..ec3c4175c8 100644 --- a/packages/wizard/src/testing/getTestUtils.tsx +++ b/packages/wizard/src/testing/getTestUtils.tsx @@ -1,4 +1,5 @@ import { findByLgId, getByLgId, queryByLgId } from '@lg-tools/test-harnesses'; +import { screen } from '@testing-library/react'; import { getTestUtils as getButtonUtils } from '@leafygreen-ui/button/testing'; import { LgIdString } from '@leafygreen-ui/lib'; From 2100c5b442227b57ccdee5d3dbf6b4fcf950b1f6 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Mon, 24 Nov 2025 15:09:32 -0500 Subject: [PATCH 18/43] fix bad merge --- packages/wizard/src/Wizard/Wizard.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/wizard/src/Wizard/Wizard.tsx b/packages/wizard/src/Wizard/Wizard.tsx index 029483a3cc..cb67c24167 100644 --- a/packages/wizard/src/Wizard/Wizard.tsx +++ b/packages/wizard/src/Wizard/Wizard.tsx @@ -59,6 +59,7 @@ export const Wizard = CompoundComponent( activeStep={activeStep} updateStep={updateStep} totalSteps={stepChildren.length} + lgIds={lgIds} > {stepChildren.map((child, i) => (i === activeStep ? child : null))} From 2ace0b51a7dcc365ffc0ea8ce2cc558986a4f4ca Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Mon, 24 Nov 2025 16:27:04 -0500 Subject: [PATCH 19/43] removes Step test utils --- packages/wizard/src/WizardStep/WizardStep.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/wizard/src/WizardStep/WizardStep.tsx b/packages/wizard/src/WizardStep/WizardStep.tsx index 3f9fc8478c..26cd587ea2 100644 --- a/packages/wizard/src/WizardStep/WizardStep.tsx +++ b/packages/wizard/src/WizardStep/WizardStep.tsx @@ -17,7 +17,7 @@ import { WizardStepProvider } from './WizardStepContext'; export const WizardStep = CompoundSubComponent( ({ children, requiresAcknowledgement = false }: WizardStepProps) => { const stepId = useIdAllocator({ prefix: 'wizard-step' }); - const { isWizardContext, lgIds } = useWizardContext(); + const { isWizardContext } = useWizardContext(); if (!isWizardContext) { consoleOnce.error( @@ -40,15 +40,13 @@ export const WizardStep = CompoundSubComponent( * This is done to ensure the Wizard is flexible, and can be rendered in any containing layout. */ return ( -
- - {restChildren} - {footerChild} - -
+ + {restChildren} + {footerChild} + ); }, { From 3ee6b63795f7889d9f0a07057729952ca3205768 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Mon, 24 Nov 2025 16:30:03 -0500 Subject: [PATCH 20/43] add layout comments --- packages/wizard/src/Wizard/Wizard.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/wizard/src/Wizard/Wizard.tsx b/packages/wizard/src/Wizard/Wizard.tsx index cb67c24167..c1d2a7a4ed 100644 --- a/packages/wizard/src/Wizard/Wizard.tsx +++ b/packages/wizard/src/Wizard/Wizard.tsx @@ -54,6 +54,10 @@ export const Wizard = CompoundComponent( [setActiveStep, stepChildren.length], ); + /** + * NB: We're intentionally do _not_ wrap the `Wizard` (or `WizardStep`) component in a container element. + * This is done to ensure the Wizard is flexible, and can be rendered in any containing layout. + */ return ( Date: Tue, 25 Nov 2025 13:20:18 -0500 Subject: [PATCH 21/43] Squashed commit of the following: commit 4fd366809bbfe07bea85c64377aa707c4ef410c9 Author: Adam Michael Thompson Date: Tue Nov 25 13:18:59 2025 -0500 fixes ack reset test commit 4f024b1cdbde2a0908e0b70540b6522878a7e975 Author: Adam Michael Thompson Date: Tue Nov 25 13:11:35 2025 -0500 fixes tests commit f919ecc0fa1155acfdcdbb50e42d710f102ae727 Author: Adam Michael Thompson Date: Tue Nov 25 13:11:29 2025 -0500 updates tsdoc commit 6842bbb7edd877ab1a2306958c4ee19b409b4da0 Author: Adam Michael Thompson Date: Tue Nov 25 13:02:43 2025 -0500 updates readme --- .../src/WizardFooter/WizardFooter.spec.tsx | 158 +++++++++--------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/packages/wizard/src/WizardFooter/WizardFooter.spec.tsx b/packages/wizard/src/WizardFooter/WizardFooter.spec.tsx index bd9e8336d1..74be2869cd 100644 --- a/packages/wizard/src/WizardFooter/WizardFooter.spec.tsx +++ b/packages/wizard/src/WizardFooter/WizardFooter.spec.tsx @@ -295,101 +295,101 @@ describe('packages/wizard-footer', () => { expect(onStepChange).toHaveBeenCalledWith(1); expect(getByTestId('step-2')).toBeInTheDocument(); }); - }); - describe('requiresAcknowledgement', () => { - test('primary button is disabled when step requires acknowledgement and is not acknowledged', () => { - const { getByRole } = render( - - -
Step content
- -
-
, - ); + describe('requiresAcknowledgement', () => { + test('primary button is disabled when step requires acknowledgement and is not acknowledged', () => { + const { getByRole } = render( + + +
Step content
+ +
+
, + ); - const primaryButton = getByRole('button', { name: 'Continue' }); - expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); - }); + const primaryButton = getByRole('button', { name: 'Continue' }); + expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); + }); - test('primary button is enabled when step requires acknowledgement and is acknowledged', async () => { - const TestComponent = () => { - const { setAcknowledged } = useWizardStepContext(); - return ( - <> -
Step content
- - - + test('primary button is enabled when step requires acknowledgement and is acknowledged', async () => { + const TestComponent = () => { + const { setAcknowledged } = useWizardStepContext(); + return ( + <> +
Step content
+ + + + ); + }; + + const { getByRole } = render( + + + + + , ); - }; - const { getByRole } = render( - - - - - , - ); + const primaryButton = getByRole('button', { name: 'Continue' }); + expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); - const primaryButton = getByRole('button', { name: 'Continue' }); - expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); + await userEvent.click(getByRole('button', { name: 'Acknowledge' })); - await userEvent.click(getByRole('button', { name: 'Acknowledge' })); + expect(primaryButton).toHaveAttribute('aria-disabled', 'false'); + }); - expect(primaryButton).toHaveAttribute('aria-disabled', 'false'); - }); + test('primary button is enabled when step does not require acknowledgement', () => { + const { getByRole } = render( + + +
Step content
+ +
+
, + ); - test('primary button is enabled when step does not require acknowledgement', () => { - const { getByRole } = render( - - -
Step content
- -
-
, - ); + const primaryButton = getByRole('button', { name: 'Continue' }); + expect(primaryButton).toHaveAttribute('aria-disabled', 'false'); + }); - const primaryButton = getByRole('button', { name: 'Continue' }); - expect(primaryButton).toHaveAttribute('aria-disabled', 'false'); - }); + test('primary button can advance step after acknowledgement', async () => { + const TestComponent = () => { + const { setAcknowledged } = useWizardStepContext(); + return ( + <> +
Step content
+ + + + ); + }; - test('primary button can advance step after acknowledgement', async () => { - const TestComponent = () => { - const { setAcknowledged } = useWizardStepContext(); - return ( - <> -
Step content
- - - + const { getByRole, getByTestId } = render( + + +
Step 1
+ +
+ +
Step 2
+
+
, ); - }; - const { getByRole, getByTestId } = render( - - -
Step 1
- -
- -
Step 2
-
-
, - ); - - expect(getByTestId('step-1')).toBeInTheDocument(); + expect(getByTestId('step-1')).toBeInTheDocument(); - const primaryButton = getByRole('button', { name: 'Continue' }); - expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); + const primaryButton = getByRole('button', { name: 'Continue' }); + expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); - // Acknowledge the step - await userEvent.click(getByRole('button', { name: 'Acknowledge' })); - expect(primaryButton).toHaveAttribute('aria-disabled', 'false'); + // Acknowledge the step + await userEvent.click(getByRole('button', { name: 'Acknowledge' })); + expect(primaryButton).toHaveAttribute('aria-disabled', 'false'); - // Advance to next step - await userEvent.click(primaryButton); - expect(getByTestId('step-2')).toBeInTheDocument(); + // Advance to next step + await userEvent.click(primaryButton); + expect(getByTestId('step-2')).toBeInTheDocument(); + }); }); }); From 4cbc8157f4ebe84305fb6467d1da26288ee3170c Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 17:09:12 -0500 Subject: [PATCH 22/43] update provider props --- packages/wizard/src/WizardContext/WizardContext.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/wizard/src/WizardContext/WizardContext.tsx b/packages/wizard/src/WizardContext/WizardContext.tsx index a8d40c933d..b067016203 100644 --- a/packages/wizard/src/WizardContext/WizardContext.tsx +++ b/packages/wizard/src/WizardContext/WizardContext.tsx @@ -1,7 +1,8 @@ import React, { createContext, PropsWithChildren, useContext } from 'react'; -import { getLgIds } from '../utils/getLgIds'; -import { GetLgIdsReturnType } from '../utils/getLgIds'; +import { Optional } from '@leafygreen-ui/lib'; + +import { getLgIds, GetLgIdsReturnType } from '../utils/getLgIds'; export interface WizardContextData { /** @@ -43,7 +44,9 @@ export const WizardContext = createContext({ }); interface WizardProviderProps - extends PropsWithChildren> {} + extends PropsWithChildren< + Omit, 'isWizardContext'> + > {} export const WizardProvider = ({ children, From b3de84848a72d5c81ae11189a4906f6ee657d983 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Thu, 20 Nov 2025 16:35:38 -0500 Subject: [PATCH 23/43] add templates scope --- package.json | 3 ++- pnpm-workspace.yaml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 196927163b..8860fea8e3 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,8 @@ "@lg-charts": "charts", "@lg-chat": "chat", "@lg-tools": "tools", - "@lg-mcp-ui": "mcp-ui" + "@lg-mcp-ui": "mcp-ui", + "@lg-templates": "templates" } }, "keywords": [ diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 89652f8019..fa74b62b57 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,3 +6,4 @@ packages: - 'mcp-ui/*' - 'packages/*' - 'tools/*' + - 'templates/*' From d91825169815ed6c48f0943fb11bd2b32887ecaf Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Wed, 19 Nov 2025 17:02:50 -0500 Subject: [PATCH 24/43] init delete wizard --- .changeset/delete-wizard.md | 5 +++ templates/delete-wizard/README.md | 26 +++++++++++ templates/delete-wizard/package.json | 43 +++++++++++++++++++ .../src/DeleteWizard.stories.tsx | 17 ++++++++ .../src/DeleteWizard/DeleteWizard.spec.tsx | 11 +++++ .../src/DeleteWizard/DeleteWizard.styles.ts | 4 ++ .../src/DeleteWizard/DeleteWizard.tsx | 8 ++++ .../src/DeleteWizard/DeleteWizard.types.ts | 1 + .../delete-wizard/src/DeleteWizard/index.ts | 3 ++ templates/delete-wizard/src/index.ts | 1 + .../src/testing/getTestUtils.spec.tsx | 10 +++++ .../src/testing/getTestUtils.tsx | 15 +++++++ .../src/testing/getTestUtils.types.ts | 1 + templates/delete-wizard/src/testing/index.ts | 2 + templates/delete-wizard/src/utils/getLgIds.ts | 12 ++++++ templates/delete-wizard/tsconfig.json | 22 ++++++++++ 16 files changed, 181 insertions(+) create mode 100644 .changeset/delete-wizard.md create mode 100644 templates/delete-wizard/README.md create mode 100644 templates/delete-wizard/package.json create mode 100644 templates/delete-wizard/src/DeleteWizard.stories.tsx create mode 100644 templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx create mode 100644 templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts create mode 100644 templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx create mode 100644 templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts create mode 100644 templates/delete-wizard/src/DeleteWizard/index.ts create mode 100644 templates/delete-wizard/src/index.ts create mode 100644 templates/delete-wizard/src/testing/getTestUtils.spec.tsx create mode 100644 templates/delete-wizard/src/testing/getTestUtils.tsx create mode 100644 templates/delete-wizard/src/testing/getTestUtils.types.ts create mode 100644 templates/delete-wizard/src/testing/index.ts create mode 100644 templates/delete-wizard/src/utils/getLgIds.ts create mode 100644 templates/delete-wizard/tsconfig.json diff --git a/.changeset/delete-wizard.md b/.changeset/delete-wizard.md new file mode 100644 index 0000000000..44ffd7fe65 --- /dev/null +++ b/.changeset/delete-wizard.md @@ -0,0 +1,5 @@ +--- +'@lg-templates/delete-wizard': minor +--- + +Initial release of `DeleteWizard` diff --git a/templates/delete-wizard/README.md b/templates/delete-wizard/README.md new file mode 100644 index 0000000000..8a12af5f2f --- /dev/null +++ b/templates/delete-wizard/README.md @@ -0,0 +1,26 @@ + +# Delete Wizard + +![npm (scoped)](https://img.shields.io/npm/v/@leafygreen-ui/delete-wizard.svg) +#### [View on MongoDB.design](https://www.mongodb.design/component/delete-wizard/live-example/) + +## Installation + +### PNPM + +```shell +pnpm add @leafygreen-ui/delete-wizard +``` + +### Yarn + +```shell +yarn add @leafygreen-ui/delete-wizard +``` + +### NPM + +```shell +npm install @leafygreen-ui/delete-wizard +``` + diff --git a/templates/delete-wizard/package.json b/templates/delete-wizard/package.json new file mode 100644 index 0000000000..154eaf7e8a --- /dev/null +++ b/templates/delete-wizard/package.json @@ -0,0 +1,43 @@ + +{ + "name": "@lg-templates/delete-wizard", + "version": "0.0.1", + "description": "LeafyGreen UI Kit Delete Wizard", + "main": "./dist/umd/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts", + "license": "Apache-2.0", + "exports": { + ".": { + "require": "./dist/umd/index.js", + "import": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts" + }, + "./testing": { + "require": "./dist/umd/testing/index.js", + "import": "./dist/esm/testing/index.js", + "types": "./dist/types/testing/index.d.ts" + } + }, + "scripts": { + "build": "lg-build bundle", + "tsc": "lg-build tsc", + "docs": "lg-build docs" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@leafygreen-ui/emotion": "workspace:^", + "@leafygreen-ui/lib": "workspace:^", + "@lg-tools/test-harnesses": "workspace:^" + }, + "homepage": "https://github.com/mongodb/leafygreen-ui/tree/main/packages/delete-wizard", + "repository": { + "type": "git", + "url": "https://github.com/mongodb/leafygreen-ui" + }, + "bugs": { + "url": "https://jira.mongodb.org/projects/LG/summary" + } +} diff --git a/templates/delete-wizard/src/DeleteWizard.stories.tsx b/templates/delete-wizard/src/DeleteWizard.stories.tsx new file mode 100644 index 0000000000..7a62dee3f6 --- /dev/null +++ b/templates/delete-wizard/src/DeleteWizard.stories.tsx @@ -0,0 +1,17 @@ + +import React from 'react'; +import { StoryFn } from '@storybook/react'; + +import { DeleteWizard } from '.'; + +export default { + title: 'Components/DeleteWizard', + component: DeleteWizard, +} + +const Template: StoryFn = (props) => ( + +); + +export const Basic = Template.bind({}); + diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx new file mode 100644 index 0000000000..101cc3ea6b --- /dev/null +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx @@ -0,0 +1,11 @@ + +import React from 'react'; +import { render } from '@testing-library/react'; + +import { DeleteWizard } from '.'; + +describe('packages/delete-wizard', () => { + test('condition', () => { + + }) +}) diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts new file mode 100644 index 0000000000..928608f58d --- /dev/null +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts @@ -0,0 +1,4 @@ + +import { css } from '@leafygreen-ui/emotion'; + +export const baseStyles = css``; diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx new file mode 100644 index 0000000000..f734e3d00a --- /dev/null +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { DeleteWizardProps } from './DeleteWizard.types'; + +export function DeleteWizard({}: DeleteWizardProps) { + return
your content here
; +} + +DeleteWizard.displayName = 'DeleteWizard'; diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts new file mode 100644 index 0000000000..48439051d1 --- /dev/null +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts @@ -0,0 +1 @@ +export interface DeleteWizardProps {} \ No newline at end of file diff --git a/templates/delete-wizard/src/DeleteWizard/index.ts b/templates/delete-wizard/src/DeleteWizard/index.ts new file mode 100644 index 0000000000..1e8f652a8f --- /dev/null +++ b/templates/delete-wizard/src/DeleteWizard/index.ts @@ -0,0 +1,3 @@ + +export { DeleteWizard } from './DeleteWizard'; +export { type DeleteWizardProps } from './DeleteWizard.types'; diff --git a/templates/delete-wizard/src/index.ts b/templates/delete-wizard/src/index.ts new file mode 100644 index 0000000000..3914a0aacd --- /dev/null +++ b/templates/delete-wizard/src/index.ts @@ -0,0 +1 @@ +export { DeleteWizard, type DeleteWizardProps } from './DeleteWizard'; \ No newline at end of file diff --git a/templates/delete-wizard/src/testing/getTestUtils.spec.tsx b/templates/delete-wizard/src/testing/getTestUtils.spec.tsx new file mode 100644 index 0000000000..f8e07cebad --- /dev/null +++ b/templates/delete-wizard/src/testing/getTestUtils.spec.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { render } from '@testing-library/react'; + +import { DeleteWizard } from '.'; + +describe('packages/delete-wizard/getTestUtils', () => { + test('condition', () => { + + }) +}) diff --git a/templates/delete-wizard/src/testing/getTestUtils.tsx b/templates/delete-wizard/src/testing/getTestUtils.tsx new file mode 100644 index 0000000000..ad89a6e99d --- /dev/null +++ b/templates/delete-wizard/src/testing/getTestUtils.tsx @@ -0,0 +1,15 @@ +import { findByLgId, getByLgId, queryByLgId } from '@lg-tools/test-harnesses'; + +import { LgIdString } from '@leafygreen-ui/lib'; + +import { DEFAULT_LGID_ROOT, getLgIds } from '../utils/getLgIds'; + +import { TestUtilsReturnType } from './getTestUtils.types'; + +export const getTestUtils = ( + lgId: LgIdString = DEFAULT_LGID_ROOT, +): TestUtilsReturnType => { + const lgIds = getLgIds(lgId); + + return {}; +}; diff --git a/templates/delete-wizard/src/testing/getTestUtils.types.ts b/templates/delete-wizard/src/testing/getTestUtils.types.ts new file mode 100644 index 0000000000..50d2fb417a --- /dev/null +++ b/templates/delete-wizard/src/testing/getTestUtils.types.ts @@ -0,0 +1 @@ +export interface TestUtilsReturnType {} \ No newline at end of file diff --git a/templates/delete-wizard/src/testing/index.ts b/templates/delete-wizard/src/testing/index.ts new file mode 100644 index 0000000000..4c102995fa --- /dev/null +++ b/templates/delete-wizard/src/testing/index.ts @@ -0,0 +1,2 @@ +export { getTestUtils } from './getTestUtils'; +export { type TestUtilsReturnType } from './getTestUtils.types'; diff --git a/templates/delete-wizard/src/utils/getLgIds.ts b/templates/delete-wizard/src/utils/getLgIds.ts new file mode 100644 index 0000000000..d3adcf2af9 --- /dev/null +++ b/templates/delete-wizard/src/utils/getLgIds.ts @@ -0,0 +1,12 @@ +import { LgIdString } from '@leafygreen-ui/lib'; + +export const DEFAULT_LGID_ROOT = 'lg-delete_wizard'; + +export const getLgIds = (root: LgIdString = DEFAULT_LGID_ROOT) => { + const ids = { + root, + } as const; + return ids; +}; + +export type GetLgIdsReturnType = ReturnType; diff --git a/templates/delete-wizard/tsconfig.json b/templates/delete-wizard/tsconfig.json new file mode 100644 index 0000000000..07d2d44b00 --- /dev/null +++ b/templates/delete-wizard/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "@lg-tools/build/config/package.tsconfig.json", + "compilerOptions": { + "paths": { + "@leafygreen-ui/icon/dist/*": ["../../packages/icon/src/generated/*"], + "@leafygreen-ui/*": ["../../packages/*/src"] + } + }, + "include": ["src/**/*"], + "exclude": ["**/*.spec.*", "**/*.stories.*"], + "references": [ + { + "path": "../../packages/emotion" + }, + { + "path": "../../packages/lib" + }, + { + "path": "../../tools/test-harnesses" + } + ] +} From 2a5fb7f58e0f19c0c4f55034c5a489d7370fe8d8 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Thu, 20 Nov 2025 16:52:19 -0500 Subject: [PATCH 25/43] initial port of delete wizard --- .changeset/delete-wizard-0.md | 5 +++ package.json | 2 +- templates/delete-wizard/package.json | 7 ++-- .../src/DeleteWizard/DeleteWizard.styles.ts | 8 +++-- .../src/DeleteWizard/DeleteWizard.tsx | 35 ++++++++++++++++--- .../src/DeleteWizard/DeleteWizard.types.ts | 6 +++- .../src/DeleteWizard/DeleteWizardFooter.tsx | 32 +++++++++++++++++ .../src/DeleteWizard/DeleteWizardHeader.tsx | 12 +++++++ templates/delete-wizard/tsconfig.json | 12 +++++++ 9 files changed, 109 insertions(+), 10 deletions(-) create mode 100644 .changeset/delete-wizard-0.md create mode 100644 templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx create mode 100644 templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx diff --git a/.changeset/delete-wizard-0.md b/.changeset/delete-wizard-0.md new file mode 100644 index 0000000000..44ffd7fe65 --- /dev/null +++ b/.changeset/delete-wizard-0.md @@ -0,0 +1,5 @@ +--- +'@lg-templates/delete-wizard': minor +--- + +Initial release of `DeleteWizard` diff --git a/package.json b/package.json index 8860fea8e3..761202a655 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "link": "lg link", "lint": "lg lint", "prepublishOnly": "pnpm run build && pnpm build:ts-downlevel && pnpm build:docs", - "publish": "pnpm changeset publish --public", + "publish": "pnpm publish -r", "reset:react17": "npx node ./scripts/react17/reset.mjs; pnpm run init", "slackbot": "lg slackbot", "start": "npx storybook dev -p 9001 --no-version-updates --no-open", diff --git a/templates/delete-wizard/package.json b/templates/delete-wizard/package.json index 154eaf7e8a..f36575a839 100644 --- a/templates/delete-wizard/package.json +++ b/templates/delete-wizard/package.json @@ -1,5 +1,4 @@ - -{ + { "name": "@lg-templates/delete-wizard", "version": "0.0.1", "description": "LeafyGreen UI Kit Delete Wizard", @@ -28,8 +27,12 @@ "access": "public" }, "dependencies": { + "@leafygreen-ui/canvas-header": "workspace:^", + "@leafygreen-ui/compound-component": "workspace:^", "@leafygreen-ui/emotion": "workspace:^", "@leafygreen-ui/lib": "workspace:^", + "@leafygreen-ui/tokens": "workspace:^", + "@leafygreen-ui/wizard": "workspace:^", "@lg-tools/test-harnesses": "workspace:^" }, "homepage": "https://github.com/mongodb/leafygreen-ui/tree/main/packages/delete-wizard", diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts index 928608f58d..eb5bfe26ac 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts @@ -1,4 +1,8 @@ - import { css } from '@leafygreen-ui/emotion'; -export const baseStyles = css``; +export const wizardWrapperStyles = css` + display: flex; + flex-direction: column; + position: relative; + overflow: scroll; +`; diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx index f734e3d00a..f6063ce1f9 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx @@ -1,8 +1,35 @@ import React from 'react'; + +import { + CompoundComponent, + findChild, +} from '@leafygreen-ui/compound-component'; +import { cx } from '@leafygreen-ui/emotion'; +import { Wizard } from '@leafygreen-ui/wizard'; + +import { wizardWrapperStyles } from './DeleteWizard.styles'; import { DeleteWizardProps } from './DeleteWizard.types'; +import { DeleteWizardFooter } from './DeleteWizardFooter'; +import { + DeleteWizardHeader, + DeleteWizardHeaderKey, +} from './DeleteWizardHeader'; -export function DeleteWizard({}: DeleteWizardProps) { - return
your content here
; -} +export const DeleteWizard = CompoundComponent( + ({ children, className }: DeleteWizardProps) => { + const header = findChild(children, DeleteWizardHeaderKey); -DeleteWizard.displayName = 'DeleteWizard'; + return ( +
+ {header} + {children} +
+ ); + }, + { + displayName: 'DeleteWizard', + Header: DeleteWizardHeader, + Step: Wizard.Step, + Footer: DeleteWizardFooter, + }, +); diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts index 48439051d1..fda7cd823f 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts @@ -1 +1,5 @@ -export interface DeleteWizardProps {} \ No newline at end of file +import { ComponentProps } from 'react'; + +export interface DeleteWizardProps extends ComponentProps<'div'> { + header?: React.ReactNode; +} diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx new file mode 100644 index 0000000000..e4e13016c1 --- /dev/null +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx @@ -0,0 +1,32 @@ +import React from 'react'; + +import { css, cx } from '@leafygreen-ui/emotion'; +import { breakpoints } from '@leafygreen-ui/tokens'; +import { Wizard, WizardFooterProps } from '@leafygreen-ui/wizard'; + +const footerStyles = css` + position: sticky; + bottom: 0; +`; + +const footerContentStyles = css` + margin-inline: auto; + max-width: ${breakpoints.XLDesktop}px; +`; + +/** + * A wrapper around Wizard.Footer with embedded styles for the DeleteWizard template + */ +export const DeleteWizardFooter = ({ + className, + contentClassName, + ...props +}: WizardFooterProps) => { + return ( + + ); +}; diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx new file mode 100644 index 0000000000..ae5caf6fc9 --- /dev/null +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx @@ -0,0 +1,12 @@ +import { CanvasHeader } from '@leafygreen-ui/canvas-header'; +import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; + +export const DeleteWizardHeaderKey = 'Header' as const; + +/** + * A wrapper around the {@link CanvasHeader} component for embedding into a DeleteWizard + */ +export const DeleteWizardHeader = CompoundSubComponent(CanvasHeader, { + displayName: 'DeleteWizardHeader', + key: DeleteWizardHeaderKey, +}); diff --git a/templates/delete-wizard/tsconfig.json b/templates/delete-wizard/tsconfig.json index 07d2d44b00..58b2d68a76 100644 --- a/templates/delete-wizard/tsconfig.json +++ b/templates/delete-wizard/tsconfig.json @@ -9,12 +9,24 @@ "include": ["src/**/*"], "exclude": ["**/*.spec.*", "**/*.stories.*"], "references": [ + { + "path": "../../packages/canvas-header" + }, + { + "path": "../../packages/compound-component" + }, { "path": "../../packages/emotion" }, { "path": "../../packages/lib" }, + { + "path": "../../packages/tokens" + }, + { + "path": "../../packages/wizard" + }, { "path": "../../tools/test-harnesses" } From ce5d1f08efd3c62a9f9ae4f0bce68898b4ce8b28 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Thu, 20 Nov 2025 17:26:02 -0500 Subject: [PATCH 26/43] adds DeleteWizardStepContents --- .changeset/delete-wizard-1.md | 5 ++ packages/wizard/src/index.ts | 1 + templates/delete-wizard/package.json | 9 ++- .../src/DeleteWizard.stories.tsx | 66 ++++++++++++++++--- .../src/DeleteWizard/DeleteWizard.styles.ts | 2 +- .../src/DeleteWizard/DeleteWizard.tsx | 2 + .../DeleteWizard/DeleteWizardStepContents.tsx | 28 ++++++++ .../compoundComponentProperties.ts | 8 +++ templates/delete-wizard/tsconfig.json | 6 ++ 9 files changed, 114 insertions(+), 13 deletions(-) create mode 100644 .changeset/delete-wizard-1.md create mode 100644 templates/delete-wizard/src/DeleteWizard/DeleteWizardStepContents.tsx create mode 100644 templates/delete-wizard/src/DeleteWizard/compoundComponentProperties.ts diff --git a/.changeset/delete-wizard-1.md b/.changeset/delete-wizard-1.md new file mode 100644 index 0000000000..9e9074b474 --- /dev/null +++ b/.changeset/delete-wizard-1.md @@ -0,0 +1,5 @@ +--- +'@lg-templates/delete-wizard': minor +--- + +Adds DeleteWizardStepContents diff --git a/packages/wizard/src/index.ts b/packages/wizard/src/index.ts index 1c370edbab..56018ddec8 100644 --- a/packages/wizard/src/index.ts +++ b/packages/wizard/src/index.ts @@ -1,3 +1,4 @@ +export { WizardSubComponentProperties } from './constants'; export { Wizard, type WizardProps } from './Wizard'; export { useWizardContext, diff --git a/templates/delete-wizard/package.json b/templates/delete-wizard/package.json index f36575a839..8b17ea2cd4 100644 --- a/templates/delete-wizard/package.json +++ b/templates/delete-wizard/package.json @@ -1,6 +1,6 @@ - { +{ "name": "@lg-templates/delete-wizard", - "version": "0.0.1", + "version": "0.1.0-local.1", "description": "LeafyGreen UI Kit Delete Wizard", "main": "./dist/umd/index.js", "module": "./dist/esm/index.js", @@ -42,5 +42,10 @@ }, "bugs": { "url": "https://jira.mongodb.org/projects/LG/summary" + }, + "devDependencies": { + "@faker-js/faker": "^10.1.0", + "@leafygreen-ui/icon": "workspace:^", + "@leafygreen-ui/typography": "workspace:^" } } diff --git a/templates/delete-wizard/src/DeleteWizard.stories.tsx b/templates/delete-wizard/src/DeleteWizard.stories.tsx index 7a62dee3f6..32bf7b0b0b 100644 --- a/templates/delete-wizard/src/DeleteWizard.stories.tsx +++ b/templates/delete-wizard/src/DeleteWizard.stories.tsx @@ -1,17 +1,63 @@ - import React from 'react'; -import { StoryFn } from '@storybook/react'; +import { faker } from '@faker-js/faker'; +import { StoryObj } from '@storybook/react'; + +import { css } from '@leafygreen-ui/emotion'; +import BeakerIcon from '@leafygreen-ui/icon/Beaker'; +import { BackLink, Body } from '@leafygreen-ui/typography'; import { DeleteWizard } from '.'; +faker.seed(0); + export default { - title: 'Components/DeleteWizard', + title: 'Templates/DeleteWizard', component: DeleteWizard, -} - -const Template: StoryFn = (props) => ( - -); - -export const Basic = Template.bind({}); +}; +export const LiveExample: StoryObj = { + render: _args => ( +
+ + } + backLink={Back} + className={css` + margin-inline: 72px; + `} + /> + + + {faker.lorem + .paragraphs(12) + .split('\n') + .map((p, i) => ( + {p} + ))} + + + + +
+ ), +}; diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts index eb5bfe26ac..80ced6203e 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts @@ -1,8 +1,8 @@ import { css } from '@leafygreen-ui/emotion'; export const wizardWrapperStyles = css` + position: relative; display: flex; flex-direction: column; - position: relative; overflow: scroll; `; diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx index f6063ce1f9..13092ac828 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx @@ -14,6 +14,7 @@ import { DeleteWizardHeader, DeleteWizardHeaderKey, } from './DeleteWizardHeader'; +import { DeleteWizardStepContents } from './DeleteWizardStepContents'; export const DeleteWizard = CompoundComponent( ({ children, className }: DeleteWizardProps) => { @@ -30,6 +31,7 @@ export const DeleteWizard = CompoundComponent( displayName: 'DeleteWizard', Header: DeleteWizardHeader, Step: Wizard.Step, + StepContent: DeleteWizardStepContents, Footer: DeleteWizardFooter, }, ); diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardStepContents.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardStepContents.tsx new file mode 100644 index 0000000000..4e0369477b --- /dev/null +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardStepContents.tsx @@ -0,0 +1,28 @@ +import React, { ComponentProps } from 'react'; + +import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; +import { css, cx } from '@leafygreen-ui/emotion'; + +import { DeleteWizardCompoundComponentProperties } from './compoundComponentProperties'; + +export const DeleteWizardStepContents = CompoundSubComponent( + ({ children, className, ...rest }: ComponentProps<'div'>) => { + return ( +
+ {children} +
+ ); + }, + { + displayName: 'DeleteWizardStepContents', + key: DeleteWizardCompoundComponentProperties.StepContent, + }, +); diff --git a/templates/delete-wizard/src/DeleteWizard/compoundComponentProperties.ts b/templates/delete-wizard/src/DeleteWizard/compoundComponentProperties.ts new file mode 100644 index 0000000000..663fd93a51 --- /dev/null +++ b/templates/delete-wizard/src/DeleteWizard/compoundComponentProperties.ts @@ -0,0 +1,8 @@ +import { WizardSubComponentProperties } from '@leafygreen-ui/wizard'; + +export const DeleteWizardCompoundComponentProperties = { + Header: 'DeleteWizardHeader', + Step: WizardSubComponentProperties.Step, + StepContent: 'DeleteWizardStepContent', + Footer: WizardSubComponentProperties.Footer, +}; diff --git a/templates/delete-wizard/tsconfig.json b/templates/delete-wizard/tsconfig.json index 58b2d68a76..85a2a89e70 100644 --- a/templates/delete-wizard/tsconfig.json +++ b/templates/delete-wizard/tsconfig.json @@ -18,12 +18,18 @@ { "path": "../../packages/emotion" }, + { + "path": "../../packages/icon" + }, { "path": "../../packages/lib" }, { "path": "../../packages/tokens" }, + { + "path": "../../packages/typography" + }, { "path": "../../packages/wizard" }, From ccb74db44637727da05265cc451d3526be012c85 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 11:33:46 -0500 Subject: [PATCH 27/43] Exports DeleteWizard . Header and Step wrappers --- .changeset/delete-wizard-1.md | 2 +- .changeset/delete-wizard-2.md | 5 + .../src/DeleteWizard.stories.tsx | 130 +++++++++++++----- .../src/DeleteWizard/DeleteWizard.tsx | 51 +++++-- .../src/DeleteWizard/DeleteWizard.types.ts | 8 +- .../src/DeleteWizard/DeleteWizardHeader.tsx | 4 +- .../src/DeleteWizard/DeleteWizardStep.tsx | 25 ++++ .../DeleteWizard/DeleteWizardStepContents.tsx | 11 +- .../compoundComponentProperties.ts | 6 +- .../delete-wizard/src/DeleteWizard/index.ts | 4 +- templates/delete-wizard/src/index.ts | 7 +- .../src/testUtils/ExampleStepContent.tsx | 55 ++++++++ 12 files changed, 246 insertions(+), 62 deletions(-) create mode 100644 .changeset/delete-wizard-2.md create mode 100644 templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx create mode 100644 templates/delete-wizard/src/testUtils/ExampleStepContent.tsx diff --git a/.changeset/delete-wizard-1.md b/.changeset/delete-wizard-1.md index 9e9074b474..729b25d35f 100644 --- a/.changeset/delete-wizard-1.md +++ b/.changeset/delete-wizard-1.md @@ -2,4 +2,4 @@ '@lg-templates/delete-wizard': minor --- -Adds DeleteWizardStepContents +Adds `DeleteWizardStepContents` diff --git a/.changeset/delete-wizard-2.md b/.changeset/delete-wizard-2.md new file mode 100644 index 0000000000..2a2b7c13c6 --- /dev/null +++ b/.changeset/delete-wizard-2.md @@ -0,0 +1,5 @@ +--- +'@lg-templates/delete-wizard': minor +--- + +Exports `DeleteWizard.Header` and `DeleteWizard.Step` wrappers. Re-exports wizard hooks to ensure correct context diff --git a/templates/delete-wizard/src/DeleteWizard.stories.tsx b/templates/delete-wizard/src/DeleteWizard.stories.tsx index 32bf7b0b0b..4790b00d2f 100644 --- a/templates/delete-wizard/src/DeleteWizard.stories.tsx +++ b/templates/delete-wizard/src/DeleteWizard.stories.tsx @@ -1,14 +1,28 @@ +/* eslint-disable no-console */ import React from 'react'; import { faker } from '@faker-js/faker'; import { StoryObj } from '@storybook/react'; import { css } from '@leafygreen-ui/emotion'; import BeakerIcon from '@leafygreen-ui/icon/Beaker'; +import TrashIcon from '@leafygreen-ui/icon/Trash'; import { BackLink, Body } from '@leafygreen-ui/typography'; +import { ExampleStepContent } from './testUtils/ExampleStepContent'; import { DeleteWizard } from '.'; faker.seed(0); +const demoResourceName = faker.database.mongodbObjectId(); +const demoSteps = [ + { + description: faker.lorem.paragraph(), + content: faker.lorem.paragraphs(24), + }, + { + description: faker.lorem.paragraph(), + content: faker.lorem.paragraphs(24), + }, +]; export default { title: 'Templates/DeleteWizard', @@ -16,48 +30,94 @@ export default { }; export const LiveExample: StoryObj = { - render: _args => ( -
- { + const handleCancel = () => { + console.log('[STORYBOOK]: Cancelling wizard. Reloading iFrame'); + window.location.reload(); + }; + + const handleDelete = () => { + alert('[STORYBOOK]: Deleting thing!'); + console.log('[STORYBOOK]: Deleting thing! Reloading iFrame'); + window.location.reload(); + }; + + return ( +
- } - backLink={Back} + - - + } + backLink={Back} className={css` margin-inline: 72px; `} - > - {faker.lorem - .paragraphs(12) - .split('\n') - .map((p, i) => ( - {p} - ))} - - - - -
- ), + + + ( + {p} + ))} + /> + + + + + + + ( + {p} + ))} + /> + + , + variant: 'danger', + children: 'Delete my thing', + onClick: handleDelete, + }} + /> + +
+
+ ); + }, }; diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx index 13092ac828..9ca67cb533 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx @@ -5,33 +5,62 @@ import { findChild, } from '@leafygreen-ui/compound-component'; import { cx } from '@leafygreen-ui/emotion'; -import { Wizard } from '@leafygreen-ui/wizard'; +import { useWizardContext, Wizard } from '@leafygreen-ui/wizard'; +import { DeleteWizardSubComponentKeys } from './compoundComponentProperties'; import { wizardWrapperStyles } from './DeleteWizard.styles'; import { DeleteWizardProps } from './DeleteWizard.types'; import { DeleteWizardFooter } from './DeleteWizardFooter'; -import { - DeleteWizardHeader, - DeleteWizardHeaderKey, -} from './DeleteWizardHeader'; -import { DeleteWizardStepContents } from './DeleteWizardStepContents'; +import { DeleteWizardHeader } from './DeleteWizardHeader'; +import { DeleteWizardStep } from './DeleteWizardStep'; +import { DeleteWizardStepContent } from './DeleteWizardStepContents'; + +/** + * A re-export of `useWizardContext` specifically for this DeleteWizard + */ +export const useDeleteWizardContext = useWizardContext; +/** + * The parent DeleteWizard component. + * Pass a `DeleteWizard.Header` and any number of `DeleteWizard.Step`s as children + */ export const DeleteWizard = CompoundComponent( - ({ children, className }: DeleteWizardProps) => { - const header = findChild(children, DeleteWizardHeaderKey); + ({ activeStep, onStepChange, children, className }: DeleteWizardProps) => { + const header = findChild(children, DeleteWizardSubComponentKeys.Header); return (
{header} - {children} + + {children} +
); }, { displayName: 'DeleteWizard', + /** + * A wrapper around the {@link CanvasHeader} component for embedding into a DeleteWizard + */ Header: DeleteWizardHeader, - Step: Wizard.Step, - StepContent: DeleteWizardStepContents, + + /** + * A simple wrapper around Wizard.Step to ensure correct Wizard context + */ + Step: DeleteWizardStep, + + /** + * A styled `div` for use inside a `DeleteWizard.Step` to ensure proper page scrolling and footer positioning + */ + StepContent: DeleteWizardStepContent, + + /** + * A wrapper around Wizard.Footer with embedded styles for the DeleteWizard template. + * Render this inside of each Step with the relevant button props for that Step. + * + * Back and Primary buttons trigger onStepChange. + * Automatically renders the "Back" button for all Steps except the first + */ Footer: DeleteWizardFooter, }, ); diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts index fda7cd823f..ea069fcd32 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts @@ -1,5 +1,7 @@ import { ComponentProps } from 'react'; -export interface DeleteWizardProps extends ComponentProps<'div'> { - header?: React.ReactNode; -} +import { WizardProps } from '@leafygreen-ui/wizard'; + +export interface DeleteWizardProps + extends WizardProps, + Omit, 'children'> {} diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx index ae5caf6fc9..0183f1e4f3 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx @@ -1,12 +1,12 @@ import { CanvasHeader } from '@leafygreen-ui/canvas-header'; import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; -export const DeleteWizardHeaderKey = 'Header' as const; +import { DeleteWizardSubComponentKeys } from './compoundComponentProperties'; /** * A wrapper around the {@link CanvasHeader} component for embedding into a DeleteWizard */ export const DeleteWizardHeader = CompoundSubComponent(CanvasHeader, { displayName: 'DeleteWizardHeader', - key: DeleteWizardHeaderKey, + key: DeleteWizardSubComponentKeys.Header, }); diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx new file mode 100644 index 0000000000..1f8b961161 --- /dev/null +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx @@ -0,0 +1,25 @@ +import React from 'react'; + +import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; +import { + useWizardStepContext, + Wizard, + WizardStepProps, +} from '@leafygreen-ui/wizard'; + +import { DeleteWizardSubComponentKeys } from './compoundComponentProperties'; + +export const useDeleteWizardStepContext = useWizardStepContext; + +/** + * A wrapper around Wizard.Step + */ +export const DeleteWizardStep = CompoundSubComponent( + (props: WizardStepProps) => { + return ; + }, + { + displayName: 'DeleteWizardStep', + key: DeleteWizardSubComponentKeys.Step, + }, +); diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardStepContents.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardStepContents.tsx index 4e0369477b..f35b41d75b 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizardStepContents.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardStepContents.tsx @@ -3,9 +3,12 @@ import React, { ComponentProps } from 'react'; import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; import { css, cx } from '@leafygreen-ui/emotion'; -import { DeleteWizardCompoundComponentProperties } from './compoundComponentProperties'; +import { DeleteWizardSubComponentKeys } from './compoundComponentProperties'; -export const DeleteWizardStepContents = CompoundSubComponent( +/** + * A styled `div` for use inside a `DeleteWizard.Step` to ensure proper page scrolling and footer positioning + */ +export const DeleteWizardStepContent = CompoundSubComponent( ({ children, className, ...rest }: ComponentProps<'div'>) => { return (
) => { + const { isAcknowledged, setAcknowledged, requiresAcknowledgement } = + useDeleteWizardStepContext(); + return ( +
+

Step {index + 1}

+ {description} + +
+ {requiresAcknowledgement && ( +
+ setAcknowledged(e.target.checked)} + /> + +
+ )} + {content} +
+
+ ); +}; From 43ff5eb0145d7c21f0330801e27f5d359bc947e5 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 13:15:17 -0500 Subject: [PATCH 28/43] Create delete-wizard-3.md --- .changeset/delete-wizard-3.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/delete-wizard-3.md diff --git a/.changeset/delete-wizard-3.md b/.changeset/delete-wizard-3.md new file mode 100644 index 0000000000..7f814e5e8d --- /dev/null +++ b/.changeset/delete-wizard-3.md @@ -0,0 +1,5 @@ +--- +'@lg-templates/delete-wizard': minor +--- + +Bumps `wizard` dependency \ No newline at end of file From c6c09c64f7ed18fe7a4387ed6953b99587059913 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 15:33:44 -0500 Subject: [PATCH 29/43] updates delete-wizard changeset --- .changeset/delete-wizard.md | 54 ++++++++++++++++++++++++++++++++++++- .changeset/wizard.md | 2 +- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/.changeset/delete-wizard.md b/.changeset/delete-wizard.md index 44ffd7fe65..f1d26deb48 100644 --- a/.changeset/delete-wizard.md +++ b/.changeset/delete-wizard.md @@ -2,4 +2,56 @@ '@lg-templates/delete-wizard': minor --- -Initial release of `DeleteWizard` +Initial release of `DeleteWizard`. + +```tsx + + + + +
Step 1 contents
+ + + + + + +
Step 2 contents
+ + , + variant: 'danger', + children: 'Delete my thing', + onClick: handleDelete, + }} + /> + + +``` + +### DeleteWizard +Establishes a context, and only renders the `activeStep` (managed internally, or provided with the `activeStep` prop). Accepts a `DeleteWizard.Header` and any number of `DeleteWizard.Step`s as children. + +`DeleteWizard` and all sub-components include template styling. + + +### DeleteWizard.Header +A convenience wrapper around `CanvasHeader` + +### DeleteWizard.Step +A convenience wrapper around `Wizard.Step` to ensure the correct context. +Like the basic `Wizard.Step`, of `requiresAcknowledgement` is true, the step must have `isAcknowledged` set in context, (or passed in as a controlled prop) for the Footer's primary button to be enabled. (see the Wizard and DeleteWizard demos in Storybook) + + +### DeleteWizard.StepContent +A styled `div` for use inside a `DeleteWizard.Step` to ensure proper page scrolling and footer positioning + +### DeleteWizard.Footer +A wrapper around Wizard.Footer with embedded styles for the DeleteWizard template \ No newline at end of file diff --git a/.changeset/wizard.md b/.changeset/wizard.md index 43a87b291f..6e23497c04 100644 --- a/.changeset/wizard.md +++ b/.changeset/wizard.md @@ -4,4 +4,4 @@ Initial Wizard package release. -See [README.md](./README.md) for usage guidelines \ No newline at end of file +See [README.md](./README.md) for usage guidelines From ed43e5b882730ee5506159721699179afef30add Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 15:20:26 -0500 Subject: [PATCH 30/43] rm temp changesets rm delete wizard changesets --- .changeset/delete-wizard-0.md | 5 ----- .changeset/delete-wizard-1.md | 5 ----- .changeset/delete-wizard-2.md | 5 ----- .changeset/delete-wizard-3.md | 5 ----- 4 files changed, 20 deletions(-) delete mode 100644 .changeset/delete-wizard-0.md delete mode 100644 .changeset/delete-wizard-1.md delete mode 100644 .changeset/delete-wizard-2.md delete mode 100644 .changeset/delete-wizard-3.md diff --git a/.changeset/delete-wizard-0.md b/.changeset/delete-wizard-0.md deleted file mode 100644 index 44ffd7fe65..0000000000 --- a/.changeset/delete-wizard-0.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@lg-templates/delete-wizard': minor ---- - -Initial release of `DeleteWizard` diff --git a/.changeset/delete-wizard-1.md b/.changeset/delete-wizard-1.md deleted file mode 100644 index 729b25d35f..0000000000 --- a/.changeset/delete-wizard-1.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@lg-templates/delete-wizard': minor ---- - -Adds `DeleteWizardStepContents` diff --git a/.changeset/delete-wizard-2.md b/.changeset/delete-wizard-2.md deleted file mode 100644 index 2a2b7c13c6..0000000000 --- a/.changeset/delete-wizard-2.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@lg-templates/delete-wizard': minor ---- - -Exports `DeleteWizard.Header` and `DeleteWizard.Step` wrappers. Re-exports wizard hooks to ensure correct context diff --git a/.changeset/delete-wizard-3.md b/.changeset/delete-wizard-3.md deleted file mode 100644 index 7f814e5e8d..0000000000 --- a/.changeset/delete-wizard-3.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@lg-templates/delete-wizard': minor ---- - -Bumps `wizard` dependency \ No newline at end of file From c7a1cfe93a30e95544d798c2e6dc01410be6123d Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 16:01:44 -0500 Subject: [PATCH 31/43] build --- pnpm-lock.yaml | 34 +++++++++++++++++++++++++++++++ tools/install/src/ALL_PACKAGES.ts | 1 + 2 files changed, 35 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8747f81e5f..cf30e28620 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3877,6 +3877,40 @@ importers: specifier: workspace:^ version: link:../typography + templates/delete-wizard: + dependencies: + '@leafygreen-ui/canvas-header': + specifier: workspace:^ + version: link:../../packages/canvas-header + '@leafygreen-ui/compound-component': + specifier: workspace:^ + version: link:../../packages/compound-component + '@leafygreen-ui/emotion': + specifier: workspace:^ + version: link:../../packages/emotion + '@leafygreen-ui/lib': + specifier: workspace:^ + version: link:../../packages/lib + '@leafygreen-ui/tokens': + specifier: workspace:^ + version: link:../../packages/tokens + '@leafygreen-ui/wizard': + specifier: workspace:^ + version: link:../../packages/wizard + '@lg-tools/test-harnesses': + specifier: workspace:^ + version: link:../../tools/test-harnesses + devDependencies: + '@faker-js/faker': + specifier: ^10.1.0 + version: 10.1.0 + '@leafygreen-ui/icon': + specifier: workspace:^ + version: link:../../packages/icon + '@leafygreen-ui/typography': + specifier: workspace:^ + version: link:../../packages/typography + tools/build: dependencies: '@babel/core': diff --git a/tools/install/src/ALL_PACKAGES.ts b/tools/install/src/ALL_PACKAGES.ts index c2964f5cfc..ad324772f6 100644 --- a/tools/install/src/ALL_PACKAGES.ts +++ b/tools/install/src/ALL_PACKAGES.ts @@ -118,4 +118,5 @@ export const ALL_PACKAGES = [ '@lg-tools/update', '@lg-tools/validate', '@lg-mcp-ui/list-databases', + '@lg-templates/delete-wizard', ] as const; From 2ebe1cb7759eacdea69c802590026e7b0f2b13b7 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 16:17:54 -0500 Subject: [PATCH 32/43] Update README.md --- templates/delete-wizard/README.md | 56 ++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/templates/delete-wizard/README.md b/templates/delete-wizard/README.md index 8a12af5f2f..b4488e72a3 100644 --- a/templates/delete-wizard/README.md +++ b/templates/delete-wizard/README.md @@ -1,7 +1,7 @@ - # Delete Wizard ![npm (scoped)](https://img.shields.io/npm/v/@leafygreen-ui/delete-wizard.svg) + #### [View on MongoDB.design](https://www.mongodb.design/component/delete-wizard/live-example/) ## Installation @@ -24,3 +24,57 @@ yarn add @leafygreen-ui/delete-wizard npm install @leafygreen-ui/delete-wizard ``` +```tsx + + + + +
Step 1 contents
+ + + + + + +
Step 2 contents
+ + , + variant: 'danger', + children: 'Delete my thing', + onClick: handleDelete, + }} + /> + + +``` + +### DeleteWizard + +Establishes a context, and only renders the `activeStep` (managed internally, or provided with the `activeStep` prop). Accepts a `DeleteWizard.Header` and any number of `DeleteWizard.Step`s as children. + +`DeleteWizard` and all sub-components include template styling. + +### DeleteWizard.Header + +A convenience wrapper around `CanvasHeader` + +### DeleteWizard.Step + +A convenience wrapper around `Wizard.Step` to ensure the correct context. +Like the basic `Wizard.Step`, of `requiresAcknowledgement` is true, the step must have `isAcknowledged` set in context, (or passed in as a controlled prop) for the Footer's primary button to be enabled. (see the Wizard and DeleteWizard demos in Storybook) + +### DeleteWizard.StepContent + +A styled `div` for use inside a `DeleteWizard.Step` to ensure proper page scrolling and footer positioning + +### DeleteWizard.Footer + +A wrapper around Wizard.Footer with embedded styles for the DeleteWizard template From d4579b112514402a2ca356caf17903309a40a1d4 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Fri, 21 Nov 2025 18:57:28 -0500 Subject: [PATCH 33/43] lint --- .../delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx | 7 ++----- templates/delete-wizard/src/testing/getTestUtils.spec.tsx | 6 ++---- templates/delete-wizard/src/testing/getTestUtils.types.ts | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx index 101cc3ea6b..609e4ac6b7 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx @@ -1,11 +1,8 @@ - import React from 'react'; import { render } from '@testing-library/react'; import { DeleteWizard } from '.'; describe('packages/delete-wizard', () => { - test('condition', () => { - - }) -}) + test('condition', () => {}); +}); diff --git a/templates/delete-wizard/src/testing/getTestUtils.spec.tsx b/templates/delete-wizard/src/testing/getTestUtils.spec.tsx index f8e07cebad..6b1e673bdc 100644 --- a/templates/delete-wizard/src/testing/getTestUtils.spec.tsx +++ b/templates/delete-wizard/src/testing/getTestUtils.spec.tsx @@ -4,7 +4,5 @@ import { render } from '@testing-library/react'; import { DeleteWizard } from '.'; describe('packages/delete-wizard/getTestUtils', () => { - test('condition', () => { - - }) -}) + test('condition', () => {}); +}); diff --git a/templates/delete-wizard/src/testing/getTestUtils.types.ts b/templates/delete-wizard/src/testing/getTestUtils.types.ts index 50d2fb417a..4b2df87c73 100644 --- a/templates/delete-wizard/src/testing/getTestUtils.types.ts +++ b/templates/delete-wizard/src/testing/getTestUtils.types.ts @@ -1 +1 @@ -export interface TestUtilsReturnType {} \ No newline at end of file +export interface TestUtilsReturnType {} From a6a7da980af93bff845e09a3e1fb596476716f01 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Mon, 24 Nov 2025 14:52:16 -0500 Subject: [PATCH 34/43] Adds onCancel/onDelete handlers --- .changeset/delete-wizard.md | 31 +++-- .../src/DeleteWizard.stories.tsx | 30 ++--- .../src/DeleteWizard/DeleteWizard.tsx | 25 ++-- .../src/DeleteWizard/DeleteWizard.types.ts | 7 +- .../src/DeleteWizard/DeleteWizardContext.tsx | 43 +++++++ .../src/DeleteWizard/DeleteWizardFooter.tsx | 118 +++++++++++++++--- .../delete-wizard/src/DeleteWizard/index.ts | 8 +- 7 files changed, 204 insertions(+), 58 deletions(-) create mode 100644 templates/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx diff --git a/.changeset/delete-wizard.md b/.changeset/delete-wizard.md index f1d26deb48..76dd935f1d 100644 --- a/.changeset/delete-wizard.md +++ b/.changeset/delete-wizard.md @@ -5,7 +5,11 @@ Initial release of `DeleteWizard`. ```tsx - + @@ -14,9 +18,9 @@ Initial release of `DeleteWizard`.
Step 1 contents
@@ -25,12 +29,9 @@ Initial release of `DeleteWizard`.
Step 2 contents
, - variant: 'danger', - children: 'Delete my thing', - onClick: handleDelete, - }} + backButtonText="Go back" + cancelButtonText="Cancel flow" + primaryButtonText='Delete my thing' /> @@ -41,7 +42,6 @@ Establishes a context, and only renders the `activeStep` (managed internally, or `DeleteWizard` and all sub-components include template styling. - ### DeleteWizard.Header A convenience wrapper around `CanvasHeader` @@ -54,4 +54,11 @@ Like the basic `Wizard.Step`, of `requiresAcknowledgement` is true, the step mu A styled `div` for use inside a `DeleteWizard.Step` to ensure proper page scrolling and footer positioning ### DeleteWizard.Footer -A wrapper around Wizard.Footer with embedded styles for the DeleteWizard template \ No newline at end of file +A wrapper around `Wizard.Footer` with embedded styles and convenience props for the DeleteWizard template. +`DeleteWizard.Footer` accepts optional `backButtonText`, `cancelButtonText` and `primaryButtonText` props for simpler wizard creation. +The primary button variant is defined based on the `activeStep`— `"danger"` for the final steps, and `"primary"` for all preceding steps. +Also defines the `leftGlyph` to for the final step. + +You can override this behavior by providing the button props object (see FormFooter). + +Use the top level `onDelete`, `onCancel` and `onStepChange` callbacks to handle footer button clicks. \ No newline at end of file diff --git a/templates/delete-wizard/src/DeleteWizard.stories.tsx b/templates/delete-wizard/src/DeleteWizard.stories.tsx index 4790b00d2f..48c463eb33 100644 --- a/templates/delete-wizard/src/DeleteWizard.stories.tsx +++ b/templates/delete-wizard/src/DeleteWizard.stories.tsx @@ -5,7 +5,6 @@ import { StoryObj } from '@storybook/react'; import { css } from '@leafygreen-ui/emotion'; import BeakerIcon from '@leafygreen-ui/icon/Beaker'; -import TrashIcon from '@leafygreen-ui/icon/Trash'; import { BackLink, Body } from '@leafygreen-ui/typography'; import { ExampleStepContent } from './testUtils/ExampleStepContent'; @@ -50,6 +49,10 @@ export const LiveExample: StoryObj = { window.location.reload(); }; + const handleStepChange = step => { + console.log('[STORYBOOK] step changed to ', step); + }; + return (
= { > = { /> @@ -104,16 +105,9 @@ export const LiveExample: StoryObj = { /> , - variant: 'danger', - children: 'Delete my thing', - onClick: handleDelete, - }} + backButtonText="Go back" + cancelButtonText="Cancel wizard" + primaryButtonText="Delete the prop" /> diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx index 9ca67cb533..f1d407db59 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx @@ -5,35 +5,40 @@ import { findChild, } from '@leafygreen-ui/compound-component'; import { cx } from '@leafygreen-ui/emotion'; -import { useWizardContext, Wizard } from '@leafygreen-ui/wizard'; +import { Wizard } from '@leafygreen-ui/wizard'; import { DeleteWizardSubComponentKeys } from './compoundComponentProperties'; import { wizardWrapperStyles } from './DeleteWizard.styles'; import { DeleteWizardProps } from './DeleteWizard.types'; +import { DeleteWizardContextProvider } from './DeleteWizardContext'; import { DeleteWizardFooter } from './DeleteWizardFooter'; import { DeleteWizardHeader } from './DeleteWizardHeader'; import { DeleteWizardStep } from './DeleteWizardStep'; import { DeleteWizardStepContent } from './DeleteWizardStepContents'; -/** - * A re-export of `useWizardContext` specifically for this DeleteWizard - */ -export const useDeleteWizardContext = useWizardContext; - /** * The parent DeleteWizard component. * Pass a `DeleteWizard.Header` and any number of `DeleteWizard.Step`s as children */ export const DeleteWizard = CompoundComponent( - ({ activeStep, onStepChange, children, className }: DeleteWizardProps) => { + ({ + activeStep, + children, + className, + onCancel, + onDelete, + onStepChange, + }: DeleteWizardProps) => { const header = findChild(children, DeleteWizardSubComponentKeys.Header); return (
{header} - - {children} - + + + {children} + +
); }, diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts index ea069fcd32..853800f6c9 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts @@ -1,7 +1,10 @@ -import { ComponentProps } from 'react'; +import { ComponentProps, MouseEventHandler } from 'react'; import { WizardProps } from '@leafygreen-ui/wizard'; export interface DeleteWizardProps extends WizardProps, - Omit, 'children'> {} + Omit, 'children'> { + onCancel?: MouseEventHandler; + onDelete?: MouseEventHandler; +} diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx new file mode 100644 index 0000000000..f0e27b3320 --- /dev/null +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx @@ -0,0 +1,43 @@ +import React, { createContext, PropsWithChildren, useContext } from 'react'; + +import { useWizardContext, WizardContextData } from '@leafygreen-ui/wizard'; + +import { DeleteWizardProps } from './DeleteWizard.types'; + +export interface DeleteWizardContextData { + onCancel?: DeleteWizardProps['onCancel']; + onDelete?: DeleteWizardProps['onDelete']; +} + +export const DeleteWizardContext = createContext({}); + +export const DeleteWizardContextProvider = ({ + children, + onCancel, + onDelete, +}: PropsWithChildren) => { + return ( + + {children} + + ); +}; + +/** + * A re-export of `useWizardContext` specifically for this DeleteWizard + */ +export const useDeleteWizardContext = (): DeleteWizardContextData & + WizardContextData => { + const wizardContext = useWizardContext(); + const deleteWizardContext = useContext(DeleteWizardContext); + + return { + ...wizardContext, + ...deleteWizardContext, + }; +}; diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx index e4e13016c1..2d1efc676b 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx @@ -1,8 +1,18 @@ -import React from 'react'; +import React, { MouseEventHandler } from 'react'; +import { Variant } from '@leafygreen-ui/button'; +import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; import { css, cx } from '@leafygreen-ui/emotion'; +import TrashIcon from '@leafygreen-ui/icon/Trash'; +import { Either } from '@leafygreen-ui/lib'; import { breakpoints } from '@leafygreen-ui/tokens'; -import { Wizard, WizardFooterProps } from '@leafygreen-ui/wizard'; +import { + Wizard, + WizardFooterProps, + WizardSubComponentProperties, +} from '@leafygreen-ui/wizard'; + +import { useDeleteWizardContext } from './DeleteWizardContext'; const footerStyles = css` position: sticky; @@ -14,19 +24,97 @@ const footerContentStyles = css` max-width: ${breakpoints.XLDesktop}px; `; +type DeleteWizardFooterProps = Either< + WizardFooterProps & { + /** + * Sets the Back button text (for steps > 1) + */ + backButtonText?: string; + /** + * Sets the Cancel button text. + * The Cancel button will not render if no text is provided + */ + cancelButtonText?: string; + + /** + * Sets the Primary button text. + * + * The Primary button icon and variant are set automatically based on the activeStep index. + * Provide a `primaryButtonProps` to override this behavior + */ + primaryButtonText?: string; + }, + 'primaryButtonProps' | 'primaryButtonText' +>; + /** * A wrapper around Wizard.Footer with embedded styles for the DeleteWizard template */ -export const DeleteWizardFooter = ({ - className, - contentClassName, - ...props -}: WizardFooterProps) => { - return ( - - ); -}; +export const DeleteWizardFooter = CompoundSubComponent( + ({ + className, + contentClassName, + primaryButtonText, + cancelButtonText, + backButtonText, + primaryButtonProps, + cancelButtonProps, + backButtonProps, + ...props + }: DeleteWizardFooterProps) => { + const { activeStep, totalSteps, onCancel, onDelete } = + useDeleteWizardContext(); + const isLastStep = activeStep === totalSteps - 1; + + const handlePrimaryButtonClick: MouseEventHandler< + HTMLButtonElement + > = e => { + primaryButtonProps?.onClick?.(e); + + if (isLastStep) { + onDelete?.(e); + } + }; + + const handleCancelButtonClick: MouseEventHandler = e => { + cancelButtonProps?.onClick?.(e); + onCancel?.(e); + }; + + const _primaryButtonProps = { + children: primaryButtonText ?? '', + variant: isLastStep ? Variant.Danger : Variant.Primary, + leftGlyph: isLastStep ? : undefined, + ...primaryButtonProps, + // we define `onClick` after spreading props so this handler will always take precedence + onClick: handlePrimaryButtonClick, + }; + + const _backButtonProps = { + children: backButtonText, + ...backButtonProps, + }; + + const _cancelButtonProps = { + children: cancelButtonText, + ...cancelButtonProps, + // we define `onClick` after spreading props so this handler will always take precedence + onClick: handleCancelButtonClick, + }; + + return ( + + ); + }, + { + displayName: 'DeleteWizardFooter', + key: WizardSubComponentProperties.Footer, + }, +); diff --git a/templates/delete-wizard/src/DeleteWizard/index.ts b/templates/delete-wizard/src/DeleteWizard/index.ts index 6f385d4d71..fcc50a1d6d 100644 --- a/templates/delete-wizard/src/DeleteWizard/index.ts +++ b/templates/delete-wizard/src/DeleteWizard/index.ts @@ -1,3 +1,9 @@ -export { DeleteWizard, useDeleteWizardContext } from './DeleteWizard'; +export { DeleteWizard } from './DeleteWizard'; export { type DeleteWizardProps } from './DeleteWizard.types'; +export { + DeleteWizardContext, + type DeleteWizardContextData, + DeleteWizardContextProvider, + useDeleteWizardContext, +} from './DeleteWizardContext'; export { useDeleteWizardStepContext } from './DeleteWizardStep'; From 973a3650561358a410d96d45955e2441d36a5631 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Mon, 24 Nov 2025 15:07:25 -0500 Subject: [PATCH 35/43] rm DeleteWizardStepContent --- .../src/DeleteWizard.stories.tsx | 36 +++++++-------- .../src/DeleteWizard/DeleteWizard.tsx | 6 --- .../src/DeleteWizard/DeleteWizardStep.tsx | 46 +++++++++++++++++-- .../DeleteWizard/DeleteWizardStepContents.tsx | 31 ------------- 4 files changed, 58 insertions(+), 61 deletions(-) delete mode 100644 templates/delete-wizard/src/DeleteWizard/DeleteWizardStepContents.tsx diff --git a/templates/delete-wizard/src/DeleteWizard.stories.tsx b/templates/delete-wizard/src/DeleteWizard.stories.tsx index 48c463eb33..d5908f9a61 100644 --- a/templates/delete-wizard/src/DeleteWizard.stories.tsx +++ b/templates/delete-wizard/src/DeleteWizard.stories.tsx @@ -78,16 +78,14 @@ export const LiveExample: StoryObj = { margin-inline: 72px; `} /> - - - ( - {p} - ))} - /> - + + ( + {p} + ))} + /> = { - - ( - {p} - ))} - /> - + ( + {p} + ))} + /> diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx index f1d407db59..ec1a453c77 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx @@ -14,7 +14,6 @@ import { DeleteWizardContextProvider } from './DeleteWizardContext'; import { DeleteWizardFooter } from './DeleteWizardFooter'; import { DeleteWizardHeader } from './DeleteWizardHeader'; import { DeleteWizardStep } from './DeleteWizardStep'; -import { DeleteWizardStepContent } from './DeleteWizardStepContents'; /** * The parent DeleteWizard component. @@ -54,11 +53,6 @@ export const DeleteWizard = CompoundComponent( */ Step: DeleteWizardStep, - /** - * A styled `div` for use inside a `DeleteWizard.Step` to ensure proper page scrolling and footer positioning - */ - StepContent: DeleteWizardStepContent, - /** * A wrapper around Wizard.Footer with embedded styles for the DeleteWizard template. * Render this inside of each Step with the relevant button props for that Step. diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx index 1f8b961161..d00ae5a6ba 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx @@ -1,22 +1,60 @@ -import React from 'react'; +import React, { ComponentProps } from 'react'; -import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; +import { + CompoundSubComponent, + filterChildren, + findChild, +} from '@leafygreen-ui/compound-component'; +import { css, cx } from '@leafygreen-ui/emotion'; import { useWizardStepContext, Wizard, WizardStepProps, + WizardSubComponentProperties, } from '@leafygreen-ui/wizard'; import { DeleteWizardSubComponentKeys } from './compoundComponentProperties'; export const useDeleteWizardStepContext = useWizardStepContext; +export interface DeleteWizardStepProps + extends WizardStepProps, + ComponentProps<'div'> {} /** * A wrapper around Wizard.Step */ export const DeleteWizardStep = CompoundSubComponent( - (props: WizardStepProps) => { - return ; + ({ + children, + className, + requiresAcknowledgement, + ...rest + }: DeleteWizardStepProps) => { + const footerChild = findChild( + children, + WizardSubComponentProperties.Footer, + ); + + const restChildren = filterChildren(children, [ + WizardSubComponentProperties.Footer, + ]); + + return ( + +
+ {restChildren} +
+ {footerChild} +
+ ); }, { displayName: 'DeleteWizardStep', diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardStepContents.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardStepContents.tsx deleted file mode 100644 index f35b41d75b..0000000000 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizardStepContents.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React, { ComponentProps } from 'react'; - -import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; -import { css, cx } from '@leafygreen-ui/emotion'; - -import { DeleteWizardSubComponentKeys } from './compoundComponentProperties'; - -/** - * A styled `div` for use inside a `DeleteWizard.Step` to ensure proper page scrolling and footer positioning - */ -export const DeleteWizardStepContent = CompoundSubComponent( - ({ children, className, ...rest }: ComponentProps<'div'>) => { - return ( -
- {children} -
- ); - }, - { - displayName: 'DeleteWizardStepContent', - key: DeleteWizardSubComponentKeys.StepContent, - }, -); From 3160611e64537f59f8fe775c99dce003e372bc51 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Tue, 25 Nov 2025 13:49:12 -0500 Subject: [PATCH 36/43] fixes dependencies --- .../delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx index 2d1efc676b..073c913706 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx @@ -1,8 +1,8 @@ import React, { MouseEventHandler } from 'react'; -import { Variant } from '@leafygreen-ui/button'; import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; import { css, cx } from '@leafygreen-ui/emotion'; +import { PrimaryStandardButtonProps } from '@leafygreen-ui/form-footer'; import TrashIcon from '@leafygreen-ui/icon/Trash'; import { Either } from '@leafygreen-ui/lib'; import { breakpoints } from '@leafygreen-ui/tokens'; @@ -81,9 +81,9 @@ export const DeleteWizardFooter = CompoundSubComponent( onCancel?.(e); }; - const _primaryButtonProps = { + const _primaryButtonProps: PrimaryStandardButtonProps = { children: primaryButtonText ?? '', - variant: isLastStep ? Variant.Danger : Variant.Primary, + variant: isLastStep ? 'danger' : 'primary', leftGlyph: isLastStep ? : undefined, ...primaryButtonProps, // we define `onClick` after spreading props so this handler will always take precedence From c9ba5064b22e939e929b172c5867be4e12d3cb4f Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Wed, 26 Nov 2025 14:39:22 -0500 Subject: [PATCH 37/43] fixes dependencies --- pnpm-lock.yaml | 9 ++++++--- templates/delete-wizard/package.json | 3 ++- .../src/DeleteWizard/DeleteWizardFooter.tsx | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf30e28620..4cba3a81a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3888,6 +3888,12 @@ importers: '@leafygreen-ui/emotion': specifier: workspace:^ version: link:../../packages/emotion + '@leafygreen-ui/form-footer': + specifier: workspace:^ + version: link:../../packages/form-footer + '@leafygreen-ui/icon': + specifier: workspace:^ + version: link:../../packages/icon '@leafygreen-ui/lib': specifier: workspace:^ version: link:../../packages/lib @@ -3904,9 +3910,6 @@ importers: '@faker-js/faker': specifier: ^10.1.0 version: 10.1.0 - '@leafygreen-ui/icon': - specifier: workspace:^ - version: link:../../packages/icon '@leafygreen-ui/typography': specifier: workspace:^ version: link:../../packages/typography diff --git a/templates/delete-wizard/package.json b/templates/delete-wizard/package.json index 8b17ea2cd4..b64c3dc88e 100644 --- a/templates/delete-wizard/package.json +++ b/templates/delete-wizard/package.json @@ -30,6 +30,8 @@ "@leafygreen-ui/canvas-header": "workspace:^", "@leafygreen-ui/compound-component": "workspace:^", "@leafygreen-ui/emotion": "workspace:^", + "@leafygreen-ui/form-footer": "workspace:^", + "@leafygreen-ui/icon": "workspace:^", "@leafygreen-ui/lib": "workspace:^", "@leafygreen-ui/tokens": "workspace:^", "@leafygreen-ui/wizard": "workspace:^", @@ -45,7 +47,6 @@ }, "devDependencies": { "@faker-js/faker": "^10.1.0", - "@leafygreen-ui/icon": "workspace:^", "@leafygreen-ui/typography": "workspace:^" } } diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx index 073c913706..bf6ab2aef1 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx @@ -2,7 +2,7 @@ import React, { MouseEventHandler } from 'react'; import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; import { css, cx } from '@leafygreen-ui/emotion'; -import { PrimaryStandardButtonProps } from '@leafygreen-ui/form-footer'; +import { type PrimaryStandardButtonProps } from '@leafygreen-ui/form-footer'; import TrashIcon from '@leafygreen-ui/icon/Trash'; import { Either } from '@leafygreen-ui/lib'; import { breakpoints } from '@leafygreen-ui/tokens'; From 9f3b30d1597ec5f71c3341257fac25e621c2ff5f Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Wed, 26 Nov 2025 15:11:41 -0500 Subject: [PATCH 38/43] adds lgids and test utils --- packages/wizard/src/index.ts | 1 + .../src/DeleteWizard/DeleteWizard.tsx | 23 +++++- .../src/DeleteWizard/DeleteWizardContext.tsx | 9 ++- .../src/DeleteWizard/DeleteWizardHeader.tsx | 17 ++-- .../src/DeleteWizard/DeleteWizardStep.tsx | 3 + .../src/testing/getTestUtils.tsx | 77 ++++++++++++++++++- .../src/testing/getTestUtils.types.ts | 20 ++++- templates/delete-wizard/src/testing/index.ts | 2 +- templates/delete-wizard/src/utils/getLgIds.ts | 6 ++ 9 files changed, 144 insertions(+), 14 deletions(-) diff --git a/packages/wizard/src/index.ts b/packages/wizard/src/index.ts index 56018ddec8..5a3311bcca 100644 --- a/packages/wizard/src/index.ts +++ b/packages/wizard/src/index.ts @@ -1,4 +1,5 @@ export { WizardSubComponentProperties } from './constants'; +export { getLgIds } from './utils/getLgIds'; export { Wizard, type WizardProps } from './Wizard'; export { useWizardContext, diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx index ec1a453c77..0609be8ca7 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx @@ -7,6 +7,8 @@ import { import { cx } from '@leafygreen-ui/emotion'; import { Wizard } from '@leafygreen-ui/wizard'; +import { DEFAULT_LGID_ROOT, getLgIds } from '../utils/getLgIds'; + import { DeleteWizardSubComponentKeys } from './compoundComponentProperties'; import { wizardWrapperStyles } from './DeleteWizard.styles'; import { DeleteWizardProps } from './DeleteWizard.types'; @@ -27,14 +29,29 @@ export const DeleteWizard = CompoundComponent( onCancel, onDelete, onStepChange, + 'data-lgid': dataLgId = DEFAULT_LGID_ROOT, + ...rest }: DeleteWizardProps) => { + const lgIds = getLgIds(dataLgId); const header = findChild(children, DeleteWizardSubComponentKeys.Header); return ( -
+
{header} - - + + {children} diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx index f0e27b3320..146140f9ab 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx @@ -2,25 +2,32 @@ import React, { createContext, PropsWithChildren, useContext } from 'react'; import { useWizardContext, WizardContextData } from '@leafygreen-ui/wizard'; +import { getLgIds, GetLgIdsReturnType } from '../utils/getLgIds'; + import { DeleteWizardProps } from './DeleteWizard.types'; export interface DeleteWizardContextData { onCancel?: DeleteWizardProps['onCancel']; onDelete?: DeleteWizardProps['onDelete']; + lgIds: GetLgIdsReturnType; } -export const DeleteWizardContext = createContext({}); +export const DeleteWizardContext = createContext({ + lgIds: getLgIds(), +}); export const DeleteWizardContextProvider = ({ children, onCancel, onDelete, + lgIds, }: PropsWithChildren) => { return ( {children} diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx index 0183f1e4f3..97d9f442d0 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx @@ -1,4 +1,6 @@ -import { CanvasHeader } from '@leafygreen-ui/canvas-header'; +import React from 'react'; + +import { CanvasHeader, CanvasHeaderProps } from '@leafygreen-ui/canvas-header'; import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; import { DeleteWizardSubComponentKeys } from './compoundComponentProperties'; @@ -6,7 +8,12 @@ import { DeleteWizardSubComponentKeys } from './compoundComponentProperties'; /** * A wrapper around the {@link CanvasHeader} component for embedding into a DeleteWizard */ -export const DeleteWizardHeader = CompoundSubComponent(CanvasHeader, { - displayName: 'DeleteWizardHeader', - key: DeleteWizardSubComponentKeys.Header, -}); +export const DeleteWizardHeader = CompoundSubComponent( + (props: CanvasHeaderProps) => { + return ; + }, + { + displayName: 'DeleteWizardHeader', + key: DeleteWizardSubComponentKeys.Header, + }, +); diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx index d00ae5a6ba..69fb69bf8e 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx @@ -14,6 +14,7 @@ import { } from '@leafygreen-ui/wizard'; import { DeleteWizardSubComponentKeys } from './compoundComponentProperties'; +import { useDeleteWizardContext } from './DeleteWizardContext'; export const useDeleteWizardStepContext = useWizardStepContext; @@ -30,6 +31,7 @@ export const DeleteWizardStep = CompoundSubComponent( requiresAcknowledgement, ...rest }: DeleteWizardStepProps) => { + const { lgIds } = useDeleteWizardContext(); const footerChild = findChild( children, WizardSubComponentProperties.Footer, @@ -48,6 +50,7 @@ export const DeleteWizardStep = CompoundSubComponent( `, className, )} + data-lgid={lgIds.step} {...rest} > {restChildren} diff --git a/templates/delete-wizard/src/testing/getTestUtils.tsx b/templates/delete-wizard/src/testing/getTestUtils.tsx index ad89a6e99d..634561bb3a 100644 --- a/templates/delete-wizard/src/testing/getTestUtils.tsx +++ b/templates/delete-wizard/src/testing/getTestUtils.tsx @@ -1,15 +1,86 @@ import { findByLgId, getByLgId, queryByLgId } from '@lg-tools/test-harnesses'; import { LgIdString } from '@leafygreen-ui/lib'; +import { getTestUtils as getWizardTestUtils } from '@leafygreen-ui/wizard/testing'; import { DEFAULT_LGID_ROOT, getLgIds } from '../utils/getLgIds'; -import { TestUtilsReturnType } from './getTestUtils.types'; +import { DeleteWizardTestUtilsReturnType } from './getTestUtils.types'; export const getTestUtils = ( lgId: LgIdString = DEFAULT_LGID_ROOT, -): TestUtilsReturnType => { +): DeleteWizardTestUtilsReturnType => { const lgIds = getLgIds(lgId); - return {}; + // Get the Wizard test utils (the DeleteWizard wraps a Wizard component internally) + const wizardUtils = getWizardTestUtils(lgId); + + /** + * @returns the DeleteWizard root element using the `data-lgid` data attribute. + * Will throw if no elements match or if more than one match is found. + */ + const getDeleteWizard = () => getByLgId!(lgIds.root); + + /** + * @returns the DeleteWizard root element using the `data-lgid` data attribute or `null` if no elements match. + * Will throw if more than one match is found. + */ + const queryDeleteWizard = () => queryByLgId!(lgIds.root); + + /** + * @returns a promise that resolves to the DeleteWizard root element using the `data-lgid` data attribute. + * The promise is rejected if no elements match or if more than one match is found. + */ + const findDeleteWizard = () => findByLgId!(lgIds.root); + + /** + * @returns the DeleteWizard Header element using the `data-lgid` data attribute. + * Will throw if no elements match or if more than one match is found. + */ + const getHeader = () => getByLgId!(lgIds.header); + + /** + * @returns the DeleteWizard Header element using the `data-lgid` data attribute or `null` if no elements match. + * Will throw if more than one match is found. + */ + const queryHeader = () => queryByLgId!(lgIds.header); + + /** + * @returns a promise that resolves to the DeleteWizard Header element using the `data-lgid` data attribute. + * The promise is rejected if no elements match or if more than one match is found. + */ + const findHeader = () => findByLgId!(lgIds.header); + + /** + * @returns the currently active WizardStep element using the `data-lgid` data attribute. + * Will throw if no elements match or if more than one match is found. + */ + const getActiveStep = () => getByLgId!(lgIds.step); + + /** + * @returns the currently active WizardStep element using the `data-lgid` data attribute or `null` if no elements match. + * Will throw if more than one match is found. + */ + const queryActiveStep = () => queryByLgId!(lgIds.step); + + /** + * @returns a promise that resolves to the currently active WizardStep element using the `data-lgid` data attribute. + * The promise is rejected if no elements match or if more than one match is found. + */ + const findActiveStep = () => findByLgId!(lgIds.step); + + return { + // Spread all Wizard test utils (footer, buttons, etc.) + ...wizardUtils, + // DeleteWizard-specific utils + getDeleteWizard, + queryDeleteWizard, + findDeleteWizard, + getHeader, + queryHeader, + findHeader, + getActiveStep, + queryActiveStep, + findActiveStep, + }; }; diff --git a/templates/delete-wizard/src/testing/getTestUtils.types.ts b/templates/delete-wizard/src/testing/getTestUtils.types.ts index 4b2df87c73..86367125d7 100644 --- a/templates/delete-wizard/src/testing/getTestUtils.types.ts +++ b/templates/delete-wizard/src/testing/getTestUtils.types.ts @@ -1 +1,19 @@ -export interface TestUtilsReturnType {} +import { TestUtilsReturnType as WizardTestUtilsReturnType } from '@leafygreen-ui/wizard/testing'; + +export interface DeleteWizardTestUtilsReturnType + extends WizardTestUtilsReturnType { + // DeleteWizard root element utils + getDeleteWizard: () => HTMLElement; + queryDeleteWizard: () => HTMLElement | null; + findDeleteWizard: () => Promise; + + // Header utils + getHeader: () => HTMLElement; + queryHeader: () => HTMLElement | null; + findHeader: () => Promise; + + // Active Step utils (from Wizard) + getActiveStep: () => HTMLElement; + queryActiveStep: () => HTMLElement | null; + findActiveStep: () => Promise; +} diff --git a/templates/delete-wizard/src/testing/index.ts b/templates/delete-wizard/src/testing/index.ts index 4c102995fa..d07ed40dec 100644 --- a/templates/delete-wizard/src/testing/index.ts +++ b/templates/delete-wizard/src/testing/index.ts @@ -1,2 +1,2 @@ export { getTestUtils } from './getTestUtils'; -export { type TestUtilsReturnType } from './getTestUtils.types'; +export { type DeleteWizardTestUtilsReturnType } from './getTestUtils.types'; diff --git a/templates/delete-wizard/src/utils/getLgIds.ts b/templates/delete-wizard/src/utils/getLgIds.ts index d3adcf2af9..d8013c828d 100644 --- a/templates/delete-wizard/src/utils/getLgIds.ts +++ b/templates/delete-wizard/src/utils/getLgIds.ts @@ -1,10 +1,16 @@ import { LgIdString } from '@leafygreen-ui/lib'; +import { getLgIds as getWizardLgIds } from '@leafygreen-ui/wizard'; export const DEFAULT_LGID_ROOT = 'lg-delete_wizard'; export const getLgIds = (root: LgIdString = DEFAULT_LGID_ROOT) => { + const wizardRoot: LgIdString = `${root}-wizard`; + const wizardLgIds = getWizardLgIds(wizardRoot); const ids = { root, + header: `${root}-header`, + wizard: wizardRoot, + ...wizardLgIds, } as const; return ids; }; From 51228b9e89985ce9411cf95ee820ff5447e46d8e Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Wed, 26 Nov 2025 15:29:17 -0500 Subject: [PATCH 39/43] creates delete wizard tests --- .../src/DeleteWizard/DeleteWizard.spec.tsx | 246 +++++++++++++++++- .../src/DeleteWizard/DeleteWizardHeader.tsx | 7 +- .../src/testUtils/ExampleStepContent.tsx | 1 + .../src/testing/getTestUtils.tsx | 2 +- 4 files changed, 252 insertions(+), 4 deletions(-) diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx index 609e4ac6b7..411d5bcb2e 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx @@ -1,8 +1,252 @@ import React from 'react'; import { render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { getTestUtils } from '../testing'; import { DeleteWizard } from '.'; describe('packages/delete-wizard', () => { - test('condition', () => {}); + describe('rendering', () => { + test('renders step 1 by default', () => { + const { getByTestId, queryByTestId } = render( + + +
Step 1 content
+
+ +
Step 2 content
+
+
, + ); + + expect(getByTestId('step-1-content')).toBeInTheDocument(); + expect(queryByTestId('step-2-content')).not.toBeInTheDocument(); + }); + + test('renders a Header component as child', () => { + render( + + + +
Step 1 content
+
+
, + ); + + const { getHeader } = getTestUtils(); + const header = getHeader(); + + expect(header).toBeInTheDocument(); + expect(header).toHaveTextContent('Delete Cluster'); + }); + + test('renders Header above steps regardless of whether it is passed as the last react child', () => { + const { container } = render( + + +
Step 1 content
+
+ +
, + ); + + const { getHeader, getActiveStep } = getTestUtils(); + const header = getHeader(); + const activeStep = getActiveStep(); + + // Get the parent (DeleteWizard root) to check order + const deleteWizardRoot = container.firstChild as HTMLElement; + const children = Array.from(deleteWizardRoot.children); + + // Header should come before the active step in the DOM + const headerIndex = children.indexOf(header); + const stepIndex = children.findIndex(child => child.contains(activeStep)); + + expect(headerIndex).toBeLessThan(stepIndex); + }); + + test('does not render any non-step or non-header children', () => { + const { queryByTestId } = render( + + +
This should not render
+ +
Step 1 content
+
+ Also should not render +
, + ); + + // Non-DeleteWizard elements should not be rendered + expect(queryByTestId('invalid-element-1')).not.toBeInTheDocument(); + expect(queryByTestId('invalid-element-2')).not.toBeInTheDocument(); + // But valid children should be rendered + expect(queryByTestId('step-1-content')).toBeInTheDocument(); + }); + + test('renders the step via activeStep prop', () => { + const { queryByTestId, getByTestId } = render( + + + +
Step 1 content
+
+ +
Step 2 content
+
+
, + ); + + // Should render the second step when activeStep is 1 + expect(queryByTestId('step-1-content')).not.toBeInTheDocument(); + expect(getByTestId('step-2-content')).toBeInTheDocument(); + }); + }); + + describe('interaction', () => { + test('renders step 2 when primary button is clicked', () => { + const { getByTestId, queryByTestId } = render( + + + +
Step 1 content
+ +
+ +
Step 2 content
+ +
+
, + ); + + // Start at step 1 + expect(getByTestId('step-1-content')).toBeInTheDocument(); + expect(queryByTestId('step-2-content')).not.toBeInTheDocument(); + + // Click next to go to step 2 + const { getPrimaryButton } = getTestUtils(); + userEvent.click(getPrimaryButton()); + + expect(queryByTestId('step-1-content')).not.toBeInTheDocument(); + expect(getByTestId('step-2-content')).toBeInTheDocument(); + }); + + test('calls onStepChange when the primary button is clicked', () => { + const onStepChange = jest.fn(); + + render( + + + +
Step 1 content
+ +
+ +
Step 2 content
+ +
+
, + ); + + const { getPrimaryButton } = getTestUtils(); + userEvent.click(getPrimaryButton()); + + expect(onStepChange).toHaveBeenCalledWith(1); + }); + + test('calls onStepChange when the back button is clicked', () => { + const onStepChange = jest.fn(); + + render( + + + +
Step 1 content
+ +
+ +
Step 2 content
+ +
+
, + ); + + const { getBackButton } = getTestUtils(); + userEvent.click(getBackButton()); + + expect(onStepChange).toHaveBeenCalledWith(0); + }); + + test('calls onCancel when the cancel button is clicked', () => { + const onCancel = jest.fn(); + + render( + + + +
Step 1 content
+ +
+
, + ); + + const { getCancelButton } = getTestUtils(); + userEvent.click(getCancelButton()); + + expect(onCancel).toHaveBeenCalled(); + }); + + test('calls onDelete when the primary button is clicked on final step', () => { + const onDelete = jest.fn(); + + render( + + + +
Step 1 content
+ +
+ +
Step 2 content
+ +
+
, + ); + + const { getPrimaryButton } = getTestUtils(); + userEvent.click(getPrimaryButton()); + + expect(onDelete).toHaveBeenCalled(); + }); + + test('does not call onDelete when primary button is clicked on non-final step', () => { + const onDelete = jest.fn(); + + render( + + + +
Step 1 content
+ +
+ +
Step 2 content
+ +
+
, + ); + + const { getPrimaryButton } = getTestUtils(); + userEvent.click(getPrimaryButton()); + + expect(onDelete).not.toHaveBeenCalled(); + }); + }); }); diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx b/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx index 97d9f442d0..fd44bdd9d5 100644 --- a/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx +++ b/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx @@ -2,15 +2,18 @@ import React from 'react'; import { CanvasHeader, CanvasHeaderProps } from '@leafygreen-ui/canvas-header'; import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; +import { LgIdProps } from '@leafygreen-ui/lib'; import { DeleteWizardSubComponentKeys } from './compoundComponentProperties'; +import { useDeleteWizardContext } from './DeleteWizardContext'; /** * A wrapper around the {@link CanvasHeader} component for embedding into a DeleteWizard */ export const DeleteWizardHeader = CompoundSubComponent( - (props: CanvasHeaderProps) => { - return ; + (props: CanvasHeaderProps & LgIdProps) => { + const { lgIds } = useDeleteWizardContext(); + return ; }, { displayName: 'DeleteWizardHeader', diff --git a/templates/delete-wizard/src/testUtils/ExampleStepContent.tsx b/templates/delete-wizard/src/testUtils/ExampleStepContent.tsx index f36d6dcd6d..1c2e98fbc0 100644 --- a/templates/delete-wizard/src/testUtils/ExampleStepContent.tsx +++ b/templates/delete-wizard/src/testUtils/ExampleStepContent.tsx @@ -42,6 +42,7 @@ export const ExampleStepContent = ({ setAcknowledged(e.target.checked)} /> diff --git a/templates/delete-wizard/src/testing/getTestUtils.tsx b/templates/delete-wizard/src/testing/getTestUtils.tsx index 634561bb3a..9f33dda6a1 100644 --- a/templates/delete-wizard/src/testing/getTestUtils.tsx +++ b/templates/delete-wizard/src/testing/getTestUtils.tsx @@ -13,7 +13,7 @@ export const getTestUtils = ( const lgIds = getLgIds(lgId); // Get the Wizard test utils (the DeleteWizard wraps a Wizard component internally) - const wizardUtils = getWizardTestUtils(lgId); + const wizardUtils = getWizardTestUtils(lgIds.wizard); /** * @returns the DeleteWizard root element using the `data-lgid` data attribute. From 5f74d60372c7e1e3cdf1b5846cc27017f548ad14 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Wed, 26 Nov 2025 15:29:23 -0500 Subject: [PATCH 40/43] add stories --- .../src/DeleteWizard.stories.tsx | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/templates/delete-wizard/src/DeleteWizard.stories.tsx b/templates/delete-wizard/src/DeleteWizard.stories.tsx index d5908f9a61..ffe471d80f 100644 --- a/templates/delete-wizard/src/DeleteWizard.stories.tsx +++ b/templates/delete-wizard/src/DeleteWizard.stories.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { faker } from '@faker-js/faker'; import { StoryObj } from '@storybook/react'; +import { userEvent, within } from '@storybook/test'; import { css } from '@leafygreen-ui/emotion'; import BeakerIcon from '@leafygreen-ui/icon/Beaker'; @@ -33,6 +34,9 @@ export const LiveExample: StoryObj = { controls: { exclude: ['children', 'onStepChange'], }, + chromatic: { + disableSnapshot: true, + }, }, args: { activeStep: undefined, @@ -111,3 +115,30 @@ export const LiveExample: StoryObj = { ); }, }; + +export const Step1: StoryObj = { + args: { + activeStep: 0, + }, + render: LiveExample.render, +}; + +export const Step2Default: StoryObj = { + args: { + activeStep: 1, + }, + render: LiveExample.render, +}; + +export const Step2Acknowledged: StoryObj = { + args: { + activeStep: 1, + }, + play: ({ canvasElement }) => { + const checkbox = within(canvasElement).getByTestId( + 'acknowledgement-checkbox', + ); + userEvent.click(checkbox); + }, + render: LiveExample.render, +}; From 306b7173b5cf0e243411b916d2a0cd4f681c0845 Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Wed, 26 Nov 2025 16:01:48 -0500 Subject: [PATCH 41/43] Squashed commit of the following: commit 8a1373e676a79e4f4ea6226cee85423089ae56dc Author: Adam Thompson <2414030+TheSonOfThomp@users.noreply.github.com> Date: Wed Nov 26 15:59:13 2025 -0500 [LG-5566] tests(Wizard) Implement TestUtils & LGIDs for `Wizard` (#3338) * rm step wrapper * rm descendants dep * export WizardProvider * delete-wizard-demo private endpoints useFetchRequiredActionTableData renam ReqAct cards composable basic table stream processing card federated db card applications card clusters card wizard step context Delete requiredActionsConfig.tsx re-enable wizard add useRequiredActionAcknowledgements mv required action. add skeleton Update ModelApiKeysCard.tsx * Update pnpm Update package.json * fix wizard changes * Adds `requiresAcknowledgement` prop to Wizard.Step * Implements `isAcknowledged` state inside provider * Update Wizard.stories.tsx * rm delete demo * Update wizard.md * rm temp changesets * Update README.md * Update WizardStep.spec.tsx * footer tests * Update Wizard.spec.tsx * update package json * update provider props * revert toast changes? * Update .npmrc * Update pnpm-lock.yaml * Update WizardStep.spec.tsx * exports form footer types * Update WizardFooter.types.ts * adds `totalSteps` to wizard context * fix bad merge * adds LGIDs * adds test utils * lint * fix bad merge * removes Step test utils * add layout comments * form-footer lgids * updates wizard testids * updates readme * updates tsdoc * fixes tests * fixes ack reset test * Squashed commit of the following: commit 4fd366809bbfe07bea85c64377aa707c4ef410c9 Author: Adam Michael Thompson Date: Tue Nov 25 13:18:59 2025 -0500 fixes ack reset test commit 4f024b1cdbde2a0908e0b70540b6522878a7e975 Author: Adam Michael Thompson Date: Tue Nov 25 13:11:35 2025 -0500 fixes tests commit f919ecc0fa1155acfdcdbb50e42d710f102ae727 Author: Adam Michael Thompson Date: Tue Nov 25 13:11:29 2025 -0500 updates tsdoc commit 6842bbb7edd877ab1a2306958c4ee19b409b4da0 Author: Adam Michael Thompson Date: Tue Nov 25 13:02:43 2025 -0500 updates readme * Update WizardStep.spec.tsx * Update WizardContext.tsx * Update WizardStep.spec.tsx * Squashed commit of the following: commit 982ef7265162cf067f489f2c10fe162394d16736 Author: Adam Michael Thompson Date: Tue Nov 25 13:52:15 2025 -0500 Update WizardStep.spec.tsx * fixes stories * Squashed commit of the following: commit 4b32ed6cc24aa347aeb79630ea146bf27ea28839 Author: Adam Michael Thompson Date: Tue Nov 25 17:49:16 2025 -0500 fixes stories commit 982ef7265162cf067f489f2c10fe162394d16736 Author: Adam Michael Thompson Date: Tue Nov 25 13:52:15 2025 -0500 Update WizardStep.spec.tsx * Update WizardStep.stories.tsx * Update packages/wizard/src/testing/getTestUtils.tsx Co-authored-by: Shaneeza * Update README.md * use Button test utils * use test utils * Update pnpm-lock.yaml --------- Co-authored-by: Shaneeza --- packages/wizard/src/WizardContext/WizardContext.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/wizard/src/WizardContext/WizardContext.tsx b/packages/wizard/src/WizardContext/WizardContext.tsx index b067016203..568b709b12 100644 --- a/packages/wizard/src/WizardContext/WizardContext.tsx +++ b/packages/wizard/src/WizardContext/WizardContext.tsx @@ -2,7 +2,8 @@ import React, { createContext, PropsWithChildren, useContext } from 'react'; import { Optional } from '@leafygreen-ui/lib'; -import { getLgIds, GetLgIdsReturnType } from '../utils/getLgIds'; +import type { GetLgIdsReturnType } from '../utils/getLgIds'; +import { getLgIds } from '../utils/getLgIds'; export interface WizardContextData { /** From 36ae8648f1406ff831cd5d8a5f4c765a6884462d Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Wed, 26 Nov 2025 16:51:49 -0500 Subject: [PATCH 42/43] reset versions --- packages/wizard/package.json | 2 +- templates/delete-wizard/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/wizard/package.json b/packages/wizard/package.json index ffa0e34b5f..3d1b790261 100644 --- a/packages/wizard/package.json +++ b/packages/wizard/package.json @@ -1,6 +1,6 @@ { "name": "@leafygreen-ui/wizard", - "version": "0.1.0-local.1", + "version": "0.0.1", "description": "LeafyGreen UI Kit Wizard", "main": "./dist/umd/index.js", "module": "./dist/esm/index.js", diff --git a/templates/delete-wizard/package.json b/templates/delete-wizard/package.json index b64c3dc88e..50b9af23ce 100644 --- a/templates/delete-wizard/package.json +++ b/templates/delete-wizard/package.json @@ -1,6 +1,6 @@ { "name": "@lg-templates/delete-wizard", - "version": "0.1.0-local.1", + "version": "0.0.1", "description": "LeafyGreen UI Kit Delete Wizard", "main": "./dist/umd/index.js", "module": "./dist/esm/index.js", From 7853e5baf2b89682d211916c4942b8595922bb6d Mon Sep 17 00:00:00 2001 From: Adam Michael Thompson Date: Mon, 1 Dec 2025 18:18:16 -0500 Subject: [PATCH 43/43] mv to @lg-ui --- package.json | 3 +- .../delete-wizard/README.md | 0 .../delete-wizard/package.json | 2 +- .../src/DeleteWizard.stories.tsx | 0 .../src/DeleteWizard/DeleteWizard.spec.tsx | 0 .../src/DeleteWizard/DeleteWizard.styles.ts | 0 .../src/DeleteWizard/DeleteWizard.tsx | 0 .../src/DeleteWizard/DeleteWizard.types.ts | 0 .../src/DeleteWizard/DeleteWizardContext.tsx | 0 .../src/DeleteWizard/DeleteWizardFooter.tsx | 0 .../src/DeleteWizard/DeleteWizardHeader.tsx | 0 .../src/DeleteWizard/DeleteWizardStep.tsx | 0 .../compoundComponentProperties.ts | 0 .../delete-wizard/src/DeleteWizard/index.ts | 0 .../delete-wizard/src/index.ts | 0 .../src/testUtils/ExampleStepContent.tsx | 0 .../src/testing/getTestUtils.spec.tsx | 0 .../src/testing/getTestUtils.tsx | 0 .../src/testing/getTestUtils.types.ts | 0 .../delete-wizard/src/testing/index.ts | 0 .../delete-wizard/src/utils/getLgIds.ts | 0 .../delete-wizard/tsconfig.json | 0 pnpm-lock.yaml | 74 +++++++++---------- pnpm-workspace.yaml | 1 - 24 files changed, 39 insertions(+), 41 deletions(-) rename {templates => packages}/delete-wizard/README.md (100%) rename {templates => packages}/delete-wizard/package.json (97%) rename {templates => packages}/delete-wizard/src/DeleteWizard.stories.tsx (100%) rename {templates => packages}/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx (100%) rename {templates => packages}/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts (100%) rename {templates => packages}/delete-wizard/src/DeleteWizard/DeleteWizard.tsx (100%) rename {templates => packages}/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts (100%) rename {templates => packages}/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx (100%) rename {templates => packages}/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx (100%) rename {templates => packages}/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx (100%) rename {templates => packages}/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx (100%) rename {templates => packages}/delete-wizard/src/DeleteWizard/compoundComponentProperties.ts (100%) rename {templates => packages}/delete-wizard/src/DeleteWizard/index.ts (100%) rename {templates => packages}/delete-wizard/src/index.ts (100%) rename {templates => packages}/delete-wizard/src/testUtils/ExampleStepContent.tsx (100%) rename {templates => packages}/delete-wizard/src/testing/getTestUtils.spec.tsx (100%) rename {templates => packages}/delete-wizard/src/testing/getTestUtils.tsx (100%) rename {templates => packages}/delete-wizard/src/testing/getTestUtils.types.ts (100%) rename {templates => packages}/delete-wizard/src/testing/index.ts (100%) rename {templates => packages}/delete-wizard/src/utils/getLgIds.ts (100%) rename {templates => packages}/delete-wizard/tsconfig.json (100%) diff --git a/package.json b/package.json index 761202a655..8eea14ade0 100644 --- a/package.json +++ b/package.json @@ -99,8 +99,7 @@ "@lg-charts": "charts", "@lg-chat": "chat", "@lg-tools": "tools", - "@lg-mcp-ui": "mcp-ui", - "@lg-templates": "templates" + "@lg-mcp-ui": "mcp-ui" } }, "keywords": [ diff --git a/templates/delete-wizard/README.md b/packages/delete-wizard/README.md similarity index 100% rename from templates/delete-wizard/README.md rename to packages/delete-wizard/README.md diff --git a/templates/delete-wizard/package.json b/packages/delete-wizard/package.json similarity index 97% rename from templates/delete-wizard/package.json rename to packages/delete-wizard/package.json index 50b9af23ce..68016d844d 100644 --- a/templates/delete-wizard/package.json +++ b/packages/delete-wizard/package.json @@ -1,5 +1,5 @@ { - "name": "@lg-templates/delete-wizard", + "name": "@leafygreen-ui/delete-wizard", "version": "0.0.1", "description": "LeafyGreen UI Kit Delete Wizard", "main": "./dist/umd/index.js", diff --git a/templates/delete-wizard/src/DeleteWizard.stories.tsx b/packages/delete-wizard/src/DeleteWizard.stories.tsx similarity index 100% rename from templates/delete-wizard/src/DeleteWizard.stories.tsx rename to packages/delete-wizard/src/DeleteWizard.stories.tsx diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx b/packages/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx similarity index 100% rename from templates/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx rename to packages/delete-wizard/src/DeleteWizard/DeleteWizard.spec.tsx diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts b/packages/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts similarity index 100% rename from templates/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts rename to packages/delete-wizard/src/DeleteWizard/DeleteWizard.styles.ts diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx b/packages/delete-wizard/src/DeleteWizard/DeleteWizard.tsx similarity index 100% rename from templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx rename to packages/delete-wizard/src/DeleteWizard/DeleteWizard.tsx diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts b/packages/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts similarity index 100% rename from templates/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts rename to packages/delete-wizard/src/DeleteWizard/DeleteWizard.types.ts diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx b/packages/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx similarity index 100% rename from templates/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx rename to packages/delete-wizard/src/DeleteWizard/DeleteWizardContext.tsx diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx b/packages/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx similarity index 100% rename from templates/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx rename to packages/delete-wizard/src/DeleteWizard/DeleteWizardFooter.tsx diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx b/packages/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx similarity index 100% rename from templates/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx rename to packages/delete-wizard/src/DeleteWizard/DeleteWizardHeader.tsx diff --git a/templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx b/packages/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx similarity index 100% rename from templates/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx rename to packages/delete-wizard/src/DeleteWizard/DeleteWizardStep.tsx diff --git a/templates/delete-wizard/src/DeleteWizard/compoundComponentProperties.ts b/packages/delete-wizard/src/DeleteWizard/compoundComponentProperties.ts similarity index 100% rename from templates/delete-wizard/src/DeleteWizard/compoundComponentProperties.ts rename to packages/delete-wizard/src/DeleteWizard/compoundComponentProperties.ts diff --git a/templates/delete-wizard/src/DeleteWizard/index.ts b/packages/delete-wizard/src/DeleteWizard/index.ts similarity index 100% rename from templates/delete-wizard/src/DeleteWizard/index.ts rename to packages/delete-wizard/src/DeleteWizard/index.ts diff --git a/templates/delete-wizard/src/index.ts b/packages/delete-wizard/src/index.ts similarity index 100% rename from templates/delete-wizard/src/index.ts rename to packages/delete-wizard/src/index.ts diff --git a/templates/delete-wizard/src/testUtils/ExampleStepContent.tsx b/packages/delete-wizard/src/testUtils/ExampleStepContent.tsx similarity index 100% rename from templates/delete-wizard/src/testUtils/ExampleStepContent.tsx rename to packages/delete-wizard/src/testUtils/ExampleStepContent.tsx diff --git a/templates/delete-wizard/src/testing/getTestUtils.spec.tsx b/packages/delete-wizard/src/testing/getTestUtils.spec.tsx similarity index 100% rename from templates/delete-wizard/src/testing/getTestUtils.spec.tsx rename to packages/delete-wizard/src/testing/getTestUtils.spec.tsx diff --git a/templates/delete-wizard/src/testing/getTestUtils.tsx b/packages/delete-wizard/src/testing/getTestUtils.tsx similarity index 100% rename from templates/delete-wizard/src/testing/getTestUtils.tsx rename to packages/delete-wizard/src/testing/getTestUtils.tsx diff --git a/templates/delete-wizard/src/testing/getTestUtils.types.ts b/packages/delete-wizard/src/testing/getTestUtils.types.ts similarity index 100% rename from templates/delete-wizard/src/testing/getTestUtils.types.ts rename to packages/delete-wizard/src/testing/getTestUtils.types.ts diff --git a/templates/delete-wizard/src/testing/index.ts b/packages/delete-wizard/src/testing/index.ts similarity index 100% rename from templates/delete-wizard/src/testing/index.ts rename to packages/delete-wizard/src/testing/index.ts diff --git a/templates/delete-wizard/src/utils/getLgIds.ts b/packages/delete-wizard/src/utils/getLgIds.ts similarity index 100% rename from templates/delete-wizard/src/utils/getLgIds.ts rename to packages/delete-wizard/src/utils/getLgIds.ts diff --git a/templates/delete-wizard/tsconfig.json b/packages/delete-wizard/tsconfig.json similarity index 100% rename from templates/delete-wizard/tsconfig.json rename to packages/delete-wizard/tsconfig.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4cba3a81a7..21651b6c36 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1713,6 +1713,43 @@ importers: specifier: ^29.5.12 version: 29.5.12 + packages/delete-wizard: + dependencies: + '@leafygreen-ui/canvas-header': + specifier: workspace:^ + version: link:../canvas-header + '@leafygreen-ui/compound-component': + specifier: workspace:^ + version: link:../compound-component + '@leafygreen-ui/emotion': + specifier: workspace:^ + version: link:../emotion + '@leafygreen-ui/form-footer': + specifier: workspace:^ + version: link:../form-footer + '@leafygreen-ui/icon': + specifier: workspace:^ + version: link:../icon + '@leafygreen-ui/lib': + specifier: workspace:^ + version: link:../lib + '@leafygreen-ui/tokens': + specifier: workspace:^ + version: link:../tokens + '@leafygreen-ui/wizard': + specifier: workspace:^ + version: link:../wizard + '@lg-tools/test-harnesses': + specifier: workspace:^ + version: link:../../tools/test-harnesses + devDependencies: + '@faker-js/faker': + specifier: ^10.1.0 + version: 10.1.0 + '@leafygreen-ui/typography': + specifier: workspace:^ + version: link:../typography + packages/descendants: dependencies: '@leafygreen-ui/hooks': @@ -3877,43 +3914,6 @@ importers: specifier: workspace:^ version: link:../typography - templates/delete-wizard: - dependencies: - '@leafygreen-ui/canvas-header': - specifier: workspace:^ - version: link:../../packages/canvas-header - '@leafygreen-ui/compound-component': - specifier: workspace:^ - version: link:../../packages/compound-component - '@leafygreen-ui/emotion': - specifier: workspace:^ - version: link:../../packages/emotion - '@leafygreen-ui/form-footer': - specifier: workspace:^ - version: link:../../packages/form-footer - '@leafygreen-ui/icon': - specifier: workspace:^ - version: link:../../packages/icon - '@leafygreen-ui/lib': - specifier: workspace:^ - version: link:../../packages/lib - '@leafygreen-ui/tokens': - specifier: workspace:^ - version: link:../../packages/tokens - '@leafygreen-ui/wizard': - specifier: workspace:^ - version: link:../../packages/wizard - '@lg-tools/test-harnesses': - specifier: workspace:^ - version: link:../../tools/test-harnesses - devDependencies: - '@faker-js/faker': - specifier: ^10.1.0 - version: 10.1.0 - '@leafygreen-ui/typography': - specifier: workspace:^ - version: link:../../packages/typography - tools/build: dependencies: '@babel/core': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fa74b62b57..89652f8019 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,4 +6,3 @@ packages: - 'mcp-ui/*' - 'packages/*' - 'tools/*' - - 'templates/*'