Skip to content
Draft
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
45 changes: 45 additions & 0 deletions pages/token-group/toggleable.page.tsx
Original file line number Diff line number Diff line change
@@ -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<string>());

return (
<>
<h1>Toggleable tokens</h1>
<SpaceBetween size="s" direction="horizontal">
{items.map((token, index) => (
<Token
key={index}
variant="inline-toggle"
label={token.label}
pressed={pressedTokens.has(token.id!)}
onChange={event => {
setPressedTokens(prev => {
const next = new Set(prev);
if (event.detail.pressed) {
next.add(token.id!);
} else {
next.delete(token.id!);
}
return next;
});
}}
/>
))}
</SpaceBetween>
</>
);
}
11 changes: 11 additions & 0 deletions pages/token/simple.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -34,6 +35,16 @@ export default function GenericTokenPage() {
return (
<Box padding="xl">
<h1>Standalone token</h1>
<h2>Toggle</h2>
<SpaceBetween size="l" direction="vertical">
<Token
data-testid="basic-inline-token"
variant="inline-toggle"
label="Toggle inline token"
pressed={pressed}
onChange={event => setPressed(event.detail.pressed)}
/>
</SpaceBetween>
<h2>Inline</h2>
<SpaceBetween size="l" direction="vertical">
<Token data-testid="basic-inline-token" variant="inline" label="Inline token" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -29820,6 +29843,7 @@ Defaults to \`normal\` if not specified.",
"values": [
"inline",
"normal",
"inline-toggle",
],
},
"name": "variant",
Expand Down
13 changes: 12 additions & 1 deletion src/token/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<TokenProps.ChangeDetail>;
}

export namespace TokenProps {
export type Variant = 'normal' | 'inline';
export type Variant = 'normal' | 'inline' | 'inline-toggle';
export interface ChangeDetail {
pressed: boolean;
}
}
25 changes: 23 additions & 2 deletions src/token/internal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -39,6 +40,8 @@ function InternalToken({
dismissLabel,
onDismiss,
tooltipContent,
pressed,
onChange,

// Internal
role,
Expand All @@ -53,7 +56,7 @@ function InternalToken({
const labelRef = useRef<HTMLSpanElement>(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 = () => {
Expand Down Expand Up @@ -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 (
<SpanOrDivTag
{...baseProps}
Expand All @@ -109,6 +119,7 @@ function InternalToken({
legacyTestingStyles.token,
testUtilStyles.root,
!isInline ? styles['token-normal'] : styles['token-inline'],
styles[`token-${variant}`],
analyticsSelectors.token,
baseProps.className
)}
Expand All @@ -129,14 +140,24 @@ function InternalToken({
setShowTooltip(false);
}}
tabIndex={!!tooltipContent && isInline && isEllipsisActive ? 0 : undefined}
onClick={event => {
if (variant !== 'inline-toggle') {
return;
}
event.preventDefault();

fireNonCancelableEvent(onChange, { pressed: !pressed });
}}
{...toggleProps}
>
<SpanOrDivTag
className={clsx(
!isInline ? styles['token-box'] : styles['token-box-inline'],
disabled && styles['token-box-disabled'],
readOnly && styles['token-box-readonly'],
!isInline && !onDismiss && styles['token-box-without-dismiss'],
disableInnerPadding && styles['disable-padding']
disableInnerPadding && styles['disable-padding'],
pressed && styles['token-pressed']
)}
>
<Option
Expand Down
10 changes: 10 additions & 0 deletions src/token/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
}
}

.token-inline-toggle {
cursor: pointer;
}

.token-option-inline {
max-block-size: constants.$token-max-height-inline;
}
Expand All @@ -88,6 +92,12 @@

.token-box-inline {
@include mixins.token-box-inline-styles();

&.token-pressed {
background: awsui.$color-background-toggle-button-normal-pressed;
border-color: awsui.$color-border-toggle-button-normal-pressed;
color: awsui.$color-text-toggle-button-normal-pressed;
}
}

.disable-padding {
Expand Down
Loading