refactor(ui): migrate to component-owned definitions

Eliminates the central componentDefinitions registry in favor of component-owned
definition files. Each of the 33 components now exports its own definition object.

Changes:
- Created definition.ts for all 33 components (Accordion, Avatar, Box, Button, etc.)
- Updated useStyles hook to accept definition objects instead of string lookups
- Removed central componentDefinitions file and related type utilities
- Updated docs-ui to import and use component definitions directly
- Added @backstage/ui/definitions subpath export to avoid importing component code
  in docs-ui, preventing Next.js SSR issues

Breaking changes:
- componentDefinitions object removed
- ComponentDefinitionName type removed
- ComponentClassNames<T> type removed
- useStyles now accepts ComponentDefinition object instead of string (internal)

Migration:
Component definitions are primarily for documenting the CSS class API for theming.
If using definitions programmatically, migrate to either:
- Use Backstage UI components directly as building blocks, or
- Duplicate component CSS in your own stylesheets

Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
Johan Persson
2025-11-10 16:35:28 +01:00
parent f3ae275cfd
commit a67670d240
148 changed files with 1915 additions and 1042 deletions
+14
View File
@@ -0,0 +1,14 @@
---
'@backstage/ui': minor
---
**BREAKING**: Removed central `componentDefinitions` object and related type utilities (`ComponentDefinitionName`, `ComponentClassNames`).
Component definitions are primarily intended for documenting the CSS class API for theming purposes, not for programmatic use in JavaScript/TypeScript.
**Migration Guide:**
If you were using component definitions or class names to build custom components, we recommend migrating to either:
- Use Backstage UI components directly as building blocks, or
- Duplicate the component CSS in your own stylesheets instead of relying on internal class names
+7 -12
View File
@@ -1,18 +1,13 @@
import { MDXRemote } from 'next-mdx-remote-client/rsc';
import { formattedMDXComponents } from '@/mdx-components';
import { Component } from '@/utils/changelog';
import { componentDefinitions } from '../../../../packages/ui/src/utils/componentDefinitions';
import type { DataAttributeValues } from '../../../../packages/ui/src/types';
import type {
ComponentDefinition,
DataAttributeValues,
} from '../../../../packages/ui/src/types';
export function Theming({ component }: { component: Component }) {
const componentDefinition = componentDefinitions[component];
if (!componentDefinition) {
return null;
}
const classNames = componentDefinition.classNames;
const dataAttributes = componentDefinition.dataAttributes;
export function Theming({ definition }: { definition: ComponentDefinition }) {
const classNames = definition.classNames;
const dataAttributes = definition.dataAttributes;
// Get the first class name
const firstClassName = Object.values(classNames)[0];
+2 -1
View File
@@ -16,6 +16,7 @@ import {
} from './accordion.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { AccordionDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -123,6 +124,6 @@ Here's a view when multiple accordions can be open simultaneously.
code={accordionGroupMultipleOpenSnippet}
/>
<Theming component="Accordion" />
<Theming definition={AccordionDefinition} />
<ChangelogComponent component="accordion" />
+2 -1
View File
@@ -11,6 +11,7 @@ import {
} from './avatar.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { AvatarDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -70,6 +71,6 @@ Control how the avatar is announced to screen readers using the `purpose` prop.
code={snippetPurpose}
/>
<Theming component="Avatar" />
<Theming definition={AvatarDefinition} />
<ChangelogComponent component="avatar" />
+2 -1
View File
@@ -12,6 +12,7 @@ import {
import { spacingPropDefs } from '@/utils/propDefs';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { BoxDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -60,6 +61,6 @@ Here's a view when buttons are responsive.
<CodeBlock code={boxResponsiveSnippet} />
<Theming component="Box" />
<Theming definition={BoxDefinition} />
<ChangelogComponent component="box" />
+2 -1
View File
@@ -15,6 +15,7 @@ import {
} from './button-icon.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ButtonIconDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -93,6 +94,6 @@ Here's a view when buttons are responsive.
<CodeBlock code={buttonIconResponsiveSnippet} />
<Theming component="ButtonIcon" />
<Theming definition={ButtonIconDefinition} />
<ChangelogComponent component="button-icon" />
+2 -1
View File
@@ -13,6 +13,7 @@ import {
} from './button-link.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ButtonLinkDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -91,6 +92,6 @@ Here's a view when buttons are responsive.
<CodeBlock code={buttonLinkResponsiveSnippet} />
<Theming component="ButtonLink" />
<Theming definition={ButtonLinkDefinition} />
<ChangelogComponent component="button-link" />
+2 -1
View File
@@ -16,6 +16,7 @@ import {
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ButtonDefinition } from '../utils/definitions';
<PageTitle
title="Button"
@@ -117,6 +118,6 @@ If you want to use a button as a link, please use the `ButtonLink` component.
code={buttonAsLinkSnippet}
/>
<Theming component="Button" />
<Theming definition={ButtonDefinition} />
<ChangelogComponent component="button" />
+2 -1
View File
@@ -14,6 +14,7 @@ import {
} from './card.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { CardDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -84,6 +85,6 @@ Here's a view when card has a list.
open
/>
<Theming component="Card" />
<Theming definition={CardDefinition} />
<ChangelogComponent component="card" />
+2 -1
View File
@@ -10,6 +10,7 @@ import {
} from './checkbox.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { CheckboxDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -46,6 +47,6 @@ Here's a view when checkboxes have different variants.
code={checkboxVariantsSnippet}
/>
<Theming component="Checkbox" />
<Theming definition={CheckboxDefinition} />
<ChangelogComponent component="checkbox" />
+2 -1
View File
@@ -11,6 +11,7 @@ import {
} from './container.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ContainerDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -48,6 +49,6 @@ create responsive designs.
<CodeBlock code={containerResponsiveSnippet} />
<Theming component="Container" />
<Theming definition={ContainerDefinition} />
<ChangelogComponent component="container" />
+2 -1
View File
@@ -18,6 +18,7 @@ import {
} from './dialog.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { DialogDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -102,6 +103,6 @@ You can also control the dialog using your own states.
<CodeBlock code={dialogWithNoTriggerSnippet} />
<Theming component="Dialog" />
<Theming definition={DialogDefinition} />
<ChangelogComponent component="dialog" />
+2 -1
View File
@@ -14,6 +14,7 @@ import {
import { spacingPropDefs } from '@/utils/propDefs';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { FlexDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -63,6 +64,6 @@ create responsive designs.
<CodeBlock code={flexAlignSnippet} />
<Theming component="Flex" />
<Theming definition={FlexDefinition} />
<ChangelogComponent component="flex" />
+2 -1
View File
@@ -16,6 +16,7 @@ import {
} from './grid.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { GridDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -93,6 +94,6 @@ The start and end props can be used to position the item in the grid.
<CodeBlock code={gridStartEndSnippet} />
<Theming component="Grid" />
<Theming definition={GridDefinition} />
<ChangelogComponent component="grid" />
+2 -1
View File
@@ -14,6 +14,7 @@ import {
} from './header-page.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { HeaderPageDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -82,6 +83,6 @@ prop is an array of objects with a `label`, `value` and `onClick` property.
code={withMenuItems}
/>
<Theming component="HeaderPage" />
<Theming definition={HeaderPageDefinition} />
<ChangelogComponent component="Header" />
+2 -1
View File
@@ -13,6 +13,7 @@ import {
} from './header.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { HeaderDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -80,6 +81,6 @@ You can use the `Header` component inside the [HeaderPage](/components/header-pa
open
/>
<Theming component="Header" />
<Theming definition={HeaderDefinition} />
<ChangelogComponent component="Header" />
+2 -1
View File
@@ -19,6 +19,7 @@ import {
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { LinkDefinition } from '../utils/definitions';
<PageTitle
title="Link"
@@ -97,6 +98,6 @@ The `Link` component has a `truncate` prop that can be used to truncate the text
code={linkTruncateSnippet}
/>
<Theming component="Link" />
<Theming definition={LinkDefinition} />
<ChangelogComponent component="link" />
+2 -1
View File
@@ -27,6 +27,7 @@ import {
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { MenuDefinition } from '../utils/definitions';
<PageTitle
title="Menu"
@@ -231,6 +232,6 @@ allow multiple selection.
code={autocompleteListboxMultiple}
/>
<Theming component="Menu" />
<Theming definition={MenuDefinition} />
<ChangelogComponent component="menu" />
+2 -1
View File
@@ -12,6 +12,7 @@ import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { CodeBlock } from '@/components/CodeBlock';
import { PasswordFieldDefinition } from '../utils/definitions';
<PageTitle
title="PasswordField"
@@ -59,6 +60,6 @@ Here's a simple PasswordField with a description.
code={passwordFieldDescriptionSnippet}
/>
<Theming component="PasswordField" />
<Theming definition={PasswordFieldDefinition} />
<ChangelogComponent component="password-field" />
+2 -1
View File
@@ -16,6 +16,7 @@ import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { CodeBlock } from '@/components/CodeBlock';
import { RadioGroupDefinition } from '../utils/definitions';
<PageTitle
title="RadioGroup"
@@ -99,6 +100,6 @@ You can make the radio group read only by adding the `isReadOnly` prop to the `R
code={radioGroupReadOnlySnippet}
/>
<Theming component="RadioGroup" />
<Theming definition={RadioGroupDefinition} />
<ChangelogComponent component="radio-group" />
+2 -1
View File
@@ -13,6 +13,7 @@ import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { CodeBlock } from '@/components/CodeBlock';
import { SearchFieldDefinition } from '../utils/definitions';
<PageTitle
title="SearchField"
@@ -72,6 +73,6 @@ You can make the SearchField collapsible by setting the `startCollapsed` prop to
code={searchFieldCollapsibleSnippet}
/>
<Theming component="SearchField" />
<Theming definition={SearchFieldDefinition} />
<ChangelogComponent component="search-field" />
+2 -1
View File
@@ -18,6 +18,7 @@ import {
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { SelectDefinition } from '../utils/definitions';
<PageTitle
title="Select"
@@ -132,6 +133,6 @@ Here's a view when the select is responsive.
<CodeBlock code={selectResponsiveSnippet} />
<Theming component="Select" />
<Theming definition={SelectDefinition} />
<ChangelogComponent component="select" />
+2 -1
View File
@@ -12,6 +12,7 @@ import {
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { SkeletonDefinition } from '../utils/definitions';
<PageTitle
title="Skeleton"
@@ -59,6 +60,6 @@ You can use a mix of different sizes to create a more complex skeleton.
open
/>
<Theming component="Skeleton" />
<Theming definition={SkeletonDefinition} />
<ChangelogComponent component="skeleton" />
+2 -1
View File
@@ -6,6 +6,7 @@ import { switchPropDefs, snippetUsage } from './switch.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { SwitchDefinition } from '../utils/definitions';
<PageTitle
title="Switch"
@@ -41,6 +42,6 @@ A switch can be disabled using the `isDisabled` prop.
code={`<Switch isDisabled />`}
/>
<Theming component="Switch" />
<Theming definition={SwitchDefinition} />
<ChangelogComponent component="switch" />
+2 -1
View File
@@ -22,6 +22,7 @@ import {
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { TableDefinition } from '../utils/definitions';
<PageTitle
title="Table"
@@ -128,6 +129,6 @@ This feature is not available yet — let us know if you'd like us to explore it
This feature is not available yet — let us know if you'd like us to explore it!
<Theming component="Table" />
<Theming definition={TableDefinition} />
<ChangelogComponent component="table" />
+2 -1
View File
@@ -13,6 +13,7 @@ import {
} from './tabs.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { TabsDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -79,6 +80,6 @@ You can use the `matchStrategy` prop on the tab to control how the tab is matche
open
/>
<Theming component="Tabs" />
<Theming definition={TabsDefinition} />
<ChangelogComponent component="tabs" />
+2 -1
View File
@@ -15,6 +15,7 @@ import {
} from './tag-group.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { TagGroupDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -109,6 +110,6 @@ A switch can be disabled using the `isDisabled` prop.
code={disabled}
/>
<Theming component="Switch" />
<Theming definition={TagGroupDefinition} />
<ChangelogComponent component="switch" />
+2 -1
View File
@@ -10,6 +10,7 @@ import {
} from './text-field.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { TextFieldDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { CodeBlock } from '@/components/CodeBlock';
@@ -59,6 +60,6 @@ Here's a simple TextField with a description.
code={textFieldDescriptionSnippet}
/>
<Theming component="TextField" />
<Theming definition={TextFieldDefinition} />
<ChangelogComponent component="text-field" />
+2 -1
View File
@@ -14,6 +14,7 @@ import {
} from './text.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { TextDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -87,6 +88,6 @@ on the screen size.
<CodeBlock code={textResponsiveSnippet} />
<Theming component="Text" />
<Theming definition={TextDefinition} />
<ChangelogComponent component="text" />
+2 -1
View File
@@ -10,6 +10,7 @@ import {
} from './tooltip.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { TooltipDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -42,6 +43,6 @@ The tooltip will wrap the content of the tooltip.
<PropsTable data={tooltipPropDefs} />
<Theming component="Tooltip" />
<Theming definition={TooltipDefinition} />
<ChangelogComponent component="tooltip" />
+2 -1
View File
@@ -10,6 +10,7 @@ import {
} from './visually-hidden.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { VisuallyHiddenDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -46,6 +47,6 @@ Here's an example of providing screen reader context for a list of links in a fo
open
/>
<Theming component="VisuallyHidden" />
<Theming definition={VisuallyHiddenDefinition} />
<ChangelogComponent component="visually-hidden" />
+1
View File
@@ -0,0 +1 @@
export * from '../../../packages/ui/src/definitions';
+433 -426
View File
File diff suppressed because it is too large Load Diff
@@ -31,6 +31,7 @@ import type {
AccordionGroupProps,
} from './types';
import { useStyles } from '../../hooks/useStyles';
import { AccordionDefinition } from './definition';
import styles from './Accordion.module.css';
import { Flex } from '../Flex';
@@ -39,7 +40,7 @@ export const Accordion = forwardRef<
React.ElementRef<typeof RADisclosure>,
AccordionProps
>(({ className, ...props }, ref) => {
const { classNames, cleanedProps } = useStyles('Accordion', props);
const { classNames, cleanedProps } = useStyles(AccordionDefinition, props);
return (
<RADisclosure
@@ -57,7 +58,7 @@ export const AccordionTrigger = forwardRef<
React.ElementRef<typeof RAHeading>,
AccordionTriggerProps
>(({ className, title, subtitle, children, ...props }, ref) => {
const { classNames, cleanedProps } = useStyles('Accordion', props);
const { classNames, cleanedProps } = useStyles(AccordionDefinition, props);
return (
<RAHeading
@@ -120,7 +121,7 @@ export const AccordionPanel = forwardRef<
React.ElementRef<typeof RADisclosurePanel>,
AccordionPanelProps
>(({ className, ...props }, ref) => {
const { classNames, cleanedProps } = useStyles('Accordion', props);
const { classNames, cleanedProps } = useStyles(AccordionDefinition, props);
return (
<RADisclosurePanel
@@ -138,7 +139,7 @@ export const AccordionGroup = forwardRef<
React.ElementRef<typeof RADisclosureGroup>,
AccordionGroupProps
>(({ className, allowsMultiple = false, ...props }, ref) => {
const { classNames, cleanedProps } = useStyles('Accordion', props);
const { classNames, cleanedProps } = useStyles(AccordionDefinition, props);
return (
<RADisclosureGroup
@@ -0,0 +1,34 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Accordion
* @public
*/
export const AccordionDefinition = {
classNames: {
root: 'bui-Accordion',
trigger: 'bui-AccordionTrigger',
triggerButton: 'bui-AccordionTriggerButton',
triggerTitle: 'bui-AccordionTriggerTitle',
triggerSubtitle: 'bui-AccordionTriggerSubtitle',
triggerIcon: 'bui-AccordionTriggerIcon',
panel: 'bui-AccordionPanel',
group: 'bui-AccordionGroup',
},
} as const satisfies ComponentDefinition;
@@ -20,6 +20,7 @@ export {
AccordionPanel,
AccordionGroup,
} from './Accordion';
export { AccordionDefinition } from './definition';
export type {
AccordionProps,
AccordionTriggerProps,
+9 -5
View File
@@ -18,15 +18,19 @@ import { forwardRef, useState, useEffect } from 'react';
import clsx from 'clsx';
import { AvatarProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import { AvatarDefinition } from './definition';
import styles from './Avatar.module.css';
/** @public */
export const Avatar = forwardRef<HTMLDivElement, AvatarProps>((props, ref) => {
const { classNames, dataAttributes, cleanedProps } = useStyles('Avatar', {
size: 'medium',
purpose: 'informative',
...props,
});
const { classNames, dataAttributes, cleanedProps } = useStyles(
AvatarDefinition,
{
size: 'medium',
purpose: 'informative',
...props,
},
);
const { className, src, name, purpose, ...rest } = cleanedProps;
@@ -0,0 +1,32 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Avatar
* @public
*/
export const AvatarDefinition = {
classNames: {
root: 'bui-AvatarRoot',
image: 'bui-AvatarImage',
fallback: 'bui-AvatarFallback',
},
dataAttributes: {
size: ['small', 'medium', 'large'] as const,
},
} as const satisfies ComponentDefinition;
@@ -15,4 +15,5 @@
*/
export { Avatar } from './Avatar';
export { AvatarDefinition } from './definition';
export type { AvatarProps } from './types';
+2 -1
View File
@@ -19,11 +19,12 @@ import { BoxProps } from './types';
import clsx from 'clsx';
import { useStyles } from '../../hooks/useStyles';
import styles from './Box.module.css';
import { BoxDefinition } from './definition';
/** @public */
export const Box = forwardRef<HTMLDivElement, BoxProps>((props, ref) => {
const { classNames, utilityClasses, style, cleanedProps } = useStyles(
'Box',
BoxDefinition,
props,
);
@@ -0,0 +1,51 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Box
* @public
*/
export const BoxDefinition = {
classNames: {
root: 'bui-Box',
},
utilityProps: [
'm',
'mb',
'ml',
'mr',
'mt',
'mx',
'my',
'p',
'pb',
'pl',
'pr',
'pt',
'px',
'py',
'position',
'display',
'width',
'minWidth',
'maxWidth',
'height',
'minHeight',
'maxHeight',
],
} as const satisfies ComponentDefinition;
+1
View File
@@ -14,4 +14,5 @@
* limitations under the License.
*/
export { Box } from './Box';
export { BoxDefinition } from './definition';
export type * from './types';
+9 -5
View File
@@ -20,16 +20,20 @@ import { Button as RAButton, ProgressBar } from 'react-aria-components';
import { RiLoader4Line } from '@remixicon/react';
import type { ButtonProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import { ButtonDefinition } from './definition';
import styles from './Button.module.css';
/** @public */
export const Button = forwardRef(
(props: ButtonProps, ref: Ref<HTMLButtonElement>) => {
const { classNames, dataAttributes, cleanedProps } = useStyles('Button', {
size: 'small',
variant: 'primary',
...props,
});
const { classNames, dataAttributes, cleanedProps } = useStyles(
ButtonDefinition,
{
size: 'small',
variant: 'primary',
...props,
},
);
const { children, className, iconStart, iconEnd, loading, ...rest } =
cleanedProps;
@@ -0,0 +1,34 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Button
* @public
*/
export const ButtonDefinition = {
classNames: {
root: 'bui-Button',
content: 'bui-ButtonContent',
spinner: 'bui-ButtonSpinner',
},
dataAttributes: {
size: ['small', 'medium', 'large'] as const,
variant: ['primary', 'secondary', 'tertiary'] as const,
loading: [true, false] as const,
},
} as const satisfies ComponentDefinition;
@@ -16,3 +16,4 @@
export * from './Button';
export * from './types';
export { ButtonDefinition } from './definition';
@@ -20,19 +20,25 @@ import { Button as RAButton, ProgressBar } from 'react-aria-components';
import { RiLoader4Line } from '@remixicon/react';
import type { ButtonIconProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import { ButtonDefinition } from '../Button/definition';
import { ButtonIconDefinition } from './definition';
import stylesButtonIcon from './ButtonIcon.module.css';
import stylesButton from '../Button/Button.module.css';
/** @public */
export const ButtonIcon = forwardRef(
(props: ButtonIconProps, ref: Ref<HTMLButtonElement>) => {
const { classNames, dataAttributes, cleanedProps } = useStyles('Button', {
size: 'small',
variant: 'primary',
...props,
});
const { classNames, dataAttributes, cleanedProps } = useStyles(
ButtonDefinition,
{
size: 'small',
variant: 'primary',
...props,
},
);
const { classNames: classNamesButtonIcon } = useStyles('ButtonIcon');
const { classNames: classNamesButtonIcon } =
useStyles(ButtonIconDefinition);
const { className, icon, loading, ...rest } = cleanedProps;
@@ -0,0 +1,29 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for ButtonIcon
* @public
*/
export const ButtonIconDefinition = {
classNames: {
root: 'bui-ButtonIcon',
content: 'bui-ButtonIconContent',
spinner: 'bui-ButtonIconSpinner',
},
} as const satisfies ComponentDefinition;
@@ -15,4 +15,5 @@
*/
export * from './ButtonIcon';
export { ButtonIconDefinition } from './definition';
export * from './types';
@@ -20,6 +20,8 @@ import { Link as RALink, RouterProvider } from 'react-aria-components';
import { useNavigate, useHref } from 'react-router-dom';
import type { ButtonLinkProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import { ButtonDefinition } from '../Button/definition';
import { ButtonLinkDefinition } from './definition';
import { isExternalLink } from '../../utils/isExternalLink';
import stylesButton from '../Button/Button.module.css';
@@ -28,13 +30,17 @@ export const ButtonLink = forwardRef(
(props: ButtonLinkProps, ref: Ref<HTMLAnchorElement>) => {
const navigate = useNavigate();
const { classNames, dataAttributes, cleanedProps } = useStyles('Button', {
size: 'small',
variant: 'primary',
...props,
});
const { classNames, dataAttributes, cleanedProps } = useStyles(
ButtonDefinition,
{
size: 'small',
variant: 'primary',
...props,
},
);
const { classNames: classNamesButtonLink } = useStyles('ButtonLink');
const { classNames: classNamesButtonLink } =
useStyles(ButtonLinkDefinition);
const { children, className, iconStart, iconEnd, href, ...rest } =
cleanedProps;
@@ -0,0 +1,27 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for ButtonLink
* @public
*/
export const ButtonLinkDefinition = {
classNames: {
root: 'bui-ButtonLink',
},
} as const satisfies ComponentDefinition;
@@ -15,4 +15,5 @@
*/
export * from './ButtonLink';
export { ButtonLinkDefinition } from './definition';
export * from './types';
+5 -4
View File
@@ -17,6 +17,7 @@
import { forwardRef } from 'react';
import clsx from 'clsx';
import { useStyles } from '../../hooks/useStyles';
import { CardDefinition } from './definition';
import type {
CardProps,
CardHeaderProps,
@@ -31,7 +32,7 @@ import styles from './Card.module.css';
* @public
*/
export const Card = forwardRef<HTMLDivElement, CardProps>((props, ref) => {
const { classNames, cleanedProps } = useStyles('Card', props);
const { classNames, cleanedProps } = useStyles(CardDefinition, props);
const { className, ...rest } = cleanedProps;
return (
@@ -50,7 +51,7 @@ export const Card = forwardRef<HTMLDivElement, CardProps>((props, ref) => {
*/
export const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(
(props, ref) => {
const { classNames, cleanedProps } = useStyles('Card', props);
const { classNames, cleanedProps } = useStyles(CardDefinition, props);
const { className, ...rest } = cleanedProps;
return (
@@ -74,7 +75,7 @@ export const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(
*/
export const CardBody = forwardRef<HTMLDivElement, CardBodyProps>(
(props, ref) => {
const { classNames, cleanedProps } = useStyles('Card', props);
const { classNames, cleanedProps } = useStyles(CardDefinition, props);
const { className, ...rest } = cleanedProps;
return (
@@ -94,7 +95,7 @@ export const CardBody = forwardRef<HTMLDivElement, CardBodyProps>(
*/
export const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(
(props, ref) => {
const { classNames, cleanedProps } = useStyles('Card', props);
const { classNames, cleanedProps } = useStyles(CardDefinition, props);
const { className, ...rest } = cleanedProps;
return (
@@ -0,0 +1,30 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Card
* @public
*/
export const CardDefinition = {
classNames: {
root: 'bui-Card',
header: 'bui-CardHeader',
body: 'bui-CardBody',
footer: 'bui-CardFooter',
},
} as const satisfies ComponentDefinition;
+1
View File
@@ -15,6 +15,7 @@
*/
export { Card, CardHeader, CardBody, CardFooter } from './Card';
export { CardDefinition } from './definition';
export type {
CardProps,
@@ -18,6 +18,7 @@ import { forwardRef } from 'react';
import { Checkbox as RACheckbox } from 'react-aria-components';
import type { CheckboxProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import { CheckboxDefinition } from './definition';
import clsx from 'clsx';
import styles from './Checkbox.module.css';
import { RiCheckLine } from '@remixicon/react';
@@ -25,7 +26,7 @@ import { RiCheckLine } from '@remixicon/react';
/** @public */
export const Checkbox = forwardRef<HTMLLabelElement, CheckboxProps>(
(props, ref) => {
const { classNames } = useStyles('Checkbox');
const { classNames } = useStyles(CheckboxDefinition);
const { className, children, ...rest } = props;
return (
@@ -0,0 +1,31 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Checkbox
* @public
*/
export const CheckboxDefinition = {
classNames: {
root: 'bui-Checkbox',
indicator: 'bui-CheckboxIndicator',
},
dataAttributes: {
selected: [true, false] as const,
},
} as const satisfies ComponentDefinition;
@@ -14,4 +14,5 @@
* limitations under the License.
*/
export { Checkbox } from './Checkbox';
export { CheckboxDefinition } from './definition';
export type { CheckboxProps } from './types';
@@ -18,13 +18,14 @@ import { forwardRef } from 'react';
import { ContainerProps } from './types';
import clsx from 'clsx';
import { useStyles } from '../../hooks/useStyles';
import { ContainerDefinition } from './definition';
import styles from './Container.module.css';
/** @public */
export const Container = forwardRef<HTMLDivElement, ContainerProps>(
(props, ref) => {
const { classNames, utilityClasses, style, cleanedProps } = useStyles(
'Container',
ContainerDefinition,
props,
);
@@ -0,0 +1,28 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Container
* @public
*/
export const ContainerDefinition = {
classNames: {
root: 'bui-Container',
},
utilityProps: ['my', 'mt', 'mb', 'py', 'pt', 'pb', 'display'],
} as const satisfies ComponentDefinition;
@@ -14,4 +14,5 @@
* limitations under the License.
*/
export { Container } from './Container';
export { ContainerDefinition } from './definition';
export type { ContainerProps } from './types';
+5 -4
View File
@@ -31,6 +31,7 @@ import type {
import { RiCloseLine } from '@remixicon/react';
import { Button } from '../Button';
import { useStyles } from '../../hooks/useStyles';
import { DialogDefinition } from './definition';
import { Flex } from '../Flex';
import styles from './Dialog.module.css';
@@ -42,7 +43,7 @@ export const DialogTrigger = (props: DialogTriggerProps) => {
/** @public */
export const Dialog = forwardRef<React.ElementRef<typeof Modal>, DialogProps>(
(props, ref) => {
const { classNames, cleanedProps } = useStyles('Dialog', props);
const { classNames, cleanedProps } = useStyles(DialogDefinition, props);
const { className, children, width, height, style, ...rest } = cleanedProps;
return (
@@ -84,7 +85,7 @@ export const DialogHeader = forwardRef<
React.ElementRef<'div'>,
DialogHeaderProps
>((props, ref) => {
const { classNames, cleanedProps } = useStyles('Dialog', props);
const { classNames, cleanedProps } = useStyles(DialogDefinition, props);
const { className, children, ...rest } = cleanedProps;
return (
@@ -110,7 +111,7 @@ DialogHeader.displayName = 'DialogHeader';
/** @public */
export const DialogBody = forwardRef<React.ElementRef<'div'>, DialogBodyProps>(
(props, ref) => {
const { classNames, cleanedProps } = useStyles('Dialog', props);
const { classNames, cleanedProps } = useStyles(DialogDefinition, props);
const { className, children, ...rest } = cleanedProps;
return (
@@ -132,7 +133,7 @@ export const DialogFooter = forwardRef<
React.ElementRef<'div'>,
React.ComponentPropsWithoutRef<'div'>
>((props, ref) => {
const { classNames, cleanedProps } = useStyles('Dialog', props);
const { classNames, cleanedProps } = useStyles(DialogDefinition, props);
const { className, children, ...rest } = cleanedProps;
return (
@@ -0,0 +1,32 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Dialog
* @public
*/
export const DialogDefinition = {
classNames: {
overlay: 'bui-DialogOverlay',
dialog: 'bui-Dialog',
header: 'bui-DialogHeader',
headerTitle: 'bui-DialogHeaderTitle',
body: 'bui-DialogBody',
footer: 'bui-DialogFooter',
},
} as const satisfies ComponentDefinition;
@@ -14,4 +14,5 @@
* limitations under the License.
*/
export * from './Dialog';
export { DialogDefinition } from './definition';
export * from './types';
@@ -22,11 +22,12 @@ import {
import clsx from 'clsx';
import styles from './FieldError.module.css';
import { useStyles } from '../../hooks/useStyles';
import { FieldErrorDefinition } from './definition';
/** @public */
export const FieldError = forwardRef<HTMLDivElement, FieldErrorProps>(
(props: FieldErrorProps, ref) => {
const { classNames, cleanedProps } = useStyles('FieldError', props);
const { classNames, cleanedProps } = useStyles(FieldErrorDefinition, props);
const { className, ...rest } = cleanedProps;
return (
@@ -0,0 +1,27 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for FieldError
* @public
*/
export const FieldErrorDefinition = {
classNames: {
root: 'bui-FieldError',
},
} as const satisfies ComponentDefinition;
@@ -15,3 +15,4 @@
*/
export * from './FieldError';
export { FieldErrorDefinition } from './definition';
@@ -17,13 +17,14 @@ import { Label } from 'react-aria-components';
import { forwardRef } from 'react';
import type { FieldLabelProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import { FieldLabelDefinition } from './definition';
import styles from './FieldLabel.module.css';
import clsx from 'clsx';
/** @public */
export const FieldLabel = forwardRef<HTMLDivElement, FieldLabelProps>(
(props: FieldLabelProps, ref) => {
const { classNames, cleanedProps } = useStyles('FieldLabel', props);
const { classNames, cleanedProps } = useStyles(FieldLabelDefinition, props);
const {
className,
label,
@@ -0,0 +1,30 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for FieldLabel
* @public
*/
export const FieldLabelDefinition = {
classNames: {
root: 'bui-FieldLabelWrapper',
label: 'bui-FieldLabel',
secondaryLabel: 'bui-FieldSecondaryLabel',
description: 'bui-FieldDescription',
},
} as const satisfies ComponentDefinition;
@@ -15,4 +15,5 @@
*/
export * from './FieldLabel';
export { FieldLabelDefinition } from './definition';
export * from './types';
+2 -1
View File
@@ -18,12 +18,13 @@ import { forwardRef } from 'react';
import { FlexProps } from './types';
import clsx from 'clsx';
import { useStyles } from '../../hooks/useStyles';
import { FlexDefinition } from './definition';
import styles from './Flex.module.css';
/** @public */
export const Flex = forwardRef<HTMLDivElement, FlexProps>((props, ref) => {
const { classNames, utilityClasses, style, cleanedProps } = useStyles(
'Flex',
FlexDefinition,
{ gap: '4', ...props },
);
@@ -0,0 +1,47 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Flex
* @public
*/
export const FlexDefinition = {
classNames: {
root: 'bui-Flex',
},
utilityProps: [
'm',
'mb',
'ml',
'mr',
'mt',
'mx',
'my',
'p',
'pb',
'pl',
'pr',
'pt',
'px',
'py',
'gap',
'align',
'justify',
'direction',
],
} as const satisfies ComponentDefinition;
+1
View File
@@ -14,4 +14,5 @@
* limitations under the License.
*/
export { Flex } from './Flex';
export { FlexDefinition } from './definition';
export type { FlexProps } from './types';
+3 -2
View File
@@ -18,11 +18,12 @@ import { forwardRef } from 'react';
import clsx from 'clsx';
import type { GridItemProps, GridProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import { GridDefinition, GridItemDefinition } from './definition';
import styles from './Grid.module.css';
const GridRoot = forwardRef<HTMLDivElement, GridProps>((props, ref) => {
const { classNames, utilityClasses, style, cleanedProps } = useStyles(
'Grid',
GridDefinition,
{ columns: 'auto', gap: '4', ...props },
);
@@ -45,7 +46,7 @@ const GridRoot = forwardRef<HTMLDivElement, GridProps>((props, ref) => {
const GridItem = forwardRef<HTMLDivElement, GridItemProps>((props, ref) => {
const { classNames, utilityClasses, style, cleanedProps } = useStyles(
'GridItem',
GridItemDefinition,
props,
);
@@ -0,0 +1,56 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Grid
* @public
*/
export const GridDefinition = {
classNames: {
root: 'bui-Grid',
},
utilityProps: [
'columns',
'gap',
'm',
'mb',
'ml',
'mr',
'mt',
'mx',
'my',
'p',
'pb',
'pl',
'pr',
'pt',
'px',
'py',
],
} as const satisfies ComponentDefinition;
/**
* Component definition for GridItem
* @public
*/
export const GridItemDefinition = {
classNames: {
root: 'bui-GridItem',
},
utilityProps: ['colSpan', 'colEnd', 'colStart', 'rowSpan'],
} as const satisfies ComponentDefinition;
+1
View File
@@ -15,4 +15,5 @@
*/
export { Grid } from './Grid';
export { GridDefinition, GridItemDefinition } from './definition';
export type { GridProps, GridItemProps } from './types';
+2 -1
View File
@@ -18,6 +18,7 @@ import type { HeaderProps } from './types';
import { HeaderToolbar } from './HeaderToolbar';
import { Tabs, TabList, Tab } from '../Tabs';
import { useStyles } from '../../hooks/useStyles';
import { HeaderDefinition } from './definition';
import { type NavigateOptions } from 'react-router-dom';
import styles from './Header.module.css';
import clsx from 'clsx';
@@ -34,7 +35,7 @@ declare module 'react-aria-components' {
* @public
*/
export const Header = (props: HeaderProps) => {
const { classNames, cleanedProps } = useStyles('Header', props);
const { classNames, cleanedProps } = useStyles(HeaderDefinition, props);
const {
className,
tabs,
@@ -16,6 +16,7 @@
import { Link } from 'react-aria-components';
import { useStyles } from '../../hooks/useStyles';
import { HeaderDefinition } from './definition';
import { useRef } from 'react';
import { RiShapesLine } from '@remixicon/react';
import type { HeaderToolbarProps } from './types';
@@ -29,7 +30,7 @@ import clsx from 'clsx';
* @internal
*/
export const HeaderToolbar = (props: HeaderToolbarProps) => {
const { classNames, cleanedProps } = useStyles('Header', props);
const { classNames, cleanedProps } = useStyles(HeaderDefinition, props);
const { className, icon, title, titleLink, customActions, hasTabs } =
cleanedProps;
@@ -0,0 +1,33 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Header
* @public
*/
export const HeaderDefinition = {
classNames: {
toolbar: 'bui-HeaderToolbar',
toolbarWrapper: 'bui-HeaderToolbarWrapper',
toolbarContent: 'bui-HeaderToolbarContent',
toolbarControls: 'bui-HeaderToolbarControls',
toolbarIcon: 'bui-HeaderToolbarIcon',
toolbarName: 'bui-HeaderToolbarName',
tabsWrapper: 'bui-HeaderTabsWrapper',
},
} as const satisfies ComponentDefinition;
@@ -15,4 +15,5 @@
*/
export { Header } from './Header';
export { HeaderDefinition } from './definition';
export type { HeaderProps, HeaderTab } from './types';
@@ -19,6 +19,7 @@ import { Text } from '../Text';
import { RiArrowRightSLine } from '@remixicon/react';
import { Tabs, TabList, Tab } from '../Tabs';
import { useStyles } from '../../hooks/useStyles';
import { HeaderPageDefinition } from './definition';
import { Container } from '../Container';
import { Link } from '../Link';
import { Fragment } from 'react/jsx-runtime';
@@ -31,7 +32,7 @@ import clsx from 'clsx';
* @public
*/
export const HeaderPage = (props: HeaderPageProps) => {
const { classNames, cleanedProps } = useStyles('HeaderPage', props);
const { classNames, cleanedProps } = useStyles(HeaderPageDefinition, props);
const { className, title, tabs, customActions, breadcrumbs } = cleanedProps;
return (
@@ -0,0 +1,31 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for HeaderPage
* @public
*/
export const HeaderPageDefinition = {
classNames: {
root: 'bui-HeaderPage',
content: 'bui-HeaderPageContent',
breadcrumbs: 'bui-HeaderPageBreadcrumbs',
tabsWrapper: 'bui-HeaderPageTabsWrapper',
controls: 'bui-HeaderPageControls',
},
} as const satisfies ComponentDefinition;
@@ -15,4 +15,5 @@
*/
export { HeaderPage } from './HeaderPage';
export { HeaderPageDefinition } from './definition';
export type { HeaderPageProps, HeaderPageBreadcrumb } from './types';
+10 -6
View File
@@ -18,6 +18,7 @@ import { forwardRef } from 'react';
import { Link as AriaLink, RouterProvider } from 'react-aria-components';
import clsx from 'clsx';
import { useStyles } from '../../hooks/useStyles';
import { LinkDefinition } from './definition';
import type { LinkProps } from './types';
import { useNavigate, useHref } from 'react-router-dom';
import { isExternalLink } from '../../utils/isExternalLink';
@@ -26,12 +27,15 @@ import styles from './Link.module.css';
/** @public */
export const Link = forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
const navigate = useNavigate();
const { classNames, dataAttributes, cleanedProps } = useStyles('Link', {
variant: 'body',
weight: 'regular',
color: 'primary',
...props,
});
const { classNames, dataAttributes, cleanedProps } = useStyles(
LinkDefinition,
{
variant: 'body',
weight: 'regular',
color: 'primary',
...props,
},
);
const { className, href, ...restProps } = cleanedProps;
@@ -0,0 +1,33 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Link
* @public
*/
export const LinkDefinition = {
classNames: {
root: 'bui-Link',
},
dataAttributes: {
variant: ['subtitle', 'body', 'caption', 'label'] as const,
weight: ['regular', 'bold'] as const,
color: ['primary', 'secondary', 'danger', 'warning', 'success'] as const,
truncate: [true, false] as const,
},
} as const satisfies ComponentDefinition;
+1
View File
@@ -15,4 +15,5 @@
*/
export { Link } from './Link';
export { LinkDefinition } from './definition';
export type { LinkProps } from './types';
+10 -9
View File
@@ -36,6 +36,7 @@ import {
OverlayTriggerStateContext,
} from 'react-aria-components';
import { useStyles } from '../../hooks/useStyles';
import { MenuDefinition } from './definition';
import type {
MenuTriggerProps,
SubmenuTriggerProps,
@@ -63,7 +64,7 @@ import clsx from 'clsx';
const rowHeight = 32;
const MenuEmptyState = () => {
const { classNames } = useStyles('Menu');
const { classNames } = useStyles(MenuDefinition);
return (
<div className={clsx(classNames.emptyState, styles[classNames.emptyState])}>
@@ -84,7 +85,7 @@ export const SubmenuTrigger = (props: SubmenuTriggerProps) => {
/** @public */
export const Menu = (props: MenuProps<object>) => {
const { classNames, cleanedProps } = useStyles('Menu', props);
const { classNames, cleanedProps } = useStyles(MenuDefinition, props);
const {
className,
placement = 'bottom start',
@@ -166,7 +167,7 @@ export const Menu = (props: MenuProps<object>) => {
/** @public */
export const MenuListBox = (props: MenuListBoxProps<object>) => {
const { classNames, cleanedProps } = useStyles('Menu', props);
const { classNames, cleanedProps } = useStyles(MenuDefinition, props);
const {
className,
selectionMode = 'single',
@@ -215,7 +216,7 @@ export const MenuListBox = (props: MenuListBoxProps<object>) => {
/** @public */
export const MenuAutocomplete = (props: MenuAutocompleteProps<object>) => {
const { classNames, cleanedProps } = useStyles('Menu', props);
const { classNames, cleanedProps } = useStyles(MenuDefinition, props);
const {
className,
placement = 'bottom start',
@@ -294,7 +295,7 @@ export const MenuAutocomplete = (props: MenuAutocompleteProps<object>) => {
export const MenuAutocompleteListbox = (
props: MenuAutocompleteListBoxProps<object>,
) => {
const { classNames, cleanedProps } = useStyles('Menu', props);
const { classNames, cleanedProps } = useStyles(MenuDefinition, props);
const {
className,
selectionMode = 'single',
@@ -370,7 +371,7 @@ export const MenuAutocompleteListbox = (
/** @public */
export const MenuItem = (props: MenuItemProps) => {
const { classNames, cleanedProps } = useStyles('Menu', props);
const { classNames, cleanedProps } = useStyles(MenuDefinition, props);
const {
className,
iconStart,
@@ -449,7 +450,7 @@ export const MenuItem = (props: MenuItemProps) => {
/** @public */
export const MenuListBoxItem = (props: MenuListBoxItemProps) => {
const { classNames, cleanedProps } = useStyles('Menu', props);
const { classNames, cleanedProps } = useStyles(MenuDefinition, props);
const { children, className, ...rest } = cleanedProps;
return (
@@ -490,7 +491,7 @@ export const MenuListBoxItem = (props: MenuListBoxItemProps) => {
/** @public */
export const MenuSection = (props: MenuSectionProps<object>) => {
const { classNames, cleanedProps } = useStyles('Menu', props);
const { classNames, cleanedProps } = useStyles(MenuDefinition, props);
const { children, className, title, ...rest } = cleanedProps;
return (
@@ -517,7 +518,7 @@ export const MenuSection = (props: MenuSectionProps<object>) => {
/** @public */
export const MenuSeparator = (props: MenuSeparatorProps) => {
const { classNames, cleanedProps } = useStyles('Menu', props);
const { classNames, cleanedProps } = useStyles(MenuDefinition, props);
const { className, ...rest } = cleanedProps;
return (
@@ -0,0 +1,42 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Menu
* @public
*/
export const MenuDefinition = {
classNames: {
root: 'bui-Menu',
popover: 'bui-MenuPopover',
content: 'bui-MenuContent',
section: 'bui-MenuSection',
sectionHeader: 'bui-MenuSectionHeader',
item: 'bui-MenuItem',
itemListBox: 'bui-MenuItemListBox',
itemListBoxCheck: 'bui-MenuItemListBoxCheck',
itemWrapper: 'bui-MenuItemWrapper',
itemContent: 'bui-MenuItemContent',
itemArrow: 'bui-MenuItemArrow',
separator: 'bui-MenuSeparator',
searchField: 'bui-MenuSearchField',
searchFieldInput: 'bui-MenuSearchFieldInput',
searchFieldClear: 'bui-MenuSearchFieldClear',
emptyState: 'bui-MenuEmptyState',
},
} as const satisfies ComponentDefinition;
+1
View File
@@ -26,6 +26,7 @@ export {
MenuSection,
MenuSeparator,
} from './Menu';
export { MenuDefinition } from './definition';
export type {
MenuTriggerProps,
SubmenuTriggerProps,
@@ -26,6 +26,7 @@ import { FieldError } from '../FieldError';
import type { PasswordFieldProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import { PasswordFieldDefinition } from './definition';
import { RiEyeLine, RiEyeOffLine } from '@remixicon/react';
import stylesPasswordField from './PasswordField.module.css';
@@ -50,7 +51,7 @@ export const PasswordField = forwardRef<HTMLDivElement, PasswordFieldProps>(
classNames: classNamesPasswordField,
dataAttributes,
cleanedProps,
} = useStyles('PasswordField', {
} = useStyles(PasswordFieldDefinition, {
size: 'small',
...props,
});
@@ -0,0 +1,34 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for PasswordField
* @public
*/
export const PasswordFieldDefinition = {
classNames: {
root: 'bui-PasswordField',
inputWrapper: 'bui-PasswordFieldInputWrapper',
input: 'bui-PasswordFieldInput',
inputIcon: 'bui-PasswordFieldIcon',
inputVisibility: 'bui-PasswordFieldVisibility',
},
dataAttributes: {
size: ['small', 'medium'] as const,
},
} as const satisfies ComponentDefinition;
@@ -15,4 +15,5 @@
*/
export * from './PasswordField';
export { PasswordFieldDefinition } from './definition';
export * from './types';
@@ -0,0 +1,27 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for Popover
* @public
*/
export const PopoverDefinition = {
classNames: {
root: 'bui-Popover',
},
} as const satisfies ComponentDefinition;
@@ -0,0 +1,17 @@
/*
* Copyright 2024 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 { PopoverDefinition } from './definition';
@@ -23,6 +23,7 @@ import clsx from 'clsx';
import { FieldLabel } from '../FieldLabel';
import { FieldError } from '../FieldError';
import { useStyles } from '../../hooks/useStyles';
import { RadioGroupDefinition } from './definition';
import styles from './RadioGroup.module.css';
import type { RadioGroupProps, RadioProps } from './types';
@@ -30,7 +31,7 @@ import type { RadioGroupProps, RadioProps } from './types';
/** @public */
export const RadioGroup = forwardRef<HTMLDivElement, RadioGroupProps>(
(props, ref) => {
const { classNames, cleanedProps } = useStyles('RadioGroup', props);
const { classNames, cleanedProps } = useStyles(RadioGroupDefinition, props);
const {
className,
label,
@@ -83,7 +84,7 @@ RadioGroup.displayName = 'RadioGroup';
export const Radio = forwardRef<HTMLLabelElement, RadioProps>((props, ref) => {
const { className, ...rest } = props;
const { classNames } = useStyles('RadioGroup');
const { classNames } = useStyles(RadioGroupDefinition);
return (
<AriaRadio
@@ -0,0 +1,29 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for RadioGroup
* @public
*/
export const RadioGroupDefinition = {
classNames: {
root: 'bui-RadioGroup',
content: 'bui-RadioGroupContent',
radio: 'bui-Radio',
},
} as const satisfies ComponentDefinition;
@@ -16,3 +16,4 @@
export * from './RadioGroup';
export * from './types';
export { RadioGroupDefinition } from './definition';
@@ -25,6 +25,7 @@ import { FieldLabel } from '../FieldLabel';
import { FieldError } from '../FieldError';
import { RiSearch2Line, RiCloseCircleLine } from '@remixicon/react';
import { useStyles } from '../../hooks/useStyles';
import { SearchFieldDefinition } from './definition';
import styles from './SearchField.module.css';
import type { SearchFieldProps } from './types';
@@ -50,7 +51,7 @@ export const SearchField = forwardRef<HTMLDivElement, SearchFieldProps>(
}, [label, ariaLabel, ariaLabelledBy]);
const { classNames, dataAttributes, style, cleanedProps } = useStyles(
'SearchField',
SearchFieldDefinition,
{
size: 'small',
placeholder: 'Search',
@@ -0,0 +1,35 @@
/*
* Copyright 2024 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 { ComponentDefinition } from '../../types';
/**
* Component definition for SearchField
* @public
*/
export const SearchFieldDefinition = {
classNames: {
root: 'bui-SearchField',
clear: 'bui-SearchFieldClear',
inputWrapper: 'bui-SearchFieldInputWrapper',
input: 'bui-SearchFieldInput',
inputIcon: 'bui-SearchFieldInputIcon',
},
dataAttributes: {
startCollapsed: [true, false] as const,
size: ['small', 'medium'] as const,
},
} as const satisfies ComponentDefinition;
@@ -16,3 +16,4 @@
export * from './SearchField';
export * from './types';
export { SearchFieldDefinition } from './definition';
+11 -6
View File
@@ -19,6 +19,8 @@ import { Select as AriaSelect, Popover } from 'react-aria-components';
import clsx from 'clsx';
import { SelectProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import { SelectDefinition } from './definition';
import { PopoverDefinition } from '../Popover/definition';
import { FieldLabel } from '../FieldLabel';
import { FieldError } from '../FieldError';
import styles from './Select.module.css';
@@ -31,12 +33,15 @@ export const Select = forwardRef<
HTMLDivElement,
SelectProps<'single' | 'multiple'>
>((props, ref) => {
const { classNames: popoverClassNames } = useStyles('Popover');
const { classNames, dataAttributes, cleanedProps } = useStyles('Select', {
size: 'small',
placeholder: 'Select an option',
...props,
});
const { classNames: popoverClassNames } = useStyles(PopoverDefinition);
const { classNames, dataAttributes, cleanedProps } = useStyles(
SelectDefinition,
{
size: 'small',
placeholder: 'Select an option',
...props,
},
);
const {
className,

Some files were not shown because too many files have changed in this diff Show More