Skip to content

Commit d8cbe57

Browse files
authored
Add story for ResourceActionButton (#52886)
* useSamlAppAction: Throw error if used outside of context * Add GCP app to teleterm's ActionButton story * Add story for ResourceActionButton
1 parent afc5c1e commit d8cbe57

File tree

4 files changed

+235
-63
lines changed

4 files changed

+235
-63
lines changed

web/packages/teleport/src/Apps/fixtures/index.ts

+73-61
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,69 @@
1818

1919
import makeApp from 'teleport/services/apps/makeApps';
2020

21+
export const awsConsoleApp = makeApp({
22+
name: 'aws-console-1',
23+
uri: 'https://console.aws.amazon.com/ec2/v2/home',
24+
publicAddr: 'awsconsole-1.teleport-proxy.com',
25+
labels: [
26+
{ name: 'aws_account_id', value: 'A1234' },
27+
{ name: 'env', value: 'dev' },
28+
{ name: 'cluster', value: 'two' },
29+
],
30+
description: 'This is an AWS Console app',
31+
awsConsole: true,
32+
awsRoles: [
33+
{
34+
name: 'role name',
35+
arn: 'arn:aws:iam::joe123:role/EC2FullAccess',
36+
display: 'EC2FullAccess',
37+
},
38+
{
39+
name: 'other role name',
40+
arn: 'arn:aws:iam::joe123:role/EC2FullAccess',
41+
display: 'ReallyLonReallyLonggggggEC2FullAccess',
42+
},
43+
{
44+
name: 'thisthing',
45+
arn: 'arn:aws:iam::joe123:role/EC2ReadOnly',
46+
display: 'EC2ReadOnly',
47+
},
48+
...new Array(20).fill(undefined).map((_, index) => {
49+
return {
50+
name: `long-${index}`,
51+
arc: `arn:aws:iam::${index}`,
52+
display: `LONG${index}`,
53+
};
54+
}),
55+
],
56+
clusterId: 'one',
57+
fqdn: 'awsconsole-1.com',
58+
});
59+
60+
export const awsIamIcAccountApp = makeApp({
61+
name: 'aws-iam-ic-account-1',
62+
uri: 'https://console.aws.amazon.com',
63+
publicAddr: 'console.aws.amazon.com',
64+
subKind: 'aws-ic-account',
65+
labels: [{ name: 'teleport.dev/origin', value: 'aws-identity-center' }],
66+
description: 'This is an AWS IAM Identity Center account',
67+
awsConsole: false,
68+
permissionSets: [
69+
{
70+
name: 'Admin perm set',
71+
arn: 'arn:aws:sso:::permissionSet/Admin',
72+
display: 'Admin',
73+
},
74+
{
75+
name: 'ReadOnly perm set',
76+
arn: 'arn:aws:sso:::permissionSet/ReadOnly',
77+
display: 'ReadOnly',
78+
},
79+
],
80+
clusterId: 'one',
81+
fqdn: 'https://console.aws.amazon.com',
82+
});
83+
2184
export const apps = [
2285
{
2386
name: 'Jenkins',
@@ -138,44 +201,6 @@ export const apps = [
138201
],
139202
clusterId: 'one',
140203
},
141-
{
142-
name: 'aws-console-1',
143-
uri: 'https://console.aws.amazon.com/ec2/v2/home',
144-
publicAddr: 'awsconsole-1.teleport-proxy.com',
145-
labels: [
146-
{ name: 'aws_account_id', value: 'A1234' },
147-
{ name: 'env', value: 'dev' },
148-
{ name: 'cluster', value: 'two' },
149-
],
150-
description: 'This is an AWS Console app',
151-
awsConsole: true,
152-
awsRoles: [
153-
{
154-
name: 'role name',
155-
arn: 'arn:aws:iam::joe123:role/EC2FullAccess',
156-
display: 'EC2FullAccess',
157-
},
158-
{
159-
name: 'other role name',
160-
arn: 'arn:aws:iam::joe123:role/EC2FullAccess',
161-
display: 'ReallyLonReallyLonggggggEC2FullAccess',
162-
},
163-
{
164-
name: 'thisthing',
165-
arn: 'arn:aws:iam::joe123:role/EC2ReadOnly',
166-
display: 'EC2ReadOnly',
167-
},
168-
...new Array(20).fill(undefined).map((_, index) => {
169-
return {
170-
name: `long-${index}`,
171-
arc: `arn:aws:iam::${index}`,
172-
display: `LONG${index}`,
173-
};
174-
}),
175-
],
176-
clusterId: 'one',
177-
fqdn: 'awsconsole-1.com',
178-
},
179204
{
180205
name: 'Cloud',
181206
uri: 'cloud://some-address',
@@ -187,29 +212,6 @@ export const apps = [
187212
],
188213
clusterId: 'one',
189214
},
190-
{
191-
name: 'aws-iam-ic-account-1',
192-
uri: 'https://console.aws.amazon.com',
193-
publicAddr: 'console.aws.amazon.com',
194-
subKind: 'aws-ic-account',
195-
labels: [{ name: 'teleport.dev/origin', value: 'aws-identity-center' }],
196-
description: 'This is an AWS IAM Identity Center account',
197-
awsConsole: false,
198-
permissionSets: [
199-
{
200-
name: 'Admin perm set',
201-
arn: 'arn:aws:sso:::permissionSet/Admin',
202-
display: 'Admin',
203-
},
204-
{
205-
name: 'ReadOnly perm set',
206-
arn: 'arn:aws:sso:::permissionSet/ReadOnly',
207-
display: 'ReadOnly',
208-
},
209-
],
210-
clusterId: 'one',
211-
fqdn: 'https://console.aws.amazon.com',
212-
},
213215
{
214216
name: 'aws-iam-ic-account-2',
215217
uri: 'https://console.aws.amazon.com',
@@ -234,6 +236,15 @@ export const apps = [
234236
fqdn: 'https://console.aws.amazon.com',
235237
},
236238
].map(makeApp);
239+
apps.push(awsConsoleApp, awsIamIcAccountApp);
240+
241+
export const gcpCloudApp = makeApp({
242+
name: 'cloud-app',
243+
description: 'Cloud app (GCP)',
244+
uri: 'cloud://GCP',
245+
publicAddr: 'cloud-app.teleport.example.com',
246+
fqdn: 'cloud-app.teleport.example.com',
247+
});
237248

238249
export const moreApps = [
239250
{
@@ -405,3 +416,4 @@ export const moreApps = [
405416
clusterId: 'one',
406417
},
407418
].map(makeApp);
419+
moreApps.push(gcpCloudApp);

web/packages/teleport/src/SamlApplications/useSamlAppActions.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,15 @@ export interface SamlAppAction {
8888
export const SamlAppActionContext = createContext<SamlAppAction>(null);
8989

9090
export function useSamlAppAction() {
91-
return useContext(SamlAppActionContext);
91+
const context = useContext(SamlAppActionContext);
92+
93+
if (!context) {
94+
throw new Error(
95+
'useSamlAppAction must be used within a SamlAppActionProvider'
96+
);
97+
}
98+
99+
return context;
92100
}
93101

94102
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* Teleport
3+
* Copyright (C) 2025 Gravitational, Inc.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
import { Meta } from '@storybook/react';
20+
import { MemoryRouter } from 'react-router';
21+
22+
import { Flex, Stack, Text } from 'design';
23+
24+
import { ContextProvider } from 'teleport';
25+
import {
26+
awsConsoleApp,
27+
awsIamIcAccountApp,
28+
gcpCloudApp,
29+
} from 'teleport/Apps/fixtures';
30+
import { databases } from 'teleport/Databases/fixtures';
31+
import { desktops } from 'teleport/Desktops/fixtures';
32+
import { kubes } from 'teleport/Kubes/fixtures';
33+
import { createTeleportContext } from 'teleport/mocks/contexts';
34+
import { nodes } from 'teleport/Nodes/fixtures';
35+
import { SamlAppActionProvider } from 'teleport/SamlApplications/useSamlAppActions';
36+
import makeApp from 'teleport/services/apps/makeApps';
37+
38+
import { ResourceActionButton as Component } from './ResourceActionButton';
39+
40+
const meta: Meta = {
41+
title: 'Teleport/UnifiedResources/ResourceActionButton',
42+
decorators: Story => {
43+
const ctx = createTeleportContext();
44+
45+
return (
46+
<MemoryRouter>
47+
<ContextProvider ctx={ctx}>
48+
<SamlAppActionProvider>
49+
<Story />
50+
</SamlAppActionProvider>
51+
</ContextProvider>
52+
</MemoryRouter>
53+
);
54+
},
55+
};
56+
export default meta;
57+
58+
export function ResourceActionButton() {
59+
return (
60+
<Flex gap={4} flexWrap="wrap">
61+
<Stack gap={3}>
62+
<Stack>
63+
<Text>TCP app</Text>
64+
<Component
65+
resource={makeApp({
66+
uri: 'tcp://localhost:1234',
67+
publicAddr: 'tcp-app.teleport.example.com',
68+
name: 'tcp-app',
69+
})}
70+
/>
71+
</Stack>
72+
73+
<Stack>
74+
<Text>Web app</Text>
75+
<Component
76+
resource={makeApp({
77+
uri: 'http://localhost:1234',
78+
publicAddr: 'web-app.teleport.example.com',
79+
name: 'web-app',
80+
})}
81+
/>
82+
</Stack>
83+
84+
<Stack>
85+
<Text>AWS console</Text>
86+
<Component resource={awsConsoleApp} />
87+
</Stack>
88+
89+
<Stack>
90+
<Text>AWS IAM IC account</Text>
91+
<Component resource={awsIamIcAccountApp} />
92+
</Stack>
93+
94+
<Stack>
95+
<Text>Cloud app (GCP)</Text>
96+
<Component resource={gcpCloudApp} />
97+
</Stack>
98+
99+
<Stack>
100+
<Text>SAML app</Text>
101+
<Component
102+
resource={makeApp({
103+
uri: 'http://localhost:300',
104+
publicAddr: 'saml-app.teleport.example.com',
105+
fqdn: 'saml-app.teleport.example.com',
106+
name: 'saml-app',
107+
samlApp: true,
108+
})}
109+
/>
110+
</Stack>
111+
</Stack>
112+
113+
<Stack>
114+
<Text>Server</Text>
115+
<Component resource={nodes[0]} />
116+
</Stack>
117+
118+
<Stack>
119+
<Text>Database</Text>
120+
<Component resource={databases[0]} />
121+
</Stack>
122+
123+
<Stack>
124+
<Text>Kube</Text>
125+
<Component resource={kubes[0]} />
126+
</Stack>
127+
128+
<Stack>
129+
<Text>Windows desktop</Text>
130+
<Component resource={desktops[0]} />
131+
</Stack>
132+
</Flex>
133+
);
134+
}

web/packages/teleterm/src/ui/DocumentCluster/ActionButtons.story.tsx

+19-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import { Meta } from '@storybook/react';
2020

2121
import { Box, Flex, Text } from 'design';
22+
import { HoverTooltip } from 'design/Tooltip';
2223

2324
import {
2425
makeApp,
@@ -83,7 +84,7 @@ export function Story(props: StoryProps) {
8384

8485
function Buttons(props: StoryProps) {
8586
return (
86-
<Flex gap={4}>
87+
<Flex gap={4} flexWrap="wrap">
8788
<Flex gap={3} flexDirection="column">
8889
<Box>
8990
<Text>TCP app</Text>
@@ -101,6 +102,12 @@ function Buttons(props: StoryProps) {
101102
<Text>AWS console</Text>
102103
<AwsConsole />
103104
</Box>
105+
<HoverTooltip tipContent="Connect doesn't support cloud apps properly yet and shows them as TCP apps instead.">
106+
<Box>
107+
<Text>Cloud app (GCP)</Text>
108+
<CloudApp />
109+
</Box>
110+
</HoverTooltip>
104111
<Box>
105112
<Text>SAML app</Text>
106113
<SamlApp />
@@ -218,6 +225,17 @@ function AwsConsole() {
218225
);
219226
}
220227

228+
function CloudApp() {
229+
return (
230+
<ConnectAppActionButton
231+
app={makeApp({
232+
endpointUri: 'cloud://GCP',
233+
uri: `${testCluster.uri}/apps/bar`,
234+
})}
235+
/>
236+
);
237+
}
238+
221239
function SamlApp() {
222240
return (
223241
<ConnectAppActionButton

0 commit comments

Comments
 (0)