;
+
+export const Empty: Story = {
+ args: {
+ allTags: [],
+ selectedTags: [],
+ },
+};
+
+export const Default: Story = {
+ args: {
+ allTags: ['tag1', 'tag2', 'tag3'],
+ selectedTags: ['tag1', 'tag3'],
+ },
+};
+
+export const Exclude: Story = {
+ args: {
+ ...Default.args,
+ exclude: true,
+ },
+};
+
+export const BuiltInTags: Story = {
+ args: {
+ allTags: [...Default.args.allTags, 'dev', 'autodocs'],
+ selectedTags: ['tag1', 'tag3'],
+ },
+};
+
+export const BuiltInTagsSelected: Story = {
+ args: {
+ ...BuiltInTags.args,
+ selectedTags: ['tag1', 'tag3', 'autodocs'],
+ },
+};
diff --git a/code/ui/manager/src/components/sidebar/TagsFilterPanel.tsx b/code/ui/manager/src/components/sidebar/TagsFilterPanel.tsx
new file mode 100644
index 000000000000..5ab710d928e0
--- /dev/null
+++ b/code/ui/manager/src/components/sidebar/TagsFilterPanel.tsx
@@ -0,0 +1,135 @@
+import type { ChangeEvent } from 'react';
+import React, { useState } from 'react';
+import { transparentize } from 'polished';
+import { styled } from '@storybook/theming';
+import { CollapseIcon } from './components/CollapseIcon';
+
+const BUILT_IN_TAGS = new Set(['dev', 'autodocs', 'test', 'attached-mdx', 'unattached-mdx']);
+
+const CollapseButton = styled.button(({ theme }) => ({
+ all: 'unset',
+ display: 'flex',
+ padding: '0px 8px',
+ borderRadius: 4,
+ transition: 'color 150ms, box-shadow 150ms',
+ gap: 6,
+ alignItems: 'center',
+ cursor: 'pointer',
+ height: 28,
+
+ '&:hover, &:focus': {
+ outline: 'none',
+ background: transparentize(0.93, theme.color.secondary),
+ },
+}));
+
+const Text = styled.span({
+ '[aria-readonly=true] &': {
+ opacity: 0.5,
+ },
+});
+
+const Label = styled.label({
+ lineHeight: '20px',
+ alignItems: 'center',
+ marginBottom: 8,
+
+ '&:last-child': {
+ marginBottom: 0,
+ },
+
+ input: {
+ margin: 0,
+ marginRight: 6,
+ },
+});
+
+interface TagsFilterPanelProps {
+ allTags: Tag[];
+ selectedTags: Tag[];
+ exclude: boolean;
+ toggleTag: (tag: Tag) => void;
+ toggleExclude: () => void;
+}
+
+interface TagsListProps {
+ tags: Tag[];
+ selectedTags: Tag[];
+ toggleTag: (tag: Tag) => void;
+}
+
+const TagsList = ({ tags, selectedTags, toggleTag }: TagsListProps) => {
+ return tags.map((tag) => {
+ const checked = selectedTags.includes(tag);
+ const id = `tag-${tag}`;
+ return (
+
+ );
+ });
+};
+
+const Wrapper = styled.div({
+ label: {
+ display: 'flex',
+ },
+});
+
+export const TagsFilterPanel = ({
+ allTags,
+ selectedTags,
+ exclude,
+ toggleTag,
+ toggleExclude,
+}: TagsFilterPanelProps) => {
+ const userTags = allTags.filter((tag) => !BUILT_IN_TAGS.has(tag)).toSorted();
+ const builtInTags = allTags.filter((tag) => BUILT_IN_TAGS.has(tag)).toSorted();
+ const [builtinsExpanded, setBuiltinsExpanded] = useState(
+ selectedTags.some((tag) => BUILT_IN_TAGS.has(tag))
+ );
+
+ return (
+
+ {userTags.length === 0 ? (
+ 'No tags defined'
+ ) : (
+
+ Tags {exclude ? 'does not contain' : 'contains'}
+
+
+ )}
+ {builtInTags.length > 0 && (
+ <>
+ {
+ event.preventDefault();
+ setBuiltinsExpanded(!builtinsExpanded);
+ }}
+ aria-expanded={builtinsExpanded}
+ >
+
+ Built-in tags
+
+ {builtinsExpanded ? (
+
+
+
+ ) : null}
+ >
+ )}
+
+ );
+};