Interaction tag
View .mdA tag is a representation of a value that someone has picked, like recipients for an email or categories on a planner task.
Default
Loading interactive example. Full source is available below.
View example source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { InteractionTagProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { InteractionTag } from "@coreai-ds/react-components";
import { InteractionTagPrimary } from "@coreai-ds/react-components";
import * as React from 'react';
export const Default = (props: Partial<InteractionTagProps>): JSXElement => (<InteractionTag {...props}>
<InteractionTagPrimary>Primary text</InteractionTagPrimary>
</InteractionTag>);
export default Default;
Resources
Behavior and states
- Interaction tags can include one primary action in addition to dismissal.
- The primary action should directly relate to the represented object.
- The secondary action is always dismiss.
- Interaction states are always enabled because interaction tags always have actions.
- Tags create a visual pattern that lets people know they can change or remove the selection.
- To show system-generated data that people can’t change, use a badge instead.
API referenceGenerated CoreAI prop and type summaries
API
Generated CoreAI prop summaries, not replacement type declarations. Most native DOM/event and ARIA props are omitted. ? means optional; JSDoc shows defaults, deprecations, and conditional requirements. Defaults assume the standard CoreAI provider.
InteractionTag API
InteractionTag component - a visual representation of an attribute with primary and secondary actions.
Can have InteractionTagPrimary and InteractionTagSecondary components as its children.
interface InteractionTagProps {
/**
* An InteractionTag can have filled, outlined or brand experience.
* @default 'filled'
*/
appearance?: "brand" | "filled" | "outline";
as?: "div";
children?: any;
className?: string;
/**
* An InteractionTag can show that it cannot be interacted with.
* @default false
*/
disabled?: boolean;
ref?: Ref<HTMLDivElement>;
/**
* An InteractionTag can be selected.
* Note: This prop only changes the appearance of the tag at the moment. A future PR will add the integration with TagGroup.
* @default false
*/
selected?: boolean;
/**
* An InteractionTag can have rounded or circular shape.
* @default 'round'
*/
shape?: "circular" | "rounded";
/**
* An InteractionTag has three sizes.
* @default 'medium'
*/
size?: "small" | "medium" | "extra-small";
style?: CSSProperties;
/**
* Unique value identifying the tag within a TagGroup
*/
value?: string;
}
InteractionTagPrimary API
InteractionTagPrimary component - used as the first child of the InteractionTag component.
Provides visual attributes such as media, icon, primary and secondary text, as well as the ability to attach a primary action.
interface InteractionTagPrimaryProps {
as?: "button";
children?: any;
className?: string;
/**
* Whether the `InteractionTag` component has a `Secondary` component that provides an secondary action.
* If `true`, the `InteractionTagPrimary` component will adjust its styles to accommodate the `Secondary` component.
* @default false
*/
hasSecondaryAction?: boolean;
/**
* Slot for an icon
*/
icon?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;
/**
* Slot for a visual element, usually an avatar
*/
media?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;
/**
* Main text for the InteractionTagPrimary button. Children of the root slot are automatically rendered here
*/
primaryText?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;
ref?: Ref<HTMLButtonElement>;
/**
* Secondary text that describes or complements the main text
*/
secondaryText?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;
style?: CSSProperties;
}
InteractionTagSecondary API
InteractionTagSecondary component - used as the second child of the InteractionTag component and provides the secondary action, which is dismiss by default.
interface InteractionTagSecondaryProps {
as?: "button";
children?: any;
className?: string;
ref?: Ref<HTMLButtonElement>;
style?: CSSProperties;
}
Implementation
CoreAI 0.12.0; Fluent 9.74.9.
Import components from @coreai-ds/react-components. CoreAI guidance above takes precedence over API demonstration examples.
Appearance
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { InteractionTag } from "@coreai-ds/react-components";
import { InteractionTagPrimary } from "@coreai-ds/react-components";
import { bundleIcon } from "@fluentui/react-icons";
import { CalendarMonthFilled } from "@fluentui/react-icons";
import { CalendarMonthRegular } from "@fluentui/react-icons";
import { InteractionTagSecondary } from "@coreai-ds/react-components";
import * as React from 'react';
const useContainerStyles = makeStyles({
container: {
columnGap: '10px',
display: 'flex',
},
});
const CalendarMonth = bundleIcon(CalendarMonthFilled, CalendarMonthRegular);
export const Appearance = (): JSXElement => {
const styles = useContainerStyles();
return (<div className={styles.container}>
<InteractionTag>
<InteractionTagPrimary icon={<CalendarMonth />} hasSecondaryAction>
filled
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove"/>
</InteractionTag>
<InteractionTag appearance="outline">
<InteractionTagPrimary icon={<CalendarMonth />} hasSecondaryAction>
outline
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove"/>
</InteractionTag>
<InteractionTag appearance="brand">
<InteractionTagPrimary icon={<CalendarMonth />} hasSecondaryAction>
brand
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove"/>
</InteractionTag>
</div>);
};
export default Appearance;
Disabled
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { InteractionTag } from "@coreai-ds/react-components";
import { InteractionTagPrimary } from "@coreai-ds/react-components";
import { CalendarMonthRegular } from "@fluentui/react-icons";
import { InteractionTagSecondary } from "@coreai-ds/react-components";
import * as React from 'react';
const useContainerStyles = makeStyles({
container: {
columnGap: '10px',
display: 'flex',
},
});
export const Disabled = (): JSXElement => {
const styles = useContainerStyles();
return (<div className={styles.container}>
<InteractionTag disabled>
<InteractionTagPrimary secondaryText="appearance=filled" icon={<CalendarMonthRegular />} hasSecondaryAction>
Disabled
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove"/>
</InteractionTag>
<InteractionTag disabled appearance="outline">
<InteractionTagPrimary secondaryText="appearance=outline" icon={<CalendarMonthRegular />} hasSecondaryAction>
Disabled
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove"/>
</InteractionTag>
<InteractionTag disabled appearance="brand">
<InteractionTagPrimary secondaryText="appearance=brand" icon={<CalendarMonthRegular />} hasSecondaryAction>
Disabled
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove"/>
</InteractionTag>
</div>);
};
export default Disabled;
Dismiss
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import * as React from 'react';
import type { TagGroupProps } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { TagGroup } from "@coreai-ds/react-components";
import { InteractionTag } from "@coreai-ds/react-components";
import { InteractionTagPrimary } from "@coreai-ds/react-components";
import { InteractionTagSecondary } from "@coreai-ds/react-components";
import { Button } from "@coreai-ds/react-components";
const initialTags = [
{ value: '1', children: 'Tag 1' },
{ value: '2', children: 'Tag 2' },
{ value: '3', children: 'Tag 3' },
];
/**
* focus management for the reset button
*/
const useResetExample = (visibleTagsLength: number) => {
const resetButtonRef = React.useRef<HTMLButtonElement>(null);
const firstTagRef = React.useRef<HTMLButtonElement>(null);
const prevVisibleTagsLengthRef = React.useRef<number>(visibleTagsLength);
React.useEffect(() => {
if (visibleTagsLength === 0) {
resetButtonRef.current?.focus();
}
else if (prevVisibleTagsLengthRef.current === 0) {
firstTagRef.current?.focus();
}
prevVisibleTagsLengthRef.current = visibleTagsLength;
}, [visibleTagsLength]);
return { firstTagRef, resetButtonRef };
};
const useStyles = makeStyles({
container: {
display: 'flex',
flexDirection: 'column',
rowGap: '10px',
},
tagGroup: {
flexWrap: 'wrap',
},
resetButton: {
width: 'fit-content',
},
});
export const Dismiss = (): JSXElement => {
const [visibleTags, setVisibleTags] = React.useState(initialTags);
const removeItem: TagGroupProps['onDismiss'] = (_e, { value }) => {
setVisibleTags([...visibleTags].filter(tag => tag.value !== value));
};
const resetItems = () => setVisibleTags(initialTags);
const { firstTagRef, resetButtonRef } = useResetExample(visibleTags.length);
const styles = useStyles();
return (<div className={styles.container}>
{visibleTags.length !== 0 && (<TagGroup className={styles.tagGroup} onDismiss={removeItem} aria-label="Dismiss example">
{visibleTags.map((tag, index) => (<InteractionTag value={tag.value} key={tag.value}>
<InteractionTagPrimary hasSecondaryAction ref={index === 0 ? firstTagRef : null}>
{tag.children}
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove"/>
</InteractionTag>))}
</TagGroup>)}
<Button onClick={resetItems} ref={resetButtonRef} disabled={visibleTags.length !== 0} className={styles.resetButton} size="small">
Reset the example
</Button>
</div>);
};
export default Dismiss;
Has Primary Action
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { InteractionTag } from "@coreai-ds/react-components";
import { Popover } from "@coreai-ds/react-components";
import { PopoverTrigger } from "@coreai-ds/react-components";
import { InteractionTagPrimary } from "@coreai-ds/react-components";
import { PopoverSurface } from "@coreai-ds/react-components";
import { Link } from "@coreai-ds/react-components";
import { Tooltip } from "@coreai-ds/react-components";
import { InteractionTagSecondary } from "@coreai-ds/react-components";
import * as React from 'react';
const useStyles = makeStyles({
popover: {
width: '360px',
maxWidth: '100%',
height: 'fit-content',
},
});
export const HasPrimaryAction = (): JSXElement => {
const styles = useStyles();
return (<InteractionTag>
<Popover trapFocus>
<PopoverTrigger>
<InteractionTagPrimary hasSecondaryAction>Golden retriever</InteractionTagPrimary>
</PopoverTrigger>
<PopoverSurface className={styles.popover}>
<Link href="https://en.wikipedia.org/wiki/Golden_Retriever">Find out more on wiki</Link>
<ul>
<li>Size: Medium to large-sized dog breed. </li>
<li>Coat: Luxurious double coat with a dense, water-repellent outer layer and a soft, dense undercoat.</li>
<li>Color: Typically a luscious golden or cream color, with variations in shade.</li>
<li>Build: Sturdy and well-proportioned body with a friendly and intelligent expression.</li>
</ul>
</PopoverSurface>
</Popover>
<Tooltip content="dismiss" relationship="label">
<InteractionTagSecondary />
</Tooltip>
</InteractionTag>);
};
export default HasPrimaryAction;
Icon
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { InteractionTag } from "@coreai-ds/react-components";
import { InteractionTagPrimary } from "@coreai-ds/react-components";
import { CalendarMonthRegular } from "@fluentui/react-icons";
import * as React from 'react';
export const Icon = (): JSXElement => (<InteractionTag>
<InteractionTagPrimary icon={<CalendarMonthRegular />}>Primary text</InteractionTagPrimary>
</InteractionTag>);
export default Icon;
Media
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { InteractionTag } from "@coreai-ds/react-components";
import { InteractionTagPrimary } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
import * as React from 'react';
export const Media = (): JSXElement => (<InteractionTag>
<InteractionTagPrimary media={<Avatar name="Katri Athokas" badge={{ status: 'busy' }}/>}>
Primary text
</InteractionTagPrimary>
</InteractionTag>);
export default Media;
SecondaryText
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { InteractionTag } from "@coreai-ds/react-components";
import { InteractionTagPrimary } from "@coreai-ds/react-components";
import * as React from 'react';
export const SecondaryText = (): JSXElement => (<InteractionTag>
<InteractionTagPrimary secondaryText="Secondary text">Primary text</InteractionTagPrimary>
</InteractionTag>);
export default SecondaryText;
Selected
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { makeResetStyles } from "@coreai-ds/react-components";
import { InteractionTag } from "@coreai-ds/react-components";
import { InteractionTagPrimary } from "@coreai-ds/react-components";
import { bundleIcon } from "@fluentui/react-icons";
import { CalendarMonthFilled } from "@fluentui/react-icons";
import { CalendarMonthRegular } from "@fluentui/react-icons";
import { InteractionTagSecondary } from "@coreai-ds/react-components";
import * as React from 'react';
const useContainerStyles = makeResetStyles({
columnGap: '10px',
display: 'flex',
});
const CalendarMonth = bundleIcon(CalendarMonthFilled, CalendarMonthRegular);
export const Selected = (): JSXElement => {
const containerStyles = useContainerStyles();
return (<div className={containerStyles}>
<InteractionTag selected>
<InteractionTagPrimary secondaryText="appearance=filled" icon={<CalendarMonth />} hasSecondaryAction>
Selected
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove"/>
</InteractionTag>
<InteractionTag selected appearance="outline">
<InteractionTagPrimary secondaryText="appearance=outline" icon={<CalendarMonth />} hasSecondaryAction>
Selected
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove"/>
</InteractionTag>
<InteractionTag selected appearance="brand">
<InteractionTagPrimary secondaryText="appearance=brand" icon={<CalendarMonth />} hasSecondaryAction>
Selected
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove"/>
</InteractionTag>
</div>);
};
export default Selected;
Shape
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { InteractionTag } from "@coreai-ds/react-components";
import { InteractionTagPrimary } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
import { CalendarMonthRegular } from "@fluentui/react-icons";
import { InteractionTagSecondary } from "@coreai-ds/react-components";
import * as React from 'react';
const useContainerStyles = makeStyles({
innerWrapper: {
alignItems: 'start',
columnGap: '10px',
display: 'flex',
flexWrap: 'wrap',
},
outerWrapper: {
display: 'flex',
flexDirection: 'column',
rowGap: '10px',
},
});
export const Shape = (): JSXElement => {
const containerStyles = useContainerStyles();
return (<div className={containerStyles.outerWrapper}>
<div className={containerStyles.innerWrapper}>
<InteractionTag>
<InteractionTagPrimary media={<Avatar name="Katri Athokas" badge={{ status: 'busy' }}/>}>
Rounded
</InteractionTagPrimary>
</InteractionTag>
<InteractionTag shape="circular">
<InteractionTagPrimary media={<Avatar name="Katri Athokas" badge={{ status: 'busy' }}/>}>
Circular
</InteractionTagPrimary>
</InteractionTag>
</div>
<div className={containerStyles.innerWrapper}>
<InteractionTag>
<InteractionTagPrimary icon={<CalendarMonthRegular />} secondaryText="Secondary text" hasSecondaryAction>
Rounded
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove"/>
</InteractionTag>
<InteractionTag shape="circular">
<InteractionTagPrimary icon={<CalendarMonthRegular />} secondaryText="Secondary text" hasSecondaryAction>
Circular
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove"/>
</InteractionTag>
</div>
</div>);
};
export default Shape;
Size
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { InteractionTag } from "@coreai-ds/react-components";
import { InteractionTagPrimary } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
import { InteractionTagSecondary } from "@coreai-ds/react-components";
import { CalendarMonthRegular } from "@fluentui/react-icons";
import * as React from 'react';
const useContainerStyles = makeStyles({
innerWrapper: {
alignItems: 'start',
columnGap: '10px',
display: 'flex',
flexWrap: 'wrap',
},
outerWrapper: {
display: 'flex',
flexDirection: 'column',
rowGap: '10px',
},
});
export const Size = (): JSXElement => {
const styles = useContainerStyles();
return (<div className={styles.outerWrapper}>
{/* medium */}
<div className={styles.innerWrapper}>
<InteractionTag>
<InteractionTagPrimary>Medium</InteractionTagPrimary>
</InteractionTag>
<InteractionTag>
<InteractionTagPrimary media={<Avatar name="Katri Athokas" badge={{ status: 'busy' }}/>} hasSecondaryAction>
Medium dismissible
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="dismiss"/>
</InteractionTag>
<InteractionTag shape="circular">
<InteractionTagPrimary icon={<CalendarMonthRegular />}>Medium circular</InteractionTagPrimary>
</InteractionTag>
</div>
{/* small */}
<div className={styles.innerWrapper}>
<InteractionTag size="small">
<InteractionTagPrimary>Small</InteractionTagPrimary>
</InteractionTag>
<InteractionTag size="small">
<InteractionTagPrimary media={<Avatar name="Katri Athokas" badge={{ status: 'busy' }}/>} hasSecondaryAction>
Small dismissible
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="dismiss"/>
</InteractionTag>
<InteractionTag size="small" shape="circular">
<InteractionTagPrimary icon={<CalendarMonthRegular />}>Small circular</InteractionTagPrimary>
</InteractionTag>
</div>
{/* extra-small */}
<div className={styles.innerWrapper}>
<InteractionTag size="extra-small">
<InteractionTagPrimary>Extra small</InteractionTagPrimary>
</InteractionTag>
<InteractionTag size="extra-small">
<InteractionTagPrimary media={<Avatar name="Katri Athokas" badge={{ status: 'busy' }}/>} hasSecondaryAction>
Extra small dismissible
</InteractionTagPrimary>
<InteractionTagSecondary aria-label="dismiss"/>
</InteractionTag>
<InteractionTag size="extra-small" shape="circular">
<InteractionTagPrimary icon={<CalendarMonthRegular />}>Extra small circular</InteractionTagPrimary>
</InteractionTag>
</div>
</div>);
};
export default Size;