diff --git a/packages/ui/src/components/Box/Box.props.ts b/packages/ui/src/components/Box/Box.props.ts deleted file mode 100644 index 3cf0291e43..0000000000 --- a/packages/ui/src/components/Box/Box.props.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 { PropDef, GetPropDefTypes } from '../../props/prop-def'; - -const as = ['div', 'span'] as const; - -/** @public */ -const boxPropDefs = { - as: { type: 'enum', values: as, default: 'div' }, -} satisfies { - as: PropDef<(typeof as)[number]>; -}; - -// Use all of the imported prop defs to ensure that JSDoc works -/** @public */ -type BoxOwnProps = GetPropDefTypes; - -export { boxPropDefs }; -export type { BoxOwnProps }; diff --git a/packages/ui/src/components/Box/index.tsx b/packages/ui/src/components/Box/index.tsx index 087a940a2d..d47e653dfd 100644 --- a/packages/ui/src/components/Box/index.tsx +++ b/packages/ui/src/components/Box/index.tsx @@ -15,5 +15,3 @@ */ export { Box } from './Box'; export type * from './types'; -export type { BoxOwnProps } from './Box.props'; -export { boxPropDefs } from './Box.props'; diff --git a/packages/ui/src/components/Flex/Flex.props.ts b/packages/ui/src/components/Flex/Flex.props.ts deleted file mode 100644 index b1d9c019d3..0000000000 --- a/packages/ui/src/components/Flex/Flex.props.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 { PropDef, GetPropDefTypes } from '../../props/prop-def'; - -const alignValues = ['start', 'center', 'end', 'baseline', 'stretch'] as const; -const directionValues = [ - 'row', - 'column', - 'row-reverse', - 'column-reverse', -] as const; -const justifyValues = ['start', 'center', 'end', 'between'] as const; - -/** @public */ -const flexPropDefs = { - align: { - type: 'enum', - className: 'bui-align', - values: alignValues, - responsive: true, - }, - direction: { - type: 'enum', - className: 'bui-fd', - values: directionValues, - responsive: true, - }, - justify: { - type: 'enum', - className: 'bui-jc', - values: justifyValues, - responsive: true, - }, -} satisfies { - align: PropDef<(typeof alignValues)[number]>; - direction: PropDef<(typeof directionValues)[number]>; - justify: PropDef<(typeof justifyValues)[number]>; -}; - -/** @public */ -type FlexOwnProps = GetPropDefTypes; - -export { flexPropDefs }; -export type { FlexOwnProps }; diff --git a/packages/ui/src/components/Flex/Flex.stories.tsx b/packages/ui/src/components/Flex/Flex.stories.tsx index 40f9339ad6..c7bc814f1b 100644 --- a/packages/ui/src/components/Flex/Flex.stories.tsx +++ b/packages/ui/src/components/Flex/Flex.stories.tsx @@ -17,6 +17,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import { Flex } from './Flex'; import { Text } from '../Text'; +import { Box } from '../Box'; const meta = { title: 'Backstage UI/Flex', @@ -43,17 +44,38 @@ const meta = { export default meta; type Story = StoryObj; -const DecorativeBox = () => { +const DecorativeBox = ({ + width = '48px', + height = '48px', +}: { + width?: string; + height?: string; +}) => { + const diagonalStripePattern = (() => { + const svg = ` + + + + + + `.trim(); + return `data:image/svg+xml,${encodeURIComponent(svg)}`; + })(); + return ( -
); @@ -85,12 +107,12 @@ export const RowDirection: Story = { }, }; -export const AlignLeft: Story = { +export const AlignStart: Story = { render: () => ( - - - + + + ), }; @@ -98,19 +120,19 @@ export const AlignLeft: Story = { export const AlignCenter: Story = { render: () => ( - - - + + + ), }; -export const AlignRight: Story = { +export const AlignEnd: Story = { render: () => ( - - - + + + ), }; @@ -118,9 +140,9 @@ export const AlignRight: Story = { export const ResponsiveAlign: Story = { render: () => ( - - - + + + ), }; diff --git a/packages/ui/src/components/Flex/Flex.tsx b/packages/ui/src/components/Flex/Flex.tsx index 021fe87820..f9ee38d1df 100644 --- a/packages/ui/src/components/Flex/Flex.tsx +++ b/packages/ui/src/components/Flex/Flex.tsx @@ -14,31 +14,28 @@ * limitations under the License. */ -import { createElement, forwardRef } from 'react'; +import { forwardRef } from 'react'; import { FlexProps } from './types'; import clsx from 'clsx'; -import { flexPropDefs } from './Flex.props'; -import { extractProps } from '../../utils/extractProps'; -import { gapPropDefs } from '../../props/gap-props'; -import { spacingPropDefs } from '../../props/spacing.props'; import { useStyles } from '../../hooks/useStyles'; /** @public */ export const Flex = forwardRef((props, ref) => { - const propDefs = { - ...gapPropDefs, - ...flexPropDefs, - ...spacingPropDefs, - }; + const { classNames, utilityClasses, style, cleanedProps } = useStyles( + 'Flex', + { gap: '4', ...props }, + ); - const { classNames } = useStyles('Flex'); - const { className, style, dataProps } = extractProps(props, propDefs); + const { children, ...rest } = cleanedProps; - return createElement('div', { - ref, - className: clsx(classNames.root, className), - ...dataProps, - style, - children: props.children, - }); + return ( +
+ {children} +
+ ); }); diff --git a/packages/ui/src/components/Flex/index.ts b/packages/ui/src/components/Flex/index.ts index 033a9bec42..4e4c883119 100644 --- a/packages/ui/src/components/Flex/index.ts +++ b/packages/ui/src/components/Flex/index.ts @@ -14,6 +14,4 @@ * limitations under the License. */ export { Flex } from './Flex'; -export { flexPropDefs } from './Flex.props'; export type { FlexProps } from './types'; -export type { FlexOwnProps } from './Flex.props'; diff --git a/packages/ui/src/hooks/useStyles.ts b/packages/ui/src/hooks/useStyles.ts index 8e9259a1a3..3f4798f77b 100644 --- a/packages/ui/src/hooks/useStyles.ts +++ b/packages/ui/src/hooks/useStyles.ts @@ -16,139 +16,7 @@ import { useBreakpoint, breakpoints } from './useBreakpoint'; import { componentDefinitions } from '../utils/componentDefinitions'; import type { ComponentDefinitionName, ComponentClassNames } from '../types'; - -// Valid spacing values that have predefined utility classes -const VALID_SPACING_VALUES = [ - '0.5', - '1', - '1.5', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - '11', - '12', - '13', - '14', -] as const; - -const utilityClassMap = { - m: { - class: 'bui-m', - cssVar: '--m', - values: VALID_SPACING_VALUES, - }, - mb: { - class: 'bui-mb', - cssVar: '--mb', - values: VALID_SPACING_VALUES, - }, - ml: { - class: 'bui-ml', - cssVar: '--ml', - values: VALID_SPACING_VALUES, - }, - mr: { - class: 'bui-mr', - cssVar: '--mr', - values: VALID_SPACING_VALUES, - }, - mt: { - class: 'bui-mt', - cssVar: '--mt', - values: VALID_SPACING_VALUES, - }, - mx: { - class: 'bui-mx', - cssVar: '--mx', - values: VALID_SPACING_VALUES, - }, - my: { - class: 'bui-my', - cssVar: '--my', - values: VALID_SPACING_VALUES, - }, - p: { - class: 'bui-p', - cssVar: '--p', - values: VALID_SPACING_VALUES, - }, - pb: { - class: 'bui-pb', - cssVar: '--pb', - values: VALID_SPACING_VALUES, - }, - pl: { - class: 'bui-pl', - cssVar: '--pl', - values: VALID_SPACING_VALUES, - }, - pr: { - class: 'bui-pr', - cssVar: '--pr', - values: VALID_SPACING_VALUES, - }, - pt: { - class: 'bui-pt', - cssVar: '--pt', - values: VALID_SPACING_VALUES, - }, - px: { - class: 'bui-px', - cssVar: '--px', - values: VALID_SPACING_VALUES, - }, - py: { - class: 'bui-py', - cssVar: '--py', - values: VALID_SPACING_VALUES, - }, - width: { - class: 'bui-w', - cssVar: '--width', - values: VALID_SPACING_VALUES, - }, - minWidth: { - class: 'bui-min-w', - cssVar: '--min-width', - values: VALID_SPACING_VALUES, - }, - maxWidth: { - class: 'bui-max-w', - cssVar: '--max-width', - values: VALID_SPACING_VALUES, - }, - height: { - class: 'bui-h', - cssVar: '--height', - values: VALID_SPACING_VALUES, - }, - minHeight: { - class: 'bui-min-h', - cssVar: '--min-height', - values: VALID_SPACING_VALUES, - }, - maxHeight: { - class: 'bui-max-h', - cssVar: '--max-height', - values: VALID_SPACING_VALUES, - }, - position: { - class: 'bui-position', - cssVar: '--position', - values: ['static', 'relative', 'absolute', 'fixed', 'sticky'], - }, - display: { - class: 'bui-display', - cssVar: '--display', - values: ['none', 'flex', 'block', 'inline'], - }, -}; +import { utilityClassMap } from '../utils/utilityClassMap'; /** * Resolve a responsive value based on the current breakpoint @@ -248,19 +116,23 @@ export function useStyles( } // Check if value is in the list of valid values for this utility - if (utilityConfig.values.includes(val as any)) { + if ( + utilityConfig.values.length > 0 && + utilityConfig.values.includes(val as string | number) + ) { // Generate utility class with value suffix and optional breakpoint prefix const className = prefix ? `${prefix}${utilityConfig.class}-${val}` : `${utilityConfig.class}-${val}`; utilityClassList.push(className); - } else { + } else if (utilityConfig.cssVar) { // Custom value - add CSS custom property AND utility class name + // Only if cssVar is defined (properties with fixed values don't have cssVar) const cssVarKey = prefix ? `${utilityConfig.cssVar}-${prefix.slice(0, -1)}` : utilityConfig.cssVar; - // CSS custom properties need to be set as any since they're not part of CSSProperties - (generatedStyle as any)[cssVarKey] = val; + // CSS custom properties need to be set on the style object as strings + (generatedStyle as Record)[cssVarKey] = val; // Add utility class name (without value suffix) with optional breakpoint prefix const className = prefix @@ -268,6 +140,7 @@ export function useStyles( : utilityConfig.class; utilityClassList.push(className); } + // If no cssVar and value is not in valid values, skip (invalid value for fixed-value property) }; for (const key of utilityPropNames) { diff --git a/packages/ui/src/utils/componentDefinitions.ts b/packages/ui/src/utils/componentDefinitions.ts index 2516f85a84..6173666a65 100644 --- a/packages/ui/src/utils/componentDefinitions.ts +++ b/packages/ui/src/utils/componentDefinitions.ts @@ -121,6 +121,26 @@ export const componentDefinitions = { classNames: { root: 'bui-Flex', }, + utilityProps: [ + 'm', + 'mb', + 'ml', + 'mr', + 'mt', + 'mx', + 'my', + 'p', + 'pb', + 'pl', + 'pr', + 'pt', + 'px', + 'py', + 'gap', + 'align', + 'justify', + 'direction', + ], }, Grid: { classNames: { diff --git a/packages/ui/src/utils/utilityClassMap.ts b/packages/ui/src/utils/utilityClassMap.ts new file mode 100644 index 0000000000..e1b2e96f0f --- /dev/null +++ b/packages/ui/src/utils/utilityClassMap.ts @@ -0,0 +1,166 @@ +/* + * 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. + */ + +// Valid spacing values that have predefined utility classes +const VALID_SPACING_VALUES = [ + '0.5', + '1', + '1.5', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '11', + '12', + '13', + '14', +] as const; + +export const utilityClassMap: Record< + string, + { class: string; cssVar?: string; values: readonly (string | number)[] } +> = { + m: { + class: 'bui-m', + cssVar: '--m', + values: VALID_SPACING_VALUES, + }, + mb: { + class: 'bui-mb', + cssVar: '--mb', + values: VALID_SPACING_VALUES, + }, + ml: { + class: 'bui-ml', + cssVar: '--ml', + values: VALID_SPACING_VALUES, + }, + mr: { + class: 'bui-mr', + cssVar: '--mr', + values: VALID_SPACING_VALUES, + }, + mt: { + class: 'bui-mt', + cssVar: '--mt', + values: VALID_SPACING_VALUES, + }, + mx: { + class: 'bui-mx', + cssVar: '--mx', + values: VALID_SPACING_VALUES, + }, + my: { + class: 'bui-my', + cssVar: '--my', + values: VALID_SPACING_VALUES, + }, + p: { + class: 'bui-p', + cssVar: '--p', + values: VALID_SPACING_VALUES, + }, + pb: { + class: 'bui-pb', + cssVar: '--pb', + values: VALID_SPACING_VALUES, + }, + pl: { + class: 'bui-pl', + cssVar: '--pl', + values: VALID_SPACING_VALUES, + }, + pr: { + class: 'bui-pr', + cssVar: '--pr', + values: VALID_SPACING_VALUES, + }, + pt: { + class: 'bui-pt', + cssVar: '--pt', + values: VALID_SPACING_VALUES, + }, + px: { + class: 'bui-px', + cssVar: '--px', + values: VALID_SPACING_VALUES, + }, + py: { + class: 'bui-py', + cssVar: '--py', + values: VALID_SPACING_VALUES, + }, + width: { + class: 'bui-w', + cssVar: '--width', + values: [], // Always use custom value + }, + minWidth: { + class: 'bui-min-w', + cssVar: '--min-width', + values: [], // Always use custom value + }, + maxWidth: { + class: 'bui-max-w', + cssVar: '--max-width', + values: [], // Always use custom value + }, + height: { + class: 'bui-h', + cssVar: '--height', + values: [], // Always use custom value + }, + minHeight: { + class: 'bui-min-h', + cssVar: '--min-height', + values: [], // Always use custom value + }, + maxHeight: { + class: 'bui-max-h', + cssVar: '--max-height', + values: [], // Always use custom value + }, + gap: { + class: 'bui-gap', + cssVar: '--gap', + values: VALID_SPACING_VALUES, + }, + position: { + class: 'bui-position', + values: ['static', 'relative', 'absolute', 'fixed', 'sticky'], + }, + display: { + class: 'bui-display', + values: ['none', 'flex', 'block', 'inline'], + }, + align: { + class: 'bui-align', + values: ['start', 'center', 'end', 'baseline', 'stretch'], + }, + justify: { + class: 'bui-justify', + values: ['start', 'center', 'end', 'between'], + }, + direction: { + class: 'bui-fd', + values: ['row', 'column', 'row-reverse', 'column-reverse'], + }, +};