Skip to content

frontend: Labels: Update StoryBook stories #3359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions frontend/src/components/common/Label.stories/DateLabel.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2025 The Kubernetes Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Box } from '@mui/material';
import { Meta, StoryFn } from '@storybook/react';
import React from 'react';
import { getTestDate } from '../../../helpers/testHelpers';
import { TestContext } from '../../../test';
import { DateLabel, DateLabelProps } from '../Label';

const baseDate = getTestDate();
const oneHourAgo = new Date(baseDate.getTime() - 60 * 60 * 1000);
const twoDaysAgo = new Date(baseDate.getTime() - 2 * 24 * 60 * 60 * 1000);
const sixtyDaysAgo = new Date(baseDate.getTime() - 60 * 24 * 60 * 60 * 1000);

export default {
title: 'Labels/DateLabel',
component: DateLabel,
decorators: [
Story => (
<TestContext>
<Box sx={{ padding: 2, display: 'flex', flexDirection: 'column', gap: 2 }}>
<Story />
</Box>
</TestContext>
),
],
argTypes: {
date: {
control: 'date',
description: 'The date to display. Can be a Date object, string, or timestamp number.',
},
format: {
control: 'radio',
options: ['brief', 'mini', undefined],
description:
'The format for displaying the time ago text (e.g., "1 hour ago" vs "1h"). Defaults to brief.',
},
iconProps: {
control: 'object',
description: 'Props to pass to the calendar Icon component (Iconify).',
},
},
} as Meta<typeof DateLabel>;

const Template: StoryFn<DateLabelProps> = args => <DateLabel {...args} />;

export const RecentBrief = Template.bind({});
RecentBrief.args = {
date: oneHourAgo.toISOString(),
format: 'brief',
};
RecentBrief.storyName = 'One Hour Ago (Brief)';

export const RecentMini = Template.bind({});
RecentMini.args = {
date: oneHourAgo.toISOString(),
format: 'mini',
};
RecentMini.storyName = 'One Hour Ago (Mini)';

export const OlderBrief = Template.bind({});
OlderBrief.args = {
date: twoDaysAgo.toISOString(),
format: 'brief',
};
OlderBrief.storyName = 'Two Days Ago (Brief)';

export const MuchOlderMiniTimestamp = Template.bind({});
MuchOlderMiniTimestamp.args = {
date: sixtyDaysAgo.getTime(),
format: 'mini',
};
MuchOlderMiniTimestamp.storyName = 'Sixty Days Ago (Mini, Timestamp Input)';

export const WithCustomIconProps = Template.bind({});
WithCustomIconProps.args = {
date: baseDate.toISOString(),
iconProps: {
color: 'green',
width: '20px',
height: '20px',
},
};
WithCustomIconProps.storyName = 'With Custom Icon Props';

export const DefaultFormat = Template.bind({});
DefaultFormat.args = {
date: oneHourAgo.toISOString(),
};
DefaultFormat.storyName = 'Default Format (Should be Brief)';
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,57 @@

import { Meta, StoryFn } from '@storybook/react';
import React from 'react';
import { HeaderLabel as HeaderLabelComponent, HeaderLabelProps } from '../Label';
import { TestContext } from '../../../test';
import { HeaderLabel, HeaderLabelProps } from '../Label';

export default {
title: 'Label/HeaderLabel',
component: HeaderLabelComponent,
argTypes: {},
} as Meta;
title: 'Labels/HeaderLabel',
component: HeaderLabel,
decorators: [
Story => (
<TestContext>
<Story />
</TestContext>
),
],
argTypes: {
label: {
control: 'text',
description: 'The main label text (typically smaller, above value).',
},
value: { control: 'text', description: 'The prominent value text (typically larger).' },
tooltip: {
control: 'text',
description: 'Optional tooltip text to show on hover of the info icon.',
},
},
} as Meta<typeof HeaderLabel>;

