diff --git a/src/Storybook/StorybookTreeView.luau b/src/Storybook/StorybookTreeView.luau index 858a2fa8..42cbba6d 100644 --- a/src/Storybook/StorybookTreeView.luau +++ b/src/Storybook/StorybookTreeView.luau @@ -2,34 +2,34 @@ local React = require("@pkg/React") local Storyteller = require("@pkg/Storyteller") local TreeView = require("@root/TreeView/TreeView") +local TreeViewContext = require("@root/TreeView/TreeViewContext") +local creaTreeNodesForStorybook = require("@root/TreeView/creaTreeNodesForStorybook") local treeViewTypes = require("@root/TreeView/types") type TreeNode = treeViewTypes.TreeNode -type Storybook = Storyteller.Storybook +type LoadedStorybook = Storyteller.LoadedStorybook -local useMemo = React.useMemo +local useEffect = React.useEffect export type Props = { - storybooks: { Storybook }, + storybooks: { LoadedStorybook }, + layoutOrder: number?, } local function StorybookTreeView(props: Props) - local storyModules = useMemo(function() - -- TODO: Collect all story modules under each storybook's `storyRoots` array - end, { props.storybooks }) + local treeViewContext = TreeViewContext.use() - local leafNodes = useMemo(function() - local nodes: { TreeNode } = {} - for _, storyModule in storyModules do - -- table.insert(nodes, {}) + useEffect(function() + local roots: { TreeNode } = {} + for _, storybook in props.storybooks do + local nodes = creaTreeNodesForStorybook(storybook) + table.insert(roots, nodes.root) end - return nodes - end, { storyModules }) - - local roots = useMemo(function() end, { props.storybooks }) + treeViewContext.setRoots(roots) + end, { props.storybooks }) return React.createElement(TreeView, { - roots = roots, + layoutOrder = props.layoutOrder, }) end diff --git a/src/Storybook/StorybookTreeView.story.luau b/src/Storybook/StorybookTreeView.story.luau new file mode 100644 index 00000000..4a9e3242 --- /dev/null +++ b/src/Storybook/StorybookTreeView.story.luau @@ -0,0 +1,33 @@ +local ModuleLoader = require("@pkg/ModuleLoader") +local React = require("@pkg/React") +local Storyteller = require("@pkg/Storyteller") + +local ContextProviders = require("@root/Common/ContextProviders") +local MockPlugin = require("@root/Testing/MockPlugin") +local StorybookTreeView = require("./StorybookTreeView") + +local loader = ModuleLoader.new() + +local function Story() + local storybooks = Storyteller.useStorybooks(game, loader) + + return React.createElement("Frame", { + Size = UDim2.fromOffset(300, 0), + AutomaticSize = Enum.AutomaticSize.Y, + BackgroundTransparency = 1, + }, { + StorybookTreeView = React.createElement(StorybookTreeView, { + storybooks = storybooks, + }), + }) +end + +return { + story = function() + return React.createElement(ContextProviders, { + plugin = MockPlugin.new(), + }, { + Story = React.createElement(Story), + }) + end, +} diff --git a/src/TreeView/TreeView.luau b/src/TreeView/TreeView.luau index 1b3dffd5..e1a5715a 100644 --- a/src/TreeView/TreeView.luau +++ b/src/TreeView/TreeView.luau @@ -9,7 +9,7 @@ type TreeNode = types.TreeNode type Tree = types.Tree local function TreeView(props: { - LayoutOrder: number?, + layoutOrder: number?, }) local treeViewContext = TreeViewContext.use() @@ -24,7 +24,7 @@ local function TreeView(props: { return React.createElement("Frame", { BackgroundTransparency = 1, AutomaticSize = Enum.AutomaticSize.XY, - LayoutOrder = props.LayoutOrder, + LayoutOrder = props.layoutOrder, }, { Layout = React.createElement("UIListLayout", { SortOrder = Enum.SortOrder.LayoutOrder, diff --git a/src/TreeView/TreeViewContext.luau b/src/TreeView/TreeViewContext.luau index ff961249..3ab2c991 100644 --- a/src/TreeView/TreeViewContext.luau +++ b/src/TreeView/TreeViewContext.luau @@ -15,7 +15,7 @@ local useMemo = React.useMemo local useState = React.useState type TreeViewContext = { - setRoots: (nodes: { PartialTreeNode }) -> (), + setRoots: (nodes: { PartialTreeNode | TreeNode }) -> (), getRoots: () -> { TreeNode }, activateNode: (node: TreeNode) -> (), isExpanded: (node: TreeNode) -> boolean, @@ -83,7 +83,7 @@ local function TreeNodeProvider(props: { end end, {}) - local setRoots = useCallback(function(partials: { PartialTreeNode }) + local setRoots = useCallback(function(partials: { TreeNode | PartialTreeNode }) local newNodes = createTreeNodesFromPartials(partials) for _, node in newNodes.expandedByDefault do diff --git a/src/TreeView/creaTreeNodesForStorybook.luau b/src/TreeView/creaTreeNodesForStorybook.luau new file mode 100644 index 00000000..06afd5ef --- /dev/null +++ b/src/TreeView/creaTreeNodesForStorybook.luau @@ -0,0 +1,70 @@ +local Storyteller = require("@pkg/Storyteller") + +local types = require("./types") + +type PartialTreeNode = types.PartialTreeNode +type TreeNode = types.TreeNode +type LoadedStorybook = Storyteller.LoadedStorybook +type LoadedStory = Storyteller.LoadedStory + +local function creaTreeNodesForStorybook(storybook: LoadedStorybook): { + root: TreeNode, + leaves: { TreeNode }, + nodesByInstance: { [Instance]: TreeNode }, +} + local leaves: { TreeNode } = {} + local nodesByInstance: { [Instance]: TreeNode } = {} + + local root: TreeNode = { + label = if storybook.name then storybook.name else "Unnamed Storybook", + icon = "storybook", + isExpanded = false, + children = {}, + } + + for _, storyModule in Storyteller.findStoryModulesForStorybook(storybook) do + local currentNode: TreeNode = { + label = storyModule.Name, + icon = "story", + isExpanded = false, + children = {}, + } + + table.insert(leaves, currentNode) + + local parentInstance = storyModule.Parent + + while parentInstance do + local existingParentNode = nodesByInstance[parentInstance] + + if existingParentNode then + if table.find(storybook.storyRoots, parentInstance) then + table.insert(root.children, existingParentNode) + break + end + + table.insert(existingParentNode.children, currentNode) + else + local parentNode: TreeNode = { + label = parentInstance.Name, + icon = "folder", + isExpanded = false, + children = { currentNode }, + } + + nodesByInstance[parentInstance] = parentNode + currentNode = parentNode + end + + parentInstance = parentInstance.Parent + end + end + + return { + root = root, + leaves = leaves, + nodesByInstance = nodesByInstance, + } +end + +return creaTreeNodesForStorybook diff --git a/src/TreeView/createTreeNodesFromPartials.luau b/src/TreeView/createTreeNodesFromPartials.luau index 551ce261..be616c0c 100644 --- a/src/TreeView/createTreeNodesFromPartials.luau +++ b/src/TreeView/createTreeNodesFromPartials.luau @@ -5,7 +5,7 @@ local types = require("./types") type PartialTreeNode = types.PartialTreeNode type TreeNode = types.TreeNode -local function createTreeNodesFromPartials(partialRoots: { PartialTreeNode }): { +local function createTreeNodesFromPartials(partialRoots: { PartialTreeNode | TreeNode }): { roots: { TreeNode }, leaves: { TreeNode }, expandedByDefault: { TreeNode }, @@ -13,7 +13,7 @@ local function createTreeNodesFromPartials(partialRoots: { PartialTreeNode }): { local leaves: { TreeNode } = {} local expandedByDefault: { TreeNode } = {} - local function process(partials: { PartialTreeNode }, parent: TreeNode?): { TreeNode } + local function process(partials: { PartialTreeNode | TreeNode }, parent: TreeNode?): { TreeNode } local siblings: { TreeNode } = {} for _, partial in partials do