diff --git a/build-tools/utils/custom-css-properties.js b/build-tools/utils/custom-css-properties.js index 79ccc7fd66..11e3a02ea6 100644 --- a/build-tools/utils/custom-css-properties.js +++ b/build-tools/utils/custom-css-properties.js @@ -51,6 +51,9 @@ const customCssPropertiesList = [ // Dropdown Custom Properties 'dropdownDefaultMaxWidth', 'dropdownDefaultMinWidth', + // Modal Custom Properties + 'modalCustomWidth', + 'modalCustomHeight', // Spinner Custom Properties 'spinnerRotatorFrom', 'spinnerRotatorTo', diff --git a/pages/modal/custom-dimensions.page.tsx b/pages/modal/custom-dimensions.page.tsx new file mode 100644 index 0000000000..004f9d0793 --- /dev/null +++ b/pages/modal/custom-dimensions.page.tsx @@ -0,0 +1,66 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useState } from 'react'; + +import { Button, Checkbox, FormField, Input, Modal, SpaceBetween } from '~components'; + +import { useAppContext } from '../app/app-context'; +import { SimplePage } from '../app/templates'; +import ScreenshotArea from '../utils/screenshot-area'; + +export default function () { + const { urlParams, setUrlParams } = useAppContext(); + const [visible, setVisible] = useState(false); + const showFooter = urlParams.footer ? true : false; + const footer = ; + + return ( + + + setUrlParams({ width: e.detail.value })} + type="number" + /> + + + setUrlParams({ height: e.detail.value })} + type="number" + /> + + setUrlParams({ footer: e.detail.checked })}> + Show footer + + + } + > + + + setVisible(false)} + width={Number(urlParams.width) || undefined} + height={Number(urlParams.height) || undefined} + footer={showFooter ? footer : undefined} + > + {Array(100) + .fill(0) + .map((value, index) => ( +
+ Text content {index} +
+ ))} + +
+
+
+ ); +} diff --git a/pages/modal/position-top.page.tsx b/pages/modal/position-top.page.tsx new file mode 100644 index 0000000000..6d26fa3043 --- /dev/null +++ b/pages/modal/position-top.page.tsx @@ -0,0 +1,24 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useState } from 'react'; + +import Button from '~components/button'; +import Modal from '~components/modal'; + +import { SimplePage } from '../app/templates'; +import ScreenshotArea from '../utils/screenshot-area'; + +export default function PositionTopPage() { + const [visible, setVisible] = useState(false); + + return ( + + + + setVisible(false)} header="Top positioned modal" position="top"> + This modal is positioned at the top of the viewport. + + + + ); +} diff --git a/pages/modal/sizes.page.tsx b/pages/modal/sizes.page.tsx new file mode 100644 index 0000000000..8ba3ed7bed --- /dev/null +++ b/pages/modal/sizes.page.tsx @@ -0,0 +1,40 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useState } from 'react'; + +import Button from '~components/button'; +import Modal from '~components/modal'; +import SpaceBetween from '~components/space-between'; + +import { SimplePage } from '../app/templates'; +import ScreenshotArea from '../utils/screenshot-area'; + +export default function SizesPage() { + const [size, setSize] = useState<'small' | 'medium' | 'large' | 'x-large' | 'xx-large' | 'max'>('medium'); + const [visible, setVisible] = useState(false); + + const sizes = ['small', 'medium', 'large', 'x-large', 'xx-large', 'max'] as const; + + return ( + + + {sizes.map(size => ( + + ))} + + + setVisible(false)} header={`${size} modal`} size={size}> + This is a {size} modal. + + + + ); +} diff --git a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap index 01fbd65f7e..5f51421589 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap @@ -17503,6 +17503,15 @@ The function will be called when a user clicks on the trigger button.", "optional": true, "type": "((options: { abortSignal: AbortSignal; }) => Promise)", }, + { + "description": "Specifies the height of the modal. When provided, the modal content becomes scrollable if it exceeds the specified height. +If the specified height exceeds available viewport space, the modal will use the maximum available space. +To make sure the content is accessible, the component will apply a minimum height of 60px for the content area, +plus the header height and the footer height.", + "name": "height", + "optional": true, + "type": "number", + }, { "deprecatedTag": "The usage of the \`id\` attribute is reserved for internal use cases. For testing and other use cases, use [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes). If you must @@ -17520,6 +17529,28 @@ render to an element under \`document.body\`.", "optional": true, "type": "HTMLElement", }, + { + "defaultValue": "'center'", + "description": "Controls the vertical positioning of the modal. + +- \`center\` (default) - The modal is vertically centered in the viewport and re-centers + when content height changes. Use for dialogs with static, predictable content. + +- \`top\` - The modal anchors at fixed distance and grows downward + as content expands. Use when the content changes dynamically to prevent disruptive + vertical repositioning that can cause users to lose context.", + "inlineType": { + "name": "ModalProps.Position", + "type": "union", + "values": [ + "center", + "top", + ], + }, + "name": "position", + "optional": true, + "type": "string", + }, { "description": "Use this property when \`getModalRoot\` is used to clean up the modal root element after a user closes the dialog. The function receives the return value @@ -17542,8 +17573,8 @@ of the most recent getModalRoot call as an argument.", { "defaultValue": "'medium'", "description": "Sets the width of the modal. \`max\` uses variable width up to the -largest size allowed by the design guidelines. Other sizes -(\`small\`/\`medium\`/\`large\`) have fixed widths.", +largest size allowed by the design guidelines. Other sizes have fixed widths: +\`small\` (320px), \`medium\` (600px), \`large\` (820px), \`x-large\` (1024px), \`xx-large\` (1280px).", "inlineType": { "name": "ModalProps.Size", "type": "union", @@ -17552,6 +17583,8 @@ largest size allowed by the design guidelines. Other sizes "max", "medium", "large", + "x-large", + "xx-large", ], }, "name": "size", @@ -17565,6 +17598,14 @@ Set this property to \`true\` to show them.", "optional": false, "type": "boolean", }, + { + "description": "Specifies the width of the modal. When provided, takes precedence over the \`size\` property. +If the specified width exceeds available viewport space, the modal will use the maximum available space. +The minimum width is 320px (equivalent to the \`small\` size).", + "name": "width", + "optional": true, + "type": "number", + }, ], "regions": [ { diff --git a/src/input/__tests__/__snapshots__/styles.test.tsx.snap b/src/input/__tests__/__snapshots__/styles.test.tsx.snap index 49f1043328..50041c913c 100644 --- a/src/input/__tests__/__snapshots__/styles.test.tsx.snap +++ b/src/input/__tests__/__snapshots__/styles.test.tsx.snap @@ -2,30 +2,30 @@ exports[`getInputStyles handles all possible style configurations 1`] = ` { - "--awsui-style-background-default-c5ek4l": undefined, - "--awsui-style-background-disabled-c5ek4l": undefined, - "--awsui-style-background-focus-c5ek4l": undefined, - "--awsui-style-background-hover-c5ek4l": undefined, - "--awsui-style-background-readonly-c5ek4l": undefined, - "--awsui-style-border-color-default-c5ek4l": undefined, - "--awsui-style-border-color-disabled-c5ek4l": undefined, - "--awsui-style-border-color-focus-c5ek4l": undefined, - "--awsui-style-border-color-hover-c5ek4l": undefined, - "--awsui-style-border-color-readonly-c5ek4l": undefined, - "--awsui-style-box-shadow-default-c5ek4l": undefined, - "--awsui-style-box-shadow-disabled-c5ek4l": undefined, - "--awsui-style-box-shadow-focus-c5ek4l": undefined, - "--awsui-style-box-shadow-hover-c5ek4l": undefined, - "--awsui-style-box-shadow-readonly-c5ek4l": undefined, - "--awsui-style-color-default-c5ek4l": undefined, - "--awsui-style-color-disabled-c5ek4l": undefined, - "--awsui-style-color-focus-c5ek4l": undefined, - "--awsui-style-color-hover-c5ek4l": undefined, - "--awsui-style-color-readonly-c5ek4l": undefined, - "--awsui-style-placeholder-color-c5ek4l": undefined, - "--awsui-style-placeholder-font-size-c5ek4l": undefined, - "--awsui-style-placeholder-font-style-c5ek4l": undefined, - "--awsui-style-placeholder-font-weight-c5ek4l": undefined, + "--awsui-style-background-default-n6lfw8": undefined, + "--awsui-style-background-disabled-n6lfw8": undefined, + "--awsui-style-background-focus-n6lfw8": undefined, + "--awsui-style-background-hover-n6lfw8": undefined, + "--awsui-style-background-readonly-n6lfw8": undefined, + "--awsui-style-border-color-default-n6lfw8": undefined, + "--awsui-style-border-color-disabled-n6lfw8": undefined, + "--awsui-style-border-color-focus-n6lfw8": undefined, + "--awsui-style-border-color-hover-n6lfw8": undefined, + "--awsui-style-border-color-readonly-n6lfw8": undefined, + "--awsui-style-box-shadow-default-n6lfw8": undefined, + "--awsui-style-box-shadow-disabled-n6lfw8": undefined, + "--awsui-style-box-shadow-focus-n6lfw8": undefined, + "--awsui-style-box-shadow-hover-n6lfw8": undefined, + "--awsui-style-box-shadow-readonly-n6lfw8": undefined, + "--awsui-style-color-default-n6lfw8": undefined, + "--awsui-style-color-disabled-n6lfw8": undefined, + "--awsui-style-color-focus-n6lfw8": undefined, + "--awsui-style-color-hover-n6lfw8": undefined, + "--awsui-style-color-readonly-n6lfw8": undefined, + "--awsui-style-placeholder-color-n6lfw8": undefined, + "--awsui-style-placeholder-font-size-n6lfw8": undefined, + "--awsui-style-placeholder-font-style-n6lfw8": undefined, + "--awsui-style-placeholder-font-weight-n6lfw8": undefined, "borderRadius": undefined, "borderWidth": undefined, "fontSize": undefined, @@ -37,30 +37,30 @@ exports[`getInputStyles handles all possible style configurations 1`] = ` exports[`getInputStyles handles all possible style configurations 2`] = ` { - "--awsui-style-background-default-c5ek4l": undefined, - "--awsui-style-background-disabled-c5ek4l": undefined, - "--awsui-style-background-focus-c5ek4l": undefined, - "--awsui-style-background-hover-c5ek4l": undefined, - "--awsui-style-background-readonly-c5ek4l": undefined, - "--awsui-style-border-color-default-c5ek4l": undefined, - "--awsui-style-border-color-disabled-c5ek4l": undefined, - "--awsui-style-border-color-focus-c5ek4l": undefined, - "--awsui-style-border-color-hover-c5ek4l": undefined, - "--awsui-style-border-color-readonly-c5ek4l": undefined, - "--awsui-style-box-shadow-default-c5ek4l": undefined, - "--awsui-style-box-shadow-disabled-c5ek4l": undefined, - "--awsui-style-box-shadow-focus-c5ek4l": undefined, - "--awsui-style-box-shadow-hover-c5ek4l": undefined, - "--awsui-style-box-shadow-readonly-c5ek4l": undefined, - "--awsui-style-color-default-c5ek4l": undefined, - "--awsui-style-color-disabled-c5ek4l": undefined, - "--awsui-style-color-focus-c5ek4l": undefined, - "--awsui-style-color-hover-c5ek4l": undefined, - "--awsui-style-color-readonly-c5ek4l": undefined, - "--awsui-style-placeholder-color-c5ek4l": undefined, - "--awsui-style-placeholder-font-size-c5ek4l": undefined, - "--awsui-style-placeholder-font-style-c5ek4l": undefined, - "--awsui-style-placeholder-font-weight-c5ek4l": undefined, + "--awsui-style-background-default-n6lfw8": undefined, + "--awsui-style-background-disabled-n6lfw8": undefined, + "--awsui-style-background-focus-n6lfw8": undefined, + "--awsui-style-background-hover-n6lfw8": undefined, + "--awsui-style-background-readonly-n6lfw8": undefined, + "--awsui-style-border-color-default-n6lfw8": undefined, + "--awsui-style-border-color-disabled-n6lfw8": undefined, + "--awsui-style-border-color-focus-n6lfw8": undefined, + "--awsui-style-border-color-hover-n6lfw8": undefined, + "--awsui-style-border-color-readonly-n6lfw8": undefined, + "--awsui-style-box-shadow-default-n6lfw8": undefined, + "--awsui-style-box-shadow-disabled-n6lfw8": undefined, + "--awsui-style-box-shadow-focus-n6lfw8": undefined, + "--awsui-style-box-shadow-hover-n6lfw8": undefined, + "--awsui-style-box-shadow-readonly-n6lfw8": undefined, + "--awsui-style-color-default-n6lfw8": undefined, + "--awsui-style-color-disabled-n6lfw8": undefined, + "--awsui-style-color-focus-n6lfw8": undefined, + "--awsui-style-color-hover-n6lfw8": undefined, + "--awsui-style-color-readonly-n6lfw8": undefined, + "--awsui-style-placeholder-color-n6lfw8": undefined, + "--awsui-style-placeholder-font-size-n6lfw8": undefined, + "--awsui-style-placeholder-font-style-n6lfw8": undefined, + "--awsui-style-placeholder-font-weight-n6lfw8": undefined, "borderRadius": undefined, "borderWidth": undefined, "fontSize": undefined, @@ -72,30 +72,30 @@ exports[`getInputStyles handles all possible style configurations 2`] = ` exports[`getInputStyles handles all possible style configurations 3`] = ` { - "--awsui-style-background-default-c5ek4l": "#ffffff", - "--awsui-style-background-disabled-c5ek4l": "#f0f0f0", - "--awsui-style-background-focus-c5ek4l": "#ffffff", - "--awsui-style-background-hover-c5ek4l": "#fafafa", - "--awsui-style-background-readonly-c5ek4l": "#ffffff", - "--awsui-style-border-color-default-c5ek4l": "#cccccc", - "--awsui-style-border-color-disabled-c5ek4l": "#e0e0e0", - "--awsui-style-border-color-focus-c5ek4l": "#0073bb", - "--awsui-style-border-color-hover-c5ek4l": "#999999", - "--awsui-style-border-color-readonly-c5ek4l": "#e0e0e0", - "--awsui-style-box-shadow-default-c5ek4l": "none", - "--awsui-style-box-shadow-disabled-c5ek4l": "none", - "--awsui-style-box-shadow-focus-c5ek4l": "0 0 0 2px #0073bb", - "--awsui-style-box-shadow-hover-c5ek4l": "0 1px 2px rgba(0,0,0,0.1)", - "--awsui-style-box-shadow-readonly-c5ek4l": "none", - "--awsui-style-color-default-c5ek4l": "#000000", - "--awsui-style-color-disabled-c5ek4l": "#999999", - "--awsui-style-color-focus-c5ek4l": "#000000", - "--awsui-style-color-hover-c5ek4l": "#000000", - "--awsui-style-color-readonly-c5ek4l": "#000000", - "--awsui-style-placeholder-color-c5ek4l": "#999999", - "--awsui-style-placeholder-font-size-c5ek4l": "14px", - "--awsui-style-placeholder-font-style-c5ek4l": "italic", - "--awsui-style-placeholder-font-weight-c5ek4l": "400", + "--awsui-style-background-default-n6lfw8": "#ffffff", + "--awsui-style-background-disabled-n6lfw8": "#f0f0f0", + "--awsui-style-background-focus-n6lfw8": "#ffffff", + "--awsui-style-background-hover-n6lfw8": "#fafafa", + "--awsui-style-background-readonly-n6lfw8": "#ffffff", + "--awsui-style-border-color-default-n6lfw8": "#cccccc", + "--awsui-style-border-color-disabled-n6lfw8": "#e0e0e0", + "--awsui-style-border-color-focus-n6lfw8": "#0073bb", + "--awsui-style-border-color-hover-n6lfw8": "#999999", + "--awsui-style-border-color-readonly-n6lfw8": "#e0e0e0", + "--awsui-style-box-shadow-default-n6lfw8": "none", + "--awsui-style-box-shadow-disabled-n6lfw8": "none", + "--awsui-style-box-shadow-focus-n6lfw8": "0 0 0 2px #0073bb", + "--awsui-style-box-shadow-hover-n6lfw8": "0 1px 2px rgba(0,0,0,0.1)", + "--awsui-style-box-shadow-readonly-n6lfw8": "none", + "--awsui-style-color-default-n6lfw8": "#000000", + "--awsui-style-color-disabled-n6lfw8": "#999999", + "--awsui-style-color-focus-n6lfw8": "#000000", + "--awsui-style-color-hover-n6lfw8": "#000000", + "--awsui-style-color-readonly-n6lfw8": "#000000", + "--awsui-style-placeholder-color-n6lfw8": "#999999", + "--awsui-style-placeholder-font-size-n6lfw8": "14px", + "--awsui-style-placeholder-font-style-n6lfw8": "italic", + "--awsui-style-placeholder-font-weight-n6lfw8": "400", "borderRadius": "4px", "borderWidth": "1px", "fontSize": "14px", diff --git a/src/modal/__integ__/modal.test.ts b/src/modal/__integ__/modal.test.ts index 922f9a9a03..2fcd5d5e30 100644 --- a/src/modal/__integ__/modal.test.ts +++ b/src/modal/__integ__/modal.test.ts @@ -206,4 +206,25 @@ test( }) ); +test( + 'keeps focused element visible with custom height', + useBrowser(async browser => { + const page = new BasePageObject(browser); + await browser.url('#/light/modal/custom-dimensions?height=400&footer=true'); + + await page.click('[data-testid="modal-trigger"]'); + const modal = createWrapper().findModal(); + const footerSelector = modal.findFooter().toSelector(); + const inputSelector = '[data-testid="final-input"]'; + + await page.keys(['Tab', 'Tab']); + await expect(page.isFocused(inputSelector)).resolves.toBe(true); + + const inputBox = await page.getBoundingBox(inputSelector); + const footerBox = await page.getBoundingBox(footerSelector); + const inputCenter = inputBox.top + inputBox.height / 2; + expect(inputCenter).toBeLessThan(footerBox.top); + }) +); + const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); diff --git a/src/modal/__tests__/modal.test.tsx b/src/modal/__tests__/modal.test.tsx index 94d43364e1..cf45b304b1 100644 --- a/src/modal/__tests__/modal.test.tsx +++ b/src/modal/__tests__/modal.test.tsx @@ -3,6 +3,8 @@ import * as React from 'react'; import { act, fireEvent, render } from '@testing-library/react'; +import { warnOnce } from '@cloudscape-design/component-toolkit/internal'; + import Autosuggest from '../../../lib/components/autosuggest'; import Button from '../../../lib/components/button/index.js'; import ButtonDropdown from '../../../lib/components/button-dropdown'; @@ -20,6 +22,15 @@ import { PerformanceMetrics } from '../../internal/analytics'; import { KeyCode } from '../../internal/keycode'; import styles from '../../../lib/components/modal/styles.css.js'; + +jest.mock('@cloudscape-design/component-toolkit/internal', () => ({ + ...jest.requireActual('@cloudscape-design/component-toolkit/internal'), + warnOnce: jest.fn(), +})); + +afterEach(() => { + (warnOnce as jest.Mock).mockReset(); +}); class ModalInternalWrapper extends ModalWrapper { findDialog(): ElementWrapper { return this.findByClassName(styles.dialog)!; @@ -112,11 +123,51 @@ describe('Modal component', () => { describe('size property', () => { it('displays correct size', () => { - (['small', 'medium', 'large', 'max'] as ModalProps.Size[]).forEach(size => { + (['small', 'medium', 'large', 'x-large', 'xx-large', 'max'] as ModalProps.Size[]).forEach(size => { const wrapper = renderModal({ size }); expect(wrapper.findDialog().getElement()).toHaveClass(styles[size]); }); }); + + it('width overrides size', () => { + const wrapper = renderModal({ size: 'large', width: 500 }); + expect(wrapper.findDialog().getElement()).not.toHaveClass(styles.large); + expect(wrapper.findDialog().getElement()).toHaveClass(styles['custom-width']); + }); + + it('applies custom width class', () => { + const wrapper = renderModal({ width: 600 }); + expect(wrapper.findDialog().getElement()).toHaveClass(styles['custom-width']); + }); + + it('applies custom height class', () => { + const wrapper = renderModal({ height: 400, footer:
Footer
}); + expect(wrapper.findDialog().getElement()).toHaveClass(styles['custom-height']); + expect(wrapper.findFooter()!.getElement()).toHaveClass(styles['custom-height']); + expect(wrapper.findByClassName(styles.container)!.getElement()).toHaveClass(styles['custom-height-container']); + expect(wrapper.findContent().getElement()).toHaveClass(styles['custom-height-content']); + }); + + it('applies custom height class when footer is absent', () => { + const wrapper = renderModal({ height: 400 }); + expect(wrapper.findDialog().getElement()).toHaveClass(styles['custom-height']); + expect(wrapper.findByClassName(styles.container)!.getElement()).toHaveClass(styles['custom-height-container']); + expect(wrapper.findContent().getElement()).toHaveClass(styles['custom-height-content']); + }); + }); + + describe('position property', () => { + it('defaults to center position', () => { + const wrapper = renderModal(); + expect(wrapper.findFocusLock().getElement()).toHaveClass(styles['position-center']); + }); + + it('displays correct position', () => { + (['center', 'top'] as ModalProps.Position[]).forEach(position => { + const wrapper = renderModal({ position }); + expect(wrapper.findFocusLock().getElement()).toHaveClass(styles[`position-${position}`]); + }); + }); }); describe('dismiss on click', () => { @@ -541,4 +592,32 @@ describe('Modal component', () => { }, 10); }); }); + + describe('custom dimensions warnings', () => { + it('warns when height is too small', () => { + renderModal({ height: 50 }); + expect(warnOnce).toHaveBeenCalledWith( + 'Modal', + expect.stringMatching(/^Height \(50px\) is too small\. Modal requires at least \d+px for content/) + ); + }); + + it('warns when width is below minimum', () => { + renderModal({ width: 200 }); + expect(warnOnce).toHaveBeenCalledWith( + 'Modal', + 'Width (200px) is below minimum (320px) and will be adjusted to 320px.' + ); + }); + + it('does not warn when dimensions are valid', () => { + renderModal({ width: 500, height: 400 }); + expect(warnOnce).not.toHaveBeenCalled(); + }); + + it('does not warn when dimensions are not provided', () => { + renderModal(); + expect(warnOnce).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/modal/index.tsx b/src/modal/index.tsx index c2b6982958..59bdf18530 100644 --- a/src/modal/index.tsx +++ b/src/modal/index.tsx @@ -70,7 +70,7 @@ function ModalWithAnalyticsFunnel({ ); } -export default function Modal({ size = 'medium', ...props }: ModalProps) { +export default function Modal({ size = 'medium', position = 'center', width, height, ...props }: ModalProps) { const { isInFunnel } = useFunnel(); const analyticsMetadata = getAnalyticsMetadataProps(props as BasePropsWithAnalyticsMetadata); const baseComponentProps = useBaseComponent( @@ -78,8 +78,11 @@ export default function Modal({ size = 'medium', ...props }: ModalProps) { { props: { size, + position, disableContentPaddings: props.disableContentPaddings, flowType: analyticsMetadata.flowType, + width, + height, }, metadata: { hasResourceType: Boolean(analyticsMetadata?.resourceType), @@ -95,12 +98,25 @@ export default function Modal({ size = 'medium', ...props }: ModalProps) { analyticsMetadata={analyticsMetadata} baseComponentProps={baseComponentProps} size={size} + position={position} + width={width} + height={height} {...props} /> ); } - return ; + return ( + + ); } applyDisplayName(Modal, 'Modal'); diff --git a/src/modal/interfaces.ts b/src/modal/interfaces.ts index f13a4b90c3..44b216e321 100644 --- a/src/modal/interfaces.ts +++ b/src/modal/interfaces.ts @@ -24,12 +24,38 @@ export interface BaseModalProps { } export interface ModalProps extends BaseComponentProps, BaseModalProps { + /** + * Specifies the width of the modal. When provided, takes precedence over the `size` property. + * If the specified width exceeds available viewport space, the modal will use the maximum available space. + * The minimum width is 320px (equivalent to the `small` size). + */ + width?: number; + + /** + * Specifies the height of the modal. When provided, the modal content becomes scrollable if it exceeds the specified height. + * If the specified height exceeds available viewport space, the modal will use the maximum available space. + * To make sure the content is accessible, the component will apply a minimum height of 60px for the content area, + * plus the header height and the footer height. + */ + height?: number; + /** * Sets the width of the modal. `max` uses variable width up to the - * largest size allowed by the design guidelines. Other sizes - * (`small`/`medium`/`large`) have fixed widths. + * largest size allowed by the design guidelines. Other sizes have fixed widths: + * `small` (320px), `medium` (600px), `large` (820px), `x-large` (1024px), `xx-large` (1280px). */ size?: ModalProps.Size; + /** + * Controls the vertical positioning of the modal. + * + * - `center` (default) - The modal is vertically centered in the viewport and re-centers + * when content height changes. Use for dialogs with static, predictable content. + * + * - `top` - The modal anchors at fixed distance and grows downward + * as content expands. Use when the content changes dynamically to prevent disruptive + * vertical repositioning that can cause users to lose context. + */ + position?: ModalProps.Position; /** * Determines whether the modal is displayed on the screen. Modals are hidden by default. * Set this property to `true` to show them. @@ -81,7 +107,8 @@ export interface ModalProps extends BaseComponentProps, BaseModalProps { } export namespace ModalProps { - export type Size = 'small' | 'medium' | 'large' | 'max'; + export type Size = 'small' | 'medium' | 'large' | 'x-large' | 'xx-large' | 'max'; + export type Position = 'center' | 'top'; export interface DismissDetail { reason: string; diff --git a/src/modal/internal.tsx b/src/modal/internal.tsx index 79e9e88c31..1159d6fb84 100644 --- a/src/modal/internal.tsx +++ b/src/modal/internal.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useRef } from 'react'; import clsx from 'clsx'; import { useContainerQuery } from '@cloudscape-design/component-toolkit'; -import { Portal, useMergeRefs, useUniqueId } from '@cloudscape-design/component-toolkit/internal'; +import { Portal, useMergeRefs, useUniqueId, warnOnce } from '@cloudscape-design/component-toolkit/internal'; import { getAnalyticsMetadataAttribute } from '@cloudscape-design/component-toolkit/internal/analytics-metadata'; import InternalBox from '../box/internal'; @@ -25,10 +25,12 @@ import { ButtonContext, ButtonContextProps } from '../internal/context/button-co import { ModalContext } from '../internal/context/modal-context'; import ResetContextsForModal from '../internal/context/reset-contexts-for-modal'; import { fireNonCancelableEvent } from '../internal/events'; +import customCssProps from '../internal/generated/custom-css-properties'; import { useContainerBreakpoints } from '../internal/hooks/container-queries'; import { InternalBaseComponentProps } from '../internal/hooks/use-base-component'; import { useIntersectionObserver } from '../internal/hooks/use-intersection-observer'; import { useVisualRefresh } from '../internal/hooks/use-visual-mode'; +import { isDevelopment } from '../internal/is-development'; import { KeyCode } from '../internal/keycode'; import { SomeRequired } from '../internal/types'; import { @@ -73,6 +75,8 @@ type InternalModalProps = SomeRequired & __injectAnalyticsComponentMetadata?: boolean; onButtonClick?: ButtonContextProps['onClick']; referrerId?: string; + width?: number; + height?: number; }; export default function InternalModal({ modalRoot, getModalRoot, removeModalRoot, ...rest }: InternalModalProps) { @@ -94,8 +98,11 @@ function PortaledModal({ children, footer, disableContentPaddings, + position = 'center', onButtonClick = () => {}, onDismiss, + width, + height, __internalRootRef, __injectAnalyticsComponentMetadata, __funnelProps, @@ -156,7 +163,7 @@ function PortaledModal({ PerformanceMetrics.modalPerformanceData({ timeToContentReadyInModal, instanceIdentifier: instanceUniqueId, - componentIdentifier: headerRef.current?.textContent || '', + componentIdentifier: headerTextRef.current?.textContent || '', }); performanceMetricLogged.current = true; } @@ -216,9 +223,46 @@ function PortaledModal({ // Add extra scroll padding to account for the height of the sticky footer, // to prevent it from covering focused elements. const [footerHeight, footerRef] = useContainerQuery(rect => rect.borderBoxHeight); - const headerRef = useRef(null); + const [headerHeight, headerRef] = useContainerQuery(rect => rect.borderBoxHeight); + const headerTextRef = useRef(null); const { subStepRef } = useFunnelSubStep(); + // Minimum content height is twice the height of the largest type style in Cloudscape (60px) + // to ensure content remains scrollable and accessible even with custom heights. + const MIN_CONTENT_HEIGHT = 60; + const MIN_MODAL_WIDTH = 320; // Matches smallest predefined size (small) + + // Calculate minimum modal height based on header, footer, and content + const minModalHeight = (headerHeight ?? 0) + (footer ? (footerHeight ?? 0) : 0) + MIN_CONTENT_HEIGHT; + + // Constrain dimensions to minimum values + const constrainedHeight = Math.max(height ?? 0, minModalHeight); + const constrainedWidth = Math.max(width ?? 0, MIN_MODAL_WIDTH); + + const hasCustomHeight = height !== undefined && !Number.isNaN(height); + const hasCustomWidth = width !== undefined && !Number.isNaN(width); + // Development warnings for adjusted values + if (isDevelopment) { + if (hasCustomHeight && constrainedHeight !== height) { + warnOnce( + 'Modal', + `Height (${height}px) is too small. Modal requires at least ${MIN_CONTENT_HEIGHT}px for content plus header/footer space (total: ${minModalHeight}px). Height will be adjusted to ${constrainedHeight}px.` + ); + } + if (hasCustomWidth && constrainedWidth !== width) { + warnOnce( + 'Modal', + `Width (${width}px) is below minimum (${MIN_MODAL_WIDTH}px) and will be adjusted to ${constrainedWidth}px.` + ); + } + } + + // Apply custom dimensions via CSS custom properties + const dialogCustomStyles: React.CSSProperties = { + ...(hasCustomWidth && { [customCssProps.modalCustomWidth]: `${constrainedWidth}px` }), + ...(hasCustomHeight && { [customCssProps.modalCustomHeight]: `${constrainedHeight}px` }), + }; + return ( @@ -247,19 +291,27 @@ function PortaledModal({ style={footerHeight ? { scrollPaddingBottom: footerHeight } : undefined} data-awsui-referrer-id={subStepRef.current?.id || referrerId} > - +
-
-
+
+
} > - + {header} @@ -291,14 +343,30 @@ function PortaledModal({
{children}
{footer && ( -
+
{footer}
diff --git a/src/modal/styles.scss b/src/modal/styles.scss index dfaf049e6c..9a8f04456a 100644 --- a/src/modal/styles.scss +++ b/src/modal/styles.scss @@ -7,6 +7,7 @@ @use '../internal/styles/tokens' as awsui; @use '../container/shared' as container; @use './motion'; +@use '../internal/generated/custom-css-properties/index.scss' as custom-props; $modal-z-index: 5000; @@ -33,12 +34,19 @@ $modal-z-index: 5000; .focus-lock { align-self: flex-start; - margin-block: auto; margin-inline: auto; padding-block: awsui.$space-s; padding-inline: 0; z-index: $modal-z-index; pointer-events: none; + + &.position-top { + margin-block-start: 0; + } + + &.position-center { + margin-block: auto; + } } .dialog { @@ -66,12 +74,29 @@ $modal-z-index: 5000; max-inline-size: 820px; } + &.x-large { + max-inline-size: 1024px; + } + + &.xx-large { + max-inline-size: 1280px; + } + + &.custom-width { + max-inline-size: var(#{custom-props.$modalCustomWidth}); + } + &.max.breakpoint-xs { // viewport - (closed app layout panel widths + 20px on each side) max-inline-size: calc(100vw - (2 * 4 * #{styles.$base-size} + #{awsui.$space-xxxl})); margin-block: auto; margin-inline: auto; } + + &.custom-height { + block-size: var(#{custom-props.$modalCustomHeight}); + max-block-size: calc(100vh - (2 * #{awsui.$space-s})); + } } .container { @@ -85,6 +110,12 @@ $modal-z-index: 5000; border-end-start-radius: awsui.$border-radius-container; border-end-end-radius: awsui.$border-radius-container; box-shadow: awsui.$shadow-modal; + + &.custom-height-container { + display: flex; + flex-direction: column; + block-size: 100%; + } } .content { @@ -96,6 +127,12 @@ $modal-z-index: 5000; padding-block: 0; padding-inline: 0; } + + // When custom height is set, make content scrollable + &.custom-height-content { + flex-grow: 1; + overflow-y: auto; + } } .header { @@ -125,6 +162,11 @@ $modal-z-index: 5000; inset-block-end: 0; z-index: 800; + &.custom-height { + border-end-start-radius: awsui.$border-radius-container; + border-end-end-radius: awsui.$border-radius-container; + } + &--stuck { border-start-start-radius: 0; border-start-end-radius: 0; diff --git a/src/segmented-control/__tests__/__snapshots__/styles.test.tsx.snap b/src/segmented-control/__tests__/__snapshots__/styles.test.tsx.snap index 510003a717..4252f20f35 100644 --- a/src/segmented-control/__tests__/__snapshots__/styles.test.tsx.snap +++ b/src/segmented-control/__tests__/__snapshots__/styles.test.tsx.snap @@ -20,17 +20,17 @@ exports[`getSegmentedControlRootStyles handles all possible style configurations exports[`getSegmentedControlSegmentStyles handles all possible style configurations 1`] = ` { - "--awsui-style-background-active-c5ek4l": undefined, - "--awsui-style-background-default-c5ek4l": undefined, - "--awsui-style-background-disabled-c5ek4l": undefined, - "--awsui-style-background-hover-c5ek4l": undefined, - "--awsui-style-color-active-c5ek4l": undefined, - "--awsui-style-color-default-c5ek4l": undefined, - "--awsui-style-color-disabled-c5ek4l": undefined, - "--awsui-style-color-hover-c5ek4l": undefined, - "--awsui-style-focus-ring-border-color-c5ek4l": undefined, - "--awsui-style-focus-ring-border-radius-c5ek4l": undefined, - "--awsui-style-focus-ring-border-width-c5ek4l": undefined, + "--awsui-style-background-active-n6lfw8": undefined, + "--awsui-style-background-default-n6lfw8": undefined, + "--awsui-style-background-disabled-n6lfw8": undefined, + "--awsui-style-background-hover-n6lfw8": undefined, + "--awsui-style-color-active-n6lfw8": undefined, + "--awsui-style-color-default-n6lfw8": undefined, + "--awsui-style-color-disabled-n6lfw8": undefined, + "--awsui-style-color-hover-n6lfw8": undefined, + "--awsui-style-focus-ring-border-color-n6lfw8": undefined, + "--awsui-style-focus-ring-border-radius-n6lfw8": undefined, + "--awsui-style-focus-ring-border-width-n6lfw8": undefined, "borderRadius": undefined, "fontSize": undefined, "paddingBlock": undefined, @@ -40,17 +40,17 @@ exports[`getSegmentedControlSegmentStyles handles all possible style configurati exports[`getSegmentedControlSegmentStyles handles all possible style configurations 2`] = ` { - "--awsui-style-background-active-c5ek4l": undefined, - "--awsui-style-background-default-c5ek4l": undefined, - "--awsui-style-background-disabled-c5ek4l": undefined, - "--awsui-style-background-hover-c5ek4l": undefined, - "--awsui-style-color-active-c5ek4l": undefined, - "--awsui-style-color-default-c5ek4l": undefined, - "--awsui-style-color-disabled-c5ek4l": undefined, - "--awsui-style-color-hover-c5ek4l": undefined, - "--awsui-style-focus-ring-border-color-c5ek4l": undefined, - "--awsui-style-focus-ring-border-radius-c5ek4l": undefined, - "--awsui-style-focus-ring-border-width-c5ek4l": undefined, + "--awsui-style-background-active-n6lfw8": undefined, + "--awsui-style-background-default-n6lfw8": undefined, + "--awsui-style-background-disabled-n6lfw8": undefined, + "--awsui-style-background-hover-n6lfw8": undefined, + "--awsui-style-color-active-n6lfw8": undefined, + "--awsui-style-color-default-n6lfw8": undefined, + "--awsui-style-color-disabled-n6lfw8": undefined, + "--awsui-style-color-hover-n6lfw8": undefined, + "--awsui-style-focus-ring-border-color-n6lfw8": undefined, + "--awsui-style-focus-ring-border-radius-n6lfw8": undefined, + "--awsui-style-focus-ring-border-width-n6lfw8": undefined, "borderRadius": undefined, "fontSize": undefined, "paddingBlock": undefined, @@ -60,17 +60,17 @@ exports[`getSegmentedControlSegmentStyles handles all possible style configurati exports[`getSegmentedControlSegmentStyles handles all possible style configurations 3`] = ` { - "--awsui-style-background-active-c5ek4l": "#0073bb", - "--awsui-style-background-default-c5ek4l": "#ffffff", - "--awsui-style-background-disabled-c5ek4l": "#f0f0f0", - "--awsui-style-background-hover-c5ek4l": "#fafafa", - "--awsui-style-color-active-c5ek4l": "#ffffff", - "--awsui-style-color-default-c5ek4l": "#000000", - "--awsui-style-color-disabled-c5ek4l": "#999999", - "--awsui-style-color-hover-c5ek4l": "#000000", - "--awsui-style-focus-ring-border-color-c5ek4l": "#0073bb", - "--awsui-style-focus-ring-border-radius-c5ek4l": "8px", - "--awsui-style-focus-ring-border-width-c5ek4l": "2px", + "--awsui-style-background-active-n6lfw8": "#0073bb", + "--awsui-style-background-default-n6lfw8": "#ffffff", + "--awsui-style-background-disabled-n6lfw8": "#f0f0f0", + "--awsui-style-background-hover-n6lfw8": "#fafafa", + "--awsui-style-color-active-n6lfw8": "#ffffff", + "--awsui-style-color-default-n6lfw8": "#000000", + "--awsui-style-color-disabled-n6lfw8": "#999999", + "--awsui-style-color-hover-n6lfw8": "#000000", + "--awsui-style-focus-ring-border-color-n6lfw8": "#0073bb", + "--awsui-style-focus-ring-border-radius-n6lfw8": "8px", + "--awsui-style-focus-ring-border-width-n6lfw8": "2px", "borderRadius": "6px", "fontSize": "14px", "paddingBlock": "8px", diff --git a/src/slider/__tests__/__snapshots__/styles.test.tsx.snap b/src/slider/__tests__/__snapshots__/styles.test.tsx.snap index 5b880a8c80..36e58f3200 100644 --- a/src/slider/__tests__/__snapshots__/styles.test.tsx.snap +++ b/src/slider/__tests__/__snapshots__/styles.test.tsx.snap @@ -2,36 +2,36 @@ exports[`getSliderStyles handles all possible style configurations 1`] = ` { - "--awsui-style-slider-handle-background-active-c5ek4l": undefined, - "--awsui-style-slider-handle-background-default-c5ek4l": undefined, - "--awsui-style-slider-handle-background-hover-c5ek4l": undefined, - "--awsui-style-slider-handle-border-radius-c5ek4l": undefined, - "--awsui-style-slider-range-background-active-c5ek4l": undefined, - "--awsui-style-slider-range-background-default-c5ek4l": undefined, - "--awsui-style-slider-track-background-color-c5ek4l": undefined, + "--awsui-style-slider-handle-background-active-n6lfw8": undefined, + "--awsui-style-slider-handle-background-default-n6lfw8": undefined, + "--awsui-style-slider-handle-background-hover-n6lfw8": undefined, + "--awsui-style-slider-handle-border-radius-n6lfw8": undefined, + "--awsui-style-slider-range-background-active-n6lfw8": undefined, + "--awsui-style-slider-range-background-default-n6lfw8": undefined, + "--awsui-style-slider-track-background-color-n6lfw8": undefined, } `; exports[`getSliderStyles handles all possible style configurations 2`] = ` { - "--awsui-style-slider-handle-background-active-c5ek4l": undefined, - "--awsui-style-slider-handle-background-default-c5ek4l": undefined, - "--awsui-style-slider-handle-background-hover-c5ek4l": undefined, - "--awsui-style-slider-handle-border-radius-c5ek4l": undefined, - "--awsui-style-slider-range-background-active-c5ek4l": undefined, - "--awsui-style-slider-range-background-default-c5ek4l": undefined, - "--awsui-style-slider-track-background-color-c5ek4l": undefined, + "--awsui-style-slider-handle-background-active-n6lfw8": undefined, + "--awsui-style-slider-handle-background-default-n6lfw8": undefined, + "--awsui-style-slider-handle-background-hover-n6lfw8": undefined, + "--awsui-style-slider-handle-border-radius-n6lfw8": undefined, + "--awsui-style-slider-range-background-active-n6lfw8": undefined, + "--awsui-style-slider-range-background-default-n6lfw8": undefined, + "--awsui-style-slider-track-background-color-n6lfw8": undefined, } `; exports[`getSliderStyles handles all possible style configurations 3`] = ` { - "--awsui-style-slider-handle-background-active-c5ek4l": "#1d4ed8", - "--awsui-style-slider-handle-background-default-c5ek4l": "#3b82f6", - "--awsui-style-slider-handle-background-hover-c5ek4l": "#2563eb", - "--awsui-style-slider-handle-border-radius-c5ek4l": "50%", - "--awsui-style-slider-range-background-active-c5ek4l": "#2563eb", - "--awsui-style-slider-range-background-default-c5ek4l": "#3b82f6", - "--awsui-style-slider-track-background-color-c5ek4l": "#dbeafe", + "--awsui-style-slider-handle-background-active-n6lfw8": "#1d4ed8", + "--awsui-style-slider-handle-background-default-n6lfw8": "#3b82f6", + "--awsui-style-slider-handle-background-hover-n6lfw8": "#2563eb", + "--awsui-style-slider-handle-border-radius-n6lfw8": "50%", + "--awsui-style-slider-range-background-active-n6lfw8": "#2563eb", + "--awsui-style-slider-range-background-default-n6lfw8": "#3b82f6", + "--awsui-style-slider-track-background-color-n6lfw8": "#dbeafe", } `; diff --git a/src/tabs/__tests__/__snapshots__/styles.test.tsx.snap b/src/tabs/__tests__/__snapshots__/styles.test.tsx.snap index 65c1b9228f..fabdb84b2c 100644 --- a/src/tabs/__tests__/__snapshots__/styles.test.tsx.snap +++ b/src/tabs/__tests__/__snapshots__/styles.test.tsx.snap @@ -2,21 +2,21 @@ exports[`getTabStyles transforms tab styles to CSS properties 1`] = ` { - "--awsui-style-background-active-c5ek4l": "#bfdbfe", - "--awsui-style-background-default-c5ek4l": "#dbeafe", - "--awsui-style-background-disabled-c5ek4l": "#f3f4f6", - "--awsui-style-background-hover-c5ek4l": "#eff6ff", - "--awsui-style-border-color-active-c5ek4l": "#1d4ed8", - "--awsui-style-border-color-default-c5ek4l": "#3b82f6", - "--awsui-style-border-color-disabled-c5ek4l": "#93c5fd", - "--awsui-style-border-color-hover-c5ek4l": "#2563eb", - "--awsui-style-color-active-c5ek4l": "#1e3a8a", - "--awsui-style-color-default-c5ek4l": "#1e40af", - "--awsui-style-color-disabled-c5ek4l": "#93c5fd", - "--awsui-style-color-hover-c5ek4l": "#1e40af", - "--awsui-style-focus-ring-border-color-c5ek4l": "#3b82f6", - "--awsui-style-focus-ring-border-radius-c5ek4l": "4px", - "--awsui-style-focus-ring-border-width-c5ek4l": "2px", + "--awsui-style-background-active-n6lfw8": "#bfdbfe", + "--awsui-style-background-default-n6lfw8": "#dbeafe", + "--awsui-style-background-disabled-n6lfw8": "#f3f4f6", + "--awsui-style-background-hover-n6lfw8": "#eff6ff", + "--awsui-style-border-color-active-n6lfw8": "#1d4ed8", + "--awsui-style-border-color-default-n6lfw8": "#3b82f6", + "--awsui-style-border-color-disabled-n6lfw8": "#93c5fd", + "--awsui-style-border-color-hover-n6lfw8": "#2563eb", + "--awsui-style-color-active-n6lfw8": "#1e3a8a", + "--awsui-style-color-default-n6lfw8": "#1e40af", + "--awsui-style-color-disabled-n6lfw8": "#93c5fd", + "--awsui-style-color-hover-n6lfw8": "#1e40af", + "--awsui-style-focus-ring-border-color-n6lfw8": "#3b82f6", + "--awsui-style-focus-ring-border-radius-n6lfw8": "4px", + "--awsui-style-focus-ring-border-width-n6lfw8": "2px", "borderRadius": "4px", "borderWidth": "2px", "fontSize": "16px", @@ -28,10 +28,10 @@ exports[`getTabStyles transforms tab styles to CSS properties 1`] = ` exports[`getTabStyles transforms tab styles to CSS properties 2`] = ` { - "--awsui-style-tabs-active-indicator-border-radius-c5ek4l": "2px", - "--awsui-style-tabs-active-indicator-color-c5ek4l": "#1d4ed8", - "--awsui-style-tabs-active-indicator-width-c5ek4l": "3px", - "--awsui-style-tabs-separator-color-c5ek4l": "#cbd5e1", - "--awsui-style-tabs-separator-width-c5ek4l": "2px", + "--awsui-style-tabs-active-indicator-border-radius-n6lfw8": "2px", + "--awsui-style-tabs-active-indicator-color-n6lfw8": "#1d4ed8", + "--awsui-style-tabs-active-indicator-width-n6lfw8": "3px", + "--awsui-style-tabs-separator-color-n6lfw8": "#cbd5e1", + "--awsui-style-tabs-separator-width-n6lfw8": "2px", } `; diff --git a/src/text-filter/__tests__/__snapshots__/styles.test.tsx.snap b/src/text-filter/__tests__/__snapshots__/styles.test.tsx.snap index 1d10ad578d..b0861036b9 100644 --- a/src/text-filter/__tests__/__snapshots__/styles.test.tsx.snap +++ b/src/text-filter/__tests__/__snapshots__/styles.test.tsx.snap @@ -2,30 +2,30 @@ exports[`getTextFilterStyles handles all possible style configurations 1`] = ` { - "--awsui-style-background-default-c5ek4l": undefined, - "--awsui-style-background-disabled-c5ek4l": undefined, - "--awsui-style-background-focus-c5ek4l": undefined, - "--awsui-style-background-hover-c5ek4l": undefined, - "--awsui-style-background-readonly-c5ek4l": undefined, - "--awsui-style-border-color-default-c5ek4l": undefined, - "--awsui-style-border-color-disabled-c5ek4l": undefined, - "--awsui-style-border-color-focus-c5ek4l": undefined, - "--awsui-style-border-color-hover-c5ek4l": undefined, - "--awsui-style-border-color-readonly-c5ek4l": undefined, - "--awsui-style-box-shadow-default-c5ek4l": undefined, - "--awsui-style-box-shadow-disabled-c5ek4l": undefined, - "--awsui-style-box-shadow-focus-c5ek4l": undefined, - "--awsui-style-box-shadow-hover-c5ek4l": undefined, - "--awsui-style-box-shadow-readonly-c5ek4l": undefined, - "--awsui-style-color-default-c5ek4l": undefined, - "--awsui-style-color-disabled-c5ek4l": undefined, - "--awsui-style-color-focus-c5ek4l": undefined, - "--awsui-style-color-hover-c5ek4l": undefined, - "--awsui-style-color-readonly-c5ek4l": undefined, - "--awsui-style-placeholder-color-c5ek4l": undefined, - "--awsui-style-placeholder-font-size-c5ek4l": undefined, - "--awsui-style-placeholder-font-style-c5ek4l": undefined, - "--awsui-style-placeholder-font-weight-c5ek4l": undefined, + "--awsui-style-background-default-n6lfw8": undefined, + "--awsui-style-background-disabled-n6lfw8": undefined, + "--awsui-style-background-focus-n6lfw8": undefined, + "--awsui-style-background-hover-n6lfw8": undefined, + "--awsui-style-background-readonly-n6lfw8": undefined, + "--awsui-style-border-color-default-n6lfw8": undefined, + "--awsui-style-border-color-disabled-n6lfw8": undefined, + "--awsui-style-border-color-focus-n6lfw8": undefined, + "--awsui-style-border-color-hover-n6lfw8": undefined, + "--awsui-style-border-color-readonly-n6lfw8": undefined, + "--awsui-style-box-shadow-default-n6lfw8": undefined, + "--awsui-style-box-shadow-disabled-n6lfw8": undefined, + "--awsui-style-box-shadow-focus-n6lfw8": undefined, + "--awsui-style-box-shadow-hover-n6lfw8": undefined, + "--awsui-style-box-shadow-readonly-n6lfw8": undefined, + "--awsui-style-color-default-n6lfw8": undefined, + "--awsui-style-color-disabled-n6lfw8": undefined, + "--awsui-style-color-focus-n6lfw8": undefined, + "--awsui-style-color-hover-n6lfw8": undefined, + "--awsui-style-color-readonly-n6lfw8": undefined, + "--awsui-style-placeholder-color-n6lfw8": undefined, + "--awsui-style-placeholder-font-size-n6lfw8": undefined, + "--awsui-style-placeholder-font-style-n6lfw8": undefined, + "--awsui-style-placeholder-font-weight-n6lfw8": undefined, "borderRadius": undefined, "borderWidth": undefined, "fontSize": undefined, @@ -37,30 +37,30 @@ exports[`getTextFilterStyles handles all possible style configurations 1`] = ` exports[`getTextFilterStyles handles all possible style configurations 2`] = ` { - "--awsui-style-background-default-c5ek4l": undefined, - "--awsui-style-background-disabled-c5ek4l": undefined, - "--awsui-style-background-focus-c5ek4l": undefined, - "--awsui-style-background-hover-c5ek4l": undefined, - "--awsui-style-background-readonly-c5ek4l": undefined, - "--awsui-style-border-color-default-c5ek4l": undefined, - "--awsui-style-border-color-disabled-c5ek4l": undefined, - "--awsui-style-border-color-focus-c5ek4l": undefined, - "--awsui-style-border-color-hover-c5ek4l": undefined, - "--awsui-style-border-color-readonly-c5ek4l": undefined, - "--awsui-style-box-shadow-default-c5ek4l": undefined, - "--awsui-style-box-shadow-disabled-c5ek4l": undefined, - "--awsui-style-box-shadow-focus-c5ek4l": undefined, - "--awsui-style-box-shadow-hover-c5ek4l": undefined, - "--awsui-style-box-shadow-readonly-c5ek4l": undefined, - "--awsui-style-color-default-c5ek4l": undefined, - "--awsui-style-color-disabled-c5ek4l": undefined, - "--awsui-style-color-focus-c5ek4l": undefined, - "--awsui-style-color-hover-c5ek4l": undefined, - "--awsui-style-color-readonly-c5ek4l": undefined, - "--awsui-style-placeholder-color-c5ek4l": undefined, - "--awsui-style-placeholder-font-size-c5ek4l": undefined, - "--awsui-style-placeholder-font-style-c5ek4l": undefined, - "--awsui-style-placeholder-font-weight-c5ek4l": undefined, + "--awsui-style-background-default-n6lfw8": undefined, + "--awsui-style-background-disabled-n6lfw8": undefined, + "--awsui-style-background-focus-n6lfw8": undefined, + "--awsui-style-background-hover-n6lfw8": undefined, + "--awsui-style-background-readonly-n6lfw8": undefined, + "--awsui-style-border-color-default-n6lfw8": undefined, + "--awsui-style-border-color-disabled-n6lfw8": undefined, + "--awsui-style-border-color-focus-n6lfw8": undefined, + "--awsui-style-border-color-hover-n6lfw8": undefined, + "--awsui-style-border-color-readonly-n6lfw8": undefined, + "--awsui-style-box-shadow-default-n6lfw8": undefined, + "--awsui-style-box-shadow-disabled-n6lfw8": undefined, + "--awsui-style-box-shadow-focus-n6lfw8": undefined, + "--awsui-style-box-shadow-hover-n6lfw8": undefined, + "--awsui-style-box-shadow-readonly-n6lfw8": undefined, + "--awsui-style-color-default-n6lfw8": undefined, + "--awsui-style-color-disabled-n6lfw8": undefined, + "--awsui-style-color-focus-n6lfw8": undefined, + "--awsui-style-color-hover-n6lfw8": undefined, + "--awsui-style-color-readonly-n6lfw8": undefined, + "--awsui-style-placeholder-color-n6lfw8": undefined, + "--awsui-style-placeholder-font-size-n6lfw8": undefined, + "--awsui-style-placeholder-font-style-n6lfw8": undefined, + "--awsui-style-placeholder-font-weight-n6lfw8": undefined, "borderRadius": undefined, "borderWidth": undefined, "fontSize": undefined, @@ -72,30 +72,30 @@ exports[`getTextFilterStyles handles all possible style configurations 2`] = ` exports[`getTextFilterStyles handles all possible style configurations 3`] = ` { - "--awsui-style-background-default-c5ek4l": "#ffffff", - "--awsui-style-background-disabled-c5ek4l": "#f0f0f0", - "--awsui-style-background-focus-c5ek4l": "#ffffff", - "--awsui-style-background-hover-c5ek4l": "#fafafa", - "--awsui-style-background-readonly-c5ek4l": "#ffffff", - "--awsui-style-border-color-default-c5ek4l": "#cccccc", - "--awsui-style-border-color-disabled-c5ek4l": "#e0e0e0", - "--awsui-style-border-color-focus-c5ek4l": "#0073bb", - "--awsui-style-border-color-hover-c5ek4l": "#999999", - "--awsui-style-border-color-readonly-c5ek4l": "#e0e0e0", - "--awsui-style-box-shadow-default-c5ek4l": "none", - "--awsui-style-box-shadow-disabled-c5ek4l": "none", - "--awsui-style-box-shadow-focus-c5ek4l": "0 0 0 2px #0073bb", - "--awsui-style-box-shadow-hover-c5ek4l": "0 1px 2px rgba(0,0,0,0.1)", - "--awsui-style-box-shadow-readonly-c5ek4l": "none", - "--awsui-style-color-default-c5ek4l": "#000000", - "--awsui-style-color-disabled-c5ek4l": "#999999", - "--awsui-style-color-focus-c5ek4l": "#000000", - "--awsui-style-color-hover-c5ek4l": "#000000", - "--awsui-style-color-readonly-c5ek4l": "#000000", - "--awsui-style-placeholder-color-c5ek4l": "#999999", - "--awsui-style-placeholder-font-size-c5ek4l": "14px", - "--awsui-style-placeholder-font-style-c5ek4l": "italic", - "--awsui-style-placeholder-font-weight-c5ek4l": "400", + "--awsui-style-background-default-n6lfw8": "#ffffff", + "--awsui-style-background-disabled-n6lfw8": "#f0f0f0", + "--awsui-style-background-focus-n6lfw8": "#ffffff", + "--awsui-style-background-hover-n6lfw8": "#fafafa", + "--awsui-style-background-readonly-n6lfw8": "#ffffff", + "--awsui-style-border-color-default-n6lfw8": "#cccccc", + "--awsui-style-border-color-disabled-n6lfw8": "#e0e0e0", + "--awsui-style-border-color-focus-n6lfw8": "#0073bb", + "--awsui-style-border-color-hover-n6lfw8": "#999999", + "--awsui-style-border-color-readonly-n6lfw8": "#e0e0e0", + "--awsui-style-box-shadow-default-n6lfw8": "none", + "--awsui-style-box-shadow-disabled-n6lfw8": "none", + "--awsui-style-box-shadow-focus-n6lfw8": "0 0 0 2px #0073bb", + "--awsui-style-box-shadow-hover-n6lfw8": "0 1px 2px rgba(0,0,0,0.1)", + "--awsui-style-box-shadow-readonly-n6lfw8": "none", + "--awsui-style-color-default-n6lfw8": "#000000", + "--awsui-style-color-disabled-n6lfw8": "#999999", + "--awsui-style-color-focus-n6lfw8": "#000000", + "--awsui-style-color-hover-n6lfw8": "#000000", + "--awsui-style-color-readonly-n6lfw8": "#000000", + "--awsui-style-placeholder-color-n6lfw8": "#999999", + "--awsui-style-placeholder-font-size-n6lfw8": "14px", + "--awsui-style-placeholder-font-style-n6lfw8": "italic", + "--awsui-style-placeholder-font-weight-n6lfw8": "400", "borderRadius": "4px", "borderWidth": "1px", "fontSize": "14px",