diff --git a/web/packages/teleport/src/components/Wizard/Navigation/StepItem.tsx b/web/packages/teleport/src/components/Wizard/Navigation/StepItem.tsx index 06bf434f56c98..6c47d7c8f7f70 100644 --- a/web/packages/teleport/src/components/Wizard/Navigation/StepItem.tsx +++ b/web/packages/teleport/src/components/Wizard/Navigation/StepItem.tsx @@ -55,7 +55,11 @@ export function StepItem(props: { return ( - + {props.view.title} diff --git a/web/packages/teleport/src/components/Wizard/flow.test.tsx b/web/packages/teleport/src/components/Wizard/flow.test.tsx index 6bf12f13d34e9..d57367bbb5fe9 100644 --- a/web/packages/teleport/src/components/Wizard/flow.test.tsx +++ b/web/packages/teleport/src/components/Wizard/flow.test.tsx @@ -41,9 +41,13 @@ test('computeViewChildrenSize', async () => { }, { title: 'Banana', + hide: true, }, ]; - expect(computeViewChildrenSize(nestedViews)).toBe(3); + expect(computeViewChildrenSize({ views: nestedViews })).toBe(3); + expect( + computeViewChildrenSize({ views: nestedViews, constrainToVisible: true }) + ).toBe(2); const notNestedViews = [ { @@ -53,7 +57,7 @@ test('computeViewChildrenSize', async () => { title: 'Banana', }, ]; - expect(computeViewChildrenSize(notNestedViews)).toBe(2); + expect(computeViewChildrenSize({ views: notNestedViews })).toBe(2); }); test('addIndexToViews and rendering correct steps', async () => { diff --git a/web/packages/teleport/src/components/Wizard/flow.tsx b/web/packages/teleport/src/components/Wizard/flow.tsx index c00f4d16ba7d4..63dd28452da65 100644 --- a/web/packages/teleport/src/components/Wizard/flow.tsx +++ b/web/packages/teleport/src/components/Wizard/flow.tsx @@ -16,25 +16,61 @@ * along with this program. If not, see . */ +/** + * BaseView is a recursive type representing a view in a Wizard flow. + * + * @template T - Any view-specific properties. + */ export type BaseView = T & { + /** + * Whether to hide the view from the list of views. + */ hide?: boolean; + /** + * Current step index in the wizard. + */ index?: number; + /** + * Current visible step index in the wizard (ignoring any hidden steps). + */ + displayIndex?: number; + /** + * Optional list of sub-views. + */ views?: BaseView[]; + /** + * Title of this view in the wizard flow. + */ title: string; }; /** * computeViewChildrenSize calculates how many children a view has, without counting the first * child. This is because the first child shares the same index with its parent, so we don't - * need to count it as it's not taking up a new index + * need to count it as it's not taking up a new index. + * + * If `constrainToVisible` is true, then we only count the visible views. */ -export function computeViewChildrenSize(views: BaseView[]) { +export function computeViewChildrenSize({ + views, + constrainToVisible = false, +}: { + views: BaseView[]; + constrainToVisible?: boolean; +}) { let size = 0; for (const view of views) { + if (constrainToVisible && view.hide) { + continue; + } + if (view.views) { - size += computeViewChildrenSize(view.views); + size += computeViewChildrenSize({ + views: view.views, + constrainToVisible, + }); } else { - size += 1; + size++; } } @@ -48,7 +84,8 @@ export function computeViewChildrenSize(views: BaseView[]) { */ export function addIndexToViews( views: BaseView[], - index = 0 + index = 0, + displayIndex = 1 ): BaseView[] { const result: BaseView[] = []; @@ -60,11 +97,20 @@ export function addIndexToViews( }; if (view.views) { - copy.views = addIndexToViews(view.views, index); - - index += computeViewChildrenSize(view.views); + copy.views = addIndexToViews(view.views, index, displayIndex); + index += computeViewChildrenSize({ views: view.views }); } else { - index += 1; + index++; + } + + if (!view.hide) { + copy.displayIndex = displayIndex; + displayIndex += view.views + ? computeViewChildrenSize({ + views: view.views, + constrainToVisible: true, + }) + : 1; } result.push(copy);