From 1263ba828390fabe6716038aa564d6269007daf9 Mon Sep 17 00:00:00 2001 From: teonemes Date: Fri, 8 Aug 2025 14:43:23 +0200 Subject: [PATCH 1/2] fix: Remove validateProps from components package --- .../validate-props-production.test.ts | 11 ---------- .../__tests__/validate-props.test.ts | 21 ------------------ src/internal/base-component/index.ts | 22 +------------------ 3 files changed, 1 insertion(+), 53 deletions(-) delete mode 100644 src/internal/base-component/__tests__/validate-props-production.test.ts delete mode 100644 src/internal/base-component/__tests__/validate-props.test.ts diff --git a/src/internal/base-component/__tests__/validate-props-production.test.ts b/src/internal/base-component/__tests__/validate-props-production.test.ts deleted file mode 100644 index d1127c3e96..0000000000 --- a/src/internal/base-component/__tests__/validate-props-production.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { validateProps } from '../../../../lib/components/internal/base-component'; -import { isDevelopment } from '../../../../lib/components/internal/is-development'; - -jest.mock('../../../../lib/components/internal/is-development', () => ({ isDevelopment: false })); - -test('does nothing in production builds', () => { - expect(isDevelopment).toBe(false); - expect(() => validateProps('TestComponent', { variant: 'foo' }, ['variant'], {})).not.toThrow(); -}); diff --git a/src/internal/base-component/__tests__/validate-props.test.ts b/src/internal/base-component/__tests__/validate-props.test.ts deleted file mode 100644 index 5a5f9a2563..0000000000 --- a/src/internal/base-component/__tests__/validate-props.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { validateProps } from '../../../../lib/components/internal/base-component'; - -test('should pass validation', () => { - expect(() => validateProps('TestComponent', {}, [], {})).not.toThrow(); - expect(() => validateProps('TestComponent', { variant: 'foo' }, ['bar'], { variant: ['foo'] })).not.toThrow(); - expect(() => validateProps('TestComponent', { variant: undefined }, ['bar'], { variant: ['foo'] })).not.toThrow(); -}); - -test('should throw error when excluded prop is used', () => { - expect(() => validateProps('TestComponent', { variant: 'foo' }, ['variant'], {})).toThrow( - new Error('TestComponent does not support "variant" property when used in default theme') - ); -}); - -test('should throw error when invalid prop is used', () => { - expect(() => validateProps('TestComponent', { variant: 'foo' }, [], { variant: ['bar'] })).toThrow( - new Error('TestComponent does not support "variant" with value "foo" when used in default theme') - ); -}); diff --git a/src/internal/base-component/index.ts b/src/internal/base-component/index.ts index 231fb47151..cf367b9269 100644 --- a/src/internal/base-component/index.ts +++ b/src/internal/base-component/index.ts @@ -4,8 +4,7 @@ import { initAwsUiVersions } from '@cloudscape-design/component-toolkit/internal'; import { AnalyticsMetadata } from '../analytics/interfaces'; -import { PACKAGE_SOURCE, PACKAGE_VERSION, THEME } from '../environment'; -import { isDevelopment } from '../is-development'; +import { PACKAGE_SOURCE, PACKAGE_VERSION } from '../environment'; // these styles needed to be imported for every public component import './styles.css.js'; @@ -40,25 +39,6 @@ export function getBaseProps(props: BaseComponentProps) { return baseProps as BaseComponentProps; } -export function validateProps( - componentName: string, - props: Record, - excludedProps: Array, - allowedEnums: Record> -) { - if (!isDevelopment) { - return; - } - for (const [prop, value] of Object.entries(props)) { - if (excludedProps.includes(prop)) { - throw new Error(`${componentName} does not support "${prop}" property when used in ${THEME} theme`); - } - if (value && allowedEnums[prop] && !allowedEnums[prop].includes(value)) { - throw new Error(`${componentName} does not support "${prop}" with value "${value}" when used in ${THEME} theme`); - } - } -} - export interface BasePropsWithAnalyticsMetadata { analyticsMetadata?: AnalyticsMetadata; __analyticsMetadata?: AnalyticsMetadata; From 4f2b6b10971a7c870536a4924fac6c810ec17265 Mon Sep 17 00:00:00 2001 From: teonemes Date: Wed, 13 Aug 2025 15:10:35 +0200 Subject: [PATCH 2/2] chore: Refactor Button Style API --- src/button/internal.tsx | 39 ++---------------------------------- src/button/style.tsx | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 37 deletions(-) create mode 100644 src/button/style.tsx diff --git a/src/button/internal.tsx b/src/button/internal.tsx index cd14ed24c4..604f25ce32 100644 --- a/src/button/internal.tsx +++ b/src/button/internal.tsx @@ -22,9 +22,7 @@ import { import Tooltip from '../internal/components/tooltip/index.js'; import { useButtonContext } from '../internal/context/button-context'; import { useSingleTabStopNavigation } from '../internal/context/single-tab-stop-navigation-context'; -import { SYSTEM } from '../internal/environment'; import { fireCancelableEvent, isPlainLeftClick } from '../internal/events'; -import customCssProps from '../internal/generated/custom-css-properties'; import useForwardFocus from '../internal/hooks/forward-focus'; import { InternalBaseComponentProps } from '../internal/hooks/use-base-component'; import useHiddenDescription from '../internal/hooks/use-hidden-description'; @@ -36,6 +34,7 @@ import InternalLiveRegion from '../live-region/internal'; import { GeneratedAnalyticsMetadataButtonFragment } from './analytics-metadata/interfaces'; import { ButtonIconProps, LeftIcon, RightIcon } from './icon-helper'; import { ButtonProps } from './interfaces'; +import { getButtonStyles } from './style'; import analyticsSelectors from './analytics-metadata/styles.css.js'; import styles from './styles.css.js'; @@ -292,41 +291,7 @@ export const InternalButton = React.forwardRef( ); - const stylePropertiesAndVariables = - SYSTEM === 'core' - ? { - borderRadius: style?.root?.borderRadius, - borderWidth: style?.root?.borderWidth, - paddingBlock: style?.root?.paddingBlock, - paddingInline: style?.root?.paddingInline, - ...(style?.root?.background && { - [customCssProps.styleBackgroundActive]: style.root.background?.active, - [customCssProps.styleBackgroundDefault]: style.root.background?.default, - [customCssProps.styleBackgroundDisabled]: style.root.background?.disabled, - [customCssProps.styleBackgroundHover]: style.root.background?.hover, - }), - ...(style?.root?.borderColor && { - [customCssProps.styleBorderColorActive]: style.root.borderColor?.active, - [customCssProps.styleBorderColorDefault]: style.root.borderColor?.default, - [customCssProps.styleBorderColorDisabled]: style.root.borderColor?.disabled, - [customCssProps.styleBorderColorHover]: style.root.borderColor?.hover, - }), - ...(style?.root?.color && { - [customCssProps.styleColorActive]: style.root.color?.active, - [customCssProps.styleColorDefault]: style.root.color?.default, - [customCssProps.styleColorDisabled]: style.root.color?.disabled, - [customCssProps.styleColorHover]: style.root.color?.hover, - }), - ...(style?.root?.focusRing && { - [customCssProps.styleFocusRingBorderColor]: style.root.focusRing?.borderColor, - [customCssProps.styleFocusRingBorderRadius]: style.root.focusRing?.borderRadius, - [customCssProps.styleFocusRingBorderWidth]: style.root.focusRing?.borderWidth, - }), - ...(style?.root?.focusRing?.borderRadius && { - [customCssProps.styleFocusRingBorderRadius]: style.root.focusRing.borderRadius, - }), - } - : undefined; + const stylePropertiesAndVariables = getButtonStyles(style); if (isAnchor) { const getAnchorTabIndex = () => { diff --git a/src/button/style.tsx b/src/button/style.tsx new file mode 100644 index 0000000000..c0e16682b9 --- /dev/null +++ b/src/button/style.tsx @@ -0,0 +1,44 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { SYSTEM } from '../internal/environment'; +import customCssProps from '../internal/generated/custom-css-properties'; +import { ButtonProps } from './interfaces'; + +export function getButtonStyles(style: ButtonProps['style']) { + if (SYSTEM !== 'core' || !style?.root) { + return undefined; + } + + return { + borderRadius: style?.root?.borderRadius, + borderWidth: style?.root?.borderWidth, + paddingBlock: style?.root?.paddingBlock, + paddingInline: style?.root?.paddingInline, + ...(style?.root?.background && { + [customCssProps.styleBackgroundActive]: style.root.background?.active, + [customCssProps.styleBackgroundDefault]: style.root.background?.default, + [customCssProps.styleBackgroundDisabled]: style.root.background?.disabled, + [customCssProps.styleBackgroundHover]: style.root.background?.hover, + }), + ...(style?.root?.borderColor && { + [customCssProps.styleBorderColorActive]: style.root.borderColor?.active, + [customCssProps.styleBorderColorDefault]: style.root.borderColor?.default, + [customCssProps.styleBorderColorDisabled]: style.root.borderColor?.disabled, + [customCssProps.styleBorderColorHover]: style.root.borderColor?.hover, + }), + ...(style?.root?.color && { + [customCssProps.styleColorActive]: style.root.color?.active, + [customCssProps.styleColorDefault]: style.root.color?.default, + [customCssProps.styleColorDisabled]: style.root.color?.disabled, + [customCssProps.styleColorHover]: style.root.color?.hover, + }), + ...(style?.root?.focusRing && { + [customCssProps.styleFocusRingBorderColor]: style.root.focusRing?.borderColor, + [customCssProps.styleFocusRingBorderRadius]: style.root.focusRing?.borderRadius, + [customCssProps.styleFocusRingBorderWidth]: style.root.focusRing?.borderWidth, + }), + ...(style?.root?.focusRing?.borderRadius && { + [customCssProps.styleFocusRingBorderRadius]: style.root.focusRing.borderRadius, + }), + }; +}