Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(react-tags-preview): use InteractionTag for TagGroup's stories #29024

Merged
merged 4 commits into from
Aug 31, 2023
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 @@ -3,5 +3,8 @@
### Do

- A Picker component is planned for displaying multiple selected values using `TagGroup` with `Combobox`, and will be the recommended approach once it's available. But for now, when using `TagGroup` with `Combobox`:

- Set the `listbox` role for `TagGroup` and the `option` role for each `Tag`.
- If using `InteractionTag`, set the `option` role for the content and make the dismiss button not focusable. When content is focused, Enter/Space should invoke the primary action, and Backspace/Delete remove the tag.

- When using `TagGroup` with non-actionable `Tag` (i.e. `Tag` without dismiss icon), set the `list` role for `TagGroup` and the `listitem` role for each `Tag`.
Original file line number Diff line number Diff line change
@@ -1,10 +1,45 @@
import * as React from 'react';
import { TagGroup, Tag, TagGroupProps } from '@fluentui/react-tags-preview';
import { TagGroup, InteractionTag, InteractionTagPrimary, Tag } from '@fluentui/react-tags-preview';
import { makeStyles } from '@fluentui/react-components';

export const Default = (props: Partial<TagGroupProps>) => (
<TagGroup {...props} aria-label="Simple tag group example">
<Tag>Tag 1</Tag>
<Tag>Tag 2</Tag>
<Tag>Tag 3</Tag>
const WithTags = () => (
<TagGroup aria-label="Simple tag group with Tag" role="list">
<Tag role="listitem">Tag 1</Tag>
<Tag role="listitem">Tag 2</Tag>
<Tag role="listitem">Tag 3</Tag>
</TagGroup>
);

const WithInteractionTags = () => (
<TagGroup aria-label="Simple tag group with InteractionTag">
<InteractionTag>
<InteractionTagPrimary>Tag 1</InteractionTagPrimary>
</InteractionTag>
<InteractionTag>
<InteractionTagPrimary>Tag 2</InteractionTagPrimary>
</InteractionTag>
<InteractionTag>
<InteractionTagPrimary>Tag 3</InteractionTagPrimary>
</InteractionTag>
</TagGroup>
);

const useStyles = makeStyles({
container: {
display: 'flex',
flexDirection: 'column',
rowGap: '10px',
},
});

export const Default = () => {
const styles = useStyles();
return (
<div className={styles.container}>
Example with Tag:
<WithTags />
Example with InteractionTag:
<WithInteractionTags />
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,69 @@ import {
InteractionTagPrimary,
InteractionTagSecondary,
} from '@fluentui/react-tags-preview';
import { makeStyles } from '@fluentui/react-components';

export const Dismiss = () => {
const defaultItems = [
{
value: '1',
tag: (
<Tag dismissible value="1" key="1" dismissIcon={{ 'aria-label': 'remove' }}>
Tag 1
</Tag>
),
},
{
value: '2',
tag: (
<Tag dismissible value="2" key="2" dismissIcon={{ 'aria-label': 'remove' }}>
Tag 2
</Tag>
),
},
{
value: 'foo',
tag: (
<InteractionTag value="foo" key="foo">
<InteractionTagPrimary hasSecondaryAction>Foo</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove" />
</InteractionTag>
),
},
{
value: 'bar',
tag: (
<InteractionTag value="bar" key="bar">
<InteractionTagPrimary hasSecondaryAction>Bar</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove" />
</InteractionTag>
),
},
];
const initialTags = [
{ value: '1', children: 'Tag 1' },
{ value: '2', children: 'Tag 2' },
{ value: '3', children: 'Tag 3' },
];

const DismissWithTags = () => {
const [visibleTags, setVisibleTags] = React.useState(initialTags);
const removeItem: TagGroupProps['onDismiss'] = (_e, { dismissedTagValue }) => {
setVisibleTags([...visibleTags].filter(tag => tag.value !== dismissedTagValue));
};

const [items, setItems] = React.useState(defaultItems);
return (
<TagGroup onDismiss={removeItem} aria-label="TagGroup example with dismissible Tags">
{visibleTags.map(tag => (
<Tag dismissible dismissIcon={{ 'aria-label': 'remove' }} value={tag.value} key={tag.value}>
{tag.children}
</Tag>
))}
</TagGroup>
);
};

const DismissWithInteractionTags = () => {
const [visibleTags, setVisibleTags] = React.useState(initialTags);
const removeItem: TagGroupProps['onDismiss'] = (_e, { dismissedTagValue }) => {
setItems([...items].filter(item => item.value !== dismissedTagValue));
setVisibleTags([...visibleTags].filter(tag => tag.value !== dismissedTagValue));
};

return (
<TagGroup onDismiss={removeItem} aria-label="Dismiss example">
{items.map(item => item.tag)}
<TagGroup onDismiss={removeItem} aria-label="TagGroup example with dismissible InteractionTags">
{visibleTags.map(tag => (
<InteractionTag value={tag.value} key={tag.value}>
<InteractionTagPrimary hasSecondaryAction>{tag.children}</InteractionTagPrimary>
<InteractionTagSecondary aria-label="remove" />
</InteractionTag>
))}
</TagGroup>
);
};

const useStyles = makeStyles({
container: {
display: 'flex',
flexDirection: 'column',
rowGap: '10px',
},
});

export const Dismiss = () => {
const styles = useStyles();
return (
<div className={styles.container}>
Example with Tag:
<DismissWithTags />
Example with InteractionTag:
<DismissWithInteractionTags />
</div>
);
};

Dismiss.storyName = 'Dismiss';
Dismiss.parameters = {
docs: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import * as React from 'react';
import { TagGroup, Tag, TagProps, tagClassNames } from '@fluentui/react-tags-preview';
import {
TagGroup,
Tag,
TagProps,
tagClassNames,
InteractionTag,
InteractionTagPrimary,
InteractionTagPrimaryProps,
} from '@fluentui/react-tags-preview';
import {
makeStyles,
shorthands,
Expand Down Expand Up @@ -29,7 +37,8 @@ const names = [
'Charlotte Waltson',
'Elliot Woodward',
];
const defaultItems: TagProps[] = names.map(name => ({
type DefaultItem = InteractionTagPrimaryProps & { value: string };
const defaultItems: DefaultItem[] = names.map(name => ({
value: name.replace(' ', '_'),
children: name,
media: (
Expand Down Expand Up @@ -97,18 +106,23 @@ const OverflowMenu = () => {
}

return (
<Menu>
<MenuTrigger disableButtonEnhancement>
<Tag ref={ref} aria-label={`${overflowCount} more tags`}>{`+${overflowCount}`}</Tag>
</MenuTrigger>
<MenuPopover>
<MenuList>
{defaultItems.map(item => (
<OverflowMenuItem key={item.value} tag={item} />
))}
</MenuList>
</MenuPopover>
</Menu>
<InteractionTag>
<Menu>
<MenuTrigger disableButtonEnhancement>
<InteractionTagPrimary
ref={ref}
aria-label={`${overflowCount} more tags`}
>{`+${overflowCount}`}</InteractionTagPrimary>
</MenuTrigger>
<MenuPopover>
<MenuList>
{defaultItems.map(item => (
<OverflowMenuItem key={item.value} tag={item} />
))}
</MenuList>
</MenuPopover>
</Menu>
</InteractionTag>
);
};

Expand All @@ -123,6 +137,7 @@ const useStyles = makeStyles({
minWidth: '150px',
resize: 'horizontal',
width: '100%',
boxSizing: 'border-box',
},
tagGroup: {
display: 'flex', // TagGroup is inline-flex by default, but we want it to be same width as the container
Expand All @@ -136,9 +151,11 @@ export const WithOverflow = () => {
<div className={styles.container}>
<Overflow minimumVisible={2} padding={30}>
<TagGroup className={styles.tagGroup} aria-label="Overflow example">
{defaultItems.map(item => (
<OverflowItem key={item.value} id={item.value!}>
<Tag key={item.value} {...item} />
{defaultItems.map(({ value, ...rest }) => (
<OverflowItem key={value} id={value!}>
<InteractionTag key={value}>
<InteractionTagPrimary {...rest} />
</InteractionTag>
</OverflowItem>
))}
<OverflowMenu />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import {
TagGroup,
Tag,
InteractionTag,
InteractionTagPrimary,
InteractionTagSecondary,
Expand All @@ -27,12 +26,12 @@ export const Sizes = () => {
<div key={size}>
{`${size}: `}
<TagGroup size={size} aria-label={`${size} tag group example`}>
<Tag dismissible media={<Avatar name="Katri Athokas" />}>
{size}
</Tag>
<Tag icon={<CalendarMonthRegular />} shape="circular" dismissible>
{size}
</Tag>
<InteractionTag>
<InteractionTagPrimary media={<Avatar name="Katri Athokas" />}>{size}</InteractionTagPrimary>
</InteractionTag>
<InteractionTag shape="circular">
<InteractionTagPrimary icon={<CalendarMonthRegular />}>{size}</InteractionTagPrimary>
</InteractionTag>
<InteractionTag>
<InteractionTagPrimary icon={<CalendarMonthRegular />} hasSecondaryAction>
{size}
Expand Down