diff --git a/package.json b/package.json index b81ca3cde1..5c4fb82830 100644 --- a/package.json +++ b/package.json @@ -183,7 +183,7 @@ { "path": "lib/components/internal/widget-exports.js", "brotli": false, - "limit": "1450 kB", + "limit": "1455 kB", "ignore": "react-dom" } ], diff --git a/pages/control-group/action-button.page.tsx b/pages/control-group/action-button.page.tsx new file mode 100644 index 0000000000..cd02388238 --- /dev/null +++ b/pages/control-group/action-button.page.tsx @@ -0,0 +1,107 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import Autosuggest, { AutosuggestProps } from '~components/autosuggest'; +import Input from '~components/input'; +import ControlGroup, { ControlGroupProps } from '~components/internal/components/control-group'; +import Multiselect from '~components/multiselect'; +import SegmentedControl from '~components/segmented-control'; +import Select from '~components/select'; + +import { PermutationsPage } from '../app/templates'; +import createPermutations from '../utils/permutations'; +import PermutationsView from '../utils/permutations-view'; +import { + DirectionSettings, + enteredTextLabel, + multiOptions, + noop, + operators, + segments, + useControlGroupDirection, +} from './common'; + +const suggestions: AutosuggestProps.Option[] = [{ value: 'CPUUtilization' }, { value: 'MemoryUtilization' }]; + +const input = ; +const select = + + ); + + const actionButton = findControlGroup(container)!.findActionButton(); + expect(actionButton).not.toBeNull(); + expect(actionButton!.getElement()).toHaveAccessibleName('Remove'); + }); + + test('renders no action-slot element when actionButton is absent', () => { + const { container } = render( + + + + ); + + expect(findControlGroup(container)!.findActionButton()).toBeNull(); + }); + + test('calls onClick when the action button is clicked', () => { + const onClick = jest.fn(); + const { container } = render( + + + + ); + + const actionButton = findControlGroup(container)!.findActionButton()!; + actionButton.click(); + expect(onClick).toHaveBeenCalledTimes(1); + }); + }); + describe('direction', () => { test('defaults the direction to "horizontal" and exposes it to each child', () => { const { getByTestId } = render( @@ -129,7 +166,7 @@ describe('Control group', () => { }); test('omits the inline label and aria-labelledby when inlineLabelText is not set', () => { - const { container, getByRole } = render( + const { getByRole } = render( @@ -137,6 +174,5 @@ describe('Control group', () => { ); expect(getByRole('group').getAttribute('aria-labelledby')).toBeNull(); - expect(findControlGroup(container)).toBeNull(); }); }); diff --git a/src/internal/components/control-group/grouped-control-styles.ts b/src/internal/components/control-group/grouped-control-styles.ts index aed1187a07..7bbf3e55b3 100644 --- a/src/internal/components/control-group/grouped-control-styles.ts +++ b/src/internal/components/control-group/grouped-control-styles.ts @@ -11,10 +11,14 @@ import { GroupedControlContextProps } from '../../context/control-group-context' */ export function getGroupedControlClassNames( styles: Record, - { position, direction }: GroupedControlContextProps + { position, direction, hasAction }: GroupedControlContextProps ): string[] { if (!position) { return []; } - return [styles.grouped, styles[`grouped-${direction}-${position}`]]; + const classNames = [styles.grouped, styles[`grouped-${direction}-${position}`]]; + if (hasAction) { + classNames.push(styles['grouped-with-action']); + } + return classNames; } diff --git a/src/internal/components/control-group/index.tsx b/src/internal/components/control-group/index.tsx index ea7bc2a1ed..13c273d102 100644 --- a/src/internal/components/control-group/index.tsx +++ b/src/internal/components/control-group/index.tsx @@ -5,64 +5,94 @@ import clsx from 'clsx'; import { useUniqueId } from '@cloudscape-design/component-toolkit/internal'; -import { BaseComponentProps } from '../../../types/base-component'; +import { InternalButton } from '../../../button/internal'; import { getBaseProps } from '../../base-component'; -import { - GroupedControlContext, - GroupedControlDirection, - GroupedControlPosition, -} from '../../context/control-group-context'; +import { GroupedControlContext, GroupedControlPosition } from '../../context/control-group-context'; import { flattenChildren } from '../../utils/flatten-children'; +import { ControlGroupProps } from './interfaces'; import styles from './styles.css.js'; +import testUtilStyles from './test-classes/styles.css.js'; -export interface InternalControlGroupProps extends BaseComponentProps { - children?: React.ReactNode; - direction?: GroupedControlDirection; - inlineLabelText?: string; -} +export { ControlGroupProps }; -export default function InternalControlGroup({ +export default function ControlGroup({ children, direction = 'horizontal', inlineLabelText, + actionButton, ...props -}: InternalControlGroupProps) { +}: ControlGroupProps) { const baseProps = getBaseProps(props); const labelId = useUniqueId('control-group-label'); const flattenedChildren = flattenChildren(children, 'ControlGroup'); const controlCount = flattenedChildren.length; - const controls = flattenedChildren.map((child, index) => { - const key = child && typeof child === 'object' ? (child as Record<'key', unknown>).key : undefined; - const position: GroupedControlPosition = - controlCount === 1 ? 'only' : index === 0 ? 'first' : index === controlCount - 1 ? 'last' : 'middle'; - return ( -
- {child} + const rootClassnames = [baseProps.className, testUtilStyles.root]; + + const controls = ( +
+ {flattenedChildren.map((child, index) => { + const key = child && typeof child === 'object' ? (child as Record<'key', unknown>).key : undefined; + const position: GroupedControlPosition = + controlCount === 1 ? 'only' : index === 0 ? 'first' : index === controlCount - 1 ? 'last' : 'middle'; + return ( +
+ + {child} + +
+ ); + })} +
+ ); + + const controlsWithAction = actionButton ? ( +
+ {controls} +
+
- ); - }); +
+ ) : ( + controls + ); if (inlineLabelText) { return ( -
-