storybook 10.0.0-beta.6 → 10.0.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/_browser-chunks/{chunk-YQV3EGQ5.js → chunk-OQ6NCFPL.js} +38 -15
  2. package/dist/_browser-chunks/{chunk-O5R5CGFA.js → chunk-TMDZCWME.js} +1 -2
  3. package/dist/_node-chunks/{builder-manager-V33CQT2M.js → builder-manager-VFP7HSCF.js} +13 -13
  4. package/dist/_node-chunks/camelcase-ZLZNQMDD.js +18 -0
  5. package/dist/_node-chunks/{chunk-4TOI4VSK.js → chunk-2GCKJYK6.js} +7 -7
  6. package/dist/_node-chunks/{chunk-NKZ4UPPV.js → chunk-3NIQHNDU.js} +7 -7
  7. package/dist/_node-chunks/{chunk-VQH4ZFTS.js → chunk-3QZ7KLON.js} +8 -8
  8. package/dist/_node-chunks/{chunk-HPFXREVG.js → chunk-3XXQRL3A.js} +7 -7
  9. package/dist/_node-chunks/{chunk-IINJT47N.js → chunk-46BWYN3K.js} +7 -7
  10. package/dist/_node-chunks/{chunk-E6TLN2J2.js → chunk-4DIDWIST.js} +7 -7
  11. package/dist/_node-chunks/{chunk-55VWKF63.js → chunk-5YUTWYNI.js} +9 -9
  12. package/dist/_node-chunks/{chunk-7WH7AGOR.js → chunk-A2DKPKBY.js} +7 -7
  13. package/dist/_node-chunks/{chunk-TJIMCNYJ.js → chunk-BC23FKU4.js} +8 -8
  14. package/dist/_node-chunks/{chunk-HEBHWRWL.js → chunk-BID2X7MU.js} +7 -7
  15. package/dist/_node-chunks/{chunk-BNOZ3EKF.js → chunk-C4EOB63P.js} +7 -7
  16. package/dist/_node-chunks/{chunk-EAOPWIKA.js → chunk-DH7BCQMG.js} +8 -8
  17. package/dist/_node-chunks/{chunk-JNHUDBJL.js → chunk-FCNWWJV7.js} +7 -7
  18. package/dist/_node-chunks/{chunk-6PTV7XGR.js → chunk-GBZZSL2K.js} +264 -109
  19. package/dist/_node-chunks/{chunk-DC7OWBHB.js → chunk-GCWAGPDV.js} +12 -12
  20. package/dist/_node-chunks/{chunk-B3AMFGAL.js → chunk-I7SRMFT5.js} +7 -7
  21. package/dist/_node-chunks/{chunk-EY5PTUZL.js → chunk-IEFSRAX2.js} +10 -10
  22. package/dist/_node-chunks/{chunk-A7CUP23N.js → chunk-IGWCAXA2.js} +7 -7
  23. package/dist/_node-chunks/chunk-IYOHZPPC.js +18 -0
  24. package/dist/_node-chunks/{chunk-NUVGSFQI.js → chunk-KVOP6ASA.js} +7 -7
  25. package/dist/_node-chunks/{chunk-F3PO67U3.js → chunk-MHVF6Y35.js} +7 -7
  26. package/dist/_node-chunks/{chunk-NJXBURX7.js → chunk-MRCMZPA2.js} +7 -7
  27. package/dist/_node-chunks/chunk-RNHUADRS.js +62 -0
  28. package/dist/_node-chunks/{chunk-UYKQJMQY.js → chunk-RP3VMQPF.js} +15 -15
  29. package/dist/_node-chunks/{chunk-MLBTKECD.js → chunk-TFIGR2HH.js} +7 -7
  30. package/dist/_node-chunks/{chunk-JOXYGIZK.js → chunk-TXDIOSHI.js} +9 -9
  31. package/dist/_node-chunks/{chunk-2DMFI367.js → chunk-U6MJQ7C6.js} +7 -7
  32. package/dist/_node-chunks/{chunk-RIPA4LFD.js → chunk-XXVKM2ZR.js} +26 -23
  33. package/dist/_node-chunks/{chunk-VSC6LSCQ.js → chunk-YWU65VY4.js} +6 -6
  34. package/dist/_node-chunks/{chunk-5IEY46LQ.js → chunk-Z3PLI7S2.js} +7 -7
  35. package/dist/_node-chunks/{chunk-XZTBG2TG.js → chunk-ZM2HT2RW.js} +6 -6
  36. package/dist/_node-chunks/{chunk-7NJGTQ3W.js → chunk-ZPNXCXIT.js} +8 -8
  37. package/dist/_node-chunks/{dist-CGGAYWME.js → dist-PG4I7ZDU.js} +9 -9
  38. package/dist/_node-chunks/{globby-3IFB7BJC.js → globby-HXB6X43W.js} +9 -9
  39. package/dist/_node-chunks/{lib-IT6OBSID.js → lib-4JJTZC7T.js} +7 -7
  40. package/dist/_node-chunks/{mdx-N42X6CFJ-WM36SSZ6.js → mdx-N42X6CFJ-3JBJ7PQF.js} +8 -8
  41. package/dist/_node-chunks/{p-limit-3V5XIKA7.js → p-limit-WBBNX4KI.js} +7 -7
  42. package/dist/_node-chunks/{plugin-GJUBUKCT.js → plugin-SEQ2OPRK.js} +10 -10
  43. package/dist/_node-chunks/{plugin-3YREMMJJ.js → plugin-SG6I7RFC.js} +10 -10
  44. package/dist/_node-chunks/{webpack-inject-mocker-runtime-plugin-DCJQFJQ5.js → webpack-inject-mocker-runtime-plugin-5O25VYCQ.js} +10 -10
  45. package/dist/_node-chunks/{webpack-mock-plugin-XJNFAHBI.js → webpack-mock-plugin-35I32SAF.js} +9 -9
  46. package/dist/babel/index.js +11 -11
  47. package/dist/bin/core.js +11 -11
  48. package/dist/bin/dispatcher.js +11 -11
  49. package/dist/bin/loader.js +8 -8
  50. package/dist/cli/index.js +20 -20
  51. package/dist/common/index.js +21 -21
  52. package/dist/components/index.js +5 -1
  53. package/dist/core-server/index.js +91 -63
  54. package/dist/core-server/presets/common-manager.js +25 -20
  55. package/dist/core-server/presets/common-override-preset.js +9 -9
  56. package/dist/core-server/presets/common-preset.js +22 -22
  57. package/dist/core-server/presets/webpack/loaders/storybook-mock-transform-loader.js +9 -9
  58. package/dist/core-server/presets/webpack/loaders/webpack-automock-loader.js +10 -10
  59. package/dist/csf/index.d.ts +9 -4
  60. package/dist/csf/index.js +37 -8
  61. package/dist/csf-tools/index.d.ts +13 -6
  62. package/dist/csf-tools/index.js +9 -9
  63. package/dist/manager/globals-runtime.js +71 -32
  64. package/dist/manager/runtime.js +459 -242
  65. package/dist/manager-api/index.d.ts +10 -5
  66. package/dist/manager-api/index.js +66 -31
  67. package/dist/node-logger/index.js +14 -14
  68. package/dist/preview/runtime.js +96 -62
  69. package/dist/preview-api/index.d.ts +70 -71
  70. package/dist/preview-api/index.js +1 -1
  71. package/dist/server-errors.js +10 -10
  72. package/dist/telemetry/index.js +24 -24
  73. package/dist/types/index.d.ts +24 -7
  74. package/dist/viewport/index.d.ts +36 -4
  75. package/dist/viewport/index.js +1 -1
  76. package/package.json +1 -1
  77. package/dist/_node-chunks/camelcase-BZ55OCHI.js +0 -18
  78. package/dist/_node-chunks/chunk-FDDJHDCE.js +0 -62
  79. package/dist/_node-chunks/chunk-R5DIBOM6.js +0 -18