const Template: StoryFn<HeaderLabelProps> = args => <HeaderLabelComponent {...args} />;
const Template: StoryFn<HeaderLabelProps> = args => <HeaderLabel {...args} />;

export const HeaderLabel = Template.bind({});
HeaderLabel.args = {
value: 'value',
label: 'name',
export const Default = Template.bind({});
Default.args = {
label: 'Uptime',
value: '25 days',
};
export const HeaderLabelToolTip = Template.bind({});
HeaderLabelToolTip.args = {
value: 'value',
label: 'name',
tooltip: 'tooltip',
Default.storyName = 'Basic Header Label';

export const WithTooltip = Template.bind({});
WithTooltip.args = {
label: 'Active Pods',
value: '120',
tooltip: 'Number of currently active pods in the cluster.',
};
WithTooltip.storyName = 'With Tooltip';

export const NumericValue = Template.bind({});
NumericValue.args = {
label: 'CPU Usage',
value: '75%',
};

export const ShortLabelAndValue = Template.bind({});
ShortLabelAndValue.args = {
label: 'Nodes',
value: '5',
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,109 @@
* limitations under the License.
*/

import { Box, Typography } from '@mui/material';
import { Meta, StoryFn } from '@storybook/react';
import { HoverInfoLabel as HoverInfoLabelComponent, HoverInfoLabelProps } from '../Label';
import React from 'react';
import { TestContext } from '../../../test';
import { HoverInfoLabel, HoverInfoLabelProps } from '../Label';

export default {
title: 'Label/HoverInfoLabel',
component: HoverInfoLabelComponent,
argTypes: {},
} as Meta;
title: 'Labels/HoverInfoLabel',
component: HoverInfoLabel,
decorators: [
Story => (
<TestContext>
<Box sx={{ padding: 2 }}>
<Story />
</Box>
</TestContext>
),
],
argTypes: {
label: { control: 'text', description: 'The main label text.' },
hoverInfo: {
control: 'text',
description: 'Information to display on hover (string or ReactNode).',
},
icon: {
control: 'text',
description: 'Iconify string for the icon (e.g., "mdi:information-outline").',
},
iconPosition: {
control: 'radio',
options: ['start', 'end'],
description: 'Position of the icon relative to the label.',
},
labelProps: {
control: 'object',
description: 'Props to pass to the label Typography component.',
},
iconProps: { control: 'object', description: 'Props to pass to the Icon component.' },
},
} as Meta<typeof HoverInfoLabel>;

const Template: StoryFn<HoverInfoLabelProps> = args => <HoverInfoLabelComponent {...args} />;
const Template: StoryFn<HoverInfoLabelProps> = args => <HoverInfoLabel {...args} />;

export const HoverInfoLabel = Template.bind({});
HoverInfoLabel.args = {
label: 'Some label',
hoverInfo: 'hover info',
export const Default = Template.bind({});
Default.args = {
label: 'Creation Timestamp',
hoverInfo: 'The time at which this resource was created.',
};
Default.storyName = 'Basic HoverInfoLabel';

export const HoverInfoLabelInfo = Template.bind({});
HoverInfoLabelInfo.args = {
label: 'Some label',
hoverInfo: <div>hover info div</div>,
export const WithCustomIcon = Template.bind({});
WithCustomIcon.args = {
label: 'Status Details',
hoverInfo: 'Current operational status of the resource.',
icon: 'mdi:help-circle-outline',
};
WithCustomIcon.storyName = 'With Custom Icon';

export const LabelProps = Template.bind({});
LabelProps.args = {
label: 'Some label',
hoverInfo: <div>hover info div</div>,
labelProps: {
variant: 'body2',
},
export const WithReactNodeAsHoverInfo = Template.bind({});
WithReactNodeAsHoverInfo.args = {
label: 'Complex Information',
hoverInfo: (
<>
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
Detailed Breakdown
</Typography>
<ul>
<li>Point one about the complex information.</li>
<li>Point two with further details.</li>
</ul>
</>
),
};
WithReactNodeAsHoverInfo.storyName = 'With ReactNode as HoverInfo';

export const IconPosition = Template.bind({});
IconPosition.args = {
label: 'Some label',
hoverInfo: <div>hover info div</div>,
export const IconAtStart = Template.bind({});
IconAtStart.args = {
label: 'Information First',
hoverInfo: 'The information icon appears before the label text.',
iconPosition: 'start',
};
IconAtStart.storyName = 'Icon Position Start';

// icon isn't used in the codebase.
// export const HoverInfoLabelIcon = Template.bind({});
// HoverInfoLabelIcon.args = {
// label: "Some label",
// hoverInfo: "value",
// icon: null, // unused it seems.
// };
export const CustomLabelStyling = Template.bind({});
CustomLabelStyling.args = {
label: 'Important System Label',
hoverInfo: 'This label uses custom typography styling.',
labelProps: {
variant: 'h6',
color: 'error.dark',
sx: { fontStyle: 'italic' },
},
};
CustomLabelStyling.storyName = 'With Custom Label Styling';

export const CustomIconProps = Template.bind({});
CustomIconProps.args = {
label: 'Label with Big Icon',
hoverInfo: 'The icon next to this label is larger and has a different color.',
iconProps: {
width: '1.5rem',
height: '1.5rem',
color: 'purple',
},
};
CustomIconProps.storyName = 'With Custom Icon Props';
66 changes: 56 additions & 10 deletions frontend/src/components/common/Label.stories/InfoLabel.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,66 @@
* limitations under the License.
*/

import { Typography } from '@mui/material';
import { Meta, StoryFn } from '@storybook/react';
import React from 'react';
import { InfoLabel as InfoLabelComponent, InfoLabelProps } from '../Label';
import { TestContext } from '../../../test';
import { InfoLabel, InfoLabelProps } from '../Label';

export default {
title: 'Label/InfoLabel',
component: InfoLabelComponent,
argTypes: {},
} as Meta;
title: 'Labels/InfoLabel',
component: InfoLabel,
decorators: [
Story => (
<TestContext>
<Story />
</TestContext>
),
],
argTypes: {
name: { control: 'text', description: 'The name/key part of the label (left side).' },
value: {
control: 'text',
description:
'The value part of the label (right side). If children are provided, this prop is ignored.',
},
children: {
control: 'object',
description: 'Custom React node for the value part. Overrides the `value` prop if provided.',
},
},
} as Meta<typeof InfoLabel>;

const Template: StoryFn<InfoLabelProps> = args => <InfoLabelComponent {...args} />;
const Template: StoryFn<InfoLabelProps> = args => <InfoLabel {...args} />;

export const InfoLabel = Template.bind({});
InfoLabel.args = {
name: 'name',
value: 'value',
export const WithStringValue = Template.bind({});
WithStringValue.args = {
name: 'Property Name',
value: 'Property Value',
};
WithStringValue.storyName = 'With String Value';

export const WithReactNodeValue = Template.bind({});
WithReactNodeValue.args = {
name: 'Complex Property',
children: (
<Typography color="secondary.main">
This is a <em>custom React node</em> value.
</Typography>
),
};
WithReactNodeValue.storyName = 'With React Node Value';

export const OnlyName = Template.bind({});
OnlyName.args = {
name: 'Property With No Explicit Value',
};
OnlyName.storyName = 'Only Name (Value Undefined)';

export const LongNameAndValue = Template.bind({});
LongNameAndValue.args = {
name: 'This is a very long property name to demonstrate how it behaves within the grid layout used by InfoLabel',
value:
'This is a correspondingly long property value that should also wrap or be handled appropriately by the layout and typography settings.',
};
LongNameAndValue.storyName = 'With Long Name and Value';
Loading
Loading