diff --git a/web/packages/design/src/Alert/Alert.jsx b/web/packages/design/src/Alert/Alert.jsx
index bf4824c5d4474..8d777bf005144 100644
--- a/web/packages/design/src/Alert/Alert.jsx
+++ b/web/packages/design/src/Alert/Alert.jsx
@@ -21,7 +21,6 @@ import styled from 'styled-components';
import PropTypes from 'prop-types';
import { space, color, width } from 'design/system';
-import { fade } from 'design/theme/utils/colorManipulator';
const kind = props => {
const { kind, theme } = props;
@@ -46,14 +45,6 @@ const kind = props => {
background: theme.colors.success.main,
color: theme.colors.text.primaryInverse,
};
- case 'outline-info':
- return {
- background: fade(theme.colors.link, 0.1),
- border: `${theme.radii[1]}px solid ${theme.colors.link}`,
- borderRadius: `${theme.radii[3]}px`,
- boxShadow: 'none',
- justifyContent: 'normal',
- };
default:
return {
background: theme.colors.error.main,
@@ -66,7 +57,7 @@ const Alert = styled.div`
display: flex;
align-items: center;
justify-content: center;
- border-radius: ${p => p.theme.radii[1]}px;
+ border-radius: 2px;
box-sizing: border-box;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.24);
margin: 0 0 24px 0;
@@ -85,13 +76,7 @@ const Alert = styled.div`
`;
Alert.propTypes = {
- kind: PropTypes.oneOf([
- 'danger',
- 'info',
- 'warning',
- 'success',
- 'outline-info',
- ]),
+ kind: PropTypes.oneOf(['danger', 'info', 'warning', 'success']),
...color.propTypes,
...space.propTypes,
...width.propTypes,
@@ -108,4 +93,3 @@ export const Danger = props => ;
export const Info = props => ;
export const Warning = props => ;
export const Success = props => ;
-export const OutlineInfo = props => ;
diff --git a/web/packages/design/src/Alert/Alert.story.js b/web/packages/design/src/Alert/Alert.story.js
index a62d8ff16e9bc..9f487a0298d76 100644
--- a/web/packages/design/src/Alert/Alert.story.js
+++ b/web/packages/design/src/Alert/Alert.story.js
@@ -32,6 +32,5 @@ export const Alerts = () => (
Some warning message
Some informational message
This is success
- Text align it yourself
);
diff --git a/web/packages/teleport/src/Discover/Discover.tsx b/web/packages/teleport/src/Discover/Discover.tsx
index a17ca20a8a11b..3af2e4036be00 100644
--- a/web/packages/teleport/src/Discover/Discover.tsx
+++ b/web/packages/teleport/src/Discover/Discover.tsx
@@ -21,16 +21,16 @@ import React from 'react';
import { Prompt } from 'react-router-dom';
import { Box } from 'design';
-import { Navigation } from 'teleport/components/Wizard/Navigation';
import { FeatureBox } from 'teleport/components/Layout';
+
+import { Navigation } from 'teleport/Discover/Navigation/Navigation';
import { SelectResource } from 'teleport/Discover/SelectResource/SelectResource';
import cfg from 'teleport/config';
-import { findViewAtIndex } from 'teleport/components/Wizard/flow';
import { EViewConfigs } from './types';
+import { findViewAtIndex } from './flow';
import { DiscoverProvider, useDiscover } from './useDiscover';
-import { DiscoverIcon } from './SelectResource/icons';
function DiscoverContent() {
const {
@@ -63,16 +63,11 @@ function DiscoverContent() {
<>
{hasSelectedResource && (
-
- ,
- }}
- />
-
+
)}
{content}
diff --git a/web/packages/teleport/src/Discover/Navigation/Navigation.tsx b/web/packages/teleport/src/Discover/Navigation/Navigation.tsx
new file mode 100644
index 0000000000000..ba7e453dac229
--- /dev/null
+++ b/web/packages/teleport/src/Discover/Navigation/Navigation.tsx
@@ -0,0 +1,58 @@
+/**
+ * Teleport
+ * Copyright (C) 2023 Gravitational, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import React from 'react';
+import styled from 'styled-components';
+
+import { Flex } from 'design';
+
+import { View } from '../flow';
+
+import { StepList } from './StepList';
+import { StepItem } from './StepItem';
+
+import type { ResourceSpec } from '../SelectResource';
+
+interface NavigationProps {
+ currentStep: number;
+ selectedResource: ResourceSpec;
+ views: View[];
+}
+
+const StyledNav = styled.div`
+ display: flex;
+`;
+
+export function Navigation(props: NavigationProps) {
+ let content;
+ if (props.views) {
+ content = (
+
+ {/*
+ This initial StepItem is to render the first "bullet"
+ in this nav, which is the selected resource's icon
+ and name.
+ */}
+
+
+
+ );
+ }
+
+ return {content};
+}
diff --git a/web/packages/teleport/src/Discover/Navigation/StepItem.tsx b/web/packages/teleport/src/Discover/Navigation/StepItem.tsx
new file mode 100644
index 0000000000000..f5dcd59d13e7f
--- /dev/null
+++ b/web/packages/teleport/src/Discover/Navigation/StepItem.tsx
@@ -0,0 +1,117 @@
+/**
+ * Teleport
+ * Copyright (C) 2023 Gravitational, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import React from 'react';
+import Flex from 'design/Flex';
+
+import { DiscoverIcon } from 'teleport/Discover/SelectResource/icons';
+import { StepTitle, StepsContainer } from 'teleport/components/StepNavigation';
+import {
+ Bullet,
+ Props as BulletProps,
+} from 'teleport/components/StepNavigation/Bullet';
+
+import { StepList } from './StepList';
+
+import type { View } from 'teleport/Discover/flow';
+import type { ResourceSpec } from '../SelectResource';
+
+// FirstStepItemProps are the required
+// props to render the first step item
+// in the step navigation.
+type FirstStepItemProps = {
+ view?: never;
+ currentStep?: never;
+ index?: never;
+ selectedResource: ResourceSpec;
+};
+
+// RestOfStepItemProps are the required
+// props to render the rest of the step item's
+// after the `FirstStepItemProps`.
+type RestOfStepItemProps = {
+ view: View;
+ currentStep: number;
+ index: number;
+ selectedResource?: never;
+};
+
+export type StepItemProps = FirstStepItemProps | RestOfStepItemProps;
+
+export function StepItem(props: StepItemProps) {
+ if (props.selectedResource) {
+ return (
+
+
+ }
+ />
+ {props.selectedResource.name}
+
+
+ );
+ }
+
+ if (props.view.hide) {
+ return null;
+ }
+
+ let isActive = props.currentStep === props.view.index;
+ // Make items for nested views.
+ // Nested views is possible when a view has it's
+ // own set of sub-steps.
+ if (props.view.views) {
+ return (
+
+ );
+ }
+
+ const isDone = props.currentStep > props.view.index;
+
+ return (
+
+
+
+ {props.view.title}
+
+
+ );
+}
+
+function BulletIcon({
+ isDone,
+ isActive,
+ Icon,
+ stepNumber,
+}: BulletProps & {
+ Icon?: JSX.Element;
+}) {
+ if (Icon) {
+ return {Icon};
+ }
+
+ return ;
+}
diff --git a/web/packages/teleport/src/components/Wizard/Navigation/StepList.tsx b/web/packages/teleport/src/Discover/Navigation/StepList.tsx
similarity index 87%
rename from web/packages/teleport/src/components/Wizard/Navigation/StepList.tsx
rename to web/packages/teleport/src/Discover/Navigation/StepList.tsx
index e4dec58e714d8..7807949348f22 100644
--- a/web/packages/teleport/src/components/Wizard/Navigation/StepList.tsx
+++ b/web/packages/teleport/src/Discover/Navigation/StepList.tsx
@@ -18,23 +18,23 @@
import React from 'react';
-import { BaseView } from '../flow';
-
import { StepItem } from './StepItem';
-interface StepListProps {
- views: BaseView[];
+import type { View } from 'teleport/Discover/flow';
+
+interface StepListProps {
+ views: View[];
currentStep: number;
index?: number;
}
-export function StepList(props: StepListProps) {
+export function StepList(props: StepListProps) {
const items = [];
let startIndex = props.index || 0;
for (const view of props.views) {
items.push(
-
+ .
+ */
+
+/*
+ Discover is a complicated wizard that has different steps depending on what
+ input has been given
+
+ To be able to support this, we have the flow configured in an object, allowing
+ infinitely deep states.
+
+ To start, you define an array of `Resource`s
+
+ const resources: Resource[] = [
+ {
+ kind: ResourceKind.Name,
+ icon: ,
+ shouldPrompt(currentStep) {
+ return true;
+ },
+ views: [],
+ }
+ ];
+
+ `shouldPrompt` allows for the resource type to decide when to prompt the user
+ if they try and navigate away. It receives `currentStep: number` which points
+ to the active view in the `views` array. It should return a `boolean`, where
+ `true` would prompt the user if they navigated away, and `false` would not.
+
+ All the different views the resource can have go into the `views` property.
+
+ const resources: Resource[] = [
+ {
+ kind: ResourceKind.Name,
+ icon: ,
+ shouldPrompt(currentStep) {
+ return true;
+ },
+ views: [
+ {
+ title: 'Select Resource Type',
+ component: SomeComponent,
+ },
+ {
+ title: 'Configure Resource',
+ component: SomeOtherComponent,
+ },
+ ],
+ }
+ ];
+
+ To add child views to a view, specify `views` again with the same schema
+
+ const resources: Resource[] = [
+ {
+ kind: ResourceKind.Name,
+ shouldPrompt(currentStep) {
+ return true;
+ },
+ icon: ,
+ views: [
+ {
+ title: 'Select Resource Type',
+ component: SomeComponent,
+ },
+ {
+ title: 'Configure Resource',
+ views: [
+ {
+ title: 'Deploy Database Agent',
+ component: DatabaseAgentComponent,
+ },
+ {
+ title: 'Register a Database',
+ component: RegisterDatabaseComponent,
+ },
+ ],
+ },
+ ],
+ }
+ ];
+
+ To keep track of what view is active, we track the currentStep index.
+
+ Once a view has children, the first child's index is the same as the parent's index.
+
+ This means we can just increment the `currentStep` by 1 each time to land on the next step,
+ regardless of how deep inside the configuration object it is.
+
+ Take this view configuration -
+
+ const views: View[] = [
+ {
+ title: 'Select Resource Type',
+ component: SomeComponent,
+ },
+ {
+ title: 'Configure Resource',
+ views: [
+ {
+ title: 'Deploy Database Agent',
+ component: DatabaseAgentComponent,
+ },
+ {
+ title: 'Register a Database',
+ component: RegisterDatabaseComponent,
+ },
+ ],
+ },
+ {
+ title: 'Test Connection',
+ component: TestConnectionComponent,
+ },
+ ];
+
+ `Select Resource Type` is index 0
+ `Configure Resource` is index 1
+ `Deploy Database Agent` is also index 1
+ - This is because when you're on step 1, you don't want to view "Configure Resource" -
+ there's no component for that stage, as it consists only of child views
+ `Register a Database` is index 2
+ `Test Connection` is index 3
+
+ By tracking the step like this, we can increment the value from 0 and end up with
+ - index === 0 - show "Select Resource Type"
+ - index === 1 - show "Deploy Database Agent"
+ - index === 2 - show "Register a Database"
+ - index === 3 - show "Test Connection"
+
+ The index of each stage is calculated via the `addParentAndIndexToViews` method.
+ */
+
+import { ResourceKind } from 'teleport/Discover/Shared';
+
+import { computeViewChildrenSize, ResourceViewConfig, View } from './flow';
+
+describe('discover flow', () => {
+ describe('computeViewChildrenSize', () => {
+ it('should calculate the children size correctly', () => {
+ const resource: ResourceViewConfig = {
+ kind: ResourceKind.Server,
+ shouldPrompt: () => null,
+ views: [
+ {
+ title: 'Select Resource Type',
+ views: [
+ {
+ title: 'Ridiculous',
+ views: [
+ {
+ title: 'Nesting',
+ views: [
+ {
+ title: 'Here',
+ },
+ {
+ title: 'Again',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ };
+
+ expect(computeViewChildrenSize(resource.views as View[])).toBe(2);
+ });
+ });
+});
diff --git a/web/packages/teleport/src/Discover/flow.tsx b/web/packages/teleport/src/Discover/flow.tsx
index 8840f59306faa..6bee54f53d843 100644
--- a/web/packages/teleport/src/Discover/flow.tsx
+++ b/web/packages/teleport/src/Discover/flow.tsx
@@ -21,7 +21,6 @@ import React from 'react';
import { ResourceKind } from 'teleport/Discover/Shared';
import { AgentStepComponent } from 'teleport/Discover/types';
import { DiscoverEvent } from 'teleport/services/userEvent';
-import { BaseView } from 'teleport/components/Wizard/flow';
import { ResourceSpec } from './SelectResource';
@@ -53,8 +52,12 @@ export interface ResourceViewConfig {
shouldPrompt?: (currentStep: number, resourceSpec: ResourceSpec) => boolean;
}
-export type View = BaseView<{
+export interface View {
+ title: string;
component?: AgentStepComponent;
+ hide?: boolean;
+ index?: number;
+ views?: View[];
eventName?: DiscoverEvent;
/**
* manuallyEmitSuccessEvent is a flag that when true
@@ -63,4 +66,93 @@ export type View = BaseView<{
* which is sent by the parent context.
*/
manuallyEmitSuccessEvent?: boolean;
-}>;
+}
+
+/**
+ * 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
+ */
+export function computeViewChildrenSize(views: View[]) {
+ let size = 0;
+ for (const view of views) {
+ if (view.views) {
+ size += computeViewChildrenSize(view.views);
+ } else {
+ size += 1;
+ }
+ }
+
+ return size;
+}
+
+/**
+ * addIndexToViews will recursively loop over the given views, adding an index value to each one
+ * The first child shares its index with the parent, as we effectively ignore the fact the parent
+ * exists when trying to find the active view by the current step index.
+ */
+export function addIndexToViews(views: View[], index = 0): View[] {
+ const result: View[] = [];
+
+ for (const view of views) {
+ const copy = {
+ ...view,
+ index,
+ parent,
+ };
+
+ if (view.views) {
+ copy.views = addIndexToViews(view.views, index);
+
+ index += computeViewChildrenSize(view.views);
+ } else {
+ index += 1;
+ }
+
+ result.push(copy);
+ }
+
+ return result;
+}
+
+/**
+ * findViewAtIndex will recursively loop views and their children in order to find the deepest
+ * match at that index.
+ */
+export function findViewAtIndex(
+ views: View[],
+ currentStep: number
+): View | null {
+ for (const view of views) {
+ if (view.views) {
+ const result = findViewAtIndex(view.views, currentStep);
+
+ if (result) {
+ return result;
+ }
+ }
+
+ if (currentStep === view.index) {
+ return view;
+ }
+ }
+}
+
+/**
+ * hasActiveChildren will recursively loop through views and their children in order to find
+ * out if there is a view with a matching index to the given `currentStep` value
+ * This is because a parent is active as long as its children are active
+ */
+export function hasActiveChildren(views: View[], currentStep: number) {
+ for (const view of views) {
+ if (view.index === currentStep) {
+ return true;
+ }
+
+ if (view.views && hasActiveChildren(view.views, currentStep)) {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/web/packages/teleport/src/Discover/useDiscover.tsx b/web/packages/teleport/src/Discover/useDiscover.tsx
index fcc0a72ee5b92..8916a62a51959 100644
--- a/web/packages/teleport/src/Discover/useDiscover.tsx
+++ b/web/packages/teleport/src/Discover/useDiscover.tsx
@@ -31,12 +31,13 @@ import {
} from 'teleport/services/userEvent';
import cfg from 'teleport/config';
import { DiscoveryConfig } from 'teleport/services/discovery';
+
import {
addIndexToViews,
findViewAtIndex,
-} from 'teleport/components/Wizard/flow';
-
-import { ResourceViewConfig, View } from './flow';
+ ResourceViewConfig,
+ View,
+} from './flow';
import { viewConfigs } from './resourceViewConfigs';
import { EViewConfigs } from './types';
import { ServiceDeployMethod } from './Database/common';
@@ -294,7 +295,7 @@ export function DiscoverProvider({
const currCfg = [...viewConfigs, ...eViewConfigs].find(
r => r.kind === resource.kind
);
- let indexedViews: View[] = [];
+ let indexedViews = [];
if (typeof currCfg.views === 'function') {
indexedViews = addIndexToViews(currCfg.views(resource));
} else {
diff --git a/web/packages/teleport/src/components/Wizard/Navigation/Bullet.tsx b/web/packages/teleport/src/components/StepNavigation/Bullet.tsx
similarity index 91%
rename from web/packages/teleport/src/components/Wizard/Navigation/Bullet.tsx
rename to web/packages/teleport/src/components/StepNavigation/Bullet.tsx
index 31f2166456b67..10b980ed7b147 100644
--- a/web/packages/teleport/src/components/Wizard/Navigation/Bullet.tsx
+++ b/web/packages/teleport/src/components/StepNavigation/Bullet.tsx
@@ -16,7 +16,6 @@
* along with this program. If not, see .
*/
-import Flex from 'design/Flex';
import React from 'react';
import styled from 'styled-components';
@@ -24,14 +23,9 @@ export type Props = {
isDone?: boolean;
isActive?: boolean;
stepNumber?: number;
- Icon?: JSX.Element;
};
-export function Bullet({ isDone, isActive, stepNumber, Icon }: Props) {
- if (Icon) {
- return {Icon};
- }
-
+export function Bullet({ isDone, isActive, stepNumber }: Props) {
if (isActive) {
return ;
}
diff --git a/web/packages/teleport/src/components/Wizard/Navigation/Shared.tsx b/web/packages/teleport/src/components/StepNavigation/Shared.tsx
similarity index 100%
rename from web/packages/teleport/src/components/Wizard/Navigation/Shared.tsx
rename to web/packages/teleport/src/components/StepNavigation/Shared.tsx
diff --git a/web/packages/teleport/src/components/StepNavigation/StepNavigation.story.tsx b/web/packages/teleport/src/components/StepNavigation/StepNavigation.story.tsx
new file mode 100644
index 0000000000000..a832cca1dbbcf
--- /dev/null
+++ b/web/packages/teleport/src/components/StepNavigation/StepNavigation.story.tsx
@@ -0,0 +1,56 @@
+/**
+ * Teleport
+ * Copyright (C) 2023 Gravitational, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import React from 'react';
+import { Box } from 'design';
+
+import { StepNavigation } from './StepNavigation';
+
+export default {
+ title: 'Teleport/StepNavigation',
+};
+
+const steps = [
+ { title: 'first title' },
+ { title: 'second title' },
+ { title: 'third title' },
+ { title: 'fourth title' },
+ { title: 'fifth title' },
+ { title: 'sixth title' },
+ { title: 'seventh title' },
+ { title: 'eighth title' },
+];
+
+export const Examples = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/web/packages/teleport/src/components/Wizard/Navigation/Navigation.test.tsx b/web/packages/teleport/src/components/StepNavigation/StepNavigation.test.tsx
similarity index 92%
rename from web/packages/teleport/src/components/Wizard/Navigation/Navigation.test.tsx
rename to web/packages/teleport/src/components/StepNavigation/StepNavigation.test.tsx
index cc9c307e041c8..9c1f11c6f8f1c 100644
--- a/web/packages/teleport/src/components/Wizard/Navigation/Navigation.test.tsx
+++ b/web/packages/teleport/src/components/StepNavigation/StepNavigation.test.tsx
@@ -19,12 +19,12 @@
import React from 'react';
import { render, screen } from 'design/utils/testing';
-import { Navigation } from './Navigation';
+import { StepNavigation } from './StepNavigation';
const steps = [{ title: 'first' }, { title: 'second' }, { title: 'third' }];
test('step 1/3', async () => {
- render();
+ render();
const firstBullet = screen.getByTestId('bullet-active');
expect(firstBullet).toHaveTextContent('');
@@ -43,7 +43,7 @@ test('step 1/3', async () => {
});
test('step 2/3', async () => {
- render();
+ render();
const firstBullet = screen.getByTestId('bullet-checked');
expect(firstBullet).toHaveTextContent('');
@@ -59,7 +59,7 @@ test('step 2/3', async () => {
});
test('step 3/3', async () => {
- render();
+ render();
const checkedBullets = screen.getAllByTestId('bullet-checked');
expect(checkedBullets).toHaveLength(2);
diff --git a/web/packages/teleport/src/components/StepNavigation/StepNavigation.tsx b/web/packages/teleport/src/components/StepNavigation/StepNavigation.tsx
new file mode 100644
index 0000000000000..c2f4fb6246878
--- /dev/null
+++ b/web/packages/teleport/src/components/StepNavigation/StepNavigation.tsx
@@ -0,0 +1,53 @@
+/**
+ * Teleport
+ * Copyright (C) 2023 Gravitational, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import React from 'react';
+
+import { Flex } from 'design';
+
+import { StepTitle, StepsContainer } from './Shared';
+import { Bullet } from './Bullet';
+
+export type StepItem = {
+ title: string;
+};
+
+interface NavigationProps {
+ currentStep: number;
+ steps: StepItem[];
+}
+
+export function StepNavigation({ currentStep, steps }: NavigationProps) {
+ const items: JSX.Element[] = [];
+
+ steps.forEach((step, index) => {
+ const isDone = currentStep > index;
+ let isActive = currentStep === index;
+
+ items.push(
+
+
+
+ {step.title}
+
+
+ );
+ });
+
+ return {items};
+}
diff --git a/web/packages/teleport/src/components/Wizard/Navigation/index.ts b/web/packages/teleport/src/components/StepNavigation/index.ts
similarity index 94%
rename from web/packages/teleport/src/components/Wizard/Navigation/index.ts
rename to web/packages/teleport/src/components/StepNavigation/index.ts
index 8591a53352d25..9dbfafc16308b 100644
--- a/web/packages/teleport/src/components/Wizard/Navigation/index.ts
+++ b/web/packages/teleport/src/components/StepNavigation/index.ts
@@ -16,6 +16,6 @@
* along with this program. If not, see .
*/
-export { Navigation } from './Navigation';
+export { StepNavigation } from './StepNavigation';
export { StepTitle, StepsContainer } from './Shared';
export { Bullet } from './Bullet';
diff --git a/web/packages/teleport/src/components/Wizard/Navigation/Navigation.story.tsx b/web/packages/teleport/src/components/Wizard/Navigation/Navigation.story.tsx
deleted file mode 100644
index be358abfa6dc8..0000000000000
--- a/web/packages/teleport/src/components/Wizard/Navigation/Navigation.story.tsx
+++ /dev/null
@@ -1,112 +0,0 @@
-/**
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-import React from 'react';
-import { Box } from 'design';
-
-import { addIndexToViews } from '../flow';
-
-import { Navigation } from './Navigation';
-
-export default {
- title: 'Teleport/StepNavigation',
-};
-
-const steps = [
- { title: 'first title' },
- { title: 'second title' },
- { title: 'third title' },
- { title: 'fourth title' },
- { title: 'fifth title' },
- { title: 'sixth title' },
- { title: 'seventh title' },
- { title: 'eighth title' },
-];
-
-export const WithoutNesting = () => {
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-};
-
-export const WithNesting = () => {
- const nestedViews = [
- {
- title: 'First Step',
- },
- {
- title: 'Nesting',
- views: [
- {
- title: 'Nesting Again',
- views: [
- {
- title: 'Second Step',
- },
- {
- title: 'Third Step',
- },
- ],
- },
- {
- title: 'Fourth Step',
- },
- ],
- },
- {
- title: 'Fifth Step',
- },
- ];
-
- const indexedViews = addIndexToViews(nestedViews);
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-};
diff --git a/web/packages/teleport/src/components/Wizard/Navigation/Navigation.tsx b/web/packages/teleport/src/components/Wizard/Navigation/Navigation.tsx
deleted file mode 100644
index 01fad8c7aec9c..0000000000000
--- a/web/packages/teleport/src/components/Wizard/Navigation/Navigation.tsx
+++ /dev/null
@@ -1,183 +0,0 @@
-/**
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-import React from 'react';
-
-import { Flex } from 'design';
-
-import { BaseView } from '../flow';
-
-import { StepTitle, StepsContainer } from './Shared';
-import { Bullet } from './Bullet';
-import { StepList } from './StepList';
-
-export type StepIcon = {
- component: JSX.Element;
- title: string;
-};
-
-interface NavigationProps {
- currentStep: number;
- views: BaseView[];
- startWithIcon?: StepIcon;
-}
-
-/**
- * Renders horizontal steps for each view.
- *
- * @param views can be simple (non-nested) or nested for
- * more complex configurations (see below for an example).
- *
- * For nested views, it is required to apply
- * function `addIndexToViews(views: BaseView[])`
- * before passing the views to Navigation so it can correctly
- * increment the steps.
- *
- * For simple views, defining indexes is not required.
- *
- *
- * @example
- * How nesting views are used for Discover wizards:
- *
- * Discover is a complicated wizard that has different steps depending on what
- * input has been given.
- *
- * To be able to support this, we have the flow configured in an object, allowing
- * infinitely deep states.
- *
- * All the different views the resource can have go into the `views` property eg:
- *
- * const resources: Resource[] = [
- * {
- * kind: ResourceKind.Name,
- * icon: ,
- * shouldPrompt(currentStep) {
- * return true;
- * },
- * views: [
- * {
- * title: 'Select Resource Type',
- * component: SomeComponent,
- * },
- * {
- * title: 'Configure Resource',
- * component: SomeOtherComponent,
- * },
- * ],
- * }
- * ];
- *
- * To add child views to a view, specify `views` again with the same schema
- *
- * const resources: Resource[] = [
- * {
- * kind: ResourceKind.Name,
- * shouldPrompt(currentStep) {
- * return true;
- * },
- * icon: ,
- * views: [
- * {
- * title: 'Select Resource Type',
- * component: SomeComponent,
- * },
- * {
- * title: 'Configure Resource',
- * views: [
- * {
- * title: 'Deploy Database Agent',
- * component: DatabaseAgentComponent,
- * },
- * {
- * title: 'Register a Database',
- * component: RegisterDatabaseComponent,
- * },
- * ],
- * },
- * ],
- * }
- * ];
- *
- * To keep track of what view is active, we track the currentStep index.
- *
- * Once a view has children, the first child's index is the same as the parent's index.
- *
- * This means we can just increment the `currentStep` by 1 each time to land on the next step,
- * regardless of how deep inside the configuration object it is.
- *
- * Take this view configuration -
- *
- * const views: View[] = [
- * {
- * title: 'Select Resource Type',
- * component: SomeComponent,
- * },
- * {
- * title: 'Configure Resource',
- * views: [
- * {
- * title: 'Deploy Database Agent',
- * component: DatabaseAgentComponent,
- * },
- * {
- * title: 'Register a Database',
- * component: RegisterDatabaseComponent,
- * },
- * ],
- * },
- * {
- * title: 'Test Connection',
- * component: TestConnectionComponent,
- * },
- * ];
- *
- * `Select Resource Type` is index 0
- * `Configure Resource` is index 1
- * `Deploy Database Agent` is also index 1
- * - This is because when you're on step 1, you don't want to view "Configure Resource" -
- * there's no component for that stage, as it consists only of child views
- * `Register a Database` is index 2
- * `Test Connection` is index 3
- *
- * By tracking the step like this, we can increment the value from 0 and end up with
- * - index === 0 - show "Select Resource Type"
- * - index === 1 - show "Deploy Database Agent"
- * - index === 2 - show "Register a Database"
- * - index === 3 - show "Test Connection"
- *
- * The index of each stage is calculated via the `addParentAndIndexToViews` method.
- */
-export function Navigation({
- currentStep,
- views,
- startWithIcon,
-}: NavigationProps) {
- return (
-
- {startWithIcon && (
-
-
-
- {startWithIcon.title}
-
-
- )}
-
-
- );
-}
diff --git a/web/packages/teleport/src/components/Wizard/Navigation/StepItem.tsx b/web/packages/teleport/src/components/Wizard/Navigation/StepItem.tsx
deleted file mode 100644
index 4236d85471209..0000000000000
--- a/web/packages/teleport/src/components/Wizard/Navigation/StepItem.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-import React from 'react';
-
-import {
- StepTitle,
- StepsContainer,
-} from 'teleport/components/Wizard/Navigation';
-import { Bullet } from 'teleport/components/Wizard/Navigation/Bullet';
-
-import { BaseView } from '../flow';
-
-import { StepList } from './StepList';
-
-export function StepItem(props: {
- view: BaseView;
- currentStep: number;
- index: number;
-}) {
- if (props.view.hide) {
- return null;
- }
-
- // Make items for nested views.
- // Nested views is possible when a view has it's
- // own set of sub-steps.
- if (props.view.views) {
- return (
-
- );
- }
-
- const index = props.view.index ?? props.index;
- const isActive = props.currentStep === index;
- const isDone = props.currentStep > index;
-
- 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
deleted file mode 100644
index b89b884007d71..0000000000000
--- a/web/packages/teleport/src/components/Wizard/flow.test.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-/**
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-import React from 'react';
-import { render, screen } from 'design/utils/testing';
-
-import { Navigation } from './Navigation';
-import { addIndexToViews, computeViewChildrenSize } from './flow';
-
-test('computeViewChildrenSize', async () => {
- const nestedViews = [
- {
- title: 'Ridiculous',
- views: [
- {
- title: 'Nesting',
- views: [
- {
- title: 'Here',
- },
- {
- title: 'Again',
- },
- ],
- },
- ],
- },
- {
- title: 'Banana',
- },
- ];
- expect(computeViewChildrenSize(nestedViews)).toBe(3);
-
- const notNestedViews = [
- {
- title: 'Apple',
- },
- {
- title: 'Banana',
- },
- ];
- expect(computeViewChildrenSize(notNestedViews)).toBe(2);
-});
-
-test('addIndexToViews and rendering correct steps', async () => {
- const nestedViews = [
- {
- title: 'First Step',
- },
- {
- title: 'Nesting',
- views: [
- {
- title: 'Nesting Again',
- views: [
- {
- title: 'Second Step',
- },
- {
- title: 'Third Step',
- },
- ],
- },
- {
- title: 'Fourth Step',
- },
- ],
- },
- {
- title: 'Fifth Step',
- },
- ];
-
- const indexedViews = addIndexToViews(nestedViews);
-
- // Should render 5 bullets.
- render();
-
- // First bullets always active.
- const firstBullet = screen.getByTestId('bullet-active');
- expect(firstBullet).toHaveTextContent('');
- expect(firstBullet.parentElement).toHaveTextContent(/first step/i);
-
- // Rest should be not active.
- const uncheckedBullets = screen.getAllByTestId('bullet-default');
- expect(uncheckedBullets).toHaveLength(4);
-
- expect(uncheckedBullets[0]).toHaveTextContent(/2/i);
- expect(uncheckedBullets[0].parentElement).toHaveTextContent(/second/i);
-
- expect(uncheckedBullets[1]).toHaveTextContent(/3/i);
- expect(uncheckedBullets[1].parentElement).toHaveTextContent(/third/i);
-
- expect(uncheckedBullets[2]).toHaveTextContent(/4/i);
- expect(uncheckedBullets[2].parentElement).toHaveTextContent(/fourth/i);
-
- expect(uncheckedBullets[3]).toHaveTextContent(/5/i);
- expect(uncheckedBullets[3].parentElement).toHaveTextContent(/fifth/i);
-});
diff --git a/web/packages/teleport/src/components/Wizard/flow.tsx b/web/packages/teleport/src/components/Wizard/flow.tsx
deleted file mode 100644
index c00f4d16ba7d4..0000000000000
--- a/web/packages/teleport/src/components/Wizard/flow.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-/**
- * Teleport
- * Copyright (C) 2024 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-export type BaseView = T & {
- hide?: boolean;
- index?: number;
- views?: BaseView[];
- 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
- */
-export function computeViewChildrenSize(views: BaseView[]) {
- let size = 0;
- for (const view of views) {
- if (view.views) {
- size += computeViewChildrenSize(view.views);
- } else {
- size += 1;
- }
- }
-
- return size;
-}
-
-/**
- * addIndexToViews will recursively loop over the given views, adding an index value to each one
- * The first child shares its index with the parent, as we effectively ignore the fact the parent
- * exists when trying to find the active view by the current step index.
- */
-export function addIndexToViews(
- views: BaseView[],
- index = 0
-): BaseView[] {
- const result: BaseView[] = [];
-
- for (const view of views) {
- const copy = {
- ...view,
- index,
- parent,
- };
-
- if (view.views) {
- copy.views = addIndexToViews(view.views, index);
-
- index += computeViewChildrenSize(view.views);
- } else {
- index += 1;
- }
-
- result.push(copy);
- }
-
- return result;
-}
-
-/**
- * findViewAtIndex will recursively loop views and their children in order to find the deepest
- * match at that index.
- */
-export function findViewAtIndex(
- views: BaseView[],
- currentStep: number
-): BaseView | undefined {
- for (const view of views) {
- if (view.views) {
- const result = findViewAtIndex(view.views, currentStep);
-
- if (result) {
- return result;
- }
- }
-
- if (currentStep === view.index) {
- return view;
- }
- }
-}
diff --git a/web/packages/teleport/src/services/integrations/types.ts b/web/packages/teleport/src/services/integrations/types.ts
index fe8681fb5ef7e..8094c0072e3d1 100644
--- a/web/packages/teleport/src/services/integrations/types.ts
+++ b/web/packages/teleport/src/services/integrations/types.ts
@@ -115,8 +115,8 @@ export type ExternalAuditStorageIntegration = Integration<
ExternalAuditStorage
>;
-export type Plugin = Integration<'plugin', PluginKind, T>;
-export type PluginSpec = PluginOktaSpec | any; // currently only okta has a plugin spec
+export type Plugin = Integration<'plugin', PluginKind, PluginSpec>;
+export type PluginSpec = Record; // currently no 'spec' fields exposed to the frontend
// PluginKind represents the type of the plugin
// and should be the same value as defined in the backend (check master branch for the latest):
// https://github.com/gravitational/teleport/blob/a410acef01e0023d41c18ca6b0a7b384d738bb32/api/types/plugin.go#L27
@@ -134,25 +134,6 @@ export type PluginKind =
| 'servicenow'
| 'jamf';
-export type PluginOktaSpec = {
- // scimBearerToken is the plain text of the bearer token that Okta will use
- // to authenticate SCIM requests
- scimBearerToken: string;
- // oktaAppID is the Okta ID of the SAML App created during the Okta plugin
- // installation
- oktaAppId: string;
- // oktaAppName is the human readable name of the Okta SAML app created
- // during the Okta plugin installation
- oktaAppName: string;
- // teleportSSOConnector is the name of the Teleport SAML SSO connector
- // created by the plugin during installation
- teleportSsoConnector: string;
- // error contains a description of any failures during plugin installation
- // that were deemed not serious enough to fail the plugin installation, but
- // may effect the operation of advanced features like User Sync or SCIM.
- error: string;
-};
-
export type IntegrationCreateRequest = {
name: string;
subKind: IntegrationKind;