@@ -6696,6 +6696,7 @@ var useLayoutSyncingState = /* @__PURE__ */ __name(({
6696
6696
  });
6697
6697
  const { navSize, rightPanelWidth, bottomPanelHeight } = internalDraggingSizeState.isDragging ? internalDraggingSizeState : managerLayoutState;
6698
6698
  const customisedNavSize = api.getNavSizeWithCustomisations?.(navSize) ?? navSize;
6699
+ const customisedShowPanel = api.getShowPanelWithCustomisations?.(isPanelShown) ?? isPanelShown;
6699
6700
  return {
6700
6701
  navSize: customisedNavSize,
6701
6702
  rightPanelWidth,
@@ -6704,7 +6705,7 @@ var useLayoutSyncingState = /* @__PURE__ */ __name(({
6704
6705
  panelResizerRef,
6705
6706
  sidebarResizerRef,
6706
6707
  showPages: isPagesShown,
6707
- showPanel: isPanelShown,
6708
+ showPanel: customisedShowPanel,
6708
6709
  isDragging: internalDraggingSizeState.isDragging
6709
6710
  };
6710
6711
  }, "useLayoutSyncingState");
@@ -6774,7 +6775,7 @@ var LayoutContainer = styled.div(
6774
6775
  gridTemplateColumns: `minmax(0, ${navSize}px) minmax(${MINIMUM_CONTENT_WIDTH_PX}px, 1fr) minmax(0, ${rightPanelWidth}px)`,
6775
6776
  gridTemplateRows: `1fr minmax(0, ${bottomPanelHeight}px)`,
6776
6777
  gridTemplateAreas: (() => {
6777
- if (viewMode === "docs" || !showPanel) {
6778
+ if (!showPanel) {
6778
6779
  return `"sidebar content content"
6779
6780
  "sidebar content content"`;
6780
6781
  }
@@ -11220,7 +11221,7 @@ var HighlightStyles = /* @__PURE__ */ __name(({ refId, itemId }) => react_defaul
11220
11221
  background: background2,
11221
11222
  "&:hover, &:focus": { background: background2 }
11222
11223
  },
11223
- [`&[data-nodetype="story"], &[data-nodetype="document"]`]: {
11224
+ [`&[data-nodetype="story"], &[data-nodetype="document"], &[data-nodetype="test"]`]: {
11224
11225
  color: color2.defaultText,
11225
11226
  background: background2,
11226
11227
  "&:hover, &:focus": { background: background2 }
@@ -11252,7 +11253,7 @@ var getAncestorIds = (0, import_memoizerific2.default)(1e3)(
11252
11253
  );
11253
11254
  var getDescendantIds = (0, import_memoizerific2.default)(1e3)((data, id, skipLeafs) => {
11254
11255
  const entry = data[id];
11255
- if (!entry || entry.type === "story" || entry.type === "docs" || !entry.children) {
11256
+ if (!entry || !("children" in entry) || !entry.children) {
11256
11257
  return [];
11257
11258
  }
11258
11259
  return entry.children.reduce((acc, childId) => {
@@ -11649,6 +11650,7 @@ var GROUP_ID = "icon--group";
11649
11650
  var COMPONENT_ID = "icon--component";
11650
11651
  var DOCUMENT_ID = "icon--document";
11651
11652
  var STORY_ID = "icon--story";
11653
+ var TEST_ID = "icon--test";
11652
11654
  var SUCCESS_ID = "icon--success";
11653
11655
  var ERROR_ID = "icon--error";
11654
11656
  var WARNING_ID = "icon--warning";
@@ -11692,6 +11694,14 @@ var IconSymbols = /* @__PURE__ */ __name(() => {
11692
11694
  d: "M3.5 0h7a.5.5 0 01.5.5v13a.5.5 0 01-.454.498.462.462 0 01-.371-.118L7 11.159l-3.175 2.72a.46.46 0 01-.379.118A.5.5 0 013 13.5V.5a.5.5 0 01.5-.5zM4 12.413l2.664-2.284a.454.454 0 01.377-.128.498.498 0 01.284.12L10 12.412V1H4v11.413z",
11693
11695
  fill: "currentColor"
11694
11696
  }
11697
+ )), react_default.createElement("symbol", { id: TEST_ID }, react_default.createElement(
11698
+ "path",
11699
+ {
11700
+ fillRule: "evenodd",
11701
+ clipRule: "evenodd",
11702
+ d: "M4.5 2h.75v3.866l-3.034 5.26A1.25 1.25 0 003.299 13H10.7a1.25 1.25 0 001.083-1.875L8.75 5.866V2h.75a.5.5 0 100-1h-5a.5.5 0 000 1zm1.75 4V2h1.5v4.134l.067.116L8.827 8H5.173l1.01-1.75.067-.116V6zM4.597 9l-1.515 2.625A.25.25 0 003.3 12H10.7a.25.25 0 00.217-.375L9.404 9H4.597z",
11703
+ fill: "currentColor"
11704
+ }
11695
11705
  )), react_default.createElement("symbol", { id: SUCCESS_ID }, react_default.createElement(
11696
11706
  "path",
11697
11707
  {
@@ -11731,6 +11741,9 @@ var UseSymbol = /* @__PURE__ */ __name(({ type }) => {
11731
11741
  if (type === "story") {
11732
11742
  return react_default.createElement("use", { xlinkHref: `#${STORY_ID}` });
11733
11743
  }
11744
+ if (type === "test") {
11745
+ return react_default.createElement("use", { xlinkHref: `#${TEST_ID}` });
11746
+ }
11734
11747
  if (type === "success") {
11735
11748
  return react_default.createElement("use", { xlinkHref: `#${SUCCESS_ID}` });
11736
11749
  }
@@ -11790,7 +11803,7 @@ var getMostCriticalStatusValue = /* @__PURE__ */ __name((statusValues) => {
11790
11803
  }, "getMostCriticalStatusValue");
11791
11804
  function getGroupStatus(collapsedData, allStatuses) {
11792
11805
  return Object.values(collapsedData).reduce((acc, item) => {
11793
- if (item.type === "group" || item.type === "component") {
11806
+ if (item.type === "group" || item.type === "component" || item.type === "story") {
11794
11807
  const leafs = getDescendantIds(collapsedData, item.id, false).map((id) => collapsedData[id]).filter((i2) => i2.type === "story");
11795
11808
  const combinedStatus = getMostCriticalStatusValue(
11796
11809
  // @ts-expect-error (non strict)
@@ -12033,23 +12046,22 @@ var useStatusSummary = /* @__PURE__ */ __name((item) => {
12033
12046
  }, "useStatusSummary");
12034
12047
 
12035
12048
  // src/manager/components/sidebar/components/CollapseIcon.tsx
12036
- var CollapseIconWrapper = styled.div(({ theme, isExpanded }) => ({
12049
+ var CollapseIconWrapper = styled.div(({ isExpanded }) => ({
12037
12050
  width: 8,
12038
12051
  height: 8,
12039
12052
  display: "flex",
12040
12053
  justifyContent: "center",
12041
12054
  alignItems: "center",
12042
- color: curriedTransparentize$1(0.4, theme.textMutedColor),
12043
12055
  transform: isExpanded ? "rotateZ(90deg)" : "none",
12044
12056
  transition: "transform .1s ease-out"
12045
12057
  }));
12046
- var CollapseIcon2 = /* @__PURE__ */ __name(({ isExpanded }) => react_default.createElement(CollapseIconWrapper, { isExpanded }, react_default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "8", height: "8", fill: "none" }, react_default.createElement(
12058
+ var CollapseIcon2 = /* @__PURE__ */ __name((props) => react_default.createElement(CollapseIconWrapper, { ...props }, react_default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "8", height: "8", fill: "none" }, react_default.createElement(
12047
12059
  "path",
12048
12060
  {
12049
- fill: "#73828C",
12061
+ fill: "currentColor",
12050
12062
  fillRule: "evenodd",
12051
- d: "M1.896 7.146a.5.5 0 1 0 .708.708l3.5-3.5a.5.5 0 0 0 0-.708l-3.5-3.5a.5.5 0 1 0-.708.708L5.043 4 1.896 7.146Z",
12052
- clipRule: "evenodd"
12063
+ clipRule: "evenodd",
12064
+ d: "M1.896 7.146a.5.5 0 1 0 .708.708l3.5-3.5a.5.5 0 0 0 0-.708l-3.5-3.5a.5.5 0 1 0-.708.708L5.043 4 1.896 7.146Z"
12053
12065
  }
12054
12066
  ))), "CollapseIcon");
12055
12067
 
@@ -12072,44 +12084,42 @@ var TypeIcon2 = styled.svg(
12072
12084
  if (type === "story") {
12073
12085
  return theme.color.seafoam;
12074
12086
  }
12087
+ if (type === "test") {
12088
+ return theme.color.green;
12089
+ }
12075
12090
  return "currentColor";
12076
12091
  })()
12077
12092
  })
12078
12093
  );
12079
- var BranchNode = styled.button(({ theme, depth = 0, isExpandable = false }) => ({
12094
+ var commonNodeStyles = /* @__PURE__ */ __name(({
12095
+ theme,
12096
+ depth = 0,
12097
+ isExpandable = false
12098
+ }) => ({
12099
+ flex: 1,
12080
12100
  width: "100%",
12081
- border: "none",
12082
12101
  cursor: "pointer",
12083
12102
  display: "flex",
12084
12103
  alignItems: "start",
12085
12104
  textAlign: "left",
12086
- paddingLeft: `${(isExpandable ? 8 : 22) + depth * 18}px`,
12105
+ textDecoration: "none",
12106
+ border: "none",
12087
12107
  color: "inherit",
12088
12108
  fontSize: `${theme.typography.size.s2}px`,
12109
+ fontWeight: "inherit",
12089
12110
  background: "transparent",
12090
12111
  minHeight: 28,
12091
12112
  borderRadius: 4,
12092
12113
  gap: 6,
12093
- paddingTop: 5,
12094
- paddingBottom: 4
12095
- }));
12096
- var LeafNode = styled.a(({ theme, depth = 0 }) => ({
12097
- width: "100%",
12098
- cursor: "pointer",
12099
- color: "inherit",
12100
- display: "flex",
12101
- gap: 6,
12102
- flex: 1,
12103
- alignItems: "start",
12104
- paddingLeft: `${22 + depth * 18}px`,
12114
+ paddingLeft: `${(isExpandable ? 8 : 22) + depth * 18}px`,
12105
12115
  paddingTop: 5,
12106
12116
  paddingBottom: 4,
12107
- fontSize: `${theme.typography.size.s2}px`,
12108
- textDecoration: "none",
12109
12117
  overflowWrap: "break-word",
12110
12118
  wordWrap: "break-word",
12111
12119
  wordBreak: "break-word"
12112
- }));
12120
+ }), "commonNodeStyles");
12121
+ var BranchNode = styled.button(commonNodeStyles);
12122
+ var LeafNode = styled.a(commonNodeStyles);
12113
12123
  var RootNode = styled.div(({ theme }) => ({
12114
12124
  display: "flex",
12115
12125
  alignItems: "center",
@@ -12139,22 +12149,37 @@ var GroupNode = react_default.memo(/* @__PURE__ */ __name(function GroupNode2({
12139
12149
  return react_default.createElement(BranchNode, { isExpandable, tabIndex: -1, ...props }, react_default.createElement(Wrapper, null, isExpandable && react_default.createElement(CollapseIcon2, { isExpanded }), react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "14", height: "14", type: "group" }, react_default.createElement(UseSymbol, { type: "group" }))), children2);
12140
12150
  }, "GroupNode"));
12141
12151
  var ComponentNode = react_default.memo(
12142
- /* @__PURE__ */ __name(function ComponentNode2({ theme, children: children2, isExpanded, isExpandable, isSelected, ...props }) {
12152
+ /* @__PURE__ */ __name(function ComponentNode2({
12153
+ theme,
12154
+ children: children2,
12155
+ isExpanded = false,
12156
+ isExpandable = false,
12157
+ isSelected,
12158
+ ...props
12159
+ }) {
12143
12160
  return react_default.createElement(BranchNode, { isExpandable, tabIndex: -1, ...props }, react_default.createElement(Wrapper, null, isExpandable && react_default.createElement(CollapseIcon2, { isExpanded }), react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "12", height: "12", type: "component" }, react_default.createElement(UseSymbol, { type: "component" }))), children2);
12144
12161
  }, "ComponentNode")
12145
12162
  );
12146
- var DocumentNode = react_default.memo(
12147
- /* @__PURE__ */ __name(function DocumentNode2({ theme, children: children2, docsMode, ...props }) {
12148
- return react_default.createElement(LeafNode, { tabIndex: -1, ...props }, react_default.createElement(Wrapper, null, react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "12", height: "12", type: "document" }, react_default.createElement(UseSymbol, { type: "document" }))), children2);
12149
- }, "DocumentNode")
12150
- );
12163
+ var DocumentNode = react_default.memo(/* @__PURE__ */ __name(function DocumentNode2({ theme, children: children2, docsMode, ...props }) {
12164
+ return react_default.createElement(LeafNode, { tabIndex: -1, ...props }, react_default.createElement(Wrapper, null, react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "12", height: "12", type: "document" }, react_default.createElement(UseSymbol, { type: "document" }))), children2);
12165
+ }, "DocumentNode"));
12151
12166
  var StoryNode = react_default.memo(/* @__PURE__ */ __name(function StoryNode2({
12152
12167
  theme,
12153
12168
  children: children2,
12169
+ isExpandable = false,
12170
+ isExpanded = false,
12171
+ isSelected,
12154
12172
  ...props
12155
12173
  }) {
12156
- return react_default.createElement(LeafNode, { tabIndex: -1, ...props }, react_default.createElement(Wrapper, null, react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "12", height: "12", type: "story" }, react_default.createElement(UseSymbol, { type: "story" }))), children2);
12174
+ return react_default.createElement(BranchNode, { isExpandable, tabIndex: -1, ...props }, react_default.createElement(Wrapper, null, isExpandable && react_default.createElement(CollapseIcon2, { isExpanded }), react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "12", height: "12", type: "story" }, react_default.createElement(UseSymbol, { type: "story" }))), children2);
12157
12175
  }, "StoryNode"));
12176
+ var TestNode = react_default.memo(/* @__PURE__ */ __name(function TestNode2({
12177
+ theme,
12178
+ children: children2,
12179
+ ...props
12180
+ }) {
12181
+ return react_default.createElement(LeafNode, { tabIndex: -1, ...props }, react_default.createElement(Wrapper, null, react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "12", height: "12", type: "test" }, react_default.createElement(UseSymbol, { type: "test" }))), children2);
12182
+ }, "TestNode"));
12158
12183
 
12159
12184
  // ../node_modules/es-toolkit/dist/function/after.mjs
12160
12185
  function after(n3, func) {
@@ -12520,7 +12545,7 @@ var useExpanded = /* @__PURE__ */ __name(({
12520
12545
  target.blur();
12521
12546
  }
12522
12547
  const type = highlightedElement.getAttribute("data-nodetype");
12523
- if ((isEnter || isSpace) && ["component", "story", "document"].includes(type)) {
12548
+ if (type && (isEnter || isSpace) && ["component", "story", "document", "test"].includes(type)) {
12524
12549
  onSelectStoryId(highlightedItemId);
12525
12550
  }
12526
12551
  const isExpanded = highlightedElement.getAttribute("aria-expanded");
@@ -12565,7 +12590,7 @@ var Container6 = styled.div((props) => ({
12565
12590
  marginTop: props.hasOrphans ? 20 : 0,
12566
12591
  marginBottom: 20
12567
12592
  }));
12568
- var CollapseButton = styled.button(({ theme }) => ({
12593
+ var CollapseButton = styled.button({
12569
12594
  all: "unset",
12570
12595
  display: "flex",
12571
12596
  padding: "0px 8px",
@@ -12579,7 +12604,7 @@ var CollapseButton = styled.button(({ theme }) => ({
12579
12604
  outline: "none",
12580
12605
  background: "var(--tree-node-background-hover)"
12581
12606
  }
12582
- }));
12607
+ });
12583
12608
  var LeafNodeStyleWrapper = styled.div(({ theme }) => ({
12584
12609
  position: "relative",
12585
12610
  display: "flex",
@@ -12673,22 +12698,23 @@ var statusOrder = [
12673
12698
  "status-value:pending",
12674
12699
  "status-value:unknown"
12675
12700
  ];
12676
- var Node2 = react_default.memo(/* @__PURE__ */ __name(function Node3({
12677
- item,
12678
- statuses,
12679
- groupStatus,
12680
- refId,
12681
- docsMode,
12682
- isOrphan,
12683
- isDisplayed,
12684
- isSelected,
12685
- isFullyExpanded,
12686
- setFullyExpanded,
12687
- isExpanded,
12688
- setExpanded,
12689
- onSelectStoryId,
12690
- api
12691
- }) {
12701
+ var Node2 = react_default.memo(/* @__PURE__ */ __name(function Node3(props) {
12702
+ const {
12703
+ item,
12704
+ statuses,
12705
+ groupStatus,
12706
+ refId,
12707
+ docsMode,
12708
+ isOrphan,
12709
+ isDisplayed,
12710
+ isSelected,
12711
+ isFullyExpanded,
12712
+ setFullyExpanded,
12713
+ isExpanded,
12714
+ setExpanded,
12715
+ onSelectStoryId,
12716
+ api
12717
+ } = props;
12692
12718
  const { isDesktop, isMobile, setMobileMenuOpen } = useLayout();
12693
12719
  const { counts, statusesByValue } = useStatusSummary(item);
12694
12720
  if (!isDisplayed) {
@@ -12745,58 +12771,6 @@ var Node2 = react_default.memo(/* @__PURE__ */ __name(function Node3({
12745
12771
  const id = createId(item.id, refId);
12746
12772
  const contextMenu = refId === "storybook_internal" ? useContextMenu(item, statusLinks, api) : { node: null, onMouseEnter: /* @__PURE__ */ __name(() => {
12747
12773
  }, "onMouseEnter") };
12748
- if (item.type === "story" || item.type === "docs") {
12749
- const LeafNode2 = item.type === "docs" ? DocumentNode : StoryNode;
12750
- const statusValue = getMostCriticalStatusValue(
12751
- Object.values(statuses || {}).map((s2) => s2.value)
12752
- );
12753
- const [icon, textColor] = statusMapping[statusValue];
12754
- return react_default.createElement(
12755
- LeafNodeStyleWrapper,
12756
- {
12757
- key: id,
12758
- className: "sidebar-item",
12759
- "data-selected": isSelected,
12760
- "data-ref-id": refId,
12761
- "data-item-id": item.id,
12762
- "data-parent-id": item.parent,
12763
- "data-nodetype": item.type === "docs" ? "document" : "story",
12764
- "data-highlightable": isDisplayed,
12765
- onMouseEnter: contextMenu.onMouseEnter
12766
- },
12767
- react_default.createElement(
12768
- LeafNode2,
12769
- {
12770
- style: isSelected ? {} : { color: textColor },
12771
- href: getLink(item, refId),
12772
- id,
12773
- depth: isOrphan ? item.depth : item.depth - 1,
12774
- onClick: (event) => {
12775
- event.preventDefault();
12776
- onSelectStoryId(item.id);
12777
- if (isMobile) {
12778
- setMobileMenuOpen(false);
12779
- }
12780
- },
12781
- ...item.type === "docs" && { docsMode }
12782
- },
12783
- item.renderLabel?.(item, api) || item.name
12784
- ),
12785
- isSelected && react_default.createElement(SkipToContentLink, { asChild: true }, react_default.createElement("a", { href: "#storybook-preview-wrapper" }, "Skip to canvas")),
12786
- contextMenu.node,
12787
- icon ? react_default.createElement(
12788
- StatusButton,
12789
- {
12790
- "aria-label": `Test status: ${statusValue.replace("status-value:", "")}`,
12791
- role: "status",
12792
- type: "button",
12793
- status: statusValue,
12794
- selectedItem: isSelected
12795
- },
12796
- icon
12797
- ) : null
12798
- );
12799
- }
12800
12774
  if (item.type === "root") {
12801
12775
  return react_default.createElement(
12802
12776
  RootNode,
@@ -12838,15 +12812,31 @@ var Node2 = react_default.memo(/* @__PURE__ */ __name(function Node3({
12838
12812
  )
12839
12813
  );
12840
12814
  }
12841
- if (item.type === "component" || item.type === "group") {
12842
- const itemStatus = groupStatus?.[item.id];
12843
- const color2 = itemStatus ? statusMapping[itemStatus][1] : null;
12844
- const BranchNode2 = item.type === "component" ? ComponentNode : GroupNode;
12815
+ const itemStatus = getMostCriticalStatusValue(Object.values(statuses || {}).map((s2) => s2.value));
12816
+ const [itemIcon, itemColor] = statusMapping[itemStatus];
12817
+ const itemStatusButton = itemIcon ? react_default.createElement(
12818
+ StatusButton,
12819
+ {
12820
+ "aria-label": `Test status: ${itemStatus.replace("status-value:", "")}`,
12821
+ role: "status",
12822
+ type: "button",
12823
+ status: itemStatus,
12824
+ selectedItem: isSelected
12825
+ },
12826
+ itemIcon
12827
+ ) : null;
12828
+ if (item.type === "component" || item.type === "group" || item.type === "story" && "children" in item && item.children) {
12829
+ const { children: children2 = [] } = item;
12830
+ const BranchNode2 = { component: ComponentNode, group: GroupNode, story: StoryNode }[item.type];
12831
+ const status = getMostCriticalStatusValue([itemStatus, groupStatus?.[item.id]]);
12832
+ const color2 = status ? statusMapping[status][1] : null;
12833
+ const showBranchStatus = status === "status-value:error" || status === "status-value:warning";
12845
12834
  return react_default.createElement(
12846
12835
  LeafNodeStyleWrapper,
12847
12836
  {
12848
12837
  key: id,
12849
12838
  className: "sidebar-item",
12839
+ "data-selected": isSelected,
12850
12840
  "data-ref-id": refId,
12851
12841
  "data-item-id": item.id,
12852
12842
  "data-parent-id": item.parent,
@@ -12858,24 +12848,32 @@ var Node2 = react_default.memo(/* @__PURE__ */ __name(function Node3({
12858
12848
  BranchNode2,
12859
12849
  {
12860
12850
  id,
12861
- style: color2 ? { color: color2 } : {},
12862
- "aria-controls": item.children && item.children.join(" "),
12851
+ style: color2 && !isSelected ? { color: color2 } : {},
12852
+ "aria-controls": children2.join(" "),
12863
12853
  "aria-expanded": isExpanded,
12864
12854
  depth: isOrphan ? item.depth : item.depth - 1,
12865
- isComponent: item.type === "component",
12866
- isExpandable: item.children && item.children.length > 0,
12855
+ isExpandable: children2.length > 0,
12867
12856
  isExpanded,
12868
12857
  onClick: (event) => {
12869
12858
  event.preventDefault();
12870
- setExpanded({ ids: [item.id], value: !isExpanded });
12871
- if (item.type === "component" && !isExpanded && isDesktop) {
12859
+ if (item.type === "story") {
12872
12860
  onSelectStoryId(item.id);
12861
+ if (!isExpanded || isSelected) {
12862
+ setExpanded({ ids: [item.id], value: !isExpanded });
12863
+ }
12864
+ } else if (item.type === "component") {
12865
+ if (!isExpanded && isDesktop) {
12866
+ onSelectStoryId(item.id);
12867
+ }
12868
+ setExpanded({ ids: [item.id], value: !isExpanded });
12869
+ } else {
12870
+ setExpanded({ ids: [item.id], value: !isExpanded });
12873
12871
  }
12874
12872
  },
12875
12873
  onMouseEnter: () => {
12876
- if (item.type === "component") {
12874
+ if (item.type === "component" || item.type === "story") {
12877
12875
  api.emit(PRELOAD_ENTRIES, {
12878
- ids: [item.children[0]],
12876
+ ids: [children2[0]],
12879
12877
  options: { target: refId }
12880
12878
  });
12881
12879
  }
@@ -12883,11 +12881,49 @@ var Node2 = react_default.memo(/* @__PURE__ */ __name(function Node3({
12883
12881
  },
12884
12882
  item.renderLabel?.(item, api) || item.name
12885
12883
  ),
12884
+ isSelected && react_default.createElement(SkipToContentLink, { asChild: true }, react_default.createElement("a", { href: "#storybook-preview-wrapper" }, "Skip to canvas")),
12886
12885
  contextMenu.node,
12887
- ["status-value:error", "status-value:warning"].includes(itemStatus) && react_default.createElement(StatusButton, { type: "button", status: itemStatus }, react_default.createElement("svg", { key: "icon", viewBox: "0 0 6 6", width: "6", height: "6", type: "dot" }, react_default.createElement(UseSymbol, { type: "dot" })))
12886
+ showBranchStatus ? react_default.createElement(StatusButton, { type: "button", status, selectedItem: isSelected }, react_default.createElement("svg", { key: "icon", viewBox: "0 0 6 6", width: "6", height: "6", type: "dot" }, react_default.createElement(UseSymbol, { type: "dot" }))) : itemStatusButton
12888
12887
  );
12889
12888
  }
12890
- return null;
12889
+ const isTest = item.type === "story" && item.subtype === "test";
12890
+ const LeafNode2 = isTest ? TestNode : { docs: DocumentNode, story: StoryNode }[item.type];
12891
+ const nodeType = isTest ? "test" : { docs: "document", story: "story" }[item.type];
12892
+ return react_default.createElement(
12893
+ LeafNodeStyleWrapper,
12894
+ {
12895
+ key: id,
12896
+ className: "sidebar-item",
12897
+ "data-selected": isSelected,
12898
+ "data-ref-id": refId,
12899
+ "data-item-id": item.id,
12900
+ "data-parent-id": item.parent,
12901
+ "data-nodetype": nodeType,
12902
+ "data-highlightable": isDisplayed,
12903
+ onMouseEnter: contextMenu.onMouseEnter
12904
+ },
12905
+ react_default.createElement(
12906
+ LeafNode2,
12907
+ {
12908
+ style: itemColor && !isSelected ? { color: itemColor } : {},
12909
+ href: getLink(item, refId),
12910
+ id,
12911
+ depth: isOrphan ? item.depth : item.depth - 1,
12912
+ onClick: (event) => {
12913
+ event.preventDefault();
12914
+ onSelectStoryId(item.id);
12915
+ if (isMobile) {
12916
+ setMobileMenuOpen(false);
12917
+ }
12918
+ },
12919
+ ...item.type === "docs" && { docsMode }
12920
+ },
12921
+ item.renderLabel?.(item, api) || item.name
12922
+ ),
12923
+ isSelected && react_default.createElement(SkipToContentLink, { asChild: true }, react_default.createElement("a", { href: "#storybook-preview-wrapper" }, "Skip to canvas")),
12924
+ contextMenu.node,
12925
+ itemStatusButton
12926
+ );
12891
12927
  }, "Node"));
12892
12928
  var Root = react_default.memo(/* @__PURE__ */ __name(function Root2({
12893
12929
  setExpanded,
@@ -12966,7 +13002,7 @@ var Tree = react_default.memo(/* @__PURE__ */ __name(function Tree2({
12966
13002
  if (onlyChild.type === "docs") {
12967
13003
  return true;
12968
13004
  }
12969
- if (onlyChild.type === "story") {
13005
+ if (onlyChild.type === "story" && onlyChild.subtype === "story") {
12970
13006
  return isStoryHoistable(onlyChild.name, name);
12971
13007
  }
12972
13008
  return false;
@@ -12974,7 +13010,7 @@ var Tree = react_default.memo(/* @__PURE__ */ __name(function Tree2({
12974
13010
  }, [data]);
12975
13011
  const collapsedItems = useMemo(
12976
13012
  () => Object.keys(data).filter((id) => !singleStoryComponentIds.includes(id)),
12977
- [singleStoryComponentIds]
13013
+ [data, singleStoryComponentIds]
12978
13014
  );
12979
13015
  const collapsedData = useMemo(() => {
12980
13016
  return singleStoryComponentIds.reduce(
@@ -12996,7 +13032,7 @@ var Tree = react_default.memo(/* @__PURE__ */ __name(function Tree2({
12996
13032
  },
12997
13033
  { ...data }
12998
13034
  );
12999
- }, [data]);
13035
+ }, [data, singleStoryComponentIds]);
13000
13036
  const ancestry = useMemo(() => {
13001
13037
  return collapsedItems.reduce(
13002
13038
  (acc, id) => Object.assign(acc, { [id]: getAncestorIds(collapsedData, id) }),
@@ -13173,11 +13209,10 @@ var Ref = react_default.memo(/* @__PURE__ */ __name(function Ref2(props) {
13173
13209
  const handleClick = useCallback(() => setExpanded((value) => !value), [setExpanded]);
13174
13210
  const setHighlightedItemId = useCallback(
13175
13211
  (itemId) => setHighlighted({ itemId, refId }),
13176
- [setHighlighted]
13212
+ [setHighlighted, refId]
13177
13213
  );
13178
13214
  const onSelectStoryId = useCallback(
13179
- // @ts-expect-error (non strict)
13180
- (storyId) => api && api.selectStory(storyId, void 0, { ref: !isMain && refId }),
13215
+ (storyId) => api?.selectStory(storyId, void 0, { ref: isMain ? void 0 : refId }),
13181
13216
  [api, isMain, refId]
13182
13217
  );
13183
13218
  return react_default.createElement(react_default.Fragment, null, isMain || react_default.createElement(
@@ -13293,9 +13328,8 @@ var useHighlighted = /* @__PURE__ */ __name(({
13293
13328
  if (highlightable[nextIndex].getAttribute("data-nodetype") === "component") {
13294
13329
  const { itemId, refId } = highlightedRef.current;
13295
13330
  const item = api.resolveStory(itemId, refId === "storybook_internal" ? void 0 : refId);
13296
- if (item.type === "component") {
13331
+ if (item?.type === "component") {
13297
13332
  api.emit(PRELOAD_ENTRIES, {
13298
- // @ts-expect-error (non strict)
13299
13333
  ids: [item.children[0]],
13300
13334
  options: { target: refId }
13301
13335
  });
@@ -13305,7 +13339,7 @@ var useHighlighted = /* @__PURE__ */ __name(({
13305
13339
  }, "navigateTree");
13306
13340
  document6.addEventListener("keydown", navigateTree);
13307
13341
  return () => document6.removeEventListener("keydown", navigateTree);
13308
- }, [isLoading, isBrowsing, highlightedRef, highlightElement]);
13342
+ }, [api, containerRef, isLoading, isBrowsing, highlightedRef, highlightElement]);
13309
13343
  return [highlighted, updateHighlighted, highlightedRef];
13310
13344
  }, "useHighlighted");
13311
13345
 
@@ -17144,7 +17178,7 @@ var Result = react_default.memo(/* @__PURE__ */ __name(function Result2({ item,
17144
17178
  const nameMatch = matches2.find((match) => match.key === "name");
17145
17179
  const pathMatches = matches2.filter((match) => match.key === "path");
17146
17180
  const [icon] = item.status ? statusMapping[item.status] : [];
17147
- return react_default.createElement(ResultRow, { ...props, onClick: click }, react_default.createElement(IconWrapper2, null, item.type === "component" && react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "14", height: "14", type: "component" }, react_default.createElement(UseSymbol, { type: "component" })), item.type === "story" && react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "14", height: "14", type: "story" }, react_default.createElement(UseSymbol, { type: "story" })), !(item.type === "component" || item.type === "story") && react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "14", height: "14", type: "document" }, react_default.createElement(UseSymbol, { type: "document" }))), react_default.createElement(ResultRowContent, { className: "search-result-item--label" }, react_default.createElement(Title, null, react_default.createElement(Highlight, { match: nameMatch }, item.name)), react_default.createElement(Path, null, item.path.map((group, index) => react_default.createElement("span", { key: index }, react_default.createElement(Highlight, { match: pathMatches.find((match) => match.arrayIndex === index) }, group))))), item.status ? react_default.createElement(StatusLabel, { status: item.status }, icon) : null);
17181
+ return react_default.createElement(ResultRow, { ...props, onClick: click }, react_default.createElement(IconWrapper2, null, item.type === "component" && react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "14", height: "14", type: "component" }, react_default.createElement(UseSymbol, { type: "component" })), item.type === "story" && react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "14", height: "14", type: item.subtype }, react_default.createElement(UseSymbol, { type: item.subtype })), !(item.type === "component" || item.type === "story") && react_default.createElement(TypeIcon2, { viewBox: "0 0 14 14", width: "14", height: "14", type: "document" }, react_default.createElement(UseSymbol, { type: "document" }))), react_default.createElement(ResultRowContent, { className: "search-result-item--label" }, react_default.createElement(Title, null, react_default.createElement(Highlight, { match: nameMatch }, item.name)), react_default.createElement(Path, null, item.path.map((group, index) => react_default.createElement("span", { key: index }, react_default.createElement(Highlight, { match: pathMatches.find((match) => match.arrayIndex === index) }, group))))), item.status ? react_default.createElement(StatusLabel, { status: item.status }, icon) : null);
17148
17182
  }, "Result"));
17149
17183
  var SearchResults = react_default.memo(/* @__PURE__ */ __name(function SearchResults2({
17150
17184
  query,
@@ -17791,19 +17825,14 @@ var SidebarBottom = /* @__PURE__ */ __name(({ isDevelopment }) => {
17791
17825
  }, "SidebarBottom");
17792
17826
 
17793
17827
  // src/manager/components/sidebar/TagsFilterPanel.tsx
17794
- var BUILT_IN_TAGS = /* @__PURE__ */ new Set([
17795
- "dev",
17796
- "test",
17797
- "autodocs",
17798
- "attached-mdx",
17799
- "unattached-mdx",
17800
- "play-fn",
17801
- "test-fn",
17802
- "vitest",
17803
- "svelte-csf",
17804
- "svelte-csf-v4",
17805
- "svelte-csf-v5"
17806
- ]);
17828
+ var groupByType = /* @__PURE__ */ __name((filters) => filters.reduce(
17829
+ (acc, filter2) => {
17830
+ acc[filter2.type] = acc[filter2.type] || [];
17831
+ acc[filter2.type].push(filter2);
17832
+ return acc;
17833
+ },
17834
+ {}
17835
+ ), "groupByType");
17807
17836
  var Wrapper3 = styled.div({
17808
17837
  minWidth: 240,
17809
17838
  maxWidth: 300
@@ -17815,79 +17844,183 @@ var Actions3 = styled.div(({ theme }) => ({
17815
17844
  padding: 4,
17816
17845
  borderBottom: `1px solid ${theme.appBorderColor}`
17817
17846
  }));
17847
+ var TagRow = styled.div({
17848
+ display: "flex",
17849
+ "& button": {
17850
+ width: 64,
17851
+ maxWidth: 64,
17852
+ marginLeft: 4,
17853
+ paddingLeft: 0,
17854
+ paddingRight: 0,
17855
+ fontWeight: "normal",
17856
+ transition: "all 150ms"
17857
+ },
17858
+ "&:not(:hover)": {
17859
+ "& button": {
17860
+ marginLeft: 0,
17861
+ maxWidth: 0,
17862
+ opacity: 0
17863
+ },
17864
+ "& svg + input": {
17865
+ display: "none"
17866
+ }
17867
+ }
17868
+ });
17869
+ var Label = styled.div({
17870
+ overflow: "hidden",
17871
+ textOverflow: "ellipsis",
17872
+ whiteSpace: "nowrap"
17873
+ });
17874
+ var MutedText = styled.span(({ theme }) => ({
17875
+ color: theme.textMutedColor
17876
+ }));
17818
17877
  var TagsFilterPanel = /* @__PURE__ */ __name(({
17819
17878
  api,
17820
- allTags,
17821
- selectedTags,
17822
- toggleTag,
17823
- setAllTags,
17824
- inverted,
17825
- setInverted,
17826
- isDevelopment
17879
+ filtersById,
17880
+ includedFilters,
17881
+ excludedFilters,
17882
+ toggleFilter,
17883
+ setAllFilters,
17884
+ resetFilters,
17885
+ isDevelopment,
17886
+ isDefaultSelection,
17887
+ hasDefaultSelection
17827
17888
  }) => {
17828
- const [builtInEntries, userEntries] = Array.from(allTags.entries()).reduce(
17829
- (acc, [tag, count]) => {
17830
- acc[BUILT_IN_TAGS.has(tag) ? 0 : 1].push([tag, count]);
17831
- return acc;
17832
- },
17833
- [[], []]
17889
+ const ref = useRef(null);
17890
+ const renderLink = /* @__PURE__ */ __name(({
17891
+ id,
17892
+ type,
17893
+ title,
17894
+ icon,
17895
+ count
17896
+ }) => {
17897
+ const onToggle = /* @__PURE__ */ __name((selected, excluded) => toggleFilter(id, selected, excluded), "onToggle");
17898
+ const isIncluded = includedFilters.has(id);
17899
+ const isExcluded = excludedFilters.has(id);
17900
+ const isChecked = isIncluded || isExcluded;
17901
+ const toggleTagLabel = `${isChecked ? "Remove" : "Add"} ${type} filter: ${title}`;
17902
+ const invertButtonLabel = `${isExcluded ? "Include" : "Exclude"} ${type}: ${title}`;
17903
+ if (count === 0 && type === "built-in") {
17904
+ return void 0;
17905
+ }
17906
+ return {
17907
+ id: `filter-${type}-${id}`,
17908
+ content: react_default.createElement(TagRow, null, react_default.createElement(
17909
+ WithTooltip,
17910
+ {
17911
+ delayShow: 1e3,
17912
+ hasChrome: false,
17913
+ style: { minWidth: 0, flex: 1 },
17914
+ tooltip: react_default.createElement(TooltipNote, { note: toggleTagLabel }),
17915
+ trigger: "hover"
17916
+ },
17917
+ react_default.createElement(
17918
+ ListItem,
17919
+ {
17920
+ as: "label",
17921
+ icon: react_default.createElement(react_default.Fragment, null, isExcluded ? react_default.createElement(DeleteIcon, null) : isIncluded ? null : icon, react_default.createElement(
17922
+ Form.Checkbox,
17923
+ {
17924
+ checked: isChecked,
17925
+ onChange: () => onToggle(!isChecked),
17926
+ "data-tag": title
17927
+ }
17928
+ )),
17929
+ "aria-label": toggleTagLabel,
17930
+ title: react_default.createElement(Label, null, title, isExcluded && react_default.createElement(MutedText, null, " (excluded)")),
17931
+ right: isExcluded ? react_default.createElement("s", null, count) : react_default.createElement("span", null, count)
17932
+ }
17933
+ )
17934
+ ), react_default.createElement(
17935
+ WithTooltip,
17936
+ {
17937
+ delayShow: 1e3,
17938
+ hasChrome: false,
17939
+ tooltip: react_default.createElement(TooltipNote, { note: invertButtonLabel }),
17940
+ trigger: "hover"
17941
+ },
17942
+ react_default.createElement(
17943
+ Button,
17944
+ {
17945
+ variant: "ghost",
17946
+ size: "medium",
17947
+ onClick: () => onToggle(true, !isExcluded),
17948
+ "aria-label": invertButtonLabel
17949
+ },
17950
+ isExcluded ? "Include" : "Exclude"
17951
+ )
17952
+ ))
17953
+ };
17954
+ }, "renderLink");
17955
+ const groups = groupByType(Object.values(filtersById));
17956
+ const links = Object.values(groups).map(
17957
+ (group) => group.sort((a2, b2) => a2.id.localeCompare(b2.id)).map((filter2) => renderLink(filter2)).filter(Boolean)
17834
17958
  );
17835
- const docsUrl = api.getDocsUrl({ subpath: "writing-stories/tags#filtering-by-custom-tags" });
17836
- const noTags = {
17837
- id: "no-tags",
17838
- title: "There are no tags. Use tags to organize and filter your Storybook.",
17839
- isIndented: false
17840
- };
17841
- const groups = [
17842
- allTags.size === 0 ? [noTags] : [],
17843
- userEntries.sort((a2, b2) => a2[0].localeCompare(b2[0])).map(([tag, count]) => {
17844
- const checked = selectedTags.includes(tag);
17845
- const id = `tag-${tag}`;
17846
- return {
17847
- id,
17848
- title: tag,
17849
- right: count,
17850
- input: react_default.createElement(Form.Checkbox, { checked, onChange: () => toggleTag(tag) })
17851
- };
17852
- }),
17853
- builtInEntries.sort((a2, b2) => a2[0].localeCompare(b2[0])).map(([tag, count]) => {
17854
- const checked = selectedTags.includes(tag);
17855
- const id = `tag-${tag}`;
17856
- return {
17857
- id,
17858
- title: tag,
17859
- right: count,
17860
- input: react_default.createElement(Form.Checkbox, { checked, onChange: () => toggleTag(tag) })
17861
- };
17862
- })
17863
- ];
17864
- if (userEntries.length === 0 && isDevelopment) {
17865
- groups.push([
17959
+ if (!groups.tag?.length && isDevelopment) {
17960
+ links.push([
17866
17961
  {
17867
17962
  id: "tags-docs",
17868
17963
  title: "Learn how to add tags",
17869
17964
  icon: react_default.createElement(DocumentIcon, null),
17870
17965
  right: react_default.createElement(ShareAltIcon, null),
17871
- href: docsUrl
17966
+ href: api.getDocsUrl({ subpath: "writing-stories/tags#filtering-by-custom-tags" })
17872
17967
  }
17873
17968
  ]);
17874
17969
  }
17875
- return react_default.createElement(Wrapper3, null, allTags.size > 0 && react_default.createElement(Actions3, null, selectedTags.length ? react_default.createElement(IconButton, { id: "unselect-all", onClick: () => setAllTags(false) }, react_default.createElement(CloseIcon, null), "Clear filters") : react_default.createElement(IconButton, { id: "select-all", onClick: () => setAllTags(true) }, react_default.createElement(BatchAcceptIcon, null), "Select all"), react_default.createElement(
17970
+ const filtersLabel = includedFilters.size === 0 && excludedFilters.size === 0 ? "Select all" : "Clear filters";
17971
+ return react_default.createElement(Wrapper3, { ref }, Object.keys(filtersById).length > 0 && react_default.createElement(Actions3, null, includedFilters.size === 0 && excludedFilters.size === 0 ? react_default.createElement(
17972
+ IconButton,
17973
+ {
17974
+ id: "select-all",
17975
+ "aria-label": filtersLabel,
17976
+ key: "select-all",
17977
+ onClick: () => setAllFilters(true)
17978
+ },
17979
+ react_default.createElement(BatchAcceptIcon, null),
17980
+ filtersLabel
17981
+ ) : react_default.createElement(
17876
17982
  IconButton,
17877
17983
  {
17878
- id: "invert-selection",
17879
- disabled: selectedTags.length === 0,
17880
- onClick: () => setInverted(!inverted),
17881
- active: inverted
17984
+ id: "deselect-all",
17985
+ "aria-label": filtersLabel,
17986
+ key: "deselect-all",
17987
+ onClick: () => setAllFilters(false)
17882
17988
  },
17883
- inverted ? react_default.createElement(EyeCloseIcon, null) : react_default.createElement(EyeIcon, null),
17884
- "Invert"
17885
- )), react_default.createElement(TooltipLinkList, { links: groups }));
17989
+ react_default.createElement(SweepIcon, null),
17990
+ filtersLabel
17991
+ ), hasDefaultSelection && react_default.createElement(
17992
+ WithTooltip,
17993
+ {
17994
+ delayShow: 1e3,
17995
+ hasChrome: false,
17996
+ tooltip: react_default.createElement(TooltipNote, { note: "Reset to default selection" }),
17997
+ trigger: "hover"
17998
+ },
17999
+ react_default.createElement(
18000
+ IconButton,
18001
+ {
18002
+ id: "reset-filters",
18003
+ key: "reset-filters",
18004
+ onClick: resetFilters,
18005
+ "aria-label": "Reset filters",
18006
+ disabled: isDefaultSelection
18007
+ },
18008
+ react_default.createElement(UndoIcon, null)
18009
+ )
18010
+ )), react_default.createElement(TooltipLinkList, { links }));
17886
18011
  }, "TagsFilterPanel");
17887
18012
 
17888
18013
  // src/manager/components/sidebar/TagsFilter.tsx
17889
18014
  var TAGS_FILTER = "tags-filter";
17890
- var BUILT_IN_TAGS_HIDE = /* @__PURE__ */ new Set(["dev", "autodocs", "test", "attached-mdx", "unattached-mdx"]);
18015
+ var BUILT_IN_TAGS = /* @__PURE__ */ new Set([
18016
+ "dev",
18017
+ "test",
18018
+ "autodocs",
18019
+ "attached-mdx",
18020
+ "unattached-mdx",
18021
+ "play-fn",
18022
+ "test-fn"
18023
+ ]);
17891
18024
  var Wrapper4 = styled.div({
17892
18025
  position: "relative"
17893
18026
  });
@@ -17908,53 +18041,119 @@ var TagSelected = styled(Badge)(({ theme }) => ({
17908
18041
  background: theme.color.secondary,
17909
18042
  color: theme.color.lightest
17910
18043
  }));
17911
- var TagsFilter = /* @__PURE__ */ __name(({
17912
- api,
17913
- indexJson,
17914
- initialSelectedTags = [],
17915
- isDevelopment
17916
- }) => {
17917
- const [selectedTags, setSelectedTags] = useState(initialSelectedTags);
18044
+ var TagsFilter = /* @__PURE__ */ __name(({ api, indexJson, isDevelopment, tagPresets }) => {
18045
+ const filtersById = useMemo(() => {
18046
+ const userTagsCounts = Object.values(indexJson.entries).reduce((acc, entry) => {
18047
+ entry.tags?.forEach((tag) => {
18048
+ if (!BUILT_IN_TAGS.has(tag)) {
18049
+ acc.set(tag, (acc.get(tag) || 0) + 1);
18050
+ }
18051
+ });
18052
+ return acc;
18053
+ }, /* @__PURE__ */ new Map());
18054
+ const userFilters = Object.fromEntries(
18055
+ userTagsCounts.entries().map(([tag, count]) => {
18056
+ const filterFn = /* @__PURE__ */ __name((entry, excluded) => excluded ? !entry.tags?.includes(tag) : !!entry.tags?.includes(tag), "filterFn");
18057
+ return [tag, { id: tag, type: "tag", title: tag, count, filterFn }];
18058
+ })
18059
+ );
18060
+ const withCount = /* @__PURE__ */ __name((filterFn) => ({
18061
+ count: Object.values(indexJson.entries).filter((entry) => filterFn(entry)).length,
18062
+ filterFn
18063
+ }), "withCount");
18064
+ const builtInFilters = {
18065
+ _docs: {
18066
+ id: "_docs",
18067
+ type: "built-in",
18068
+ title: "Documentation",
18069
+ icon: react_default.createElement(DocumentIcon, { color: color.gold }),
18070
+ ...withCount(
18071
+ (entry, excluded) => excluded ? entry.type !== "docs" : entry.type === "docs"
18072
+ )
18073
+ },
18074
+ _play: {
18075
+ id: "_play",
18076
+ type: "built-in",
18077
+ title: "Play",
18078
+ icon: react_default.createElement(PlayHollowIcon, { color: color.seafoam }),
18079
+ ...withCount(
18080
+ (entry, excluded) => excluded ? entry.type !== "story" || !entry.tags?.includes("play-fn") : entry.type === "story" && !!entry.tags?.includes("play-fn")
18081
+ )
18082
+ },
18083
+ _test: {
18084
+ id: "_test",
18085
+ type: "built-in",
18086
+ title: "Testing",
18087
+ icon: react_default.createElement(BeakerIcon, { color: color.green }),
18088
+ ...withCount(
18089
+ (entry, excluded) => excluded ? entry.type !== "story" || entry.subtype !== "test" : entry.type === "story" && entry.subtype === "test"
18090
+ )
18091
+ }
18092
+ };
18093
+ return { ...userFilters, ...builtInFilters };
18094
+ }, [indexJson.entries]);
18095
+ const { defaultIncluded, defaultExcluded } = useMemo(() => {
18096
+ return Object.entries(tagPresets).reduce(
18097
+ (acc, [tag, { defaultFilterSelection }]) => {
18098
+ if (defaultFilterSelection === "include") {
18099
+ acc.defaultIncluded.add(tag);
18100
+ } else if (defaultFilterSelection === "exclude") {
18101
+ acc.defaultExcluded.add(tag);
18102
+ }
18103
+ return acc;
18104
+ },
18105
+ { defaultIncluded: /* @__PURE__ */ new Set(), defaultExcluded: /* @__PURE__ */ new Set() }
18106
+ );
18107
+ }, [tagPresets]);
18108
+ const [includedFilters, setIncludedFilters] = useState(new Set(defaultIncluded));
18109
+ const [excludedFilters, setExcludedFilters] = useState(new Set(defaultExcluded));
17918
18110
  const [expanded, setExpanded] = useState(false);
17919
- const [inverted, setInverted] = useState(false);
17920
- const tagsActive = selectedTags.length > 0;
18111
+ const tagsActive = includedFilters.size > 0 || excludedFilters.size > 0;
18112
+ const resetFilters = useCallback(() => {
18113
+ setIncludedFilters(new Set(defaultIncluded));
18114
+ setExcludedFilters(new Set(defaultExcluded));
18115
+ }, [defaultIncluded, defaultExcluded]);
18116
+ useEffect(resetFilters, [resetFilters]);
17921
18117
  useEffect(() => {
17922
18118
  api.experimental_setFilter(TAGS_FILTER, (item) => {
17923
- if (selectedTags.length === 0) {
17924
- return true;
17925
- }
17926
- const match = selectedTags.some((tag) => item.tags?.includes(tag));
17927
- return inverted ? !match : match;
17928
- });
17929
- }, [api, selectedTags, inverted]);
17930
- const allTags = Object.values(indexJson.entries).reduce((acc, entry) => {
17931
- entry.tags?.forEach((tag) => {
17932
- if (!BUILT_IN_TAGS_HIDE.has(tag)) {
17933
- acc.set(tag, (acc.get(tag) || 0) + 1);
17934
- }
18119
+ const included = Object.values(
18120
+ groupByType(Array.from(includedFilters).map((id) => filtersById[id]))
18121
+ );
18122
+ const excluded = Object.values(
18123
+ groupByType(Array.from(excludedFilters).map((id) => filtersById[id]))
18124
+ );
18125
+ return (!included.length || included.every((group) => group.some(({ filterFn }) => filterFn(item, false)))) && (!excluded.length || excluded.every((group) => group.every(({ filterFn }) => filterFn(item, true))));
17935
18126
  });
17936
- return acc;
17937
- }, /* @__PURE__ */ new Map());
17938
- const toggleTag = useCallback(
17939
- (tag) => {
17940
- if (selectedTags.includes(tag)) {
17941
- setSelectedTags(selectedTags.filter((t2) => t2 !== tag));
18127
+ }, [api, includedFilters, excludedFilters, filtersById]);
18128
+ const toggleFilter = useCallback(
18129
+ (id, selected, excluded) => {
18130
+ const set2 = /* @__PURE__ */ new Set([id]);
18131
+ if (excluded === true) {
18132
+ setExcludedFilters(excludedFilters.union(set2));
18133
+ setIncludedFilters(includedFilters.difference(set2));
18134
+ } else if (excluded === false) {
18135
+ setIncludedFilters(includedFilters.union(set2));
18136
+ setExcludedFilters(excludedFilters.difference(set2));
18137
+ } else if (selected) {
18138
+ setIncludedFilters(includedFilters.union(set2));
18139
+ setExcludedFilters(excludedFilters.difference(set2));
17942
18140
  } else {
17943
- setSelectedTags([...selectedTags, tag]);
18141
+ setIncludedFilters(includedFilters.difference(set2));
18142
+ setExcludedFilters(excludedFilters.difference(set2));
17944
18143
  }
17945
18144
  },
17946
- [selectedTags, setSelectedTags]
18145
+ [includedFilters, excludedFilters]
17947
18146
  );
17948
- const setAllTags = useCallback(
18147
+ const setAllFilters = useCallback(
17949
18148
  (selected) => {
17950
18149
  if (selected) {
17951
- setSelectedTags(Array.from(allTags.keys()));
18150
+ setIncludedFilters(new Set(Object.keys(filtersById)));
17952
18151
  } else {
17953
- setSelectedTags([]);
17954
- setInverted(false);
18152
+ setIncludedFilters(/* @__PURE__ */ new Set());
17955
18153
  }
18154
+ setExcludedFilters(/* @__PURE__ */ new Set());
17956
18155
  },
17957
- [allTags, setSelectedTags]
18156
+ [filtersById]
17958
18157
  );
17959
18158
  const handleToggleExpand = useCallback(
17960
18159
  (event) => {
@@ -17963,7 +18162,7 @@ var TagsFilter = /* @__PURE__ */ __name(({
17963
18162
  },
17964
18163
  [expanded, setExpanded]
17965
18164
  );
17966
- if (allTags.size === 0 && !isDevelopment) {
18165
+ if (Object.keys(filtersById).length === 0 && !isDevelopment) {
17967
18166
  return null;
17968
18167
  }
17969
18168
  return react_default.createElement(
@@ -17977,18 +18176,20 @@ var TagsFilter = /* @__PURE__ */ __name(({
17977
18176
  TagsFilterPanel,
17978
18177
  {
17979
18178
  api,
17980
- allTags,
17981
- selectedTags,
17982
- toggleTag,
17983
- setAllTags,
17984
- inverted,
17985
- setInverted,
17986
- isDevelopment
18179
+ filtersById,
18180
+ includedFilters,
18181
+ excludedFilters,
18182
+ toggleFilter,
18183
+ setAllFilters,
18184
+ resetFilters,
18185
+ isDevelopment,
18186
+ isDefaultSelection: includedFilters.symmetricDifference(defaultIncluded).size === 0 && excludedFilters.symmetricDifference(defaultExcluded).size === 0,
18187
+ hasDefaultSelection: defaultIncluded.size > 0 || defaultExcluded.size > 0
17987
18188
  }
17988
18189
  ),
17989
18190
  closeOnOutsideClick: true
17990
18191
  },
17991
- react_default.createElement(Wrapper4, null, react_default.createElement(IconButton, { key: "tags", title: "Tag filters", active: tagsActive, onClick: handleToggleExpand }, react_default.createElement(FilterIcon, null)), selectedTags.length > 0 && react_default.createElement(TagSelected, null))
18192
+ react_default.createElement(Wrapper4, null, react_default.createElement(IconButton, { key: "tags", title: "Tag filters", active: tagsActive, onClick: handleToggleExpand }, react_default.createElement(FilterIcon, null)), includedFilters.size + excludedFilters.size > 0 && react_default.createElement(TagSelected, null))
17992
18193
  );
17993
18194
  }, "TagsFilter");
17994
18195
 
@@ -25047,6 +25248,14 @@ var Sidebar = react_default.memo(/* @__PURE__ */ __name(function Sidebar2({
25047
25248
  const lastViewedProps = useLastViewed(selected);
25048
25249
  const { isMobile } = useLayout();
25049
25250
  const api = useStorybookApi();
25251
+ const tagPresets = useMemo(
25252
+ () => Object.entries(scope.TAGS_OPTIONS ?? {}).reduce((acc, entry) => {
25253
+ const [tag, option] = entry;
25254
+ acc[tag] = option;
25255
+ return acc;
25256
+ }, {}),
25257
+ []
25258
+ );
25050
25259
  return react_default.createElement(Container7, { className: "container sidebar-container", "aria-label": "Global" }, react_default.createElement(ScrollArea, { vertical: true, offset: 3, scrollbarSize: 6 }, react_default.createElement(Top, { row: 1.6 }, react_default.createElement(
25051
25260
  Heading,
25052
25261
  {
@@ -25088,7 +25297,15 @@ var Sidebar = react_default.memo(/* @__PURE__ */ __name(function Sidebar2({
25088
25297
  onOpenChange: setIsFileSearchModalOpen
25089
25298
  }
25090
25299
  )),
25091
- searchFieldContent: indexJson && react_default.createElement(TagsFilter, { api, indexJson, isDevelopment }),
25300
+ searchFieldContent: indexJson && react_default.createElement(
25301
+ TagsFilter,
25302
+ {
25303
+ api,
25304
+ indexJson,
25305
+ isDevelopment,
25306
+ tagPresets
25307
+ }
25308
+ ),
25092
25309
  ...lastViewedProps
25093
25310
  },
25094
25311
  ({