Skip to content

TagGroup: add default empty state and support for custom empty state#4358

Merged
reidbarber merged 27 commits into
mainfrom
taggroup-empty-state
May 8, 2023
Merged

TagGroup: add default empty state and support for custom empty state#4358
reidbarber merged 27 commits into
mainfrom
taggroup-empty-state

Conversation

@reidbarber
Copy link
Copy Markdown
Member

Adds a default empty state of 'None', and support for a custom empty state via renderEmptyState.

If the empty state contains no focusable children, then the group itself should be focusable.

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

Test new stories at TagGroup -> Empty state and Custom empty state

🧢 Your Project:

@rspbot
Copy link
Copy Markdown

rspbot commented Apr 10, 2023

Comment thread packages/@react-spectrum/tag/src/TagGroup.tsx Outdated
Comment thread packages/@adobe/spectrum-css-temp/components/tags/index.css
@rspbot
Copy link
Copy Markdown

rspbot commented Apr 12, 2023

snowystinger
snowystinger previously approved these changes Apr 13, 2023
@rspbot
Copy link
Copy Markdown

rspbot commented Apr 13, 2023

@rspbot
Copy link
Copy Markdown

rspbot commented Apr 14, 2023

"hideButtonLabel": "Show less",
"actions": "Actions"
"actions": "Actions",
"noTags": "None"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran chromatic and it broke cuz it doesn't have ar-AE translations, wanna put a dummy string for noTags for ar-AE for now?

onAction,
labelPosition
labelPosition,
renderEmptyState = () => <div>{stringFormatter.format('noTags')}</div>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make this div the same height as a Tag? Right now the height of the container will shrink when the final tag is removed and replaced with this div.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a min-height? To handle the case of a user having a longer string that wraps as their message, or putting something in there that takes up more height.

{item.rendered}
</Tag>
))}
{isEmpty && renderEmptyState()}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed that focus is being lost to the body when all the tags are removed, is that going be handled in this PR or a separate one?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should now go to the container when the last tag is removed.

}
)
}>
<FocusRing focusRingClass={classNames(styles, 'focus-ring')}>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure the FocusRing makes sense when ShowAll hides all items. The hiding all items in this story is an existing bug. Would this go away if we fix that bug and always show one tag?

@rspbot
Copy link
Copy Markdown

rspbot commented May 5, 2023

Comment thread packages/@react-aria/tag/src/useTag.ts Outdated
if (!lastTag.current) {
container.focus();
}
}, 50);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why 50ms out of curiosity? why not immediately? I assume it's because the tag container doesn't get a tabIndex of -1 until the state change has taken effect? we could instead use a useEffect and a ref, the ref tracks previous count of tags and if the tag group has focus, if the count of tags goes from > 0 to === 0, then we focus the container

does focus get set to the container if we mouse click to remove the last tag already?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention was to wait and check that the list tag actually got removed from the DOM. Mostly in order to avoid the case that it wasn't removed immediately (like a confirmation dialog before it actually gets removed). I think your suggestion is much better though, so I just changed it to that.

@rspbot
Copy link
Copy Markdown

rspbot commented May 8, 2023

snowystinger
snowystinger previously approved these changes May 8, 2023
Copy link
Copy Markdown
Member

@snowystinger snowystinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, tested in chrome/safari/ff

@rspbot
Copy link
Copy Markdown

rspbot commented May 8, 2023

Comment on lines +63 to +68
let prevCount = useRef(state.collection.size);
useEffect(() => {
if (prevCount.current > 0 && state.collection.size === 0 && isFocusWithin) {
ref.current.focus();
}
}, [state.collection.size, isFocusWithin, ref]);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we update prevCount.current here at the end of the useEffect? Thinking about if the TagGroup goes from empty -> gets content -> becomes empty again (wouldn't focus the ref.current since prevCount never gets updated from initial render right?)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eagle eyes, good catch, yes, this should always be updated at the end of the useEffect body regardless of anything else

@rspbot
Copy link
Copy Markdown

rspbot commented May 8, 2023

@rspbot
Copy link
Copy Markdown

rspbot commented May 8, 2023

@rspbot
Copy link
Copy Markdown

rspbot commented May 8, 2023

@rspbot
Copy link
Copy Markdown

rspbot commented May 8, 2023

## API Changes

unknown top level export { type: 'identifier', name: 'Column' }
unknown top level export { type: 'identifier', name: 'Column' }
unknown type { type: 'link' }
unknown type { type: 'link' }
unknown type { type: 'link' }
unknown type { type: 'link' }
unknown type { type: 'link' }
unknown type { type: 'link' }

@react-spectrum/tag

TagGroup

 TagGroup<T extends {}> {
   actionLabel?: string
   onAction?: () => void
+  renderEmptyState?: () => JSX.Element
 }

SpectrumTagGroupProps

 SpectrumTagGroupProps<T> {
   actionLabel?: string
   onAction?: () => void
+  renderEmptyState?: () => JSX.Element
 }

LFDanLu

This comment was marked as duplicate.

Copy link
Copy Markdown
Member

@LFDanLu LFDanLu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants