diff --git a/pages/token-group/toggleable.page.tsx b/pages/token-group/toggleable.page.tsx new file mode 100644 index 0000000000..7077ba8799 --- /dev/null +++ b/pages/token-group/toggleable.page.tsx @@ -0,0 +1,45 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useState } from 'react'; + +import SpaceBetween from '~components/space-between'; +import Token, { TokenProps } from '~components/token'; + +const generateItems = (numberOfItems: number) => { + return [...new Array(numberOfItems)].map((_item, index) => ({ + id: index + '', + label: `Item ${index + 1}`, + })) as TokenProps[]; +}; + +export default function TokenGroupToggleablePage() { + const items = generateItems(12); + const [pressedTokens, setPressedTokens] = useState(new Set()); + + return ( + <> +

Toggleable tokens

+ + {items.map((token, index) => ( + { + setPressedTokens(prev => { + const next = new Set(prev); + if (event.detail.pressed) { + next.add(token.id!); + } else { + next.delete(token.id!); + } + return next; + }); + }} + /> + ))} + + + ); +} diff --git a/pages/token/simple.page.tsx b/pages/token/simple.page.tsx index 0ba52ab3e1..8e857d5a2c 100644 --- a/pages/token/simple.page.tsx +++ b/pages/token/simple.page.tsx @@ -24,6 +24,7 @@ const LONG_LABEL = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed export default function GenericTokenPage() { const [files, setFiles] = useState(range(0, 4)); + const [pressed, setPressed] = useState(false); const onDismiss = (itemIndex: number) => { const newItems = [...files]; @@ -34,6 +35,16 @@ export default function GenericTokenPage() { return (

Standalone token

+

Toggle

+ + setPressed(event.detail.pressed)} + /> +

Inline

diff --git a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap index 9e92f77915..94c1e804aa 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap @@ -29729,6 +29729,24 @@ exports[`Components definition for token matches the snapshot: token 1`] = ` { "dashCaseName": "token", "events": [ + { + "cancelable": false, + "description": "Called when the user changes their selection. +The event \`detail\` contains the current value for the \`pressed\` property.", + "detailInlineType": { + "name": "TokenProps.ChangeDetail", + "properties": [ + { + "name": "pressed", + "optional": false, + "type": "boolean", + }, + ], + "type": "object", + }, + "detailType": "TokenProps.ChangeDetail", + "name": "onChange", + }, { "cancelable": false, "description": "Called when the user clicks on the dismiss button. @@ -29788,6 +29806,11 @@ use the \`id\` attribute, consider setting it on a parent element instead.", "optional": true, "type": "string", }, + { + "name": "pressed", + "optional": true, + "type": "boolean", + }, { "description": "Specifies if the control is read-only. A read-only control is still focusable.", "name": "readOnly", @@ -29820,6 +29843,7 @@ Defaults to \`normal\` if not specified.", "values": [ "inline", "normal", + "inline-toggle", ], }, "name": "variant", diff --git a/src/token/interfaces.ts b/src/token/interfaces.ts index 47bfa0d75a..0ecf73e25f 100644 --- a/src/token/interfaces.ts +++ b/src/token/interfaces.ts @@ -69,8 +69,19 @@ export interface TokenProps extends BaseComponentProps { * Only applies to plain text labels. */ tooltipContent?: string; + + pressed?: boolean; + + /** + * Called when the user changes their selection. + * The event `detail` contains the current value for the `pressed` property. + */ + onChange?: NonCancelableEventHandler; } export namespace TokenProps { - export type Variant = 'normal' | 'inline'; + export type Variant = 'normal' | 'inline' | 'inline-toggle'; + export interface ChangeDetail { + pressed: boolean; + } } diff --git a/src/token/internal.tsx b/src/token/internal.tsx index bd16485a73..414e8be0fa 100644 --- a/src/token/internal.tsx +++ b/src/token/internal.tsx @@ -8,6 +8,7 @@ import { useResizeObserver, useUniqueId, warnOnce } from '@cloudscape-design/com import { getBaseProps } from '../internal/base-component'; import Option from '../internal/components/option'; +import { fireNonCancelableEvent } from '../internal/events'; import { InternalBaseComponentProps } from '../internal/hooks/use-base-component'; import LiveRegion from '../live-region/internal'; import Tooltip from '../tooltip/internal.js'; @@ -39,6 +40,8 @@ function InternalToken({ dismissLabel, onDismiss, tooltipContent, + pressed, + onChange, // Internal role, @@ -53,7 +56,7 @@ function InternalToken({ const labelRef = useRef(null); const [showTooltip, setShowTooltip] = useState(false); const [isEllipsisActive, setIsEllipsisActive] = useState(false); - const isInline = variant === 'inline'; + const isInline = variant === 'inline' || variant === 'inline-toggle'; const ariaLabelledbyId = useUniqueId(); const isLabelOverflowing = () => { @@ -100,6 +103,13 @@ function InternalToken({ // Use span for inline tokens (e.g. inside contentEditable) to avoid block-level elements breaking text flow. const SpanOrDivTag = isInline ? 'span' : 'div'; + const toggleProps = + variant === 'inline-toggle' + ? { + 'aria-pressed': pressed, + } + : {}; + return ( { + if (variant !== 'inline-toggle') { + return; + } + event.preventDefault(); + + fireNonCancelableEvent(onChange, { pressed: !pressed }); + }} + {...toggleProps} >