Skip to content

Commit

Permalink
New tree view (#295)
Browse files Browse the repository at this point in the history
# Problem

Our tree view is one of the oldest parts of the plugin and needs some
upgrades before we can solve issues like #275, #282, and #291

# Solution

I've created a new tree view from scratch that should support all the
issues mentioned above.

The tree view itself is fairly generic with the intent of supporting
future cases that require manipulating the structure beyond just
mimicking the DataModel. And due to it being general it should make a
good candidate to be packaged up later.

The StorybookTreeView component sits on top of the tree view to build
out the tree of storybooks and stories. I've also introduced a new user
setting to remember the last opened story. Now when reopening Flipbook
the last story is restored

Resolves #275

# Checklist

- [ ] Resolve issues with search
- [ ] Ran `lune run test` locally before merging
  • Loading branch information
vocksel authored Dec 12, 2024
1 parent 42e091c commit 20bdafe
Show file tree
Hide file tree
Showing 36 changed files with 1,318 additions and 735 deletions.
2 changes: 2 additions & 0 deletions src/Common/ContextProviders.luau
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
local React = require("@pkg/React")
local TreeView = require("@root/TreeView")

local ContextStack = require("@root/Common/ContextStack")
local NavigationContext = require("@root/Navigation/NavigationContext")
Expand All @@ -20,6 +21,7 @@ local function ContextProviders(props: Props)
defaultScreen = "Home",
}),
React.createElement(SettingsContext.Provider),
React.createElement(TreeView.TreeViewProvider),
},
}, props.children)
end
Expand Down
68 changes: 68 additions & 0 deletions src/Common/getInstanceFromFullName.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
--[[
Gets an instance based off the result of GetFullName().
This is used in conjunction with debug.info() to locate the calling script.
Returns nil if the instance is outside the DataModel.
]]

local Sift = require("@pkg/Sift")

local PATH_SEPERATOR = "."

local function maybeGetService(serviceName: string): Instance?
local success, current: any = pcall(function()
return game:GetService(serviceName)
end)

if success and current and current:IsA("Instance") then
return current
else
return nil
end
end

local function getInstanceFromFullName(fullName: string): Instance?
local parts = fullName:split(PATH_SEPERATOR)
local serviceName = table.remove(parts, 1)

if serviceName then
-- This function only works for instances in the DataModel. As such, the
-- first part of the path will always be a service, so if we can't find
-- one we exit out and return nil
local current = maybeGetService(serviceName)

if current then
while #parts > 0 do
-- Keep around a copy of the `parts` array. We are going to concat this
-- into new paths, and incrementally remove from the right to narrow
-- down the file path.
local tempParts = Sift.Array.copy(parts)

-- The result of GetFullName() uses dots to separate paths, but we also
-- use dots in our file names (e.g. with spec and story files). As such,
-- this block will look ahead to see if multiple parts are actually a
-- single filename.
for _ = 1, #tempParts do
local name = table.concat(tempParts, PATH_SEPERATOR)
local found = current:FindFirstChild(name)

if found then
current = found
parts = Sift.List.shift(parts, #name:split(PATH_SEPERATOR))
break
else
-- Reduce from the right until we find the next instance
tempParts = Sift.List.pop(tempParts)
end
end
end

return current
end
end

return nil
end

return getInstanceFromFullName
70 changes: 70 additions & 0 deletions src/Common/getInstanceFromFullName.spec.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local JestGlobals = require("@pkg/JestGlobals")
local newFolder = require("@root/Testing/newFolder")

local getInstanceFromFullName = require("./getInstanceFromFullName")

local expect = JestGlobals.expect
local test = JestGlobals.test
local afterEach = JestGlobals.afterEach

local folder: Folder

afterEach(function()
if folder then
folder:Destroy()
end
end)

test("gets services", function()
local path = getInstanceFromFullName("ReplicatedStorage")
expect(path).toBe(ReplicatedStorage)
end)

test("works on nested instances", function()
local module = Instance.new("ModuleScript")

folder = newFolder({
foo = newFolder({
bar = module,
}),
})
folder.Parent = ReplicatedStorage

local path = getInstanceFromFullName(module:GetFullName())
expect(path).toBe(module)
end)

test("works with spec files", function()
local module = Instance.new("ModuleScript")

folder = newFolder({
foo = newFolder({
["bar.spec"] = module,
}),
})
folder.Parent = ReplicatedStorage

local path = getInstanceFromFullName(module:GetFullName())
expect(path).toBe(module)
end)

test("finds spec files BEFORE the module it is associated with", function()
local module = Instance.new("ModuleScript")

folder = newFolder({
foo = newFolder({
bar = Instance.new("ModuleScript"),
["bar.spec"] = module,
}),
})
folder.Parent = ReplicatedStorage

local path = getInstanceFromFullName(module:GetFullName())
expect(path).toBe(module)
end)

test("returns nil if the first part of the path is not a service", function()
expect(getInstanceFromFullName("Part")).toBeUndefined()
end)
55 changes: 0 additions & 55 deletions src/Explorer/Component.story.luau

This file was deleted.

103 changes: 0 additions & 103 deletions src/Explorer/Component/Directory.luau

This file was deleted.

Loading

0 comments on commit 20bdafe

Please sign in to comment.