Skip to content

Commit e828f82

Browse files
committed
Frontend: Components: Added seperate component to display Env Variables
1 parent b228929 commit e828f82

11 files changed

+749
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2025 The Kubernetes Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { Meta, StoryFn } from '@storybook/react';
18+
import { TestContext } from '../../../test';
19+
import { EnvVarGrid } from './EnvVarDisplay';
20+
21+
export default {
22+
title: 'Resource/EnvVarDisplay',
23+
component: EnvVarGrid,
24+
decorators: [
25+
Story => (
26+
<TestContext>
27+
<Story />
28+
</TestContext>
29+
),
30+
],
31+
argTypes: {
32+
namespace: { control: 'text' },
33+
cluster: { control: 'text' },
34+
},
35+
} as Meta;
36+
37+
const Template: StoryFn<React.ComponentProps<typeof EnvVarGrid>> = args => <EnvVarGrid {...args} />;
38+
39+
export const PlainValues = Template.bind({});
40+
PlainValues.args = {
41+
namespace: 'default',
42+
cluster: 'minikube',
43+
envVars: [
44+
{ name: 'NODE_ENV', value: 'production' },
45+
{ name: 'DEBUG', value: 'true' },
46+
],
47+
};
48+
49+
export const ComplexReferences = Template.bind({});
50+
ComplexReferences.args = {
51+
namespace: 'default',
52+
cluster: 'minikube',
53+
envVars: [
54+
{ name: 'DB_HOST', value: '127.0.0.1' },
55+
{
56+
name: 'API_KEY',
57+
valueFrom: {
58+
secretKeyRef: { name: 'my-secret', key: 'api-key' },
59+
},
60+
},
61+
{
62+
name: 'APP_CONFIG',
63+
valueFrom: {
64+
configMapKeyRef: { name: 'app-config', key: 'config.json' },
65+
},
66+
},
67+
{
68+
name: 'MY_POD_IP',
69+
valueFrom: {
70+
fieldRef: { fieldPath: 'status.podIP' },
71+
},
72+
},
73+
{
74+
name: 'CPU_LIMIT',
75+
valueFrom: {
76+
resourceFieldRef: { resource: 'limits.cpu' },
77+
},
78+
},
79+
],
80+
};
81+
82+
export const ManyVariables = Template.bind({});
83+
ManyVariables.args = {
84+
namespace: 'default',
85+
cluster: 'minikube',
86+
envVars: Array.from({ length: 35 }, (_, i) => ({
87+
name: `VAR_${i}`,
88+
value: `value-${i}`,
89+
})),
90+
};
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright 2025 The Kubernetes Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { Icon } from '@iconify/react';
18+
import Box from '@mui/material/Box';
19+
import Button from '@mui/material/Button';
20+
import { Theme } from '@mui/material/styles';
21+
import Typography from '@mui/material/Typography';
22+
import React from 'react';
23+
import { useTranslation } from 'react-i18next';
24+
import Link from '../Link';
25+
26+
interface EnvVarGridProps {
27+
envVars: any[];
28+
namespace: string;
29+
cluster: string;
30+
}
31+
32+
export function EnvVarGrid(props: EnvVarGridProps) {
33+
const { envVars = [], namespace, cluster } = props;
34+
const { t } = useTranslation();
35+
const [expanded, setExpanded] = React.useState(false);
36+
const defaultNumShown = 20;
37+
38+
const envEntryStyle = (theme: Theme) => ({
39+
color: theme.palette.text.primary,
40+
borderRadius: theme.shape.borderRadius + 'px',
41+
backgroundColor: theme.palette.background.muted,
42+
border: '1px solid',
43+
borderColor: theme.palette.divider,
44+
fontSize: theme.typography.pxToRem(14),
45+
padding: '4px 8px',
46+
marginRight: theme.spacing(1),
47+
whiteSpace: 'nowrap',
48+
display: 'inline-block',
49+
});
50+
51+
const renderEnvVar = (envVar: any) => {
52+
//Secret Key:
53+
if (envVar.valueFrom?.secretKeyRef) {
54+
const { name: secretName, key: secretKey } = envVar.valueFrom.secretKeyRef;
55+
const secretUrl = `/c/${cluster}/secrets/${namespace}/${secretName}`;
56+
57+
return (
58+
<Typography component="span" sx={envEntryStyle} key={envVar.name}>
59+
{envVar.name}:{' '}
60+
<Link to={secretUrl} style={{ textDecoration: 'underline', fontWeight: 'bold' }}>
61+
Secret: {secretName} (Key: {secretKey})
62+
</Link>
63+
</Typography>
64+
);
65+
}
66+
67+
//Config Map:
68+
if (envVar.valueFrom?.configMapKeyRef) {
69+
const { name: cmName, key: cmKey } = envVar.valueFrom.configMapKeyRef;
70+
const secretUrl = `/c/${cluster}/secrets/${namespace}/${cmName}`;
71+
return (
72+
<Typography>
73+
{envVar.name}:{' '}
74+
<Link to={secretUrl} style={{ textDecoration: 'underline', fontWeight: 'bold' }}>
75+
ConfigMap: {cmName} (Key: {cmKey})
76+
</Link>
77+
</Typography>
78+
);
79+
}
80+
81+
//FieldRef:
82+
if (envVar.valueFrom?.fieldRef) {
83+
const { fieldPath } = envVar.valueFrom.fieldRef;
84+
return (
85+
<Typography component="span" sx={envEntryStyle} key={envVar.name}>
86+
{envVar.name}:FieldRef ({fieldPath})
87+
</Typography>
88+
);
89+
}
90+
91+
//ResourceFieldRef:
92+
if (envVar.valueFrom?.resourceFieldRef) {
93+
const { resource } = envVar.valueFrom.resourceFieldRef;
94+
return (
95+
<Typography component="span" sx={envEntryStyle} key={envVar.name}>
96+
{envVar.name}: ResourceField ({resource})
97+
</Typography>
98+
);
99+
}
100+
101+
//Plaintext
102+
return (
103+
<Typography component="span" sx={envEntryStyle} key={envVar.name}>
104+
{envVar.name}: {envVar.value}
105+
</Typography>
106+
);
107+
};
108+
109+
return (
110+
<Box>
111+
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
112+
{envVars
113+
.slice(0, expanded ? envVars.length : defaultNumShown)
114+
.map(env => renderEnvVar(env))}
115+
</Box>
116+
{envVars.length > defaultNumShown && (
117+
<Button
118+
onClick={() => setExpanded(!expanded)}
119+
size="small"
120+
startIcon={<Icon icon={expanded ? 'mdi:menu-up' : 'mdi:menu-down'} />}
121+
>
122+
{!expanded
123+
? t('translation|Show all environment variables (+{{count}} more)', {
124+
count: envVars.length - defaultNumShown,
125+
})
126+
: t('translation|Show fewer')}
127+
</Button>
128+
)}
129+
</Box>
130+
);
131+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<body>
2+
<div>
3+
<div
4+
class="MuiBox-root css-0"
5+
>
6+
<div
7+
class="MuiBox-root css-yi3mkw"
8+
>
9+
<span
10+
class="MuiTypography-root MuiTypography-body1 css-no9i7w-MuiTypography-root"
11+
>
12+
DB_HOST
13+
:
14+
127.0.0.1
15+
</span>
16+
<span
17+
class="MuiTypography-root MuiTypography-body1 css-no9i7w-MuiTypography-root"
18+
>
19+
API_KEY
20+
:
21+
22+
<a
23+
class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineHover css-2ugbm1-MuiTypography-root-MuiLink-root"
24+
href="/c/minikube/secrets/default/my-secret"
25+
style="text-decoration: underline; font-weight: bold;"
26+
>
27+
Secret:
28+
my-secret
29+
(Key:
30+
api-key
31+
)
32+
</a>
33+
</span>
34+
<p
35+
class="MuiTypography-root MuiTypography-body1 css-1ezega9-MuiTypography-root"
36+
>
37+
APP_CONFIG
38+
:
39+
40+
<a
41+
class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineHover css-2ugbm1-MuiTypography-root-MuiLink-root"
42+
href="/c/minikube/secrets/default/app-config"
43+
style="text-decoration: underline; font-weight: bold;"
44+
>
45+
ConfigMap:
46+
app-config
47+
(Key:
48+
config.json
49+
)
50+
</a>
51+
</p>
52+
<span
53+
class="MuiTypography-root MuiTypography-body1 css-no9i7w-MuiTypography-root"
54+
>
55+
MY_POD_IP
56+
:FieldRef (
57+
status.podIP
58+
)
59+
</span>
60+
<span
61+
class="MuiTypography-root MuiTypography-body1 css-no9i7w-MuiTypography-root"
62+
>
63+
CPU_LIMIT
64+
: ResourceField (
65+
limits.cpu
66+
)
67+
</span>
68+
</div>
69+
</div>
70+
</div>
71+
</body>

0 commit comments

Comments
 (0)