Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20111,7 +20111,7 @@ being included in a form submission. A read-only control is still focusable.",
},
{
"inlineType": {
"name": "RadioGroupProps.Style",
"name": "RadioButtonProps.Style",
"properties": [
{
"inlineType": {
Expand Down Expand Up @@ -20336,7 +20336,7 @@ being included in a form submission. A read-only control is still focusable.",
"systemTags": [
"core",
],
"type": "RadioGroupProps.Style",
"type": "RadioButtonProps.Style",
},
{
"description": "Sets the value of the selected radio button.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ exports[`test-utils selectors 1`] = `
"awsui_placeholder_18eso",
"awsui_recovery_vrgzu",
"awsui_root_11n0s",
"awsui_root_15oj2",
"awsui_root_1fcus",
"awsui_root_1kjc7",
"awsui_root_1om0h",
Expand Down Expand Up @@ -514,8 +515,7 @@ exports[`test-utils selectors 1`] = `
"awsui_token-editor-token-remove-actions_1heb1",
],
"radio-group": [
"awsui_radio_1mabk",
"awsui_root_1mabk",
"awsui_root_1np5w",
],
"s3-resource-selector": [
"awsui_alert_1u0yw",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,20 @@ import { useMergeRefs } from '@cloudscape-design/component-toolkit/internal';
import { useSingleTabStopNavigation } from '@cloudscape-design/component-toolkit/internal';
import { copyAnalyticsMetadataAttribute } from '@cloudscape-design/component-toolkit/internal/analytics-metadata';

import AbstractSwitch from '../internal/components/abstract-switch';
import { fireNonCancelableEvent, NonCancelableEventHandler } from '../internal/events';
import { RadioGroupProps } from './interfaces';
import { getAbstractSwitchStyles, getInnerCircleStyle, getOuterCircleStyle } from './style';
import { getAbstractSwitchStyles, getInnerCircleStyle, getOuterCircleStyle } from '../../../radio-group/style';
import { getBaseProps } from '../../base-component';
import AbstractSwitch from '../../components/abstract-switch';
import { fireNonCancelableEvent } from '../../events';
import { InternalBaseComponentProps } from '../../hooks/use-base-component';
import { RadioButtonProps } from './interfaces';

import styles from './styles.css.js';

interface RadioButtonProps extends RadioGroupProps.RadioButtonDefinition {
name: string;
checked: boolean;
onChange?: NonCancelableEventHandler<RadioGroupProps.ChangeDetail>;
readOnly?: boolean;
className?: string;
style?: RadioGroupProps.Style;
}
import testUtilStyles from './test-classes/styles.css.js';

export default React.forwardRef(function RadioButton(
{
name,
label,
children,
value,
checked,
description,
Expand All @@ -37,25 +31,28 @@ export default React.forwardRef(function RadioButton(
className,
style,
...rest
}: RadioButtonProps,
}: RadioButtonProps & InternalBaseComponentProps,
ref: React.Ref<HTMLInputElement>
) {
const radioButtonRef = useRef<HTMLInputElement>(null);
const mergedRefs = useMergeRefs(radioButtonRef, ref);

const { tabIndex } = useSingleTabStopNavigation(radioButtonRef);
const baseProps = getBaseProps(rest);

return (
<AbstractSwitch
className={clsx(styles.radio, description && styles['radio--has-description'], className)}
{...baseProps}
className={clsx(testUtilStyles.root, className)}
controlClassName={styles['radio-control']}
outlineClassName={styles.outline}
label={label}
label={children}
description={description}
disabled={disabled}
readOnly={readOnly}
controlId={controlId}
style={getAbstractSwitchStyles(style, checked, disabled, readOnly)}
__internalRootRef={rest.__internalRootRef}
{...copyAnalyticsMetadataAttribute(rest)}
nativeControl={nativeControlProps => (
<input
Expand All @@ -73,10 +70,7 @@ export default React.forwardRef(function RadioButton(
)}
onClick={() => {
radioButtonRef.current?.focus();
if (checked) {
return;
}
fireNonCancelableEvent(onChange, { value });
fireNonCancelableEvent(onChange, { checked: !checked });
}}
styledControl={
<svg viewBox="0 0 100 100" focusable="false" aria-hidden="true">
Expand Down
143 changes: 143 additions & 0 deletions src/internal/components/radio-button/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';

import { BaseComponentProps } from '../../base-component';
import { NonCancelableEventHandler } from '../../events';
/**
* @awsuiSystem core
*/
import { NativeAttributes } from '../../utils/with-native-attributes';

export interface RadioButtonProps extends BaseComponentProps {
/**
* Specifies if the component is selected.
*/
checked: boolean;

/**
* Specifies the ID of the native form element. You can use it to relate
* a label element's `for` attribute to this control.
*/
controlId?: string;

/**
* Name of the group that the radio button belongs to.
*/
name: string;

/**
* Description that appears below the label.
*/
description?: React.ReactNode;

/**
* Specifies if the control is disabled, which prevents the
* user from modifying the value and prevents the value from
* being included in a form submission. A disabled control can't
* receive focus.
*/
disabled?: boolean;

/**
* The control's label that's displayed next to the radio button. A state change occurs when a user clicks on it.
* @displayname label
*/
children?: React.ReactNode;

/**
* Attributes to add to the native `input` element.
* Some attributes will be automatically combined with internal attribute values:
* - `className` will be appended.
* - Event handlers will be chained, unless the default is prevented.
*
* We do not support using this attribute to apply custom styling.
*
* @awsuiSystem core
*/
nativeInputAttributes?: NativeAttributes<React.InputHTMLAttributes<HTMLInputElement>>;

/**
* Called when the user changes the component state. The event `detail` contains the current value for the `checked` property.
*/
onChange?: NonCancelableEventHandler<RadioButtonProps.ChangeDetail>;

/**
* Specifies if the radio button is read-only, which prevents the
* user from modifying the value, but does not prevent the value from
* being included in a form submission. A read-only control is still focusable.
*
* This property should be set for either all or none of the radio buttons in a group.
*/
readOnly?: boolean;

/**
* @awsuiSystem core
*/
style?: RadioButtonProps.Style;

/**
* Sets the value attribute to the native control.
* If using native form submission, this value is sent to the server if the radio button is checked.
* It is never shown to the user by their user agent.
* For more details, see the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/radio#value).
*/
value?: string;
}

export namespace RadioButtonProps {
export interface ChangeDetail {
checked: boolean;
}

export interface Ref {
/**
* Sets input focus onto the UI control.
*/
focus(): void;
}

export interface Style {
input?: {
fill?: {
checked?: string;
default?: string;
disabled?: string;
readOnly?: string;
};
stroke?: {
default?: string;
disabled?: string;
readOnly?: string;
};
circle?: {
fill?: {
checked?: string;
disabled?: string;
readOnly?: string;
};
};
focusRing?: {
borderColor?: string;
borderRadius?: string;
borderWidth?: string;
};
};
label?: {
color?: {
checked?: string;
default?: string;
disabled?: string;
readOnly?: string;
};
};
description?: {
color?: {
checked?: string;
default?: string;
disabled?: string;
readOnly?: string;
};
};
}
}
57 changes: 57 additions & 0 deletions src/internal/components/radio-button/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

@use '../../styles' as styles;
@use '../../styles/tokens' as awsui;
@use '../../styles/foundation' as foundation;
@use '../../generated/custom-css-properties/index.scss' as custom-props;

$radio-size: awsui.$size-control;

.radio-control {
@include styles.make-control-size($radio-size);
}

.outline {
#{custom-props.$styleFocusRingBoxShadow}: 0 0 0
var(#{custom-props.$styleFocusRingBorderWidth}, foundation.$box-shadow-focused-width)
var(#{custom-props.$styleFocusRingBorderColor}, awsui.$color-border-item-focused);

@include styles.focus-highlight(
$gutter: 2px,
$border-radius: var(#{custom-props.$styleFocusRingBorderRadius}, awsui.$border-radius-control-circular-focus-ring),
$box-shadow: var(#{custom-props.$styleFocusRingBoxShadow})
);
}

.styled-circle-border {
stroke: awsui.$color-border-control-default;
fill: awsui.$color-background-control-default;
&.styled-circle-disabled,
&.styled-circle-readonly {
fill: awsui.$color-background-control-disabled;
stroke: awsui.$color-background-control-disabled;
}
}

.styled-circle-fill {
stroke: awsui.$color-background-control-checked;
fill: awsui.$color-foreground-control-default;
opacity: 0;
@include styles.with-motion {
transition: opacity awsui.$motion-duration-transition-quick awsui.$motion-easing-transition-quick;
}
&.styled-circle-checked {
opacity: 1;
}
&.styled-circle-disabled {
fill: awsui.$color-foreground-control-disabled;
stroke: awsui.$color-background-control-disabled;
}
&.styled-circle-readonly {
fill: awsui.$color-foreground-control-read-only;
stroke: awsui.$color-background-control-disabled;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

.root {
/*used in test-utils*/
}
8 changes: 4 additions & 4 deletions src/radio-group/__integ__/radio-group.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import useBrowser from '@cloudscape-design/browser-test-tools/use-browser';

import createWrapper, { RadioGroupWrapper } from '../../../lib/components/test-utils/selectors';

import styles from '../../../lib/components/radio-group/styles.selectors.js';
import radioButtonStyles from '../../../lib/components/internal/components/radio-button/styles.selectors.js';

const radioGroupWrapper = createWrapper().findRadioGroup('#simple');

Expand Down Expand Up @@ -75,8 +75,8 @@ test(

await page.click('[data-testid="1"]');
await page.keys('Tab');
await expect((await browser.$(`.${styles.outline}`).getCSSProperty('box-shadow', '::before')).value).toBe(
'rgb(4,125,149)0px0px0px1px'
);
await expect(
(await browser.$(`.${radioButtonStyles.outline}`).getCSSProperty('box-shadow', '::before')).value
).toBe('rgb(4,125,149)0px0px0px1px');
})
);
6 changes: 3 additions & 3 deletions src/radio-group/__tests__/radio-group.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import RadioButtonWrapper from '../../../lib/components/test-utils/dom/radio-gro
import customCssProps from '../../internal/generated/custom-css-properties';

import abstractSwitchStyles from '../../../lib/components/internal/components/abstract-switch/styles.css.js';
import styles from '../../../lib/components/radio-group/styles.selectors.js';
import radioButtonStyles from '../../../lib/components/internal/components/radio-button/styles.selectors.js';

const defaultItems: RadioGroupProps.RadioButtonDefinition[] = [
{ value: 'val1', label: 'Option one' },
Expand Down Expand Up @@ -434,8 +434,8 @@ test('all style api properties', function () {
/>
);

const outerCircle = wrapper.findByClassName(styles['styled-circle-border'])!.getElement();
const innerCircle = wrapper.findByClassName(styles['styled-circle-fill'])!.getElement();
const outerCircle = wrapper.findByClassName(radioButtonStyles['styled-circle-border'])!.getElement();
const innerCircle = wrapper.findByClassName(radioButtonStyles['styled-circle-fill'])!.getElement();
const label = wrapper.findByClassName(abstractSwitchStyles.label)!.getElement();
const description = wrapper.findByClassName(abstractSwitchStyles.description)!.getElement();
const control = wrapper.findByClassName(abstractSwitchStyles.control)!.getElement();
Expand Down
Loading
Loading