First pass introducing the Alert component for BUI
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Added new `Alert` component with support for status variants, icons, loading states, and custom actions.
|
||||
|
||||
**Affected components**: Alert
|
||||
@@ -124,6 +124,71 @@ export interface AccordionTriggerProps extends HeadingProps {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const Alert: ForwardRefExoticComponent<
|
||||
AlertProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
|
||||
// @public
|
||||
export const AlertDefinition: {
|
||||
readonly styles: {
|
||||
readonly [key: string]: string;
|
||||
};
|
||||
readonly classNames: {
|
||||
readonly root: 'bui-Alert';
|
||||
readonly content: 'bui-AlertContent';
|
||||
readonly icon: 'bui-AlertIcon';
|
||||
readonly spinner: 'bui-AlertSpinner';
|
||||
readonly actions: 'bui-AlertActions';
|
||||
};
|
||||
readonly surface: 'container';
|
||||
readonly propDefs: {
|
||||
readonly status: {
|
||||
readonly dataAttribute: true;
|
||||
readonly default: 'info';
|
||||
};
|
||||
readonly loading: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
readonly icon: {};
|
||||
readonly customActions: {};
|
||||
readonly surface: {};
|
||||
readonly children: {};
|
||||
readonly className: {};
|
||||
readonly style: {};
|
||||
};
|
||||
readonly utilityProps: readonly [
|
||||
'm',
|
||||
'mb',
|
||||
'ml',
|
||||
'mr',
|
||||
'mt',
|
||||
'mx',
|
||||
'my',
|
||||
'p',
|
||||
'pb',
|
||||
'pl',
|
||||
'pr',
|
||||
'pt',
|
||||
'px',
|
||||
'py',
|
||||
];
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type AlertOwnProps = ContainerSurfaceProps & {
|
||||
status?: Responsive<'info' | 'success' | 'warning' | 'danger'>;
|
||||
icon?: boolean | ReactElement;
|
||||
loading?: boolean;
|
||||
customActions?: ReactNode;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface AlertProps extends SpaceProps, AlertOwnProps {}
|
||||
|
||||
// @public (undocumented)
|
||||
export type AlignItems = 'stretch' | 'start' | 'center' | 'end';
|
||||
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage 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.
|
||||
*/
|
||||
|
||||
@layer tokens, base, components, utilities;
|
||||
|
||||
@layer components {
|
||||
.bui-Alert {
|
||||
--loading-duration: 200ms;
|
||||
--alert-bg: var(--bui-bg-surface-1);
|
||||
--alert-fg: var(--bui-fg-primary);
|
||||
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--bui-space-3);
|
||||
padding: var(--bui-space-3) var(--bui-space-4);
|
||||
border-radius: var(--bui-radius-2);
|
||||
font-family: var(--bui-font-regular);
|
||||
font-size: var(--bui-font-size-3);
|
||||
line-height: 1.5;
|
||||
transition: opacity var(--loading-duration) ease-out;
|
||||
|
||||
/* Apply variables */
|
||||
background-color: var(--alert-bg);
|
||||
color: var(--alert-fg);
|
||||
|
||||
&[data-loading='true'] {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.bui-Alert[data-status='info'] {
|
||||
--alert-bg: var(--bui-bg-surface-1);
|
||||
--alert-fg: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-Alert[data-status='success'] {
|
||||
--alert-bg: var(--bui-bg-success);
|
||||
--alert-fg: var(--bui-fg-success);
|
||||
}
|
||||
|
||||
.bui-Alert[data-status='warning'] {
|
||||
--alert-bg: var(--bui-bg-warning);
|
||||
--alert-fg: var(--bui-fg-warning);
|
||||
}
|
||||
|
||||
.bui-Alert[data-status='danger'] {
|
||||
--alert-bg: var(--bui-bg-danger);
|
||||
--alert-fg: var(--bui-fg-danger);
|
||||
}
|
||||
|
||||
.bui-AlertIcon {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 0.125rem;
|
||||
transition: opacity var(--loading-duration) ease-out;
|
||||
|
||||
svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.bui-Alert[data-loading='true'] & {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.bui-AlertContent {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.bui-AlertSpinner {
|
||||
position: absolute;
|
||||
top: var(--bui-space-3);
|
||||
left: var(--bui-space-4);
|
||||
display: flex;
|
||||
opacity: 0;
|
||||
transition: opacity var(--loading-duration) ease-in;
|
||||
|
||||
.bui-Alert[data-loading='true'] & {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
& svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
animation: bui-spin 1s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.bui-AlertActions {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--bui-space-2);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.bui-Alert {
|
||||
transition-duration: 50ms;
|
||||
}
|
||||
|
||||
.bui-AlertIcon {
|
||||
transition-duration: 50ms;
|
||||
}
|
||||
|
||||
.bui-AlertSpinner {
|
||||
transition-duration: 50ms;
|
||||
}
|
||||
|
||||
.bui-AlertSpinner svg {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bui-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage 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 preview from '../../../../../.storybook/preview';
|
||||
import { Alert } from './Alert';
|
||||
import { Flex } from '../Flex';
|
||||
import { Box } from '../Box';
|
||||
import { Text } from '../Text';
|
||||
import { Button } from '../Button';
|
||||
import { RiCloudLine } from '@remixicon/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
const meta = preview.meta({
|
||||
title: 'Backstage UI/Alert',
|
||||
component: Alert,
|
||||
argTypes: {
|
||||
status: {
|
||||
control: 'select',
|
||||
options: ['info', 'success', 'warning', 'danger'],
|
||||
},
|
||||
icon: {
|
||||
control: 'boolean',
|
||||
},
|
||||
loading: {
|
||||
control: 'boolean',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const Default = meta.story({
|
||||
args: {
|
||||
children: 'This is an alert message',
|
||||
icon: true,
|
||||
},
|
||||
});
|
||||
|
||||
export const StatusVariants = meta.story({
|
||||
args: {
|
||||
children: 'This is an alert message',
|
||||
},
|
||||
parameters: {
|
||||
argTypes: {
|
||||
status: {
|
||||
control: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Alert status="info" icon={true}>
|
||||
This is an informational alert with helpful information.
|
||||
</Alert>
|
||||
<Alert status="success" icon={true}>
|
||||
Your changes have been saved successfully.
|
||||
</Alert>
|
||||
<Alert status="warning" icon={true}>
|
||||
This action may have unintended consequences.
|
||||
</Alert>
|
||||
<Alert status="danger" icon={true}>
|
||||
An error occurred while processing your request.
|
||||
</Alert>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const WithoutIcons = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Alert status="info" icon={false}>
|
||||
This is an informational alert without an icon.
|
||||
</Alert>
|
||||
<Alert status="success" icon={false}>
|
||||
Your changes have been saved successfully.
|
||||
</Alert>
|
||||
<Alert status="warning" icon={false}>
|
||||
This action may have unintended consequences.
|
||||
</Alert>
|
||||
<Alert status="danger" icon={false}>
|
||||
An error occurred while processing your request.
|
||||
</Alert>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const CustomIcon = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Alert status="info" icon={<RiCloudLine />}>
|
||||
This alert uses a custom cloud icon instead of the default info icon.
|
||||
</Alert>
|
||||
<Alert status="success" icon={<RiCloudLine />}>
|
||||
Custom icons work with any status variant.
|
||||
</Alert>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const WithActions = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Alert
|
||||
status="info"
|
||||
icon={true}
|
||||
customActions={
|
||||
<>
|
||||
<Button size="small" variant="tertiary">
|
||||
Dismiss
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
This alert has a dismiss action on the right.
|
||||
</Alert>
|
||||
<Alert
|
||||
status="success"
|
||||
icon={true}
|
||||
customActions={
|
||||
<>
|
||||
<Button size="small" variant="tertiary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="small" variant="primary">
|
||||
Continue
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
Your changes have been saved. Would you like to continue?
|
||||
</Alert>
|
||||
<Alert
|
||||
status="danger"
|
||||
icon={true}
|
||||
customActions={
|
||||
<>
|
||||
<Button size="small" variant="primary">
|
||||
Retry
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
An error occurred while processing your request. Please try again.
|
||||
</Alert>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const Loading = meta.story({
|
||||
render: () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleLoad = () => {
|
||||
setIsLoading(true);
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="4">
|
||||
<Alert
|
||||
status="info"
|
||||
icon={true}
|
||||
loading={isLoading}
|
||||
customActions={
|
||||
<Button size="small" variant="primary" onPress={handleLoad}>
|
||||
Load
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
Click the button to see the loading state
|
||||
</Alert>
|
||||
</Flex>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const LoadingVariants = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Info</Text>
|
||||
<Alert status="info" icon={true} loading>
|
||||
Processing your request...
|
||||
</Alert>
|
||||
|
||||
<Text>Success</Text>
|
||||
<Alert status="success" icon={true} loading>
|
||||
Saving changes...
|
||||
</Alert>
|
||||
|
||||
<Text>Warning</Text>
|
||||
<Alert status="warning" icon={true} loading>
|
||||
Checking for issues...
|
||||
</Alert>
|
||||
|
||||
<Text>Danger</Text>
|
||||
<Alert status="danger" icon={true} loading>
|
||||
Attempting recovery...
|
||||
</Alert>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const LongContent = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Alert status="info" icon={true}>
|
||||
This is a longer alert message that demonstrates how the component
|
||||
handles multiple lines of text. The content will wrap naturally and
|
||||
maintain proper spacing with the icon and any actions. This is useful
|
||||
for providing detailed information to users when necessary.
|
||||
</Alert>
|
||||
<Alert
|
||||
status="warning"
|
||||
icon={true}
|
||||
customActions={
|
||||
<Button size="small" variant="tertiary">
|
||||
Dismiss
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
This alert combines long content with actions. The actions remain
|
||||
aligned to the right even when the content wraps to multiple lines. This
|
||||
ensures a consistent and predictable layout regardless of content
|
||||
length.
|
||||
</Alert>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const OnDifferentSurfaces = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Default Surface</Text>
|
||||
<Flex direction="column" gap="2" p="4">
|
||||
<Alert status="info" icon={true}>
|
||||
Alert on default surface
|
||||
</Alert>
|
||||
<Alert status="success" icon={true}>
|
||||
Alert on default surface
|
||||
</Alert>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 0</Text>
|
||||
<Flex direction="column" gap="2" surface="0" p="4">
|
||||
<Alert status="info" icon={true}>
|
||||
Alert on surface 0
|
||||
</Alert>
|
||||
<Alert status="success" icon={true}>
|
||||
Alert on surface 0
|
||||
</Alert>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 1</Text>
|
||||
<Flex direction="column" gap="2" surface="1" p="4">
|
||||
<Alert status="info" icon={true}>
|
||||
Alert on surface 1
|
||||
</Alert>
|
||||
<Alert status="success" icon={true}>
|
||||
Alert on surface 1
|
||||
</Alert>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 2</Text>
|
||||
<Flex direction="column" gap="2" surface="2" p="4">
|
||||
<Alert status="info" icon={true}>
|
||||
Alert on surface 2
|
||||
</Alert>
|
||||
<Alert status="success" icon={true}>
|
||||
Alert on surface 2
|
||||
</Alert>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 3</Text>
|
||||
<Flex direction="column" gap="2" surface="3" p="4">
|
||||
<Alert status="info" icon={true}>
|
||||
Alert on surface 3
|
||||
</Alert>
|
||||
<Alert status="success" icon={true}>
|
||||
Alert on surface 3
|
||||
</Alert>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const Responsive = meta.story({
|
||||
args: {
|
||||
children: 'This alert changes status responsively',
|
||||
icon: true,
|
||||
status: {
|
||||
initial: 'info',
|
||||
sm: 'success',
|
||||
md: 'warning',
|
||||
lg: 'danger',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const WithUtilityProps = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Alert status="info" icon={true} mb="4" p="5">
|
||||
Alert with custom margin and padding using utility props
|
||||
</Alert>
|
||||
<Box surface="1" p="4">
|
||||
<Alert status="success" icon={true} mb="0">
|
||||
Alert with zero margin bottom
|
||||
</Alert>
|
||||
</Box>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage 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 { forwardRef, Ref, isValidElement, ReactElement } from 'react';
|
||||
import { ProgressBar } from 'react-aria-components';
|
||||
import {
|
||||
RiLoader4Line,
|
||||
RiInformationLine,
|
||||
RiCheckLine,
|
||||
RiErrorWarningLine,
|
||||
RiAlertLine,
|
||||
} from '@remixicon/react';
|
||||
import type { AlertProps } from './types';
|
||||
import { useDefinition } from '../../hooks/useDefinition';
|
||||
import { AlertDefinition } from './definition';
|
||||
|
||||
/**
|
||||
* A component for displaying alert messages with different status levels.
|
||||
*
|
||||
* @remarks
|
||||
* The Alert component supports multiple status variants (info, success, warning, danger)
|
||||
* and can display icons, loading states, and custom actions. It automatically handles
|
||||
* icon selection based on status when the icon prop is set to true.
|
||||
*
|
||||
* @example
|
||||
* Basic usage:
|
||||
* ```tsx
|
||||
* <Alert status="info">This is an informational message</Alert>
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* With custom actions and loading state:
|
||||
* ```tsx
|
||||
* <Alert
|
||||
* status="success"
|
||||
* icon={true}
|
||||
* loading={isProcessing}
|
||||
* customActions={
|
||||
* <>
|
||||
* <Button size="small" variant="tertiary">Dismiss</Button>
|
||||
* <Button size="small" variant="primary">Action</Button>
|
||||
* </>
|
||||
* }
|
||||
* >
|
||||
* Operation completed successfully
|
||||
* </Alert>
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const Alert = forwardRef(
|
||||
(props: AlertProps, ref: Ref<HTMLDivElement>) => {
|
||||
const { ownProps, restProps, dataAttributes, utilityStyle } = useDefinition(
|
||||
AlertDefinition,
|
||||
props,
|
||||
);
|
||||
const {
|
||||
classes,
|
||||
status,
|
||||
icon,
|
||||
loading,
|
||||
customActions,
|
||||
style,
|
||||
surfaceChildren: children,
|
||||
} = ownProps;
|
||||
|
||||
// Determine which icon to render
|
||||
const getStatusIcon = (): ReactElement | null => {
|
||||
// If icon is explicitly false, don't render any icon
|
||||
if (icon === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If icon is a custom React element, use it
|
||||
if (isValidElement(icon)) {
|
||||
return icon;
|
||||
}
|
||||
|
||||
// If icon is true, auto-select based on status
|
||||
if (icon === true) {
|
||||
switch (status) {
|
||||
case 'success':
|
||||
return <RiCheckLine />;
|
||||
case 'warning':
|
||||
return <RiErrorWarningLine />;
|
||||
case 'danger':
|
||||
return <RiAlertLine />;
|
||||
case 'info':
|
||||
default:
|
||||
return <RiInformationLine />;
|
||||
}
|
||||
}
|
||||
|
||||
// Default: no icon
|
||||
return null;
|
||||
};
|
||||
|
||||
const statusIcon = getStatusIcon();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes.root}
|
||||
ref={ref}
|
||||
style={{ ...style, ...utilityStyle }}
|
||||
{...dataAttributes}
|
||||
{...restProps}
|
||||
>
|
||||
{statusIcon && <div className={classes.icon}>{statusIcon}</div>}
|
||||
|
||||
{loading && (
|
||||
<ProgressBar
|
||||
aria-label="Loading"
|
||||
isIndeterminate
|
||||
className={classes.spinner}
|
||||
>
|
||||
<RiLoader4Line aria-hidden="true" />
|
||||
</ProgressBar>
|
||||
)}
|
||||
|
||||
<div className={classes.content}>{children}</div>
|
||||
|
||||
{customActions && (
|
||||
<div className={classes.actions}>{customActions}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Alert.displayName = 'Alert';
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage 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 { defineComponent } from '../../hooks/useDefinition';
|
||||
import type { AlertOwnProps } from './types';
|
||||
import styles from './Alert.module.css';
|
||||
|
||||
/**
|
||||
* Component definition for Alert
|
||||
* @public
|
||||
*/
|
||||
export const AlertDefinition = defineComponent<AlertOwnProps>()({
|
||||
styles,
|
||||
classNames: {
|
||||
root: 'bui-Alert',
|
||||
content: 'bui-AlertContent',
|
||||
icon: 'bui-AlertIcon',
|
||||
spinner: 'bui-AlertSpinner',
|
||||
actions: 'bui-AlertActions',
|
||||
},
|
||||
surface: 'container',
|
||||
propDefs: {
|
||||
status: { dataAttribute: true, default: 'info' },
|
||||
loading: { dataAttribute: true },
|
||||
icon: {},
|
||||
customActions: {},
|
||||
surface: {},
|
||||
children: {},
|
||||
className: {},
|
||||
style: {},
|
||||
},
|
||||
utilityProps: [
|
||||
'm',
|
||||
'mb',
|
||||
'ml',
|
||||
'mr',
|
||||
'mt',
|
||||
'mx',
|
||||
'my',
|
||||
'p',
|
||||
'pb',
|
||||
'pl',
|
||||
'pr',
|
||||
'pt',
|
||||
'px',
|
||||
'py',
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage 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.
|
||||
*/
|
||||
|
||||
export * from './Alert';
|
||||
export * from './types';
|
||||
export { AlertDefinition } from './definition';
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage 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 type { ReactElement, ReactNode, CSSProperties } from 'react';
|
||||
import type {
|
||||
ContainerSurfaceProps,
|
||||
Responsive,
|
||||
SpaceProps,
|
||||
} from '../../types';
|
||||
|
||||
/** @public */
|
||||
export type AlertOwnProps = ContainerSurfaceProps & {
|
||||
status?: Responsive<'info' | 'success' | 'warning' | 'danger'>;
|
||||
icon?: boolean | ReactElement;
|
||||
loading?: boolean;
|
||||
customActions?: ReactNode;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
|
||||
/**
|
||||
* Properties for {@link Alert}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface AlertProps extends SpaceProps, AlertOwnProps {}
|
||||
@@ -27,10 +27,11 @@ export * from './components/Flex';
|
||||
export * from './components/Container';
|
||||
|
||||
// UI components
|
||||
export * from './components/Accordion';
|
||||
export * from './components/Alert';
|
||||
export * from './components/Avatar';
|
||||
export * from './components/Button';
|
||||
export * from './components/Card';
|
||||
export * from './components/Accordion';
|
||||
export * from './components/Dialog';
|
||||
export * from './components/FieldLabel';
|
||||
export * from './components/Header';
|
||||
|
||||
Reference in New Issue
Block a user